前面的博文中已经交代过,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中读取的视频包。如果不保存,这些视频包将会丢掉,同样会增加视频打开的时间。方法内部一些子方法,稍后会文件上传。经现场测试,主流的摄像机例如海康,英飞拓,中威等都可以用它来打开。