Expose ILBC codec in webrtc/api/audio_codecs/
BUG=webrtc:7834, webrtc:7840 Review-Url: https://codereview.webrtc.org/2951873002 Cr-Commit-Position: refs/heads/master@{#18803}
This commit is contained in:
@ -78,7 +78,7 @@ NamedDecoderConstructor decoder_constructors[] = {
|
||||
[](const SdpAudioFormat& format, std::unique_ptr<AudioDecoder>* out) {
|
||||
if (format.clockrate_hz == 8000 && format.num_channels == 1) {
|
||||
if (out) {
|
||||
out->reset(new AudioDecoderIlbc);
|
||||
out->reset(new AudioDecoderIlbcImpl);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@ -65,7 +65,7 @@ NamedEncoderFactory encoder_factories[] = {
|
||||
NamedEncoderFactory::ForEncoder<AudioEncoderG722Impl>(),
|
||||
#endif
|
||||
#ifdef WEBRTC_CODEC_ILBC
|
||||
NamedEncoderFactory::ForEncoder<AudioEncoderIlbc>(),
|
||||
NamedEncoderFactory::ForEncoder<AudioEncoderIlbcImpl>(),
|
||||
#endif
|
||||
#if defined(WEBRTC_CODEC_ISACFX)
|
||||
NamedEncoderFactory::ForEncoder<AudioEncoderIsacFix>(),
|
||||
|
||||
@ -19,20 +19,20 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
AudioDecoderIlbc::AudioDecoderIlbc() {
|
||||
AudioDecoderIlbcImpl::AudioDecoderIlbcImpl() {
|
||||
WebRtcIlbcfix_DecoderCreate(&dec_state_);
|
||||
WebRtcIlbcfix_Decoderinit30Ms(dec_state_);
|
||||
}
|
||||
|
||||
AudioDecoderIlbc::~AudioDecoderIlbc() {
|
||||
AudioDecoderIlbcImpl::~AudioDecoderIlbcImpl() {
|
||||
WebRtcIlbcfix_DecoderFree(dec_state_);
|
||||
}
|
||||
|
||||
bool AudioDecoderIlbc::HasDecodePlc() const {
|
||||
bool AudioDecoderIlbcImpl::HasDecodePlc() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
int AudioDecoderIlbc::DecodeInternal(const uint8_t* encoded,
|
||||
int AudioDecoderIlbcImpl::DecodeInternal(const uint8_t* encoded,
|
||||
size_t encoded_len,
|
||||
int sample_rate_hz,
|
||||
int16_t* decoded,
|
||||
@ -45,22 +45,22 @@ int AudioDecoderIlbc::DecodeInternal(const uint8_t* encoded,
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t AudioDecoderIlbc::DecodePlc(size_t num_frames, int16_t* decoded) {
|
||||
size_t AudioDecoderIlbcImpl::DecodePlc(size_t num_frames, int16_t* decoded) {
|
||||
return WebRtcIlbcfix_NetEqPlc(dec_state_, decoded, num_frames);
|
||||
}
|
||||
|
||||
void AudioDecoderIlbc::Reset() {
|
||||
void AudioDecoderIlbcImpl::Reset() {
|
||||
WebRtcIlbcfix_Decoderinit30Ms(dec_state_);
|
||||
}
|
||||
|
||||
std::vector<AudioDecoder::ParseResult> AudioDecoderIlbc::ParsePayload(
|
||||
std::vector<AudioDecoder::ParseResult> AudioDecoderIlbcImpl::ParsePayload(
|
||||
rtc::Buffer&& payload,
|
||||
uint32_t timestamp) {
|
||||
std::vector<ParseResult> results;
|
||||
size_t bytes_per_frame;
|
||||
int timestamps_per_frame;
|
||||
if (payload.size() >= 950) {
|
||||
LOG(LS_WARNING) << "AudioDecoderIlbc::ParsePayload: Payload too large";
|
||||
LOG(LS_WARNING) << "AudioDecoderIlbcImpl::ParsePayload: Payload too large";
|
||||
return results;
|
||||
}
|
||||
if (payload.size() % 38 == 0) {
|
||||
@ -72,7 +72,7 @@ std::vector<AudioDecoder::ParseResult> AudioDecoderIlbc::ParsePayload(
|
||||
bytes_per_frame = 50;
|
||||
timestamps_per_frame = 240;
|
||||
} else {
|
||||
LOG(LS_WARNING) << "AudioDecoderIlbc::ParsePayload: Invalid payload";
|
||||
LOG(LS_WARNING) << "AudioDecoderIlbcImpl::ParsePayload: Invalid payload";
|
||||
return results;
|
||||
}
|
||||
|
||||
@ -97,11 +97,11 @@ std::vector<AudioDecoder::ParseResult> AudioDecoderIlbc::ParsePayload(
|
||||
return results;
|
||||
}
|
||||
|
||||
int AudioDecoderIlbc::SampleRateHz() const {
|
||||
int AudioDecoderIlbcImpl::SampleRateHz() const {
|
||||
return 8000;
|
||||
}
|
||||
|
||||
size_t AudioDecoderIlbc::Channels() const {
|
||||
size_t AudioDecoderIlbcImpl::Channels() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@ -18,10 +18,10 @@ typedef struct iLBC_decinst_t_ IlbcDecoderInstance;
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class AudioDecoderIlbc final : public AudioDecoder {
|
||||
class AudioDecoderIlbcImpl final : public AudioDecoder {
|
||||
public:
|
||||
AudioDecoderIlbc();
|
||||
~AudioDecoderIlbc() override;
|
||||
AudioDecoderIlbcImpl();
|
||||
~AudioDecoderIlbcImpl() override;
|
||||
bool HasDecodePlc() const override;
|
||||
size_t DecodePlc(size_t num_frames, int16_t* decoded) override;
|
||||
void Reset() override;
|
||||
@ -39,7 +39,7 @@ class AudioDecoderIlbc final : public AudioDecoder {
|
||||
|
||||
private:
|
||||
IlbcDecoderInstance* dec_state_;
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(AudioDecoderIlbc);
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(AudioDecoderIlbcImpl);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -24,34 +24,20 @@ namespace {
|
||||
|
||||
const int kSampleRateHz = 8000;
|
||||
|
||||
AudioEncoderIlbc::Config CreateConfig(const CodecInst& codec_inst) {
|
||||
AudioEncoderIlbc::Config config;
|
||||
AudioEncoderIlbcConfig CreateConfig(const CodecInst& codec_inst) {
|
||||
AudioEncoderIlbcConfig config;
|
||||
config.frame_size_ms = codec_inst.pacsize / 8;
|
||||
config.payload_type = codec_inst.pltype;
|
||||
return config;
|
||||
}
|
||||
|
||||
AudioEncoderIlbc::Config CreateConfig(int payload_type,
|
||||
const SdpAudioFormat& format) {
|
||||
AudioEncoderIlbc::Config config;
|
||||
config.payload_type = payload_type;
|
||||
auto ptime_iter = format.parameters.find("ptime");
|
||||
if (ptime_iter != format.parameters.end()) {
|
||||
auto ptime = rtc::StringToNumber<int>(ptime_iter->second);
|
||||
if (ptime && *ptime > 0) {
|
||||
const int whole_packets = *ptime / 10;
|
||||
config.frame_size_ms = std::max(20, std::min(whole_packets * 10, 60));
|
||||
}
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
int GetIlbcBitrate(int ptime) {
|
||||
switch (ptime) {
|
||||
case 20: case 40:
|
||||
case 20:
|
||||
case 40:
|
||||
// 38 bytes per frame of 20 ms => 15200 bits/s.
|
||||
return 15200;
|
||||
case 30: case 60:
|
||||
case 30:
|
||||
case 60:
|
||||
// 50 bytes per frame of 30 ms => (approx) 13333 bits/s.
|
||||
return 13333;
|
||||
default:
|
||||
@ -61,71 +47,85 @@ int GetIlbcBitrate(int ptime) {
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
const size_t AudioEncoderIlbc::kMaxSamplesPerPacket;
|
||||
rtc::Optional<AudioEncoderIlbcConfig> AudioEncoderIlbcImpl::SdpToConfig(
|
||||
const SdpAudioFormat& format) {
|
||||
if (STR_CASE_CMP(format.name.c_str(), "ilbc") != 0 ||
|
||||
format.clockrate_hz != 8000 || format.num_channels != 1) {
|
||||
return rtc::Optional<AudioEncoderIlbcConfig>();
|
||||
}
|
||||
|
||||
bool AudioEncoderIlbc::Config::IsOk() const {
|
||||
return (frame_size_ms == 20 || frame_size_ms == 30 || frame_size_ms == 40 ||
|
||||
frame_size_ms == 60) &&
|
||||
static_cast<size_t>(kSampleRateHz / 100 * (frame_size_ms / 10)) <=
|
||||
kMaxSamplesPerPacket;
|
||||
AudioEncoderIlbcConfig config;
|
||||
auto ptime_iter = format.parameters.find("ptime");
|
||||
if (ptime_iter != format.parameters.end()) {
|
||||
auto ptime = rtc::StringToNumber<int>(ptime_iter->second);
|
||||
if (ptime && *ptime > 0) {
|
||||
const int whole_packets = *ptime / 10;
|
||||
config.frame_size_ms = std::max(20, std::min(whole_packets * 10, 60));
|
||||
}
|
||||
}
|
||||
return config.IsOk() ? rtc::Optional<AudioEncoderIlbcConfig>(config)
|
||||
: rtc::Optional<AudioEncoderIlbcConfig>();
|
||||
}
|
||||
|
||||
AudioEncoderIlbc::AudioEncoderIlbc(const Config& config)
|
||||
: config_(config),
|
||||
AudioEncoderIlbcImpl::AudioEncoderIlbcImpl(const AudioEncoderIlbcConfig& config,
|
||||
int payload_type)
|
||||
: frame_size_ms_(config.frame_size_ms),
|
||||
payload_type_(payload_type),
|
||||
num_10ms_frames_per_packet_(
|
||||
static_cast<size_t>(config.frame_size_ms / 10)),
|
||||
encoder_(nullptr) {
|
||||
RTC_CHECK(config.IsOk());
|
||||
Reset();
|
||||
}
|
||||
|
||||
AudioEncoderIlbc::AudioEncoderIlbc(const CodecInst& codec_inst)
|
||||
: AudioEncoderIlbc(CreateConfig(codec_inst)) {}
|
||||
AudioEncoderIlbcImpl::AudioEncoderIlbcImpl(const CodecInst& codec_inst)
|
||||
: AudioEncoderIlbcImpl(CreateConfig(codec_inst), codec_inst.pltype) {}
|
||||
|
||||
AudioEncoderIlbc::AudioEncoderIlbc(int payload_type,
|
||||
const SdpAudioFormat& format)
|
||||
: AudioEncoderIlbc(CreateConfig(payload_type, format)) {}
|
||||
AudioEncoderIlbcImpl::AudioEncoderIlbcImpl(int payload_type,
|
||||
const SdpAudioFormat& format)
|
||||
: AudioEncoderIlbcImpl(*SdpToConfig(format), payload_type) {}
|
||||
|
||||
rtc::Optional<AudioCodecInfo> AudioEncoderIlbc::QueryAudioEncoder(
|
||||
const SdpAudioFormat& format) {
|
||||
if (STR_CASE_CMP(format.name.c_str(), GetPayloadName()) == 0 &&
|
||||
format.clockrate_hz == 8000 && format.num_channels == 1) {
|
||||
Config config = CreateConfig(0, format);
|
||||
if (config.IsOk()) {
|
||||
return rtc::Optional<AudioCodecInfo>(
|
||||
{kSampleRateHz, 1, GetIlbcBitrate(config.frame_size_ms)});
|
||||
}
|
||||
}
|
||||
|
||||
return rtc::Optional<AudioCodecInfo>();
|
||||
}
|
||||
|
||||
AudioEncoderIlbc::~AudioEncoderIlbc() {
|
||||
AudioEncoderIlbcImpl::~AudioEncoderIlbcImpl() {
|
||||
RTC_CHECK_EQ(0, WebRtcIlbcfix_EncoderFree(encoder_));
|
||||
}
|
||||
|
||||
int AudioEncoderIlbc::SampleRateHz() const {
|
||||
rtc::Optional<AudioCodecInfo> AudioEncoderIlbcImpl::QueryAudioEncoder(
|
||||
const SdpAudioFormat& format) {
|
||||
if (STR_CASE_CMP(format.name.c_str(), GetPayloadName()) == 0) {
|
||||
const auto config_opt = SdpToConfig(format);
|
||||
if (format.clockrate_hz == 8000 && format.num_channels == 1 &&
|
||||
config_opt) {
|
||||
RTC_DCHECK(config_opt->IsOk());
|
||||
return rtc::Optional<AudioCodecInfo>(
|
||||
{rtc::dchecked_cast<int>(kSampleRateHz), 1,
|
||||
GetIlbcBitrate(config_opt->frame_size_ms)});
|
||||
}
|
||||
}
|
||||
return rtc::Optional<AudioCodecInfo>();
|
||||
}
|
||||
|
||||
int AudioEncoderIlbcImpl::SampleRateHz() const {
|
||||
return kSampleRateHz;
|
||||
}
|
||||
|
||||
size_t AudioEncoderIlbc::NumChannels() const {
|
||||
size_t AudioEncoderIlbcImpl::NumChannels() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t AudioEncoderIlbc::Num10MsFramesInNextPacket() const {
|
||||
size_t AudioEncoderIlbcImpl::Num10MsFramesInNextPacket() const {
|
||||
return num_10ms_frames_per_packet_;
|
||||
}
|
||||
|
||||
size_t AudioEncoderIlbc::Max10MsFramesInAPacket() const {
|
||||
size_t AudioEncoderIlbcImpl::Max10MsFramesInAPacket() const {
|
||||
return num_10ms_frames_per_packet_;
|
||||
}
|
||||
|
||||
int AudioEncoderIlbc::GetTargetBitrate() const {
|
||||
int AudioEncoderIlbcImpl::GetTargetBitrate() const {
|
||||
return GetIlbcBitrate(rtc::dchecked_cast<int>(num_10ms_frames_per_packet_) *
|
||||
10);
|
||||
}
|
||||
|
||||
AudioEncoder::EncodedInfo AudioEncoderIlbc::EncodeImpl(
|
||||
AudioEncoder::EncodedInfo AudioEncoderIlbcImpl::EncodeImpl(
|
||||
uint32_t rtp_timestamp,
|
||||
rtc::ArrayView<const int16_t> audio,
|
||||
rtc::Buffer* encoded) {
|
||||
@ -166,24 +166,23 @@ AudioEncoder::EncodedInfo AudioEncoderIlbc::EncodeImpl(
|
||||
EncodedInfo info;
|
||||
info.encoded_bytes = encoded_bytes;
|
||||
info.encoded_timestamp = first_timestamp_in_buffer_;
|
||||
info.payload_type = config_.payload_type;
|
||||
info.payload_type = payload_type_;
|
||||
info.encoder_type = CodecType::kIlbc;
|
||||
return info;
|
||||
}
|
||||
|
||||
void AudioEncoderIlbc::Reset() {
|
||||
void AudioEncoderIlbcImpl::Reset() {
|
||||
if (encoder_)
|
||||
RTC_CHECK_EQ(0, WebRtcIlbcfix_EncoderFree(encoder_));
|
||||
RTC_CHECK(config_.IsOk());
|
||||
RTC_CHECK_EQ(0, WebRtcIlbcfix_EncoderCreate(&encoder_));
|
||||
const int encoder_frame_size_ms = config_.frame_size_ms > 30
|
||||
? config_.frame_size_ms / 2
|
||||
: config_.frame_size_ms;
|
||||
const int encoder_frame_size_ms = frame_size_ms_ > 30
|
||||
? frame_size_ms_ / 2
|
||||
: frame_size_ms_;
|
||||
RTC_CHECK_EQ(0, WebRtcIlbcfix_EncoderInit(encoder_, encoder_frame_size_ms));
|
||||
num_10ms_frames_buffered_ = 0;
|
||||
}
|
||||
|
||||
size_t AudioEncoderIlbc::RequiredOutputSizeBytes() const {
|
||||
size_t AudioEncoderIlbcImpl::RequiredOutputSizeBytes() const {
|
||||
switch (num_10ms_frames_per_packet_) {
|
||||
case 2: return 38;
|
||||
case 3: return 50;
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
|
||||
#include "webrtc/api/audio_codecs/audio_encoder.h"
|
||||
#include "webrtc/api/audio_codecs/audio_format.h"
|
||||
#include "webrtc/api/audio_codecs/ilbc/audio_encoder_ilbc_config.h"
|
||||
#include "webrtc/base/constructormagic.h"
|
||||
#include "webrtc/modules/audio_coding/codecs/ilbc/ilbc.h"
|
||||
|
||||
@ -20,21 +21,15 @@ namespace webrtc {
|
||||
|
||||
struct CodecInst;
|
||||
|
||||
class AudioEncoderIlbc final : public AudioEncoder {
|
||||
class AudioEncoderIlbcImpl final : public AudioEncoder {
|
||||
public:
|
||||
struct Config {
|
||||
bool IsOk() const;
|
||||
static rtc::Optional<AudioEncoderIlbcConfig> SdpToConfig(
|
||||
const SdpAudioFormat& format);
|
||||
|
||||
int payload_type = 102;
|
||||
int frame_size_ms = 30; // Valid values are 20, 30, 40, and 60 ms.
|
||||
// Note that frame size 40 ms produces encodings with two 20 ms frames in
|
||||
// them, and frame size 60 ms consists of two 30 ms frames.
|
||||
};
|
||||
|
||||
explicit AudioEncoderIlbc(const Config& config);
|
||||
explicit AudioEncoderIlbc(const CodecInst& codec_inst);
|
||||
AudioEncoderIlbc(int payload_type, const SdpAudioFormat& format);
|
||||
~AudioEncoderIlbc() override;
|
||||
AudioEncoderIlbcImpl(const AudioEncoderIlbcConfig& config, int payload_type);
|
||||
explicit AudioEncoderIlbcImpl(const CodecInst& codec_inst);
|
||||
AudioEncoderIlbcImpl(int payload_type, const SdpAudioFormat& format);
|
||||
~AudioEncoderIlbcImpl() override;
|
||||
|
||||
static constexpr const char* GetPayloadName() { return "ILBC"; }
|
||||
static rtc::Optional<AudioCodecInfo> QueryAudioEncoder(
|
||||
@ -53,14 +48,15 @@ class AudioEncoderIlbc final : public AudioEncoder {
|
||||
private:
|
||||
size_t RequiredOutputSizeBytes() const;
|
||||
|
||||
static const size_t kMaxSamplesPerPacket = 480;
|
||||
const Config config_;
|
||||
static constexpr size_t kMaxSamplesPerPacket = 480;
|
||||
const int frame_size_ms_;
|
||||
const int payload_type_;
|
||||
const size_t num_10ms_frames_per_packet_;
|
||||
size_t num_10ms_frames_buffered_;
|
||||
uint32_t first_timestamp_in_buffer_;
|
||||
int16_t input_buffer_[kMaxSamplesPerPacket];
|
||||
IlbcEncoderInstance* encoder_;
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(AudioEncoderIlbc);
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(AudioEncoderIlbcImpl);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -17,11 +17,11 @@ namespace webrtc {
|
||||
|
||||
TEST(IlbcTest, BadPacket) {
|
||||
// Get a good packet.
|
||||
AudioEncoderIlbc::Config config;
|
||||
AudioEncoderIlbcConfig config;
|
||||
config.frame_size_ms = 20; // We need 20 ms rather than the default 30 ms;
|
||||
// otherwise, all possible values of cb_index[2]
|
||||
// are valid.
|
||||
AudioEncoderIlbc encoder(config);
|
||||
AudioEncoderIlbcImpl encoder(config, 102);
|
||||
std::vector<int16_t> samples(encoder.SampleRateHz() / 100, 4711);
|
||||
rtc::Buffer packet;
|
||||
int num_10ms_chunks = 0;
|
||||
@ -39,7 +39,7 @@ TEST(IlbcTest, BadPacket) {
|
||||
bad_packet[30] |= 0x80; // Bit 0.
|
||||
|
||||
// Decode the bad packet. We expect the decoder to respond by returning -1.
|
||||
AudioDecoderIlbc decoder;
|
||||
AudioDecoderIlbcImpl decoder;
|
||||
std::vector<int16_t> decoded_samples(num_10ms_chunks * samples.size());
|
||||
AudioDecoder::SpeechType speech_type;
|
||||
EXPECT_EQ(-1, decoder.Decode(bad_packet.data(), bad_packet.size(),
|
||||
@ -69,7 +69,7 @@ class SplitIlbcTest : public ::testing::TestWithParam<std::pair<int, int> > {
|
||||
};
|
||||
|
||||
TEST_P(SplitIlbcTest, NumFrames) {
|
||||
AudioDecoderIlbc decoder;
|
||||
AudioDecoderIlbcImpl decoder;
|
||||
const size_t frame_length_samples = frame_length_ms_ * 8;
|
||||
const auto generate_payload = [] (size_t payload_length_bytes) {
|
||||
rtc::Buffer payload(payload_length_bytes);
|
||||
@ -120,7 +120,7 @@ INSTANTIATE_TEST_CASE_P(
|
||||
|
||||
// Test too large payload size.
|
||||
TEST(IlbcTest, SplitTooLargePayload) {
|
||||
AudioDecoderIlbc decoder;
|
||||
AudioDecoderIlbcImpl decoder;
|
||||
constexpr size_t kPayloadLengthBytes = 950;
|
||||
const auto results =
|
||||
decoder.ParsePayload(rtc::Buffer(kPayloadLengthBytes), 0);
|
||||
@ -129,7 +129,7 @@ TEST(IlbcTest, SplitTooLargePayload) {
|
||||
|
||||
// Payload not an integer number of frames.
|
||||
TEST(IlbcTest, SplitUnevenPayload) {
|
||||
AudioDecoderIlbc decoder;
|
||||
AudioDecoderIlbcImpl decoder;
|
||||
constexpr size_t kPayloadLengthBytes = 39; // Not an even number of frames.
|
||||
const auto results =
|
||||
decoder.ParsePayload(rtc::Buffer(kPayloadLengthBytes), 0);
|
||||
|
||||
Reference in New Issue
Block a user