Bug 563825 - Factor out non-Ogg specific parts of nsOggPlayStateMachine.cpp Part 2 - r=cpearce sr=roc
This commit is contained in:
@@ -50,6 +50,8 @@ EXPORTS = \
|
|||||||
nsMediaStream.h \
|
nsMediaStream.h \
|
||||||
nsMediaCache.h \
|
nsMediaCache.h \
|
||||||
nsBuiltinDecoder.h \
|
nsBuiltinDecoder.h \
|
||||||
|
nsBuiltinDecoderStateMachine.h \
|
||||||
|
nsBuiltinDecoderReader.h \
|
||||||
VideoUtils.h \
|
VideoUtils.h \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
@@ -58,6 +60,8 @@ CPPSRCS = \
|
|||||||
nsMediaCache.cpp \
|
nsMediaCache.cpp \
|
||||||
nsMediaStream.cpp \
|
nsMediaStream.cpp \
|
||||||
nsBuiltinDecoder.cpp \
|
nsBuiltinDecoder.cpp \
|
||||||
|
nsBuiltinDecoderStateMachine.cpp \
|
||||||
|
nsBuiltinDecoderReader.cpp \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
ifdef MOZ_SYDNEYAUDIO
|
ifdef MOZ_SYDNEYAUDIO
|
||||||
|
|||||||
@@ -47,9 +47,7 @@
|
|||||||
#include "VideoUtils.h"
|
#include "VideoUtils.h"
|
||||||
#include "nsBuiltinDecoder.h"
|
#include "nsBuiltinDecoder.h"
|
||||||
|
|
||||||
using mozilla::Monitor;
|
using namespace mozilla;
|
||||||
using mozilla::MonitorAutoEnter;
|
|
||||||
using mozilla::MonitorAutoExit;
|
|
||||||
|
|
||||||
#ifdef PR_LOGGING
|
#ifdef PR_LOGGING
|
||||||
PRLogModuleInfo* gBuiltinDecoderLog;
|
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
|
occurs nsBuiltinDecoder then calls the methods on the decoder state
|
||||||
machine object to cause it to behave appropriate to the play 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:
|
The general sequence of events is:
|
||||||
|
|
||||||
1) The video element calls Load on nsMediaDecoder. This creates the
|
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.
|
way the shutdown can occur at a safe time.
|
||||||
|
|
||||||
This means the owning object of a nsBuiltinDecoder object *MUST* call
|
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_)
|
#if !defined(nsBuiltinDecoder_h_)
|
||||||
#define nsBuiltinDecoder_h_
|
#define nsBuiltinDecoder_h_
|
||||||
@@ -158,10 +225,24 @@ static inline PRBool IsCurrentThread(nsIThread* aThread) {
|
|||||||
class nsDecoderStateMachine : public nsRunnable
|
class nsDecoderStateMachine : public nsRunnable
|
||||||
{
|
{
|
||||||
public:
|
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
|
// Initializes the state machine, returns NS_OK on success, or
|
||||||
// NS_ERROR_FAILURE on failure.
|
// NS_ERROR_FAILURE on failure.
|
||||||
virtual nsresult Init() = 0;
|
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
|
// Set the audio volume. The decoder monitor must be obtained before
|
||||||
// calling this.
|
// calling this.
|
||||||
virtual void SetVolume(float aVolume) = 0;
|
virtual void SetVolume(float aVolume) = 0;
|
||||||
@@ -204,6 +285,13 @@ public:
|
|||||||
// Called from the main thread to set whether the media resource can
|
// Called from the main thread to set whether the media resource can
|
||||||
// be seeked. The decoder monitor must be obtained before calling this.
|
// be seeked. The decoder monitor must be obtained before calling this.
|
||||||
virtual void SetSeekable(PRBool aSeekable) = 0;
|
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
|
class nsBuiltinDecoder : public nsMediaDecoder
|
||||||
@@ -215,6 +303,8 @@ class nsBuiltinDecoder : public nsMediaDecoder
|
|||||||
NS_DECL_NSIOBSERVER
|
NS_DECL_NSIOBSERVER
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
typedef mozilla::Monitor Monitor;
|
||||||
|
|
||||||
// Enumeration for the valid play states (see mPlayState)
|
// Enumeration for the valid play states (see mPlayState)
|
||||||
enum PlayState {
|
enum PlayState {
|
||||||
PLAY_STATE_START,
|
PLAY_STATE_START,
|
||||||
@@ -325,7 +415,7 @@ class nsBuiltinDecoder : public nsMediaDecoder
|
|||||||
|
|
||||||
// Returns the monitor for other threads to synchronise access to
|
// Returns the monitor for other threads to synchronise access to
|
||||||
// state.
|
// state.
|
||||||
mozilla::Monitor& GetMonitor() {
|
Monitor& GetMonitor() {
|
||||||
return mMonitor;
|
return mMonitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,6 +442,15 @@ class nsBuiltinDecoder : public nsMediaDecoder
|
|||||||
// The actual playback rate computation. The monitor must be held.
|
// The actual playback rate computation. The monitor must be held.
|
||||||
double ComputePlaybackRate(PRPackedBool* aReliable);
|
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
|
* The following methods must only be called on the main
|
||||||
* thread.
|
* thread.
|
||||||
@@ -362,7 +461,7 @@ class nsBuiltinDecoder : public nsMediaDecoder
|
|||||||
// change. Call on the main thread only.
|
// change. Call on the main thread only.
|
||||||
void ChangeState(PlayState aState);
|
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.
|
// Call on the main thread only.
|
||||||
void MetadataLoaded();
|
void MetadataLoaded();
|
||||||
|
|
||||||
@@ -409,6 +508,13 @@ class nsBuiltinDecoder : public nsMediaDecoder
|
|||||||
// used to calculate the readyState transitions.
|
// used to calculate the readyState transitions.
|
||||||
void UpdatePlaybackOffset(PRInt64 aOffset);
|
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:
|
public:
|
||||||
// Notifies the element that decoding has failed.
|
// Notifies the element that decoding has failed.
|
||||||
void DecodeError();
|
void DecodeError();
|
||||||
@@ -453,7 +559,7 @@ public:
|
|||||||
float mRequestedSeekTime;
|
float mRequestedSeekTime;
|
||||||
|
|
||||||
// Duration of the media resource. Set to -1 if unknown.
|
// 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.
|
// only.
|
||||||
PRInt64 mDuration;
|
PRInt64 mDuration;
|
||||||
|
|
||||||
@@ -478,7 +584,7 @@ public:
|
|||||||
// Monitor for detecting when the video play state changes. A call
|
// Monitor for detecting when the video play state changes. A call
|
||||||
// to Wait on this monitor will block the thread until the next
|
// to Wait on this monitor will block the thread until the next
|
||||||
// state change.
|
// state change.
|
||||||
mozilla::Monitor mMonitor;
|
Monitor mMonitor;
|
||||||
|
|
||||||
// Set to one of the valid play states. It is protected by the
|
// Set to one of the valid play states. It is protected by the
|
||||||
// monitor mMonitor. This monitor must be acquired when reading or
|
// monitor mMonitor. This monitor must be acquired when reading or
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -36,14 +36,10 @@
|
|||||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
*
|
*
|
||||||
* ***** END LICENSE BLOCK ***** */
|
* ***** END LICENSE BLOCK ***** */
|
||||||
#if !defined(nsOggReader_h_)
|
#if !defined(nsBuiltinDecoderReader_h_)
|
||||||
#define nsOggReader_h_
|
#define nsBuiltinDecoderReader_h_
|
||||||
|
|
||||||
#include <nsDeque.h>
|
#include <nsDeque.h>
|
||||||
#include "nsOggCodecState.h"
|
|
||||||
#include <ogg/ogg.h>
|
|
||||||
#include <theora/theoradec.h>
|
|
||||||
#include <vorbis/codec.h>
|
|
||||||
#include "nsAutoLock.h"
|
#include "nsAutoLock.h"
|
||||||
#include "nsClassHashtable.h"
|
#include "nsClassHashtable.h"
|
||||||
#include "mozilla/TimeStamp.h"
|
#include "mozilla/TimeStamp.h"
|
||||||
@@ -51,12 +47,7 @@
|
|||||||
#include "nsRect.h"
|
#include "nsRect.h"
|
||||||
#include "mozilla/Monitor.h"
|
#include "mozilla/Monitor.h"
|
||||||
|
|
||||||
class nsOggPlayStateMachine;
|
class nsBuiltinDecoderStateMachine;
|
||||||
|
|
||||||
using mozilla::Monitor;
|
|
||||||
using mozilla::MonitorAutoEnter;
|
|
||||||
using mozilla::TimeDuration;
|
|
||||||
using mozilla::TimeStamp;
|
|
||||||
|
|
||||||
// Holds chunk a decoded sound samples.
|
// Holds chunk a decoded sound samples.
|
||||||
class SoundData {
|
class SoundData {
|
||||||
@@ -112,34 +103,49 @@ public:
|
|||||||
nsAutoArrayPtr<float> mAudioData;
|
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 {
|
class VideoData {
|
||||||
public:
|
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.
|
// 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
|
// 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,
|
static VideoData* Create(PRInt64 aOffset,
|
||||||
PRInt64 aTime,
|
PRInt64 aTime,
|
||||||
th_ycbcr_buffer aBuffer,
|
const YCbCrBuffer &aBuffer,
|
||||||
PRBool aKeyframe,
|
PRBool aKeyframe,
|
||||||
PRInt64 aGranulepos);
|
PRInt64 aTimecode);
|
||||||
|
|
||||||
// Constructs a duplicate VideoData object. This intrinsically tells the
|
// Constructs a duplicate VideoData object. This intrinsically tells the
|
||||||
// player that it does not need to update the displayed frame when this
|
// player that it does not need to update the displayed frame when this
|
||||||
// frame is played; this frame is identical to the previous.
|
// frame is played; this frame is identical to the previous.
|
||||||
static VideoData* CreateDuplicate(PRInt64 aOffset,
|
static VideoData* CreateDuplicate(PRInt64 aOffset,
|
||||||
PRInt64 aTime,
|
PRInt64 aTime,
|
||||||
PRInt64 aGranulepos)
|
PRInt64 aTimecode)
|
||||||
{
|
{
|
||||||
return new VideoData(aOffset, aTime, aGranulepos);
|
return new VideoData(aOffset, aTime, aTimecode);
|
||||||
}
|
}
|
||||||
|
|
||||||
~VideoData()
|
~VideoData()
|
||||||
{
|
{
|
||||||
MOZ_COUNT_DTOR(VideoData);
|
MOZ_COUNT_DTOR(VideoData);
|
||||||
for (PRUint32 i = 0; i < 3; ++i) {
|
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.
|
// Start time of frame in milliseconds.
|
||||||
PRInt64 mTime;
|
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
|
// When PR_TRUE, denotes that this frame is identical to the frame that
|
||||||
// came before; it's a duplicate. mBuffer will be empty.
|
// came before; it's a duplicate. mBuffer will be empty.
|
||||||
PRPackedBool mDuplicate;
|
PRPackedBool mDuplicate;
|
||||||
PRPackedBool mKeyframe;
|
PRPackedBool mKeyframe;
|
||||||
|
|
||||||
private:
|
public:
|
||||||
VideoData(PRInt64 aOffset, PRInt64 aTime, PRInt64 aGranulepos)
|
VideoData(PRInt64 aOffset, PRInt64 aTime, PRInt64 aTimecode)
|
||||||
: mOffset(aOffset),
|
: mOffset(aOffset),
|
||||||
mTime(aTime),
|
mTime(aTime),
|
||||||
mGranulepos(aGranulepos),
|
mTimecode(aTimecode),
|
||||||
mDuplicate(PR_TRUE),
|
mDuplicate(PR_TRUE),
|
||||||
mKeyframe(PR_FALSE)
|
mKeyframe(PR_FALSE)
|
||||||
{
|
{
|
||||||
MOZ_COUNT_CTOR(VideoData);
|
MOZ_COUNT_CTOR(VideoData);
|
||||||
memset(&mBuffer, 0, sizeof(th_ycbcr_buffer));
|
memset(&mBuffer, 0, sizeof(YCbCrBuffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoData(PRInt64 aOffset,
|
VideoData(PRInt64 aOffset,
|
||||||
PRInt64 aTime,
|
PRInt64 aTime,
|
||||||
PRBool aKeyframe,
|
PRBool aKeyframe,
|
||||||
PRInt64 aGranulepos)
|
PRInt64 aTimecode)
|
||||||
: mOffset(aOffset),
|
: mOffset(aOffset),
|
||||||
mTime(aTime),
|
mTime(aTime),
|
||||||
mGranulepos(aGranulepos),
|
mTimecode(aTimecode),
|
||||||
mDuplicate(PR_FALSE),
|
mDuplicate(PR_FALSE),
|
||||||
mKeyframe(aKeyframe)
|
mKeyframe(aKeyframe)
|
||||||
{
|
{
|
||||||
@@ -195,6 +204,8 @@ class MediaQueueDeallocator : public nsDequeFunctor {
|
|||||||
|
|
||||||
template <class T> class MediaQueue : private nsDeque {
|
template <class T> class MediaQueue : private nsDeque {
|
||||||
public:
|
public:
|
||||||
|
typedef mozilla::MonitorAutoEnter MonitorAutoEnter;
|
||||||
|
typedef mozilla::Monitor Monitor;
|
||||||
|
|
||||||
MediaQueue()
|
MediaQueue()
|
||||||
: nsDeque(new MediaQueueDeallocator<T>()),
|
: nsDeque(new MediaQueueDeallocator<T>()),
|
||||||
@@ -284,8 +295,8 @@ template <class T> class MediaQueue : private nsDeque {
|
|||||||
private:
|
private:
|
||||||
Monitor mMonitor;
|
Monitor mMonitor;
|
||||||
|
|
||||||
// PR_TRUE when we've decoded the last packet in the bitstream for which
|
// PR_TRUE when we've decoded the last frame of data in the
|
||||||
// we're queueing sample-data.
|
// bitstream for which we're queueing sample-data.
|
||||||
PRBool mEndOfStream;
|
PRBool mEndOfStream;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -323,9 +334,9 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Stores info relevant to presenting media samples.
|
// Stores info relevant to presenting media samples.
|
||||||
class nsOggInfo {
|
class nsVideoInfo {
|
||||||
public:
|
public:
|
||||||
nsOggInfo()
|
nsVideoInfo()
|
||||||
: mFramerate(0.0),
|
: mFramerate(0.0),
|
||||||
mAspectRatio(1.0),
|
mAspectRatio(1.0),
|
||||||
mCallbackPeriod(1),
|
mCallbackPeriod(1),
|
||||||
@@ -339,7 +350,7 @@ public:
|
|||||||
// Frames per second.
|
// Frames per second.
|
||||||
float mFramerate;
|
float mFramerate;
|
||||||
|
|
||||||
// Aspect ratio, as stored in the video header packet.
|
// Aspect ratio, as stored in the metadata.
|
||||||
float mAspectRatio;
|
float mAspectRatio;
|
||||||
|
|
||||||
// Length of a video frame in milliseconds, or the callback period if
|
// Length of a video frame in milliseconds, or the callback period if
|
||||||
@@ -369,33 +380,46 @@ public:
|
|||||||
PRPackedBool mHasVideo;
|
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
|
// 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 reader thread (when it's reading and decoding). The reader encapsulates
|
||||||
// the reading state and maintains it's own monitor to ensure thread safety
|
// 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.
|
// this class.
|
||||||
class nsOggReader : public nsRunnable {
|
class nsBuiltinDecoderReader : public nsRunnable {
|
||||||
public:
|
public:
|
||||||
nsOggReader(nsOggPlayStateMachine* aStateMachine);
|
typedef mozilla::Monitor Monitor;
|
||||||
~nsOggReader();
|
|
||||||
|
|
||||||
PRBool HasAudio()
|
nsBuiltinDecoderReader(nsBuiltinDecoder* aDecoder);
|
||||||
{
|
~nsBuiltinDecoderReader();
|
||||||
MonitorAutoEnter mon(mMonitor);
|
|
||||||
return mVorbisState != 0 && mVorbisState->mActive;
|
|
||||||
}
|
|
||||||
|
|
||||||
PRBool HasVideo()
|
// Initializes the reader, returns NS_OK on success, or NS_ERROR_FAILURE
|
||||||
{
|
// on failure.
|
||||||
MonitorAutoEnter mon(mMonitor);
|
virtual nsresult Init() = 0;
|
||||||
return mTheoraState != 0 && mTheoraState->mActive;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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,
|
// the data required to present the media. Returns NS_OK on success,
|
||||||
// or NS_ERROR_FAILURE on failure.
|
// 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
|
// Stores the presentation time of the first sample in the stream in
|
||||||
// aOutStartTime, and returns the first video sample, if we have video.
|
// 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.
|
// Returns the end time of the last page which occurs before aEndOffset.
|
||||||
// This will not read past aEndOffset. Returns -1 on failure.
|
// This will not read past aEndOffset. Returns -1 on failure.
|
||||||
PRInt64 FindEndTime(PRInt64 aEndOffset);
|
virtual PRInt64 FindEndTime(PRInt64 aEndOffset) = 0;
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// Moves the decode head to aTime milliseconds. aStartTime and aEndTime
|
// Moves the decode head to aTime milliseconds. aStartTime and aEndTime
|
||||||
// denote the start and end times of the media.
|
// 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.
|
// Queue of audio samples. This queue is threadsafe.
|
||||||
MediaQueue<SoundData> mAudioQueue;
|
MediaQueue<SoundData> mAudioQueue;
|
||||||
@@ -431,15 +440,11 @@ public:
|
|||||||
// Queue of video samples. This queue is threadsafe.
|
// Queue of video samples. This queue is threadsafe.
|
||||||
MediaQueue<VideoData> mVideoQueue;
|
MediaQueue<VideoData> mVideoQueue;
|
||||||
|
|
||||||
// Initializes the reader, returns NS_OK on success, or NS_ERROR_FAILURE
|
protected:
|
||||||
// on failure.
|
|
||||||
nsresult Init();
|
|
||||||
|
|
||||||
private:
|
// Reader decode function. Matches DecodeVideoFrame() and
|
||||||
|
// DecodeAudioData().
|
||||||
// Ogg reader decode function. Matches DecodeVideoPage() and
|
typedef PRBool (nsBuiltinDecoderReader::*DecodeFn)();
|
||||||
// DecodeAudioPage().
|
|
||||||
typedef PRBool (nsOggReader::*DecodeFn)();
|
|
||||||
|
|
||||||
// Calls aDecodeFn on *this until aQueue has a sample, whereupon
|
// Calls aDecodeFn on *this until aQueue has a sample, whereupon
|
||||||
// we return the first sample.
|
// we return the first sample.
|
||||||
@@ -447,43 +452,13 @@ private:
|
|||||||
Data* DecodeToFirstData(DecodeFn aDecodeFn,
|
Data* DecodeToFirstData(DecodeFn aDecodeFn,
|
||||||
MediaQueue<Data>& aQueue);
|
MediaQueue<Data>& aQueue);
|
||||||
|
|
||||||
// Wrapper so that DecodeVideoPage(PRBool&,PRInt64) can be called from
|
// Wrapper so that DecodeVideoFrame(PRBool&,PRInt64) can be called from
|
||||||
// DecodeToFirstData().
|
// DecodeToFirstData().
|
||||||
PRBool DecodeVideoPage() {
|
PRBool DecodeVideoFrame() {
|
||||||
PRBool f = PR_FALSE;
|
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
|
// Fills aRanges with ByteRanges denoting the sections of the media which
|
||||||
// have been downloaded and are stored in the media cache. The reader
|
// have been downloaded and are stored in the media cache. The reader
|
||||||
// monitor must must be held with exactly one lock count. The nsMediaStream
|
// 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.
|
// safety of the reader and its data fields.
|
||||||
Monitor mMonitor;
|
Monitor mMonitor;
|
||||||
|
|
||||||
// Reference to the owning player state machine object. Do not hold the
|
// Reference to the owning decoder object. Do not hold the
|
||||||
// reader's monitor when accessing the player.
|
// reader's monitor when accessing this.
|
||||||
nsOggPlayStateMachine* mPlayer;
|
nsBuiltinDecoder* mDecoder;
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
// The offset of the start of the first non-header page in the file.
|
// The offset of the start of the first non-header page in the file.
|
||||||
// Used to seek to media start time.
|
// Used to seek to media start time.
|
||||||
PRInt64 mDataOffset;
|
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
|
#endif
|
||||||
|
|||||||
@@ -41,13 +41,13 @@
|
|||||||
#include "nsAudioStream.h"
|
#include "nsAudioStream.h"
|
||||||
#include "nsTArray.h"
|
#include "nsTArray.h"
|
||||||
#include "nsBuiltinDecoder.h"
|
#include "nsBuiltinDecoder.h"
|
||||||
#include "nsOggReader.h"
|
#include "nsBuiltinDecoderReader.h"
|
||||||
#include "nsOggPlayStateMachine.h"
|
#include "nsBuiltinDecoderStateMachine.h"
|
||||||
#include "mozilla/mozalloc.h"
|
#include "mozilla/mozalloc.h"
|
||||||
#include "VideoUtils.h"
|
#include "VideoUtils.h"
|
||||||
|
|
||||||
|
using namespace mozilla;
|
||||||
using namespace mozilla::layers;
|
using namespace mozilla::layers;
|
||||||
using mozilla::MonitorAutoExit;
|
|
||||||
|
|
||||||
// Adds two 32bit unsigned numbers, retuns PR_TRUE if addition succeeded,
|
// Adds two 32bit unsigned numbers, retuns PR_TRUE if addition succeeded,
|
||||||
// or PR_FALSE the if addition would result in an overflow.
|
// 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_MIN_RATE 50000
|
||||||
#define BUFFERING_RATE(x) ((x)< BUFFERING_MIN_RATE ? BUFFERING_MIN_RATE : (x))
|
#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
|
// 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
|
// trying to decode the video, we'll skip decoding video up to the next
|
||||||
// keyframe.
|
// keyframe.
|
||||||
@@ -96,7 +92,12 @@ static const PRUint32 LOW_AUDIO_MS = 100;
|
|||||||
// less than LOW_VIDEO_FRAMES frames.
|
// less than LOW_VIDEO_FRAMES frames.
|
||||||
static const PRUint32 LOW_VIDEO_FRAMES = 1;
|
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),
|
mDecoder(aDecoder),
|
||||||
mState(DECODER_STATE_DECODING_METADATA),
|
mState(DECODER_STATE_DECODING_METADATA),
|
||||||
mAudioMonitor("media.audiostream"),
|
mAudioMonitor("media.audiostream"),
|
||||||
@@ -106,6 +107,7 @@ nsOggPlayStateMachine::nsOggPlayStateMachine(nsBuiltinDecoder* aDecoder) :
|
|||||||
mStartTime(-1),
|
mStartTime(-1),
|
||||||
mEndTime(-1),
|
mEndTime(-1),
|
||||||
mSeekTime(0),
|
mSeekTime(0),
|
||||||
|
mReader(aReader),
|
||||||
mCurrentFrameTime(0),
|
mCurrentFrameTime(0),
|
||||||
mAudioStartTime(-1),
|
mAudioStartTime(-1),
|
||||||
mAudioEndTime(-1),
|
mAudioEndTime(-1),
|
||||||
@@ -118,15 +120,15 @@ nsOggPlayStateMachine::nsOggPlayStateMachine(nsBuiltinDecoder* aDecoder) :
|
|||||||
mGotDurationFromHeader(PR_FALSE),
|
mGotDurationFromHeader(PR_FALSE),
|
||||||
mStopDecodeThreads(PR_TRUE)
|
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.");
|
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
|
||||||
PRBool videoPlaying = PR_FALSE;
|
PRBool videoPlaying = PR_FALSE;
|
||||||
@@ -232,7 +234,7 @@ void nsOggPlayStateMachine::DecodeLoop()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (videoPlaying && !videoWait) {
|
if (videoPlaying && !videoWait) {
|
||||||
videoPlaying = mReader->DecodeVideoPage(skipToNextKeyframe, currentTime);
|
videoPlaying = mReader->DecodeVideoFrame(skipToNextKeyframe, currentTime);
|
||||||
{
|
{
|
||||||
MonitorAutoEnter mon(mDecoder->GetMonitor());
|
MonitorAutoEnter mon(mDecoder->GetMonitor());
|
||||||
if (mDecoder->mDecoderPosition >= initialDownloadPosition) {
|
if (mDecoder->mDecoderPosition >= initialDownloadPosition) {
|
||||||
@@ -248,7 +250,7 @@ void nsOggPlayStateMachine::DecodeLoop()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (audioPlaying && !audioWait) {
|
if (audioPlaying && !audioWait) {
|
||||||
audioPlaying = mReader->DecodeAudioPage();
|
audioPlaying = mReader->DecodeAudioData();
|
||||||
{
|
{
|
||||||
MonitorAutoEnter mon(mDecoder->GetMonitor());
|
MonitorAutoEnter mon(mDecoder->GetMonitor());
|
||||||
if (mDecoder->mDecoderPosition >= initialDownloadPosition) {
|
if (mDecoder->mDecoderPosition >= initialDownloadPosition) {
|
||||||
@@ -300,14 +302,14 @@ void nsOggPlayStateMachine::DecodeLoop()
|
|||||||
LOG(PR_LOG_DEBUG, ("Shutting down DecodeLoop this=%p", this));
|
LOG(PR_LOG_DEBUG, ("Shutting down DecodeLoop this=%p", this));
|
||||||
}
|
}
|
||||||
|
|
||||||
PRBool nsOggPlayStateMachine::IsPlaying()
|
PRBool nsBuiltinDecoderStateMachine::IsPlaying()
|
||||||
{
|
{
|
||||||
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
||||||
|
|
||||||
return !mPlayStartTime.IsNull();
|
return !mPlayStartTime.IsNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsOggPlayStateMachine::AudioLoop()
|
void nsBuiltinDecoderStateMachine::AudioLoop()
|
||||||
{
|
{
|
||||||
NS_ASSERTION(OnAudioThread(), "Should be on audio thread.");
|
NS_ASSERTION(OnAudioThread(), "Should be on audio thread.");
|
||||||
LOG(PR_LOG_DEBUG, ("Begun audio thread/loop"));
|
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"));
|
LOG(PR_LOG_DEBUG, ("Audio stream finished playing, audio thread exit"));
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult nsOggPlayStateMachine::Init()
|
nsresult nsBuiltinDecoderStateMachine::Init()
|
||||||
{
|
{
|
||||||
mReader = new nsOggReader(this);
|
|
||||||
return mReader->Init();
|
return mReader->Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsOggPlayStateMachine::StopPlayback(eStopMode aMode)
|
void nsBuiltinDecoderStateMachine::StopPlayback(eStopMode aMode)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
|
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
|
||||||
"Should be on state machine thread.");
|
"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),
|
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
|
||||||
"Should be on state machine thread.");
|
"Should be on state machine thread.");
|
||||||
@@ -466,7 +467,7 @@ void nsOggPlayStateMachine::StartPlayback()
|
|||||||
mDecoder->GetMonitor().NotifyAll();
|
mDecoder->GetMonitor().NotifyAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsOggPlayStateMachine::UpdatePlaybackPosition(PRInt64 aTime)
|
void nsBuiltinDecoderStateMachine::UpdatePlaybackPosition(PRInt64 aTime)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
|
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
|
||||||
"Should be on state machine thread.");
|
"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.");
|
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||||
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
||||||
@@ -499,7 +500,7 @@ void nsOggPlayStateMachine::ClearPositionChangeFlag()
|
|||||||
mPositionChangeQueued = PR_FALSE;
|
mPositionChangeQueued = PR_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsHTMLMediaElement::NextFrameStatus nsOggPlayStateMachine::GetNextFrameStatus()
|
nsHTMLMediaElement::NextFrameStatus nsBuiltinDecoderStateMachine::GetNextFrameStatus()
|
||||||
{
|
{
|
||||||
MonitorAutoEnter mon(mDecoder->GetMonitor());
|
MonitorAutoEnter mon(mDecoder->GetMonitor());
|
||||||
if (IsBuffering() || IsSeeking()) {
|
if (IsBuffering() || IsSeeking()) {
|
||||||
@@ -510,7 +511,7 @@ nsHTMLMediaElement::NextFrameStatus nsOggPlayStateMachine::GetNextFrameStatus()
|
|||||||
return nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE;
|
return nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsOggPlayStateMachine::SetVolume(float volume)
|
void nsBuiltinDecoderStateMachine::SetVolume(float volume)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
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.");
|
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||||
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
||||||
@@ -533,7 +534,7 @@ float nsOggPlayStateMachine::GetCurrentTime()
|
|||||||
return (float)mCurrentFrameTime / 1000.0;
|
return (float)mCurrentFrameTime / 1000.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
PRInt64 nsOggPlayStateMachine::GetDuration()
|
PRInt64 nsBuiltinDecoderStateMachine::GetDuration()
|
||||||
{
|
{
|
||||||
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
||||||
|
|
||||||
@@ -542,7 +543,7 @@ PRInt64 nsOggPlayStateMachine::GetDuration()
|
|||||||
return mEndTime - mStartTime;
|
return mEndTime - mStartTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsOggPlayStateMachine::SetDuration(PRInt64 aDuration)
|
void nsBuiltinDecoderStateMachine::SetDuration(PRInt64 aDuration)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||||
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
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.");
|
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||||
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
||||||
@@ -563,7 +564,7 @@ void nsOggPlayStateMachine::SetSeekable(PRBool aSeekable)
|
|||||||
mSeekable = aSeekable;
|
mSeekable = aSeekable;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsOggPlayStateMachine::Shutdown()
|
void nsBuiltinDecoderStateMachine::Shutdown()
|
||||||
{
|
{
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||||
|
|
||||||
@@ -577,7 +578,7 @@ void nsOggPlayStateMachine::Shutdown()
|
|||||||
mDecoder->GetMonitor().NotifyAll();
|
mDecoder->GetMonitor().NotifyAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsOggPlayStateMachine::Decode()
|
void nsBuiltinDecoderStateMachine::Decode()
|
||||||
{
|
{
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||||
// When asked to decode, switch to decoding only if
|
// 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),
|
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
|
||||||
"Should be on state machine thread.");
|
"Should be on state machine thread.");
|
||||||
@@ -600,7 +601,7 @@ void nsOggPlayStateMachine::ResetPlayback()
|
|||||||
mAudioCompleted = PR_FALSE;
|
mAudioCompleted = PR_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsOggPlayStateMachine::Seek(float aTime)
|
void nsBuiltinDecoderStateMachine::Seek(float aTime)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||||
MonitorAutoEnter mon(mDecoder->GetMonitor());
|
MonitorAutoEnter mon(mDecoder->GetMonitor());
|
||||||
@@ -629,7 +630,7 @@ void nsOggPlayStateMachine::Seek(float aTime)
|
|||||||
mState = DECODER_STATE_SEEKING;
|
mState = DECODER_STATE_SEEKING;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsOggPlayStateMachine::StopDecodeThreads()
|
void nsBuiltinDecoderStateMachine::StopDecodeThreads()
|
||||||
{
|
{
|
||||||
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
|
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
|
||||||
"Should be on state machine thread.");
|
"Should be on state machine thread.");
|
||||||
@@ -653,7 +654,7 @@ void nsOggPlayStateMachine::StopDecodeThreads()
|
|||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsOggPlayStateMachine::StartDecodeThreads()
|
nsBuiltinDecoderStateMachine::StartDecodeThreads()
|
||||||
{
|
{
|
||||||
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
|
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
|
||||||
"Should be on state machine thread.");
|
"Should be on state machine thread.");
|
||||||
@@ -666,7 +667,7 @@ nsOggPlayStateMachine::StartDecodeThreads()
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
nsCOMPtr<nsIRunnable> event =
|
nsCOMPtr<nsIRunnable> event =
|
||||||
NS_NewRunnableMethod(this, &nsOggPlayStateMachine::DecodeLoop);
|
NS_NewRunnableMethod(this, &nsBuiltinDecoderStateMachine::DecodeLoop);
|
||||||
mDecodeThread->Dispatch(event, NS_DISPATCH_NORMAL);
|
mDecodeThread->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||||
}
|
}
|
||||||
if (HasAudio() && !mAudioThread) {
|
if (HasAudio() && !mAudioThread) {
|
||||||
@@ -676,13 +677,13 @@ nsOggPlayStateMachine::StartDecodeThreads()
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
nsCOMPtr<nsIRunnable> event =
|
nsCOMPtr<nsIRunnable> event =
|
||||||
NS_NewRunnableMethod(this, &nsOggPlayStateMachine::AudioLoop);
|
NS_NewRunnableMethod(this, &nsBuiltinDecoderStateMachine::AudioLoop);
|
||||||
mAudioThread->Dispatch(event, NS_DISPATCH_NORMAL);
|
mAudioThread->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||||
}
|
}
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult nsOggPlayStateMachine::Run()
|
nsresult nsBuiltinDecoderStateMachine::Run()
|
||||||
{
|
{
|
||||||
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
|
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
|
||||||
"Should be on state machine thread.");
|
"Should be on state machine thread.");
|
||||||
@@ -703,7 +704,7 @@ nsresult nsOggPlayStateMachine::Run()
|
|||||||
|
|
||||||
case DECODER_STATE_DECODING_METADATA:
|
case DECODER_STATE_DECODING_METADATA:
|
||||||
{
|
{
|
||||||
LoadOggHeaders();
|
LoadMetadata();
|
||||||
if (mState == DECODER_STATE_SHUTDOWN) {
|
if (mState == DECODER_STATE_SHUTDOWN) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -731,7 +732,7 @@ nsresult nsOggPlayStateMachine::Run()
|
|||||||
if (mState == DECODER_STATE_SHUTDOWN)
|
if (mState == DECODER_STATE_SHUTDOWN)
|
||||||
continue;
|
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.
|
// first frame.
|
||||||
nsCOMPtr<nsIRunnable> metadataLoadedEvent =
|
nsCOMPtr<nsIRunnable> metadataLoadedEvent =
|
||||||
NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::MetadataLoaded);
|
NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::MetadataLoaded);
|
||||||
@@ -870,7 +871,7 @@ nsresult nsOggPlayStateMachine::Run()
|
|||||||
LOG(PR_LOG_DEBUG, ("Seek completed, mCurrentFrameTime=%lld\n", mCurrentFrameTime));
|
LOG(PR_LOG_DEBUG, ("Seek completed, mCurrentFrameTime=%lld\n", mCurrentFrameTime));
|
||||||
|
|
||||||
// Change state to DECODING or COMPLETED now. SeekingStopped will
|
// 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.
|
// if we need to seek again.
|
||||||
|
|
||||||
nsCOMPtr<nsIRunnable> stopEvent;
|
nsCOMPtr<nsIRunnable> stopEvent;
|
||||||
@@ -982,7 +983,7 @@ nsresult nsOggPlayStateMachine::Run()
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsOggPlayStateMachine::RenderVideoFrame(VideoData* aData)
|
void nsBuiltinDecoderStateMachine::RenderVideoFrame(VideoData* aData)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
|
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,
|
NS_ASSERTION(mInfo.mPicture.width != 0 && mInfo.mPicture.height != 0,
|
||||||
"We can only render non-zero-sized video");
|
"We can only render non-zero-sized video");
|
||||||
NS_ASSERTION(aData->mBuffer[0].stride >= 0 && aData->mBuffer[0].height >= 0 &&
|
NS_ASSERTION(aData->mBuffer.mPlanes[0].mStride >= 0 && aData->mBuffer.mPlanes[0].mHeight >= 0 &&
|
||||||
aData->mBuffer[1].stride >= 0 && aData->mBuffer[1].height >= 0 &&
|
aData->mBuffer.mPlanes[1].mStride >= 0 && aData->mBuffer.mPlanes[1].mHeight >= 0 &&
|
||||||
aData->mBuffer[2].stride >= 0 && aData->mBuffer[2].height >= 0,
|
aData->mBuffer.mPlanes[2].mStride >= 0 && aData->mBuffer.mPlanes[2].mHeight >= 0,
|
||||||
"YCbCr stride and height must be non-negative");
|
"YCbCr stride and height must be non-negative");
|
||||||
|
|
||||||
// Ensure the picture size specified in the headers can be extracted out of
|
// 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 picXLimit;
|
||||||
PRUint32 picYLimit;
|
PRUint32 picYLimit;
|
||||||
if (!AddOverflow(mInfo.mPicture.x, mInfo.mPicture.width, picXLimit) ||
|
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) ||
|
!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
|
// The specified picture dimensions can't be contained inside the video
|
||||||
// frame, we'll stomp memory if we try to copy it. Fail.
|
// frame, we'll stomp memory if we try to copy it. Fail.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned ySize = aData->mBuffer[0].stride * aData->mBuffer[0].height;
|
unsigned ySize = aData->mBuffer.mPlanes[0].mStride * aData->mBuffer.mPlanes[0].mHeight;
|
||||||
unsigned cbSize = aData->mBuffer[1].stride * aData->mBuffer[1].height;
|
unsigned cbSize = aData->mBuffer.mPlanes[1].mStride * aData->mBuffer.mPlanes[1].mHeight;
|
||||||
unsigned crSize = aData->mBuffer[2].stride * aData->mBuffer[2].height;
|
unsigned crSize = aData->mBuffer.mPlanes[2].mStride * aData->mBuffer.mPlanes[2].mHeight;
|
||||||
unsigned cbCrSize = ySize + cbSize + crSize;
|
unsigned cbCrSize = ySize + cbSize + crSize;
|
||||||
|
|
||||||
if (cbCrSize != mCbCrSize) {
|
if (cbCrSize != mCbCrSize) {
|
||||||
@@ -1032,12 +1033,12 @@ void nsOggPlayStateMachine::RenderVideoFrame(VideoData* aData)
|
|||||||
unsigned char* cb = y + ySize;
|
unsigned char* cb = y + ySize;
|
||||||
unsigned char* cr = cb + cbSize;
|
unsigned char* cr = cb + cbSize;
|
||||||
|
|
||||||
memcpy(y, aData->mBuffer[0].data, ySize);
|
memcpy(y, aData->mBuffer.mPlanes[0].mData, ySize);
|
||||||
memcpy(cb, aData->mBuffer[1].data, cbSize);
|
memcpy(cb, aData->mBuffer.mPlanes[1].mData, cbSize);
|
||||||
memcpy(cr, aData->mBuffer[2].data, crSize);
|
memcpy(cr, aData->mBuffer.mPlanes[2].mData, crSize);
|
||||||
|
|
||||||
ImageContainer* container = mDecoder->GetImageContainer();
|
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.
|
// format.
|
||||||
Image::Format format = Image::PLANAR_YCBCR;
|
Image::Format format = Image::PLANAR_YCBCR;
|
||||||
nsRefPtr<Image> image;
|
nsRefPtr<Image> image;
|
||||||
@@ -1051,11 +1052,11 @@ void nsOggPlayStateMachine::RenderVideoFrame(VideoData* aData)
|
|||||||
PlanarYCbCrImage::Data data;
|
PlanarYCbCrImage::Data data;
|
||||||
data.mYChannel = y;
|
data.mYChannel = y;
|
||||||
data.mYSize = gfxIntSize(mInfo.mFrame.width, mInfo.mFrame.height);
|
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.mCbChannel = cb;
|
||||||
data.mCrChannel = cr;
|
data.mCrChannel = cr;
|
||||||
data.mCbCrSize = gfxIntSize(aData->mBuffer[1].width, aData->mBuffer[1].height);
|
data.mCbCrSize = gfxIntSize(aData->mBuffer.mPlanes[1].mWidth, aData->mBuffer.mPlanes[1].mHeight);
|
||||||
data.mCbCrStride = aData->mBuffer[1].stride;
|
data.mCbCrStride = aData->mBuffer.mPlanes[1].mStride;
|
||||||
data.mPicX = mInfo.mPicture.x;
|
data.mPicX = mInfo.mPicture.x;
|
||||||
data.mPicY = mInfo.mPicture.y;
|
data.mPicY = mInfo.mPicture.y;
|
||||||
data.mPicSize = gfxIntSize(mInfo.mPicture.width, mInfo.mPicture.height);
|
data.mPicSize = gfxIntSize(mInfo.mPicture.width, mInfo.mPicture.height);
|
||||||
@@ -1065,7 +1066,7 @@ void nsOggPlayStateMachine::RenderVideoFrame(VideoData* aData)
|
|||||||
}
|
}
|
||||||
|
|
||||||
PRInt64
|
PRInt64
|
||||||
nsOggPlayStateMachine::GetAudioClock()
|
nsBuiltinDecoderStateMachine::GetAudioClock()
|
||||||
{
|
{
|
||||||
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
|
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
|
||||||
if (!mAudioStream || !HasAudio())
|
if (!mAudioStream || !HasAudio())
|
||||||
@@ -1074,7 +1075,7 @@ nsOggPlayStateMachine::GetAudioClock()
|
|||||||
return (t == -1) ? -1 : t + mAudioStartTime;
|
return (t == -1) ? -1 : t + mAudioStartTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsOggPlayStateMachine::AdvanceFrame()
|
void nsBuiltinDecoderStateMachine::AdvanceFrame()
|
||||||
{
|
{
|
||||||
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
|
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
|
||||||
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
||||||
@@ -1177,7 +1178,7 @@ void nsOggPlayStateMachine::AdvanceFrame()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsOggPlayStateMachine::Wait(PRUint32 aMs) {
|
void nsBuiltinDecoderStateMachine::Wait(PRUint32 aMs) {
|
||||||
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
||||||
TimeStamp end = TimeStamp::Now() + TimeDuration::FromMilliseconds(aMs);
|
TimeStamp end = TimeStamp::Now() + TimeDuration::FromMilliseconds(aMs);
|
||||||
TimeStamp now;
|
TimeStamp now;
|
||||||
@@ -1191,63 +1192,12 @@ void nsOggPlayStateMachine::Wait(PRUint32 aMs) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
NS_ASSERTION(ms <= aMs && ms > 0,
|
NS_ASSERTION(ms <= aMs && ms > 0,
|
||||||
"nsOggPlayStateMachine::Wait interval very wrong!");
|
"nsBuiltinDecoderStateMachine::Wait interval very wrong!");
|
||||||
mDecoder->GetMonitor().Wait(PR_MillisecondsToInterval(ms));
|
mDecoder->GetMonitor().Wait(PR_MillisecondsToInterval(ms));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsOggPlayStateMachine::LoadOggHeaders()
|
VideoData* nsBuiltinDecoderStateMachine::FindStartTime()
|
||||||
{
|
|
||||||
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()
|
|
||||||
{
|
{
|
||||||
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
|
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
|
||||||
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
||||||
@@ -1273,7 +1223,7 @@ VideoData* nsOggPlayStateMachine::FindStartTime()
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsOggPlayStateMachine::FindEndTime()
|
void nsBuiltinDecoderStateMachine::FindEndTime()
|
||||||
{
|
{
|
||||||
NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
|
NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
|
||||||
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
||||||
@@ -1303,7 +1253,7 @@ void nsOggPlayStateMachine::FindEndTime()
|
|||||||
LOG(PR_LOG_DEBUG, ("%p Media end time is %lldms", mDecoder, mEndTime));
|
LOG(PR_LOG_DEBUG, ("%p Media end time is %lldms", mDecoder, mEndTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsOggPlayStateMachine::UpdateReadyState() {
|
void nsBuiltinDecoderStateMachine::UpdateReadyState() {
|
||||||
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
||||||
|
|
||||||
nsCOMPtr<nsIRunnable> event;
|
nsCOMPtr<nsIRunnable> event;
|
||||||
@@ -1333,3 +1283,54 @@ static PRBool AddOverflow(PRUint32 a, PRUint32 b, PRUint32& aResult) {
|
|||||||
aResult = static_cast<PRUint32>(rl);
|
aResult = static_cast<PRUint32>(rl);
|
||||||
return true;
|
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 ***** */
|
* ***** 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.
|
those needed by nsBuiltinDecoder.
|
||||||
|
|
||||||
1) The Audio thread writes the decoded audio data to the audio
|
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
|
state is independent of the other threads, and to ensure that it's not
|
||||||
hogging the nsBuiltinDecoder monitor while decoding.
|
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
|
a/v synchronisation is handled by the state machine thread. It
|
||||||
examines the audio playback time and compares this to the next frame
|
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
|
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
|
The decode thread idles if the video queue is empty or if it is
|
||||||
not yet time to display the next frame.
|
not yet time to display the next frame.
|
||||||
*/
|
*/
|
||||||
#if !defined(nsOggPlayStateMachine_h__)
|
#if !defined(nsBuiltinDecoderStateMachine_h__)
|
||||||
#define nsOggPlayStateMachine_h__
|
#define nsBuiltinDecoderStateMachine_h__
|
||||||
|
|
||||||
#include "prmem.h"
|
#include "prmem.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
#include "nsOggReader.h"
|
|
||||||
#include "nsBuiltinDecoder.h"
|
#include "nsBuiltinDecoder.h"
|
||||||
|
#include "nsBuiltinDecoderReader.h"
|
||||||
#include "nsHTMLMediaElement.h"
|
#include "nsHTMLMediaElement.h"
|
||||||
#include "mozilla/Monitor.h"
|
#include "mozilla/Monitor.h"
|
||||||
|
|
||||||
using mozilla::TimeDuration;
|
|
||||||
using mozilla::TimeStamp;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The playback state machine class. This manages the decoding in the
|
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.
|
state machine thread, and controls the audio "push" thread.
|
||||||
|
|
||||||
All internal state is synchronised via the decoder monitor. NotifyAll
|
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:
|
by the main thread. The following changes to state cause a notify:
|
||||||
|
|
||||||
mState and data related to that state changed (mSeekTime, etc)
|
mState and data related to that state changed (mSeekTime, etc)
|
||||||
Ogg Metadata Loaded
|
Metadata Loaded
|
||||||
First Frame Loaded
|
First Frame Loaded
|
||||||
Frame decoded
|
Frame decoded
|
||||||
data pushed or popped from the video and audio queues
|
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:
|
public:
|
||||||
// Enumeration for the valid states
|
typedef mozilla::Monitor Monitor;
|
||||||
enum State {
|
typedef mozilla::TimeStamp TimeStamp;
|
||||||
DECODER_STATE_DECODING_METADATA,
|
typedef mozilla::TimeDuration TimeDuration;
|
||||||
DECODER_STATE_DECODING,
|
|
||||||
DECODER_STATE_SEEKING,
|
|
||||||
DECODER_STATE_BUFFERING,
|
|
||||||
DECODER_STATE_COMPLETED,
|
|
||||||
DECODER_STATE_SHUTDOWN
|
|
||||||
};
|
|
||||||
|
|
||||||
nsOggPlayStateMachine(nsBuiltinDecoder* aDecoder);
|
nsBuiltinDecoderStateMachine(nsBuiltinDecoder* aDecoder, nsBuiltinDecoderReader* aReader);
|
||||||
~nsOggPlayStateMachine();
|
~nsBuiltinDecoderStateMachine();
|
||||||
|
|
||||||
// nsDecoderStateMachine interface
|
// nsDecoderStateMachine interface
|
||||||
virtual nsresult Init();
|
virtual nsresult Init();
|
||||||
|
State GetState()
|
||||||
|
{
|
||||||
|
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
||||||
|
return mState;
|
||||||
|
}
|
||||||
virtual void SetVolume(float aVolume);
|
virtual void SetVolume(float aVolume);
|
||||||
virtual void Shutdown();
|
virtual void Shutdown();
|
||||||
virtual PRInt64 GetDuration();
|
virtual PRInt64 GetDuration();
|
||||||
@@ -238,6 +167,12 @@ public:
|
|||||||
virtual float GetCurrentTime();
|
virtual float GetCurrentTime();
|
||||||
virtual void ClearPositionChangeFlag();
|
virtual void ClearPositionChangeFlag();
|
||||||
virtual void SetSeekable(PRBool aSeekable);
|
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
|
// State machine thread run function. Polls the state, sends frames to be
|
||||||
// displayed at appropriate times, and generally manages the decode.
|
// displayed at appropriate times, and generally manages the decode.
|
||||||
@@ -269,14 +204,14 @@ public:
|
|||||||
PRBool IsBuffering() const {
|
PRBool IsBuffering() const {
|
||||||
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
||||||
|
|
||||||
return mState == nsOggPlayStateMachine::DECODER_STATE_BUFFERING;
|
return mState == nsBuiltinDecoderStateMachine::DECODER_STATE_BUFFERING;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must be called with the decode monitor held.
|
// Must be called with the decode monitor held.
|
||||||
PRBool IsSeeking() const {
|
PRBool IsSeeking() const {
|
||||||
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
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
|
// 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.
|
// read only on the AV, state machine, audio and main thread.
|
||||||
nsBuiltinDecoder* mDecoder;
|
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.
|
// The decoder monitor must be obtained before modifying this state.
|
||||||
// NotifyAll on the monitor must be called when the state is changed by
|
// NotifyAll on the monitor must be called when the state is changed by
|
||||||
// the main thread so the decoder thread can wake up.
|
// the main thread so the decoder thread can wake up.
|
||||||
// Accessed on state machine, audio, main, and AV thread.
|
// Accessed on state machine, audio, main, and AV thread.
|
||||||
State mState;
|
State mState;
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
|
|
||||||
// Waits on the decoder Monitor for aMs. If the decoder monitor is awoken
|
// 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
|
// by a Notify() call, we'll continue waiting, unless we've moved into
|
||||||
@@ -337,7 +265,7 @@ private:
|
|||||||
// machine thread.
|
// machine thread.
|
||||||
VideoData* FindStartTime();
|
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
|
// in mEndTime if successful. The decoder must be held with exactly one lock
|
||||||
// count. Called on the state machine thread.
|
// count. Called on the state machine thread.
|
||||||
void FindEndTime();
|
void FindEndTime();
|
||||||
@@ -361,11 +289,6 @@ private:
|
|||||||
// one lock count. Called on the state machine thread.
|
// one lock count. Called on the state machine thread.
|
||||||
nsresult StartDecodeThreads();
|
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
|
// The main loop for the audio thread. Sent to the thread as
|
||||||
// an nsRunnableMethod. This continually does blocking writes to
|
// an nsRunnableMethod. This continually does blocking writes to
|
||||||
// to audio stream to play audio data.
|
// to audio stream to play audio data.
|
||||||
@@ -394,20 +317,12 @@ private:
|
|||||||
// be held.
|
// be held.
|
||||||
PRBool IsPlaying();
|
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
|
// Monitor on mAudioStream. This monitor must be held in order to delete
|
||||||
// or use the audio stream. This stops us destroying the audio stream
|
// 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
|
// while it's being used on another thread (typically when it's being
|
||||||
// written to on the audio thread).
|
// written to on the audio thread).
|
||||||
Monitor mAudioMonitor;
|
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.
|
// The size of the decoded YCbCr frame.
|
||||||
// Accessed on state machine thread.
|
// Accessed on state machine thread.
|
||||||
PRUint32 mCbCrSize;
|
PRUint32 mCbCrSize;
|
||||||
@@ -461,6 +376,14 @@ private:
|
|||||||
// monitor when using the audio stream!
|
// monitor when using the audio stream!
|
||||||
nsAutoPtr<nsAudioStream> mAudioStream;
|
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
|
// The time of the current frame in milliseconds. This is referenced from
|
||||||
// 0 which is the initial playback position. Set by the state machine
|
// 0 which is the initial playback position. Set by the state machine
|
||||||
// thread, and read-only from the main thread to get the current
|
// thread, and read-only from the main thread to get the current
|
||||||
|
|||||||
@@ -55,7 +55,6 @@ CPPSRCS = \
|
|||||||
nsOggDecoder.cpp \
|
nsOggDecoder.cpp \
|
||||||
nsOggCodecState.cpp \
|
nsOggCodecState.cpp \
|
||||||
nsOggReader.cpp \
|
nsOggReader.cpp \
|
||||||
nsOggPlayStateMachine.cpp \
|
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
FORCE_STATIC_LIB = 1
|
FORCE_STATIC_LIB = 1
|
||||||
|
|||||||
@@ -37,10 +37,11 @@
|
|||||||
*
|
*
|
||||||
* ***** END LICENSE BLOCK ***** */
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
#include "nsOggPlayStateMachine.h"
|
#include "nsBuiltinDecoderStateMachine.h"
|
||||||
|
#include "nsOggReader.h"
|
||||||
#include "nsOggDecoder.h"
|
#include "nsOggDecoder.h"
|
||||||
|
|
||||||
nsDecoderStateMachine* nsOggDecoder::CreateStateMachine()
|
nsDecoderStateMachine* nsOggDecoder::CreateStateMachine()
|
||||||
{
|
{
|
||||||
return new nsOggPlayStateMachine(this);
|
return new nsBuiltinDecoderStateMachine(this, new nsOggReader(this));
|
||||||
}
|
}
|
||||||
|
|||||||
1375
content/media/ogg/nsOggReader.cpp
Normal file
1375
content/media/ogg/nsOggReader.cpp
Normal file
File diff suppressed because it is too large
Load Diff
137
content/media/ogg/nsOggReader.h
Normal file
137
content/media/ogg/nsOggReader.h
Normal file
@@ -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
|
||||||
Reference in New Issue
Block a user