JCHub

  • Home
  • Category
    • A/V
    • WebRTC
    • Beauty of Programming
    • Linux
    • Windows
    • Moments of Life
    • Campus Life
  • Reference
    • API Reference
    • Utilities
    • AV Test
    • Doc
  • Message Board
  • About
JCHub
Code as My Sword, Lost in Obsession
  1. Main page
  2. WebRTC
  3. Main content

WebRTC研究:Encoded Transform

2021年3月28日 4794hotness 11likes 6comments

前段时间看到腾讯云的分享:《WebRTC Insertable Stream 初探与 WebRTC “管道化”》。想不到国内也开始关注这个新特性了,作为一个经常关注WebRTC最新提交代码的人,去年初就关注到了WebRTC Insertable Streams相关代码提交,不过对我这样一个Native Code开发者来说,用途不大,毕竟自己也可以在源码层面实现,获取或者修改自己需要的任何数据。但是这一特性对Web开发者来说用途就大了,给了Web开发者更多的自由与操作性。

WebRTC Insertable Streams

以前WebRTC Web开发者无法操作WebRTC采集或者编码后的音视频数据,当然也无法使用自定义的音视频编解码器,很多人使用WebRTC可能只是想用到传输这一块功能。当然也有比较黑科技的做法,使用WebAssembly技术把WebRTC库编译成二进制文件给Javascript调用,不过这一做法难度颇大,很多人编译WebRTC都废了九牛二虎之力,更别说使用WebAssembly技术编译,而且自己改的不一定稳定。所以WebRTC Insertable Streams这项新特性就诞生了。
WebRTC Insertable Streams目前包含两项内容:

  • Encoded Transform:操作编码后的数据以及解码前数据(Post-encoding/Pre-decoding)
  • Mediacapture Transform:操作编码前数据以及解码后数据(Pre-encoding /Post-decoding)

WebRTC Insertable Streams这项新特性增加了些新API,让Web开发者对媒体数据可以做如下的事:

    • 允许用户不打破WebRTC正常处理pipeline,手动处理数据
  • 允许使用WASM技术进行数据处理
  • 允许使用Web Worker技术处理数据,避免阻塞主线程
  • 允许端到端间进行数据加解密,避免安全以及隐私泄露风险(不信任SFU服务器,避免中间人攻击)

根据[1],这里列举几个应用场景:

  • 视频特效
  • 视频背景移除
  • 语音处理
  • 动态控制编解码器参数
  • 各个媒体Tracks的自定义带宽分配
  • 自定义编解码器(与WebCodecs结合)

是不是很有趣?不过截止今天,WebRTC也只是实现了Encoded Transform特性,只能操作编码后的数据。所以本篇文章就从源码层面分析下目前的Encoded Transform实现。

Encoded Transform

前面说到Encoded Transform处理的是Post-encoding/Pre-decoding的媒体数据。

该特性接口定义位于FrameTransformerInterface中。

FrameTransformerInterface

看下接口定义:

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
29
// Objects implement this interface to be notified with the transformed frame.
class TransformedFrameCallback : public rtc::RefCountInterface {
public:
  virtual void OnTransformedFrame(
      std::unique_ptr<TransformableFrameInterface> frame) = 0;
 
protected:
  ~TransformedFrameCallback() override = default;
};
 
// Transforms encoded frames. The transformed frame is sent in a callback using
// the TransformedFrameCallback interface (see above).
class FrameTransformerInterface : public rtc::RefCountInterface {
public:
  // Transforms |frame| using the implementing class' processing logic.
  virtual void Transform(
      std::unique_ptr<TransformableFrameInterface> transformable_frame) = 0;
 
  virtual void RegisterTransformedFrameCallback(
      rtc::scoped_refptr<TransformedFrameCallback>) {}
  virtual void RegisterTransformedFrameSinkCallback(
      rtc::scoped_refptr<TransformedFrameCallback>,
      uint32_t ssrc) {}
  virtual void UnregisterTransformedFrameCallback() {}
  virtual void UnregisterTransformedFrameSinkCallback(uint32_t ssrc) {}
 
protected:
  ~FrameTransformerInterface() override = default;
};

上面代码中,FrameTransformerInterface注册完TransformedFrame相关回调后,在回调TransformedFrameCallback::OnTransformedFrame获取编码后的Payload数据,调用TransformableFrameInterface接口对Payload数据进行操作:

调用GetData获取Payload数据,调用SetData设置Payload数据,我们可以通过GetData获取原始编码后的媒体数据,处理后再调用SetData达到修改媒体数据目的。当然了,也可以获取音视频的其他信息,例如音频,可以获取音频的RTP报头信息。

接下来我们以音频为例看下目前Encoded Transform相关代码实现。

音频发送端

这里先看下发送端代码流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
     采集
      ↓
3A等音频前处理
      ↓
     编码
      ↓
ChannelSend::SendData
      ↓
ChannelSend::SendRtpAudio
      ↓
    RTP打包
      ↓
   发送到网络

WebRTC对ChannelSend::SendData进行了修改,其中frame_transformer_delegate_(ChannelSendFrameTransformerDelegate)继承自TransformedFrameCallback。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int32_t ChannelSend::SendData(AudioFrameType frameType,
                              uint8_t payloadType,
                              uint32_t rtp_timestamp,
                              const uint8_t* payloadData,
                              size_t payloadSize,
                              int64_t absolute_capture_timestamp_ms) {
  RTC_DCHECK_RUN_ON(&encoder_queue_);
  rtc::ArrayView<const uint8_t> payload(payloadData, payloadSize);
  if (frame_transformer_delegate_) {
    // Asynchronously transform the payload before sending it. After the payload
    // is transformed, the delegate will call SendRtpAudio to send it.
    frame_transformer_delegate_->Transform(
        frameType, payloadType, rtp_timestamp, rtp_rtcp_->StartTimestamp(),
        payloadData, payloadSize, absolute_capture_timestamp_ms,
        rtp_rtcp_->SSRC());
    return 0;
  }
  return SendRtpAudio(frameType, payloadType, rtp_timestamp, payload,
                      absolute_capture_timestamp_ms);
}

处理完数据后通过ChannelSend::SendRtpAudio将数据返回到原来的的处理Pipeline。所以若初始化了ChannelSendFrameTransformerDelegate,处理流程变为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
     采集
      ↓
3A等音频前处理
      ↓
     编码
      ↓
ChannelSend::SendData
      ↓
ChannelSendFrameTransformerDelegate::Transform
// 由我们处理Payload数据
FrameTransformerInterface::Transform
TransformableAudioFrameInterface::GetData
TransformableAudioFrameInterface::SetData
TransformedFrameCallback::OnTransformedFrame
ChannelSendFrameTransformerDelegate::SendFrame
      ↓
ChannelSend::SendRtpAudio
      ↓
    RTP打包
      ↓
   发送到网络

这里FrameTransformerInterface由我们外部自己实现。所以我们需要修改Payload数据的话需要实现FrameTransformerInterface,在FrameTransformerInterface::Transform中通过TransformableAudioFrameInterface进行获取或者修改Paylod数据,最后通过注册的回调接口返回修改的Payload数据。。

音频接收端

这里先看下接收端代码流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
  网络接收
    ↓
ChannelReceive::OnRtpPacket
    ↓
ChannelReceive::ReceivePacket
    ↓
ChannelReceive::OnReceivedPayloadData
    ↓
   NetEq
    ↓
   解码
    ↓
混音等后处理

WebRTC对ChannelReceive::ReceivePacket处理流程进行了修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void ChannelReceive::ReceivePacket(const uint8_t* packet,
                                   size_t packet_length,
                                   const RTPHeader& header) {
  const uint8_t* payload = packet + header.headerLength;
 
  rtc::ArrayView<const uint8_t> payload_data(payload, payload_data_length);
  if (frame_transformer_delegate_) {
    // Asynchronously transform the received payload. After the payload is
    // transformed, the delegate will call OnReceivedPayloadData to handle it.
    frame_transformer_delegate_->Transform(payload_data, header, remote_ssrc_);
  } else {
    OnReceivedPayloadData(payload_data, header);
  }
}

同前面发送端代码,其中frame_transformer_delegate_(ChannelReceiveFrameTransformerDelegate)也是继承自TransformedFrameCallback。最后处理完数据后通过ChannelReceive::OnReceivedPayloadData将数据返回到原来的处理Pipeline。具体代码类似发送端,就不再分析了。最后代码处理流程变为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  网络接收
    ↓
ChannelReceive::OnRtpPacket
    ↓
ChannelReceive::ReceivePacket
    ↓
ChannelReceiveFrameTransformerDelegate::Transform
// 由我们处理Payload数据
FrameTransformerInterface::Transform
TransformableAudioFrameInterface::GetData
TransformableAudioFrameInterface::SetData
TransformedFrameCallback::OnTransformedFrame
ChannelReceiveFrameTransformerDelegate::ReceiveFrame
    ↓
ChannelReceive::OnReceivedPayloadData
    ↓
   NetEq
    ↓
   解码
    ↓
3A等后处理

在接收端我们也需要在外部实现FrameTransformerInterface,从而进行数据处理。

总结

本文简单介绍了Insertable Streams这个新特性,并从音频角度分析了其中一项内容的源码实现:Encoded Transform。等后续WebRTC官方更新了Mediacapture Transform实现代码,我也将做下分析。

参考

[1] webrtc-encoded-transform.https://github.com/w3c/webrtc-encoded-transform.

This article is licensed with Creative Commons Attribution-NonCommercial-No Derivatives 4.0 International License
Tag: WebRTC
Last updated:2024年3月15日

Jeff

管理员——代码为剑,如痴如醉

Tip the author Like
< Last article
Next article >

Comments

  • bibo

    谢谢大佬的分享。

    2021年3月29日
    Reply
  • wosshzhb

    如果自己实现了FrameTransformerInterface这个接口,那么注册的时机是在哪里?目前貌似没有webrtc的实际demo?

    2021年6月28日
    Reply
    • Jeff

      @wosshzhb 这个chromium源码有相关使用,这个就一个非常简单的接口,如果你看过代码,就不会问什么时候注册了

      2021年7月5日
      Reply
  • sven

    谢谢大佬的分享
    请问大佬,如果想在neteq后获取packet,native是否有现成途径获取到,还是要魔改呢?

    2021年7月29日
    Reply
  • Richard

    楼主你好,图片不见了,可以更新一下嘛?

    2022年5月17日
    Reply
  • Ivan

    可以在安卓中使用吗? 我尝试了这种方法,但似乎框架没有改变。 您能否给出如何进行简单转换的完整代码示例? 例如只是对帧进行异或。 谢谢你

    2022年7月20日
    Reply
  • razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
    Cancel

    This site uses Akismet to reduce spam. Learn how your comment data is processed.

    文章目录
    • WebRTC Insertable Streams
    • Encoded Transform
      • FrameTransformerInterface
      • 音频发送端
      • 音频接收端
    • 总结
    • 参考
    Related Posts
    • 浅谈基于SFU实现一对一效果
    • WebRTC资讯:H265支持进展
    • Protected: WebRTC硬件编解码器出错无缝切换软编软解
    • WebRTC研究:Audio level
    • Mac平台WebRTC编译
    Categories

    COPYRIGHT © 2026 jianchihu.net. ALL RIGHTS RESERVED.

    Theme Kratos Made By Seaton Jiang