一、推流,初始阶段
处理推流时,publish命令由server_session.go#doPublish方法负责处理,调用栈如下
server_session.go#doCommandMessage
->
doPublish
->
server.go#OnNewRtmpPubSession
->
server_manager__.go#OnNewRtmpPubSession
->
group__in.go#AddRtmpPubSession
->
addIn
上面这复杂的调用栈,重点是rtmp2mpegts_filter_.go的逻辑,先从入口Push方法看起,此方法的功能是从消息中取得音频和视频的codecID,用于确定ts文件所需的pat表和pmt表的内容
func (q *rtmp2MpegtsFilter) Push(msg base.RtmpMsg) {
        // q.done是个标志,一旦等于true,今后收到的消息都直接给观察者,
        // 但是等于true之前,收到的消息都放在切片中缓存起来,
        // 如果从消息中成功取得音频和视频的codecID,就在drain方法中把标准设置为true
        if q.done {
                q.observer.onPop(msg)
                return
        }
        // 将数据缓存到q.data
        q.data = append(q.data, msg.Clone())
        // 如果是音频消息或者视频消息,就可以得到对应的codecID
        switch msg.Header.MsgTypeId {
        case base.RtmpTypeIdAudio:
                q.audioCodecId = int(msg.Payload[0] >> 4)
        case base.RtmpTypeIdVideo:
                q.videoCodecId = int(msg.Payload[0] & 0xF)
        }
        
        // 一旦音频和视频的codecID都搜集到了,就执行drain,
        if q.videoCodecId != -1 && q.audioCodecId != -1 {
                q.drain()
                return
        }
        
        // 缓存存不下的时候也会执行drain
        if len(q.data) >= q.maxMsgSize {
                q.drain()
                return
        }
}
func (q *rtmp2MpegtsFilter) drain() {
        // 根据当前视频的codecId,确定ts文件的PAT,PMT格式
        switch q.videoCodecId {
        case int(base.RtmpCodecIdAvc):
                q.observer.onPatPmt(mpegts.FixedFragmentHeader)
        case int(base.RtmpCodecIdHevc):
                q.observer.onPatPmt(mpegts.FixedFragmentHeaderHevc)
        default:
                // TODO(chef) 正确处理只有音频或只有视频的情况 #56
                q.observer.onPatPmt(mpegts.FixedFragmentHeader)
        }
        
        // 将缓存的所有消息输出给观察者
        for i := range q.data {
                q.observer.onPop(q.data[i])
        }
        q.data = nil
        q.done = true
}
二、FFmpeg基础功能
1. 音视频转码
FFmpeg的转码功能基于其丰富的编码器和解码器支持。例如,将一个MP4格式的视频文件转换为AVI格式:
ffmpeg -i input.mp4 -c:v libxvid -c:a copy output.avi
• -i input.mp4 指定输入文件。
• -c:v libxvid 设置视频编码器为Xvid,用于转码视频流。
• -c:a copy 表示音频流保持不变(直接复制),若需转码音频可替换为指定的音频编码器,如 -c:a libmp3lame 转为MP3格式。
2. 音视频剪辑
对于精确的时间戳剪辑,可以使用-ss参数定位开始时间点,并用-t或-to设定持续时长:
ffmpeg -i input.mp4 -ss 00:01:30 -t 00:00:30 -c copy cut.mp4
这里从原始视频的1分30秒处开始截取,时长为30秒,且由于使用了-c copy,因此进行的是无损剪辑(假设源容器支持)。
3. 音视频合并
将多个音视频文件拼接成一个文件,需要对各个文件进行同步处理并按顺序合并:
ffmpeg -i video1.mp4 -i audio1.mp3 -i video2.mp4 -i audio2.mp3 \
-filter_complex "[0:v][0:a][1:v][1:a][2:v][2:a]concat=n=3:v=1:a=1[v][a]" \
-map "[v]" -map "[a]" -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 192k output.mp4
此命令中,-filter_complex 参数内的 concat 过滤器用来连接所有音视频流,生成最终的输出文件。
三、常用流媒体协议
RTSP(Real Time Streaming Protocol),即实时流传输协议,是TCP/IP协议体系中的一个应用层协议。由哥伦比亚大学、网景和RealNetworks公司提交的IETF RFC2326标准。该协议主要用于通过IP网络高效传送多媒体数据,一对多应用程序的理想选择。RTSP在体系结构上位于RTP和RTCP之上,可使用TCP或UDP完成数据传输。
RTP(Real-time Transport Protocol),1996年由IETF多媒体传输工作小组在RFC 1889中定义。RTP详细说明了音频和视频数据在互联网上传递的标准数据包格式,并基于UDP协议构建。
RTCP(Real-time Transport Control Protocol),是RTP的姐妹协议,由RFC 3550定义(替代旧的RFC 1889)。RTCP主要负责在RTP传输过程中提供传输信息反馈。RTP使用偶数UDP端口进行数据传输,而RTCP则使用RTP的下一个端口(奇数端口)进行控制信息传输。
RTSP协议:负责服务器与客户端之间的请求与响应
RTP协议:负责传输媒体数据
RTCP协议:在RTP传输过程中提供传输信息