1
0
mirror of https://git.ffmpeg.org/ffmpeg.git synced 2026-10-24 02:09:03 +03:00

avformat/demux: allow returning AVERROR(EAGAIN) from read_probe()

This fills a gap in the probe scoring API; where it's not possible for
a demuxer to both express "I don't know yet" and "this file could definitely
be mine" at the same time.

This is needed for the example of JPEG Multi-Picture Format files, which
are uniquely distinguished from regular JPEG files by the presence of a
special marker that can live arbitrarily far into the file.

This is a bit analogous to the situation with ID3v2 tags, which can also
hide the true file format behind them. In this case, the probe logic already
has a special hack to detect and force more data (cf. `nodat`) - but letting
the demuxer itself decide this case is an IMHO more elegant solution.

Signed-off-by: Niklas Haas <git@haasn.dev>
This commit is contained in:
Niklas Haas
2026-10-05 13:27:07 +02:00
committed by James Almer
parent 42344c7cbb
commit 851bdb6208
2 changed files with 40 additions and 6 deletions

View File

@@ -88,6 +88,15 @@ typedef struct FFInputFormat {
* Tell if a given file has a chance of being parsed as this format.
* The buffer provided is guaranteed to be AVPROBE_PADDING_SIZE bytes
* big so you do not have to check for that unless you need more.
*
* If the final score is not yet resolvable, the special return value
* AVERROR(EAGAIN) can be used to signal to the caller that probing must
* be tried again with more bytes.
*
* This is distinct from AVPROBE_SCORE_RETRY, which means "possibly mine,
* but if another format is more confident, let it have the file", whereas
* AVERROR(EAGAIN) means "possibly mine, don't let another format claim
* it until I've seen enough data to make a decision".
*/
int (*read_probe)(const AVProbeData *);

View File

@@ -153,8 +153,9 @@ const AVInputFormat *av_find_input_format(const char *short_name)
return NULL;
}
const AVInputFormat *av_probe_input_format3(const AVProbeData *pd,
int is_opened, int *score_ret)
static const AVInputFormat *
probe_input_format_internal(const AVProbeData *pd, int is_opened,
int *score_ret, int *undecided)
{
AVProbeData lpd = *pd;
const AVInputFormat *fmt1 = NULL;
@@ -196,7 +197,13 @@ const AVInputFormat *av_probe_input_format3(const AVProbeData *pd,
score = 0;
if (ffifmt(fmt1)->read_probe) {
score = ffifmt(fmt1)->read_probe(&lpd);
if (score)
if (score == AVERROR(EAGAIN)) {
av_log(NULL, AV_LOG_TRACE, "Probing %s score:undecided size:%d\n",
fmt1->name, lpd.buf_size);
if (undecided)
*undecided = 1;
score = 0;
} else if (score)
av_log(NULL, AV_LOG_TRACE, "Probing %s score:%d size:%d\n", fmt1->name, score, lpd.buf_size);
if (fmt1->extensions && av_match_ext(lpd.filename, fmt1->extensions)) {
switch (nodat) {
@@ -235,11 +242,18 @@ const AVInputFormat *av_probe_input_format3(const AVProbeData *pd,
return fmt;
}
const AVInputFormat *av_probe_input_format3(const AVProbeData *pd,
int is_opened, int *score_ret)
{
return probe_input_format_internal(pd, is_opened, score_ret, NULL);
}
const AVInputFormat *av_probe_input_format2(const AVProbeData *pd,
int is_opened, int *score_max)
{
int score_ret;
const AVInputFormat *fmt = av_probe_input_format3(pd, is_opened, &score_ret);
const AVInputFormat *fmt;
fmt = probe_input_format_internal(pd, is_opened, &score_ret, NULL);
if (score_ret > *score_max) {
*score_max = score_ret;
return fmt;
@@ -250,7 +264,7 @@ const AVInputFormat *av_probe_input_format2(const AVProbeData *pd,
const AVInputFormat *av_probe_input_format(const AVProbeData *pd, int is_opened)
{
int score = 0;
return av_probe_input_format2(pd, is_opened, &score);
return probe_input_format_internal(pd, is_opened, &score, NULL);
}
int av_probe_input_buffer2(AVIOContext *pb, const AVInputFormat **fmt,
@@ -313,7 +327,18 @@ int av_probe_input_buffer2(AVIOContext *pb, const AVInputFormat **fmt,
memset(pd.buf + pd.buf_size, 0, AVPROBE_PADDING_SIZE);
/* Guess file format. */
*fmt = av_probe_input_format2(&pd, 1, &score);
int score_new, undecided = 0;
const AVInputFormat *fmt_new;
fmt_new = probe_input_format_internal(&pd, 1, &score_new, &undecided);
/* Retry undecided formats as long as we have more data to feed */
if (undecided && probe_size < max_probe_size && !eof) {
continue;
} else if (score_new > score) {
score = score_new;
*fmt = fmt_new;
}
if (*fmt) {
/* This can only be true in the last iteration. */
if (score <= AVPROBE_SCORE_RETRY) {