ffmpeg 打开视频流太慢(下)

前面的博文中已经交代过,ffmpeg打开视频慢主要是因为av_find_stream_info 耗时久。下面给出重写查找音视频stream info的一段代码,用来替代av_find_stream_info 。

 

static int avformatFindStreamInfo(AVFormatContext *ic, AVDictionary **options,std::vector> &packets,int timeout = 5)
  {
   int i, count, ret = 0, j;
   int64_t read_size;
   AVStream *st;
   // AVPacket  *pkt;
   AVPacket packet;
   int64_t old_offset  = 0;
   int64_t last_offset  = 0;
   bool firstKeyPacket = false;
   int tryCount = 0;
   int orig_nb_streams = ic->nb_streams;
   int flush_codecs    = ic->probesize > 0;
   int64_t startTime = av_gettime();
   int64_t max_analyze_duration = ic->max_analyze_duration2;
   for (i = 0; i < ic->nb_streams; i++) {
    const AVCodec *codec;
    AVDictionary *thread_opt = NULL;
    st = ic->streams[i];

    if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
     st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) {
      if (!st->codec->time_base.num)
       st->codec->time_base = st->time_base;
    }

    if (!st->parser && !(ic->flags & AVFMT_FLAG_NOPARSE)) {
     st->parser = av_parser_init(st->codec->codec_id);
     if (st->parser) {
      if (st->need_parsing == AVSTREAM_PARSE_HEADERS) {
       st->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
      } else if (st->need_parsing == AVSTREAM_PARSE_FULL_RAW) {
       st->parser->flags |= PARSER_FLAG_USE_CODEC_TS;
      }
     } else if (st->need_parsing) {

     }
    }
    codec = avcodec_find_decoder(st->codec->codec_id);

    av_dict_set(options ? &options[i] : &thread_opt, "threads", "1", 0);
    if (!has_codec_parameters(st, NULL) && st->request_probe <= 0) {
     if (codec && !st->codec->codec)
      if (avcodec_open2(st->codec, codec, options ? &options[i] : &thread_opt) < 0)
       av_log(ic, AV_LOG_WARNING,
       "Failed to open codec in av_find_stream_info\n");
    }
    if (!options)
     av_dict_free(&thread_opt);
   }

   for (i = 0; i < ic->nb_streams; i++) {
    ic->streams[i]->info->fps_first_dts = AV_NOPTS_VALUE;
    ic->streams[i]->info->fps_last_dts  = AV_NOPTS_VALUE;
   }

   count = 0;
   read_size = 0;
   bool saveAudioFlag = false;
   shared_ptr lastAudioPacket[AudioPacketsNum] = {nullptr};
   
   for (;;) {
    if (ff_check_interrupt(&ic->interrupt_callback)) {
     ret = AVERROR_EXIT;
     av_log(ic, AV_LOG_DEBUG, "interrupted\n");
     break;
    }
    if(av_gettime() - startTime > timeout * 1000 * 1000)
    {
     ret = -1;
     break;
    }
    
    for (i = 0; i < ic->nb_streams; i++) {
     st = ic->streams[i];
     if (!has_codec_parameters(st, NULL,firstKeyPacket))
      break;

     if (st->parser && st->parser->parser->split &&
      !st->codec->extradata)
      break;
    }
    if (i == ic->nb_streams && ic->nb_streams > 0)
    {
     break;
    }

    shared_ptr pkt((AVPacket*)av_malloc(sizeof(AVPacket)), [&](AVPacket *p) { av_free_packet(p); av_freep(&p); });
    av_init_packet(pkt.get());
    old_offset  = avio_tell(ic->pb);
    ret = av_read_frame(ic, pkt.get()); 
    if (ret == AVERROR(EAGAIN))
    {
     std::this_thread::sleep_for(std::chrono::milliseconds(100));
     continue;
    }
    if (ret < 0) {
     
     break;
    }

    if(ic->nb_streams == 0) return -1;
    auto codeType = ic->streams[pkt->stream_index]->codec->codec_type;
    if(codeType == AVMEDIA_TYPE_VIDEO && pkt->flags&AV_PKT_FLAG_KEY)
    {
     firstKeyPacket = true;
    }
    if(!firstKeyPacket && codeType == AVMEDIA_TYPE_VIDEO)
    {
     continue;
    }

    shared_ptr pkt1((AVPacket*)av_malloc(sizeof(AVPacket)), [&](AVPacket *p) { av_free_packet(p); av_freep(&p); });
    av_init_packet(pkt1.get()); 
    av_copy_packet(pkt1.get(),pkt.get());
    packets.push_back(pkt1);

    st = ic->streams[pkt->stream_index];
    if (!(st->disposition & AV_DISPOSITION_ATTACHED_PIC))
     read_size += pkt->size;

    if (pkt->dts != AV_NOPTS_VALUE && st->codec_info_nb_frames > 1) {
     
     if (st->info->fps_last_dts != AV_NOPTS_VALUE &&
      st->info->fps_last_dts >= pkt->dts) {

       st->info->fps_first_dts =
        st->info->fps_last_dts  = AV_NOPTS_VALUE;
     }
     
     if (st->info->fps_last_dts != AV_NOPTS_VALUE &&
      st->info->fps_last_dts_idx > st->info->fps_first_dts_idx &&
      (pkt->dts - st->info->fps_last_dts) / 1000 >
      (st->info->fps_last_dts     - st->info->fps_first_dts) /
      (st->info->fps_last_dts_idx - st->info->fps_first_dts_idx)) {

       st->info->fps_first_dts =
        st->info->fps_last_dts  = AV_NOPTS_VALUE;
     }

     
     if (st->info->fps_first_dts == AV_NOPTS_VALUE) {
      st->info->fps_first_dts     = pkt->dts;
      st->info->fps_first_dts_idx = st->codec_info_nb_frames;
     }
     st->info->fps_last_dts     = pkt->dts;
     st->info->fps_last_dts_idx = st->codec_info_nb_frames;
    }

    if (st->parser && st->parser->parser->split && !st->codec->extradata) {
     int i = st->parser->parser->split(st->codec, pkt->data, pkt->size);
     if (i > 0 && i < FF_MAX_EXTRADATA_SIZE) {
      if (ff_alloc_extradata(st->codec, i))
       return AVERROR(ENOMEM);
      memcpy(st->codec->extradata, pkt->data,
       st->codec->extradata_size);
     }
    }
    auto decodeRet = try_decode_frame(ic, st, pkt.get(),
     (options && i < orig_nb_streams) ? &options[i] : NULL);
    st->codec_info_nb_frames++;

   }

   for (i = 0; i < ic->nb_streams; i++) {
    st = ic->streams[i];
    avcodec_close(st->codec);
   }
   for (i = 0; i < ic->nb_streams; i++)
   {
    st = ic->streams[i];
    if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)
    {
     if (st->codec->codec_id == AV_CODEC_ID_RAWVIDEO && !st->codec->codec_tag && !st->codec->bits_per_coded_sample) {
      uint32_t tag= avcodec_pix_fmt_to_codec_tag(st->codec->pix_fmt);
      
     }

     if (!st->r_frame_rate.num) {
      if (    st->codec->time_base.den * (int64_t) st->time_base.num
       <= st->codec->time_base.num * st->codec->ticks_per_frame * (int64_t) st->time_base.den) {
        st->r_frame_rate.num = st->codec->time_base.den;
        st->r_frame_rate.den = st->codec->time_base.num * st->codec->ticks_per_frame;
      } else {
       st->r_frame_rate.num = st->time_base.den;
       st->r_frame_rate.den = st->time_base.num;
      }
     }
    }
    else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO)
    {
     if (!st->codec->bits_per_coded_sample)
      st->codec->bits_per_coded_sample =
      av_get_bits_per_sample(st->codec->codec_id);
     // set stream disposition based on audio service type
     switch (st->codec->audio_service_type) {
     case AV_AUDIO_SERVICE_TYPE_EFFECTS:
      st->disposition = AV_DISPOSITION_CLEAN_EFFECTS;
      break;
     case AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED:
      st->disposition = AV_DISPOSITION_VISUAL_IMPAIRED;
      break;
     case AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED:
      st->disposition = AV_DISPOSITION_HEARING_IMPAIRED;
      break;
     case AV_AUDIO_SERVICE_TYPE_COMMENTARY:
      st->disposition = AV_DISPOSITION_COMMENT;
      break;
     case AV_AUDIO_SERVICE_TYPE_KARAOKE:
      st->disposition = AV_DISPOSITION_KARAOKE;
      break;
     }
    }
   }

find_stream_info_err:

   for (i = 0; i < ic->nb_streams; i++) {
    st = ic->streams[i];
    if (ic->streams[i]->codec->codec_type != AVMEDIA_TYPE_AUDIO)
     ic->streams[i]->codec->thread_count = 0;
    if (st->info)
     av_freep(&st->info->duration_error);
    av_freep(&ic->streams[i]->info);
   }
   if(ic->nb_streams == 0)
    ret = -1;
   return ret;
  }

这段代码的宗旨是找到音视频必要的信息后,立即返回。方法参数里传入vector> packets 是为了保存在查找的工程中已经从context中读取的视频包。如果不保存,这些视频包将会丢掉,同样会增加视频打开的时间。方法内部一些子方法,稍后会文件上传。经现场测试,主流的摄像机例如海康,英飞拓,中威等都可以用它来打开。

转载地址:http://blog.sina.com.cn/s/blog_81c5781c0102wh29.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值