1 /*
2 decode_tm6000.c - decode multiplexed format from TM5600/TM6000 USB
3
4 Copyright (C) 2007 Mauro Carvalho Chehab <mchehab@kernel.org>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation version 2.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA.
18 */
19 #include "../utils/libv4l2util/v4l2_driver.h"
20 #include <stdio.h>
21 #include <string.h>
22 #include <argp.h>
23 #include <unistd.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <stdlib.h>
28 #include <errno.h>
29
30 const char *argp_program_version="decode_tm6000 version 0.0.1";
31 const char *argp_program_bug_address="Mauro Carvalho Chehab <mchehab@kernel.org>";
32 const char doc[]="Decodes tm6000 proprietary format streams";
33 const struct argp_option options[] = {
34 {"verbose", 'v', 0, 0, "enables debug messages", 0},
35 {"device", 'd', "DEV", 0, "uses device for reading", 0},
36 {"output", 'o', "FILE", 0, "outputs raw stream to a file", 0},
37 {"input", 'i', "FILE", 0, "parses a file, instead of a device", 0},
38 {"freq", 'f', "Freq", 0, "station frequency, in MHz (default is 193.25)", 0},
39 {"nbufs", 'n', "quant",0, "number of video buffers", 0},
40 {"audio", 'a', 0, 0, "outputs audio on stdout", 0},
41 {"read", 'r', 0, 0, "use read() instead of mmap method", 0},
42 { 0, 0, 0, 0, 0, 0 }
43 };
44
45 static char outbuf[692224];
46 static int debug=0, audio=0, use_mmap=1, nbufs=4;
47 static float freq_mhz=193.25;
48 static char *devicename="/dev/video0";
49 static char *filename=NULL;
50 static enum {
51 NORMAL,
52 INPUT,
53 OUTPUT
54 } mode = NORMAL;
55
56 static FILE *fout;
57
58 //const char args_doc[]="ARG1 ARG2";
59
parse_opt(int key,char * arg,struct argp_state * state)60 static int parse_opt (int key, char *arg, struct argp_state *state)
61 {
62 switch (key) {
63 case 'a':
64 audio++;
65 break;
66 case 'r':
67 use_mmap=0;
68 break;
69 case 'v':
70 debug++;
71 break;
72 case 'd':
73 devicename=arg;
74 break;
75 case 'i':
76 case 'o':
77 if (mode!=NORMAL) {
78 argp_error(state,"You can't use input/output options simultaneously.\n");
79 break;
80 }
81 if (key=='i')
82 mode=INPUT;
83 else
84 mode=OUTPUT;
85
86 filename=arg;
87 break;
88 case 'f':
89 freq_mhz=atof(arg);
90 break;
91 case 'n':
92 nbufs=atoi(arg);
93 if (nbufs<2)
94 nbufs=2;
95 break;
96 default:
97 return ARGP_ERR_UNKNOWN;
98 }
99 return 0;
100 }
101
102 static struct argp argp = {
103 .options=options,
104 .parser=parse_opt,
105 .args_doc=NULL,
106 .doc=doc,
107 };
108
109 #define TM6000_URB_MSG_LEN 180
110 enum {
111 TM6000_URB_MSG_VIDEO=1,
112 TM6000_URB_MSG_AUDIO,
113 TM6000_URB_MSG_VBI,
114 TM6000_URB_MSG_PTS,
115 TM6000_URB_MSG_ERR,
116 };
117
118 const char *tm6000_msg_type[]= {
119 "unknown(0)", //0
120 "video", //1
121 "audio", //2
122 "vbi", //3
123 "pts", //4
124 "err", //5
125 "unknown(6)", //6
126 "unknown(7)", //7
127 };
128
129 #define dprintf(fmt,arg...) \
130 if (debug) fprintf(stderr, fmt, ##arg)
131
recebe_buffer(struct v4l2_buffer * v4l2_buf,struct v4l2_t_buf * buf)132 static int recebe_buffer (struct v4l2_buffer *v4l2_buf, struct v4l2_t_buf *buf)
133 {
134 dprintf("Received %zd bytes\n", buf->length);
135 fflush(stdout);
136 memcpy (outbuf,buf->start,buf->length);
137 return buf->length;
138 }
139
140
prepare_read(struct v4l2_driver * drv)141 static int prepare_read (struct v4l2_driver *drv)
142 {
143 struct v4l2_format fmt;
144 double freq;
145 int rc;
146
147 memset (drv,0,sizeof(*drv));
148
149 if (v4l2_open (devicename, 1,drv)<0) {
150 perror ("Error opening dev");
151 return -1;
152 }
153
154 memset (&fmt,0,sizeof(fmt));
155
156 uint32_t pixelformat=V4L2_PIX_FMT_TM6000;
157
158 if (v4l2_gettryset_fmt_cap (drv,V4L2_SET,&fmt, 720, 480,
159 pixelformat,V4L2_FIELD_ANY)) {
160 perror("set_input to tm6000 raw format");
161 return -1;
162 }
163
164 if (freq_mhz) {
165 freq=freq_mhz * 1000 * 1000;
166 rc=v4l2_getset_freq (drv,V4L2_SET, &freq);
167 if (rc<0)
168 printf ("Cannot set freq to %.3f MHz\n",freq_mhz);
169 }
170
171 if (use_mmap) {
172 printf("Preparing for receiving frames on %d buffers...\n",nbufs);
173 fflush (stdout);
174
175 rc=v4l2_mmap_bufs(drv, nbufs);
176 if (rc<0) {
177 printf ("Cannot mmap %d buffers\n",nbufs);
178 return -1;
179 }
180
181 // v4l2_stop_streaming(&drv);
182 rc=v4l2_start_streaming(drv);
183 if (rc<0) {
184 printf ("Cannot start streaming\n");
185 return -1;
186 }
187 }
188 printf("Waiting for frames...\n");
189
190 return 0;
191 }
192
read_stream(struct v4l2_driver * drv,int fd)193 static int read_stream (struct v4l2_driver *drv, int fd)
194 {
195 if (use_mmap) {
196 fd_set fds;
197 struct timeval tv;
198 int r;
199
200 FD_ZERO (&fds);
201 FD_SET (fd, &fds);
202
203 /* Timeout. */
204 tv.tv_sec = 2;
205 tv.tv_usec = 0;
206
207 r = select (fd + 1, &fds, NULL, NULL, &tv);
208 if (-1 == r) {
209 if (EINTR == errno) {
210 perror ("select");
211 return -errno;
212 }
213 }
214
215 if (0 == r) {
216 fprintf (stderr, "select timeout\n");
217 return -errno;
218 }
219
220 return v4l2_rcvbuf(drv, recebe_buffer);
221 } else {
222 int size=read(fd, outbuf, sizeof(outbuf));
223 return size;
224 }
225
226 return 0;
227 }
228
read_char(struct v4l2_driver * drv,int fd)229 static int read_char (struct v4l2_driver *drv, int fd)
230 {
231 static int sizebuf=0;
232 static unsigned char *p=NULL;
233 unsigned char c;
234
235 if (sizebuf<=0) {
236 sizebuf=read_stream(drv,fd);
237 if (sizebuf<=0)
238 return -1;
239 p=(unsigned char *)outbuf;
240 }
241 c=*p;
242 p++;
243 sizebuf--;
244
245 return c;
246 }
247
248
main(int argc,char * argv[])249 int main (int argc, char*argv[])
250 {
251 int fd;
252 unsigned int i;
253 unsigned char buf[TM6000_URB_MSG_LEN], img[720*2*480];
254 unsigned int cmd, size, field, block, line, pos=0;
255 unsigned long header=0;
256 int linesize=720*2,skip=0;
257 struct v4l2_driver drv;
258
259 argp_parse (&argp, argc, argv, 0, 0, 0);
260
261 if (mode!=INPUT) {
262 if (prepare_read (&drv)<0)
263 return -1;
264 fd=drv.fd;
265 } else {
266 /*mode == INPUT */
267
268 fd=open(filename,O_RDONLY);
269 if (fd<0) {
270 perror ("error opening a file for parsing");
271 return -1;
272 }
273 dprintf("file %s opened for parsing\n",filename);
274 use_mmap=0;
275 }
276
277 if (mode==OUTPUT) {
278 fout=fopen(filename,"w");
279 if (!fout) {
280 perror ("error opening a file to write");
281 return -1;
282 }
283 dprintf("file %s opened for output\n",filename);
284
285 do {
286 size=read_stream (&drv,fd);
287
288 if (size<=0) {
289 close (fd);
290 return -1;
291 }
292 dprintf("writing %d bytes\n",size);
293 fwrite(outbuf,1, size,fout);
294 // fflush(fout);
295 } while (1);
296 }
297
298
299 while (1) {
300 skip=0;
301 header=0;
302 do {
303 int c;
304 c=read_char (&drv,fd);
305 if (c<0) {
306 perror("read");
307 return -1;
308 }
309
310 header=(header>>8)&0xffffff;
311 header=header|(c<<24);
312 skip++;
313 } while ( (((header>>24)&0xff) != 0x47) );
314
315 /* split the header fields */
316 size = (((header & 0x7e)<<1) -1) * 4;
317 block = (header>>7) & 0xf;
318 field = (header>>11) & 0x1;
319 line = (header>>12) & 0x1ff;
320 cmd = (header>>21) & 0x7;
321
322 /* Read the remaining buffer */
323 for (i=0;i<sizeof(buf);i++) {
324 int c;
325 c=read_char (&drv,fd);
326 if (c<0) {
327 perror("read");
328 return -1;
329 }
330 buf[i]=c;
331 }
332
333 /* FIXME: Mounts the image as field0+field1
334 * It should, instead, check if the user selected
335 * entrelaced or non-entrelaced mode
336 */
337 pos=((line<<1)+field)*linesize+
338 block*TM6000_URB_MSG_LEN;
339
340 /* Prints debug info */
341 dprintf("0x%08x (skip %d), %s size=%d, line=%d, field=%d, block=%d\n",
342 (unsigned int)header, skip,
343 tm6000_msg_type[cmd],
344 size, line, field, block);
345
346 /* Don't allow to write out of the buffer */
347 if (pos+sizeof(buf) > sizeof(img))
348 cmd = TM6000_URB_MSG_ERR;
349
350 /* handles each different URB message */
351 switch(cmd) {
352 case TM6000_URB_MSG_VIDEO:
353 /* Fills video buffer */
354 memcpy(buf,&img[pos],sizeof(buf));
355 case TM6000_URB_MSG_AUDIO:
356 if (audio)
357 fwrite(buf,sizeof(buf),1,stdout);
358 // case TM6000_URB_MSG_VBI:
359 // case TM6000_URB_MSG_PTS:
360 break;
361 }
362 }
363 close(fd);
364 return 0;
365 }
366