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