1 /*
2 # (C) 2010 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 "libv4lconvert-priv.h"
20 #include <stdlib.h>
21 #include <string.h>
22
23 #define MAGIC_0 0x19
24 #define MAGIC_1 0x68
25 #define SUBSAMPLE_420 0
26 #define SUBSAMPLE_422 1
27 #define YUVORDER_YUYV 0
28 #define YUVORDER_UYVY 1
29 #define NOT_COMPRESSED 0
30 #define COMPRESSED 1
31 #define NO_DECIMATION 0
32 #define DECIMATION_ENAB 1
33 #define EOI 0xff /* End Of Image */
34 #define EOL 0xfd /* End Of Line */
35 #define FRAME_HEADER_SIZE 64
36
37 /* CPIA YUYV (sometimes sort of compressed) */
v4lconvert_cpia1_to_yuv420(struct v4lconvert_data * data,const unsigned char * src,int src_size,unsigned char * dest,int width,int height,int yvu)38 int v4lconvert_cpia1_to_yuv420(struct v4lconvert_data *data,
39 const unsigned char *src, int src_size,
40 unsigned char *dest, int width, int height, int yvu)
41 {
42 int x, y, ll, compressed;
43 unsigned char *udest, *vdest;
44
45 if (width > 352 || height > 288) {
46 fprintf(stderr, "FATAL ERROR CPIA1 size > 352x288, please report!\n");
47 return -1;
48 }
49
50 if (data->previous_frame == NULL) {
51 data->previous_frame = malloc(352 * 288 * 3 / 2);
52 if (data->previous_frame == NULL) {
53 fprintf(stderr, "cpia1 decode error: could not allocate buffer!\n");
54 return -1;
55 }
56 }
57
58 if (yvu) {
59 vdest = dest + width * height;
60 udest = vdest + width * height / 4;
61 } else {
62 udest = dest + width * height;
63 vdest = udest + width * height / 4;
64 }
65
66 /* Verify header */
67 if (src_size < FRAME_HEADER_SIZE ||
68 src[0] != MAGIC_0 || src[1] != MAGIC_1 ||
69 src[17] != SUBSAMPLE_420 ||
70 src[18] != YUVORDER_YUYV ||
71 (src[25] - src[24]) * 8 != width ||
72 (src[27] - src[26]) * 4 != height ||
73 (src[28] != NOT_COMPRESSED && src[28] != COMPRESSED) ||
74 (src[29] != NO_DECIMATION && src[29] != DECIMATION_ENAB)) {
75 fprintf(stderr, "cpia1 decode error: invalid header\n");
76 return -1;
77 }
78
79 if (src[29] == DECIMATION_ENAB) {
80 fprintf(stderr, "cpia1 decode error: decimation is not supported\n");
81 return -1;
82 }
83
84 compressed = src[28] == COMPRESSED;
85
86 src += FRAME_HEADER_SIZE;
87 src_size -= FRAME_HEADER_SIZE;
88
89 if (!compressed) {
90 for (y = 0; y < height && src_size > 2; y++) {
91 ll = src[0] | (src[1] << 8);
92 src += 2;
93 src_size -= 2;
94 if (src_size < ll) {
95 fprintf(stderr, "cpia1 decode error: short frame\n");
96 return -1;
97 }
98 if (src[ll - 1] != EOL) {
99 fprintf(stderr, "cpia1 decode error: invalid terminated line\n");
100 return -1;
101 }
102
103 if (!(y & 1)) { /* Even line Y + UV in the form of YUYV */
104 if (ll != 2 * width + 1) {
105 fprintf(stderr, "cpia1 decode error: invalid uncompressed even ll\n");
106 return -1;
107 }
108
109 /* copy the Y values */
110 for (x = 0; x < width; x += 2) {
111 *dest++ = src[0];
112 *dest++ = src[2];
113 src += 4;
114 }
115
116 /* copy the UV values */
117 src -= 2 * width;
118 for (x = 0; x < width; x += 2) {
119 *udest++ = src[1];
120 *vdest++ = src[3];
121 src += 4;
122 }
123 } else { /* Odd line only Y values */
124 if (ll != width + 1) {
125 fprintf(stderr, "cpia1 decode error: invalid uncompressed odd ll\n");
126 return -1;
127 }
128
129 memcpy(dest, src, width);
130 dest += width;
131 src += width;
132 }
133 src++; /* Skip EOL */
134 src_size -= ll;
135 }
136 } else { /* compressed */
137 /* Pre-fill dest with previous frame, as the cpia1 "compression" consists
138 of simply ommitting certain pixels */
139 memcpy(dest, data->previous_frame, width * height * 3 / 2);
140
141 for (y = 0; y < height && src_size > 2; y++) {
142 ll = src[0] | (src[1] << 8);
143 src += 2;
144 src_size -= 2;
145 if (src_size < ll) {
146 fprintf(stderr, "cpia1 decode error: short frame\n");
147 return -1;
148 }
149 if (src[ll - 1] != EOL) {
150 fprintf(stderr, "cpia1 decode error: invalid terminated line\n");
151 return -1;
152 }
153
154 /* Do this now as we use ll as loop variable below */
155 src_size -= ll;
156 for (x = 0; x < width && ll > 1; ) {
157 if (*src & 1) { /* skip N pixels */
158 int skip = *src >> 1;
159
160 if (skip & 1) {
161 fprintf(stderr, "cpia1 decode error: odd number of pixels to skip");
162 return -1;
163 }
164
165 if (!(y & 1)) { /* Even line Y + UV in the form of YUYV */
166 dest += skip;
167 udest += skip / 2;
168 vdest += skip / 2;
169 } else { /* Odd line only Y values */
170 dest += skip;
171 }
172 x += skip;
173 src++;
174 ll--;
175 } else {
176 if (!(y & 1)) { /* Even line Y + UV in the form of YUYV */
177 *dest++ = *src++;
178 *udest++ = *src++;
179 *dest++ = *src++;
180 *vdest++ = *src++;
181 ll -= 4;
182 } else { /* Odd line only Y values */
183 *dest++ = *src++;
184 *dest++ = *src++;
185 ll -= 2;
186 }
187 x += 2;
188 }
189 }
190 if (ll != 1 || x != width) {
191 fprintf(stderr, "cpia1 decode error: line length mismatch\n");
192 return -1;
193 }
194 src++; /* Skip EOL */
195 }
196 }
197
198 if (y != height) {
199 fprintf(stderr, "cpia1 decode error: frame height mismatch\n");
200 return -1;
201 }
202
203 if (src_size < 4 || src[src_size - 4] != EOI || src[src_size - 3] != EOI ||
204 src[src_size - 2] != EOI || src[src_size - 1] != EOI) {
205 fprintf(stderr, "cpia1 decode error: invaled EOI marker\n");
206 return -1;
207 }
208
209 /* Safe frame for decompression of the next frame */
210 dest -= width * height;
211 memcpy(data->previous_frame, dest, width * height * 3 / 2);
212
213 return 0;
214 }
215