Improve ConvertToADTS function performance (#639)

Remove the extra data copying.
This commit is contained in:
Tomohiro IKEDA 2019-09-11 05:15:17 +09:00 committed by Kongqun Yang
parent 443519aeb3
commit b8ee20df1d
6 changed files with 56 additions and 43 deletions

View File

@ -137,27 +137,33 @@ bool AACAudioSpecificConfig::Parse(const std::vector<uint8_t>& data) {
channel_config_ <= 7;
}
bool AACAudioSpecificConfig::ConvertToADTS(std::vector<uint8_t>* buffer) const {
size_t size = buffer->size() + kADTSHeaderSize;
bool AACAudioSpecificConfig::ConvertToADTS(
const uint8_t* data,
size_t data_size,
std::vector<uint8_t>* audio_frame) const {
DCHECK(audio_object_type_ >= 1 && audio_object_type_ <= 4 &&
frequency_index_ != 0xf && channel_config_ <= 7);
size_t size = kADTSHeaderSize + data_size;
// ADTS header uses 13 bits for packet size.
if (size >= (1 << 13))
return false;
std::vector<uint8_t>& adts = *buffer;
audio_frame->reserve(size);
audio_frame->resize(kADTSHeaderSize);
adts.insert(buffer->begin(), kADTSHeaderSize, 0);
adts[0] = 0xff;
adts[1] = 0xf1;
adts[2] = ((audio_object_type_ - 1) << 6) + (frequency_index_ << 2) +
(channel_config_ >> 2);
adts[3] = ((channel_config_ & 0x3) << 6) + static_cast<uint8_t>(size >> 11);
adts[4] = static_cast<uint8_t>((size & 0x7ff) >> 3);
adts[5] = static_cast<uint8_t>(((size & 7) << 5) + 0x1f);
adts[6] = 0xfc;
audio_frame->at(0) = 0xff;
audio_frame->at(1) = 0xf1;
audio_frame->at(2) = ((audio_object_type_ - 1) << 6) +
(frequency_index_ << 2) + (channel_config_ >> 2);
audio_frame->at(3) =
((channel_config_ & 0x3) << 6) + static_cast<uint8_t>(size >> 11);
audio_frame->at(4) = static_cast<uint8_t>((size & 0x7ff) >> 3);
audio_frame->at(5) = static_cast<uint8_t>(((size & 7) << 5) + 0x1f);
audio_frame->at(6) = 0xfc;
audio_frame->insert(audio_frame->end(), data, data + data_size);
return true;
}

View File

@ -84,11 +84,14 @@ class AACAudioSpecificConfig {
virtual bool Parse(const std::vector<uint8_t>& data);
/// Convert a raw AAC frame into an AAC frame with an ADTS header.
/// @param[in,out] buffer contains the raw AAC frame on input, and the
/// converted frame on output if successful; it is untouched
/// on failure.
/// @param data points to the raw AAC frame to be converted.
/// @param data_size the size of a raw AAC frame.
/// @param[out] audio_frame contains the converted frame if successful; it is
/// untouched on failure.
/// @return true on success, false otherwise.
virtual bool ConvertToADTS(std::vector<uint8_t>* buffer) const;
virtual bool ConvertToADTS(const uint8_t* data,
size_t data_size,
std::vector<uint8_t>* audio_frame) const;
/// @return The audio object type for this AAC config, with possible extension
/// considered.

View File

@ -52,7 +52,8 @@ bool PesPacketGenerator::Initialize(const StreamInfo& stream_info) {
converter_.reset(new NalUnitToByteStreamConverter());
return converter_->Initialize(video_stream_info.codec_config().data(),
video_stream_info.codec_config().size());
} else if (stream_type_ == kStreamAudio) {
}
if (stream_type_ == kStreamAudio) {
const AudioStreamInfo& audio_stream_info =
static_cast<const AudioStreamInfo&>(stream_info);
timescale_scale_ = kTsTimescale / audio_stream_info.time_scale();
@ -60,7 +61,8 @@ bool PesPacketGenerator::Initialize(const StreamInfo& stream_info) {
audio_stream_id_ = kAacAudioStreamId;
adts_converter_.reset(new AACAudioSpecificConfig());
return adts_converter_->Parse(audio_stream_info.codec_config());
} else if (audio_stream_info.codec() == Codec::kCodecAC3 ||
}
if (audio_stream_info.codec() == Codec::kCodecAC3 ||
audio_stream_info.codec() == Codec::kCodecEAC3) {
audio_stream_id_ = kAc3AudioStreamId;
// No converter needed for AC3 and E-AC3.
@ -117,17 +119,15 @@ bool PesPacketGenerator::PushSample(const MediaSample& sample) {
}
DCHECK_EQ(stream_type_, kStreamAudio);
std::vector<uint8_t> audio_frame(sample.data(),
sample.data() + sample.data_size());
std::vector<uint8_t> audio_frame;
// AAC is carried in ADTS.
if (adts_converter_) {
// TODO(rkuroiwa): ConvertToADTS() makes another copy of audio_frame
// internally. Optimize copying in this function, possibly by adding a
// method on AACAudioSpecificConfig that takes {pointer, length} pair and
// returns a vector that has the ADTS header.
if (!adts_converter_->ConvertToADTS(&audio_frame))
if (!adts_converter_->ConvertToADTS(sample.data(), sample.data_size(),
&audio_frame))
return false;
} else {
audio_frame.assign(sample.data(), sample.data() + sample.data_size());
}
// TODO(rkuriowa): Put multiple samples in the PES packet to reduce # of PES

View File

@ -112,7 +112,10 @@ class MockNalUnitToByteStreamConverter : public NalUnitToByteStreamConverter {
class MockAACAudioSpecificConfig : public AACAudioSpecificConfig {
public:
MOCK_METHOD1(Parse, bool(const std::vector<uint8_t>& data));
MOCK_CONST_METHOD1(ConvertToADTS, bool(std::vector<uint8_t>* buffer));
MOCK_CONST_METHOD3(ConvertToADTS,
bool(const uint8_t* data,
size_t data_size,
std::vector<uint8_t>* audio_frame));
};
std::shared_ptr<VideoStreamInfo> CreateVideoStreamInfo(Codec codec) {
@ -307,8 +310,8 @@ TEST_F(PesPacketGeneratorTest, AddAudioSample) {
std::unique_ptr<MockAACAudioSpecificConfig> mock(
new MockAACAudioSpecificConfig());
EXPECT_CALL(*mock, ConvertToADTS(_))
.WillOnce(DoAll(SetArgPointee<0>(expected_data), Return(true)));
EXPECT_CALL(*mock, ConvertToADTS(sample->data(), sample->data_size(), _))
.WillOnce(DoAll(SetArgPointee<2>(expected_data), Return(true)));
UseMockAACAudioSpecificConfig(std::move(mock));
@ -335,7 +338,7 @@ TEST_F(PesPacketGeneratorTest, AddAudioSampleFailedToConvert) {
std::unique_ptr<MockAACAudioSpecificConfig> mock(
new MockAACAudioSpecificConfig());
EXPECT_CALL(*mock, ConvertToADTS(_)).WillOnce(Return(false));
EXPECT_CALL(*mock, ConvertToADTS(_, _, _)).WillOnce(Return(false));
UseMockAACAudioSpecificConfig(std::move(mock));

View File

@ -67,9 +67,9 @@ Status PackedAudioSegmenter::AddSample(const MediaSample& sample) {
}
if (adts_converter_) {
std::vector<uint8_t> audio_frame(sample.data(),
sample.data() + sample.data_size());
if (!adts_converter_->ConvertToADTS(&audio_frame))
std::vector<uint8_t> audio_frame;
if (!adts_converter_->ConvertToADTS(sample.data(), sample.data_size(),
&audio_frame))
return Status(error::MUXER_FAILURE, "Failed to convert to ADTS.");
segment_buffer_.AppendArray(audio_frame.data(), audio_frame.size());
} else {

View File

@ -19,9 +19,7 @@ using ::testing::_;
using ::testing::ByMove;
using ::testing::DoAll;
using ::testing::ElementsAreArray;
using ::testing::Eq;
using ::testing::Invoke;
using ::testing::Pointee;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::Test;
@ -107,7 +105,10 @@ std::shared_ptr<MediaSample> CreateEncryptedSample(
class MockAACAudioSpecificConfig : public AACAudioSpecificConfig {
public:
MOCK_METHOD1(Parse, bool(const std::vector<uint8_t>& data));
MOCK_CONST_METHOD1(ConvertToADTS, bool(std::vector<uint8_t>* buffer));
MOCK_CONST_METHOD3(ConvertToADTS,
bool(const uint8_t* data,
size_t data_size,
std::vector<uint8_t>* audio_frame));
};
class MockId3Tag : public Id3Tag {
@ -170,8 +171,8 @@ TEST_F(PackedAudioSegmenterTest, AacAddSample) {
EXPECT_CALL(*mock_adts_converter_, Parse(ElementsAreArray(kCodecConfig)))
.WillOnce(Return(true));
EXPECT_CALL(*mock_adts_converter_,
ConvertToADTS(Pointee(Eq(StringToVector(kSample1Data)))))
.WillOnce(DoAll(SetArgPointee<0>(StringToVector(kAdtsSample1Data)),
ConvertToADTS(_, sizeof(kSample1Data) - 1, _))
.WillOnce(DoAll(SetArgPointee<2>(StringToVector(kAdtsSample1Data)),
Return(true)));
EXPECT_CALL(segmenter_, CreateAdtsConverter())
@ -199,8 +200,8 @@ TEST_F(PackedAudioSegmenterTest, TruncateLargeTimestamp) {
EXPECT_CALL(*mock_adts_converter_, Parse(ElementsAreArray(kCodecConfig)))
.WillOnce(Return(true));
EXPECT_CALL(*mock_adts_converter_,
ConvertToADTS(Pointee(Eq(StringToVector(kSample1Data)))))
.WillOnce(DoAll(SetArgPointee<0>(StringToVector(kAdtsSample1Data)),
ConvertToADTS(_, sizeof(kSample1Data) - 1, _))
.WillOnce(DoAll(SetArgPointee<2>(StringToVector(kAdtsSample1Data)),
Return(true)));
EXPECT_CALL(segmenter_, CreateAdtsConverter())
@ -301,8 +302,8 @@ TEST_F(PackedAudioSegmenterTest, AacAddEncryptedSample) {
EXPECT_CALL(*mock_adts_converter_, Parse(ElementsAreArray(kCodecConfig)))
.WillOnce(Return(true));
EXPECT_CALL(*mock_adts_converter_,
ConvertToADTS(Pointee(Eq(StringToVector(kSample1Data)))))
.WillOnce(DoAll(SetArgPointee<0>(StringToVector(kAdtsSample1Data)),
ConvertToADTS(_, sizeof(kSample1Data) - 1, _))
.WillOnce(DoAll(SetArgPointee<2>(StringToVector(kAdtsSample1Data)),
Return(true)));
EXPECT_CALL(segmenter_, CreateAdtsConverter())