2802
|
1 /*
|
3284
|
2 Video 4 Linux input
|
2802
|
3
|
|
4 (C) Alex Beregszaszi <alex@naxine.org>
|
|
5
|
|
6 Some ideas are based on xawtv/libng's grab-v4l.c written by
|
|
7 Gerd Knorr <kraxel@bytesex.org>
|
|
8
|
|
9 CODE IS UNDER DEVELOPMENT, NO FEATURE REQUESTS PLEASE!
|
|
10 */
|
|
11
|
2790
|
12 #include "config.h"
|
|
13
|
3243
|
14 #if defined(USE_TV) && defined(HAVE_TV_V4L)
|
2790
|
15
|
|
16 #include <stdio.h>
|
|
17 #include <errno.h>
|
|
18 #include <fcntl.h>
|
2802
|
19 #include <signal.h>
|
2790
|
20 #include <sys/ioctl.h>
|
|
21 #include <sys/types.h>
|
|
22 #include <linux/videodev.h>
|
3815
|
23 #include <linux/soundcard.h>
|
2790
|
24 #include <unistd.h>
|
|
25 #include <sys/mman.h>
|
2931
|
26 #include <stdlib.h>
|
|
27 #include <string.h>
|
2790
|
28
|
2830
|
29 #include "mp_msg.h"
|
|
30 #include "../libao2/afmt.h"
|
|
31 #include "../libvo/img_format.h"
|
|
32 #include "../libvo/fastmemcpy.h"
|
|
33
|
2790
|
34 #include "tv.h"
|
|
35
|
|
36 static tvi_info_t info = {
|
3815
|
37 "Video 4 Linux input",
|
2790
|
38 "v4l",
|
2802
|
39 "Alex Beregszaszi <alex@naxine.org>",
|
|
40 "under development"
|
|
41 };
|
|
42
|
3815
|
43 #define MAX_AUDIO_CHANNELS 10
|
|
44
|
2790
|
45 typedef struct {
|
2802
|
46 /* general */
|
2790
|
47 char *video_device;
|
3815
|
48 int video_fd;
|
2790
|
49 struct video_capability capability;
|
|
50 struct video_channel *channels;
|
2841
|
51 int act_channel;
|
2790
|
52 struct video_tuner tuner;
|
2802
|
53
|
|
54 /* video */
|
2790
|
55 struct video_picture picture;
|
2802
|
56 int format; /* output format */
|
2790
|
57 int width;
|
|
58 int height;
|
2802
|
59 int bytesperline;
|
|
60
|
|
61 struct video_mbuf mbuf;
|
|
62 unsigned char *mmap;
|
|
63 struct video_mmap *buf;
|
|
64 int nbuf;
|
|
65 int queue;
|
5088
|
66 int currentframe;
|
2802
|
67
|
|
68 /* audio */
|
3815
|
69 int audio_id;
|
|
70 char *audio_device;
|
|
71 struct video_audio audio[MAX_AUDIO_CHANNELS];
|
|
72 int audio_fd;
|
|
73 int audio_channels[MAX_AUDIO_CHANNELS];
|
|
74 int audio_format[MAX_AUDIO_CHANNELS];
|
|
75 int audio_samplesize[MAX_AUDIO_CHANNELS];
|
|
76 int audio_samplerate[MAX_AUDIO_CHANNELS];
|
|
77 int audio_blocksize;
|
2790
|
78 } priv_t;
|
|
79
|
|
80 #include "tvi_def.h"
|
|
81
|
2841
|
82 static const char *device_cap2name[] = {
|
2790
|
83 "capture", "tuner", "teletext", "overlay", "chromakey", "clipping",
|
2802
|
84 "frameram", "scales", "monochrome", "subcapture", "mpeg-decoder",
|
|
85 "mpeg-encoder", "mjpeg-decoder", "mjpeg-encoder", NULL
|
|
86 };
|
|
87
|
2841
|
88 static const char *device_palette2name[] = {
|
2802
|
89 "-", "grey", "hi240", "rgb16", "rgb24", "rgb32", "rgb15", "yuv422",
|
|
90 "yuyv", "uyvy", "yuv420", "yuv411", "raw", "yuv422p", "yuv411p",
|
|
91 "yuv420p", "yuv410p", NULL
|
|
92 };
|
|
93 #define PALETTE(x) ((x < sizeof(device_pal)/sizeof(char*)) ? device_pal[x] : "UNKNOWN")
|
|
94
|
2841
|
95 static const char *audio_mode2name[] = {
|
|
96 "unknown", "mono", "stereo", "language1", "language2", NULL
|
|
97 };
|
|
98
|
2802
|
99 static int palette2depth(int palette)
|
|
100 {
|
2810
|
101 switch(palette)
|
|
102 {
|
3220
|
103 /* component */
|
2810
|
104 case VIDEO_PALETTE_RGB555:
|
|
105 return(15);
|
|
106 case VIDEO_PALETTE_RGB565:
|
|
107 return(16);
|
|
108 case VIDEO_PALETTE_RGB24:
|
|
109 return(24);
|
|
110 case VIDEO_PALETTE_RGB32:
|
|
111 return(32);
|
3220
|
112 /* planar */
|
|
113 case VIDEO_PALETTE_YUV411P:
|
2810
|
114 case VIDEO_PALETTE_YUV420P:
|
3220
|
115 case VIDEO_PALETTE_YUV410P:
|
2810
|
116 return(12);
|
3220
|
117 /* packed */
|
3815
|
118 case VIDEO_PALETTE_YUV422P:
|
2810
|
119 case VIDEO_PALETTE_YUV422:
|
3220
|
120 case VIDEO_PALETTE_YUYV:
|
2810
|
121 case VIDEO_PALETTE_UYVY:
|
3220
|
122 case VIDEO_PALETTE_YUV420:
|
|
123 case VIDEO_PALETTE_YUV411:
|
2810
|
124 return(16);
|
|
125 }
|
|
126 return(-1);
|
2802
|
127 }
|
|
128
|
|
129 static int format2palette(int format)
|
|
130 {
|
2810
|
131 switch(format)
|
|
132 {
|
|
133 case IMGFMT_RGB15:
|
|
134 return(VIDEO_PALETTE_RGB555);
|
|
135 case IMGFMT_RGB16:
|
|
136 return(VIDEO_PALETTE_RGB565);
|
|
137 case IMGFMT_RGB24:
|
|
138 return(VIDEO_PALETTE_RGB24);
|
|
139 case IMGFMT_RGB32:
|
|
140 return(VIDEO_PALETTE_RGB32);
|
|
141 case IMGFMT_YV12:
|
3703
|
142 case IMGFMT_I420:
|
2810
|
143 return(VIDEO_PALETTE_YUV420P);
|
|
144 case IMGFMT_UYVY:
|
|
145 return(VIDEO_PALETTE_YUV422);
|
3815
|
146 case IMGFMT_YUY2:
|
|
147 return(VIDEO_PALETTE_YUYV);
|
2810
|
148 }
|
|
149 return(-1);
|
2802
|
150 }
|
|
151
|
|
152 static int one = 1, zero = 0;
|
|
153
|
2790
|
154 tvi_handle_t *tvi_init_v4l(char *device)
|
|
155 {
|
|
156 tvi_handle_t *h;
|
|
157 priv_t *priv;
|
|
158
|
|
159 h = new_handle();
|
|
160 if (!h)
|
|
161 return(NULL);
|
|
162
|
|
163 priv = h->priv;
|
|
164
|
2802
|
165 /* set video device name */
|
2790
|
166 if (!device)
|
5088
|
167 priv->video_device = strdup("/dev/video");
|
2790
|
168 else
|
3611
|
169 priv->video_device = strdup(device);
|
|
170
|
|
171 /* allocation failed */
|
|
172 if (!priv->video_device) {
|
|
173 free_handle(h);
|
|
174 return(NULL);
|
2790
|
175 }
|
|
176
|
3815
|
177 /* set audio device name */
|
5088
|
178 priv->audio_device = "/dev/dsp";
|
3815
|
179
|
2790
|
180 return(h);
|
|
181 }
|
|
182
|
3815
|
183 static int init(priv_t *priv)
|
2790
|
184 {
|
|
185 int i;
|
|
186
|
3815
|
187 priv->video_fd = open(priv->video_device, O_RDWR);
|
5088
|
188 mp_msg(MSGT_TV, MSGL_DBG2, "Video fd: %d, %x\n", priv->video_fd,
|
|
189 priv->video_device);
|
3815
|
190 if (priv->video_fd == -1)
|
2790
|
191 {
|
2818
|
192 mp_msg(MSGT_TV, MSGL_ERR, "unable to open '%s': %s\n",
|
2802
|
193 priv->video_device, strerror(errno));
|
2790
|
194 goto err;
|
|
195 }
|
|
196
|
3815
|
197 mp_msg(MSGT_TV, MSGL_V, "Video fd: %d\n", priv->video_fd);
|
2790
|
198
|
2802
|
199 /* get capabilities (priv->capability is needed!) */
|
3815
|
200 if (ioctl(priv->video_fd, VIDIOCGCAP, &priv->capability) == -1)
|
2790
|
201 {
|
2818
|
202 mp_msg(MSGT_TV, MSGL_ERR, "ioctl get capabilites failed: %s\n", strerror(errno));
|
2790
|
203 goto err;
|
|
204 }
|
|
205
|
3815
|
206 fcntl(priv->video_fd, F_SETFD, FD_CLOEXEC);
|
2802
|
207
|
2818
|
208 mp_msg(MSGT_TV, MSGL_INFO, "Selected device: %s\n", priv->capability.name);
|
|
209 mp_msg(MSGT_TV, MSGL_INFO, " Capabilites: ");
|
2841
|
210 for (i = 0; device_cap2name[i] != NULL; i++)
|
2790
|
211 if (priv->capability.type & (1 << i))
|
2841
|
212 mp_msg(MSGT_TV, MSGL_INFO, "%s ", device_cap2name[i]);
|
2818
|
213 mp_msg(MSGT_TV, MSGL_INFO, "\n");
|
|
214 mp_msg(MSGT_TV, MSGL_INFO, " Device type: %d\n", priv->capability.type);
|
|
215 mp_msg(MSGT_TV, MSGL_INFO, " Supported sizes: %dx%d => %dx%d\n",
|
2790
|
216 priv->capability.minwidth, priv->capability.minheight,
|
|
217 priv->capability.maxwidth, priv->capability.maxheight);
|
|
218 priv->width = priv->capability.minwidth;
|
|
219 priv->height = priv->capability.minheight;
|
2818
|
220 mp_msg(MSGT_TV, MSGL_INFO, " Inputs: %d\n", priv->capability.channels);
|
2790
|
221
|
2819
|
222 priv->channels = (struct video_channel *)malloc(sizeof(struct video_channel)*priv->capability.channels);
|
3611
|
223 if (!priv->channels)
|
|
224 goto malloc_failed;
|
2790
|
225 memset(priv->channels, 0, sizeof(struct video_channel)*priv->capability.channels);
|
|
226 for (i = 0; i < priv->capability.channels; i++)
|
|
227 {
|
|
228 priv->channels[i].channel = i;
|
3815
|
229 if (ioctl(priv->video_fd, VIDIOCGCHAN, &priv->channels[i]) == -1)
|
2841
|
230 {
|
|
231 mp_msg(MSGT_TV, MSGL_ERR, "ioctl get channel failed: %s\n", strerror(errno));
|
|
232 break;
|
|
233 }
|
2818
|
234 mp_msg(MSGT_TV, MSGL_INFO, " %d: %s: %s%s%s%s (tuner:%d, norm:%d)\n", i,
|
2790
|
235 priv->channels[i].name,
|
|
236 (priv->channels[i].flags & VIDEO_VC_TUNER) ? "tuner " : "",
|
|
237 (priv->channels[i].flags & VIDEO_VC_AUDIO) ? "audio " : "",
|
|
238 (priv->channels[i].flags & VIDEO_TYPE_TV) ? "tv " : "",
|
2802
|
239 (priv->channels[i].flags & VIDEO_TYPE_CAMERA) ? "camera " : "",
|
|
240 priv->channels[i].tuners,
|
|
241 priv->channels[i].norm);
|
|
242 }
|
|
243
|
3815
|
244 /* audio chanlist */
|
2841
|
245 if (priv->capability.audios)
|
|
246 {
|
|
247 mp_msg(MSGT_TV, MSGL_INFO, " Audio devices: %d\n", priv->capability.audios);
|
|
248
|
|
249 for (i = 0; i < priv->capability.audios; i++)
|
|
250 {
|
3815
|
251 if (i >= MAX_AUDIO_CHANNELS)
|
|
252 {
|
|
253 mp_msg(MSGT_TV, MSGL_ERR, "no space for more audio channels (incrase in source!) (%d > %d)\n",
|
|
254 i, MAX_AUDIO_CHANNELS);
|
|
255 i = priv->capability.audios;
|
|
256 break;
|
|
257 }
|
|
258
|
3284
|
259 priv->audio[i].audio = i;
|
3815
|
260 if (ioctl(priv->video_fd, VIDIOCGAUDIO, &priv->audio[i]) == -1)
|
2841
|
261 {
|
|
262 mp_msg(MSGT_TV, MSGL_ERR, "ioctl get audio failed: %s\n", strerror(errno));
|
|
263 break;
|
|
264 }
|
|
265
|
3815
|
266 if (priv->audio[i].volume <= 0)
|
|
267 priv->audio[i].volume = 100;
|
|
268 priv->audio[i].flags &= ~VIDEO_AUDIO_MUTE;
|
|
269 ioctl(priv->video_fd, VIDIOCSAUDIO, &priv->audio[i]);
|
|
270
|
|
271 switch(priv->audio[i].mode)
|
|
272 {
|
|
273 case VIDEO_SOUND_MONO:
|
|
274 case VIDEO_SOUND_LANG1:
|
|
275 case VIDEO_SOUND_LANG2:
|
|
276 priv->audio_channels[i] = 1;
|
|
277 break;
|
|
278 case VIDEO_SOUND_STEREO:
|
|
279 priv->audio_channels[i] = 2;
|
|
280 break;
|
|
281 }
|
|
282
|
|
283 priv->audio_format[i] = AFMT_S16_LE;
|
|
284 priv->audio_samplerate[i] = 44100;
|
|
285 priv->audio_samplesize[i] = /*76000*/priv->audio_channels[i]*
|
|
286 16*priv->audio_samplerate[i]/8;
|
|
287
|
|
288 /* display stuff */
|
3284
|
289 mp_msg(MSGT_TV, MSGL_V, " %d: %s: ", priv->audio[i].audio,
|
|
290 priv->audio[i].name);
|
|
291 if (priv->audio[i].flags & VIDEO_AUDIO_MUTABLE)
|
2841
|
292 mp_msg(MSGT_TV, MSGL_V, "muted=%s ",
|
3284
|
293 (priv->audio[i].flags & VIDEO_AUDIO_MUTE) ? "yes" : "no");
|
2841
|
294 mp_msg(MSGT_TV, MSGL_V, "volume=%d bass=%d treble=%d balance=%d mode=%s\n",
|
3284
|
295 priv->audio[i].volume, priv->audio[i].bass, priv->audio[i].treble,
|
|
296 priv->audio[i].balance, audio_mode2name[priv->audio[i].mode]);
|
3815
|
297 mp_msg(MSGT_TV, MSGL_V, " channels: %d, samplerate: %d, samplesize: %d, format: %s\n",
|
|
298 priv->audio_channels[i], priv->audio_samplerate[i], priv->audio_samplesize[i],
|
|
299 audio_out_format_name(priv->audio_format[i]));
|
2841
|
300 }
|
|
301 }
|
|
302
|
2802
|
303 if (!(priv->capability.type & VID_TYPE_CAPTURE))
|
|
304 {
|
2818
|
305 mp_msg(MSGT_TV, MSGL_ERR, "Only grabbing supported (for overlay use another program)\n");
|
2802
|
306 goto err;
|
|
307 }
|
|
308
|
|
309 /* map grab buffer */
|
3815
|
310 if (ioctl(priv->video_fd, VIDIOCGMBUF, &priv->mbuf) == -1)
|
2802
|
311 {
|
2818
|
312 mp_msg(MSGT_TV, MSGL_ERR, "ioctl get mbuf failed: %s\n", strerror(errno));
|
2802
|
313 goto err;
|
2790
|
314 }
|
|
315
|
2818
|
316 mp_msg(MSGT_TV, MSGL_V, "mbuf: size=%d, frames=%d\n",
|
2802
|
317 priv->mbuf.size, priv->mbuf.frames);
|
|
318 priv->mmap = mmap(0, priv->mbuf.size, PROT_READ|PROT_WRITE,
|
3815
|
319 MAP_SHARED, priv->video_fd, 0);
|
2819
|
320 if (priv->mmap == (unsigned char *)-1)
|
2802
|
321 {
|
3815
|
322 mp_msg(MSGT_TV, MSGL_ERR, "Unable to map memory for buffers: %s\n", strerror(errno));
|
2802
|
323 goto err;
|
|
324 }
|
2818
|
325 mp_msg(MSGT_TV, MSGL_DBG2, "our buffer: %p\n", priv->mmap);
|
2790
|
326
|
2802
|
327 /* num of buffers */
|
|
328 priv->nbuf = priv->mbuf.frames;
|
2790
|
329
|
2802
|
330 /* video buffers */
|
2819
|
331 priv->buf = (struct video_mmap *)malloc(priv->nbuf * sizeof(struct video_mmap));
|
3611
|
332 if (!priv->buf)
|
|
333 goto malloc_failed;
|
2802
|
334 memset(priv->buf, 0, priv->nbuf * sizeof(struct video_mmap));
|
2790
|
335
|
3815
|
336 /* audio init */
|
5088
|
337 #if 1
|
3815
|
338 priv->audio_fd = open(priv->audio_device, O_RDONLY);
|
|
339 if (priv->audio_fd < 0)
|
|
340 {
|
|
341 mp_msg(MSGT_TV, MSGL_ERR, "unable to open '%s': %s\n",
|
|
342 priv->audio_device, strerror(errno));
|
|
343 }
|
|
344 else
|
|
345 {
|
|
346 int ioctl_param;
|
|
347
|
|
348 fcntl(priv->audio_fd, F_SETFL, O_NONBLOCK);
|
|
349
|
|
350 #if 0
|
|
351 ioctl_param = 0x7fff000d; /* 8k */
|
|
352 printf("ioctl dsp setfragment: %d\n",
|
|
353 ioctl(priv->audio_fd, SNDCTL_DSP_SETFRAGMENT, &ioctl_param));
|
|
354 #endif
|
|
355
|
|
356 ioctl_param = 0 ;
|
|
357 printf("ioctl dsp getfmt: %d\n",
|
|
358 ioctl(priv->audio_fd, SNDCTL_DSP_GETFMTS, &ioctl_param));
|
|
359
|
|
360 printf("Supported formats: %x\n", ioctl_param);
|
|
361 if (!(ioctl_param & priv->audio_format[priv->audio_id]))
|
|
362 printf("notsupported format\n");
|
|
363
|
|
364 ioctl_param = priv->audio_format[priv->audio_id];
|
|
365 printf("ioctl dsp setfmt: %d\n",
|
|
366 ioctl(priv->audio_fd, SNDCTL_DSP_SETFMT, &ioctl_param));
|
|
367
|
|
368 // ioctl(priv->audio_fd, SNDCTL_DSP_GETISPACE, &ioctl_param);
|
|
369 // printf("getispace: %d\n", ioctl_param);
|
|
370
|
|
371 if (priv->audio_channels[priv->audio_id] > 2)
|
|
372 {
|
|
373 ioctl_param = priv->audio_channels[priv->audio_id];
|
|
374 printf("ioctl dsp channels: %d\n",
|
|
375 ioctl(priv->audio_fd, SNDCTL_DSP_CHANNELS, &ioctl_param));
|
|
376 }
|
|
377 else
|
|
378 {
|
|
379 // if (priv->audio_channels[priv->audio_id] == 2)
|
|
380 // ioctl_param = 1;
|
|
381 // else
|
|
382 // ioctl_param = 0;
|
|
383
|
|
384 ioctl_param = (priv->audio_channels[priv->audio_id] == 2);
|
|
385 printf("ioctl dsp stereo: %d (req: %d)\n",
|
|
386 ioctl(priv->audio_fd, SNDCTL_DSP_STEREO, &ioctl_param),
|
|
387 ioctl_param);
|
|
388 }
|
|
389
|
|
390 ioctl_param = priv->audio_samplerate[priv->audio_id];
|
|
391 printf("ioctl dsp speed: %d\n",
|
|
392 ioctl(priv->audio_fd, SNDCTL_DSP_SPEED, &ioctl_param));
|
|
393
|
|
394 #if 0
|
|
395 ioctl_param = 0;
|
|
396 ioctl_param = ~PCM_ENABLE_INPUT;
|
|
397 printf("ioctl dsp trigger: %d\n",
|
|
398 ioctl(priv->audio_fd, SNDCTL_DSP_SETTRIGGER, &ioctl_param));
|
|
399 ioctl_param = PCM_ENABLE_INPUT;
|
|
400 printf("ioctl dsp trigger: %d\n",
|
|
401 ioctl(priv->audio_fd, SNDCTL_DSP_SETTRIGGER, &ioctl_param));
|
|
402 #endif
|
|
403
|
|
404 printf("ioctl dsp trigger: %d\n",
|
|
405 ioctl(priv->audio_fd, SNDCTL_DSP_GETTRIGGER, &ioctl_param));
|
|
406 printf("trigger: %x\n", ioctl_param);
|
|
407 ioctl_param = PCM_ENABLE_INPUT;
|
|
408 printf("ioctl dsp trigger: %d\n",
|
|
409 ioctl(priv->audio_fd, SNDCTL_DSP_SETTRIGGER, &ioctl_param));
|
|
410
|
|
411 printf("ioctl dsp getblocksize: %d\n",
|
|
412 ioctl(priv->audio_fd, SNDCTL_DSP_GETBLKSIZE, &priv->audio_blocksize));
|
|
413 printf("blocksize: %d\n", priv->audio_blocksize);
|
|
414 }
|
|
415 #endif
|
2790
|
416 return(1);
|
|
417
|
3611
|
418
|
|
419 malloc_failed:
|
|
420 if (priv->channels)
|
|
421 free(priv->channels);
|
|
422 if (priv->buf)
|
|
423 free(priv->buf);
|
2790
|
424 err:
|
3815
|
425 if (priv->video_fd != -1)
|
|
426 close(priv->video_fd);
|
2790
|
427 return(0);
|
|
428 }
|
|
429
|
2802
|
430 static int uninit(priv_t *priv)
|
2790
|
431 {
|
3815
|
432 close(priv->video_fd);
|
|
433
|
|
434 priv->audio[priv->audio_id].volume = 0;
|
|
435 priv->audio[priv->audio_id].flags |= VIDEO_AUDIO_MUTE;
|
|
436 ioctl(priv->video_fd, VIDIOCSAUDIO, &priv->audio[priv->audio_id]);
|
|
437 close(priv->audio_fd);
|
2802
|
438 #warning "Implement uninit!"
|
2931
|
439
|
|
440 return(1);
|
2790
|
441 }
|
|
442
|
2802
|
443 static int start(priv_t *priv)
|
2790
|
444 {
|
2802
|
445 int i;
|
|
446
|
|
447
|
3815
|
448 if (ioctl(priv->video_fd, VIDIOCGPICT, &priv->picture) == -1)
|
2790
|
449 {
|
2818
|
450 mp_msg(MSGT_TV, MSGL_ERR, "ioctl get picture failed: %s\n", strerror(errno));
|
2802
|
451 return(0);
|
2790
|
452 }
|
|
453
|
2802
|
454 priv->picture.palette = format2palette(priv->format);
|
|
455 priv->picture.depth = palette2depth(priv->picture.palette);
|
|
456 priv->bytesperline = priv->width * priv->picture.depth / 8;
|
3815
|
457 // if (IMGFMT_IS_BGR(priv->format) || IMGFMT_IS_RGB(priv->format))
|
|
458 // priv->bytesperline = priv->width * priv->picture.depth / 8;
|
|
459 // if ((priv->format == IMGFMT_YV12) || (priv->format == IMGFMT_I420) || (priv->format == IMGFMT_IYUV))
|
|
460 // priv->bytesperline = priv->width * 3 / 2;
|
|
461
|
|
462 printf("palette: %d, depth: %d, bytesperline: %d\n",
|
|
463 priv->picture.palette, priv->picture.depth, priv->bytesperline);
|
|
464
|
2818
|
465 mp_msg(MSGT_TV, MSGL_INFO, "Picture values:\n");
|
|
466 mp_msg(MSGT_TV, MSGL_INFO, " Depth: %d, Palette: %d (Format: %s)\n", priv->picture.depth,
|
2802
|
467 priv->picture.palette, vo_format_name(priv->format));
|
2818
|
468 mp_msg(MSGT_TV, MSGL_INFO, " Brightness: %d, Hue: %d, Colour: %d, Contrast: %d\n",
|
2802
|
469 priv->picture.brightness, priv->picture.hue,
|
|
470 priv->picture.colour, priv->picture.contrast);
|
|
471
|
|
472
|
3815
|
473 if (ioctl(priv->video_fd, VIDIOCSPICT, &priv->picture) == -1)
|
2790
|
474 {
|
2818
|
475 mp_msg(MSGT_TV, MSGL_ERR, "ioctl set picture failed: %s\n", strerror(errno));
|
2802
|
476 return(0);
|
2790
|
477 }
|
|
478
|
2802
|
479 priv->nbuf = priv->mbuf.frames;
|
|
480 for (i=0; i < priv->nbuf; i++)
|
|
481 {
|
|
482 priv->buf[i].format = priv->picture.palette;
|
|
483 priv->buf[i].frame = i;
|
|
484 priv->buf[i].width = priv->width;
|
|
485 priv->buf[i].height = priv->height;
|
2818
|
486 mp_msg(MSGT_TV, MSGL_DBG2, "buffer: %d => %p\n", i, &priv->buf[i]);
|
2802
|
487 }
|
2837
|
488
|
2931
|
489 #if 0
|
|
490 {
|
|
491 struct video_play_mode pmode;
|
|
492
|
|
493 pmode.mode = VID_PLAY_NORMAL;
|
|
494 pmode.p1 = 1;
|
|
495 pmode.p2 = 0;
|
3815
|
496 if (ioctl(priv->video_fd, VIDIOCSPLAYMODE, &pmode) == -1)
|
2931
|
497 {
|
|
498 mp_msg(MSGT_TV, MSGL_ERR, "ioctl set play mode failed: %s\n", strerror(errno));
|
|
499 // return(0);
|
|
500 }
|
|
501 }
|
|
502 #endif
|
2837
|
503
|
|
504 #if 0
|
|
505 {
|
|
506 struct video_window win;
|
|
507
|
|
508 win.x = 0;
|
|
509 win.y = 0;
|
|
510 win.width = priv->width;
|
|
511 win.height = priv->height;
|
|
512 win.chromakey = -1;
|
|
513 win.flags = 0;
|
5088
|
514 //win.clipcount = 0;
|
2837
|
515
|
3815
|
516 ioctl(priv->video_fd, VIDIOCSWIN, &win);
|
2837
|
517 }
|
|
518
|
2802
|
519 /* start capture */
|
3815
|
520 if (ioctl(priv->video_fd, VIDIOCCAPTURE, &one) == -1)
|
2802
|
521 {
|
2818
|
522 mp_msg(MSGT_TV, MSGL_ERR, "ioctl capture failed: %s\n", strerror(errno));
|
2802
|
523 return(0);
|
|
524 }
|
2837
|
525 #endif
|
|
526
|
|
527 return(1);
|
2790
|
528 }
|
|
529
|
|
530 static int control(priv_t *priv, int cmd, void *arg)
|
|
531 {
|
2818
|
532 mp_msg(MSGT_TV, MSGL_DBG2, "debug: control(priv=%p, cmd=%d, arg=%p)\n",
|
2802
|
533 priv, cmd, arg);
|
2790
|
534 switch(cmd)
|
|
535 {
|
2802
|
536 /* ========== GENERIC controls =========== */
|
|
537 case TVI_CONTROL_IS_VIDEO:
|
|
538 {
|
|
539 if (priv->capability.type & VID_TYPE_CAPTURE)
|
|
540 return(TVI_CONTROL_TRUE);
|
|
541 return(TVI_CONTROL_FALSE);
|
|
542 }
|
|
543 case TVI_CONTROL_IS_AUDIO:
|
5088
|
544 // return(TVI_CONTROL_FALSE);
|
3815
|
545 /* also disable audio for as it's not working! */
|
2841
|
546 if (priv->channels[priv->act_channel].flags & VIDEO_VC_AUDIO)
|
3815
|
547 {
|
|
548 // printf("yeah, audio csennel!!");
|
2841
|
549 return(TVI_CONTROL_TRUE);
|
3815
|
550 }
|
|
551 return(TVI_CONTROL_TRUE);
|
2802
|
552 case TVI_CONTROL_IS_TUNER:
|
|
553 {
|
2841
|
554 // if (priv->capability.type & VID_TYPE_TUNER)
|
|
555 if (priv->channels[priv->act_channel].flags & VIDEO_VC_TUNER)
|
2802
|
556 return(TVI_CONTROL_TRUE);
|
|
557 return(TVI_CONTROL_FALSE);
|
|
558 }
|
|
559
|
|
560 /* ========== VIDEO controls =========== */
|
2790
|
561 case TVI_CONTROL_VID_GET_FORMAT:
|
2802
|
562 {
|
|
563 int output_fmt = -1;
|
|
564
|
|
565 output_fmt = priv->format;
|
|
566 (int)*(void **)arg = output_fmt;
|
2818
|
567 mp_msg(MSGT_TV, MSGL_INFO, "Output format: %s\n", vo_format_name(output_fmt));
|
2802
|
568 return(TVI_CONTROL_TRUE);
|
|
569 }
|
|
570 case TVI_CONTROL_VID_SET_FORMAT:
|
|
571 priv->format = (int)*(void **)arg;
|
2790
|
572 return(TVI_CONTROL_TRUE);
|
|
573 case TVI_CONTROL_VID_GET_PLANES:
|
3220
|
574 (int)*(void **)arg = 1; /* FIXME, also not needed at this time */
|
2790
|
575 return(TVI_CONTROL_TRUE);
|
|
576 case TVI_CONTROL_VID_GET_BITS:
|
2810
|
577 (int)*(void **)arg = palette2depth(format2palette(priv->format));
|
2790
|
578 return(TVI_CONTROL_TRUE);
|
|
579 case TVI_CONTROL_VID_GET_WIDTH:
|
|
580 (int)*(void **)arg = priv->width;
|
|
581 return(TVI_CONTROL_TRUE);
|
|
582 case TVI_CONTROL_VID_CHK_WIDTH:
|
|
583 {
|
|
584 int req_width = (int)*(void **)arg;
|
|
585
|
2818
|
586 mp_msg(MSGT_TV, MSGL_INFO, "Requested width: %d\n", req_width);
|
2810
|
587 if ((req_width >= priv->capability.minwidth) &&
|
|
588 (req_width <= priv->capability.maxwidth))
|
2790
|
589 return(TVI_CONTROL_TRUE);
|
|
590 return(TVI_CONTROL_FALSE);
|
|
591 }
|
|
592 case TVI_CONTROL_VID_SET_WIDTH:
|
|
593 priv->width = (int)*(void **)arg;
|
|
594 return(TVI_CONTROL_TRUE);
|
|
595 case TVI_CONTROL_VID_GET_HEIGHT:
|
|
596 (int)*(void **)arg = priv->height;
|
|
597 return(TVI_CONTROL_TRUE);
|
|
598 case TVI_CONTROL_VID_CHK_HEIGHT:
|
|
599 {
|
|
600 int req_height = (int)*(void **)arg;
|
|
601
|
2818
|
602 mp_msg(MSGT_TV, MSGL_INFO, "Requested height: %d\n", req_height);
|
2810
|
603 if ((req_height >= priv->capability.minheight) &&
|
|
604 (req_height <= priv->capability.maxheight))
|
2790
|
605 return(TVI_CONTROL_TRUE);
|
|
606 return(TVI_CONTROL_FALSE);
|
|
607 }
|
|
608 case TVI_CONTROL_VID_SET_HEIGHT:
|
|
609 priv->height = (int)*(void **)arg;
|
|
610 return(TVI_CONTROL_TRUE);
|
2937
|
611 case TVI_CONTROL_VID_GET_PICTURE:
|
3815
|
612 if (ioctl(priv->video_fd, VIDIOCGPICT, &priv->picture) == -1)
|
2937
|
613 {
|
|
614 mp_msg(MSGT_TV, MSGL_ERR, "ioctl get picture failed: %s\n", strerror(errno));
|
|
615 return(TVI_CONTROL_FALSE);
|
|
616 }
|
|
617 return(TVI_CONTROL_TRUE);
|
|
618 case TVI_CONTROL_VID_SET_PICTURE:
|
3815
|
619 if (ioctl(priv->video_fd, VIDIOCSPICT, &priv->picture) == -1)
|
2937
|
620 {
|
|
621 mp_msg(MSGT_TV, MSGL_ERR, "ioctl get picture failed: %s\n", strerror(errno));
|
|
622 return(TVI_CONTROL_FALSE);
|
|
623 }
|
|
624 return(TVI_CONTROL_TRUE);
|
|
625 case TVI_CONTROL_VID_SET_BRIGHTNESS:
|
|
626 priv->picture.brightness = (int)*(void **)arg;
|
|
627 control(priv, TVI_CONTROL_VID_SET_PICTURE, 0);
|
|
628 return(TVI_CONTROL_TRUE);
|
|
629 case TVI_CONTROL_VID_SET_HUE:
|
|
630 priv->picture.hue = (int)*(void **)arg;
|
|
631 control(priv, TVI_CONTROL_VID_SET_PICTURE, 0);
|
|
632 return(TVI_CONTROL_TRUE);
|
|
633 case TVI_CONTROL_VID_SET_SATURATION:
|
|
634 priv->picture.colour = (int)*(void **)arg;
|
|
635 control(priv, TVI_CONTROL_VID_SET_PICTURE, 0);
|
|
636 return(TVI_CONTROL_TRUE);
|
|
637 case TVI_CONTROL_VID_SET_CONTRAST:
|
|
638 priv->picture.contrast = (int)*(void **)arg;
|
|
639 control(priv, TVI_CONTROL_VID_SET_PICTURE, 0);
|
|
640 return(TVI_CONTROL_TRUE);
|
2790
|
641
|
2802
|
642 /* ========== TUNER controls =========== */
|
|
643 case TVI_CONTROL_TUN_GET_FREQ:
|
|
644 {
|
|
645 unsigned long freq;
|
|
646
|
3815
|
647 if (ioctl(priv->video_fd, VIDIOCGFREQ, &freq) == -1)
|
2802
|
648 {
|
2818
|
649 mp_msg(MSGT_TV, MSGL_ERR, "ioctl get freq failed: %s\n", strerror(errno));
|
2802
|
650 return(TVI_CONTROL_FALSE);
|
|
651 }
|
|
652
|
|
653 /* tuner uses khz not mhz ! */
|
2837
|
654 // if (priv->tuner.flags & VIDEO_TUNER_LOW)
|
|
655 // freq /= 1000;
|
2802
|
656 (unsigned long)*(void **)arg = freq;
|
|
657 return(TVI_CONTROL_TRUE);
|
|
658 }
|
2790
|
659 case TVI_CONTROL_TUN_SET_FREQ:
|
|
660 {
|
2802
|
661 /* argument is in MHz ! */
|
|
662 unsigned long freq = (unsigned long)*(void **)arg;
|
|
663
|
2837
|
664 mp_msg(MSGT_TV, MSGL_V, "requested frequency: %.3f\n", (float)freq/16);
|
2802
|
665
|
|
666 /* tuner uses khz not mhz ! */
|
2837
|
667 // if (priv->tuner.flags & VIDEO_TUNER_LOW)
|
|
668 // freq *= 1000;
|
|
669 // mp_msg(MSGT_TV, MSGL_V, " requesting from driver: freq=%.3f\n", (float)freq/16);
|
3815
|
670 if (ioctl(priv->video_fd, VIDIOCSFREQ, &freq) == -1)
|
2802
|
671 {
|
2818
|
672 mp_msg(MSGT_TV, MSGL_ERR, "ioctl set freq failed: %s\n", strerror(errno));
|
2802
|
673 return(TVI_CONTROL_FALSE);
|
|
674 }
|
|
675 return(TVI_CONTROL_TRUE);
|
|
676 }
|
|
677 case TVI_CONTROL_TUN_GET_TUNER:
|
|
678 {
|
3815
|
679 if (ioctl(priv->video_fd, VIDIOCGTUNER, &priv->tuner) == -1)
|
2802
|
680 {
|
2818
|
681 mp_msg(MSGT_TV, MSGL_ERR, "ioctl get tuner failed: %s\n", strerror(errno));
|
2802
|
682 return(TVI_CONTROL_FALSE);
|
|
683 }
|
|
684
|
2818
|
685 mp_msg(MSGT_TV, MSGL_INFO, "Tuner (%s) range: %lu -> %lu\n", priv->tuner.name,
|
2802
|
686 priv->tuner.rangelow, priv->tuner.rangehigh);
|
|
687 return(TVI_CONTROL_TRUE);
|
|
688 }
|
|
689 case TVI_CONTROL_TUN_SET_TUNER:
|
|
690 {
|
3815
|
691 if (ioctl(priv->video_fd, VIDIOCSTUNER, &priv->tuner) == -1)
|
2802
|
692 {
|
2818
|
693 mp_msg(MSGT_TV, MSGL_ERR, "ioctl get tuner failed: %s\n", strerror(errno));
|
2802
|
694 return(TVI_CONTROL_FALSE);
|
|
695 }
|
|
696 return(TVI_CONTROL_TRUE);
|
|
697 }
|
|
698 case TVI_CONTROL_TUN_SET_NORM:
|
|
699 {
|
|
700 int req_mode = (int)*(void **)arg;
|
|
701
|
|
702 if ((!(priv->tuner.flags & VIDEO_TUNER_NORM)) ||
|
|
703 ((req_mode == VIDEO_MODE_PAL) && !(priv->tuner.flags & VIDEO_TUNER_PAL)) ||
|
|
704 ((req_mode == VIDEO_MODE_NTSC) && !(priv->tuner.flags & VIDEO_TUNER_NTSC)) ||
|
|
705 ((req_mode == VIDEO_MODE_SECAM) && !(priv->tuner.flags & VIDEO_TUNER_SECAM)))
|
|
706 {
|
2818
|
707 mp_msg(MSGT_TV, MSGL_ERR, "Tuner isn't capable to set norm!\n");
|
2802
|
708 return(TVI_CONTROL_FALSE);
|
|
709 }
|
|
710
|
|
711 priv->tuner.mode = req_mode;
|
2790
|
712
|
3815
|
713 if (control(priv->video_fd, TVI_CONTROL_TUN_SET_TUNER, &priv->tuner) != TVI_CONTROL_TRUE)
|
2802
|
714 return(TVI_CONTROL_FALSE);
|
|
715 return(TVI_CONTROL_TRUE);
|
|
716 }
|
|
717 case TVI_CONTROL_TUN_GET_NORM:
|
|
718 {
|
|
719 (int)*(void **)arg = priv->tuner.mode;
|
|
720
|
|
721 return(TVI_CONTROL_TRUE);
|
|
722 }
|
|
723
|
|
724 /* ========== AUDIO controls =========== */
|
|
725 case TVI_CONTROL_AUD_GET_FORMAT:
|
|
726 {
|
3815
|
727 (int)*(void **)arg = priv->audio_format[priv->audio_id];
|
2802
|
728 return(TVI_CONTROL_TRUE);
|
|
729 }
|
|
730 case TVI_CONTROL_AUD_GET_CHANNELS:
|
|
731 {
|
3815
|
732 (int)*(void **)arg = priv->audio_channels[priv->audio_id];
|
2802
|
733 return(TVI_CONTROL_TRUE);
|
|
734 }
|
|
735 case TVI_CONTROL_AUD_GET_SAMPLERATE:
|
|
736 {
|
3815
|
737 (int)*(void **)arg = priv->audio_samplerate[priv->audio_id];
|
2802
|
738 return(TVI_CONTROL_TRUE);
|
|
739 }
|
|
740 case TVI_CONTROL_AUD_GET_SAMPLESIZE:
|
|
741 {
|
3815
|
742 (int)*(void **)arg = priv->audio_samplesize[priv->audio_id];
|
2802
|
743 return(TVI_CONTROL_TRUE);
|
|
744 }
|
|
745
|
|
746 /* ========== SPECIFIC controls =========== */
|
|
747 case TVI_CONTROL_SPC_GET_INPUT:
|
|
748 {
|
|
749 int req_chan = (int)*(void **)arg;
|
|
750 int i;
|
|
751
|
|
752 for (i = 0; i < priv->capability.channels; i++)
|
|
753 {
|
|
754 if (priv->channels[i].channel == req_chan)
|
|
755 break;
|
|
756 }
|
2841
|
757
|
|
758 priv->act_channel = i;
|
2802
|
759
|
3815
|
760 if (ioctl(priv->video_fd, VIDIOCGCHAN, &priv->channels[i]) == -1)
|
2802
|
761 {
|
2818
|
762 mp_msg(MSGT_TV, MSGL_ERR, "ioctl get channel failed: %s\n", strerror(errno));
|
2802
|
763 return(TVI_CONTROL_FALSE);
|
|
764 }
|
|
765 return(TVI_CONTROL_TRUE);
|
|
766 }
|
|
767
|
|
768 case TVI_CONTROL_SPC_SET_INPUT:
|
|
769 {
|
|
770 struct video_channel chan;
|
|
771 int req_chan = (int)*(void **)arg;
|
|
772 int i;
|
|
773
|
|
774 if (req_chan >= priv->capability.channels)
|
|
775 {
|
2818
|
776 mp_msg(MSGT_TV, MSGL_ERR, "Invalid input requested: %d, valid: 0-%d\n",
|
2802
|
777 req_chan, priv->capability.channels);
|
|
778 return(TVI_CONTROL_FALSE);
|
|
779 }
|
|
780
|
|
781 for (i = 0; i < priv->capability.channels; i++)
|
|
782 {
|
|
783 if (priv->channels[i].channel == req_chan)
|
|
784 chan = priv->channels[i];
|
|
785 }
|
|
786
|
3815
|
787 if (ioctl(priv->video_fd, VIDIOCSCHAN, &chan) == -1)
|
2802
|
788 {
|
2818
|
789 mp_msg(MSGT_TV, MSGL_ERR, "ioctl set chan failed: %s\n", strerror(errno));
|
2802
|
790 return(TVI_CONTROL_FALSE);
|
|
791 }
|
2818
|
792 mp_msg(MSGT_TV, MSGL_INFO, "Using input '%s'\n", chan.name);
|
2802
|
793
|
2841
|
794 priv->act_channel = i;
|
|
795
|
2802
|
796 /* update tuner state */
|
2841
|
797 // if (priv->capability.type & VID_TYPE_TUNER)
|
|
798 if (priv->channels[priv->act_channel].flags & VIDEO_VC_TUNER)
|
2802
|
799 control(priv, TVI_CONTROL_TUN_GET_TUNER, 0);
|
|
800
|
|
801 /* update local channel list */
|
|
802 control(priv, TVI_CONTROL_SPC_GET_INPUT, &req_chan);
|
|
803 return(TVI_CONTROL_TRUE);
|
2790
|
804 }
|
|
805 }
|
|
806
|
|
807 return(TVI_CONTROL_UNKNOWN);
|
|
808 }
|
|
809
|
|
810 static int grab_video_frame(priv_t *priv, char *buffer, int len)
|
|
811 {
|
2802
|
812 int frame = priv->queue % priv->nbuf;
|
2814
|
813 int nextframe = (priv->queue+1) % priv->nbuf;
|
2802
|
814
|
5088
|
815 priv->currentframe++;
|
|
816
|
3284
|
817 mp_dbg(MSGT_TV, MSGL_DBG2, "grab_video_frame(priv=%p, buffer=%p, len=%d)\n",
|
2802
|
818 priv, buffer, len);
|
|
819
|
2931
|
820 mp_dbg(MSGT_TV, MSGL_DBG3, "buf: %p + frame: %d => %p\n",
|
2814
|
821 priv->buf, nextframe, &priv->buf[nextframe]);
|
3815
|
822 if (ioctl(priv->video_fd, VIDIOCMCAPTURE, &priv->buf[nextframe]) == -1)
|
2790
|
823 {
|
2818
|
824 mp_msg(MSGT_TV, MSGL_ERR, "ioctl mcapture failed: %s\n", strerror(errno));
|
2802
|
825 return(0);
|
2790
|
826 }
|
3711
|
827
|
3815
|
828 while (ioctl(priv->video_fd, VIDIOCSYNC, &priv->buf[frame].frame) < 0 &&
|
3711
|
829 (errno == EAGAIN || errno == EINTR));
|
|
830 mp_dbg(MSGT_TV, MSGL_DBG3, "picture sync failed\n");
|
|
831
|
2802
|
832 priv->queue++;
|
|
833
|
2931
|
834 mp_dbg(MSGT_TV, MSGL_DBG3, "mmap: %p + offset: %d => %p\n",
|
2802
|
835 priv->mmap, priv->mbuf.offsets[frame],
|
|
836 priv->mmap+priv->mbuf.offsets[frame]);
|
2931
|
837
|
|
838 /* XXX also directrendering would be nicer! */
|
|
839 /* 3 times copying the same picture to other buffer :( */
|
|
840
|
|
841 /* copy the actual frame */
|
2802
|
842 memcpy(buffer, priv->mmap+priv->mbuf.offsets[frame], len);
|
|
843
|
5088
|
844 return(priv->currentframe);
|
2790
|
845 }
|
|
846
|
|
847 static int get_video_framesize(priv_t *priv)
|
|
848 {
|
2931
|
849 return(priv->bytesperline * priv->height);
|
2790
|
850 }
|
|
851
|
|
852 static int grab_audio_frame(priv_t *priv, char *buffer, int len)
|
|
853 {
|
3815
|
854 int in_len = 0;
|
5088
|
855 // int max_tries = 128;
|
3815
|
856
|
5088
|
857 mp_dbg(MSGT_TV, MSGL_DBG2, "grab_audio_frame(priv=%p, buffer=%p, len=%d)\n",
|
3815
|
858 priv, buffer, len);
|
|
859
|
5088
|
860 // while (--max_tries > 0)
|
3815
|
861 for (;;)
|
|
862 {
|
|
863 in_len = read(priv->audio_fd, buffer, len);
|
|
864 // printf("in_len: %d\n", in_len);
|
|
865 // fflush(NULL);
|
|
866
|
|
867 if (in_len > 0)
|
|
868 break;
|
|
869 if (!((in_len == 0) || (in_len == -1 && (errno == EAGAIN || errno == EINTR))))
|
|
870 {
|
|
871 in_len = 0; /* -EIO */
|
|
872 break;
|
|
873 }
|
|
874 }
|
5088
|
875 // printf("tries: %d\n", 128-max_tries);
|
3815
|
876
|
|
877 return(in_len);
|
2790
|
878 }
|
|
879
|
|
880 static int get_audio_framesize(priv_t *priv)
|
|
881 {
|
3815
|
882 return(priv->audio_blocksize);
|
|
883 // return(priv->audio_samplesize[priv->audio_id]);
|
2790
|
884 }
|
|
885
|
|
886 #endif /* USE_TV */
|