Fix incorrect CHANNELS reporting for AC3/EC3

Fixes #313

Change-Id: I0b8d8081cda8aaa4db2ca4916bfc6721bfa3bd22
This commit is contained in:
KongQun Yang 2018-01-05 17:18:23 -08:00
parent 858fcd8698
commit 2ba23e1075
9 changed files with 179 additions and 23 deletions

View File

@ -0,0 +1,52 @@
// Copyright 2018 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "packager/media/codecs/ac3_audio_util.h"
#include "packager/base/strings/string_number_conversions.h"
#include "packager/media/base/bit_reader.h"
#include "packager/media/base/rcheck.h"
namespace shaka {
namespace media {
namespace {
// ASTC Standard A/52:2012 Table 5.8 Audio Coding Mode.
const uint8_t kAc3NumChannelsTable[] = {2, 1, 2, 3, 3, 4, 4, 5};
bool ExtractAc3Data(const std::vector<uint8_t>& ac3_data,
uint8_t* audio_coding_mode,
bool* lfe_channel_on) {
BitReader bit_reader(ac3_data.data(), ac3_data.size());
// fscod: 2 bits
// bsid: 5 bits
// bsmod: 3 bits
// acmod: 3 bits
// lfeon: 1 bit
// bit_rate_code: 5 bits
RCHECK(bit_reader.SkipBits(10));
RCHECK(bit_reader.ReadBits(3, audio_coding_mode));
RCHECK(bit_reader.ReadBits(1, lfe_channel_on));
return true;
}
} // namespace
size_t GetAc3NumChannels(const std::vector<uint8_t>& ac3_data) {
uint8_t audio_coding_mode;
bool lfe_channel_on;
if (!ExtractAc3Data(ac3_data, &audio_coding_mode, &lfe_channel_on)) {
LOG(WARNING) << "Seeing invalid AC3 data: "
<< base::HexEncode(ac3_data.data(), ac3_data.size());
return 0;
}
return kAc3NumChannelsTable[audio_coding_mode] + (lfe_channel_on ? 1 : 0);
}
} // namespace media
} // namespace shaka

View File

@ -0,0 +1,27 @@
// Copyright 2018 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
//
// AC3 audio utility functions.
#ifndef PACKAGER_MEDIA_CODECS_AC3_AUDIO_UTIL_H_
#define PACKAGER_MEDIA_CODECS_AC3_AUDIO_UTIL_H_
#include <stddef.h>
#include <stdint.h>
#include <vector>
namespace shaka {
namespace media {
/// Parse data from AC3Specific box and calculate number of channels.
/// @return The number of channels associated with the input ac3 data on
/// success; otherwise 0 is returned.
size_t GetAc3NumChannels(const std::vector<uint8_t>& ac3_data);
} // namespace media
} // namespace shaka
#endif // PACKAGER_MEDIA_CODECS_AC3_AUDIO_UTIL_H_

View File

@ -0,0 +1,30 @@
// Copyright 2018 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include <gtest/gtest.h>
#include "packager/media/codecs/ac3_audio_util.h"
namespace shaka {
namespace media {
TEST(Ac3AudioUtilTest, ChannelTest1) {
// audio_coding_mode is 7, which is Left, Center, Right, Left surround, Right
// surround. LFE channel on.
const std::vector<uint8_t> ac3_data = {0x10, 0x3d, 0xc0};
EXPECT_EQ(6u, GetAc3NumChannels(ac3_data));
}
TEST(Ac3AudioUtilTest, ChannelTest2) {
// audio_coding_mode is 2, which is Left and Right. LFE channel off.
const std::vector<uint8_t> ac3_data = {0x10, 0x11, 0xc0};
EXPECT_EQ(2u, GetAc3NumChannels(ac3_data));
}
} // namespace media
} // namespace shaka

View File

@ -15,6 +15,8 @@
'sources': [
'aac_audio_specific_config.cc',
'aac_audio_specific_config.h',
'ac3_audio_util.cc',
'ac3_audio_util.h',
'avc_decoder_configuration_record.cc',
'avc_decoder_configuration_record.h',
'decoder_configuration_record.cc',
@ -61,6 +63,7 @@
'type': '<(gtest_target_type)',
'sources': [
'aac_audio_specific_config_unittest.cc',
'ac3_audio_util_unittest.cc',
'avc_decoder_configuration_record_unittest.cc',
'ec3_audio_util_unittest.cc',
'es_descriptor_unittest.cc',

View File

@ -4,9 +4,12 @@
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "packager/media/codecs/ec3_audio_util.h"
#include "packager/base/macros.h"
#include "packager/base/strings/string_number_conversions.h"
#include "packager/media/base/bit_reader.h"
#include "packager/media/base/rcheck.h"
#include "packager/media/codecs/ec3_audio_util.h"
namespace shaka {
namespace media {
@ -49,6 +52,13 @@ enum kEC3AudioChannelMap {
kLFE2 = 0x2,
kLFEScreen = 0x1
};
// Number of channels for the channel bit above. The first entry corresponds to
// kLeft, which has one channel. All the XxxPairs bits have two channels.
const size_t kChannelCountArray[] = {
1, 1, 1, 1, 1, 2, 2, 1, 1, 2, 2, 2, 1, 2, 1, 1,
};
static_assert(arraysize(kChannelCountArray) == 16u,
"Channel count array should have 16 entries.");
// EC3 Audio coding mode map (acmod) to determine EC3 audio channel layout. The
// value stands for the existence of Left, Center, Right, Left surround, and
@ -118,13 +128,17 @@ bool ExtractEc3Data(const std::vector<uint8_t>& ec3_data,
} // namespace
bool CalculateEC3ChannelMap(const std::vector<uint8_t>& ec3_data, uint32_t* channel_map) {
bool CalculateEC3ChannelMap(const std::vector<uint8_t>& ec3_data,
uint32_t* channel_map) {
uint8_t audio_coding_mode;
bool lfe_channel_on;
uint16_t dependent_substreams_layout;
if (!ExtractEc3Data(ec3_data, &audio_coding_mode, &lfe_channel_on,
&dependent_substreams_layout))
&dependent_substreams_layout)) {
LOG(WARNING) << "Seeing invalid EC3 data: "
<< base::HexEncode(ec3_data.data(), ec3_data.size());
return false;
}
// Dependent substreams layout bit map:
// Bit, Location
@ -151,5 +165,21 @@ bool CalculateEC3ChannelMap(const std::vector<uint8_t>& ec3_data, uint32_t* chan
return true;
}
size_t GetEc3NumChannels(const std::vector<uint8_t>& ec3_data) {
uint32_t channel_map;
if (!CalculateEC3ChannelMap(ec3_data, &channel_map))
return 0;
size_t num_channels = 0;
int bit = kLeft;
for (size_t channel_count : kChannelCountArray) {
if (channel_map & bit)
num_channels += channel_count;
bit >>= 1;
}
DCHECK_EQ(bit, 0);
return num_channels;
}
} // namespace media
} // namespace shaka

View File

@ -9,6 +9,7 @@
#ifndef PACKAGER_MEDIA_CODECS_EC3_AUDIO_UTIL_H_
#define PACKAGER_MEDIA_CODECS_EC3_AUDIO_UTIL_H_
#include <stddef.h>
#include <stdint.h>
#include <vector>
@ -21,6 +22,11 @@ namespace media {
bool CalculateEC3ChannelMap(const std::vector<uint8_t>& ec3_data,
uint32_t* channel_map);
/// Parse data from EC3Specific box and calculate number of channels.
/// @return The number of channels associated with the input ec3 data on
/// success; otherwise 0 is returned.
size_t GetEc3NumChannels(const std::vector<uint8_t>& ec3_data);
} // namespace media
} // namespace shaka

View File

@ -6,44 +6,43 @@
#include <gtest/gtest.h>
#include "packager/base/macros.h"
#include "packager/media/codecs/ec3_audio_util.h"
namespace shaka {
namespace media {
TEST(EC3AudioUtilTest, CalculateEC3ChannelMapTest1) {
TEST(EC3AudioUtilTest, ChannelTest1) {
// audio_coding_mode is 7, which is Left, Center, Right, Left surround, Right
// surround. No dependent substreams. LFE channel on.
const uint8_t kEc3Data[] = {0, 0, 0, 0x0f, 0};
const std::vector<uint8_t> ec3_data = {0, 0, 0, 0x0f, 0};
uint32_t channel_map;
EXPECT_TRUE(CalculateEC3ChannelMap(
std::vector<uint8_t>(kEc3Data, kEc3Data + arraysize(kEc3Data)),
&channel_map));
EXPECT_TRUE(CalculateEC3ChannelMap(ec3_data, &channel_map));
EXPECT_EQ(0xF801u, channel_map);
EXPECT_EQ(6u, GetEc3NumChannels(ec3_data));
}
TEST(EC3AudioUtilTest, CalculateEC3ChannelMapTest2) {
TEST(EC3AudioUtilTest, ChannelTest2) {
// audio_coding_mode is 2, which is Left and Right. No dependent substreams.
// LFE channel off.
const uint8_t kEc3Data[] = {0, 0, 0, 0x04, 0};
const std::vector<uint8_t> ec3_data = {0, 0, 0, 0x04, 0};
uint32_t channel_map;
EXPECT_TRUE(CalculateEC3ChannelMap(
std::vector<uint8_t>(kEc3Data, kEc3Data + arraysize(kEc3Data)),
&channel_map));
EXPECT_TRUE(CalculateEC3ChannelMap(ec3_data, &channel_map));
EXPECT_EQ(0xA000u, channel_map);
EXPECT_EQ(2u, GetEc3NumChannels(ec3_data));
}
TEST(EC3AudioUtilTest, CalculateEC3ChannelMapTest3) {
TEST(EC3AudioUtilTest, ChannelTest3) {
// audio_coding_mode is 3, which is Left, Center, and Right. Dependent
// substreams layout is 0b100000011, which is Left center/ Right center pair,
// Left rear surround/ Right rear surround pair, LFE2 on. LFE channel on.
const uint8_t kEc3Data[] = {0, 0, 0, 0x07, 0x07, 0x03};
const std::vector<uint8_t> ec3_data = {0, 0, 0, 0x07, 0x07, 0x03};
uint32_t channel_map;
EXPECT_TRUE(CalculateEC3ChannelMap(
std::vector<uint8_t>(kEc3Data, kEc3Data + arraysize(kEc3Data)),
&channel_map));
EXPECT_TRUE(CalculateEC3ChannelMap(ec3_data, &channel_map));
EXPECT_EQ(0xE603u, channel_map);
EXPECT_EQ(9u, GetEc3NumChannels(ec3_data));
}
} // namespace media

View File

@ -21,7 +21,9 @@
#include "packager/media/base/media_sample.h"
#include "packager/media/base/rcheck.h"
#include "packager/media/base/video_stream_info.h"
#include "packager/media/codecs/ac3_audio_util.h"
#include "packager/media/codecs/avc_decoder_configuration_record.h"
#include "packager/media/codecs/ec3_audio_util.h"
#include "packager/media/codecs/es_descriptor.h"
#include "packager/media/codecs/hevc_decoder_configuration_record.h"
#include "packager/media/codecs/vp_codec_configuration_record.h"
@ -438,12 +440,12 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
break;
case FOURCC_ac_3:
codec_config = entry.dac3.data;
num_channels = entry.channelcount;
num_channels = static_cast<uint8_t>(GetAc3NumChannels(codec_config));
sampling_frequency = entry.samplerate;
break;
case FOURCC_ec_3:
codec_config = entry.dec3.data;
num_channels = entry.channelcount;
num_channels = static_cast<uint8_t>(GetEc3NumChannels(codec_config));
sampling_frequency = entry.samplerate;
break;
case FOURCC_Opus:

View File

@ -348,8 +348,15 @@ void MP4Muxer::GenerateAudioTrak(const AudioStreamInfo* audio_info,
break;
}
if (audio_info->codec() == kCodecAC3 || audio_info->codec() == kCodecEAC3) {
// AC3 and EC3 does not fill in actual channel count and sample size in
// sample description entry. Instead, two constants are used.
audio.channelcount = 2;
audio.samplesize = 16;
} else {
audio.channelcount = audio_info->num_channels();
audio.samplesize = audio_info->sample_bits();
}
audio.samplerate = audio_info->sampling_frequency();
SampleTable& sample_table = trak->media.information.sample_table;
SampleDescription& sample_description = sample_table.description;