Bug 1322070: P6. Construct AudioInfo object from Vorbis CodecState. r?gerald draft
authorJean-Yves Avenard <jyavenard@mozilla.com>
Tue, 06 Dec 2016 15:41:52 -1000
changeset 448646 80a4a2137fa2db48def26ffef7de387aceb997d3
parent 448645 6b025c61ff5956428a63dfe1597a6da2fb8527fa
child 448647 f2bf734c35553ceaad971c90d3259fc7ec051f03
push id38389
push userbmo:jyavenard@mozilla.com
push dateMon, 12 Dec 2016 05:01:11 +0000
reviewersgerald
bugs1322070
milestone53.0a1
Bug 1322070: P6. Construct AudioInfo object from Vorbis CodecState. r?gerald MozReview-Commit-ID: GitrIqFbSVd
dom/media/ogg/OggCodecState.cpp
dom/media/ogg/OggCodecState.h
dom/media/ogg/OggDemuxer.cpp
dom/media/ogg/OggDemuxer.h
--- a/dom/media/ogg/OggCodecState.cpp
+++ b/dom/media/ogg/OggCodecState.cpp
@@ -13,16 +13,17 @@
 #include "OggCodecState.h"
 #include "OpusDecoder.h"
 #include "OpusParser.h"
 #include "VideoUtils.h"
 #include <algorithm>
 
 #include <opus/opus.h>
 #include "opus/opus_multistream.h"
+#include "XiphExtradata.h"
 
 // On Android JellyBean, the hardware.h header redefines version_major and
 // version_minor, which breaks our build.  See:
 // https://bugzilla.mozilla.org/show_bug.cgi?id=912702#c6
 #ifdef MOZ_WIDGET_GONK
 #ifdef version_major
 #undef version_major
 #endif
@@ -621,54 +622,55 @@ TheoraState::ReconstructTheoraGranulepos
 
 nsresult
 VorbisState::Reset()
 {
   nsresult res = NS_OK;
   if (mActive && vorbis_synthesis_restart(&mDsp) != 0) {
     res = NS_ERROR_FAILURE;
   }
+  mHeaders.Erase();
   if (NS_FAILED(OggCodecState::Reset())) {
     return NS_ERROR_FAILURE;
   }
 
   mGranulepos = 0;
   mPrevVorbisBlockSize = 0;
 
   return res;
 }
 
 VorbisState::VorbisState(ogg_page* aBosPage)
   : OggCodecState(aBosPage, true)
   , mPrevVorbisBlockSize(0)
   , mGranulepos(0)
 {
   MOZ_COUNT_CTOR(VorbisState);
-  vorbis_info_init(&mInfo);
+  vorbis_info_init(&mVorbisInfo);
   vorbis_comment_init(&mComment);
   memset(&mDsp, 0, sizeof(vorbis_dsp_state));
   memset(&mBlock, 0, sizeof(vorbis_block));
 }
 
 VorbisState::~VorbisState()
 {
   MOZ_COUNT_DTOR(VorbisState);
   Reset();
   vorbis_block_clear(&mBlock);
   vorbis_dsp_clear(&mDsp);
-  vorbis_info_clear(&mInfo);
+  vorbis_info_clear(&mVorbisInfo);
   vorbis_comment_clear(&mComment);
 }
 
 bool
 VorbisState::DecodeHeader(ogg_packet* aPacket)
 {
-  nsAutoRef<ogg_packet> autoRelease(aPacket);
+  mHeaders.Append(aPacket);
   mPacketCount++;
-  int ret = vorbis_synthesis_headerin(&mInfo,
+  int ret = vorbis_synthesis_headerin(&mVorbisInfo,
                                       &mComment,
                                       aPacket);
   // We must determine when we've read the last header packet.
   // vorbis_synthesis_headerin() does not tell us when it's read the last
   // header, so we must keep track of the headers externally.
   //
   // There are 3 header packets, the Identification, Comment, and Setup
   // headers, which must be in that order. If they're out of order, the file
@@ -684,55 +686,74 @@ VorbisState::DecodeHeader(ogg_packet* aP
 
   bool isSetupHeader = aPacket->bytes > 0 && aPacket->packet[0] == 0x5;
 
   if (ret < 0 || mPacketCount > 3) {
     // We've received an error, or the first three packets weren't valid
     // header packets. Assume bad input. Our caller will deactivate the
     // bitstream.
     return false;
-  } else if (ret == 0 && isSetupHeader && mPacketCount == 3) {
+  } else if (!ret && isSetupHeader && mPacketCount == 3) {
     // Successfully read the three header packets.
     // The bitstream remains active.
     mDoneReadingHeaders = true;
   }
+
   return true;
 }
 
 bool
 VorbisState::Init()
 {
   if (!mActive) {
     return false;
   }
 
-  int ret = vorbis_synthesis_init(&mDsp, &mInfo);
+  int ret = vorbis_synthesis_init(&mDsp, &mVorbisInfo);
   if (ret != 0) {
     NS_WARNING("vorbis_synthesis_init() failed initializing vorbis bitstream");
     return mActive = false;
   }
   ret = vorbis_block_init(&mDsp, &mBlock);
   if (ret != 0) {
     NS_WARNING("vorbis_block_init() failed initializing vorbis bitstream");
     if (mActive) {
       vorbis_dsp_clear(&mDsp);
     }
     return mActive = false;
   }
+
+  nsTArray<const unsigned char*> headers;
+  nsTArray<size_t> headerLens;
+  for (size_t i = 0; i < mHeaders.Length(); i++) {
+    headers.AppendElement(mHeaders[i]->packet);
+    headerLens.AppendElement(mHeaders[i]->bytes);
+  }
+  // Save header packets for the decoder
+  if (!XiphHeadersToExtradata(mInfo.mCodecSpecificConfig,
+                              headers, headerLens)) {
+    return mActive = false;
+  }
+  mHeaders.Erase();
+  mInfo.mMimeType = NS_LITERAL_CSTRING("audio/vorbis");
+  mInfo.mRate = mVorbisInfo.rate;
+  mInfo.mChannels = mVorbisInfo.channels;
+  mInfo.mBitDepth = 16;
+
   return true;
 }
 
 int64_t
 VorbisState::Time(int64_t granulepos)
 {
   if (!mActive) {
     return -1;
   }
 
-  return VorbisState::Time(&mInfo, granulepos);
+  return VorbisState::Time(&mVorbisInfo, granulepos);
 }
 
 int64_t
 VorbisState::Time(vorbis_info* aInfo, int64_t aGranulepos)
 {
   if (aGranulepos == -1 || aInfo->rate == 0) {
     return -1;
   }
@@ -833,17 +854,17 @@ VorbisState::ReconstructVorbisGranulepos
   // each packet.
 
   NS_ASSERTION(mUnstamped.Length() > 0, "Length must be > 0");
   ogg_packet* last = mUnstamped.LastElement();
   NS_ASSERTION(last->e_o_s || last->granulepos >= 0,
     "Must know last granulepos!");
   if (mUnstamped.Length() == 1) {
     ogg_packet* packet = mUnstamped[0];
-    long blockSize = vorbis_packet_blocksize(&mInfo, packet);
+    long blockSize = vorbis_packet_blocksize(&mVorbisInfo, packet);
     if (blockSize < 0) {
       // On failure vorbis_packet_blocksize returns < 0. If we've got
       // a bad packet, we just assume that decode will have to skip this
       // packet, i.e. assume 0 samples are decodable from this packet.
       blockSize = 0;
       mPrevVorbisBlockSize = 0;
     }
     long samples = mPrevVorbisBlockSize / 4 + blockSize / 4;
@@ -864,18 +885,18 @@ VorbisState::ReconstructVorbisGranulepos
 
   bool unknownGranulepos = last->granulepos == -1;
   int totalSamples = 0;
   for (int32_t i = mUnstamped.Length() - 1; i > 0; i--) {
     ogg_packet* packet = mUnstamped[i];
     ogg_packet* prev = mUnstamped[i-1];
     ogg_int64_t granulepos = packet->granulepos;
     NS_ASSERTION(granulepos != -1, "Must know granulepos!");
-    long prevBlockSize = vorbis_packet_blocksize(&mInfo, prev);
-    long blockSize = vorbis_packet_blocksize(&mInfo, packet);
+    long prevBlockSize = vorbis_packet_blocksize(&mVorbisInfo, prev);
+    long blockSize = vorbis_packet_blocksize(&mVorbisInfo, packet);
 
     if (blockSize < 0 || prevBlockSize < 0) {
       // On failure vorbis_packet_blocksize returns < 0. If we've got
       // a bad packet, we just assume that decode will have to skip this
       // packet, i.e. assume 0 samples are decodable from this packet.
       blockSize = 0;
       prevBlockSize = 0;
     }
@@ -889,17 +910,17 @@ VorbisState::ReconstructVorbisGranulepos
   if (unknownGranulepos) {
     for (uint32_t i = 0; i < mUnstamped.Length(); i++) {
       ogg_packet* packet = mUnstamped[i];
       packet->granulepos += mGranulepos + totalSamples + 1;
     }
   }
 
   ogg_packet* first = mUnstamped[0];
-  long blockSize = vorbis_packet_blocksize(&mInfo, first);
+  long blockSize = vorbis_packet_blocksize(&mVorbisInfo, first);
   if (blockSize < 0) {
     mPrevVorbisBlockSize = 0;
     blockSize = 0;
   }
 
   long samples = (mPrevVorbisBlockSize == 0) ? 0 :
                   mPrevVorbisBlockSize / 4 + blockSize / 4;
   int64_t start = first->granulepos - samples;
@@ -915,17 +936,17 @@ VorbisState::ReconstructVorbisGranulepos
     for (uint32_t i = 0; i < mUnstamped.Length() - 1; i++) {
       mUnstamped[i]->granulepos += pruned;
     }
 #ifdef VALIDATE_VORBIS_SAMPLE_CALCULATION
     mVorbisPacketSamples[last] -= pruned;
 #endif
   }
 
-  mPrevVorbisBlockSize = vorbis_packet_blocksize(&mInfo, last);
+  mPrevVorbisBlockSize = vorbis_packet_blocksize(&mVorbisInfo, last);
   mPrevVorbisBlockSize = std::max(static_cast<long>(0), mPrevVorbisBlockSize);
   mGranulepos = last->granulepos;
 
   return NS_OK;
 }
 
 OpusState::OpusState(ogg_page* aBosPage)
   : OggCodecState(aBosPage, true)
--- a/dom/media/ogg/OggCodecState.h
+++ b/dom/media/ogg/OggCodecState.h
@@ -63,16 +63,21 @@ class OggPacketQueue : private nsDeque
 public:
   OggPacketQueue() : nsDeque(new OggPacketDeallocator()) {}
   ~OggPacketQueue() { Erase(); }
   bool IsEmpty() { return nsDeque::GetSize() == 0; }
   void Append(ogg_packet* aPacket);
   ogg_packet* PopFront() { return static_cast<ogg_packet*>(nsDeque::PopFront()); }
   ogg_packet* PeekFront() { return static_cast<ogg_packet*>(nsDeque::PeekFront()); }
   ogg_packet* Pop() { return static_cast<ogg_packet*>(nsDeque::Pop()); }
+  ogg_packet* operator[](size_t aIndex) const
+  {
+    return static_cast<ogg_packet*>(nsDeque::ObjectAt(aIndex));
+  }
+  size_t Length() const { return nsDeque::GetSize(); }
   void PushFront(ogg_packet* aPacket) { nsDeque::PushFront(aPacket); }
   void Erase() { nsDeque::Erase(); }
 };
 
 // Encapsulates the data required for decoding an ogg bitstream and for
 // converting granulepos to timestamps.
 class OggCodecState
 {
@@ -275,29 +280,31 @@ public:
   CodecType GetType() override { return TYPE_VORBIS; }
   bool DecodeHeader(ogg_packet* aPacket) override;
   int64_t Time(int64_t granulepos) override;
   int64_t PacketDuration(ogg_packet* aPacket) override;
   bool Init() override;
   nsresult Reset() override;
   bool IsHeader(ogg_packet* aPacket) override;
   nsresult PageIn(ogg_page* aPage) override;
+  const TrackInfo* GetInfo() const override { return &mInfo; }
 
   // Return a hash table with tag metadata.
   MetadataTags* GetTags() override;
 
-  // Returns the end time that a granulepos represents.
-  static int64_t Time(vorbis_info* aInfo, int64_t aGranulePos);
-
-  vorbis_info mInfo;
+private:
+  AudioInfo mInfo;
+  vorbis_info mVorbisInfo;
   vorbis_comment mComment;
   vorbis_dsp_state mDsp;
   vorbis_block mBlock;
+  OggPacketQueue mHeaders;
 
-private:
+  // Returns the end time that a granulepos represents.
+  static int64_t Time(vorbis_info* aInfo, int64_t aGranulePos);
 
   // Reconstructs the granulepos of Vorbis packets stored in the mUnstamped
   // array.
   nsresult ReconstructVorbisGranulepos();
 
   // The "block size" of the previously decoded Vorbis packet, or 0 if we've
   // not yet decoded anything. This is used to calculate the number of samples
   // in a Vorbis packet, since each Vorbis packet depends on the previous
--- a/dom/media/ogg/OggDemuxer.cpp
+++ b/dom/media/ogg/OggDemuxer.cpp
@@ -416,30 +416,17 @@ OggDemuxer::SetupTargetTheora(TheoraStat
 
 void
 OggDemuxer::SetupTargetVorbis(VorbisState* aVorbisState, OggHeaders& aHeaders)
 {
   if (mVorbisState) {
     mVorbisState->Reset();
   }
 
-  // Copy Vorbis info data for time computations on other threads.
-  memcpy(&mVorbisInfo, &aVorbisState->mInfo, sizeof(mVorbisInfo));
-  mVorbisInfo.codec_setup = nullptr;
-
-  mInfo.mAudio.mMimeType = "audio/vorbis";
-  mInfo.mAudio.mRate = aVorbisState->mInfo.rate;
-  mInfo.mAudio.mChannels = aVorbisState->mInfo.channels;
-
-  // Save header packets for the decoder
-  if (!XiphHeadersToExtradata(mInfo.mAudio.mCodecSpecificConfig,
-                              aHeaders.mHeaders, aHeaders.mHeaderLens)) {
-    return;
-  }
-
+  mInfo.mAudio = *aVorbisState->GetInfo()->GetAsAudioInfo();
   mVorbisState = aVorbisState;
   mVorbisSerial = aVorbisState->mSerial;
 }
 
 void
 OggDemuxer::SetupTargetOpus(OpusState* aOpusState, OggHeaders& aHeaders)
 {
   if (mOpusState) {
@@ -532,24 +519,22 @@ OggDemuxer::SetupMediaTracksInfo(const n
         mInfo.mVideo.mDisplay = displaySize;
       }
     } else if (codecState->GetType() == OggCodecState::TYPE_VORBIS) {
       VorbisState* vorbisState = static_cast<VorbisState*>(codecState);
       if (!(mVorbisState && mVorbisState->mSerial == vorbisState->mSerial)) {
         continue;
       }
 
+      mInfo.mAudio = *vorbisState->GetInfo()->GetAsAudioInfo();
+
       if (msgInfo) {
-        InitTrack(msgInfo,
-                  &mInfo.mAudio,
-                  mVorbisState == vorbisState);
+        InitTrack(msgInfo, &mInfo.mAudio, mVorbisState == vorbisState);
       }
 
-      mInfo.mAudio.mRate = vorbisState->mInfo.rate;
-      mInfo.mAudio.mChannels = vorbisState->mInfo.channels;
       FillTags(&mInfo.mAudio, vorbisState->GetTags());
     } else if (codecState->GetType() == OggCodecState::TYPE_OPUS) {
       OpusState* opusState = static_cast<OpusState*>(codecState);
       if (!(mOpusState && mOpusState->mSerial == opusState->mSerial)) {
         continue;
       }
 
       mInfo.mAudio = *opusState->GetInfo()->GetAsAudioInfo();
@@ -807,28 +792,27 @@ OggDemuxer::ReadOggChain(const media::Ti
   MessageField* msgInfo = nullptr;
   if (mSkeletonState && mSkeletonState->mMsgFieldStore.Contains(serial)) {
     mSkeletonState->mMsgFieldStore.Get(serial, &msgInfo);
   }
 
   OggHeaders vorbisHeaders;
   if ((newVorbisState &&
        ReadHeaders(TrackInfo::kAudioTrack, newVorbisState, vorbisHeaders)) &&
-      (mVorbisState->mInfo.rate == newVorbisState->mInfo.rate) &&
-      (mVorbisState->mInfo.channels == newVorbisState->mInfo.channels)) {
+      (mVorbisState->GetInfo()->GetAsAudioInfo()->mRate ==
+       newVorbisState->GetInfo()->GetAsAudioInfo()->mRate) &&
+      (mVorbisState->GetInfo()->GetAsAudioInfo()->mChannels ==
+       newVorbisState->GetInfo()->GetAsAudioInfo()->mChannels)) {
 
     SetupTargetVorbis(newVorbisState, vorbisHeaders);
     LOG(LogLevel::Debug, ("New vorbis ogg link, serial=%d\n", mVorbisSerial));
 
     if (msgInfo) {
       InitTrack(msgInfo, &mInfo.mAudio, true);
     }
-    mInfo.mAudio.mMimeType = NS_LITERAL_CSTRING("audio/vorbis");
-    mInfo.mAudio.mRate = newVorbisState->mInfo.rate;
-    mInfo.mAudio.mChannels = newVorbisState->mInfo.channels;
 
     chained = true;
     tags = newVorbisState->GetTags();
   }
 
   OggHeaders opusHeaders;
   if ((newOpusState &&
        ReadHeaders(TrackInfo::kAudioTrack, newOpusState, opusHeaders)) &&
@@ -1094,17 +1078,17 @@ OggDemuxer::GetBuffered(TrackInfo::Track
         // until we find one.
         startOffset += page.header_len + page.body_len;
         continue;
       }
 
       uint32_t serial = ogg_page_serialno(&page);
       if (aType == TrackInfo::kAudioTrack && mVorbisState &&
           serial == mVorbisSerial) {
-        startTime = VorbisState::Time(&mVorbisInfo, granulepos);
+        startTime = mVorbisState->Time(granulepos);
         NS_ASSERTION(startTime > 0, "Must have positive start time");
       } else if (aType == TrackInfo::kAudioTrack && mOpusState &&
                  serial == mOpusSerial) {
         startTime = mOpusState->Time(granulepos);
         NS_ASSERTION(startTime > 0, "Must have positive start time");
       } else if (aType == TrackInfo::kAudioTrack && mFlacState &&
                  serial == mFlacSerial) {
         startTime = mFlacState->Time(granulepos);
--- a/dom/media/ogg/OggDemuxer.h
+++ b/dom/media/ogg/OggDemuxer.h
@@ -303,17 +303,16 @@ private:
   // data in the codec states due to threading issues. You must check the
   // associated mTheoraState or mVorbisState pointer is non-null before
   // using this codec data.
   uint32_t mVorbisSerial;
   uint32_t mOpusSerial;
   uint32_t mTheoraSerial;
   uint32_t mFlacSerial;
 
-  vorbis_info mVorbisInfo;
   th_info mTheoraInfo;
 
   Maybe<int64_t> mStartTime;
 
   // Booleans to indicate if we have audio and/or video data
   bool HasVideo() const;
   bool HasAudio() const;
   bool HasSkeleton() const