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

View File

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

View File

@ -52,7 +52,8 @@ bool PesPacketGenerator::Initialize(const StreamInfo& stream_info) {
converter_.reset(new NalUnitToByteStreamConverter()); converter_.reset(new NalUnitToByteStreamConverter());
return converter_->Initialize(video_stream_info.codec_config().data(), return converter_->Initialize(video_stream_info.codec_config().data(),
video_stream_info.codec_config().size()); video_stream_info.codec_config().size());
} else if (stream_type_ == kStreamAudio) { }
if (stream_type_ == kStreamAudio) {
const AudioStreamInfo& audio_stream_info = const AudioStreamInfo& audio_stream_info =
static_cast<const AudioStreamInfo&>(stream_info); static_cast<const AudioStreamInfo&>(stream_info);
timescale_scale_ = kTsTimescale / audio_stream_info.time_scale(); timescale_scale_ = kTsTimescale / audio_stream_info.time_scale();
@ -60,8 +61,9 @@ bool PesPacketGenerator::Initialize(const StreamInfo& stream_info) {
audio_stream_id_ = kAacAudioStreamId; audio_stream_id_ = kAacAudioStreamId;
adts_converter_.reset(new AACAudioSpecificConfig()); adts_converter_.reset(new AACAudioSpecificConfig());
return adts_converter_->Parse(audio_stream_info.codec_config()); return adts_converter_->Parse(audio_stream_info.codec_config());
} else if (audio_stream_info.codec() == Codec::kCodecAC3 || }
audio_stream_info.codec() == Codec::kCodecEAC3) { if (audio_stream_info.codec() == Codec::kCodecAC3 ||
audio_stream_info.codec() == Codec::kCodecEAC3) {
audio_stream_id_ = kAc3AudioStreamId; audio_stream_id_ = kAc3AudioStreamId;
// No converter needed for AC3 and E-AC3. // No converter needed for AC3 and E-AC3.
return true; return true;
@ -117,17 +119,15 @@ bool PesPacketGenerator::PushSample(const MediaSample& sample) {
} }
DCHECK_EQ(stream_type_, kStreamAudio); DCHECK_EQ(stream_type_, kStreamAudio);
std::vector<uint8_t> audio_frame(sample.data(), std::vector<uint8_t> audio_frame;
sample.data() + sample.data_size());
// AAC is carried in ADTS. // AAC is carried in ADTS.
if (adts_converter_) { if (adts_converter_) {
// TODO(rkuroiwa): ConvertToADTS() makes another copy of audio_frame if (!adts_converter_->ConvertToADTS(sample.data(), sample.data_size(),
// internally. Optimize copying in this function, possibly by adding a &audio_frame))
// method on AACAudioSpecificConfig that takes {pointer, length} pair and
// returns a vector that has the ADTS header.
if (!adts_converter_->ConvertToADTS(&audio_frame))
return false; 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 // 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 { class MockAACAudioSpecificConfig : public AACAudioSpecificConfig {
public: public:
MOCK_METHOD1(Parse, bool(const std::vector<uint8_t>& data)); 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) { std::shared_ptr<VideoStreamInfo> CreateVideoStreamInfo(Codec codec) {
@ -307,8 +310,8 @@ TEST_F(PesPacketGeneratorTest, AddAudioSample) {
std::unique_ptr<MockAACAudioSpecificConfig> mock( std::unique_ptr<MockAACAudioSpecificConfig> mock(
new MockAACAudioSpecificConfig()); new MockAACAudioSpecificConfig());
EXPECT_CALL(*mock, ConvertToADTS(_)) EXPECT_CALL(*mock, ConvertToADTS(sample->data(), sample->data_size(), _))
.WillOnce(DoAll(SetArgPointee<0>(expected_data), Return(true))); .WillOnce(DoAll(SetArgPointee<2>(expected_data), Return(true)));
UseMockAACAudioSpecificConfig(std::move(mock)); UseMockAACAudioSpecificConfig(std::move(mock));
@ -335,7 +338,7 @@ TEST_F(PesPacketGeneratorTest, AddAudioSampleFailedToConvert) {
std::unique_ptr<MockAACAudioSpecificConfig> mock( std::unique_ptr<MockAACAudioSpecificConfig> mock(
new MockAACAudioSpecificConfig()); new MockAACAudioSpecificConfig());
EXPECT_CALL(*mock, ConvertToADTS(_)).WillOnce(Return(false)); EXPECT_CALL(*mock, ConvertToADTS(_, _, _)).WillOnce(Return(false));
UseMockAACAudioSpecificConfig(std::move(mock)); UseMockAACAudioSpecificConfig(std::move(mock));

View File

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

View File

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