Fix incorrect CHANNELS reporting for AC3/EC3
Fixes #313 Change-Id: I0b8d8081cda8aaa4db2ca4916bfc6721bfa3bd22
This commit is contained in:
parent
858fcd8698
commit
2ba23e1075
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue