1 /*
2 # (C) 2008-2011 Hans de Goede <hdegoede@redhat.com>
3
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU Lesser General Public License as published by
6 # the Free Software Foundation; either version 2.1 of the License, or
7 # (at your option) any later version.
8 #
9 # This program 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
12 # GNU Lesser General Public License for more details.
13 #
14 # You should have received a copy of the GNU Lesser General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
17 */
18
19 #include <errno.h>
20 #include <stdlib.h>
21 #include "libv4lconvert-priv.h"
22 #ifdef HAVE_JPEG
23 #include "jpeg_memsrcdest.h"
24 #endif
25
v4lconvert_decode_jpeg_tinyjpeg(struct v4lconvert_data * data,unsigned char * src,int src_size,unsigned char * dest,struct v4l2_format * fmt,unsigned int dest_pix_fmt,int flags)26 int v4lconvert_decode_jpeg_tinyjpeg(struct v4lconvert_data *data,
27 unsigned char *src, int src_size, unsigned char *dest,
28 struct v4l2_format *fmt, unsigned int dest_pix_fmt, int flags)
29 {
30 int result = 0;
31 unsigned char *components[3];
32 unsigned int header_width, header_height;
33 unsigned int width = fmt->fmt.pix.width;
34 unsigned int height = fmt->fmt.pix.height;
35
36 if (!data->tinyjpeg) {
37 data->tinyjpeg = tinyjpeg_init();
38 if (!data->tinyjpeg)
39 return v4lconvert_oom_error(data);
40 }
41 flags |= TINYJPEG_FLAGS_MJPEG_TABLE;
42 tinyjpeg_set_flags(data->tinyjpeg, flags);
43 if (tinyjpeg_parse_header(data->tinyjpeg, src, src_size)) {
44 V4LCONVERT_ERR("parsing JPEG header: %s",
45 tinyjpeg_get_errorstring(data->tinyjpeg));
46 errno = EAGAIN;
47 return -1;
48 }
49 tinyjpeg_get_size(data->tinyjpeg, &header_width, &header_height);
50
51 if (data->control_flags & V4LCONTROL_ROTATED_90_JPEG) {
52 unsigned int tmp = width;
53 width = height;
54 height = tmp;
55 }
56
57 if (header_width != width || header_height != height) {
58 V4LCONVERT_ERR("unexpected width / height in JPEG header: "
59 "expected: %ux%u, header: %ux%u\n",
60 width, height, header_width, header_height);
61 errno = EIO;
62 return -1;
63 }
64 fmt->fmt.pix.width = header_width;
65 fmt->fmt.pix.height = header_height;
66
67 components[0] = dest;
68
69 switch (dest_pix_fmt) {
70 case V4L2_PIX_FMT_RGB24:
71 tinyjpeg_set_components(data->tinyjpeg, components, 1);
72 result = tinyjpeg_decode(data->tinyjpeg, TINYJPEG_FMT_RGB24);
73 break;
74 case V4L2_PIX_FMT_BGR24:
75 tinyjpeg_set_components(data->tinyjpeg, components, 1);
76 result = tinyjpeg_decode(data->tinyjpeg, TINYJPEG_FMT_BGR24);
77 break;
78 case V4L2_PIX_FMT_YUV420:
79 components[1] = components[0] + width * height;
80 components[2] = components[1] + width * height / 4;
81 tinyjpeg_set_components(data->tinyjpeg, components, 3);
82 result = tinyjpeg_decode(data->tinyjpeg, TINYJPEG_FMT_YUV420P);
83 break;
84 case V4L2_PIX_FMT_YVU420:
85 components[2] = components[0] + width * height;
86 components[1] = components[2] + width * height / 4;
87 tinyjpeg_set_components(data->tinyjpeg, components, 3);
88 result = tinyjpeg_decode(data->tinyjpeg, TINYJPEG_FMT_YUV420P);
89 break;
90 }
91
92 if (result) {
93 /* The JPEG header checked out ok but we got an error
94 during decompression. Some webcams, esp pixart and
95 sn9c20x based ones regulary generate corrupt frames,
96 which are best thrown away to avoid flashes in the
97 video stream. We use EPIPE to signal the upper layer
98 we have some video data, but it is incomplete.
99
100 The upper layer (usually libv4l2) should respond to
101 this by trying a number of times to get a new frame
102 and if that fails just passing up whatever we did
103 manage to decompress. */
104 V4LCONVERT_ERR("decompressing JPEG: %s",
105 tinyjpeg_get_errorstring(data->tinyjpeg));
106 errno = EPIPE;
107 return -1;
108 }
109 return 0;
110 }
111
112 #ifdef HAVE_JPEG
113
jerr_error_exit(j_common_ptr cinfo)114 static void jerr_error_exit(j_common_ptr cinfo)
115 {
116 struct v4lconvert_data *data = cinfo->client_data;
117
118 longjmp(data->jerr_jmp_state, data->jerr_errno);
119 }
120
jerr_emit_message(j_common_ptr cinfo,int msg_level)121 static void jerr_emit_message(j_common_ptr cinfo, int msg_level)
122 {
123 char buffer[JMSG_LENGTH_MAX];
124 struct v4lconvert_data *data = cinfo->client_data;
125
126 /* < -1 error, == -1 warning, >= 0 trace */
127 if (msg_level < -1)
128 return;
129
130 cinfo->err->format_message(cinfo, buffer);
131 snprintf(data->error_msg, V4LCONVERT_ERROR_MSG_SIZE,
132 "v4l-convert: libjpeg error: %s\n", buffer);
133 }
134
init_libjpeg_cinfo(struct v4lconvert_data * data)135 static void init_libjpeg_cinfo(struct v4lconvert_data *data)
136 {
137 struct jpeg_compress_struct cinfo;
138 unsigned char *jpeg_header = NULL;
139 unsigned long jpeg_header_size = 0;
140
141 if (data->cinfo_initialized)
142 return;
143
144 /* Setup our error handling */
145 jpeg_std_error(&data->jerr);
146 data->jerr.error_exit = jerr_error_exit;
147 data->jerr.emit_message = jerr_emit_message;
148
149 /* Create a jpeg compression object with default params and write
150 default jpeg headers to a mem buffer, so that we can use them to
151 pre-fill a jpeg_decompress_struct with default quant and huffman
152 tables, so that libjpeg can be used to parse [m]jpg-s with
153 incomplete headers */
154 cinfo.err = &data->jerr;
155 cinfo.client_data = data;
156 jpeg_create_compress(&cinfo);
157 jpeg_mem_dest(&cinfo, &jpeg_header, &jpeg_header_size);
158 cinfo.input_components = 3;
159 cinfo.in_color_space = JCS_RGB;
160 jpeg_set_defaults(&cinfo);
161 jpeg_write_tables(&cinfo);
162 jpeg_destroy_compress(&cinfo);
163
164 /* Init the jpeg_decompress_struct */
165 data->cinfo.err = &data->jerr;
166 data->cinfo.client_data = data;
167 jpeg_create_decompress(&data->cinfo);
168 jpeg_mem_src(&data->cinfo, jpeg_header, jpeg_header_size);
169 jpeg_read_header(&data->cinfo, FALSE);
170
171 free(jpeg_header);
172 data->cinfo_initialized = 1;
173 }
174
decode_libjpeg_h_samp1(struct v4lconvert_data * data,unsigned char * ydest,unsigned char * udest,unsigned char * vdest,int v_samp)175 static int decode_libjpeg_h_samp1(struct v4lconvert_data *data,
176 unsigned char *ydest, unsigned char *udest, unsigned char *vdest,
177 int v_samp)
178 {
179 struct jpeg_decompress_struct *cinfo = &data->cinfo;
180 int x, y;
181 unsigned char *uv_buf;
182 unsigned int width = cinfo->image_width;
183 JSAMPROW y_rows[16], u_rows[8], v_rows[8];
184 JSAMPARRAY rows[3] = { y_rows, u_rows, v_rows };
185
186 uv_buf = v4lconvert_alloc_buffer(width * 16,
187 &data->convert_pixfmt_buf,
188 &data->convert_pixfmt_buf_size);
189 if (!uv_buf)
190 return v4lconvert_oom_error(data);
191
192 for (y = 0; y < 8; y++) {
193 u_rows[y] = uv_buf;
194 uv_buf += width;
195 v_rows[y] = uv_buf;
196 uv_buf += width;
197 }
198 uv_buf -= width * 16;
199
200 while (cinfo->output_scanline < cinfo->image_height) {
201 for (y = 0; y < 8 * v_samp; y++) {
202 y_rows[y] = ydest;
203 ydest += cinfo->image_width;
204 }
205 y = jpeg_read_raw_data(cinfo, rows, 8 * v_samp);
206 if (y != 8 * v_samp)
207 return -1;
208
209 /* For v_samp == 1 skip copying uv vals every other time */
210 if (cinfo->output_scanline % 16)
211 continue;
212
213 /* Copy over every other u + v pixel for 8 lines */
214 for (y = 0; y < 8; y++) {
215 for (x = 0; x < width; x += 2) {
216 *udest++ = *uv_buf++;
217 uv_buf++;
218 }
219 for (x = 0; x < width; x += 2) {
220 *vdest++ = *uv_buf++;
221 uv_buf++;
222 }
223 }
224 uv_buf -= width * 16;
225 }
226 return 0;
227 }
228
decode_libjpeg_h_samp2(struct v4lconvert_data * data,unsigned char * ydest,unsigned char * udest,unsigned char * vdest,int v_samp)229 static int decode_libjpeg_h_samp2(struct v4lconvert_data *data,
230 unsigned char *ydest, unsigned char *udest, unsigned char *vdest,
231 int v_samp)
232 {
233 struct jpeg_decompress_struct *cinfo = &data->cinfo;
234 int y;
235 unsigned int width = cinfo->image_width;
236 JSAMPROW y_rows[16], u_rows[8], v_rows[8];
237 JSAMPARRAY rows[3] = { y_rows, u_rows, v_rows };
238
239 while (cinfo->output_scanline < cinfo->image_height) {
240 for (y = 0; y < 8 * v_samp; y++) {
241 y_rows[y] = ydest;
242 ydest += width;
243 }
244 /*
245 * For v_samp == 1 were going to get 1 set of uv values per
246 * line, but we need only 1 set per 2 lines since our output
247 * has v_samp == 2. We store every 2 sets in 1 line,
248 * effectively using the second set for each output line.
249 */
250 if (v_samp == 1) {
251 for (y = 0; y < 8; y++) {
252 u_rows[y] = udest;
253 v_rows[y] = vdest;
254 y++;
255 u_rows[y] = udest;
256 v_rows[y] = vdest;
257 udest += width / 2;
258 vdest += width / 2;
259 }
260 } else { /* v_samp == 2 */
261 for (y = 0; y < 8; y++) {
262 u_rows[y] = udest;
263 v_rows[y] = vdest;
264 udest += width / 2;
265 vdest += width / 2;
266 }
267 }
268
269 y = jpeg_read_raw_data(cinfo, rows, 8 * v_samp);
270 if (y != 8 * v_samp)
271 return -1;
272 }
273 return 0;
274 }
275
v4lconvert_decode_jpeg_libjpeg(struct v4lconvert_data * data,unsigned char * src,int src_size,unsigned char * dest,struct v4l2_format * fmt,unsigned int dest_pix_fmt)276 int v4lconvert_decode_jpeg_libjpeg(struct v4lconvert_data *data,
277 unsigned char *src, int src_size, unsigned char *dest,
278 struct v4l2_format *fmt, unsigned int dest_pix_fmt)
279 {
280 unsigned int width = fmt->fmt.pix.width;
281 unsigned int height = fmt->fmt.pix.height;
282 int result = 0;
283
284 /* libjpeg errors before decoding the first line should signal EAGAIN */
285 data->jerr_errno = EAGAIN;
286 result = setjmp(data->jerr_jmp_state);
287 if (result) {
288 if (data->cinfo_initialized)
289 jpeg_abort_decompress(&data->cinfo);
290 errno = result;
291 return -1;
292 }
293
294 init_libjpeg_cinfo(data);
295
296 jpeg_mem_src(&data->cinfo, src, src_size);
297 jpeg_read_header(&data->cinfo, TRUE);
298
299 if (data->cinfo.image_width != width ||
300 data->cinfo.image_height != height) {
301 V4LCONVERT_ERR("unexpected width / height in JPEG header: "
302 "expected: %ux%u, header: %ux%u\n", width,
303 height, data->cinfo.image_width,
304 data->cinfo.image_height);
305 errno = EIO;
306 return -1;
307 }
308
309 if (data->cinfo.num_components != 3) {
310 V4LCONVERT_ERR("unexpected no components in JPEG: %d\n",
311 data->cinfo.num_components);
312 errno = EIO;
313 return -1;
314 }
315
316 if (dest_pix_fmt == V4L2_PIX_FMT_RGB24 ||
317 dest_pix_fmt == V4L2_PIX_FMT_BGR24) {
318 JSAMPROW row_pointer[1];
319
320 #ifdef JCS_EXTENSIONS
321 if (dest_pix_fmt == V4L2_PIX_FMT_BGR24)
322 data->cinfo.out_color_space = JCS_EXT_BGR;
323 #endif
324 row_pointer[0] = dest;
325 jpeg_start_decompress(&data->cinfo);
326 /* Make libjpeg errors report that we've got some data */
327 data->jerr_errno = EPIPE;
328 while (data->cinfo.output_scanline < height) {
329 jpeg_read_scanlines(&data->cinfo, row_pointer, 1);
330 row_pointer[0] += 3 * width;
331 }
332 jpeg_finish_decompress(&data->cinfo);
333 #ifndef JCS_EXTENSIONS
334 if (dest_pix_fmt == V4L2_PIX_FMT_BGR24)
335 v4lconvert_swap_rgb(dest, dest, width, height);
336 #endif
337 } else {
338 int h_samp, v_samp;
339 unsigned char *udest, *vdest;
340
341 if (data->cinfo.max_h_samp_factor == 2 &&
342 data->cinfo.cur_comp_info[0]->h_samp_factor == 2 &&
343 data->cinfo.cur_comp_info[1]->h_samp_factor == 1 &&
344 data->cinfo.cur_comp_info[2]->h_samp_factor == 1) {
345 h_samp = 2;
346 #if 0 /* HDG: untested, disable for now */
347 } else if (data->cinfo.max_h_samp_factor == 1 &&
348 data->cinfo.cur_comp_info[0]->h_samp_factor == 1 &&
349 data->cinfo.cur_comp_info[1]->h_samp_factor == 1 &&
350 data->cinfo.cur_comp_info[2]->h_samp_factor == 1) {
351 h_samp = 1;
352 #endif
353 } else {
354 fprintf(stderr,
355 "libv4lconvert: unsupported jpeg h-sampling "
356 "factors %d:%d:%d, please report this to "
357 "hdegoede@redhat.com\n",
358 data->cinfo.cur_comp_info[0]->h_samp_factor,
359 data->cinfo.cur_comp_info[1]->h_samp_factor,
360 data->cinfo.cur_comp_info[2]->h_samp_factor);
361 errno = EOPNOTSUPP;
362 return -1;
363 }
364
365 if (data->cinfo.max_v_samp_factor == 2 &&
366 data->cinfo.cur_comp_info[0]->v_samp_factor == 2 &&
367 data->cinfo.cur_comp_info[1]->v_samp_factor == 1 &&
368 data->cinfo.cur_comp_info[2]->v_samp_factor == 1) {
369 v_samp = 2;
370 } else if (data->cinfo.max_v_samp_factor == 1 &&
371 data->cinfo.cur_comp_info[0]->v_samp_factor == 1 &&
372 data->cinfo.cur_comp_info[1]->v_samp_factor == 1 &&
373 data->cinfo.cur_comp_info[2]->v_samp_factor == 1) {
374 v_samp = 1;
375 } else {
376 fprintf(stderr,
377 "libv4lconvert: unsupported jpeg v-sampling "
378 "factors %d:%d:%d, please report this to "
379 "hdegoede@redhat.com\n",
380 data->cinfo.cur_comp_info[0]->v_samp_factor,
381 data->cinfo.cur_comp_info[1]->v_samp_factor,
382 data->cinfo.cur_comp_info[2]->v_samp_factor);
383 errno = EOPNOTSUPP;
384 return -1;
385 }
386
387 /* We don't want any padding as that may overflow our dest */
388 if (width % (8 * h_samp) || height % (8 * v_samp)) {
389 V4LCONVERT_ERR(
390 "resolution is not a multiple of dctsize");
391 errno = EIO;
392 return -1;
393 }
394
395 if (dest_pix_fmt == V4L2_PIX_FMT_YVU420) {
396 vdest = dest + width * height;
397 udest = vdest + (width * height) / 4;
398 } else {
399 udest = dest + width * height;
400 vdest = udest + (width * height) / 4;
401 }
402
403 data->cinfo.raw_data_out = TRUE;
404 data->cinfo.do_fancy_upsampling = FALSE;
405 jpeg_start_decompress(&data->cinfo);
406 /* Make libjpeg errors report that we've got some data */
407 data->jerr_errno = EPIPE;
408 if (h_samp == 1) {
409 result = decode_libjpeg_h_samp1(data, dest, udest,
410 vdest, v_samp);
411 } else {
412 result = decode_libjpeg_h_samp2(data, dest, udest,
413 vdest, v_samp);
414 }
415 if (result)
416 jpeg_abort_decompress(&data->cinfo);
417 else
418 jpeg_finish_decompress(&data->cinfo);
419 }
420
421 return result;
422 }
423
424 #endif // HAVE_JPEG
425