zlmediakit的优势就是支持多种媒体容器和媒体协议。我从推流和拉流的两个角度,梳理出了转流的核心骨架。
协议和容器格式的转换,最基本的内核就是音视频数据的扭转。
对视频而言就是,解封装帧数据,组帧,封装帧。
对音频而言简单些,只有解封装,封装。
如下是rtsp中的视频转换为rtmp,mp4,webrtc的简单示意图。
源端是rtsp的推流,目的端是各种协议的拉流。
下面是以rtsp推流中的整个流程为例子,画了一个视频的流转图(音频也类似)。
当一个rtsp推流端推流后,媒体流会经过解封装,组帧再经过封装成不同协议放到对应的ringbuffer
中。流程图中可以很明显的看到整个过程。
对推到ZLMediaKit的流,都会固定的产生FMP4MediaSource(有宏控制)
,RtmpMediaSource
,RtspMediaSource
,TSMediaSource
,MP4Recorder(mp4存储,按需产生)
,HlsRecorder(Hls存储,按需产生)
,RingBuffer(未经过封装的裸帧数据)
。
这些对象都会注册到全局的MediaSource
容器中,就是s_media_source_map
,下面是它的定义
using StreamMap = unordered_map<string/*strema_id*/, weak_ptr<MediaSource> >;
using AppStreamMap = unordered_map<string/*app*/, StreamMap>;
using VhostAppStreamMap = unordered_map<string/*vhost*/, AppStreamMap>;
using SchemaVhostAppStreamMap = unordered_map<string/*schema*/, VhostAppStreamMap>;
static SchemaVhostAppStreamMap s_media_source_map;
就是多个unorder_map
的套娃,记录了流的信息和对应的MediaSource
对象。当有需要该流时,会根据流信息在容器中找对应的MediaSource
。
每路推流(不同的stream id)都会这样的流程,产生几个对应协议的MediaSource
对象。
所以在媒体层面,不管该流是否有被消费(拉流),媒体层面的rtsp,rtmp,fmp4,ts数据都已准备好。那么在消息(拉流)时,只需要媒体信令完成,就可以直接发流了。
上面了解了推流的处理流程,那么拉流的流程就比较好理解了,如下图:
RtmpSession
中处理。s_media_source_map
找到MediaSource
。MediaSoruce
取到RingBuffer
对象。RingBuffer
对象的attch
方法,打通转流。下面是rtmp拉流与源端对接的代码,位于RtmpSession::sendPlayResponse
中。
_ring_reader = src->getRing()->attach(getPoller());
weak_ptr<RtmpSession> weak_self = static_pointer_cast<RtmpSession>(shared_from_this());
_ring_reader->setGetInfoCB([weak_self]() {
Any ret;
ret.set(static_pointer_cast<SockInfo>(weak_self.lock()));
return ret;
});
_ring_reader->setReadCB([weak_self](const RtmpMediaSource::RingDataType &pkt) {
auto strong_self = weak_self.lock();
if (!strong_self) {
return;
}
size_t i = 0;
auto size = pkt->size();
strong_self->setSendFlushFlag(false);
pkt->for_each([&](const RtmpPacket::Ptr &rtmp){
if(++i == size){
strong_self->setSendFlushFlag(true);
}
strong_self->onSendMedia(rtmp);
});
});
通过RingBuffer
的attach
方法将RtmpSession
对象关联到源buffer中,再将数据发送出去。
这就是ZLMediaKit转流的骨架,当然整个流程涉及到很多"皮毛",比如媒体格式的匹配,时间戳的转换,同步等。掌握了骨架在解读细节就不会困难了。