Bug 1322070: P5. Construct AudioInfo object from Opus CodecState. r?gerald draft
authorJean-Yves Avenard <jyavenard@mozilla.com>
Mon, 05 Dec 2016 20:34:38 +1100
changeset 448645 6b025c61ff5956428a63dfe1597a6da2fb8527fa
parent 448644 3d131025ccc0e707d4fd1d0b32c8f85a24fe3efc
child 448646 80a4a2137fa2db48def26ffef7de387aceb997d3
push id38389
push userbmo:jyavenard@mozilla.com
push dateMon, 12 Dec 2016 05:01:11 +0000
reviewersgerald
bugs1322070
milestone53.0a1
Bug 1322070: P5. Construct AudioInfo object from Opus CodecState. r?gerald MozReview-Commit-ID: 3lCqIOuIhV2
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
@@ -6,16 +6,17 @@
 
 #include <string.h>
 
 #include "mozilla/EndianUtils.h"
 #include <stdint.h>
 
 #include "nsDebug.h"
 #include "OggCodecState.h"
+#include "OpusDecoder.h"
 #include "OpusParser.h"
 #include "VideoUtils.h"
 #include <algorithm>
 
 #include <opus/opus.h>
 #include "opus/opus_multistream.h"
 
 // On Android JellyBean, the hardware.h header redefines version_major and
@@ -990,35 +991,46 @@ OpusState::Init(void)
 
   mDecoder = opus_multistream_decoder_create(mParser->mRate,
                                              mParser->mChannels,
                                              mParser->mStreams,
                                              mParser->mCoupledStreams,
                                              mParser->mMappingTable,
                                              &error);
 
+  mInfo.mMimeType = NS_LITERAL_CSTRING("audio/opus");
+  mInfo.mRate = mParser->mRate;
+  mInfo.mChannels = mParser->mChannels;
+  mInfo.mBitDepth = 16;
+  // Save preskip & the first header packet for the Opus decoder
+  OpusDataDecoder::AppendCodecDelay(mInfo.mCodecSpecificConfig,
+                                    Time(0, mParser->mPreSkip));
+  if (!mHeaders.PeekFront()) {
+    return false;
+  }
+  mInfo.mCodecSpecificConfig->AppendElements(mHeaders.PeekFront()->packet,
+                                             mHeaders.PeekFront()->bytes);
+  mHeaders.Erase();
   LOG(LogLevel::Debug, ("Opus decoder init"));
 
   return error == OPUS_OK;
 }
 
 bool
 OpusState::DecodeHeader(ogg_packet* aPacket)
 {
   nsAutoRef<ogg_packet> autoRelease(aPacket);
   switch(mPacketCount++) {
     // Parse the id header.
     case 0:
       mParser = new OpusParser;
       if (!mParser->DecodeHeader(aPacket->packet, aPacket->bytes)) {
         return false;
       }
-      mRate = mParser->mRate;
-      mChannels = mParser->mChannels;
-      mPreSkip = mParser->mPreSkip;
+      mHeaders.Append(autoRelease.disown());
       break;
 
     // Parse the metadata header.
     case 1:
       if (!mParser->DecodeTags(aPacket->packet, aPacket->bytes)) {
         return false;
       }
       break;
@@ -1141,17 +1153,17 @@ OpusState::ReconstructOpusGranulepos(voi
       "Must know last granulepos!");
   int64_t gp;
   // If this is the last page, and we've seen at least one previous page (or
   // this is the first page)...
   if (last->e_o_s) {
     if (mPrevPageGranulepos != -1) {
       // If this file only has one page and the final granule position is
       // smaller than the pre-skip amount, we MUST reject the stream.
-      if (!mDoneReadingHeaders && last->granulepos < mPreSkip)
+      if (!mDoneReadingHeaders && last->granulepos < mParser->mPreSkip)
         return false;
       int64_t last_gp = last->granulepos;
       gp = mPrevPageGranulepos;
       // Loop through the packets forwards, adding the current packet's
       // duration to the previous granulepos to get the value for the
       // current packet.
       for (uint32_t i = 0; i < mUnstamped.Length() - 1; ++i) {
         ogg_packet* packet = mUnstamped[i];
--- a/dom/media/ogg/OggCodecState.h
+++ b/dom/media/ogg/OggCodecState.h
@@ -393,48 +393,45 @@ public:
   int64_t Time(int64_t aGranulepos) override;
   int64_t PacketDuration(ogg_packet* aPacket) override;
   bool Init() override;
   nsresult Reset() override;
   nsresult Reset(bool aStart);
   bool IsHeader(ogg_packet* aPacket) override;
   nsresult PageIn(ogg_page* aPage) override;
   already_AddRefed<MediaRawData> PacketOutAsMediaRawData() override;
+  const TrackInfo* GetInfo() const override { return &mInfo; }
+
   // Returns the end time that a granulepos represents.
   static int64_t Time(int aPreSkip, int64_t aGranulepos);
 
-  // Various fields from the Ogg Opus header.
-  int mRate;        // Sample rate the decoder uses (always 48 kHz).
-  int mChannels;    // Number of channels the stream encodes.
-  uint16_t mPreSkip; // Number of samples to strip after decoder reset.
+  // Construct and return a table of tags from the metadata header.
+  MetadataTags* GetTags() override;
 
+private:
   nsAutoPtr<OpusParser> mParser;
   OpusMSDecoder* mDecoder;
 
   // Granule position (end sample) of the last decoded Opus packet. This is
   // used to calculate the amount we should trim from the last packet.
   int64_t mPrevPacketGranulepos;
 
-  // Construct and return a table of tags from the metadata header.
-  MetadataTags* GetTags() override;
-
-private:
-
   // Reconstructs the granulepos of Opus packets stored in the
   // mUnstamped array. mUnstamped must be filled with consecutive packets from
   // the stream, with the last packet having a known granulepos. Using this
   // known granulepos, and the known frame numbers, we recover the granulepos
   // of all frames in the array. This enables us to determine their timestamps.
   bool ReconstructOpusGranulepos();
 
   // Granule position (end sample) of the last decoded Opus page. This is
   // used to calculate the Opus per-packet granule positions on the last page,
   // where we may need to trim some samples from the end.
   int64_t mPrevPageGranulepos;
-
+  AudioInfo mInfo;
+  OggPacketQueue mHeaders;
 };
 
 // Constructs a 32bit version number out of two 16 bit major,minor
 // version numbers.
 #define SKELETON_VERSION(major, minor) (((major)<<16)|(minor))
 
 enum EMsgHeaderType {
   eContentType,
--- a/dom/media/ogg/OggDemuxer.cpp
+++ b/dom/media/ogg/OggDemuxer.cpp
@@ -134,17 +134,16 @@ OggDemuxer::OggDemuxer(MediaResource* aR
   , mOpusEnabled(MediaDecoder::IsOpusEnabled())
   , mSkeletonState(nullptr)
   , mAudioOggState(aResource)
   , mVideoOggState(aResource)
   , mVorbisSerial(0)
   , mOpusSerial(0)
   , mTheoraSerial(0)
   , mFlacSerial(0)
-  , mOpusPreSkip(0)
   , mIsChained(false)
   , mTimedMetadataEvent(nullptr)
   , mOnSeekableEvent(nullptr)
 {
   MOZ_COUNT_CTOR(OggDemuxer);
   PodZero(&mTheoraInfo);
 }
 
@@ -442,31 +441,19 @@ OggDemuxer::SetupTargetVorbis(VorbisStat
 
 void
 OggDemuxer::SetupTargetOpus(OpusState* aOpusState, OggHeaders& aHeaders)
 {
   if (mOpusState) {
     mOpusState->Reset();
   }
 
-  mInfo.mAudio.mMimeType = "audio/opus";
-  mInfo.mAudio.mRate = aOpusState->mRate;
-  mInfo.mAudio.mChannels = aOpusState->mChannels;
-
-  // Save preskip & the first header packet for the Opus decoder
-  uint64_t preSkip = aOpusState->Time(0, aOpusState->mPreSkip);
-  uint8_t c[sizeof(preSkip)];
-  BigEndian::writeUint64(&c[0], preSkip);
-  mInfo.mAudio.mCodecSpecificConfig->AppendElements(&c[0], sizeof(preSkip));
-  mInfo.mAudio.mCodecSpecificConfig->AppendElements(aHeaders.mHeaders[0],
-                                                    aHeaders.mHeaderLens[0]);
-
+  mInfo.mAudio = *aOpusState->GetInfo()->GetAsAudioInfo();
   mOpusState = aOpusState;
   mOpusSerial = aOpusState->mSerial;
-  mOpusPreSkip = aOpusState->mPreSkip;
 }
 
 void
 OggDemuxer::SetupTargetFlac(FlacState* aFlacState, OggHeaders& aHeaders)
 {
   if (mFlacState) {
     mFlacState->Reset();
   }
@@ -560,22 +547,22 @@ OggDemuxer::SetupMediaTracksInfo(const n
       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();
+
       if (msgInfo) {
         InitTrack(msgInfo, &mInfo.mAudio, mOpusState == opusState);
       }
 
-      mInfo.mAudio.mRate = opusState->mRate;
-      mInfo.mAudio.mChannels = opusState->mChannels;
       FillTags(&mInfo.mAudio, opusState->GetTags());
     } else if (codecState->GetType() == OggCodecState::TYPE_FLAC) {
       FlacState* flacState = static_cast<FlacState*>(codecState);
       if (!(mFlacState && mFlacState->mSerial == flacState->mSerial)) {
         continue;
       }
 
       mInfo.mAudio = *flacState->GetInfo()->GetAsAudioInfo();
@@ -840,27 +827,26 @@ OggDemuxer::ReadOggChain(const media::Ti
 
     chained = true;
     tags = newVorbisState->GetTags();
   }
 
   OggHeaders opusHeaders;
   if ((newOpusState &&
        ReadHeaders(TrackInfo::kAudioTrack, newOpusState, opusHeaders)) &&
-      (mOpusState->mRate == newOpusState->mRate) &&
-      (mOpusState->mChannels == newOpusState->mChannels)) {
+      (mOpusState->GetInfo()->GetAsAudioInfo()->mRate ==
+       newOpusState->GetInfo()->GetAsAudioInfo()->mRate) &&
+      (mOpusState->GetInfo()->GetAsAudioInfo()->mChannels ==
+       newOpusState->GetInfo()->GetAsAudioInfo()->mChannels)) {
 
     SetupTargetOpus(newOpusState, opusHeaders);
 
     if (msgInfo) {
       InitTrack(msgInfo, &mInfo.mAudio, true);
     }
-    mInfo.mAudio.mMimeType = NS_LITERAL_CSTRING("audio/opus");
-    mInfo.mAudio.mRate = newOpusState->mRate;
-    mInfo.mAudio.mChannels = newOpusState->mChannels;
 
     chained = true;
     tags = newOpusState->GetTags();
   }
 
   OggHeaders flacHeaders;
   if ((newFlacState &&
        ReadHeaders(TrackInfo::kAudioTrack, newFlacState, flacHeaders)) &&
@@ -1112,17 +1098,17 @@ OggDemuxer::GetBuffered(TrackInfo::Track
 
       uint32_t serial = ogg_page_serialno(&page);
       if (aType == TrackInfo::kAudioTrack && mVorbisState &&
           serial == mVorbisSerial) {
         startTime = VorbisState::Time(&mVorbisInfo, granulepos);
         NS_ASSERTION(startTime > 0, "Must have positive start time");
       } else if (aType == TrackInfo::kAudioTrack && mOpusState &&
                  serial == mOpusSerial) {
-        startTime = OpusState::Time(mOpusPreSkip, granulepos);
+        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);
         NS_ASSERTION(startTime > 0, "Must have positive start time");
       } else if (aType == TrackInfo::kVideoTrack && mTheoraState &&
                  serial == mTheoraSerial) {
         startTime = TheoraState::Time(&mTheoraInfo, granulepos);
--- a/dom/media/ogg/OggDemuxer.h
+++ b/dom/media/ogg/OggDemuxer.h
@@ -304,17 +304,16 @@ private:
   // 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;
-  int mOpusPreSkip;
   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