// SPDX-License-Identifier: GPL-2.0-only /* * V4L2 JPEG header parser helpers. * * Copyright (C) 2019 Pengutronix, Philipp Zabel * * For reference, see JPEG ITU-T.81 (ISO/IEC 10918-1) [1] * * [1] https://www.w3.org/Graphics/JPEG/itu-t81.pdf */ #include #include #include #include #include #include MODULE_DESCRIPTION("V4L2 JPEG header parser helpers"); MODULE_AUTHOR("Philipp Zabel "); MODULE_LICENSE("GPL"); /* Table B.1 - Marker code assignments */ #define SOF0 0xffc0 /* start of frame */ #define SOF1 0xffc1 #define SOF2 0xffc2 #define SOF3 0xffc3 #define SOF5 0xffc5 #define SOF7 0xffc7 #define JPG 0xffc8 /* extensions */ #define SOF9 0xffc9 #define SOF11 0xffcb #define SOF13 0xffcd #define SOF15 0xffcf #define DHT 0xffc4 /* huffman table */ #define DAC 0xffcc /* arithmetic coding conditioning */ #define RST0 0xffd0 /* restart */ #define RST7 0xffd7 #define SOI 0xffd8 /* start of image */ #define EOI 0xffd9 /* end of image */ #define SOS 0xffda /* start of stream */ #define DQT 0xffdb /* quantization table */ #define DNL 0xffdc /* number of lines */ #define DRI 0xffdd /* restart interval */ #define DHP 0xffde /* hierarchical progression */ #define EXP 0xffdf /* expand reference */ #define APP0 0xffe0 /* application data */ #define APP14 0xffee /* application data for colour encoding */ #define APP15 0xffef #define JPG0 0xfff0 /* extensions */ #define JPG13 0xfffd #define COM 0xfffe /* comment */ #define TEM 0xff01 /* temporary */ /** * struct jpeg_stream - JPEG byte stream * @curr: current position in stream * @end: end position, after last byte */ struct jpeg_stream { u8 *curr; u8 *end; }; /* returns a value that fits into u8, or negative error */ static int jpeg_get_byte(struct jpeg_stream *stream) { if (stream->curr >= stream->end) return -EINVAL; return *stream->curr++; } /* returns a value that fits into u16, or negative error */ static int jpeg_get_word_be(struct jpeg_stream *stream) { u16 word; if (stream->curr + sizeof(__be16) > stream->end) return -EINVAL; word = get_unaligned_be16(stream->curr); stream->curr += sizeof(__be16); return word; } static int jpeg_skip(struct jpeg_stream *stream, size_t len) { if (stream->curr + len > stream->end) return -EINVAL; stream->curr += len; return 0; } static int jpeg_next_marker(struct jpeg_stream *stream) { int byte; u16 marker = 0; while ((byte = jpeg_get_byte(stream)) >= 0) { marker = (marker << 8) | byte; /* skip stuffing bytes and REServed markers */ if (marker == TEM || (marker > 0xffbf && marker < 0xffff)) return marker; } return byte; } /* this does not advance the current position in the stream */ static int jpeg_reference_segment(struct jpeg_stream *stream, struct v4l2_jpeg_reference *segment) { u16 len; if (stream->curr + sizeof(__be16) > stream->end) return -EINVAL; len = get_unaligned_be16(stream->curr); if (stream->curr + len > stream->end) return -EINVAL; segment->start = stream->curr; segment->length = len; return 0; } static int v4l2_jpeg_decode_subsampling(u8 nf, u8 h_v) { if (nf == 1) return V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY; /* no chroma subsampling for 4-component images */ if (nf == 4 && h_v != 0x11) return -EINVAL; switch (h_v) { case 0x11: return V4L2_JPEG_CHROMA_SUBSAMPLING_444; case 0x21: return V4L2_JPEG_CHROMA_SUBSAMPLING_422; case 0x22: return V4L2_JPEG_CHROMA_SUBSAMPLING_420; case 0x41: return V4L2_JPEG_CHROMA_SUBSAMPLING_411; default: return -EINVAL; } } static int jpeg_parse_frame_header(struct jpeg_stream *stream, u16 sof_marker, struct v4l2_jpeg_frame_header *frame_header) { int len = jpeg_get_word_be(stream); if (len < 0) return len; /* Lf = 8 + 3 * Nf, Nf >= 1 */ if (len < 8 + 3) return -EINVAL; if (frame_header) { /* Table B.2 - Frame header parameter sizes and values */ int p, y, x, nf; int i; p = jpeg_get_byte(stream); if (p < 0) return p; /* * Baseline DCT only supports 8-bit precision. * Extended sequential DCT also supports 12-bit precision. */ if (p != 8 && (p != 12 || sof_marker != SOF1)) return -EINVAL; y = jpeg_get_word_be(stream); if (y < 0) return y; if (y == 0) return -EINVAL; x = jpeg_get_word_be(stream); if (x < 0) return x; if (x == 0) return -EINVAL; nf = jpeg_get_byte(stream); if (nf < 0) return nf; /* * The spec allows 1 <= Nf <= 255, but we only support up to 4 * components. */ if (nf < 1 || nf > V4L2_JPEG_MAX_COMPONENTS) return -EINVAL; if (len != 8 + 3 * nf) return -EINVAL; frame_header->precision = p; frame_header->height = y; frame_header->width = x; frame_header->num_components = nf; for (i = 0; i < nf; i++) { struct v4l2_jpeg_frame_component_spec *component; int c, h_v, tq; c = jpeg_get_byte(stream); if (c < 0) return c; h_v = jpeg_get_byte(stream); if (h_v < 0) return h_v; if (i == 0) { int subs; subs = v4l2_jpeg_decode_subsampling(nf, h_v); if (subs < 0) return subs; frame_header->subsampling = subs; } else if (h_v != 0x11) { /* all chroma sampling factors must be 1 */ return -EINVAL; } tq = jpeg_get_byte(stream); if (tq < 0) return tq; component = &frame_header->component[i]; component->component_identifier = c; component->horizontal_sampling_factor = (h_v >> 4) & 0xf; component->vertical_sampling_factor = h_v & 0xf; component->quantization_table_selector = tq; } } else { return jpeg_skip(stream, len - 2); } return 0; } static int jpeg_parse_scan_header(struct jpeg_stream *stream, struct v4l2_jpeg_scan_header *scan_header) { size_t skip; int len = jpeg_get_word_be(stream); if (len < 0) return len; /* Ls = 8 + 3 * Ns, Ns >= 1 */ if (len < 6 + 2) return -EINVAL; if (scan_header) { int ns; int i; ns = jpeg_get_byte(stream); if (ns < 0) return ns; if (ns < 1 || ns > 4 || len != 6 + 2 * ns) return -EINVAL; scan_header->num_components = ns; for (i = 0; i < ns; i++) { struct v4l2_jpeg_scan_component_spec *component; int cs, td_ta; cs = jpeg_get_byte(stream); if (cs < 0) return cs; td_ta = jpeg_get_byte(stream); if (td_ta < 0) return td_ta; component = &scan_header->component[i]; component->component_selector = cs; component->dc_entropy_coding_table_selector = (td_ta >> 4) & 0xf; component->ac_entropy_coding_table_selector = td_ta & 0xf; } skip = 3; /* skip Ss, Se, Ah, and Al */ } else { skip = len - 2; } return jpeg_skip(stream, skip); } /* B.2.4.1 Quantization table-specification syntax */ static int jpeg_parse_quantization_tables(struct jpeg_stream *stream, u8 precision, struct v4l2_jpeg_reference *tables) { int len = jpeg_get_word_be(stream); if (len < 0) return len; /* Lq = 2 + n * 65 (for baseline DCT), n >= 1 */ if (len < 2 + 65) return -EINVAL; len -= 2; while (len >= 65) { u8 pq, tq, *qk; int ret; int pq_tq = jpeg_get_byte(stream); if (pq_tq < 0) return pq_tq; /* quantization table element precision */ pq = (pq_tq >> 4) & 0xf; /* * Only 8-bit Qk values for 8-bit sample precision. Extended * sequential DCT with 12-bit sample precision also supports * 16-bit Qk values. */ if (pq != 0 && (pq != 1 || precision != 12)) return -EINVAL; /* quantization table destination identifier */ tq = pq_tq & 0xf; if (tq > 3) return -EINVAL; /* quantization table element */ qk = stream->curr; ret = jpeg_skip(stream, pq ? 128 : 64); if (ret < 0) return -EINVAL; if (tables) { tables[tq].start = qk; tables[tq].length = pq ? 128 : 64; } len -= pq ? 129 : 65; } return 0; } /* B.2.4.2 Huffman table-specification syntax */ static int jpeg_parse_huffman_tables(struct jpeg_stream *stream, struct v4l2_jpeg_reference *tables) { int mt; int len = jpeg_get_word_be(stream); if (len < 0) return len; /* Table B.5 - Huffman table specification parameter sizes and values */ if (len < 2 + 17) return -EINVAL; for (len -= 2; len >= 17; len -= 17 + mt) { u8 tc, th, *table; int tc_th = jpeg_get_byte(stream); int i, ret; if (tc_th < 0) return tc_th; /* table class - 0 = DC, 1 = AC */ tc = (tc_th >> 4) & 0xf; if (tc > 1) return -EINVAL; /* huffman table destination identifier */ th = tc_th & 0xf; /* only two Huffman tables for baseline DCT */ if (th > 1) return -EINVAL; /* BITS - number of Huffman codes with length i */ table = stream->curr; mt = 0; for (i = 0; i < 16; i++) { int li; li = jpeg_get_byte(stream); if (li < 0) return li; mt += li; } /* HUFFVAL - values associated with each Huffman code */ ret = jpeg_skip(stream, mt); if (ret < 0) return ret; if (tables) { tables[(tc << 1) | th].start = table; tables[(tc << 1) | th].length = stream->curr - table; } } return jpeg_skip(stream, len - 2); } /* B.2.4.4 Restart interval definition syntax */ static int jpeg_parse_restart_interval(struct jpeg_stream *stream, u16 *restart_interval) { int len = jpeg_get_word_be(stream); int ri; if (len < 0) return len; if (len != 4) return -EINVAL; ri = jpeg_get_word_be(stream); if (ri < 0) return ri; *restart_interval = ri; return 0; } static int jpeg_skip_segment(struct jpeg_stream *stream) { int len = jpeg_get_word_be(stream); if (len < 0) return len; if (len < 2) return -EINVAL; return jpeg_skip(stream, len - 2); } /* Rec. ITU-T T.872 (06/2012) 6.5.3 */ static int jpeg_parse_app14_data(struct jpeg_stream *stream, enum v4l2_jpeg_app14_tf *tf) { int ret; int lp; int skip; lp = jpeg_get_word_be(stream); if (lp < 0) return lp; /* Check for "Adobe\0" in Ap1..6 */ if (stream->curr + 6 > stream->end || strncmp(stream->curr, "Adobe\0", 6)) return jpeg_skip(stream, lp - 2); /* get to Ap12 */ ret = jpeg_skip(stream, 11); if (ret < 0) return ret; ret = jpeg_get_byte(stream); if (ret < 0) return ret; *tf = ret; /* skip the rest of the segment, this ensures at least it is complete */ skip = lp - 2 - 11 - 1; return jpeg_skip(stream, skip); } /** * v4l2_jpeg_parse_header - locate marker segments and optionally parse headers * @buf: address of the JPEG buffer, should start with a SOI marker * @len: length of the JPEG buffer * @out: returns marker segment positions and optionally parsed headers * * The out->scan_header pointer must be initialized to NULL or point to a valid * v4l2_jpeg_scan_header structure. The out->huffman_tables and * out->quantization_tables pointers must be initialized to NULL or point to a * valid array of 4 v4l2_jpeg_reference structures each. * * Returns 0 or negative error if parsing failed. */ int v4l2_jpeg_parse_header(void *buf, size_t len, struct v4l2_jpeg_header *out) { struct jpeg_stream stream; int marker; int ret = 0; stream.curr = buf; stream.end = stream.curr + len; out->num_dht = 0; out->num_dqt = 0; /* the first bytes must be SOI, B.2.1 High-level syntax */ if (jpeg_get_word_be(&stream) != SOI) return -EINVAL; /* init value to signal if this marker is not present */ out->app14_tf = V4L2_JPEG_APP14_TF_UNKNOWN; /* loop through marker segments */ while ((marker = jpeg_next_marker(&stream)) >= 0) { switch (marker) { /* baseline DCT, extended sequential DCT */ case SOF0 ... SOF1: ret = jpeg_reference_segment(&stream, &out->sof); if (ret < 0) return ret; ret = jpeg_parse_frame_header(&stream, marker, &out->frame); break; /* progressive, lossless */ case SOF2 ... SOF3: /* differential coding */ case SOF5 ... SOF7: /* arithmetic coding */ case SOF9 ... SOF11: case SOF13 ... SOF15: case DAC: case TEM: return -EINVAL; case DHT: ret = jpeg_reference_segment(&stream, &out->dht[out->num_dht++ % 4]); if (ret < 0) return ret; if (!out->huffman_tables) { ret = jpeg_skip_segment(&stream); break; } ret = jpeg_parse_huffman_tables(&stream, out->huffman_tables); break; case DQT: ret = jpeg_reference_segment(&stream, &out->dqt[out->num_dqt++ % 4]); if (ret < 0) return ret; if (!out->quantization_tables) { ret = jpeg_skip_segment(&stream); break; } ret = jpeg_parse_quantization_tables(&stream, out->frame.precision, out->quantization_tables); break; case DRI: ret = jpeg_parse_restart_interval(&stream, &out->restart_interval); break; case APP14: ret = jpeg_parse_app14_data(&stream, &out->app14_tf); break; case SOS: ret = jpeg_reference_segment(&stream, &out->sos); if (ret < 0) return ret; ret = jpeg_parse_scan_header(&stream, out->scan); /* * stop parsing, the scan header marks the beginning of * the entropy coded segment */ out->ecs_offset = stream.curr - (u8 *)buf; return ret; /* markers without parameters */ case RST0 ... RST7: /* restart */ case SOI: /* start of image */ case EOI: /* end of image */ break; /* skip unknown or unsupported marker segments */ default: ret = jpeg_skip_segment(&stream); break; } if (ret < 0) return ret; } return marker; } EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_header); /** * v4l2_jpeg_parse_frame_header - parse frame header * @buf: address of the frame header, after the SOF0 marker * @len: length of the frame header * @frame_header: returns the parsed frame header * * Returns 0 or negative error if parsing failed. */ int v4l2_jpeg_parse_frame_header(void *buf, size_t len, struct v4l2_jpeg_frame_header *frame_header) { struct jpeg_stream stream; stream.curr = buf; stream.end = stream.curr + len; return jpeg_parse_frame_header(&stream, SOF0, frame_header); } EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_frame_header); /** * v4l2_jpeg_parse_scan_header - parse scan header * @buf: address of the scan header, after the SOS marker * @len: length of the scan header * @scan_header: returns the parsed scan header * * Returns 0 or negative error if parsing failed. */ int v4l2_jpeg_parse_scan_header(void *buf, size_t len, struct v4l2_jpeg_scan_header *scan_header) { struct jpeg_stream stream; stream.curr = buf; stream.end = stream.curr + len; return jpeg_parse_scan_header(&stream, scan_header); } EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_scan_header); /** * v4l2_jpeg_parse_quantization_tables - parse quantization tables segment * @buf: address of the quantization table segment, after the DQT marker * @len: length of the quantization table segment * @precision: sample precision (P) in bits per component * @q_tables: returns four references into the buffer for the * four possible quantization table destinations * * Returns 0 or negative error if parsing failed. */ int v4l2_jpeg_parse_quantization_tables(void *buf, size_t len, u8 precision, struct v4l2_jpeg_reference *q_tables) { struct jpeg_stream stream; stream.curr = buf; stream.end = stream.curr + len; return jpeg_parse_quantization_tables(&stream, precision, q_tables); } EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_quantization_tables); /** * v4l2_jpeg_parse_huffman_tables - parse huffman tables segment * @buf: address of the Huffman table segment, after the DHT marker * @len: length of the Huffman table segment * @huffman_tables: returns four references into the buffer for the * four possible Huffman table destinations, in * the order DC0, DC1, AC0, AC1 * * Returns 0 or negative error if parsing failed. */ int v4l2_jpeg_parse_huffman_tables(void *buf, size_t len, struct v4l2_jpeg_reference *huffman_tables) { struct jpeg_stream stream; stream.curr = buf; stream.end = stream.curr + len; return jpeg_parse_huffman_tables(&stream, huffman_tables); } EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_huffman_tables);