Fix a parsing problem with AC3 in TS

Audio samples per frame for AC3 was not specified correctly earlier.

Also the number of channels is not correct if LFE channel is on.

Issue #165

Change-Id: Ibf20aa4c7aec43c07ec7cd394d631c537cb387dd
This commit is contained in:
KongQun Yang 2017-11-06 15:45:44 -08:00
parent 72df5af150
commit 0a69779f7c
14 changed files with 71 additions and 96 deletions

View File

@ -20,7 +20,6 @@
'../base/media_base.gyp:media_base',
'../formats/mp2t/mp2t.gyp:mp2t',
'../formats/mp4/mp4.gyp:mp4',
'../formats/mpeg/mpeg.gyp:mpeg',
'../formats/webm/webm.gyp:webm',
'../formats/webvtt/webvtt.gyp:webvtt',
'../formats/wvm/wvm.gyp:wvm',

View File

@ -43,6 +43,7 @@ const size_t kFrameSizeCodeTable[][3] = {
bool Ac3Header::IsSyncWord(const uint8_t* buf) const {
DCHECK(buf);
// ATSC Standard A/52:2012 5.4.1 syncinfo: Synchronization Information.
return buf[0] == 0x0B && buf[1] == 0x77;
}
@ -52,6 +53,13 @@ size_t Ac3Header::GetMinFrameSize() const {
return kMinAc3FrameSize;
}
size_t Ac3Header::GetSamplesPerFrame() const {
// ATSC Standard A/52:2012
// Annex A: AC-3 Elementary Streams in the MPEG-2 Multiplex.
const size_t kSamplesPerAc3Frame = 1536;
return kSamplesPerAc3Frame;
}
bool Ac3Header::Parse(const uint8_t* audio_frame, size_t audio_frame_size) {
BitReader frame(audio_frame, audio_frame_size);
@ -127,7 +135,7 @@ uint32_t Ac3Header::GetSamplingFrequency() const {
uint8_t Ac3Header::GetNumChannels() const {
DCHECK_LT(acmod_, arraysize(kAc3NumChannelsTable));
return kAc3NumChannelsTable[acmod_];
return kAc3NumChannelsTable[acmod_] + (lfeon_ ? 1 : 0);
}
} // namespace mp2t

View File

@ -28,6 +28,7 @@ class Ac3Header : public AudioHeader {
/// @{
bool IsSyncWord(const uint8_t* buf) const override;
size_t GetMinFrameSize() const override;
size_t GetSamplesPerFrame() const override;
bool Parse(const uint8_t* adts_frame, size_t adts_frame_size) override;
size_t GetHeaderSize() const override;
size_t GetFrameSize() const override;

View File

@ -25,6 +25,11 @@ const char kValidPartialAc3Frame[] =
"000000000000000000001DDDDDDE3C78DB6DB6DB6F9F35AD6B5AD6B5AD6B5AD6B5AD6B5AD6"
"9800000000000F1B6DB6DB6DE3C78F1DDD";
const char kValidPartialAc3FrameSixChannels[] =
"0B77A3B35E40EBF8403EFF9DF0C3F8430FE1FC155755DF3E7CFA33E7CF9F3E7CF9F3E7CF9F"
"3ECDFF3ABE7CF9F3E7CF9F3E7CF9F3E7CF9F3E7CF9F3E7CF9F3E7CF9F3E7CF9F3E7CF9F3E7"
"CF9F3E7CF9F3E7CF9F3E7C31F3E7CF9F3E7C7FCEAF9F3E7CF9F3";
} // anonymous namespace
namespace shaka {
@ -35,10 +40,13 @@ class Ac3HeaderTest : public testing::Test {
public:
void SetUp() override {
ASSERT_TRUE(base::HexStringToBytes(kValidPartialAc3Frame, &ac3_frame_));
ASSERT_TRUE(base::HexStringToBytes(kValidPartialAc3FrameSixChannels,
&ac3_frame_six_channels_));
}
protected:
std::vector<uint8_t> ac3_frame_;
std::vector<uint8_t> ac3_frame_six_channels_;
};
TEST_F(Ac3HeaderTest, ParseSuccess) {
@ -65,6 +73,31 @@ TEST_F(Ac3HeaderTest, ParseSuccess) {
audio_specific_config);
}
TEST_F(Ac3HeaderTest, ParseMultiChannelSuccess) {
const size_t kExpectedFrameSize(1950);
const size_t kExpectedHeaderSize(0);
const uint8_t kExpectedObjectType(0);
const uint32_t kExpectedSamplingFrequency(44100);
const uint8_t kExpectedNumChannels(6);
const uint8_t kExpectedAudioSpecificConfig[] = {0x50, 0x3D, 0xE0};
Ac3Header ac3_header;
ASSERT_TRUE(ac3_header.Parse(ac3_frame_six_channels_.data(),
ac3_frame_six_channels_.size()));
EXPECT_EQ(kExpectedFrameSize, ac3_header.GetFrameSize());
EXPECT_EQ(kExpectedHeaderSize, ac3_header.GetHeaderSize());
EXPECT_EQ(kExpectedObjectType, ac3_header.GetObjectType());
EXPECT_EQ(kExpectedSamplingFrequency, ac3_header.GetSamplingFrequency());
EXPECT_EQ(kExpectedNumChannels, ac3_header.GetNumChannels());
std::vector<uint8_t> audio_specific_config;
ac3_header.GetAudioSpecificConfig(&audio_specific_config);
EXPECT_EQ(arraysize(kExpectedAudioSpecificConfig),
audio_specific_config.size());
EXPECT_EQ(std::vector<uint8_t>(std::begin(kExpectedAudioSpecificConfig),
std::end(kExpectedAudioSpecificConfig)),
audio_specific_config);
}
TEST_F(Ac3HeaderTest, ParseVariousDataSize) {
Ac3Header ac3_header;

View File

@ -9,7 +9,22 @@
#include "packager/media/base/bit_reader.h"
#include "packager/media/base/bit_writer.h"
#include "packager/media/formats/mp2t/mp2t_common.h"
#include "packager/media/formats/mpeg/adts_constants.h"
namespace {
const size_t kAdtsHeaderMinSize = 7;
// The following conversion table is extracted from ISO 14496 Part 3 -
// Table 1.16 - Sampling Frequency Index.
const int kAdtsFrequencyTable[] = {96000, 88200, 64000, 48000, 44100,
32000, 24000, 22050, 16000, 12000,
11025, 8000, 7350};
const size_t kAdtsFrequencyTableSize = arraysize(kAdtsFrequencyTable);
// The following conversion table is extracted from ISO 14496 Part 3 -
// Table 1.17 - Channel Configuration.
const int kAdtsNumChannelsTable[] = {0, 1, 2, 3, 4, 5, 6, 8};
const size_t kAdtsNumChannelsTableSize = arraysize(kAdtsNumChannelsTable);
} // namespace
namespace shaka {
namespace media {
@ -23,6 +38,11 @@ size_t AdtsHeader::GetMinFrameSize() const {
return kAdtsHeaderMinSize + 1;
}
size_t AdtsHeader::GetSamplesPerFrame() const {
const size_t kSamplesPerAacFrame = 1024;
return kSamplesPerAacFrame;
}
bool AdtsHeader::Parse(const uint8_t* adts_frame, size_t adts_frame_size) {
CHECK(adts_frame);

View File

@ -28,6 +28,7 @@ class AdtsHeader : public AudioHeader {
/// @{
bool IsSyncWord(const uint8_t* buf) const override;
size_t GetMinFrameSize() const override;
size_t GetSamplesPerFrame() const override;
bool Parse(const uint8_t* adts_frame, size_t adts_frame_size) override;
size_t GetHeaderSize() const override;
size_t GetFrameSize() const override;

View File

@ -29,6 +29,9 @@ class AudioHeader {
/// @return The minium frame size.
virtual size_t GetMinFrameSize() const = 0;
/// @return Number of audio samples per frame.
virtual size_t GetSamplesPerFrame() const = 0;
/// Parse a partial audio frame, extracting the fields within. Only audio
/// frame header / metadata is parsed. The audio_frame_size must contain the
/// full header / metadata.

View File

@ -19,7 +19,6 @@
#include "packager/media/formats/mp2t/adts_header.h"
#include "packager/media/formats/mp2t/mp2t_common.h"
#include "packager/media/formats/mp2t/ts_stream_type.h"
#include "packager/media/formats/mpeg/adts_constants.h"
namespace shaka {
namespace media {
@ -144,8 +143,8 @@ bool EsParserAudio::Parse(const uint8_t* buf,
}
int64_t current_pts = audio_timestamp_helper_->GetTimestamp();
int64_t frame_duration =
audio_timestamp_helper_->GetFrameDuration(kSamplesPerAACFrame);
int64_t frame_duration = audio_timestamp_helper_->GetFrameDuration(
audio_header_->GetSamplesPerFrame());
// Emit an audio frame.
bool is_key_frame = true;
@ -160,7 +159,7 @@ bool EsParserAudio::Parse(const uint8_t* buf,
emit_sample_cb_.Run(pid(), sample);
// Update the PTS of the next frame.
audio_timestamp_helper_->AddFrames(kSamplesPerAACFrame);
audio_timestamp_helper_->AddFrames(audio_header_->GetSamplesPerFrame());
// Skip the current frame.
es_position += static_cast<int>(audio_header_->GetFrameSize());

View File

@ -83,7 +83,6 @@
'../../codecs/codecs.gyp:codecs',
'../../event/media_event.gyp:mock_muxer_listener',
'../../test/media_test.gyp:media_test_support',
'../mpeg/mpeg.gyp:mpeg',
'mp2t',
]
},

View File

@ -1,26 +0,0 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "packager/media/formats/mpeg/adts_constants.h"
#include "packager/base/macros.h"
namespace shaka {
namespace media {
// The following conversion table is extracted from ISO 14496 Part 3 -
// Table 1.16 - Sampling Frequency Index.
const int kAdtsFrequencyTable[] = {96000, 88200, 64000, 48000, 44100,
32000, 24000, 22050, 16000, 12000,
11025, 8000, 7350};
const size_t kAdtsFrequencyTableSize = arraysize(kAdtsFrequencyTable);
// The following conversion table is extracted from ISO 14496 Part 3 -
// Table 1.17 - Channel Configuration.
const int kAdtsNumChannelsTable[] = {
0, 1, 2, 3, 4, 5, 6, 8 };
const size_t kAdtsNumChannelsTableSize = arraysize(kAdtsNumChannelsTable);
} // namespace media
} // namespace shaka

View File

@ -1,27 +0,0 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MEDIA_FORMATS_MPEG_ADTS_CONSTANTS_H_
#define MEDIA_FORMATS_MPEG_ADTS_CONSTANTS_H_
#include <stddef.h>
namespace shaka {
namespace media {
enum {
kAdtsHeaderMinSize = 7,
kSamplesPerAACFrame = 1024,
};
extern const int kAdtsFrequencyTable[];
extern const size_t kAdtsFrequencyTableSize;
extern const int kAdtsNumChannelsTable[];
extern const size_t kAdtsNumChannelsTableSize;
} // namespace media
} // namespace shaka
#endif // MEDIA_FORMATS_MPEG_ADTS_CONSTANTS_H_

View File

@ -1,33 +0,0 @@
# Copyright 2014 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
{
'includes': [
'../../../common.gypi',
],
'targets': [
{
'target_name': 'mpeg',
'type': '<(component)',
'sources': [
'adts_constants.cc',
'adts_constants.h',
],
},
{
'target_name': 'mpeg_unittest',
'type': '<(gtest_target_type)',
'sources': [
],
'dependencies': [
'../../../testing/gtest.gyp:gtest',
'../../../testing/gmock.gyp:gmock',
'../../test/media_test.gyp:media_test_support',
'mpeg',
]
},
],
}

View File

@ -20,7 +20,6 @@
'../../base/media_base.gyp:media_base',
'../../codecs/codecs.gyp:codecs',
'../../formats/mp2t/mp2t.gyp:mp2t',
'../mpeg/mpeg.gyp:mpeg',
],
},
{

View File

@ -30,7 +30,6 @@
'media/event/media_event.gyp:media_event',
'media/formats/mp2t/mp2t.gyp:mp2t',
'media/formats/mp4/mp4.gyp:mp4',
'media/formats/mpeg/mpeg.gyp:mpeg',
'media/formats/webm/webm.gyp:webm',
'media/formats/webvtt/webvtt.gyp:webvtt',
'media/formats/wvm/wvm.gyp:wvm',