前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >协议转换之rtsp转rtmp推流

协议转换之rtsp转rtmp推流

作者头像
视界音你而不同
发布2020-07-14 16:54:52
3.9K0
发布2020-07-14 16:54:52
举报

近些年,每一年的风口,几乎都离不开音视频。2020年了,音视频依然很火,只不过是火的领域有所不同,从年初受疫情而爆发的视频会议,到后来为了大力发展网络经济的直播带货!不过呢,本篇我们暂时不讨论这个,咱们聊技术,说到直播,rtmp协议依然是主流!不过呢,音视频中,流媒体协议rtsp也占有很大份额!日常生活呢,我们也会遇到一些不同流媒体协议转换的问题!本篇记录实现一个rtsp转rtmp直播流的程序!闲言少叙,接下来置入主题。

需求

有一个摄像头,比如海康的监控摄像头,可以通过rtsp流的方式访问其视频画面!需要将其画面转换为rtmp协议,并实现直播!

实现思路

我们的程序,称之为rtsp2rtmp,使用该程序实现拉取摄像头rtsp视频流,并将rtmp视频流转换为rtmp视频流,然后推送到直播服务器,直播服务器采用nginx+rtmp_module的方式实现,环境搭建的步骤可以参考之前的文章:搭一个简单的直播平台,嗨起来

rtsp2rtmp,使用FFmpeg API来实现!

准备工作

  • visual studio(笔者使用vs2019)
  • FFmpeg sdk: 下载下载地址:
代码语言:javascript
复制
https://ffmpeg.zeranoe.com/builds/

具体实现步骤

  • 打开输入流上下文
代码语言:javascript
复制
  //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;

代码语言:javascript
复制
  //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;
    }
  }

  • 打开输出流上下文
代码语言:javascript
复制
  //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;
  • 读取输入流
代码语言:javascript
复制
    m_nRet = av_read_frame(m_pRtspAVFormatContext, &pkt);
    if (m_nRet < 0)
    {
      break;
    }
  • 写入输出流
代码语言:javascript
复制
    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传送门

代码语言:javascript
复制
https://github.com/mlfcjob/Rtsp2Rtmp.git

欢迎star,欢迎使用修改和提交!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-07-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 视界音你而不同 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云直播
云直播(Cloud Streaming Services,CSS)为您提供极速、稳定、专业的云端直播处理服务,根据业务的不同直播场景需求,云直播提供了标准直播、快直播、云导播台三种服务,分别针对大规模实时观看、超低延时直播、便捷云端导播的场景,配合腾讯云视立方·直播 SDK,为您提供一站式的音视频直播解决方案。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档