ZLMedia--AAC的流程
将conf\config.ini中[rtsp ]directProxy=1。未搞明白直接代理和非直接代理在流程上有什么不同。
一.RtspSession::onRecv--RtspSession.cpp
从网络上得到一个包。对于ACC包buf->data()[1]==0x2。
b /opt/ZLMediaKit/src/Rtsp/RtspSession.cpp:132(因加日志有所差异;打印时只打印前100个字节。下同。)
1、第1个字节标识符,用于与RTSP区分。为0x24,即字符”$",表示传输的RTP包。
2、第2个字节,channel,用于区分RTP和RTCP。ACC:0x02,H264:0x00,RTCP:0x01或03.
3、第3、4个字节RTP包的大小,如050b,表示字节的长度为1291,表示整个包的长度,包括RTP包header和payload。但不包括RTP over TCP 的这4个字节。
4、第5个字节以后的12个字节:
(1)80:10000000
V: 2bits,表示版本号。
P: 1bit,表示是否支持填充,不支持。
X: 1bit, 表示是否支持Rtp头扩展,不支持。
CC(CSRC count): 4bits,表示头部之后contributing sources identifiers的个数。
(2)e1:11100001
M: 1bit; 该值为1时,表示该数据包是一帧数据的最后一个数据包。
PT: 7bits,表示传输的多媒体类型(标识了RTP载荷的类型)。常用的的96表示h264,97 表示ACC。1100001表示97。
(3)sequence number:16bits(2字节),表示RTP包序号。
(4)timestamp:32bits(4字节),表示时间戳, 必须使用90 kHz 时钟频率。
(5)SSRC:32bits(4字节),用于标识同步信源,参加同一视频会议的两个同步信源不能有相同的SSRC.
(6)CSRC:特约信源标识符,每个CSRC占用4个字节,可以有0~15个。每个CSRC标识了包含在该RTP报文有效载荷中的所有特约信源。cc为0,所以这个没有。
5.Payload的前两个字节,表示ACC的ACC的帧数。
如用FFMPEG推送,前两个字节为00 50.,表示Au-Header的个数,单位bit,所以除以16得到Au-Header个数
0000 0000 0101 0000
auto au_header_count = ((ptr[0] << 8) | ptr[1]) >> 4;
表示一个RTP包有5个AAC包。
6.如前两个字节为00 50,后面10个字节为AAC包的长度表示。
之后的2字节是AU_HEADER,其中高13位表示一帧AAC负载的字节长度,低3位无用
uint16_t size = ((au_header_ptr[0] << 8) | au_header_ptr[1]) >> 3;
如07 f8,用二进制表示为:0000 0111 1111 1000。其中高13位表示一帧AAC负载的字节长度,低3位无用。即 1111 1111表示为这个AAC包为255个字节。
7.每个AA包不带头。
二. HttpRequestSplitter::input--HttpRequestSplitter.cpp
b /opt/ZLMediaKit/src/Http/HttpRequestSplitter.cpp:68
这一步主要是将不同的类型的包分类处理,对于rtp没有改变接收的内容,调用 onRecvHeader。
三.RtspSplitter::onRecvHeader--RtspSplitter.cpp
在这个函数中将rtp包和rtsp包分别处理,为了调试的方便,只处理ACC时,将相关代码改为:
if(_isRtpPacket){ if(data[1]==0x2) { onRtpPacket(data,len); } return 0; } 加了行条件if(data[1]==0x2)
四.RtspSession::onRtpPacket--RtspSession.cpp
这个函数将RTP与RTCP包分开处理。
RTP包调用:handleOneRtp函数。传入参数时将RTSP去掉,前4个字节。
五.RtpMultiReceiver::handleOneRtp--RtpReceiver.h
没有对数据进行改变,将由track处理。
六.RtpTrack::inputRtp--RtpReceiver.cpp
b /opt/ZLMediaKit/src/Rtsp/RtpReceiver.cpp:35
在这个函数中又重新加上了RTSP的前4个字节。并调用 rtp->ntp_stamp = _ntp_stamp.getNtpStamp(rtp->getStamp(), sample_rate);改变时间戳。
七.RtspSession::onBeforeRtpSorted--RtspSession.cpp
调用updateRtcpContext对RTCP进行处理。RTCP先不详细解释。
###################################################################
1、2、3、4、5、6、7可以重复执行,存储起来供后续调用。
###################################################################
八.RtspSession::onRtpSorted--RtspSession.cpp
将RTP包存储到推流相关绑定的源中。
九.RtspMediaSourceImp::onWrite--RtspMediaSourceImp.h
将RTP包存储到解复用中。
十.RtspDemuxer::inputRtp--RtspDemuxer.cpp
将RTP包存储到解码器中。
十一.AACRtpDecoder::flushData--AACRtp.cpp
b /opt/ZLMediaKit/src/Extension/AACRtp.cpp:93
1.首2字节表示Au-Header的个数,单位bit,所以除以16得到Au-Header个数
auto au_header_count = ((ptr[0] << 8) | ptr[1]) >> 4;
2.之后的2字节是AU_HEADER,其中高13位表示一帧AAC负载的字节长度,低3位无用
uint16_t size = ((au_header_ptr[0] << 8) | au_header_ptr[1]) >> 3;
3.一般情况下 ADTS 的头信息都是 7 个字节,分为 2 部分:
- adts_fixed_header(); —— 固定头信息,头信息中的每一帧都相同.
- adts_variable_header(); —— 可变头信息,头信息则在帧与帧之间可变.
class AdtsHeader{
public:
unsigned int syncword = 0; //12 bslbf 同步字The bit string ‘1111 1111 1111’,说明一个ADTS帧的开始
unsigned int id; //1 bslbf MPEG 标示符, 设置为1。0 for MPEG-4, 1 for MPEG-2
unsigned int layer; //2 uimsbf Indicates which layer is used. Set to ‘00’
unsigned int protection_absent; //1 bslbf 表示是否误码校验 set to 1 if there is no CRC and 0 if there is CRC
unsigned int profile; //2 uimsbf 表示使用哪个级别的AAC,如01 Low Complexity(LC)--- AACLC,表示使用哪个级别的AAC,如profile的值等于 Audio Object Type的值减1,即profile = MPEG-4 Audio Object Type - 1
unsigned int sf_index; //4 uimsbf 表示使用的采样率下标(见下图)
unsigned int private_bit; //1 bslbf
unsigned int channel_configuration; //3 uimsbf 表示声道数。比如2
表示立体声双声道.
unsigned int original; //1 bslbf
unsigned int home; //1 bslbf
//下面的为改变的参数即每一帧都不同
unsigned int copyright_identification_bit; //1 bslbf
unsigned int copyright_identification_start; //1 bslbf
unsigned int aac_frame_length; // 13 bslbf 一个ADTS帧的长度包括ADTS头和raw data block
unsigned int adts_buffer_fullness; //11 bslbf 0x7FF 说明是码率可变的码流
//no_raw_data_blocks_in_frame 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧.
//所以说number_of_raw_data_blocks_in_frame == 0
//表示说ADTS帧中有一个AAC数据块并不是说没有。(一个AAC原始帧包含一段时间内1024个采样及相关数据)
unsigned int no_raw_data_blocks_in_frame; //2 uimsfb
};
固定: 28 位 可变:28位,共56位,7个字节。
4. ADTS头部赋值的代码。
static void parseAacConfig(const string &config, AdtsHeader &adts) { uint8_t cfg1 = config[0]; uint8_t cfg2 = config[1]; int audioObjectType; int sampling_frequency_index; int channel_configuration; audioObjectType = cfg1 >> 3; sampling_frequency_index = ((cfg1 & 0x07) << 1) | (cfg2 >> 7); channel_configuration = (cfg2 & 0x7F) >> 3; adts.syncword = 0x0FFF; adts.id = 0; adts.layer = 0; adts.protection_absent = 1; adts.profile = audioObjectType - 1; adts.sf_index = sampling_frequency_index; adts.private_bit = 0; adts.channel_configuration = channel_configuration; adts.original = 0; adts.home = 0; adts.copyright_identification_bit = 0; adts.copyright_identification_start = 0; adts.aac_frame_length = 7; adts.adts_buffer_fullness = 2047; adts.no_raw_data_blocks_in_frame = 0; }
(1)config在SDP中config中得到。如1408.
(2)cfg1、cfg2分别为16进制的0x14和0x08,二制的0001 0100 ,0000 1000。
(3)audioObjectType 为2.profile的值等于 Audio Object Type的值减1,表示使用哪个级别的AAC。1: AAC Main 2:AAC LC (Low Complexity) 3:AAC SSR (Scalable Sample Rate) 4:AAC LTP (Long Term Prediction)。
(4)sampling_frequency_index 采样率的下标.8表示16000。
(5)channel_configuration 标识声道数。
5.将ADTS的头部赋给7个字节。
static void dumpAdtsHeader(const AdtsHeader &hed, uint8_t *out) { out[0] = (hed.syncword >> 4 & 0xFF); //8bit out[1] = (hed.syncword << 4 & 0xF0); //4 bit out[1] |= (hed.id << 3 & 0x08); //1 bit out[1] |= (hed.layer << 1 & 0x06); //2bit out[1] |= (hed.protection_absent & 0x01); //1 bit out[2] = (hed.profile << 6 & 0xC0); // 2 bit out[2] |= (hed.sf_index << 2 & 0x3C); //4bit out[2] |= (hed.private_bit << 1 & 0x02); //1 bit out[2] |= (hed.channel_configuration >> 2 & 0x03); //1 bit out[3] = (hed.channel_configuration << 6 & 0xC0); // 2 bit out[3] |= (hed.original << 5 & 0x20); //1 bit out[3] |= (hed.home << 4 & 0x10); //1 bit out[3] |= (hed.copyright_identification_bit << 3 & 0x08); //1 bit out[3] |= (hed.copyright_identification_start << 2 & 0x04); //1 bit out[3] |= (hed.aac_frame_length >> 11 & 0x03); //2 bit out[4] = (hed.aac_frame_length >> 3 & 0xFF); //8 bit out[5] = (hed.aac_frame_length << 5 & 0xE0); //3 bit out[5] |= (hed.adts_buffer_fullness >> 6 & 0x1F); //5 bit out[6] = (hed.adts_buffer_fullness << 2 & 0xFC); //6 bit out[6] |= (hed.no_raw_data_blocks_in_frame & 0x03); //2 bit }
6.加入ADTS头
经auto size = dumpAacConfig(_aac_cfg, _frame->_buffer.size(), (uint8_t *) adts_header, sizeof(adts_header));后
frame中入了7个字节的头
十二.AACTrack::inputFrame--AAC.cpp
将frame送入到track中。根据ADTS字头生成相关信息,更新ADTS字头中关于 track的信息。
1.得到frame的长度,包含了ADTS的头。
调用getAacFrameLength((uint8_t *) ptr, end - ptr),关键代码就一行:
len = ((uint16_t) (data[3] & 0x03) << 11) | ((uint16_t) data[4] << 3) | ((uint16_t) (data[5] >> 5) & 0x07);
2.再次到 frame.h 中bool inputFrame(const Frame::Ptr &frame) override,然后进行十三步。
十三.MediaSink::inputFrame--MediaSink.cpp
判断t各个rack是否准备好。
十二-1.再次执行步骤十二
十四.MediaSink::addTrack--track->addDelegate--MediaSink.cpp
各个track准备好以后,执行代理函数。
十五.MultiMediaSourceMuxer::onTrackFrame--MultiMediaSourceMuxer.cpp
将frame传给RtspMediaSourceMuxer。
十六.RtspMediaSourceMuxer:inputFrame--RtspMediaSourceMuxer.h
将frame传给RtspMuxer。
十七.RtspMuxer::inputFrame---RtspMuxer.cpp
将frame传给RtpCodec。
十八. AACRtpEncoder::inputFrame--AACRtp.cpp
将frame加上4个字节。0和16固定,另两个字节是frame长度。
_section_buf[2] = (len >> 5) & 0xFF;
_section_buf[3] = ((len & 0x1F) << 3) & 0xFF;
高13位表示一帧AAC负载的字节长度,低3位无用。
十九.ACRtpEncoder::makeAACRtp--AACRtp.cpp
生成rtp,并传给RtpCodec。
二十.RtpInfo::makeRtp--RtpCodec.cpp
对于每个frame加上rtsp over tcp 头和 rtp头.
二十一.RtpRing::inputRtp--RtpCodec.h
将rtp写RingBuffer中。
二十二.RingBuffer::write--RingBuffer.h
调用代理进一步处理。 _delegate->onWrite(std::move(in), is_key)。
二十三.RtspMediaSource:onWrite--RtspMediaSource.h
对 SdpTrack信息进行更新,并通知和存入PacketCache<RtpPacket>中。
二十四.PacketCache:inputPacket--MediaSource.h
追加数据到最后。
二十四.RtspMediaSource:onFlush--RtspMediaSource.h
更新RingBuffer。
¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥
以FFMPEG为例,每个RTP中包含5个FRAME数据,11-24要重复执行5次,再处理下一个包。
¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥
===================================下面为拉流的流程====================================================
1._play_reader->setReadCB( RtspSession::handleReq_Play--RtspSession.cpp)
2.RtspSession::sendRtpPacket(RtspSession.cpp)
3.RtspSession::send(RtspSession.cpp))
4.SocketHelper::send(Socket.cpp)
最后发送的是1帧数据。RTSP启始+RTSP头+0x00 0x01 两个字节的帧长+AAC数据。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!