summaryrefslogtreecommitdiff
path: root/src/input
diff options
context:
space:
mode:
authorGuenter Bartsch <guenter@users.sourceforge.net>2003-01-19 19:41:03 +0000
committerGuenter Bartsch <guenter@users.sourceforge.net>2003-01-19 19:41:03 +0000
commitc0e4d42912cae537b086d64bcecc021f99b0d2c9 (patch)
tree2b4428aea8ee481798339d08f1f3fb86a253b2be /src/input
parent2351d7fc961c52854286795f3599f4b81e1599d2 (diff)
downloadxine-lib-c0e4d42912cae537b086d64bcecc021f99b0d2c9.tar.gz
xine-lib-c0e4d42912cae537b086d64bcecc021f99b0d2c9.tar.bz2
v4l analog tv input plugin
CVS patchset: 3969 CVS date: 2003/01/19 19:41:03
Diffstat (limited to 'src/input')
-rw-r--r--src/input/Makefile.am8
-rw-r--r--src/input/input_v4l.c494
2 files changed, 501 insertions, 1 deletions
diff --git a/src/input/Makefile.am b/src/input/Makefile.am
index f18a23790..0e5870ad1 100644
--- a/src/input/Makefile.am
+++ b/src/input/Makefile.am
@@ -57,7 +57,9 @@ lib_LTLIBRARIES = \
xineplug_inp_rtsp.la \
xineplug_inp_net.la \
xineplug_inp_dvb.la \
- xineplug_inp_cdda.la
+ xineplug_inp_cdda.la \
+ xineplug_inp_v4l.la
+
#lib_LTLIBRARIES = \
# $(in_cda) \
@@ -123,6 +125,10 @@ xineplug_inp_cdda_la_SOURCES = input_cdda.c
xineplug_inp_cdda_la_LIBADD = $(XINE_LIB)
xineplug_inp_cdda_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
+xineplug_inp_v4l_la_SOURCES = input_v4l.c
+xineplug_inp_v4l_la_LIBADD = $(XINE_LIB)
+xineplug_inp_v4l_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
+
xineplug_inp_gnome_vfs_la_SOURCES = input_gnome_vfs.c
xineplug_inp_gnome_vfs_la_LIBADD = $(GNOME_VFS_LIBS) $(XINE_LIB)
xineplug_inp_gnome_vfs_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
diff --git a/src/input/input_v4l.c b/src/input/input_v4l.c
new file mode 100644
index 000000000..9f6097867
--- /dev/null
+++ b/src/input/input_v4l.c
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2003 the xine project
+ *
+ * This file is part of xine, a free video player.
+ *
+ * xine is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * xine is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * v4l input plugin
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <linux/videodev.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+#include "xine_internal.h"
+#include "xineutils.h"
+#include "input_plugin.h"
+
+#define NUM_FRAMES 10
+#define GRAB_WIDTH 768
+#define GRAB_HEIGHT 576
+/*
+#define GRAB_WIDTH 384
+#define GRAB_HEIGHT 288
+*/
+#define LOG
+
+#if !defined(NDELAY) && defined(O_NDELAY)
+#define FNDELAY O_NDELAY
+#endif
+
+typedef struct {
+
+ input_class_t input_class;
+
+ xine_t *xine;
+} v4l_input_class_t;
+
+typedef struct {
+ input_plugin_t input_plugin;
+
+ char *mrl;
+
+ off_t curpos;
+
+ buf_element_t *frames;
+ pthread_mutex_t frames_lock;
+ pthread_cond_t frame_freed;
+
+ int video_fd;
+ struct video_capability video_cap;
+ struct video_audio audio;
+ struct video_audio audio_saved;
+ struct video_mbuf gb_buffers;
+ int frame_format;
+ int frame_size;
+ int use_mmap;
+ uint8_t *video_buf;
+ int gb_frame;
+ struct video_mmap gb_buf;
+ int64_t start_time;
+
+} v4l_input_plugin_t;
+
+static buf_element_t *alloc_frame (v4l_input_plugin_t *this) {
+
+ buf_element_t *frame;
+
+#ifdef LOG
+ printf ("input_v4l: alloc_frame. trying to get lock...\n");
+#endif
+
+ pthread_mutex_lock (&this->frames_lock) ;
+
+#ifdef LOG
+ printf ("input_v4l: got the lock\n");
+#endif
+
+ while (!this->frames) {
+ printf ("input_v4l: no frame available...\n");
+ pthread_cond_wait (&this->frame_freed, &this->frames_lock);
+ }
+
+ frame = this->frames;
+ this->frames = this->frames->next;
+
+ pthread_mutex_unlock (&this->frames_lock);
+
+#ifdef LOG
+ printf ("input_v4l: alloc_frame done\n");
+#endif
+
+ return frame;
+}
+
+static void store_frame (buf_element_t *frame) {
+
+ v4l_input_plugin_t *this = (v4l_input_plugin_t *) frame->source;
+
+#ifdef LOG
+ printf ("input_v4l: store_frame\n");
+#endif
+
+ pthread_mutex_lock (&this->frames_lock) ;
+
+ frame->next = this->frames;
+ this->frames = frame;
+
+ pthread_cond_signal (&this->frame_freed);
+
+ pthread_mutex_unlock (&this->frames_lock);
+}
+
+static off_t v4l_plugin_read (input_plugin_t *this_gen,
+ char *buf, off_t len) {
+ return 0;
+}
+
+int64_t get_time() {
+ struct timeval tv;
+ gettimeofday(&tv,NULL);
+
+ return (int64_t) tv.tv_sec * 90000 + (int64_t) tv.tv_usec * 9 / 100;
+}
+
+
+static buf_element_t *v4l_plugin_read_block (input_plugin_t *this_gen,
+ fifo_buffer_t *fifo, off_t todo) {
+ v4l_input_plugin_t *this = (v4l_input_plugin_t *) this_gen;
+ buf_element_t *buf;
+ uint8_t *ptr;
+
+
+#ifdef LOG
+ printf ("input_v4l: %lld bytes...\n",
+ todo);
+#endif
+
+ buf = alloc_frame (this);
+
+ this->gb_buf.frame = this->gb_frame;
+
+#ifdef LOG
+ printf ("input_v4l: VIDIOCMCAPTURE\n");
+#endif
+
+
+ while (ioctl(this->video_fd, VIDIOCMCAPTURE, &this->gb_buf) < 0) {
+ printf("input_v4l: upper while loop\n");
+ if (errno == EAGAIN)
+ printf ("input_v4l: cannot sync\n");
+ else {
+ perror("VIDIOCMCAPTURE");
+ return NULL;
+ }
+ }
+
+ this->gb_frame = (this->gb_frame + 1) % this->gb_buffers.frames;
+
+ while (ioctl(this->video_fd, VIDIOCSYNC, &this->gb_frame) < 0 &&
+ (errno == EAGAIN || errno == EINTR))
+ {
+ printf("input_v4l: waiting for videosync\n");
+ }
+
+ if (this->start_time == 0)
+ this->start_time = get_time();
+ buf->pts = (get_time() - this->start_time)+50*3600;
+
+ /* printf ("grabbing frame #%d\n", frame_num); */
+
+ ptr = this->video_buf + this->gb_buffers.offsets[this->gb_frame];
+
+ xine_fast_memcpy (buf->content, ptr, this->frame_size);
+
+ printf("input_v4l: read block done\n");
+ return buf;
+}
+
+static off_t v4l_plugin_seek (input_plugin_t *this_gen, off_t offset, int origin) {
+ v4l_input_plugin_t *this = (v4l_input_plugin_t *) this_gen;
+
+#ifdef LOG
+ printf ("input_v4l: seek %lld bytes, origin %d\n",
+ offset, origin);
+#endif
+
+ return this->curpos;
+}
+
+static off_t v4l_plugin_get_length (input_plugin_t *this_gen) {
+
+ /*
+ v4l_input_plugin_t *this = (v4l_input_plugin_t *) this_gen;
+ off_t length;
+ */
+
+ return -1;
+}
+
+static uint32_t v4l_plugin_get_capabilities (input_plugin_t *this_gen) {
+ return 0;
+}
+
+static uint32_t v4l_plugin_get_blocksize (input_plugin_t *this_gen) {
+ return 0;
+}
+
+static off_t v4l_plugin_get_current_pos (input_plugin_t *this_gen){
+ v4l_input_plugin_t *this = (v4l_input_plugin_t *) this_gen;
+
+ /*
+ printf ("current pos is %lld\n", this->curpos);
+ */
+
+ return this->curpos;
+}
+
+static void v4l_plugin_dispose (input_plugin_t *this_gen) {
+ v4l_input_plugin_t *this = (v4l_input_plugin_t *) this_gen;
+
+ if(this->mrl)
+ free(this->mrl);
+
+ close(this->video_fd);
+
+ free (this);
+}
+
+static char* v4l_plugin_get_mrl (input_plugin_t *this_gen) {
+ v4l_input_plugin_t *this = (v4l_input_plugin_t *) this_gen;
+
+ return this->mrl;
+}
+
+static int v4l_plugin_get_optional_data (input_plugin_t *this_gen,
+ void *data, int data_type) {
+ /* v4l_input_plugin_t *this = (v4l_input_plugin_t *) this_gen; */
+
+ return INPUT_OPTIONAL_UNSUPPORTED;
+}
+
+
+static input_plugin_t *open_plugin (input_class_t *cls_gen, xine_stream_t *stream,
+ const char *data) {
+
+ /* v4l_input_class_t *cls = (v4l_input_class_t *) cls_gen; */
+ v4l_input_plugin_t *this;
+ int i, ret;
+ char *mrl = strdup(data);
+
+#ifdef LOG
+ printf ("input_v4l: trying to open '%s'\n", mrl);
+#endif
+
+ if (strncasecmp (mrl, "v4l://", 6)) {
+ free (mrl);
+ return NULL;
+ }
+
+ this = (v4l_input_plugin_t *) xine_xmalloc (sizeof (v4l_input_plugin_t));
+
+ this->mrl = mrl;
+
+ /*
+ * pre-alloc a bunch of frames
+ */
+
+ pthread_mutex_init (&this->frames_lock, NULL);
+ pthread_cond_init (&this->frame_freed, NULL);
+
+ for (i=0; i<NUM_FRAMES; i++) {
+
+ buf_element_t *frame;
+
+ frame = xine_xmalloc (sizeof (buf_element_t));
+
+ frame->decoder_info[0] = GRAB_WIDTH;
+ frame->decoder_info[1] = GRAB_HEIGHT;
+ frame->content = xine_xmalloc (frame->decoder_info[0] * frame->decoder_info[1] * 3 / 2);
+ frame->type = BUF_VIDEO_YUV_FRAMES;
+
+ frame->source = this;
+ frame->free_buffer = store_frame;
+ frame->extra_info = xine_xmalloc(sizeof(extra_info_t));
+
+ store_frame (frame);
+ }
+
+ this->video_fd = open("/dev/video0", O_RDWR);
+ if (this->video_fd < 0) {
+ printf ("input_v4l: cannot open v4l device\n");
+ free(this);
+ return NULL;
+ }
+
+ if (ioctl(this->video_fd,VIDIOCGCAP,&this->video_cap) < 0) {
+ printf ("input_v4l: VIDIOCGCAP ioctl went wrong\n");
+ free(this);
+ return NULL;
+ }
+
+ if (!(this->video_cap.type & VID_TYPE_CAPTURE)) {
+ printf ("input_v4l: grab device does not handle capture\n");
+ free(this);
+ return NULL;
+ }
+ /* unmute audio */
+ ioctl(this->video_fd, VIDIOCGAUDIO, &this->audio);
+ memcpy(&this->audio_saved, &this->audio, sizeof(this->audio));
+ this->audio.flags &= ~VIDEO_AUDIO_MUTE;
+ ioctl(this->video_fd, VIDIOCSAUDIO, &this->audio);
+
+ ret = ioctl(this->video_fd,VIDIOCGMBUF, &this->gb_buffers);
+ if (ret < 0) {
+ /* try to use read based access */
+ struct video_picture pict;
+ int val;
+
+ ioctl(this->video_fd, VIDIOCGPICT, &pict);
+#if 0
+ printf("v4l: colour=%d hue=%d brightness=%d constrast=%d whiteness=%d\n",
+ pict.colour,
+ pict.hue,
+ pict.brightness,
+ pict.contrast,
+ pict.whiteness);
+#endif
+ /* try to choose a suitable video format */
+ pict.palette=VIDEO_PALETTE_YUV420P;
+ ret = ioctl(this->video_fd, VIDIOCSPICT, &pict);
+ if (ret < 0) {
+ pict.palette=VIDEO_PALETTE_YUV422;
+ ret = ioctl(this->video_fd, VIDIOCSPICT, &pict);
+ if (ret < 0) {
+ close (this->video_fd);
+ this->video_fd = -1;
+ printf ("input_v4l: grab: no colorspace format found\n");
+ return 0;
+ } else
+ printf ("input_v4l: grab: format YUV 4:2:2\n");
+ } else
+ printf ("input_v4l: grab: format YUV 4:2:0\n");
+
+ this->frame_format = pict.palette;
+
+ val = 1;
+ ioctl(this->video_fd, VIDIOCCAPTURE, &val);
+
+ this->use_mmap = 0;
+
+ } else {
+
+ printf ("input_v4l: using mmap, size %d\n", this->gb_buffers.size);
+
+ this->video_buf = mmap(0, this->gb_buffers.size,
+ PROT_READ|PROT_WRITE, MAP_SHARED,
+ this->video_fd,0);
+ if ((unsigned char*)-1 == this->video_buf) {
+ perror("mmap");
+ close (this->video_fd);
+ free(this);
+ return NULL;
+ }
+ this->gb_frame = 0;
+
+ /* start to grab the first frame */
+ this->gb_buf.frame = (this->gb_frame + 1) % this->gb_buffers.frames;
+ this->gb_buf.height = GRAB_HEIGHT;
+ this->gb_buf.width = GRAB_WIDTH;
+ this->gb_buf.format = VIDEO_PALETTE_YUV420P;
+
+ ret = ioctl(this->video_fd, VIDIOCMCAPTURE, &this->gb_buf);
+ if (ret < 0 && errno != EAGAIN) {
+ /* try YUV422 */
+ this->gb_buf.format = VIDEO_PALETTE_YUV422;
+
+ ret = ioctl(this->video_fd, VIDIOCMCAPTURE, &this->gb_buf);
+ } else
+ printf ("input_v4l: YUV420 should work\n");
+
+ if (ret < 0) {
+ if (errno != EAGAIN) {
+ printf("input_v4l: grab device does not support suitable format\n");
+ } else {
+ printf("input_v4l: grab device does not receive any video signal\n");
+ }
+ close (this->video_fd);
+ free (this);
+ return NULL;
+ }
+ this->frame_format = this->gb_buf.format;
+ this->use_mmap = 1;
+ }
+
+ switch(this->frame_format) {
+ case VIDEO_PALETTE_YUV420P:
+ this->frame_size = ( GRAB_WIDTH * GRAB_HEIGHT * 3) / 2;
+ break;
+ case VIDEO_PALETTE_YUV422:
+ this->frame_size = GRAB_WIDTH * GRAB_HEIGHT * 2;
+ break;
+ }
+
+
+ this->start_time=0;
+
+ this->input_plugin.get_capabilities = v4l_plugin_get_capabilities;
+ this->input_plugin.read = v4l_plugin_read;
+ this->input_plugin.read_block = v4l_plugin_read_block;
+ this->input_plugin.seek = v4l_plugin_seek;
+ this->input_plugin.get_current_pos = v4l_plugin_get_current_pos;
+ this->input_plugin.get_length = v4l_plugin_get_length;
+ this->input_plugin.get_blocksize = v4l_plugin_get_blocksize;
+ this->input_plugin.get_mrl = v4l_plugin_get_mrl;
+ this->input_plugin.dispose = v4l_plugin_dispose;
+ this->input_plugin.get_optional_data = v4l_plugin_get_optional_data;
+ this->input_plugin.input_class = cls_gen;
+
+ return &this->input_plugin;
+}
+
+/*
+ * v4l input plugin class stuff
+ */
+
+static char *v4l_class_get_description (input_class_t *this_gen) {
+ return _("v4l input plugin");
+}
+
+static char *v4l_class_get_identifier (input_class_t *this_gen) {
+ return "v4l";
+}
+
+static void v4l_class_dispose (input_class_t *this_gen) {
+ v4l_input_class_t *this = (v4l_input_class_t *) this_gen;
+
+ free (this);
+}
+
+static void *init_class (xine_t *xine, void *data) {
+
+ v4l_input_class_t *this;
+
+ this = (v4l_input_class_t *) xine_xmalloc (sizeof (v4l_input_class_t));
+
+ this->xine = xine;
+
+ this->input_class.open_plugin = open_plugin;
+ this->input_class.get_identifier = v4l_class_get_identifier;
+ this->input_class.get_description = v4l_class_get_description;
+ this->input_class.get_dir = NULL;
+ this->input_class.get_autoplay_list = NULL;
+ this->input_class.dispose = v4l_class_dispose;
+ this->input_class.eject_media = NULL;
+
+ return this;
+}
+
+/*
+ * exported plugin catalog entry
+ */
+
+plugin_info_t xine_plugin_info[] = {
+ /* type, API, "name", version, special_info, init_function */
+ { PLUGIN_INPUT, 11, "v4l", XINE_VERSION_CODE, NULL, init_class },
+ { PLUGIN_NONE, 0, "", 0, NULL, NULL }
+};
+
+