协议转换之rtsp转rtmp推流

本文详细描述了如何使用FFmpegAPI将海康摄像头的RTSP视频流转换为RTMP协议进行直播,包括准备步骤、创建输入和输出流、设置编码参数以及写入RTMP头信息的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

实现思路

我们的程序,称之为rtsp2rtmp,使用该程序实现拉取摄像头rtsp视频流,并将rtmp视频流转换为rtmp视频流,然后推送到直播服务器,直播服务器采用nginx+rtmp_module的方式实现,

rtsp2rtmp,使用FFmpeg API来实现!

准备工作

  • visual studio
  • 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值