summaryrefslogtreecommitdiffstats
path: root/dom/media/MediaFormatReader.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/media/MediaFormatReader.h876
1 files changed, 876 insertions, 0 deletions
diff --git a/dom/media/MediaFormatReader.h b/dom/media/MediaFormatReader.h
new file mode 100644
index 0000000000..cb398cd147
--- /dev/null
+++ b/dom/media/MediaFormatReader.h
@@ -0,0 +1,876 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#if !defined(MediaFormatReader_h_)
+# define MediaFormatReader_h_
+
+# include "FrameStatistics.h"
+# include "MediaDataDemuxer.h"
+# include "MediaEventSource.h"
+# include "MediaMetadataManager.h"
+# include "MediaPromiseDefs.h"
+# include "PlatformDecoderModule.h"
+# include "SeekTarget.h"
+# include "mozilla/Atomics.h"
+# include "mozilla/Maybe.h"
+# include "mozilla/MozPromise.h"
+# include "mozilla/Mutex.h"
+# include "mozilla/StateMirroring.h"
+# include "mozilla/StaticPrefs_media.h"
+# include "mozilla/TaskQueue.h"
+# include "mozilla/ThreadSafeWeakPtr.h"
+# include "mozilla/dom/MediaDebugInfoBinding.h"
+
+namespace mozilla {
+
+class CDMProxy;
+class GMPCrashHelper;
+class MediaResource;
+class VideoFrameContainer;
+
+struct WaitForDataRejectValue {
+ enum Reason { SHUTDOWN, CANCELED };
+
+ WaitForDataRejectValue(MediaData::Type aType, Reason aReason)
+ : mType(aType), mReason(aReason) {}
+ MediaData::Type mType;
+ Reason mReason;
+};
+
+struct SeekRejectValue {
+ MOZ_IMPLICIT SeekRejectValue(const MediaResult& aError)
+ : mType(MediaData::Type::NULL_DATA), mError(aError) {}
+ MOZ_IMPLICIT SeekRejectValue(nsresult aResult)
+ : mType(MediaData::Type::NULL_DATA), mError(aResult) {}
+ SeekRejectValue(MediaData::Type aType, const MediaResult& aError)
+ : mType(aType), mError(aError) {}
+ MediaData::Type mType;
+ MediaResult mError;
+};
+
+struct MetadataHolder {
+ UniquePtr<MediaInfo> mInfo;
+ UniquePtr<MetadataTags> mTags;
+};
+
+typedef void* MediaDecoderOwnerID;
+
+struct MOZ_STACK_CLASS MediaFormatReaderInit {
+ MediaResource* mResource = nullptr;
+ VideoFrameContainer* mVideoFrameContainer = nullptr;
+ FrameStatistics* mFrameStats = nullptr;
+ already_AddRefed<layers::KnowsCompositor> mKnowsCompositor;
+ already_AddRefed<GMPCrashHelper> mCrashHelper;
+ // Used in bug 1393399 for temporary telemetry.
+ MediaDecoderOwnerID mMediaDecoderOwnerID = nullptr;
+ Maybe<TrackingId> mTrackingId;
+};
+
+DDLoggedTypeDeclName(MediaFormatReader);
+
+class MediaFormatReader final
+ : public SupportsThreadSafeWeakPtr<MediaFormatReader>,
+ public DecoderDoctorLifeLogger<MediaFormatReader> {
+ static const bool IsExclusive = true;
+ typedef TrackInfo::TrackType TrackType;
+ typedef MozPromise<bool, MediaResult, IsExclusive> NotifyDataArrivedPromise;
+
+ public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(MediaFormatReader)
+
+ using TrackSet = EnumSet<TrackInfo::TrackType>;
+ using MetadataPromise = MozPromise<MetadataHolder, MediaResult, IsExclusive>;
+
+ template <typename Type>
+ using DataPromise = MozPromise<RefPtr<Type>, MediaResult, IsExclusive>;
+ using AudioDataPromise = DataPromise<AudioData>;
+ using VideoDataPromise = DataPromise<VideoData>;
+
+ using SeekPromise = MozPromise<media::TimeUnit, SeekRejectValue, IsExclusive>;
+
+ // Note that, conceptually, WaitForData makes sense in a non-exclusive sense.
+ // But in the current architecture it's only ever used exclusively (by MDSM),
+ // so we mark it that way to verify our assumptions. If you have a use-case
+ // for multiple WaitForData consumers, feel free to flip the exclusivity here.
+ using WaitForDataPromise =
+ MozPromise<MediaData::Type, WaitForDataRejectValue, IsExclusive>;
+
+ MediaFormatReader(MediaFormatReaderInit& aInit, MediaDataDemuxer* aDemuxer);
+ virtual ~MediaFormatReader();
+
+ // Initializes the reader, returns NS_OK on success, or NS_ERROR_FAILURE
+ // on failure.
+ nsresult Init();
+
+ size_t SizeOfVideoQueueInFrames();
+ size_t SizeOfAudioQueueInFrames();
+
+ // Requests one video sample from the reader.
+ RefPtr<VideoDataPromise> RequestVideoData(
+ const media::TimeUnit& aTimeThreshold,
+ bool aRequestNextVideoKeyFrame = false);
+
+ // Requests one audio sample from the reader.
+ //
+ // The decode should be performed asynchronously, and the promise should
+ // be resolved when it is complete.
+ RefPtr<AudioDataPromise> RequestAudioData();
+
+ // The default implementation of AsyncReadMetadata is implemented in terms of
+ // synchronous ReadMetadata() calls. Implementations may also
+ // override AsyncReadMetadata to create a more proper async implementation.
+ RefPtr<MetadataPromise> AsyncReadMetadata();
+
+ // Fills aInfo with the latest cached data required to present the media,
+ // ReadUpdatedMetadata will always be called once ReadMetadata has succeeded.
+ void ReadUpdatedMetadata(MediaInfo* aInfo);
+
+ RefPtr<SeekPromise> Seek(const SeekTarget& aTarget);
+
+ // Called once new data has been cached by the MediaResource.
+ // mBuffered should be recalculated and updated accordingly.
+ void NotifyDataArrived();
+
+ // Update ID for the external playback engine. Currently it's only used on
+ // Windows when the media engine playback is enabled.
+ void UpdateMediaEngineId(uint64_t aMediaEngineId);
+
+ protected:
+ // Recomputes mBuffered.
+ void UpdateBuffered();
+
+ public:
+ // Called by MDSM in dormant state to release resources allocated by this
+ // reader. The reader can resume decoding by calling Seek() to a specific
+ // position.
+ void ReleaseResources();
+
+ bool OnTaskQueue() const { return OwnerThread()->IsCurrentThreadIn(); }
+
+ // Resets all state related to decoding, emptying all buffers etc.
+ // Cancels all pending Request*Data() request callbacks, rejects any
+ // outstanding seek promises, and flushes the decode pipeline. The
+ // decoder must not call any of the callbacks for outstanding
+ // Request*Data() calls after this is called. Calls to Request*Data()
+ // made after this should be processed as usual.
+ //
+ // Normally this call preceedes a Seek() call, or shutdown.
+ //
+ // aParam is a set of TrackInfo::TrackType enums specifying which
+ // queues need to be reset, defaulting to both audio and video tracks.
+ nsresult ResetDecode(TrackSet aTracks);
+
+ // Destroys the decoding state. The reader cannot be made usable again.
+ // This is different from ReleaseMediaResources() as it is irreversable,
+ // whereas ReleaseMediaResources() is. Must be called on the decode
+ // thread.
+ RefPtr<ShutdownPromise> Shutdown();
+
+ // Returns true if this decoder reader uses hardware accelerated video
+ // decoding.
+ bool VideoIsHardwareAccelerated() const;
+
+ // By default, the state machine polls the reader once per second when it's
+ // in buffering mode. Some readers support a promise-based mechanism by which
+ // they notify the state machine when the data arrives.
+ bool IsWaitForDataSupported() const { return true; }
+
+ RefPtr<WaitForDataPromise> WaitForData(MediaData::Type aType);
+
+ // The MediaDecoderStateMachine uses various heuristics that assume that
+ // raw media data is arriving sequentially from a network channel. This
+ // makes sense in the <video src="foo"> case, but not for more advanced use
+ // cases like MSE.
+ bool UseBufferingHeuristics() const { return mTrackDemuxersMayBlock; }
+
+ RefPtr<SetCDMPromise> SetCDMProxy(CDMProxy* aProxy);
+
+ // Requests that the MediaFormatReader populates aInfo with debug information.
+ // This may be done asynchronously, and aInfo should *not* be accessed by the
+ // caller until the returned promise is resolved or rejected.
+ RefPtr<GenericPromise> RequestDebugInfo(
+ dom::MediaFormatReaderDebugInfo& aInfo);
+
+ // Switch the video decoder to NullDecoderModule. It might takes effective
+ // since a few samples later depends on how much demuxed samples are already
+ // queued in the original video decoder.
+ void SetVideoNullDecode(bool aIsNullDecode);
+
+ void UpdateCompositor(already_AddRefed<layers::KnowsCompositor>);
+
+ void UpdateDuration(const media::TimeUnit& aDuration) {
+ MOZ_ASSERT(OnTaskQueue());
+ UpdateBuffered();
+ }
+
+ AbstractCanonical<media::TimeIntervals>* CanonicalBuffered() {
+ return &mBuffered;
+ }
+
+ TaskQueue* OwnerThread() const { return mTaskQueue; }
+
+ TimedMetadataEventSource& TimedMetadataEvent() { return mTimedMetadataEvent; }
+
+ // Notified by the OggDemuxer during playback when chained ogg is detected.
+ MediaEventSource<void>& OnMediaNotSeekable() { return mOnMediaNotSeekable; }
+
+ TimedMetadataEventProducer& TimedMetadataProducer() {
+ return mTimedMetadataEvent;
+ }
+
+ MediaEventProducer<void>& MediaNotSeekableProducer() {
+ return mOnMediaNotSeekable;
+ }
+
+ // Notified if the reader can't decode a sample due to a missing decryption
+ // key.
+ MediaEventSource<TrackInfo::TrackType>& OnTrackWaitingForKey() {
+ return mOnTrackWaitingForKey;
+ }
+
+ MediaEventProducer<TrackInfo::TrackType>& OnTrackWaitingForKeyProducer() {
+ return mOnTrackWaitingForKey;
+ }
+
+ MediaEventSource<nsTArray<uint8_t>, nsString>& OnEncrypted() {
+ return mOnEncrypted;
+ }
+
+ MediaEventSource<void>& OnWaitingForKey() { return mOnWaitingForKey; }
+
+ MediaEventSource<MediaResult>& OnDecodeWarning() { return mOnDecodeWarning; }
+
+ MediaEventSource<VideoInfo>& OnStoreDecoderBenchmark() {
+ return mOnStoreDecoderBenchmark;
+ }
+
+ MediaEventProducer<VideoInfo, AudioInfo>& OnTrackInfoUpdatedEvent() {
+ return mTrackInfoUpdatedEvent;
+ }
+
+ private:
+ bool HasVideo() const { return mVideo.mTrackDemuxer; }
+ bool HasAudio() const { return mAudio.mTrackDemuxer; }
+
+ bool IsWaitingOnCDMResource();
+
+ bool InitDemuxer();
+ // Notify the track demuxers that new data has been received.
+ void NotifyTrackDemuxers();
+ void ReturnOutput(MediaData* aData, TrackType aTrack);
+
+ // Enqueues a task to call Update(aTrack) on the decoder task queue.
+ // Lock for corresponding track must be held.
+ void ScheduleUpdate(TrackType aTrack);
+ void Update(TrackType aTrack);
+ // Handle actions should more data be received.
+ // Returns true if no more action is required.
+ bool UpdateReceivedNewData(TrackType aTrack);
+ // Called when new samples need to be demuxed.
+ void RequestDemuxSamples(TrackType aTrack);
+ // Handle demuxed samples by the input behavior.
+ void HandleDemuxedSamples(TrackType aTrack,
+ FrameStatistics::AutoNotifyDecoded& aA);
+ // Decode any pending already demuxed samples.
+ void DecodeDemuxedSamples(TrackType aTrack, MediaRawData* aSample);
+
+ struct InternalSeekTarget {
+ InternalSeekTarget(const media::TimeInterval& aTime, bool aDropTarget)
+ : mTime(aTime),
+ mDropTarget(aDropTarget),
+ mWaiting(false),
+ mHasSeeked(false) {}
+
+ media::TimeUnit Time() const { return mTime.mStart; }
+ media::TimeUnit EndTime() const { return mTime.mEnd; }
+ bool Contains(const media::TimeUnit& aTime) const {
+ return mTime.Contains(aTime);
+ }
+
+ media::TimeInterval mTime;
+ bool mDropTarget;
+ bool mWaiting;
+ bool mHasSeeked;
+ };
+
+ // Perform an internal seek to aTime. If aDropTarget is true then
+ // the first sample past the target will be dropped.
+ void InternalSeek(TrackType aTrack, const InternalSeekTarget& aTarget);
+ // Return the end time of the internal seek target if it exists. Otherwise,
+ // return infinity.
+ media::TimeUnit GetInternalSeekTargetEndTime() const;
+
+ // Drain the current decoder.
+ void DrainDecoder(TrackType aTrack);
+ void NotifyNewOutput(TrackType aTrack,
+ MediaDataDecoder::DecodedData&& aResults);
+ void NotifyError(TrackType aTrack, const MediaResult& aError);
+ void NotifyWaitingForData(TrackType aTrack);
+ void NotifyWaitingForKey(TrackType aTrack);
+ void NotifyEndOfStream(TrackType aTrack);
+
+ void ExtractCryptoInitData(nsTArray<uint8_t>& aInitData);
+
+ // Initializes mLayersBackendType if possible.
+ void InitLayersBackendType();
+
+ void Reset(TrackType aTrack);
+ void DropDecodedSamples(TrackType aTrack);
+
+ // Return a target timeunit which the reader should skip to, this would be
+ // either the timethreshold we pass, or the time of the next keyframe. Return
+ // nothing if we don't need to skip.
+ // @param aTimeThreshold
+ // The time that we expect the time of next video frame should be or go beyond
+ // @param aRequestNextVideoKeyFrame
+ // If true and the next keyframe's time is larger than aTimeThreshold, skip to
+ // the next keyframe time instead of aTimeThreshold.
+ Maybe<media::TimeUnit> ShouldSkip(media::TimeUnit aTimeThreshold,
+ bool aRequestNextVideoKeyFrame);
+
+ void SetVideoDecodeThreshold();
+
+ size_t SizeOfQueue(TrackType aTrack);
+
+ // Fire a new OnStoreDecoderBenchmark event that will create new
+ // storage of the decoder benchmark.
+ // This is called only on TaskQueue.
+ void NotifyDecoderBenchmarkStore();
+
+ void NotifyTrackInfoUpdated();
+
+ enum class DrainState {
+ None,
+ DrainRequested,
+ Draining,
+ PartialDrainPending,
+ DrainCompleted,
+ DrainAborted,
+ };
+
+ class SharedShutdownPromiseHolder : public MozPromiseHolder<ShutdownPromise> {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedShutdownPromiseHolder)
+ private:
+ ~SharedShutdownPromiseHolder() = default;
+ };
+
+ struct DecoderData {
+ DecoderData(MediaFormatReader* aOwner, MediaData::Type aType,
+ uint32_t aNumOfMaxError)
+ : mOwner(aOwner),
+ mType(aType),
+ mMutex("DecoderData"),
+ mDescription("shutdown"),
+ mUpdateScheduled(false),
+ mDemuxEOS(false),
+ mWaitingForData(false),
+ mWaitingForKey(false),
+ mReceivedNewData(false),
+ mFlushing(false),
+ mFlushed(true),
+ mDrainState(DrainState::None),
+ mNumOfConsecutiveDecodingError(0),
+ mMaxConsecutiveDecodingError(aNumOfMaxError),
+ mNumOfConsecutiveRDDOrGPUCrashes(0),
+ mMaxConsecutiveRDDOrGPUCrashes(
+ StaticPrefs::media_rdd_process_max_crashes()),
+ mNumOfConsecutiveUtilityCrashes(0),
+ mMaxConsecutiveUtilityCrashes(
+ StaticPrefs::media_utility_process_max_crashes()),
+ mFirstFrameTime(Some(media::TimeUnit::Zero())),
+ mNumSamplesInput(0),
+ mNumSamplesOutput(0),
+ mNumSamplesOutputTotal(0),
+ mNumSamplesSkippedTotal(0),
+ mSizeOfQueue(0),
+ mIsHardwareAccelerated(false),
+ mLastStreamSourceID(UINT32_MAX),
+ mIsNullDecode(false),
+ mHardwareDecodingDisabled(false) {
+ DecoderDoctorLogger::LogConstruction("MediaFormatReader::DecoderData",
+ this);
+ }
+
+ ~DecoderData() {
+ DecoderDoctorLogger::LogDestruction("MediaFormatReader::DecoderData",
+ this);
+ }
+
+ MediaFormatReader* mOwner;
+ // Disambiguate Audio vs Video.
+ MediaData::Type mType;
+ RefPtr<MediaTrackDemuxer> mTrackDemuxer;
+ // TaskQueue on which decoder can choose to decode.
+ // Only non-null up until the decoder is created.
+ RefPtr<TaskQueue> mTaskQueue;
+
+ // Mutex protecting mDescription, mDecoder, mTrackDemuxer and mWorkingInfo
+ // as those can be read outside the TaskQueue.
+ // They are only written on the TaskQueue however, as such mMutex doesn't
+ // need to be held when those members are read on the TaskQueue.
+ Mutex mMutex MOZ_UNANNOTATED;
+ // The platform decoder.
+ RefPtr<MediaDataDecoder> mDecoder;
+ nsCString mDescription;
+ void ShutdownDecoder();
+
+ // Only accessed from reader's task queue.
+ bool mUpdateScheduled;
+ bool mDemuxEOS;
+ bool mWaitingForData;
+ bool mWaitingForKey;
+ bool mReceivedNewData;
+
+ // Pending seek.
+ MozPromiseRequestHolder<MediaTrackDemuxer::SeekPromise> mSeekRequest;
+
+ // Queued demux samples waiting to be decoded.
+ nsTArray<RefPtr<MediaRawData>> mQueuedSamples;
+ MozPromiseRequestHolder<MediaTrackDemuxer::SamplesPromise> mDemuxRequest;
+ // A WaitingPromise is pending if the demuxer is waiting for data or
+ // if the decoder is waiting for a key.
+ MozPromiseHolder<WaitForDataPromise> mWaitingPromise;
+ bool HasWaitingPromise() const {
+ MOZ_ASSERT(mOwner->OnTaskQueue());
+ return !mWaitingPromise.IsEmpty();
+ }
+
+ bool IsWaitingForData() const {
+ MOZ_ASSERT(mOwner->OnTaskQueue());
+ return mWaitingForData;
+ }
+
+ bool IsWaitingForKey() const {
+ MOZ_ASSERT(mOwner->OnTaskQueue());
+ return mWaitingForKey && mDecodeRequest.Exists();
+ }
+
+ // MediaDataDecoder handler's variables.
+ MozPromiseRequestHolder<MediaDataDecoder::DecodePromise> mDecodeRequest;
+ bool mFlushing; // True if flush is in action.
+ // Set to true if the last operation run on the decoder was a flush.
+ bool mFlushed;
+ RefPtr<SharedShutdownPromiseHolder> mShutdownPromise;
+
+ MozPromiseRequestHolder<MediaDataDecoder::DecodePromise> mDrainRequest;
+ DrainState mDrainState;
+ bool HasPendingDrain() const { return mDrainState != DrainState::None; }
+ bool HasCompletedDrain() const {
+ return mDrainState == DrainState::DrainCompleted ||
+ mDrainState == DrainState::DrainAborted;
+ }
+ void RequestDrain() {
+ MOZ_RELEASE_ASSERT(mDrainState == DrainState::None);
+ mDrainState = DrainState::DrainRequested;
+ }
+
+ // Track decoding error and fail when we hit the limit.
+ uint32_t mNumOfConsecutiveDecodingError;
+ uint32_t mMaxConsecutiveDecodingError;
+
+ // Track RDD or GPU process crashes and fail when we hit the limit.
+ uint32_t mNumOfConsecutiveRDDOrGPUCrashes;
+ uint32_t mMaxConsecutiveRDDOrGPUCrashes;
+
+ // Track Utility process crashes and fail when we hit the limit.
+ uint32_t mNumOfConsecutiveUtilityCrashes;
+ uint32_t mMaxConsecutiveUtilityCrashes;
+
+ // Set when we haven't yet decoded the first frame.
+ // Cleared once the first frame has been decoded.
+ // This is used to determine, upon error, if we should try again to decode
+ // the frame, or skip to the next keyframe.
+ Maybe<media::TimeUnit> mFirstFrameTime;
+
+ Maybe<MediaResult> mError;
+ bool HasFatalError() const {
+ if (!mError.isSome()) {
+ return false;
+ }
+ if (mError.ref() == NS_ERROR_DOM_MEDIA_DECODE_ERR) {
+ // Allow decode errors to be non-fatal, but give up
+ // if we have too many, or if warnings should be treated as errors.
+ return mNumOfConsecutiveDecodingError > mMaxConsecutiveDecodingError ||
+ StaticPrefs::media_playback_warnings_as_errors();
+ } else if (mError.ref() == NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER) {
+ // If the caller asked for a new decoder we shouldn't treat
+ // it as fatal.
+ return false;
+ } else if (mError.ref() ==
+ NS_ERROR_DOM_MEDIA_REMOTE_DECODER_CRASHED_RDD_OR_GPU_ERR) {
+ // Allow RDD crashes to be non-fatal, but give up
+ // if we have too many, or if warnings should be treated as errors.
+ return mNumOfConsecutiveRDDOrGPUCrashes >
+ mMaxConsecutiveRDDOrGPUCrashes ||
+ StaticPrefs::media_playback_warnings_as_errors();
+ } else if (mError.ref() ==
+ NS_ERROR_DOM_MEDIA_REMOTE_DECODER_CRASHED_UTILITY_ERR) {
+ bool tooManyConsecutiveCrashes =
+ mNumOfConsecutiveUtilityCrashes > mMaxConsecutiveUtilityCrashes;
+ // TODO: Telemetry?
+ return tooManyConsecutiveCrashes ||
+ StaticPrefs::media_playback_warnings_as_errors();
+ } else {
+ // All other error types are fatal
+ return true;
+ }
+ }
+
+ // If set, all decoded samples prior mTimeThreshold will be dropped.
+ // Used for internal seeking when a change of stream is detected or when
+ // encountering data discontinuity.
+ Maybe<InternalSeekTarget> mTimeThreshold;
+ // Time of last decoded sample returned.
+ Maybe<media::TimeInterval> mLastDecodedSampleTime;
+
+ // Decoded samples returned my mDecoder awaiting being returned to
+ // state machine upon request.
+ nsTArray<RefPtr<MediaData>> mOutput;
+ uint64_t mNumSamplesInput;
+ uint64_t mNumSamplesOutput;
+ uint64_t mNumSamplesOutputTotal;
+ uint64_t mNumSamplesSkippedTotal;
+
+ // These get overridden in the templated concrete class.
+ // Indicate if we have a pending promise for decoded frame.
+ // Rejecting the promise will stop the reader from decoding ahead.
+ virtual bool HasPromise() const = 0;
+ virtual void RejectPromise(const MediaResult& aError,
+ const char* aMethodName) = 0;
+
+ // Clear track demuxer related data.
+ void ResetDemuxer() {
+ mDemuxRequest.DisconnectIfExists();
+ mSeekRequest.DisconnectIfExists();
+ mTrackDemuxer->Reset();
+ mQueuedSamples.Clear();
+ }
+
+ // Flush the decoder if present and reset decoding related data.
+ // Following a flush, the decoder is ready to accept any new data.
+ void Flush();
+
+ bool CancelWaitingForKey() {
+ if (!mWaitingForKey) {
+ return false;
+ }
+ mWaitingForKey = false;
+ if (IsWaitingForData() || !HasWaitingPromise()) {
+ return false;
+ }
+ mWaitingPromise.Resolve(mType, __func__);
+ return true;
+ }
+
+ // Reset the state of the DecoderData, clearing all queued frames
+ // (pending demuxed and decoded).
+ // The track demuxer is *not* reset.
+ void ResetState() {
+ MOZ_ASSERT(mOwner->OnTaskQueue());
+ mDemuxEOS = false;
+ mWaitingForData = false;
+ mQueuedSamples.Clear();
+ mDecodeRequest.DisconnectIfExists();
+ mDrainRequest.DisconnectIfExists();
+ mDrainState = DrainState::None;
+ CancelWaitingForKey();
+ mTimeThreshold.reset();
+ mLastDecodedSampleTime.reset();
+ mOutput.Clear();
+ mNumSamplesInput = 0;
+ mNumSamplesOutput = 0;
+ mSizeOfQueue = 0;
+ mNextStreamSourceID.reset();
+ if (!HasFatalError()) {
+ mError.reset();
+ }
+ }
+
+ bool HasInternalSeekPending() const {
+ return mTimeThreshold && !mTimeThreshold.ref().mHasSeeked;
+ }
+
+ // Return the current TrackInfo in the stream. If the stream content never
+ // changed since AsyncReadMetadata was called then the TrackInfo used is
+ // mOriginalInfo, other it will be mInfo. The later case is only ever true
+ // with MSE or the WebMDemuxer.
+ const TrackInfo* GetCurrentInfo() const {
+ if (mInfo) {
+ return *mInfo;
+ }
+ return mOriginalInfo.get();
+ }
+ // Return the current TrackInfo updated as per the decoder output.
+ // Typically for audio, the number of channels and/or sampling rate can vary
+ // between what was found in the metadata and what the decoder returned.
+ const TrackInfo* GetWorkingInfo() const { return mWorkingInfo.get(); }
+ bool IsEncrypted() const { return GetCurrentInfo()->mCrypto.IsEncrypted(); }
+
+ // Used by the MDSM for logging purposes.
+ Atomic<size_t> mSizeOfQueue;
+ // Used by the MDSM to determine if video decoding is hardware accelerated.
+ // This value is updated after a frame is successfully decoded.
+ Atomic<bool> mIsHardwareAccelerated;
+ // Sample format monitoring.
+ uint32_t mLastStreamSourceID;
+ Maybe<uint32_t> mNextStreamSourceID;
+ media::TimeIntervals mTimeRanges;
+ Maybe<media::TimeUnit> mLastTimeRangesEnd;
+ // TrackInfo as first discovered during ReadMetadata.
+ UniquePtr<TrackInfo> mOriginalInfo;
+ // Written exclusively on the TaskQueue, can be read on MDSM's TaskQueue.
+ // Must be read with parent's mutex held.
+ UniquePtr<TrackInfo> mWorkingInfo;
+ RefPtr<TrackInfoSharedPtr> mInfo;
+ Maybe<media::TimeUnit> mFirstDemuxedSampleTime;
+ // Use NullDecoderModule or not.
+ bool mIsNullDecode;
+ bool mHardwareDecodingDisabled;
+ // Whether we have reported hardware decoding support for video. Used only
+ // on reader's task queue,
+ bool mHasReportedVideoHardwareSupportTelemtry = false;
+
+ class {
+ public:
+ float Mean() const { return mMean; }
+
+ void Update(const media::TimeUnit& aValue) {
+ if (aValue == media::TimeUnit::Zero()) {
+ return;
+ }
+ mMean += (1.0f / aValue.ToSeconds() - mMean) / ++mCount;
+ }
+
+ void Reset() {
+ mMean = 0;
+ mCount = 0;
+ }
+
+ private:
+ float mMean = 0;
+ uint64_t mCount = 0;
+ } mMeanRate;
+ };
+
+ template <typename Type>
+ class DecoderDataWithPromise : public DecoderData {
+ public:
+ DecoderDataWithPromise(MediaFormatReader* aOwner, MediaData::Type aType,
+ uint32_t aNumOfMaxError)
+ : DecoderData(aOwner, aType, aNumOfMaxError), mHasPromise(false) {
+ DecoderDoctorLogger::LogConstructionAndBase(
+ "MediaFormatReader::DecoderDataWithPromise", this,
+ "MediaFormatReader::DecoderData",
+ static_cast<const MediaFormatReader::DecoderData*>(this));
+ }
+
+ ~DecoderDataWithPromise() {
+ DecoderDoctorLogger::LogDestruction(
+ "MediaFormatReader::DecoderDataWithPromise", this);
+ }
+
+ bool HasPromise() const override { return mHasPromise; }
+
+ RefPtr<DataPromise<Type>> EnsurePromise(const char* aMethodName) {
+ MOZ_ASSERT(mOwner->OnTaskQueue());
+ mHasPromise = true;
+ return mPromise.Ensure(aMethodName);
+ }
+
+ void ResolvePromise(Type* aData, const char* aMethodName) {
+ MOZ_ASSERT(mOwner->OnTaskQueue());
+ mPromise.Resolve(aData, aMethodName);
+ mHasPromise = false;
+ }
+
+ void RejectPromise(const MediaResult& aError,
+ const char* aMethodName) override {
+ MOZ_ASSERT(mOwner->OnTaskQueue());
+ mPromise.Reject(aError, aMethodName);
+ mHasPromise = false;
+ }
+
+ private:
+ MozPromiseHolder<DataPromise<Type>> mPromise;
+ Atomic<bool> mHasPromise;
+ };
+
+ // Decode task queue.
+ RefPtr<TaskQueue> mTaskQueue;
+
+ DecoderDataWithPromise<AudioData> mAudio;
+ DecoderDataWithPromise<VideoData> mVideo;
+
+ Watchable<bool> mWorkingInfoChanged;
+ WatchManager<MediaFormatReader> mWatchManager;
+ bool mIsWatchingWorkingInfo;
+
+ // Returns true when the decoder for this track needs input.
+ bool NeedInput(DecoderData& aDecoder);
+
+ DecoderData& GetDecoderData(TrackType aTrack);
+
+ // Demuxer objects.
+ class DemuxerProxy;
+ UniquePtr<DemuxerProxy> mDemuxer;
+ bool mDemuxerInitDone;
+ void OnDemuxerInitDone(const MediaResult& aResult);
+ void OnDemuxerInitFailed(const MediaResult& aError);
+ MozPromiseRequestHolder<MediaDataDemuxer::InitPromise> mDemuxerInitRequest;
+ MozPromiseRequestHolder<NotifyDataArrivedPromise> mNotifyDataArrivedPromise;
+ bool mPendingNotifyDataArrived;
+ void OnDemuxFailed(TrackType aTrack, const MediaResult& aError);
+
+ void DoDemuxVideo();
+ void OnVideoDemuxCompleted(RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples);
+ void OnVideoDemuxFailed(const MediaResult& aError) {
+ OnDemuxFailed(TrackType::kVideoTrack, aError);
+ }
+
+ void DoDemuxAudio();
+ void OnAudioDemuxCompleted(RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples);
+ void OnAudioDemuxFailed(const MediaResult& aError) {
+ OnDemuxFailed(TrackType::kAudioTrack, aError);
+ }
+
+ void SkipVideoDemuxToNextKeyFrame(media::TimeUnit aTimeThreshold);
+ MozPromiseRequestHolder<MediaTrackDemuxer::SkipAccessPointPromise>
+ mSkipRequest;
+ void VideoSkipReset(uint32_t aSkipped);
+ void OnVideoSkipCompleted(uint32_t aSkipped);
+ void OnVideoSkipFailed(MediaTrackDemuxer::SkipFailureHolder aFailure);
+
+ // The last number of decoded output frames that we've reported to
+ // MediaDecoder::NotifyDecoded(). We diff the number of output video
+ // frames every time that DecodeVideoData() is called, and report the
+ // delta there.
+ uint64_t mLastReportedNumDecodedFrames;
+
+ // Timestamp of the previous decoded keyframe, in microseconds.
+ int64_t mPreviousDecodedKeyframeTime_us;
+ // Default mLastDecodedKeyframeTime_us value, must be bigger than anything.
+ static const int64_t sNoPreviousDecodedKeyframe = INT64_MAX;
+
+ RefPtr<layers::KnowsCompositor> mKnowsCompositor;
+
+ // Metadata objects
+ // True if we've read the streams' metadata.
+ bool mInitDone;
+ MozPromiseHolder<MetadataPromise> mMetadataPromise;
+ bool IsEncrypted() const;
+
+ // Set to true if any of our track buffers may be blocking.
+ bool mTrackDemuxersMayBlock;
+
+ // Seeking objects.
+ void SetSeekTarget(const SeekTarget& aTarget);
+ bool IsSeeking() const { return mPendingSeekTime.isSome(); }
+ bool IsVideoOnlySeeking() const {
+ return IsSeeking() && mOriginalSeekTarget.IsVideoOnly();
+ }
+ bool IsAudioOnlySeeking() const {
+ return IsSeeking() && mOriginalSeekTarget.IsAudioOnly();
+ }
+ void ScheduleSeek();
+ void AttemptSeek();
+ void OnSeekFailed(TrackType aTrack, const MediaResult& aError);
+ void DoVideoSeek();
+ void OnVideoSeekCompleted(media::TimeUnit aTime);
+ void OnVideoSeekFailed(const MediaResult& aError);
+ bool mSeekScheduled;
+
+ void DoAudioSeek();
+ void OnAudioSeekCompleted(media::TimeUnit aTime);
+ void OnAudioSeekFailed(const MediaResult& aError);
+ // The SeekTarget that was last given to Seek()
+ SeekTarget mOriginalSeekTarget;
+ // Temporary seek information while we wait for the data
+ Maybe<media::TimeUnit> mFallbackSeekTime;
+ Maybe<media::TimeUnit> mPendingSeekTime;
+ MozPromiseHolder<SeekPromise> mSeekPromise;
+
+ RefPtr<VideoFrameContainer> mVideoFrameContainer;
+ layers::ImageContainer* GetImageContainer();
+
+ RefPtr<CDMProxy> mCDMProxy;
+
+ RefPtr<GMPCrashHelper> mCrashHelper;
+
+ void SetNullDecode(TrackType aTrack, bool aIsNullDecode);
+
+ class DecoderFactory;
+ UniquePtr<DecoderFactory> mDecoderFactory;
+
+ class ShutdownPromisePool;
+ UniquePtr<ShutdownPromisePool> mShutdownPromisePool;
+
+ MediaEventListener mOnTrackWaitingForKeyListener;
+
+ void OnFirstDemuxCompleted(TrackInfo::TrackType aType,
+ RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples);
+
+ void OnFirstDemuxFailed(TrackInfo::TrackType aType,
+ const MediaResult& aError);
+
+ void MaybeResolveMetadataPromise();
+
+ // Stores presentation info required for playback.
+ MediaInfo mInfo;
+
+ UniquePtr<MetadataTags> mTags;
+
+ // A flag indicating if the start time is known or not.
+ bool mHasStartTime = false;
+
+ void ShutdownDecoder(TrackType aTrack);
+ RefPtr<ShutdownPromise> TearDownDecoders();
+
+ bool mShutdown = false;
+
+ // Buffered range.
+ Canonical<media::TimeIntervals> mBuffered;
+
+ // Used to send TimedMetadata to the listener.
+ TimedMetadataEventProducer mTimedMetadataEvent;
+
+ // Notify if this media is not seekable.
+ MediaEventProducer<void> mOnMediaNotSeekable;
+
+ // Notify if we are waiting for a decryption key.
+ MediaEventProducer<TrackInfo::TrackType> mOnTrackWaitingForKey;
+
+ MediaEventProducer<nsTArray<uint8_t>, nsString> mOnEncrypted;
+
+ MediaEventProducer<void> mOnWaitingForKey;
+
+ MediaEventProducer<MediaResult> mOnDecodeWarning;
+
+ MediaEventProducer<VideoInfo> mOnStoreDecoderBenchmark;
+
+ MediaEventProducer<VideoInfo, AudioInfo> mTrackInfoUpdatedEvent;
+
+ RefPtr<FrameStatistics> mFrameStats;
+
+ // Used in bug 1393399 for telemetry.
+ const MediaDecoderOwnerID mMediaDecoderOwnerID;
+
+ bool ResolveSetCDMPromiseIfDone(TrackType aTrack);
+ void PrepareToSetCDMForTrack(TrackType aTrack);
+ MozPromiseHolder<SetCDMPromise> mSetCDMPromise;
+ TrackSet mSetCDMForTracks{};
+ bool IsDecoderWaitingForCDM(TrackType aTrack);
+
+ void GetDebugInfo(dom::MediaFormatReaderDebugInfo& aInfo);
+
+ // Only be used on Windows when the media engine playback is enabled.
+ Maybe<uint64_t> mMediaEngineId;
+
+ const Maybe<TrackingId> mTrackingId;
+};
+
+} // namespace mozilla
+
+#endif