зеркало из https://github.com/mozilla/pjs.git
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:
Родитель
77250a8eb2
Коммит
9145874c60
|
@ -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
|
Загрузка…
Ссылка в новой задаче