/src/mozilla-central/dom/media/ogg/OggCodecState.h
Line | Count | Source (jump to first uncovered line) |
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(OggCodecState_h_) |
7 | | #define OggCodecState_h_ |
8 | | |
9 | | #include <ogg/ogg.h> |
10 | | // For MOZ_SAMPLE_TYPE_* |
11 | | #include "FlacFrameParser.h" |
12 | | #include "VideoUtils.h" |
13 | | #include <nsDeque.h> |
14 | | #include <nsTArray.h> |
15 | | #include <nsClassHashtable.h> |
16 | | |
17 | | #include <theora/theoradec.h> |
18 | | #ifdef MOZ_TREMOR |
19 | | #include <tremor/ivorbiscodec.h> |
20 | | #else |
21 | | #include <vorbis/codec.h> |
22 | | #endif |
23 | | |
24 | | // Uncomment the following to validate that we're predicting the number |
25 | | // of Vorbis samples in each packet correctly. |
26 | | #define VALIDATE_VORBIS_SAMPLE_CALCULATION |
27 | | #ifdef VALIDATE_VORBIS_SAMPLE_CALCULATION |
28 | | #include <map> |
29 | | #endif |
30 | | |
31 | | struct OpusMSDecoder; |
32 | | |
33 | | namespace mozilla { |
34 | | |
35 | | class OpusParser; |
36 | | |
37 | | struct OggPacketDeletePolicy |
38 | | { |
39 | | void operator()(ogg_packet* aPacket) const |
40 | 0 | { |
41 | 0 | delete [] aPacket->packet; |
42 | 0 | delete aPacket; |
43 | 0 | } |
44 | | }; |
45 | | |
46 | | using OggPacketPtr = UniquePtr<ogg_packet, OggPacketDeletePolicy>; |
47 | | |
48 | | // Deallocates a packet, used in OggPacketQueue below. |
49 | | class OggPacketDeallocator : public nsDequeFunctor |
50 | | { |
51 | | virtual void operator()(void* aPacket) override |
52 | 0 | { |
53 | 0 | OggPacketDeletePolicy()(static_cast<ogg_packet*>(aPacket)); |
54 | 0 | } |
55 | | }; |
56 | | |
57 | | // A queue of ogg_packets. When we read a page, we extract the page's packets |
58 | | // and buffer them in the owning stream's OggCodecState. This is because |
59 | | // if we're skipping up to the next keyframe in very large frame sized videos, |
60 | | // there may be several megabytes of data between keyframes, and the |
61 | | // ogg_stream_state would end up resizing its buffer every time we added a |
62 | | // new 4KB page to the bitstream, which kills performance on Windows. This |
63 | | // also gives us the option to timestamp packets rather than decoded |
64 | | // frames/samples, reducing the amount of frames/samples we must decode to |
65 | | // determine start-time at a particular offset, and gives us finer control |
66 | | // over memory usage. |
67 | | class OggPacketQueue : private nsDeque |
68 | | { |
69 | | public: |
70 | 0 | OggPacketQueue() : nsDeque(new OggPacketDeallocator()) { } |
71 | 0 | ~OggPacketQueue() { Erase(); } |
72 | 0 | bool IsEmpty() { return nsDeque::GetSize() == 0; } |
73 | | void Append(OggPacketPtr aPacket); |
74 | | OggPacketPtr PopFront() |
75 | 0 | { |
76 | 0 | return OggPacketPtr(static_cast<ogg_packet*>(nsDeque::PopFront())); |
77 | 0 | } |
78 | | ogg_packet* PeekFront() |
79 | 0 | { |
80 | 0 | return static_cast<ogg_packet*>(nsDeque::PeekFront()); |
81 | 0 | } |
82 | | OggPacketPtr Pop() |
83 | 0 | { |
84 | 0 | return OggPacketPtr(static_cast<ogg_packet*>(nsDeque::Pop())); |
85 | 0 | } |
86 | | ogg_packet* operator[](size_t aIndex) const |
87 | 0 | { |
88 | 0 | return static_cast<ogg_packet*>(nsDeque::ObjectAt(aIndex)); |
89 | 0 | } |
90 | 0 | size_t Length() const { return nsDeque::GetSize(); } |
91 | | void PushFront(OggPacketPtr aPacket) |
92 | 0 | { |
93 | 0 | nsDeque::PushFront(aPacket.release()); |
94 | 0 | } |
95 | 0 | void Erase() { nsDeque::Erase(); } |
96 | | }; |
97 | | |
98 | | // Encapsulates the data required for decoding an ogg bitstream and for |
99 | | // converting granulepos to timestamps. |
100 | | class OggCodecState |
101 | | { |
102 | | public: |
103 | | typedef mozilla::MetadataTags MetadataTags; |
104 | | // Ogg types we know about |
105 | | enum CodecType |
106 | | { |
107 | | TYPE_VORBIS=0, |
108 | | TYPE_THEORA, |
109 | | TYPE_OPUS, |
110 | | TYPE_SKELETON, |
111 | | TYPE_FLAC, |
112 | | TYPE_UNKNOWN |
113 | | }; |
114 | | |
115 | | virtual ~OggCodecState(); |
116 | | |
117 | | // Factory for creating nsCodecStates. Use instead of constructor. |
118 | | // aPage should be a beginning-of-stream page. |
119 | | static OggCodecState* Create(ogg_page* aPage); |
120 | | |
121 | 0 | virtual CodecType GetType() { return TYPE_UNKNOWN; } |
122 | | |
123 | | // Reads a header packet. Returns false if an error was encountered |
124 | | // while reading header packets. Callers should check DoneReadingHeaders() |
125 | | // to determine if the last header has been read. |
126 | | // This function takes ownership of the packet and is responsible for |
127 | | // releasing it or queuing it for later processing. |
128 | | virtual bool DecodeHeader(OggPacketPtr aPacket) |
129 | 0 | { |
130 | 0 | return (mDoneReadingHeaders = true); |
131 | 0 | } |
132 | | |
133 | | // Build a hash table with tag metadata parsed from the stream. |
134 | | virtual MetadataTags* GetTags() |
135 | 0 | { |
136 | 0 | return nullptr; |
137 | 0 | } |
138 | | |
139 | | // Returns the end time that a granulepos represents. |
140 | 0 | virtual int64_t Time(int64_t granulepos) { return -1; } |
141 | | |
142 | | // Returns the start time that a granulepos represents. |
143 | 0 | virtual int64_t StartTime(int64_t granulepos) { return -1; } |
144 | | |
145 | | // Returns the duration of the given packet, if it can be determined. |
146 | 0 | virtual int64_t PacketDuration(ogg_packet* aPacket) { return -1; } |
147 | | |
148 | | // Returns the start time of the given packet, if it can be determined. |
149 | | virtual int64_t PacketStartTime(ogg_packet* aPacket) |
150 | 0 | { |
151 | 0 | if (aPacket->granulepos < 0) { |
152 | 0 | return -1; |
153 | 0 | } |
154 | 0 | int64_t endTime = Time(aPacket->granulepos); |
155 | 0 | int64_t duration = PacketDuration(aPacket); |
156 | 0 | if (duration > endTime) { |
157 | 0 | // Audio preskip may eat a whole packet or more. |
158 | 0 | return 0; |
159 | 0 | } else { |
160 | 0 | return endTime - duration; |
161 | 0 | } |
162 | 0 | } |
163 | | |
164 | | // Initializes the codec state. |
165 | 0 | virtual bool Init() { return true; } |
166 | | |
167 | | // Returns true when this bitstream has finished reading all its |
168 | | // header packets. |
169 | 0 | bool DoneReadingHeaders() { return mDoneReadingHeaders; } |
170 | | |
171 | | // Deactivates the bitstream. Only the primary video and audio bitstreams |
172 | | // should be active. |
173 | | void Deactivate() |
174 | 0 | { |
175 | 0 | mActive = false; |
176 | 0 | mDoneReadingHeaders = true; |
177 | 0 | Reset(); |
178 | 0 | } |
179 | | |
180 | | // Resets decoding state. |
181 | | virtual nsresult Reset(); |
182 | | |
183 | | // Returns true if the OggCodecState thinks this packet is a header |
184 | | // packet. Note this does not verify the validity of the header packet, |
185 | | // it just guarantees that the packet is marked as a header packet (i.e. |
186 | | // it is definintely not a data packet). Do not use this to identify |
187 | | // streams, use it to filter header packets from data packets while |
188 | | // decoding. |
189 | 0 | virtual bool IsHeader(ogg_packet* aPacket) { return false; } |
190 | | |
191 | | // Returns true if the OggCodecState thinks this packet represents a |
192 | | // keyframe, from which decoding can restart safely. |
193 | 0 | virtual bool IsKeyframe(ogg_packet* aPacket) { return true; } |
194 | | |
195 | | // Returns true if there is a packet available for dequeueing in the stream. |
196 | | bool IsPacketReady(); |
197 | | |
198 | | // Returns the next raw packet in the stream, or nullptr if there are no more |
199 | | // packets buffered in the packet queue. More packets can be buffered by |
200 | | // inserting one or more pages into the stream by calling PageIn(). |
201 | | // The packet will have a valid granulepos. |
202 | | OggPacketPtr PacketOut(); |
203 | | |
204 | | // Returns the next raw packet in the stream, or nullptr if there are no more |
205 | | // packets buffered in the packet queue, without consuming it. |
206 | | // The packet will have a valid granulepos. |
207 | | ogg_packet* PacketPeek(); |
208 | | |
209 | | // Moves all raw packets from aOther to the front of the current packet queue. |
210 | | void PushFront(OggPacketQueue&& aOther); |
211 | | |
212 | | // Returns the next packet in the stream as a MediaRawData, or nullptr |
213 | | // if there are no more packets buffered in the packet queue. More packets |
214 | | // can be buffered by inserting one or more pages into the stream by calling |
215 | | // PageIn(). The packet will have a valid granulepos. |
216 | | virtual already_AddRefed<MediaRawData> PacketOutAsMediaRawData(); |
217 | | |
218 | | // Extracts all packets from the page, and inserts them into the packet |
219 | | // queue. They can be extracted by calling PacketOut(). Packets from an |
220 | | // inactive stream are not buffered, i.e. this call has no effect for |
221 | | // inactive streams. Multiple pages may need to be inserted before |
222 | | // PacketOut() starts to return packets, as granulepos may need to be |
223 | | // captured. |
224 | | virtual nsresult PageIn(ogg_page* aPage); |
225 | | |
226 | | // Returns the maximum number of microseconds which a keyframe can be offset |
227 | | // from any given interframe.b |
228 | 0 | virtual int64_t MaxKeyframeOffset() { return 0; } |
229 | | // Public access for mTheoraInfo.keyframe_granule_shift |
230 | 0 | virtual int32_t KeyFrameGranuleJobs() { return 0; } |
231 | | |
232 | | // Number of packets read. |
233 | | uint64_t mPacketCount; |
234 | | |
235 | | // Serial number of the bitstream. |
236 | | uint32_t mSerial; |
237 | | |
238 | | // Ogg specific state. |
239 | | ogg_stream_state mState; |
240 | | |
241 | | // Queue of as yet undecoded packets. Packets are guaranteed to have |
242 | | // a valid granulepos. |
243 | | OggPacketQueue mPackets; |
244 | | |
245 | | // Is the bitstream active; whether we're decoding and playing this bitstream. |
246 | | bool mActive; |
247 | | |
248 | | // True when all headers packets have been read. |
249 | | bool mDoneReadingHeaders; |
250 | | |
251 | | virtual const TrackInfo* GetInfo() const |
252 | 0 | { |
253 | 0 | MOZ_RELEASE_ASSERT(false, "Can't be called directly"); |
254 | 0 | return nullptr; |
255 | 0 | } |
256 | | |
257 | | // Validation utility for vorbis-style tag names. |
258 | | static bool IsValidVorbisTagName(nsCString& aName); |
259 | | |
260 | | // Utility method to parse and add a vorbis-style comment |
261 | | // to a metadata hash table. Most Ogg-encapsulated codecs |
262 | | // use the vorbis comment format for metadata. |
263 | | static bool AddVorbisComment(MetadataTags* aTags, |
264 | | const char* aComment, |
265 | | uint32_t aLength); |
266 | | |
267 | | protected: |
268 | | // Constructs a new OggCodecState. aActive denotes whether the stream is |
269 | | // active. For streams of unsupported or unknown types, aActive should be |
270 | | // false. |
271 | | OggCodecState(ogg_page* aBosPage, bool aActive); |
272 | | |
273 | | // Deallocates all packets stored in mUnstamped, and clears the array. |
274 | | void ClearUnstamped(); |
275 | | |
276 | | // Extracts packets out of mState until a data packet with a non -1 |
277 | | // granulepos is encountered, or no more packets are readable. Header |
278 | | // packets are pushed into the packet queue immediately, and data packets |
279 | | // are buffered in mUnstamped. Once a non -1 granulepos packet is read |
280 | | // the granulepos of the packets in mUnstamped can be inferred, and they |
281 | | // can be pushed over to mPackets. Used by PageIn() implementations in |
282 | | // subclasses. |
283 | | nsresult PacketOutUntilGranulepos(bool& aFoundGranulepos); |
284 | | |
285 | | // Temporary buffer in which to store packets while we're reading packets |
286 | | // in order to capture granulepos. |
287 | | nsTArray<OggPacketPtr> mUnstamped; |
288 | | |
289 | | bool SetCodecSpecificConfig(MediaByteBuffer* aBuffer, |
290 | | OggPacketQueue& aHeaders); |
291 | | |
292 | | private: |
293 | | bool InternalInit(); |
294 | | }; |
295 | | |
296 | | class VorbisState : public OggCodecState |
297 | | { |
298 | | public: |
299 | | explicit VorbisState(ogg_page* aBosPage); |
300 | | virtual ~VorbisState(); |
301 | | |
302 | 0 | CodecType GetType() override { return TYPE_VORBIS; } |
303 | | bool DecodeHeader(OggPacketPtr aPacket) override; |
304 | | int64_t Time(int64_t granulepos) override; |
305 | | int64_t PacketDuration(ogg_packet* aPacket) override; |
306 | | bool Init() override; |
307 | | nsresult Reset() override; |
308 | | bool IsHeader(ogg_packet* aPacket) override; |
309 | | nsresult PageIn(ogg_page* aPage) override; |
310 | 0 | const TrackInfo* GetInfo() const override { return &mInfo; } |
311 | | |
312 | | // Return a hash table with tag metadata. |
313 | | MetadataTags* GetTags() override; |
314 | | |
315 | | private: |
316 | | AudioInfo mInfo; |
317 | | vorbis_info mVorbisInfo; |
318 | | vorbis_comment mComment; |
319 | | vorbis_dsp_state mDsp; |
320 | | vorbis_block mBlock; |
321 | | OggPacketQueue mHeaders; |
322 | | |
323 | | // Returns the end time that a granulepos represents. |
324 | | static int64_t Time(vorbis_info* aInfo, int64_t aGranulePos); |
325 | | |
326 | | // Reconstructs the granulepos of Vorbis packets stored in the mUnstamped |
327 | | // array. |
328 | | nsresult ReconstructVorbisGranulepos(); |
329 | | |
330 | | // The "block size" of the previously decoded Vorbis packet, or 0 if we've |
331 | | // not yet decoded anything. This is used to calculate the number of samples |
332 | | // in a Vorbis packet, since each Vorbis packet depends on the previous |
333 | | // packet while being decoded. |
334 | | long mPrevVorbisBlockSize; |
335 | | |
336 | | // Granulepos (end sample) of the last decoded Vorbis packet. This is used |
337 | | // to calculate the Vorbis granulepos when we don't find a granulepos to |
338 | | // back-propagate from. |
339 | | int64_t mGranulepos; |
340 | | |
341 | | #ifdef VALIDATE_VORBIS_SAMPLE_CALCULATION |
342 | | // When validating that we've correctly predicted Vorbis packets' number |
343 | | // of samples, we store each packet's predicted number of samples in this |
344 | | // map, and verify we decode the predicted number of samples. |
345 | | std::map<ogg_packet*, long> mVorbisPacketSamples; |
346 | | #endif |
347 | | |
348 | | // Records that aPacket is predicted to have aSamples samples. |
349 | | // This function has no effect if VALIDATE_VORBIS_SAMPLE_CALCULATION |
350 | | // is not defined. |
351 | | void RecordVorbisPacketSamples(ogg_packet* aPacket, long aSamples); |
352 | | |
353 | | // Verifies that aPacket has had its number of samples predicted. |
354 | | // This function has no effect if VALIDATE_VORBIS_SAMPLE_CALCULATION |
355 | | // is not defined. |
356 | | void AssertHasRecordedPacketSamples(ogg_packet* aPacket); |
357 | | |
358 | | public: |
359 | | // Asserts that the number of samples predicted for aPacket is aSamples. |
360 | | // This function has no effect if VALIDATE_VORBIS_SAMPLE_CALCULATION |
361 | | // is not defined. |
362 | | void ValidateVorbisPacketSamples(ogg_packet* aPacket, long aSamples); |
363 | | |
364 | | }; |
365 | | |
366 | | // Returns 1 if the Theora info struct is decoding a media of Theora |
367 | | // version (maj,min,sub) or later, otherwise returns 0. |
368 | | int TheoraVersion(th_info* info, |
369 | | unsigned char maj, |
370 | | unsigned char min, |
371 | | unsigned char sub); |
372 | | |
373 | | class TheoraState : public OggCodecState |
374 | | { |
375 | | public: |
376 | | explicit TheoraState(ogg_page* aBosPage); |
377 | | virtual ~TheoraState(); |
378 | | |
379 | 0 | CodecType GetType() override { return TYPE_THEORA; } |
380 | | bool DecodeHeader(OggPacketPtr aPacket) override; |
381 | | int64_t Time(int64_t granulepos) override; |
382 | | int64_t StartTime(int64_t granulepos) override; |
383 | | int64_t PacketDuration(ogg_packet* aPacket) override; |
384 | | bool Init() override; |
385 | | nsresult Reset() override; |
386 | | bool IsHeader(ogg_packet* aPacket) override; |
387 | | bool IsKeyframe(ogg_packet* aPacket) override; |
388 | | nsresult PageIn(ogg_page* aPage) override; |
389 | 0 | const TrackInfo* GetInfo() const override { return &mInfo; } |
390 | | int64_t MaxKeyframeOffset() override; |
391 | | int32_t KeyFrameGranuleJobs() override |
392 | 0 | { |
393 | 0 | return mTheoraInfo.keyframe_granule_shift; |
394 | 0 | } |
395 | | |
396 | | private: |
397 | | // Returns the end time that a granulepos represents. |
398 | | static int64_t Time(th_info* aInfo, int64_t aGranulePos); |
399 | | |
400 | | th_info mTheoraInfo; |
401 | | th_comment mComment; |
402 | | th_setup_info* mSetup; |
403 | | th_dec_ctx* mCtx; |
404 | | |
405 | | VideoInfo mInfo; |
406 | | OggPacketQueue mHeaders; |
407 | | |
408 | | // Reconstructs the granulepos of Theora packets stored in the |
409 | | // mUnstamped array. mUnstamped must be filled with consecutive packets from |
410 | | // the stream, with the last packet having a known granulepos. Using this |
411 | | // known granulepos, and the known frame numbers, we recover the granulepos |
412 | | // of all frames in the array. This enables us to determine their timestamps. |
413 | | void ReconstructTheoraGranulepos(); |
414 | | }; |
415 | | |
416 | | class OpusState : public OggCodecState |
417 | | { |
418 | | public: |
419 | | explicit OpusState(ogg_page* aBosPage); |
420 | | virtual ~OpusState(); |
421 | | |
422 | 0 | CodecType GetType() override { return TYPE_OPUS; } |
423 | | bool DecodeHeader(OggPacketPtr aPacket) override; |
424 | | int64_t Time(int64_t aGranulepos) override; |
425 | | int64_t PacketDuration(ogg_packet* aPacket) override; |
426 | | bool Init() override; |
427 | | nsresult Reset() override; |
428 | | nsresult Reset(bool aStart); |
429 | | bool IsHeader(ogg_packet* aPacket) override; |
430 | | nsresult PageIn(ogg_page* aPage) override; |
431 | | already_AddRefed<MediaRawData> PacketOutAsMediaRawData() override; |
432 | 0 | const TrackInfo* GetInfo() const override { return &mInfo; } |
433 | | |
434 | | // Returns the end time that a granulepos represents. |
435 | | static int64_t Time(int aPreSkip, int64_t aGranulepos); |
436 | | |
437 | | // Construct and return a table of tags from the metadata header. |
438 | | MetadataTags* GetTags() override; |
439 | | |
440 | | private: |
441 | | nsAutoPtr<OpusParser> mParser; |
442 | | OpusMSDecoder* mDecoder; |
443 | | |
444 | | // Granule position (end sample) of the last decoded Opus packet. This is |
445 | | // used to calculate the amount we should trim from the last packet. |
446 | | int64_t mPrevPacketGranulepos; |
447 | | |
448 | | // Reconstructs the granulepos of Opus packets stored in the |
449 | | // mUnstamped array. mUnstamped must be filled with consecutive packets from |
450 | | // the stream, with the last packet having a known granulepos. Using this |
451 | | // known granulepos, and the known frame numbers, we recover the granulepos |
452 | | // of all frames in the array. This enables us to determine their timestamps. |
453 | | bool ReconstructOpusGranulepos(); |
454 | | |
455 | | // Granule position (end sample) of the last decoded Opus page. This is |
456 | | // used to calculate the Opus per-packet granule positions on the last page, |
457 | | // where we may need to trim some samples from the end. |
458 | | int64_t mPrevPageGranulepos; |
459 | | AudioInfo mInfo; |
460 | | OggPacketQueue mHeaders; |
461 | | }; |
462 | | |
463 | | // Constructs a 32bit version number out of two 16 bit major,minor |
464 | | // version numbers. |
465 | 0 | #define SKELETON_VERSION(major, minor) (((major)<<16)|(minor)) |
466 | | |
467 | | enum EMsgHeaderType |
468 | | { |
469 | | eContentType, |
470 | | eRole, |
471 | | eName, |
472 | | eLanguage, |
473 | | eTitle, |
474 | | eDisplayHint, |
475 | | eAltitude, |
476 | | eTrackOrder, |
477 | | eTrackDependencies |
478 | | }; |
479 | | |
480 | | typedef struct |
481 | | { |
482 | | const char* mPatternToRecognize; |
483 | | EMsgHeaderType mMsgHeaderType; |
484 | | } FieldPatternType; |
485 | | |
486 | | // Stores the message information for different logical bitstream. |
487 | | typedef struct |
488 | | { |
489 | | nsClassHashtable<nsUint32HashKey, nsCString> mValuesStore; |
490 | | } MessageField; |
491 | | |
492 | | class SkeletonState : public OggCodecState |
493 | | { |
494 | | public: |
495 | | explicit SkeletonState(ogg_page* aBosPage); |
496 | | ~SkeletonState(); |
497 | | |
498 | | nsClassHashtable<nsUint32HashKey, MessageField> mMsgFieldStore; |
499 | | |
500 | 0 | CodecType GetType() override { return TYPE_SKELETON; } |
501 | | bool DecodeHeader(OggPacketPtr aPacket) override; |
502 | 0 | int64_t Time(int64_t granulepos) override { return -1; } |
503 | 0 | bool IsHeader(ogg_packet* aPacket) override { return true; } |
504 | | |
505 | | // Return true if the given time (in milliseconds) is within |
506 | | // the presentation time defined in the skeleton track. |
507 | | bool IsPresentable(int64_t aTime) { return aTime >= mPresentationTime; } |
508 | | |
509 | | // Stores the offset of the page on which a keyframe starts, |
510 | | // and its presentation time. |
511 | | class nsKeyPoint |
512 | | { |
513 | | public: |
514 | | nsKeyPoint() |
515 | | : mOffset(INT64_MAX) |
516 | 0 | , mTime(INT64_MAX) {} |
517 | | |
518 | | nsKeyPoint(int64_t aOffset, int64_t aTime) |
519 | | : mOffset(aOffset) |
520 | 0 | ,mTime(aTime) {} |
521 | | |
522 | | // Offset from start of segment/link-in-the-chain in bytes. |
523 | | int64_t mOffset; |
524 | | |
525 | | // Presentation time in usecs. |
526 | | int64_t mTime; |
527 | | |
528 | | bool IsNull() |
529 | 0 | { |
530 | 0 | return mOffset == INT64_MAX && mTime == INT64_MAX; |
531 | 0 | } |
532 | | }; |
533 | | |
534 | | // Stores a keyframe's byte-offset, presentation time and the serialno |
535 | | // of the stream it belongs to. |
536 | | class nsSeekTarget |
537 | | { |
538 | | public: |
539 | 0 | nsSeekTarget() : mSerial(0) { } |
540 | | nsKeyPoint mKeyPoint; |
541 | | uint32_t mSerial; |
542 | | bool IsNull() |
543 | 0 | { |
544 | 0 | return mKeyPoint.IsNull() && mSerial == 0; |
545 | 0 | } |
546 | | }; |
547 | | |
548 | | // Determines from the seek index the keyframe which you must seek back to |
549 | | // in order to get all keyframes required to render all streams with |
550 | | // serialnos in aTracks, at time aTarget. |
551 | | nsresult IndexedSeekTarget(int64_t aTarget, |
552 | | nsTArray<uint32_t>& aTracks, |
553 | | nsSeekTarget& aResult); |
554 | | |
555 | | bool HasIndex() const |
556 | 0 | { |
557 | 0 | return mIndex.Count() > 0; |
558 | 0 | } |
559 | | |
560 | | // Returns the duration of the active tracks in the media, if we have |
561 | | // an index. aTracks must be filled with the serialnos of the active tracks. |
562 | | // The duration is calculated as the greatest end time of all active tracks, |
563 | | // minus the smalled start time of all the active tracks. |
564 | | nsresult GetDuration(const nsTArray<uint32_t>& aTracks, int64_t& aDuration); |
565 | | |
566 | | private: |
567 | | |
568 | | // Decodes an index packet. Returns false on failure. |
569 | | bool DecodeIndex(ogg_packet* aPacket); |
570 | | // Decodes an fisbone packet. Returns false on failure. |
571 | | bool DecodeFisbone(ogg_packet* aPacket); |
572 | | |
573 | | // Gets the keypoint you must seek to in order to get the keyframe required |
574 | | // to render the stream at time aTarget on stream with serial aSerialno. |
575 | | nsresult IndexedSeekTargetForTrack(uint32_t aSerialno, |
576 | | int64_t aTarget, |
577 | | nsKeyPoint& aResult); |
578 | | |
579 | | // Version of the decoded skeleton track, as per the SKELETON_VERSION macro. |
580 | | uint32_t mVersion; |
581 | | |
582 | | // Presentation time of the resource in milliseconds |
583 | | int64_t mPresentationTime; |
584 | | |
585 | | // Length of the resource in bytes. |
586 | | int64_t mLength; |
587 | | |
588 | | // Stores the keyframe index and duration information for a particular |
589 | | // stream. |
590 | | class nsKeyFrameIndex |
591 | | { |
592 | | public: |
593 | | |
594 | | nsKeyFrameIndex(int64_t aStartTime, int64_t aEndTime) |
595 | | : mStartTime(aStartTime) |
596 | | , mEndTime(aEndTime) |
597 | 0 | { |
598 | 0 | MOZ_COUNT_CTOR(nsKeyFrameIndex); |
599 | 0 | } |
600 | | |
601 | | ~nsKeyFrameIndex() |
602 | 0 | { |
603 | 0 | MOZ_COUNT_DTOR(nsKeyFrameIndex); |
604 | 0 | } |
605 | | |
606 | | void Add(int64_t aOffset, int64_t aTimeMs) |
607 | 0 | { |
608 | 0 | mKeyPoints.AppendElement(nsKeyPoint(aOffset, aTimeMs)); |
609 | 0 | } |
610 | | |
611 | | const nsKeyPoint& Get(uint32_t aIndex) const |
612 | 0 | { |
613 | 0 | return mKeyPoints[aIndex]; |
614 | 0 | } |
615 | | |
616 | | uint32_t Length() const |
617 | 0 | { |
618 | 0 | return mKeyPoints.Length(); |
619 | 0 | } |
620 | | |
621 | | // Presentation time of the first sample in this stream in usecs. |
622 | | const int64_t mStartTime; |
623 | | |
624 | | // End time of the last sample in this stream in usecs. |
625 | | const int64_t mEndTime; |
626 | | |
627 | | private: |
628 | | nsTArray<nsKeyPoint> mKeyPoints; |
629 | | }; |
630 | | |
631 | | // Maps Ogg serialnos to the index-keypoint list. |
632 | | nsClassHashtable<nsUint32HashKey, nsKeyFrameIndex> mIndex; |
633 | | }; |
634 | | |
635 | | class FlacState : public OggCodecState |
636 | | { |
637 | | public: |
638 | | explicit FlacState(ogg_page* aBosPage); |
639 | | |
640 | 0 | CodecType GetType() override { return TYPE_FLAC; } |
641 | | bool DecodeHeader(OggPacketPtr aPacket) override; |
642 | | int64_t Time(int64_t granulepos) override; |
643 | | int64_t PacketDuration(ogg_packet* aPacket) override; |
644 | | bool IsHeader(ogg_packet* aPacket) override; |
645 | | nsresult PageIn(ogg_page* aPage) override; |
646 | | |
647 | | // Return a hash table with tag metadata. |
648 | | MetadataTags* GetTags() override; |
649 | | |
650 | | const TrackInfo* GetInfo() const override; |
651 | | |
652 | | private: |
653 | | bool ReconstructFlacGranulepos(void); |
654 | | |
655 | | FlacFrameParser mParser; |
656 | | }; |
657 | | |
658 | | } // namespace mozilla |
659 | | |
660 | | #endif |