LCOV - code coverage report
Current view: top level - dom/media/ogg - OggCodecState.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 896 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 91 0.0 %
Legend: Lines: hit not hit

          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             : 

Generated by: LCOV version 1.13