ZLMediaKit的转流流程

2023-12-20 10:23:24

zlmediakit的优势就是支持多种媒体容器和媒体协议。我从推流和拉流的两个角度,梳理出了转流的核心骨架。

推流

协议和容器格式的转换,最基本的内核就是音视频数据的扭转。
对视频而言就是,解封装帧数据,组帧,封装帧。
对音频而言简单些,只有解封装,封装。
如下是rtsp中的视频转换为rtmp,mp4,webrtc的简单示意图。

image.png
源端是rtsp的推流,目的端是各种协议的拉流。

  1. 最开始的是rtsp信令协商。
  2. 信令协议协商成功后,通过rtp传输媒体数据。
  3. 从rtp包中解出视频nalu数据。
  4. 组成完整的nalu数据,再根据具体的目的协议或容器进行封装。
  5. 目标协议也是媒体的协商,在媒体协商完成后,由拉流端主动发起转流。

下面是以rtsp推流中的整个流程为例子,画了一个视频的流转图(音频也类似)。

在这里插入图片描述

当一个rtsp推流端推流后,媒体流会经过解封装,组帧再经过封装成不同协议放到对应的ringbuffer中。流程图中可以很明显的看到整个过程。
对推到ZLMediaKit的流,都会固定的产生FMP4MediaSource(有宏控制)RtmpMediaSourceRtspMediaSourceTSMediaSourceMP4Recorder(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数据都已准备好。那么在消息(拉流)时,只需要媒体信令完成,就可以直接发流了。

拉流

上面了解了推流的处理流程,那么拉流的流程就比较好理解了,如下图:
image.png

  1. 以rtmp协议拉流,rtmp的信令协商处理最终会放到RtmpSession中处理。
  2. 在协商完成后,会在s_media_source_map找到MediaSource
  3. 再通过MediaSoruce取到RingBuffer对象。
  4. 通过调用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);
        });
    });

通过RingBufferattach方法将RtmpSession对象关联到源buffer中,再将数据发送出去。

这就是ZLMediaKit转流的骨架,当然整个流程涉及到很多"皮毛",比如媒体格式的匹配,时间戳的转换,同步等。掌握了骨架在解读细节就不会困难了。

文章来源:https://blog.csdn.net/mo4776/article/details/135098657
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。