博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ffmpeg 推rtsp/rtmp流
阅读量:380 次
发布时间:2019-03-04

本文共 12643 字,大约阅读时间需要 42 分钟。

参考雷神的

以及另一位实现,然后对比两种实现方式。

1、ffmpeg读rtsp/rtmp/内存数据再推流

  这部分主要是参考雷神的,在此基础上实现了读取rtsp/rtmp流后再推rtsp/rtmp流,也实现了读取内存数据再推流。其中rtsp可推h264/h265流。flv只能推送h264,暂时265不支持拓展。

从内存读取数据时,在调用avformat_open_input(&ifmt_ctx, NULL, NULL, NULL)之前,内存数据必须到位,不然会open失败,而且第一帧必须是IDR帧。

/* 使能直接读取内存数据然后进行推流 */#define  READ_FROME_MEM_BUFFER (1)/* 推流测试 可推rtsp/rtmp流*/int test_pusher_main( ){  AVOutputFormat *ofmt = NULL;  //Input AVFormatContext and Output AVFormatContext  AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;  AVPacket pkt;  const char *in_filename, *out_filename;  int ret, i;  int videoindex = -1;  int frame_index = 0;  int64_t start_time = 0;#ifndef READ_FROME_MEM_BUFFER  in_filename  = "rtsp://localhost:556/test";//输入URL(Input file URL)#else  Read_Data read_handle("cuc_ieschool.flv");#endif  out_filename = "rtmp://localhost:1936/live/livestream";//输出 URL(Output URL)[RTMP]  //out_filename = "rtp://233.233.233.233:6666";//输出 URL(Output URL)[UDP]  //avfilter_register_all();  av_log_set_level(AV_LOG_DEBUG);  avcodec_register_all();  av_register_all();  //Network  avformat_network_init();#ifdef READ_FROME_MEM_BUFFER  ifmt_ctx = avformat_alloc_context();  unsigned char* aviobuffer = (unsigned char *)av_malloc(32768);  AVIOContext* avio = avio_alloc_context(aviobuffer, 32768, 0, &read_handle, Read_Data::read_buffer, NULL, NULL);  /* Open an input stream and read the header. */  ifmt_ctx->pb = avio;  if (avformat_open_input(&ifmt_ctx, NULL, NULL, NULL) != 0)  {      printf("Couldn't open input stream.\n");      return -1;  }#else  //Input  if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0)  {      printf("Could not open input file.");      goto end;  }#endif  if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0)   {      printf("Failed to retrieve input stream information");      goto end;  }  for (i = 0; i
nb_streams; i++) if (ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { videoindex = i; break; } if (videoindex == -1) { printf("Didn't find a video stream.\n"); return -1; }#ifndef READ_FROME_MEM_BUFFER av_dump_format(ifmt_ctx, 0, in_filename, 0);#endif //Output avformat_alloc_output_context2(&ofmt_ctx, NULL, "flv", out_filename); //RTMP //avformat_alloc_output_context2(&ofmt_ctx, NULL, "mpegts", out_filename);//UDP if (!ofmt_ctx) { printf("Could not create output context\n"); ret = AVERROR_UNKNOWN; goto end; } ofmt = ofmt_ctx->oformat; for (i = 0; i < ifmt_ctx->nb_streams; i++) { //Create output AVStream according to input AVStream AVStream *in_stream = ifmt_ctx->streams[i]; AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec); if (!out_stream) { printf("Failed allocating output stream\n"); ret = AVERROR_UNKNOWN; goto end; } //Copy the settings of AVCodecContext ret = avcodec_copy_context(out_stream->codec, in_stream->codec); if (ret < 0) { printf("Failed to copy context from input to output stream codec context\n"); goto end; } out_stream->codec->codec_tag = 0; //if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) //{ //printf("###### set AV_CODEC_FLAG_GLOBAL_HEADER ######\n"); //out_stream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; //} } //Dump Format------------------ av_dump_format(ofmt_ctx, 0, out_filename, 1); //Open output URL if (!(ofmt->flags & AVFMT_NOFILE)) { ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE); if (ret < 0) { printf("Could not open output URL '%s'", out_filename); goto end; } } //Write file header ret = avformat_write_header(ofmt_ctx, NULL); if (ret < 0) { printf("Error occurred when opening output URL\n"); goto end; } start_time = av_gettime(); while (1) { AVStream *in_stream, *out_stream; //Get an AVPacket ret = av_read_frame(ifmt_ctx, &pkt); if (ret < 0) break; //FIX:No PTS (Example: Raw H.264) //Simple Write PTS if (pkt.pts == AV_NOPTS_VALUE) { //Write PTS AVRational time_base1 = ifmt_ctx->streams[videoindex]->time_base; //Duration between 2 frames (us) int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(ifmt_ctx->streams[videoindex]->r_frame_rate); //Parameters pkt.pts = (double)(frame_index*calc_duration) / (double)(av_q2d(time_base1)*AV_TIME_BASE); pkt.dts = pkt.pts; pkt.duration = (double)calc_duration / (double)(av_q2d(time_base1)*AV_TIME_BASE); } //Important:Delay if (pkt.stream_index == videoindex) { AVRational time_base = ifmt_ctx->streams[videoindex]->time_base; AVRational time_base_q = { 1, AV_TIME_BASE }; int64_t pts_time = av_rescale_q(pkt.dts, time_base, time_base_q); int64_t now_time = av_gettime() - start_time; if (pts_time > now_time) av_usleep(pts_time - now_time); } in_stream = ifmt_ctx->streams[pkt.stream_index]; out_stream = ofmt_ctx->streams[pkt.stream_index]; /* copy packet */ //Convert PTS/DTS pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)); pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)); pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base); pkt.pos = -1; //Print to Screen if (pkt.stream_index == videoindex) { printf("Send %8d video frames to output URL\n", frame_index); frame_index++; } //ret = av_write_frame(ofmt_ctx, &pkt); ret = av_interleaved_write_frame(ofmt_ctx, &pkt); if (ret < 0) { printf("Error muxing packet ret = %d\n", ret); char buf[128]; memset(buf, 0, 128); av_strerror(ret, buf, 128); printf("error: %s\n", buf); break; } av_free_packet(&pkt); } //Write file trailer av_write_trailer(ofmt_ctx); getchar();end:#ifdef READ_FROME_MEM_BUFFER av_freep(&avio->buffer); av_freep(&avio);#endif avformat_close_input(&ifmt_ctx); /* close output */ if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE)) avio_close(ofmt_ctx->pb); avformat_free_context(ofmt_ctx); if (ret < 0 && ret != AVERROR_EOF) { printf("Error occurred.\n"); return -1; } getchar(); return 0;}

2、直接填充ffmpeg的pack再推流

  参考第二篇博文,在参考的博文中只给出代码框架,细节需要自己实现。

这种方法实现的推流,配置的部分输出码流参数并不起效,如码率、帧率及分辨率,这些参数的实际推流输出会等于输入的。
  主要是判别h264的I帧,若此处判别失败则推流成功但是客户端无法解析出。
这种方法也适合推rtsp/rtmp流,支持h264/h265码流,音频支持部分暂未实现。

/* 直接读buffer数据后填充AVPack结构体,然后推流 */int test_pusher_rtmp_main2(){  int ret;  char buf[1024] = "";  AVOutputFormat *ofmt = NULL;  AVFormatContext *ofmt_ctx = NULL;  AVStream *out_stream = NULL;  AVPacket pkt = { 0 };  AVPacket in_pkt = { 0 };   AVDictionary *dic = NULL;  AVCodecContext *avctx = NULL;  avcodec_register_all();  av_register_all();  av_log_set_level(AV_LOG_DEBUG);  avformat_network_init();  int frame_cnt = 0;  //Read_Data read_handle("test.h265");  const int buffer_size = 1024 * 1024 * 3;  uint8_t* frame_buf = new uint8_t[buffer_size];  memset(frame_buf, 0, buffer_size);  //const char* in_filename = "cuc_ieschool.flv";  const char* in_filename = "test.h264";  //const char* in_filename = "4K.h264";  AVFormatContext *ifmt_ctx = NULL;  int videoindex = -1;  //Input  if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0)  {      printf("Could not open input file.");      memset(buf, 0 ,1024);      av_strerror(ret, buf, 1024);      printf("Couldn't open file %s with error[%s]\n", in_filename, buf);      return -1;  }  if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0)  {      printf("Failed to retrieve input stream information");      return -1;  }  int i = 0;  for (i = 0; i < ifmt_ctx->nb_streams; i++)  if (ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)  {      videoindex = i;      break;  }  if (videoindex == -1)  {      printf("Didn't find a video stream.\n");      return -1;  }  const char* out_filename = "rtmp://localhost:1936/live/livestream";  avformat_alloc_output_context2(&ofmt_ctx, NULL, "flv", out_filename);  //const char* out_filename = "rtsp://localhost:556/livestream";  /* allocate the output media context */  //avformat_alloc_output_context2(&ofmt_ctx, NULL, "rtsp", out_filename);  if (!ofmt_ctx)   {      printf("Could not deduce output format from file extension: using AVI.\n");      //avformat_alloc_output_context2(&ofmt_ctx, NULL, "avi", out_filename);      goto exit;  }  if (!ofmt_ctx)   {      goto exit;  }  ofmt = ofmt_ctx->oformat;  out_stream = avformat_new_stream(ofmt_ctx, NULL);  if (!out_stream)  {      printf("Failed allocating output stream\n");      ret = AVERROR_UNKNOWN;      goto exit;  }  avctx = out_stream->codec;  avctx->codec_type = AVMEDIA_TYPE_VIDEO;  ret = av_dict_set(&dic, "qp", "0", 0);  ret = av_dict_set(&dic, "bufsize", "1524000", 0);  if (ret < 0)  {      printf("av_dict_set  bufsize fail \n");      return -1;  }  ///*此处,需指定编码后的H264数据的分辨率、帧率及码率*/  /* avctx->codec_id   = AV_CODEC_ID_H264;   avctx->codec_type = AVMEDIA_TYPE_VIDEO;   avctx->bit_rate = 2000000;   avctx->width = 1280;   avctx->height = 720;   avctx->time_base.num = 1;   avctx->time_base.den = 25;   avctx->qmin = 10;   avctx->qmax = 60;   avctx->codec_tag = 0;   avctx->has_b_frames = 0;*/ /* 实际上并不起效,但是必须设置否则启动不起来  */  out_stream->codecpar->codec_id = AV_CODEC_ID_H264;  out_stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;  out_stream->codecpar->width = 1920;  out_stream->codecpar->height = 1080;  out_stream->codecpar->codec_tag = 0;  out_stream->codecpar->bit_rate = 8000000;  out_stream->codecpar->format = AV_PIX_FMT_YUV420P;  //out_stream->r_frame_rate = (AVRational){ 25, 1 };    /* print output stream information*/  av_dump_format(ofmt_ctx, 0, out_filename, 1);  if (!(ofmt->flags & AVFMT_NOFILE))  {      ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);      if (ret < 0)      {          printf("Could not open output file '%s'\n", out_filename);          goto exit;      }      printf("Open output file success!\n");  }  //写文件头(Write file header)  ret = avformat_write_header(ofmt_ctx, NULL);  if (ret < 0)  {      printf("write avi file header failed\n");      goto exit;  }  while (1)  {      av_init_packet(&pkt);      ret = av_read_frame(ifmt_ctx, &in_pkt);      if (ret < 0)      {          av_init_packet(&pkt);          avformat_close_input(&ifmt_ctx);          ret = avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL);          if (ret < 0)          {              return -1;          }          if (av_read_frame(ifmt_ctx, &in_pkt) != 0)          {              return -1;          }                }      memcpy(frame_buf, in_pkt.data, in_pkt.size);      printf("size = %d \n", in_pkt.size);       if (in_pkt.size <= 4)      {          continue;          av_packet_unref(&pkt);      }       int frame_type = H264_GetFrameType(frame_buf, in_pkt.size, 4);       if (frame_type == H264_FRAME_I || frame_type == H264_FRAME_SI)      {           // 判断该H264帧是否为I帧          printf("####I frame ######\n");          pkt.flags |= AV_PKT_FLAG_KEY;          //getchar();      }      else      {          frame_type = H264_GetFrameType((unsigned char*)frame_buf, in_pkt.size, 3);          if (frame_type == H264_FRAME_I || frame_type == H264_FRAME_SI)          {              // 判断该H264帧是否为I帧              printf("11111####I frame ######\n");              pkt.flags |= AV_PKT_FLAG_KEY;          }          else          {              /* p frame*/              pkt.flags = 0;          }          }      //pkt.dts = pkt.pts = AV_NOPTS_VALUE;      pkt.dts = pkt.pts = frame_cnt;      pkt.size = in_pkt.size; /*帧大小*/      pkt.data = frame_buf; /*帧数据*/      if (!pkt.data)       {          printf("no data\n");          continue;      }      //写入(Write)      ret = av_interleaved_write_frame(ofmt_ctx, &pkt);      if (ret < 0)       {          av_strerror(ret, buf, 1024);      }      if (frame_cnt % 2)          printf("Send frame[%d] \n", frame_cnt);      frame_cnt++;      av_packet_unref(&pkt);      memset(frame_buf, 0, buffer_size);      av_usleep(40000);  }  av_write_trailer(ofmt_ctx);exit:  if (frame_buf)  {      delete[] frame_buf;      frame_buf = NULL;  }  /* close output */  if (ofmt_ctx && !(ofmt_ctx->oformat->flags & AVFMT_NOFILE))      avio_close(ofmt_ctx->pb);   avformat_free_context(ofmt_ctx);  return 0;}

3、

在ubuntu14上实现的demo。

2020-1-16补充:完整的推音视频的

转载地址:http://vtoe.baihongyu.com/

你可能感兴趣的文章