Compare commits

...

14 Commits

Author SHA1 Message Date
Haihao Xiang 8c62d77139 lavc/vp9: Fix regression introduced in 0ba05857
It is possible that ff_progress_frame_await() is called but
ff_progress_frame_report() isn't called when a hardware acceleration
method is used, so a thread for vp9 decoding might get stuck.

Reviewed-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
Signed-off-by: Haihao Xiang <haihao.xiang@intel.com>
2024-04-24 11:47:34 +08:00
James Almer 725d3b6f17 doc/APIchanges: fix date for the latest entry
And add the commit hash while at it.

Signed-off-by: James Almer <jamrial@gmail.com>
2024-04-24 00:19:36 -03:00
James Almer 757367e068 avformat/demux: extract extradata from packets when context update is requested
If the demuxer doesn't set extradata in the stream's codecpar, a
need_context_update request will delete the previously extracted extradata in
the stream's internal AVCodecContext.
As we can't ensure the old extradata is valid for the stream in its post
context update request state, try to get extradata from the new packet instead
of attempting to preserve the old in some form.

Signed-off-by: James Almer <jamrial@gmail.com>
2024-04-24 00:11:37 -03:00
James Almer c4e3d6cdb0 avcodec/codec_par: always clear extradata_size in avcodec_parameters_to_context()
Missed in d383ae43c2.

Signed-off-by: James Almer <jamrial@gmail.com>
2024-04-24 00:11:37 -03:00
James Almer ecf87dd230 fftools/ffmpeg_mux_init: allow mapping a stream group from one of the inputs
Signed-off-by: James Almer <jamrial@gmail.com>
2024-04-23 23:54:46 -03:00
James Almer 8616cfe089 avutil/opt: add support for children objects in av_opt_serialize
Signed-off-by: James Almer <jamrial@gmail.com>
2024-04-23 23:54:46 -03:00
James Almer 855d4b5254 avutil/tests/opt: test av_opt_find2()
Signed-off-by: James Almer <jamrial@gmail.com>
2024-04-23 23:54:46 -03:00
James Almer a9df9f95c4 avutil/test/opt: test the AV_OPT_SERIALIZE_SKIP_DEFAULTS flag
Signed-off-by: James Almer <jamrial@gmail.com>
2024-04-23 23:54:46 -03:00
Michael Niedermayer b91e3c4c90
avcodec/cbs_h2645: Check NAL space
Found-by-reviewing: CID1419833 Untrusted loop bound

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2024-04-24 02:52:17 +02:00
Michael Niedermayer 02301017d2
avfilter/vf_thumbnail_cuda: Set ret before checking it
Fixes: CID1418336 Logically dead code

Sponsored-by: Sovereign Tech Fund
Reviewed-by: Timo Rothenpieler <timo@rothenpieler.org>
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2024-04-24 02:52:17 +02:00
Michael Niedermayer e7174e66ac
avfilter/signature_lookup: Dont copy uninitialized stuff around
Fixes: CID1403238 Uninitialized pointer read
Fixes: CID1403239 Uninitialized pointer read

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2024-04-24 02:52:16 +02:00
Michael Niedermayer 25cb66369e
avfilter/signature_lookup: Fix 2 differences to the refernce SW
Fixes: CID1403227 Division or modulo by float zero

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2024-04-24 02:52:16 +02:00
Michael Niedermayer fb4a1eaadf
tools: add target_enc_fuzzer.c
Sponsored-by: Sovereign Tech Fund
Reviewed-by: James Almer <jamrial@gmail.com>
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2024-04-24 02:52:16 +02:00
Timo Rothenpieler 61f27e5f71 doc: use HOSTEXESUF when calling print_options
This is neccesary on WSL, and has no averse effects in normal
environments.
2024-04-24 00:09:38 +02:00
18 changed files with 569 additions and 38 deletions

View File

@ -52,6 +52,9 @@ $(TOOLS): %$(EXESUF): %.o
target_dec_%_fuzzer$(EXESUF): target_dec_%_fuzzer.o $(FF_DEP_LIBS)
$(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) $(LIBFUZZER_PATH)
target_enc_%_fuzzer$(EXESUF): target_enc_%_fuzzer.o $(FF_DEP_LIBS)
$(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) $(LIBFUZZER_PATH)
tools/target_bsf_%_fuzzer$(EXESUF): tools/target_bsf_%_fuzzer.o $(FF_DEP_LIBS)
$(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) $(LIBFUZZER_PATH)

View File

@ -2,6 +2,9 @@ The last version increases of all libraries were on 2024-03-07
API changes, most recent first:
2024-04-24 - 8616cfe0890 - lavu 59.16.100 - opt.h
Add AV_OPT_SERIALIZE_SEARCH_CHILDREN.
2024-04-11 - xxxxxxxxxx - lavc 61.5.102 - avcodec.h
AVCodecContext.decoded_side_data may now be set by libavcodec after
calling avcodec_open2().

View File

@ -60,7 +60,7 @@ GENTEXI := $(GENTEXI:%=doc/avoptions_%.texi)
$(GENTEXI): TAG = GENTEXI
$(GENTEXI): doc/avoptions_%.texi: doc/print_options$(HOSTEXESUF)
$(M)doc/print_options $* > $@
$(M)doc/print_options$(HOSTEXESUF) $* > $@
doc/%.html: TAG = HTML
doc/%-all.html: TAG = HTML

View File

@ -663,10 +663,11 @@ Not all muxers support embedded thumbnails, and those who do, only support a few
Creates a program with the specified @var{title}, @var{program_num} and adds the specified
@var{stream}(s) to it.
@item -stream_group type=@var{type}:st=@var{stream}[:st=@var{stream}][:stg=@var{stream_group}][:id=@var{stream_group_id}...] (@emph{output})
@item -stream_group [map=@var{input_file_id}=@var{stream_group}][type=@var{type}:]st=@var{stream}[:st=@var{stream}][:stg=@var{stream_group}][:id=@var{stream_group_id}...] (@emph{output})
Creates a stream group of the specified @var{type}, @var{stream_group_id} and adds the specified
@var{stream}(s) and/or previously defined @var{stream_group}(s) to it.
Creates a stream group of the specified @var{type} and @var{stream_group_id}, or by
@var{map}ping an input group, adding the specified @var{stream}(s) and/or previously
defined @var{stream_group}(s) to it.
@var{type} can be one of the following:
@table @option
@ -863,6 +864,27 @@ all sub-mix element's @var{annotations}s
@end table
E.g. to create an scalable 5.1 IAMF file from several WAV input files
@example
ffmpeg -i front.wav -i back.wav -i center.wav -i lfe.wav
-map 0:0 -map 1:0 -map 2:0 -map 3:0 -c:a opus
-stream_group type=iamf_audio_element:id=1:st=0:st=1:st=2:st=3,
demixing=parameter_id=998,
recon_gain=parameter_id=101,
layer=ch_layout=stereo,
layer=ch_layout=5.1,
-stream_group type=iamf_mix_presentation:id=2:stg=0:annotations=en-us=Mix_Presentation,
submix=parameter_id=100:parameter_rate=48000|element=stg=0:parameter_id=100:annotations=en-us=Scalable_Submix|layout=sound_system=stereo|layout=sound_system=5.1
-streamid 0:0 -streamid 1:1 -streamid 2:2 -streamid 3:3 output.iamf
@end example
To copy the two stream groups (Audio Element and Mix Presentation) from an input IAMF file with four
streams into an mp4 output
@example
ffmpeg -i input.iamf -c:a copy -stream_group map=0=0:st=0:st=1:st=2:st=3 -stream_group map=0=1:stg=0
-streamid 0:0 -streamid 1:1 -streamid 2:2 -streamid 3:3 output.mp4
@end example
@item -target @var{type} (@emph{output})
Specify target file type (@code{vcd}, @code{svcd}, @code{dvd}, @code{dv},
@code{dv50}). @var{type} may be prefixed with @code{pal-}, @code{ntsc-} or

View File

@ -2232,11 +2232,137 @@ fail:
return ret;
}
static int of_serialize_options(Muxer *mux, void *obj, AVBPrint *bp)
{
char *ptr;
int ret;
ret = av_opt_serialize(obj, 0, AV_OPT_SERIALIZE_SKIP_DEFAULTS | AV_OPT_SERIALIZE_SEARCH_CHILDREN,
&ptr, '=', ':');
if (ret < 0) {
av_log(mux, AV_LOG_ERROR, "Failed to serialize group\n");
return ret;
}
av_bprintf(bp, "%s", ptr);
ret = strlen(ptr);
av_free(ptr);
return ret;
}
#define SERIALIZE(parent, child) do { \
ret = of_serialize_options(mux, parent->child, bp); \
if (ret < 0) \
return ret; \
} while (0)
#define SERIALIZE_LOOP(parent, child, suffix, separator) do { \
for (int j = 0; j < parent->nb_## child ## suffix; j++) { \
av_bprintf(bp, separator#child "="); \
SERIALIZE(parent, child ## suffix[j]); \
} \
} while (0)
static int64_t get_stream_group_index_from_id(Muxer *mux, int64_t id)
{
AVFormatContext *oc = mux->fc;
for (unsigned i = 0; i < oc->nb_stream_groups; i++)
if (oc->stream_groups[i]->id == id)
return oc->stream_groups[i]->index;
return AVERROR(EINVAL);
}
static int of_map_group(Muxer *mux, AVDictionary **dict, AVBPrint *bp, const char *map)
{
AVStreamGroup *stg;
int ret, file_idx, stream_idx;
char *ptr;
file_idx = strtol(map, &ptr, 0);
if (file_idx >= nb_input_files || file_idx < 0 || map == ptr) {
av_log(mux, AV_LOG_ERROR, "Invalid input file index: %d.\n", file_idx);
return AVERROR(EINVAL);
}
stream_idx = strtol(*ptr == '=' ? ptr + 1 : ptr, &ptr, 0);
if (*ptr || stream_idx >= input_files[file_idx]->ctx->nb_stream_groups || stream_idx < 0) {
av_log(mux, AV_LOG_ERROR, "Invalid input stream group index: %d.\n", stream_idx);
return AVERROR(EINVAL);
}
stg = input_files[file_idx]->ctx->stream_groups[stream_idx];
ret = of_serialize_options(mux, stg, bp);
if (ret < 0)
return ret;
ret = av_dict_parse_string(dict, bp->str, "=", ":", 0);
if (ret < 0)
av_log(mux, AV_LOG_ERROR, "Error parsing mapped group specification %s\n", ptr);
av_dict_set_int(dict, "type", stg->type, 0);
av_log(mux, AV_LOG_VERBOSE, "stg %s\n", bp->str);
av_bprint_clear(bp);
switch(stg->type) {
case AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT: {
AVIAMFAudioElement *audio_element = stg->params.iamf_audio_element;
if (audio_element->demixing_info) {
av_bprintf(bp, ",demixing=");
SERIALIZE(audio_element, demixing_info);
}
if (audio_element->recon_gain_info) {
av_bprintf(bp, ",recon_gain=");
SERIALIZE(audio_element, recon_gain_info);
}
SERIALIZE_LOOP(audio_element, layer, s, ",");
break;
}
case AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION: {
AVIAMFMixPresentation *mix = stg->params.iamf_mix_presentation;
for (int i = 0; i < mix->nb_submixes; i++) {
AVIAMFSubmix *submix = mix->submixes[i];
av_bprintf(bp, ",submix=");
SERIALIZE(mix, submixes[i]);
for (int j = 0; j < submix->nb_elements; j++) {
AVIAMFSubmixElement *element = submix->elements[j];
int64_t id = get_stream_group_index_from_id(mux, element->audio_element_id);
if (id < 0) {
av_log(mux, AV_LOG_ERROR, "Invalid or missing stream group index in"
"submix element");
return id;
}
av_bprintf(bp, "|element=");
SERIALIZE(submix, elements[j]);
if (ret)
av_bprintf(bp, ":");
av_bprintf(bp, "stg=%"PRId64, id);
}
SERIALIZE_LOOP(submix, layout, s, "|");
}
break;
}
default:
av_log(mux, AV_LOG_ERROR, "Unsupported mapped group type %d.\n", stg->type);
ret = AVERROR(EINVAL);
break;
}
av_log(mux, AV_LOG_VERBOSE, "extra %s\n", bp->str);
return 0;
}
static int of_parse_group_token(Muxer *mux, const char *token, char *ptr)
{
AVFormatContext *oc = mux->fc;
AVStreamGroup *stg;
AVDictionary *dict = NULL, *tmp = NULL;
char *mapped_string = NULL;
const AVDictionaryEntry *e;
const AVOption opts[] = {
{ "type", "Set group type", offsetof(AVStreamGroup, type), AV_OPT_TYPE_INT,
@ -2262,8 +2388,31 @@ static int of_parse_group_token(Muxer *mux, const char *token, char *ptr)
return ret;
}
av_dict_copy(&tmp, dict, 0);
e = av_dict_get(dict, "map", NULL, 0);
if (e) {
AVBPrint bp;
if (ptr) {
av_log(mux, AV_LOG_ERROR, "Unexpected extra parameters when mapping a"
" stream group\n");
ret = AVERROR(EINVAL);
goto end;
}
av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC);
ret = of_map_group(mux, &tmp, &bp, e->value);
if (ret < 0) {
av_bprint_finalize(&bp, NULL);
goto end;
}
av_bprint_finalize(&bp, &mapped_string);
ptr = mapped_string;
}
// "type" is not a user settable AVOption in AVStreamGroup, so handle it here
e = av_dict_get(dict, "type", NULL, 0);
e = av_dict_get(tmp, "type", NULL, 0);
if (!e) {
av_log(mux, AV_LOG_ERROR, "No type specified for Stream Group in \"%s\"\n", token);
ret = AVERROR(EINVAL);
@ -2278,7 +2427,6 @@ static int of_parse_group_token(Muxer *mux, const char *token, char *ptr)
goto end;
}
av_dict_copy(&tmp, dict, 0);
stg = avformat_stream_group_create(oc, type, &tmp);
if (!stg) {
ret = AVERROR(ENOMEM);
@ -2331,6 +2479,7 @@ static int of_parse_group_token(Muxer *mux, const char *token, char *ptr)
// make sure that nothing but "st" and "stg" entries are left in the dict
e = NULL;
av_dict_set(&tmp, "map", NULL, 0);
av_dict_set(&tmp, "type", NULL, 0);
while (e = av_dict_iterate(tmp, e)) {
if (!strcmp(e->key, "st") || !strcmp(e->key, "stg"))
@ -2343,6 +2492,7 @@ static int of_parse_group_token(Muxer *mux, const char *token, char *ptr)
ret = 0;
end:
av_free(mapped_string);
av_dict_free(&dict);
av_dict_free(&tmp);

View File

@ -709,7 +709,11 @@ static int cbs_h2645_split_fragment(CodedBitstreamContext *ctx,
start = bytestream2_tell(&gbc);
for(i = 0; i < num_nalus; i++) {
if (bytestream2_get_bytes_left(&gbc) < 2)
return AVERROR_INVALIDDATA;
size = bytestream2_get_be16(&gbc);
if (bytestream2_get_bytes_left(&gbc) < size)
return AVERROR_INVALIDDATA;
bytestream2_skip(&gbc, size);
}
end = bytestream2_tell(&gbc);

View File

@ -250,6 +250,7 @@ int avcodec_parameters_to_context(AVCodecContext *codec,
}
av_freep(&codec->extradata);
codec->extradata_size = 0;
if (par->extradata) {
codec->extradata = av_mallocz(par->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
if (!codec->extradata)

View File

@ -1735,9 +1735,9 @@ static int vp9_decode_frame(AVCodecContext *avctx, AVFrame *frame,
if (ret < 0)
goto fail;
}
ff_progress_frame_report(&s->s.frames[CUR_FRAME].tf, INT_MAX);
finish:
ff_progress_frame_report(&s->s.frames[CUR_FRAME].tf, INT_MAX);
// ref frame setup
for (int i = 0; i < 8; i++)
ff_progress_frame_replace(&s->s.refs[i], &s->next_refs[i]);

View File

@ -448,14 +448,14 @@ static MatchingInfo evaluate_parameters(AVFilterContext *ctx, SignatureContext *
}
if (tolerancecount > 2) {
a = aprev;
b = bprev;
if (dir == DIR_NEXT) {
/* turn around */
a = infos->first;
b = infos->second;
dir = DIR_PREV;
} else {
a = aprev;
b = bprev;
break;
}
}
@ -496,10 +496,10 @@ static MatchingInfo evaluate_parameters(AVFilterContext *ctx, SignatureContext *
continue; /* matching sequence is too short */
if ((double) goodfcount / (double) fcount < sc->thit)
continue;
if ((double) goodfcount*0.5 < FFMAX(gooda, goodb))
if ((double) goodfcount*0.5 <= FFMAX(gooda, goodb))
continue;
meandist = (double) goodfcount / (double) distsum;
meandist = (double) distsum / (double) goodfcount;
if (meandist < minmeandist ||
status == (STATUS_END_REACHED | STATUS_BEGIN_REACHED) ||

View File

@ -291,7 +291,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
hist[i] = 4 * hist[i];
}
CHECK_CU(cu->cuCtxPopCurrent(&dummy));
ret = CHECK_CU(cu->cuCtxPopCurrent(&dummy));
if (ret < 0)
return ret;

View File

@ -1319,6 +1319,8 @@ fail:
return ret;
}
static int extract_extradata(FFFormatContext *si, AVStream *st, const AVPacket *pkt);
static int read_frame_internal(AVFormatContext *s, AVPacket *pkt)
{
FFFormatContext *const si = ffformatcontext(s);
@ -1373,6 +1375,16 @@ static int read_frame_internal(AVFormatContext *s, AVPacket *pkt)
return ret;
}
if (!sti->avctx->extradata) {
sti->extract_extradata.inited = 0;
ret = extract_extradata(si, st, pkt);
if (ret < 0) {
av_packet_unref(pkt);
return ret;
}
}
sti->codec_desc = avcodec_descriptor_get(sti->avctx->codec_id);
sti->need_context_update = 0;
@ -2427,6 +2439,7 @@ static int extract_extradata_init(AVStream *st)
if (!ret)
goto finish;
av_bsf_free(&sti->extract_extradata.bsf);
ret = av_bsf_alloc(f, &sti->extract_extradata.bsf);
if (ret < 0)
return ret;

View File

@ -2386,14 +2386,54 @@ int av_opt_is_set_to_default_by_name(void *obj, const char *name, int search_fla
return av_opt_is_set_to_default(target, o);
}
static int opt_serialize(void *obj, int opt_flags, int flags, int *cnt,
AVBPrint *bprint, const char key_val_sep, const char pairs_sep)
{
const AVOption *o = NULL;
void *child = NULL;
uint8_t *buf;
int ret;
const char special_chars[] = {pairs_sep, key_val_sep, '\0'};
if (flags & AV_OPT_SERIALIZE_SEARCH_CHILDREN)
while (child = av_opt_child_next(obj, child)) {
ret = opt_serialize(child, opt_flags, flags, cnt, bprint,
key_val_sep, pairs_sep);
if (ret < 0)
return ret;
}
while (o = av_opt_next(obj, o)) {
if (o->type == AV_OPT_TYPE_CONST)
continue;
if ((flags & AV_OPT_SERIALIZE_OPT_FLAGS_EXACT) && o->flags != opt_flags)
continue;
else if (((o->flags & opt_flags) != opt_flags))
continue;
if (flags & AV_OPT_SERIALIZE_SKIP_DEFAULTS && av_opt_is_set_to_default(obj, o) > 0)
continue;
if ((ret = av_opt_get(obj, o->name, 0, &buf)) < 0) {
av_bprint_finalize(bprint, NULL);
return ret;
}
if (buf) {
if ((*cnt)++)
av_bprint_append_data(bprint, &pairs_sep, 1);
av_bprint_escape(bprint, o->name, special_chars, AV_ESCAPE_MODE_BACKSLASH, 0);
av_bprint_append_data(bprint, &key_val_sep, 1);
av_bprint_escape(bprint, buf, special_chars, AV_ESCAPE_MODE_BACKSLASH, 0);
av_freep(&buf);
}
}
return 0;
}
int av_opt_serialize(void *obj, int opt_flags, int flags, char **buffer,
const char key_val_sep, const char pairs_sep)
{
const AVOption *o = NULL;
uint8_t *buf;
AVBPrint bprint;
int ret, cnt = 0;
const char special_chars[] = {pairs_sep, key_val_sep, '\0'};
if (pairs_sep == '\0' || key_val_sep == '\0' || pairs_sep == key_val_sep ||
pairs_sep == '\\' || key_val_sep == '\\') {
@ -2407,28 +2447,11 @@ int av_opt_serialize(void *obj, int opt_flags, int flags, char **buffer,
*buffer = NULL;
av_bprint_init(&bprint, 64, AV_BPRINT_SIZE_UNLIMITED);
while (o = av_opt_next(obj, o)) {
if (o->type == AV_OPT_TYPE_CONST)
continue;
if ((flags & AV_OPT_SERIALIZE_OPT_FLAGS_EXACT) && o->flags != opt_flags)
continue;
else if (((o->flags & opt_flags) != opt_flags))
continue;
if (flags & AV_OPT_SERIALIZE_SKIP_DEFAULTS && av_opt_is_set_to_default(obj, o) > 0)
continue;
if ((ret = av_opt_get(obj, o->name, 0, &buf)) < 0) {
av_bprint_finalize(&bprint, NULL);
return ret;
}
if (buf) {
if (cnt++)
av_bprint_append_data(&bprint, &pairs_sep, 1);
av_bprint_escape(&bprint, o->name, special_chars, AV_ESCAPE_MODE_BACKSLASH, 0);
av_bprint_append_data(&bprint, &key_val_sep, 1);
av_bprint_escape(&bprint, buf, special_chars, AV_ESCAPE_MODE_BACKSLASH, 0);
av_freep(&buf);
}
}
ret = opt_serialize(obj, opt_flags, flags, &cnt, &bprint,
key_val_sep, pairs_sep);
if (ret < 0)
return ret;
ret = av_bprint_finalize(&bprint, buffer);
if (ret < 0)
return ret;

View File

@ -929,6 +929,7 @@ int av_opt_flag_is_set(void *obj, const char *field_name, const char *flag_name)
#define AV_OPT_SERIALIZE_SKIP_DEFAULTS 0x00000001 ///< Serialize options that are not set to default values only.
#define AV_OPT_SERIALIZE_OPT_FLAGS_EXACT 0x00000002 ///< Serialize options that exactly match opt_flags only.
#define AV_OPT_SERIALIZE_SEARCH_CHILDREN 0x00000004 ///< Serialize options in possible children of the given object.
/**
* Serialize object's options.

View File

@ -30,6 +30,7 @@
typedef struct TestContext {
const AVClass *class;
struct ChildContext *child;
int num;
int toggle;
char *string;
@ -123,10 +124,46 @@ static const char *test_get_name(void *ctx)
return "test";
}
typedef struct ChildContext {
const AVClass *class;
int64_t child_num64;
int child_num;
} ChildContext;
#undef OFFSET
#define OFFSET(x) offsetof(ChildContext, x)
static const AVOption child_options[]= {
{"child_num64", "set num 64bit", OFFSET(child_num64), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, 100, 1 },
{"child_num", "set child_num", OFFSET(child_num), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 100, 1 },
{ NULL },
};
static const char *child_get_name(void *ctx)
{
return "child";
}
static const AVClass child_class = {
.class_name = "ChildContext",
.item_name = child_get_name,
.option = child_options,
.version = LIBAVUTIL_VERSION_INT,
};
static void *test_child_next(void *obj, void *prev)
{
TestContext *test_ctx = obj;
if (!prev)
return test_ctx->child;
return NULL;
}
static const AVClass test_class = {
.class_name = "TestContext",
.item_name = test_get_name,
.option = test_options,
.child_next = test_child_next,
.version = LIBAVUTIL_VERSION_INT,
};
@ -277,8 +314,19 @@ int main(void)
av_set_options_string(&test_ctx, buf, "=", ",");
av_free(buf);
if (av_opt_serialize(&test_ctx, 0, 0, &buf, '=', ',') >= 0) {
ChildContext child_ctx = { 0 };
printf("%s\n", buf);
av_free(buf);
child_ctx.class = &child_class;
test_ctx.child = &child_ctx;
if (av_opt_serialize(&test_ctx, 0,
AV_OPT_SERIALIZE_SKIP_DEFAULTS|AV_OPT_SERIALIZE_SEARCH_CHILDREN,
&buf, '=', ',') >= 0) {
printf("%s\n", buf);
av_free(buf);
}
av_opt_free(&child_ctx);
test_ctx.child = NULL;
}
}
av_opt_free(&test_ctx);
@ -404,5 +452,54 @@ int main(void)
av_opt_free(&test_ctx);
}
printf("\nTesting av_opt_find2()\n");
{
TestContext test_ctx = { 0 };
ChildContext child_ctx = { 0 };
void *target;
const AVOption *opt;
test_ctx.class = &test_class;
child_ctx.class = &child_class;
test_ctx.child = &child_ctx;
av_log_set_level(AV_LOG_QUIET);
// Should succeed. num exists and has opt_flags 1
opt = av_opt_find2(&test_ctx, "num", NULL, 1, 0, &target);
if (opt && target == &test_ctx)
printf("OK '%s'\n", opt->name);
else
printf("Error 'num'\n");
// Should fail. num64 exists but has opt_flags 1, not 2
opt = av_opt_find(&test_ctx, "num64", NULL, 2, 0);
if (opt)
printf("OK '%s'\n", opt->name);
else
printf("Error 'num64'\n");
// Should fail. child_num exists but in a child object we're not searching
opt = av_opt_find(&test_ctx, "child_num", NULL, 0, 0);
if (opt)
printf("OK '%s'\n", opt->name);
else
printf("Error 'child_num'\n");
// Should succeed. child_num exists in a child object we're searching
opt = av_opt_find2(&test_ctx, "child_num", NULL, 0, AV_OPT_SEARCH_CHILDREN, &target);
if (opt && target == &child_ctx)
printf("OK '%s'\n", opt->name);
else
printf("Error 'child_num'\n");
// Should fail. foo doesn't exist
opt = av_opt_find(&test_ctx, "foo", NULL, 0, 0);
if (opt)
printf("OK '%s'\n", opt->name);
else
printf("Error 'foo'\n");
}
return 0;
}

View File

@ -79,7 +79,7 @@
*/
#define LIBAVUTIL_VERSION_MAJOR 59
#define LIBAVUTIL_VERSION_MINOR 15
#define LIBAVUTIL_VERSION_MINOR 16
#define LIBAVUTIL_VERSION_MICRO 100
#define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \

View File

@ -179,6 +179,7 @@ Setting entry with key 'array_int' to value ''
Setting entry with key 'array_str' to value 'str0|str\|1|str\\2'
Setting entry with key 'array_dict' to value 'k00=v\\\\00:k01=v\,01,k10=v\\=1\\:0'
num=0,toggle=1,rational=1/1,string=default,escape=\\\=\,,flags=0x00000001,size=200x300,pix_fmt=0bgr,sample_fmt=s16,video_rate=25/1,duration=0.001,color=0xffc0cbff,cl=hexagonal,bin=62696E00,bin1=,bin2=,num64=1,flt=0.333333,dbl=0.333333,bool1=auto,bool2=true,bool3=false,dict1=,dict2=happy\=\\:-),array_int=,array_str=str0|str\\|1|str\\\\2,array_dict=k00\=v\\\\\\\\00:k01\=v\\\,01\,k10\=v\\\\\=1\\\\:0
child_num=0,flt=0.333333,dbl=0.333333,array_int=
Testing av_set_options_string()
Setting options string ''
@ -448,3 +449,10 @@ Setting options string 'a_very_long_option_name_that_will_need_to_be_ellipsized_
Setting 'a_very_long_option_name_that_will_need_to_be_ellipsized_around_here' to value '42'
Option 'a_very_long_option_name_that_will_need_to_be_ellipsized_around_here' not found
Error 'a_very_long_option_name_that_will_need_to_be_ellipsized_around_here=42'
Testing av_opt_find2()
OK 'num'
Error 'num64'
Error 'child_num'
OK 'child_num'
Error 'foo'

View File

@ -5,6 +5,9 @@ TOOLS-$(CONFIG_ZLIB) += cws2fws
tools/target_dec_%_fuzzer.o: tools/target_dec_fuzzer.c
$(COMPILE_C) -DFFMPEG_DECODER=$*
tools/target_enc_%_fuzzer.o: tools/target_enc_fuzzer.c
$(COMPILE_C) -DFFMPEG_ENCODER=$*
tools/target_bsf_%_fuzzer.o: tools/target_bsf_fuzzer.c
$(COMPILE_C) -DFFMPEG_BSF=$*

203
tools/target_enc_fuzzer.c Normal file
View File

@ -0,0 +1,203 @@
/*
* Copyright (c) 2024 Michael Niedermayer <michael-ffmpeg@niedermayer.cc>
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Based on target_dec_fuzzer
*/
#include "config.h"
#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/cpu.h"
#include "libavutil/imgutils.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/mem.h"
#include "libavcodec/avcodec.h"
#include "libavcodec/bytestream.h"
#include "libavcodec/codec_internal.h"
#include "libavformat/avformat.h"
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
extern const FFCodec * codec_list[];
static void error(const char *err)
{
fprintf(stderr, "%s", err);
exit(1);
}
static const FFCodec *c = NULL;
// Ensure we don't loop forever
const uint32_t maxiteration = 8096;
static int encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt)
{
int ret;
ret = avcodec_send_frame(enc_ctx, frame);
if (ret < 0)
return ret;
while (ret >= 0) {
ret = avcodec_receive_packet(enc_ctx, pkt);
if (ret == AVERROR(EAGAIN)) {
return 0;
} else if (ret < 0) {
return ret;
}
av_packet_unref(pkt);
}
av_assert0(0);
}
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
uint64_t maxpixels_per_frame = 512 * 512;
uint64_t maxpixels;
const uint8_t *end = data + size;
uint32_t it = 0;
uint64_t nb_samples = 0;
AVDictionary *opts = NULL;
if (!c) {
#define ENCODER_SYMBOL0(CODEC) ff_##CODEC##_encoder
#define ENCODER_SYMBOL(CODEC) ENCODER_SYMBOL0(CODEC)
extern FFCodec ENCODER_SYMBOL(FFMPEG_ENCODER);
codec_list[0] = &ENCODER_SYMBOL(FFMPEG_ENCODER);
c = &ENCODER_SYMBOL(FFMPEG_ENCODER);
av_log_set_level(AV_LOG_PANIC);
}
av_assert0(c->p.type == AVMEDIA_TYPE_VIDEO);
maxpixels = maxpixels_per_frame * maxiteration;
maxpixels_per_frame = FFMIN(maxpixels_per_frame , maxpixels);
AVCodecContext* ctx = avcodec_alloc_context3(&c->p);
if (!ctx)
error("Failed memory allocation");
if (ctx->max_pixels == 0 || ctx->max_pixels > maxpixels_per_frame)
ctx->max_pixels = maxpixels_per_frame; //To reduce false positive OOM and hangs
ctx->pix_fmt = AV_PIX_FMT_YUV420P;
if (size > 1024) {
GetByteContext gbc;
int flags;
int64_t flags64;
size -= 1024;
bytestream2_init(&gbc, data + size, 1024);
ctx->width = bytestream2_get_le32(&gbc) & 0xFFFF;
ctx->height = bytestream2_get_le32(&gbc) & 0xFFFF;
ctx->bit_rate = bytestream2_get_le64(&gbc);
ctx->gop_size = bytestream2_get_le32(&gbc) & 0x7FFFFFFF;
ctx->max_b_frames = bytestream2_get_le32(&gbc) & 0x7FFFFFFF;
ctx->time_base.num = bytestream2_get_le32(&gbc) & 0x7FFFFFFF;
ctx->time_base.den = bytestream2_get_le32(&gbc) & 0x7FFFFFFF;
ctx->framerate.num = bytestream2_get_le32(&gbc) & 0x7FFFFFFF;
ctx->framerate.den = bytestream2_get_le32(&gbc) & 0x7FFFFFFF;
flags = bytestream2_get_byte(&gbc);
if (flags & 2)
ctx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
if (flags & 0x40)
av_force_cpu_flags(0);
flags64 = bytestream2_get_le64(&gbc);
int npixfmts = 0;
while (c->p.pix_fmts[npixfmts++] != AV_PIX_FMT_NONE)
;
ctx->pix_fmt = c->p.pix_fmts[bytestream2_get_byte(&gbc) % npixfmts];
switch (c->p.id) {
case AV_CODEC_ID_FFV1:{
int coder = bytestream2_get_byte(&gbc)&3;
if (coder == 3) coder = -2;
av_dict_set_int(&opts, "coder", coder, 0);
av_dict_set_int(&opts, "context", bytestream2_get_byte(&gbc)&1, 0);
av_dict_set_int(&opts, "slicecrc", bytestream2_get_byte(&gbc)&1, 0);
break;}
}
}
if (ctx->width == 0 || av_image_check_size(ctx->width, ctx->height, 0, ctx))
ctx->width = ctx->height = 64;
int res = avcodec_open2(ctx, &c->p, &opts);
if (res < 0) {
avcodec_free_context(&ctx);
av_dict_free(&opts);
return 0; // Failure of avcodec_open2() does not imply that a issue was found
}
AVFrame *frame = av_frame_alloc();
AVPacket *avpkt = av_packet_alloc();
if (!frame || !avpkt)
error("Failed memory allocation");
frame->format = ctx->pix_fmt;
frame->width = ctx->width;
frame->height = ctx->height;
while (data < end && it < maxiteration) {
res = av_frame_get_buffer(frame, 0);
if (res < 0)
error("Failed av_frame_get_buffer");
for (int i=0; i<FF_ARRAY_ELEMS(frame->buf); i++) {
if (frame->buf[i]) {
int buf_size = FFMIN(end-data, frame->buf[i]->size);
memcpy(frame->buf[i]->data, data, buf_size);
memset(frame->buf[i]->data + buf_size, 0, frame->buf[i]->size - buf_size);
data += buf_size;
}
}
frame->pts = nb_samples;
res = encode(ctx, frame, avpkt);
if (res < 0)
break;
it++;
for (int i=0; i<FF_ARRAY_ELEMS(frame->buf); i++)
av_buffer_unref(&frame->buf[i]);
av_packet_unref(avpkt);
}
encode(ctx, NULL, avpkt);
av_packet_unref(avpkt);
// fprintf(stderr, "frames encoded: %"PRId64", iterations: %d\n", nb_samples , it);
av_frame_free(&frame);
avcodec_free_context(&ctx);
av_packet_free(&avpkt);
av_dict_free(&opts);
return 0;
}