第二次看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
分为三种类型处理:
- MSG_CMD、MSG_CMD3由onProcessCmd(12)处理。
- MSG_DATA、MSG_DATA3由(13)解析。
- MSG_AUDIO、MSG_VIDEO由(14)解析。
12. RtmpSession::onProcessCmd
与RTSP的实现方法一样。处理下列命令:
- RtmpSession::onCmd_connect--15
- RtmpSession::onCmd_createStream--16
- RtmpSession::onCmd_publish--17
- RtmpSession::onCmd_deleteStream--18
- RtmpSession::onCmd_play--19
- RtmpSession::onCmd_play2--20
- RtmpSession::onCmd_seek--21
- RtmpSession::onCmd_pause--22
- 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
- 创建RtmpMediaSourceImp。
- 回复onStatus。
18. RtmpSession::onCmd_deleteStream
- RtmpMediaSourceImp清空。
- 回复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个:
- makeVideoTrack—>34
- makeAudioTrack?35
- addTrackCompleted? Demuxer::addTrackCompleted?
_listener->addTrackCompleted
Track添加完毕。
33. RtmpMediaSource--> setMetaData
(1)未执行过多的操作,只是进行了赋值和取值。
(2)未执行regist。
34. RtmpDemuxer::makeVideoTrack
和RTSP有点类似了。RTSP是从SDP中得到的,RTMP是从metadata中得到的相关信息。
- _video_track:H264Track。
- _video_rtmp_decoder:H264RtmpEncoder
- 以后执行的为14. MSG_AUDIO、MSG_VIDEO
- 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)