blob: b4cffd5883d7234910c1f8dfa36ad24d0678b74f [file] [log] [blame]
/*
* 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