Line data Source code
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 :
7 : #include <string.h>
8 :
9 : #include "mozilla/EndianUtils.h"
10 : #include <stdint.h>
11 : #include <algorithm>
12 : #include <opus/opus.h>
13 :
14 : #include "OggCodecState.h"
15 : #include "OpusDecoder.h"
16 : #include "OpusParser.h"
17 : #include "VideoUtils.h"
18 : #include "XiphExtradata.h"
19 : #include "nsDebug.h"
20 : #include "opus/opus_multistream.h"
21 :
22 : // On Android JellyBean, the hardware.h header redefines version_major and
23 : // version_minor, which breaks our build. See:
24 : // https://bugzilla.mozilla.org/show_bug.cgi?id=912702#c6
25 : #ifdef MOZ_WIDGET_GONK
26 : #ifdef version_major
27 : #undef version_major
28 : #endif
29 : #ifdef version_minor
30 : #undef version_minor
31 : #endif
32 : #endif
33 :
34 : namespace mozilla {
35 :
36 : extern LazyLogModule gMediaDecoderLog;
37 : #define LOG(type, msg) MOZ_LOG(gMediaDecoderLog, type, msg)
38 :
39 : using media::TimeUnit;
40 :
41 : /** Decoder base class for Ogg-encapsulated streams. */
42 : OggCodecState*
43 0 : OggCodecState::Create(ogg_page* aPage)
44 : {
45 0 : NS_ASSERTION(ogg_page_bos(aPage), "Only call on BOS page!");
46 0 : nsAutoPtr<OggCodecState> codecState;
47 0 : if (aPage->body_len > 6 && memcmp(aPage->body+1, "theora", 6) == 0) {
48 0 : codecState = new TheoraState(aPage);
49 0 : } else if (aPage->body_len > 6 && memcmp(aPage->body+1, "vorbis", 6) == 0) {
50 0 : codecState = new VorbisState(aPage);
51 0 : } else if (aPage->body_len > 8 && memcmp(aPage->body, "OpusHead", 8) == 0) {
52 0 : codecState = new OpusState(aPage);
53 0 : } else if (aPage->body_len > 8 && memcmp(aPage->body, "fishead\0", 8) == 0) {
54 0 : codecState = new SkeletonState(aPage);
55 0 : } else if (aPage->body_len > 5 && memcmp(aPage->body, "\177FLAC", 5) == 0) {
56 0 : codecState = new FlacState(aPage);
57 : } else {
58 0 : codecState = new OggCodecState(aPage, false);
59 : }
60 0 : return codecState->OggCodecState::InternalInit() ? codecState.forget() : nullptr;
61 : }
62 :
63 0 : OggCodecState::OggCodecState(ogg_page* aBosPage, bool aActive)
64 : : mPacketCount(0)
65 0 : , mSerial(ogg_page_serialno(aBosPage))
66 : , mActive(aActive)
67 0 : , mDoneReadingHeaders(!aActive)
68 : {
69 0 : MOZ_COUNT_CTOR(OggCodecState);
70 0 : memset(&mState, 0, sizeof(ogg_stream_state));
71 0 : }
72 :
73 0 : OggCodecState::~OggCodecState()
74 : {
75 0 : MOZ_COUNT_DTOR(OggCodecState);
76 0 : Reset();
77 : #ifdef DEBUG
78 : int ret =
79 : #endif
80 0 : ogg_stream_clear(&mState);
81 0 : NS_ASSERTION(ret == 0, "ogg_stream_clear failed");
82 0 : }
83 :
84 : nsresult
85 0 : OggCodecState::Reset()
86 : {
87 0 : if (ogg_stream_reset(&mState) != 0) {
88 0 : return NS_ERROR_FAILURE;
89 : }
90 0 : mPackets.Erase();
91 0 : ClearUnstamped();
92 0 : return NS_OK;
93 : }
94 :
95 : void
96 0 : OggCodecState::ClearUnstamped()
97 : {
98 0 : mUnstamped.Clear();
99 0 : }
100 :
101 : bool
102 0 : OggCodecState::InternalInit()
103 : {
104 0 : int ret = ogg_stream_init(&mState, mSerial);
105 0 : return ret == 0;
106 : }
107 :
108 : bool
109 0 : OggCodecState::IsValidVorbisTagName(nsCString& aName)
110 : {
111 : // Tag names must consist of ASCII 0x20 through 0x7D,
112 : // excluding 0x3D '=' which is the separator.
113 0 : uint32_t length = aName.Length();
114 0 : const char* data = aName.Data();
115 0 : for (uint32_t i = 0; i < length; i++) {
116 0 : if (data[i] < 0x20 || data[i] > 0x7D || data[i] == '=') {
117 0 : return false;
118 : }
119 : }
120 0 : return true;
121 : }
122 :
123 : bool
124 0 : OggCodecState::AddVorbisComment(MetadataTags* aTags,
125 : const char* aComment,
126 : uint32_t aLength)
127 : {
128 0 : const char* div = (const char*)memchr(aComment, '=', aLength);
129 0 : if (!div) {
130 0 : LOG(LogLevel::Debug, ("Skipping comment: no separator"));
131 0 : return false;
132 : }
133 0 : nsCString key = nsCString(aComment, div-aComment);
134 0 : if (!IsValidVorbisTagName(key)) {
135 0 : LOG(LogLevel::Debug, ("Skipping comment: invalid tag name"));
136 0 : return false;
137 : }
138 0 : uint32_t valueLength = aLength - (div-aComment);
139 0 : nsCString value = nsCString(div + 1, valueLength);
140 0 : if (!IsUTF8(value)) {
141 0 : LOG(LogLevel::Debug, ("Skipping comment: invalid UTF-8 in value"));
142 0 : return false;
143 : }
144 0 : aTags->Put(key, value);
145 0 : return true;
146 : }
147 :
148 : bool
149 0 : OggCodecState::SetCodecSpecificConfig(MediaByteBuffer* aBuffer,
150 : OggPacketQueue& aHeaders)
151 : {
152 0 : nsTArray<const unsigned char*> headers;
153 0 : nsTArray<size_t> headerLens;
154 0 : for (size_t i = 0; i < aHeaders.Length(); i++) {
155 0 : headers.AppendElement(aHeaders[i]->packet);
156 0 : headerLens.AppendElement(aHeaders[i]->bytes);
157 : }
158 : // Save header packets for the decoder
159 0 : if (!XiphHeadersToExtradata(aBuffer, headers, headerLens)) {
160 0 : return false;
161 : }
162 0 : aHeaders.Erase();
163 0 : return true;
164 : }
165 :
166 : void
167 0 : VorbisState::RecordVorbisPacketSamples(ogg_packet* aPacket, long aSamples)
168 : {
169 : #ifdef VALIDATE_VORBIS_SAMPLE_CALCULATION
170 0 : mVorbisPacketSamples[aPacket] = aSamples;
171 : #endif
172 0 : }
173 :
174 : void
175 0 : VorbisState::ValidateVorbisPacketSamples(ogg_packet* aPacket, long aSamples)
176 : {
177 : #ifdef VALIDATE_VORBIS_SAMPLE_CALCULATION
178 0 : NS_ASSERTION(mVorbisPacketSamples[aPacket] == aSamples,
179 : "Decoded samples for Vorbis packet don't match expected!");
180 0 : mVorbisPacketSamples.erase(aPacket);
181 : #endif
182 0 : }
183 :
184 : void
185 0 : VorbisState::AssertHasRecordedPacketSamples(ogg_packet* aPacket)
186 : {
187 : #ifdef VALIDATE_VORBIS_SAMPLE_CALCULATION
188 0 : NS_ASSERTION(mVorbisPacketSamples.count(aPacket) == 1,
189 : "Must have recorded packet samples");
190 : #endif
191 0 : }
192 :
193 : static OggPacketPtr
194 0 : Clone(ogg_packet* aPacket)
195 : {
196 0 : ogg_packet* p = new ogg_packet();
197 0 : memcpy(p, aPacket, sizeof(ogg_packet));
198 0 : p->packet = new unsigned char[p->bytes];
199 0 : memcpy(p->packet, aPacket->packet, p->bytes);
200 0 : return OggPacketPtr(p);
201 : }
202 :
203 : void
204 0 : OggPacketQueue::Append(OggPacketPtr aPacket)
205 : {
206 0 : nsDeque::Push(aPacket.release());
207 0 : }
208 :
209 : bool
210 0 : OggCodecState::IsPacketReady()
211 : {
212 0 : return !mPackets.IsEmpty();
213 : }
214 :
215 : OggPacketPtr
216 0 : OggCodecState::PacketOut()
217 : {
218 0 : if (mPackets.IsEmpty()) {
219 0 : return nullptr;
220 : }
221 0 : return mPackets.PopFront();
222 : }
223 :
224 : ogg_packet*
225 0 : OggCodecState::PacketPeek()
226 : {
227 0 : if (mPackets.IsEmpty()) {
228 0 : return nullptr;
229 : }
230 0 : return mPackets.PeekFront();
231 : }
232 :
233 : void
234 0 : OggCodecState::PushFront(OggPacketQueue&& aOther)
235 : {
236 0 : while (!aOther.IsEmpty()) {
237 0 : mPackets.PushFront(aOther.Pop());
238 : }
239 0 : }
240 :
241 : already_AddRefed<MediaRawData>
242 0 : OggCodecState::PacketOutAsMediaRawData()
243 : {
244 0 : OggPacketPtr packet = PacketOut();
245 0 : if (!packet) {
246 0 : return nullptr;
247 : }
248 :
249 0 : NS_ASSERTION(
250 : !IsHeader(packet.get()),
251 : "PacketOutAsMediaRawData can only be called on non-header packets");
252 0 : RefPtr<MediaRawData> sample = new MediaRawData(packet->packet, packet->bytes);
253 0 : if (packet->bytes && !sample->Data()) {
254 : // OOM.
255 0 : return nullptr;
256 : }
257 :
258 0 : int64_t end_tstamp = Time(packet->granulepos);
259 0 : NS_ASSERTION(end_tstamp >= 0, "timestamp invalid");
260 :
261 0 : int64_t duration = PacketDuration(packet.get());
262 0 : NS_ASSERTION(duration >= 0, "duration invalid");
263 :
264 0 : sample->mTimecode = TimeUnit::FromMicroseconds(packet->granulepos);
265 0 : sample->mTime = TimeUnit::FromMicroseconds(end_tstamp - duration);
266 0 : sample->mDuration = TimeUnit::FromMicroseconds(duration);
267 0 : sample->mKeyframe = IsKeyframe(packet.get());
268 0 : sample->mEOS = packet->e_o_s;
269 :
270 0 : return sample.forget();
271 : }
272 :
273 : nsresult
274 0 : OggCodecState::PageIn(ogg_page* aPage)
275 : {
276 0 : if (!mActive) {
277 0 : return NS_OK;
278 : }
279 0 : NS_ASSERTION(static_cast<uint32_t>(ogg_page_serialno(aPage)) == mSerial,
280 : "Page must be for this stream!");
281 0 : if (ogg_stream_pagein(&mState, aPage) == -1) {
282 0 : return NS_ERROR_FAILURE;
283 : }
284 : int r;
285 0 : do {
286 : ogg_packet packet;
287 0 : r = ogg_stream_packetout(&mState, &packet);
288 0 : if (r == 1) {
289 0 : mPackets.Append(Clone(&packet));
290 : }
291 0 : } while (r != 0);
292 0 : if (ogg_stream_check(&mState)) {
293 0 : NS_WARNING("Unrecoverable error in ogg_stream_packetout");
294 0 : return NS_ERROR_FAILURE;
295 : }
296 0 : return NS_OK;
297 : }
298 :
299 : nsresult
300 0 : OggCodecState::PacketOutUntilGranulepos(bool& aFoundGranulepos)
301 : {
302 : int r;
303 0 : aFoundGranulepos = false;
304 : // Extract packets from the sync state until either no more packets
305 : // come out, or we get a data packet with non -1 granulepos.
306 0 : do {
307 : ogg_packet packet;
308 0 : r = ogg_stream_packetout(&mState, &packet);
309 0 : if (r == 1) {
310 0 : OggPacketPtr clone = Clone(&packet);
311 0 : if (IsHeader(&packet)) {
312 : // Header packets go straight into the packet queue.
313 0 : mPackets.Append(Move(clone));
314 : } else {
315 : // We buffer data packets until we encounter a granulepos. We'll
316 : // then use the granulepos to figure out the granulepos of the
317 : // preceeding packets.
318 0 : mUnstamped.AppendElement(Move(clone));
319 0 : aFoundGranulepos = packet.granulepos > 0;
320 : }
321 : }
322 0 : } while (r != 0 && !aFoundGranulepos);
323 0 : if (ogg_stream_check(&mState)) {
324 0 : NS_WARNING("Unrecoverable error in ogg_stream_packetout");
325 0 : return NS_ERROR_FAILURE;
326 : }
327 0 : return NS_OK;
328 : }
329 :
330 0 : TheoraState::TheoraState(ogg_page* aBosPage)
331 : : OggCodecState(aBosPage, true)
332 : , mSetup(0)
333 0 : , mCtx(0)
334 : {
335 0 : MOZ_COUNT_CTOR(TheoraState);
336 0 : th_info_init(&mTheoraInfo);
337 0 : th_comment_init(&mComment);
338 0 : }
339 :
340 0 : TheoraState::~TheoraState()
341 : {
342 0 : MOZ_COUNT_DTOR(TheoraState);
343 0 : th_setup_free(mSetup);
344 0 : th_decode_free(mCtx);
345 0 : th_comment_clear(&mComment);
346 0 : th_info_clear(&mTheoraInfo);
347 0 : Reset();
348 0 : }
349 :
350 : bool
351 0 : TheoraState::Init()
352 : {
353 0 : if (!mActive) {
354 0 : return false;
355 : }
356 :
357 0 : int64_t n = mTheoraInfo.aspect_numerator;
358 0 : int64_t d = mTheoraInfo.aspect_denominator;
359 :
360 : float aspectRatio =
361 0 : (n == 0 || d == 0) ? 1.0f : static_cast<float>(n) / static_cast<float>(d);
362 :
363 : // Ensure the frame and picture regions aren't larger than our prescribed
364 : // maximum, or zero sized.
365 0 : nsIntSize frame(mTheoraInfo.frame_width, mTheoraInfo.frame_height);
366 : nsIntRect picture(mTheoraInfo.pic_x, mTheoraInfo.pic_y,
367 0 : mTheoraInfo.pic_width, mTheoraInfo.pic_height);
368 0 : nsIntSize display(mTheoraInfo.pic_width, mTheoraInfo.pic_height);
369 0 : ScaleDisplayByAspectRatio(display, aspectRatio);
370 0 : if (!IsValidVideoRegion(frame, picture, display)) {
371 0 : return mActive = false;
372 : }
373 :
374 0 : mCtx = th_decode_alloc(&mTheoraInfo, mSetup);
375 0 : if (!mCtx) {
376 0 : return mActive = false;
377 : }
378 :
379 : // Video track's frame sizes will not overflow. Activate the video track.
380 0 : mInfo.mMimeType = NS_LITERAL_CSTRING("video/theora");
381 0 : mInfo.mDisplay = display;
382 0 : mInfo.mImage = frame;
383 0 : mInfo.SetImageRect(picture);
384 :
385 0 : return mActive = SetCodecSpecificConfig(mInfo.mCodecSpecificConfig, mHeaders);
386 : }
387 :
388 : nsresult
389 0 : TheoraState::Reset()
390 : {
391 0 : mHeaders.Erase();
392 0 : return OggCodecState::Reset();
393 : }
394 :
395 : bool
396 0 : TheoraState::DecodeHeader(OggPacketPtr aPacket)
397 : {
398 0 : ogg_packet* packet = aPacket.get(); // Will be owned by mHeaders.
399 0 : mHeaders.Append(Move(aPacket));
400 0 : mPacketCount++;
401 0 : int ret = th_decode_headerin(&mTheoraInfo,
402 : &mComment,
403 : &mSetup,
404 0 : packet);
405 :
406 : // We must determine when we've read the last header packet.
407 : // th_decode_headerin() does not tell us when it's read the last header, so
408 : // we must keep track of the headers externally.
409 : //
410 : // There are 3 header packets, the Identification, Comment, and Setup
411 : // headers, which must be in that order. If they're out of order, the file
412 : // is invalid. If we've successfully read a header, and it's the setup
413 : // header, then we're done reading headers. The first byte of each packet
414 : // determines it's type as follows:
415 : // 0x80 -> Identification header
416 : // 0x81 -> Comment header
417 : // 0x82 -> Setup header
418 : // See http://www.theora.org/doc/Theora.pdf Chapter 6, "Bitstream Headers",
419 : // for more details of the Ogg/Theora containment scheme.
420 0 : bool isSetupHeader = packet->bytes > 0 && packet->packet[0] == 0x82;
421 0 : if (ret < 0 || mPacketCount > 3) {
422 : // We've received an error, or the first three packets weren't valid
423 : // header packets. Assume bad input.
424 : // Our caller will deactivate the bitstream.
425 0 : return false;
426 0 : } else if (ret > 0 && isSetupHeader && mPacketCount == 3) {
427 : // Successfully read the three header packets.
428 0 : mDoneReadingHeaders = true;
429 : }
430 0 : return true;
431 : }
432 :
433 : int64_t
434 0 : TheoraState::Time(int64_t granulepos)
435 : {
436 0 : if (!mActive) {
437 0 : return -1;
438 : }
439 0 : return TheoraState::Time(&mTheoraInfo, granulepos);
440 : }
441 :
442 : bool
443 0 : TheoraState::IsHeader(ogg_packet* aPacket)
444 : {
445 0 : return th_packet_isheader(aPacket);
446 : }
447 :
448 : # define TH_VERSION_CHECK(_info,_maj,_min,_sub) \
449 : (((_info)->version_major>(_maj)||(_info)->version_major==(_maj)) \
450 : && (((_info)->version_minor>(_min)||(_info)->version_minor==(_min)) \
451 : && (_info)->version_subminor>=(_sub)))
452 :
453 : int64_t
454 0 : TheoraState::Time(th_info* aInfo, int64_t aGranulepos)
455 : {
456 0 : if (aGranulepos < 0 || aInfo->fps_numerator == 0) {
457 0 : return -1;
458 : }
459 : // Implementation of th_granule_frame inlined here to operate
460 : // on the th_info structure instead of the theora_state.
461 0 : int shift = aInfo->keyframe_granule_shift;
462 0 : ogg_int64_t iframe = aGranulepos >> shift;
463 0 : ogg_int64_t pframe = aGranulepos - (iframe << shift);
464 0 : int64_t frameno = iframe + pframe - TH_VERSION_CHECK(aInfo, 3, 2, 1);
465 : CheckedInt64 t =
466 0 : ((CheckedInt64(frameno) + 1) * USECS_PER_S) * aInfo->fps_denominator;
467 0 : if (!t.isValid()) {
468 0 : return -1;
469 : }
470 0 : t /= aInfo->fps_numerator;
471 0 : return t.isValid() ? t.value() : -1;
472 : }
473 :
474 0 : int64_t TheoraState::StartTime(int64_t granulepos)
475 : {
476 0 : if (granulepos < 0 || !mActive || mTheoraInfo.fps_numerator == 0) {
477 0 : return -1;
478 : }
479 : CheckedInt64 t =
480 0 : (CheckedInt64(th_granule_frame(mCtx, granulepos)) * USECS_PER_S)
481 0 : * mTheoraInfo.fps_denominator;
482 0 : if (!t.isValid()) {
483 0 : return -1;
484 : }
485 0 : return t.value() / mTheoraInfo.fps_numerator;
486 : }
487 :
488 : int64_t
489 0 : TheoraState::PacketDuration(ogg_packet* aPacket)
490 : {
491 0 : if (!mActive || mTheoraInfo.fps_numerator == 0) {
492 0 : return -1;
493 : }
494 0 : CheckedInt64 t = SaferMultDiv(mTheoraInfo.fps_denominator, USECS_PER_S,
495 0 : mTheoraInfo.fps_numerator);
496 0 : return t.isValid() ? t.value() : -1;
497 : }
498 :
499 : int64_t
500 0 : TheoraState::MaxKeyframeOffset()
501 : {
502 : // Determine the maximum time in microseconds by which a key frame could
503 : // offset for the theora bitstream. Theora granulepos encode time as:
504 : // ((key_frame_number << granule_shift) + frame_offset).
505 : // Therefore the maximum possible time by which any frame could be offset
506 : // from a keyframe is the duration of (1 << granule_shift) - 1) frames.
507 : int64_t frameDuration;
508 :
509 : // Max number of frames keyframe could possibly be offset.
510 0 : int64_t keyframeDiff = (1 << mTheoraInfo.keyframe_granule_shift) - 1;
511 :
512 : // Length of frame in usecs.
513 0 : frameDuration =
514 0 : (mTheoraInfo.fps_denominator * USECS_PER_S) / mTheoraInfo.fps_numerator;
515 :
516 : // Total time in usecs keyframe can be offset from any given frame.
517 0 : return frameDuration * keyframeDiff;
518 : }
519 :
520 : bool
521 0 : TheoraState::IsKeyframe(ogg_packet* pkt)
522 : {
523 : // first bit of packet is 1 for header, 0 for data
524 : // second bit of packet is 1 for inter frame, 0 for intra frame
525 0 : return (pkt->bytes >= 1 && (pkt->packet[0] & 0x40) == 0x00);
526 : }
527 :
528 : nsresult
529 0 : TheoraState::PageIn(ogg_page* aPage)
530 : {
531 0 : if (!mActive)
532 0 : return NS_OK;
533 0 : NS_ASSERTION(static_cast<uint32_t>(ogg_page_serialno(aPage)) == mSerial,
534 : "Page must be for this stream!");
535 0 : if (ogg_stream_pagein(&mState, aPage) == -1)
536 0 : return NS_ERROR_FAILURE;
537 : bool foundGp;
538 0 : nsresult res = PacketOutUntilGranulepos(foundGp);
539 0 : if (NS_FAILED(res))
540 0 : return res;
541 0 : if (foundGp && mDoneReadingHeaders) {
542 : // We've found a packet with a granulepos, and we've loaded our metadata
543 : // and initialized our decoder. Determine granulepos of buffered packets.
544 0 : ReconstructTheoraGranulepos();
545 0 : for (uint32_t i = 0; i < mUnstamped.Length(); ++i) {
546 0 : OggPacketPtr packet = Move(mUnstamped[i]);
547 : #ifdef DEBUG
548 0 : NS_ASSERTION(!IsHeader(packet.get()), "Don't try to recover header packet gp");
549 0 : NS_ASSERTION(packet->granulepos != -1, "Packet must have gp by now");
550 : #endif
551 0 : mPackets.Append(Move(packet));
552 : }
553 0 : mUnstamped.Clear();
554 : }
555 0 : return NS_OK;
556 : }
557 :
558 : // Returns 1 if the Theora info struct is decoding a media of Theora
559 : // version (maj,min,sub) or later, otherwise returns 0.
560 : int
561 0 : TheoraVersion(th_info* info,
562 : unsigned char maj,
563 : unsigned char min,
564 : unsigned char sub)
565 : {
566 0 : ogg_uint32_t ver = (maj << 16) + (min << 8) + sub;
567 0 : ogg_uint32_t th_ver = (info->version_major << 16)
568 0 : + (info->version_minor << 8)
569 0 : + info->version_subminor;
570 0 : return (th_ver >= ver) ? 1 : 0;
571 : }
572 :
573 : void
574 0 : TheoraState::ReconstructTheoraGranulepos()
575 : {
576 0 : if (mUnstamped.Length() == 0) {
577 0 : return;
578 : }
579 0 : ogg_int64_t lastGranulepos = mUnstamped[mUnstamped.Length() - 1]->granulepos;
580 0 : NS_ASSERTION(lastGranulepos != -1, "Must know last granulepos");
581 :
582 : // Reconstruct the granulepos (and thus timestamps) of the decoded
583 : // frames. Granulepos are stored as ((keyframe<<shift)+offset). We
584 : // know the granulepos of the last frame in the list, so we can infer
585 : // the granulepos of the intermediate frames using their frame numbers.
586 0 : ogg_int64_t shift = mTheoraInfo.keyframe_granule_shift;
587 0 : ogg_int64_t version_3_2_1 = TheoraVersion(&mTheoraInfo,3,2,1);
588 : ogg_int64_t lastFrame =
589 0 : th_granule_frame(mCtx, lastGranulepos) + version_3_2_1;
590 0 : ogg_int64_t firstFrame = lastFrame - mUnstamped.Length() + 1;
591 :
592 : // Until we encounter a keyframe, we'll assume that the "keyframe"
593 : // segment of the granulepos is the first frame, or if that causes
594 : // the "offset" segment to overflow, we assume the required
595 : // keyframe is maximumally offset. Until we encounter a keyframe
596 : // the granulepos will probably be wrong, but we can't decode the
597 : // frame anyway (since we don't have its keyframe) so it doesn't really
598 : // matter.
599 0 : ogg_int64_t keyframe = lastGranulepos >> shift;
600 :
601 : // The lastFrame, firstFrame, keyframe variables, as well as the frame
602 : // variable in the loop below, store the frame number for Theora
603 : // version >= 3.2.1 streams, and store the frame index for Theora
604 : // version < 3.2.1 streams.
605 0 : for (uint32_t i = 0; i < mUnstamped.Length() - 1; ++i) {
606 0 : ogg_int64_t frame = firstFrame + i;
607 : ogg_int64_t granulepos;
608 0 : auto& packet = mUnstamped[i];
609 0 : bool isKeyframe = th_packet_iskeyframe(packet.get()) == 1;
610 :
611 0 : if (isKeyframe) {
612 0 : granulepos = frame << shift;
613 0 : keyframe = frame;
614 0 : } else if (frame >= keyframe
615 0 : && frame - keyframe < ((ogg_int64_t)1 << shift))
616 : {
617 : // (frame - keyframe) won't overflow the "offset" segment of the
618 : // granulepos, so it's safe to calculate the granulepos.
619 0 : granulepos = (keyframe << shift) + (frame - keyframe);
620 : } else {
621 : // (frame - keyframeno) will overflow the "offset" segment of the
622 : // granulepos, so we take "keyframe" to be the max possible offset
623 : // frame instead.
624 : ogg_int64_t k =
625 0 : std::max(frame - (((ogg_int64_t)1 << shift) - 1), version_3_2_1);
626 0 : granulepos = (k << shift) + (frame - k);
627 : }
628 : // Theora 3.2.1+ granulepos store frame number [1..N], so granulepos
629 : // should be > 0.
630 : // Theora 3.2.0 granulepos store the frame index [0..(N-1)], so
631 : // granulepos should be >= 0.
632 0 : NS_ASSERTION(granulepos >= version_3_2_1,
633 : "Invalid granulepos for Theora version");
634 :
635 : // Check that the frame's granule number is one more than the
636 : // previous frame's.
637 0 : NS_ASSERTION(i == 0
638 : || th_granule_frame(mCtx, granulepos)
639 : == th_granule_frame(mCtx, mUnstamped[i-1]->granulepos)
640 : + 1,
641 : "Granulepos calculation is incorrect!");
642 :
643 0 : packet->granulepos = granulepos;
644 : }
645 :
646 : // Check that the second to last frame's granule number is one less than
647 : // the last frame's (the known granule number). If not our granulepos
648 : // recovery missed a beat.
649 0 : NS_ASSERTION(
650 : mUnstamped.Length() < 2
651 : || th_granule_frame(mCtx, mUnstamped[mUnstamped.Length() - 2]->granulepos)
652 : + 1
653 : == th_granule_frame(mCtx, lastGranulepos),
654 : "Granulepos recovery should catch up with packet->granulepos!");
655 : }
656 :
657 : nsresult
658 0 : VorbisState::Reset()
659 : {
660 0 : nsresult res = NS_OK;
661 0 : if (mActive && vorbis_synthesis_restart(&mDsp) != 0) {
662 0 : res = NS_ERROR_FAILURE;
663 : }
664 0 : mHeaders.Erase();
665 0 : if (NS_FAILED(OggCodecState::Reset())) {
666 0 : return NS_ERROR_FAILURE;
667 : }
668 :
669 0 : mGranulepos = 0;
670 0 : mPrevVorbisBlockSize = 0;
671 :
672 0 : return res;
673 : }
674 :
675 0 : VorbisState::VorbisState(ogg_page* aBosPage)
676 : : OggCodecState(aBosPage, true)
677 : , mPrevVorbisBlockSize(0)
678 0 : , mGranulepos(0)
679 : {
680 0 : MOZ_COUNT_CTOR(VorbisState);
681 0 : vorbis_info_init(&mVorbisInfo);
682 0 : vorbis_comment_init(&mComment);
683 0 : memset(&mDsp, 0, sizeof(vorbis_dsp_state));
684 0 : memset(&mBlock, 0, sizeof(vorbis_block));
685 0 : }
686 :
687 0 : VorbisState::~VorbisState()
688 : {
689 0 : MOZ_COUNT_DTOR(VorbisState);
690 0 : Reset();
691 0 : vorbis_block_clear(&mBlock);
692 0 : vorbis_dsp_clear(&mDsp);
693 0 : vorbis_info_clear(&mVorbisInfo);
694 0 : vorbis_comment_clear(&mComment);
695 0 : }
696 :
697 : bool
698 0 : VorbisState::DecodeHeader(OggPacketPtr aPacket)
699 : {
700 0 : ogg_packet* packet = aPacket.get(); // Will be owned by mHeaders.
701 0 : mHeaders.Append(Move(aPacket));
702 0 : mPacketCount++;
703 0 : int ret = vorbis_synthesis_headerin(&mVorbisInfo,
704 : &mComment,
705 0 : packet);
706 : // We must determine when we've read the last header packet.
707 : // vorbis_synthesis_headerin() does not tell us when it's read the last
708 : // header, so we must keep track of the headers externally.
709 : //
710 : // There are 3 header packets, the Identification, Comment, and Setup
711 : // headers, which must be in that order. If they're out of order, the file
712 : // is invalid. If we've successfully read a header, and it's the setup
713 : // header, then we're done reading headers. The first byte of each packet
714 : // determines it's type as follows:
715 : // 0x1 -> Identification header
716 : // 0x3 -> Comment header
717 : // 0x5 -> Setup header
718 : // For more details of the Vorbis/Ogg containment scheme, see the Vorbis I
719 : // Specification, Chapter 4, Codec Setup and Packet Decode:
720 : // http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-580004
721 :
722 0 : bool isSetupHeader = packet->bytes > 0 && packet->packet[0] == 0x5;
723 :
724 0 : if (ret < 0 || mPacketCount > 3) {
725 : // We've received an error, or the first three packets weren't valid
726 : // header packets. Assume bad input. Our caller will deactivate the
727 : // bitstream.
728 0 : return false;
729 0 : } else if (!ret && isSetupHeader && mPacketCount == 3) {
730 : // Successfully read the three header packets.
731 : // The bitstream remains active.
732 0 : mDoneReadingHeaders = true;
733 : }
734 :
735 0 : return true;
736 : }
737 :
738 : bool
739 0 : VorbisState::Init()
740 : {
741 0 : if (!mActive) {
742 0 : return false;
743 : }
744 :
745 0 : int ret = vorbis_synthesis_init(&mDsp, &mVorbisInfo);
746 0 : if (ret != 0) {
747 0 : NS_WARNING("vorbis_synthesis_init() failed initializing vorbis bitstream");
748 0 : return mActive = false;
749 : }
750 0 : ret = vorbis_block_init(&mDsp, &mBlock);
751 0 : if (ret != 0) {
752 0 : NS_WARNING("vorbis_block_init() failed initializing vorbis bitstream");
753 0 : if (mActive) {
754 0 : vorbis_dsp_clear(&mDsp);
755 : }
756 0 : return mActive = false;
757 : }
758 :
759 0 : nsTArray<const unsigned char*> headers;
760 0 : nsTArray<size_t> headerLens;
761 0 : for (size_t i = 0; i < mHeaders.Length(); i++) {
762 0 : headers.AppendElement(mHeaders[i]->packet);
763 0 : headerLens.AppendElement(mHeaders[i]->bytes);
764 : }
765 : // Save header packets for the decoder
766 0 : if (!XiphHeadersToExtradata(mInfo.mCodecSpecificConfig,
767 : headers, headerLens)) {
768 0 : return mActive = false;
769 : }
770 0 : mHeaders.Erase();
771 0 : mInfo.mMimeType = NS_LITERAL_CSTRING("audio/vorbis");
772 0 : mInfo.mRate = mVorbisInfo.rate;
773 0 : mInfo.mChannels = mVorbisInfo.channels;
774 0 : mInfo.mBitDepth = 16;
775 :
776 0 : return true;
777 : }
778 :
779 : int64_t
780 0 : VorbisState::Time(int64_t granulepos)
781 : {
782 0 : if (!mActive) {
783 0 : return -1;
784 : }
785 :
786 0 : return VorbisState::Time(&mVorbisInfo, granulepos);
787 : }
788 :
789 : int64_t
790 0 : VorbisState::Time(vorbis_info* aInfo, int64_t aGranulepos)
791 : {
792 0 : if (aGranulepos == -1 || aInfo->rate == 0) {
793 0 : return -1;
794 : }
795 0 : CheckedInt64 t = SaferMultDiv(aGranulepos, USECS_PER_S, aInfo->rate);
796 0 : return t.isValid() ? t.value() : 0;
797 : }
798 :
799 : int64_t
800 0 : VorbisState::PacketDuration(ogg_packet* aPacket)
801 : {
802 0 : if (!mActive) {
803 0 : return -1;
804 : }
805 0 : if (aPacket->granulepos == -1) {
806 0 : return -1;
807 : }
808 : // @FIXME store these in a more stable place
809 0 : if (mVorbisPacketSamples.count(aPacket) == 0) {
810 : // We haven't seen this packet, don't know its size?
811 0 : return -1;
812 : }
813 :
814 0 : long samples = mVorbisPacketSamples[aPacket];
815 0 : return Time(samples);
816 : }
817 :
818 : bool
819 0 : VorbisState::IsHeader(ogg_packet* aPacket)
820 : {
821 : // The first byte in each Vorbis header packet is either 0x01, 0x03, or 0x05,
822 : // i.e. the first bit is odd. Audio data packets have their first bit as 0x0.
823 : // Any packet with its first bit set cannot be a data packet, it's a
824 : // (possibly invalid) header packet.
825 : // See: http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-610004.2.1
826 0 : return aPacket->bytes > 0 ? (aPacket->packet[0] & 0x1) : false;
827 : }
828 :
829 : MetadataTags*
830 0 : VorbisState::GetTags()
831 : {
832 : MetadataTags* tags;
833 0 : NS_ASSERTION(mComment.user_comments, "no vorbis comment strings!");
834 0 : NS_ASSERTION(mComment.comment_lengths, "no vorbis comment lengths!");
835 0 : tags = new MetadataTags;
836 0 : for (int i = 0; i < mComment.comments; i++) {
837 0 : AddVorbisComment(tags, mComment.user_comments[i],
838 0 : mComment.comment_lengths[i]);
839 : }
840 0 : return tags;
841 : }
842 :
843 : nsresult
844 0 : VorbisState::PageIn(ogg_page* aPage)
845 : {
846 0 : if (!mActive) {
847 0 : return NS_OK;
848 : }
849 0 : NS_ASSERTION(static_cast<uint32_t>(ogg_page_serialno(aPage)) == mSerial,
850 : "Page must be for this stream!");
851 0 : if (ogg_stream_pagein(&mState, aPage) == -1)
852 0 : return NS_ERROR_FAILURE;
853 : bool foundGp;
854 0 : nsresult res = PacketOutUntilGranulepos(foundGp);
855 0 : if (NS_FAILED(res)) {
856 0 : return res;
857 : }
858 0 : if (foundGp && mDoneReadingHeaders) {
859 : // We've found a packet with a granulepos, and we've loaded our metadata
860 : // and initialized our decoder. Determine granulepos of buffered packets.
861 0 : ReconstructVorbisGranulepos();
862 0 : for (uint32_t i = 0; i < mUnstamped.Length(); ++i) {
863 0 : OggPacketPtr packet = Move(mUnstamped[i]);
864 0 : AssertHasRecordedPacketSamples(packet.get());
865 0 : NS_ASSERTION(!IsHeader(packet.get()), "Don't try to recover header packet gp");
866 0 : NS_ASSERTION(packet->granulepos != -1, "Packet must have gp by now");
867 0 : mPackets.Append(Move(packet));
868 : }
869 0 : mUnstamped.Clear();
870 : }
871 0 : return NS_OK;
872 : }
873 :
874 : nsresult
875 0 : VorbisState::ReconstructVorbisGranulepos()
876 : {
877 : // The number of samples in a Vorbis packet is:
878 : // window_blocksize(previous_packet)/4+window_blocksize(current_packet)/4
879 : // See: http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-230001.3.2
880 : // So we maintain mPrevVorbisBlockSize, the block size of the last packet
881 : // encountered. We also maintain mGranulepos, which is the granulepos of
882 : // the last encountered packet. This enables us to give granulepos to
883 : // packets when the last packet in mUnstamped doesn't have a granulepos
884 : // (for example if the stream was truncated).
885 : //
886 : // We validate our prediction of the number of samples decoded when
887 : // VALIDATE_VORBIS_SAMPLE_CALCULATION is defined by recording the predicted
888 : // number of samples, and verifing we extract that many when decoding
889 : // each packet.
890 :
891 0 : NS_ASSERTION(mUnstamped.Length() > 0, "Length must be > 0");
892 0 : auto& last = mUnstamped.LastElement();
893 0 : NS_ASSERTION(last->e_o_s || last->granulepos >= 0,
894 : "Must know last granulepos!");
895 0 : if (mUnstamped.Length() == 1) {
896 0 : auto& packet = mUnstamped[0];
897 0 : long blockSize = vorbis_packet_blocksize(&mVorbisInfo, packet.get());
898 0 : if (blockSize < 0) {
899 : // On failure vorbis_packet_blocksize returns < 0. If we've got
900 : // a bad packet, we just assume that decode will have to skip this
901 : // packet, i.e. assume 0 samples are decodable from this packet.
902 0 : blockSize = 0;
903 0 : mPrevVorbisBlockSize = 0;
904 : }
905 0 : long samples = mPrevVorbisBlockSize / 4 + blockSize / 4;
906 0 : mPrevVorbisBlockSize = blockSize;
907 0 : if (packet->granulepos == -1) {
908 0 : packet->granulepos = mGranulepos + samples;
909 : }
910 :
911 : // Account for a partial last frame
912 0 : if (packet->e_o_s && packet->granulepos >= mGranulepos) {
913 0 : samples = packet->granulepos - mGranulepos;
914 : }
915 :
916 0 : mGranulepos = packet->granulepos;
917 0 : RecordVorbisPacketSamples(packet.get(), samples);
918 0 : return NS_OK;
919 : }
920 :
921 0 : bool unknownGranulepos = last->granulepos == -1;
922 0 : int totalSamples = 0;
923 0 : for (int32_t i = mUnstamped.Length() - 1; i > 0; i--) {
924 0 : auto& packet = mUnstamped[i];
925 0 : auto& prev = mUnstamped[i-1];
926 0 : ogg_int64_t granulepos = packet->granulepos;
927 0 : NS_ASSERTION(granulepos != -1, "Must know granulepos!");
928 0 : long prevBlockSize = vorbis_packet_blocksize(&mVorbisInfo, prev.get());
929 0 : long blockSize = vorbis_packet_blocksize(&mVorbisInfo, packet.get());
930 :
931 0 : if (blockSize < 0 || prevBlockSize < 0) {
932 : // On failure vorbis_packet_blocksize returns < 0. If we've got
933 : // a bad packet, we just assume that decode will have to skip this
934 : // packet, i.e. assume 0 samples are decodable from this packet.
935 0 : blockSize = 0;
936 0 : prevBlockSize = 0;
937 : }
938 :
939 0 : long samples = prevBlockSize / 4 + blockSize / 4;
940 0 : totalSamples += samples;
941 0 : prev->granulepos = granulepos - samples;
942 0 : RecordVorbisPacketSamples(packet.get(), samples);
943 : }
944 :
945 0 : if (unknownGranulepos) {
946 0 : for (uint32_t i = 0; i < mUnstamped.Length(); i++) {
947 0 : mUnstamped[i]->granulepos += mGranulepos + totalSamples + 1;
948 : }
949 : }
950 :
951 0 : auto& first = mUnstamped[0];
952 0 : long blockSize = vorbis_packet_blocksize(&mVorbisInfo, first.get());
953 0 : if (blockSize < 0) {
954 0 : mPrevVorbisBlockSize = 0;
955 0 : blockSize = 0;
956 : }
957 :
958 0 : long samples = (mPrevVorbisBlockSize == 0)
959 0 : ? 0
960 0 : : mPrevVorbisBlockSize / 4 + blockSize / 4;
961 0 : int64_t start = first->granulepos - samples;
962 0 : RecordVorbisPacketSamples(first.get(), samples);
963 :
964 0 : if (last->e_o_s && start < mGranulepos) {
965 : // We've calculated that there are more samples in this page than its
966 : // granulepos claims, and it's the last page in the stream. This is legal,
967 : // and we will need to prune the trailing samples when we come to decode it.
968 : // We must correct the timestamps so that they follow the last Vorbis page's
969 : // samples.
970 0 : int64_t pruned = mGranulepos - start;
971 0 : for (uint32_t i = 0; i < mUnstamped.Length() - 1; i++) {
972 0 : mUnstamped[i]->granulepos += pruned;
973 : }
974 : #ifdef VALIDATE_VORBIS_SAMPLE_CALCULATION
975 0 : mVorbisPacketSamples[last.get()] -= pruned;
976 : #endif
977 : }
978 :
979 0 : mPrevVorbisBlockSize = vorbis_packet_blocksize(&mVorbisInfo, last.get());
980 0 : mPrevVorbisBlockSize = std::max(static_cast<long>(0), mPrevVorbisBlockSize);
981 0 : mGranulepos = last->granulepos;
982 :
983 0 : return NS_OK;
984 : }
985 :
986 0 : OpusState::OpusState(ogg_page* aBosPage)
987 : : OggCodecState(aBosPage, true)
988 : , mParser(nullptr)
989 : , mDecoder(nullptr)
990 : , mPrevPacketGranulepos(0)
991 0 : , mPrevPageGranulepos(0)
992 : {
993 0 : MOZ_COUNT_CTOR(OpusState);
994 0 : }
995 :
996 0 : OpusState::~OpusState()
997 : {
998 0 : MOZ_COUNT_DTOR(OpusState);
999 0 : Reset();
1000 :
1001 0 : if (mDecoder) {
1002 0 : opus_multistream_decoder_destroy(mDecoder);
1003 0 : mDecoder = nullptr;
1004 : }
1005 0 : }
1006 :
1007 : nsresult
1008 0 : OpusState::Reset()
1009 : {
1010 0 : return Reset(false);
1011 : }
1012 :
1013 : nsresult
1014 0 : OpusState::Reset(bool aStart)
1015 : {
1016 0 : nsresult res = NS_OK;
1017 :
1018 0 : if (mActive && mDecoder) {
1019 : // Reset the decoder.
1020 0 : opus_multistream_decoder_ctl(mDecoder, OPUS_RESET_STATE);
1021 : // This lets us distinguish the first page being the last page vs. just
1022 : // not having processed the previous page when we encounter the last page.
1023 0 : mPrevPageGranulepos = aStart ? 0 : -1;
1024 0 : mPrevPacketGranulepos = aStart ? 0 : -1;
1025 : }
1026 :
1027 : // Clear queued data.
1028 0 : if (NS_FAILED(OggCodecState::Reset())) {
1029 0 : return NS_ERROR_FAILURE;
1030 : }
1031 :
1032 0 : LOG(LogLevel::Debug, ("Opus decoder reset"));
1033 :
1034 0 : return res;
1035 : }
1036 :
1037 : bool
1038 0 : OpusState::Init(void)
1039 : {
1040 0 : if (!mActive) {
1041 0 : return false;
1042 : }
1043 :
1044 : int error;
1045 :
1046 0 : NS_ASSERTION(mDecoder == nullptr, "leaking OpusDecoder");
1047 :
1048 0 : mDecoder = opus_multistream_decoder_create(mParser->mRate,
1049 0 : mParser->mChannels,
1050 0 : mParser->mStreams,
1051 0 : mParser->mCoupledStreams,
1052 0 : mParser->mMappingTable,
1053 : &error);
1054 :
1055 0 : mInfo.mMimeType = NS_LITERAL_CSTRING("audio/opus");
1056 0 : mInfo.mRate = mParser->mRate;
1057 0 : mInfo.mChannels = mParser->mChannels;
1058 0 : mInfo.mBitDepth = 16;
1059 : // Save preskip & the first header packet for the Opus decoder
1060 0 : OpusDataDecoder::AppendCodecDelay(mInfo.mCodecSpecificConfig,
1061 0 : Time(0, mParser->mPreSkip));
1062 0 : if (!mHeaders.PeekFront()) {
1063 0 : return false;
1064 : }
1065 0 : mInfo.mCodecSpecificConfig->AppendElements(mHeaders.PeekFront()->packet,
1066 0 : mHeaders.PeekFront()->bytes);
1067 0 : mHeaders.Erase();
1068 0 : LOG(LogLevel::Debug, ("Opus decoder init"));
1069 :
1070 0 : return error == OPUS_OK;
1071 : }
1072 :
1073 : bool
1074 0 : OpusState::DecodeHeader(OggPacketPtr aPacket)
1075 : {
1076 0 : switch(mPacketCount++) {
1077 : // Parse the id header.
1078 : case 0:
1079 0 : mParser = new OpusParser;
1080 0 : if (!mParser->DecodeHeader(aPacket->packet, aPacket->bytes)) {
1081 0 : return false;
1082 : }
1083 0 : mHeaders.Append(Move(aPacket));
1084 0 : break;
1085 :
1086 : // Parse the metadata header.
1087 : case 1:
1088 0 : if (!mParser->DecodeTags(aPacket->packet, aPacket->bytes)) {
1089 0 : return false;
1090 : }
1091 0 : break;
1092 :
1093 : // We made it to the first data packet (which includes reconstructing
1094 : // timestamps for it in PageIn). Success!
1095 : default:
1096 0 : mDoneReadingHeaders = true;
1097 : // Put it back on the queue so we can decode it.
1098 0 : mPackets.PushFront(Move(aPacket));
1099 0 : break;
1100 : }
1101 0 : return true;
1102 : }
1103 :
1104 : /* Construct and return a tags hashmap from our internal array */
1105 : MetadataTags*
1106 0 : OpusState::GetTags()
1107 : {
1108 : MetadataTags* tags;
1109 :
1110 0 : tags = new MetadataTags;
1111 0 : for (uint32_t i = 0; i < mParser->mTags.Length(); i++) {
1112 0 : AddVorbisComment(tags, mParser->mTags[i].Data(),
1113 0 : mParser->mTags[i].Length());
1114 : }
1115 :
1116 0 : return tags;
1117 : }
1118 :
1119 : /* Return the timestamp (in microseconds) equivalent to a granulepos. */
1120 : int64_t
1121 0 : OpusState::Time(int64_t aGranulepos)
1122 : {
1123 0 : if (!mActive) {
1124 0 : return -1;
1125 : }
1126 :
1127 0 : return Time(mParser->mPreSkip, aGranulepos);
1128 : }
1129 :
1130 : int64_t
1131 0 : OpusState::Time(int aPreSkip, int64_t aGranulepos)
1132 : {
1133 0 : if (aGranulepos < 0) {
1134 0 : return -1;
1135 : }
1136 :
1137 : // Ogg Opus always runs at a granule rate of 48 kHz.
1138 0 : CheckedInt64 t = SaferMultDiv(aGranulepos - aPreSkip, USECS_PER_S, 48000);
1139 0 : return t.isValid() ? t.value() : -1;
1140 : }
1141 :
1142 : bool
1143 0 : OpusState::IsHeader(ogg_packet* aPacket)
1144 : {
1145 0 : return aPacket->bytes >= 16
1146 0 : && (!memcmp(aPacket->packet, "OpusHead", 8)
1147 0 : || !memcmp(aPacket->packet, "OpusTags", 8));
1148 : }
1149 :
1150 : nsresult
1151 0 : OpusState::PageIn(ogg_page* aPage)
1152 : {
1153 0 : if (!mActive) {
1154 0 : return NS_OK;
1155 : }
1156 0 : NS_ASSERTION(static_cast<uint32_t>(ogg_page_serialno(aPage)) == mSerial,
1157 : "Page must be for this stream!");
1158 0 : if (ogg_stream_pagein(&mState, aPage) == -1)
1159 0 : return NS_ERROR_FAILURE;
1160 :
1161 : bool haveGranulepos;
1162 0 : nsresult rv = PacketOutUntilGranulepos(haveGranulepos);
1163 0 : if (NS_FAILED(rv) || !haveGranulepos || mPacketCount < 2) {
1164 0 : return rv;
1165 : }
1166 0 : if (!ReconstructOpusGranulepos()) {
1167 0 : return NS_ERROR_FAILURE;
1168 : }
1169 0 : for (uint32_t i = 0; i < mUnstamped.Length(); i++) {
1170 0 : OggPacketPtr packet = Move(mUnstamped[i]);
1171 0 : NS_ASSERTION(!IsHeader(packet.get()), "Don't try to play a header packet");
1172 0 : NS_ASSERTION(packet->granulepos != -1, "Packet should have a granulepos");
1173 0 : mPackets.Append(Move(packet));
1174 : }
1175 0 : mUnstamped.Clear();
1176 0 : return NS_OK;
1177 : }
1178 :
1179 : // Helper method to return the change in granule position due to an Opus packet
1180 : // (as distinct from the number of samples in the packet, which depends on the
1181 : // decoder rate). It should work with a multistream Opus file, and continue to
1182 : // work should we ever allow the decoder to decode at a rate other than 48 kHz.
1183 : // It even works before we've created the actual Opus decoder.
1184 : static int
1185 0 : GetOpusDeltaGP(ogg_packet* packet)
1186 : {
1187 : int nframes;
1188 0 : nframes = opus_packet_get_nb_frames(packet->packet, packet->bytes);
1189 0 : if (nframes > 0) {
1190 0 : return nframes*opus_packet_get_samples_per_frame(packet->packet, 48000);
1191 : }
1192 0 : NS_WARNING("Invalid Opus packet.");
1193 0 : return nframes;
1194 : }
1195 :
1196 : int64_t
1197 0 : OpusState::PacketDuration(ogg_packet* aPacket)
1198 : {
1199 0 : CheckedInt64 t = SaferMultDiv(GetOpusDeltaGP(aPacket), USECS_PER_S, 48000);
1200 0 : return t.isValid() ? t.value() : -1;
1201 : }
1202 :
1203 : bool
1204 0 : OpusState::ReconstructOpusGranulepos(void)
1205 : {
1206 0 : NS_ASSERTION(mUnstamped.Length() > 0, "Must have unstamped packets");
1207 0 : NS_ASSERTION(mUnstamped.LastElement()->e_o_s
1208 : || mUnstamped.LastElement()->granulepos > 0,
1209 : "Must know last granulepos!");
1210 : int64_t gp;
1211 : // If this is the last page, and we've seen at least one previous page (or
1212 : // this is the first page)...
1213 0 : if (mUnstamped.LastElement()->e_o_s) {
1214 0 : auto& last = mUnstamped.LastElement();
1215 0 : if (mPrevPageGranulepos != -1) {
1216 : // If this file only has one page and the final granule position is
1217 : // smaller than the pre-skip amount, we MUST reject the stream.
1218 0 : if (!mDoneReadingHeaders && last->granulepos < mParser->mPreSkip)
1219 0 : return false;
1220 0 : int64_t last_gp = last->granulepos;
1221 0 : gp = mPrevPageGranulepos;
1222 : // Loop through the packets forwards, adding the current packet's
1223 : // duration to the previous granulepos to get the value for the
1224 : // current packet.
1225 0 : for (uint32_t i = 0; i < mUnstamped.Length() - 1; ++i) {
1226 0 : auto& packet = mUnstamped[i];
1227 0 : int offset = GetOpusDeltaGP(packet.get());
1228 : // Check for error (negative offset) and overflow.
1229 0 : if (offset >= 0 && gp <= INT64_MAX - offset) {
1230 0 : gp += offset;
1231 0 : if (gp >= last_gp) {
1232 0 : NS_WARNING("Opus end trimming removed more than a full packet.");
1233 : // We were asked to remove a full packet's worth of data or more.
1234 : // Encoders SHOULD NOT produce streams like this, but we'll handle
1235 : // it for them anyway.
1236 0 : gp = last_gp;
1237 0 : mUnstamped.RemoveElementsAt(i+1, mUnstamped.Length() - (i+1));
1238 0 : packet->e_o_s = 1;
1239 : }
1240 : }
1241 0 : packet->granulepos = gp;
1242 : }
1243 0 : mPrevPageGranulepos = last_gp;
1244 0 : return true;
1245 : } else {
1246 0 : NS_WARNING("No previous granule position to use for Opus end trimming.");
1247 : // If we don't have a previous granule position, fall through.
1248 : // We simply won't trim any samples from the end.
1249 : // TODO: Are we guaranteed to have seen a previous page if there is one?
1250 : }
1251 : }
1252 :
1253 0 : auto& last = mUnstamped.LastElement();
1254 0 : gp = last->granulepos;
1255 : // Loop through the packets backwards, subtracting the next
1256 : // packet's duration from its granulepos to get the value
1257 : // for the current packet.
1258 0 : for (uint32_t i = mUnstamped.Length() - 1; i > 0; i--) {
1259 0 : int offset = GetOpusDeltaGP(mUnstamped[i].get());
1260 : // Check for error (negative offset) and overflow.
1261 0 : if (offset >= 0) {
1262 0 : if (offset <= gp) {
1263 0 : gp -= offset;
1264 : } else {
1265 : // If the granule position of the first data page is smaller than the
1266 : // number of decodable audio samples on that page, then we MUST reject
1267 : // the stream.
1268 0 : if (!mDoneReadingHeaders)
1269 0 : return false;
1270 : // It's too late to reject the stream.
1271 : // If we get here, this almost certainly means the file has screwed-up
1272 : // timestamps somewhere after the first page.
1273 0 : NS_WARNING("Clamping negative Opus granulepos to zero.");
1274 0 : gp = 0;
1275 : }
1276 : }
1277 0 : mUnstamped[i - 1]->granulepos = gp;
1278 : }
1279 :
1280 : // Check to make sure the first granule position is at least as large as the
1281 : // total number of samples decodable from the first page with completed
1282 : // packets. This requires looking at the duration of the first packet, too.
1283 : // We MUST reject such streams.
1284 0 : if (!mDoneReadingHeaders && GetOpusDeltaGP(mUnstamped[0].get()) > gp) {
1285 0 : return false;
1286 : }
1287 0 : mPrevPageGranulepos = last->granulepos;
1288 0 : return true;
1289 : }
1290 :
1291 : already_AddRefed<MediaRawData>
1292 0 : OpusState::PacketOutAsMediaRawData()
1293 : {
1294 0 : ogg_packet* packet = PacketPeek();
1295 0 : if (!packet) {
1296 0 : return nullptr;
1297 : }
1298 :
1299 0 : uint32_t frames = 0;
1300 0 : const int64_t endFrame = packet->granulepos;
1301 :
1302 0 : if (packet->e_o_s) {
1303 0 : frames = GetOpusDeltaGP(packet);
1304 : }
1305 :
1306 0 : RefPtr<MediaRawData> data = OggCodecState::PacketOutAsMediaRawData();
1307 0 : if (!data) {
1308 0 : return nullptr;
1309 : }
1310 :
1311 0 : if (data->mEOS && mPrevPacketGranulepos != -1) {
1312 : // If this is the last packet, perform end trimming.
1313 0 : int64_t startFrame = mPrevPacketGranulepos;
1314 0 : frames -= std::max<int64_t>(
1315 0 : 0, std::min(endFrame - startFrame, static_cast<int64_t>(frames)));
1316 0 : data->mDiscardPadding = frames;
1317 : }
1318 :
1319 : // Save this packet's granule position in case we need to perform end
1320 : // trimming on the next packet.
1321 0 : mPrevPacketGranulepos = endFrame;
1322 :
1323 0 : return data.forget();
1324 : }
1325 :
1326 0 : FlacState::FlacState(ogg_page* aBosPage)
1327 0 : : OggCodecState(aBosPage, true)
1328 : {
1329 0 : }
1330 :
1331 : bool
1332 0 : FlacState::DecodeHeader(OggPacketPtr aPacket)
1333 : {
1334 0 : if (!mParser.DecodeHeaderBlock(aPacket->packet, aPacket->bytes)) {
1335 0 : return false;
1336 : }
1337 0 : if (mParser.HasFullMetadata()) {
1338 0 : mDoneReadingHeaders = true;
1339 : }
1340 0 : return true;
1341 : }
1342 :
1343 : int64_t
1344 0 : FlacState::Time(int64_t granulepos)
1345 : {
1346 0 : if (!mParser.mInfo.IsValid()) {
1347 0 : return -1;
1348 : }
1349 : CheckedInt64 t =
1350 0 : SaferMultDiv(granulepos, USECS_PER_S, mParser.mInfo.mRate);
1351 0 : if (!t.isValid()) {
1352 0 : return -1;
1353 : }
1354 0 : return t.value();
1355 : }
1356 :
1357 : int64_t
1358 0 : FlacState::PacketDuration(ogg_packet* aPacket)
1359 : {
1360 0 : return mParser.BlockDuration(aPacket->packet, aPacket->bytes);
1361 : }
1362 :
1363 : bool
1364 0 : FlacState::IsHeader(ogg_packet* aPacket)
1365 : {
1366 0 : return mParser.IsHeaderBlock(aPacket->packet, aPacket->bytes);
1367 : }
1368 :
1369 : nsresult
1370 0 : FlacState::PageIn(ogg_page* aPage)
1371 : {
1372 0 : if (!mActive) {
1373 0 : return NS_OK;
1374 : }
1375 0 : NS_ASSERTION(static_cast<uint32_t>(ogg_page_serialno(aPage)) == mSerial,
1376 : "Page must be for this stream!");
1377 0 : if (ogg_stream_pagein(&mState, aPage) == -1)
1378 0 : return NS_ERROR_FAILURE;
1379 : bool foundGp;
1380 0 : nsresult res = PacketOutUntilGranulepos(foundGp);
1381 0 : if (NS_FAILED(res)) {
1382 0 : return res;
1383 : }
1384 0 : if (foundGp && mDoneReadingHeaders) {
1385 : // We've found a packet with a granulepos, and we've loaded our metadata
1386 : // and initialized our decoder. Determine granulepos of buffered packets.
1387 0 : ReconstructFlacGranulepos();
1388 0 : for (uint32_t i = 0; i < mUnstamped.Length(); ++i) {
1389 0 : OggPacketPtr packet = Move(mUnstamped[i]);
1390 0 : NS_ASSERTION(!IsHeader(packet.get()), "Don't try to recover header packet gp");
1391 0 : NS_ASSERTION(packet->granulepos != -1, "Packet must have gp by now");
1392 0 : mPackets.Append(Move(packet));
1393 : }
1394 0 : mUnstamped.Clear();
1395 : }
1396 0 : return NS_OK;
1397 : }
1398 :
1399 : // Return a hash table with tag metadata.
1400 : MetadataTags*
1401 0 : FlacState::GetTags()
1402 : {
1403 0 : return mParser.GetTags();
1404 : }
1405 :
1406 : const TrackInfo*
1407 0 : FlacState::GetInfo() const
1408 : {
1409 0 : return &mParser.mInfo;
1410 : }
1411 :
1412 : bool
1413 0 : FlacState::ReconstructFlacGranulepos(void)
1414 : {
1415 0 : NS_ASSERTION(mUnstamped.Length() > 0, "Must have unstamped packets");
1416 0 : auto& last = mUnstamped.LastElement();
1417 0 : NS_ASSERTION(last->e_o_s || last->granulepos > 0,
1418 : "Must know last granulepos!");
1419 : int64_t gp;
1420 :
1421 0 : gp = last->granulepos;
1422 : // Loop through the packets backwards, subtracting the next
1423 : // packet's duration from its granulepos to get the value
1424 : // for the current packet.
1425 0 : for (uint32_t i = mUnstamped.Length() - 1; i > 0; i--) {
1426 : int offset =
1427 0 : mParser.BlockDuration(mUnstamped[i]->packet, mUnstamped[i]->bytes);
1428 : // Check for error (negative offset) and overflow.
1429 0 : if (offset >= 0) {
1430 0 : if (offset <= gp) {
1431 0 : gp -= offset;
1432 : } else {
1433 : // If the granule position of the first data page is smaller than the
1434 : // number of decodable audio samples on that page, then we MUST reject
1435 : // the stream.
1436 0 : if (!mDoneReadingHeaders) {
1437 0 : return false;
1438 : }
1439 : // It's too late to reject the stream.
1440 : // If we get here, this almost certainly means the file has screwed-up
1441 : // timestamps somewhere after the first page.
1442 0 : NS_WARNING("Clamping negative granulepos to zero.");
1443 0 : gp = 0;
1444 : }
1445 : }
1446 0 : mUnstamped[i - 1]->granulepos = gp;
1447 : }
1448 :
1449 0 : return true;
1450 : }
1451 :
1452 0 : SkeletonState::SkeletonState(ogg_page* aBosPage)
1453 : : OggCodecState(aBosPage, true)
1454 : , mVersion(0)
1455 : , mPresentationTime(0)
1456 0 : , mLength(0)
1457 : {
1458 0 : MOZ_COUNT_CTOR(SkeletonState);
1459 0 : }
1460 :
1461 0 : SkeletonState::~SkeletonState()
1462 : {
1463 0 : MOZ_COUNT_DTOR(SkeletonState);
1464 0 : }
1465 :
1466 : // Support for Ogg Skeleton 4.0, as per specification at:
1467 : // http://wiki.xiph.org/Ogg_Skeleton_4
1468 :
1469 : // Minimum length in bytes of a Skeleton header packet.
1470 : static const long SKELETON_MIN_HEADER_LEN = 28;
1471 : static const long SKELETON_4_0_MIN_HEADER_LEN = 80;
1472 :
1473 : // Minimum length in bytes of a Skeleton 4.0 index packet.
1474 : static const long SKELETON_4_0_MIN_INDEX_LEN = 42;
1475 :
1476 : // Minimum length in bytes of a Skeleton 3.0/4.0 Fisbone packet.
1477 : static const long SKELETON_MIN_FISBONE_LEN = 52;
1478 :
1479 : // Minimum possible size of a compressed index keypoint.
1480 : static const size_t MIN_KEY_POINT_SIZE = 2;
1481 :
1482 : // Byte offset of the major and minor version numbers in the
1483 : // Ogg Skeleton 4.0 header packet.
1484 : static const size_t SKELETON_VERSION_MAJOR_OFFSET = 8;
1485 : static const size_t SKELETON_VERSION_MINOR_OFFSET = 10;
1486 :
1487 : // Byte-offsets of the presentation time numerator and denominator
1488 : static const size_t SKELETON_PRESENTATION_TIME_NUMERATOR_OFFSET = 12;
1489 : static const size_t SKELETON_PRESENTATION_TIME_DENOMINATOR_OFFSET = 20;
1490 :
1491 : // Byte-offsets of the length of file field in the Skeleton 4.0 header packet.
1492 : static const size_t SKELETON_FILE_LENGTH_OFFSET = 64;
1493 :
1494 : // Byte-offsets of the fields in the Skeleton index packet.
1495 : static const size_t INDEX_SERIALNO_OFFSET = 6;
1496 : static const size_t INDEX_NUM_KEYPOINTS_OFFSET = 10;
1497 : static const size_t INDEX_TIME_DENOM_OFFSET = 18;
1498 : static const size_t INDEX_FIRST_NUMER_OFFSET = 26;
1499 : static const size_t INDEX_LAST_NUMER_OFFSET = 34;
1500 : static const size_t INDEX_KEYPOINT_OFFSET = 42;
1501 :
1502 : // Byte-offsets of the fields in the Skeleton Fisbone packet.
1503 : static const size_t FISBONE_MSG_FIELDS_OFFSET = 8;
1504 : static const size_t FISBONE_SERIALNO_OFFSET = 12;
1505 :
1506 : static bool
1507 0 : IsSkeletonBOS(ogg_packet* aPacket)
1508 : {
1509 : static_assert(SKELETON_MIN_HEADER_LEN >= 8,
1510 : "Minimum length of skeleton BOS header incorrect");
1511 0 : return aPacket->bytes >= SKELETON_MIN_HEADER_LEN
1512 0 : && memcmp(reinterpret_cast<char*>(aPacket->packet), "fishead", 8) == 0;
1513 : }
1514 :
1515 : static bool
1516 0 : IsSkeletonIndex(ogg_packet* aPacket)
1517 : {
1518 : static_assert(SKELETON_4_0_MIN_INDEX_LEN >= 5,
1519 : "Minimum length of skeleton index header incorrect");
1520 0 : return aPacket->bytes >= SKELETON_4_0_MIN_INDEX_LEN
1521 0 : && memcmp(reinterpret_cast<char*>(aPacket->packet), "index", 5) == 0;
1522 : }
1523 :
1524 : static bool
1525 0 : IsSkeletonFisbone(ogg_packet* aPacket)
1526 : {
1527 : static_assert(SKELETON_MIN_FISBONE_LEN >= 8,
1528 : "Minimum length of skeleton fisbone header incorrect");
1529 0 : return aPacket->bytes >= SKELETON_MIN_FISBONE_LEN
1530 0 : && memcmp(reinterpret_cast<char*>(aPacket->packet), "fisbone", 8) == 0;
1531 : }
1532 :
1533 : // Reads a variable length encoded integer at p. Will not read
1534 : // past aLimit. Returns pointer to character after end of integer.
1535 : static const unsigned char*
1536 0 : ReadVariableLengthInt(const unsigned char* p,
1537 : const unsigned char* aLimit,
1538 : int64_t& n)
1539 : {
1540 0 : int shift = 0;
1541 0 : int64_t byte = 0;
1542 0 : n = 0;
1543 0 : while (p < aLimit
1544 0 : && (byte & 0x80) != 0x80
1545 0 : && shift < 57)
1546 : {
1547 0 : byte = static_cast<int64_t>(*p);
1548 0 : n |= ((byte & 0x7f) << shift);
1549 0 : shift += 7;
1550 0 : p++;
1551 : }
1552 0 : return p;
1553 : }
1554 :
1555 : bool
1556 0 : SkeletonState::DecodeIndex(ogg_packet* aPacket)
1557 : {
1558 0 : NS_ASSERTION(aPacket->bytes >= SKELETON_4_0_MIN_INDEX_LEN,
1559 : "Index must be at least minimum size");
1560 0 : if (!mActive) {
1561 0 : return false;
1562 : }
1563 :
1564 : uint32_t serialno =
1565 0 : LittleEndian::readUint32(aPacket->packet + INDEX_SERIALNO_OFFSET);
1566 : int64_t numKeyPoints =
1567 0 : LittleEndian::readInt64(aPacket->packet + INDEX_NUM_KEYPOINTS_OFFSET);
1568 :
1569 0 : int64_t endTime = 0, startTime = 0;
1570 0 : const unsigned char* p = aPacket->packet;
1571 :
1572 : int64_t timeDenom =
1573 0 : LittleEndian::readInt64(aPacket->packet + INDEX_TIME_DENOM_OFFSET);
1574 0 : if (timeDenom == 0) {
1575 0 : LOG(LogLevel::Debug, ("Ogg Skeleton Index packet for stream %u has 0 "
1576 : "timestamp denominator.", serialno));
1577 0 : return (mActive = false);
1578 : }
1579 :
1580 : // Extract the start time.
1581 0 : int64_t timeRawInt = LittleEndian::readInt64(p + INDEX_FIRST_NUMER_OFFSET);
1582 0 : CheckedInt64 t = SaferMultDiv(timeRawInt, USECS_PER_S, timeDenom);
1583 0 : if (!t.isValid()) {
1584 0 : return (mActive = false);
1585 : } else {
1586 0 : startTime = t.value();
1587 : }
1588 :
1589 : // Extract the end time.
1590 0 : timeRawInt = LittleEndian::readInt64(p + INDEX_LAST_NUMER_OFFSET);
1591 0 : t = SaferMultDiv(timeRawInt, USECS_PER_S, timeDenom);
1592 0 : if (!t.isValid()) {
1593 0 : return (mActive = false);
1594 : } else {
1595 0 : endTime = t.value();
1596 : }
1597 :
1598 : // Check the numKeyPoints value read, ensure we're not going to run out of
1599 : // memory while trying to decode the index packet.
1600 : CheckedInt64 minPacketSize =
1601 0 : (CheckedInt64(numKeyPoints) * MIN_KEY_POINT_SIZE) + INDEX_KEYPOINT_OFFSET;
1602 0 : if (!minPacketSize.isValid())
1603 : {
1604 0 : return (mActive = false);
1605 : }
1606 :
1607 0 : int64_t sizeofIndex = aPacket->bytes - INDEX_KEYPOINT_OFFSET;
1608 0 : int64_t maxNumKeyPoints = sizeofIndex / MIN_KEY_POINT_SIZE;
1609 0 : if (aPacket->bytes < minPacketSize.value()
1610 0 : || numKeyPoints > maxNumKeyPoints
1611 0 : || numKeyPoints < 0) {
1612 : // Packet size is less than the theoretical minimum size, or the packet is
1613 : // claiming to store more keypoints than it's capable of storing. This means
1614 : // that the numKeyPoints field is too large or small for the packet to
1615 : // possibly contain as many packets as it claims to, so the numKeyPoints
1616 : // field is possibly malicious. Don't try decoding this index, we may run
1617 : // out of memory.
1618 0 : LOG(LogLevel::Debug, ("Possibly malicious number of key points reported "
1619 : "(%" PRId64 ") in index packet for stream %u.",
1620 : numKeyPoints,
1621 : serialno));
1622 0 : return (mActive = false);
1623 : }
1624 :
1625 0 : nsAutoPtr<nsKeyFrameIndex> keyPoints(new nsKeyFrameIndex(startTime, endTime));
1626 :
1627 0 : p = aPacket->packet + INDEX_KEYPOINT_OFFSET;
1628 0 : const unsigned char* limit = aPacket->packet + aPacket->bytes;
1629 0 : int64_t numKeyPointsRead = 0;
1630 0 : CheckedInt64 offset = 0;
1631 0 : CheckedInt64 time = 0;
1632 0 : while (p < limit && numKeyPointsRead < numKeyPoints) {
1633 0 : int64_t delta = 0;
1634 0 : p = ReadVariableLengthInt(p, limit, delta);
1635 0 : offset += delta;
1636 0 : if (p == limit
1637 0 : || !offset.isValid()
1638 0 : || offset.value() > mLength
1639 0 : || offset.value() < 0) {
1640 0 : return (mActive = false);
1641 : }
1642 0 : p = ReadVariableLengthInt(p, limit, delta);
1643 0 : time += delta;
1644 0 : if (!time.isValid()
1645 0 : || time.value() > endTime
1646 0 : || time.value() < startTime) {
1647 0 : return (mActive = false);
1648 : }
1649 0 : CheckedInt64 timeUsecs = SaferMultDiv(time.value(), USECS_PER_S, timeDenom);
1650 0 : if (!timeUsecs.isValid()) {
1651 0 : return (mActive = false);
1652 : }
1653 0 : keyPoints->Add(offset.value(), timeUsecs.value());
1654 0 : numKeyPointsRead++;
1655 : }
1656 :
1657 0 : int32_t keyPointsRead = keyPoints->Length();
1658 0 : if (keyPointsRead > 0) {
1659 0 : mIndex.Put(serialno, keyPoints.forget());
1660 : }
1661 :
1662 0 : LOG(LogLevel::Debug, ("Loaded %d keypoints for Skeleton on stream %u",
1663 : keyPointsRead, serialno));
1664 0 : return true;
1665 : }
1666 :
1667 : nsresult
1668 0 : SkeletonState::IndexedSeekTargetForTrack(uint32_t aSerialno,
1669 : int64_t aTarget,
1670 : nsKeyPoint& aResult)
1671 : {
1672 0 : nsKeyFrameIndex* index = nullptr;
1673 0 : mIndex.Get(aSerialno, &index);
1674 :
1675 0 : if (!index
1676 0 : || index->Length() == 0
1677 0 : || aTarget < index->mStartTime
1678 0 : || aTarget > index->mEndTime) {
1679 0 : return NS_ERROR_FAILURE;
1680 : }
1681 :
1682 : // Binary search to find the last key point with time less than target.
1683 0 : int start = 0;
1684 0 : int end = index->Length() - 1;
1685 0 : while (end > start) {
1686 0 : int mid = start + ((end - start + 1) >> 1);
1687 0 : if (index->Get(mid).mTime == aTarget) {
1688 0 : start = mid;
1689 0 : break;
1690 0 : } else if (index->Get(mid).mTime < aTarget) {
1691 0 : start = mid;
1692 : } else {
1693 0 : end = mid - 1;
1694 : }
1695 : }
1696 :
1697 0 : aResult = index->Get(start);
1698 0 : NS_ASSERTION(aResult.mTime <= aTarget, "Result should have time <= target");
1699 0 : return NS_OK;
1700 : }
1701 :
1702 : nsresult
1703 0 : SkeletonState::IndexedSeekTarget(int64_t aTarget,
1704 : nsTArray<uint32_t>& aTracks,
1705 : nsSeekTarget& aResult)
1706 : {
1707 0 : if (!mActive || mVersion < SKELETON_VERSION(4,0)) {
1708 0 : return NS_ERROR_FAILURE;
1709 : }
1710 : // Loop over all requested tracks' indexes, and get the keypoint for that
1711 : // seek target. Record the keypoint with the lowest offset, this will be
1712 : // our seek result. User must seek to the one with lowest offset to ensure we
1713 : // pass "keyframes" on all tracks when we decode forwards to the seek target.
1714 0 : nsSeekTarget r;
1715 0 : for (uint32_t i=0; i<aTracks.Length(); i++) {
1716 0 : nsKeyPoint k;
1717 0 : if (NS_SUCCEEDED(IndexedSeekTargetForTrack(aTracks[i], aTarget, k))
1718 0 : && k.mOffset < r.mKeyPoint.mOffset) {
1719 0 : r.mKeyPoint = k;
1720 0 : r.mSerial = aTracks[i];
1721 : }
1722 : }
1723 0 : if (r.IsNull()) {
1724 0 : return NS_ERROR_FAILURE;
1725 : }
1726 0 : LOG(LogLevel::Debug, ("Indexed seek target for time %" PRId64 " is offset %" PRId64,
1727 : aTarget, r.mKeyPoint.mOffset));
1728 0 : aResult = r;
1729 0 : return NS_OK;
1730 : }
1731 :
1732 : nsresult
1733 0 : SkeletonState::GetDuration(const nsTArray<uint32_t>& aTracks,
1734 : int64_t& aDuration)
1735 : {
1736 0 : if (!mActive
1737 0 : || mVersion < SKELETON_VERSION(4,0)
1738 0 : || !HasIndex()
1739 0 : || aTracks.Length() == 0) {
1740 0 : return NS_ERROR_FAILURE;
1741 : }
1742 0 : int64_t endTime = INT64_MIN;
1743 0 : int64_t startTime = INT64_MAX;
1744 0 : for (uint32_t i=0; i<aTracks.Length(); i++) {
1745 0 : nsKeyFrameIndex* index = nullptr;
1746 0 : mIndex.Get(aTracks[i], &index);
1747 0 : if (!index) {
1748 : // Can't get the timestamps for one of the required tracks, fail.
1749 0 : return NS_ERROR_FAILURE;
1750 : }
1751 0 : if (index->mEndTime > endTime) {
1752 0 : endTime = index->mEndTime;
1753 : }
1754 0 : if (index->mStartTime < startTime) {
1755 0 : startTime = index->mStartTime;
1756 : }
1757 : }
1758 0 : NS_ASSERTION(endTime > startTime, "Duration must be positive");
1759 0 : CheckedInt64 duration = CheckedInt64(endTime) - startTime;
1760 0 : aDuration = duration.isValid() ? duration.value() : 0;
1761 0 : return duration.isValid() ? NS_OK : NS_ERROR_FAILURE;
1762 : }
1763 :
1764 : bool
1765 0 : SkeletonState::DecodeFisbone(ogg_packet* aPacket)
1766 : {
1767 0 : if (aPacket->bytes < static_cast<long>(FISBONE_MSG_FIELDS_OFFSET + 4)) {
1768 0 : return false;
1769 : }
1770 : uint32_t offsetMsgField =
1771 0 : LittleEndian::readUint32(aPacket->packet + FISBONE_MSG_FIELDS_OFFSET);
1772 :
1773 0 : if (aPacket->bytes < static_cast<long>(FISBONE_SERIALNO_OFFSET + 4)) {
1774 0 : return false;
1775 : }
1776 : uint32_t serialno =
1777 0 : LittleEndian::readUint32(aPacket->packet + FISBONE_SERIALNO_OFFSET);
1778 :
1779 : CheckedUint32 checked_fields_pos =
1780 0 : CheckedUint32(FISBONE_MSG_FIELDS_OFFSET) + offsetMsgField;
1781 0 : if (!checked_fields_pos.isValid() ||
1782 0 : aPacket->bytes < static_cast<int64_t>(checked_fields_pos.value())) {
1783 0 : return false;
1784 : }
1785 0 : int64_t msgLength = aPacket->bytes - checked_fields_pos.value();
1786 0 : char* msgProbe = (char*)aPacket->packet + checked_fields_pos.value();
1787 0 : char* msgHead = msgProbe;
1788 0 : nsAutoPtr<MessageField> field(new MessageField());
1789 :
1790 : const static FieldPatternType kFieldTypeMaps[] = {
1791 : {"Content-Type:", eContentType},
1792 : {"Role:", eRole},
1793 : {"Name:", eName},
1794 : {"Language:", eLanguage},
1795 : {"Title:", eTitle},
1796 : {"Display-hint:", eDisplayHint},
1797 : {"Altitude:", eAltitude},
1798 : {"TrackOrder:", eTrackOrder},
1799 : {"Track dependencies:", eTrackDependencies}
1800 : };
1801 :
1802 0 : bool isContentTypeParsed = false;
1803 0 : while (msgLength > 1) {
1804 0 : if (*msgProbe == '\r' && *(msgProbe+1) == '\n') {
1805 0 : nsAutoCString strMsg(msgHead, msgProbe-msgHead);
1806 0 : for (size_t i = 0; i < ArrayLength(kFieldTypeMaps); i++) {
1807 0 : if (strMsg.Find(kFieldTypeMaps[i].mPatternToRecognize) != -1) {
1808 : // The content of message header fields follows [RFC2822], and the
1809 : // mandatory message field must be encoded in US-ASCII, others
1810 : // must be be encoded in UTF-8. "Content-Type" must come first
1811 : // for all of message header fields.
1812 : // See http://svn.annodex.net/standards/draft-pfeiffer-oggskeleton-current.txt.
1813 0 : if (i != 0 && !isContentTypeParsed) {
1814 0 : return false;
1815 : }
1816 :
1817 0 : if ((i == 0 && IsASCII(strMsg)) || (i != 0 && IsUTF8(strMsg))) {
1818 0 : EMsgHeaderType eHeaderType = kFieldTypeMaps[i].mMsgHeaderType;
1819 0 : field->mValuesStore.LookupForAdd(eHeaderType).OrInsert(
1820 0 : [i, msgHead, msgProbe] () {
1821 0 : uint32_t nameLen = strlen(kFieldTypeMaps[i].mPatternToRecognize);
1822 0 : return new nsCString(msgHead + nameLen, msgProbe - msgHead - nameLen);
1823 0 : });
1824 0 : isContentTypeParsed = i == 0 ? true : isContentTypeParsed;
1825 : }
1826 0 : break;
1827 : }
1828 : }
1829 0 : msgProbe += 2;
1830 0 : msgLength -= 2;
1831 0 : msgHead = msgProbe;
1832 0 : continue;
1833 : }
1834 0 : msgLength--;
1835 0 : msgProbe++;
1836 : }
1837 :
1838 0 : auto entry = mMsgFieldStore.LookupForAdd(serialno);
1839 0 : if (entry) {
1840 : // mMsgFieldStore has an entry for serialno already.
1841 0 : return false;
1842 : }
1843 0 : entry.OrInsert([&field]() { return field.forget(); });
1844 0 : return true;
1845 : }
1846 :
1847 : bool
1848 0 : SkeletonState::DecodeHeader(OggPacketPtr aPacket)
1849 : {
1850 0 : if (IsSkeletonBOS(aPacket.get())) {
1851 : uint16_t verMajor =
1852 0 : LittleEndian::readUint16(aPacket->packet + SKELETON_VERSION_MAJOR_OFFSET);
1853 : uint16_t verMinor =
1854 0 : LittleEndian::readUint16(aPacket->packet + SKELETON_VERSION_MINOR_OFFSET);
1855 :
1856 : // Read the presentation time. We read this before the version check as the
1857 : // presentation time exists in all versions.
1858 : int64_t n = LittleEndian::readInt64(
1859 0 : aPacket->packet + SKELETON_PRESENTATION_TIME_NUMERATOR_OFFSET);
1860 : int64_t d = LittleEndian::readInt64(
1861 0 : aPacket->packet + SKELETON_PRESENTATION_TIME_DENOMINATOR_OFFSET);
1862 0 : mPresentationTime =
1863 : d == 0 ? 0
1864 0 : : (static_cast<float>(n) / static_cast<float>(d)) * USECS_PER_S;
1865 :
1866 0 : mVersion = SKELETON_VERSION(verMajor, verMinor);
1867 : // We can only care to parse Skeleton version 4.0+.
1868 0 : if (mVersion < SKELETON_VERSION(4,0)
1869 0 : || mVersion >= SKELETON_VERSION(5,0)
1870 0 : || aPacket->bytes < SKELETON_4_0_MIN_HEADER_LEN) {
1871 0 : return false;
1872 : }
1873 :
1874 : // Extract the segment length.
1875 0 : mLength =
1876 0 : LittleEndian::readInt64(aPacket->packet + SKELETON_FILE_LENGTH_OFFSET);
1877 :
1878 0 : LOG(LogLevel::Debug, ("Skeleton segment length: %" PRId64, mLength));
1879 :
1880 : // Initialize the serialno-to-index map.
1881 0 : return true;
1882 0 : } else if (IsSkeletonIndex(aPacket.get()) && mVersion >= SKELETON_VERSION(4,0)) {
1883 0 : return DecodeIndex(aPacket.get());
1884 0 : } else if (IsSkeletonFisbone(aPacket.get())) {
1885 0 : return DecodeFisbone(aPacket.get());
1886 0 : } else if (aPacket->e_o_s) {
1887 0 : mDoneReadingHeaders = true;
1888 0 : return true;
1889 : }
1890 0 : return true;
1891 : }
1892 :
1893 : } // namespace mozilla
1894 :
|