近些年,每一年的风口,几乎都离不开音视频。音视频依然很火,只不过是火的领域有所不同,从年初受疫情而爆发的视频会议,到后来为了大力发展网络经济的直播带货!不过呢,本篇我们暂时不讨论这个,咱们聊技术,说到直播,rtmp协议依然是主流!不过呢,音视频中,流媒体协议rtsp也占有很大份额!日常生活呢,我们也会遇到一些不同流媒体协议转换的问题!本篇记录实现一个rtsp转rtmp直播流的程序!闲言少叙,接下来置入主题。
需求
有一个摄像头,比如海康的监控摄像头,可以通过rtsp流的方式访问其视频画面!需要将其画面转换为rtmp协议,并实现直播!
实现思路
我们的程序,称之为rtsp2rtmp,使用该程序实现拉取摄像头rtsp视频流,并将rtmp视频流转换为rtmp视频流,然后推送到直播服务器,直播服务器采用nginx+rtmp_module的方式实现,环境搭建的步骤可以参考之前的文章:搭一个简单的直播平台,嗨起来。
rtsp2rtmp,使用FFmpeg API来实现!
【相关学习资料推荐,点击下方链接免费报名,先码住不迷路~】
【免费分享】我整理了一些比较好的面试题、学习资料、教学视频和学习路线图,资料包括(C/C++,Linux,FFmpeg webRTC rtmp hls rtsp ffplay srs 等等)有需要的可以点击加群领取~
准备工作
- visual studio(笔者使用vs2019)
- FFmpeg sdk: 下载下载地址:
https://ffmpeg.zeranoe.com/builds/
具体实现步骤
- 打开输入流上下文
//av_register_all();
avformat_network_init();
m_strRtspUrl = rtspUrl;
m_strRtmpUrl = rtmpUrl;
m_pRtspAVFormatContext = NULL;
m_nRet = 0;
m_nVideoIndex = -1;
m_nAudioIndex = -1;
memset(errBuf, 0, 1024);
// 1. 申请输出流上下文
m_pRtmpAVFormatContext = NULL;
avformat_alloc_output_context2(&m_pRtmpAVFormatContext, NULL, "flv", m_strRtmpUrl.c_str());
if (!m_pRtmpAVFormatContext)
{
std::cout << "avformat_alloc_output_contex2 failed!" << std::endl;
return false;
}
std::cout << "[" << m_strRtmpUrl.c_str() << "] avformat_alloc_output_context2 success " << std::endl;
//1.打开输入流
m_nRet = avformat_open_input(&m_pRtspAVFormatContext, m_strRtspUrl.c_str(), 0, 0);
if (m_nRet != 0)
{
std::cout << "open " << m_strRtspUrl.c_str() << "error!" << std::endl;
return m_nRet;
}
std::cout << "open " << m_strRtspUrl.c_str() << "success !" << std::endl;
// 2.获得流信息
m_nRet = avformat_find_stream_info(m_pRtspAVFormatContext, 0);
if (m_nRet < 0)
{
std::cout << "avformat_find_stream_info failed" << std::endl;
return m_nRet;
}
std::cout << "avformat_find_stream_info success" << std::endl;
av_dump_format(m_pRtspAVFormatContext, 0, m_strRtspUrl.c_str(),0);
for (int i = 0; i < m_pRtspAVFormatContext->nb_streams; i++)
{
if (m_pRtspAVFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
m_nVideoIndex = i;
}
if (m_pRtspAVFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
{
m_nAudioIndex = i;
}
}
打开输出流上下文
//3. 申请输出AVStream(rtmp)
const AVCodec* outCodec = avcodec_find_decoder(m_pRtspAVFormatContext->streams[m_nVideoIndex]->codecpar->codec_id);
m_pRtmpAVStream = avformat_new_stream(m_pRtmpAVFormatContext, outCodec);
if (!m_pRtmpAVStream)
{
std::cout << "[rtmp] Failed allocating output stream!" << std::endl;
return m_nRet;
}
std::cout << "[rtmp] avformat_new_stream success!" << std::endl;
//4. stream信息复制
AVCodecContext* rtmpOutCodec = avcodec_alloc_context3(outCodec);
m_nRet = avcodec_parameters_to_context(rtmpOutCodec, m_pRtspAVFormatContext->streams[m_nVideoIndex]->codecpar);
if (m_nRet < 0)
{
std::cout << "[rtmp] avcodec_paramertes_to_context failed " << std::endl;
return m_nRet;
}
std::cout << "[rtmp] avcodec_paramertes_to_context success" << std::endl;
m_nRet = avcodec_parameters_from_context(m_pRtmpAVStream->codecpar, rtmpOutCodec);
if (m_nRet < 0)
{
std::cout << "[rtmp] avcodec_parameters_from_context failed";
return m_nRet;
}
if (!(m_pRtmpAVFormatContext->flags & AVFMT_NOFILE)) {
m_nRet = avio_open2(&m_pRtmpAVFormatContext->pb, m_strRtmpUrl.c_str(), AVIO_FLAG_WRITE, NULL, NULL);
if (m_nRet < 0) {
av_strerror(m_nRet, errBuf, 1024);
//std::cout << "avio_open2 failed: " << errBuf << std::endl;
printf("avio_open2 failed: %s\n", errBuf);
return m_nRet;
}
}
//5. 写rtmp头信息
m_nRet = avformat_write_header(m_pRtmpAVFormatContext, NULL);
if (m_nRet < 0)
{
std::cout << "[rtmp] avformat_write_header failed" << std::endl;
av_strerror(m_nRet, errBuf, 1024);
printf("errBuf: %s\n", errBuf);
//std::cout << err << std::endl;
return m_nRet;
}
std::cout << "[rtmp] avformat_write_header success!" << std::endl;
读取输入流
m_nRet = av_read_frame(m_pRtspAVFormatContext, &pkt);
if (m_nRet < 0)
{
break;
}
写入输出流
rtspStream = m_pRtspAVFormatContext->streams[pkt.stream_index];
rtmpStream = m_pRtmpAVFormatContext->streams[pkt.stream_index];
pkt.pts = av_rescale_q_rnd(pkt.pts, rtspStream->time_base, rtmpStream->time_base, AVRounding(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
pkt.dts = av_rescale_q_rnd(pkt.dts, rtspStream->time_base, rtmpStream->time_base, AVRounding(AV_ROUND_NEAR_INF |AV_ROUND_PASS_MINMAX));
pkt.duration = av_rescale_q(pkt.duration, rtspStream->time_base, rtmpStream->time_base);
pkt.pos = -1;
m_nRet = av_interleaved_write_frame(m_pRtmpAVFormatContext, &pkt);
if (m_nRet < 0)
{
break;
}
av_packet_unref(&pkt);
github传送门
https://github.com/mlfcjob/Rtsp2Rtmp.git
欢迎star,欢迎使用修改和提交!