【AndroidRTC-11】如何理解webrtc的Source、TrackSink

news/2025/3/26 3:12:35/

Android-RTC系列软重启,改变以往细读源代码的方式 改为 带上实际问题分析代码。增加实用性,方便形成肌肉记忆。同时不分种类、不分难易程度,在线征集问题切入点。

问题1:如何理解VideoSource、VideoTrack&VideoSink三者的关系?它们只能是1v1v1的关系吗?

问题2:有前三者,还有一个MediaStream,这又有什么作用?

答1:在WebRTC中,VideoSource、VideoSink和VideoTrack三者构成了视频数据的生产、传输和消费链路,其关系可概括如下:

组件角色功能
VideoSource数据生产者生成原始视频帧(如摄像头、屏幕捕获、文件解码器)。
VideoTrack数据通道与管理者将VideoSource的数据封装为媒体轨道,管理数据的传输和分发。
VideoSink数据消费者接收并处理视频帧(如渲染到屏幕、编码发送、保存到文件等)。

三者构成 ​​“1:N:M”​ 的拓扑结构:

1个VideoTrack 必须关联 ​1个VideoSource强制一对一,VideoTrack与VideoSource必须一一绑定,无法跨源管理。

1个VideoTrack 可以分发到 ​多个VideoSink灵活一对多,VideoTrack可分发到多个VideoSink,支持复杂业务场景。

1个VideoSource 可以被 ​多个VideoTrack 共享,多源共存方案,通过创建多个 VideoTrack 实现多源混合,并通过 MediaStream 统一管理。

代码逻辑示例:

**(1) 创建VideoTrack并绑定Source**

// 创建摄像头VideoSource
rtc::scoped_refptr<VideoCaptureModule> capture_module = ...;
std::unique_ptr<VideoSource> video_source(new VideoSource(capture_module));// 创建VideoTrack并绑定Source
rtc::scoped_refptr<VideoTrackInterface> video_track =peer_connection_factory->CreateVideoTrack("camera_track", video_source.get());

**(2) 添加VideoSink渲染画面**

class VideoRenderer : public rtc::VideoSinkInterface<VideoFrame> {
public:void OnFrame(const VideoFrame& frame) override {// 渲染帧到屏幕RenderFrameToScreen(frame);}
};// 将渲染器注册为VideoSink
VideoRenderer renderer;
video_track->AddOrUpdateSink(&renderer, rtc::VideoSinkWants());// 移除VideoSink
video_track->RemoveSink(&renderer);

以上基础知识来自 腾讯元宝版的DeepSeek

至于第二个问题,不太好说明白。但基于从问题出发,我们还是看看MediaStream有什么内容。

/** Java wrapper for a C++ MediaStreamInterface. */
public class MediaStream {private static final String TAG = "MediaStream";public final List<AudioTrack> audioTracks = new ArrayList<>();public final List<VideoTrack> videoTracks = new ArrayList<>();public final List<VideoTrack> preservedVideoTracks = new ArrayList<>();private long nativeStream;@CalledByNativepublic MediaStream(long nativeStream) {this.nativeStream = nativeStream;}
}

代码很简单,就是三个Track列表,看到一句关键的注释 Java wrapper for a C++ MediaStreamInterface. 我们不妨再去看看MediaStreamInterface。

// C++ version of https://www.w3.org/TR/mediacapture-streams/#mediastream.
//
// A major difference is that remote audio/video tracks (received by a
// PeerConnection/RtpReceiver) are not synchronized simply by adding them to
// the same stream; a session description with the correct "a=msid" attributes
// must be pushed down.
//
// Thus, this interface acts as simply a container for tracks.
class MediaStreamInterface : public webrtc::RefCountInterface,public NotifierInterface {... ...
}

注释翻译:https://www.w3.org/TR/mediacapture-streams/#mediastream.的C++版本实现。一个主要区别是,远程音频/视频轨道(由PeerConnection的RtpReceiver接收)不是简单地通过将它们添加到同一流中来同步的;必须向下推送具有正确“a=msid”属性的会话描述。因此,此接口仅充当轨道的容器。

大致意思应该是,MediaStreamInterface这个类只是一个简单的包装器,把同一(msid)会话的音频视频轨包装在一起。

我们再深挖一下MediaStream的引用地方,也就是pc/peer_connection.cc

看到这就很关键的 RTC_CHECK( ! IsUnifiedPlan()),原来这个MediaStream是旧标准的接口,这下就好理解了。

再提 PlanB and UnifiedPlan

在前一篇文章中,我们简单提过《PlanB and UnifiedPlan》 其核心差异体现在媒体流(Track)的表示方式、m-line(媒体行)数量、SSRC关联逻辑等方面。

PlanB 和 UnifiedPlan 其实就是 WebRTC 在多路媒体源(multi media source)场景下的两种不同的 SDP 协商方式。如果引入 Stream 和 Track 的概念,那么一个 Stream 可能包含 AudioTrack 和 VideoTrack,当有多路 Stream 时,就会有更多的 Track,如果每一个 Track 唯一对应一个自己的 M 描述,那么这就是 UnifiedPlan,如果每一个 M line 描述了多个 Track(track id),那么这就是 Plan B。

Plan B的SDP片段:同一行 m-line下两个SSRC流都用着VP8编码参数。

m=video 9 UDP/TLS/RTP/SAVPF 96 97  
a=ssrc:1234 cname:stream1  
a=ssrc:5678 cname:stream2  
a=sendrecv
a=rtpmap:96 VP8/90000  
a=fmtp:96 max-fs=12288;max-fr=60  

Unified Plan的SDP片段:每个m-line独立配置编码格式,通过mid标识不同Track

m=video 9 UDP/TLS/RTP/SAVPF 96
a=sendonly  
a=mid:video1  
a=rtpmap:96 VP8/90000  m=video 9 UDP/TLS/RTP/SAVPF 97  
a=recvonly
a=mid:video2  
a=rtpmap:97 H264/90000  

Note: 当只有一路音频流和一路视频流时,Plan B 和 UnifiedPlan 的格式是相互兼容的。

Note: 如何快速判断is_unified_plan_?直接看m=video/m=audio的行数吧。

 借用大佬的两张图直观分析。

sdp - planb

sdp - unified plan

http://www.ppmy.cn/news/1583116.html

相关文章

4(四) Jmeter自动化报表html生成

从JMeter 3.0开始已支持自动生成动态报告&#xff0c;我们可以更容易根据生成的报告来完成我们的性能测试报告。 如何生成html测试报告 如果未生成结果文件&#xff08;.jtl&#xff09;,可运行如下命令生成报告: jmeter -n -t test.jmx -l result.jtl -e -o /tmp/ResultReport…

解决chrome无法通过公网访问内网(或者127.0.0.1)

Chrome 更新至 94 版本后&#xff0c;为了保护用户免受针对专用网络&#xff08;也就是内网&#xff09;上的路由器和其他设备的跨站点请求伪造 (CSRF) 攻击&#xff0c;限制网站向专用网络上服务器发送请求的能力&#xff0c;该限制当前(Chrome94)中可以用配置开关临时解除&am…

工业通讯新趋势:Modbus RTU转TCP的发展前景

一、Modbus RTU 和 Modbus TCP 在多个方面存在差异 物理层与传输介质 Modbus RTU&#xff1a;常使用串口通信&#xff0c;如 RS - 232 或 RS - 485。RS - 232 适合短距离通信&#xff0c;通常在十几米以内&#xff1b;RS - 485 则支持更长距离&#xff0c;可达 1200 米左右&a…

SpringBoot2集成Elasticsearch8(使用spring-boot-starter-data-elasticsearch)

写在前面 使用spring-boot-starter-data-elasticsearch集成Elasticsearch8&#xff1f; What? 官方写的不支持啊&#xff1f;让我们来看下官方给出的版本建议。 官方地址&#xff1a; https://docs.spring.io/spring-data/elasticsearch/reference/elasticsearch/versions.…

跨平台IPC通信、嵌入式WebRTC轻量化引擎:解析EasyRTC在ARM/Linux平台的性能突破

随着智能安防、智慧城市等领域的快速发展&#xff0c;网络摄像机&#xff08;IPC&#xff09;作为核心感知设备&#xff0c;其音视频通信能力与跨平台兼容性成为行业关注焦点。EasyRTC嵌入式WebRTC音视频通话SDK通过深度优化WebRTC技术栈&#xff0c;实现了对Linux、ARM、RTOS等…

文件的分片上传vs流失上传

最近复盘项目&#xff0c;有要点整理&#xff1a; (1) 分片上传 定义&#xff1a;将文件分成多个固定大小的块&#xff08;chunk&#xff09;&#xff0c;逐块上传到服务器。 特点&#xff1a; 每个分片是独立的&#xff0c;可以乱序上传。 支持断点续传&#xff0c;服务器可…

curl使用报错error LNK2001: 无法解析的外部符号 __imp__CertCloseStore@8

使用curl静态库libcurl_a.lib 时报错&#xff0c;内容如下&#xff1a; 1>libcurl_a.lib(openssl.obj) : error LNK2001: 无法解析的外部符号 __imp__CertCloseStore8 1>libcrypto.lib(libcrypto-lib-e_capi.obj) : error LNK2001: 无法解析的外部符号 __imp__CertClose…

【QT5 多线程示例】互斥锁

互斥锁 互斥锁介绍&#xff1a;【C并发编程】&#xff08;三&#xff09;互斥锁&#xff1a;std::mutex。原理都一样&#xff0c;这里就不赘述了。 QMutex 是 Qt 框架中提供的一个互斥锁类&#xff0c;主要包括以下成员函数&#xff1a; lock()&#xff1a;试图锁定互斥量。…