今天我們來ffmpeg api編程實現推流並測試效果
項目源碼
開發環境
Visual Studio 2015 + FFmpeg-3.2 + nginx服務器
開發過程
1.環境準備
首先在FFmpeg官網下載ffmpeg dev 和share sdk
下載解壓之後,打開文件夾目錄,拷貝dev中的include和lib目錄,shared中的bin目錄,然後創建一個項目目錄,將這三個目錄拷貝進去,方便我們編寫代碼時配置相對路徑。其中bin存放的都是dll動態庫文件,include存放的是頭文件,lib存放的是每一個dll對應的lib文件
然後創建src目錄用於我們的項目根目錄,我們將會使用Visual Studio在這個目錄下創建控制檯項目來編寫推流代碼。
項目創建完成之後,需要對AS做一些配置:
a.右鍵項目名打開屬性,在配置屬性的常規選項中的輸出目錄配置一個目錄用於存放我們編譯後的文件,這裏直接設置爲上邊創建的bin目錄
b.然後同樣在配置屬性下找到調試,設置工作目錄爲bin
然後在編譯時就會去這裏找動態庫文件
c.下一步在C/C++選項中找到常規,設置頭文件目錄
d.在鏈接器下配置lib目錄位置
這樣一來,VS的工作環境就搭建好了
2.代碼編寫
接下來正式開始寫代碼,我們這次的推流以本地文件爲源進行推,推流的過程大概是這麼幾步
1.ffmpeg初始化
av_register_all註冊所有封裝器和解封裝器以及協議,這個方法在前邊文章已經提到過
avformat_network_init初始化網絡相關的東西,推流必須用到這個
av_register_all();
avformat_network_init();
2.讀取源文件
avformat_open_input打開一個流讀取流的文件頭信息,如果沒有文件頭則無法讀取到正確的信息
avformat_find_stream_info 針對一些不包含頭信息的源(MPEG)很有效,調用防止無法讀取到文件的正確信息
avformat_open_input(&ictx, input, 0, &opts);
avformat_find_stream_info(ictx, 0);
3.推流的準備工作
源文件信息我們已經讀取到了,接下來開始準備推流,推流之前我們需要構造出幾個對象:
AVFormatContext:輸出格式上下文
AVStream:輸出流,有幾條stream就構造幾個
avcodec_parameters_copy的作用是將輸入流也就是源文件的parameter信息拷貝到輸出流的參數中
avformat_alloc_output_context2(&octx, 0, "flv", output)
avformat_new_stream(octx, ictx->streams[i]->codec->codec)
avcodec_parameters_copy(out->codecpar, ictx->streams[i]->codecpar)
4.開始推流
avio_open會初始化出一個AVIOContext對象,我們可以把他看作一個普通的io流對象,只不過區別在於它是向流媒體服務器寫入數據的
avformat_write_header是初始化推流的頭信息
av_read_frame是在一個循環中進行的,不斷的讀取源得到AVPacket,然後由av_interleaved_write_frame負責開始推
avio_open(&octx->pb, output, AVIO_FLAG_WRITE);
avformat_write_header(octx, 0);
av_read_frame(ictx, &pkt)
av_interleaved_write_frame(octx, &pkt);
推流的過程很簡單,不過其中涉及到一點比較重要的東西就是推流中的速度控制,我們可以設置一個時間基準,通過對比packet的pts和時間基準的大小來決定推送的速度,從而實現正常效果的推送,關鍵代碼如下
//推流每一幀數據
AVPacket pkt;
long long startTime = av_gettime();
for (;;) {
result = av_read_frame(ictx, &pkt);
if (result != 0) {
break;
}
//計算轉換時間戳pts dts
AVRational itime = ictx->streams[pkt.stream_index]->time_base;
AVRational otime = octx->streams[pkt.stream_index]->time_base;
pkt.pts = av_rescale_q_rnd(pkt.pts, itime, otime, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_NEAR_INF));
pkt.dts = av_rescale_q_rnd(pkt.pts, itime, otime, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_NEAR_INF));
pkt.duration = av_rescale_q_rnd(pkt.duration, itime, otime, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_NEAR_INF));
pkt.pos = -1;
//視頻幀推送速度
if (ictx->streams[pkt.stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
//獲取視頻的時間戳
AVRational rational = ictx->streams[pkt.stream_index]->time_base;
//從開始解碼到現在過去的時間,可以作爲推流進行的時間,用當前幀的pts做對比,進行同步
long long now = av_gettime() - startTime;
long long dts = 0;
//單位微秒
dts = pkt.dts*r2d(rational)*1000*1000;
//說明推送太快,等一等
if (dts > now) {
av_usleep(dts - now);
cout << "等待"<< dts - now<< endl;
}
else {
cout << "無需等待" << dts - now << endl;
}
}
result = av_interleaved_write_frame(octx, &pkt);
if (result < 0) {
return XError(result);
}
//av_packet_unref(&pkt);
}
總結
本篇文章簡單講述了ffmpeg實現推流的過程,推流服務器使用的是nginx,所以在開始推之前要確保nginx服務器已經開始,並且正確配置服務器的ip地址,推流之後我們可以通過vlc播放器測試推流效果。本文代碼比較簡要,具體可查看源碼項目源碼