Bug 1240201: [vorbis] P1. Properly determine sample duration and time in webm demuxer. r=kinetik
☠☠ backed out by 8381cc4aa935 ☠ ☠
authorJean-Yves Avenard <jyavenard@mozilla.com>
Thu, 21 Jan 2016 02:26:49 +1100
changeset 280930 69b695ad6d43bfa2344161b31102e56d8a27ec19
parent 280929 18e7baac03cedde5d4307362d0d8ee4255eeef55
child 280931 b06a049cafb789e5f7b4d20be64b1c745173a86c
push id70626
push userjyavenard@mozilla.com
push dateThu, 21 Jan 2016 13:43:17 +0000
treeherdermozilla-inbound@b06a049cafb7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik
bugs1240201
milestone46.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1240201: [vorbis] P1. Properly determine sample duration and time in webm demuxer. r=kinetik
dom/media/platforms/agnostic/VorbisDecoder.cpp
dom/media/platforms/agnostic/VorbisDecoder.h
dom/media/webm/WebMDemuxer.cpp
dom/media/webm/WebMDemuxer.h
--- a/dom/media/platforms/agnostic/VorbisDecoder.cpp
+++ b/dom/media/platforms/agnostic/VorbisDecoder.cpp
@@ -12,19 +12,73 @@
 #include "nsAutoPtr.h"
 
 #undef LOG
 extern mozilla::LogModule* GetPDMLog();
 #define LOG(type, msg) MOZ_LOG(GetPDMLog(), type, msg)
 
 namespace mozilla {
 
-ogg_packet InitVorbisPacket(const unsigned char* aData, size_t aLength,
-                         bool aBOS, bool aEOS,
-                         int64_t aGranulepos, int64_t aPacketNo)
+VorbisPacketSampleCounter::VorbisPacketSampleCounter()
+  : mError(0)
+  , mVorbisPacketCount(0)
+{
+  vorbis_info_init(&mVorbisInfo);
+  vorbis_comment_init(&mVorbisComment);
+}
+
+bool
+VorbisPacketSampleCounter::Init(const nsTArray<const uint8_t*>& aHeaders,
+                                const nsTArray<size_t>& aHeaderLens)
+{
+  for (size_t i = 0; i < aHeaders.Length(); i++) {
+    ogg_packet pkt =
+      VorbisDataDecoder::InitVorbisPacket(aHeaders[i], aHeaderLens[i],
+                                          i == 0, false, 0, mVorbisPacketCount++);
+    if ((mError = vorbis_synthesis_headerin(&mVorbisInfo, &mVorbisComment, &pkt))) {
+      return false;
+    }
+  }
+  return true;
+}
+
+int
+VorbisPacketSampleCounter::GetNumSamples(const uint8_t* aData, size_t aLength)
+{
+  if (mError) {
+    return -1;
+  }
+  ogg_packet pkt =
+    VorbisDataDecoder::InitVorbisPacket(aData, aLength, false, false, -1, mVorbisPacketCount++);
+  long blockSize = vorbis_packet_blocksize(&mVorbisInfo, &pkt);
+  if (blockSize < 0) {
+    return blockSize;
+  }
+  int nsamples;
+  if (mVorbisLastBlockSize) {
+    nsamples = mVorbisLastBlockSize.ref() / 4 + blockSize / 4;
+  } else {
+    // The first packet will output no audio, so set its count as 0.
+    nsamples = 0;
+  }
+  mVorbisLastBlockSize = Some(blockSize);
+  return nsamples;
+}
+
+VorbisPacketSampleCounter::~VorbisPacketSampleCounter()
+{
+  vorbis_info_clear(&mVorbisInfo);
+  vorbis_comment_clear(&mVorbisComment);
+}
+
+/* static */
+ogg_packet
+VorbisDataDecoder::InitVorbisPacket(const unsigned char* aData, size_t aLength,
+                                    bool aBOS, bool aEOS,
+                                    int64_t aGranulepos, int64_t aPacketNo)
 {
   ogg_packet packet;
   packet.packet = const_cast<unsigned char*>(aData);
   packet.bytes = aLength;
   packet.b_o_s = aBOS;
   packet.e_o_s = aEOS;
   packet.granulepos = aGranulepos;
   packet.packetno = aPacketNo;
--- a/dom/media/platforms/agnostic/VorbisDecoder.h
+++ b/dom/media/platforms/agnostic/VorbisDecoder.h
@@ -12,16 +12,35 @@
 #ifdef MOZ_TREMOR
 #include "tremor/ivorbiscodec.h"
 #else
 #include "vorbis/codec.h"
 #endif
 
 namespace mozilla {
 
+class VorbisPacketSampleCounter
+{
+public:
+  VorbisPacketSampleCounter();
+  ~VorbisPacketSampleCounter();
+
+  bool Init(const nsTArray<const uint8_t*>& aHeaders,
+            const nsTArray<size_t>& aHeaderLens);
+  int GetNumSamples(const uint8_t* aData, size_t aLength);
+  void Reset() { mVorbisLastBlockSize.reset(); }
+
+private:
+  int mError;
+  vorbis_info mVorbisInfo;
+  vorbis_comment mVorbisComment;
+  int64_t mVorbisPacketCount;
+  Maybe<long> mVorbisLastBlockSize;
+};
+
 class VorbisDataDecoder : public MediaDataDecoder
 {
 public:
   VorbisDataDecoder(const AudioInfo& aConfig,
                 FlushableTaskQueue* aTaskQueue,
                 MediaDataDecoderCallback* aCallback);
   ~VorbisDataDecoder();
 
@@ -29,16 +48,20 @@ public:
   nsresult Input(MediaRawData* aSample) override;
   nsresult Flush() override;
   nsresult Drain() override;
   nsresult Shutdown() override;
 
   // Return true if mimetype is Vorbis
   static bool IsVorbis(const nsACString& aMimeType);
 
+  static ogg_packet InitVorbisPacket(const unsigned char* aData, size_t aLength,
+                                     bool aBOS, bool aEOS,
+                                     int64_t aGranulepos, int64_t aPacketNo);
+
 private:
   nsresult DecodeHeader(const unsigned char* aData, size_t aLength);
 
   void Decode (MediaRawData* aSample);
   int DoDecode (MediaRawData* aSample);
   void DoDrain ();
 
   const AudioInfo& mInfo;
--- a/dom/media/webm/WebMDemuxer.cpp
+++ b/dom/media/webm/WebMDemuxer.cpp
@@ -7,21 +7,23 @@
 #include "nsError.h"
 #include "MediaDecoderStateMachine.h"
 #include "AbstractMediaDecoder.h"
 #include "MediaResource.h"
 #include "WebMDemuxer.h"
 #include "WebMBufferedParser.h"
 #include "gfx2DGlue.h"
 #include "mozilla/Endian.h"
+#include "mozilla/PodOperations.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/SharedThreadPool.h"
 #include "MediaDataDemuxer.h"
 #include "nsAutoRef.h"
 #include "NesteggPacketHolder.h"
+#include "VideoUtils.h"
 #include "XiphExtradata.h"
 #include "prprf.h"
 
 #include <algorithm>
 #include <stdint.h>
 
 #define VPX_DONT_DEFINE_STDINT_TYPES
 #include "vpx/vp8dx.h"
@@ -412,16 +414,23 @@ WebMDemuxer::ReadMetadata()
                                     headers, headerLens)) {
           return NS_ERROR_FAILURE;
         }
       }
       else {
         mInfo.mAudio.mCodecSpecificConfig->AppendElements(headers[0],
                                                           headerLens[0]);
       }
+
+      if (mAudioCodec == NESTEGG_CODEC_VORBIS) {
+        if (!mVorbisCounter.Init(headers, headerLens)) {
+          return NS_ERROR_FAILURE;
+        }
+      }
+
       uint64_t duration = 0;
       r = nestegg_duration(mContext, &duration);
       if (!r) {
         mInfo.mAudio.mDuration = media::TimeUnit::FromNanoseconds(duration).ToMicroseconds();
       }
     }
   }
   return NS_OK;
@@ -537,26 +546,41 @@ WebMDemuxer::GetNextPacket(TrackInfo::Tr
   if (mIsMediaSource && next_tstamp == INT64_MIN) {
     return false;
   }
 
   int64_t discardPadding = 0;
   (void) nestegg_packet_discard_padding(holder->Packet(), &discardPadding);
 
   for (uint32_t i = 0; i < count; ++i) {
+    int64_t duration = next_tstamp - tstamp;
     unsigned char* data;
     size_t length;
     r = nestegg_packet_data(holder->Packet(), i, &data, &length);
     if (r == -1) {
       WEBM_DEBUG("nestegg_packet_data failed r=%d", r);
       return false;
     }
     bool isKeyframe = false;
     if (aType == TrackInfo::kAudioTrack) {
       isKeyframe = true;
+      switch (mAudioCodec) {
+        case NESTEGG_CODEC_VORBIS:
+        {
+          int nsamples = mVorbisCounter.GetNumSamples(data, length);
+          if (nsamples < 0) {
+            return false;
+          }
+          duration =
+            FramesToTimeUnit(nsamples, mInfo.mAudio.mRate).ToMicroseconds();
+          break;
+        }
+        default:
+          break;
+      }
     } else if (aType == TrackInfo::kVideoTrack) {
       vpx_codec_stream_info_t si;
       PodZero(&si);
       si.sz = sizeof(si);
       switch (mVideoCodec) {
         case NESTEGG_CODEC_VP8:
           vpx_codec_peek_stream_info(vpx_codec_vp8_dx(), data, length, &si);
           break;
@@ -567,26 +591,29 @@ WebMDemuxer::GetNextPacket(TrackInfo::Tr
       isKeyframe = si.is_kf;
     }
 
     WEBM_DEBUG("push sample tstamp: %ld next_tstamp: %ld length: %ld kf: %d",
                tstamp, next_tstamp, length, isKeyframe);
     RefPtr<MediaRawData> sample = new MediaRawData(data, length);
     sample->mTimecode = tstamp;
     sample->mTime = tstamp;
-    sample->mDuration = next_tstamp - tstamp;
+    sample->mDuration = duration;
     sample->mOffset = holder->Offset();
     sample->mKeyframe = isKeyframe;
     if (discardPadding) {
       uint8_t c[8];
       BigEndian::writeInt64(&c[0], discardPadding);
       sample->mExtraData = new MediaByteBuffer;
       sample->mExtraData->AppendElements(&c[0], 8);
     }
     aSamples->Push(sample);
+    if (mAudioCodec == NESTEGG_CODEC_VORBIS) {
+      tstamp += duration;
+    }
   }
   return true;
 }
 
 RefPtr<NesteggPacketHolder>
 WebMDemuxer::NextPacket(TrackInfo::TrackType aType)
 {
   bool isVideo = aType == TrackInfo::kVideoTrack;
@@ -716,16 +743,17 @@ WebMDemuxer::SeekInternal(const media::T
       WEBM_DEBUG("and nestegg_offset_seek to %" PRIu64 " failed", offset);
       return NS_ERROR_FAILURE;
     }
     WEBM_DEBUG("got offset from buffered state: %" PRIu64 "", offset);
   }
 
   mLastAudioFrameTime.reset();
   mLastVideoFrameTime.reset();
+  mVorbisCounter.Reset();
 
   return NS_OK;
 }
 
 media::TimeIntervals
 WebMDemuxer::GetBuffered()
 {
   EnsureUpToDateIndex();
--- a/dom/media/webm/WebMDemuxer.h
+++ b/dom/media/webm/WebMDemuxer.h
@@ -4,18 +4,20 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #if !defined(WebMDemuxer_h_)
 #define WebMDemuxer_h_
 
 #include "nsTArray.h"
 #include "MediaDataDemuxer.h"
 #include "NesteggPacketHolder.h"
+#include "mozilla/Maybe.h"
 #include "mozilla/Move.h"
 
+#include "VorbisDecoder.h"
 typedef struct nestegg nestegg;
 
 namespace mozilla {
 
 class WebMBufferedState;
 
 // Queue for holding MediaRawData samples
 class MediaRawDataQueue {
@@ -194,16 +196,18 @@ private:
   bool mHasAudio;
   bool mNeedReIndex;
 
   // The last complete block parsed by the WebMBufferedState. -1 if not set.
   // We cache those values rather than retrieving them for performance reasons
   // as nestegg only performs 1-byte read at a time.
   int64_t mLastWebMBlockOffset;
   const bool mIsMediaSource;
+
+  VorbisPacketSampleCounter mVorbisCounter;
 };
 
 class WebMTrackDemuxer : public MediaTrackDemuxer
 {
 public:
   WebMTrackDemuxer(WebMDemuxer* aParent,
                   TrackInfo::TrackType aType,
                   uint32_t aTrackNumber);