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"
60#include "libavcodec/mjpeg.h"
61
62#define JPEGAPP_MAX_RECORD (16 * 1024 * 1024)
63
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->sidecar_len <= 0) {
103 av_log(ctx, AV_LOG_ERROR, "Option sidecar is required\n");
104 return AVERROR(EINVAL);
105 }
106
107 bytestream2_init(&s->gb, s->sidecar, s->sidecar_len);
108
109 return 0;
110}
111
113{
114 JPEGAppContext *s = ctx->priv_data;
115 uint32_t n;
116 int ret;
117
118 if (!bytestream2_get_bytes_left(&s->gb)) {
120 "jpegapp: no sidecar record for packet %"PRId64"\n",
121 s->packet_index);
122 return AVERROR_INVALIDDATA;
123 }
124 if (bytestream2_get_bytes_left(&s->gb) < 4) {
125 av_log(ctx, AV_LOG_ERROR, "jpegapp: truncated length prefix\n");
126 return AVERROR_INVALIDDATA;
127 }
128 n = bytestream2_get_be32(&s->gb);
129 if (n > JPEGAPP_MAX_RECORD) {
130 av_log(ctx, AV_LOG_ERROR, "jpegapp: record length %u too large\n", n);
131 return AVERROR_INVALIDDATA;
132 }
133 if (n) {
134 if (n > bytestream2_get_bytes_left(&s->gb)) {
135 av_log(ctx, AV_LOG_ERROR, "jpegapp: truncated record payload\n");
136 return AVERROR_INVALIDDATA;
137 }
138 ret = jpegapp_validate_payload(s->gb.buffer, n);
139 if (ret < 0) {
141 "jpegapp: payload is not a sequence of APP0..APP15 segments\n");
142 return ret;
143 }
144 }
145 *out_size = n;
146 return 0;
147}
148
150{
151 JPEGAppContext *s = ctx->priv_data;
152 AVPacket *in;
153 int rec_size = 0, src_off, dst_size, ret;
154
155 ret = ff_bsf_get_packet(ctx, &in);
156 if (ret < 0)
157 return ret;
158
159 ret = s->read_error;
160 if (!ret)
161 ret = jpegapp_read_record(ctx, &rec_size);
162 s->packet_index++;
163 if (ret < 0) {
164 s->read_error = ret;
165 goto fail;
166 }
167
168 if (in->size < 2 || AV_RB16(in->data) != 0xffd8) {
169 av_log(ctx, AV_LOG_ERROR, "jpegapp: packet is not JPEG (missing SOI)\n");
171 goto fail;
172 }
173
174 src_off = 2;
175 if (rec_size >= 2 && s->gb.buffer[0] == 0xff && s->gb.buffer[1] == APP0 &&
176 in->size >= 4 && in->data[2] == 0xff && in->data[3] == APP0) {
177 int skip = jpeg_app_segment_len(in->data + 2, in->size - 2);
178 if (skip < 0) {
180 "jpegapp: existing APP0 is invalid or truncated\n");
181 ret = skip;
182 goto fail;
183 }
184 src_off = 2 + skip;
185 }
186
187 if (!rec_size) {
188 ret = av_packet_ref(out, in);
189 goto fail;
190 }
191
192 if (in->size - src_off > INT_MAX - 2 - rec_size) {
193 ret = AVERROR(ERANGE);
194 goto fail;
195 }
196 dst_size = 2 + rec_size + (in->size - src_off);
197 ret = av_new_packet(out, dst_size);
198 if (ret < 0)
199 goto fail;
201 if (ret < 0)
202 goto fail;
203
204 memcpy(out->data, in->data, 2);
205 bytestream2_get_buffer(&s->gb, out->data + 2, rec_size);
206 memcpy(out->data + 2 + rec_size, in->data + src_off, in->size - src_off);
207 av_packet_free(&in);
208 return 0;
209
210fail:
211 if (ret < 0)
213 av_packet_free(&in);
214 return ret;
215}
216
217#define OFFSET(x) offsetof(JPEGAppContext, x)
218#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_BSF_PARAM)
219static const AVOption jpegapp_options[] = {
220 { "sidecar", "sidecar of u32be-length + APP-segment records", OFFSET(sidecar),
221 AV_OPT_TYPE_BINARY, .flags = FLAGS },
222 { NULL },
223};
224
225static const AVClass jpegapp_class = {
226 .class_name = "jpegapp",
227 .item_name = av_default_item_name,
228 .option = jpegapp_options,
229 .version = LIBAVUTIL_VERSION_INT,
230};
231
235
237 .p.name = "jpegapp",
238 .p.codec_ids = jpegapp_codec_ids,
239 .p.priv_class = &jpegapp_class,
240 .priv_data_size = sizeof(JPEGAppContext),
243};
static FILE * out
static int out_size
static AVFormatContext * ctx
const FFBitStreamFilter ff_jpegapp_bsf
Definition jpegapp.c:236
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
static av_always_inline unsigned int bytestream2_get_buffer(GetByteContext *g, uint8_t *dst, unsigned int size)
Definition bytestream.h:267
static av_always_inline int bytestream2_get_bytes_left(const GetByteContext *g)
Definition bytestream.h:158
static av_always_inline void bytestream2_init(GetByteContext *g, const uint8_t *buf, int buf_size)
Definition bytestream.h:137
#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:613
error code definitions
int ret
Definition ffplay.c:391
#define fail
Definition test.h:479
@ AV_OPT_TYPE_BINARY
Underlying C type is a uint8_t* that is either NULL or points to an array allocated with the av_mallo...
Definition opt.h:285
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 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:247
#define LIBAVUTIL_VERSION_INT
Definition version.h:85
#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:112
#define JPEGAPP_MAX_RECORD
Sidecar format (big-endian), one record per encoded JPEG packet:
Definition jpegapp.c:62
static int jpegapp_filter(AVBSFContext *ctx, AVPacket *out)
Definition jpegapp.c:149
static const AVClass jpegapp_class
Definition jpegapp.c:225
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:217
static const AVOption jpegapp_options[]
Definition jpegapp.c:219
static enum AVCodecID jpegapp_codec_ids[]
Definition jpegapp.c:232
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
GetByteContext gb
Definition jpegapp.c:66
int64_t packet_index
Definition jpegapp.c:70
int sidecar_len
Definition jpegapp.c:68
uint8_t * sidecar
Definition jpegapp.c:67
#define av_log(a,...)
void(* filter)(uint8_t *src, ptrdiff_t stride, int qscale)
Definition h263dsp.c:29
int size
int len