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:
parent
72df5af150
commit
0a69779f7c
|
@ -20,7 +20,6 @@
|
||||||
'../base/media_base.gyp:media_base',
|
'../base/media_base.gyp:media_base',
|
||||||
'../formats/mp2t/mp2t.gyp:mp2t',
|
'../formats/mp2t/mp2t.gyp:mp2t',
|
||||||
'../formats/mp4/mp4.gyp:mp4',
|
'../formats/mp4/mp4.gyp:mp4',
|
||||||
'../formats/mpeg/mpeg.gyp:mpeg',
|
|
||||||
'../formats/webm/webm.gyp:webm',
|
'../formats/webm/webm.gyp:webm',
|
||||||
'../formats/webvtt/webvtt.gyp:webvtt',
|
'../formats/webvtt/webvtt.gyp:webvtt',
|
||||||
'../formats/wvm/wvm.gyp:wvm',
|
'../formats/wvm/wvm.gyp:wvm',
|
||||||
|
|
|
@ -43,6 +43,7 @@ const size_t kFrameSizeCodeTable[][3] = {
|
||||||
|
|
||||||
bool Ac3Header::IsSyncWord(const uint8_t* buf) const {
|
bool Ac3Header::IsSyncWord(const uint8_t* buf) const {
|
||||||
DCHECK(buf);
|
DCHECK(buf);
|
||||||
|
// ATSC Standard A/52:2012 5.4.1 syncinfo: Synchronization Information.
|
||||||
return buf[0] == 0x0B && buf[1] == 0x77;
|
return buf[0] == 0x0B && buf[1] == 0x77;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,6 +53,13 @@ size_t Ac3Header::GetMinFrameSize() const {
|
||||||
return kMinAc3FrameSize;
|
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) {
|
bool Ac3Header::Parse(const uint8_t* audio_frame, size_t audio_frame_size) {
|
||||||
BitReader frame(audio_frame, audio_frame_size);
|
BitReader frame(audio_frame, audio_frame_size);
|
||||||
|
|
||||||
|
@ -127,7 +135,7 @@ uint32_t Ac3Header::GetSamplingFrequency() const {
|
||||||
|
|
||||||
uint8_t Ac3Header::GetNumChannels() const {
|
uint8_t Ac3Header::GetNumChannels() const {
|
||||||
DCHECK_LT(acmod_, arraysize(kAc3NumChannelsTable));
|
DCHECK_LT(acmod_, arraysize(kAc3NumChannelsTable));
|
||||||
return kAc3NumChannelsTable[acmod_];
|
return kAc3NumChannelsTable[acmod_] + (lfeon_ ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mp2t
|
} // namespace mp2t
|
||||||
|
|
|
@ -28,6 +28,7 @@ class Ac3Header : public AudioHeader {
|
||||||
/// @{
|
/// @{
|
||||||
bool IsSyncWord(const uint8_t* buf) const override;
|
bool IsSyncWord(const uint8_t* buf) const override;
|
||||||
size_t GetMinFrameSize() 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;
|
bool Parse(const uint8_t* adts_frame, size_t adts_frame_size) override;
|
||||||
size_t GetHeaderSize() const override;
|
size_t GetHeaderSize() const override;
|
||||||
size_t GetFrameSize() const override;
|
size_t GetFrameSize() const override;
|
||||||
|
|
|
@ -25,6 +25,11 @@ const char kValidPartialAc3Frame[] =
|
||||||
"000000000000000000001DDDDDDE3C78DB6DB6DB6F9F35AD6B5AD6B5AD6B5AD6B5AD6B5AD6"
|
"000000000000000000001DDDDDDE3C78DB6DB6DB6F9F35AD6B5AD6B5AD6B5AD6B5AD6B5AD6"
|
||||||
"9800000000000F1B6DB6DB6DE3C78F1DDD";
|
"9800000000000F1B6DB6DB6DE3C78F1DDD";
|
||||||
|
|
||||||
|
const char kValidPartialAc3FrameSixChannels[] =
|
||||||
|
"0B77A3B35E40EBF8403EFF9DF0C3F8430FE1FC155755DF3E7CFA33E7CF9F3E7CF9F3E7CF9F"
|
||||||
|
"3ECDFF3ABE7CF9F3E7CF9F3E7CF9F3E7CF9F3E7CF9F3E7CF9F3E7CF9F3E7CF9F3E7CF9F3E7"
|
||||||
|
"CF9F3E7CF9F3E7CF9F3E7C31F3E7CF9F3E7C7FCEAF9F3E7CF9F3";
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
|
@ -35,10 +40,13 @@ class Ac3HeaderTest : public testing::Test {
|
||||||
public:
|
public:
|
||||||
void SetUp() override {
|
void SetUp() override {
|
||||||
ASSERT_TRUE(base::HexStringToBytes(kValidPartialAc3Frame, &ac3_frame_));
|
ASSERT_TRUE(base::HexStringToBytes(kValidPartialAc3Frame, &ac3_frame_));
|
||||||
|
ASSERT_TRUE(base::HexStringToBytes(kValidPartialAc3FrameSixChannels,
|
||||||
|
&ac3_frame_six_channels_));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector<uint8_t> ac3_frame_;
|
std::vector<uint8_t> ac3_frame_;
|
||||||
|
std::vector<uint8_t> ac3_frame_six_channels_;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(Ac3HeaderTest, ParseSuccess) {
|
TEST_F(Ac3HeaderTest, ParseSuccess) {
|
||||||
|
@ -65,6 +73,31 @@ TEST_F(Ac3HeaderTest, ParseSuccess) {
|
||||||
audio_specific_config);
|
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) {
|
TEST_F(Ac3HeaderTest, ParseVariousDataSize) {
|
||||||
Ac3Header ac3_header;
|
Ac3Header ac3_header;
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,22 @@
|
||||||
#include "packager/media/base/bit_reader.h"
|
#include "packager/media/base/bit_reader.h"
|
||||||
#include "packager/media/base/bit_writer.h"
|
#include "packager/media/base/bit_writer.h"
|
||||||
#include "packager/media/formats/mp2t/mp2t_common.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 shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
@ -23,6 +38,11 @@ size_t AdtsHeader::GetMinFrameSize() const {
|
||||||
return kAdtsHeaderMinSize + 1;
|
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) {
|
bool AdtsHeader::Parse(const uint8_t* adts_frame, size_t adts_frame_size) {
|
||||||
CHECK(adts_frame);
|
CHECK(adts_frame);
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ class AdtsHeader : public AudioHeader {
|
||||||
/// @{
|
/// @{
|
||||||
bool IsSyncWord(const uint8_t* buf) const override;
|
bool IsSyncWord(const uint8_t* buf) const override;
|
||||||
size_t GetMinFrameSize() 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;
|
bool Parse(const uint8_t* adts_frame, size_t adts_frame_size) override;
|
||||||
size_t GetHeaderSize() const override;
|
size_t GetHeaderSize() const override;
|
||||||
size_t GetFrameSize() const override;
|
size_t GetFrameSize() const override;
|
||||||
|
|
|
@ -29,6 +29,9 @@ class AudioHeader {
|
||||||
/// @return The minium frame size.
|
/// @return The minium frame size.
|
||||||
virtual size_t GetMinFrameSize() const = 0;
|
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
|
/// Parse a partial audio frame, extracting the fields within. Only audio
|
||||||
/// frame header / metadata is parsed. The audio_frame_size must contain the
|
/// frame header / metadata is parsed. The audio_frame_size must contain the
|
||||||
/// full header / metadata.
|
/// full header / metadata.
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
#include "packager/media/formats/mp2t/adts_header.h"
|
#include "packager/media/formats/mp2t/adts_header.h"
|
||||||
#include "packager/media/formats/mp2t/mp2t_common.h"
|
#include "packager/media/formats/mp2t/mp2t_common.h"
|
||||||
#include "packager/media/formats/mp2t/ts_stream_type.h"
|
#include "packager/media/formats/mp2t/ts_stream_type.h"
|
||||||
#include "packager/media/formats/mpeg/adts_constants.h"
|
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
@ -144,8 +143,8 @@ bool EsParserAudio::Parse(const uint8_t* buf,
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t current_pts = audio_timestamp_helper_->GetTimestamp();
|
int64_t current_pts = audio_timestamp_helper_->GetTimestamp();
|
||||||
int64_t frame_duration =
|
int64_t frame_duration = audio_timestamp_helper_->GetFrameDuration(
|
||||||
audio_timestamp_helper_->GetFrameDuration(kSamplesPerAACFrame);
|
audio_header_->GetSamplesPerFrame());
|
||||||
|
|
||||||
// Emit an audio frame.
|
// Emit an audio frame.
|
||||||
bool is_key_frame = true;
|
bool is_key_frame = true;
|
||||||
|
@ -160,7 +159,7 @@ bool EsParserAudio::Parse(const uint8_t* buf,
|
||||||
emit_sample_cb_.Run(pid(), sample);
|
emit_sample_cb_.Run(pid(), sample);
|
||||||
|
|
||||||
// Update the PTS of the next frame.
|
// Update the PTS of the next frame.
|
||||||
audio_timestamp_helper_->AddFrames(kSamplesPerAACFrame);
|
audio_timestamp_helper_->AddFrames(audio_header_->GetSamplesPerFrame());
|
||||||
|
|
||||||
// Skip the current frame.
|
// Skip the current frame.
|
||||||
es_position += static_cast<int>(audio_header_->GetFrameSize());
|
es_position += static_cast<int>(audio_header_->GetFrameSize());
|
||||||
|
|
|
@ -83,7 +83,6 @@
|
||||||
'../../codecs/codecs.gyp:codecs',
|
'../../codecs/codecs.gyp:codecs',
|
||||||
'../../event/media_event.gyp:mock_muxer_listener',
|
'../../event/media_event.gyp:mock_muxer_listener',
|
||||||
'../../test/media_test.gyp:media_test_support',
|
'../../test/media_test.gyp:media_test_support',
|
||||||
'../mpeg/mpeg.gyp:mpeg',
|
|
||||||
'mp2t',
|
'mp2t',
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -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
|
|
|
@ -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_
|
|
|
@ -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',
|
|
||||||
]
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
|
@ -20,7 +20,6 @@
|
||||||
'../../base/media_base.gyp:media_base',
|
'../../base/media_base.gyp:media_base',
|
||||||
'../../codecs/codecs.gyp:codecs',
|
'../../codecs/codecs.gyp:codecs',
|
||||||
'../../formats/mp2t/mp2t.gyp:mp2t',
|
'../../formats/mp2t/mp2t.gyp:mp2t',
|
||||||
'../mpeg/mpeg.gyp:mpeg',
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
'media/event/media_event.gyp:media_event',
|
'media/event/media_event.gyp:media_event',
|
||||||
'media/formats/mp2t/mp2t.gyp:mp2t',
|
'media/formats/mp2t/mp2t.gyp:mp2t',
|
||||||
'media/formats/mp4/mp4.gyp:mp4',
|
'media/formats/mp4/mp4.gyp:mp4',
|
||||||
'media/formats/mpeg/mpeg.gyp:mpeg',
|
|
||||||
'media/formats/webm/webm.gyp:webm',
|
'media/formats/webm/webm.gyp:webm',
|
||||||
'media/formats/webvtt/webvtt.gyp:webvtt',
|
'media/formats/webvtt/webvtt.gyp:webvtt',
|
||||||
'media/formats/wvm/wvm.gyp:wvm',
|
'media/formats/wvm/wvm.gyp:wvm',
|
||||||
|
|
Loading…
Reference in New Issue