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:
solenberg
2017-06-28 02:05:04 -07:00
committed by Commit Bot
parent cd9dd458d0
commit db3c9b0f72
21 changed files with 390 additions and 110 deletions

View File

@ -0,0 +1,45 @@
# 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.
import("../../../webrtc.gni")
if (is_android) {
import("//build/config/android/config.gni")
import("//build/config/android/rules.gni")
}
rtc_source_set("audio_encoder_ilbc_config") {
sources = [
"audio_encoder_ilbc_config.h",
]
}
rtc_static_library("audio_encoder_ilbc") {
sources = [
"audio_encoder_ilbc.cc",
"audio_encoder_ilbc.h",
]
deps = [
":audio_encoder_ilbc_config",
"..:audio_codecs_api",
"../../../base:rtc_base_approved",
"../../../modules/audio_coding:ilbc",
]
}
rtc_static_library("audio_decoder_ilbc") {
sources = [
"audio_decoder_ilbc.cc",
"audio_decoder_ilbc.h",
]
deps = [
"..:audio_codecs_api",
"../../..:webrtc_common",
"../../../base:rtc_base_approved",
"../../../modules/audio_coding:ilbc",
]
}

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.
*/
#include "webrtc/api/audio_codecs/ilbc/audio_decoder_ilbc.h"
#include <memory>
#include <vector>
#include "webrtc/base/ptr_util.h"
#include "webrtc/common_types.h"
#include "webrtc/modules/audio_coding/codecs/ilbc/audio_decoder_ilbc.h"
namespace webrtc {
rtc::Optional<AudioDecoderIlbc::Config> AudioDecoderIlbc::SdpToConfig(
const SdpAudioFormat& format) {
return STR_CASE_CMP(format.name.c_str(), "ilbc") == 0 &&
format.clockrate_hz == 8000 && format.num_channels == 1
? rtc::Optional<Config>(Config())
: rtc::Optional<Config>();
}
void AudioDecoderIlbc::AppendSupportedDecoders(
std::vector<AudioCodecSpec>* specs) {
specs->push_back({{"ILBC", 8000, 1}, {8000, 1, 13300}});
}
std::unique_ptr<AudioDecoder> AudioDecoderIlbc::MakeAudioDecoder(
Config config) {
return rtc::MakeUnique<AudioDecoderIlbcImpl>();
}
} // namespace webrtc

View File

@ -0,0 +1,36 @@
/*
* 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_ILBC_AUDIO_DECODER_ILBC_H_
#define WEBRTC_API_AUDIO_CODECS_ILBC_AUDIO_DECODER_ILBC_H_
#include <memory>
#include <vector>
#include "webrtc/api/audio_codecs/audio_decoder.h"
#include "webrtc/api/audio_codecs/audio_format.h"
#include "webrtc/base/optional.h"
namespace webrtc {
// ILBC decoder API for use as a template parameter to
// CreateAudioDecoderFactory<...>().
//
// NOTE: This struct is still under development and may change without notice.
struct AudioDecoderIlbc {
struct Config {}; // Empty---no config values needed!
static rtc::Optional<Config> SdpToConfig(const SdpAudioFormat& audio_format);
static void AppendSupportedDecoders(std::vector<AudioCodecSpec>* specs);
static std::unique_ptr<AudioDecoder> MakeAudioDecoder(Config config);
};
} // namespace webrtc
#endif // WEBRTC_API_AUDIO_CODECS_ILBC_AUDIO_DECODER_ILBC_H_

View File

@ -0,0 +1,63 @@
/*
* 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/ilbc/audio_encoder_ilbc.h"
#include <memory>
#include <vector>
#include "webrtc/base/ptr_util.h"
#include "webrtc/base/safe_conversions.h"
#include "webrtc/modules/audio_coding/codecs/ilbc/audio_encoder_ilbc.h"
namespace webrtc {
namespace {
int GetIlbcBitrate(int ptime) {
switch (ptime) {
case 20:
case 40:
// 38 bytes per frame of 20 ms => 15200 bits/s.
return 15200;
case 30:
case 60:
// 50 bytes per frame of 30 ms => (approx) 13333 bits/s.
return 13333;
default:
FATAL();
}
}
} // namespace
rtc::Optional<AudioEncoderIlbcConfig> AudioEncoderIlbc::SdpToConfig(
const SdpAudioFormat& format) {
return AudioEncoderIlbcImpl::SdpToConfig(format);
}
void AudioEncoderIlbc::AppendSupportedEncoders(
std::vector<AudioCodecSpec>* specs) {
const SdpAudioFormat fmt = {"ILBC", 8000, 1};
const AudioCodecInfo info = QueryAudioEncoder(*SdpToConfig(fmt));
specs->push_back({fmt, info});
}
AudioCodecInfo AudioEncoderIlbc::QueryAudioEncoder(
const AudioEncoderIlbcConfig& config) {
RTC_DCHECK(config.IsOk());
return {8000, 1, GetIlbcBitrate(config.frame_size_ms)};
}
std::unique_ptr<AudioEncoder> AudioEncoderIlbc::MakeAudioEncoder(
const AudioEncoderIlbcConfig& config,
int payload_type) {
RTC_DCHECK(config.IsOk());
return rtc::MakeUnique<AudioEncoderIlbcImpl>(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_ILBC_AUDIO_ENCODER_ILBC_H_
#define WEBRTC_API_AUDIO_CODECS_ILBC_AUDIO_ENCODER_ILBC_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/ilbc/audio_encoder_ilbc_config.h"
#include "webrtc/base/optional.h"
namespace webrtc {
// ILBC encoder API for use as a template parameter to
// CreateAudioEncoderFactory<...>().
//
// NOTE: This struct is still under development and may change without notice.
struct AudioEncoderIlbc {
static rtc::Optional<AudioEncoderIlbcConfig> SdpToConfig(
const SdpAudioFormat& audio_format);
static void AppendSupportedEncoders(std::vector<AudioCodecSpec>* specs);
static AudioCodecInfo QueryAudioEncoder(const AudioEncoderIlbcConfig& config);
static std::unique_ptr<AudioEncoder> MakeAudioEncoder(
const AudioEncoderIlbcConfig& config,
int payload_type);
};
} // namespace webrtc
#endif // WEBRTC_API_AUDIO_CODECS_ILBC_AUDIO_ENCODER_ILBC_H_

View File

@ -0,0 +1,29 @@
/*
* 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_ILBC_AUDIO_ENCODER_ILBC_CONFIG_H_
#define WEBRTC_API_AUDIO_CODECS_ILBC_AUDIO_ENCODER_ILBC_CONFIG_H_
namespace webrtc {
// NOTE: This struct is still under development and may change without notice.
struct AudioEncoderIlbcConfig {
bool IsOk() const {
return (frame_size_ms == 20 || frame_size_ms == 30 || frame_size_ms == 40 ||
frame_size_ms == 60);
}
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.
};
} // namespace webrtc
#endif // WEBRTC_API_AUDIO_CODECS_ILBC_AUDIO_ENCODER_ILBC_CONFIG_H_

View File

@ -26,6 +26,8 @@ if (rtc_include_tests) {
"../../../test:test_support", "../../../test:test_support",
"../g722:audio_decoder_g722", "../g722:audio_decoder_g722",
"../g722:audio_encoder_g722", "../g722:audio_encoder_g722",
"../ilbc:audio_decoder_ilbc",
"../ilbc:audio_encoder_ilbc",
"//testing/gmock", "//testing/gmock",
] ]
} }

View File

@ -10,6 +10,7 @@
#include "webrtc/api/audio_codecs/audio_decoder_factory_template.h" #include "webrtc/api/audio_codecs/audio_decoder_factory_template.h"
#include "webrtc/api/audio_codecs/g722/audio_decoder_g722.h" #include "webrtc/api/audio_codecs/g722/audio_decoder_g722.h"
#include "webrtc/api/audio_codecs/ilbc/audio_decoder_ilbc.h"
#include "webrtc/base/ptr_util.h" #include "webrtc/base/ptr_util.h"
#include "webrtc/test/gmock.h" #include "webrtc/test/gmock.h"
#include "webrtc/test/gtest.h" #include "webrtc/test/gtest.h"
@ -131,4 +132,17 @@ TEST(AudioDecoderFactoryTemplateTest, G722) {
ASSERT_EQ(nullptr, dec3); ASSERT_EQ(nullptr, dec3);
} }
TEST(AudioDecoderFactoryTemplateTest, Ilbc) {
auto factory = CreateAudioDecoderFactory<AudioDecoderIlbc>();
EXPECT_THAT(factory->GetSupportedDecoders(),
testing::ElementsAre(
AudioCodecSpec{{"ILBC", 8000, 1}, {8000, 1, 13300}}));
EXPECT_FALSE(factory->IsSupportedDecoder({"foo", 8000, 1}));
EXPECT_TRUE(factory->IsSupportedDecoder({"ilbc", 8000, 1}));
EXPECT_EQ(nullptr, factory->MakeAudioDecoder({"bar", 8000, 1}));
auto dec = factory->MakeAudioDecoder({"ilbc", 8000, 1});
ASSERT_NE(nullptr, dec);
EXPECT_EQ(8000, dec->SampleRateHz());
}
} // namespace webrtc } // namespace webrtc

View File

@ -10,6 +10,7 @@
#include "webrtc/api/audio_codecs/audio_encoder_factory_template.h" #include "webrtc/api/audio_codecs/audio_encoder_factory_template.h"
#include "webrtc/api/audio_codecs/g722/audio_encoder_g722.h" #include "webrtc/api/audio_codecs/g722/audio_encoder_g722.h"
#include "webrtc/api/audio_codecs/ilbc/audio_encoder_ilbc.h"
#include "webrtc/base/ptr_util.h" #include "webrtc/base/ptr_util.h"
#include "webrtc/test/gmock.h" #include "webrtc/test/gmock.h"
#include "webrtc/test/gtest.h" #include "webrtc/test/gtest.h"
@ -133,4 +134,19 @@ TEST(AudioEncoderFactoryTemplateTest, G722) {
EXPECT_EQ(16000, enc->SampleRateHz()); EXPECT_EQ(16000, enc->SampleRateHz());
} }
TEST(AudioEncoderFactoryTemplateTest, Ilbc) {
auto factory = CreateAudioEncoderFactory<AudioEncoderIlbc>();
EXPECT_THAT(factory->GetSupportedEncoders(),
testing::ElementsAre(
AudioCodecSpec{{"ILBC", 8000, 1}, {8000, 1, 13333}}));
EXPECT_EQ(rtc::Optional<AudioCodecInfo>(),
factory->QueryAudioEncoder({"foo", 8000, 1}));
EXPECT_EQ(rtc::Optional<AudioCodecInfo>({8000, 1, 13333}),
factory->QueryAudioEncoder({"ilbc", 8000, 1}));
EXPECT_EQ(nullptr, factory->MakeAudioEncoder(17, {"bar", 8000, 1}));
auto enc = factory->MakeAudioEncoder(17, {"ilbc", 8000, 1});
ASSERT_NE(nullptr, enc);
EXPECT_EQ(8000, enc->SampleRateHz());
}
} // namespace webrtc } // namespace webrtc

View File

@ -322,6 +322,7 @@ rtc_static_library("ilbc") {
":legacy_encoded_audio_frame", ":legacy_encoded_audio_frame",
"../..:webrtc_common", "../..:webrtc_common",
"../../api/audio_codecs:audio_codecs_api", "../../api/audio_codecs:audio_codecs_api",
"../../api/audio_codecs/ilbc:audio_encoder_ilbc_config",
"../../base:rtc_base_approved", "../../base:rtc_base_approved",
"../../common_audio", "../../common_audio",
] ]

View File

@ -173,7 +173,7 @@ std::unique_ptr<AudioEncoder> CreateEncoder(
return std::unique_ptr<AudioEncoder>(new AudioEncoderPcm16B(speech_inst)); return std::unique_ptr<AudioEncoder>(new AudioEncoderPcm16B(speech_inst));
#ifdef WEBRTC_CODEC_ILBC #ifdef WEBRTC_CODEC_ILBC
if (STR_CASE_CMP(speech_inst.plname, "ilbc") == 0) if (STR_CASE_CMP(speech_inst.plname, "ilbc") == 0)
return std::unique_ptr<AudioEncoder>(new AudioEncoderIlbc(speech_inst)); return std::unique_ptr<AudioEncoder>(new AudioEncoderIlbcImpl(speech_inst));
#endif #endif
#ifdef WEBRTC_CODEC_G722 #ifdef WEBRTC_CODEC_G722
if (STR_CASE_CMP(speech_inst.plname, "g722") == 0) if (STR_CASE_CMP(speech_inst.plname, "g722") == 0)

View File

@ -78,7 +78,7 @@ NamedDecoderConstructor decoder_constructors[] = {
[](const SdpAudioFormat& format, std::unique_ptr<AudioDecoder>* out) { [](const SdpAudioFormat& format, std::unique_ptr<AudioDecoder>* out) {
if (format.clockrate_hz == 8000 && format.num_channels == 1) { if (format.clockrate_hz == 8000 && format.num_channels == 1) {
if (out) { if (out) {
out->reset(new AudioDecoderIlbc); out->reset(new AudioDecoderIlbcImpl);
} }
return true; return true;
} else { } else {

View File

@ -65,7 +65,7 @@ NamedEncoderFactory encoder_factories[] = {
NamedEncoderFactory::ForEncoder<AudioEncoderG722Impl>(), NamedEncoderFactory::ForEncoder<AudioEncoderG722Impl>(),
#endif #endif
#ifdef WEBRTC_CODEC_ILBC #ifdef WEBRTC_CODEC_ILBC
NamedEncoderFactory::ForEncoder<AudioEncoderIlbc>(), NamedEncoderFactory::ForEncoder<AudioEncoderIlbcImpl>(),
#endif #endif
#if defined(WEBRTC_CODEC_ISACFX) #if defined(WEBRTC_CODEC_ISACFX)
NamedEncoderFactory::ForEncoder<AudioEncoderIsacFix>(), NamedEncoderFactory::ForEncoder<AudioEncoderIsacFix>(),

View File

@ -19,20 +19,20 @@
namespace webrtc { namespace webrtc {
AudioDecoderIlbc::AudioDecoderIlbc() { AudioDecoderIlbcImpl::AudioDecoderIlbcImpl() {
WebRtcIlbcfix_DecoderCreate(&dec_state_); WebRtcIlbcfix_DecoderCreate(&dec_state_);
WebRtcIlbcfix_Decoderinit30Ms(dec_state_); WebRtcIlbcfix_Decoderinit30Ms(dec_state_);
} }
AudioDecoderIlbc::~AudioDecoderIlbc() { AudioDecoderIlbcImpl::~AudioDecoderIlbcImpl() {
WebRtcIlbcfix_DecoderFree(dec_state_); WebRtcIlbcfix_DecoderFree(dec_state_);
} }
bool AudioDecoderIlbc::HasDecodePlc() const { bool AudioDecoderIlbcImpl::HasDecodePlc() const {
return true; return true;
} }
int AudioDecoderIlbc::DecodeInternal(const uint8_t* encoded, int AudioDecoderIlbcImpl::DecodeInternal(const uint8_t* encoded,
size_t encoded_len, size_t encoded_len,
int sample_rate_hz, int sample_rate_hz,
int16_t* decoded, int16_t* decoded,
@ -45,22 +45,22 @@ int AudioDecoderIlbc::DecodeInternal(const uint8_t* encoded,
return ret; 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); return WebRtcIlbcfix_NetEqPlc(dec_state_, decoded, num_frames);
} }
void AudioDecoderIlbc::Reset() { void AudioDecoderIlbcImpl::Reset() {
WebRtcIlbcfix_Decoderinit30Ms(dec_state_); WebRtcIlbcfix_Decoderinit30Ms(dec_state_);
} }
std::vector<AudioDecoder::ParseResult> AudioDecoderIlbc::ParsePayload( std::vector<AudioDecoder::ParseResult> AudioDecoderIlbcImpl::ParsePayload(
rtc::Buffer&& payload, rtc::Buffer&& payload,
uint32_t timestamp) { uint32_t timestamp) {
std::vector<ParseResult> results; std::vector<ParseResult> results;
size_t bytes_per_frame; size_t bytes_per_frame;
int timestamps_per_frame; int timestamps_per_frame;
if (payload.size() >= 950) { if (payload.size() >= 950) {
LOG(LS_WARNING) << "AudioDecoderIlbc::ParsePayload: Payload too large"; LOG(LS_WARNING) << "AudioDecoderIlbcImpl::ParsePayload: Payload too large";
return results; return results;
} }
if (payload.size() % 38 == 0) { if (payload.size() % 38 == 0) {
@ -72,7 +72,7 @@ std::vector<AudioDecoder::ParseResult> AudioDecoderIlbc::ParsePayload(
bytes_per_frame = 50; bytes_per_frame = 50;
timestamps_per_frame = 240; timestamps_per_frame = 240;
} else { } else {
LOG(LS_WARNING) << "AudioDecoderIlbc::ParsePayload: Invalid payload"; LOG(LS_WARNING) << "AudioDecoderIlbcImpl::ParsePayload: Invalid payload";
return results; return results;
} }
@ -97,11 +97,11 @@ std::vector<AudioDecoder::ParseResult> AudioDecoderIlbc::ParsePayload(
return results; return results;
} }
int AudioDecoderIlbc::SampleRateHz() const { int AudioDecoderIlbcImpl::SampleRateHz() const {
return 8000; return 8000;
} }
size_t AudioDecoderIlbc::Channels() const { size_t AudioDecoderIlbcImpl::Channels() const {
return 1; return 1;
} }

View File

@ -18,10 +18,10 @@ typedef struct iLBC_decinst_t_ IlbcDecoderInstance;
namespace webrtc { namespace webrtc {
class AudioDecoderIlbc final : public AudioDecoder { class AudioDecoderIlbcImpl final : public AudioDecoder {
public: public:
AudioDecoderIlbc(); AudioDecoderIlbcImpl();
~AudioDecoderIlbc() override; ~AudioDecoderIlbcImpl() override;
bool HasDecodePlc() const override; bool HasDecodePlc() const override;
size_t DecodePlc(size_t num_frames, int16_t* decoded) override; size_t DecodePlc(size_t num_frames, int16_t* decoded) override;
void Reset() override; void Reset() override;
@ -39,7 +39,7 @@ class AudioDecoderIlbc final : public AudioDecoder {
private: private:
IlbcDecoderInstance* dec_state_; IlbcDecoderInstance* dec_state_;
RTC_DISALLOW_COPY_AND_ASSIGN(AudioDecoderIlbc); RTC_DISALLOW_COPY_AND_ASSIGN(AudioDecoderIlbcImpl);
}; };
} // namespace webrtc } // namespace webrtc

View File

@ -24,34 +24,20 @@ namespace {
const int kSampleRateHz = 8000; const int kSampleRateHz = 8000;
AudioEncoderIlbc::Config CreateConfig(const CodecInst& codec_inst) { AudioEncoderIlbcConfig CreateConfig(const CodecInst& codec_inst) {
AudioEncoderIlbc::Config config; AudioEncoderIlbcConfig config;
config.frame_size_ms = codec_inst.pacsize / 8; 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; return config;
} }
int GetIlbcBitrate(int ptime) { int GetIlbcBitrate(int ptime) {
switch (ptime) { switch (ptime) {
case 20: case 40: case 20:
case 40:
// 38 bytes per frame of 20 ms => 15200 bits/s. // 38 bytes per frame of 20 ms => 15200 bits/s.
return 15200; return 15200;
case 30: case 60: case 30:
case 60:
// 50 bytes per frame of 30 ms => (approx) 13333 bits/s. // 50 bytes per frame of 30 ms => (approx) 13333 bits/s.
return 13333; return 13333;
default: default:
@ -61,71 +47,85 @@ int GetIlbcBitrate(int ptime) {
} // namespace } // namespace
// static rtc::Optional<AudioEncoderIlbcConfig> AudioEncoderIlbcImpl::SdpToConfig(
const size_t AudioEncoderIlbc::kMaxSamplesPerPacket; 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 { AudioEncoderIlbcConfig config;
return (frame_size_ms == 20 || frame_size_ms == 30 || frame_size_ms == 40 || auto ptime_iter = format.parameters.find("ptime");
frame_size_ms == 60) && if (ptime_iter != format.parameters.end()) {
static_cast<size_t>(kSampleRateHz / 100 * (frame_size_ms / 10)) <= auto ptime = rtc::StringToNumber<int>(ptime_iter->second);
kMaxSamplesPerPacket; 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) AudioEncoderIlbcImpl::AudioEncoderIlbcImpl(const AudioEncoderIlbcConfig& config,
: config_(config), int payload_type)
: frame_size_ms_(config.frame_size_ms),
payload_type_(payload_type),
num_10ms_frames_per_packet_( num_10ms_frames_per_packet_(
static_cast<size_t>(config.frame_size_ms / 10)), static_cast<size_t>(config.frame_size_ms / 10)),
encoder_(nullptr) { encoder_(nullptr) {
RTC_CHECK(config.IsOk());
Reset(); Reset();
} }
AudioEncoderIlbc::AudioEncoderIlbc(const CodecInst& codec_inst) AudioEncoderIlbcImpl::AudioEncoderIlbcImpl(const CodecInst& codec_inst)
: AudioEncoderIlbc(CreateConfig(codec_inst)) {} : AudioEncoderIlbcImpl(CreateConfig(codec_inst), codec_inst.pltype) {}
AudioEncoderIlbc::AudioEncoderIlbc(int payload_type, AudioEncoderIlbcImpl::AudioEncoderIlbcImpl(int payload_type,
const SdpAudioFormat& format) const SdpAudioFormat& format)
: AudioEncoderIlbc(CreateConfig(payload_type, format)) {} : AudioEncoderIlbcImpl(*SdpToConfig(format), payload_type) {}
rtc::Optional<AudioCodecInfo> AudioEncoderIlbc::QueryAudioEncoder( AudioEncoderIlbcImpl::~AudioEncoderIlbcImpl() {
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() {
RTC_CHECK_EQ(0, WebRtcIlbcfix_EncoderFree(encoder_)); 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; return kSampleRateHz;
} }
size_t AudioEncoderIlbc::NumChannels() const { size_t AudioEncoderIlbcImpl::NumChannels() const {
return 1; return 1;
} }
size_t AudioEncoderIlbc::Num10MsFramesInNextPacket() const { size_t AudioEncoderIlbcImpl::Num10MsFramesInNextPacket() const {
return num_10ms_frames_per_packet_; return num_10ms_frames_per_packet_;
} }
size_t AudioEncoderIlbc::Max10MsFramesInAPacket() const { size_t AudioEncoderIlbcImpl::Max10MsFramesInAPacket() const {
return num_10ms_frames_per_packet_; 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_) * return GetIlbcBitrate(rtc::dchecked_cast<int>(num_10ms_frames_per_packet_) *
10); 10);
} }
AudioEncoder::EncodedInfo AudioEncoderIlbc::EncodeImpl( AudioEncoder::EncodedInfo AudioEncoderIlbcImpl::EncodeImpl(
uint32_t rtp_timestamp, uint32_t rtp_timestamp,
rtc::ArrayView<const int16_t> audio, rtc::ArrayView<const int16_t> audio,
rtc::Buffer* encoded) { rtc::Buffer* encoded) {
@ -166,24 +166,23 @@ AudioEncoder::EncodedInfo AudioEncoderIlbc::EncodeImpl(
EncodedInfo info; EncodedInfo info;
info.encoded_bytes = encoded_bytes; info.encoded_bytes = encoded_bytes;
info.encoded_timestamp = first_timestamp_in_buffer_; info.encoded_timestamp = first_timestamp_in_buffer_;
info.payload_type = config_.payload_type; info.payload_type = payload_type_;
info.encoder_type = CodecType::kIlbc; info.encoder_type = CodecType::kIlbc;
return info; return info;
} }
void AudioEncoderIlbc::Reset() { void AudioEncoderIlbcImpl::Reset() {
if (encoder_) if (encoder_)
RTC_CHECK_EQ(0, WebRtcIlbcfix_EncoderFree(encoder_)); RTC_CHECK_EQ(0, WebRtcIlbcfix_EncoderFree(encoder_));
RTC_CHECK(config_.IsOk());
RTC_CHECK_EQ(0, WebRtcIlbcfix_EncoderCreate(&encoder_)); RTC_CHECK_EQ(0, WebRtcIlbcfix_EncoderCreate(&encoder_));
const int encoder_frame_size_ms = config_.frame_size_ms > 30 const int encoder_frame_size_ms = frame_size_ms_ > 30
? config_.frame_size_ms / 2 ? frame_size_ms_ / 2
: config_.frame_size_ms; : frame_size_ms_;
RTC_CHECK_EQ(0, WebRtcIlbcfix_EncoderInit(encoder_, encoder_frame_size_ms)); RTC_CHECK_EQ(0, WebRtcIlbcfix_EncoderInit(encoder_, encoder_frame_size_ms));
num_10ms_frames_buffered_ = 0; num_10ms_frames_buffered_ = 0;
} }
size_t AudioEncoderIlbc::RequiredOutputSizeBytes() const { size_t AudioEncoderIlbcImpl::RequiredOutputSizeBytes() const {
switch (num_10ms_frames_per_packet_) { switch (num_10ms_frames_per_packet_) {
case 2: return 38; case 2: return 38;
case 3: return 50; case 3: return 50;

View File

@ -13,6 +13,7 @@
#include "webrtc/api/audio_codecs/audio_encoder.h" #include "webrtc/api/audio_codecs/audio_encoder.h"
#include "webrtc/api/audio_codecs/audio_format.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/base/constructormagic.h"
#include "webrtc/modules/audio_coding/codecs/ilbc/ilbc.h" #include "webrtc/modules/audio_coding/codecs/ilbc/ilbc.h"
@ -20,21 +21,15 @@ namespace webrtc {
struct CodecInst; struct CodecInst;
class AudioEncoderIlbc final : public AudioEncoder { class AudioEncoderIlbcImpl final : public AudioEncoder {
public: public:
struct Config { static rtc::Optional<AudioEncoderIlbcConfig> SdpToConfig(
bool IsOk() const; const SdpAudioFormat& format);
int payload_type = 102; AudioEncoderIlbcImpl(const AudioEncoderIlbcConfig& config, int payload_type);
int frame_size_ms = 30; // Valid values are 20, 30, 40, and 60 ms. explicit AudioEncoderIlbcImpl(const CodecInst& codec_inst);
// Note that frame size 40 ms produces encodings with two 20 ms frames in AudioEncoderIlbcImpl(int payload_type, const SdpAudioFormat& format);
// them, and frame size 60 ms consists of two 30 ms frames. ~AudioEncoderIlbcImpl() override;
};
explicit AudioEncoderIlbc(const Config& config);
explicit AudioEncoderIlbc(const CodecInst& codec_inst);
AudioEncoderIlbc(int payload_type, const SdpAudioFormat& format);
~AudioEncoderIlbc() override;
static constexpr const char* GetPayloadName() { return "ILBC"; } static constexpr const char* GetPayloadName() { return "ILBC"; }
static rtc::Optional<AudioCodecInfo> QueryAudioEncoder( static rtc::Optional<AudioCodecInfo> QueryAudioEncoder(
@ -53,14 +48,15 @@ class AudioEncoderIlbc final : public AudioEncoder {
private: private:
size_t RequiredOutputSizeBytes() const; size_t RequiredOutputSizeBytes() const;
static const size_t kMaxSamplesPerPacket = 480; static constexpr size_t kMaxSamplesPerPacket = 480;
const Config config_; const int frame_size_ms_;
const int payload_type_;
const size_t num_10ms_frames_per_packet_; const size_t num_10ms_frames_per_packet_;
size_t num_10ms_frames_buffered_; size_t num_10ms_frames_buffered_;
uint32_t first_timestamp_in_buffer_; uint32_t first_timestamp_in_buffer_;
int16_t input_buffer_[kMaxSamplesPerPacket]; int16_t input_buffer_[kMaxSamplesPerPacket];
IlbcEncoderInstance* encoder_; IlbcEncoderInstance* encoder_;
RTC_DISALLOW_COPY_AND_ASSIGN(AudioEncoderIlbc); RTC_DISALLOW_COPY_AND_ASSIGN(AudioEncoderIlbcImpl);
}; };
} // namespace webrtc } // namespace webrtc

View File

@ -17,11 +17,11 @@ namespace webrtc {
TEST(IlbcTest, BadPacket) { TEST(IlbcTest, BadPacket) {
// Get a good packet. // Get a good packet.
AudioEncoderIlbc::Config config; AudioEncoderIlbcConfig config;
config.frame_size_ms = 20; // We need 20 ms rather than the default 30 ms; config.frame_size_ms = 20; // We need 20 ms rather than the default 30 ms;
// otherwise, all possible values of cb_index[2] // otherwise, all possible values of cb_index[2]
// are valid. // are valid.
AudioEncoderIlbc encoder(config); AudioEncoderIlbcImpl encoder(config, 102);
std::vector<int16_t> samples(encoder.SampleRateHz() / 100, 4711); std::vector<int16_t> samples(encoder.SampleRateHz() / 100, 4711);
rtc::Buffer packet; rtc::Buffer packet;
int num_10ms_chunks = 0; int num_10ms_chunks = 0;
@ -39,7 +39,7 @@ TEST(IlbcTest, BadPacket) {
bad_packet[30] |= 0x80; // Bit 0. bad_packet[30] |= 0x80; // Bit 0.
// Decode the bad packet. We expect the decoder to respond by returning -1. // 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()); std::vector<int16_t> decoded_samples(num_10ms_chunks * samples.size());
AudioDecoder::SpeechType speech_type; AudioDecoder::SpeechType speech_type;
EXPECT_EQ(-1, decoder.Decode(bad_packet.data(), bad_packet.size(), 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) { TEST_P(SplitIlbcTest, NumFrames) {
AudioDecoderIlbc decoder; AudioDecoderIlbcImpl decoder;
const size_t frame_length_samples = frame_length_ms_ * 8; const size_t frame_length_samples = frame_length_ms_ * 8;
const auto generate_payload = [] (size_t payload_length_bytes) { const auto generate_payload = [] (size_t payload_length_bytes) {
rtc::Buffer payload(payload_length_bytes); rtc::Buffer payload(payload_length_bytes);
@ -120,7 +120,7 @@ INSTANTIATE_TEST_CASE_P(
// Test too large payload size. // Test too large payload size.
TEST(IlbcTest, SplitTooLargePayload) { TEST(IlbcTest, SplitTooLargePayload) {
AudioDecoderIlbc decoder; AudioDecoderIlbcImpl decoder;
constexpr size_t kPayloadLengthBytes = 950; constexpr size_t kPayloadLengthBytes = 950;
const auto results = const auto results =
decoder.ParsePayload(rtc::Buffer(kPayloadLengthBytes), 0); decoder.ParsePayload(rtc::Buffer(kPayloadLengthBytes), 0);
@ -129,7 +129,7 @@ TEST(IlbcTest, SplitTooLargePayload) {
// Payload not an integer number of frames. // Payload not an integer number of frames.
TEST(IlbcTest, SplitUnevenPayload) { TEST(IlbcTest, SplitUnevenPayload) {
AudioDecoderIlbc decoder; AudioDecoderIlbcImpl decoder;
constexpr size_t kPayloadLengthBytes = 39; // Not an even number of frames. constexpr size_t kPayloadLengthBytes = 39; // Not an even number of frames.
const auto results = const auto results =
decoder.ParsePayload(rtc::Buffer(kPayloadLengthBytes), 0); decoder.ParsePayload(rtc::Buffer(kPayloadLengthBytes), 0);

View File

@ -315,12 +315,11 @@ class AudioDecoderIlbcTest : public AudioDecoderTest {
codec_input_rate_hz_ = 8000; codec_input_rate_hz_ = 8000;
frame_size_ = 240; frame_size_ = 240;
data_length_ = 10 * frame_size_; data_length_ = 10 * frame_size_;
decoder_ = new AudioDecoderIlbc; decoder_ = new AudioDecoderIlbcImpl;
assert(decoder_); assert(decoder_);
AudioEncoderIlbc::Config config; AudioEncoderIlbcConfig config;
config.frame_size_ms = 30; config.frame_size_ms = 30;
config.payload_type = payload_type_; audio_encoder_.reset(new AudioEncoderIlbcImpl(config, payload_type_));
audio_encoder_.reset(new AudioEncoderIlbc(config));
} }
// Overload the default test since iLBC's function WebRtcIlbcfix_NetEqPlc does // Overload the default test since iLBC's function WebRtcIlbcfix_NetEqPlc does

View File

@ -51,9 +51,9 @@ class NetEqIlbcQualityTest : public NetEqQualityTest {
void SetUp() override { void SetUp() override {
ASSERT_EQ(1u, channels_) << "iLBC supports only mono audio."; ASSERT_EQ(1u, channels_) << "iLBC supports only mono audio.";
AudioEncoderIlbc::Config config; AudioEncoderIlbcConfig config;
config.frame_size_ms = FLAGS_frame_size_ms; config.frame_size_ms = FLAGS_frame_size_ms;
encoder_.reset(new AudioEncoderIlbc(config)); encoder_.reset(new AudioEncoderIlbcImpl(config, 102));
NetEqQualityTest::SetUp(); NetEqQualityTest::SetUp();
} }
@ -75,7 +75,7 @@ class NetEqIlbcQualityTest : public NetEqQualityTest {
} }
private: private:
std::unique_ptr<AudioEncoderIlbc> encoder_; std::unique_ptr<AudioEncoderIlbcImpl> encoder_;
}; };
TEST_F(NetEqIlbcQualityTest, Test) { TEST_F(NetEqIlbcQualityTest, Test) {

View File

@ -13,7 +13,7 @@
namespace webrtc { namespace webrtc {
void FuzzOneInput(const uint8_t* data, size_t size) { void FuzzOneInput(const uint8_t* data, size_t size) {
AudioDecoderIlbc dec; AudioDecoderIlbcImpl dec;
static const int kSampleRateHz = 8000; static const int kSampleRateHz = 8000;
static const size_t kAllocatedOuputSizeSamples = kSampleRateHz / 10; static const size_t kAllocatedOuputSizeSamples = kSampleRateHz / 10;
int16_t output[kAllocatedOuputSizeSamples]; int16_t output[kAllocatedOuputSizeSamples];