michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: /* michael@0: Each video element based on MediaDecoder has a state machine to manage michael@0: its play state and keep the current frame up to date. All state machines michael@0: share time in a single shared thread. Each decoder also has one thread michael@0: dedicated to decoding audio and video data. This thread is shutdown when michael@0: playback is paused. Each decoder also has a thread to push decoded audio michael@0: to the hardware. This thread is not created until playback starts, but michael@0: currently is not destroyed when paused, only when playback ends. michael@0: michael@0: The decoder owns the resources for downloading the media file, and the michael@0: high level state. It holds an owning reference to the state machine that michael@0: owns all the resources related to decoding data, and manages the low level michael@0: decoding operations and A/V sync. michael@0: michael@0: Each state machine runs on the shared state machine thread. Every time some michael@0: action is required for a state machine, it is scheduled to run on the shared michael@0: the state machine thread. The state machine runs one "cycle" on the state michael@0: machine thread, and then returns. If necessary, it will schedule itself to michael@0: run again in future. While running this cycle, it must not block the michael@0: thread, as other state machines' events may need to run. State shared michael@0: between a state machine's threads is synchronised via the monitor owned michael@0: by its MediaDecoder object. michael@0: michael@0: The Main thread controls the decode state machine by setting the value michael@0: of a mPlayState variable and notifying on the monitor based on the michael@0: high level player actions required (Seek, Pause, Play, etc). michael@0: michael@0: The player states are the states requested by the client through the michael@0: DOM API. They represent the desired state of the player, while the michael@0: decoder's state represents the actual state of the decoder. michael@0: michael@0: The high level state of the player is maintained via a PlayState value. michael@0: It can have the following states: michael@0: michael@0: START michael@0: The decoder has been initialized but has no resource loaded. michael@0: PAUSED michael@0: A request via the API has been received to pause playback. michael@0: LOADING michael@0: A request via the API has been received to load a resource. michael@0: PLAYING michael@0: A request via the API has been received to start playback. michael@0: SEEKING michael@0: A request via the API has been received to start seeking. michael@0: COMPLETED michael@0: Playback has completed. michael@0: SHUTDOWN michael@0: The decoder is about to be destroyed. michael@0: michael@0: State transition occurs when the Media Element calls the Play, Seek, michael@0: etc methods on the MediaDecoder object. When the transition michael@0: occurs MediaDecoder then calls the methods on the decoder state michael@0: machine object to cause it to behave as required by the play state. michael@0: State transitions will likely schedule the state machine to run to michael@0: affect the change. michael@0: michael@0: An implementation of the MediaDecoderStateMachine class is the event michael@0: that gets dispatched to the state machine thread. Each time the event is run, michael@0: the state machine must cycle the state machine once, and then return. michael@0: michael@0: The state machine has the following states: michael@0: michael@0: DECODING_METADATA michael@0: The media headers are being loaded, and things like framerate, etc are michael@0: being determined, and the first frame of audio/video data is being decoded. michael@0: DECODING michael@0: The decode has started. If the PlayState is PLAYING, the decode thread michael@0: should be alive and decoding video and audio frame, the audio thread michael@0: should be playing audio, and the state machine should run periodically michael@0: to update the video frames being displayed. michael@0: SEEKING michael@0: A seek operation is in progress. The decode thread should be seeking. michael@0: BUFFERING michael@0: Decoding is paused while data is buffered for smooth playback. If playback michael@0: is paused (PlayState transitions to PAUSED) we'll destory the decode thread. michael@0: COMPLETED michael@0: The resource has completed decoding, but possibly not finished playback. michael@0: The decode thread will be destroyed. Once playback finished, the audio michael@0: thread will also be destroyed. michael@0: SHUTDOWN michael@0: The decoder object and its state machine are about to be destroyed. michael@0: Once the last state machine has been destroyed, the shared state machine michael@0: thread will also be destroyed. It will be recreated later if needed. michael@0: michael@0: The following result in state transitions. michael@0: michael@0: Shutdown() michael@0: Clean up any resources the MediaDecoderStateMachine owns. michael@0: Play() michael@0: Start decoding and playback of media data. michael@0: Buffer michael@0: This is not user initiated. It occurs when the michael@0: available data in the stream drops below a certain point. michael@0: Complete michael@0: This is not user initiated. It occurs when the michael@0: stream is completely decoded. michael@0: Seek(double) michael@0: Seek to the time position given in the resource. michael@0: michael@0: A state transition diagram: michael@0: michael@0: DECODING_METADATA michael@0: | | michael@0: v | Shutdown() michael@0: | | michael@0: v -->-------------------->--------------------------| michael@0: |---------------->----->------------------------| v michael@0: DECODING | | | | | michael@0: ^ v Seek(t) | | | | michael@0: | Play() | v | | | michael@0: ^-----------<----SEEKING | v Complete v v michael@0: | | | | | | michael@0: | | | COMPLETED SHUTDOWN-<-| michael@0: ^ ^ | |Shutdown() | michael@0: | | | >-------->-----^ michael@0: | Play() |Seek(t) |Buffer() | michael@0: -----------<--------<-------BUFFERING | michael@0: | ^ michael@0: v Shutdown() | michael@0: | | michael@0: ------------>-----| michael@0: michael@0: The following represents the states that the MediaDecoder object michael@0: can be in, and the valid states the MediaDecoderStateMachine can be in at that michael@0: time: michael@0: michael@0: player LOADING decoder DECODING_METADATA michael@0: player PLAYING decoder DECODING, BUFFERING, SEEKING, COMPLETED michael@0: player PAUSED decoder DECODING, BUFFERING, SEEKING, COMPLETED michael@0: player SEEKING decoder SEEKING michael@0: player COMPLETED decoder SHUTDOWN michael@0: player SHUTDOWN decoder SHUTDOWN michael@0: michael@0: The general sequence of events is: michael@0: michael@0: 1) The video element calls Load on MediaDecoder. This creates the michael@0: state machine and starts the channel for downloading the michael@0: file. It instantiates and schedules the MediaDecoderStateMachine. The michael@0: high level LOADING state is entered, which results in the decode michael@0: thread being created and starting to decode metadata. These are michael@0: the headers that give the video size, framerate, etc. Load() returns michael@0: immediately to the calling video element. michael@0: michael@0: 2) When the metadata has been loaded by the decode thread, the state machine michael@0: will call a method on the video element object to inform it that this michael@0: step is done, so it can do the things required by the video specification michael@0: at this stage. The decode thread then continues to decode the first frame michael@0: of data. michael@0: michael@0: 3) When the first frame of data has been successfully decoded the state michael@0: machine calls a method on the video element object to inform it that michael@0: this step has been done, once again so it can do the required things michael@0: by the video specification at this stage. michael@0: michael@0: This results in the high level state changing to PLAYING or PAUSED michael@0: depending on any user action that may have occurred. michael@0: michael@0: While the play state is PLAYING, the decode thread will decode michael@0: data, and the audio thread will push audio data to the hardware to michael@0: be played. The state machine will run periodically on the shared michael@0: state machine thread to ensure video frames are played at the michael@0: correct time; i.e. the state machine manages A/V sync. michael@0: michael@0: The Shutdown method on MediaDecoder closes the download channel, and michael@0: signals to the state machine that it should shutdown. The state machine michael@0: shuts down asynchronously, and will release the owning reference to the michael@0: state machine once its threads are shutdown. michael@0: michael@0: The owning object of a MediaDecoder object *MUST* call Shutdown when michael@0: destroying the MediaDecoder object. michael@0: michael@0: */ michael@0: #if !defined(MediaDecoder_h_) michael@0: #define MediaDecoder_h_ michael@0: michael@0: #include "nsISupports.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIObserver.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "MediaResource.h" michael@0: #include "mozilla/dom/AudioChannelBinding.h" michael@0: #include "mozilla/gfx/Rect.h" michael@0: #include "mozilla/ReentrantMonitor.h" michael@0: #include "mozilla/TimeStamp.h" michael@0: #include "MediaStreamGraph.h" michael@0: #include "AbstractMediaDecoder.h" michael@0: #include "necko-config.h" michael@0: michael@0: class nsIStreamListener; michael@0: class nsIPrincipal; michael@0: class nsITimer; michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: class TimeRanges; michael@0: } michael@0: } michael@0: michael@0: namespace mozilla { michael@0: namespace layers { michael@0: class Image; michael@0: } //namespace layers michael@0: michael@0: class VideoFrameContainer; michael@0: class MediaDecoderStateMachine; michael@0: class MediaDecoderOwner; michael@0: michael@0: // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to michael@0: // GetTickCount() and conflicts with MediaDecoder::GetCurrentTime implementation. michael@0: #ifdef GetCurrentTime michael@0: #undef GetCurrentTime michael@0: #endif michael@0: michael@0: // Stores the seek target; the time to seek to, and whether an Accurate, michael@0: // or "Fast" (nearest keyframe) seek was requested. michael@0: struct SeekTarget { michael@0: enum Type { michael@0: Invalid, michael@0: PrevSyncPoint, michael@0: Accurate michael@0: }; michael@0: SeekTarget() michael@0: : mTime(-1.0) michael@0: , mType(SeekTarget::Invalid) michael@0: { michael@0: } michael@0: SeekTarget(int64_t aTimeUsecs, Type aType) michael@0: : mTime(aTimeUsecs) michael@0: , mType(aType) michael@0: { michael@0: } michael@0: bool IsValid() const { michael@0: return mType != SeekTarget::Invalid; michael@0: } michael@0: void Reset() { michael@0: mTime = -1; michael@0: mType = SeekTarget::Invalid; michael@0: } michael@0: // Seek target time in microseconds. michael@0: int64_t mTime; michael@0: // Whether we should seek "Fast", or "Accurate". michael@0: // "Fast" seeks to the seek point preceeding mTime, whereas michael@0: // "Accurate" seeks as close as possible to mTime. michael@0: Type mType; michael@0: }; michael@0: michael@0: class MediaDecoder : public nsIObserver, michael@0: public AbstractMediaDecoder michael@0: { michael@0: public: michael@0: class DecodedStreamGraphListener; michael@0: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSIOBSERVER michael@0: michael@0: // Enumeration for the valid play states (see mPlayState) michael@0: enum PlayState { michael@0: PLAY_STATE_START, michael@0: PLAY_STATE_LOADING, michael@0: PLAY_STATE_PAUSED, michael@0: PLAY_STATE_PLAYING, michael@0: PLAY_STATE_SEEKING, michael@0: PLAY_STATE_ENDED, michael@0: PLAY_STATE_SHUTDOWN michael@0: }; michael@0: michael@0: MediaDecoder(); michael@0: virtual ~MediaDecoder(); michael@0: michael@0: // Reset the decoder and notify the media element that michael@0: // server connection is closed. michael@0: virtual void ResetConnectionState(); michael@0: // Create a new decoder of the same type as this one. michael@0: // Subclasses must implement this. michael@0: virtual MediaDecoder* Clone() = 0; michael@0: // Create a new state machine to run this decoder. michael@0: // Subclasses must implement this. michael@0: virtual MediaDecoderStateMachine* CreateStateMachine() = 0; michael@0: michael@0: // Call on the main thread only. michael@0: // Perform any initialization required for the decoder. michael@0: // Return true on successful initialisation, false michael@0: // on failure. michael@0: virtual bool Init(MediaDecoderOwner* aOwner); michael@0: michael@0: // Cleanup internal data structures. Must be called on the main michael@0: // thread by the owning object before that object disposes of this object. michael@0: virtual void Shutdown(); michael@0: michael@0: // Start downloading the media. Decode the downloaded data up to the michael@0: // point of the first frame of data. michael@0: // This is called at most once per decoder, after Init(). michael@0: virtual nsresult Load(nsIStreamListener** aListener, michael@0: MediaDecoder* aCloneDonor); michael@0: michael@0: // Called in |Load| to open mResource. michael@0: nsresult OpenResource(nsIStreamListener** aStreamListener); michael@0: michael@0: // Called when the video file has completed downloading. michael@0: virtual void ResourceLoaded(); michael@0: michael@0: // Called if the media file encounters a network error. michael@0: virtual void NetworkError(); michael@0: michael@0: // Get the current MediaResource being used. Its URI will be returned michael@0: // by currentSrc. Returns what was passed to Load(), if Load() has been called. michael@0: // Note: The MediaResource is refcounted, but it outlives the MediaDecoder, michael@0: // so it's OK to use the reference returned by this function without michael@0: // refcounting, *unless* you need to store and use the reference after the michael@0: // MediaDecoder has been destroyed. You might need to do this if you're michael@0: // wrapping the MediaResource in some kind of byte stream interface to be michael@0: // passed to a platform decoder. michael@0: MediaResource* GetResource() const MOZ_FINAL MOZ_OVERRIDE michael@0: { michael@0: return mResource; michael@0: } michael@0: void SetResource(MediaResource* aResource) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Should only be called on main thread"); michael@0: mResource = aResource; michael@0: } michael@0: michael@0: // Return the principal of the current URI being played or downloaded. michael@0: virtual already_AddRefed GetCurrentPrincipal(); michael@0: michael@0: // Return the time position in the video stream being michael@0: // played measured in seconds. michael@0: virtual double GetCurrentTime(); michael@0: michael@0: // Seek to the time position in (seconds) from the start of the video. michael@0: // If aDoFastSeek is true, we'll seek to the sync point/keyframe preceeding michael@0: // the seek target. michael@0: virtual nsresult Seek(double aTime, SeekTarget::Type aSeekType); michael@0: michael@0: // Enables decoders to supply an enclosing byte range for a seek offset. michael@0: // E.g. used by ChannelMediaResource to download a whole cluster for michael@0: // DASH-WebM. michael@0: virtual nsresult GetByteRangeForSeek(int64_t const aOffset, michael@0: MediaByteRange &aByteRange) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: // Initialize state machine and schedule it. michael@0: nsresult InitializeStateMachine(MediaDecoder* aCloneDonor); michael@0: michael@0: // Start playback of a video. 'Load' must have previously been michael@0: // called. michael@0: virtual nsresult Play(); michael@0: michael@0: // Set/Unset dormant state if necessary. michael@0: // Dormant state is a state to free all scarce media resources michael@0: // (like hw video codec), did not decoding and stay dormant. michael@0: // It is used to share scarece media resources in system. michael@0: virtual void SetDormantIfNecessary(bool aDormant); michael@0: michael@0: // Pause video playback. michael@0: virtual void Pause(); michael@0: // Adjust the speed of the playback, optionally with pitch correction, michael@0: virtual void SetVolume(double aVolume); michael@0: // Sets whether audio is being captured. If it is, we won't play any michael@0: // of our audio. michael@0: virtual void SetAudioCaptured(bool aCaptured); michael@0: michael@0: virtual void NotifyWaitingForResourcesStatusChanged() MOZ_OVERRIDE; michael@0: michael@0: virtual void SetPlaybackRate(double aPlaybackRate); michael@0: void SetPreservesPitch(bool aPreservesPitch); michael@0: michael@0: // Directs the decoder to not preroll extra samples until the media is michael@0: // played. This reduces the memory overhead of media elements that may michael@0: // not be played. Note that seeking also doesn't cause us start prerolling. michael@0: void SetMinimizePrerollUntilPlaybackStarts(); michael@0: michael@0: // All MediaStream-related data is protected by mReentrantMonitor. michael@0: // We have at most one DecodedStreamData per MediaDecoder. Its stream michael@0: // is used as the input for each ProcessedMediaStream created by calls to michael@0: // captureStream(UntilEnded). Seeking creates a new source stream, as does michael@0: // replaying after the input as ended. In the latter case, the new source is michael@0: // not connected to streams created by captureStreamUntilEnded. michael@0: michael@0: struct DecodedStreamData { michael@0: typedef gfx::IntSize IntSize; michael@0: michael@0: DecodedStreamData(MediaDecoder* aDecoder, michael@0: int64_t aInitialTime, SourceMediaStream* aStream); michael@0: ~DecodedStreamData(); michael@0: michael@0: StreamTime GetLastOutputTime() { return mListener->GetLastOutputTime(); } michael@0: bool IsFinished() { return mListener->IsFinishedOnMainThread(); } michael@0: michael@0: // The following group of fields are protected by the decoder's monitor michael@0: // and can be read or written on any thread. michael@0: int64_t mLastAudioPacketTime; // microseconds michael@0: int64_t mLastAudioPacketEndTime; // microseconds michael@0: // Count of audio frames written to the stream michael@0: int64_t mAudioFramesWritten; michael@0: // Saved value of aInitialTime. Timestamp of the first audio and/or michael@0: // video packet written. michael@0: int64_t mInitialTime; // microseconds michael@0: // mNextVideoTime is the end timestamp for the last packet sent to the stream. michael@0: // Therefore video packets starting at or after this time need to be copied michael@0: // to the output stream. michael@0: int64_t mNextVideoTime; // microseconds michael@0: MediaDecoder* mDecoder; michael@0: // The last video image sent to the stream. Useful if we need to replicate michael@0: // the image. michael@0: nsRefPtr mLastVideoImage; michael@0: IntSize mLastVideoImageDisplaySize; michael@0: // This is set to true when the stream is initialized (audio and michael@0: // video tracks added). michael@0: bool mStreamInitialized; michael@0: bool mHaveSentFinish; michael@0: bool mHaveSentFinishAudio; michael@0: bool mHaveSentFinishVideo; michael@0: michael@0: // The decoder is responsible for calling Destroy() on this stream. michael@0: // Can be read from any thread. michael@0: const nsRefPtr mStream; michael@0: // Can be read from any thread. michael@0: nsRefPtr mListener; michael@0: // True when we've explicitly blocked this stream because we're michael@0: // not in PLAY_STATE_PLAYING. Used on the main thread only. michael@0: bool mHaveBlockedForPlayState; michael@0: // We also have an explicit blocker on the stream when michael@0: // mDecoderStateMachine is non-null and MediaDecoderStateMachine is false. michael@0: bool mHaveBlockedForStateMachineNotPlaying; michael@0: }; michael@0: michael@0: class DecodedStreamGraphListener : public MediaStreamListener { michael@0: public: michael@0: DecodedStreamGraphListener(MediaStream* aStream, DecodedStreamData* aData); michael@0: virtual void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) MOZ_OVERRIDE; michael@0: virtual void NotifyFinished(MediaStreamGraph* aGraph) MOZ_OVERRIDE; michael@0: michael@0: void DoNotifyFinished(); michael@0: michael@0: StreamTime GetLastOutputTime() michael@0: { michael@0: MutexAutoLock lock(mMutex); michael@0: return mLastOutputTime; michael@0: } michael@0: void Forget() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Main thread only"); michael@0: mData = nullptr; michael@0: michael@0: MutexAutoLock lock(mMutex); michael@0: mStream = nullptr; michael@0: } michael@0: bool SetFinishedOnMainThread(bool aFinished) michael@0: { michael@0: MutexAutoLock lock(mMutex); michael@0: bool result = !mStreamFinishedOnMainThread; michael@0: mStreamFinishedOnMainThread = aFinished; michael@0: return result; michael@0: } michael@0: bool IsFinishedOnMainThread() michael@0: { michael@0: MutexAutoLock lock(mMutex); michael@0: return mStreamFinishedOnMainThread; michael@0: } michael@0: private: michael@0: // Main thread only michael@0: DecodedStreamData* mData; michael@0: michael@0: Mutex mMutex; michael@0: // Protected by mMutex michael@0: nsRefPtr mStream; michael@0: // Protected by mMutex michael@0: StreamTime mLastOutputTime; michael@0: // Protected by mMutex michael@0: bool mStreamFinishedOnMainThread; michael@0: }; michael@0: michael@0: struct OutputStreamData { michael@0: void Init(ProcessedMediaStream* aStream, bool aFinishWhenEnded) michael@0: { michael@0: mStream = aStream; michael@0: mFinishWhenEnded = aFinishWhenEnded; michael@0: } michael@0: nsRefPtr mStream; michael@0: // mPort connects mDecodedStream->mStream to our mStream. michael@0: nsRefPtr mPort; michael@0: bool mFinishWhenEnded; michael@0: }; michael@0: /** michael@0: * Connects mDecodedStream->mStream to aStream->mStream. michael@0: */ michael@0: void ConnectDecodedStreamToOutputStream(OutputStreamData* aStream); michael@0: /** michael@0: * Disconnects mDecodedStream->mStream from all outputs and clears michael@0: * mDecodedStream. michael@0: */ michael@0: void DestroyDecodedStream(); michael@0: /** michael@0: * Recreates mDecodedStream. Call this to create mDecodedStream at first, michael@0: * and when seeking, to ensure a new stream is set up with fresh buffers. michael@0: * aStartTimeUSecs is relative to the state machine's mStartTime. michael@0: * Decoder monitor must be held. michael@0: */ michael@0: void RecreateDecodedStream(int64_t aStartTimeUSecs); michael@0: /** michael@0: * Call this when mDecoderStateMachine or mDecoderStateMachine->IsPlaying() changes. michael@0: * Decoder monitor must be held. michael@0: */ michael@0: void UpdateStreamBlockingForStateMachinePlaying(); michael@0: nsTArray& OutputStreams() michael@0: { michael@0: GetReentrantMonitor().AssertCurrentThreadIn(); michael@0: return mOutputStreams; michael@0: } michael@0: DecodedStreamData* GetDecodedStream() michael@0: { michael@0: GetReentrantMonitor().AssertCurrentThreadIn(); michael@0: return mDecodedStream; michael@0: } michael@0: michael@0: // Add an output stream. All decoder output will be sent to the stream. michael@0: // The stream is initially blocked. The decoder is responsible for unblocking michael@0: // it while it is playing back. michael@0: virtual void AddOutputStream(ProcessedMediaStream* aStream, bool aFinishWhenEnded); michael@0: michael@0: // Return the duration of the video in seconds. michael@0: virtual double GetDuration(); michael@0: michael@0: // Return the duration of the video in seconds. michael@0: int64_t GetMediaDuration() MOZ_FINAL MOZ_OVERRIDE; michael@0: michael@0: // A media stream is assumed to be infinite if the metadata doesn't michael@0: // contain the duration, and range requests are not supported, and michael@0: // no headers give a hint of a possible duration (Content-Length, michael@0: // Content-Duration, and variants), and we cannot seek in the media michael@0: // stream to determine the duration. michael@0: // michael@0: // When the media stream ends, we can know the duration, thus the stream is michael@0: // no longer considered to be infinite. michael@0: virtual void SetInfinite(bool aInfinite); michael@0: michael@0: // Return true if the stream is infinite (see SetInfinite). michael@0: virtual bool IsInfinite(); michael@0: michael@0: // Called by MediaResource when the "cache suspended" status changes. michael@0: // If MediaResource::IsSuspendedByCache returns true, then the decoder michael@0: // should stop buffering or otherwise waiting for download progress and michael@0: // start consuming data, if possible, because the cache is full. michael@0: virtual void NotifySuspendedStatusChanged(); michael@0: michael@0: // Called by MediaResource when some data has been received. michael@0: // Call on the main thread only. michael@0: virtual void NotifyBytesDownloaded(); michael@0: michael@0: // Called by nsChannelToPipeListener or MediaResource when the michael@0: // download has ended. Called on the main thread only. aStatus is michael@0: // the result from OnStopRequest. michael@0: virtual void NotifyDownloadEnded(nsresult aStatus); michael@0: michael@0: // Called as data arrives on the stream and is read into the cache. Called michael@0: // on the main thread only. michael@0: virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset); michael@0: michael@0: // Called by MediaResource when the principal of the resource has michael@0: // changed. Called on main thread only. michael@0: virtual void NotifyPrincipalChanged(); michael@0: michael@0: // Called by the MediaResource to keep track of the number of bytes read michael@0: // from the resource. Called on the main by an event runner dispatched michael@0: // by the MediaResource read functions. michael@0: void NotifyBytesConsumed(int64_t aBytes, int64_t aOffset) MOZ_FINAL MOZ_OVERRIDE; michael@0: michael@0: int64_t GetEndMediaTime() const MOZ_FINAL MOZ_OVERRIDE; michael@0: michael@0: // Return true if we are currently seeking in the media resource. michael@0: // Call on the main thread only. michael@0: virtual bool IsSeeking() const; michael@0: michael@0: // Return true if the decoder has reached the end of playback. michael@0: // Call on the main thread only. michael@0: virtual bool IsEnded() const; michael@0: michael@0: // Set the duration of the media resource in units of seconds. michael@0: // This is called via a channel listener if it can pick up the duration michael@0: // from a content header. Must be called from the main thread only. michael@0: virtual void SetDuration(double aDuration); michael@0: michael@0: // Sets the initial duration of the media. Called while the media metadata michael@0: // is being read and the decode is being setup. michael@0: void SetMediaDuration(int64_t aDuration) MOZ_OVERRIDE; michael@0: // Updates the media duration. This is called while the media is being michael@0: // played, calls before the media has reached loaded metadata are ignored. michael@0: // The duration is assumed to be an estimate, and so a degree of michael@0: // instability is expected; if the incoming duration is not significantly michael@0: // different from the existing duration, the change request is ignored. michael@0: // If the incoming duration is significantly different, the duration is michael@0: // changed, this causes a durationchanged event to fire to the media michael@0: // element. michael@0: void UpdateEstimatedMediaDuration(int64_t aDuration) MOZ_OVERRIDE; michael@0: michael@0: // Set a flag indicating whether seeking is supported michael@0: virtual void SetMediaSeekable(bool aMediaSeekable) MOZ_OVERRIDE; michael@0: virtual void SetTransportSeekable(bool aTransportSeekable) MOZ_FINAL MOZ_OVERRIDE; michael@0: // Returns true if this media supports seeking. False for example for WebM michael@0: // files without an index and chained ogg files. michael@0: virtual bool IsMediaSeekable() MOZ_FINAL MOZ_OVERRIDE; michael@0: // Returns true if seeking is supported on a transport level (e.g. the server michael@0: // supports range requests, we are playing a file, etc.). michael@0: virtual bool IsTransportSeekable(); michael@0: michael@0: // Return the time ranges that can be seeked into. michael@0: virtual nsresult GetSeekable(dom::TimeRanges* aSeekable); michael@0: michael@0: // Set the end time of the media resource. When playback reaches michael@0: // this point the media pauses. aTime is in seconds. michael@0: virtual void SetFragmentEndTime(double aTime); michael@0: michael@0: // Set the end time of the media. aTime is in microseconds. michael@0: void SetMediaEndTime(int64_t aTime) MOZ_FINAL MOZ_OVERRIDE; michael@0: michael@0: // Invalidate the frame. michael@0: void Invalidate(); michael@0: void InvalidateWithFlags(uint32_t aFlags); michael@0: michael@0: // Suspend any media downloads that are in progress. Called by the michael@0: // media element when it is sent to the bfcache, or when we need michael@0: // to throttle the download. Call on the main thread only. This can michael@0: // be called multiple times, there's an internal "suspend count". michael@0: virtual void Suspend(); michael@0: michael@0: // Resume any media downloads that have been suspended. Called by the michael@0: // media element when it is restored from the bfcache, or when we need michael@0: // to stop throttling the download. Call on the main thread only. michael@0: // The download will only actually resume once as many Resume calls michael@0: // have been made as Suspend calls. When aForceBuffering is true, michael@0: // we force the decoder to go into buffering state before resuming michael@0: // playback. michael@0: virtual void Resume(bool aForceBuffering); michael@0: michael@0: // Moves any existing channel loads into the background, so that they don't michael@0: // block the load event. This is called when we stop delaying the load michael@0: // event. Any new loads initiated (for example to seek) will also be in the michael@0: // background. Implementations of this must call MoveLoadsToBackground() on michael@0: // their MediaResource. michael@0: virtual void MoveLoadsToBackground(); michael@0: michael@0: // Returns a weak reference to the media decoder owner. michael@0: MediaDecoderOwner* GetMediaOwner() const; michael@0: michael@0: // Called by the state machine to notify the decoder that the duration michael@0: // has changed. michael@0: void DurationChanged(); michael@0: michael@0: bool OnStateMachineThread() const MOZ_OVERRIDE; michael@0: michael@0: bool OnDecodeThread() const MOZ_OVERRIDE; michael@0: michael@0: // Returns the monitor for other threads to synchronise access to michael@0: // state. michael@0: ReentrantMonitor& GetReentrantMonitor() MOZ_OVERRIDE; michael@0: michael@0: // Returns true if the decoder is shut down michael@0: bool IsShutdown() const MOZ_FINAL MOZ_OVERRIDE; michael@0: michael@0: // Constructs the time ranges representing what segments of the media michael@0: // are buffered and playable. michael@0: virtual nsresult GetBuffered(dom::TimeRanges* aBuffered); michael@0: michael@0: // Returns the size, in bytes, of the heap memory used by the currently michael@0: // queued decoded video and audio data. michael@0: size_t SizeOfVideoQueue(); michael@0: size_t SizeOfAudioQueue(); michael@0: michael@0: VideoFrameContainer* GetVideoFrameContainer() MOZ_FINAL MOZ_OVERRIDE michael@0: { michael@0: return mVideoFrameContainer; michael@0: } michael@0: layers::ImageContainer* GetImageContainer() MOZ_OVERRIDE; michael@0: michael@0: // Return the current state. Can be called on any thread. If called from michael@0: // a non-main thread, the decoder monitor must be held. michael@0: PlayState GetState() { michael@0: return mPlayState; michael@0: } michael@0: michael@0: // Fire progress events if needed according to the time and byte michael@0: // constraints outlined in the specification. aTimer is true michael@0: // if the method is called as a result of the progress timer rather michael@0: // than the result of downloaded data. michael@0: void Progress(bool aTimer); michael@0: michael@0: // Fire timeupdate events if needed according to the time constraints michael@0: // outlined in the specification. michael@0: void FireTimeUpdate(); michael@0: michael@0: // Stop updating the bytes downloaded for progress notifications. Called michael@0: // when seeking to prevent wild changes to the progress notification. michael@0: // Must be called with the decoder monitor held. michael@0: virtual void StopProgressUpdates(); michael@0: michael@0: // Allow updating the bytes downloaded for progress notifications. Must michael@0: // be called with the decoder monitor held. michael@0: virtual void StartProgressUpdates(); michael@0: michael@0: // Something has changed that could affect the computed playback rate, michael@0: // so recompute it. The monitor must be held. michael@0: virtual void UpdatePlaybackRate(); michael@0: michael@0: // Used to estimate rates of data passing through the decoder's channel. michael@0: // Records activity stopping on the channel. The monitor must be held. michael@0: virtual void NotifyPlaybackStarted() { michael@0: GetReentrantMonitor().AssertCurrentThreadIn(); michael@0: mPlaybackStatistics.Start(); michael@0: } michael@0: michael@0: // Used to estimate rates of data passing through the decoder's channel. michael@0: // Records activity stopping on the channel. The monitor must be held. michael@0: virtual void NotifyPlaybackStopped() { michael@0: GetReentrantMonitor().AssertCurrentThreadIn(); michael@0: mPlaybackStatistics.Stop(); michael@0: } michael@0: michael@0: // The actual playback rate computation. The monitor must be held. michael@0: virtual double ComputePlaybackRate(bool* aReliable); michael@0: michael@0: // Return true when the media is same-origin with the element. The monitor michael@0: // must be held. michael@0: bool IsSameOriginMedia(); michael@0: michael@0: // Returns true if we can play the entire media through without stopping michael@0: // to buffer, given the current download and playback rates. michael@0: bool CanPlayThrough(); michael@0: michael@0: // Make the decoder state machine update the playback position. Called by michael@0: // the reader on the decoder thread (Assertions for this checked by michael@0: // mDecoderStateMachine). This must be called with the decode monitor michael@0: // held. michael@0: void UpdatePlaybackPosition(int64_t aTime) MOZ_FINAL MOZ_OVERRIDE; michael@0: michael@0: void SetAudioChannel(dom::AudioChannel aChannel) { mAudioChannel = aChannel; } michael@0: dom::AudioChannel GetAudioChannel() { return mAudioChannel; } michael@0: michael@0: // Send a new set of metadata to the state machine, to be dispatched to the michael@0: // main thread to be presented when the |currentTime| of the media is greater michael@0: // or equal to aPublishTime. michael@0: void QueueMetadata(int64_t aPublishTime, michael@0: int aChannels, michael@0: int aRate, michael@0: bool aHasAudio, michael@0: bool aHasVideo, michael@0: MetadataTags* aTags); michael@0: michael@0: /****** michael@0: * The following methods must only be called on the main michael@0: * thread. michael@0: ******/ michael@0: michael@0: // Change to a new play state. This updates the mState variable and michael@0: // notifies any thread blocking on this object's monitor of the michael@0: // change. Call on the main thread only. michael@0: virtual void ChangeState(PlayState aState); michael@0: michael@0: // Called by |ChangeState|, to update the state machine. michael@0: // Call on the main thread only and the lock must be obtained. michael@0: virtual void ApplyStateToStateMachine(PlayState aState); michael@0: michael@0: // May be called by the reader to notify this decoder that the metadata from michael@0: // the media file has been read. Call on the decode thread only. michael@0: void OnReadMetadataCompleted() MOZ_OVERRIDE { } michael@0: michael@0: // Called when the metadata from the media file has been loaded by the michael@0: // state machine. Call on the main thread only. michael@0: virtual void MetadataLoaded(int aChannels, michael@0: int aRate, michael@0: bool aHasAudio, michael@0: bool aHasVideo, michael@0: MetadataTags* aTags); michael@0: michael@0: // Called when the first frame has been loaded. michael@0: // Call on the main thread only. michael@0: void FirstFrameLoaded(); michael@0: michael@0: // Returns true if the resource has been loaded. Acquires the monitor. michael@0: // Call from any thread. michael@0: virtual bool IsDataCachedToEndOfResource(); michael@0: michael@0: // Called when the video has completed playing. michael@0: // Call on the main thread only. michael@0: void PlaybackEnded(); michael@0: michael@0: // Seeking has stopped. Inform the element on the main michael@0: // thread. michael@0: void SeekingStopped(); michael@0: michael@0: // Seeking has stopped at the end of the resource. Inform the element on the main michael@0: // thread. michael@0: void SeekingStoppedAtEnd(); michael@0: michael@0: // Seeking has started. Inform the element on the main michael@0: // thread. michael@0: void SeekingStarted(); michael@0: michael@0: // Called when the backend has changed the current playback michael@0: // position. It dispatches a timeupdate event and invalidates the frame. michael@0: // This must be called on the main thread only. michael@0: void PlaybackPositionChanged(); michael@0: michael@0: // Calls mElement->UpdateReadyStateForData, telling it whether we have michael@0: // data for the next frame and if we're buffering. Main thread only. michael@0: virtual void UpdateReadyStateForData(); michael@0: michael@0: // Find the end of the cached data starting at the current decoder michael@0: // position. michael@0: int64_t GetDownloadPosition(); michael@0: michael@0: // Updates the approximate byte offset which playback has reached. This is michael@0: // used to calculate the readyState transitions. michael@0: void UpdatePlaybackOffset(int64_t aOffset); michael@0: michael@0: // Provide access to the state machine object michael@0: MediaDecoderStateMachine* GetStateMachine() const; michael@0: michael@0: // Drop reference to state machine. Only called during shutdown dance. michael@0: virtual void ReleaseStateMachine(); michael@0: michael@0: // Notifies the element that decoding has failed. michael@0: virtual void DecodeError(); michael@0: michael@0: // Indicate whether the media is same-origin with the element. michael@0: void UpdateSameOriginStatus(bool aSameOrigin); michael@0: michael@0: MediaDecoderOwner* GetOwner() MOZ_OVERRIDE; michael@0: michael@0: // Returns true if we're logically playing, that is, if the Play() has michael@0: // been called and Pause() has not or we have not yet reached the end michael@0: // of media. This is irrespective of the seeking state; if the owner michael@0: // calls Play() and then Seek(), we still count as logically playing. michael@0: // The decoder monitor must be held. michael@0: bool IsLogicallyPlaying(); michael@0: michael@0: #ifdef MOZ_RAW michael@0: static bool IsRawEnabled(); michael@0: #endif michael@0: michael@0: static bool IsOggEnabled(); michael@0: static bool IsOpusEnabled(); michael@0: michael@0: #ifdef MOZ_WAVE michael@0: static bool IsWaveEnabled(); michael@0: #endif michael@0: michael@0: #ifdef MOZ_WEBM michael@0: static bool IsWebMEnabled(); michael@0: #endif michael@0: #ifdef NECKO_PROTOCOL_rtsp michael@0: static bool IsRtspEnabled(); michael@0: #endif michael@0: michael@0: #ifdef MOZ_GSTREAMER michael@0: static bool IsGStreamerEnabled(); michael@0: #endif michael@0: michael@0: #ifdef MOZ_OMX_DECODER michael@0: static bool IsOmxEnabled(); michael@0: #endif michael@0: michael@0: #ifdef MOZ_MEDIA_PLUGINS michael@0: static bool IsMediaPluginsEnabled(); michael@0: #endif michael@0: michael@0: #ifdef MOZ_WMF michael@0: static bool IsWMFEnabled(); michael@0: #endif michael@0: michael@0: #ifdef MOZ_APPLEMEDIA michael@0: static bool IsAppleMP3Enabled(); michael@0: #endif michael@0: michael@0: // Schedules the state machine to run one cycle on the shared state michael@0: // machine thread. Main thread only. michael@0: nsresult ScheduleStateMachineThread(); michael@0: michael@0: struct Statistics { michael@0: // Estimate of the current playback rate (bytes/second). michael@0: double mPlaybackRate; michael@0: // Estimate of the current download rate (bytes/second). This michael@0: // ignores time that the channel was paused by Gecko. michael@0: double mDownloadRate; michael@0: // Total length of media stream in bytes; -1 if not known michael@0: int64_t mTotalBytes; michael@0: // Current position of the download, in bytes. This is the offset of michael@0: // the first uncached byte after the decoder position. michael@0: int64_t mDownloadPosition; michael@0: // Current position of decoding, in bytes (how much of the stream michael@0: // has been consumed) michael@0: int64_t mDecoderPosition; michael@0: // Current position of playback, in bytes michael@0: int64_t mPlaybackPosition; michael@0: // If false, then mDownloadRate cannot be considered a reliable michael@0: // estimate (probably because the download has only been running michael@0: // a short time). michael@0: bool mDownloadRateReliable; michael@0: // If false, then mPlaybackRate cannot be considered a reliable michael@0: // estimate (probably because playback has only been running michael@0: // a short time). michael@0: bool mPlaybackRateReliable; michael@0: }; michael@0: michael@0: // Return statistics. This is used for progress events and other things. michael@0: // This can be called from any thread. It's only a snapshot of the michael@0: // current state, since other threads might be changing the state michael@0: // at any time. michael@0: virtual Statistics GetStatistics(); michael@0: michael@0: // Frame decoding/painting related performance counters. michael@0: // Threadsafe. michael@0: class FrameStatistics { michael@0: public: michael@0: michael@0: FrameStatistics() : michael@0: mReentrantMonitor("MediaDecoder::FrameStats"), michael@0: mParsedFrames(0), michael@0: mDecodedFrames(0), michael@0: mPresentedFrames(0) {} michael@0: michael@0: // Returns number of frames which have been parsed from the media. michael@0: // Can be called on any thread. michael@0: uint32_t GetParsedFrames() { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: return mParsedFrames; michael@0: } michael@0: michael@0: // Returns the number of parsed frames which have been decoded. michael@0: // Can be called on any thread. michael@0: uint32_t GetDecodedFrames() { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: return mDecodedFrames; michael@0: } michael@0: michael@0: // Returns the number of decoded frames which have been sent to the rendering michael@0: // pipeline for painting ("presented"). michael@0: // Can be called on any thread. michael@0: uint32_t GetPresentedFrames() { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: return mPresentedFrames; michael@0: } michael@0: michael@0: // Increments the parsed and decoded frame counters by the passed in counts. michael@0: // Can be called on any thread. michael@0: void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded) { michael@0: if (aParsed == 0 && aDecoded == 0) michael@0: return; michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: mParsedFrames += aParsed; michael@0: mDecodedFrames += aDecoded; michael@0: } michael@0: michael@0: // Increments the presented frame counters. michael@0: // Can be called on any thread. michael@0: void NotifyPresentedFrame() { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: ++mPresentedFrames; michael@0: } michael@0: michael@0: private: michael@0: michael@0: // ReentrantMonitor to protect access of playback statistics. michael@0: ReentrantMonitor mReentrantMonitor; michael@0: michael@0: // Number of frames parsed and demuxed from media. michael@0: // Access protected by mReentrantMonitor. michael@0: uint32_t mParsedFrames; michael@0: michael@0: // Number of parsed frames which were actually decoded. michael@0: // Access protected by mReentrantMonitor. michael@0: uint32_t mDecodedFrames; michael@0: michael@0: // Number of decoded frames which were actually sent down the rendering michael@0: // pipeline to be painted ("presented"). Access protected by mReentrantMonitor. michael@0: uint32_t mPresentedFrames; michael@0: }; michael@0: michael@0: // Return the frame decode/paint related statistics. michael@0: FrameStatistics& GetFrameStatistics() { return mFrameStats; } michael@0: michael@0: // Increments the parsed and decoded frame counters by the passed in counts. michael@0: // Can be called on any thread. michael@0: virtual void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded) MOZ_OVERRIDE michael@0: { michael@0: GetFrameStatistics().NotifyDecodedFrames(aParsed, aDecoded); michael@0: } michael@0: michael@0: protected: michael@0: /****** michael@0: * The following members should be accessed with the decoder lock held. michael@0: ******/ michael@0: michael@0: // Current decoding position in the stream. This is where the decoder michael@0: // is up to consuming the stream. This is not adjusted during decoder michael@0: // seek operations, but it's updated at the end when we start playing michael@0: // back again. michael@0: int64_t mDecoderPosition; michael@0: // Current playback position in the stream. This is (approximately) michael@0: // where we're up to playing back the stream. This is not adjusted michael@0: // during decoder seek operations, but it's updated at the end when we michael@0: // start playing back again. michael@0: int64_t mPlaybackPosition; michael@0: michael@0: // The current playback position of the media resource in units of michael@0: // seconds. This is updated approximately at the framerate of the michael@0: // video (if it is a video) or the callback period of the audio. michael@0: // It is read and written from the main thread only. michael@0: double mCurrentTime; michael@0: michael@0: // Volume that playback should start at. 0.0 = muted. 1.0 = full michael@0: // volume. Readable/Writeable from the main thread. michael@0: double mInitialVolume; michael@0: michael@0: // PlaybackRate and pitch preservation status we should start at. michael@0: // Readable/Writeable from the main thread. michael@0: double mInitialPlaybackRate; michael@0: bool mInitialPreservesPitch; michael@0: michael@0: // Duration of the media resource. Set to -1 if unknown. michael@0: // Set when the metadata is loaded. Accessed on the main thread michael@0: // only. michael@0: int64_t mDuration; michael@0: michael@0: // True when playback should start with audio captured (not playing). michael@0: bool mInitialAudioCaptured; michael@0: michael@0: // True if the resource is seekable at a transport level (server supports byte michael@0: // range requests, local file, etc.). michael@0: bool mTransportSeekable; michael@0: michael@0: // True if the media is seekable (i.e. supports random access). michael@0: bool mMediaSeekable; michael@0: michael@0: // True if the media is same-origin with the element. Data can only be michael@0: // passed to MediaStreams when this is true. michael@0: bool mSameOriginMedia; michael@0: michael@0: /****** michael@0: * The following member variables can be accessed from any thread. michael@0: ******/ michael@0: michael@0: // The state machine object for handling the decoding. It is safe to michael@0: // call methods of this object from other threads. Its internal data michael@0: // is synchronised on a monitor. The lifetime of this object is michael@0: // after mPlayState is LOADING and before mPlayState is SHUTDOWN. It michael@0: // is safe to access it during this period. michael@0: nsRefPtr mDecoderStateMachine; michael@0: michael@0: // Media data resource. michael@0: nsRefPtr mResource; michael@0: michael@0: // |ReentrantMonitor| for detecting when the video play state changes. A call michael@0: // to |Wait| on this monitor will block the thread until the next state michael@0: // change. michael@0: // Using a wrapper class to restrict direct access to the |ReentrantMonitor| michael@0: // object. Subclasses may override |MediaDecoder|::|GetReentrantMonitor| michael@0: // e.g. |DASHRepDecoder|::|GetReentrantMonitor| returns the monitor in the michael@0: // main |DASHDecoder| object. In this case, directly accessing the michael@0: // member variable mReentrantMonitor in |DASHRepDecoder| is wrong. michael@0: // The wrapper |RestrictedAccessMonitor| restricts use to the getter michael@0: // function rather than the object itself. michael@0: private: michael@0: class RestrictedAccessMonitor michael@0: { michael@0: public: michael@0: RestrictedAccessMonitor(const char* aName) : michael@0: mReentrantMonitor(aName) michael@0: { michael@0: MOZ_COUNT_CTOR(RestrictedAccessMonitor); michael@0: } michael@0: ~RestrictedAccessMonitor() michael@0: { michael@0: MOZ_COUNT_DTOR(RestrictedAccessMonitor); michael@0: } michael@0: michael@0: // Returns a ref to the reentrant monitor michael@0: ReentrantMonitor& GetReentrantMonitor() { michael@0: return mReentrantMonitor; michael@0: } michael@0: private: michael@0: ReentrantMonitor mReentrantMonitor; michael@0: }; michael@0: michael@0: // The |RestrictedAccessMonitor| member object. michael@0: RestrictedAccessMonitor mReentrantMonitor; michael@0: michael@0: protected: michael@0: // Data about MediaStreams that are being fed by this decoder. michael@0: nsTArray mOutputStreams; michael@0: // The SourceMediaStream we are using to feed the mOutputStreams. This stream michael@0: // is never exposed outside the decoder. michael@0: // Only written on the main thread while holding the monitor. Therefore it michael@0: // can be read on any thread while holding the monitor, or on the main thread michael@0: // without holding the monitor. michael@0: nsAutoPtr mDecodedStream; michael@0: michael@0: // True if this decoder is in dormant state. michael@0: // Should be true only when PlayState is PLAY_STATE_LOADING. michael@0: bool mIsDormant; michael@0: michael@0: // True if this decoder is exiting from dormant state. michael@0: // Should be true only when PlayState is PLAY_STATE_LOADING. michael@0: bool mIsExitingDormant; michael@0: michael@0: // Set to one of the valid play states. michael@0: // This can only be changed on the main thread while holding the decoder michael@0: // monitor. Thus, it can be safely read while holding the decoder monitor michael@0: // OR on the main thread. michael@0: // Any change to the state on the main thread must call NotifyAll on the michael@0: // monitor so the decode thread can wake up. michael@0: PlayState mPlayState; michael@0: michael@0: // The state to change to after a seek or load operation. michael@0: // This can only be changed on the main thread while holding the decoder michael@0: // monitor. Thus, it can be safely read while holding the decoder monitor michael@0: // OR on the main thread. michael@0: // Any change to the state must call NotifyAll on the monitor. michael@0: // This can only be PLAY_STATE_PAUSED or PLAY_STATE_PLAYING. michael@0: PlayState mNextState; michael@0: michael@0: // Position to seek to when the seek notification is received by the michael@0: // decode thread. michael@0: // This can only be changed on the main thread while holding the decoder michael@0: // monitor. Thus, it can be safely read while holding the decoder monitor michael@0: // OR on the main thread. michael@0: // If the SeekTarget's IsValid() accessor returns false, then no seek has michael@0: // been requested. When a seek is started this is reset to invalid. michael@0: SeekTarget mRequestedSeekTarget; michael@0: michael@0: // True when we have fully loaded the resource and reported that michael@0: // to the element (i.e. reached NETWORK_LOADED state). michael@0: // Accessed on the main thread only. michael@0: bool mCalledResourceLoaded; michael@0: michael@0: // True when seeking or otherwise moving the play position around in michael@0: // such a manner that progress event data is inaccurate. This is set michael@0: // during seek and duration operations to prevent the progress indicator michael@0: // from jumping around. Read/Write from any thread. Must have decode monitor michael@0: // locked before accessing. michael@0: bool mIgnoreProgressData; michael@0: michael@0: // True if the stream is infinite (e.g. a webradio). michael@0: bool mInfiniteStream; michael@0: michael@0: // Start timer to update download progress information. michael@0: nsresult StartProgress(); michael@0: michael@0: // Stop progress information timer. michael@0: nsresult StopProgress(); michael@0: michael@0: // Ensures our media stream has been pinned. michael@0: void PinForSeek(); michael@0: michael@0: // Ensures our media stream has been unpinned. michael@0: void UnpinForSeek(); michael@0: michael@0: // Timer used for updating progress events michael@0: nsCOMPtr mProgressTimer; michael@0: michael@0: // This should only ever be accessed from the main thread. michael@0: // It is set in Init and cleared in Shutdown when the element goes away. michael@0: // The decoder does not add a reference the element. michael@0: MediaDecoderOwner* mOwner; michael@0: michael@0: // Counters related to decode and presentation of frames. michael@0: FrameStatistics mFrameStats; michael@0: michael@0: nsRefPtr mVideoFrameContainer; michael@0: michael@0: // Time that the last progress event was fired. Read/Write from the michael@0: // main thread only. michael@0: TimeStamp mProgressTime; michael@0: michael@0: // Time that data was last read from the media resource. Used for michael@0: // computing if the download has stalled and to rate limit progress events michael@0: // when data is arriving slower than PROGRESS_MS. A value of null indicates michael@0: // that a stall event has already fired and not to fire another one until michael@0: // more data is received. Read/Write from the main thread only. michael@0: TimeStamp mDataTime; michael@0: michael@0: // Data needed to estimate playback data rate. The timeline used for michael@0: // this estimate is "decode time" (where the "current time" is the michael@0: // time of the last decoded video frame). michael@0: MediaChannelStatistics mPlaybackStatistics; michael@0: michael@0: // True when our media stream has been pinned. We pin the stream michael@0: // while seeking. michael@0: bool mPinnedForSeek; michael@0: michael@0: // True if the decoder is being shutdown. At this point all events that michael@0: // are currently queued need to return immediately to prevent javascript michael@0: // being run that operates on the element and decoder during shutdown. michael@0: // Read/Write from the main thread only. michael@0: bool mShuttingDown; michael@0: michael@0: // True if the playback is paused because the playback rate member is 0.0. michael@0: bool mPausedForPlaybackRateNull; michael@0: michael@0: // Be assigned from media element during the initialization and pass to michael@0: // AudioStream Class. michael@0: dom::AudioChannel mAudioChannel; michael@0: michael@0: // True if the decoder has been directed to minimize its preroll before michael@0: // playback starts. After the first time playback starts, we don't attempt michael@0: // to minimize preroll, as we assume the user is likely to keep playing, michael@0: // or play the media again. michael@0: bool mMinimizePreroll; michael@0: }; michael@0: michael@0: } // namespace mozilla michael@0: michael@0: #endif