FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/cbs_jpeg.c
Date: 2024-04-19 17:50:32
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 + 2 > frag->data_size) {
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 (i + length > frag->data_size) {
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 const 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