Bug 1196353 - Use standard Xiph extradata format to pass headers from demuxers to decoders. r=jya a=ritu
authorTimothy B. Terriberry <tterribe@vt.edu>
Fri, 21 Aug 2015 10:17:00 -0400
changeset 289197 a958dfd3b19e261a7ac9c898b828ca3768dcb18f
parent 289196 216bc8e9f02bb1d0bd0fdc23b187ed6aaae4c233
child 289198 1a54a3cfdceabb7fcff063a993a138ebf04a4fc1
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjya, ritu
bugs1196353
milestone42.0a2
Bug 1196353 - Use standard Xiph extradata format to pass headers from demuxers to decoders. r=jya a=ritu
dom/media/XiphExtradata.cpp
dom/media/XiphExtradata.h
dom/media/moz.build
dom/media/platforms/agnostic/VorbisDecoder.cpp
dom/media/webm/WebMDemuxer.cpp
new file mode 100644
--- /dev/null
+++ b/dom/media/XiphExtradata.cpp
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#include "XiphExtradata.h"
+
+namespace mozilla {
+
+bool XiphHeadersToExtradata(MediaByteBuffer* aCodecSpecificConfig,
+                            const nsTArray<const unsigned char*>& aHeaders,
+                            const nsTArray<size_t>& aHeaderLens)
+{
+  size_t nheaders = aHeaders.Length();
+  if (!nheaders || nheaders > 255) return false;
+  aCodecSpecificConfig->AppendElement(nheaders - 1);
+  for (size_t i = 0; i < nheaders - 1; i++) {
+    size_t headerLen;
+    for (headerLen = aHeaderLens[i]; headerLen >= 255; headerLen -= 255) {
+      aCodecSpecificConfig->AppendElement(255);
+    }
+    aCodecSpecificConfig->AppendElement(headerLen);
+  }
+  for (size_t i = 0; i < nheaders; i++) {
+    aCodecSpecificConfig->AppendElements(aHeaders[i], aHeaderLens[i]);
+  }
+  return true;
+}
+
+bool XiphExtradataToHeaders(nsTArray<unsigned char*>& aHeaders,
+                            nsTArray<size_t>& aHeaderLens,
+                            unsigned char* aData,
+                            size_t aAvailable)
+{
+  size_t total = 0;
+  if (aAvailable < 1) {
+    return false;
+  }
+  aAvailable--;
+  int nHeaders = *aData++ + 1;
+  for (int i = 0; i < nHeaders - 1; i++) {
+    size_t headerLen = 0;
+    for (;;) {
+      // After this test, we know that (aAvailable - total > headerLen) and
+      // (headerLen >= 0) so (aAvailable - total > 0). The loop decrements
+      // aAvailable by 1 and total remains fixed, so we know that in the next
+      // iteration (aAvailable - total >= 0). Thus (aAvailable - total) can
+      // never underflow.
+      if (aAvailable - total <= headerLen) {
+        return false;
+      }
+      // Since we know (aAvailable > total + headerLen), this can't overflow
+      // unless total is near 0 and both aAvailable and headerLen are within
+      // 255 bytes of the maximum representable size. However, that is
+      // impossible, since we would have had to have gone through this loop
+      // more than 255 times to make headerLen that large, and thus decremented
+      // aAvailable more than 255 times.
+      headerLen += *aData;
+      aAvailable--;
+      if (*aData++ != 255) break;
+    }
+    // And this check ensures updating total won't cause (aAvailable - total)
+    // to underflow.
+    if (aAvailable - total < headerLen) {
+      return false;
+    }
+    aHeaderLens.AppendElement(headerLen);
+    // Since we know aAvailable >= total + headerLen, this can't overflow.
+    total += headerLen;
+  }
+  aHeaderLens.AppendElement(aAvailable);
+  for (int i = 0; i < nHeaders; i++) {
+    aHeaders.AppendElement(aData);
+    aData += aHeaderLens[i];
+  }
+  return true;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/XiphExtradata.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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(XiphExtradata_h)
+#define XiphExtradata_h
+
+#include "MediaData.h"
+
+namespace mozilla {
+
+/* This converts a list of headers to the canonical form of extradata for Xiph
+   codecs in non-Ogg containers. We use it to pass those headers from demuxer
+   to decoder even when demuxing from an Ogg cotainer. */
+bool XiphHeadersToExtradata(MediaByteBuffer* aCodecSpecificConfig,
+                            const nsTArray<const unsigned char*>& aHeaders,
+                            const nsTArray<size_t>& aHeaderLens);
+
+/* This converts a set of extradata back into a list of headers. */
+bool XiphExtradataToHeaders(nsTArray<unsigned char*>& aHeaders,
+                            nsTArray<size_t>& aHeaderLens,
+                            unsigned char* aData,
+                            size_t aAvailable);
+
+} // namespace mozilla
+
+#endif // XiphExtradata_h
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -142,16 +142,17 @@ EXPORTS += [
     'ThreadPoolCOMListener.h',
     'TimeUnits.h',
     'TimeVarying.h',
     'TrackUnionStream.h',
     'VideoFrameContainer.h',
     'VideoSegment.h',
     'VideoUtils.h',
     'VorbisUtils.h',
+    'XiphExtradata.h',
 ]
 
 EXPORTS.mozilla += [
     'MediaManager.h',
     'MozPromise.h',
     'StateMirroring.h',
     'StateWatching.h',
     'TaskQueue.h',
@@ -243,16 +244,17 @@ UNIFIED_SOURCES += [
     'VideoFrameContainer.cpp',
     'VideoPlaybackQuality.cpp',
     'VideoSegment.cpp',
     'VideoStreamTrack.cpp',
     'VideoTrack.cpp',
     'VideoTrackList.cpp',
     'VideoUtils.cpp',
     'WebVTTListener.cpp',
+    'XiphExtradata.cpp',
 ]
 
 if CONFIG['OS_TARGET'] == 'WINNT':
   SOURCES += [ 'ThreadPoolCOMListener.cpp' ]
 
 if CONFIG['MOZ_B2G']:
     SOURCES += [
         'MediaPermissionGonk.cpp',
--- a/dom/media/platforms/agnostic/VorbisDecoder.cpp
+++ b/dom/media/platforms/agnostic/VorbisDecoder.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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/. */
 
 #include "VorbisDecoder.h"
 #include "VorbisUtils.h"
+#include "XiphExtradata.h"
 
 #include "mozilla/PodOperations.h"
 #include "nsAutoPtr.h"
 
 #undef LOG
 #define LOG(type, msg) MOZ_LOG(gMediaDecoderLog, type, msg)
 
 namespace mozilla {
@@ -66,33 +67,27 @@ VorbisDataDecoder::Shutdown()
 nsresult
 VorbisDataDecoder::Init()
 {
   vorbis_info_init(&mVorbisInfo);
   vorbis_comment_init(&mVorbisComment);
   PodZero(&mVorbisDsp);
   PodZero(&mVorbisBlock);
 
-  size_t available = mInfo.mCodecSpecificConfig->Length();
-  uint8_t *p = mInfo.mCodecSpecificConfig->Elements();
-  for(int i = 0; i < 3; i++) {
-    if (available < 2) {
+  nsAutoTArray<unsigned char*,4> headers;
+  nsAutoTArray<size_t,4> headerLens;
+  if (!XiphExtradataToHeaders(headers, headerLens,
+                              mInfo.mCodecSpecificConfig->Elements(),
+                              mInfo.mCodecSpecificConfig->Length())) {
+    return NS_ERROR_FAILURE;
+  }
+  for (size_t i = 0; i < headers.Length(); i++) {
+    if (NS_FAILED(DecodeHeader(headers[i], headerLens[i]))) {
       return NS_ERROR_FAILURE;
     }
-    available -= 2;
-    size_t length = BigEndian::readUint16(p);
-    p += 2;
-    if (available < length) {
-      return NS_ERROR_FAILURE;
-    }
-    available -= length;
-    if (NS_FAILED(DecodeHeader((const unsigned char*)p, length))) {
-        return NS_ERROR_FAILURE;
-    }
-    p += length;
   }
 
   MOZ_ASSERT(mPacketCount == 3);
 
   int r = vorbis_synthesis_init(&mVorbisDsp, &mVorbisInfo);
   if (r) {
     return NS_ERROR_FAILURE;
   }
--- a/dom/media/webm/WebMDemuxer.cpp
+++ b/dom/media/webm/WebMDemuxer.cpp
@@ -11,16 +11,17 @@
 #include "WebMDemuxer.h"
 #include "WebMBufferedParser.h"
 #include "gfx2DGlue.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/SharedThreadPool.h"
 #include "MediaDataDemuxer.h"
 #include "nsAutoRef.h"
 #include "NesteggPacketHolder.h"
+#include "XiphExtradata.h"
 
 #include <algorithm>
 #include <stdint.h>
 
 #define VPX_DONT_DEFINE_STDINT_TYPES
 #include "vpx/vp8dx.h"
 #include "vpx/vpx_decoder.h"
 
@@ -384,30 +385,43 @@ WebMDemuxer::ReadMetadata()
       mInfo.mAudio.mChannels = params.channels;
 
       unsigned int nheaders = 0;
       r = nestegg_track_codec_data_count(mContext, track, &nheaders);
       if (r == -1) {
         return NS_ERROR_FAILURE;
       }
 
+      nsAutoTArray<const unsigned char*,4> headers;
+      nsAutoTArray<size_t,4> headerLens;
       for (uint32_t header = 0; header < nheaders; ++header) {
         unsigned char* data = 0;
         size_t length = 0;
         r = nestegg_track_codec_data(mContext, track, header, &data, &length);
         if (r == -1) {
           return NS_ERROR_FAILURE;
         }
-        // Vorbis has 3 headers write length + data for each header
-        if (nheaders > 1) {
-          uint8_t c[2];
-          BigEndian::writeUint16(&c[0], length);
-          mInfo.mAudio.mCodecSpecificConfig->AppendElements(&c[0], 2);
+        headers.AppendElement(data);
+        headerLens.AppendElement(length);
+      }
+
+      // Vorbis has 3 headers, convert to Xiph extradata format to send them to
+      // the demuxer.
+      // TODO: This is already the format WebM stores them in. Would be nice
+      // to avoid having libnestegg split them only for us to pack them again,
+      // but libnestegg does not give us an API to access this data directly.
+      if (nheaders > 1) {
+        if (!XiphHeadersToExtradata(mInfo.mAudio.mCodecSpecificConfig,
+                                    headers, headerLens)) {
+          return NS_ERROR_FAILURE;
         }
-        mInfo.mAudio.mCodecSpecificConfig->AppendElements(data, length);
+      }
+      else {
+        mInfo.mAudio.mCodecSpecificConfig->AppendElements(headers[0],
+                                                          headerLens[0]);
       }
       uint64_t duration = 0;
       r = nestegg_duration(mContext, &duration);
       if (!r) {
         mInfo.mAudio.mDuration = media::TimeUnit::FromNanoseconds(duration).ToMicroseconds();
       }
     }
   }