|
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 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 #if !defined(OggReader_h_) |
|
7 #define OggReader_h_ |
|
8 |
|
9 #include <ogg/ogg.h> |
|
10 #include <theora/theoradec.h> |
|
11 #ifdef MOZ_TREMOR |
|
12 #include <tremor/ivorbiscodec.h> |
|
13 #else |
|
14 #include <vorbis/codec.h> |
|
15 #endif |
|
16 #include "MediaDecoderReader.h" |
|
17 #include "OggCodecState.h" |
|
18 #include "VideoUtils.h" |
|
19 #include "mozilla/Monitor.h" |
|
20 |
|
21 namespace mozilla { |
|
22 namespace dom { |
|
23 class TimeRanges; |
|
24 } |
|
25 } |
|
26 |
|
27 namespace mozilla { |
|
28 |
|
29 // Thread safe container to store the codec information and the serial for each |
|
30 // streams. |
|
31 class OggCodecStore |
|
32 { |
|
33 public: |
|
34 OggCodecStore(); |
|
35 void Add(uint32_t serial, OggCodecState* codecState); |
|
36 bool Contains(uint32_t serial); |
|
37 OggCodecState* Get(uint32_t serial); |
|
38 bool IsKnownStream(uint32_t aSerial); |
|
39 |
|
40 private: |
|
41 // Maps Ogg serialnos to OggStreams. |
|
42 nsClassHashtable<nsUint32HashKey, OggCodecState> mCodecStates; |
|
43 |
|
44 // Protects the |mCodecStates| and the |mKnownStreams| members. |
|
45 Monitor mMonitor; |
|
46 }; |
|
47 |
|
48 class OggReader : public MediaDecoderReader |
|
49 { |
|
50 public: |
|
51 OggReader(AbstractMediaDecoder* aDecoder); |
|
52 ~OggReader(); |
|
53 |
|
54 virtual nsresult Init(MediaDecoderReader* aCloneDonor); |
|
55 virtual nsresult ResetDecode(); |
|
56 virtual bool DecodeAudioData(); |
|
57 |
|
58 // If the Theora granulepos has not been captured, it may read several packets |
|
59 // until one with a granulepos has been captured, to ensure that all packets |
|
60 // read have valid time info. |
|
61 virtual bool DecodeVideoFrame(bool &aKeyframeSkip, |
|
62 int64_t aTimeThreshold); |
|
63 |
|
64 virtual bool HasAudio() { |
|
65 return (mVorbisState != 0 && mVorbisState->mActive) |
|
66 #ifdef MOZ_OPUS |
|
67 || (mOpusState != 0 && mOpusState->mActive) |
|
68 #endif /* MOZ_OPUS */ |
|
69 ; |
|
70 } |
|
71 |
|
72 virtual bool HasVideo() { |
|
73 return mTheoraState != 0 && mTheoraState->mActive; |
|
74 } |
|
75 |
|
76 virtual nsresult ReadMetadata(MediaInfo* aInfo, |
|
77 MetadataTags** aTags); |
|
78 virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime); |
|
79 virtual nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime); |
|
80 |
|
81 private: |
|
82 // This monitor should be taken when reading or writing to mIsChained. |
|
83 ReentrantMonitor mMonitor; |
|
84 |
|
85 // Specialized Reset() method to signal if the seek is |
|
86 // to the start of the stream. |
|
87 nsresult ResetDecode(bool start); |
|
88 |
|
89 bool HasSkeleton() { |
|
90 return mSkeletonState != 0 && mSkeletonState->mActive; |
|
91 } |
|
92 |
|
93 // Seeks to the keyframe preceeding the target time using available |
|
94 // keyframe indexes. |
|
95 enum IndexedSeekResult { |
|
96 SEEK_OK, // Success. |
|
97 SEEK_INDEX_FAIL, // Failure due to no index, or invalid index. |
|
98 SEEK_FATAL_ERROR // Error returned by a stream operation. |
|
99 }; |
|
100 IndexedSeekResult SeekToKeyframeUsingIndex(int64_t aTarget); |
|
101 |
|
102 // Rolls back a seek-using-index attempt, returning a failure error code. |
|
103 IndexedSeekResult RollbackIndexedSeek(int64_t aOffset); |
|
104 |
|
105 // Represents a section of contiguous media, with a start and end offset, |
|
106 // and the timestamps of the start and end of that range, that is cached. |
|
107 // Used to denote the extremities of a range in which we can seek quickly |
|
108 // (because it's cached). |
|
109 class SeekRange { |
|
110 public: |
|
111 SeekRange() |
|
112 : mOffsetStart(0), |
|
113 mOffsetEnd(0), |
|
114 mTimeStart(0), |
|
115 mTimeEnd(0) |
|
116 {} |
|
117 |
|
118 SeekRange(int64_t aOffsetStart, |
|
119 int64_t aOffsetEnd, |
|
120 int64_t aTimeStart, |
|
121 int64_t aTimeEnd) |
|
122 : mOffsetStart(aOffsetStart), |
|
123 mOffsetEnd(aOffsetEnd), |
|
124 mTimeStart(aTimeStart), |
|
125 mTimeEnd(aTimeEnd) |
|
126 {} |
|
127 |
|
128 bool IsNull() const { |
|
129 return mOffsetStart == 0 && |
|
130 mOffsetEnd == 0 && |
|
131 mTimeStart == 0 && |
|
132 mTimeEnd == 0; |
|
133 } |
|
134 |
|
135 int64_t mOffsetStart, mOffsetEnd; // in bytes. |
|
136 int64_t mTimeStart, mTimeEnd; // in usecs. |
|
137 }; |
|
138 |
|
139 // Seeks to aTarget usecs in the buffered range aRange using bisection search, |
|
140 // or to the keyframe prior to aTarget if we have video. aAdjustedTarget is |
|
141 // an adjusted version of the target used to account for Opus pre-roll, if |
|
142 // necessary. aStartTime must be the presentation time at the start of media, |
|
143 // and aEndTime the time at end of media. aRanges must be the time/byte ranges |
|
144 // buffered in the media cache as per GetSeekRanges(). |
|
145 nsresult SeekInBufferedRange(int64_t aTarget, |
|
146 int64_t aAdjustedTarget, |
|
147 int64_t aStartTime, |
|
148 int64_t aEndTime, |
|
149 const nsTArray<SeekRange>& aRanges, |
|
150 const SeekRange& aRange); |
|
151 |
|
152 // Seeks to before aTarget usecs in media using bisection search. If the media |
|
153 // has video, this will seek to before the keyframe required to render the |
|
154 // media at aTarget. Will use aRanges in order to narrow the bisection |
|
155 // search space. aStartTime must be the presentation time at the start of |
|
156 // media, and aEndTime the time at end of media. aRanges must be the time/byte |
|
157 // ranges buffered in the media cache as per GetSeekRanges(). |
|
158 nsresult SeekInUnbuffered(int64_t aTarget, |
|
159 int64_t aStartTime, |
|
160 int64_t aEndTime, |
|
161 const nsTArray<SeekRange>& aRanges); |
|
162 |
|
163 // Get the end time of aEndOffset. This is the playback position we'd reach |
|
164 // after playback finished at aEndOffset. |
|
165 int64_t RangeEndTime(int64_t aEndOffset); |
|
166 |
|
167 // Get the end time of aEndOffset, without reading before aStartOffset. |
|
168 // This is the playback position we'd reach after playback finished at |
|
169 // aEndOffset. If bool aCachedDataOnly is true, then we'll only read |
|
170 // from data which is cached in the media cached, otherwise we'll do |
|
171 // regular blocking reads from the media stream. If bool aCachedDataOnly |
|
172 // is true, this can safely be called on the main thread, otherwise it |
|
173 // must be called on the state machine thread. |
|
174 int64_t RangeEndTime(int64_t aStartOffset, |
|
175 int64_t aEndOffset, |
|
176 bool aCachedDataOnly); |
|
177 |
|
178 // Get the start time of the range beginning at aOffset. This is the start |
|
179 // time of the first frame and or audio sample we'd be able to play if we |
|
180 // started playback at aOffset. |
|
181 int64_t RangeStartTime(int64_t aOffset); |
|
182 |
|
183 // Performs a seek bisection to move the media stream's read cursor to the |
|
184 // last ogg page boundary which has end time before aTarget usecs on both the |
|
185 // Theora and Vorbis bitstreams. Limits its search to data inside aRange; |
|
186 // i.e. it will only read inside of the aRange's start and end offsets. |
|
187 // aFuzz is the number of usecs of leniency we'll allow; we'll terminate the |
|
188 // seek when we land in the range (aTime - aFuzz, aTime) usecs. |
|
189 nsresult SeekBisection(int64_t aTarget, |
|
190 const SeekRange& aRange, |
|
191 uint32_t aFuzz); |
|
192 |
|
193 // Returns true if the serial number is for a stream we encountered |
|
194 // while reading metadata. Call on the main thread only. |
|
195 bool IsKnownStream(uint32_t aSerial); |
|
196 |
|
197 // Fills aRanges with SeekRanges denoting the sections of the media which |
|
198 // have been downloaded and are stored in the media cache. The reader |
|
199 // monitor must must be held with exactly one lock count. The MediaResource |
|
200 // must be pinned while calling this. |
|
201 nsresult GetSeekRanges(nsTArray<SeekRange>& aRanges); |
|
202 |
|
203 // Returns the range in which you should perform a seek bisection if |
|
204 // you wish to seek to aTarget usecs, given the known (buffered) byte ranges |
|
205 // in aRanges. If aExact is true, we only return an exact copy of a |
|
206 // range in which aTarget lies, or a null range if aTarget isn't contained |
|
207 // in any of the (buffered) ranges. Otherwise, when aExact is false, |
|
208 // we'll construct the smallest possible range we can, based on the times |
|
209 // and byte offsets known in aRanges. We can then use this to minimize our |
|
210 // bisection's search space when the target isn't in a known buffered range. |
|
211 SeekRange SelectSeekRange(const nsTArray<SeekRange>& aRanges, |
|
212 int64_t aTarget, |
|
213 int64_t aStartTime, |
|
214 int64_t aEndTime, |
|
215 bool aExact); |
|
216 private: |
|
217 |
|
218 // Decodes a packet of Vorbis data, and inserts its samples into the |
|
219 // audio queue. |
|
220 nsresult DecodeVorbis(ogg_packet* aPacket); |
|
221 |
|
222 // Decodes a packet of Opus data, and inserts its samples into the |
|
223 // audio queue. |
|
224 nsresult DecodeOpus(ogg_packet* aPacket); |
|
225 |
|
226 // Decodes a packet of Theora data, and inserts its frame into the |
|
227 // video queue. May return NS_ERROR_OUT_OF_MEMORY. Caller must have obtained |
|
228 // the reader's monitor. aTimeThreshold is the current playback position |
|
229 // in media time in microseconds. Frames with an end time before this will |
|
230 // not be enqueued. |
|
231 nsresult DecodeTheora(ogg_packet* aPacket, int64_t aTimeThreshold); |
|
232 |
|
233 // Read a page of data from the Ogg file. Returns true if a page has been |
|
234 // read, false if the page read failed or end of file reached. |
|
235 bool ReadOggPage(ogg_page* aPage); |
|
236 |
|
237 // Reads and decodes header packets for aState, until either header decode |
|
238 // fails, or is complete. Initializes the codec state before returning. |
|
239 // Returns true if reading headers and initializtion of the stream |
|
240 // succeeds. |
|
241 bool ReadHeaders(OggCodecState* aState); |
|
242 |
|
243 // Reads the next link in the chain. |
|
244 bool ReadOggChain(); |
|
245 |
|
246 // Set this media as being a chain and notifies the state machine that the |
|
247 // media is no longer seekable. |
|
248 void SetChained(bool aIsChained); |
|
249 |
|
250 // Returns the next Ogg packet for an bitstream/codec state. Returns a |
|
251 // pointer to an ogg_packet on success, or nullptr if the read failed. |
|
252 // The caller is responsible for deleting the packet and its |packet| field. |
|
253 ogg_packet* NextOggPacket(OggCodecState* aCodecState); |
|
254 |
|
255 // Fills aTracks with the serial numbers of each active stream, for use by |
|
256 // various SkeletonState functions. |
|
257 void BuildSerialList(nsTArray<uint32_t>& aTracks); |
|
258 |
|
259 OggCodecStore mCodecStore; |
|
260 |
|
261 // Decode state of the Theora bitstream we're decoding, if we have video. |
|
262 TheoraState* mTheoraState; |
|
263 |
|
264 // Decode state of the Vorbis bitstream we're decoding, if we have audio. |
|
265 VorbisState* mVorbisState; |
|
266 |
|
267 #ifdef MOZ_OPUS |
|
268 // Decode state of the Opus bitstream we're decoding, if we have one. |
|
269 OpusState *mOpusState; |
|
270 |
|
271 // Represents the user pref media.opus.enabled at the time our |
|
272 // contructor was called. We can't check it dynamically because |
|
273 // we're not on the main thread; |
|
274 bool mOpusEnabled; |
|
275 #endif /* MOZ_OPUS */ |
|
276 |
|
277 // Decode state of the Skeleton bitstream. |
|
278 SkeletonState* mSkeletonState; |
|
279 |
|
280 // Ogg decoding state. |
|
281 ogg_sync_state mOggState; |
|
282 |
|
283 // Vorbis/Opus/Theora data used to compute timestamps. This is written on the |
|
284 // decoder thread and read on the main thread. All reading on the main |
|
285 // thread must be done after metadataloaded. We can't use the existing |
|
286 // data in the codec states due to threading issues. You must check the |
|
287 // associated mTheoraState or mVorbisState pointer is non-null before |
|
288 // using this codec data. |
|
289 uint32_t mVorbisSerial; |
|
290 uint32_t mOpusSerial; |
|
291 uint32_t mTheoraSerial; |
|
292 vorbis_info mVorbisInfo; |
|
293 int mOpusPreSkip; |
|
294 th_info mTheoraInfo; |
|
295 |
|
296 // The picture region inside Theora frame to be displayed, if we have |
|
297 // a Theora video track. |
|
298 nsIntRect mPicture; |
|
299 |
|
300 // True if we are decoding a chained ogg. Reading or writing to this member |
|
301 // should be done with |mMonitor| acquired. |
|
302 bool mIsChained; |
|
303 |
|
304 // Number of audio frames decoded so far. |
|
305 int64_t mDecodedAudioFrames; |
|
306 }; |
|
307 |
|
308 } // namespace mozilla |
|
309 |
|
310 #endif |