寫在前面
本文將介紹以下內(nèi)容:
- 什么是推流?將介紹推流常見的協(xié)議RTMP,HLS等。
- 怎么用ffmpeg做推流,包括cmd和code兩種方式。
- 筆者在開發(fā)推流時遇到的一些坑點。
I. 推流簡介
筆者最初聽到“推流”時,內(nèi)心想:“這是什么高端玩意兒?”,迫于項目壓力,不得不頂著壓力調(diào)研和開發(fā)。經(jīng)過一段時間的學習、開發(fā)和總結(jié),筆者終于明白了推流,到底是個什么高端玩意兒?
什么是推流?
在?科普 | 直播中常說的推流拉流究竟是什么東東?中說:
推流,指的是把采集階段封包好的內(nèi)容傳輸?shù)椒?wù)器的過程。其實就是將現(xiàn)場的視頻信號傳到網(wǎng)絡(luò)的過程。
用大白話講,推流就是把本地音視頻數(shù)據(jù)通過網(wǎng)絡(luò)上傳到云端/后臺服務(wù)器,所謂“采集階段封包好”,筆者認為是未解碼的H264的NALU。
推流的整個階段如下圖,本圖同樣摘自?科普 | 直播中常說的推流拉流究竟是什么東東?(讀者請先忽略“拉流”這個名詞,姑且替換為客戶端用戶在視頻門戶網(wǎng)站,如騰訊視頻、B站等看網(wǎng)絡(luò)在線的視頻,自然也包括看直播。):
上圖從“推流端”到“源站”(同上文所說到的“服務(wù)器”),再到CDN分發(fā)節(jié)點,最后到“播放端”,整個過程的視音頻數(shù)據(jù),都是壓縮的數(shù)據(jù)流。也就是說,對視頻數(shù)據(jù)來說,就是H264碼流。解碼工作是在播放端進行的。
推流的工作可想而知,最多的應(yīng)用就是直播;而在大多數(shù)視頻門戶網(wǎng)站,筆者目前也很疑惑,是否存在步驟1,從效率上來說,視頻數(shù)據(jù)直接存放在“源站”,通過CDN根據(jù)客戶端請求下發(fā),應(yīng)該就可以了。至于具體做法,有待考證,筆者在這里姑且記一筆吧。
了解了什么是推流之后,下一個問題自然就出來了:應(yīng)該怎么推流呢?這其實是一個復(fù)雜的過程,而且還需要了解”源站“,也就是推流服務(wù)器。本文僅涉及本地音視頻數(shù)據(jù)通過網(wǎng)絡(luò)上傳到云端/后臺服務(wù)器的本地音視頻上傳階段,所以,先來了解本地的數(shù)據(jù)是怎么上傳的,第一步就是了解推流的協(xié)議!
筆者了解到的幾個目前常用的推流協(xié)議有:RTMP,HLS,webRTC,HTTP-FLV。本文僅介紹RTMP,原因是:筆者目前只接觸了RTMP協(xié)議的推流工作。
RTMP
RTMP是Real Time Messaging Protocol(實時消息傳輸協(xié)議)的首字母縮寫。是Adobe公司開發(fā)的一個基于TCP的應(yīng)用層協(xié)議,也就是說,RTMP是和HTTP/HTTPS一樣,是應(yīng)用層的一個協(xié)議族。RTMP在TCP通道上一般傳輸?shù)氖莊lv 格式流。請注意,RTMP是網(wǎng)絡(luò)傳輸協(xié)議,而flv則是視頻的封裝格式。flv封裝格式設(shè)計出來的目的是為了用于網(wǎng)絡(luò)傳輸使用的,因此RTMP+FLV可以說是”黃金搭檔“。
RTMP協(xié)議包括:基本協(xié)議及RTMPT/RTMPS/RTMPE等多種變種。從視頻協(xié)議學習:推流拉流都擅長的 RTMP了解到,RTMP協(xié)議家族有以下幾個點挺有趣,讀者們不妨看看:
- RTMP工作在TCP之上,默認使用端口1935,這個是基本形態(tài);
- RTMPE在RTMP的基礎(chǔ)上增加了加密功能;
- RTMPT封裝在HTTP請求之上,可穿透防火墻;
- RTMPS類似RTMPT,增加了TLS/SSL的安全功能;
- RTMFP使用UDP進行傳輸?shù)腞TMP;
RTMP就是專門針對多媒體數(shù)據(jù)流的實時通信設(shè)計出來的一種網(wǎng)絡(luò)數(shù)據(jù)傳輸協(xié)議,主要用來在Flash/AIR平臺和支持RTMP協(xié)議的流媒體/交互服務(wù)器之間進行音視頻和數(shù)據(jù)通信?,F(xiàn)在Adobe公司已經(jīng)不支持了,不過目前,該協(xié)議還在廣泛使用。
RTMP具體如何進行握手連接、傳輸數(shù)據(jù),以及其封裝的數(shù)據(jù)包格式,讀者都可以通過各種資料了解到,本文不再贅述。(主要原因是:筆者還沒有深入了解這塊知識。捂臉)
II. ffmpeg的rtmp推流
下面將介紹2種ffmpeg推流的方式:命令行(cmd)和代碼(code)。
命令行(CMD)推流
ffmpeg cmd的參數(shù)實在太多,因此這里只介紹基礎(chǔ)的、以及筆者了解的跟讀者們做一個分享。
首先,來看一個將本地視頻文件推流到服務(wù)器的最基礎(chǔ)的命令:
ffmpeg -i ${input_video} -f flv rtmp://${server}/live/${streamName}
-
-i
:表示輸入視頻文件,后跟視頻文件路徑/URL。 -
-f
:強制ffmpeg采用某種格式,后跟對應(yīng)的格式。
上文有提到,RTMP一般用flv流數(shù)據(jù),因此多設(shè)置-f flv
。
接著,另一個基本需求,就是在推流的時候希望不要加上音頻,這個也好實現(xiàn):
ffmpeg -i ${input_video} -vcodec copy -an -f flv rtmp://${server}/live/${streamName}
-
-vcodec
:指定視頻解碼器,v
是視頻video
,codec
就是解碼器,后跟解碼器名稱,copy
表示不作解碼; -
-acodec
:指定音頻解碼器,同理,a
是audio
,后跟解碼器名稱。an
代表acodec none
就是去掉音頻的意思。
關(guān)于a/v
的寫法很多,除了上面介紹的,還有-c:v copy -c:a copy
等。
再有其他的需求,讀者可自行Google。
代碼(code)推流
ffmpeg的c++代碼推流,網(wǎng)上也是一搜一大堆。筆者推薦雷神的最簡單的基于FFmpeg的推流器(以推送RTMP為例),能夠滿足基礎(chǔ)的推流需求。但是筆者在實際應(yīng)用場景時遇到過幾個case,最后總結(jié)得到一份相對魯棒可用的code segment:
AVFormatContext *mp_ifmt_ctx = nullptr;
AVFormatContext *mp_ofmt_ctx = nullptr;
uint64_t start_timestamp; // 獲取得到第一幀的時間
// ...
?
int pushStreaming(AVPacket *pkt, int frm_cnt) {
// 做篩選:因為實際源視頻文件可能包括多個音視頻碼流,這里只選取一路視頻流m_vs_index和一路音頻流m_as_index。
if (pkt->stream_index == m_vs_index || pkt->stream_index == m_as_index) {
// 沒有pts的視頻數(shù)據(jù),如未解碼的H.264裸流,需要重新計算其pts。
if (pkt->pts == AV_NOPTS_VALUE) {
AVRational time_base = mp_ifmt_ctx->streams[m_vs_index]->time_base;
// Duration between 2 frames (us)
int64_t calc_duration = (double)AV_TIME_BASE /
av_q2d(mp_ifmt_ctx->streams[m_vs_index]->r_frame_rate);
// Reset Parameters
pkt->pts = (double)(frm_cnt * calc_duration) /
(double)(av_q2d(time_base) * AV_TIME_BASE);
pkt->dts = pkt->pts;
pkt->duration = (double)calc_duration /
(double)(av_q2d(time_base) * AV_TIME_BASE);
}
// 筆者在這里省去了delay操作,讀者可根據(jù)需求增加。該操作通過控制推流的速率來減輕推流服務(wù)器的壓力。
// if (pkt->stream_index == m_vs_index) {
// AVRational time_base = mp_ifmt_ctx->streams[m_vs_index]->time_base;
// AVRational time_base_q = {1, AV_TIME_BASE};
// int64_t pts_time = av_rescale_q(pkt->dts, time_base, time_base_q);
// int64_t now_time = av_gettime() - start_timestamp;
// if (pts_time > now_time) {
// av_usleep((unsigned int)(pts_time - now_time));
// }
// }
?
//計算延時后,重新指定時間戳
AVRational istream_base = mp_ifmt_ctx->streams[pkt->stream_index]->time_base;
AVRational ostream_base = mp_ofmt_ctx->streams[pkt->stream_index]->time_base;
pkt->pts = av_rescale_q_rnd(pkt->pts, istream_base, ostream_base,
(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
pkt->dts = av_rescale_q_rnd(pkt->dts, istream_base, ostream_base,
(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
pkt->pts = pkt->pts < 0 ? 0 : pkt->pts;
pkt->dts = pkt->dts < 0 ? 0 : pkt->dts;
pkt->duration = (int)av_rescale_q(pkt->duration, istream_base, ostream_base);
pkt->pos = -1;
if (pkt->pts < pkt->dts) {
return 1;
}
// 向推流服務(wù)器推送流數(shù)據(jù)
int ret = av_interleaved_write_frame(mp_ofmt_ctx, pkt);
if (ret < 0) {
return ret;
}
}
return 0;
}
III. 推流遇到的坑
筆者遇到過2個推流失敗的case:
- 推一個RTSP攝像頭的流數(shù)據(jù)時,一旦打開該攝像頭的音頻軌道就會在
av_interleaved_write_frame()
函數(shù)處出錯。(返回碼不記得了) - 使用英飛拓某幾款攝像頭推流時,總是失敗,返回碼顯示-33。
以上2個問題都成功定位問題所在:
- 操作人員在打開音頻軌道時總是同時打開2個音頻軌道,只要選擇關(guān)閉其中1個音頻,馬上就可以推流了;
- 這幾款攝像頭同時包括多個視頻碼流,似乎他們會通過RTSP同時把多個碼流同時傳過來;最后在代碼中強行過濾視音頻碼流,只保留一路視頻+一路音頻就可以了。
原因歸結(jié)為一個:flv格式至多只能包括一個視頻流和一個音頻流。
至此,筆者原以為視頻封裝格式就只是記錄了幾個無關(guān)緊要的參數(shù)的認知,完全崩塌。據(jù)筆者了解,MP4是可以同時包括多路碼流的??磥矶嗝襟w/流媒體技術(shù)的水還很深呢,還有很多很多地方需要筆者去學習、踩坑、總結(jié)呢!自勉自勉。
寫在后面
文章中有不嚴謹?shù)牡胤?,歡迎指摘。
?文章來源地址http://www.zghlxwxcb.cn/news/detail-742972.html
原文? 視頻和視頻幀:ffmpeg的RTMP推流 - 知乎?
★文末名片可以免費領(lǐng)取音視頻開發(fā)學習資料,內(nèi)容包括(FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)以及音視頻學習路線圖等等。
見下方!↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓文章來源:http://www.zghlxwxcb.cn/news/detail-742972.html
?
到了這里,關(guān)于視頻和視頻幀:ffmpeg的RTMP推流的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!