1 /*
2 * Copyright (C) 2003-2020 the xine project
3 * Copyright (C) 2003 J.Asselman <j.asselman@itsec-ps.nl>
4 *
5 * This file is part of xine, a free video player.
6 *
7 * xine is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * xine 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 General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
20 *
21 * v4l input plugin
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include <unistd.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34
35 /* From GStreamer's v4l plugin:
36 * Because of some really cool feature in video4linux1, also known as
37 * 'not including sys/types.h and sys/time.h', we had to include it
38 * ourselves. In all their intelligence, these people decided to fix
39 * this in the next version (video4linux2) in such a cool way that it
40 * breaks all compilations of old stuff...
41 * The real problem is actually that linux/time.h doesn't use proper
42 * macro checks before defining types like struct timeval. The proper
43 * fix here is to either fuck the kernel header (which is what we do
44 * by defining _LINUX_TIME_H, an innocent little hack) or by fixing it
45 * upstream, which I'll consider doing later on. If you get compiler
46 * errors here, check your linux/time.h && sys/time.h header setup.
47 */
48 #define _LINUX_TIME_H
49
50 #include <linux/videodev.h>
51 #include <sys/ioctl.h>
52 #include <sys/mman.h>
53 #include <errno.h>
54
55 /* Used to capture the audio data */
56 #define ALSA_PCM_NEW_HW_PARAMS_API
57 #define ALSA_PCM_NEW_SW_PARAMS_API
58 #ifdef HAVE_ALSA
59 #include <alsa/asoundlib.h>
60 #endif
61
62 #define XINE_ENABLE_EXPERIMENTAL_FEATURES
63
64 /********** logging **********/
65 /* #define LOG_MODULE "input_v4l" */
66 #define LOG_VERBOSE
67 /*
68 #define LOG
69 */
70
71 #ifdef LOG
72 #define LOG_MODULE log_line_prefix()
73
log_line_prefix()74 static char *log_line_prefix()
75 {
76 static int print_timestamp = 1;
77 struct timeval now;
78 struct tm now_tm;
79 char buffer[64];
80
81 if( print_timestamp ) {
82 gettimeofday( &now, NULL );
83 localtime_r( &now.tv_sec, &now_tm );
84 strftime( buffer, sizeof( buffer ), "%Y-%m-%d %H:%M:%S", &now_tm );
85 printf( "%s.%6.6ld: ", buffer, now.tv_usec );
86 }
87 return "input_v4l";
88 }
89 #endif
90
91 #include <xine/xine_internal.h>
92 #include <xine/xineutils.h>
93 #include <xine/input_plugin.h>
94
95 #define NUM_FRAMES 15
96
97 /* Our CPU can't handle de-interlacing at 768. */
98 #define MAX_RES 640
99
100 typedef struct {
101 int width;
102 int height;
103 } resolution_t;
104
105 static const resolution_t resolutions[] = {
106 { 768, 576 },
107 { 640, 480 },
108 { 384, 288 },
109 { 320, 240 },
110 { 160, 120 }
111 };
112
113 #define NUM_RESOLUTIONS (sizeof(resolutions)/sizeof(resolutions[0]))
114 #define RADIO_DEV "/dev/radio0"
115 #define VIDEO_DEV "/dev/video0"
116 #ifdef HAVE_ALSA
117 #define AUDIO_DEV "plughw:0,0"
118 #endif
119
120 static const char *const tv_standard_names[] = { "AUTO", "PAL", "NTSC", "SECAM", "OLD", NULL };
121 static const int tv_standard_values[] = { VIDEO_MODE_AUTO, VIDEO_MODE_PAL, VIDEO_MODE_NTSC, VIDEO_MODE_SECAM, -1 };
122
123 #if !defined(NDELAY) && defined(O_NDELAY)
124 #define FNDELAY O_NDELAY
125 #endif
126
127 typedef struct pvrscr_s pvrscr_t;
128
129 typedef struct {
130 input_plugin_t input_plugin;
131
132 xine_stream_t *stream;
133 char *mrl;
134
135 off_t curpos;
136
137 int old_interlace;
138 int old_zoomx;
139 int old_zoomy;
140 int audio_only;
141
142 buf_element_t *frames_base;
143 void *audio_content_base;
144 void *video_content_base;
145
146 /* Audio */
147 buf_element_t *aud_frames;
148 pthread_mutex_t aud_frames_lock;
149 pthread_cond_t aud_frame_freed;
150
151 #ifdef HAVE_ALSA
152 /* Handle for the PCM device */
153 snd_pcm_t *pcm_handle;
154
155 /* Record stream (via line 1) */
156 snd_pcm_stream_t pcm_stream;
157
158 /* Information and configuration for the PCM stream */
159 snd_pcm_hw_params_t *pcm_hwparams;
160
161 /* Name of the PCM device, plughw:0,0?=>soundcard,device*/
162 char *pcm_name;
163
164 /* Use alsa to capture the sound (for a/v sync) */
165 char audio_capture;
166
167 int exact_rate; /* Actual sample rate
168 sndpcm_hw_params_set_rate_near */
169 int dir; /* exact rate == rate --> dir = 0
170 exact rate < rate --> dir = -1
171 exact rate > rate --> dir = 1 */
172
173 unsigned char *pcm_data;
174
175 int64_t pts_aud_start;
176 #endif
177
178 int audio_header_sent;
179
180 int rate; /* Sample rate */
181 int periods; /* Number of periods */
182 int periodsize; /* Periodsize in bytes */
183 int bits;
184
185 /* Video */
186 buf_element_t *vid_frames;
187 pthread_mutex_t vid_frames_lock;
188 pthread_cond_t vid_frame_freed;
189
190 int video_fd;
191 int radio_fd;
192
193 int input;
194 int tuner;
195 unsigned long frequency;
196 unsigned long calc_frequency;
197 char *tuner_name;
198
199 int radio; /* ask for a radio channel */
200 int channel; /* channel number */
201
202 struct video_channel video_channel;
203 struct video_tuner video_tuner;
204 struct video_capability video_cap;
205 struct video_audio audio;
206 struct video_audio audio_saved;
207 struct video_mbuf gb_buffers;
208
209 int video_header_sent;
210
211 int frame_format;
212 const resolution_t *resolution;
213 int frame_size;
214 int use_mmap;
215 uint8_t *video_buf;
216 int gb_frame;
217 struct video_mmap gb_buf;
218 int64_t start_time;
219
220 xine_event_queue_t *event_queue;
221
222 pvrscr_t *scr;
223 int scr_tuning;
224
225 } v4l_input_plugin_t;
226
227 /*
228 * ***************************************************
229 * unix System Clock Reference + fine tuning
230 *
231 * This code is copied and paste from the input_pvr.c
232 *
233 * the fine tuning option is used to change play
234 * speed in order to regulate fifo usage, that is,
235 * trying to match the rate of generated data.
236 *
237 * OBS: use with audio.synchronization.av_sync_method=resample
238 * ***************************************************
239 */
240
241 #define SCR_PAUSED -2
242 #define SCR_FW -3
243 #define SCR_SKIP -4
244
245 struct pvrscr_s {
246 scr_plugin_t scr;
247
248 struct timeval cur_time;
249 int64_t cur_pts;
250 int xine_speed;
251 double speed_factor;
252 double speed_tuning;
253
254 pthread_mutex_t lock;
255 };
256
pvrscr_get_priority(scr_plugin_t * scr)257 static int pvrscr_get_priority(scr_plugin_t *scr)
258 {
259 (void)scr;
260 return 10; /* high priority */
261 }
262
263 /* Only call this when already mutex locked */
pvrscr_set_pivot(pvrscr_t * this)264 static void pvrscr_set_pivot(pvrscr_t *this)
265 {
266 struct timeval tv;
267 int64_t pts;
268 double pts_calc;
269
270 xine_monotonic_clock(&tv, NULL);
271 pts_calc = (tv.tv_sec - this->cur_time.tv_sec) * this->speed_factor;
272 pts_calc += (tv.tv_usec - this->cur_time.tv_usec) * this->speed_factor / 1e6;
273 pts = this->cur_pts + pts_calc;
274
275 /* This next part introduces a one off inaccuracy
276 * to the scr due to rounding tv to pts.
277 */
278 this->cur_time.tv_sec = tv.tv_sec;
279 this->cur_time.tv_usec = tv.tv_usec;
280 this->cur_pts = pts;
281
282 return;
283 }
284
pvrscr_set_fine_speed(scr_plugin_t * scr,int speed)285 static int pvrscr_set_fine_speed (scr_plugin_t *scr, int speed)
286 {
287 pvrscr_t *this = (pvrscr_t*) scr;
288
289 pthread_mutex_lock (&this->lock);
290
291 pvrscr_set_pivot( this );
292 this->xine_speed = speed;
293 this->speed_factor = (double) speed * 90000.0 / XINE_FINE_SPEED_NORMAL *
294 this->speed_tuning;
295
296 pthread_mutex_unlock (&this->lock);
297
298 return speed;
299 }
300
pvrscr_speed_tuning(pvrscr_t * this,double factor)301 static void pvrscr_speed_tuning (pvrscr_t *this, double factor)
302 {
303 pthread_mutex_lock (&this->lock);
304
305 pvrscr_set_pivot( this );
306 this->speed_tuning = factor;
307 this->speed_factor = (double) this->xine_speed * 90000.0 / XINE_FINE_SPEED_NORMAL *
308 this->speed_tuning;
309
310 pthread_mutex_unlock (&this->lock);
311 }
312
pvrscr_adjust(scr_plugin_t * scr,int64_t vpts)313 static void pvrscr_adjust (scr_plugin_t *scr, int64_t vpts)
314 {
315 pvrscr_t *this = (pvrscr_t*) scr;
316 struct timeval tv;
317
318 pthread_mutex_lock (&this->lock);
319
320 xine_monotonic_clock(&tv, NULL);
321 this->cur_time.tv_sec = tv.tv_sec;
322 this->cur_time.tv_usec = tv.tv_usec;
323 this->cur_pts = vpts;
324
325 pthread_mutex_unlock (&this->lock);
326 }
327
pvrscr_start(scr_plugin_t * scr,int64_t start_vpts)328 static void pvrscr_start (scr_plugin_t *scr, int64_t start_vpts)
329 {
330 pvrscr_t *this = (pvrscr_t*) scr;
331
332 pthread_mutex_lock (&this->lock);
333
334 xine_monotonic_clock(&this->cur_time, NULL);
335 this->cur_pts = start_vpts;
336
337 pthread_mutex_unlock (&this->lock);
338
339 pvrscr_set_fine_speed (&this->scr, XINE_FINE_SPEED_NORMAL);
340 }
341
pvrscr_get_current(scr_plugin_t * scr)342 static int64_t pvrscr_get_current (scr_plugin_t *scr)
343 {
344 pvrscr_t *this = (pvrscr_t*) scr;
345 struct timeval tv;
346 int64_t pts;
347 double pts_calc;
348
349 pthread_mutex_lock (&this->lock);
350
351 xine_monotonic_clock(&tv, NULL);
352
353 pts_calc = (tv.tv_sec - this->cur_time.tv_sec) * this->speed_factor;
354 pts_calc += (tv.tv_usec - this->cur_time.tv_usec) * this->speed_factor / 1e6;
355 pts = this->cur_pts + pts_calc;
356
357 pthread_mutex_unlock (&this->lock);
358
359 /*printf("returning pts %lld\n", pts);*/
360 return pts;
361 }
362
pvrscr_exit(scr_plugin_t * scr)363 static void pvrscr_exit (scr_plugin_t *scr)
364 {
365 pvrscr_t *this = (pvrscr_t*) scr;
366
367 pthread_mutex_destroy (&this->lock);
368 free(this);
369 }
370
pvrscr_init(void)371 static pvrscr_t *XINE_MALLOC pvrscr_init (void)
372 {
373 pvrscr_t *this;
374
375 this = calloc(1, sizeof(pvrscr_t));
376
377 this->scr.interface_version = 3;
378 this->scr.get_priority = pvrscr_get_priority;
379 this->scr.set_fine_speed = pvrscr_set_fine_speed;
380 this->scr.adjust = pvrscr_adjust;
381 this->scr.start = pvrscr_start;
382 this->scr.get_current = pvrscr_get_current;
383 this->scr.exit = pvrscr_exit;
384
385 pthread_mutex_init (&this->lock, NULL);
386
387 pvrscr_speed_tuning(this, 1.0 );
388 pvrscr_set_fine_speed (&this->scr, XINE_SPEED_PAUSE);
389 #ifdef SCRLOG
390 printf("input_v4l: scr init complete\n");
391 #endif
392
393 return this;
394 }
395
396 /*** END COPY AND PASTE from PVR**************************/
397
398 /*** The following is copy and past from net_buf_ctrl ****/
report_progress(xine_stream_t * stream,int p)399 static void report_progress (xine_stream_t *stream, int p)
400 {
401 xine_event_t event;
402 xine_progress_data_t prg;
403
404 if (p == SCR_PAUSED) {
405 prg.description = _("Buffer underrun...");
406 p = 0;
407 } else
408 if (p == SCR_FW) {
409 prg.description = _("Buffer overrun...");
410 p = 100;
411 } else
412 prg.description = _("Adjusting...");
413
414 prg.percent = (p>100)?100:p;
415
416 event.type = XINE_EVENT_PROGRESS;
417 event.data = &prg;
418 event.data_length = sizeof (xine_progress_data_t);
419
420 xine_event_send (stream, &event);
421 }
422
423 /**** END COPY AND PASTE from net_buf_ctrl ***************/
424
425 static int search_by_tuner(v4l_input_plugin_t *this, char *input_source);
426 static int search_by_channel(v4l_input_plugin_t *this, char *input_source);
427 static void v4l_event_handler(v4l_input_plugin_t *this);
428
429 /**
430 * Allocate an audio frame.
431 */
alloc_aud_frame(v4l_input_plugin_t * this)432 inline static buf_element_t *alloc_aud_frame (v4l_input_plugin_t *this)
433 {
434 buf_element_t *frame;
435
436 lprintf("alloc_aud_frame. trying to get lock...\n");
437
438 pthread_mutex_lock (&this->aud_frames_lock) ;
439
440 lprintf("got the lock\n");
441
442 while (!this->aud_frames) {
443 lprintf("no audio frame available...\n");
444 pthread_cond_wait (&this->aud_frame_freed, &this->aud_frames_lock);
445 }
446
447 frame = this->aud_frames;
448 this->aud_frames = this->aud_frames->next;
449
450 pthread_mutex_unlock (&this->aud_frames_lock);
451
452 lprintf("alloc_aud_frame done\n");
453
454 return frame;
455 }
456
457 /**
458 * Stores an audio frame.
459 */
store_aud_frame(buf_element_t * frame)460 static void store_aud_frame (buf_element_t *frame)
461 {
462 v4l_input_plugin_t *this = (v4l_input_plugin_t *) frame->source;
463
464 lprintf("store_aud_frame\n");
465
466 pthread_mutex_lock (&this->aud_frames_lock) ;
467
468 frame->next = this->aud_frames;
469 this->aud_frames = frame;
470
471 pthread_cond_signal (&this->aud_frame_freed);
472 pthread_mutex_unlock (&this->aud_frames_lock);
473 }
474
475 /**
476 * Allocate a video frame.
477 */
alloc_vid_frame(v4l_input_plugin_t * this)478 inline static buf_element_t *alloc_vid_frame (v4l_input_plugin_t *this)
479 {
480 buf_element_t *frame;
481
482 lprintf("alloc_vid_frame. trying to get lock...\n");
483
484 pthread_mutex_lock (&this->vid_frames_lock) ;
485
486 lprintf("got the lock\n");
487
488 while (!this->vid_frames) {
489 lprintf("no video frame available...\n");
490 pthread_cond_wait (&this->vid_frame_freed, &this->vid_frames_lock);
491 }
492
493 frame = this->vid_frames;
494 this->vid_frames = this->vid_frames->next;
495
496 pthread_mutex_unlock (&this->vid_frames_lock);
497
498 lprintf("alloc_vid_frame done\n");
499
500 return frame;
501 }
502
503 /**
504 * Stores a video frame.
505 */
store_vid_frame(buf_element_t * frame)506 static void store_vid_frame (buf_element_t *frame)
507 {
508
509 v4l_input_plugin_t *this = (v4l_input_plugin_t *) frame->source;
510
511 lprintf("store_vid_frame\n");
512
513 pthread_mutex_lock (&this->vid_frames_lock) ;
514
515 frame->next = this->vid_frames;
516 this->vid_frames = frame;
517
518 pthread_cond_signal (&this->vid_frame_freed);
519 pthread_mutex_unlock (&this->vid_frames_lock);
520 }
521
extract_mrl(v4l_input_plugin_t * this,const char * mrl)522 static int extract_mrl(v4l_input_plugin_t *this, const char *mrl)
523 {
524 char *tuner_name = NULL;
525 int frequency = 0;
526 const char *locator = NULL;
527 const char *begin = NULL;
528
529 if (mrl == NULL) {
530 lprintf("Someone passed an empty mrl\n");
531 return 0;
532 }
533
534 for (locator = mrl; *locator != '\0' && *locator != '/' ; locator++);
535
536 /* Get tuner name */
537 if (*locator == '/') {
538 begin = ++locator;
539
540 for (; *locator != '\0' && *locator != '/' ; locator++);
541
542 tuner_name = (char *) strndup(begin, locator - begin);
543
544 /* Get frequency, if available */
545 sscanf(locator, "/%d", &frequency);
546
547 /* cannot use xprintf to log in this routine */
548 lprintf("input_v4l: Tuner name: %s frequency %d\n", tuner_name, frequency );
549 }
550
551 this->frequency = frequency;
552 this->tuner_name = tuner_name;
553
554 return 1;
555 }
556
set_frequency(v4l_input_plugin_t * this,unsigned long frequency)557 static int set_frequency(v4l_input_plugin_t *this, unsigned long frequency)
558 {
559 int ret = 0;
560 int fd;
561
562 if (this->video_fd > 0)
563 fd = this->video_fd;
564 else
565 fd = this->radio_fd;
566
567 if (frequency != 0) {
568 /* FIXME: Don't assume tuner 0 ? */
569 this->tuner = 0;
570 ret = ioctl(fd, VIDIOCSTUNER, &this->tuner);
571 lprintf("(%d) Response on set tuner to %d\n", ret, this->tuner);
572 this->video_tuner.tuner = this->tuner;
573
574 if (this->video_tuner.flags & VIDEO_TUNER_LOW) {
575 this->calc_frequency = frequency * 16;
576 } else {
577 this->calc_frequency = (frequency * 16) / 1000;
578 }
579
580 ret = ioctl(fd, VIDIOCSFREQ, &this->calc_frequency);
581
582 xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
583 "input_v4l: set frequency (%ld) returned: %d\n", frequency, ret);
584 } else {
585 xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
586 "input_v4l: No frequency given. Expected syntax: v4l:/tuner/frequency\n"
587 "input_v4l: Using currently tuned settings\n");
588 }
589
590 this->frequency = frequency;
591
592 if (ret < 0)
593 return ret;
594 else
595 return 1;
596 }
597
set_input_source(v4l_input_plugin_t * this,char * input_source)598 static int set_input_source(v4l_input_plugin_t *this, char *input_source)
599 {
600 int ret = 0;
601
602 if ((ret = search_by_channel(this, input_source)) != 1) {
603 ret = search_by_tuner(this, input_source);
604 }
605
606 return ret;
607 }
608
search_by_tuner(v4l_input_plugin_t * this,char * input_source)609 static int search_by_tuner(v4l_input_plugin_t *this, char *input_source)
610 {
611 int ret = 0;
612 int fd = 0;
613 int cur_tuner = 0;
614
615 if (this->video_fd > 0)
616 fd = this->video_fd;
617 else
618 fd = this->radio_fd;
619
620 this->video_tuner.tuner = cur_tuner;
621 ioctl(fd, VIDIOCGCAP, &this->video_cap);
622
623 lprintf("This device has %d channel(s)\n", this->video_cap.channels);
624
625 for (ret = ioctl(fd, VIDIOCGTUNER, &this->video_tuner);
626 ret == 0 && this->video_cap.channels > cur_tuner && strstr(this->video_tuner.name, input_source) == NULL;
627 cur_tuner++) {
628
629 this->video_tuner.tuner = cur_tuner;
630
631 lprintf("(%d) V4L device currently set to: \n", ret);
632 lprintf("Tuner: %d\n", this->video_tuner.tuner);
633 lprintf("Name: %s\n", this->video_tuner.name);
634 if (this->video_tuner.flags & VIDEO_TUNER_LOW) {
635 lprintf("Range: %ld - %ld\n", this->video_tuner.rangelow / 16, this->video_tuner.rangehigh * 16);
636 } else {
637 lprintf("Range: %ld - %ld\n", this->video_tuner.rangelow * 1000 / 16, this->video_tuner.rangehigh * 1000 / 16);
638 }
639 }
640
641 lprintf("(%d) V4L device final: \n", ret);
642 lprintf("Tuner: %d\n", this->video_tuner.tuner);
643 lprintf("Name: %s\n", this->video_tuner.name);
644 if (this->video_tuner.flags & VIDEO_TUNER_LOW) {
645 lprintf("Range: %ld - %ld\n", this->video_tuner.rangelow / 16, this->video_tuner.rangehigh * 16);
646 } else {
647 lprintf("Range: %ld - %ld\n", this->video_tuner.rangelow * 1000 / 16, this->video_tuner.rangehigh * 1000 / 16);
648 }
649
650 if (strstr(this->video_tuner.name, input_source) == NULL)
651 return -1;
652
653 return 1;
654 }
655
search_by_channel(v4l_input_plugin_t * this,char * input_source)656 static int search_by_channel(v4l_input_plugin_t *this, char *input_source)
657 {
658 int ret = 0;
659 int fd = 0;
660 cfg_entry_t *tv_standard_entry;
661
662 lprintf("input_source: %s\n", input_source);
663
664 this->input = 0;
665
666 if (this->video_fd > 0)
667 fd = this->video_fd;
668 else
669 fd = this->radio_fd;
670
671 /* Tune into channel */
672 if (strlen(input_source) > 0) {
673 for( this->video_channel.channel = 0;
674 ioctl(fd, VIDIOCGCHAN, &this->video_channel) == 0;
675 this->video_channel.channel++ ) {
676
677 lprintf("V4L device currently set to:\n");
678 lprintf("Channel: %d\n", this->video_channel.channel);
679 lprintf("Name: %s\n", this->video_channel.name);
680 lprintf("Tuners: %d\n", this->video_channel.tuners);
681 lprintf("Flags: %d\n", this->video_channel.flags);
682 lprintf("Type: %d\n", this->video_channel.type);
683 lprintf("Norm: %d\n", this->video_channel.norm);
684
685 if (strstr(this->video_channel.name, input_source) != NULL) {
686 this->input = this->video_channel.channel;
687 break;
688 }
689 }
690
691 if (strstr(this->video_channel.name, input_source) == NULL) {
692 xprintf(this->stream->xine, XINE_VERBOSITY_LOG, _("Tuner name not found\n"));
693 return -1;
694 }
695
696 tv_standard_entry = this->stream->xine->config->lookup_entry(this->stream->xine->config,
697 "media.video4linux.tv_standard");
698 this->tuner_name = input_source;
699 if (tv_standard_entry->num_value != 0) {
700 this->video_channel.norm = tv_standard_values[ tv_standard_entry->num_value ];
701 xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
702 "input_v4l: TV Standard configured as STD %s (%d)\n",
703 tv_standard_names[ tv_standard_entry->num_value ], this->video_channel.norm );
704 ret = ioctl(fd, VIDIOCSCHAN, &this->video_channel);
705 } else
706 ret = ioctl(fd, VIDIOCSCHAN, &this->input);
707
708 lprintf("(%d) Set channel to %d\n", ret, this->input);
709 } else {
710 xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
711 "input_v4l: Not setting video source. No source given\n");
712 }
713 ret = ioctl(fd, VIDIOCGTUNER, &this->video_tuner);
714
715 lprintf("(%d) Flags %d\n", ret, this->video_tuner.flags);
716
717 lprintf("VIDEO_TUNER_PAL %s set\n", this->video_tuner.flags & VIDEO_TUNER_PAL ? "" : "not");
718 lprintf("VIDEO_TUNER_NTSC %s set\n", this->video_tuner.flags & VIDEO_TUNER_NTSC ? "" : "not");
719 lprintf("VIDEO_TUNER_SECAM %s set\n", this->video_tuner.flags & VIDEO_TUNER_SECAM ? "" : "not");
720 lprintf("VIDEO_TUNER_LOW %s set\n", this->video_tuner.flags & VIDEO_TUNER_LOW ? "" : "not");
721 lprintf("VIDEO_TUNER_NORM %s set\n", this->video_tuner.flags & VIDEO_TUNER_NORM ? "" : "not");
722 lprintf("VIDEO_TUNER_STEREO_ON %s set\n", this->video_tuner.flags & VIDEO_TUNER_STEREO_ON ? "" : "not");
723 lprintf("VIDEO_TUNER_RDS_ON %s set\n", this->video_tuner.flags & VIDEO_TUNER_RDS_ON ? "" : "not");
724 lprintf("VIDEO_TUNER_MBS_ON %s set\n", this->video_tuner.flags & VIDEO_TUNER_MBS_ON ? "" : "not");
725
726 switch (this->video_tuner.mode) {
727 case VIDEO_MODE_PAL:
728 lprintf("The tuner is in PAL mode\n");
729 break;
730 case VIDEO_MODE_NTSC:
731 lprintf("The tuner is in NTSC mode\n");
732 break;
733 case VIDEO_MODE_SECAM:
734 lprintf("The tuner is in SECAM mode\n");
735 break;
736 case VIDEO_MODE_AUTO:
737 lprintf("The tuner is in AUTO mode\n");
738 break;
739 }
740
741 return 1;
742 }
743
allocate_frames(v4l_input_plugin_t * this,unsigned dovideo)744 static void allocate_frames(v4l_input_plugin_t *this, unsigned dovideo)
745 {
746 const size_t framescount = dovideo ? 2*NUM_FRAMES : NUM_FRAMES;
747
748 /* Allocate a single memory area for both audio and video frames */
749 buf_element_t *frames = this->frames_base =
750 calloc(framescount, sizeof(buf_element_t));
751 extra_info_t *infos =
752 calloc(framescount, sizeof(extra_info_t));
753
754 int i;
755
756 uint8_t *audio_content = this->audio_content_base =
757 calloc(NUM_FRAMES, this->periodsize);
758
759 /* Set up audio frames */
760 for (i = 0; i < NUM_FRAMES; i++) {
761 /* Audio frame */
762 frames[i].content = audio_content;
763 frames[i].type = BUF_AUDIO_LPCM_LE;
764 frames[i].source = this;
765 frames[i].free_buffer = store_aud_frame;
766 frames[i].extra_info = &infos[i];
767
768 audio_content += this->periodsize;
769 store_aud_frame(&frames[i]);
770 }
771
772 if ( dovideo ) {
773 uint8_t *video_content = this->video_content_base =
774 calloc(NUM_FRAMES, this->frame_size);
775
776 /* Set up video frames */
777 for (i = NUM_FRAMES; i < 2*NUM_FRAMES; i++) {
778 /* Video frame */
779 frames[i].content = video_content;
780 frames[i].type = this->frame_format;
781 frames[i].source = this;
782 frames[i].free_buffer = store_vid_frame;
783 frames[i].extra_info = &infos[i];
784
785 video_content += this->frame_size;
786 store_vid_frame(&frames[i]);
787 }
788 }
789 }
790
unmute_audio(v4l_input_plugin_t * this)791 static void unmute_audio(v4l_input_plugin_t *this)
792 {
793 int fd;
794
795 lprintf("unmute_audio\n");
796
797 if (this->video_fd > 0)
798 fd = this->video_fd;
799 else
800 fd = this->radio_fd;
801
802 ioctl(fd, VIDIOCGAUDIO, &this->audio);
803 memcpy(&this->audio_saved, &this->audio, sizeof(this->audio));
804
805 this->audio.flags &= ~VIDEO_AUDIO_MUTE;
806 this->audio.volume = 0xD000;
807
808 ioctl(fd, VIDIOCSAUDIO, &this->audio);
809 }
810
open_radio_capture_device(v4l_input_plugin_t * this)811 static int open_radio_capture_device(v4l_input_plugin_t *this)
812 {
813 int tuner_found = 0;
814 cfg_entry_t *entry;
815
816 lprintf("open_radio_capture_device\n");
817
818 entry = this->stream->xine->config->lookup_entry(this->stream->xine->config,
819 "media.video4linux.radio_device");
820
821 if((this->radio_fd = xine_open_cloexec(entry->str_value, O_RDWR)) < 0) {
822 xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
823 "input_v4l: error opening v4l device (%s): %s\n",
824 entry->str_value, strerror(errno));
825 return 0;
826 }
827
828 lprintf("Device opened, radio %d\n", this->radio_fd);
829
830 if (set_input_source(this, this->tuner_name) > 0)
831 tuner_found = 1;
832
833 _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_AUDIO, 1);
834 _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_VIDEO, 0);
835
836 /* Pre-allocate some frames for audio so it doesn't have to be done during
837 * capture */
838 allocate_frames(this, 0);
839
840 this->audio_only = 1;
841
842 /* Unmute audio off video capture device */
843 unmute_audio(this);
844
845 set_frequency(this, this->frequency);
846
847 if (tuner_found)
848 return 1;
849 else
850 return 2;
851 }
852
853 /**
854 * Open the video capture device.
855 *
856 * This opens the video capture device and if given, selects a tuner from
857 * which the signal should be grabbed.
858 * @return 1 on success, 0 on failure.
859 */
open_video_capture_device(v4l_input_plugin_t * this)860 static int open_video_capture_device(v4l_input_plugin_t *this)
861 {
862 int found = 0;
863 int tuner_found = 0;
864 int ret;
865 unsigned int j;
866 cfg_entry_t *entry;
867
868 lprintf("open_video_capture_device\n");
869
870 entry = this->stream->xine->config->lookup_entry(this->stream->xine->config,
871 "media.video4linux.video_device");
872
873 /* Try to open the video device */
874 if((this->video_fd = xine_open_cloexec(entry->str_value, O_RDWR)) < 0) {
875 xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
876 "input_v4l: error opening v4l device (%s): %s\n",
877 entry->str_value, strerror(errno));
878 return 0;
879 }
880
881 lprintf("Device opened, tv %d\n", this->video_fd);
882
883 /* figure out the resolution */
884 for (j = 0; j < NUM_RESOLUTIONS; j++)
885 {
886 if (resolutions[j].width <= this->video_cap.maxwidth
887 && resolutions[j].height <= this->video_cap.maxheight
888 && resolutions[j].width <= MAX_RES)
889 {
890 found = 1;
891 break;
892 }
893 }
894
895 if (found == 0 || resolutions[j].width < this->video_cap.minwidth
896 || resolutions[j].height < this->video_cap.minheight)
897 {
898 /* Looks like the device does not support one of the preset resolutions */
899 lprintf("Grab device does not support any preset resolutions");
900 return 0;
901 }
902
903 this->resolution = &resolutions[j];
904
905 _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_AUDIO, 1);
906 _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_VIDEO, 1);
907
908 /* Pre-allocate some frames for audio and video so it doesn't have to be
909 * done during capture */
910 allocate_frames(this, 1);
911
912 /* Unmute audio off video capture device */
913 unmute_audio(this);
914
915 if (strlen(this->tuner_name) > 0) {
916 /* Tune into source and given frequency */
917 if (set_input_source(this, this->tuner_name) <= 0)
918 return 0;
919 else
920 tuner_found = 1;
921 }
922
923 set_frequency(this, this->frequency);
924
925 /* Test for mmap video access */
926 ret = ioctl(this->video_fd,VIDIOCGMBUF, &this->gb_buffers);
927
928 if (ret < 0) {
929 /* Device driver does not support mmap */
930 /* try to use read based access */
931 struct video_picture pict;
932 int val;
933
934 ioctl(this->video_fd, VIDIOCGPICT, &pict);
935
936 /* try to choose a suitable video format */
937 pict.palette = VIDEO_PALETTE_YUV420P;
938 ret = ioctl(this->video_fd, VIDIOCSPICT, &pict);
939 if (ret < 0) {
940 pict.palette = VIDEO_PALETTE_YUV422;
941 ret = ioctl(this->video_fd, VIDIOCSPICT, &pict);
942 if (ret < 0) {
943 close (this->video_fd);
944 this->video_fd = -1;
945 lprintf("Grab: no colour space format found\n");
946 return 0;
947 }
948 else
949 lprintf("Grab: format YUV 4:2:2\n");
950 }
951 else
952 lprintf("Grab: format YUV 4:2:0\n");
953
954 this->frame_format = pict.palette;
955 val = 1;
956 ioctl(this->video_fd, VIDIOCCAPTURE, &val);
957
958 this->use_mmap = 0;
959
960 } else {
961 /* Good, device driver support mmap. Mmap the memory */
962 lprintf("using mmap, size %d\n", this->gb_buffers.size);
963 this->video_buf = mmap(0, this->gb_buffers.size,
964 PROT_READ|PROT_WRITE, MAP_SHARED,
965 this->video_fd,0);
966 if ((unsigned char*)-1 == this->video_buf) {
967 /* mmap failed. */;
968 perror("mmap");
969 close (this->video_fd);
970 return 0;
971 }
972 this->gb_frame = 0;
973
974 /* start to grab the first frame */
975 this->gb_buf.frame = (this->gb_frame + 1) % this->gb_buffers.frames;
976 this->gb_buf.height = resolutions[j].height;
977 this->gb_buf.width = resolutions[j].width;
978 this->gb_buf.format = VIDEO_PALETTE_YUV420P;
979
980 ret = ioctl(this->video_fd, VIDIOCMCAPTURE, &this->gb_buf);
981 if (ret < 0 && errno != EAGAIN) {
982 /* try YUV422 */
983 this->gb_buf.format = VIDEO_PALETTE_YUV422;
984
985 ret = ioctl(this->video_fd, VIDIOCMCAPTURE, &this->gb_buf);
986 }
987 else
988 lprintf("(%d) YUV420 should work\n", ret);
989
990 if (ret < 0) {
991 if (errno != EAGAIN) {
992 lprintf("grab device does not support suitable format\n");
993 } else {
994 lprintf("grab device does not receive any video signal\n");
995 }
996 close (this->video_fd);
997 return 0;
998 }
999 this->frame_format = this->gb_buf.format;
1000 this->use_mmap = 1;
1001 }
1002
1003 switch(this->frame_format) {
1004 case VIDEO_PALETTE_YUV420P:
1005 this->frame_format = BUF_VIDEO_I420;
1006 this->frame_size = (resolutions[j].width * resolutions[j].height * 3) / 2;
1007 break;
1008 case VIDEO_PALETTE_YUV422:
1009 this->frame_format = BUF_VIDEO_YUY2;
1010 this->frame_size = resolutions[j].width * resolutions[j].height * 2;
1011 break;
1012 }
1013
1014 /* Strip the vbi / sync signal from the image by zooming in */
1015 this->old_zoomx = xine_get_param(this->stream, XINE_PARAM_VO_ZOOM_X);
1016 this->old_zoomy = xine_get_param(this->stream, XINE_PARAM_VO_ZOOM_Y);
1017
1018 xine_set_param(this->stream, XINE_PARAM_VO_ZOOM_X, 103);
1019 xine_set_param(this->stream, XINE_PARAM_VO_ZOOM_Y, 103);
1020
1021 /* Pre-allocate some frames for audio and video so it doesn't have to be
1022 * done during capture */
1023 allocate_frames(this, 1);
1024
1025 /* If we made it here, everything went ok */
1026 this->audio_only = 0;
1027 if (tuner_found)
1028 return 1;
1029 else
1030 /* Not a real error, appart that the tuner name is unknown to us */
1031 return 2;
1032 }
1033
1034 /**
1035 * Open audio capture device.
1036 *
1037 * This function opens an alsa capture device. This will be used to capture
1038 * audio data from.
1039 */
open_audio_capture_device(v4l_input_plugin_t * this)1040 static int open_audio_capture_device(v4l_input_plugin_t *this)
1041 {
1042 #ifdef HAVE_ALSA
1043 int mode = 0;
1044 snd_pcm_uframes_t buf_size = (this->periodsize * this->periods) >> 2;
1045 lprintf("open_audio_capture_device\n");
1046
1047 /* Allocate the snd_pcm_hw_params_t structure on the stack. */
1048 snd_pcm_hw_params_alloca(&this->pcm_hwparams);
1049
1050 /* If we are not capturing video, open the sound device in blocking mode,
1051 * otherwise xine gets too many NULL bufs and doesn't seem to handle them
1052 * correctly. If we are capturing video, open the sound device in non-
1053 * blocking mode, otherwise we will loose video frames while waiting */
1054 if(!this->audio_only)
1055 mode = SND_PCM_NONBLOCK;
1056
1057 /* Open the PCM device. */
1058 if(snd_pcm_open(&this->pcm_handle, this->pcm_name, this->pcm_stream, mode) < 0) {
1059 xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
1060 "input_v4l: Error opening PCM device: %s\n", this->pcm_name);
1061 this->audio_capture = 0;
1062 }
1063
1064 /* Get parameters */
1065 if (this->audio_capture &&
1066 (snd_pcm_hw_params_any(this->pcm_handle, this->pcm_hwparams) < 0)) {
1067 xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
1068 "input_v4l: Broken configuration for PCM device: No configurations available\n");
1069 this->audio_capture = 0;
1070 }
1071
1072 /* Set access type */
1073 if (this->audio_capture &&
1074 (snd_pcm_hw_params_set_access(this->pcm_handle, this->pcm_hwparams,
1075 SND_PCM_ACCESS_RW_INTERLEAVED) < 0)) {
1076 xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
1077 "input_v4l: Error setting SND_PCM_ACCESS_RW_INTERLEAVED\n");
1078 this->audio_capture = 0;
1079 }
1080
1081 /* Set sample format */
1082 if (this->audio_capture &&
1083 (snd_pcm_hw_params_set_format(this->pcm_handle,
1084 this->pcm_hwparams, SND_PCM_FORMAT_S16_LE) < 0)) {
1085 xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
1086 "input_v4l: Error setting SND_PCM_FORMAT_S16_LE\n");
1087 this->audio_capture = 0;
1088 }
1089
1090 /* Set sample rate */
1091 this->exact_rate = this->rate;
1092 if (this->audio_capture &&
1093 (snd_pcm_hw_params_set_rate_near(this->pcm_handle, this->pcm_hwparams,
1094 &this->exact_rate, &this->dir) < 0)) {
1095 xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
1096 "input_v4l: Error setting samplerate\n");
1097 this->audio_capture = 0;
1098 }
1099 if (this->audio_capture && this->dir != 0) {
1100 xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
1101 "input_v4l: Samplerate %d Hz is not supported by your hardware\n", this->rate);
1102 xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
1103 "input_v4l: Using %d instead\n", this->exact_rate);
1104 }
1105
1106 /* Set number of channels */
1107 if (this->audio_capture &&
1108 (snd_pcm_hw_params_set_channels(this->pcm_handle, this->pcm_hwparams, 2) < 0)) {
1109 xprintf(this->stream->xine, XINE_VERBOSITY_LOG, "input_v4l: Error setting PCM channels\n");
1110 this->audio_capture = 0;
1111 }
1112
1113 if (this->audio_capture &&
1114 (snd_pcm_hw_params_set_periods(this->pcm_handle, this->pcm_hwparams, this->periods, 0) < 0)) {
1115 xprintf(this->stream->xine, XINE_VERBOSITY_LOG, "input_v4l: Error setting PCM periods\n");
1116 this->audio_capture = 0;
1117 }
1118
1119 /* Set buffersize */
1120 if (this->audio_capture &&
1121 (snd_pcm_hw_params_set_buffer_size_near(this->pcm_handle,
1122 this->pcm_hwparams,
1123 &buf_size) < 0)) {
1124 xprintf(this->stream->xine, XINE_VERBOSITY_LOG, "input_v4l: Error setting PCM buffer size to %d\n", (int)buf_size );
1125 this->audio_capture = 0;
1126 }
1127
1128 /* Apply HW parameter settings */
1129 if (this->audio_capture &&
1130 (snd_pcm_hw_params(this->pcm_handle, this->pcm_hwparams) < 0)) {
1131 xprintf(this->stream->xine, XINE_VERBOSITY_LOG, "input_v4l: Error Setting PCM HW params\n");
1132 this->audio_capture = 0;
1133 }
1134
1135 if (this->audio_capture) {
1136 lprintf("Allocating memory for PCM capture :%d\n", this->periodsize);
1137 this->pcm_data = (unsigned char*) malloc(this->periodsize);
1138 } else
1139 this->pcm_data = NULL;
1140
1141 lprintf("Audio device succesfully configured\n");
1142 #endif
1143 return 0;
1144 }
1145
1146 /**
1147 * Adjust realtime speed
1148 *
1149 * If xine is playing at normal speed, tries to adjust xines playing speed to
1150 * avoid buffer overrun and buffer underrun
1151 */
v4l_adjust_realtime_speed(v4l_input_plugin_t * this,fifo_buffer_t * fifo,int speed)1152 static int v4l_adjust_realtime_speed(v4l_input_plugin_t *this, fifo_buffer_t *fifo, int speed)
1153 {
1154 int num_used, num_free;
1155 int scr_tuning = this->scr_tuning;
1156
1157 if (fifo == NULL)
1158 return 0;
1159
1160 num_used = fifo->size(fifo);
1161 num_free = NUM_FRAMES - num_used;
1162
1163 if (!this->audio_only && num_used == 0 && scr_tuning != SCR_PAUSED) {
1164 /* Buffer is empty, and we did not pause playback */
1165 report_progress(this->stream, SCR_PAUSED);
1166
1167 xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG,
1168 "input_v4l: Buffer empty, pausing playback (used: %d, num_free: %d)\n",
1169 num_used, num_free);
1170
1171 _x_set_speed(this->stream, XINE_SPEED_PAUSE);
1172 this->stream->xine->clock->set_option (this->stream->xine->clock, CLOCK_SCR_ADJUSTABLE, 0);
1173
1174 this->scr_tuning = SCR_PAUSED;
1175 /* pvrscr_speed_tuning(this->scr, 0.0); */
1176
1177 } else if (num_free <= 1 && scr_tuning != SCR_SKIP) {
1178 this->scr_tuning = SCR_SKIP;
1179 xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG,
1180 "input_v4l: Buffer full, skipping (used: %d, free: %d)\n", num_used, num_free);
1181 return 0;
1182 } else if (scr_tuning == SCR_PAUSED) {
1183 if (2 * num_used > num_free) {
1184 /* Playback was paused, but we have normal buffer usage again */
1185 xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG,
1186 "input_v4l: Resuming from paused (used: %d, free: %d)\n", num_used, num_free);
1187
1188 this->scr_tuning = 0;
1189
1190 pvrscr_speed_tuning(this->scr, 1.0);
1191
1192 _x_set_speed(this->stream, XINE_SPEED_NORMAL);
1193 this->stream->xine->clock->set_option (this->stream->xine->clock, CLOCK_SCR_ADJUSTABLE, 1);
1194 }
1195 } else if (scr_tuning == SCR_SKIP) {
1196 if (num_used < 2 * num_free) {
1197 xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG,
1198 "input_v4l: Resuming from skipping (used: %d, free %d)\n", num_used, num_free);
1199 this->scr_tuning = 0;
1200 } else {
1201 return 0;
1202 }
1203 } else if (speed == XINE_SPEED_NORMAL) {
1204 if (num_used > 2 * num_free)
1205 /* buffer used > 2/3. Increase playback speed to avoid buffer
1206 * overrun */
1207 scr_tuning = +1;
1208 else if (num_free > 2 * num_used)
1209 /* Buffer used < 1/3. Decrease playback speed to avoid buffer
1210 * underrun */
1211 scr_tuning = -1;
1212 else if ((scr_tuning > 0 && num_free > num_used) ||
1213 (scr_tuning < 0 && num_used > num_free))
1214 /* Buffer usage is ok again. Set playback speed to normal */
1215 scr_tuning = 0;
1216
1217 /* Check if speed adjustment should be changed */
1218 if (scr_tuning != this->scr_tuning) {
1219 this->scr_tuning = scr_tuning;
1220 xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG,
1221 "input_v4l: scr tuning = %d (used: %d, free: %d)\n",
1222 scr_tuning, num_used, num_free);
1223 pvrscr_speed_tuning(this->scr, 1.0 + (0.01 * scr_tuning));
1224 }
1225 } else if (this->scr_tuning) {
1226 /* Currently speed adjustment is on. But xine is not playing at normal
1227 * speed, so there is no reason why we should try to adjust our playback
1228 * speed
1229 */
1230 this->scr_tuning = 0;
1231
1232 xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG,
1233 "input_v4l: scr tuning resetting (used: %d, free: %d\n", num_used, num_free);
1234
1235 pvrscr_speed_tuning(this->scr, 1.0);
1236 }
1237
1238 return 1;
1239 }
1240
1241 /**
1242 * Plugin read.
1243 * This function is not supported by the plugin.
1244 */
v4l_plugin_read(input_plugin_t * this_gen,void * buf,off_t len)1245 static off_t v4l_plugin_read (input_plugin_t *this_gen, void *buf, off_t len) {
1246 lprintf("Read not supported\n");
1247 (void)this_gen;
1248 (void)buf;
1249 (void)len;
1250 return 0;
1251 }
1252
1253 /**
1254 * Get time.
1255 * Gets a pts time value.
1256 */
get_time(void)1257 inline static int64_t get_time(void) {
1258 struct timeval tv;
1259
1260 xine_monotonic_clock(&tv,NULL);
1261
1262 return (int64_t) tv.tv_sec * 90000 + (int64_t) tv.tv_usec * 9 / 100;
1263 }
1264
1265
1266 /**
1267 * Plugin read block
1268 * Reads one data block. This is either an audio frame or an video frame
1269 */
v4l_plugin_read_block(input_plugin_t * this_gen,fifo_buffer_t * fifo,off_t todo)1270 static buf_element_t *v4l_plugin_read_block (input_plugin_t *this_gen, fifo_buffer_t *fifo, off_t todo)
1271 {
1272 v4l_input_plugin_t *this = (v4l_input_plugin_t *) this_gen;
1273 buf_element_t *buf = NULL;
1274 uint8_t *ptr;
1275 static char video = 0;
1276 int speed = _x_get_speed(this->stream);
1277
1278 #ifndef LOG
1279 (void)todo;
1280 #endif
1281 v4l_event_handler(this);
1282
1283 #ifdef HAVE_ALSA
1284 if (!this->audio_header_sent) {
1285 lprintf("sending audio header\n");
1286
1287 buf = alloc_aud_frame (this);
1288
1289 buf->size = 0;
1290 buf->decoder_flags = BUF_FLAG_HEADER|BUF_FLAG_STDHEADER|BUF_FLAG_FRAME_END;
1291
1292 buf->decoder_info[0] = 0;
1293 buf->decoder_info[1] = this->exact_rate;
1294 buf->decoder_info[2] = this->bits;
1295 buf->decoder_info[3] = 2;
1296
1297 this->audio_header_sent = 1;
1298
1299 return buf;
1300 }
1301 #endif
1302
1303 if (!this->audio_only && !this->video_header_sent) {
1304 xine_bmiheader bih;
1305
1306 lprintf("sending video header");
1307
1308 memset(&bih, 0, sizeof(bih));
1309 bih.biSize = sizeof(xine_bmiheader);
1310 bih.biWidth = this->resolution->width;
1311 bih.biHeight = this->resolution->height;
1312
1313 buf = alloc_vid_frame (this);
1314
1315 buf->size = sizeof(xine_bmiheader);
1316 buf->decoder_flags = BUF_FLAG_HEADER|BUF_FLAG_STDHEADER|BUF_FLAG_FRAME_END;
1317
1318 memcpy(buf->content, &bih, sizeof(xine_bmiheader));
1319
1320 this->video_header_sent = 1;
1321
1322 return buf;
1323 }
1324
1325 if (!this->audio_only) {
1326 if (!v4l_adjust_realtime_speed(this, fifo, speed)) {
1327 return NULL;
1328 }
1329 }
1330
1331 if (!this->audio_only)
1332 video = !video;
1333 else
1334 video = 0;
1335
1336 lprintf("%lld bytes...\n", todo);
1337
1338 if (this->start_time == 0)
1339 /* Create a start pts value */
1340 this->start_time = get_time(); /* this->stream->xine->clock->get_current_time(this->stream->xine->clock); */
1341
1342 if (video) {
1343 /* Capture video */
1344 buf = alloc_vid_frame (this);
1345 buf->decoder_flags = BUF_FLAG_FRAME_START|BUF_FLAG_FRAME_END;
1346
1347 this->gb_buf.frame = this->gb_frame;
1348
1349 lprintf("VIDIOCMCAPTURE\n");
1350
1351 while (ioctl(this->video_fd, VIDIOCMCAPTURE, &this->gb_buf) < 0) {
1352 lprintf("Upper while loop\n");
1353 if (errno == EAGAIN) {
1354 lprintf("Cannot sync\n");
1355 continue;
1356 } else {
1357 perror("VIDIOCMCAPTURE");
1358 buf->free_buffer(buf);
1359 return NULL;
1360 }
1361 }
1362
1363 this->gb_frame = (this->gb_frame + 1) % this->gb_buffers.frames;
1364
1365 while (ioctl(this->video_fd, VIDIOCSYNC, &this->gb_frame) < 0 &&
1366 (errno == EAGAIN || errno == EINTR))
1367 {
1368 lprintf("Waiting for videosync\n");
1369 }
1370
1371 /* printf ("grabbing frame #%d\n", frame_num); */
1372
1373 ptr = this->video_buf + this->gb_buffers.offsets[this->gb_frame];
1374 buf->pts = get_time(); /* this->stream->xine->clock->get_current_time(this->stream->xine->clock); */
1375 xine_fast_memcpy (buf->content, ptr, this->frame_size);
1376 }
1377 #ifdef HAVE_ALSA
1378 else if (this->audio_capture) {
1379 /* Record audio */
1380 int pcmreturn;
1381
1382 if ((pcmreturn = snd_pcm_readi(this->pcm_handle, this->pcm_data, (this->periodsize)>> 2)) < 0) {
1383 switch (pcmreturn) {
1384 case -EAGAIN:
1385 /* No data available at the moment */
1386 break;
1387 case -EBADFD: /* PCM device in wrong state */
1388 xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
1389 "input_v4l: PCM is not in the right state\n");
1390 break;
1391 case -EPIPE: /* Buffer overrun */
1392 xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
1393 "input_v4l: PCM buffer Overrun (lost some samples)\n");
1394 /* On buffer overrun we need to re prepare the capturing pcm device */
1395 snd_pcm_prepare(this->pcm_handle);
1396 break;
1397 default: /* Unknown */
1398 xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
1399 "input_v4l: Unknown PCM error code: %d\n", pcmreturn);
1400 snd_pcm_prepare(this->pcm_handle);
1401 }
1402 } else {
1403 /* Succesfully read audio data */
1404
1405 if (this->pts_aud_start) {
1406 buf = alloc_aud_frame (this);
1407 buf->decoder_flags = 0;
1408 }
1409
1410 /* We want the pts on the start of the sample. As the soundcard starts
1411 * sampling a new sample as soon as the read function returned with a
1412 * success we will save the current pts and assign the current pts to
1413 * that sample when we read it
1414 */
1415
1416 /* Assign start pts to sample */
1417 if (buf)
1418 buf->pts = this->pts_aud_start;
1419
1420 /* Save start pts */
1421 this->pts_aud_start = get_time(); /* this->stream->xine->clock->get_current_time(this->stream->xine->clock); */
1422
1423 if (!buf)
1424 /* Skip first sample as we don't have a good pts for this one */
1425 return NULL;
1426
1427 lprintf("Audio: Data read: %d [%d, %d]. Pos: %d\n",
1428 pcmreturn, (int) (*this->pcm_data), (int) (*(this->pcm_data + this->periodsize - 3)),
1429 (int) this->curpos);
1430
1431
1432 /* Tell decoder the number of bytes we have read */
1433 buf->size = pcmreturn<<2;
1434
1435 this->curpos++;
1436
1437 xine_fast_memcpy(buf->content, this->pcm_data, buf->size);
1438 }
1439 }
1440 #endif
1441
1442 lprintf("read block done\n");
1443
1444 return buf;
1445 }
1446
1447 /**
1448 * Plugin seek.
1449 * Not supported by the plugin.
1450 */
v4l_plugin_seek(input_plugin_t * this_gen,off_t offset,int origin)1451 static off_t v4l_plugin_seek (input_plugin_t *this_gen, off_t offset, int origin) {
1452 v4l_input_plugin_t *this = (v4l_input_plugin_t *) this_gen;
1453
1454 (void)this_gen;
1455 #ifndef LOG
1456 (void)offset;
1457 (void)origin;
1458 #endif
1459 lprintf("seek %lld bytes, origin %d\n", offset, origin);
1460 return this->curpos;
1461 }
1462
1463 /**
1464 * Plugin get length.
1465 * This is a live stream, and as such does not have an known end.
1466 */
v4l_plugin_get_length(input_plugin_t * this_gen)1467 static off_t v4l_plugin_get_length (input_plugin_t *this_gen) {
1468 /*
1469 v4l_input_plugin_t *this = (v4l_input_plugin_t *) this_gen;
1470 off_t length;
1471 */
1472
1473 (void)this_gen;
1474 return -1;
1475 }
1476
1477 /**
1478 * Plugin get capabilitiets.
1479 * This plugin does not support any special capabilities.
1480 */
v4l_plugin_get_capabilities(input_plugin_t * this_gen)1481 static uint32_t v4l_plugin_get_capabilities (input_plugin_t *this_gen)
1482 {
1483 v4l_input_plugin_t *this = (v4l_input_plugin_t *) this_gen;
1484
1485 if (this->audio_only)
1486 return 0x10;
1487 else
1488 return 0; /* 0x10: Has audio only. */
1489 }
1490
1491 /**
1492 * Plugin get block size.
1493 * Unsupported by the plugin.
1494 */
v4l_plugin_get_blocksize(input_plugin_t * this_gen)1495 static uint32_t v4l_plugin_get_blocksize (input_plugin_t *this_gen)
1496 {
1497 (void)this_gen;
1498 return 0;
1499 }
1500
1501 /**
1502 * Plugin get current pos.
1503 * Unsupported by the plugin.
1504 */
v4l_plugin_get_current_pos(input_plugin_t * this_gen)1505 static off_t v4l_plugin_get_current_pos (input_plugin_t *this_gen){
1506 v4l_input_plugin_t *this = (v4l_input_plugin_t *) this_gen;
1507
1508 /*
1509 printf ("current pos is %lld\n", this->curpos);
1510 */
1511
1512 return this->curpos;
1513 }
1514
1515 /**
1516 * Event handler.
1517 *
1518 * Processes events from a frontend. This way frequencies can be changed
1519 * without closing the v4l plugin.
1520 */
v4l_event_handler(v4l_input_plugin_t * this)1521 static void v4l_event_handler (v4l_input_plugin_t *this) {
1522 xine_event_t *event;
1523
1524 while ((event = xine_event_get (this->event_queue))) {
1525 xine_set_v4l2_data_t *v4l2_data = event->data;
1526
1527 switch (event->type) {
1528 case XINE_EVENT_SET_V4L2:
1529 if( v4l2_data->input != this->input ||
1530 v4l2_data->channel != this->channel ||
1531 v4l2_data->frequency != this->frequency ) {
1532
1533 this->input = v4l2_data->input;
1534 this->channel = v4l2_data->channel;
1535 this->frequency = v4l2_data->frequency;
1536
1537 lprintf("Switching to input:%d chan:%d freq:%.2f\n",
1538 v4l2_data->input,
1539 v4l2_data->channel,
1540 (float)v4l2_data->frequency);
1541
1542 set_frequency(this, this->frequency);
1543 _x_demux_flush_engine(this->stream);
1544 }
1545 break;
1546 /* default:
1547
1548 lprintf("Got an event, type 0x%08x\n", event->type);
1549 */
1550 }
1551
1552 xine_event_free (event);
1553 }
1554 }
1555
1556 /**
1557 * Dispose plugin.
1558 *
1559 * Closes the plugin, restore the V4L device in the initial state (volume) and
1560 * frees the allocated memory
1561 */
v4l_plugin_dispose(input_plugin_t * this_gen)1562 static void v4l_plugin_dispose (input_plugin_t *this_gen) {
1563 v4l_input_plugin_t *this = (v4l_input_plugin_t *) this_gen;
1564
1565 _x_freep(&this->mrl);
1566
1567 if (this->scr) {
1568 this->stream->xine->clock->unregister_scr(this->stream->xine->clock, &this->scr->scr);
1569 this->scr->scr.exit(&this->scr->scr);
1570 }
1571
1572 /* Close and free video device */
1573 _x_freep(&this->tuner_name);
1574
1575 /* Close video device only if device was opened */
1576 if (this->video_fd > 0) {
1577
1578 /* Restore v4l audio volume */
1579 lprintf("Restoring v4l audio volume %d\n",
1580 ioctl(this->video_fd, VIDIOCSAUDIO, &this->audio_saved));
1581 ioctl(this->video_fd, VIDIOCSAUDIO, &this->audio_saved);
1582
1583 /* Unmap memory */
1584 if (this->video_buf != NULL &&
1585 munmap(this->video_buf, this->gb_buffers.size) != 0) {
1586 xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
1587 "input_v4l: Could not unmap video memory: %s\n", strerror(errno));
1588 } else
1589 lprintf("Succesfully unmapped video memory (size %d)\n", this->gb_buffers.size);
1590
1591 lprintf("Closing video filehandler %d\n", this->video_fd);
1592
1593 /* Now close the video device */
1594 if (close(this->video_fd) != 0)
1595 xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
1596 "input_v4l: Error while closing video file handler: %s\n", strerror(errno));
1597 else
1598 lprintf("Video device succesfully closed\n");
1599
1600 /* Restore zoom setting */
1601 xine_set_param(this->stream, XINE_PARAM_VO_ZOOM_X, this->old_zoomx);
1602 xine_set_param(this->stream, XINE_PARAM_VO_ZOOM_Y, this->old_zoomy);
1603 }
1604
1605 if (this->radio_fd > 0) {
1606 close(this->radio_fd);
1607 }
1608
1609 #ifdef HAVE_ALSA
1610 /* Close audio device */
1611 if (this->pcm_handle) {
1612 snd_pcm_drop(this->pcm_handle);
1613 snd_pcm_close(this->pcm_handle);
1614 }
1615
1616 _x_freep(&this->pcm_data);
1617 _x_freep(&this->pcm_name);
1618 #endif
1619
1620 if (this->event_queue)
1621 xine_event_dispose_queue (this->event_queue);
1622
1623 /* All the frames, both video and audio, are allocated in a single
1624 memory area pointed by the frames_base pointer. The content of
1625 the frames is divided in two areas, one pointed by
1626 audio_content_base and the other by video_content_base. The
1627 extra_info structures are all allocated in the first frame
1628 data. */
1629 _x_freep(&this->audio_content_base);
1630 _x_freep(&this->video_content_base);
1631 if (this->frames_base)
1632 _x_freep(&this->frames_base->extra_info);
1633 _x_freep(&this->frames_base);
1634
1635 #ifdef LOG
1636 printf("\n");
1637 #endif
1638
1639 free (this);
1640
1641 lprintf("plugin Bye bye! \n");
1642 }
1643
1644 /**
1645 * Get MRL.
1646 *
1647 * Get the current MRL used by the plugin.
1648 */
v4l_plugin_get_mrl(input_plugin_t * this_gen)1649 static const char* v4l_plugin_get_mrl (input_plugin_t *this_gen) {
1650 v4l_input_plugin_t *this = (v4l_input_plugin_t *) this_gen;
1651
1652 return this->mrl;
1653 }
1654
v4l_plugin_get_optional_data(input_plugin_t * this_gen,void * data,int data_type)1655 static int v4l_plugin_get_optional_data (input_plugin_t *this_gen,
1656 void *data, int data_type) {
1657 /* v4l_input_plugin_t *this = (v4l_input_plugin_t *) this_gen; */
1658
1659 (void)this_gen;
1660 (void)data;
1661 (void)data_type;
1662 return INPUT_OPTIONAL_UNSUPPORTED;
1663 }
1664
v4l_plugin_radio_open(input_plugin_t * this_gen)1665 static int v4l_plugin_radio_open (input_plugin_t *this_gen)
1666 {
1667 v4l_input_plugin_t *this = (v4l_input_plugin_t *) this_gen;
1668
1669 if(open_radio_capture_device(this) != 1)
1670 return 0;
1671
1672 open_audio_capture_device(this);
1673
1674 #ifdef HAVE_ALSA
1675 this->start_time = 0;
1676 this->pts_aud_start = 0;
1677 this->curpos = 0;
1678 this->event_queue = xine_event_new_queue (this->stream);
1679 #endif
1680
1681 return 1;
1682 }
1683
1684
v4l_plugin_video_open(input_plugin_t * this_gen)1685 static int v4l_plugin_video_open (input_plugin_t *this_gen)
1686 {
1687 v4l_input_plugin_t *this = (v4l_input_plugin_t *) this_gen;
1688 int64_t time;
1689
1690 if(!open_video_capture_device(this))
1691 return 0;
1692
1693 open_audio_capture_device(this);
1694
1695 #ifdef HAVE_ALSA
1696 this->pts_aud_start = 0;
1697 #endif
1698 this->start_time = 0;
1699 this->curpos = 0;
1700
1701 /* Register our own scr provider */
1702 time = this->stream->xine->clock->get_current_time(this->stream->xine->clock);
1703 this->scr = pvrscr_init();
1704 this->scr->scr.start(&this->scr->scr, time);
1705 this->stream->xine->clock->register_scr(this->stream->xine->clock, &this->scr->scr);
1706 this->scr_tuning = 0;
1707
1708 /* enable resample method */
1709 this->stream->xine->config->update_num(this->stream->xine->config, "audio.synchronization.av_sync_method", 1);
1710
1711 this->event_queue = xine_event_new_queue (this->stream);
1712
1713 return 1;
1714 }
1715
1716 /**
1717 * Create a new instance.
1718 *
1719 * Creates a new instance of the plugin. Doesn't initialise the V4L device,
1720 * does initialise the structure.
1721 */
v4l_class_get_instance(input_class_t * cls_gen,xine_stream_t * stream,const char * data)1722 static input_plugin_t *v4l_class_get_instance (input_class_t *cls_gen,
1723 xine_stream_t *stream, const char *data)
1724 {
1725 /* v4l_input_class_t *cls = (v4l_input_class_t *) cls_gen; */
1726 v4l_input_plugin_t *this;
1727 #ifdef HAVE_ALSA
1728 cfg_entry_t *entry;
1729 #endif
1730
1731 /* Example mrl: v4l:/Television/62500 */
1732 if(!data || strncasecmp(data, "v4l:/", 5)) {
1733 return NULL;
1734 }
1735
1736 this = calloc(1, sizeof (v4l_input_plugin_t));
1737 if (!this)
1738 return NULL;
1739
1740 this->stream = stream;
1741 this->mrl = strdup(data);
1742 if (!this->mrl) {
1743 free(this);
1744 return NULL;
1745 }
1746 this->video_buf = NULL;
1747 this->video_fd = -1;
1748 this->radio_fd = -1;
1749 this->event_queue = NULL;
1750 this->scr = NULL;
1751 #ifdef HAVE_ALSA
1752 this->pcm_data = NULL;
1753 this->pcm_hwparams = NULL;
1754
1755 extract_mrl(this, this->mrl);
1756
1757 /* Audio */
1758 this->pcm_stream = SND_PCM_STREAM_CAPTURE;
1759 entry = this->stream->xine->config->lookup_entry(this->stream->xine->config,
1760 "media.video4linux.audio_device");
1761 this->pcm_name = strdup (entry->str_value);
1762 this->audio_capture = 1;
1763 if (!this->pcm_name) {
1764 v4l_plugin_dispose(&this->input_plugin);
1765 return NULL;
1766 }
1767 #endif
1768 this->rate = 44100;
1769 this->periods = 2;
1770 this->periodsize = 2 * 8192;
1771 this->bits = 16;
1772
1773 pthread_mutex_init (&this->aud_frames_lock, NULL);
1774 pthread_cond_init (&this->aud_frame_freed, NULL);
1775
1776 pthread_mutex_init (&this->vid_frames_lock, NULL);
1777 pthread_cond_init (&this->vid_frame_freed, NULL);
1778
1779 this->input_plugin.get_capabilities = v4l_plugin_get_capabilities;
1780 this->input_plugin.read = v4l_plugin_read;
1781 this->input_plugin.read_block = v4l_plugin_read_block;
1782 this->input_plugin.seek = v4l_plugin_seek;
1783 this->input_plugin.get_current_pos = v4l_plugin_get_current_pos;
1784 this->input_plugin.get_length = v4l_plugin_get_length;
1785 this->input_plugin.get_blocksize = v4l_plugin_get_blocksize;
1786 this->input_plugin.get_mrl = v4l_plugin_get_mrl;
1787 this->input_plugin.dispose = v4l_plugin_dispose;
1788 this->input_plugin.get_optional_data = v4l_plugin_get_optional_data;
1789 this->input_plugin.input_class = cls_gen;
1790
1791 return &this->input_plugin;
1792 }
1793
v4l_class_get_video_instance(input_class_t * cls_gen,xine_stream_t * stream,const char * data)1794 static input_plugin_t *v4l_class_get_video_instance (input_class_t *cls_gen,
1795 xine_stream_t *stream, const char *data)
1796 {
1797 v4l_input_plugin_t *this = NULL;
1798 int is_ok = 1;
1799 cfg_entry_t *entry;
1800
1801 this = (v4l_input_plugin_t *) v4l_class_get_instance (cls_gen, stream, data);
1802
1803 if (this)
1804 this->input_plugin.open = v4l_plugin_video_open;
1805 else
1806 return NULL;
1807
1808 entry = this->stream->xine->config->lookup_entry(this->stream->xine->config,
1809 "media.video4linux.video_device");
1810
1811 /* Try to open the video device */
1812 if((this->video_fd = xine_open_cloexec(entry->str_value, O_RDWR)) < 0) {
1813 xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
1814 "input_v4l: error opening v4l device (%s): %s\n",
1815 entry->str_value, strerror(errno));
1816 is_ok = 0;
1817 } else
1818 lprintf("Device opened, tv %d\n", this->video_fd);
1819
1820 /* Get capabilities */
1821 if (is_ok && ioctl(this->video_fd, VIDIOCGCAP, &this->video_cap) < 0) {
1822 xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
1823 "input_v4l: v4l card doesn't support some features needed by xine\n");
1824 is_ok = 0;;
1825 }
1826
1827 if (is_ok && !(this->video_cap.type & VID_TYPE_CAPTURE)) {
1828 /* Capture is not supported by the device. This is a must though! */
1829 xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
1830 "input_v4l: v4l card doesn't support frame grabbing\n");
1831 is_ok = 0;
1832 }
1833
1834 if (is_ok && set_input_source(this, this->tuner_name) <= 0) {
1835 xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
1836 "input_v4l: unable to locate the tuner name (%s) on your v4l card\n",
1837 this->tuner_name);
1838 is_ok = 0;
1839 }
1840
1841 if (this->video_fd > 0) {
1842 close(this->video_fd);
1843 this->video_fd = -1;
1844 }
1845
1846 if (!is_ok) {
1847 v4l_plugin_dispose((input_plugin_t *) this);
1848 return NULL;
1849 }
1850
1851 return &this->input_plugin;
1852 }
1853
1854
v4l_class_get_radio_instance(input_class_t * cls_gen,xine_stream_t * stream,const char * data)1855 static input_plugin_t *v4l_class_get_radio_instance (input_class_t *cls_gen,
1856 xine_stream_t *stream, const char *data)
1857 {
1858 v4l_input_plugin_t *this = NULL;
1859 int is_ok = 1;
1860 cfg_entry_t *entry;
1861
1862 if (strstr(data, "Radio") == NULL)
1863 return NULL;
1864
1865 this = (v4l_input_plugin_t *) v4l_class_get_instance (cls_gen, stream, data);
1866
1867 if (this)
1868 this->input_plugin.open = v4l_plugin_radio_open;
1869 else
1870 return NULL;
1871
1872 entry = this->stream->xine->config->lookup_entry(this->stream->xine->config,
1873 "media.video4linux.radio_device");
1874
1875 if((this->radio_fd = xine_open_cloexec(entry->str_value, O_RDWR)) < 0) {
1876 xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
1877 "input_v4l: error opening v4l device (%s): %s\n",
1878 entry->str_value, strerror(errno));
1879 is_ok = 0;
1880 } else
1881 lprintf("Device opened, radio %d\n", this->radio_fd);
1882
1883 if (is_ok && set_input_source(this, this->tuner_name) <= 0) {
1884 xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
1885 "input_v4l: unable to locate the tuner name (%s) on your v4l card\n",
1886 this->tuner_name);
1887 is_ok = 0;
1888 }
1889
1890 if (this->radio_fd > 0) {
1891 close(this->radio_fd);
1892 this->radio_fd = -1;
1893 }
1894
1895 if (!is_ok) {
1896 v4l_plugin_dispose((input_plugin_t *) this);
1897 return NULL;
1898 }
1899
1900 return &this->input_plugin;
1901 }
1902
1903
1904 /*
1905 * v4l input plugin class stuff
1906 */
init_video_class(xine_t * xine,const void * data)1907 static void *init_video_class (xine_t *xine, const void *data)
1908 {
1909 static const input_class_t v4l_video_input_class = {
1910 .get_instance = v4l_class_get_video_instance,
1911 .identifier = "v4l",
1912 .description = N_("v4l tv input plugin"),
1913 .get_dir = NULL,
1914 .get_autoplay_list = NULL,
1915 .dispose = NULL,
1916 .eject_media = NULL,
1917 };
1918
1919 config_values_t *config = xine->config;
1920
1921 config->register_filename (config, "media.video4linux.video_device",
1922 VIDEO_DEV, XINE_CONFIG_STRING_IS_DEVICE_NAME,
1923 _("v4l video device"),
1924 _("The path to your Video4Linux video device."),
1925 10, NULL, NULL);
1926 #ifdef HAVE_ALSA
1927 config->register_filename (config, "media.video4linux.audio_device",
1928 AUDIO_DEV, 0,
1929 _("v4l ALSA audio input device"),
1930 _("The name of the audio device which corresponds "
1931 "to your Video4Linux video device."),
1932 10, NULL, NULL);
1933 #endif
1934 config->register_enum (config, "media.video4linux.tv_standard", 0 /* auto */,
1935 (char **)tv_standard_names, _("v4l TV standard"),
1936 _("Selects the TV standard of the input signals. "
1937 "Either: AUTO, PAL, NTSC or SECAM. "), 20, NULL, NULL);
1938
1939 (void)data;
1940 return (void *)&v4l_video_input_class;
1941 }
1942
init_radio_class(xine_t * xine,const void * data)1943 static void *init_radio_class (xine_t *xine, const void *data)
1944 {
1945 static const input_class_t v4l_radio_input_class = {
1946 .get_instance = v4l_class_get_radio_instance,
1947 .identifier = "v4l",
1948 .description = N_("v4l radio input plugin"),
1949 .get_dir = NULL,
1950 .get_autoplay_list = NULL,
1951 .dispose = NULL,
1952 .eject_media = NULL,
1953 };
1954
1955 config_values_t *config = xine->config;
1956
1957 config->register_filename (config, "media.video4linux.radio_device",
1958 RADIO_DEV, XINE_CONFIG_STRING_IS_DEVICE_NAME,
1959 _("v4l radio device"),
1960 _("The path to your Video4Linux radio device."),
1961 10, NULL, NULL);
1962
1963 (void)data;
1964 return (void *)&v4l_radio_input_class;
1965 }
1966
1967 /*
1968 * exported plugin catalog entry
1969 */
1970
1971 const plugin_info_t xine_plugin_info[] EXPORTED = {
1972 /* type, API, "name", version, special_info, init_function */
1973 { PLUGIN_INPUT | PLUGIN_MUST_PRELOAD, 18, "v4l_radio", XINE_VERSION_CODE, NULL, init_radio_class },
1974 { PLUGIN_INPUT | PLUGIN_MUST_PRELOAD, 18, "v4l_tv", XINE_VERSION_CODE, NULL, init_video_class },
1975 { PLUGIN_NONE, 0, NULL, 0, NULL, NULL }
1976 };
1977
1978 /*
1979 * vim:sw=3:sts=3:
1980 */
1981