diff options
Diffstat (limited to 'dom/media/MediaDecoderStateMachineBase.h')
-rw-r--r-- | dom/media/MediaDecoderStateMachineBase.h | 308 |
1 files changed, 308 insertions, 0 deletions
diff --git a/dom/media/MediaDecoderStateMachineBase.h b/dom/media/MediaDecoderStateMachineBase.h new file mode 100644 index 0000000000..b4950746e8 --- /dev/null +++ b/dom/media/MediaDecoderStateMachineBase.h @@ -0,0 +1,308 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef DOM_MEDIA_MEDIADECODERSTATEMACHINEBASE_H_ +#define DOM_MEDIA_MEDIADECODERSTATEMACHINEBASE_H_ + +#include "DecoderDoctorDiagnostics.h" +#include "MediaDecoder.h" +#include "MediaDecoderOwner.h" +#include "MediaEventSource.h" +#include "MediaInfo.h" +#include "MediaMetadataManager.h" +#include "MediaPromiseDefs.h" +#include "ReaderProxy.h" +#include "VideoFrameContainer.h" +#include "mozilla/dom/MediaDebugInfoBinding.h" +#include "mozilla/Variant.h" +#include "nsISupportsImpl.h" + +class AudioDeviceInfo; + +namespace mozilla { + +class AbstractThread; +class CDMProxy; +class FrameStatistics; +class MediaFormatReader; +class TaskQueue; + +struct MediaPlaybackEvent { + enum EventType { + PlaybackStarted, + PlaybackStopped, + PlaybackProgressed, + PlaybackEnded, + SeekStarted, + Invalidate, + EnterVideoSuspend, + ExitVideoSuspend, + StartVideoSuspendTimer, + CancelVideoSuspendTimer, + VideoOnlySeekBegin, + VideoOnlySeekCompleted, + } mType; + + using DataType = Variant<Nothing, int64_t>; + DataType mData; + + MOZ_IMPLICIT MediaPlaybackEvent(EventType aType) + : mType(aType), mData(Nothing{}) {} + + template <typename T> + MediaPlaybackEvent(EventType aType, T&& aArg) + : mType(aType), mData(std::forward<T>(aArg)) {} +}; + +enum class VideoDecodeMode : uint8_t { Normal, Suspend }; + +/** + * The state machine class. This manages the decoding and seeking in the + * MediaDecoderReader on the decode task queue, and A/V sync on the shared + * state machine thread, and controls the audio "push" thread. + * + * All internal state is synchronised via the decoder monitor. State changes + * are propagated by scheduling the state machine to run another cycle on the + * shared state machine thread. + */ +class MediaDecoderStateMachineBase { + public: + NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING + + using FirstFrameEventSourceExc = + MediaEventSourceExc<UniquePtr<MediaInfo>, MediaDecoderEventVisibility>; + using MetadataEventSourceExc = + MediaEventSourceExc<UniquePtr<MediaInfo>, UniquePtr<MetadataTags>, + MediaDecoderEventVisibility>; + using NextFrameStatus = MediaDecoderOwner::NextFrameStatus; + + MediaDecoderStateMachineBase(MediaDecoder* aDecoder, + MediaFormatReader* aReader); + + virtual nsresult Init(MediaDecoder* aDecoder); + + RefPtr<ShutdownPromise> BeginShutdown(); + + // Seeks to the decoder to aTarget asynchronously. + RefPtr<MediaDecoder::SeekPromise> InvokeSeek(const SeekTarget& aTarget); + + virtual size_t SizeOfVideoQueue() const = 0; + virtual size_t SizeOfAudioQueue() const = 0; + + // Sets the video decode mode. Used by the suspend-video-decoder feature. + virtual void SetVideoDecodeMode(VideoDecodeMode aMode) = 0; + + // Set new sink device. ExternalEngineStateMachine will reject the returned + // promise with NS_ERROR_ABORT to indicate that the action is not supported. + // MediaDecoderStateMachine will resolve the promise when the previous + // device is no longer in use and an attempt to open the new device + // completes (successfully or not) or is deemed unnecessary because the + // device is not required for output at this time. MediaDecoderStateMachine + // will always consider the switch in underlying output device successful + // and continue attempting to open the new device even if opening initially + // fails. + virtual RefPtr<GenericPromise> InvokeSetSink( + const RefPtr<AudioDeviceInfo>& aSink) = 0; + virtual void InvokeSuspendMediaSink() = 0; + virtual void InvokeResumeMediaSink() = 0; + + virtual RefPtr<GenericPromise> RequestDebugInfo( + dom::MediaDecoderStateMachineDebugInfo& aInfo) = 0; + + // Returns the state machine task queue. + TaskQueue* OwnerThread() const { return mTaskQueue; } + + MetadataEventSourceExc& MetadataLoadedEvent() { return mMetadataLoadedEvent; } + + FirstFrameEventSourceExc& FirstFrameLoadedEvent() { + return mFirstFrameLoadedEvent; + } + + MediaEventSourceExc<RefPtr<VideoFrameContainer>>& + OnSecondaryVideoContainerInstalled() { + return mOnSecondaryVideoContainerInstalled; + } + + TimedMetadataEventSource& TimedMetadataEvent() { + return mMetadataManager.TimedMetadataEvent(); + } + + MediaEventSource<MediaPlaybackEvent>& OnPlaybackEvent() { + return mOnPlaybackEvent; + } + MediaEventSource<MediaResult>& OnPlaybackErrorEvent() { + return mOnPlaybackErrorEvent; + } + + MediaEventSource<DecoderDoctorEvent>& OnDecoderDoctorEvent() { + return mOnDecoderDoctorEvent; + } + + MediaEventSource<NextFrameStatus>& OnNextFrameStatus() { + return mOnNextFrameStatus; + } + + MediaEventProducer<VideoInfo, AudioInfo>& OnTrackInfoUpdatedEvent() { + return mReader->OnTrackInfoUpdatedEvent(); + } + + MediaEventSource<void>& OnMediaNotSeekable() const; + + AbstractCanonical<media::NullableTimeUnit>* CanonicalDuration() { + return &mDuration; + } + AbstractCanonical<media::TimeUnit>* CanonicalCurrentPosition() { + return &mCurrentPosition; + } + AbstractCanonical<bool>* CanonicalIsAudioDataAudible() { + return &mIsAudioDataAudible; + } + AbstractCanonical<media::TimeIntervals>* CanonicalBuffered() const; + + void DispatchSetFragmentEndTime(const media::TimeUnit& aEndTime); + void DispatchCanPlayThrough(bool aCanPlayThrough); + void DispatchIsLiveStream(bool aIsLiveStream); + void DispatchSetPlaybackRate(double aPlaybackRate); + + virtual RefPtr<SetCDMPromise> SetCDMProxy(CDMProxy* aProxy); + + virtual bool IsCDMProxySupported(CDMProxy* aProxy) = 0; + + protected: + virtual ~MediaDecoderStateMachineBase() = default; + + bool HasAudio() const { return mInfo.ref().HasAudio(); } + bool HasVideo() const { return mInfo.ref().HasVideo(); } + const MediaInfo& Info() const { return mInfo.ref(); } + + virtual void SetPlaybackRate(double aPlaybackRate) = 0; + virtual void SetIsLiveStream(bool aIsLiveStream) = 0; + virtual void SetCanPlayThrough(bool aCanPlayThrough) = 0; + virtual void SetFragmentEndTime(const media::TimeUnit& aFragmentEndTime) = 0; + + virtual void BufferedRangeUpdated() = 0; + virtual void VolumeChanged() = 0; + virtual void PreservesPitchChanged() = 0; + virtual void PlayStateChanged() = 0; + virtual void LoopingChanged() = 0; + virtual void UpdateSecondaryVideoContainer() = 0; + + // Init tasks which should be done on the task queue. + virtual void InitializationTask(MediaDecoder* aDecoder); + + virtual RefPtr<ShutdownPromise> Shutdown() = 0; + + virtual RefPtr<MediaDecoder::SeekPromise> Seek(const SeekTarget& aTarget) = 0; + + void DecodeError(const MediaResult& aError); + + // Functions used by assertions to ensure we're calling things + // on the appropriate threads. + bool OnTaskQueue() const; + + bool IsRequestingAudioData() const { return mAudioDataRequest.Exists(); } + bool IsRequestingVideoData() const { return mVideoDataRequest.Exists(); } + bool IsWaitingAudioData() const { return mAudioWaitRequest.Exists(); } + bool IsWaitingVideoData() const { return mVideoWaitRequest.Exists(); } + + void* const mDecoderID; + const RefPtr<AbstractThread> mAbstractMainThread; + const RefPtr<FrameStatistics> mFrameStats; + const RefPtr<VideoFrameContainer> mVideoFrameContainer; + const RefPtr<TaskQueue> mTaskQueue; + const RefPtr<ReaderProxy> mReader; + mozilla::MediaMetadataManager mMetadataManager; + + // Playback rate. 1.0 : normal speed, 0.5 : two times slower. + double mPlaybackRate; + + // Event producers + MediaEventProducerExc<UniquePtr<MediaInfo>, UniquePtr<MetadataTags>, + MediaDecoderEventVisibility> + mMetadataLoadedEvent; + MediaEventProducerExc<UniquePtr<MediaInfo>, MediaDecoderEventVisibility> + mFirstFrameLoadedEvent; + MediaEventProducerExc<RefPtr<VideoFrameContainer>> + mOnSecondaryVideoContainerInstalled; + MediaEventProducer<MediaPlaybackEvent> mOnPlaybackEvent; + MediaEventProducer<MediaResult> mOnPlaybackErrorEvent; + MediaEventProducer<DecoderDoctorEvent> mOnDecoderDoctorEvent; + MediaEventProducer<NextFrameStatus> mOnNextFrameStatus; + + // The buffered range. Mirrored from the decoder thread. + Mirror<media::TimeIntervals> mBuffered; + + // The current play state, mirrored from the main thread. + Mirror<MediaDecoder::PlayState> mPlayState; + + // Volume of playback. 0.0 = muted. 1.0 = full volume. + Mirror<double> mVolume; + + // Pitch preservation for the playback rate. + Mirror<bool> mPreservesPitch; + + // Whether to seek back to the start of the media resource + // upon reaching the end. + Mirror<bool> mLooping; + + // Set if the decoder is sending video to a secondary container. While set we + // should not suspend the decoder. + Mirror<RefPtr<VideoFrameContainer>> mSecondaryVideoContainer; + + // Duration of the media. This is guaranteed to be non-null after we finish + // decoding the first frame. + Canonical<media::NullableTimeUnit> mDuration; + + // The time of the current frame, corresponding to the "current + // playback position" in HTML5. This is referenced from 0, which is the + // initial playback position. + Canonical<media::TimeUnit> mCurrentPosition; + + // Used to distinguish whether the audio is producing sound. + Canonical<bool> mIsAudioDataAudible; + + // Stores presentation info required for playback. + Maybe<MediaInfo> mInfo; + + // True if the media is seekable (i.e. supports random access). + bool mMediaSeekable = true; + + // True if the media is seekable only in buffered ranges. + bool mMediaSeekableOnlyInBufferedRanges = false; + + // True if we've decoded first frames (thus having the start time) and + // notified the FirstFrameLoaded event. Note we can't initiate seek until the + // start time is known which happens when the first frames are decoded or we + // are playing an MSE stream (the start time is always assumed 0). + bool mSentFirstFrameLoadedEvent = false; + + // True if we should not decode/preroll unnecessary samples, unless we're + // played. "Prerolling" in this context refers to when we decode and + // buffer decoded samples in advance of when they're needed for playback. + // This flag is set for preload=metadata media, and means we won't + // decode more than the first video frame and first block of audio samples + // for that media when we startup, or after a seek. When Play() is called, + // we reset this flag, as we assume the user is playing the media, so + // prerolling is appropriate then. This flag is used to reduce the overhead + // of prerolling samples for media elements that may not play, both + // memory and CPU overhead. + bool mMinimizePreroll; + + // Only one of a given pair of ({Audio,Video}DataPromise, WaitForDataPromise) + // should exist at any given moment. + using AudioDataPromise = MediaFormatReader::AudioDataPromise; + using VideoDataPromise = MediaFormatReader::VideoDataPromise; + using WaitForDataPromise = MediaFormatReader::WaitForDataPromise; + MozPromiseRequestHolder<AudioDataPromise> mAudioDataRequest; + MozPromiseRequestHolder<VideoDataPromise> mVideoDataRequest; + MozPromiseRequestHolder<WaitForDataPromise> mAudioWaitRequest; + MozPromiseRequestHolder<WaitForDataPromise> mVideoWaitRequest; + + private: + WatchManager<MediaDecoderStateMachineBase> mWatchManager; +}; + +} // namespace mozilla + +#endif // DOM_MEDIA_MEDIADECODERSTATEMACHINEBASE_H_ |