Bug 1196353 - Use standard Xiph extradata format to pass headers from demuxers to decoders. r=jya

This commit is contained in:
Timothy B. Terriberry 2015-08-21 10:17:00 -04:00
parent b30cf3972d
commit 94ebb79c9f
5 changed files with 140 additions and 21 deletions

View File

@ -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

28
dom/media/XiphExtradata.h Normal file
View File

@ -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

View File

@ -149,6 +149,7 @@ EXPORTS += [
'VideoSegment.h',
'VideoUtils.h',
'VorbisUtils.h',
'XiphExtradata.h',
]
EXPORTS.mozilla += [
@ -245,6 +246,7 @@ UNIFIED_SOURCES += [
'VideoTrackList.cpp',
'VideoUtils.cpp',
'WebVTTListener.cpp',
'XiphExtradata.cpp',
]
if CONFIG['OS_TARGET'] == 'WINNT':

View File

@ -6,6 +6,7 @@
#include "VorbisDecoder.h"
#include "VorbisUtils.h"
#include "XiphExtradata.h"
#include "mozilla/PodOperations.h"
#include "nsAutoPtr.h"
@ -71,23 +72,17 @@ VorbisDataDecoder::Init()
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 InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
}
for (size_t i = 0; i < headers.Length(); i++) {
if (NS_FAILED(DecodeHeader(headers[i], headerLens[i]))) {
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
}
available -= 2;
size_t length = BigEndian::readUint16(p);
p += 2;
if (available < length) {
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
}
available -= length;
if (NS_FAILED(DecodeHeader((const unsigned char*)p, length))) {
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
}
p += length;
}
MOZ_ASSERT(mPacketCount == 3);

View File

@ -16,6 +16,7 @@
#include "MediaDataDemuxer.h"
#include "nsAutoRef.h"
#include "NesteggPacketHolder.h"
#include "XiphExtradata.h"
#include <algorithm>
#include <stdint.h>
@ -378,6 +379,8 @@ WebMDemuxer::ReadMetadata()
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;
@ -385,13 +388,24 @@ WebMDemuxer::ReadMetadata()
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);