LCOV - code coverage report
Current view: directory - content/media/ogg - nsOggCodecState.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 516 0 0.0 %
Date: 2012-06-02 Functions: 54 0 0.0 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
       3                 : /* ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is Mozilla code.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is the Mozilla Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2010
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *  Chris Double <chris.double@double.co.nz>
      24                 :  *  Chris Pearce <chris@pearce.org.nz>
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      28                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : #include "nsDebug.h"
      40                 : #include "nsOggCodecState.h"
      41                 : #include "nsOggDecoder.h"
      42                 : #include <string.h>
      43                 : #include "nsTraceRefcnt.h"
      44                 : #include "VideoUtils.h"
      45                 : #include "nsBuiltinDecoderReader.h"
      46                 : 
      47                 : #include "mozilla/StandardInteger.h"
      48                 : 
      49                 : #ifdef PR_LOGGING
      50                 : extern PRLogModuleInfo* gBuiltinDecoderLog;
      51                 : #define LOG(type, msg) PR_LOG(gBuiltinDecoderLog, type, msg)
      52                 : #else
      53                 : #define LOG(type, msg)
      54                 : #endif
      55                 : 
      56                 : nsOggCodecState*
      57               0 : nsOggCodecState::Create(ogg_page* aPage)
      58                 : {
      59               0 :   NS_ASSERTION(ogg_page_bos(aPage), "Only call on BOS page!");
      60               0 :   nsAutoPtr<nsOggCodecState> codecState;
      61               0 :   if (aPage->body_len > 6 && memcmp(aPage->body+1, "theora", 6) == 0) {
      62               0 :     codecState = new nsTheoraState(aPage);
      63               0 :   } else if (aPage->body_len > 6 && memcmp(aPage->body+1, "vorbis", 6) == 0) {
      64               0 :     codecState = new nsVorbisState(aPage);
      65               0 :   } else if (aPage->body_len > 8 && memcmp(aPage->body, "fishead\0", 8) == 0) {
      66               0 :     codecState = new nsSkeletonState(aPage);
      67                 :   } else {
      68               0 :     codecState = new nsOggCodecState(aPage, false);
      69                 :   }
      70               0 :   return codecState->nsOggCodecState::Init() ? codecState.forget() : nsnull;
      71                 : }
      72                 : 
      73               0 : nsOggCodecState::nsOggCodecState(ogg_page* aBosPage, bool aActive) :
      74                 :   mPacketCount(0),
      75               0 :   mSerial(ogg_page_serialno(aBosPage)),
      76                 :   mActive(aActive),
      77               0 :   mDoneReadingHeaders(!aActive)
      78                 : {
      79               0 :   MOZ_COUNT_CTOR(nsOggCodecState);
      80               0 :   memset(&mState, 0, sizeof(ogg_stream_state));
      81               0 : }
      82                 : 
      83               0 : nsOggCodecState::~nsOggCodecState() {
      84               0 :   MOZ_COUNT_DTOR(nsOggCodecState);
      85               0 :   Reset();
      86                 : #ifdef DEBUG
      87                 :   int ret =
      88                 : #endif
      89               0 :   ogg_stream_clear(&mState);
      90               0 :   NS_ASSERTION(ret == 0, "ogg_stream_clear failed");
      91               0 : }
      92                 : 
      93               0 : nsresult nsOggCodecState::Reset() {
      94               0 :   if (ogg_stream_reset(&mState) != 0) {
      95               0 :     return NS_ERROR_FAILURE;
      96                 :   }
      97               0 :   mPackets.Erase();
      98               0 :   ClearUnstamped();
      99               0 :   return NS_OK;
     100                 : }
     101                 : 
     102               0 : void nsOggCodecState::ClearUnstamped()
     103                 : {
     104               0 :   for (PRUint32 i = 0; i < mUnstamped.Length(); ++i) {
     105               0 :     nsOggCodecState::ReleasePacket(mUnstamped[i]);
     106                 :   }
     107               0 :   mUnstamped.Clear();
     108               0 : }
     109                 : 
     110               0 : bool nsOggCodecState::Init() {
     111               0 :   int ret = ogg_stream_init(&mState, mSerial);
     112               0 :   return ret == 0;
     113                 : }
     114                 : 
     115               0 : void nsVorbisState::RecordVorbisPacketSamples(ogg_packet* aPacket,
     116                 :                                               long aSamples)
     117                 : {
     118                 : #ifdef VALIDATE_VORBIS_SAMPLE_CALCULATION
     119               0 :   mVorbisPacketSamples[aPacket] = aSamples;
     120                 : #endif
     121               0 : }
     122                 : 
     123               0 : void nsVorbisState::ValidateVorbisPacketSamples(ogg_packet* aPacket,
     124                 :                                                 long aSamples)
     125                 : {
     126                 : #ifdef VALIDATE_VORBIS_SAMPLE_CALCULATION
     127               0 :   NS_ASSERTION(mVorbisPacketSamples[aPacket] == aSamples,
     128                 :     "Decoded samples for Vorbis packet don't match expected!");
     129               0 :   mVorbisPacketSamples.erase(aPacket);
     130                 : #endif
     131               0 : }
     132                 : 
     133               0 : void nsVorbisState::AssertHasRecordedPacketSamples(ogg_packet* aPacket)
     134                 : {
     135                 : #ifdef VALIDATE_VORBIS_SAMPLE_CALCULATION
     136               0 :   NS_ASSERTION(mVorbisPacketSamples.count(aPacket) == 1,
     137                 :     "Must have recorded packet samples");
     138                 : #endif
     139               0 : }
     140                 : 
     141               0 : static ogg_packet* Clone(ogg_packet* aPacket) {
     142               0 :   ogg_packet* p = new ogg_packet();
     143               0 :   memcpy(p, aPacket, sizeof(ogg_packet));
     144               0 :   p->packet = new unsigned char[p->bytes];
     145               0 :   memcpy(p->packet, aPacket->packet, p->bytes);
     146               0 :   return p;
     147                 : }
     148                 : 
     149               0 : void nsOggCodecState::ReleasePacket(ogg_packet* aPacket) {
     150               0 :   if (aPacket)
     151               0 :     delete [] aPacket->packet;
     152                 :   delete aPacket;
     153               0 : }
     154                 : 
     155               0 : void nsPacketQueue::Append(ogg_packet* aPacket) {
     156               0 :   nsDeque::Push(aPacket);
     157               0 : }
     158                 : 
     159               0 : ogg_packet* nsOggCodecState::PacketOut() {
     160               0 :   if (mPackets.IsEmpty()) {
     161               0 :     return nsnull;
     162                 :   }
     163               0 :   return mPackets.PopFront();
     164                 : }
     165                 : 
     166               0 : nsresult nsOggCodecState::PageIn(ogg_page* aPage) {
     167               0 :   if (!mActive)
     168               0 :     return NS_OK;
     169               0 :   NS_ASSERTION(static_cast<PRUint32>(ogg_page_serialno(aPage)) == mSerial,
     170                 :                "Page must be for this stream!");
     171               0 :   if (ogg_stream_pagein(&mState, aPage) == -1)
     172               0 :     return NS_ERROR_FAILURE;
     173                 :   int r;
     174               0 :   do {
     175                 :     ogg_packet packet;
     176               0 :     r = ogg_stream_packetout(&mState, &packet);
     177               0 :     if (r == 1) {
     178               0 :       mPackets.Append(Clone(&packet));
     179                 :     }
     180                 :   } while (r != 0);
     181               0 :   if (ogg_stream_check(&mState)) {
     182               0 :     NS_WARNING("Unrecoverable error in ogg_stream_packetout");
     183               0 :     return NS_ERROR_FAILURE;
     184                 :   }
     185               0 :   return NS_OK;
     186                 : }
     187                 : 
     188               0 : nsresult nsOggCodecState::PacketOutUntilGranulepos(bool& aFoundGranulepos) {
     189                 :   int r;
     190               0 :   aFoundGranulepos = false;
     191                 :   // Extract packets from the sync state until either no more packets
     192                 :   // come out, or we get a data packet with non -1 granulepos.
     193               0 :   do {
     194                 :     ogg_packet packet;
     195               0 :     r = ogg_stream_packetout(&mState, &packet);
     196               0 :     if (r == 1) {
     197               0 :       ogg_packet* clone = Clone(&packet);
     198               0 :       if (IsHeader(&packet)) {
     199                 :         // Header packets go straight into the packet queue.
     200               0 :         mPackets.Append(clone);
     201                 :       } else {
     202                 :         // We buffer data packets until we encounter a granulepos. We'll
     203                 :         // then use the granulepos to figure out the granulepos of the
     204                 :         // preceeding packets.
     205               0 :         mUnstamped.AppendElement(clone);
     206               0 :         aFoundGranulepos = packet.granulepos > 0;
     207                 :       }
     208                 :     }
     209               0 :   } while (r != 0 && !aFoundGranulepos);
     210               0 :   if (ogg_stream_check(&mState)) {
     211               0 :     NS_WARNING("Unrecoverable error in ogg_stream_packetout");
     212               0 :     return NS_ERROR_FAILURE;
     213                 :   }
     214               0 :   return NS_OK;
     215                 : }
     216                 : 
     217               0 : nsTheoraState::nsTheoraState(ogg_page* aBosPage) :
     218                 :   nsOggCodecState(aBosPage, true),
     219                 :   mSetup(0),
     220                 :   mCtx(0),
     221               0 :   mPixelAspectRatio(0)
     222                 : {
     223               0 :   MOZ_COUNT_CTOR(nsTheoraState);
     224               0 :   th_info_init(&mInfo);
     225               0 :   th_comment_init(&mComment);
     226               0 : }
     227                 : 
     228               0 : nsTheoraState::~nsTheoraState() {
     229               0 :   MOZ_COUNT_DTOR(nsTheoraState);
     230               0 :   th_setup_free(mSetup);
     231               0 :   th_decode_free(mCtx);
     232               0 :   th_comment_clear(&mComment);
     233               0 :   th_info_clear(&mInfo);
     234               0 : }
     235                 : 
     236               0 : bool nsTheoraState::Init() {
     237               0 :   if (!mActive)
     238               0 :     return false;
     239                 : 
     240               0 :   PRInt64 n = mInfo.aspect_numerator;
     241               0 :   PRInt64 d = mInfo.aspect_denominator;
     242                 : 
     243                 :   mPixelAspectRatio = (n == 0 || d == 0) ?
     244               0 :     1.0f : static_cast<float>(n) / static_cast<float>(d);
     245                 : 
     246                 :   // Ensure the frame and picture regions aren't larger than our prescribed
     247                 :   // maximum, or zero sized.
     248               0 :   nsIntSize frame(mInfo.frame_width, mInfo.frame_height);
     249               0 :   nsIntRect picture(mInfo.pic_x, mInfo.pic_y, mInfo.pic_width, mInfo.pic_height);
     250               0 :   if (!nsVideoInfo::ValidateVideoRegion(frame, picture, frame)) {
     251               0 :     return mActive = false;
     252                 :   }
     253                 : 
     254               0 :   mCtx = th_decode_alloc(&mInfo, mSetup);
     255               0 :   if (mCtx == NULL) {
     256               0 :     return mActive = false;
     257                 :   }
     258                 : 
     259               0 :   return true;
     260                 : }
     261                 : 
     262                 : bool
     263               0 : nsTheoraState::DecodeHeader(ogg_packet* aPacket)
     264                 : {
     265               0 :   mPacketCount++;
     266                 :   int ret = th_decode_headerin(&mInfo,
     267                 :                                &mComment,
     268                 :                                &mSetup,
     269               0 :                                aPacket);
     270                 :  
     271                 :   // We must determine when we've read the last header packet.
     272                 :   // th_decode_headerin() does not tell us when it's read the last header, so
     273                 :   // we must keep track of the headers externally.
     274                 :   //
     275                 :   // There are 3 header packets, the Identification, Comment, and Setup
     276                 :   // headers, which must be in that order. If they're out of order, the file
     277                 :   // is invalid. If we've successfully read a header, and it's the setup
     278                 :   // header, then we're done reading headers. The first byte of each packet
     279                 :   // determines it's type as follows:
     280                 :   //    0x80 -> Identification header
     281                 :   //    0x81 -> Comment header
     282                 :   //    0x82 -> Setup header
     283                 :   // See http://www.theora.org/doc/Theora.pdf Chapter 6, "Bitstream Headers",
     284                 :   // for more details of the Ogg/Theora containment scheme.
     285               0 :   bool isSetupHeader = aPacket->bytes > 0 && aPacket->packet[0] == 0x82;
     286               0 :   if (ret < 0 || mPacketCount > 3) {
     287                 :     // We've received an error, or the first three packets weren't valid
     288                 :     // header packets, assume bad input, and don't activate the bitstream.
     289               0 :     mDoneReadingHeaders = true;
     290               0 :   } else if (ret > 0 && isSetupHeader && mPacketCount == 3) {
     291                 :     // Successfully read the three header packets.
     292               0 :     mDoneReadingHeaders = true;
     293               0 :     mActive = true;
     294                 :   }
     295               0 :   return mDoneReadingHeaders;
     296                 : }
     297                 : 
     298                 : PRInt64
     299               0 : nsTheoraState::Time(PRInt64 granulepos) {
     300               0 :   if (!mActive) {
     301               0 :     return -1;
     302                 :   }
     303               0 :   return nsTheoraState::Time(&mInfo, granulepos);
     304                 : }
     305                 : 
     306                 : bool
     307               0 : nsTheoraState::IsHeader(ogg_packet* aPacket) {
     308               0 :   return th_packet_isheader(aPacket);
     309                 : }
     310                 : 
     311                 : # define TH_VERSION_CHECK(_info,_maj,_min,_sub) \
     312                 :  (((_info)->version_major>(_maj)||(_info)->version_major==(_maj))&& \
     313                 :  (((_info)->version_minor>(_min)||(_info)->version_minor==(_min))&& \
     314                 :  (_info)->version_subminor>=(_sub)))
     315                 : 
     316               0 : PRInt64 nsTheoraState::Time(th_info* aInfo, PRInt64 aGranulepos)
     317                 : {
     318               0 :   if (aGranulepos < 0 || aInfo->fps_numerator == 0) {
     319               0 :     return -1;
     320                 :   }
     321                 :   // Implementation of th_granule_frame inlined here to operate
     322                 :   // on the th_info structure instead of the theora_state.
     323               0 :   int shift = aInfo->keyframe_granule_shift; 
     324               0 :   ogg_int64_t iframe = aGranulepos >> shift;
     325               0 :   ogg_int64_t pframe = aGranulepos - (iframe << shift);
     326               0 :   PRInt64 frameno = iframe + pframe - TH_VERSION_CHECK(aInfo, 3, 2, 1);
     327               0 :   CheckedInt64 t = ((CheckedInt64(frameno) + 1) * USECS_PER_S) * aInfo->fps_denominator;
     328               0 :   if (!t.valid())
     329               0 :     return -1;
     330               0 :   t /= aInfo->fps_numerator;
     331               0 :   return t.valid() ? t.value() : -1;
     332                 : }
     333                 : 
     334               0 : PRInt64 nsTheoraState::StartTime(PRInt64 granulepos) {
     335               0 :   if (granulepos < 0 || !mActive || mInfo.fps_numerator == 0) {
     336               0 :     return -1;
     337                 :   }
     338               0 :   CheckedInt64 t = (CheckedInt64(th_granule_frame(mCtx, granulepos)) * USECS_PER_S) * mInfo.fps_denominator;
     339               0 :   if (!t.valid())
     340               0 :     return -1;
     341               0 :   return t.value() / mInfo.fps_numerator;
     342                 : }
     343                 : 
     344                 : PRInt64
     345               0 : nsTheoraState::MaxKeyframeOffset()
     346                 : {
     347                 :   // Determine the maximum time in microseconds by which a key frame could
     348                 :   // offset for the theora bitstream. Theora granulepos encode time as:
     349                 :   // ((key_frame_number << granule_shift) + frame_offset).
     350                 :   // Therefore the maximum possible time by which any frame could be offset
     351                 :   // from a keyframe is the duration of (1 << granule_shift) - 1) frames.
     352                 :   PRInt64 frameDuration;
     353                 :   
     354                 :   // Max number of frames keyframe could possibly be offset.
     355               0 :   PRInt64 keyframeDiff = (1 << mInfo.keyframe_granule_shift) - 1;
     356                 : 
     357                 :   // Length of frame in usecs.
     358               0 :   CheckedInt64 d = CheckedInt64(mInfo.fps_denominator) * USECS_PER_S;
     359               0 :   if (!d.valid())
     360               0 :     d = 0;
     361               0 :   frameDuration = d.value() / mInfo.fps_numerator;
     362                 : 
     363                 :   // Total time in usecs keyframe can be offset from any given frame.
     364               0 :   return frameDuration * keyframeDiff;
     365                 : }
     366                 : 
     367                 : nsresult
     368               0 : nsTheoraState::PageIn(ogg_page* aPage)
     369                 : {
     370               0 :   if (!mActive)
     371               0 :     return NS_OK;
     372               0 :   NS_ASSERTION(static_cast<PRUint32>(ogg_page_serialno(aPage)) == mSerial,
     373                 :                "Page must be for this stream!");
     374               0 :   if (ogg_stream_pagein(&mState, aPage) == -1)
     375               0 :     return NS_ERROR_FAILURE;
     376                 :   bool foundGp;
     377               0 :   nsresult res = PacketOutUntilGranulepos(foundGp);
     378               0 :   if (NS_FAILED(res))
     379               0 :     return res;
     380               0 :   if (foundGp && mDoneReadingHeaders) {
     381                 :     // We've found a packet with a granulepos, and we've loaded our metadata
     382                 :     // and initialized our decoder. Determine granulepos of buffered packets.
     383               0 :     ReconstructTheoraGranulepos();
     384               0 :     for (PRUint32 i = 0; i < mUnstamped.Length(); ++i) {
     385               0 :       ogg_packet* packet = mUnstamped[i];
     386                 : #ifdef DEBUG
     387               0 :       NS_ASSERTION(!IsHeader(packet), "Don't try to recover header packet gp");
     388               0 :       NS_ASSERTION(packet->granulepos != -1, "Packet must have gp by now");
     389                 : #endif
     390               0 :       mPackets.Append(packet);
     391                 :     }
     392               0 :     mUnstamped.Clear();
     393                 :   }
     394               0 :   return NS_OK;
     395                 : }
     396                 : 
     397                 : // Returns 1 if the Theora info struct is decoding a media of Theora
     398                 : // version (maj,min,sub) or later, otherwise returns 0.
     399                 : int
     400               0 : TheoraVersion(th_info* info,
     401                 :               unsigned char maj,
     402                 :               unsigned char min,
     403                 :               unsigned char sub)
     404                 : {
     405               0 :   ogg_uint32_t ver = (maj << 16) + (min << 8) + sub;
     406                 :   ogg_uint32_t th_ver = (info->version_major << 16) +
     407                 :                         (info->version_minor << 8) +
     408               0 :                         info->version_subminor;
     409               0 :   return (th_ver >= ver) ? 1 : 0;
     410                 : }
     411                 : 
     412               0 : void nsTheoraState::ReconstructTheoraGranulepos()
     413                 : {
     414               0 :   if (mUnstamped.Length() == 0) {
     415               0 :     return;
     416                 :   }
     417               0 :   ogg_int64_t lastGranulepos = mUnstamped[mUnstamped.Length() - 1]->granulepos;
     418               0 :   NS_ASSERTION(lastGranulepos != -1, "Must know last granulepos");
     419                 : 
     420                 :   // Reconstruct the granulepos (and thus timestamps) of the decoded
     421                 :   // frames. Granulepos are stored as ((keyframe<<shift)+offset). We
     422                 :   // know the granulepos of the last frame in the list, so we can infer
     423                 :   // the granulepos of the intermediate frames using their frame numbers.
     424               0 :   ogg_int64_t shift = mInfo.keyframe_granule_shift;
     425               0 :   ogg_int64_t version_3_2_1 = TheoraVersion(&mInfo,3,2,1);
     426                 :   ogg_int64_t lastFrame = th_granule_frame(mCtx,
     427               0 :                                            lastGranulepos) + version_3_2_1;
     428               0 :   ogg_int64_t firstFrame = lastFrame - mUnstamped.Length() + 1;
     429                 : 
     430                 :   // Until we encounter a keyframe, we'll assume that the "keyframe"
     431                 :   // segment of the granulepos is the first frame, or if that causes
     432                 :   // the "offset" segment to overflow, we assume the required
     433                 :   // keyframe is maximumally offset. Until we encounter a keyframe
     434                 :   // the granulepos will probably be wrong, but we can't decode the
     435                 :   // frame anyway (since we don't have its keyframe) so it doesn't really
     436                 :   // matter.
     437               0 :   ogg_int64_t keyframe = lastGranulepos >> shift;
     438                 : 
     439                 :   // The lastFrame, firstFrame, keyframe variables, as well as the frame
     440                 :   // variable in the loop below, store the frame number for Theora
     441                 :   // version >= 3.2.1 streams, and store the frame index for Theora
     442                 :   // version < 3.2.1 streams.
     443               0 :   for (PRUint32 i = 0; i < mUnstamped.Length() - 1; ++i) {
     444               0 :     ogg_int64_t frame = firstFrame + i;
     445                 :     ogg_int64_t granulepos;
     446               0 :     ogg_packet* packet = mUnstamped[i];
     447               0 :     bool isKeyframe = th_packet_iskeyframe(packet) == 1;
     448                 : 
     449               0 :     if (isKeyframe) {
     450               0 :       granulepos = frame << shift;
     451               0 :       keyframe = frame;
     452               0 :     } else if (frame >= keyframe &&
     453                 :                 frame - keyframe < ((ogg_int64_t)1 << shift))
     454                 :     {
     455                 :       // (frame - keyframe) won't overflow the "offset" segment of the
     456                 :       // granulepos, so it's safe to calculate the granulepos.
     457               0 :       granulepos = (keyframe << shift) + (frame - keyframe);
     458                 :     } else {
     459                 :       // (frame - keyframeno) will overflow the "offset" segment of the
     460                 :       // granulepos, so we take "keyframe" to be the max possible offset
     461                 :       // frame instead.
     462               0 :       ogg_int64_t k = NS_MAX(frame - (((ogg_int64_t)1 << shift) - 1), version_3_2_1);
     463               0 :       granulepos = (k << shift) + (frame - k);
     464                 :     }
     465                 :     // Theora 3.2.1+ granulepos store frame number [1..N], so granulepos
     466                 :     // should be > 0.
     467                 :     // Theora 3.2.0 granulepos store the frame index [0..(N-1)], so
     468                 :     // granulepos should be >= 0. 
     469               0 :     NS_ASSERTION(granulepos >= version_3_2_1,
     470                 :                   "Invalid granulepos for Theora version");
     471                 : 
     472                 :     // Check that the frame's granule number is one more than the
     473                 :     // previous frame's.
     474               0 :     NS_ASSERTION(i == 0 ||
     475                 :                  th_granule_frame(mCtx, granulepos) ==
     476                 :                  th_granule_frame(mCtx, mUnstamped[i-1]->granulepos) + 1,
     477                 :                  "Granulepos calculation is incorrect!");
     478                 : 
     479               0 :     packet->granulepos = granulepos;
     480                 :   }
     481                 : 
     482                 :   // Check that the second to last frame's granule number is one less than
     483                 :   // the last frame's (the known granule number). If not our granulepos
     484                 :   // recovery missed a beat.
     485               0 :   NS_ASSERTION(mUnstamped.Length() < 2 ||
     486                 :     th_granule_frame(mCtx, mUnstamped[mUnstamped.Length()-2]->granulepos) + 1 ==
     487                 :     th_granule_frame(mCtx, lastGranulepos),
     488                 :     "Granulepos recovery should catch up with packet->granulepos!");
     489                 : }
     490                 : 
     491               0 : nsresult nsVorbisState::Reset()
     492                 : {
     493               0 :   nsresult res = NS_OK;
     494               0 :   if (mActive && vorbis_synthesis_restart(&mDsp) != 0) {
     495               0 :     res = NS_ERROR_FAILURE;
     496                 :   }
     497               0 :   if (NS_FAILED(nsOggCodecState::Reset())) {
     498               0 :     return NS_ERROR_FAILURE;
     499                 :   }
     500                 : 
     501               0 :   mGranulepos = 0;
     502               0 :   mPrevVorbisBlockSize = 0;
     503                 : 
     504               0 :   return res;
     505                 : }
     506                 : 
     507               0 : nsVorbisState::nsVorbisState(ogg_page* aBosPage) :
     508                 :   nsOggCodecState(aBosPage, true),
     509                 :   mPrevVorbisBlockSize(0),
     510               0 :   mGranulepos(0)
     511                 : {
     512               0 :   MOZ_COUNT_CTOR(nsVorbisState);
     513               0 :   vorbis_info_init(&mInfo);
     514               0 :   vorbis_comment_init(&mComment);
     515               0 :   memset(&mDsp, 0, sizeof(vorbis_dsp_state));
     516               0 :   memset(&mBlock, 0, sizeof(vorbis_block));
     517               0 : }
     518                 : 
     519               0 : nsVorbisState::~nsVorbisState() {
     520               0 :   MOZ_COUNT_DTOR(nsVorbisState);
     521               0 :   Reset();
     522               0 :   vorbis_block_clear(&mBlock);
     523               0 :   vorbis_dsp_clear(&mDsp);
     524               0 :   vorbis_info_clear(&mInfo);
     525               0 :   vorbis_comment_clear(&mComment);
     526               0 : }
     527                 : 
     528               0 : bool nsVorbisState::DecodeHeader(ogg_packet* aPacket) {
     529               0 :   mPacketCount++;
     530                 :   int ret = vorbis_synthesis_headerin(&mInfo,
     531                 :                                       &mComment,
     532               0 :                                       aPacket);
     533                 :   // We must determine when we've read the last header packet.
     534                 :   // vorbis_synthesis_headerin() does not tell us when it's read the last
     535                 :   // header, so we must keep track of the headers externally.
     536                 :   //
     537                 :   // There are 3 header packets, the Identification, Comment, and Setup
     538                 :   // headers, which must be in that order. If they're out of order, the file
     539                 :   // is invalid. If we've successfully read a header, and it's the setup
     540                 :   // header, then we're done reading headers. The first byte of each packet
     541                 :   // determines it's type as follows:
     542                 :   //    0x1 -> Identification header
     543                 :   //    0x3 -> Comment header
     544                 :   //    0x5 -> Setup header
     545                 :   // For more details of the Vorbis/Ogg containment scheme, see the Vorbis I
     546                 :   // Specification, Chapter 4, Codec Setup and Packet Decode:
     547                 :   // http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-580004
     548                 : 
     549               0 :   bool isSetupHeader = aPacket->bytes > 0 && aPacket->packet[0] == 0x5;
     550                 : 
     551               0 :   if (ret < 0 || mPacketCount > 3) {
     552                 :     // We've received an error, or the first three packets weren't valid
     553                 :     // header packets, assume bad input, and deactivate the bitstream.
     554               0 :     mDoneReadingHeaders = true;
     555               0 :     mActive = false;
     556               0 :   } else if (ret == 0 && isSetupHeader && mPacketCount == 3) {
     557                 :     // Successfully read the three header packets.
     558                 :     // The bitstream remains active.
     559               0 :     mDoneReadingHeaders = true;
     560                 :   }
     561               0 :   return mDoneReadingHeaders;
     562                 : }
     563                 : 
     564               0 : bool nsVorbisState::Init()
     565                 : {
     566               0 :   if (!mActive)
     567               0 :     return false;
     568                 : 
     569               0 :   int ret = vorbis_synthesis_init(&mDsp, &mInfo);
     570               0 :   if (ret != 0) {
     571               0 :     NS_WARNING("vorbis_synthesis_init() failed initializing vorbis bitstream");
     572               0 :     return mActive = false;
     573                 :   }
     574               0 :   ret = vorbis_block_init(&mDsp, &mBlock);
     575               0 :   if (ret != 0) {
     576               0 :     NS_WARNING("vorbis_block_init() failed initializing vorbis bitstream");
     577               0 :     if (mActive) {
     578               0 :       vorbis_dsp_clear(&mDsp);
     579                 :     }
     580               0 :     return mActive = false;
     581                 :   }
     582               0 :   return true;
     583                 : }
     584                 : 
     585               0 : PRInt64 nsVorbisState::Time(PRInt64 granulepos)
     586                 : {
     587               0 :   if (!mActive) {
     588               0 :     return -1;
     589                 :   }
     590                 : 
     591               0 :   return nsVorbisState::Time(&mInfo, granulepos);
     592                 : }
     593                 : 
     594               0 : PRInt64 nsVorbisState::Time(vorbis_info* aInfo, PRInt64 aGranulepos)
     595                 : {
     596               0 :   if (aGranulepos == -1 || aInfo->rate == 0) {
     597               0 :     return -1;
     598                 :   }
     599               0 :   CheckedInt64 t = CheckedInt64(aGranulepos) * USECS_PER_S;
     600               0 :   if (!t.valid())
     601               0 :     t = 0;
     602               0 :   return t.value() / aInfo->rate;
     603                 : }
     604                 : 
     605                 : bool
     606               0 : nsVorbisState::IsHeader(ogg_packet* aPacket)
     607                 : {
     608                 :   // The first byte in each Vorbis header packet is either 0x01, 0x03, or 0x05,
     609                 :   // i.e. the first bit is odd. Audio data packets have their first bit as 0x0.
     610                 :   // Any packet with its first bit set cannot be a data packet, it's a
     611                 :   // (possibly invalid) header packet.
     612                 :   // See: http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-610004.2.1
     613               0 :   return aPacket->bytes > 0 ? (aPacket->packet[0] & 0x1) : false;
     614                 : }
     615                 : 
     616                 : nsresult
     617               0 : nsVorbisState::PageIn(ogg_page* aPage)
     618                 : {
     619               0 :   if (!mActive)
     620               0 :     return NS_OK;
     621               0 :   NS_ASSERTION(static_cast<PRUint32>(ogg_page_serialno(aPage)) == mSerial,
     622                 :                "Page must be for this stream!");
     623               0 :   if (ogg_stream_pagein(&mState, aPage) == -1)
     624               0 :     return NS_ERROR_FAILURE;
     625                 :   bool foundGp;
     626               0 :   nsresult res = PacketOutUntilGranulepos(foundGp);
     627               0 :   if (NS_FAILED(res))
     628               0 :     return res;
     629               0 :   if (foundGp && mDoneReadingHeaders) {
     630                 :     // We've found a packet with a granulepos, and we've loaded our metadata
     631                 :     // and initialized our decoder. Determine granulepos of buffered packets.
     632               0 :     ReconstructVorbisGranulepos();
     633               0 :     for (PRUint32 i = 0; i < mUnstamped.Length(); ++i) {
     634               0 :       ogg_packet* packet = mUnstamped[i];
     635               0 :       AssertHasRecordedPacketSamples(packet);
     636               0 :       NS_ASSERTION(!IsHeader(packet), "Don't try to recover header packet gp");
     637               0 :       NS_ASSERTION(packet->granulepos != -1, "Packet must have gp by now");
     638               0 :       mPackets.Append(packet);
     639                 :     }
     640               0 :     mUnstamped.Clear();
     641                 :   }
     642               0 :   return NS_OK;
     643                 : }
     644                 : 
     645               0 : nsresult nsVorbisState::ReconstructVorbisGranulepos()
     646                 : {
     647                 :   // The number of samples in a Vorbis packet is:
     648                 :   // window_blocksize(previous_packet)/4+window_blocksize(current_packet)/4
     649                 :   // See: http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-230001.3.2
     650                 :   // So we maintain mPrevVorbisBlockSize, the block size of the last packet
     651                 :   // encountered. We also maintain mGranulepos, which is the granulepos of
     652                 :   // the last encountered packet. This enables us to give granulepos to
     653                 :   // packets when the last packet in mUnstamped doesn't have a granulepos
     654                 :   // (for example if the stream was truncated).
     655                 :   //
     656                 :   // We validate our prediction of the number of samples decoded when
     657                 :   // VALIDATE_VORBIS_SAMPLE_CALCULATION is defined by recording the predicted
     658                 :   // number of samples, and verifing we extract that many when decoding
     659                 :   // each packet.
     660                 : 
     661               0 :   NS_ASSERTION(mUnstamped.Length() > 0, "Length must be > 0");
     662               0 :   ogg_packet* last = mUnstamped[mUnstamped.Length()-1];
     663               0 :   NS_ASSERTION(last->e_o_s || last->granulepos >= 0,
     664                 :     "Must know last granulepos!");
     665               0 :   if (mUnstamped.Length() == 1) {
     666               0 :     ogg_packet* packet = mUnstamped[0];
     667               0 :     long blockSize = vorbis_packet_blocksize(&mInfo, packet);
     668               0 :     if (blockSize < 0) {
     669                 :       // On failure vorbis_packet_blocksize returns < 0. If we've got
     670                 :       // a bad packet, we just assume that decode will have to skip this
     671                 :       // packet, i.e. assume 0 samples are decodable from this packet.
     672               0 :       blockSize = 0;
     673               0 :       mPrevVorbisBlockSize = 0;
     674                 :     }
     675               0 :     long samples = mPrevVorbisBlockSize / 4 + blockSize / 4;
     676               0 :     mPrevVorbisBlockSize = blockSize;
     677               0 :     if (packet->granulepos == -1) {
     678               0 :       packet->granulepos = mGranulepos + samples;
     679                 :     }
     680                 : 
     681                 :     // Account for a partial last frame
     682               0 :     if (packet->e_o_s && packet->granulepos >= mGranulepos) {
     683               0 :        samples = packet->granulepos - mGranulepos;
     684                 :     }
     685                 :  
     686               0 :     mGranulepos = packet->granulepos;
     687               0 :     RecordVorbisPacketSamples(packet, samples);
     688               0 :     return NS_OK;
     689                 :   }
     690                 : 
     691               0 :   bool unknownGranulepos = last->granulepos == -1;
     692               0 :   int totalSamples = 0;
     693               0 :   for (PRInt32 i = mUnstamped.Length() - 1; i > 0; i--) {
     694               0 :     ogg_packet* packet = mUnstamped[i];
     695               0 :     ogg_packet* prev = mUnstamped[i-1];
     696               0 :     ogg_int64_t granulepos = packet->granulepos;
     697               0 :     NS_ASSERTION(granulepos != -1, "Must know granulepos!");
     698               0 :     long prevBlockSize = vorbis_packet_blocksize(&mInfo, prev);
     699               0 :     long blockSize = vorbis_packet_blocksize(&mInfo, packet);
     700                 : 
     701               0 :     if (blockSize < 0 || prevBlockSize < 0) {
     702                 :       // On failure vorbis_packet_blocksize returns < 0. If we've got
     703                 :       // a bad packet, we just assume that decode will have to skip this
     704                 :       // packet, i.e. assume 0 samples are decodable from this packet.
     705               0 :       blockSize = 0;
     706               0 :       prevBlockSize = 0;
     707                 :     }
     708                 : 
     709               0 :     long samples = prevBlockSize / 4 + blockSize / 4;
     710               0 :     totalSamples += samples;
     711               0 :     prev->granulepos = granulepos - samples;
     712               0 :     RecordVorbisPacketSamples(packet, samples);
     713                 :   }
     714                 : 
     715               0 :   if (unknownGranulepos) {
     716               0 :     for (PRUint32 i = 0; i < mUnstamped.Length(); i++) {
     717               0 :       ogg_packet* packet = mUnstamped[i];
     718               0 :       packet->granulepos += mGranulepos + totalSamples + 1;
     719                 :     }
     720                 :   }
     721                 : 
     722               0 :   ogg_packet* first = mUnstamped[0];
     723               0 :   long blockSize = vorbis_packet_blocksize(&mInfo, first);
     724               0 :   if (blockSize < 0) {
     725               0 :     mPrevVorbisBlockSize = 0;
     726               0 :     blockSize = 0;
     727                 :   }
     728                 : 
     729                 :   long samples = (mPrevVorbisBlockSize == 0) ? 0 :
     730               0 :                   mPrevVorbisBlockSize / 4 + blockSize / 4;
     731               0 :   PRInt64 start = first->granulepos - samples;
     732               0 :   RecordVorbisPacketSamples(first, samples);
     733                 : 
     734               0 :   if (last->e_o_s && start < mGranulepos) {
     735                 :     // We've calculated that there are more samples in this page than its
     736                 :     // granulepos claims, and it's the last page in the stream. This is legal,
     737                 :     // and we will need to prune the trailing samples when we come to decode it.
     738                 :     // We must correct the timestamps so that they follow the last Vorbis page's
     739                 :     // samples.
     740               0 :     PRInt64 pruned = mGranulepos - start;
     741               0 :     for (PRUint32 i = 0; i < mUnstamped.Length() - 1; i++) {
     742               0 :       mUnstamped[i]->granulepos += pruned;
     743                 :     }
     744                 : #ifdef VALIDATE_VORBIS_SAMPLE_CALCULATION
     745               0 :     mVorbisPacketSamples[last] -= pruned;
     746                 : #endif
     747                 :   }
     748                 : 
     749               0 :   mPrevVorbisBlockSize = vorbis_packet_blocksize(&mInfo, last);
     750               0 :   mPrevVorbisBlockSize = NS_MAX(static_cast<long>(0), mPrevVorbisBlockSize);
     751               0 :   mGranulepos = last->granulepos;
     752                 : 
     753               0 :   return NS_OK;
     754                 : }
     755                 : 
     756                 : 
     757               0 : nsSkeletonState::nsSkeletonState(ogg_page* aBosPage)
     758                 :   : nsOggCodecState(aBosPage, true),
     759                 :     mVersion(0),
     760                 :     mPresentationTime(0),
     761               0 :     mLength(0)
     762                 : {
     763               0 :   MOZ_COUNT_CTOR(nsSkeletonState);
     764               0 : }
     765                 :  
     766               0 : nsSkeletonState::~nsSkeletonState()
     767                 : {
     768               0 :   MOZ_COUNT_DTOR(nsSkeletonState);
     769               0 : }
     770                 : 
     771                 : // Support for Ogg Skeleton 4.0, as per specification at:
     772                 : // http://wiki.xiph.org/Ogg_Skeleton_4
     773                 : 
     774                 : // Minimum length in bytes of a Skeleton header packet.
     775                 : static const long SKELETON_MIN_HEADER_LEN = 28;
     776                 : static const long SKELETON_4_0_MIN_HEADER_LEN = 80;
     777                 : 
     778                 : // Minimum length in bytes of a Skeleton 4.0 index packet.
     779                 : static const long SKELETON_4_0_MIN_INDEX_LEN = 42;
     780                 : 
     781                 : // Minimum possible size of a compressed index keypoint.
     782                 : static const size_t MIN_KEY_POINT_SIZE = 2;
     783                 : 
     784                 : // Byte offset of the major and minor version numbers in the
     785                 : // Ogg Skeleton 4.0 header packet.
     786                 : static const size_t SKELETON_VERSION_MAJOR_OFFSET = 8;
     787                 : static const size_t SKELETON_VERSION_MINOR_OFFSET = 10;
     788                 : 
     789                 : // Byte-offsets of the presentation time numerator and denominator
     790                 : static const size_t SKELETON_PRESENTATION_TIME_NUMERATOR_OFFSET = 12;
     791                 : static const size_t SKELETON_PRESENTATION_TIME_DENOMINATOR_OFFSET = 20;
     792                 : 
     793                 : // Byte-offsets of the length of file field in the Skeleton 4.0 header packet.
     794                 : static const size_t SKELETON_FILE_LENGTH_OFFSET = 64;
     795                 : 
     796                 : // Byte-offsets of the fields in the Skeleton index packet.
     797                 : static const size_t INDEX_SERIALNO_OFFSET = 6;
     798                 : static const size_t INDEX_NUM_KEYPOINTS_OFFSET = 10;
     799                 : static const size_t INDEX_TIME_DENOM_OFFSET = 18;
     800                 : static const size_t INDEX_FIRST_NUMER_OFFSET = 26;
     801                 : static const size_t INDEX_LAST_NUMER_OFFSET = 34;
     802                 : static const size_t INDEX_KEYPOINT_OFFSET = 42;
     803                 : 
     804               0 : static bool IsSkeletonBOS(ogg_packet* aPacket)
     805                 : {
     806                 :   return aPacket->bytes >= SKELETON_MIN_HEADER_LEN && 
     807               0 :          memcmp(reinterpret_cast<char*>(aPacket->packet), "fishead", 8) == 0;
     808                 : }
     809                 : 
     810               0 : static bool IsSkeletonIndex(ogg_packet* aPacket)
     811                 : {
     812                 :   return aPacket->bytes >= SKELETON_4_0_MIN_INDEX_LEN &&
     813               0 :          memcmp(reinterpret_cast<char*>(aPacket->packet), "index", 5) == 0;
     814                 : }
     815                 : 
     816                 : // Reads a little-endian encoded unsigned 32bit integer at p.
     817               0 : static PRUint32 LEUint32(const unsigned char* p)
     818                 : {
     819               0 :   return p[0] +
     820               0 :         (p[1] << 8) + 
     821               0 :         (p[2] << 16) +
     822               0 :         (p[3] << 24);
     823                 : }
     824                 : 
     825                 : // Reads a little-endian encoded 64bit integer at p.
     826               0 : static PRInt64 LEInt64(const unsigned char* p)
     827                 : {
     828               0 :   PRUint32 lo = LEUint32(p);
     829               0 :   PRUint32 hi = LEUint32(p + 4);
     830               0 :   return static_cast<PRInt64>(lo) | (static_cast<PRInt64>(hi) << 32);
     831                 : }
     832                 : 
     833                 : // Reads a little-endian encoded unsigned 16bit integer at p.
     834               0 : static PRUint16 LEUint16(const unsigned char* p)
     835                 : {
     836               0 :   return p[0] + (p[1] << 8);  
     837                 : }
     838                 : 
     839                 : // Reads a variable length encoded integer at p. Will not read
     840                 : // past aLimit. Returns pointer to character after end of integer.
     841               0 : static const unsigned char* ReadVariableLengthInt(const unsigned char* p,
     842                 :                                                   const unsigned char* aLimit,
     843                 :                                                   PRInt64& n)
     844                 : {
     845               0 :   int shift = 0;
     846               0 :   PRInt64 byte = 0;
     847               0 :   n = 0;
     848               0 :   while (p < aLimit &&
     849                 :          (byte & 0x80) != 0x80 &&
     850                 :          shift < 57)
     851                 :   {
     852               0 :     byte = static_cast<PRInt64>(*p);
     853               0 :     n |= ((byte & 0x7f) << shift);
     854               0 :     shift += 7;
     855               0 :     p++;
     856                 :   }
     857               0 :   return p;
     858                 : }
     859                 : 
     860               0 : bool nsSkeletonState::DecodeIndex(ogg_packet* aPacket)
     861                 : {
     862               0 :   NS_ASSERTION(aPacket->bytes >= SKELETON_4_0_MIN_INDEX_LEN,
     863                 :                "Index must be at least minimum size");
     864               0 :   if (!mActive) {
     865               0 :     return false;
     866                 :   }
     867                 : 
     868               0 :   PRUint32 serialno = LEUint32(aPacket->packet + INDEX_SERIALNO_OFFSET);
     869               0 :   PRInt64 numKeyPoints = LEInt64(aPacket->packet + INDEX_NUM_KEYPOINTS_OFFSET);
     870                 : 
     871               0 :   PRInt64 endTime = 0, startTime = 0;
     872               0 :   const unsigned char* p = aPacket->packet;
     873                 : 
     874               0 :   PRInt64 timeDenom = LEInt64(aPacket->packet + INDEX_TIME_DENOM_OFFSET);
     875               0 :   if (timeDenom == 0) {
     876               0 :     LOG(PR_LOG_DEBUG, ("Ogg Skeleton Index packet for stream %u has 0 "
     877                 :                        "timestamp denominator.", serialno));
     878               0 :     return (mActive = false);
     879                 :   }
     880                 : 
     881                 :   // Extract the start time.
     882               0 :   CheckedInt64 t = CheckedInt64(LEInt64(p + INDEX_FIRST_NUMER_OFFSET)) * USECS_PER_S;
     883               0 :   if (!t.valid()) {
     884               0 :     return (mActive = false);
     885                 :   } else {
     886               0 :     startTime = t.value() / timeDenom;
     887                 :   }
     888                 : 
     889                 :   // Extract the end time.
     890               0 :   t = LEInt64(p + INDEX_LAST_NUMER_OFFSET) * USECS_PER_S;
     891               0 :   if (!t.valid()) {
     892               0 :     return (mActive = false);
     893                 :   } else {
     894               0 :     endTime = t.value() / timeDenom;
     895                 :   }
     896                 : 
     897                 :   // Check the numKeyPoints value read, ensure we're not going to run out of
     898                 :   // memory while trying to decode the index packet.
     899               0 :   CheckedInt64 minPacketSize = (CheckedInt64(numKeyPoints) * MIN_KEY_POINT_SIZE) + INDEX_KEYPOINT_OFFSET;
     900               0 :   if (!minPacketSize.valid())
     901                 :   {
     902               0 :     return (mActive = false);
     903                 :   }
     904                 :   
     905               0 :   PRInt64 sizeofIndex = aPacket->bytes - INDEX_KEYPOINT_OFFSET;
     906               0 :   PRInt64 maxNumKeyPoints = sizeofIndex / MIN_KEY_POINT_SIZE;
     907               0 :   if (aPacket->bytes < minPacketSize.value() ||
     908                 :       numKeyPoints > maxNumKeyPoints || 
     909                 :       numKeyPoints < 0)
     910                 :   {
     911                 :     // Packet size is less than the theoretical minimum size, or the packet is
     912                 :     // claiming to store more keypoints than it's capable of storing. This means
     913                 :     // that the numKeyPoints field is too large or small for the packet to
     914                 :     // possibly contain as many packets as it claims to, so the numKeyPoints
     915                 :     // field is possibly malicious. Don't try decoding this index, we may run
     916                 :     // out of memory.
     917               0 :     LOG(PR_LOG_DEBUG, ("Possibly malicious number of key points reported "
     918                 :                        "(%lld) in index packet for stream %u.",
     919                 :                        numKeyPoints,
     920                 :                        serialno));
     921               0 :     return (mActive = false);
     922                 :   }
     923                 : 
     924               0 :   nsAutoPtr<nsKeyFrameIndex> keyPoints(new nsKeyFrameIndex(startTime, endTime));
     925                 :   
     926               0 :   p = aPacket->packet + INDEX_KEYPOINT_OFFSET;
     927               0 :   const unsigned char* limit = aPacket->packet + aPacket->bytes;
     928               0 :   PRInt64 numKeyPointsRead = 0;
     929               0 :   CheckedInt64 offset = 0;
     930               0 :   CheckedInt64 time = 0;
     931               0 :   while (p < limit &&
     932                 :          numKeyPointsRead < numKeyPoints)
     933                 :   {
     934               0 :     PRInt64 delta = 0;
     935               0 :     p = ReadVariableLengthInt(p, limit, delta);
     936               0 :     offset += delta;
     937               0 :     if (p == limit ||
     938               0 :         !offset.valid() ||
     939               0 :         offset.value() > mLength ||
     940               0 :         offset.value() < 0)
     941                 :     {
     942               0 :       return (mActive = false);
     943                 :     }
     944               0 :     p = ReadVariableLengthInt(p, limit, delta);
     945               0 :     time += delta;
     946               0 :     if (!time.valid() ||
     947               0 :         time.value() > endTime ||
     948               0 :         time.value() < startTime)
     949                 :     {
     950               0 :       return (mActive = false);
     951                 :     }
     952               0 :     CheckedInt64 timeUsecs = time * USECS_PER_S;
     953               0 :     if (!timeUsecs.valid())
     954               0 :       return mActive = false;
     955               0 :     timeUsecs /= timeDenom;
     956               0 :     keyPoints->Add(offset.value(), timeUsecs.value());
     957               0 :     numKeyPointsRead++;
     958                 :   }
     959                 : 
     960               0 :   PRInt32 keyPointsRead = keyPoints->Length();
     961               0 :   if (keyPointsRead > 0) {
     962               0 :     mIndex.Put(serialno, keyPoints.forget());
     963                 :   }
     964                 : 
     965               0 :   LOG(PR_LOG_DEBUG, ("Loaded %d keypoints for Skeleton on stream %u",
     966                 :                      keyPointsRead, serialno));
     967               0 :   return true;
     968                 : }
     969                 : 
     970               0 : nsresult nsSkeletonState::IndexedSeekTargetForTrack(PRUint32 aSerialno,
     971                 :                                                     PRInt64 aTarget,
     972                 :                                                     nsKeyPoint& aResult)
     973                 : {
     974               0 :   nsKeyFrameIndex* index = nsnull;
     975               0 :   mIndex.Get(aSerialno, &index);
     976                 : 
     977               0 :   if (!index ||
     978               0 :       index->Length() == 0 ||
     979                 :       aTarget < index->mStartTime ||
     980                 :       aTarget > index->mEndTime)
     981                 :   {
     982               0 :     return NS_ERROR_FAILURE;
     983                 :   }
     984                 : 
     985                 :   // Binary search to find the last key point with time less than target.
     986               0 :   int start = 0;
     987               0 :   int end = index->Length() - 1;
     988               0 :   while (end > start) {
     989               0 :     int mid = start + ((end - start + 1) >> 1);
     990               0 :     if (index->Get(mid).mTime == aTarget) {
     991               0 :        start = mid;
     992               0 :        break;
     993               0 :     } else if (index->Get(mid).mTime < aTarget) {
     994               0 :       start = mid;
     995                 :     } else {
     996               0 :       end = mid - 1;
     997                 :     }
     998                 :   }
     999                 : 
    1000               0 :   aResult = index->Get(start);
    1001               0 :   NS_ASSERTION(aResult.mTime <= aTarget, "Result should have time <= target");
    1002               0 :   return NS_OK;
    1003                 : }
    1004                 : 
    1005               0 : nsresult nsSkeletonState::IndexedSeekTarget(PRInt64 aTarget,
    1006                 :                                             nsTArray<PRUint32>& aTracks,
    1007                 :                                             nsSeekTarget& aResult)
    1008                 : {
    1009               0 :   if (!mActive || mVersion < SKELETON_VERSION(4,0)) {
    1010               0 :     return NS_ERROR_FAILURE;
    1011                 :   }
    1012                 :   // Loop over all requested tracks' indexes, and get the keypoint for that
    1013                 :   // seek target. Record the keypoint with the lowest offset, this will be
    1014                 :   // our seek result. User must seek to the one with lowest offset to ensure we
    1015                 :   // pass "keyframes" on all tracks when we decode forwards to the seek target.
    1016               0 :   nsSeekTarget r;
    1017               0 :   for (PRUint32 i=0; i<aTracks.Length(); i++) {
    1018               0 :     nsKeyPoint k;
    1019               0 :     if (NS_SUCCEEDED(IndexedSeekTargetForTrack(aTracks[i], aTarget, k)) &&
    1020                 :         k.mOffset < r.mKeyPoint.mOffset)
    1021                 :     {
    1022               0 :       r.mKeyPoint = k;
    1023               0 :       r.mSerial = aTracks[i];
    1024                 :     }
    1025                 :   }
    1026               0 :   if (r.IsNull()) {
    1027               0 :     return NS_ERROR_FAILURE;
    1028                 :   }
    1029               0 :   LOG(PR_LOG_DEBUG, ("Indexed seek target for time %lld is offset %lld",
    1030                 :                      aTarget, r.mKeyPoint.mOffset));
    1031               0 :   aResult = r;
    1032               0 :   return NS_OK;
    1033                 : }
    1034                 : 
    1035               0 : nsresult nsSkeletonState::GetDuration(const nsTArray<PRUint32>& aTracks,
    1036                 :                                       PRInt64& aDuration)
    1037                 : {
    1038               0 :   if (!mActive ||
    1039                 :       mVersion < SKELETON_VERSION(4,0) ||
    1040               0 :       !HasIndex() ||
    1041               0 :       aTracks.Length() == 0)
    1042                 :   {
    1043               0 :     return NS_ERROR_FAILURE;
    1044                 :   }
    1045               0 :   PRInt64 endTime = INT64_MIN;
    1046               0 :   PRInt64 startTime = INT64_MAX;
    1047               0 :   for (PRUint32 i=0; i<aTracks.Length(); i++) {
    1048               0 :     nsKeyFrameIndex* index = nsnull;
    1049               0 :     mIndex.Get(aTracks[i], &index);
    1050               0 :     if (!index) {
    1051                 :       // Can't get the timestamps for one of the required tracks, fail.
    1052               0 :       return NS_ERROR_FAILURE;
    1053                 :     }
    1054               0 :     if (index->mEndTime > endTime) {
    1055               0 :       endTime = index->mEndTime;
    1056                 :     }
    1057               0 :     if (index->mStartTime < startTime) {
    1058               0 :       startTime = index->mStartTime;
    1059                 :     }
    1060                 :   }
    1061               0 :   NS_ASSERTION(endTime > startTime, "Duration must be positive");
    1062               0 :   CheckedInt64 duration = CheckedInt64(endTime) - startTime;
    1063               0 :   aDuration = duration.valid() ? duration.value() : 0;
    1064               0 :   return duration.valid() ? NS_OK : NS_ERROR_FAILURE;
    1065                 : }
    1066                 : 
    1067               0 : bool nsSkeletonState::DecodeHeader(ogg_packet* aPacket)
    1068                 : {
    1069               0 :   if (IsSkeletonBOS(aPacket)) {
    1070               0 :     PRUint16 verMajor = LEUint16(aPacket->packet + SKELETON_VERSION_MAJOR_OFFSET);
    1071               0 :     PRUint16 verMinor = LEUint16(aPacket->packet + SKELETON_VERSION_MINOR_OFFSET);
    1072                 : 
    1073                 :     // Read the presentation time. We read this before the version check as the
    1074                 :     // presentation time exists in all versions.
    1075               0 :     PRInt64 n = LEInt64(aPacket->packet + SKELETON_PRESENTATION_TIME_NUMERATOR_OFFSET);
    1076               0 :     PRInt64 d = LEInt64(aPacket->packet + SKELETON_PRESENTATION_TIME_DENOMINATOR_OFFSET);
    1077               0 :     mPresentationTime = d == 0 ? 0 : (static_cast<float>(n) / static_cast<float>(d)) * USECS_PER_S;
    1078                 : 
    1079               0 :     mVersion = SKELETON_VERSION(verMajor, verMinor);
    1080               0 :     if (mVersion < SKELETON_VERSION(4,0) ||
    1081                 :         mVersion >= SKELETON_VERSION(5,0) ||
    1082                 :         aPacket->bytes < SKELETON_4_0_MIN_HEADER_LEN)
    1083                 :     {
    1084                 :       // We can only care to parse Skeleton version 4.0+.
    1085               0 :       mActive = false;
    1086               0 :       return mDoneReadingHeaders = true;
    1087                 :     }
    1088                 : 
    1089                 :     // Extract the segment length.
    1090               0 :     mLength = LEInt64(aPacket->packet + SKELETON_FILE_LENGTH_OFFSET);
    1091                 : 
    1092               0 :     LOG(PR_LOG_DEBUG, ("Skeleton segment length: %lld", mLength));
    1093                 : 
    1094                 :     // Initialize the serianlno-to-index map.
    1095               0 :     bool init = mIndex.Init();
    1096               0 :     if (!init) {
    1097               0 :       NS_WARNING("Failed to initialize Ogg skeleton serialno-to-index map");
    1098               0 :       mActive = false;
    1099               0 :       return mDoneReadingHeaders = true;
    1100                 :     }
    1101               0 :     mActive = true;
    1102               0 :   } else if (IsSkeletonIndex(aPacket) && mVersion >= SKELETON_VERSION(4,0)) {
    1103               0 :     if (!DecodeIndex(aPacket)) {
    1104                 :       // Failed to parse index, or invalid/hostile index. DecodeIndex() will
    1105                 :       // have deactivated the track.
    1106               0 :       return mDoneReadingHeaders = true;
    1107                 :     }
    1108                 : 
    1109               0 :   } else if (aPacket->e_o_s) {
    1110               0 :     mDoneReadingHeaders = true;
    1111                 :   }
    1112               0 :   return mDoneReadingHeaders;
    1113                 : }

Generated by: LCOV version 1.7