在我自己的自定义ffmpeg播放器应用程序中,我尝试复制ffplay的AVDictionary编解码器初始化代码,但我不明白它的作用。就是这样,但它看起来更像是对命令行提供的选项的过滤器,而不是选择选项并启用它们的代码。
int LIBAV_Video::check_stream_specifier(AVFormatContext *s, AVStream *st, const char *spec)
{
int ret = avformat_match_stream_specifier(s, st, spec);
if (ret < 0)
av_log(s, AV_LOG_ERROR, "Invalid stream specifier: %s.\n", spec);
return ret;
}
//////////////////////////////////////////////////////////////////////////////////////
AVDictionary* LIBAV_Video::filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id,
AVFormatContext *s, AVStream *st, AVCodec *codec)
{
AVDictionary *ret = NULL;
AVDictionaryEntry *t = NULL;
int flags = s->oformat ? AV_OPT_FLAG_ENCODING_PARAM
:
AV_OPT_FLAG_DECODING_PARAM;
char prefix = 0;
const AVClass *cc = avcodec_get_class();
if (!codec)
codec = s->oformat ? avcodec_find_encoder(codec_id)
:
avcodec_find_decoder(codec_id);
switch (st->codecpar->codec_type)
{
case AVMEDIA_TYPE_VIDEO:
prefix = 'v';
flags |= AV_OPT_FLAG_VIDEO_PARAM;
break;
case AVMEDIA_TYPE_AUDIO:
prefix = 'a';
flags |= AV_OPT_FLAG_AUDIO_PARAM;
break;
case AVMEDIA_TYPE_SUBTITLE:
prefix = 's';
flags |= AV_OPT_FLAG_SUBTITLE_PARAM;
break;
}
while (t = av_dict_get(opts, "", t, AV_DICT_IGNORE_SUFFIX))
{
char *p = strchr(t->key, ':');
/* check stream specification in opt name */
if (p)
switch (check_stream_specifier(s, st, p + 1))
{
case 1: *p = 0; break;
case 0: continue;
default: exit(1);
}
if (av_opt_find(&cc, t->key, NULL, flags, AV_OPT_SEARCH_FAKE_OBJ) ||
!codec ||
(codec->priv_class &&
av_opt_find(&codec->priv_class, t->key, NULL, flags,
AV_OPT_SEARCH_FAKE_OBJ)))
{
av_dict_set(&ret, t->key, t->value, 0);
}
else if (t->key[0] == prefix &&
av_opt_find(&cc, t->key + 1, NULL, flags,
AV_OPT_SEARCH_FAKE_OBJ))
{
av_dict_set(&ret, t->key + 1, t->value, 0);
}
if (p)
*p = ':';
}
return ret;
}
//////////////////////////////////////////////////////////////////////////////////////
AVDictionary** LIBAV_Video::setup_find_stream_info_opts(AVFormatContext *s, AVDictionary *codec_opts)
{
unsigned int i;
AVDictionary **opts;
if (!s->nb_streams)
return NULL;
opts = (AVDictionary **)av_mallocz_array(s->nb_streams, sizeof(*opts));
if (!opts)
{
av_log(NULL, AV_LOG_ERROR, "Could not alloc memory for stream options.\n");
return NULL;
}
for (i = 0; i < s->nb_streams; i++)
opts[i] = filter_codec_opts(codec_opts, s->streams[i]->codecpar->codec_id, s, s->streams[i], NULL);
return opts;
}
如果有人知道任何程序化参考或文档,以识别用于常见用例的选项,例如最小播放延迟,我们将不胜感激。
我发现需要解决两个问题才能在自定义代码中获得高质量的 ffmpeg libav 播放:1) 一系列 AVDictionary 设置似乎随每个库版本的不同而不同,以及 2) 来自 ffplay 的 AVFilter 图实现需要被复制。我怀疑需要 AVFilter Graph,因为它是在关键帧和非关键帧之间进行插值的逻辑,并且似乎对损坏的帧进行了某种类型的校正。
这是似乎适用于 ffmpeg 4.2.3 的 AVDictionary 设置,但似乎每个版本都不同,我不知道为什么:
av_dict_set(&mp_opts, "threads", "auto", 0); // if multi-threading is needed, do it
av_dict_set( &mp_opts, "refcounted_frames", "1", 0 ); // ffplay sets this
av_dict_set( &mp_opts, "sync", "video", 0 );
av_dict_set( &mp_opts, "fflags", "discardcorrupt", 0 );// sets flag in mp_format_context
switch (mp_av_player->m_stream_type)
{
case 0: // media file
av_dict_set( &mp_opts, "framerate", "24", 0 );
av_dict_set( &mp_opts, "scan_all_pmts", "1", 0 ); // ffplay uses this flag
break;
case 1: // usb cam
// av_dict_set(&mp_opts,"show_video_device_dialog","true",0);
if (usb_format)
{
// av_dict_set( &mp_opts, "max_delay", "500000", 0 ); // half second
// av_dict_set( &mp_opts, "rtbufsize", "106M", 0 ); // a half second of 1280x720x4
av_dict_set( &mp_opts, "fflags", "nobuffer", 0 );
char bsjnk[1024];
sprintf( bsjnk, "%dx%d", usb_format->m_max_width, usb_format->m_max_height );
av_dict_set( &mp_opts, "video_size", bsjnk, 0 );
//
sprintf( bsjnk, "%1.2f", usb_format->m_max_fps );
av_dict_set( &mp_opts, "framerate", bsjnk, 0 );
//
if (usb_format->m_pixelFormat.size() > 0)
{
av_dict_set( &mp_opts, "pixel_format", usb_format->m_pixelFormat.c_str(), 0 );
}
else if (usb_format->m_pixelFormat.size() > 0)
{
av_dict_set( &mp_opts, "vcodec", usb_format->m_vcodec.c_str(), 0 );
}
}
else
{
av_dict_set( &mp_opts, "video_size", "640x480", 0 );
av_dict_set( &mp_opts, "framerate", "30", 0 );
}
break;
case 2: // ip cam
// av_dict_set( &mp_opts, "flags2", "fast", 0 ); does this cause bad frames?
av_dict_set( &mp_opts, "max_delay", "100000", 0 );
av_dict_set( &mp_opts, "flags", "low_delay", 0 );
// av_dict_set( &mp_opts, "fflags", "flush_packets", 0 );
// av_dict_set( &mp_opts, "fflags", "nobuffer", 0 ); testing removing this
av_dict_set( &mp_opts, "rtsp_transport", "tcp", 0 );
av_dict_set( &mp_opts, "framerate", "29.97", 0 );
av_dict_set( &mp_opts, "allowed_media_types", "video", 0 );
break;
}