mirror of https://git.ffmpeg.org/ffmpeg.git
Compare commits
14 Commits
cdf71f789b
...
8c62d77139
Author | SHA1 | Date |
---|---|---|
Haihao Xiang | 8c62d77139 | |
James Almer | 725d3b6f17 | |
James Almer | 757367e068 | |
James Almer | c4e3d6cdb0 | |
James Almer | ecf87dd230 | |
James Almer | 8616cfe089 | |
James Almer | 855d4b5254 | |
James Almer | a9df9f95c4 | |
Michael Niedermayer | b91e3c4c90 | |
Michael Niedermayer | 02301017d2 | |
Michael Niedermayer | e7174e66ac | |
Michael Niedermayer | 25cb66369e | |
Michael Niedermayer | fb4a1eaadf | |
Timo Rothenpieler | 61f27e5f71 |
3
Makefile
3
Makefile
|
@ -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)
|
||||
|
||||
|
|
|
@ -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().
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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) ||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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, \
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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=$*
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue