Commit 94cba3150c for ffmpeg
commit 94cba3150c6b83057ed48b833689adc806715cc0
Author: James Almer <jamrial@gmail.com>
Date: Sat Oct 17 15:25:26 2026 -0300
avcodec/bsf/jpegapp: use an AVOption of binary type for the sidecar
Opening a file for this is hacky and the tests added for it are broken on Windows.
Signed-off-by: James Almer <jamrial@gmail.com>
diff --git a/doc/bitstream_filters.texi b/doc/bitstream_filters.texi
index 47bb75892b..cc1cd453a7 100644
--- a/doc/bitstream_filters.texi
+++ b/doc/bitstream_filters.texi
@@ -635,7 +635,7 @@ the two bytes immediately following SOI, that APP0 segment is dropped.
APP0 segments preceded by fill bytes or another segment are retained.
An APP1-only payload keeps the encoder APP0: SOI, APP1, APP0, DQT, ...
-The filter requires @option{filename}. Extra unused sidecar bytes are
+The filter requires @option{sidecar}. Extra unused sidecar bytes are
ignored; a packet without a matching record is an error.
Records follow packets in arrival order. Flushing the filter does not
rewind the sidecar, so a reused context needs a sidecar positioned for
@@ -645,12 +645,9 @@ by that filter context. To resume, create a new context with a sidecar
matching the remaining packets. Flushing does not clear this error.
@example
-ffmpeg -i in.mp4 -c:v mjpeg -bsf:v jpegapp=filename=app.bin frame_%d.jpg
+ffmpeg -i in.mp4 -c:v mjpeg -bsf:v jpegapp=sidecar=00000004DEADBEEF frame_%d.jpg
@end example
-On Windows, escape the drive colon in the BSF option:
-@code{filename='E\:/path/app.bin'}.
-
@section mjpeg2jpeg
Convert MJPEG/AVI1 packets to full JPEG/JFIF packets.
diff --git a/libavcodec/bsf/jpegapp.c b/libavcodec/bsf/jpegapp.c
index c75822dfb6..8024b77d46 100644
--- a/libavcodec/bsf/jpegapp.c
+++ b/libavcodec/bsf/jpegapp.c
@@ -56,16 +56,16 @@
#include "libavcodec/bsf.h"
#include "libavcodec/bsf_internal.h"
+#include "libavcodec/bytestream.h"
#include "libavcodec/mjpeg.h"
#define JPEGAPP_MAX_RECORD (16 * 1024 * 1024)
typedef struct JPEGAppContext {
const AVClass *class;
- char *filename;
- FILE *file;
- uint8_t *buf;
- int buf_size;
+ GetByteContext gb;
+ uint8_t *sidecar;
+ int sidecar_len;
int read_error;
int64_t packet_index;
} JPEGAppContext;
@@ -99,84 +99,43 @@ static int jpegapp_init(AVBSFContext *ctx)
{
JPEGAppContext *s = ctx->priv_data;
- if (!s->filename || !s->filename[0]) {
- av_log(ctx, AV_LOG_ERROR, "Option filename is required\n");
+ if (s->sidecar_len <= 0) {
+ av_log(ctx, AV_LOG_ERROR, "Option sidecar is required\n");
return AVERROR(EINVAL);
}
- s->file = avpriv_fopen_utf8(s->filename, "rb");
- if (!s->file) {
- int err = errno ? AVERROR(errno) : AVERROR(EIO);
- av_log(ctx, AV_LOG_ERROR, "Could not open '%s': %s\n",
- s->filename, av_err2str(err));
- return err;
- }
- return 0;
-}
+ bytestream2_init(&s->gb, s->sidecar, s->sidecar_len);
-static void jpegapp_close(AVBSFContext *ctx)
-{
- JPEGAppContext *s = ctx->priv_data;
-
- if (s->file) {
- fclose(s->file);
- s->file = NULL;
- }
- av_freep(&s->buf);
- s->buf_size = 0;
+ return 0;
}
static int jpegapp_read_record(AVBSFContext *ctx, int *out_size)
{
JPEGAppContext *s = ctx->priv_data;
- uint8_t hdr[4];
uint32_t n;
- size_t got;
int ret;
- errno = 0;
- got = fread(hdr, 1, 4, s->file);
- ret = errno;
- if (got == 0 && feof(s->file) && !ferror(s->file)) {
+ if (!bytestream2_get_bytes_left(&s->gb)) {
av_log(ctx, AV_LOG_ERROR,
"jpegapp: no sidecar record for packet %"PRId64"\n",
s->packet_index);
return AVERROR_INVALIDDATA;
}
- if (got != 4) {
- if (ferror(s->file)) {
- av_log(ctx, AV_LOG_ERROR, "jpegapp: sidecar read failed\n");
- return ret ? AVERROR(ret) : AVERROR(EIO);
- }
+ if (bytestream2_get_bytes_left(&s->gb) < 4) {
av_log(ctx, AV_LOG_ERROR, "jpegapp: truncated length prefix\n");
return AVERROR_INVALIDDATA;
}
- n = AV_RB32(hdr);
+ n = bytestream2_get_be32(&s->gb);
if (n > JPEGAPP_MAX_RECORD) {
av_log(ctx, AV_LOG_ERROR, "jpegapp: record length %u too large\n", n);
return AVERROR_INVALIDDATA;
}
- if (n > (uint32_t)s->buf_size) {
- ret = av_reallocp(&s->buf, n);
- if (ret < 0) {
- s->buf_size = 0;
- return ret;
- }
- s->buf_size = n;
- }
if (n) {
- errno = 0;
- got = fread(s->buf, 1, n, s->file);
- ret = errno;
- if (got != n) {
- if (ferror(s->file)) {
- av_log(ctx, AV_LOG_ERROR, "jpegapp: sidecar read failed\n");
- return ret ? AVERROR(ret) : AVERROR(EIO);
- }
+ if (n > bytestream2_get_bytes_left(&s->gb)) {
av_log(ctx, AV_LOG_ERROR, "jpegapp: truncated record payload\n");
return AVERROR_INVALIDDATA;
}
- ret = jpegapp_validate_payload(s->buf, n);
+ ret = jpegapp_validate_payload(s->gb.buffer, n);
if (ret < 0) {
av_log(ctx, AV_LOG_ERROR,
"jpegapp: payload is not a sequence of APP0..APP15 segments\n");
@@ -213,7 +172,7 @@ static int jpegapp_filter(AVBSFContext *ctx, AVPacket *out)
}
src_off = 2;
- if (rec_size >= 2 && s->buf[0] == 0xff && s->buf[1] == APP0 &&
+ if (rec_size >= 2 && s->gb.buffer[0] == 0xff && s->gb.buffer[1] == APP0 &&
in->size >= 4 && in->data[2] == 0xff && in->data[3] == APP0) {
int skip = jpeg_app_segment_len(in->data + 2, in->size - 2);
if (skip < 0) {
@@ -243,7 +202,7 @@ static int jpegapp_filter(AVBSFContext *ctx, AVPacket *out)
goto fail;
memcpy(out->data, in->data, 2);
- memcpy(out->data + 2, s->buf, rec_size);
+ bytestream2_get_buffer(&s->gb, out->data + 2, rec_size);
memcpy(out->data + 2 + rec_size, in->data + src_off, in->size - src_off);
av_packet_free(&in);
return 0;
@@ -258,10 +217,8 @@ fail:
#define OFFSET(x) offsetof(JPEGAppContext, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_BSF_PARAM)
static const AVOption jpegapp_options[] = {
- { "filename", "sidecar of u32be-length + APP-segment records", OFFSET(filename),
- AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS },
- { "file", "sidecar of u32be-length + APP-segment records", OFFSET(filename),
- AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS },
+ { "sidecar", "sidecar of u32be-length + APP-segment records", OFFSET(sidecar),
+ AV_OPT_TYPE_BINARY, .flags = FLAGS },
{ NULL },
};
@@ -282,6 +239,5 @@ const FFBitStreamFilter ff_jpegapp_bsf = {
.p.priv_class = &jpegapp_class,
.priv_data_size = sizeof(JPEGAppContext),
.init = jpegapp_init,
- .close = jpegapp_close,
.filter = jpegapp_filter,
};
diff --git a/libavcodec/version.h b/libavcodec/version.h
index d0980e28de..ef9018e70b 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -30,7 +30,7 @@
#include "version_major.h"
#define LIBAVCODEC_VERSION_MINOR 20
-#define LIBAVCODEC_VERSION_MICRO 100
+#define LIBAVCODEC_VERSION_MICRO 101
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
LIBAVCODEC_VERSION_MINOR, \
diff --git a/tests/fate/jpegapp.mak b/tests/fate/jpegapp.mak
index 995189af11..1ec4b41d26 100644
--- a/tests/fate/jpegapp.mak
+++ b/tests/fate/jpegapp.mak
@@ -5,34 +5,28 @@
FATE_JPEGAPP-$(call ALLYES, SMJPEG_DEMUXER JPEGAPP_BSF MJPEG_MUXER FILE_PROTOCOL) += fate-bsf-jpegapp
fate-bsf-jpegapp: CMD = \
- cleanfiles="$${cleanfiles} $${outfile}.bin"; \
- printf "\\000\\000\\000\\010\\377\\341\\000\\006FATE\\000\\000\\000\\000\\000\\000\\000\\010\\377\\340\\000\\006FATE" > "$${outfile}.bin"; \
md5 -i $(TARGET_SAMPLES)/smjpeg/scenwin.mjpg -map 0:v:0 -frames:v 3 -c:v copy \
- -bsf:v "jpegapp=filename=$$(target_path "$${outfile}.bin" | sed "s/:/\\\\\\\\:/g")" -fflags +bitexact -f mjpeg
+ -bsf:v "jpegapp=sidecar=00000008FFE10006464154450000000000000008FFE0000646415445" -fflags +bitexact -f mjpeg
# One record for three packets: the second packet must fail instead of
# silently passing through without its metadata.
-FATE_JPEGAPP-$(call ALLYES, SMJPEG_DEMUXER JPEGAPP_BSF MJPEG_MUXER FILE_PROTOCOL) += fate-bsf-jpegapp-missing
+FATE_JPEGAPP-$(call ALLYES, SMJPEG_DEMUXER JPEGAPP_BSF NULL_MUXER FILE_PROTOCOL) += fate-bsf-jpegapp-missing
fate-bsf-jpegapp-missing: CMP = grep
fate-bsf-jpegapp-missing: REF = jpegapp: no sidecar record for packet 1
fate-bsf-jpegapp-missing: CMD = \
- cleanfiles="$${cleanfiles} $${outfile}.bin $${outfile}.mjpeg"; \
- printf "\\000\\000\\000\\000" > "$${outfile}.bin"; \
ffmpeg -xerror -i $(TARGET_SAMPLES)/smjpeg/scenwin.mjpg -map 0:v:0 -frames:v 3 -c:v copy \
- -bsf:v "jpegapp=filename=$$(target_path "$${outfile}.bin" | sed "s/:/\\\\\\\\:/g")" \
- -fflags +bitexact -f mjpeg -y "$$(target_path "$${outfile}.mjpeg")" ; \
+ -bsf:v "jpegapp=sidecar=00000000" \
+ -fflags +bitexact -f null -; \
test $$? -ne 0
-# A record longer than the 16 MiB limit must be rejected.
-FATE_JPEGAPP-$(call ALLYES, SMJPEG_DEMUXER JPEGAPP_BSF MJPEG_MUXER FILE_PROTOCOL) += fate-bsf-jpegapp-oversize
+# A record longer than the 16 MiB limit zmust be rejected.
+FATE_JPEGAPP-$(call ALLYES, SMJPEG_DEMUXER JPEGAPP_BSF NULL_MUXER FILE_PROTOCOL) += fate-bsf-jpegapp-oversize
fate-bsf-jpegapp-oversize: CMP = grep
fate-bsf-jpegapp-oversize: REF = jpegapp: record length 16777217 too large
fate-bsf-jpegapp-oversize: CMD = \
- cleanfiles="$${cleanfiles} $${outfile}.bin $${outfile}.mjpeg"; \
- printf "\\001\\000\\000\\001\\377\\341\\000\\006FATE" > "$${outfile}.bin"; \
ffmpeg -xerror -i $(TARGET_SAMPLES)/smjpeg/scenwin.mjpg -map 0:v:0 -frames:v 3 -c:v copy \
- -bsf:v "jpegapp=filename=$$(target_path "$${outfile}.bin" | sed "s/:/\\\\\\\\:/g")" \
- -fflags +bitexact -f mjpeg -y "$$(target_path "$${outfile}.mjpeg")" ; \
+ -bsf:v "jpegapp=sidecar=01000001FFE1000646415445" \
+ -fflags +bitexact -f null -; \
test $$? -ne 0
# Without -xerror, subsequent packets must still fail after an oversized
@@ -40,9 +34,7 @@ fate-bsf-jpegapp-oversize: CMD = \
# after the oversized prefix would form valid records if reading resumed.
FATE_JPEGAPP-$(call ALLYES, SMJPEG_DEMUXER JPEGAPP_BSF MJPEG_MUXER FILE_PROTOCOL) += fate-bsf-jpegapp-oversize-continuation
fate-bsf-jpegapp-oversize-continuation: CMD = \
- cleanfiles="$${cleanfiles} $${outfile}.bin"; \
- printf "\\000\\000\\000\\000\\001\\000\\000\\001\\000\\000\\000\\010\\377\\341\\000\\006FATE\\000\\000\\000\\000" > "$${outfile}.bin"; \
md5 -i $(TARGET_SAMPLES)/smjpeg/scenwin.mjpg -map 0:v:0 -frames:v 4 -c:v copy \
- -bsf:v "jpegapp=filename=$$(target_path "$${outfile}.bin" | sed "s/:/\\\\\\\\:/g")" -fflags +bitexact -f mjpeg
+ -bsf:v "jpegapp=sidecar=000000000100000100000008ffe100064641544500000000" -fflags +bitexact -f mjpeg
FATE_SAMPLES_FFMPEG += $(FATE_JPEGAPP-yes)