FFmpeg 接收网络流并推送rtmp流到服务器

FFmpeg 接收网络流并推送rtmp流到服务器

    • FFmpeg接收网络流
    • FFmpeg推送rtmp流到服务器

由于项目一直在忙,刚抽出空来,在这期间身体有些不适,这段时间中有一段时间感觉自己有上班恐惧症,一到工作环境中,不自然的就会有压力、焦虑;最后去医院看了医生,把情况给医生说了之后,医生认为是内脏比较敏感,压力和焦虑会在身体上有所体现,开了些药,回到家后妻子也是一直在开导,现在已经好了,在这里要感谢我妻子的开导和陪伴。也希望大家工作顺利,有个好心情。言归正传,这里记录一下在开发Android NDK模式下使用FFempg开发音视频解码和转流功能。

先说第一部分吧,FFmpeg接收网络流,在开发前期并没有去使用网络流,而是使用的本地文件作为推流的源,大家也可以先试试推送本地文件,然后再推送网络流,如rtsp流等。如何编译FFmpeg就不介绍了,网络上自己找一下吧,教程挺多的;这里说一下接收网络流时调用ffmpeg的流程:

FFmpeg接收网络流

(1)第一步就是找到一个能够使用的rtsp流地址,由于公司内网有rtsp服务器,我这边比较方便获取,大家可以在网上自己找一些开放的rtsp流地址使用。也可以使用海康球机自带的rtsp流,这里给大家找了几个:

rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov
rtmp://58.200.131.2:1935/livetv/cctv1

(2)初始化FFmpeg封装器,初始化网络库,解析本地文件时不需要初始化网络库,由于这里拉取的是rtsp网络流,所以需要添加网络库。

av_register_all();
avformat_network_init();

(3)设置接收流的方式及参数,ffmpeg会默认使用udp去通信,想用tcp的同学需要注意,在参数设置中“rtsp_transport”为“tcp”。

int res = 0;
//打开文件,解封装文件头
//输入封装上下文
AVFormatContext* ictx = nullptr;
//设置rtsp协议延时最大值
AVDictionary *opts = nullptr;
av_dict_set(&opts, "rtsp_transport", "tcp", 0);
av_dict_set(&opts, "max_delay", "500", 0);
if ((res = avformat_open_input(&ictx, inUrl, nullptr, &opts)) != 0)
	return Error(res);

(4)获取音视频流输入信息,avformat_find_stream_info()该函数可以读取一部分视音频数据并且获得一些相关的信息。avformat_find_stream_info()的声明位于libavformat\avformat.h。 av_dump_format()打印关于输入或输出格式的详细信息,最后一个参数为0代表打印输入信息,1代表打印输出信息。

if ((res = avformat_find_stream_info(ictx, nullptr)) < 0)
    return Error(res);
av_dump_format(ictx, 0, inUrl, 0);

(5)创建的AVFormatContext结构体,并让ffmpeg推理出输出格式,这里指定了输出格式为"flv",倒数第二个参数也可以填写NULL,通过最后一个参数,让ffmpeg自己推导出flv。

    //创建输出上下文
 AVFormatContext* octx = nullptr;
 if ((res = avformat_alloc_output_context2(&octx, nullptr, "flv", outUrl) < 0))
     return Error(res);

(6)配置输出流

  for (int i = 0; i < ictx->nb_streams; ++i)
  {
      //创建输出流
      AVStream* out = avformat_new_stream(octx, ictx->streams[i]->codec->codec);
      if (out == nullptr)
      {
          printf("new stream error.\n");
          return -1;
      }
      //复制配置信息
      if ((res = avcodec_copy_context(out->codec, ictx->streams[i]->codec)) != 0)
          return Error(res);
      //out->codec->codec_tag = 0;//标记不需要重新编解码
  }
  av_dump_format(octx, 0, outUrl, 1);

第二部分就是使用ffmpeg推送rtmp流,这一部分重点在于重新设置视频流的时间戳、pts、dts,这一部分也是在网上参考的,非常感谢博主。

FFmpeg推送rtmp流到服务器

(1)打开IO输出并写入头文件

 res = avio_open(&octx->pb, outUrl, AVIO_FLAG_WRITE);
 if (octx->pb == nullptr)
     return Error(res);
 //写入头信息
 //avformat_write_header可能会改变流的timebase
 if ((res = avformat_write_header(octx, nullptr)) < 0)
     return Error(res);

(2)转化pts、dts、duration并推送rtmp流。

  long long  begintime = av_gettime();
  long long  realdts = 0;
  long long  caldts = 0;
  AVPacket pkt;
  m_thread_status = true;
  while (m_thread_status)
  {
      if ((res = av_read_frame(ictx, &pkt)) != 0)
          break;
      if(pkt.size <= 0)//读取rtsp时pkt.size可能会等于0
          continue;
      //转换pts、dts、duration
      pkt.pts = pkt.pts * av_q2d(ictx->streams[pkt.stream_index]->time_base) / av_q2d(octx->streams[pkt.stream_index]->time_base);
      pkt.dts = pkt.dts * av_q2d(ictx->streams[pkt.stream_index]->time_base) / av_q2d(octx->streams[pkt.stream_index]->time_base);
      pkt.duration = pkt.duration * av_q2d(ictx->streams[pkt.stream_index]->time_base) / av_q2d(octx->streams[pkt.stream_index]->time_base);
      pkt.pos = -1;//byte position in stream, -1 if unknown
      if ((res = av_interleaved_write_frame(octx, &pkt)) < 0)//推流,推完之后pkt的pts,dts竟然都被重置了!而且前面几帧还因为dts没有增长而返回-22错误
          Error(res);
      av_free_packet(&pkt);//回收pkt内部分配的内存
  }
  av_write_trailer(octx);//写文件尾

完整代码推送rtmp流到服务器

你可能感兴趣的:(音视频,ffmpeg,rtmp)