基于Android的手机推流

文章链接:http://www.liuschen.com

这个推流器卡了很久,基于大神的代码,但是编译的时候总是出错,ffmpeg用的版本是3.1.4,有些函数提示不用了。有些很顺利就找到了替代的函数,有些则郁闷了很久,也正因为此,才逼自己去理解其中的含义。

1.将java中获取到的字符串转换成c语言中的字符数组

strcpy(input_str,(*env)->GetStringUTFChars(env,input-jstr, NULL));

strcpy(output_str,(*env)->GetStringUTFChars(env,output-jstr, NULL));

2.初始化组件

av-register-all();

3.如果有网络,初始化

avformat-network-init();

4.打开输入函数ifmt-ctx为AVFormatContext对象,里面存储流以及参数信息

avformat-open-input(&ifmt-ctx, input-str, 0, 0)

5.将流的信息写入ifmt-ctx

avformat-find-stream-info(ifmt-ctx, 0)

6.获取输出流的AVFormatContext对象,计算出输出方式

avformat-alloc-output-context2(&ofmt-ctx, NULL, “flv”,output-str);

7.然后是流交换

需要将输入流对应的AVFormatContext中的信息写入到输出流中,AVFormatContext中有一个流数组,存储的流如音频流,视频流等等。

for (i = 0; i < ifmt_ctx->nb_streams; i++) {
    AVStream *in_stream = ifmt_ctx->streams[i];
    //通过id获取到对应解码器
    pCodec = avcodec_find_decoder(ofmt->video_codec);
	//avformat_new_stream会新建流并把新的流存储在对应streams数组中
    AVStream *out_stream = avformat_new_stream(ofmt_ctx, pCodec);
    if (!out_stream) {
        LOGE( "Failed allocating  output stream\n");
        ret = AVERROR_UNKNOWN;
        goto end;
     }
	//avcodec_copy_context,
     avcodec_parameters_copy(out_stream->codecpar,in_stream->codecpar);
     
     out_stream->codecpar->codec_tag = 0;
  }

avcodec_parameters_copy,这个是最坑爹的函数,原来大神的代码是avcodec_copy_context,我在avcodec.h中找到函数说的是

If you need to transfer the stream parameters from one codec context
 	to another, use an intermediate AVCodecParameters instance and the
 	avcodec_parameters_from_context() / avcodec_parameters_to_context()
 	functions.*/
attribute_deprecated
int avcodec_copy_context(AVCodecContext *dest, const AVCodecContext *src);

结果完全被误导了,结果就是avformat_write_header一直返回-22,参数异常

8.打开流

avio-open2(&ofmt-ctx->pb, output-str, AVIO-FLAG-WRITE,NULL,NULL);

9.写入头文件

avformat-write-header(ofmt-ctx, NULL);

10.往外写数据

while (1) {
      AVStream *in_stream, *out_stream;
      //Get an AVPacket
      ret = av_read_frame(ifmt_ctx, &pkt);
      if (ret < 0){
      LOGE( "av_read_frame_break%d---%s\n",ret,av_err2str(ret));
      break;
      }

      //FIX:No PTS (Example: Raw H.264)
      //Simple Write PTS,如果没有pts值得话需要设置,pts是显示时间戳,dts是解码是解码时间戳
      if(pkt.pts==AV_NOPTS_VALUE){
          //Write PTS
          LOGE( "AV_NOPTS_VALUE\n");
          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;
          LOGE( "time_base==%d\n",pkt.stream_index);
          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;
          LOGE( "new_time%lld-----pts_time%lld\n",now_time,pts_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];

      LOGE( "pkt.stream_index==%d\n",pkt.stream_index);

      /* copy packet */
      //Convert PTS/DTS
      LOGE("--------pkt.pts:%lld----pkt.dts:%lld----pkt.duration:%lld\n",pkt.pts,pkt.dts,pkt.duration);
      pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
      pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, 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;

      LOGE("pkt.pts:%lld----pkt.dts:%lld----pkt.duration:%lld\n",pkt.pts,pkt.dts,pkt.duration);
      //Print to Screen
      if(pkt.stream_index==videoindex){
          LOGE("Send %8d video frames to output URL\n",pkt.stream_index);
          frame_index++;
        }

	//写入数据,关键函数
      ret = av_write_frame(ofmt_ctx, &pkt);
      //ret = av_interleaved_write_frame(ofmt_ctx, &pkt);

      if (ret < 0) {
      LOGE( "Error muxing packet%d---%s\n",ret,av_err2str(ret));
          break;
      }
      av_packet_unref(&pkt);

  }
  //Write file trailer

11.写入文件尾,推流结束

av-write-trailer(ofmt-ctx);

12.最后关闭流,释放资源

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) {
      LOGE( "Error occurred.\n");
      return -1;
  }

PS:遇到的问题主要有两个,一个是avformat-write-header(ofmt-ctx, NULL)返回-22,一个是av_write_frame(ofmt_ctx, &pkt)返回-22,前者是由于copy参数的函数没有找对,后者则是由于AVFormatContext中的关系没有理清,导致音频缺失,写入数据时也没有筛选,导致后面音频帧的pts,和dts数据错误,写入数据时报参数异常。

参考:最简单的基于FFmpeg的推流器

Loading Disqus comments...
Table of Contents