Catalog
  1. 1. 概要
  2. 2. 音频编解码模块分层
  3. 3. 涉及到的类
  4. 4. 构造编码器对象的过程
  5. 5. 自定义编码器
  6. 6. 关于解码器
webrtc中实现自定义编解码器

概要

在 webrtc 框架中如何实现自定义的编解码器。近期对服务器做一下压测,但是模拟客户端在建立大量的推拉流对象时占用 cpu 过多,所以想实现一个简单的编码器,在具体编码时跳过编码算法,直接读取一段预加载好的音频数据,期望将cpu占用将下来,以提升压力测试的规模。所以这里记录一下 webrtc 框架中,编解码模块和如何自定义实现。

音频编解码模块分层

分层示意

涉及到的类

这里先看一下默认框架中,构造编码器用到的一些类,下面会有分析说一下这些类是在何时作用的。

  • CreateBuiltinAudioEncoderFactory 这是一个方法,用来创建内建的 AudioEncoderFactory
  • AudioEncoderFactory 顾名思义,是 audio encoder 工厂,用来创建 encoder
  • AudioEncoderOpus 编码器配置类,它对应了一个编码器类,工厂类使用它来创建爱它所对应的编码器
  • AudioEncoderOpusImpl 编码器类

构造编码器对象的过程

下面是构造编码器对象的过程,在 offer/answer 时,会根据 sdp 协商信息,选择一个编码器作为双方媒体的编码器,然后在编码器工厂中匹配编码器类并构造实例。这里假设双方协商使用 opus 编码器,下面看一下 opus 编码器构造的过程。

首先是要构造一个编码器的工厂类对象, 框架中默认有内建的 api 来构造编码器工厂。这个接口返回一个 AudioEncoderFactory 对象,可以看到内建的编码器工厂提供了下面这些默认的编码器,包括 opus、G711 等等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
rtc::scoped_refptr<AudioEncoderFactory> CreateBuiltinAudioEncoderFactory() {
return CreateAudioEncoderFactory<

#if WEBRTC_USE_BUILTIN_OPUS
AudioEncoderOpus, NotAdvertised<AudioEncoderMultiChannelOpus>,
#endif

AudioEncoderIsac, AudioEncoderG722,

#if WEBRTC_USE_BUILTIN_ILBC
AudioEncoderIlbc,
#endif

AudioEncoderG711, NotAdvertised<AudioEncoderL16>>();
}

接下来根据协商信息来生成编码器对象。这部分功能是由模板实现的,具体代码是 audio_encoder_factory_template.h。多个编解码器配置类(例如 AudioEncoderOpus …)会以模板参数形式传递过来,可以看到下面是一个递归函数,实际上是循环遍历模板参数,依次调用编码器类的 SdpToConfig 方法,来进行匹配,如果成功,那么就通过 MakeAudioEncoder 方法创建编码器, 否则就继续递归。

1
2
3
4
5
6
7
8
9
10
11
12
static std::unique_ptr<AudioEncoder> MakeAudioEncoder(
int payload_type,
const SdpAudioFormat& format,
absl::optional<AudioCodecPairId> codec_pair_id) {
auto opt_config = T::SdpToConfig(format);
if (opt_config) {
return T::MakeAudioEncoder(*opt_config, payload_type, codec_pair_id);
} else {
return Helper<Ts...>::MakeAudioEncoder(payload_type, format,
codec_pair_id);
}
}

编码器配置类里是具体的编码器类的创建方法,这里以 opus 为例。

1
2
3
4
5
6
std::unique_ptr<AudioEncoder> AudioEncoderOpus::MakeAudioEncoder(
const AudioEncoderOpusConfig& config,
int payload_type,
absl::optional<AudioCodecPairId> /*codec_pair_id*/) {
return AudioEncoderOpusImpl::MakeAudioEncoder(config, payload_type);
}

再说一下具体的编码实现吧, 其实会调用 codecs 的 api 来对数据进行编码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
AudioEncoder::EncodedInfo AudioEncoderOpusImpl::EncodeImpl(
uint32_t rtp_timestamp,
rtc::ArrayView<const int16_t> audio,
rtc::Buffer* encoded) {
// ... 省略
info.encoded_bytes = encoded->AppendData(
max_encoded_bytes, [&](rtc::ArrayView<uint8_t> encoded) {
// start
int64_t s = rtc::SystemTimeNanos();
// opus 编码器的编码 api
int status = WebRtcOpus_Encode(
inst_, &input_buffer_[0],
rtc::CheckedDivExact(input_buffer_.size(), config_.num_channels),
rtc::saturated_cast<int16_t>(max_encoded_bytes), encoded.data());

// end
int64_t e = rtc::SystemTimeNanos();
RTC_LOG(LS_INFO) << "60ms frame encode cpu usage: " << e - s << " ns";

RTC_CHECK_GE(status, 0); // Fails only if fed invalid data.

return static_cast<size_t>(status);
});
input_buffer_.clear();

... //省略

}

最后再看一下创建的编码器工厂应该如何使用, 我们需要在创建 PeerConnectionFactory 时,将编码器工厂传递进去。我是自己实现了 CreateCustomAudioEncoderFactory 替代了内建的 CreateBuiltinAudioEncoderFactory

1
2
3
4
5
6
7
8
9
10
m_peer_connection_factory_ = webrtc::CreatePeerConnectionFactory(
m_network_thread_ /*network_thread */, m_worker_thread_ /*worker_thread */,
m_signaling_thread_/* signaling_thread */, m_audioDeviceModule/* default_adm */,
//webrtc::CreateBuiltinAudioEncoderFactory(),
// use custom audio encoder factory
CreateCustomAudioEncoderFactory(),
m_audio_decoder_factory_,
webrtc::CreateBuiltinVideoEncoderFactory()/*nullptr*/,
webrtc::CreateBuiltinVideoDecoderFactory()/*nullptr*/, nullptr /* audio_mixer */,
nullptr /* audio_processing */);

自定义编码器

其实要实现自定义编码器,只要实现上面提到的一个方法和几个类就可以了。 我其实就是 copy 了那几个类,仅修改了一下 EncodeImpl 里调用 WebRtcOpus_Encode 部分。

关于解码器

解码器与编码器的接口是类似的,可以参照编码器的修改方式进行自定义。

Author: 42
Link: http://blog.ikernel.cn/2020/09/10/webrtc%E4%B8%AD%E5%AE%9E%E7%8E%B0%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BC%96%E8%A7%A3%E7%A0%81%E5%99%A8/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.

Comment