小白學習音視頻系列(三)
使用FFmpeg代碼推流
關注微信號:cpp手藝人,獲取更多文章
這一節主要是通過FFmpeg推流,主要就是代碼部分,我做了詳細的註解
這裏有幾個注意點還是需要注意下的
1.首先要記得開啓nginx rtmp服務
2.服務端口記得要開啓
3.ip地址需要換成你自己
4.當你推流成功之後,點擊bin目錄下的play.bat,同時注意下把play.bat裏面的ip地址換成你自己的,就會啓動ffplay.exe拉流
#include "common.h"
extern "C"
{
#include "libavformat/avformat.h"
#include "libavutil/time.h"
}
#include <iostream>
using std::cout;
using std::endl;
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avutil.lib")
#pragma comment(lib, "avcodec.lib")
int main(int argc, char *argv[])
{
char *in_url = "test.flv";
// 初始化所有封裝和解封裝 flv mp4 mov mp3
av_register_all();
// 初始化網絡庫
avformat_network_init();
///////////////////////////////////////////////////////////////////
// 輸入流打開文件,解封裝
// 輸入封裝上下文
AVFormatContext *ictx = NULL;
// 打開文件,解封文件頭
int ret_code = avformat_open_input(&ictx, in_url, 0, 0);
if (ret_code < 0) { return av_error(ret_code); }
// 獲取音視頻流信息,h264 flv
ret_code = avformat_find_stream_info(ictx, 0);
if (ret_code < 0) { return av_error(ret_code); }
cout << "---------------------------------" << endl;
av_dump_format(ictx, 0, in_url, 0);
cout << "---------------------------------" << endl;
///////////////////////////////////////////////////////////////////
// 輸出流
char *out_url = "rtmp://192.168.26.31/live";
AVFormatContext *octx = NULL;
ret_code = avformat_alloc_output_context2(&octx, 0, "flv", out_url);
if (NULL == octx) { return av_error(ret_code); }
for (int i = 0; i < ictx->nb_streams; ++i) {
// 創建輸出流
AVStream *out = avformat_new_stream(octx, ictx->streams[i]->codec->codec);
if (NULL == out) { return av_error(0); }
ret_code = avcodec_parameters_copy(out->codecpar, ictx->streams[i]->codecpar);
out->codec->codec_tag = 0;
}
cout << "---------------------------------" << endl;
av_dump_format(octx, 0, out_url, 1);
cout << "---------------------------------" << endl;
////////////////////////////////////////////////////////////////////////
//RTMP推流
ret_code = avio_open(&octx->pb, out_url, AVIO_FLAG_WRITE);
if (NULL == octx->pb) { return av_error(ret_code); }
// 寫入頭信息
ret_code = avformat_write_header(octx, 0);
if (ret_code < 0) { return av_error(ret_code); }
cout << "avformat_write_header" << endl;
AVPacket pkt;
int64_t last_pts = 0;
long long startTime = av_gettime();
for (;;) {
ret_code = av_read_frame(ictx, &pkt);
if (0 != ret_code) { break; }
// 跳過異常的pts
if (last_pts > pkt.pts) { continue; }
cout << pkt.pts << " " << std::flush;
// 計算轉換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;
last_pts = pkt.pts;
// 視頻幀推送速度
if (ictx->streams[pkt.stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
AVRational tb = ictx->streams[pkt.stream_index]->time_base;
// 過去的時間
long long now = av_gettime()-startTime;
long long dts = 0;
dts = pkt.dts * (1000 * 1000 * r2d(tb));
if (dts > now) { av_usleep(dts-now); }
}
ret_code = av_interleaved_write_frame(octx, &pkt);
// 注意出錯不用直接返回,否則推流會直接結束
if (ret_code < 0) { /*return av_error(ret_code);*/ }
}
getchar();
return 0;
}
github地址:https://github.com/MingYueRuYa/FFmpeg-RTMP
好,至此,ffmpeg推流結束,雖然走了點彎路,但是你還是會成功的。