| /* |
| * Copyright (C) 2014-2021 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #pragma once |
| |
| #include "MediaSessionGroupIdentifier.h" |
| #include "MediaSessionIdentifier.h" |
| #include "Timer.h" |
| #include <wtf/Logger.h> |
| #include <wtf/LoggerHelper.h> |
| #include <wtf/Noncopyable.h> |
| #include <wtf/WeakPtr.h> |
| #include <wtf/text/WTFString.h> |
| |
| #if ENABLE(WIRELESS_PLAYBACK_TARGET) |
| #include "MediaPlaybackTargetClient.h" |
| #endif |
| |
| namespace WebCore { |
| |
| class Document; |
| class MediaPlaybackTarget; |
| class PlatformMediaSessionClient; |
| class PlatformMediaSessionManager; |
| enum class DelayCallingUpdateNowPlaying { No, Yes }; |
| struct NowPlayingInfo; |
| |
| class PlatformMediaSession |
| : public CanMakeWeakPtr<PlatformMediaSession> |
| #if ENABLE(WIRELESS_PLAYBACK_TARGET) |
| , public MediaPlaybackTargetClient |
| #endif |
| #if !RELEASE_LOG_DISABLED |
| , private LoggerHelper |
| #endif |
| { |
| WTF_MAKE_FAST_ALLOCATED; |
| public: |
| static std::unique_ptr<PlatformMediaSession> create(PlatformMediaSessionManager&, PlatformMediaSessionClient&); |
| |
| virtual ~PlatformMediaSession(); |
| |
| void setActive(bool); |
| |
| enum class MediaType : uint8_t { |
| None = 0, |
| Video, |
| VideoAudio, |
| Audio, |
| WebAudio, |
| }; |
| MediaType mediaType() const; |
| MediaType presentationType() const; |
| |
| enum State : uint8_t { |
| Idle, |
| Autoplaying, |
| Playing, |
| Paused, |
| Interrupted, |
| }; |
| State state() const { return m_state; } |
| void setState(State); |
| |
| enum InterruptionType : uint8_t { |
| NoInterruption, |
| SystemSleep, |
| EnteringBackground, |
| SystemInterruption, |
| SuspendedUnderLock, |
| InvisibleAutoplay, |
| ProcessInactive, |
| PlaybackSuspended, |
| PageNotVisible, |
| }; |
| InterruptionType interruptionType() const { return m_interruptionType; } |
| |
| enum EndInterruptionFlags : uint8_t { |
| NoFlags = 0, |
| MayResumePlaying = 1 << 0, |
| }; |
| |
| virtual void clientCharacteristicsChanged(bool); |
| |
| void beginInterruption(InterruptionType); |
| void endInterruption(EndInterruptionFlags); |
| |
| virtual void clientWillBeginAutoplaying(); |
| virtual bool clientWillBeginPlayback(); |
| virtual bool clientWillPausePlayback(); |
| |
| void clientWillBeDOMSuspended(); |
| |
| void pauseSession(); |
| void stopSession(); |
| |
| virtual void suspendBuffering() { } |
| virtual void resumeBuffering() { } |
| |
| struct RemoteCommandArgument { |
| std::optional<double> time; |
| std::optional<bool> fastSeek; |
| |
| template<class Encoder> void encode(Encoder&) const; |
| template<class Decoder> static std::optional<RemoteCommandArgument> decode(Decoder&); |
| }; |
| |
| enum RemoteControlCommandType : uint8_t { |
| NoCommand, |
| PlayCommand, |
| PauseCommand, |
| StopCommand, |
| TogglePlayPauseCommand, |
| BeginSeekingBackwardCommand, |
| EndSeekingBackwardCommand, |
| BeginSeekingForwardCommand, |
| EndSeekingForwardCommand, |
| SeekToPlaybackPositionCommand, |
| SkipForwardCommand, |
| SkipBackwardCommand, |
| NextTrackCommand, |
| PreviousTrackCommand, |
| BeginScrubbingCommand, |
| EndScrubbingCommand, |
| }; |
| bool canReceiveRemoteControlCommands() const; |
| virtual void didReceiveRemoteControlCommand(RemoteControlCommandType, const RemoteCommandArgument&); |
| bool supportsSeeking() const; |
| |
| enum DisplayType : uint8_t { |
| Normal, |
| Fullscreen, |
| Optimized, |
| }; |
| DisplayType displayType() const; |
| |
| bool isHidden() const; |
| bool isSuspended() const; |
| bool isPlaying() const; |
| |
| bool shouldOverrideBackgroundLoadingRestriction() const; |
| |
| virtual bool isPlayingToWirelessPlaybackTarget() const { return m_isPlayingToWirelessPlaybackTarget; } |
| void isPlayingToWirelessPlaybackTargetChanged(bool); |
| |
| #if ENABLE(WIRELESS_PLAYBACK_TARGET) |
| // MediaPlaybackTargetClient |
| void setPlaybackTarget(Ref<MediaPlaybackTarget>&&) override { } |
| void externalOutputDeviceAvailableDidChange(bool) override { } |
| void setShouldPlayToPlaybackTarget(bool) override { } |
| void playbackTargetPickerWasDismissed() override { } |
| #endif |
| |
| #if PLATFORM(IOS_FAMILY) |
| virtual bool requiresPlaybackTargetRouteMonitoring() const { return false; } |
| #endif |
| |
| bool activeAudioSessionRequired() const; |
| bool canProduceAudio() const; |
| bool hasMediaStreamSource() const; |
| void canProduceAudioChanged(); |
| |
| virtual void resetPlaybackSessionState() { } |
| String sourceApplicationIdentifier() const; |
| |
| bool hasPlayedSinceLastInterruption() const { return m_hasPlayedSinceLastInterruption; } |
| void clearHasPlayedSinceLastInterruption() { m_hasPlayedSinceLastInterruption = false; } |
| |
| bool preparingToPlay() const { return m_preparingToPlay; } |
| |
| #if !RELEASE_LOG_DISABLED |
| const Logger& logger() const final { return m_logger.get(); } |
| const void* logIdentifier() const override { return m_logIdentifier; } |
| const char* logClassName() const override { return "PlatformMediaSession"; } |
| WTFLogChannel& logChannel() const final; |
| #endif |
| |
| bool canPlayConcurrently(const PlatformMediaSession&) const; |
| bool shouldOverridePauseDuringRouteChange() const; |
| |
| class AudioCaptureSource : public CanMakeWeakPtr<AudioCaptureSource> { |
| public: |
| virtual ~AudioCaptureSource() = default; |
| virtual bool isCapturingAudio() const = 0; |
| }; |
| |
| virtual std::optional<NowPlayingInfo> nowPlayingInfo() const; |
| virtual void updateMediaUsageIfChanged() { } |
| |
| MediaSessionIdentifier mediaSessionIdentifier() const { return m_mediaSessionIdentifier; } |
| |
| protected: |
| PlatformMediaSession(PlatformMediaSessionManager&, PlatformMediaSessionClient&); |
| PlatformMediaSessionClient& client() const { return m_client; } |
| |
| private: |
| bool processClientWillPausePlayback(DelayCallingUpdateNowPlaying); |
| |
| PlatformMediaSessionClient& m_client; |
| MediaSessionIdentifier m_mediaSessionIdentifier; |
| State m_state { Idle }; |
| State m_stateToRestore { Idle }; |
| InterruptionType m_interruptionType { NoInterruption }; |
| int m_interruptionCount { 0 }; |
| bool m_active { false }; |
| bool m_notifyingClient { false }; |
| bool m_isPlayingToWirelessPlaybackTarget { false }; |
| bool m_hasPlayedSinceLastInterruption { false }; |
| bool m_preparingToPlay { false }; |
| |
| #if !RELEASE_LOG_DISABLED |
| Ref<const Logger> m_logger; |
| const void* m_logIdentifier; |
| #endif |
| |
| friend class PlatformMediaSessionManager; |
| }; |
| |
| class PlatformMediaSessionClient { |
| WTF_MAKE_NONCOPYABLE(PlatformMediaSessionClient); |
| public: |
| PlatformMediaSessionClient() = default; |
| |
| virtual PlatformMediaSession::MediaType mediaType() const = 0; |
| virtual PlatformMediaSession::MediaType presentationType() const = 0; |
| virtual PlatformMediaSession::DisplayType displayType() const { return PlatformMediaSession::Normal; } |
| |
| virtual void resumeAutoplaying() { } |
| virtual void mayResumePlayback(bool shouldResume) = 0; |
| virtual void suspendPlayback() = 0; |
| |
| virtual bool canReceiveRemoteControlCommands() const = 0; |
| virtual void didReceiveRemoteControlCommand(PlatformMediaSession::RemoteControlCommandType, const PlatformMediaSession::RemoteCommandArgument&) = 0; |
| virtual bool supportsSeeking() const = 0; |
| |
| virtual bool canProduceAudio() const { return false; } |
| virtual bool isSuspended() const { return false; }; |
| virtual bool isPlaying() const { return false; }; |
| |
| virtual bool shouldOverrideBackgroundPlaybackRestriction(PlatformMediaSession::InterruptionType) const = 0; |
| virtual bool shouldOverrideBackgroundLoadingRestriction() const { return false; } |
| |
| virtual void wirelessRoutesAvailableDidChange() { } |
| virtual void setWirelessPlaybackTarget(Ref<MediaPlaybackTarget>&&) { } |
| virtual bool isPlayingToWirelessPlaybackTarget() const { return false; } |
| virtual void setShouldPlayToPlaybackTarget(bool) { } |
| virtual void playbackTargetPickerWasDismissed() { } |
| |
| virtual bool isPlayingOnSecondScreen() const { return false; } |
| |
| virtual MediaSessionGroupIdentifier mediaSessionGroupIdentifier() const = 0; |
| |
| virtual bool hasMediaStreamSource() const { return false; } |
| |
| virtual void processIsSuspendedChanged() { } |
| |
| virtual bool shouldOverridePauseDuringRouteChange() const { return false; } |
| |
| #if !RELEASE_LOG_DISABLED |
| virtual const Logger& logger() const = 0; |
| #endif |
| |
| protected: |
| virtual ~PlatformMediaSessionClient() = default; |
| }; |
| |
| String convertEnumerationToString(PlatformMediaSession::State); |
| String convertEnumerationToString(PlatformMediaSession::InterruptionType); |
| WEBCORE_EXPORT String convertEnumerationToString(PlatformMediaSession::RemoteControlCommandType); |
| |
| template<class Encoder> inline void PlatformMediaSession::RemoteCommandArgument::encode(Encoder& encoder) const |
| { |
| encoder << time << fastSeek; |
| } |
| |
| template<class Decoder> inline std::optional<PlatformMediaSession::RemoteCommandArgument> PlatformMediaSession::RemoteCommandArgument::decode(Decoder& decoder) |
| { |
| #define DECODE(name, type) \ |
| std::optional<std::optional<type>> name; \ |
| decoder >> name; \ |
| if (!name) \ |
| return std::nullopt; \ |
| |
| DECODE(time, double); |
| DECODE(fastSeek, bool); |
| |
| #undef DECODE |
| |
| return { { *time, *fastSeek } }; |
| } |
| |
| } // namespace WebCore |
| |
| namespace WTF { |
| |
| template<typename Type> |
| struct LogArgument; |
| |
| template <> |
| struct LogArgument<WebCore::PlatformMediaSession::State> { |
| static String toString(const WebCore::PlatformMediaSession::State state) |
| { |
| return convertEnumerationToString(state); |
| } |
| }; |
| |
| template <> |
| struct LogArgument<WebCore::PlatformMediaSession::InterruptionType> { |
| static String toString(const WebCore::PlatformMediaSession::InterruptionType state) |
| { |
| return convertEnumerationToString(state); |
| } |
| }; |
| |
| template <> |
| struct LogArgument<WebCore::PlatformMediaSession::RemoteControlCommandType> { |
| static String toString(const WebCore::PlatformMediaSession::RemoteControlCommandType command) |
| { |
| return convertEnumerationToString(command); |
| } |
| }; |
| |
| template <> struct EnumTraits<WebCore::PlatformMediaSession::MediaType> { |
| using values = EnumValues < |
| WebCore::PlatformMediaSession::MediaType, |
| WebCore::PlatformMediaSession::MediaType::None, |
| WebCore::PlatformMediaSession::MediaType::Video, |
| WebCore::PlatformMediaSession::MediaType::VideoAudio, |
| WebCore::PlatformMediaSession::MediaType::Audio, |
| WebCore::PlatformMediaSession::MediaType::WebAudio |
| >; |
| }; |
| |
| template <> struct EnumTraits<WebCore::PlatformMediaSession::State> { |
| using values = EnumValues < |
| WebCore::PlatformMediaSession::State, |
| WebCore::PlatformMediaSession::State::Idle, |
| WebCore::PlatformMediaSession::State::Autoplaying, |
| WebCore::PlatformMediaSession::State::Playing, |
| WebCore::PlatformMediaSession::State::Paused, |
| WebCore::PlatformMediaSession::State::Interrupted |
| >; |
| }; |
| |
| template <> struct EnumTraits<WebCore::PlatformMediaSession::InterruptionType> { |
| using values = EnumValues < |
| WebCore::PlatformMediaSession::InterruptionType, |
| WebCore::PlatformMediaSession::InterruptionType::NoInterruption, |
| WebCore::PlatformMediaSession::InterruptionType::SystemSleep, |
| WebCore::PlatformMediaSession::InterruptionType::EnteringBackground, |
| WebCore::PlatformMediaSession::InterruptionType::SystemInterruption, |
| WebCore::PlatformMediaSession::InterruptionType::SuspendedUnderLock, |
| WebCore::PlatformMediaSession::InterruptionType::InvisibleAutoplay, |
| WebCore::PlatformMediaSession::InterruptionType::ProcessInactive, |
| WebCore::PlatformMediaSession::InterruptionType::PlaybackSuspended, |
| WebCore::PlatformMediaSession::InterruptionType::PageNotVisible |
| >; |
| }; |
| |
| template <> struct EnumTraits<WebCore::PlatformMediaSession::EndInterruptionFlags> { |
| using values = EnumValues < |
| WebCore::PlatformMediaSession::EndInterruptionFlags, |
| WebCore::PlatformMediaSession::EndInterruptionFlags::NoFlags, |
| WebCore::PlatformMediaSession::EndInterruptionFlags::MayResumePlaying |
| >; |
| }; |
| |
| template <> struct EnumTraits<WebCore::PlatformMediaSession::RemoteControlCommandType> { |
| using values = EnumValues < |
| WebCore::PlatformMediaSession::RemoteControlCommandType, |
| WebCore::PlatformMediaSession::RemoteControlCommandType::NoCommand, |
| WebCore::PlatformMediaSession::RemoteControlCommandType::PlayCommand, |
| WebCore::PlatformMediaSession::RemoteControlCommandType::PauseCommand, |
| WebCore::PlatformMediaSession::RemoteControlCommandType::StopCommand, |
| WebCore::PlatformMediaSession::RemoteControlCommandType::TogglePlayPauseCommand, |
| WebCore::PlatformMediaSession::RemoteControlCommandType::BeginSeekingBackwardCommand, |
| WebCore::PlatformMediaSession::RemoteControlCommandType::EndSeekingBackwardCommand, |
| WebCore::PlatformMediaSession::RemoteControlCommandType::BeginSeekingForwardCommand, |
| WebCore::PlatformMediaSession::RemoteControlCommandType::EndSeekingForwardCommand, |
| WebCore::PlatformMediaSession::RemoteControlCommandType::SeekToPlaybackPositionCommand, |
| WebCore::PlatformMediaSession::RemoteControlCommandType::SkipForwardCommand, |
| WebCore::PlatformMediaSession::RemoteControlCommandType::SkipBackwardCommand, |
| WebCore::PlatformMediaSession::RemoteControlCommandType::NextTrackCommand, |
| WebCore::PlatformMediaSession::RemoteControlCommandType::PreviousTrackCommand, |
| WebCore::PlatformMediaSession::RemoteControlCommandType::BeginScrubbingCommand, |
| WebCore::PlatformMediaSession::RemoteControlCommandType::EndScrubbingCommand |
| >; |
| }; |
| |
| } // namespace WTF |