1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: ML 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla code.
17 : *
18 : * The Initial Developer of the Original Code is the Mozilla Foundation.
19 : * Portions created by the Initial Developer are Copyright (C) 2010
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Chris Double <chris.double@double.co.nz>
24 : * Chris Pearce <chris@pearce.org.nz>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 : #if !defined(nsBuiltinDecoderReader_h_)
40 : #define nsBuiltinDecoderReader_h_
41 :
42 : #include <nsDeque.h>
43 : #include "ImageLayers.h"
44 : #include "nsSize.h"
45 : #include "mozilla/ReentrantMonitor.h"
46 :
47 : // Stores info relevant to presenting media frames.
48 : class nsVideoInfo {
49 : public:
50 : nsVideoInfo()
51 : : mAudioRate(0),
52 : mAudioChannels(0),
53 : mDisplay(0,0),
54 : mStereoMode(mozilla::layers::STEREO_MODE_MONO),
55 : mHasAudio(false),
56 : mHasVideo(false)
57 : {}
58 :
59 : // Returns true if it's safe to use aPicture as the picture to be
60 : // extracted inside a frame of size aFrame, and scaled up to and displayed
61 : // at a size of aDisplay. You should validate the frame, picture, and
62 : // display regions before using them to display video frames.
63 : static bool ValidateVideoRegion(const nsIntSize& aFrame,
64 : const nsIntRect& aPicture,
65 : const nsIntSize& aDisplay);
66 :
67 : // Sample rate.
68 : PRUint32 mAudioRate;
69 :
70 : // Number of audio channels.
71 : PRUint32 mAudioChannels;
72 :
73 : // Size in pixels at which the video is rendered. This is after it has
74 : // been scaled by its aspect ratio.
75 : nsIntSize mDisplay;
76 :
77 : // Indicates the frame layout for single track stereo videos.
78 : mozilla::layers::StereoMode mStereoMode;
79 :
80 : // True if we have an active audio bitstream.
81 : bool mHasAudio;
82 :
83 : // True if we have an active video bitstream.
84 : bool mHasVideo;
85 : };
86 :
87 : #ifdef MOZ_TREMOR
88 : #include <ogg/os_types.h>
89 : typedef ogg_int32_t VorbisPCMValue;
90 : typedef short AudioDataValue;
91 :
92 : #define MOZ_AUDIO_DATA_FORMAT (nsAudioStream::FORMAT_S16_LE)
93 : #define MOZ_CLIP_TO_15(x) ((x)<-32768?-32768:(x)<=32767?(x):32767)
94 : // Convert the output of vorbis_synthesis_pcmout to a AudioDataValue
95 : #define MOZ_CONVERT_VORBIS_SAMPLE(x) \
96 : (static_cast<AudioDataValue>(MOZ_CLIP_TO_15((x)>>9)))
97 : // Convert a AudioDataValue to a float for the Audio API
98 : #define MOZ_CONVERT_AUDIO_SAMPLE(x) ((x)*(1.F/32768))
99 : #define MOZ_SAMPLE_TYPE_S16LE 1
100 :
101 : #else /*MOZ_VORBIS*/
102 :
103 : typedef float VorbisPCMValue;
104 : typedef float AudioDataValue;
105 :
106 : #define MOZ_AUDIO_DATA_FORMAT (nsAudioStream::FORMAT_FLOAT32)
107 : #define MOZ_CONVERT_VORBIS_SAMPLE(x) (x)
108 : #define MOZ_CONVERT_AUDIO_SAMPLE(x) (x)
109 : #define MOZ_SAMPLE_TYPE_FLOAT32 1
110 :
111 : #endif
112 :
113 : // Holds chunk a decoded audio frames.
114 : class AudioData {
115 : public:
116 0 : AudioData(PRInt64 aOffset,
117 : PRInt64 aTime,
118 : PRInt64 aDuration,
119 : PRUint32 aFrames,
120 : AudioDataValue* aData,
121 : PRUint32 aChannels)
122 : : mOffset(aOffset),
123 : mTime(aTime),
124 : mDuration(aDuration),
125 : mFrames(aFrames),
126 : mChannels(aChannels),
127 0 : mAudioData(aData)
128 : {
129 0 : MOZ_COUNT_CTOR(AudioData);
130 0 : }
131 :
132 : ~AudioData()
133 : {
134 : MOZ_COUNT_DTOR(AudioData);
135 : }
136 :
137 : // Approximate byte offset of the end of the page on which this chunk
138 : // ends.
139 : const PRInt64 mOffset;
140 :
141 : PRInt64 mTime; // Start time of data in usecs.
142 : const PRInt64 mDuration; // In usecs.
143 : const PRUint32 mFrames;
144 : const PRUint32 mChannels;
145 : nsAutoArrayPtr<AudioDataValue> mAudioData;
146 : };
147 :
148 : // Holds a decoded video frame, in YCbCr format. These are queued in the reader.
149 : class VideoData {
150 : public:
151 : typedef mozilla::layers::ImageContainer ImageContainer;
152 : typedef mozilla::layers::Image Image;
153 :
154 : // YCbCr data obtained from decoding the video. The index's are:
155 : // 0 = Y
156 : // 1 = Cb
157 : // 2 = Cr
158 : struct YCbCrBuffer {
159 : struct Plane {
160 : PRUint8* mData;
161 : PRUint32 mWidth;
162 : PRUint32 mHeight;
163 : PRUint32 mStride;
164 : };
165 :
166 : Plane mPlanes[3];
167 : };
168 :
169 : // Constructs a VideoData object. Makes a copy of YCbCr data in aBuffer.
170 : // aTimecode is a codec specific number representing the timestamp of
171 : // the frame of video data. Returns nsnull if an error occurs. This may
172 : // indicate that memory couldn't be allocated to create the VideoData
173 : // object, or it may indicate some problem with the input data (e.g.
174 : // negative stride).
175 : static VideoData* Create(nsVideoInfo& aInfo,
176 : ImageContainer* aContainer,
177 : PRInt64 aOffset,
178 : PRInt64 aTime,
179 : PRInt64 aEndTime,
180 : const YCbCrBuffer &aBuffer,
181 : bool aKeyframe,
182 : PRInt64 aTimecode,
183 : nsIntRect aPicture);
184 :
185 : // Constructs a duplicate VideoData object. This intrinsically tells the
186 : // player that it does not need to update the displayed frame when this
187 : // frame is played; this frame is identical to the previous.
188 0 : static VideoData* CreateDuplicate(PRInt64 aOffset,
189 : PRInt64 aTime,
190 : PRInt64 aEndTime,
191 : PRInt64 aTimecode)
192 : {
193 0 : return new VideoData(aOffset, aTime, aEndTime, aTimecode);
194 : }
195 :
196 : ~VideoData()
197 : {
198 : MOZ_COUNT_DTOR(VideoData);
199 : }
200 :
201 : // Dimensions at which to display the video frame. The picture region
202 : // will be scaled to this size. This is should be the picture region's
203 : // dimensions scaled with respect to its aspect ratio.
204 : nsIntSize mDisplay;
205 :
206 : // Approximate byte offset of the end of the frame in the media.
207 : PRInt64 mOffset;
208 :
209 : // Start time of frame in microseconds.
210 : PRInt64 mTime;
211 :
212 : // End time of frame in microseconds.
213 : PRInt64 mEndTime;
214 :
215 : // Codec specific internal time code. For Ogg based codecs this is the
216 : // granulepos.
217 : PRInt64 mTimecode;
218 :
219 : // This frame's image.
220 : nsRefPtr<Image> mImage;
221 :
222 : // When true, denotes that this frame is identical to the frame that
223 : // came before; it's a duplicate. mBuffer will be empty.
224 : bool mDuplicate;
225 : bool mKeyframe;
226 :
227 : public:
228 0 : VideoData(PRInt64 aOffset, PRInt64 aTime, PRInt64 aEndTime, PRInt64 aTimecode)
229 : : mOffset(aOffset),
230 : mTime(aTime),
231 : mEndTime(aEndTime),
232 : mTimecode(aTimecode),
233 : mDuplicate(true),
234 0 : mKeyframe(false)
235 : {
236 0 : MOZ_COUNT_CTOR(VideoData);
237 0 : NS_ASSERTION(aEndTime >= aTime, "Frame must start before it ends.");
238 0 : }
239 :
240 : VideoData(PRInt64 aOffset,
241 : PRInt64 aTime,
242 : PRInt64 aEndTime,
243 : bool aKeyframe,
244 : PRInt64 aTimecode,
245 : nsIntSize aDisplay)
246 : : mDisplay(aDisplay),
247 : mOffset(aOffset),
248 : mTime(aTime),
249 : mEndTime(aEndTime),
250 : mTimecode(aTimecode),
251 : mDuplicate(false),
252 : mKeyframe(aKeyframe)
253 : {
254 : MOZ_COUNT_CTOR(VideoData);
255 : NS_ASSERTION(aEndTime >= aTime, "Frame must start before it ends.");
256 : }
257 :
258 : };
259 :
260 : // Thread and type safe wrapper around nsDeque.
261 : template <class T>
262 : class MediaQueueDeallocator : public nsDequeFunctor {
263 : virtual void* operator() (void* anObject) {
264 : delete static_cast<T*>(anObject);
265 : return nsnull;
266 : }
267 : };
268 :
269 : template <class T> class MediaQueue : private nsDeque {
270 : public:
271 : typedef mozilla::ReentrantMonitorAutoEnter ReentrantMonitorAutoEnter;
272 : typedef mozilla::ReentrantMonitor ReentrantMonitor;
273 :
274 : MediaQueue()
275 : : nsDeque(new MediaQueueDeallocator<T>()),
276 : mReentrantMonitor("mediaqueue"),
277 : mEndOfStream(0)
278 : {}
279 :
280 : ~MediaQueue() {
281 : Reset();
282 : }
283 :
284 0 : inline PRInt32 GetSize() {
285 0 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
286 0 : return nsDeque::GetSize();
287 : }
288 :
289 0 : inline void Push(T* aItem) {
290 0 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
291 0 : nsDeque::Push(aItem);
292 0 : }
293 :
294 : inline void PushFront(T* aItem) {
295 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
296 : nsDeque::PushFront(aItem);
297 : }
298 :
299 : inline T* Pop() {
300 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
301 : return static_cast<T*>(nsDeque::Pop());
302 : }
303 :
304 : inline T* PopFront() {
305 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
306 : return static_cast<T*>(nsDeque::PopFront());
307 : }
308 :
309 : inline T* Peek() {
310 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
311 : return static_cast<T*>(nsDeque::Peek());
312 : }
313 :
314 0 : inline T* PeekFront() {
315 0 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
316 0 : return static_cast<T*>(nsDeque::PeekFront());
317 : }
318 :
319 : inline void Empty() {
320 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
321 : nsDeque::Empty();
322 : }
323 :
324 : inline void Erase() {
325 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
326 : nsDeque::Erase();
327 : }
328 :
329 : void Reset() {
330 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
331 : while (GetSize() > 0) {
332 : T* x = PopFront();
333 : delete x;
334 : }
335 : mEndOfStream = false;
336 : }
337 :
338 : bool AtEndOfStream() {
339 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
340 : return GetSize() == 0 && mEndOfStream;
341 : }
342 :
343 : // Returns true if the media queue has had it last item added to it.
344 : // This happens when the media stream has been completely decoded. Note this
345 : // does not mean that the corresponding stream has finished playback.
346 : bool IsFinished() {
347 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
348 : return mEndOfStream;
349 : }
350 :
351 : // Informs the media queue that it won't be receiving any more items.
352 0 : void Finish() {
353 0 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
354 0 : mEndOfStream = true;
355 0 : }
356 :
357 : // Returns the approximate number of microseconds of items in the queue.
358 : PRInt64 Duration() {
359 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
360 : if (GetSize() < 2) {
361 : return 0;
362 : }
363 : T* last = Peek();
364 : T* first = PeekFront();
365 : return last->mTime - first->mTime;
366 : }
367 :
368 0 : void LockedForEach(nsDequeFunctor& aFunctor) const {
369 0 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
370 0 : ForEach(aFunctor);
371 0 : }
372 :
373 : private:
374 : mutable ReentrantMonitor mReentrantMonitor;
375 :
376 : // True when we've decoded the last frame of data in the
377 : // bitstream for which we're queueing frame data.
378 : bool mEndOfStream;
379 : };
380 :
381 : // Encapsulates the decoding and reading of media data. Reading can only be
382 : // done on the decode thread. Never hold the decoder monitor when
383 : // calling into this class. Unless otherwise specified, methods and fields of
384 : // this class can only be accessed on the decode thread.
385 : class nsBuiltinDecoderReader : public nsRunnable {
386 : public:
387 : typedef mozilla::ReentrantMonitor ReentrantMonitor;
388 : typedef mozilla::ReentrantMonitorAutoEnter ReentrantMonitorAutoEnter;
389 : typedef mozilla::VideoFrameContainer VideoFrameContainer;
390 :
391 : nsBuiltinDecoderReader(nsBuiltinDecoder* aDecoder);
392 : ~nsBuiltinDecoderReader();
393 :
394 : // Initializes the reader, returns NS_OK on success, or NS_ERROR_FAILURE
395 : // on failure.
396 : virtual nsresult Init(nsBuiltinDecoderReader* aCloneDonor) = 0;
397 :
398 : // Resets all state related to decoding, emptying all buffers etc.
399 : virtual nsresult ResetDecode();
400 :
401 : // Decodes an unspecified amount of audio data, enqueuing the audio data
402 : // in mAudioQueue. Returns true when there's more audio to decode,
403 : // false if the audio is finished, end of file has been reached,
404 : // or an un-recoverable read error has occured.
405 : virtual bool DecodeAudioData() = 0;
406 :
407 : // Reads and decodes one video frame. Packets with a timestamp less
408 : // than aTimeThreshold will be decoded (unless they're not keyframes
409 : // and aKeyframeSkip is true), but will not be added to the queue.
410 : virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
411 : PRInt64 aTimeThreshold) = 0;
412 :
413 : virtual bool HasAudio() = 0;
414 : virtual bool HasVideo() = 0;
415 :
416 : // Read header data for all bitstreams in the file. Fills mInfo with
417 : // the data required to present the media. Returns NS_OK on success,
418 : // or NS_ERROR_FAILURE on failure.
419 : virtual nsresult ReadMetadata(nsVideoInfo* aInfo) = 0;
420 :
421 : // Stores the presentation time of the first frame we'd be able to play if
422 : // we started playback at the current position. Returns the first video
423 : // frame, if we have video.
424 : VideoData* FindStartTime(PRInt64& aOutStartTime);
425 :
426 : // Moves the decode head to aTime microseconds. aStartTime and aEndTime
427 : // denote the start and end times of the media in usecs, and aCurrentTime
428 : // is the current playback position in microseconds.
429 : virtual nsresult Seek(PRInt64 aTime,
430 : PRInt64 aStartTime,
431 : PRInt64 aEndTime,
432 : PRInt64 aCurrentTime) = 0;
433 :
434 : // Queue of audio frames. This queue is threadsafe, and is accessed from
435 : // the audio, decoder, state machine, and main threads.
436 : MediaQueue<AudioData> mAudioQueue;
437 :
438 : // Queue of video frames. This queue is threadsafe, and is accessed from
439 : // the decoder, state machine, and main threads.
440 : MediaQueue<VideoData> mVideoQueue;
441 :
442 : // Populates aBuffered with the time ranges which are buffered. aStartTime
443 : // must be the presentation time of the first frame in the media, e.g.
444 : // the media time corresponding to playback time/position 0. This function
445 : // should only be called on the main thread.
446 : virtual nsresult GetBuffered(nsTimeRanges* aBuffered,
447 : PRInt64 aStartTime) = 0;
448 :
449 : class VideoQueueMemoryFunctor : public nsDequeFunctor {
450 : public:
451 0 : VideoQueueMemoryFunctor() : mResult(0) {}
452 :
453 0 : virtual void* operator()(void* anObject) {
454 0 : const VideoData* v = static_cast<const VideoData*>(anObject);
455 0 : if (!v->mImage) {
456 0 : return nsnull;
457 : }
458 0 : NS_ASSERTION(v->mImage->GetFormat() == mozilla::layers::Image::PLANAR_YCBCR,
459 : "Wrong format?");
460 0 : mozilla::layers::PlanarYCbCrImage* vi = static_cast<mozilla::layers::PlanarYCbCrImage*>(v->mImage.get());
461 :
462 0 : mResult += vi->GetDataSize();
463 0 : return nsnull;
464 : }
465 :
466 : PRInt64 mResult;
467 : };
468 :
469 0 : PRInt64 VideoQueueMemoryInUse() {
470 0 : VideoQueueMemoryFunctor functor;
471 0 : mVideoQueue.LockedForEach(functor);
472 0 : return functor.mResult;
473 : }
474 :
475 : class AudioQueueMemoryFunctor : public nsDequeFunctor {
476 : public:
477 0 : AudioQueueMemoryFunctor() : mResult(0) {}
478 :
479 0 : virtual void* operator()(void* anObject) {
480 0 : const AudioData* audioData = static_cast<const AudioData*>(anObject);
481 0 : mResult += audioData->mFrames * audioData->mChannels * sizeof(AudioDataValue);
482 0 : return nsnull;
483 : }
484 :
485 : PRInt64 mResult;
486 : };
487 :
488 0 : PRInt64 AudioQueueMemoryInUse() {
489 0 : AudioQueueMemoryFunctor functor;
490 0 : mAudioQueue.LockedForEach(functor);
491 0 : return functor.mResult;
492 : }
493 :
494 : // Only used by nsWebMReader for now, so stub here rather than in every
495 : // reader than inherits from nsBuiltinDecoderReader.
496 0 : virtual void NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRInt64 aOffset) {}
497 :
498 : protected:
499 :
500 : // Pumps the decode until we reach frames required to play at time aTarget
501 : // (usecs).
502 : nsresult DecodeToTarget(PRInt64 aTarget);
503 :
504 : // Reader decode function. Matches DecodeVideoFrame() and
505 : // DecodeAudioData().
506 : typedef bool (nsBuiltinDecoderReader::*DecodeFn)();
507 :
508 : // Calls aDecodeFn on *this until aQueue has an item, whereupon
509 : // we return the first item.
510 : template<class Data>
511 : Data* DecodeToFirstData(DecodeFn aDecodeFn,
512 : MediaQueue<Data>& aQueue);
513 :
514 : // Wrapper so that DecodeVideoFrame(bool&,PRInt64) can be called from
515 : // DecodeToFirstData().
516 : bool DecodeVideoFrame() {
517 : bool f = false;
518 : return DecodeVideoFrame(f, 0);
519 : }
520 :
521 : // Reference to the owning decoder object.
522 : nsBuiltinDecoder* mDecoder;
523 :
524 : // Stores presentation info required for playback.
525 : nsVideoInfo mInfo;
526 : };
527 :
528 : #endif
|