Reputation: 504
As I get decoding artefacts with a custom ffmpeg player application (ffmpeg stream decoding - artefacts when not using ffplay) while ffplay shows a perfect image without artefacts, I need to implement my player in the same way ffplay does.
ffplay.c uses an
AVDictionary *codec_opts;
which is initialized (correct me) in cmdutils.c. I need this dictionary to do the codec initialization analogue to ffplay (line 2615f.), but I dont understand how it is created. Is there a way to
a) include cmdutils in the same way ffplay does - linking to cmdutils.o in the end gives me in the end
:-1: error: /.../cmdutils.o: undefined reference to symbol 'swresample_version@@LIBSWRESAMPLE_3'
although I included libswresample using (QT):
LIBS +=-L/usr/local/lib -lavformat -lswscale -lswresample -lavutil -lavcodec
b) get access to the AVDictionary codec_opts in any other way?
Upvotes: 0
Views: 3796
Reputation: 786
In my own custom ffmpeg player application, I tried copying ffplay's AVDictionary codec initializing code, but I don't understand what it does. Here it is, but it looks like more of a filter to command line provided options than code that selects options and enables them.
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;
}
If anyone knows of any programmatic reference or documentation on identifying which options to use for common use cases, such as minimal playback latency, that would be appreciated.
I've found two issues that need to be addressed to get quality ffmpeg libav playback in custom code: 1) a series of AVDictionary settings that seem to vary with each library release, and 2) the AVFilter graph implementation from ffplay needs to be duplicated. I suspect the AVFilter Graph is needed because it is the logic that interpolates between key and non-key frames as well as appears to have some type of correction for corrupt frames.
Here's the AVDictionary setup that seems to work well for ffmpeg 4.2.3, but seems to vary which each release, and I don't know why:
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;
}
Upvotes: 1
Reputation: 1988
Yes, you can create and set AVDictionary *codec_opts
for your own implementation.
Check these examples:
1) https://github.com/sparrowli/laimpeg/blob/0a68db7d88c0b42c3df584e66366b4e81d3b31ee/libavutil/tests/dict.c
2) https://github.com/ithinkso117/DotNetPusher/blob/cf24e7e10a0b2fa843b435d3d3b759a7aa049d1d/DotNetPusher.Native/Encoder.cpp
And as for including cmdutils.c
, this will never work. You can only use FFmpeg's libraries via API and linking with shared libraries.
Hope these help.
Upvotes: 1