ffmpeg api转rtsp到rtmp,推流到服务nignx-rtmp-module 和 srs
代码非原创,来自于B站千魅太子哥:https://www.bilibili.com/video/BV1jD4y1U7de
推流流程和《ffmpeg从入门到精通》流程相同
我的ffmpeg版本为:ffmpeg-N-100672-gf3f5ba0bf8-win64-gpl-shared-vulkan
在ffmpeg官网下载的
ide为visual studio 2019
推流测试结果:https://blog.csdn.net/m0_46144966/article/details/115409372
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <libavutil/time.h>
}
#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avutil.lib")
void netStreaming(const char* path, const char* outPath)
{
avformat_network_init();
AVFormatContext* pFormat = NULL;
AVDictionary* opt = NULL;
av_dict_set(&opt, "rtsp_transport", "tcp", 0);
av_dict_set(&opt, "max_delay", "550", 0);
int ret = avformat_open_input(&pFormat, path, NULL, &opt);
if (ret)
{
printf("avformat_open_input failed\n");
system("pause");
return;
}
printf("avformat_open_input success\n");
ret = avformat_find_stream_info(pFormat, NULL);
if (ret)
{
printf("avformat_find_stream_info failed\n");
system("pause");
return;
}
printf("avformat_find_stream_info success\n");
av_dump_format(pFormat, 0, path, 0);
AVFormatContext* out = NULL;
ret = avformat_alloc_output_context2(&out, 0, "flv", outPath);
if (ret < 0)
{
printf("avformat_alloc_output_context2 failed\n");
system("pause");
return;
}
printf("avformat_alloc_output_context2 success\n");
for (size_t i = 0; i < pFormat->nb_streams; ++i)
{
AVStream* outStream = avformat_new_stream(out, pFormat->streams[i]->codec->codec);
if (!outStream)
{
printf("avformat_new_stream failed\n");
system("pause");
return;
}
avcodec_parameters_copy(outStream->codecpar, pFormat->streams[i]->codecpar);
//配置
outStream->codecpar->codec_tag = 0;
}
av_dump_format(out, 0, outPath, 1);
//rtmp 推流
ret = avio_open(&out->pb, outPath, AVIO_FLAG_WRITE);
if (ret < 0)
{
printf("avio_open failed\n");
system("pause");
return;
}
printf("%d avio_open success\n", ret);
ret = avformat_write_header(out, 0);
if (ret < 0)
{
printf("avformat_write_header failed\n");
system("pause");
return;
}
printf("%d avformat_write_header success\n", ret);
//推流每一帧数据
AVPacket pkt;
//两种办法 1.GetTickCount DWORD
//2. long long starttime
long long StartTime = av_gettime();
int64_t old_dts = 0;
//pts 音视频
while (true)
{
ret = av_read_frame(pFormat, &pkt);
if (ret)
break;
std::cout << "pkt pts: " << pkt.pts << std::endl << std::flush;
//av_packet_unref(&pkt);
//开始计算pts dts
AVRational intime = pFormat->streams[pkt.stream_index]->time_base;
AVRational outtime = out->streams[pkt.stream_index]->time_base;
pkt.pts = av_rescale_q_rnd(pkt.pts, intime, outtime, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
pkt.dts = av_rescale_q_rnd(pkt.dts, intime, outtime, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
pkt.duration = av_rescale_q_rnd(pkt.duration, intime, outtime, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
pkt.pos = -1;
if (old_dts > pkt.dts)
{
continue;
}
old_dts = pkt.dts;
ret = av_interleaved_write_frame(out, &pkt);
/*if (pkt.stream_index == 0)
av_usleep(30);*/
if (pFormat->streams[pkt.stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
AVRational rt = pFormat->streams[pkt.stream_index]->time_base;
long long newtime = av_gettime() - StartTime;
long long dts = 0;
dts = pkt.dts * (1000 * 1000 * rt.num / rt.den);
if (dts > newtime)
{
av_usleep(dts - newtime);
}
}
if (ret < 0)
{
printf("%d av_interleaved_write_frame failed\n", ret);
system("pause");
return;
}
av_packet_unref(&pkt);
}
ret = av_write_trailer(out);
if (ret)
{
printf("av_write_trailer failed\n");
system("pause");
return;
}
printf("av_write_trailer success\n");
avformat_close_input(&pFormat);
}
int main()
{
printf("%s\n", avcodec_configuration());
//rtsp地址
const char* path = "rtsp://用户名:密码@192.168.1.108:554/cam/realmonitor?channel=1&subtype=0";
//配置流属性, sdk 版本特性, 输出流
const char* outPath = "rtmp://192.168.1.2/live/livestream";
netStreaming(path, outPath);
return 0;
}