Bug 563825 - Factor out non-Ogg specific parts of nsOggPlayStateMachine.cpp Part 2 - r=cpearce sr=roc

--HG--
rename : content/media/ogg/nsOggReader.cpp => content/media/nsBuiltinDecoderReader.cpp
rename : content/media/ogg/nsOggReader.h => content/media/nsBuiltinDecoderReader.h
rename : content/media/ogg/nsOggPlayStateMachine.cpp => content/media/nsBuiltinDecoderStateMachine.cpp
rename : content/media/ogg/nsOggPlayStateMachine.h => content/media/nsBuiltinDecoderStateMachine.h
extra : rebase_source : ca2b9dced11a077b4eb0d5889cd8a0ff25a4b31f
This commit is contained in:
Chris Double 2010-05-06 14:31:02 +12:00
Родитель 77250a8eb2
Коммит 9145874c60
11 изменённых файлов: 1919 добавлений и 1704 удалений

Просмотреть файл

@ -50,6 +50,8 @@ EXPORTS = \
nsMediaStream.h \
nsMediaCache.h \
nsBuiltinDecoder.h \
nsBuiltinDecoderStateMachine.h \
nsBuiltinDecoderReader.h \
VideoUtils.h \
$(NULL)
@ -58,6 +60,8 @@ CPPSRCS = \
nsMediaCache.cpp \
nsMediaStream.cpp \
nsBuiltinDecoder.cpp \
nsBuiltinDecoderStateMachine.cpp \
nsBuiltinDecoderReader.cpp \
$(NULL)
ifdef MOZ_SYDNEYAUDIO

Просмотреть файл

@ -47,9 +47,7 @@
#include "VideoUtils.h"
#include "nsBuiltinDecoder.h"
using mozilla::Monitor;
using mozilla::MonitorAutoEnter;
using mozilla::MonitorAutoExit;
using namespace mozilla;
#ifdef PR_LOGGING
PRLogModuleInfo* gBuiltinDecoderLog;

Просмотреть файл

@ -87,6 +87,73 @@ etc methods on the nsBuiltinDecoder object. When the transition
occurs nsBuiltinDecoder then calls the methods on the decoder state
machine object to cause it to behave appropriate to the play state.
An implementation of the nsDecoderStateMachine class is the event
that gets dispatched to the state machine thread. It has the following states:
DECODING_METADATA
The media headers are being loaded, and things like framerate, etc are
being determined, and the first frame of audio/video data is being decoded.
DECODING
The decode and audio threads are started and video frames displayed at
the required time.
SEEKING
A seek operation is in progress.
BUFFERING
Decoding is paused while data is buffered for smooth playback.
COMPLETED
The resource has completed decoding, but not finished playback.
SHUTDOWN
The decoder object is about to be destroyed.
The following result in state transitions.
Shutdown()
Clean up any resources the nsDecoderStateMachine owns.
Decode()
Start decoding media data.
Buffer
This is not user initiated. It occurs when the
available data in the stream drops below a certain point.
Complete
This is not user initiated. It occurs when the
stream is completely decoded.
Seek(float)
Seek to the time position given in the resource.
A state transition diagram:
DECODING_METADATA
| |
v | Shutdown()
| |
v -->-------------------->--------------------------|
|---------------->----->------------------------| v
DECODING | | | | |
^ v Seek(t) | | | |
| Decode() | v | | |
^-----------<----SEEKING | v Complete v v
| | | | | |
| | | COMPLETED SHUTDOWN-<-|
^ ^ | |Shutdown() |
| | | >-------->-----^
| Decode() |Seek(t) |Buffer() |
-----------<--------<-------BUFFERING |
| ^
v Shutdown() |
| |
------------>-----|
The following represents the states that the nsBuiltinDecoder object
can be in, and the valid states the nsDecoderStateMachine can be in at that
time:
player LOADING decoder DECODING_METADATA
player PLAYING decoder DECODING, BUFFERING, SEEKING, COMPLETED
player PAUSED decoder DECODING, BUFFERING, SEEKING, COMPLETED
player SEEKING decoder SEEKING
player COMPLETED decoder SHUTDOWN
player SHUTDOWN decoder SHUTDOWN
The general sequence of events is:
1) The video element calls Load on nsMediaDecoder. This creates the
@ -124,7 +191,7 @@ queue an event to the main thread to perform the actual Shutdown. This
way the shutdown can occur at a safe time.
This means the owning object of a nsBuiltinDecoder object *MUST* call
Shutdown when destroying the nsOggDecoder object.
Shutdown when destroying the nsBuiltinDecoder object.
*/
#if !defined(nsBuiltinDecoder_h_)
#define nsBuiltinDecoder_h_
@ -158,10 +225,24 @@ static inline PRBool IsCurrentThread(nsIThread* aThread) {
class nsDecoderStateMachine : public nsRunnable
{
public:
// Enumeration for the valid decoding states
enum State {
DECODER_STATE_DECODING_METADATA,
DECODER_STATE_DECODING,
DECODER_STATE_SEEKING,
DECODER_STATE_BUFFERING,
DECODER_STATE_COMPLETED,
DECODER_STATE_SHUTDOWN
};
// Initializes the state machine, returns NS_OK on success, or
// NS_ERROR_FAILURE on failure.
virtual nsresult Init() = 0;
// Return the current decode state. The decoder monitor must be
// obtained before calling this.
virtual State GetState() = 0;
// Set the audio volume. The decoder monitor must be obtained before
// calling this.
virtual void SetVolume(float aVolume) = 0;
@ -204,6 +285,13 @@ public:
// Called from the main thread to set whether the media resource can
// be seeked. The decoder monitor must be obtained before calling this.
virtual void SetSeekable(PRBool aSeekable) = 0;
// Update the playback position. This can result in a timeupdate event
// and an invalidate of the frame being dispatched asynchronously if
// there is no such event currently queued.
// Only called on the decoder thread. Must be called with
// the decode monitor held.
virtual void UpdatePlaybackPosition(PRInt64 aTime) = 0;
};
class nsBuiltinDecoder : public nsMediaDecoder
@ -215,6 +303,8 @@ class nsBuiltinDecoder : public nsMediaDecoder
NS_DECL_NSIOBSERVER
public:
typedef mozilla::Monitor Monitor;
// Enumeration for the valid play states (see mPlayState)
enum PlayState {
PLAY_STATE_START,
@ -325,7 +415,7 @@ class nsBuiltinDecoder : public nsMediaDecoder
// Returns the monitor for other threads to synchronise access to
// state.
mozilla::Monitor& GetMonitor() {
Monitor& GetMonitor() {
return mMonitor;
}
@ -352,6 +442,15 @@ class nsBuiltinDecoder : public nsMediaDecoder
// The actual playback rate computation. The monitor must be held.
double ComputePlaybackRate(PRPackedBool* aReliable);
// Make the decoder state machine update the playback position. Called by
// the reader on the decoder thread (Assertions for this checked by
// mDecoderStateMachine). This must be called with the decode monitor
// held.
void UpdatePlaybackPosition(PRInt64 aTime)
{
mDecoderStateMachine->UpdatePlaybackPosition(aTime);
}
/******
* The following methods must only be called on the main
* thread.
@ -362,7 +461,7 @@ class nsBuiltinDecoder : public nsMediaDecoder
// change. Call on the main thread only.
void ChangeState(PlayState aState);
// Called when the metadata from the Ogg file has been read.
// Called when the metadata from the media file has been read.
// Call on the main thread only.
void MetadataLoaded();
@ -408,7 +507,14 @@ class nsBuiltinDecoder : public nsMediaDecoder
// Updates the approximate byte offset which playback has reached. This is
// used to calculate the readyState transitions.
void UpdatePlaybackOffset(PRInt64 aOffset);
// Provide access to the state machine object
nsDecoderStateMachine* GetStateMachine() { return mDecoderStateMachine; }
// Return the current decode state. The decoder monitor must be
// obtained before calling this.
nsDecoderStateMachine::State GetDecodeState() { return mDecoderStateMachine->GetState(); }
public:
// Notifies the element that decoding has failed.
void DecodeError();
@ -453,7 +559,7 @@ public:
float mRequestedSeekTime;
// Duration of the media resource. Set to -1 if unknown.
// Set when the Ogg metadata is loaded. Accessed on the main thread
// Set when the metadata is loaded. Accessed on the main thread
// only.
PRInt64 mDuration;
@ -478,7 +584,7 @@ public:
// Monitor for detecting when the video play state changes. A call
// to Wait on this monitor will block the thread until the next
// state change.
mozilla::Monitor mMonitor;
Monitor mMonitor;
// Set to one of the valid play states. It is protected by the
// monitor mMonitor. This monitor must be acquired when reading or

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -36,14 +36,10 @@
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#if !defined(nsOggReader_h_)
#define nsOggReader_h_
#if !defined(nsBuiltinDecoderReader_h_)
#define nsBuiltinDecoderReader_h_
#include <nsDeque.h>
#include "nsOggCodecState.h"
#include <ogg/ogg.h>
#include <theora/theoradec.h>
#include <vorbis/codec.h>
#include "nsAutoLock.h"
#include "nsClassHashtable.h"
#include "mozilla/TimeStamp.h"
@ -51,12 +47,7 @@
#include "nsRect.h"
#include "mozilla/Monitor.h"
class nsOggPlayStateMachine;
using mozilla::Monitor;
using mozilla::MonitorAutoEnter;
using mozilla::TimeDuration;
using mozilla::TimeStamp;
class nsBuiltinDecoderStateMachine;
// Holds chunk a decoded sound samples.
class SoundData {
@ -112,34 +103,49 @@ public:
nsAutoArrayPtr<float> mAudioData;
};
// Holds a decoded Theora frame, in YCbCr format. These are queued in the reader.
// Holds a decoded video frame, in YCbCr format. These are queued in the reader.
class VideoData {
public:
// YCbCr data obtained from decoding the video. The index's are:
// 0 = Y
// 1 = Cb
// 2 = Cr
struct YCbCrBuffer {
struct Plane {
PRUint8* mData;
PRUint32 mWidth;
PRUint32 mHeight;
PRUint32 mStride;
};
Plane mPlanes[3];
};
// Constructs a VideoData object. Makes a copy of YCbCr data in aBuffer.
// This may return nsnull if we run out of memory when allocating buffers
// to store the frame.
// to store the frame. aTimecode is a codec specific number representing
// the timestamp of the frame of video data.
static VideoData* Create(PRInt64 aOffset,
PRInt64 aTime,
th_ycbcr_buffer aBuffer,
const YCbCrBuffer &aBuffer,
PRBool aKeyframe,
PRInt64 aGranulepos);
PRInt64 aTimecode);
// Constructs a duplicate VideoData object. This intrinsically tells the
// player that it does not need to update the displayed frame when this
// frame is played; this frame is identical to the previous.
static VideoData* CreateDuplicate(PRInt64 aOffset,
PRInt64 aTime,
PRInt64 aGranulepos)
PRInt64 aTimecode)
{
return new VideoData(aOffset, aTime, aGranulepos);
return new VideoData(aOffset, aTime, aTimecode);
}
~VideoData()
{
MOZ_COUNT_DTOR(VideoData);
for (PRUint32 i = 0; i < 3; ++i) {
delete mBuffer[i].data;
moz_free(mBuffer.mPlanes[i].mData);
}
}
@ -148,34 +154,37 @@ public:
// Start time of frame in milliseconds.
PRInt64 mTime;
PRInt64 mGranulepos;
th_ycbcr_buffer mBuffer;
// Codec specific internal time code. For Ogg based codecs this is the
// granulepos.
PRInt64 mTimecode;
YCbCrBuffer mBuffer;
// When PR_TRUE, denotes that this frame is identical to the frame that
// came before; it's a duplicate. mBuffer will be empty.
PRPackedBool mDuplicate;
PRPackedBool mKeyframe;
private:
VideoData(PRInt64 aOffset, PRInt64 aTime, PRInt64 aGranulepos)
public:
VideoData(PRInt64 aOffset, PRInt64 aTime, PRInt64 aTimecode)
: mOffset(aOffset),
mTime(aTime),
mGranulepos(aGranulepos),
mTimecode(aTimecode),
mDuplicate(PR_TRUE),
mKeyframe(PR_FALSE)
{
MOZ_COUNT_CTOR(VideoData);
memset(&mBuffer, 0, sizeof(th_ycbcr_buffer));
memset(&mBuffer, 0, sizeof(YCbCrBuffer));
}
VideoData(PRInt64 aOffset,
PRInt64 aTime,
PRBool aKeyframe,
PRInt64 aGranulepos)
PRInt64 aTimecode)
: mOffset(aOffset),
mTime(aTime),
mGranulepos(aGranulepos),
mTimecode(aTimecode),
mDuplicate(PR_FALSE),
mKeyframe(aKeyframe)
{
@ -195,7 +204,9 @@ class MediaQueueDeallocator : public nsDequeFunctor {
template <class T> class MediaQueue : private nsDeque {
public:
typedef mozilla::MonitorAutoEnter MonitorAutoEnter;
typedef mozilla::Monitor Monitor;
MediaQueue()
: nsDeque(new MediaQueueDeallocator<T>()),
mMonitor("mediaqueue"),
@ -284,8 +295,8 @@ template <class T> class MediaQueue : private nsDeque {
private:
Monitor mMonitor;
// PR_TRUE when we've decoded the last packet in the bitstream for which
// we're queueing sample-data.
// PR_TRUE when we've decoded the last frame of data in the
// bitstream for which we're queueing sample-data.
PRBool mEndOfStream;
};
@ -323,9 +334,9 @@ public:
};
// Stores info relevant to presenting media samples.
class nsOggInfo {
class nsVideoInfo {
public:
nsOggInfo()
nsVideoInfo()
: mFramerate(0.0),
mAspectRatio(1.0),
mCallbackPeriod(1),
@ -339,7 +350,7 @@ public:
// Frames per second.
float mFramerate;
// Aspect ratio, as stored in the video header packet.
// Aspect ratio, as stored in the metadata.
float mAspectRatio;
// Length of a video frame in milliseconds, or the callback period if
@ -369,33 +380,46 @@ public:
PRPackedBool mHasVideo;
};
// Encapsulates the decoding and reading of Ogg data. Reading can be done
// Encapsulates the decoding and reading of media data. Reading can be done
// on either the state machine thread (when loading and seeking) or on
// the reader thread (when it's reading and decoding). The reader encapsulates
// the reading state and maintains it's own monitor to ensure thread safety
// and correctness. Never hold the nsOggDecoder's monitor when calling into
// and correctness. Never hold the nsBuiltinDecoder's monitor when calling into
// this class.
class nsOggReader : public nsRunnable {
class nsBuiltinDecoderReader : public nsRunnable {
public:
nsOggReader(nsOggPlayStateMachine* aStateMachine);
~nsOggReader();
typedef mozilla::Monitor Monitor;
PRBool HasAudio()
{
MonitorAutoEnter mon(mMonitor);
return mVorbisState != 0 && mVorbisState->mActive;
}
nsBuiltinDecoderReader(nsBuiltinDecoder* aDecoder);
~nsBuiltinDecoderReader();
PRBool HasVideo()
{
MonitorAutoEnter mon(mMonitor);
return mTheoraState != 0 && mTheoraState->mActive;
}
// Initializes the reader, returns NS_OK on success, or NS_ERROR_FAILURE
// on failure.
virtual nsresult Init() = 0;
// Read header data for all bitstreams in the Ogg file. Fills aInfo with
// Resets all state related to decoding, emptying all buffers etc.
virtual nsresult ResetDecode();
// Decodes an unspecified amount of audio data, enqueuing the audio data
// in mAudioQueue. Returns PR_TRUE when there's more audio to decode,
// PR_FALSE if the audio is finished, end of file has been reached,
// or an un-recoverable read error has occured.
virtual PRBool DecodeAudioData() = 0;
// Reads and decodes one video frame. Packets with a timestamp less
// than aTimeThreshold will be decoded (unless they're not keyframes
// and aKeyframeSkip is PR_TRUE), but will not be added to the queue.
virtual PRBool DecodeVideoFrame(PRBool &aKeyframeSkip,
PRInt64 aTimeThreshold) = 0;
virtual PRBool HasAudio() = 0;
virtual PRBool HasVideo() = 0;
// Read header data for all bitstreams in the file. Fills aInfo with
// the data required to present the media. Returns NS_OK on success,
// or NS_ERROR_FAILURE on failure.
nsresult ReadOggHeaders(nsOggInfo& aInfo);
virtual nsresult ReadMetadata(nsVideoInfo& aInfo) = 0;
// Stores the presentation time of the first sample in the stream in
// aOutStartTime, and returns the first video sample, if we have video.
@ -404,26 +428,11 @@ public:
// Returns the end time of the last page which occurs before aEndOffset.
// This will not read past aEndOffset. Returns -1 on failure.
PRInt64 FindEndTime(PRInt64 aEndOffset);
// Decodes one Vorbis page, enqueuing the audio data in mAudioQueue.
// Returns PR_TRUE when there's more audio to decode, PR_FALSE if the
// audio is finished, end of file has been reached, or an un-recoverable
// read error has occured.
PRBool DecodeAudioPage();
// Reads and decodes one video frame. If the Theora granulepos has not
// been captured, it may read several packets until one with a granulepos
// has been captured, to ensure that all packets read have valid time info.
// Packets with a timestamp less than aTimeThreshold will be decoded (unless
// they're not keyframes and aKeyframeSkip is PR_TRUE), but will not be
// added to the queue.
PRBool DecodeVideoPage(PRBool &aKeyframeSkip,
PRInt64 aTimeThreshold);
virtual PRInt64 FindEndTime(PRInt64 aEndOffset) = 0;
// Moves the decode head to aTime milliseconds. aStartTime and aEndTime
// denote the start and end times of the media.
nsresult Seek(PRInt64 aTime, PRInt64 aStartTime, PRInt64 aEndTime);
virtual nsresult Seek(PRInt64 aTime, PRInt64 aStartTime, PRInt64 aEndTime) = 0;
// Queue of audio samples. This queue is threadsafe.
MediaQueue<SoundData> mAudioQueue;
@ -431,15 +440,11 @@ public:
// Queue of video samples. This queue is threadsafe.
MediaQueue<VideoData> mVideoQueue;
// Initializes the reader, returns NS_OK on success, or NS_ERROR_FAILURE
// on failure.
nsresult Init();
protected:
private:
// Ogg reader decode function. Matches DecodeVideoPage() and
// DecodeAudioPage().
typedef PRBool (nsOggReader::*DecodeFn)();
// Reader decode function. Matches DecodeVideoFrame() and
// DecodeAudioData().
typedef PRBool (nsBuiltinDecoderReader::*DecodeFn)();
// Calls aDecodeFn on *this until aQueue has a sample, whereupon
// we return the first sample.
@ -447,43 +452,13 @@ private:
Data* DecodeToFirstData(DecodeFn aDecodeFn,
MediaQueue<Data>& aQueue);
// Wrapper so that DecodeVideoPage(PRBool&,PRInt64) can be called from
// Wrapper so that DecodeVideoFrame(PRBool&,PRInt64) can be called from
// DecodeToFirstData().
PRBool DecodeVideoPage() {
PRBool DecodeVideoFrame() {
PRBool f = PR_FALSE;
return DecodeVideoPage(f, 0);
return DecodeVideoFrame(f, 0);
}
// Decodes one packet of Vorbis data, storing the resulting chunks of
// PCM samples in aChunks.
nsresult DecodeVorbis(nsTArray<SoundData*>& aChunks,
ogg_packet* aPacket);
// May return NS_ERROR_OUT_OF_MEMORY.
nsresult DecodeTheora(nsTArray<VideoData*>& aFrames,
ogg_packet* aPacket);
// Resets all state related to decoding, emptying all buffers etc.
nsresult ResetDecode();
// Read a page of data from the Ogg file. Returns the offset of the start
// of the page, or -1 if the page read failed.
PRInt64 ReadOggPage(ogg_page* aPage);
// Read a packet for an Ogg bitstream/codec state. Returns PR_TRUE on
// success, or PR_FALSE if the read failed.
PRBool ReadOggPacket(nsOggCodecState* aCodecState, ogg_packet* aPacket);
// Performs a seek bisection to move the media stream's read cursor to the
// last ogg page boundary which has end time before aTarget ms on both the
// Theora and Vorbis bitstreams. Limits its search to data inside aRange;
// i.e. it will only read inside of the aRange's start and end offsets.
// aFuzz is the number of ms of leniency we'll allow; we'll terminate the
// seek when we land in the range (aTime - aFuzz, aTime) ms.
nsresult SeekBisection(PRInt64 aTarget,
const ByteRange& aRange,
PRUint32 aFuzz);
// Fills aRanges with ByteRanges denoting the sections of the media which
// have been downloaded and are stored in the media cache. The reader
// monitor must must be held with exactly one lock count. The nsMediaStream
@ -508,39 +483,13 @@ private:
// safety of the reader and its data fields.
Monitor mMonitor;
// Reference to the owning player state machine object. Do not hold the
// reader's monitor when accessing the player.
nsOggPlayStateMachine* mPlayer;
// Maps Ogg serialnos to nsOggStreams.
nsClassHashtable<nsUint32HashKey, nsOggCodecState> mCodecStates;
// Decode state of the Theora bitstream we're decoding, if we have video.
nsTheoraState* mTheoraState;
// Decode state of the Vorbis bitstream we're decoding, if we have audio.
nsVorbisState* mVorbisState;
// Ogg decoding state.
ogg_sync_state mOggState;
// The offset of the end of the last page we've read, or the start of
// the page we're about to read.
PRInt64 mPageOffset;
// Reference to the owning decoder object. Do not hold the
// reader's monitor when accessing this.
nsBuiltinDecoder* mDecoder;
// The offset of the start of the first non-header page in the file.
// Used to seek to media start time.
PRInt64 mDataOffset;
// The granulepos of the last decoded Theora frame.
PRInt64 mTheoraGranulepos;
// The granulepos of the last decoded Vorbis sample.
PRInt64 mVorbisGranulepos;
// Number of milliseconds of data video/audio data held in a frame.
PRUint32 mCallbackPeriod;
};
#endif

Просмотреть файл

@ -41,13 +41,13 @@
#include "nsAudioStream.h"
#include "nsTArray.h"
#include "nsBuiltinDecoder.h"
#include "nsOggReader.h"
#include "nsOggPlayStateMachine.h"
#include "nsBuiltinDecoderReader.h"
#include "nsBuiltinDecoderStateMachine.h"
#include "mozilla/mozalloc.h"
#include "VideoUtils.h"
using namespace mozilla;
using namespace mozilla::layers;
using mozilla::MonitorAutoExit;
// Adds two 32bit unsigned numbers, retuns PR_TRUE if addition succeeded,
// or PR_FALSE the if addition would result in an overflow.
@ -72,10 +72,6 @@ extern PRLogModuleInfo* gBuiltinDecoderLog;
#define BUFFERING_MIN_RATE 50000
#define BUFFERING_RATE(x) ((x)< BUFFERING_MIN_RATE ? BUFFERING_MIN_RATE : (x))
// The frame rate to use if there is no video data in the resource to
// be played.
#define AUDIO_FRAME_RATE 25.0
// If audio queue has less than this many ms of decoded audio, we won't risk
// trying to decode the video, we'll skip decoding video up to the next
// keyframe.
@ -96,7 +92,12 @@ static const PRUint32 LOW_AUDIO_MS = 100;
// less than LOW_VIDEO_FRAMES frames.
static const PRUint32 LOW_VIDEO_FRAMES = 1;
nsOggPlayStateMachine::nsOggPlayStateMachine(nsBuiltinDecoder* aDecoder) :
// The frame rate to use if there is no video data in the resource to
// be played.
#define AUDIO_FRAME_RATE 25.0
nsBuiltinDecoderStateMachine::nsBuiltinDecoderStateMachine(nsBuiltinDecoder* aDecoder,
nsBuiltinDecoderReader* aReader) :
mDecoder(aDecoder),
mState(DECODER_STATE_DECODING_METADATA),
mAudioMonitor("media.audiostream"),
@ -106,6 +107,7 @@ nsOggPlayStateMachine::nsOggPlayStateMachine(nsBuiltinDecoder* aDecoder) :
mStartTime(-1),
mEndTime(-1),
mSeekTime(0),
mReader(aReader),
mCurrentFrameTime(0),
mAudioStartTime(-1),
mAudioEndTime(-1),
@ -118,15 +120,15 @@ nsOggPlayStateMachine::nsOggPlayStateMachine(nsBuiltinDecoder* aDecoder) :
mGotDurationFromHeader(PR_FALSE),
mStopDecodeThreads(PR_TRUE)
{
MOZ_COUNT_CTOR(nsOggPlayStateMachine);
MOZ_COUNT_CTOR(nsBuiltinDecoderStateMachine);
}
nsOggPlayStateMachine::~nsOggPlayStateMachine()
nsBuiltinDecoderStateMachine::~nsBuiltinDecoderStateMachine()
{
MOZ_COUNT_DTOR(nsOggPlayStateMachine);
MOZ_COUNT_DTOR(nsBuiltinDecoderStateMachine);
}
void nsOggPlayStateMachine::DecodeLoop()
void nsBuiltinDecoderStateMachine::DecodeLoop()
{
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
PRBool videoPlaying = PR_FALSE;
@ -232,7 +234,7 @@ void nsOggPlayStateMachine::DecodeLoop()
}
if (videoPlaying && !videoWait) {
videoPlaying = mReader->DecodeVideoPage(skipToNextKeyframe, currentTime);
videoPlaying = mReader->DecodeVideoFrame(skipToNextKeyframe, currentTime);
{
MonitorAutoEnter mon(mDecoder->GetMonitor());
if (mDecoder->mDecoderPosition >= initialDownloadPosition) {
@ -248,7 +250,7 @@ void nsOggPlayStateMachine::DecodeLoop()
}
if (audioPlaying && !audioWait) {
audioPlaying = mReader->DecodeAudioPage();
audioPlaying = mReader->DecodeAudioData();
{
MonitorAutoEnter mon(mDecoder->GetMonitor());
if (mDecoder->mDecoderPosition >= initialDownloadPosition) {
@ -300,14 +302,14 @@ void nsOggPlayStateMachine::DecodeLoop()
LOG(PR_LOG_DEBUG, ("Shutting down DecodeLoop this=%p", this));
}
PRBool nsOggPlayStateMachine::IsPlaying()
PRBool nsBuiltinDecoderStateMachine::IsPlaying()
{
mDecoder->GetMonitor().AssertCurrentThreadIn();
return !mPlayStartTime.IsNull();
}
void nsOggPlayStateMachine::AudioLoop()
void nsBuiltinDecoderStateMachine::AudioLoop()
{
NS_ASSERTION(OnAudioThread(), "Should be on audio thread.");
LOG(PR_LOG_DEBUG, ("Begun audio thread/loop"));
@ -404,13 +406,12 @@ void nsOggPlayStateMachine::AudioLoop()
LOG(PR_LOG_DEBUG, ("Audio stream finished playing, audio thread exit"));
}
nsresult nsOggPlayStateMachine::Init()
nsresult nsBuiltinDecoderStateMachine::Init()
{
mReader = new nsOggReader(this);
return mReader->Init();
}
void nsOggPlayStateMachine::StopPlayback(eStopMode aMode)
void nsBuiltinDecoderStateMachine::StopPlayback(eStopMode aMode)
{
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
"Should be on state machine thread.");
@ -439,7 +440,7 @@ void nsOggPlayStateMachine::StopPlayback(eStopMode aMode)
}
}
void nsOggPlayStateMachine::StartPlayback()
void nsBuiltinDecoderStateMachine::StartPlayback()
{
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
"Should be on state machine thread.");
@ -466,7 +467,7 @@ void nsOggPlayStateMachine::StartPlayback()
mDecoder->GetMonitor().NotifyAll();
}
void nsOggPlayStateMachine::UpdatePlaybackPosition(PRInt64 aTime)
void nsBuiltinDecoderStateMachine::UpdatePlaybackPosition(PRInt64 aTime)
{
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
"Should be on state machine thread.");
@ -491,7 +492,7 @@ void nsOggPlayStateMachine::UpdatePlaybackPosition(PRInt64 aTime)
}
}
void nsOggPlayStateMachine::ClearPositionChangeFlag()
void nsBuiltinDecoderStateMachine::ClearPositionChangeFlag()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
mDecoder->GetMonitor().AssertCurrentThreadIn();
@ -499,7 +500,7 @@ void nsOggPlayStateMachine::ClearPositionChangeFlag()
mPositionChangeQueued = PR_FALSE;
}
nsHTMLMediaElement::NextFrameStatus nsOggPlayStateMachine::GetNextFrameStatus()
nsHTMLMediaElement::NextFrameStatus nsBuiltinDecoderStateMachine::GetNextFrameStatus()
{
MonitorAutoEnter mon(mDecoder->GetMonitor());
if (IsBuffering() || IsSeeking()) {
@ -510,7 +511,7 @@ nsHTMLMediaElement::NextFrameStatus nsOggPlayStateMachine::GetNextFrameStatus()
return nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE;
}
void nsOggPlayStateMachine::SetVolume(float volume)
void nsBuiltinDecoderStateMachine::SetVolume(float volume)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
{
@ -525,7 +526,7 @@ void nsOggPlayStateMachine::SetVolume(float volume)
}
}
float nsOggPlayStateMachine::GetCurrentTime()
float nsBuiltinDecoderStateMachine::GetCurrentTime()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
mDecoder->GetMonitor().AssertCurrentThreadIn();
@ -533,7 +534,7 @@ float nsOggPlayStateMachine::GetCurrentTime()
return (float)mCurrentFrameTime / 1000.0;
}
PRInt64 nsOggPlayStateMachine::GetDuration()
PRInt64 nsBuiltinDecoderStateMachine::GetDuration()
{
mDecoder->GetMonitor().AssertCurrentThreadIn();
@ -542,7 +543,7 @@ PRInt64 nsOggPlayStateMachine::GetDuration()
return mEndTime - mStartTime;
}
void nsOggPlayStateMachine::SetDuration(PRInt64 aDuration)
void nsBuiltinDecoderStateMachine::SetDuration(PRInt64 aDuration)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
mDecoder->GetMonitor().AssertCurrentThreadIn();
@ -555,7 +556,7 @@ void nsOggPlayStateMachine::SetDuration(PRInt64 aDuration)
}
}
void nsOggPlayStateMachine::SetSeekable(PRBool aSeekable)
void nsBuiltinDecoderStateMachine::SetSeekable(PRBool aSeekable)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
mDecoder->GetMonitor().AssertCurrentThreadIn();
@ -563,7 +564,7 @@ void nsOggPlayStateMachine::SetSeekable(PRBool aSeekable)
mSeekable = aSeekable;
}
void nsOggPlayStateMachine::Shutdown()
void nsBuiltinDecoderStateMachine::Shutdown()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
@ -577,7 +578,7 @@ void nsOggPlayStateMachine::Shutdown()
mDecoder->GetMonitor().NotifyAll();
}
void nsOggPlayStateMachine::Decode()
void nsBuiltinDecoderStateMachine::Decode()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
// When asked to decode, switch to decoding only if
@ -590,7 +591,7 @@ void nsOggPlayStateMachine::Decode()
}
}
void nsOggPlayStateMachine::ResetPlayback()
void nsBuiltinDecoderStateMachine::ResetPlayback()
{
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
"Should be on state machine thread.");
@ -600,7 +601,7 @@ void nsOggPlayStateMachine::ResetPlayback()
mAudioCompleted = PR_FALSE;
}
void nsOggPlayStateMachine::Seek(float aTime)
void nsBuiltinDecoderStateMachine::Seek(float aTime)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
MonitorAutoEnter mon(mDecoder->GetMonitor());
@ -629,7 +630,7 @@ void nsOggPlayStateMachine::Seek(float aTime)
mState = DECODER_STATE_SEEKING;
}
void nsOggPlayStateMachine::StopDecodeThreads()
void nsBuiltinDecoderStateMachine::StopDecodeThreads()
{
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
"Should be on state machine thread.");
@ -653,7 +654,7 @@ void nsOggPlayStateMachine::StopDecodeThreads()
}
nsresult
nsOggPlayStateMachine::StartDecodeThreads()
nsBuiltinDecoderStateMachine::StartDecodeThreads()
{
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
"Should be on state machine thread.");
@ -666,7 +667,7 @@ nsOggPlayStateMachine::StartDecodeThreads()
return rv;
}
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(this, &nsOggPlayStateMachine::DecodeLoop);
NS_NewRunnableMethod(this, &nsBuiltinDecoderStateMachine::DecodeLoop);
mDecodeThread->Dispatch(event, NS_DISPATCH_NORMAL);
}
if (HasAudio() && !mAudioThread) {
@ -676,13 +677,13 @@ nsOggPlayStateMachine::StartDecodeThreads()
return rv;
}
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(this, &nsOggPlayStateMachine::AudioLoop);
NS_NewRunnableMethod(this, &nsBuiltinDecoderStateMachine::AudioLoop);
mAudioThread->Dispatch(event, NS_DISPATCH_NORMAL);
}
return NS_OK;
}
nsresult nsOggPlayStateMachine::Run()
nsresult nsBuiltinDecoderStateMachine::Run()
{
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
"Should be on state machine thread.");
@ -703,7 +704,7 @@ nsresult nsOggPlayStateMachine::Run()
case DECODER_STATE_DECODING_METADATA:
{
LoadOggHeaders();
LoadMetadata();
if (mState == DECODER_STATE_SHUTDOWN) {
continue;
}
@ -731,7 +732,7 @@ nsresult nsOggPlayStateMachine::Run()
if (mState == DECODER_STATE_SHUTDOWN)
continue;
// Inform the element that we've loaded the Ogg metadata and the
// Inform the element that we've loaded the metadata and the
// first frame.
nsCOMPtr<nsIRunnable> metadataLoadedEvent =
NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::MetadataLoaded);
@ -870,7 +871,7 @@ nsresult nsOggPlayStateMachine::Run()
LOG(PR_LOG_DEBUG, ("Seek completed, mCurrentFrameTime=%lld\n", mCurrentFrameTime));
// Change state to DECODING or COMPLETED now. SeekingStopped will
// call nsOggPlayStateMachine::Seek to reset our state to SEEKING
// call nsBuiltinDecoderStateMachine::Seek to reset our state to SEEKING
// if we need to seek again.
nsCOMPtr<nsIRunnable> stopEvent;
@ -982,7 +983,7 @@ nsresult nsOggPlayStateMachine::Run()
return NS_OK;
}
void nsOggPlayStateMachine::RenderVideoFrame(VideoData* aData)
void nsBuiltinDecoderStateMachine::RenderVideoFrame(VideoData* aData)
{
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
@ -992,9 +993,9 @@ void nsOggPlayStateMachine::RenderVideoFrame(VideoData* aData)
NS_ASSERTION(mInfo.mPicture.width != 0 && mInfo.mPicture.height != 0,
"We can only render non-zero-sized video");
NS_ASSERTION(aData->mBuffer[0].stride >= 0 && aData->mBuffer[0].height >= 0 &&
aData->mBuffer[1].stride >= 0 && aData->mBuffer[1].height >= 0 &&
aData->mBuffer[2].stride >= 0 && aData->mBuffer[2].height >= 0,
NS_ASSERTION(aData->mBuffer.mPlanes[0].mStride >= 0 && aData->mBuffer.mPlanes[0].mHeight >= 0 &&
aData->mBuffer.mPlanes[1].mStride >= 0 && aData->mBuffer.mPlanes[1].mHeight >= 0 &&
aData->mBuffer.mPlanes[2].mStride >= 0 && aData->mBuffer.mPlanes[2].mHeight >= 0,
"YCbCr stride and height must be non-negative");
// Ensure the picture size specified in the headers can be extracted out of
@ -1002,18 +1003,18 @@ void nsOggPlayStateMachine::RenderVideoFrame(VideoData* aData)
PRUint32 picXLimit;
PRUint32 picYLimit;
if (!AddOverflow(mInfo.mPicture.x, mInfo.mPicture.width, picXLimit) ||
picXLimit > PRUint32(PR_ABS(aData->mBuffer[0].stride)) ||
picXLimit > PRUint32(PR_ABS(aData->mBuffer.mPlanes[0].mStride)) ||
!AddOverflow(mInfo.mPicture.y, mInfo.mPicture.height, picYLimit) ||
picYLimit > PRUint32(PR_ABS(aData->mBuffer[0].height)))
picYLimit > PRUint32(PR_ABS(aData->mBuffer.mPlanes[0].mHeight)))
{
// The specified picture dimensions can't be contained inside the video
// frame, we'll stomp memory if we try to copy it. Fail.
return;
}
unsigned ySize = aData->mBuffer[0].stride * aData->mBuffer[0].height;
unsigned cbSize = aData->mBuffer[1].stride * aData->mBuffer[1].height;
unsigned crSize = aData->mBuffer[2].stride * aData->mBuffer[2].height;
unsigned ySize = aData->mBuffer.mPlanes[0].mStride * aData->mBuffer.mPlanes[0].mHeight;
unsigned cbSize = aData->mBuffer.mPlanes[1].mStride * aData->mBuffer.mPlanes[1].mHeight;
unsigned crSize = aData->mBuffer.mPlanes[2].mStride * aData->mBuffer.mPlanes[2].mHeight;
unsigned cbCrSize = ySize + cbSize + crSize;
if (cbCrSize != mCbCrSize) {
@ -1032,12 +1033,12 @@ void nsOggPlayStateMachine::RenderVideoFrame(VideoData* aData)
unsigned char* cb = y + ySize;
unsigned char* cr = cb + cbSize;
memcpy(y, aData->mBuffer[0].data, ySize);
memcpy(cb, aData->mBuffer[1].data, cbSize);
memcpy(cr, aData->mBuffer[2].data, crSize);
memcpy(y, aData->mBuffer.mPlanes[0].mData, ySize);
memcpy(cb, aData->mBuffer.mPlanes[1].mData, cbSize);
memcpy(cr, aData->mBuffer.mPlanes[2].mData, crSize);
ImageContainer* container = mDecoder->GetImageContainer();
// Currently our Ogg decoder only knows how to output to PLANAR_YCBCR
// Currently our decoder only knows how to output to PLANAR_YCBCR
// format.
Image::Format format = Image::PLANAR_YCBCR;
nsRefPtr<Image> image;
@ -1051,11 +1052,11 @@ void nsOggPlayStateMachine::RenderVideoFrame(VideoData* aData)
PlanarYCbCrImage::Data data;
data.mYChannel = y;
data.mYSize = gfxIntSize(mInfo.mFrame.width, mInfo.mFrame.height);
data.mYStride = aData->mBuffer[0].stride;
data.mYStride = aData->mBuffer.mPlanes[0].mStride;
data.mCbChannel = cb;
data.mCrChannel = cr;
data.mCbCrSize = gfxIntSize(aData->mBuffer[1].width, aData->mBuffer[1].height);
data.mCbCrStride = aData->mBuffer[1].stride;
data.mCbCrSize = gfxIntSize(aData->mBuffer.mPlanes[1].mWidth, aData->mBuffer.mPlanes[1].mHeight);
data.mCbCrStride = aData->mBuffer.mPlanes[1].mStride;
data.mPicX = mInfo.mPicture.x;
data.mPicY = mInfo.mPicture.y;
data.mPicSize = gfxIntSize(mInfo.mPicture.width, mInfo.mPicture.height);
@ -1065,7 +1066,7 @@ void nsOggPlayStateMachine::RenderVideoFrame(VideoData* aData)
}
PRInt64
nsOggPlayStateMachine::GetAudioClock()
nsBuiltinDecoderStateMachine::GetAudioClock()
{
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
if (!mAudioStream || !HasAudio())
@ -1074,7 +1075,7 @@ nsOggPlayStateMachine::GetAudioClock()
return (t == -1) ? -1 : t + mAudioStartTime;
}
void nsOggPlayStateMachine::AdvanceFrame()
void nsBuiltinDecoderStateMachine::AdvanceFrame()
{
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
mDecoder->GetMonitor().AssertCurrentThreadIn();
@ -1177,7 +1178,7 @@ void nsOggPlayStateMachine::AdvanceFrame()
}
}
void nsOggPlayStateMachine::Wait(PRUint32 aMs) {
void nsBuiltinDecoderStateMachine::Wait(PRUint32 aMs) {
mDecoder->GetMonitor().AssertCurrentThreadIn();
TimeStamp end = TimeStamp::Now() + TimeDuration::FromMilliseconds(aMs);
TimeStamp now;
@ -1191,63 +1192,12 @@ void nsOggPlayStateMachine::Wait(PRUint32 aMs) {
break;
}
NS_ASSERTION(ms <= aMs && ms > 0,
"nsOggPlayStateMachine::Wait interval very wrong!");
"nsBuiltinDecoderStateMachine::Wait interval very wrong!");
mDecoder->GetMonitor().Wait(PR_MillisecondsToInterval(ms));
}
}
void nsOggPlayStateMachine::LoadOggHeaders()
{
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
"Should be on state machine thread.");
mDecoder->GetMonitor().AssertCurrentThreadIn();
LOG(PR_LOG_DEBUG, ("Loading Ogg Headers"));
nsMediaStream* stream = mDecoder->mStream;
nsOggInfo info;
{
MonitorAutoExit exitMon(mDecoder->GetMonitor());
mReader->ReadOggHeaders(info);
}
mInfo = info;
mDecoder->StartProgressUpdates();
if (!mInfo.mHasVideo && !mInfo.mHasAudio) {
mState = DECODER_STATE_SHUTDOWN;
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::DecodeError);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
return;
}
if (!mInfo.mHasVideo) {
mInfo.mCallbackPeriod = 1000 / AUDIO_FRAME_RATE;
}
LOG(PR_LOG_DEBUG, ("%p Callback Period: %u", mDecoder, mInfo.mCallbackPeriod));
// TODO: Get the duration from Skeleton index, if available.
// Get the duration from the Ogg file. We only do this if the
// content length of the resource is known as we need to seek
// to the end of the file to get the last time field. We also
// only do this if the resource is seekable and if we haven't
// already obtained the duration via an HTTP header.
mGotDurationFromHeader = (GetDuration() != -1);
if (mState != DECODER_STATE_SHUTDOWN &&
stream->GetLength() >= 0 &&
mSeekable &&
mEndTime == -1)
{
mDecoder->StopProgressUpdates();
FindEndTime();
mDecoder->StartProgressUpdates();
mDecoder->UpdatePlaybackRate();
}
}
VideoData* nsOggPlayStateMachine::FindStartTime()
VideoData* nsBuiltinDecoderStateMachine::FindStartTime()
{
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
mDecoder->GetMonitor().AssertCurrentThreadIn();
@ -1273,7 +1223,7 @@ VideoData* nsOggPlayStateMachine::FindStartTime()
return v;
}
void nsOggPlayStateMachine::FindEndTime()
void nsBuiltinDecoderStateMachine::FindEndTime()
{
NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
mDecoder->GetMonitor().AssertCurrentThreadIn();
@ -1303,7 +1253,7 @@ void nsOggPlayStateMachine::FindEndTime()
LOG(PR_LOG_DEBUG, ("%p Media end time is %lldms", mDecoder, mEndTime));
}
void nsOggPlayStateMachine::UpdateReadyState() {
void nsBuiltinDecoderStateMachine::UpdateReadyState() {
mDecoder->GetMonitor().AssertCurrentThreadIn();
nsCOMPtr<nsIRunnable> event;
@ -1333,3 +1283,54 @@ static PRBool AddOverflow(PRUint32 a, PRUint32 b, PRUint32& aResult) {
aResult = static_cast<PRUint32>(rl);
return true;
}
void nsBuiltinDecoderStateMachine::LoadMetadata()
{
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
"Should be on state machine thread.");
mDecoder->GetMonitor().AssertCurrentThreadIn();
LOG(PR_LOG_DEBUG, ("Loading Media Headers"));
nsMediaStream* stream = mDecoder->mStream;
nsVideoInfo info;
{
MonitorAutoExit exitMon(mDecoder->GetMonitor());
mReader->ReadMetadata(info);
}
mInfo = info;
mDecoder->StartProgressUpdates();
if (!mInfo.mHasVideo && !mInfo.mHasAudio) {
mState = DECODER_STATE_SHUTDOWN;
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::DecodeError);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
return;
}
if (!mInfo.mHasVideo) {
mInfo.mCallbackPeriod = 1000 / AUDIO_FRAME_RATE;
}
LOG(PR_LOG_DEBUG, ("%p Callback Period: %u", mDecoder, mInfo.mCallbackPeriod));
// TODO: Get the duration from Skeleton index, if available.
// Get the duration from the media file. We only do this if the
// content length of the resource is known as we need to seek
// to the end of the file to get the last time field. We also
// only do this if the resource is seekable and if we haven't
// already obtained the duration via an HTTP header.
mGotDurationFromHeader = (GetDuration() != -1);
if (mState != DECODER_STATE_SHUTDOWN &&
stream->GetLength() >= 0 &&
mSeekable &&
mEndTime == -1)
{
mDecoder->StopProgressUpdates();
FindEndTime();
mDecoder->StartProgressUpdates();
mDecoder->UpdatePlaybackRate();
}
}

Просмотреть файл

@ -37,7 +37,7 @@
*
* ***** END LICENSE BLOCK ***** */
/*
Each video element for an Ogg file has two additional threads beyond
Each video element for a media file has two additional threads beyond
those needed by nsBuiltinDecoder.
1) The Audio thread writes the decoded audio data to the audio
@ -66,73 +66,6 @@ The decode thread has its own monitor to ensure that its internal
state is independent of the other threads, and to ensure that it's not
hogging the nsBuiltinDecoder monitor while decoding.
The nsOggPlayStateMachine class is the event that gets dispatched to
the state machine thread. It has the following states:
DECODING_METADATA
The Ogg headers are being loaded, and things like framerate, etc are
being determined, and the first frame of audio/video data is being decoded.
DECODING
The decode and audio threads are started and video frames displayed at
the required time.
SEEKING
A seek operation is in progress.
BUFFERING
Decoding is paused while data is buffered for smooth playback.
COMPLETED
The resource has completed decoding, but not finished playback.
SHUTDOWN
The decoder object is about to be destroyed.
The following result in state transitions.
Shutdown()
Clean up any resources the nsOggPlayStateMachine owns.
Decode()
Start decoding video frames.
Buffer
This is not user initiated. It occurs when the
available data in the stream drops below a certain point.
Complete
This is not user initiated. It occurs when the
stream is completely decoded.
Seek(float)
Seek to the time position given in the resource.
A state transition diagram:
DECODING_METADATA
| |
v | Shutdown()
| |
v -->-------------------->--------------------------|
|---------------->----->------------------------| v
DECODING | | | | |
^ v Seek(t) | | | |
| Decode() | v | | |
^-----------<----SEEKING | v Complete v v
| | | | | |
| | | COMPLETED SHUTDOWN-<-|
^ ^ | |Shutdown() |
| | | >-------->-----^
| Decode() |Seek(t) |Buffer() |
-----------<--------<-------BUFFERING |
| ^
v Shutdown() |
| |
------------>-----|
The following represents the states that the nsBuiltinDecoder object
can be in, and the valid states the decode thread can be in at that
time:
player LOADING decoder DECODING_METADATA
player PLAYING decoder DECODING, BUFFERING, SEEKING, COMPLETED
player PAUSED decoder DECODING, BUFFERING, SEEKING, COMPLETED
player SEEKING decoder SEEKING
player COMPLETED decoder SHUTDOWN
player SHUTDOWN decoder SHUTDOWN
a/v synchronisation is handled by the state machine thread. It
examines the audio playback time and compares this to the next frame
in the queue of frames. If it is time to play the video frame it is
@ -176,22 +109,19 @@ hardware (via nsAudioStream and libsydneyaudio).
The decode thread idles if the video queue is empty or if it is
not yet time to display the next frame.
*/
#if !defined(nsOggPlayStateMachine_h__)
#define nsOggPlayStateMachine_h__
#if !defined(nsBuiltinDecoderStateMachine_h__)
#define nsBuiltinDecoderStateMachine_h__
#include "prmem.h"
#include "nsThreadUtils.h"
#include "nsOggReader.h"
#include "nsBuiltinDecoder.h"
#include "nsBuiltinDecoderReader.h"
#include "nsHTMLMediaElement.h"
#include "mozilla/Monitor.h"
using mozilla::TimeDuration;
using mozilla::TimeStamp;
/*
The playback state machine class. This manages the decoding in the
nsOggReader on the decode thread, seeking and in-sync-playback on the
nsBuiltinDecoderReader on the decode thread, seeking and in-sync-playback on the
state machine thread, and controls the audio "push" thread.
All internal state is synchronised via the decoder monitor. NotifyAll
@ -199,31 +129,30 @@ using mozilla::TimeStamp;
by the main thread. The following changes to state cause a notify:
mState and data related to that state changed (mSeekTime, etc)
Ogg Metadata Loaded
Metadata Loaded
First Frame Loaded
Frame decoded
data pushed or popped from the video and audio queues
See nsOggDecoder.h for more details.
See nsBuiltinDecoder.h for more details.
*/
class nsOggPlayStateMachine : public nsDecoderStateMachine
class nsBuiltinDecoderStateMachine : public nsDecoderStateMachine
{
public:
// Enumeration for the valid states
enum State {
DECODER_STATE_DECODING_METADATA,
DECODER_STATE_DECODING,
DECODER_STATE_SEEKING,
DECODER_STATE_BUFFERING,
DECODER_STATE_COMPLETED,
DECODER_STATE_SHUTDOWN
};
typedef mozilla::Monitor Monitor;
typedef mozilla::TimeStamp TimeStamp;
typedef mozilla::TimeDuration TimeDuration;
nsOggPlayStateMachine(nsBuiltinDecoder* aDecoder);
~nsOggPlayStateMachine();
nsBuiltinDecoderStateMachine(nsBuiltinDecoder* aDecoder, nsBuiltinDecoderReader* aReader);
~nsBuiltinDecoderStateMachine();
// nsDecoderStateMachine interface
virtual nsresult Init();
State GetState()
{
mDecoder->GetMonitor().AssertCurrentThreadIn();
return mState;
}
virtual void SetVolume(float aVolume);
virtual void Shutdown();
virtual PRInt64 GetDuration();
@ -238,6 +167,12 @@ public:
virtual float GetCurrentTime();
virtual void ClearPositionChangeFlag();
virtual void SetSeekable(PRBool aSeekable);
virtual void UpdatePlaybackPosition(PRInt64 aTime);
// Load metadata Called on the state machine thread. The decoder monitor must be held with
// exactly one lock count.
virtual void LoadMetadata();
// State machine thread run function. Polls the state, sends frames to be
// displayed at appropriate times, and generally manages the decode.
@ -269,14 +204,14 @@ public:
PRBool IsBuffering() const {
mDecoder->GetMonitor().AssertCurrentThreadIn();
return mState == nsOggPlayStateMachine::DECODER_STATE_BUFFERING;
return mState == nsBuiltinDecoderStateMachine::DECODER_STATE_BUFFERING;
}
// Must be called with the decode monitor held.
PRBool IsSeeking() const {
mDecoder->GetMonitor().AssertCurrentThreadIn();
return mState == nsOggPlayStateMachine::DECODER_STATE_SEEKING;
return mState == nsBuiltinDecoderStateMachine::DECODER_STATE_SEEKING;
}
// Functions used by assertions to ensure we're calling things
@ -297,20 +232,13 @@ public:
// read only on the AV, state machine, audio and main thread.
nsBuiltinDecoder* mDecoder;
// Update the playback position. This can result in a timeupdate event
// and an invalidate of the frame being dispatched asynchronously if
// there is no such event currently queued.
// Only called on the decoder thread. Must be called with
// the decode monitor held.
void UpdatePlaybackPosition(PRInt64 aTime);
// The decoder monitor must be obtained before modifying this state.
// NotifyAll on the monitor must be called when the state is changed by
// the main thread so the decoder thread can wake up.
// Accessed on state machine, audio, main, and AV thread.
State mState;
private:
protected:
// Waits on the decoder Monitor for aMs. If the decoder monitor is awoken
// by a Notify() call, we'll continue waiting, unless we've moved into
@ -337,7 +265,7 @@ private:
// machine thread.
VideoData* FindStartTime();
// Finds the end time of the last page in the Ogg file, storing the value
// Finds the end time of the last frame of data in the file, storing the value
// in mEndTime if successful. The decoder must be held with exactly one lock
// count. Called on the state machine thread.
void FindEndTime();
@ -361,11 +289,6 @@ private:
// one lock count. Called on the state machine thread.
nsresult StartDecodeThreads();
// Reads the Ogg headers using the nsOggReader, and initializes playback.
// Called on the state machine thread. The decoder monitor must be held with
// exactly one lock count.
void LoadOggHeaders();
// The main loop for the audio thread. Sent to the thread as
// an nsRunnableMethod. This continually does blocking writes to
// to audio stream to play audio data.
@ -394,20 +317,12 @@ private:
// be held.
PRBool IsPlaying();
// Stores presentation info about required for playback of the media.
nsOggInfo mInfo;
// Monitor on mAudioStream. This monitor must be held in order to delete
// or use the audio stream. This stops us destroying the audio stream
// while it's being used on another thread (typically when it's being
// written to on the audio thread).
Monitor mAudioMonitor;
// The reader, don't call its methods with the decoder monitor held.
// This is created in the play state machine's constructor, and destroyed
// in the play state machine's destructor.
nsAutoPtr<nsOggReader> mReader;
// The size of the decoded YCbCr frame.
// Accessed on state machine thread.
PRUint32 mCbCrSize;
@ -461,6 +376,14 @@ private:
// monitor when using the audio stream!
nsAutoPtr<nsAudioStream> mAudioStream;
// Stores presentation info about required for playback of the media.
nsVideoInfo mInfo;
// The reader, don't call its methods with the decoder monitor held.
// This is created in the play state machine's constructor, and destroyed
// in the play state machine's destructor.
nsAutoPtr<nsBuiltinDecoderReader> mReader;
// The time of the current frame in milliseconds. This is referenced from
// 0 which is the initial playback position. Set by the state machine
// thread, and read-only from the main thread to get the current

Просмотреть файл

@ -55,7 +55,6 @@ CPPSRCS = \
nsOggDecoder.cpp \
nsOggCodecState.cpp \
nsOggReader.cpp \
nsOggPlayStateMachine.cpp \
$(NULL)
FORCE_STATIC_LIB = 1

Просмотреть файл

@ -37,10 +37,11 @@
*
* ***** END LICENSE BLOCK ***** */
#include "nsOggPlayStateMachine.h"
#include "nsBuiltinDecoderStateMachine.h"
#include "nsOggReader.h"
#include "nsOggDecoder.h"
nsDecoderStateMachine* nsOggDecoder::CreateStateMachine()
{
return new nsOggPlayStateMachine(this);
return new nsBuiltinDecoderStateMachine(this, new nsOggReader(this));
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,137 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla code.
*
* The Initial Developer of the Original Code is the Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Chris Double <chris.double@double.co.nz>
* Chris Pearce <chris@pearce.org.nz>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#if !defined(nsOggReader_h_)
#define nsOggReader_h_
#include <ogg/ogg.h>
#include <theora/theoradec.h>
#include <vorbis/codec.h>
#include "nsBuiltinDecoderReader.h"
#include "nsOggCodecState.h"
class nsMediaDecoder;
class nsOggReader : public nsBuiltinDecoderReader
{
public:
nsOggReader(nsBuiltinDecoder* aDecoder);
~nsOggReader();
virtual nsresult Init();
virtual nsresult ResetDecode();
virtual PRBool DecodeAudioData();
// If the Theora granulepos has not been captured, it may read several packets
// until one with a granulepos has been captured, to ensure that all packets
// read have valid time info.
virtual PRBool DecodeVideoFrame(PRBool &aKeyframeSkip,
PRInt64 aTimeThreshold);
virtual PRInt64 FindEndTime(PRInt64 aEndOffset);
virtual PRBool HasAudio()
{
mozilla::MonitorAutoEnter mon(mMonitor);
return mVorbisState != 0 && mVorbisState->mActive;
}
virtual PRBool HasVideo()
{
mozilla::MonitorAutoEnter mon(mMonitor);
return mTheoraState != 0 && mTheoraState->mActive;
}
virtual nsresult ReadMetadata(nsVideoInfo& aInfo);
virtual nsresult Seek(PRInt64 aTime, PRInt64 aStartTime, PRInt64 aEndTime);
private:
// Decodes one packet of Vorbis data, storing the resulting chunks of
// PCM samples in aChunks.
nsresult DecodeVorbis(nsTArray<SoundData*>& aChunks,
ogg_packet* aPacket);
// May return NS_ERROR_OUT_OF_MEMORY.
nsresult DecodeTheora(nsTArray<VideoData*>& aFrames,
ogg_packet* aPacket);
// Read a page of data from the Ogg file. Returns the offset of the start
// of the page, or -1 if the page read failed.
PRInt64 ReadOggPage(ogg_page* aPage);
// Read a packet for an Ogg bitstream/codec state. Returns PR_TRUE on
// success, or PR_FALSE if the read failed.
PRBool ReadOggPacket(nsOggCodecState* aCodecState, ogg_packet* aPacket);
// Performs a seek bisection to move the media stream's read cursor to the
// last ogg page boundary which has end time before aTarget ms on both the
// Theora and Vorbis bitstreams. Limits its search to data inside aRange;
// i.e. it will only read inside of the aRange's start and end offsets.
// aFuzz is the number of ms of leniency we'll allow; we'll terminate the
// seek when we land in the range (aTime - aFuzz, aTime) ms.
nsresult SeekBisection(PRInt64 aTarget,
const ByteRange& aRange,
PRUint32 aFuzz);
private:
// Maps Ogg serialnos to nsOggStreams.
nsClassHashtable<nsUint32HashKey, nsOggCodecState> mCodecStates;
// Decode state of the Theora bitstream we're decoding, if we have video.
nsTheoraState* mTheoraState;
// Decode state of the Vorbis bitstream we're decoding, if we have audio.
nsVorbisState* mVorbisState;
// Ogg decoding state.
ogg_sync_state mOggState;
// The offset of the end of the last page we've read, or the start of
// the page we're about to read.
PRInt64 mPageOffset;
// Number of milliseconds of data video/audio data held in a frame.
PRUint32 mCallbackPeriod;
// The granulepos of the last decoded Theora frame.
PRInt64 mTheoraGranulepos;
// The granulepos of the last decoded Vorbis sample.
PRInt64 mVorbisGranulepos;
};
#endif