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: MPL 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 Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 2007
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(nsOggReader_h_)
40 : #define nsOggReader_h_
41 :
42 : #include <ogg/ogg.h>
43 : #include <theora/theoradec.h>
44 : #ifdef MOZ_TREMOR
45 : #include <tremor/ivorbiscodec.h>
46 : #else
47 : #include <vorbis/codec.h>
48 : #endif
49 : #include "nsBuiltinDecoderReader.h"
50 : #include "nsOggCodecState.h"
51 : #include "VideoUtils.h"
52 :
53 : using namespace mozilla;
54 :
55 : class nsMediaDecoder;
56 : class nsTimeRanges;
57 :
58 : class nsOggReader : public nsBuiltinDecoderReader
59 : {
60 : public:
61 : nsOggReader(nsBuiltinDecoder* aDecoder);
62 : ~nsOggReader();
63 :
64 : virtual nsresult Init(nsBuiltinDecoderReader* aCloneDonor);
65 : virtual nsresult ResetDecode();
66 : virtual bool DecodeAudioData();
67 :
68 : // If the Theora granulepos has not been captured, it may read several packets
69 : // until one with a granulepos has been captured, to ensure that all packets
70 : // read have valid time info.
71 : virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
72 : PRInt64 aTimeThreshold);
73 :
74 0 : virtual bool HasAudio() {
75 0 : return mVorbisState != 0 && mVorbisState->mActive;
76 : }
77 :
78 0 : virtual bool HasVideo() {
79 0 : return mTheoraState != 0 && mTheoraState->mActive;
80 : }
81 :
82 : virtual nsresult ReadMetadata(nsVideoInfo* aInfo);
83 : virtual nsresult Seek(PRInt64 aTime, PRInt64 aStartTime, PRInt64 aEndTime, PRInt64 aCurrentTime);
84 : virtual nsresult GetBuffered(nsTimeRanges* aBuffered, PRInt64 aStartTime);
85 :
86 : private:
87 :
88 0 : bool HasSkeleton() {
89 0 : return mSkeletonState != 0 && mSkeletonState->mActive;
90 : }
91 :
92 : // Seeks to the keyframe preceeding the target time using available
93 : // keyframe indexes.
94 : enum IndexedSeekResult {
95 : SEEK_OK, // Success.
96 : SEEK_INDEX_FAIL, // Failure due to no index, or invalid index.
97 : SEEK_FATAL_ERROR // Error returned by a stream operation.
98 : };
99 : IndexedSeekResult SeekToKeyframeUsingIndex(PRInt64 aTarget);
100 :
101 : // Rolls back a seek-using-index attempt, returning a failure error code.
102 : IndexedSeekResult RollbackIndexedSeek(PRInt64 aOffset);
103 :
104 : // Represents a section of contiguous media, with a start and end offset,
105 : // and the timestamps of the start and end of that range, that is cached.
106 : // Used to denote the extremities of a range in which we can seek quickly
107 : // (because it's cached).
108 0 : class SeekRange {
109 : public:
110 0 : SeekRange()
111 : : mOffsetStart(0),
112 : mOffsetEnd(0),
113 : mTimeStart(0),
114 0 : mTimeEnd(0)
115 0 : {}
116 :
117 0 : SeekRange(PRInt64 aOffsetStart,
118 : PRInt64 aOffsetEnd,
119 : PRInt64 aTimeStart,
120 : PRInt64 aTimeEnd)
121 : : mOffsetStart(aOffsetStart),
122 : mOffsetEnd(aOffsetEnd),
123 : mTimeStart(aTimeStart),
124 0 : mTimeEnd(aTimeEnd)
125 0 : {}
126 :
127 0 : bool IsNull() const {
128 : return mOffsetStart == 0 &&
129 : mOffsetEnd == 0 &&
130 : mTimeStart == 0 &&
131 0 : mTimeEnd == 0;
132 : }
133 :
134 : PRInt64 mOffsetStart, mOffsetEnd; // in bytes.
135 : PRInt64 mTimeStart, mTimeEnd; // in usecs.
136 : };
137 :
138 : // Seeks to aTarget usecs in the buffered range aRange using bisection search,
139 : // or to the keyframe prior to aTarget if we have video. aStartTime must be
140 : // the presentation time at the start of media, and aEndTime the time at
141 : // end of media. aRanges must be the time/byte ranges buffered in the media
142 : // cache as per GetSeekRanges().
143 : nsresult SeekInBufferedRange(PRInt64 aTarget,
144 : PRInt64 aStartTime,
145 : PRInt64 aEndTime,
146 : const nsTArray<SeekRange>& aRanges,
147 : const SeekRange& aRange);
148 :
149 : // Seeks to before aTarget usecs in media using bisection search. If the media
150 : // has video, this will seek to before the keyframe required to render the
151 : // media at aTarget. Will use aRanges in order to narrow the bisection
152 : // search space. aStartTime must be the presentation time at the start of
153 : // media, and aEndTime the time at end of media. aRanges must be the time/byte
154 : // ranges buffered in the media cache as per GetSeekRanges().
155 : nsresult SeekInUnbuffered(PRInt64 aTarget,
156 : PRInt64 aStartTime,
157 : PRInt64 aEndTime,
158 : const nsTArray<SeekRange>& aRanges);
159 :
160 : // Get the end time of aEndOffset. This is the playback position we'd reach
161 : // after playback finished at aEndOffset.
162 : PRInt64 RangeEndTime(PRInt64 aEndOffset);
163 :
164 : // Get the end time of aEndOffset, without reading before aStartOffset.
165 : // This is the playback position we'd reach after playback finished at
166 : // aEndOffset. If bool aCachedDataOnly is true, then we'll only read
167 : // from data which is cached in the media cached, otherwise we'll do
168 : // regular blocking reads from the media stream. If bool aCachedDataOnly
169 : // is true, this can safely be called on the main thread, otherwise it
170 : // must be called on the state machine thread.
171 : PRInt64 RangeEndTime(PRInt64 aStartOffset,
172 : PRInt64 aEndOffset,
173 : bool aCachedDataOnly);
174 :
175 : // Get the start time of the range beginning at aOffset. This is the start
176 : // time of the first frame and or audio sample we'd be able to play if we
177 : // started playback at aOffset.
178 : PRInt64 RangeStartTime(PRInt64 aOffset);
179 :
180 : // Performs a seek bisection to move the media stream's read cursor to the
181 : // last ogg page boundary which has end time before aTarget usecs on both the
182 : // Theora and Vorbis bitstreams. Limits its search to data inside aRange;
183 : // i.e. it will only read inside of the aRange's start and end offsets.
184 : // aFuzz is the number of usecs of leniency we'll allow; we'll terminate the
185 : // seek when we land in the range (aTime - aFuzz, aTime) usecs.
186 : nsresult SeekBisection(PRInt64 aTarget,
187 : const SeekRange& aRange,
188 : PRUint32 aFuzz);
189 :
190 : // Returns true if the serial number is for a stream we encountered
191 : // while reading metadata. Call on the main thread only.
192 : bool IsKnownStream(PRUint32 aSerial);
193 :
194 : // Fills aRanges with SeekRanges denoting the sections of the media which
195 : // have been downloaded and are stored in the media cache. The reader
196 : // monitor must must be held with exactly one lock count. The MediaResource
197 : // must be pinned while calling this.
198 : nsresult GetSeekRanges(nsTArray<SeekRange>& aRanges);
199 :
200 : // Returns the range in which you should perform a seek bisection if
201 : // you wish to seek to aTarget usecs, given the known (buffered) byte ranges
202 : // in aRanges. If aExact is true, we only return an exact copy of a
203 : // range in which aTarget lies, or a null range if aTarget isn't contained
204 : // in any of the (buffered) ranges. Otherwise, when aExact is false,
205 : // we'll construct the smallest possible range we can, based on the times
206 : // and byte offsets known in aRanges. We can then use this to minimize our
207 : // bisection's search space when the target isn't in a known buffered range.
208 : SeekRange SelectSeekRange(const nsTArray<SeekRange>& aRanges,
209 : PRInt64 aTarget,
210 : PRInt64 aStartTime,
211 : PRInt64 aEndTime,
212 : bool aExact);
213 : private:
214 :
215 : // Decodes a packet of Vorbis data, and inserts its samples into the
216 : // audio queue.
217 : nsresult DecodeVorbis(ogg_packet* aPacket);
218 :
219 : // Decodes a packet of Theora data, and inserts its frame into the
220 : // video queue. May return NS_ERROR_OUT_OF_MEMORY. Caller must have obtained
221 : // the reader's monitor. aTimeThreshold is the current playback position
222 : // in media time in microseconds. Frames with an end time before this will
223 : // not be enqueued.
224 : nsresult DecodeTheora(ogg_packet* aPacket, PRInt64 aTimeThreshold);
225 :
226 : // Read a page of data from the Ogg file. Returns the offset of the start
227 : // of the page, or -1 if the page read failed.
228 : PRInt64 ReadOggPage(ogg_page* aPage);
229 :
230 : // Reads and decodes header packets for aState, until either header decode
231 : // fails, or is complete. Initializes the codec state before returning.
232 : // Returns true if reading headers and initializtion of the stream
233 : // succeeds.
234 : bool ReadHeaders(nsOggCodecState* aState);
235 :
236 : // Returns the next Ogg packet for an bitstream/codec state. Returns a
237 : // pointer to an ogg_packet on success, or nsnull if the read failed.
238 : // The caller is responsible for deleting the packet and its |packet| field.
239 : ogg_packet* NextOggPacket(nsOggCodecState* aCodecState);
240 :
241 : // Maps Ogg serialnos to nsOggStreams.
242 : nsClassHashtable<nsUint32HashKey, nsOggCodecState> mCodecStates;
243 :
244 : // Array of serial numbers of streams that were encountered during
245 : // initial metadata load. Written on state machine thread during
246 : // metadata loading and read on the main thread only after metadata
247 : // is loaded.
248 : nsAutoTArray<PRUint32,4> mKnownStreams;
249 :
250 : // Decode state of the Theora bitstream we're decoding, if we have video.
251 : nsTheoraState* mTheoraState;
252 :
253 : // Decode state of the Vorbis bitstream we're decoding, if we have audio.
254 : nsVorbisState* mVorbisState;
255 :
256 : // Decode state of the Skeleton bitstream.
257 : nsSkeletonState* mSkeletonState;
258 :
259 : // Ogg decoding state.
260 : ogg_sync_state mOggState;
261 :
262 : // Vorbis/Theora data used to compute timestamps. This is written on the
263 : // decoder thread and read on the main thread. All reading on the main
264 : // thread must be done after metadataloaded. We can't use the existing
265 : // data in the codec states due to threading issues. You must check the
266 : // associated mTheoraState or mVorbisState pointer is non-null before
267 : // using this codec data.
268 : PRUint32 mVorbisSerial;
269 : PRUint32 mTheoraSerial;
270 : vorbis_info mVorbisInfo;
271 : th_info mTheoraInfo;
272 :
273 : // The offset of the end of the last page we've read, or the start of
274 : // the page we're about to read.
275 : PRInt64 mPageOffset;
276 :
277 : // The picture region inside Theora frame to be displayed, if we have
278 : // a Theora video track.
279 : nsIntRect mPicture;
280 : };
281 :
282 : #endif
|