Bug 1444479 - P6. Make Opus and Vorbis decoder deal with more channels than 8. r?padenot
Under 8 channels, the audio will be reordered so it can be playable on any platforms.
Over 8 channels, the channels will be as output by the decoder. Playing of such stream will be platform dependent as neither Opus nor Vorbis define a channel layout with more than 8 channels.
With WebAudio however, the result will be platform independent (as long as you don't attempt to play it)
MozReview-Commit-ID: 93ATiKm9y20
--- a/dom/media/ogg/OpusParser.cpp
+++ b/dom/media/ogg/OpusParser.cpp
@@ -75,24 +75,25 @@ bool OpusParser::DecodeHeader(unsigned c
" mapping family 0.", mChannels));
return false;
}
mStreams = 1;
mCoupledStreams = mChannels - 1;
mMappingTable[0] = 0;
mMappingTable[1] = 1;
} else if (mChannelMapping == 1 || mChannelMapping == 255) {
- // Currently only up to 8 channels are defined for mapping family 1 and we
- // only supports only up to 8 channels for mapping family 255.
- if (mChannels>8) {
- OPUS_LOG(LogLevel::Debug, ("Invalid Opus file: too many channels (%d) for"
- " mapping family 1.", mChannels));
+ // Currently only up to 8 channels are defined for mapping family 1
+ if (mChannelMapping == 1 && mChannels > 8) {
+ OPUS_LOG(LogLevel::Debug,
+ ("Invalid Opus file: too many channels (%d) for"
+ " mapping family 1.",
+ mChannels));
return false;
}
- if (aLength>static_cast<unsigned>(20+mChannels)) {
+ if (aLength > static_cast<unsigned>(20 + mChannels)) {
mStreams = aData[19];
mCoupledStreams = aData[20];
int i;
for (i=0; i<mChannels; i++)
mMappingTable[i] = aData[21+i];
} else {
OPUS_LOG(LogLevel::Debug, ("Invalid Opus file: channel mapping %d,"
" but no channel mapping table", mChannelMapping));
--- a/dom/media/platforms/agnostic/OpusDecoder.cpp
+++ b/dom/media/platforms/agnostic/OpusDecoder.cpp
@@ -82,22 +82,23 @@ OpusDataDecoder::Init()
if (NS_FAILED(DecodeHeader(p, length))) {
OPUS_DEBUG("Error decoding header!");
return InitPromise::CreateAndReject(
MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
RESULT_DETAIL("Error decoding header!")),
__func__);
}
+ MOZ_ASSERT(mMappingTable.Length() >= mOpusParser->mChannels);
int r;
mOpusDecoder = opus_multistream_decoder_create(mOpusParser->mRate,
mOpusParser->mChannels,
mOpusParser->mStreams,
mOpusParser->mCoupledStreams,
- mMappingTable,
+ mMappingTable.Elements(),
&r);
mSkip = mOpusParser->mPreSkip;
mPaddingDiscarded = false;
if (codecDelay != FramesToUsecs(mOpusParser->mPreSkip,
mOpusParser->mRate).value()) {
NS_WARNING("Invalid Opus header: CodecDelay and pre-skip do not match!");
return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
@@ -126,41 +127,42 @@ OpusDataDecoder::DecodeHeader(const unsi
mDecodedHeader = true;
mOpusParser = new OpusParser;
if (!mOpusParser->DecodeHeader(const_cast<unsigned char*>(aData), aLength)) {
return NS_ERROR_FAILURE;
}
int channels = mOpusParser->mChannels;
+ mMappingTable.SetLength(channels);
AudioConfig::ChannelLayout vorbisLayout(
channels, VorbisDataDecoder::VorbisLayout(channels));
- if (!vorbisLayout.IsValid()) {
- OPUS_DEBUG("Invalid channel mapping. Source is %d channels", channels);
- return NS_ERROR_FAILURE;
- }
- mChannelMap = vorbisLayout.Map();
+ if (vorbisLayout.IsValid()) {
+ mChannelMap = vorbisLayout.Map();
- AudioConfig::ChannelLayout smpteLayout(
- AudioConfig::ChannelLayout::SMPTEDefault(vorbisLayout));
+ AudioConfig::ChannelLayout smpteLayout(
+ AudioConfig::ChannelLayout::SMPTEDefault(vorbisLayout));
- static_assert(sizeof(mOpusParser->mMappingTable) /
- sizeof(mOpusParser->mMappingTable[0]) >=
- MAX_AUDIO_CHANNELS,
- "Invalid size set");
- uint8_t map[sizeof(mOpusParser->mMappingTable) /
- sizeof(mOpusParser->mMappingTable[0])];
- if (vorbisLayout.MappingTable(smpteLayout, map)) {
- for (int i = 0; i < channels; i++) {
- mMappingTable[i] = mOpusParser->mMappingTable[map[i]];
+ AutoTArray<uint8_t, 8> map;
+ map.SetLength(channels);
+ if (vorbisLayout.MappingTable(smpteLayout, &map)) {
+ for (int i = 0; i < channels; i++) {
+ mMappingTable[i] = mOpusParser->mMappingTable[map[i]];
+ }
+ } else {
+ // Should never get here as vorbis layout is always convertible to SMPTE
+ // default layout.
+ PodCopy(mMappingTable.Elements(), mOpusParser->mMappingTable, channels);
}
} else {
- // Should never get here as vorbis layout is always convertible to SMPTE
- // default layout.
- PodCopy(mMappingTable, mOpusParser->mMappingTable, MAX_AUDIO_CHANNELS);
+ // Create a dummy mapping table so that channel ordering stay the same
+ // during decoding.
+ for (uint32_t i = 0; i < channels; i++) {
+ mMappingTable[i] = i;
+ }
}
return NS_OK;
}
RefPtr<MediaDataDecoder::DecodePromise>
OpusDataDecoder::Decode(MediaRawData* aSample)
{
--- a/dom/media/platforms/agnostic/OpusDecoder.h
+++ b/dom/media/platforms/agnostic/OpusDecoder.h
@@ -5,16 +5,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#if !defined(OpusDecoder_h_)
#define OpusDecoder_h_
#include "PlatformDecoderModule.h"
#include "mozilla/Maybe.h"
#include "nsAutoPtr.h"
+#include "nsTArray.h"
struct OpusMSDecoder;
namespace mozilla {
class OpusParser;
DDLoggedTypeDeclNameAndBase(OpusDataDecoder, MediaDataDecoder);
@@ -63,14 +64,14 @@ private:
bool mDecodedHeader;
// Opus padding should only be discarded on the final packet. Once this
// is set to true, if the reader attempts to decode any further packets it
// will raise an error so we can indicate that the file is invalid.
bool mPaddingDiscarded;
int64_t mFrames;
Maybe<int64_t> mLastFrameTime;
- uint8_t mMappingTable[MAX_AUDIO_CHANNELS]; // Channel mapping table.
+ AutoTArray<uint8_t, 8> mMappingTable;
AudioConfig::ChannelLayout::ChannelMap mChannelMap;
};
} // namespace mozilla
#endif
--- a/dom/media/platforms/agnostic/VorbisDecoder.cpp
+++ b/dom/media/platforms/agnostic/VorbisDecoder.cpp
@@ -230,24 +230,19 @@ VorbisDataDecoder::ProcessDecode(MediaRa
NS_ERROR_DOM_MEDIA_OVERFLOW_ERR,
RESULT_DETAIL("Overflow adding total_duration and aSample->mTime")),
__func__);
};
if (!mAudioConverter) {
const AudioConfig::ChannelLayout layout =
AudioConfig::ChannelLayout(channels, VorbisLayout(channels));
- AudioConfig in(layout, rate);
- AudioConfig out(AudioConfig::ChannelLayout::SMPTEDefault(layout), rate);
- if (!in.IsValid() || !out.IsValid()) {
- return DecodePromise::CreateAndReject(
- MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
- RESULT_DETAIL("Invalid channel layout:%u", channels)),
- __func__);
- }
+ AudioConfig in(layout, channels, rate);
+ AudioConfig out(
+ AudioConfig::ChannelLayout::SMPTEDefault(layout), channels, rate);
mAudioConverter = MakeUnique<AudioConverter>(in, out);
}
MOZ_ASSERT(mAudioConverter->CanWorkInPlace());
AudioSampleBuffer data(Move(buffer));
data = mAudioConverter->Process(Move(data));
results.AppendElement(
new AudioData(aOffset,