一、推流,初始階段
處理推流時(shí),publish命令由server_session.go#doPublish方法負(fù)責(zé)處理,調(diào)用棧如下
server_session.go#doCommandMessage
->
doPublish
->
server.go#OnNewRtmpPubSession
->
server_manager__.go#OnNewRtmpPubSession
->
group__in.go#AddRtmpPubSession
->
addIn
上面這復(fù)雜的調(diào)用棧,重點(diǎn)是rtmp2mpegts_filter_.go的邏輯,先從入口Push方法看起,此方法的功能是從消息中取得音頻和視頻的codecID,用于確定ts文件所需的pat表和pmt表的內(nèi)容
func (q *rtmp2MpegtsFilter) Push(msg base.RtmpMsg) {
// q.done是個(gè)標(biāo)志,一旦等于true,今后收到的消息都直接給觀察者,
// 但是等于true之前,收到的消息都放在切片中緩存起來(lái),
// 如果從消息中成功取得音頻和視頻的codecID,就在drain方法中把標(biāo)準(zhǔn)設(shè)置為true
if q.done {
q.observer.onPop(msg)
return
}
// 將數(shù)據(jù)緩存到q.data
q.data = append(q.data, msg.Clone())
// 如果是音頻消息或者視頻消息,就可以得到對(duì)應(yīng)的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都搜集到了,就執(zhí)行drain,
if q.videoCodecId != -1 && q.audioCodecId != -1 {
q.drain()
return
}
// 緩存存不下的時(shí)候也會(huì)執(zhí)行drain
if len(q.data) >= q.maxMsgSize {
q.drain()
return
}
}
func (q *rtmp2MpegtsFilter) drain() {
// 根據(jù)當(dāng)前視頻的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基礎(chǔ)功能
1. 音視頻轉(zhuǎn)碼
FFmpeg的轉(zhuǎn)碼功能基于其豐富的編碼器和解碼器支持。例如,將一個(gè)MP4格式的視頻文件轉(zhuǎn)換為AVI格式:
ffmpeg -i input.mp4 -c:v libxvid -c:a copy output.avi
• -i input.mp4 指定輸入文件。
• -c:v libxvid 設(shè)置視頻編碼器為Xvid,用于轉(zhuǎn)碼視頻流。
• -c:a copy 表示音頻流保持不變(直接復(fù)制),若需轉(zhuǎn)碼音頻可替換為指定的音頻編碼器,如 -c:a libmp3lame 轉(zhuǎn)為MP3格式。
2. 音視頻剪輯
對(duì)于精確的時(shí)間戳剪輯,可以使用-ss參數(shù)定位開(kāi)始時(shí)間點(diǎn),并用-t或-to設(shè)定持續(xù)時(shí)長(zhǎng):
ffmpeg -i input.mp4 -ss 00:01:30 -t 00:00:30 -c copy cut.mp4
這里從原始視頻的1分30秒處開(kāi)始截取,時(shí)長(zhǎng)為30秒,且由于使用了-c copy,因此進(jìn)行的是無(wú)損剪輯(假設(shè)源容器支持)。
3. 音視頻合并
將多個(gè)音視頻文件拼接成一個(gè)文件,需要對(duì)各個(gè)文件進(jìn)行同步處理并按順序合并:
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 參數(shù)內(nèi)的 concat 過(guò)濾器用來(lái)連接所有音視頻流,生成最終的輸出文件。
三、常用流媒體協(xié)議
RTSP(Real Time Streaming Protocol),即實(shí)時(shí)流傳輸協(xié)議,是TCP/IP協(xié)議體系中的一個(gè)應(yīng)用層協(xié)議。由哥倫比亞大學(xué)、網(wǎng)景和RealNetworks公司提交的IETF RFC2326標(biāo)準(zhǔn)。該協(xié)議主要用于通過(guò)IP網(wǎng)絡(luò)高效傳送多媒體數(shù)據(jù),一對(duì)多應(yīng)用程序的理想選擇。RTSP在體系結(jié)構(gòu)上位于RTP和RTCP之上,可使用TCP或UDP完成數(shù)據(jù)傳輸。
RTP(Real-time Transport Protocol),1996年由IETF多媒體傳輸工作小組在RFC 1889中定義。RTP詳細(xì)說(shuō)明了音頻和視頻數(shù)據(jù)在互聯(lián)網(wǎng)上傳遞的標(biāo)準(zhǔn)數(shù)據(jù)包格式,并基于UDP協(xié)議構(gòu)建。
RTCP(Real-time Transport Control Protocol),是RTP的姐妹協(xié)議,由RFC 3550定義(替代舊的RFC 1889)。RTCP主要負(fù)責(zé)在RTP傳輸過(guò)程中提供傳輸信息反饋。RTP使用偶數(shù)UDP端口進(jìn)行數(shù)據(jù)傳輸,而RTCP則使用RTP的下一個(gè)端口(奇數(shù)端口)進(jìn)行控制信息傳輸。
RTSP協(xié)議:負(fù)責(zé)服務(wù)器與客戶端之間的請(qǐng)求與響應(yīng)
RTP協(xié)議:負(fù)責(zé)傳輸媒體數(shù)據(jù)
RTCP協(xié)議:在RTP傳輸過(guò)程中提供傳輸信息