FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/cbs_jpeg.c
Date: 2026-09-25 02:02:46
Exec Total Coverage
Lines: 0 189 0.0%
Functions: 0 6 0.0%
Branches: 0 148 0.0%

Line Branch Exec Source
1 /*
2 * This file is part of FFmpeg.
3 *
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 #include "libavutil/mem.h"
20 #include "cbs.h"
21 #include "cbs_internal.h"
22 #include "cbs_jpeg.h"
23
24
25 #define HEADER(name) do { \
26 ff_cbs_trace_header(ctx, name); \
27 } while (0)
28
29 #define CHECK(call) do { \
30 err = (call); \
31 if (err < 0) \
32 return err; \
33 } while (0)
34
35 #define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]){ subs, __VA_ARGS__ }) : NULL)
36
37 #define u(width, name, range_min, range_max) \
38 xu(width, name, range_min, range_max, 0, )
39 #define us(width, name, sub, range_min, range_max) \
40 xu(width, name, range_min, range_max, 1, sub)
41
42
43 #define READ
44 #define READWRITE read
45 #define RWContext GetBitContext
46 #define FUNC(name) cbs_jpeg_read_ ## name
47
48 #define xu(width, name, range_min, range_max, subs, ...) do { \
49 uint32_t value; \
50 CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \
51 SUBSCRIPTS(subs, __VA_ARGS__), \
52 &value, range_min, range_max)); \
53 current->name = value; \
54 } while (0)
55
56 #include "cbs_jpeg_syntax_template.c"
57
58 #undef READ
59 #undef READWRITE
60 #undef RWContext
61 #undef FUNC
62 #undef xu
63
64 #define WRITE
65 #define READWRITE write
66 #define RWContext PutBitContext
67 #define FUNC(name) cbs_jpeg_write_ ## name
68
69 #define xu(width, name, range_min, range_max, subs, ...) do { \
70 uint32_t value = current->name; \
71 CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \
72 SUBSCRIPTS(subs, __VA_ARGS__), \
73 value, range_min, range_max)); \
74 } while (0)
75
76
77 #include "cbs_jpeg_syntax_template.c"
78
79 #undef WRITE
80 #undef READWRITE
81 #undef RWContext
82 #undef FUNC
83 #undef xu
84
85
86 ✗ static int cbs_jpeg_split_fragment(CodedBitstreamContext *ctx,
87 CodedBitstreamFragment *frag,
88 int header)
89 {
90 AVBufferRef *data_ref;
91 uint8_t *data;
92 size_t data_size;
93 int start, end, marker, next_start, next_marker;
94 int err, i, j, length;
95
96 ✗ if (frag->data_size < 4) {
97 // Definitely too short to be meaningful.
98 ✗ return AVERROR_INVALIDDATA;
99 }
100
101 ✗ for (i = 0; i + 1 < frag->data_size && frag->data[i] != 0xff; i++);
102 ✗ if (i > 0) {
103 ✗ av_log(ctx->log_ctx, AV_LOG_WARNING, "Discarding %d bytes at "
104 "beginning of image.\n", i);
105 }
106 ✗ for (++i; i + 1 < frag->data_size && frag->data[i] == 0xff; i++);
107 ✗ if (i + 1 >= frag->data_size && frag->data[i]) {
108 ✗ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: "
109 "no SOI marker found.\n");
110 ✗ return AVERROR_INVALIDDATA;
111 }
112 ✗ marker = frag->data[i];
113 ✗ if (marker != JPEG_MARKER_SOI) {
114 ✗ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: first "
115 "marker is %02x, should be SOI.\n", marker);
116 ✗ return AVERROR_INVALIDDATA;
117 }
118 ✗ for (++i; i + 1 < frag->data_size && frag->data[i] == 0xff; i++);
119 ✗ if (i + 1 >= frag->data_size) {
120 ✗ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: "
121 "no image content found.\n");
122 ✗ return AVERROR_INVALIDDATA;
123 }
124 ✗ marker = frag->data[i];
125 ✗ start = i + 1;
126
127 do {
128 ✗ if (marker == JPEG_MARKER_EOI) {
129 ✗ break;
130 ✗ } else if (marker == JPEG_MARKER_SOS) {
131 ✗ next_marker = -1;
132 ✗ end = start;
133 ✗ for (i = start; i + 1 < frag->data_size; i++) {
134 ✗ if (frag->data[i] != 0xff)
135 ✗ continue;
136 ✗ end = i;
137 ✗ for (++i; i + 1 < frag->data_size &&
138 ✗ frag->data[i] == 0xff; i++);
139 ✗ if (i + 1 < frag->data_size) {
140 ✗ if (frag->data[i] == 0x00)
141 ✗ continue;
142 ✗ next_marker = frag->data[i];
143 ✗ next_start = i + 1;
144 }
145 ✗ break;
146 }
147 } else {
148 ✗ i = start;
149 ✗ if (i > frag->data_size - 2) {
150 ✗ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: "
151 "truncated at %02x marker.\n", marker);
152 ✗ return AVERROR_INVALIDDATA;
153 }
154 ✗ length = AV_RB16(frag->data + i);
155 ✗ if (length > frag->data_size - i) {
156 ✗ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: "
157 "truncated at %02x marker segment.\n", marker);
158 ✗ return AVERROR_INVALIDDATA;
159 }
160 ✗ end = start + length;
161
162 ✗ i = end;
163 ✗ if (frag->data[i] != 0xff) {
164 ✗ next_marker = -1;
165 } else {
166 ✗ for (++i; i + 1 < frag->data_size &&
167 ✗ frag->data[i] == 0xff; i++);
168 ✗ if (i + 1 >= frag->data_size) {
169 ✗ next_marker = -1;
170 } else {
171 ✗ next_marker = frag->data[i];
172 ✗ next_start = i + 1;
173 }
174 }
175 }
176
177 ✗ if (marker == JPEG_MARKER_SOS) {
178 ✗ length = AV_RB16(frag->data + start);
179
180 ✗ if (length > end - start)
181 ✗ return AVERROR_INVALIDDATA;
182
183 ✗ data_ref = NULL;
184 ✗ data = av_malloc(end - start +
185 AV_INPUT_BUFFER_PADDING_SIZE);
186 ✗ if (!data)
187 ✗ return AVERROR(ENOMEM);
188
189 ✗ memcpy(data, frag->data + start, length);
190 ✗ for (i = start + length, j = length; i < end; i++, j++) {
191 ✗ if (frag->data[i] == 0xff) {
192 ✗ while (frag->data[i] == 0xff)
193 ✗ ++i;
194 ✗ data[j] = 0xff;
195 } else {
196 ✗ data[j] = frag->data[i];
197 }
198 }
199 ✗ data_size = j;
200
201 ✗ memset(data + data_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
202
203 } else {
204 ✗ data = frag->data + start;
205 ✗ data_size = end - start;
206 ✗ data_ref = frag->data_ref;
207 }
208
209 ✗ err = ff_cbs_append_unit_data(frag, marker,
210 data, data_size, data_ref);
211 ✗ if (err < 0)
212 ✗ return err;
213
214 ✗ marker = next_marker;
215 ✗ start = next_start;
216 ✗ } while (next_marker != -1);
217
218 ✗ return 0;
219 }
220
221 ✗ static int cbs_jpeg_read_unit(CodedBitstreamContext *ctx,
222 CodedBitstreamUnit *unit)
223 {
224 GetBitContext gbc;
225 int err;
226
227 ✗ err = init_get_bits(&gbc, unit->data, 8 * unit->data_size);
228 ✗ if (err < 0)
229 ✗ return err;
230
231 ✗ err = ff_cbs_alloc_unit_content(ctx, unit);
232 ✗ if (err < 0)
233 ✗ return err;
234
235 ✗ if (unit->type >= JPEG_MARKER_SOF0 &&
236 ✗ unit->type <= JPEG_MARKER_SOF3) {
237 ✗ err = cbs_jpeg_read_frame_header(ctx, &gbc, unit->content);
238 ✗ if (err < 0)
239 ✗ return err;
240
241 ✗ } else if (unit->type >= JPEG_MARKER_APPN &&
242 ✗ unit->type <= JPEG_MARKER_APPN + 15) {
243 ✗ err = cbs_jpeg_read_application_data(ctx, &gbc, unit->content);
244 ✗ if (err < 0)
245 ✗ return err;
246
247 ✗ } else if (unit->type == JPEG_MARKER_SOS) {
248 ✗ JPEGRawScan *scan = unit->content;
249 int pos;
250
251 ✗ err = cbs_jpeg_read_scan_header(ctx, &gbc, &scan->header);
252 ✗ if (err < 0)
253 ✗ return err;
254
255 ✗ pos = get_bits_count(&gbc);
256 ✗ av_assert0(pos % 8 == 0);
257 ✗ if (pos > 0) {
258 ✗ scan->data_size = unit->data_size - pos / 8;
259 ✗ scan->data_ref = av_buffer_ref(unit->data_ref);
260 ✗ if (!scan->data_ref)
261 ✗ return AVERROR(ENOMEM);
262 ✗ scan->data = unit->data + pos / 8;
263 }
264
265 } else {
266 ✗ switch (unit->type) {
267 #define SEGMENT(marker, func) \
268 case JPEG_MARKER_ ## marker: \
269 { \
270 err = cbs_jpeg_read_ ## func(ctx, &gbc, unit->content); \
271 if (err < 0) \
272 return err; \
273 } \
274 break
275 ✗ SEGMENT(DQT, dqt);
276 ✗ SEGMENT(DHT, dht);
277 ✗ SEGMENT(COM, comment);
278 #undef SEGMENT
279 ✗ default:
280 ✗ return AVERROR(ENOSYS);
281 }
282 }
283
284 ✗ return 0;
285 }
286
287 ✗ static int cbs_jpeg_write_scan(CodedBitstreamContext *ctx,
288 CodedBitstreamUnit *unit,
289 PutBitContext *pbc)
290 {
291 ✗ JPEGRawScan *scan = unit->content;
292 int err;
293
294 ✗ err = cbs_jpeg_write_scan_header(ctx, pbc, &scan->header);
295 ✗ if (err < 0)
296 ✗ return err;
297
298 ✗ if (scan->data) {
299 ✗ if (scan->data_size * 8 > put_bits_left(pbc))
300 ✗ return AVERROR(ENOSPC);
301
302 ✗ av_assert0(put_bits_count(pbc) % 8 == 0);
303
304 ✗ flush_put_bits(pbc);
305
306 ✗ memcpy(put_bits_ptr(pbc), scan->data, scan->data_size);
307 ✗ skip_put_bytes(pbc, scan->data_size);
308 }
309
310 ✗ return 0;
311 }
312
313 ✗ static int cbs_jpeg_write_segment(CodedBitstreamContext *ctx,
314 CodedBitstreamUnit *unit,
315 PutBitContext *pbc)
316 {
317 int err;
318
319 ✗ if (unit->type >= JPEG_MARKER_SOF0 &&
320 ✗ unit->type <= JPEG_MARKER_SOF3) {
321 ✗ err = cbs_jpeg_write_frame_header(ctx, pbc, unit->content);
322 ✗ } else if (unit->type >= JPEG_MARKER_APPN &&
323 ✗ unit->type <= JPEG_MARKER_APPN + 15) {
324 ✗ err = cbs_jpeg_write_application_data(ctx, pbc, unit->content);
325 } else {
326 ✗ switch (unit->type) {
327 #define SEGMENT(marker, func) \
328 case JPEG_MARKER_ ## marker: \
329 err = cbs_jpeg_write_ ## func(ctx, pbc, unit->content); \
330 break;
331 ✗ SEGMENT(DQT, dqt);
332 ✗ SEGMENT(DHT, dht);
333 ✗ SEGMENT(COM, comment);
334 ✗ default:
335 ✗ return AVERROR_PATCHWELCOME;
336 }
337 }
338
339 ✗ return err;
340 }
341
342 ✗ static int cbs_jpeg_write_unit(CodedBitstreamContext *ctx,
343 CodedBitstreamUnit *unit,
344 PutBitContext *pbc)
345 {
346 ✗ if (unit->type == JPEG_MARKER_SOS)
347 ✗ return cbs_jpeg_write_scan (ctx, unit, pbc);
348 else
349 ✗ return cbs_jpeg_write_segment(ctx, unit, pbc);
350 }
351
352 ✗ static int cbs_jpeg_assemble_fragment(CodedBitstreamContext *ctx,
353 CodedBitstreamFragment *frag)
354 {
355 const CodedBitstreamUnit *unit;
356 uint8_t *data;
357 size_t size, dp, sp;
358 int i;
359
360 ✗ size = 4; // SOI + EOI.
361 ✗ for (i = 0; i < frag->nb_units; i++) {
362 ✗ unit = &frag->units[i];
363 ✗ size += 2 + unit->data_size;
364 ✗ if (unit->type == JPEG_MARKER_SOS) {
365 ✗ for (sp = 0; sp < unit->data_size; sp++) {
366 ✗ if (unit->data[sp] == 0xff)
367 ✗ ++size;
368 }
369 }
370 }
371
372 ✗ frag->data_ref = av_buffer_alloc(size + AV_INPUT_BUFFER_PADDING_SIZE);
373 ✗ if (!frag->data_ref)
374 ✗ return AVERROR(ENOMEM);
375 ✗ data = frag->data_ref->data;
376
377 ✗ dp = 0;
378
379 ✗ data[dp++] = 0xff;
380 ✗ data[dp++] = JPEG_MARKER_SOI;
381
382 ✗ for (i = 0; i < frag->nb_units; i++) {
383 ✗ unit = &frag->units[i];
384
385 ✗ data[dp++] = 0xff;
386 ✗ data[dp++] = unit->type;
387
388 ✗ if (unit->type != JPEG_MARKER_SOS) {
389 ✗ memcpy(data + dp, unit->data, unit->data_size);
390 ✗ dp += unit->data_size;
391 } else {
392 ✗ sp = AV_RB16(unit->data);
393 ✗ av_assert0(sp <= unit->data_size);
394 ✗ memcpy(data + dp, unit->data, sp);
395 ✗ dp += sp;
396
397 ✗ for (; sp < unit->data_size; sp++) {
398 ✗ if (unit->data[sp] == 0xff) {
399 ✗ data[dp++] = 0xff;
400 ✗ data[dp++] = 0x00;
401 } else {
402 ✗ data[dp++] = unit->data[sp];
403 }
404 }
405 }
406 }
407
408 ✗ data[dp++] = 0xff;
409 ✗ data[dp++] = JPEG_MARKER_EOI;
410
411 ✗ av_assert0(dp == size);
412
413 ✗ memset(data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
414 ✗ frag->data = data;
415 ✗ frag->data_size = size;
416
417 ✗ return 0;
418 }
419
420 static CodedBitstreamUnitTypeDescriptor cbs_jpeg_unit_types[] = {
421 CBS_UNIT_RANGE_POD(JPEG_MARKER_SOF0, JPEG_MARKER_SOF3, JPEGRawFrameHeader),
422
423 CBS_UNIT_RANGE_INTERNAL_REF(JPEG_MARKER_APPN, JPEG_MARKER_APPN + 15,
424 JPEGRawApplicationData, Ap),
425
426 CBS_UNIT_TYPE_INTERNAL_REF(JPEG_MARKER_SOS, JPEGRawScan, data),
427
428 CBS_UNIT_TYPE_POD(JPEG_MARKER_DQT, JPEGRawQuantisationTableSpecification),
429 CBS_UNIT_TYPE_POD(JPEG_MARKER_DHT, JPEGRawHuffmanTableSpecification),
430
431 CBS_UNIT_TYPE_INTERNAL_REF(JPEG_MARKER_COM, JPEGRawComment, Cm),
432
433 CBS_UNIT_TYPE_END_OF_LIST
434 };
435
436 const CodedBitstreamType ff_cbs_type_jpeg = {
437 .codec_id = AV_CODEC_ID_MJPEG,
438
439 .unit_types = cbs_jpeg_unit_types,
440
441 .split_fragment = &cbs_jpeg_split_fragment,
442 .read_unit = &cbs_jpeg_read_unit,
443 .write_unit = &cbs_jpeg_write_unit,
444 .assemble_fragment = &cbs_jpeg_assemble_fragment,
445 };
446