ZLMediaKit学习记录--RTMP

发布时间 2023-07-24 14:50:59作者: 泽良_小涛

第二次看ZLMediaKit部分的记录,有许多理解错误的地方,将进一步整理。

cd /opt/ZLMediaKit

mkdir build

cd build

cmake .. -DENABLE_WEBRTC=true -DOPENSSL_ROOT_DIR=/opt/openssl -DOPENSSL_LIBRARIES=/opt/openssl/lib

cmake --build . --target MediaServer

cd /opt/ZLMediaKit/release/linux/Debug

./MediaServer

e:

cd e:\Demo\CGAvioRead\Debug

ffmpeg -re -stream_loop -1 -i d:/H264_AAC_2021-02-10_1080P.mp4 -vcodec copy -acodec copy -f flv -y rtmp://10.10.14.103:1935/live/Camera_00002

ffmpeg -re -stream_loop -1 -i d:/H264_AAC_2021-02-10_1080P.mp4 -vcodec copy -acodec copy -f flv -y rtmp://192.168.0.107:1935/live/Camera_00002

ffmpeg -re -stream_loop -1 -i e:/H264_AAC_2021-02-10_1080P.mp4 -vcodec copy -acodec copy -f rtsp -rtsp_transport tcp rtsp://10.10.15.30:554/live/Camera_00001

1. RtmpSession::onRecv

onParseRtmp

2. RtmpProtocol::onParseRtmp

input(data, size)

3. HttpRequestSplitter::input

(index = onSearchPacketTail(ptr,_remain_data_size)) != nullptr)

4. RtmpProtocol::onSearchPacketTail

构造函数:

_next_step_func = [this](const char *data, size_t len) {

return handle_C0C1(data, len);

};

auto ret = next_step_func(data, len);

5. RtmpProtocol::handle_C0C1

if (data[0] != HANDSHAKE_PLAINTEXT)

对C0的判断,为0x03

handle_C1_complex(data);

编译时ENABLE_OPENSSL打开。

6. RtmpProtocol::handle_C1_complex

C1的方式为schema1:

c1s1 schema0

time: 4bytes

version: 4bytes

digest: 764bytes

key: 764bytes

RtmpProtocol::get_C1_digest() //skip c0,time,version

得到digest,764bytes digest结构:

offset: 4bytes

random-data: (offset)bytes

digest-data: 32bytes

random-data: (764-4-offset-32)bytes

check_C1_Digest:验证digest.

Send_complex_S0S1S2(1, digest);

7. RtmpProtocol::send_complex_S0S1S2

(1)//发送S0

char handshake_head = HANDSHAKE_PLAINTEXT;

onSendRawData(obtainBuffer(&handshake_head, 1));

(2)//发送S1

memcpy(s1.zero, "\x04\x05\x00\x01", 4);为版本号

c1s1 schema1

time: 4bytes

version: 4bytes

digest: 764bytes

key: 764bytes

get_C1_digest(s1.random, &digestPos);

//将digest加入,并发送

memcpy(digestPos, s1_digest.data(), s1_digest.size());

onSendRawData(obtainBuffer((char *) &s1, sizeof(s1)));

(3)//发送S2

生成key和digest,然后发送。

(4)等待C2

handle_C2(data, len)

8. RtmpProtocol::handle_C2

未过多进行处理。握手结束,进入命令模。

9. RtmpProtocol::handle_rtmp

(1)static constexpr size_t HEADER_LENGTH[] = {12, 8, 4, 1};

auto header_len = HEADER_LENGTH[header->fmt];

得到RTMP header的长度。

(2) switch (_now_chunk_id)

对编码块流进一步处理。ID 0、1、2作为保留。0,两个字节表示,第一个字节+64;1,三个字节表示,(第三个字节) * 256 + 第二个字节 + 64);2表示低层协议消息。3-63,表示完整的流ID。这个switch一般不会走到。

(3)handle_chunk(std::move(now_packet));

10. RtmpProtocol::handle_chunk

(1)RtmpPacket中的变量

bool is_abs_stamp;

uint8_t type_id;

uint32_t time_stamp;

uint32_t ts_field;

uint32_t stream_index;

uint32_t chunk_id;

size_t body_size;

toolkit::BufferLikeString buffer;

bool is_abs_stamp;

uint8_t type_id;

uint32_t time_stamp;

uint32_t ts_field;

uint32_t stream_index;

uint32_t chunk_id;

size_t body_size;

toolkit::BufferLikeString buffer;

(2)MSG_SET_CHUNK

type_id:1,body_size:4

来表示chunk_size的长度,如ea 60:60000

(3) MSG_WIN_SIZE

type_id:5,body_size:4

这个在播放时为发送。这个值应在32 * 1024U-1280 * 1024U之间。客户端收到大于这个字节的数据,要向服务器发送ACK消息。

(4) MSG_SET_PEER_BW

type_id:6,body_size:5

4个字节的字节的大小,1个字节的类型。未调试到。

(5)MSG_AGGREGAT

将包含子包的进行拆分。未调试到。

(6)MSG_USER_CONTROL

未调试到。包含有多一个事件信息,从代码来看,一般前2个字节是事件的类型,接着的4个字节是内容。

(7)MSG_ACK

Acknowledgement。确认信息。未进行过多的处理。

(8)其他的消息

  • 收到设定的字节,回复确认信息。
  • 基他的交由onRtmpChunk处理。

11. RtmpSession::onRtmpChunk

分为三种类型处理:

  1. MSG_CMD、MSG_CMD3由onProcessCmd(12)处理。
  2. MSG_DATA、MSG_DATA3由(13)解析。
  3. MSG_AUDIO、MSG_VIDEO由(14)解析。

12. RtmpSession::onProcessCmd

与RTSP的实现方法一样。处理下列命令:

  1. RtmpSession::onCmd_connect--15
  2. RtmpSession::onCmd_createStream--16
  3. RtmpSession::onCmd_publish--17
  4. RtmpSession::onCmd_deleteStream--18
  5. RtmpSession::onCmd_play--19
  6. RtmpSession::onCmd_play2--20
  7. RtmpSession::onCmd_seek--21
  8. RtmpSession::onCmd_pause--22
  9. RtmpSession::onCmd_playCtrl--23

13. MSG_DATA、MSG_DATA3

推流时会处理。执行setMetaData(dec);得到推流源的metadata数据。?29(不进行其他协议时,这个是没有用的。)

14. MSG_AUDIO、MSG_VIDEO

(1) _push_src->setMetaData-->29

(2)_push_src->onWrite(std::move(packet));?

RtmpMediaSourceImp::onWrite?

分成两部分执行:_demuxer->inputRtmp(pkt);?30

RtmpMediaSource::onWrite?31

 

15. RtmpSession::onCmd_connect

(1)向客户端回Chunksize、AcknowledgementSize、PeerBandwidth都为固定值。

(2)_tc_url,如:rtmp://10.10.15.30:1935/live。

(3)回复connect命令。

(4)发送onBWDone,值为0.0。

16. RtmpSession::onCmd_createStream

直接回复。

17. RtmpSession::onCmd_publish

Broadcast::PublishAuthInvoker invoker—> NoticeCenter::Instance().emitEvent? auto on_res

  1. 创建RtmpMediaSourceImp。
  2. 回复onStatus。

18. RtmpSession::onCmd_deleteStream

  1. RtmpMediaSourceImp清空。
  2. 回复onStatus。与RtmpSession::onCmd_publish的值有所不同。

19. RtmpSession::onCmd_play

20. RtmpSession::onCmd_play2

调试执行的为RtmpSession::onCmd_play。

NoticeCenter::Instance().emitEvent—> Broadcast::AuthInvoker invoker?

RtmpSession::doPlayResponse?24

if (!flag)不执行

21. RtmpSession::onCmd_seek

(1)sendStatus回复状态。

(2)strong_src->seekTo

(3)通过继承关系调用MediaSource::seekTo

22. RtmpSession::onCmd_pause

与RtmpSession::onCmd_seek相似

23. RtmpSession::onCmd_playCtrl

与RtmpSession::onCmd_seek相似,控制播放速度的。VCL播放器不会执行到这里

24. RtmpSession::doPlayResponse

strong_self->sendPlayResponse("", rtmp_src);

25. RtmpSession::sendPlayResponse

(1)sendUserControl(CONTROL_STREAM_BEGIN, STREAM_MEDIA);// stream begin

(2) sendStatus({ "level", (ok ? "status" : "error"), //onStatus(NetStream.Play.Reset)

(3) sendStatus({ "level", "status", // onStatus(NetStream.Play.Start)

(4) sendResponse// RtmpSampleAccess(true, true)

(5) sendResponse// onStatus(NetStream.Data.Start)

(6) sendStatus// onStatus(NetStream.Play.PublishNotify)

(7) sendResponse(MSG_DATA, invoke.data());//发送onMetaData数据

(8) _ring_reader->setReadCB—》 strong_self->onSendMedia(rtmp);发送存入的环形数据。

26. RtmpSession::onSendMedia

27. RtmpProtocol::sendRtmp

(1)通过RtmpSession : public toolkit::Session, public RtmpProtocol继承关系,调用这个函数,然后调用同名函数。

(2)onSendRawData(std::move(buffer_header));//发送rtmp头

(3) onSendRawData发送真正的数据。

28. RtmpSession:: onSendRawData

调用send发送数据。

29. RtmpMediaSourceImp::setMetaData

(1) _demuxer->loadMetaData(metadata)?32

(2) RtmpMediaSource::setMetaData(metadata)?33

30. RtmpDemuxer::inputRtmp

Track已经有metadata创建,不再创建。

(1)_video_rtmp_decoder->inputRtmp--36

(2)_audio_rtmp_decoder->inputRtmp--37

31. RtmpMediaSourceImp::onWrite

(1)_demuxer->inputRtmp(pkt);RTMP推流时不经这个过程。

(2)RtmpMediaSource::onWrite RTMP推流时经过这个过程。--47

32. RtmpDemuxer::loadMetaData

主要执行了下面3个:

  1. makeVideoTrack—>34
  2. makeAudioTrack?35
  3. addTrackCompleted? Demuxer::addTrackCompleted?

_listener->addTrackCompleted

Track添加完毕。

33. RtmpMediaSource--> setMetaData

(1)未执行过多的操作,只是进行了赋值和取值。

(2)未执行regist。

34. RtmpDemuxer::makeVideoTrack

和RTSP有点类似了。RTSP是从SDP中得到的,RTMP是从metadata中得到的相关信息。

  1. _video_track:H264Track。
  2. _video_rtmp_decoder:H264RtmpEncoder
  3. 以后执行的为14. MSG_AUDIO、MSG_VIDEO
  4. addTrack(_video_track);通过继承关系—MediaSink::addTrack--42

35. RtmpDemuxer::makeAudioTrack

执行的和34. RtmpDemuxer::makeVideoTrack。区别为AACTrack、AACRtmpEncoder。

36. H264RtmpDecoder::inputRtmp

(1)对于sps、pps直接h264的头。

(2)对于其他的RtmpPacket要进行拆分

(3) 前两个字节没有用。

(4)接着的3个字节为cts时间戳。

(5)接着的4个字节为长度,根据这4个字节的长度进行复制。

(6)加H264的头,通过RtmpCodec::inputFrame写入到环形缓存中。--38

37. AACRtmpDecoder::inputRtmp

(1)buffer[1]决定是否为isCfgFrame。buffer[1]决定Acc的类型。

(2)第一次到的是onGetAAC(nullptr, 0, 0);

第二次到的是onGetAAC(pkt->buffer.data() + 2, pkt->buffer.size() - 2, pkt->time_stamp);--49

 

38. FrameDispatcher-- bool inputFrame

(1)RtmpCodec::inputFramece通过继承关系到FrameDispatcher类的inputFrame函数。

(2)pr.second->inputFrame(frame) 通过继承关系到MediaSink::inputFrame。

(3)it->second.first->inputFrame通过继承关系到H264Track::inputFrame。--39

(4)_mute_audio_maker为静音设备器。不设置时为空。

(5)checkTrackIfReady检查track是否完毕。--40

39. H264Track::inputFrame

(1)type是00 00 00 01后的第一个字节的低5位。

(2)如果/非I/B/P帧情况下,split一下,防止多个帧粘合在一起。这个情况会执行的。

(3)最终执行的是inputFrame_l--41

40. MediaSink::checkTrackIfReady

(1) it->second();--

onTrackReady(track);--

MultiMediaSourceMuxer::onTrackReady--44

(2) emitAllTrackReady--45

41. H264Track::inputFrame_l

(1)取出sps、pps的值。

(2)最终执行的都是VideoTrack::inputFrame-->等待checkTrackIfReady检查track是否完毕。--38—40

42. MediaSink::addTrack

(1)track未就绪,缓存frame

(2)track就绪,则执行onTrackFrame? MultiMediaSourceMuxer::onTrackFrame—43

43. MultiMediaSourceMuxer::onTrackFrame

(1)对应的(Rtmp)MediaSourceMuxer输入。

(2)如果是rtmp推流,RtmpMediaSourceMuxer输入将不成立。是为了不重复生成rtmp协议。

44. MultiMediaSourceMuxer::onTrackReady

将不为空的RtmpMediaSourceMuxer添加track.rtmp推流时_rtmp为空。

45. MediaSink::emitAllTrackReady

(1)在循环中将缓存的的取出MediaSink::inputFrame中处理—38.

(2)onAllTrackReady_l();?onAllTrackReady();?MultiMediaSourceMuxer::onAllTrackReady

?46

46. MultiMediaSourceMuxer::onAllTrackReady

(1)_rtmp->onAllTrackReady();--》RtmpMediaSourceMuxer --onAllTrackReady()—》

H264RtmpEncoder::makeConfigPacket()

创建sps、pps.

(2)listener->onAllTrackReady()通知准备完毕。

47. RtmpMediaSource::onWrite

(1)PacketCache<RtmpPacket>::inputPacket——> _cache->emplace_back(std::move(pkt)); //追加数据到最后

(2) flush();->onFlush(std::move(_cache), _key_pos);?48

48. RtmpMediaSource—onFlush

_ring->write? RingBuffer—write? _storage->write(std::move(in), is_key);? _RingStorage—write写入环形缓存数据。?对上play的读数据。

49.AACRtmpDecoder::onGetAAC

(1)生成adts头—》dumpAacConfig,如何生成,以后结合代码和解析一起看(zzl)。

(2)然后生成frame。

(3) RtmpCodec::inputFrame(frame);--其执行和视频的执行过程相似(38、39)。但执行到

AACTrack::inputFrame—》50

50. AACTrack::inputFrame(与39对比)

(1)getAacFrameLength序号3、4、5决定frame的长度。

(2) if (frame_len == (int)frame->size())这个一般是成立的,所以要执行return inputFrame_l(frame);--51

51. AACTrack::inputFrame_l(const Frame::Ptr &frame)

(1)makeAacConfig 生成Config.

(2)onReady();解析Config.。

(3)AudioTrack::inputFrame(frame);--》FrameDispatcher—inputFrame. 写入帧并派发—52

52. FrameDispatcher—inputFrame

(1)和h264类似。

(2)