• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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