1 /*
2 #             (C) 2011 Hans de Goede <hdegoede@redhat.com>
3 
4 # The compression algorithm has been taken from the v4l1 se401 linux kernel
5 # driver by Jeroen B. Vreeken (pe1rxq@amsat.org)
6 
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU Lesser General Public License as published by
9 # the Free Software Foundation; either version 2.1 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU Lesser General Public License for more details.
16 #
17 # You should have received a copy of the GNU Lesser General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335  USA
20  */
21 
22 #include "libv4lconvert-priv.h"
23 #include <errno.h>
24 
25 /* The se401 compression algorithm uses a fixed quant factor, which
26    can be configured by setting the high nibble of the SE401_OPERATINGMODE
27    feature. This needs to exactly match what is in the SE401 driver! */
28 #define SE401_QUANT_FACT 8
29 
wr_pixel(int p,uint8_t ** dest,int pitch,int * x)30 static void wr_pixel(int p, uint8_t **dest, int pitch, int *x)
31 {
32 	int i = *x;
33 
34 	/* First 3 pixels of each line are absolute */
35 	if (i < 3) {
36 		(*dest)[i] = p * SE401_QUANT_FACT;
37 	} else {
38 		(*dest)[i] = (*dest)[i - 3] + p * SE401_QUANT_FACT;
39 	}
40 
41 	*x += 1;
42 	if (*x == pitch) {
43 		*x = 0;
44 		*dest += pitch;
45 	}
46 }
47 
48 enum decode_state {
49 	get_len,
50 	sign_bit,
51 	other_bits,
52 };
53 
decode_JangGu(const uint8_t * data,int bits,int plen,int pixels,uint8_t ** dest,int pitch,int * x)54 static int decode_JangGu(const uint8_t *data, int bits, int plen, int pixels,
55 			 uint8_t **dest, int pitch, int *x)
56 {
57 	enum decode_state state = get_len;
58 	int len = 0;
59 	int value = 0;
60 	int bitnr;
61 	int bit;
62 
63 	while (plen) {
64 		bitnr = 8;
65 		while (bitnr && bits) {
66 			bit = ((*data) >> (bitnr-1))&1;
67 			switch (state) {
68 			case get_len:
69 				if (bit) {
70 					len++;
71 				} else {
72 					if (!len) {
73 						wr_pixel(0, dest, pitch, x);
74 						if (!--pixels)
75 							return 0;
76 					} else {
77 						state = sign_bit;
78 						value = 0;
79 					}
80 				}
81 				break;
82 			case sign_bit:
83 				if (bit)
84 					value = 0;
85 				else
86 					value = -(1 << len) + 1;
87 				state = other_bits;
88 				/* fall through for positive number and
89 				   len == 1 handling */
90 			case other_bits:
91 				len--;
92 				value += bit << len;
93 				if (len == 0) {
94 					/* Done write pixel and get bit len of
95 					   the next one */
96 					state = get_len;
97 					wr_pixel(value, dest, pitch, x);
98 					if (!--pixels)
99 						return 0;
100 				}
101 				break;
102 			}
103 			bitnr--;
104 			bits--;
105 		}
106 		data++;
107 		plen--;
108 	}
109 	return -1;
110 }
111 
v4lconvert_se401_to_rgb24(struct v4lconvert_data * data,const unsigned char * src,int src_size,unsigned char * dest,int width,int height)112 int v4lconvert_se401_to_rgb24(struct v4lconvert_data *data,
113 		const unsigned char *src, int src_size,
114 		unsigned char *dest, int width, int height)
115 {
116 	int in, plen, bits, pixels, info;
117 	int x = 0, total_pixels = 0;
118 
119 	if (!src || !dest)
120 		goto error;
121 
122 	for (in = 0; in + 4 < src_size; in += plen) {
123 		bits   = src[in + 3] + (src[in + 2] << 8);
124 		pixels = src[in + 1] + ((src[in + 0] & 0x3f) << 8);
125 		info   = (src[in + 0] & 0xc0) >> 6;
126 		plen   = ((bits + 47) >> 4) << 1;
127 		/* Sanity checks */
128 		if (plen > 1024) {
129 			V4LCONVERT_ERR("invalid se401 packet len %d", plen);
130 			goto error;
131 		}
132 		if (in + plen > src_size) {
133 			V4LCONVERT_ERR("incomplete se401 packet");
134 			goto error;
135 		}
136 		if (total_pixels + pixels > width * height) {
137 			V4LCONVERT_ERR("se401 frame overflow");
138 			goto error;
139 		}
140 		/* info: 0 inter packet, 1 eof, 2 sof, 3 not used */
141 		if ((in == 0 && info != 2) ||
142 		    (in > 0 && in + plen < src_size && info != 0) ||
143 		    (in + plen == src_size && info != 1)) {
144 			V4LCONVERT_ERR("invalid se401 frame info value");
145 			goto error;
146 		}
147 		if (decode_JangGu(&src[in + 4], bits, plen, pixels * 3,
148 				  &dest, width * 3, &x)) {
149 			V4LCONVERT_ERR("short se401 packet");
150 			goto error;
151 		}
152 		total_pixels += pixels;
153 	}
154 
155 	if (in != src_size || total_pixels != width * height) {
156 		V4LCONVERT_ERR("se401 frame size mismatch");
157 		goto error;
158 	}
159 
160 	return 0;
161 
162 error:
163 	errno = EIO;
164 	return -1;
165 }
166