/* -*- 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: ML 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 Foundation. * Portions created by the Initial Developer are Copyright (C) 2010 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Chris Double * Chris Pearce * * 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(nsBuiltinDecoderReader_h_) #define nsBuiltinDecoderReader_h_ #include #include "Layers.h" #include "ImageLayers.h" #include "nsClassHashtable.h" #include "mozilla/TimeStamp.h" #include "nsSize.h" #include "nsRect.h" #include "mozilla/ReentrantMonitor.h" class nsBuiltinDecoderStateMachine; // Stores info relevant to presenting media samples. class nsVideoInfo { public: nsVideoInfo() : mAudioRate(0), mAudioChannels(0), mDisplay(0,0), mStereoMode(mozilla::layers::STEREO_MODE_MONO), mHasAudio(PR_FALSE), mHasVideo(PR_FALSE) {} // Returns PR_TRUE if it's safe to use aPicture as the picture to be // extracted inside a frame of size aFrame, and scaled up to and displayed // at a size of aDisplay. You should validate the frame, picture, and // display regions before using them to display video frames. static PRBool ValidateVideoRegion(const nsIntSize& aFrame, const nsIntRect& aPicture, const nsIntSize& aDisplay); // Samples per second. PRUint32 mAudioRate; // Number of audio channels. PRUint32 mAudioChannels; // Size in pixels at which the video is rendered. This is after it has // been scaled by its aspect ratio. nsIntSize mDisplay; // Indicates the frame layout for single track stereo videos. mozilla::layers::StereoMode mStereoMode; // PR_TRUE if we have an active audio bitstream. PRPackedBool mHasAudio; // PR_TRUE if we have an active video bitstream. PRPackedBool mHasVideo; }; #ifdef MOZ_TREMOR #include typedef ogg_int32_t VorbisPCMValue; typedef short SoundDataValue; #define MOZ_SOUND_DATA_FORMAT (nsAudioStream::FORMAT_S16_LE) #define MOZ_CLIP_TO_15(x) ((x)<-32768?-32768:(x)<=32767?(x):32767) // Convert the output of vorbis_synthesis_pcmout to a SoundDataValue #define MOZ_CONVERT_VORBIS_SAMPLE(x) \ (static_cast(MOZ_CLIP_TO_15((x)>>9))) // Convert a SoundDataValue to a float for the Audio API #define MOZ_CONVERT_SOUND_SAMPLE(x) ((x)*(1.F/32768)) #define MOZ_SAMPLE_TYPE_S16LE 1 #else /*MOZ_VORBIS*/ typedef float VorbisPCMValue; typedef float SoundDataValue; #define MOZ_SOUND_DATA_FORMAT (nsAudioStream::FORMAT_FLOAT32) #define MOZ_CONVERT_VORBIS_SAMPLE(x) (x) #define MOZ_CONVERT_SOUND_SAMPLE(x) (x) #define MOZ_SAMPLE_TYPE_FLOAT32 1 #endif // Holds chunk a decoded sound samples. class SoundData { public: SoundData(PRInt64 aOffset, PRInt64 aTime, PRInt64 aDuration, PRUint32 aSamples, SoundDataValue* aData, PRUint32 aChannels) : mOffset(aOffset), mTime(aTime), mDuration(aDuration), mSamples(aSamples), mChannels(aChannels), mAudioData(aData) { MOZ_COUNT_CTOR(SoundData); } SoundData(PRInt64 aOffset, PRInt64 aDuration, PRUint32 aSamples, SoundDataValue* aData, PRUint32 aChannels) : mOffset(aOffset), mTime(-1), mDuration(aDuration), mSamples(aSamples), mChannels(aChannels), mAudioData(aData) { MOZ_COUNT_CTOR(SoundData); } ~SoundData() { MOZ_COUNT_DTOR(SoundData); } PRUint32 AudioDataLength() { return mChannels * mSamples; } // Approximate byte offset of the end of the page on which this sample // chunk ends. const PRInt64 mOffset; PRInt64 mTime; // Start time of samples in usecs. const PRInt64 mDuration; // In usecs. const PRUint32 mSamples; const PRUint32 mChannels; nsAutoArrayPtr mAudioData; }; // Holds a decoded video frame, in YCbCr format. These are queued in the reader. class VideoData { public: typedef mozilla::layers::ImageContainer ImageContainer; typedef mozilla::layers::Image Image; // 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. // aTimecode is a codec specific number representing the timestamp of // the frame of video data. Returns nsnull if an error occurs. This may // indicate that memory couldn't be allocated to create the VideoData // object, or it may indicate some problem with the input data (e.g. // negative stride). static VideoData* Create(nsVideoInfo& aInfo, ImageContainer* aContainer, PRInt64 aOffset, PRInt64 aTime, PRInt64 aEndTime, const YCbCrBuffer &aBuffer, PRBool aKeyframe, PRInt64 aTimecode, nsIntRect aPicture); // 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 aEndTime, PRInt64 aTimecode) { return new VideoData(aOffset, aTime, aEndTime, aTimecode); } ~VideoData() { MOZ_COUNT_DTOR(VideoData); } // Dimensions at which to display the video frame. The picture region // will be scaled to this size. This is should be the picture region's // dimensions scaled with respect to its aspect ratio. nsIntSize mDisplay; // Approximate byte offset of the end of the frame in the media. PRInt64 mOffset; // Start time of frame in microseconds. PRInt64 mTime; // End time of frame in microseconds. PRInt64 mEndTime; // Codec specific internal time code. For Ogg based codecs this is the // granulepos. PRInt64 mTimecode; // This frame's image. nsRefPtr mImage; // 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; public: VideoData(PRInt64 aOffset, PRInt64 aTime, PRInt64 aEndTime, PRInt64 aTimecode) : mOffset(aOffset), mTime(aTime), mEndTime(aEndTime), mTimecode(aTimecode), mDuplicate(PR_TRUE), mKeyframe(PR_FALSE) { MOZ_COUNT_CTOR(VideoData); NS_ASSERTION(aEndTime >= aTime, "Frame must start before it ends."); } VideoData(PRInt64 aOffset, PRInt64 aTime, PRInt64 aEndTime, PRBool aKeyframe, PRInt64 aTimecode, nsIntSize aDisplay) : mDisplay(aDisplay), mOffset(aOffset), mTime(aTime), mEndTime(aEndTime), mTimecode(aTimecode), mDuplicate(PR_FALSE), mKeyframe(aKeyframe) { MOZ_COUNT_CTOR(VideoData); NS_ASSERTION(aEndTime >= aTime, "Frame must start before it ends."); } }; // Thread and type safe wrapper around nsDeque. template class MediaQueueDeallocator : public nsDequeFunctor { virtual void* operator() (void* anObject) { delete static_cast(anObject); return nsnull; } }; template class MediaQueue : private nsDeque { public: typedef mozilla::ReentrantMonitorAutoEnter ReentrantMonitorAutoEnter; typedef mozilla::ReentrantMonitor ReentrantMonitor; MediaQueue() : nsDeque(new MediaQueueDeallocator()), mReentrantMonitor("mediaqueue"), mEndOfStream(0) {} ~MediaQueue() { Reset(); } inline PRInt32 GetSize() { ReentrantMonitorAutoEnter mon(mReentrantMonitor); return nsDeque::GetSize(); } inline void Push(T* aItem) { ReentrantMonitorAutoEnter mon(mReentrantMonitor); nsDeque::Push(aItem); } inline void PushFront(T* aItem) { ReentrantMonitorAutoEnter mon(mReentrantMonitor); nsDeque::PushFront(aItem); } inline T* Pop() { ReentrantMonitorAutoEnter mon(mReentrantMonitor); return static_cast(nsDeque::Pop()); } inline T* PopFront() { ReentrantMonitorAutoEnter mon(mReentrantMonitor); return static_cast(nsDeque::PopFront()); } inline T* Peek() { ReentrantMonitorAutoEnter mon(mReentrantMonitor); return static_cast(nsDeque::Peek()); } inline T* PeekFront() { ReentrantMonitorAutoEnter mon(mReentrantMonitor); return static_cast(nsDeque::PeekFront()); } inline void Empty() { ReentrantMonitorAutoEnter mon(mReentrantMonitor); nsDeque::Empty(); } inline void Erase() { ReentrantMonitorAutoEnter mon(mReentrantMonitor); nsDeque::Erase(); } void Reset() { ReentrantMonitorAutoEnter mon(mReentrantMonitor); while (GetSize() > 0) { T* x = PopFront(); delete x; } mEndOfStream = PR_FALSE; } PRBool AtEndOfStream() { ReentrantMonitorAutoEnter mon(mReentrantMonitor); return GetSize() == 0 && mEndOfStream; } // Returns PR_TRUE if the media queue has had it last sample added to it. // This happens when the media stream has been completely decoded. Note this // does not mean that the corresponding stream has finished playback. PRBool IsFinished() { ReentrantMonitorAutoEnter mon(mReentrantMonitor); return mEndOfStream; } // Informs the media queue that it won't be receiving any more samples. void Finish() { ReentrantMonitorAutoEnter mon(mReentrantMonitor); mEndOfStream = PR_TRUE; } // Returns the approximate number of microseconds of samples in the queue. PRInt64 Duration() { ReentrantMonitorAutoEnter mon(mReentrantMonitor); if (GetSize() < 2) { return 0; } T* last = Peek(); T* first = PeekFront(); return last->mTime - first->mTime; } private: ReentrantMonitor mReentrantMonitor; // PR_TRUE when we've decoded the last frame of data in the // bitstream for which we're queueing sample-data. PRBool mEndOfStream; }; // Encapsulates the decoding and reading of media data. Reading can only be // done on the decode thread thread. Never hold the decoder monitor when // calling into this class. Unless otherwise specified, methods and fields of // this class can only be accessed on the decode thread. class nsBuiltinDecoderReader : public nsRunnable { public: typedef mozilla::ReentrantMonitor ReentrantMonitor; typedef mozilla::ReentrantMonitorAutoEnter ReentrantMonitorAutoEnter; nsBuiltinDecoderReader(nsBuiltinDecoder* aDecoder); ~nsBuiltinDecoderReader(); // Initializes the reader, returns NS_OK on success, or NS_ERROR_FAILURE // on failure. virtual nsresult Init(nsBuiltinDecoderReader* aCloneDonor) = 0; // 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 mInfo with // the data required to present the media. Returns NS_OK on success, // or NS_ERROR_FAILURE on failure. virtual nsresult ReadMetadata(nsVideoInfo* aInfo) = 0; // Stores the presentation time of the first frame/sample we'd be // able to play if we started playback at the current position. Returns // the first video sample, if we have video. VideoData* FindStartTime(PRInt64& aOutStartTime); // Moves the decode head to aTime microseconds. aStartTime and aEndTime // denote the start and end times of the media in usecs, and aCurrentTime // is the current playback position in microseconds. virtual nsresult Seek(PRInt64 aTime, PRInt64 aStartTime, PRInt64 aEndTime, PRInt64 aCurrentTime) = 0; // Queue of audio samples. This queue is threadsafe, and is accessed from // the audio, decoder, state machine, and main threads. MediaQueue mAudioQueue; // Queue of video samples. This queue is threadsafe, and is accessed from // the decoder, state machine, and main threads. MediaQueue mVideoQueue; // Populates aBuffered with the time ranges which are buffered. aStartTime // must be the presentation time of the first sample/frame in the media, e.g. // the media time corresponding to playback time/position 0. This function // should only be called on the main thread. virtual nsresult GetBuffered(nsTimeRanges* aBuffered, PRInt64 aStartTime) = 0; // Only used by nsWebMReader for now, so stub here rather than in every // reader than inherits from nsBuiltinDecoderReader. virtual void NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRUint32 aOffset) {} protected: // Pumps the decode until we reach frames/samples required to play at // time aTarget (usecs). nsresult DecodeToTarget(PRInt64 aTarget); // 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. template Data* DecodeToFirstData(DecodeFn aDecodeFn, MediaQueue& aQueue); // Wrapper so that DecodeVideoFrame(PRBool&,PRInt64) can be called from // DecodeToFirstData(). PRBool DecodeVideoFrame() { PRBool f = PR_FALSE; return DecodeVideoFrame(f, 0); } // Reference to the owning decoder object. nsBuiltinDecoder* mDecoder; // Stores presentation info required for playback. nsVideoInfo mInfo; }; #endif