FFmpeg
Loading...
Searching...
No Matches
jpegapp.c
Go to the documentation of this file.
1/*
2 * Inject per-frame JPEG APP segments from a length-prefixed sidecar.
3 *
4 * Copyright (c) 2026 Reinhard Nißl
5 *
6 * This file is part of FFmpeg.
7 *
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23/**
24 * Sidecar format (big-endian), one record per encoded JPEG packet:
25 *
26 * u32be length
27 * u8 payload[length]
28 *
29 * Payload is zero or more complete JPEG APP segments (APP0..APP15),
30 * each ``FF En`` + 16-bit length (length includes those two bytes).
31 * A zero length leaves the packet unchanged.
32 *
33 * After SOI the payload is inserted. If it starts with APP0 and the
34 * two bytes immediately after SOI are an APP0 marker, that segment is
35 * dropped so the sidecar can replace it. An APP0 preceded by fill
36 * bytes or another segment is retained. An APP1-only payload keeps
37 * the encoder APP0: SOI, APP1, APP0, DQT, ...
38 *
39 * A sidecar read or validation error stops further record consumption
40 * for this filter context. Flushing does not clear that error.
41 */
42
43#include <errno.h>
44#include <inttypes.h>
45#include <limits.h>
46#include <stdint.h>
47#include <stdio.h>
48#include <string.h>
49
50#include "libavutil/error.h"
51#include "libavutil/file_open.h"
53#include "libavutil/log.h"
54#include "libavutil/mem.h"
55#include "libavutil/opt.h"
56
57#include "libavcodec/bsf.h"
59#include "libavcodec/mjpeg.h"
60
61#define JPEGAPP_MAX_RECORD (16 * 1024 * 1024)
62
63typedef struct JPEGAppContext {
64 const AVClass *class;
65 char *filename;
66 FILE *file;
67 uint8_t *buf;
72
73static int jpeg_app_segment_len(const uint8_t *data, int size)
74{
75 int len;
76
77 if (size < 4 || data[0] != 0xff || data[1] < APP0 || data[1] > APP15)
79 len = AV_RB16(data + 2);
80 if (len < 2 || len > size - 2)
82 return 2 + len;
83}
84
85static int jpegapp_validate_payload(const uint8_t *data, int size)
86{
87 int pos = 0, n;
88
89 while (pos < size) {
91 if (n < 0)
92 return n;
93 pos += n;
94 }
95 return 0;
96}
97
99{
100 JPEGAppContext *s = ctx->priv_data;
101
102 if (!s->filename || !s->filename[0]) {
103 av_log(ctx, AV_LOG_ERROR, "Option filename is required\n");
104 return AVERROR(EINVAL);
105 }
106
107 s->file = avpriv_fopen_utf8(s->filename, "rb");
108 if (!s->file) {
109 int err = errno ? AVERROR(errno) : AVERROR(EIO);
110 av_log(ctx, AV_LOG_ERROR, "Could not open '%s': %s\n",
111 s->filename, av_err2str(err));
112 return err;
113 }
114 return 0;
115}
116
118{
119 JPEGAppContext *s = ctx->priv_data;
120
121 if (s->file) {
122 fclose(s->file);
123 s->file = NULL;
124 }
125 av_freep(&s->buf);
126 s->buf_size = 0;
127}
128
130{
131 JPEGAppContext *s = ctx->priv_data;
132 uint8_t hdr[4];
133 uint32_t n;
134 size_t got;
135 int ret;
136
137 errno = 0;
138 got = fread(hdr, 1, 4, s->file);
139 ret = errno;
140 if (got == 0 && feof(s->file) && !ferror(s->file)) {
142 "jpegapp: no sidecar record for packet %"PRId64"\n",
143 s->packet_index);
144 return AVERROR_INVALIDDATA;
145 }
146 if (got != 4) {
147 if (ferror(s->file)) {
148 av_log(ctx, AV_LOG_ERROR, "jpegapp: sidecar read failed\n");
149 return ret ? AVERROR(ret) : AVERROR(EIO);
150 }
151 av_log(ctx, AV_LOG_ERROR, "jpegapp: truncated length prefix\n");
152 return AVERROR_INVALIDDATA;
153 }
154 n = AV_RB32(hdr);
155 if (n > JPEGAPP_MAX_RECORD) {
156 av_log(ctx, AV_LOG_ERROR, "jpegapp: record length %u too large\n", n);
157 return AVERROR_INVALIDDATA;
158 }
159 if (n > (uint32_t)s->buf_size) {
160 ret = av_reallocp(&s->buf, n);
161 if (ret < 0) {
162 s->buf_size = 0;
163 return ret;
164 }
165 s->buf_size = n;
166 }
167 if (n) {
168 errno = 0;
169 got = fread(s->buf, 1, n, s->file);
170 ret = errno;
171 if (got != n) {
172 if (ferror(s->file)) {
173 av_log(ctx, AV_LOG_ERROR, "jpegapp: sidecar read failed\n");
174 return ret ? AVERROR(ret) : AVERROR(EIO);
175 }
176 av_log(ctx, AV_LOG_ERROR, "jpegapp: truncated record payload\n");
177 return AVERROR_INVALIDDATA;
178 }
179 ret = jpegapp_validate_payload(s->buf, n);
180 if (ret < 0) {
182 "jpegapp: payload is not a sequence of APP0..APP15 segments\n");
183 return ret;
184 }
185 }
186 *out_size = n;
187 return 0;
188}
189
191{
192 JPEGAppContext *s = ctx->priv_data;
193 AVPacket *in;
194 int rec_size = 0, src_off, dst_size, ret;
195
196 ret = ff_bsf_get_packet(ctx, &in);
197 if (ret < 0)
198 return ret;
199
200 ret = s->read_error;
201 if (!ret)
202 ret = jpegapp_read_record(ctx, &rec_size);
203 s->packet_index++;
204 if (ret < 0) {
205 s->read_error = ret;
206 goto fail;
207 }
208
209 if (in->size < 2 || AV_RB16(in->data) != 0xffd8) {
210 av_log(ctx, AV_LOG_ERROR, "jpegapp: packet is not JPEG (missing SOI)\n");
212 goto fail;
213 }
214
215 src_off = 2;
216 if (rec_size >= 2 && s->buf[0] == 0xff && s->buf[1] == APP0 &&
217 in->size >= 4 && in->data[2] == 0xff && in->data[3] == APP0) {
218 int skip = jpeg_app_segment_len(in->data + 2, in->size - 2);
219 if (skip < 0) {
221 "jpegapp: existing APP0 is invalid or truncated\n");
222 ret = skip;
223 goto fail;
224 }
225 src_off = 2 + skip;
226 }
227
228 if (!rec_size) {
229 ret = av_packet_ref(out, in);
230 goto fail;
231 }
232
233 if (in->size - src_off > INT_MAX - 2 - rec_size) {
234 ret = AVERROR(ERANGE);
235 goto fail;
236 }
237 dst_size = 2 + rec_size + (in->size - src_off);
238 ret = av_new_packet(out, dst_size);
239 if (ret < 0)
240 goto fail;
241 ret = av_packet_copy_props(out, in);
242 if (ret < 0)
243 goto fail;
244
245 memcpy(out->data, in->data, 2);
246 memcpy(out->data + 2, s->buf, rec_size);
247 memcpy(out->data + 2 + rec_size, in->data + src_off, in->size - src_off);
248 av_packet_free(&in);
249 return 0;
250
251fail:
252 if (ret < 0)
254 av_packet_free(&in);
255 return ret;
256}
257
258#define OFFSET(x) offsetof(JPEGAppContext, x)
259#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_BSF_PARAM)
260static const AVOption jpegapp_options[] = {
261 { "filename", "sidecar of u32be-length + APP-segment records", OFFSET(filename),
262 AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS },
263 { "file", "sidecar of u32be-length + APP-segment records", OFFSET(filename),
264 AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS },
265 { NULL },
266};
267
268static const AVClass jpegapp_class = {
269 .class_name = "jpegapp",
270 .item_name = av_default_item_name,
271 .option = jpegapp_options,
272 .version = LIBAVUTIL_VERSION_INT,
273};
274
278
280 .p.name = "jpegapp",
281 .p.codec_ids = jpegapp_codec_ids,
282 .p.priv_class = &jpegapp_class,
283 .priv_data_size = sizeof(JPEGAppContext),
287};
static FILE * out
static int out_size
static AVFormatContext * ctx
static av_cold void close(AVCodecParserContext *s)
Definition apv_parser.c:197
const FFBitStreamFilter ff_jpegapp_bsf
Definition jpegapp.c:279
static void BS_FUNC skip(BSCTX *bc, unsigned int n)
Skip n bits in the buffer.
int ff_bsf_get_packet(AVBSFContext *ctx, AVPacket **pkt)
Called by the bitstream filters to get the next packet for filtering.
Definition bsf.c:233
#define s(width, name)
Definition cbs_vp9.c:198
#define FLAGS
Definition cmdutils.c:596
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
int(* init)(AVBSFContext *ctx)
Definition dts2pts.c:608
error code definitions
#define fail
Definition test.h:479
@ AV_OPT_TYPE_STRING
Underlying C type is a uint8_t* that is either NULL or points to a C string allocated with the av_mal...
Definition opt.h:275
AVCodecID
Identify the syntax and semantics of the bitstream.
Definition codec_id.h:47
@ AV_CODEC_ID_NONE
Definition codec_id.h:48
@ AV_CODEC_ID_MJPEG
Definition codec_id.h:57
void av_packet_free(AVPacket **pkt)
Free the packet, if the packet is reference counted, it will be unreferenced first.
Definition packet.c:74
void av_packet_unref(AVPacket *pkt)
Wipe the packet.
Definition packet.c:435
int av_packet_ref(AVPacket *dst, const AVPacket *src)
Setup a new reference to the data described by a given packet.
Definition packet.c:443
int av_packet_copy_props(AVPacket *dst, const AVPacket *src)
Copy only "properties" fields from src to dst.
Definition packet.c:398
int av_new_packet(AVPacket *pkt, int size)
Allocate the payload of a packet and initialize its fields with default values.
Definition packet.c:98
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition error.h:61
#define av_err2str(errnum)
Convenience macro, the return value should be used only directly in function arguments but never stan...
Definition error.h:122
#define AVERROR(e)
Definition error.h:45
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
const char * av_default_item_name(void *ptr)
Return the context name.
Definition log.c:241
int av_reallocp(void *ptr, size_t size)
Allocate, reallocate, or free a block of memory through a pointer to a pointer.
Definition mem.c:289
#define LIBAVUTIL_VERSION_INT
Definition version.h:85
#define AV_RB32(p)
#define AV_RB16(p)
static int jpegapp_init(AVBSFContext *ctx)
Definition jpegapp.c:98
static int jpegapp_read_record(AVBSFContext *ctx, int *out_size)
Definition jpegapp.c:129
#define JPEGAPP_MAX_RECORD
Sidecar format (big-endian), one record per encoded JPEG packet:
Definition jpegapp.c:61
static int jpegapp_filter(AVBSFContext *ctx, AVPacket *out)
Definition jpegapp.c:190
static const AVClass jpegapp_class
Definition jpegapp.c:268
static int jpeg_app_segment_len(const uint8_t *data, int size)
Definition jpegapp.c:73
static int jpegapp_validate_payload(const uint8_t *data, int size)
Definition jpegapp.c:85
#define OFFSET(x)
Definition jpegapp.c:258
static const AVOption jpegapp_options[]
Definition jpegapp.c:260
static enum AVCodecID jpegapp_codec_ids[]
Definition jpegapp.c:275
static void jpegapp_close(AVBSFContext *ctx)
Definition jpegapp.c:117
FILE * avpriv_fopen_utf8(const char *path, const char *mode)
Open a file using a UTF-8 filename.
Definition file_open.c:160
Memory handling functions.
MJPEG encoder and decoder.
@ APP15
Definition mjpeg.h:94
@ APP0
Definition mjpeg.h:79
const char data[16]
Definition mxf.c:149
AVOptions.
unsigned int pos
Definition spdifenc.c:431
The bitstream filter state.
Definition bsf.h:68
Describe the class of an AVClass context structure.
Definition log.h:76
AVOption.
Definition opt.h:428
This structure stores compressed data.
Definition packet.h:587
int size
Definition packet.h:611
uint8_t * data
Definition packet.h:610
int64_t packet_index
Definition jpegapp.c:70
char * filename
Definition jpegapp.c:65
uint8_t * buf
Definition jpegapp.c:67
FILE * file
Definition jpegapp.c:66
#define av_freep(p)
#define av_log(a,...)
void(* filter)(uint8_t *src, ptrdiff_t stride, int qscale)
Definition h263dsp.c:29
int size
int len