FFmpeg菜雞互啄#第7篇#檔案/rtsp推流到rtmp
阿新 • • 發佈:2022-05-03
關鍵步驟
avformat_open_input//開啟輸入檔案/rtsp
avformat_find_stream_info////獲取音視訊流資訊
avformat_alloc_output_context2//建立輸出上下文
avformat_new_stream//建立輸出流
avcodec_copy_context//複製配置輸出流
avio_open//開啟io
avformat_write_header//寫入頭資訊
av_interleaved_write_frame//推流幀
av_write_trailer(octx);//寫檔案尾
大致過程:開啟(/配置)輸入、建立輸出(上下文/流)、配置輸出流、開啟輸出IO、迴圈推流。推流本地檔案的時候要計算一下pts、dts,並作延時推送。
Code
#include <stdio.h> extern "C" { #include "libavformatavformat.h" #include "libavutiltime.h" } #pragma comment(lib, "avformat.lib") #pragma comment(lib, "avutil.lib") #pragma comment(lib, "avcodec.lib") int Error(int res) { char buf[1024] = { 0 }; av_strerror(res, buf, sizeof(buf)); printf("error : %s.n", buf); return res; } int main(int argc, char* argv[]) { char* inUrl = "rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov";//可以是本地檔案 char* outUrl = "rtmp://123.207.71.137/live/test"; //初始化所有封裝器 av_register_all(); //初始化網路庫 avformat_network_init(); int res = 0; //開啟檔案,解封裝檔案頭 //輸入封裝上下文 AVFormatContext* ictx = NULL; //設定rtsp協議延時最大值 AVDictionary *opts = NULL; av_dict_set(&opts, "max_delay", "500", 0); if ((res = avformat_open_input(&ictx, inUrl, NULL, &opts)) != 0) return Error(res); //獲取音視訊流資訊 if ((res = avformat_find_stream_info(ictx, NULL)) < 0) return Error(res); av_dump_format(ictx, 0, inUrl, 0); //建立輸出上下文 AVFormatContext* octx = NULL; if ((res = avformat_alloc_output_context2(&octx, NULL, "flv", outUrl) < 0)) return Error(res); //配置輸出流 //遍歷輸入的AVStream for (int i = 0; i < ictx->nb_streams; ++i) { //建立輸出流 AVStream* out = avformat_new_stream(octx, ictx->streams[i]->codec->codec); if (out == NULL) { printf("new stream error.n"); return -1; } //複製配置資訊 if ((res = avcodec_copy_context(out->codec, ictx->streams[i]->codec)) != 0) return Error(res); //out->codec->codec_tag = 0;//標記不需要重新編解碼 } av_dump_format(octx, 0, outUrl, 1); //rtmp推流 //開啟io //@param s Used to return the pointer to the created AVIOContext.In case of failure the pointed to value is set to NULL. res = avio_open(&octx->pb, outUrl, AVIO_FLAG_WRITE); if (octx->pb == NULL) return Error(res); //寫入頭資訊 //avformat_write_header可能會改變流的timebase if ((res = avformat_write_header(octx, NULL)) < 0) return Error(res); long long begintime = av_gettime(); long long realdts = 0; long long caldts = 0; AVPacket pkt; while (true) { if ((res = av_read_frame(ictx, &pkt)) != 0) break; if(pkt.size <= 0)//讀取rtsp時pkt.size可能會等於0 continue; //轉換pts、dts、duration pkt.pts = pkt.pts * av_q2d(ictx->streams[pkt.stream_index]->time_base) / av_q2d(octx->streams[pkt.stream_index]->time_base); pkt.dts = pkt.dts * av_q2d(ictx->streams[pkt.stream_index]->time_base) / av_q2d(octx->streams[pkt.stream_index]->time_base); pkt.duration = pkt.duration * av_q2d(ictx->streams[pkt.stream_index]->time_base) / av_q2d(octx->streams[pkt.stream_index]->time_base); pkt.pos = -1;//byte position in stream, -1 if unknown //檔案推流計算延時 //av_usleep(30 * 1000); /*realdts = av_gettime() - begintime; caldts = 1000 * 1000 * pkt.pts * av_q2d(octx->streams[pkt.stream_index]->time_base); if (caldts > realdts) av_usleep(caldts - realdts);*/ if ((res = av_interleaved_write_frame(octx, &pkt)) < 0)//推流,推完之後pkt的pts,dts竟然都被重置了!而且前面幾幀還因為dts沒有增長而返回-22錯誤 Error(res); av_free_packet(&pkt);//回收pkt內部分配的記憶體 } av_write_trailer(octx);//寫檔案尾 system("pause"); return 0; }