G722 implementation of the AudioEncoderFactoryTemplate API

Now the templated AudioEncoderFactory can create G722 encoders!

BUG=webrtc:7833

Review-Url: https://codereview.webrtc.org/2934833002
Cr-Commit-Position: refs/heads/master@{#18644}
This commit is contained in:
kwiberg
2017-06-17 17:41:59 -07:00
committed by Commit Bot
parent b1ed7f09c0
commit b8727aebc1
12 changed files with 209 additions and 61 deletions

View File

@ -12,6 +12,25 @@ if (is_android) {
import("//build/config/android/rules.gni")
}
rtc_source_set("audio_encoder_g722_config") {
sources = [
"audio_encoder_g722_config.h",
]
}
rtc_static_library("audio_encoder_g722") {
sources = [
"audio_encoder_g722.cc",
"audio_encoder_g722.h",
]
deps = [
":audio_encoder_g722_config",
"..:audio_codecs_api",
"../../../base:rtc_base_approved",
"../../../modules/audio_coding:g722",
]
}
rtc_static_library("audio_decoder_g722") {
sources = [
"audio_decoder_g722.cc",

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/api/audio_codecs/g722/audio_encoder_g722.h"
#include <memory>
#include <vector>
#include "webrtc/base/ptr_util.h"
#include "webrtc/base/safe_conversions.h"
#include "webrtc/modules/audio_coding/codecs/g722/audio_encoder_g722.h"
namespace webrtc {
rtc::Optional<AudioEncoderG722Config> AudioEncoderG722::SdpToConfig(
const SdpAudioFormat& format) {
return AudioEncoderG722Impl::SdpToConfig(format);
}
void AudioEncoderG722::AppendSupportedEncoders(
std::vector<AudioCodecSpec>* specs) {
const SdpAudioFormat fmt = {"g722", 8000, 1};
const AudioCodecInfo info = QueryAudioEncoder(*SdpToConfig(fmt));
specs->push_back({fmt, info});
}
AudioCodecInfo AudioEncoderG722::QueryAudioEncoder(
const AudioEncoderG722Config& config) {
RTC_DCHECK(config.IsOk());
return {16000, rtc::dchecked_cast<size_t>(config.num_channels),
64000 * config.num_channels};
}
std::unique_ptr<AudioEncoder> AudioEncoderG722::MakeAudioEncoder(
const AudioEncoderG722Config& config,
int payload_type) {
RTC_DCHECK(config.IsOk());
return rtc::MakeUnique<AudioEncoderG722Impl>(config, payload_type);
}
} // namespace webrtc

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef WEBRTC_API_AUDIO_CODECS_G722_AUDIO_ENCODER_G722_H_
#define WEBRTC_API_AUDIO_CODECS_G722_AUDIO_ENCODER_G722_H_
#include <memory>
#include <vector>
#include "webrtc/api/audio_codecs/audio_encoder.h"
#include "webrtc/api/audio_codecs/audio_format.h"
#include "webrtc/api/audio_codecs/g722/audio_encoder_g722_config.h"
#include "webrtc/base/optional.h"
namespace webrtc {
// G722 encoder API for use as a template parameter to
// CreateAudioEncoderFactory<...>().
//
// NOTE: This struct is still under development and may change without notice.
struct AudioEncoderG722 {
static rtc::Optional<AudioEncoderG722Config> SdpToConfig(
const SdpAudioFormat& audio_format);
static void AppendSupportedEncoders(std::vector<AudioCodecSpec>* specs);
static AudioCodecInfo QueryAudioEncoder(const AudioEncoderG722Config& config);
static std::unique_ptr<AudioEncoder> MakeAudioEncoder(
const AudioEncoderG722Config& config,
int payload_type);
};
} // namespace webrtc
#endif // WEBRTC_API_AUDIO_CODECS_G722_AUDIO_ENCODER_G722_H_

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef WEBRTC_API_AUDIO_CODECS_G722_AUDIO_ENCODER_G722_CONFIG_H_
#define WEBRTC_API_AUDIO_CODECS_G722_AUDIO_ENCODER_G722_CONFIG_H_
namespace webrtc {
// NOTE: This struct is still under development and may change without notice.
struct AudioEncoderG722Config {
bool IsOk() const {
return frame_size_ms > 0 && frame_size_ms % 10 == 0 && num_channels >= 1;
}
int frame_size_ms = 20;
int num_channels = 1;
};
} // namespace webrtc
#endif // WEBRTC_API_AUDIO_CODECS_G722_AUDIO_ENCODER_G722_CONFIG_H_

View File

@ -25,6 +25,7 @@ if (rtc_include_tests) {
"../../../test:audio_codec_mocks",
"../../../test:test_support",
"../g722:audio_decoder_g722",
"../g722:audio_encoder_g722",
"//testing/gmock",
]
}

View File

@ -9,6 +9,7 @@
*/
#include "webrtc/api/audio_codecs/audio_encoder_factory_template.h"
#include "webrtc/api/audio_codecs/g722/audio_encoder_g722.h"
#include "webrtc/base/ptr_util.h"
#include "webrtc/test/gmock.h"
#include "webrtc/test/gtest.h"
@ -117,4 +118,19 @@ TEST(AudioEncoderFactoryTemplateTest, TwoEncoderTypes) {
EXPECT_EQ(16000, enc2->SampleRateHz());
}
TEST(AudioEncoderFactoryTemplateTest, G722) {
auto factory = CreateAudioEncoderFactory<AudioEncoderG722>();
EXPECT_THAT(factory->GetSupportedEncoders(),
testing::ElementsAre(
AudioCodecSpec{{"g722", 8000, 1}, {16000, 1, 64000}}));
EXPECT_EQ(rtc::Optional<AudioCodecInfo>(),
factory->QueryAudioEncoder({"foo", 8000, 1}));
EXPECT_EQ(rtc::Optional<AudioCodecInfo>({16000, 1, 64000}),
factory->QueryAudioEncoder({"g722", 8000, 1}));
EXPECT_EQ(nullptr, factory->MakeAudioEncoder(17, {"bar", 16000, 1}));
auto enc = factory->MakeAudioEncoder(17, {"g722", 8000, 1});
ASSERT_NE(nullptr, enc);
EXPECT_EQ(16000, enc->SampleRateHz());
}
} // namespace webrtc

View File

@ -279,6 +279,7 @@ rtc_static_library("g722") {
":legacy_encoded_audio_frame",
"../..:webrtc_common",
"../../api/audio_codecs:audio_codecs_api",
"../../api/audio_codecs/g722:audio_encoder_g722_config",
"../../base:rtc_base_approved",
]
public_deps = [

View File

@ -177,7 +177,7 @@ std::unique_ptr<AudioEncoder> CreateEncoder(
#endif
#ifdef WEBRTC_CODEC_G722
if (STR_CASE_CMP(speech_inst.plname, "g722") == 0)
return std::unique_ptr<AudioEncoder>(new AudioEncoderG722(speech_inst));
return std::unique_ptr<AudioEncoder>(new AudioEncoderG722Impl(speech_inst));
#endif
LOG_F(LS_ERROR) << "Could not create encoder of type " << speech_inst.plname;
return std::unique_ptr<AudioEncoder>();

View File

@ -62,7 +62,7 @@ struct NamedEncoderFactory {
NamedEncoderFactory encoder_factories[] = {
#ifdef WEBRTC_CODEC_G722
NamedEncoderFactory::ForEncoder<AudioEncoderG722>(),
NamedEncoderFactory::ForEncoder<AudioEncoderG722Impl>(),
#endif
#ifdef WEBRTC_CODEC_ILBC
NamedEncoderFactory::ForEncoder<AudioEncoderIlbc>(),

View File

@ -25,19 +25,24 @@ namespace {
const size_t kSampleRateHz = 16000;
AudioEncoderG722::Config CreateConfig(const CodecInst& codec_inst) {
AudioEncoderG722::Config config;
config.num_channels = codec_inst.channels;
AudioEncoderG722Config CreateConfig(const CodecInst& codec_inst) {
AudioEncoderG722Config config;
config.num_channels = rtc::dchecked_cast<int>(codec_inst.channels);
config.frame_size_ms = codec_inst.pacsize / 16;
config.payload_type = codec_inst.pltype;
return config;
}
AudioEncoderG722::Config CreateConfig(int payload_type,
const SdpAudioFormat& format) {
AudioEncoderG722::Config config;
config.payload_type = payload_type;
config.num_channels = format.num_channels;
} // namespace
rtc::Optional<AudioEncoderG722Config> AudioEncoderG722Impl::SdpToConfig(
const SdpAudioFormat& format) {
if (STR_CASE_CMP(format.name.c_str(), "g722") != 0 ||
format.clockrate_hz != 8000) {
return rtc::Optional<AudioEncoderG722Config>();
}
AudioEncoderG722Config config;
config.num_channels = rtc::dchecked_cast<int>(format.num_channels);
auto ptime_iter = format.parameters.find("ptime");
if (ptime_iter != format.parameters.end()) {
auto ptime = rtc::StringToNumber<int>(ptime_iter->second);
@ -46,19 +51,14 @@ AudioEncoderG722::Config CreateConfig(int payload_type,
config.frame_size_ms = std::max(10, std::min(whole_packets * 10, 60));
}
}
return config;
return config.IsOk() ? rtc::Optional<AudioEncoderG722Config>(config)
: rtc::Optional<AudioEncoderG722Config>();
}
} // namespace
bool AudioEncoderG722::Config::IsOk() const {
return (frame_size_ms > 0) && (frame_size_ms % 10 == 0) &&
(num_channels >= 1);
}
AudioEncoderG722::AudioEncoderG722(const Config& config)
AudioEncoderG722Impl::AudioEncoderG722Impl(const AudioEncoderG722Config& config,
int payload_type)
: num_channels_(config.num_channels),
payload_type_(config.payload_type),
payload_type_(payload_type),
num_10ms_frames_per_packet_(
static_cast<size_t>(config.frame_size_ms / 10)),
num_10ms_frames_buffered_(0),
@ -75,61 +75,63 @@ AudioEncoderG722::AudioEncoderG722(const Config& config)
Reset();
}
AudioEncoderG722::AudioEncoderG722(const CodecInst& codec_inst)
: AudioEncoderG722(CreateConfig(codec_inst)) {}
AudioEncoderG722Impl::AudioEncoderG722Impl(const CodecInst& codec_inst)
: AudioEncoderG722Impl(CreateConfig(codec_inst), codec_inst.pltype) {}
AudioEncoderG722::AudioEncoderG722(int payload_type,
const SdpAudioFormat& format)
: AudioEncoderG722(CreateConfig(payload_type, format)) {}
AudioEncoderG722Impl::AudioEncoderG722Impl(int payload_type,
const SdpAudioFormat& format)
: AudioEncoderG722Impl(*SdpToConfig(format), payload_type) {}
AudioEncoderG722::~AudioEncoderG722() = default;
AudioEncoderG722Impl::~AudioEncoderG722Impl() = default;
rtc::Optional<AudioCodecInfo> AudioEncoderG722::QueryAudioEncoder(
rtc::Optional<AudioCodecInfo> AudioEncoderG722Impl::QueryAudioEncoder(
const SdpAudioFormat& format) {
if (STR_CASE_CMP(format.name.c_str(), GetPayloadName()) == 0) {
Config config = CreateConfig(0, format);
if (format.clockrate_hz == 8000 && config.IsOk()) {
const auto config_opt = SdpToConfig(format);
if (format.clockrate_hz == 8000 && config_opt) {
RTC_DCHECK(config_opt->IsOk());
return rtc::Optional<AudioCodecInfo>(
{rtc::dchecked_cast<int>(kSampleRateHz), config.num_channels, 64000});
{rtc::dchecked_cast<int>(kSampleRateHz),
rtc::dchecked_cast<size_t>(config_opt->num_channels), 64000});
}
}
return rtc::Optional<AudioCodecInfo>();
}
int AudioEncoderG722::SampleRateHz() const {
int AudioEncoderG722Impl::SampleRateHz() const {
return kSampleRateHz;
}
size_t AudioEncoderG722::NumChannels() const {
size_t AudioEncoderG722Impl::NumChannels() const {
return num_channels_;
}
int AudioEncoderG722::RtpTimestampRateHz() const {
int AudioEncoderG722Impl::RtpTimestampRateHz() const {
// The RTP timestamp rate for G.722 is 8000 Hz, even though it is a 16 kHz
// codec.
return kSampleRateHz / 2;
}
size_t AudioEncoderG722::Num10MsFramesInNextPacket() const {
size_t AudioEncoderG722Impl::Num10MsFramesInNextPacket() const {
return num_10ms_frames_per_packet_;
}
size_t AudioEncoderG722::Max10MsFramesInAPacket() const {
size_t AudioEncoderG722Impl::Max10MsFramesInAPacket() const {
return num_10ms_frames_per_packet_;
}
int AudioEncoderG722::GetTargetBitrate() const {
int AudioEncoderG722Impl::GetTargetBitrate() const {
// 4 bits/sample, 16000 samples/s/channel.
return static_cast<int>(64000 * NumChannels());
}
void AudioEncoderG722::Reset() {
void AudioEncoderG722Impl::Reset() {
num_10ms_frames_buffered_ = 0;
for (size_t i = 0; i < num_channels_; ++i)
RTC_CHECK_EQ(0, WebRtcG722_EncoderInit(encoders_[i].encoder));
}
AudioEncoder::EncodedInfo AudioEncoderG722::EncodeImpl(
AudioEncoder::EncodedInfo AudioEncoderG722Impl::EncodeImpl(
uint32_t rtp_timestamp,
rtc::ArrayView<const int16_t> audio,
rtc::Buffer* encoded) {
@ -185,15 +187,15 @@ AudioEncoder::EncodedInfo AudioEncoderG722::EncodeImpl(
return info;
}
AudioEncoderG722::EncoderState::EncoderState() {
AudioEncoderG722Impl::EncoderState::EncoderState() {
RTC_CHECK_EQ(0, WebRtcG722_CreateEncoder(&encoder));
}
AudioEncoderG722::EncoderState::~EncoderState() {
AudioEncoderG722Impl::EncoderState::~EncoderState() {
RTC_CHECK_EQ(0, WebRtcG722_FreeEncoder(encoder));
}
size_t AudioEncoderG722::SamplesPerChannel() const {
size_t AudioEncoderG722Impl::SamplesPerChannel() const {
return kSampleRateHz / 100 * num_10ms_frames_per_packet_;
}

View File

@ -15,6 +15,7 @@
#include "webrtc/api/audio_codecs/audio_encoder.h"
#include "webrtc/api/audio_codecs/audio_format.h"
#include "webrtc/api/audio_codecs/g722/audio_encoder_g722_config.h"
#include "webrtc/base/buffer.h"
#include "webrtc/base/constructormagic.h"
#include "webrtc/modules/audio_coding/codecs/g722/g722_interface.h"
@ -23,20 +24,15 @@ namespace webrtc {
struct CodecInst;
class AudioEncoderG722 final : public AudioEncoder {
class AudioEncoderG722Impl final : public AudioEncoder {
public:
struct Config {
bool IsOk() const;
static rtc::Optional<AudioEncoderG722Config> SdpToConfig(
const SdpAudioFormat& format);
int payload_type = 9;
int frame_size_ms = 20;
size_t num_channels = 1;
};
explicit AudioEncoderG722(const Config& config);
explicit AudioEncoderG722(const CodecInst& codec_inst);
AudioEncoderG722(int payload_type, const SdpAudioFormat& format);
~AudioEncoderG722() override;
AudioEncoderG722Impl(const AudioEncoderG722Config& config, int payload_type);
explicit AudioEncoderG722Impl(const CodecInst& codec_inst);
AudioEncoderG722Impl(int payload_type, const SdpAudioFormat& format);
~AudioEncoderG722Impl() override;
static constexpr const char* GetPayloadName() { return "G722"; }
static rtc::Optional<AudioCodecInfo> QueryAudioEncoder(
@ -74,7 +70,7 @@ class AudioEncoderG722 final : public AudioEncoder {
uint32_t first_timestamp_in_buffer_;
const std::unique_ptr<EncoderState[]> encoders_;
rtc::Buffer interleave_buffer_;
RTC_DISALLOW_COPY_AND_ASSIGN(AudioEncoderG722);
RTC_DISALLOW_COPY_AND_ASSIGN(AudioEncoderG722Impl);
};
} // namespace webrtc

View File

@ -404,11 +404,10 @@ class AudioDecoderG722Test : public AudioDecoderTest {
data_length_ = 10 * frame_size_;
decoder_ = new AudioDecoderG722Impl;
assert(decoder_);
AudioEncoderG722::Config config;
AudioEncoderG722Config config;
config.frame_size_ms = 10;
config.payload_type = payload_type_;
config.num_channels = 1;
audio_encoder_.reset(new AudioEncoderG722(config));
audio_encoder_.reset(new AudioEncoderG722Impl(config, payload_type_));
}
};
@ -421,11 +420,10 @@ class AudioDecoderG722StereoTest : public AudioDecoderTest {
data_length_ = 10 * frame_size_;
decoder_ = new AudioDecoderG722Stereo;
assert(decoder_);
AudioEncoderG722::Config config;
AudioEncoderG722Config config;
config.frame_size_ms = 10;
config.payload_type = payload_type_;
config.num_channels = 2;
audio_encoder_.reset(new AudioEncoderG722(config));
audio_encoder_.reset(new AudioEncoderG722Impl(config, payload_type_));
}
};