根据W3C的WebRTC 1.0: Real-time Communication Between Browsers规范,WebRTC的源码中定义了两套主要的C++接口,分别是MediaStream
与PeerConnection
相关的API。本篇文章主要介绍下MediaStream
API中一些概念,方便理解内部代码如何处理。
MediaStream 相关API定义在源码api\media_stream_interface.h中。里面主要涉及这4个概念:source
,sink
,mediatrack
,mediastream
。
source与sink
在浏览器中,存在source到sink的媒体管道,source是生产媒体资源的,sink负责消费。传统的source一般是些静态资源,例如文件,以及web资源,不随时间改变。对于我们的WebRTC来说,source是动态资源,例如麦克风采集的音频,相机采集的视频,随时间而改变。sink的工作就是将这些source呈现给用户。sink可以是<img>
,<video>
,<audio>
这些标签,用于本地渲染,也可以是RTCPeerConnection
,将source通过网络传递到远端。在网络流传输中,RTCPeerConnection
可同时扮演source与sink的角色,作为sink,可以将获取的source降低码率,缩放,调整帧率等,然后传递到远端,作为source,将获取的远端码流传递到本地渲染。
下面我们以video source举例,说下WebRTC源码中,source与sink定义。
在api\video\video_source_interface.h中,video source定义如下:
1 2 3 4 5 6 7 8 9 10 11 |
template <typename VideoFrameT> class VideoSourceInterface { public: virtual ~VideoSourceInterface() = default; virtual void AddOrUpdateSink(VideoSinkInterface<VideoFrameT>* sink, const VideoSinkWants& wants) = 0; // RemoveSink must guarantee that at the time the method returns, // there is no current and no future calls to VideoSinkInterface::OnFrame. virtual void RemoveSink(VideoSinkInterface<VideoFrameT>* sink) = 0; }; |
source可以添加、移除、更新sink,从而将VideoFrame
送给对应的sink处理,一个source可对应多个sink。
在video\video_sink_interface.h中,video sink定义如下:
1 2 3 4 5 6 7 8 9 10 11 |
template <typename VideoFrameT> class VideoSinkInterface { public: virtual ~VideoSinkInterface() = default; virtual void OnFrame(const VideoFrameT& frame) = 0; // Should be called by the source when it discards the frame due to rate // limiting. virtual void OnDiscardedFrame() {} }; |
sink通过OnFrame
获取source传递的VideoFrame
。
MediaStreamTrack与MediaStream
MediaStream API中有两个重要组成:MediaStreamTrack
以及MediaStream
。MediaStreamTrack
对象代表单一类型的媒体流,产生自客户端的media source
,可以是音频或者视频,但只能是其中一种,是音频称作audio track
,视频的话称作video track
,这其实就是我们平时所说的音轨与视频轨。
一个track由source与sink组成。source给track提供数据。
Medi
用于将多个
aStreamMediaStreamTrack
对象打包到一起。一个MediaStream
可包含audio track
与video track
。类似我们平时的多媒体文件,可包含音频与视频。
一个MediaStream
对象包含0或多个MediaStreamTrack
对象。MediaStream
中的所有MediaStreamTrack
对象在渲染时必须同步。就像我们平时播放媒体文件时,音视频的同步。
简单点说,source 与sink构成一个track,多个track构成MediaStram。
在api\media_stream_interface.h中,MediaStream
定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class MediaStreamInterface : public rtc::RefCountInterface, public NotifierInterface { public: virtual std::string id() const = 0; virtual AudioTrackVector GetAudioTracks() = 0; virtual VideoTrackVector GetVideoTracks() = 0; virtual rtc::scoped_refptr<AudioTrackInterface> FindAudioTrack( const std::string& track_id) = 0; virtual rtc::scoped_refptr<VideoTrackInterface> FindVideoTrack( const std::string& track_id) = 0; virtual bool AddTrack(AudioTrackInterface* track) = 0; virtual bool AddTrack(VideoTrackInterface* track) = 0; virtual bool RemoveTrack(AudioTrackInterface* track) = 0; virtual bool RemoveTrack(VideoTrackInterface* track) = 0; protected: ~MediaStreamInterface() override = default; }; |
MediaStream
可以添加移除audio track以及video track。
下面说下MediaTrack
的定义,这里我们举例VideoTrack
,代码同样位于api\media_stream_interface.h中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
class VideoTrackInterface : public MediaStreamTrackInterface, public rtc::VideoSourceInterface<VideoFrame> { public: // Video track content hint, used to override the source is_screencast // property. // See https://crbug.com/653531 and https://w3c.github.io/mst-content-hint. enum class ContentHint { kNone, kFluid, kDetailed, kText }; // Register a video sink for this track. Used to connect the track to the // underlying video engine. void AddOrUpdateSink(rtc::VideoSinkInterface<VideoFrame>* sink, const rtc::VideoSinkWants& wants) override {} void RemoveSink(rtc::VideoSinkInterface<VideoFrame>* sink) override {} virtual VideoTrackSourceInterface* GetSource() const = 0; virtual ContentHint content_hint() const; virtual void set_content_hint(ContentHint hint) {} protected: ~VideoTrackInterface() override = default; }; |
可以看到VideoTrack
由video source与video sink组成。
pc\video_track.cc中,我们看下MediaTrack
接口的一些具体实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void VideoTrack::AddOrUpdateSink(rtc::VideoSinkInterface<VideoFrame>* sink, const rtc::VideoSinkWants& wants) { RTC_DCHECK(worker_thread_->IsCurrent()); VideoSourceBase::AddOrUpdateSink(sink, wants); rtc::VideoSinkWants modified_wants = wants; modified_wants.black_frames = !enabled(); video_source_->AddOrUpdateSink(sink, modified_wants); } void VideoTrack::RemoveSink(rtc::VideoSinkInterface<VideoFrame>* sink) { RTC_DCHECK(worker_thread_->IsCurrent()); VideoSourceBase::RemoveSink(sink); video_source_->RemoveSink(sink); } |
由于VideoTrack
由video source与video sink组成,所以对VideoTrack
进行AddOrUpdateSink
操作时,其实就是让VideoTrack
的source进行AddOrUpdateSink
。
示例
为了更好地理解上述概念,我们举个例子说明。下图home client中,摄像机产生视频video source。source的宽高设置分别是800以及600像素。home client 中三个MediaStream
包含的track使用该摄像机视频作为source,此时就有三个video track了。三个video track分别连接三个不同的sink:<video>
标签A,<video>
标签B以及一个peer connection C。<video>
A与<video>
B分别对source的视频进行缩放处理后渲染到本地浏览器界面中。peer connection C作为sink把该video source 推流到remote client。在remote client,两个media stream使用peer connection作为source,连接到两个<video>
sink(Y与Z),进行本地渲染。
参考
[1] Media Capture and Streams.https://www.w3.org/TR/mediacapture-streams/.
[2] WebRTC 1.0: Real-time Communication Between Browsers.http://w3c.github.io/webrtc-pc/.
文章评论