summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/demuxers/demux_real.c4
-rw-r--r--src/demuxers/demux_yuv_frames.c107
-rw-r--r--src/input/Makefile.am6
-rw-r--r--src/input/input_v4l.c2060
-rw-r--r--src/libxineadec/Makefile.am5
-rw-r--r--src/libxineadec/pcm.c221
-rw-r--r--src/xine-engine/buffer.h3
7 files changed, 2047 insertions, 359 deletions
diff --git a/src/demuxers/demux_real.c b/src/demuxers/demux_real.c
index 58de9e532..b8bdc65a7 100644
--- a/src/demuxers/demux_real.c
+++ b/src/demuxers/demux_real.c
@@ -28,7 +28,7 @@
*
* Based on FFmpeg's libav/rm.c.
*
- * $Id: demux_real.c,v 1.56 2003/05/29 00:13:11 jstembridge Exp $
+ * $Id: demux_real.c,v 1.57 2003/06/16 16:42:51 holstsn Exp $
*/
#ifdef HAVE_CONFIG_H
@@ -1416,7 +1416,7 @@ static char *get_mimetypes (demux_class_t *this_gen) {
return "audio/x-pn-realaudio: ra, rm, ram: Real Media file;"
"audio/x-pn-realaudio-plugin: rpm: Real Media plugin file;"
"audio/x-real-audio: ra, rm, ram: Real Media file;"
- "application/vnd.rn-realmedia ra, rm, ram: Real Media file;";
+ "application/vnd.rn-realmedia: ra, rm, ram: Real Media file;";
}
static void class_dispose (demux_class_t *this_gen) {
diff --git a/src/demuxers/demux_yuv_frames.c b/src/demuxers/demux_yuv_frames.c
index 2bfc3085f..f10dbd4cb 100644
--- a/src/demuxers/demux_yuv_frames.c
+++ b/src/demuxers/demux_yuv_frames.c
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2003 the xine project
+ * Copyright (C) 2003 Jeroen Asselman <j.asselman@itsec-ps.nl>
*
* This file is part of xine, a free video player.
*
@@ -17,7 +18,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*
- * $Id: demux_yuv_frames.c,v 1.5 2003/04/26 20:16:29 guenter Exp $
+ * $Id: demux_yuv_frames.c,v 1.6 2003/06/16 16:42:51 holstsn Exp $
*
* dummy demultiplexer for raw yuv frames (delivered by v4l)
*
@@ -37,6 +38,9 @@
#include "xineutils.h"
#include "demux.h"
+#define WRAP_THRESHOLD 20000
+
+#define PLUGIN "demux_yuv_frames"
/*
#define LOG
*/
@@ -52,7 +56,9 @@ typedef struct demux_yuv_frames_s {
input_plugin_t *input;
int status;
-
+ int seek_flag;
+ int64_t last_pts;
+
} demux_yuv_frames_t ;
typedef struct {
@@ -72,51 +78,77 @@ static int demux_yuv_frames_get_status (demux_plugin_t *this_gen) {
return this->status;
}
-static int demux_yuv_frames_send_chunk (demux_plugin_t *this_gen) {
-
- demux_yuv_frames_t *this = (demux_yuv_frames_t *) this_gen;
- buf_element_t *buf;
-
- buf = this->input->read_block (this->input, NULL, 0);
-
- if (!buf)
- this->status = DEMUX_FINISHED;
- else {
-
- switch (buf->type) {
- case BUF_VIDEO_YUV_FRAMES:
- this->video_fifo->put (this->video_fifo, buf);
- break;
- default:
+static int switch_buf(demux_yuv_frames_t *this , buf_element_t *buf)
+{
+ int result = 0;
+
+ if (!buf)
+ return 0;
+
+ if (this->seek_flag) {
+ this->seek_flag = 0;
+ xine_demux_control_newpts(this->stream, buf->pts, BUF_FLAG_SEEK);
+ } else
+ if (abs(this->last_pts - buf->pts) > WRAP_THRESHOLD) {
+ xine_demux_control_newpts(this->stream, buf->pts, 0);
+ }
+
+ this->last_pts = buf->pts;
+
+ switch (buf->type) {
+ case BUF_VIDEO_YUV_FRAMES:
+ this->video_fifo->put(this->video_fifo, buf);
+ result = 1; /* 1, we still should read audio */
+ break;
+ case BUF_AUDIO_RAWPCM:
+ if (!this->stream->stream_info[XINE_STREAM_INFO_HAS_VIDEO])
+ xine_demux_control_newpts(this->stream, buf->pts, 0);
+
+ this->audio_fifo->put(this->audio_fifo, buf);
+ break;
+ default:
#ifdef LOG
- printf ("demux_yuv_frames: help, unknown buffer type %08x\n",
- buf->type);
+ printf ("demux_yuv_frames: help, unknown buffer type %08x\n",
+ buf->type);
#endif
- buf->free_buffer (buf);
- }
- }
+ buf->free_buffer(buf);
+ }
- return this->status;
+ return result;
}
-static void demux_yuv_frames_send_headers (demux_plugin_t *this_gen) {
-
- demux_yuv_frames_t *this = (demux_yuv_frames_t *) this_gen;
-
- this->video_fifo = this->stream->video_fifo;
- this->audio_fifo = this->stream->audio_fifo;
-
- this->status = DEMUX_OK;
+static int demux_yuv_frames_send_chunk (demux_plugin_t *this_gen)
+{
+ demux_yuv_frames_t *this = (demux_yuv_frames_t *) this_gen;
+ buf_element_t *buf;
+ int first = 1;
+
+ do {
+ if ( this->stream->stream_info[XINE_STREAM_INFO_HAS_VIDEO])
+ buf = this->input->read_block (this->input, this->video_fifo, 0);
+ else
+ buf = this->input->read_block (this->input, this->audio_fifo, 0);
+
+ } while (switch_buf(this, buf));
+
+ return this->status;
+}
- this->stream->stream_info[XINE_STREAM_INFO_HAS_VIDEO] = 1;
- this->stream->stream_info[XINE_STREAM_INFO_HAS_AUDIO] = 1;
+static void demux_yuv_frames_send_headers (demux_plugin_t *this_gen)
+{
+ demux_yuv_frames_t *this = (demux_yuv_frames_t *) this_gen;
+ this->video_fifo = this->stream->video_fifo;
+ this->audio_fifo = this->stream->audio_fifo;
+
+ this->status = DEMUX_OK;
}
static int demux_yuv_frames_seek (demux_plugin_t *this_gen,
off_t start_pos, int start_time) {
demux_yuv_frames_t *this = (demux_yuv_frames_t *) this_gen;
-
+ this->seek_flag = 1;
+ this->last_pts = 0;
return this->status;
}
@@ -257,3 +289,8 @@ plugin_info_t xine_plugin_info[] = {
{ PLUGIN_DEMUX, 21, "yuv_frames", XINE_VERSION_CODE, NULL, init_class },
{ PLUGIN_NONE, 0, "", 0, NULL, NULL }
};
+
+/*
+ * vim:sw=3:sts=3:
+ */
+
diff --git a/src/input/Makefile.am b/src/input/Makefile.am
index d04395100..8754d7726 100644
--- a/src/input/Makefile.am
+++ b/src/input/Makefile.am
@@ -31,6 +31,10 @@ if HAVE_GNOME_VFS
in_gnome_vfs = xineplug_inp_gnome_vfs.la
endif
+if HAVE_ALSA
+v4l_ldflags = -lasound
+endif
+
# For DVD
if HAVE_DVDNAV
DVD_CFLAGS = -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE $(DVDNAV_CFLAGS)
@@ -124,7 +128,7 @@ 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_v4l_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@ $(v4l_ldflags)
xineplug_inp_gnome_vfs_la_SOURCES = input_gnome_vfs.c
xineplug_inp_gnome_vfs_la_LIBADD = $(GNOME_VFS_LIBS) $(XINE_LIB)
diff --git a/src/input/input_v4l.c b/src/input/input_v4l.c
index f45a1a7da..97cf62093 100644
--- a/src/input/input_v4l.c
+++ b/src/input/input_v4l.c
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2003 the xine project
+ * Copyright (C) 2003 J.Asselman <j.asselman@itsec-ps.nl>
*
* This file is part of xine, a free video player.
*
@@ -24,6 +25,8 @@
#include "config.h"
#endif
+#define _GNU_SOURCE
+
#include <unistd.h>
#include <stdio.h>
#include <string.h>
@@ -35,11 +38,22 @@
#include <sys/mman.h>
#include <errno.h>
+
+/* Used to capture the audio data */
+#ifdef HAVE_ALSA
+#include <alsa/asoundlib.h>
+#endif
+
+#define XINE_ENABLE_EXPERIMENTAL_FEATURES
+
#include "xine_internal.h"
#include "xineutils.h"
#include "input_plugin.h"
-#define NUM_FRAMES 40
+#define NUM_FRAMES 15
+
+/* Our CPU can't handle de-interlacing at 768. */
+#define MAX_RES 640
static struct {
int width;
@@ -53,15 +67,27 @@ static struct {
};
#define NUM_RESOLUTIONS (sizeof(resolutions)/sizeof(resolutions[0]))
-
+#define RADIO_DEV "/dev/v4l/radio0"
+#define VIDEO_DEV "/dev/v4l/video0"
/*
#define LOG
*/
+#define PLUGIN "input_v4l"
+
+#ifdef LOG
+#define DBGPRINT(args...) printf(PLUGIN ": " args); fflush(stdout)
+#else
+#define DBGPRINT(args...) {}
+#endif
+
+#define PRINT(args...) printf(PLUGIN ": " args)
#if !defined(NDELAY) && defined(O_NDELAY)
#define FNDELAY O_NDELAY
#endif
+typedef struct pvrscr_s pvrscr_t;
+
typedef struct {
input_class_t input_class;
@@ -77,11 +103,62 @@ typedef struct {
off_t curpos;
- buf_element_t *frames;
- pthread_mutex_t frames_lock;
- pthread_cond_t frame_freed;
+ int old_interlace;
+ int old_zoomx;
+ int old_zoomy;
+ int audio_only;
+
+ /* Audio */
+ buf_element_t *aud_frames;
+ pthread_mutex_t aud_frames_lock;
+ pthread_cond_t aud_frame_freed;
+
+#ifdef HAVE_ALSA
+ /* Handle for the PCM device */
+ snd_pcm_t *pcm_handle;
+
+ /* Record stream (via line 1) */
+ snd_pcm_stream_t pcm_stream;
+
+ /* Information and configuration for the PCM stream */
+ snd_pcm_hw_params_t *pcm_hwparams;
+
+ /* Name of the PCM device, plughw:0,0?=>soundcard,device*/
+ char *pcm_name;
+
+ /* Use alsa to capture the sound (for a/v sync) */
+ char audio_capture;
+
+ int exact_rate; /* Actual sample rate
+ sndpcm_hw_params_set_rate_near */
+ int dir; /* exact rate == rate --> dir = 0
+ exact rate < rate --> dir = -1
+ exact rate > rate --> dir = 1 */
+
+ unsigned char *pcm_data;
+
+ int64_t pts_aud_start;
+#endif
+
+/* Video */
+ buf_element_t *vid_frames;
+ pthread_mutex_t vid_frames_lock;
+ pthread_cond_t vid_frame_freed;
int video_fd;
+ int radio_fd;
+
+ int input;
+ int tuner;
+ unsigned long frequency;
+ unsigned long calc_frequency;
+ char *tuner_name;
+
+ int radio; /* ask for a radio channel */
+ int channel; /* channel number */
+
+ struct video_channel video_channel;
+ struct video_tuner video_tuner;
struct video_capability video_cap;
struct video_audio audio;
struct video_audio audio_saved;
@@ -94,146 +171,1264 @@ typedef struct {
struct video_mmap gb_buf;
int64_t start_time;
+ xine_event_queue_t *event_queue;
+
+ pvrscr_t *scr;
+ int scr_tunning;
+
} v4l_input_plugin_t;
-static buf_element_t *alloc_frame (v4l_input_plugin_t *this) {
+/*
+ * ***************************************************
+ * unix System Clock Reference + fine tunning
+ *
+ * This code is copied and paste from the input_pvr.c
+ *
+ * the fine tunning option is used to change play
+ * speed in order to regulate fifo usage, that is,
+ * trying to match the rate of generated data.
+ *
+ * OBS: use with audio.av_sync_method=resample
+ * ***************************************************
+ */
- buf_element_t *frame;
-
-#ifdef LOG
- printf ("input_v4l: alloc_frame. trying to get lock...\n");
-#endif
-
- pthread_mutex_lock (&this->frames_lock) ;
+#define SCR_PAUSED -2
+#define SCR_FW -3
+#define SCR_SKIP -4
-#ifdef LOG
- printf ("input_v4l: got the lock\n");
+struct pvrscr_s {
+ scr_plugin_t scr;
+
+ struct timeval cur_time;
+ int64_t cur_pts;
+ int xine_speed;
+ double speed_factor;
+ double speed_tunning;
+
+ pthread_mutex_t lock;
+};
+
+static int pvrscr_get_priority(scr_plugin_t *scr)
+{
+ return 10; /* high priority */
+}
+
+/* Only call this when already mutex locked */
+static void pvrscr_set_pivot(pvrscr_t *this)
+{
+ struct timeval tv;
+ int64_t pts;
+ double pts_calc;
+
+ gettimeofday(&tv, NULL);
+ pts_calc = (tv.tv_sec - this->cur_time.tv_sec) * this->speed_factor;
+ pts_calc += (tv.tv_usec - this->cur_time.tv_usec) * this->speed_factor / 1e6;
+ pts = this->cur_pts + pts_calc;
+
+ /* This next part introduces a one off inaccuracy
+ * to the scr due to rounding tv to pts.
+ */
+ this->cur_time.tv_sec=tv.tv_sec;
+ this->cur_time.tv_usec=tv.tv_usec;
+ this->cur_pts=pts;
+
+ return;
+}
+
+static int pvrscr_set_speed (scr_plugin_t *scr, int speed)
+{
+ pvrscr_t *this = (pvrscr_t*) scr;
+
+ pthread_mutex_lock (&this->lock);
+
+ pvrscr_set_pivot( this );
+ this->xine_speed = speed;
+ this->speed_factor = (double) speed * 90000.0 / 4.0 *
+ this->speed_tunning;
+
+ pthread_mutex_unlock (&this->lock);
+
+ return speed;
+}
+
+static void pvrscr_speed_tunning (pvrscr_t *this, double factor)
+{
+ pthread_mutex_lock (&this->lock);
+
+ pvrscr_set_pivot( this );
+ this->speed_tunning = factor;
+ this->speed_factor = (double) this->xine_speed * 90000.0 / 4.0 *
+ this->speed_tunning;
+
+ pthread_mutex_unlock (&this->lock);
+}
+
+static void pvrscr_adjust (scr_plugin_t *scr, int64_t vpts)
+{
+ pvrscr_t *this = (pvrscr_t*) scr;
+ struct timeval tv;
+
+ pthread_mutex_lock (&this->lock);
+
+ gettimeofday(&tv, NULL);
+ this->cur_time.tv_sec=tv.tv_sec;
+ this->cur_time.tv_usec=tv.tv_usec;
+ this->cur_pts = vpts;
+
+ pthread_mutex_unlock (&this->lock);
+}
+
+static void pvrscr_start (scr_plugin_t *scr, int64_t start_vpts)
+{
+ pvrscr_t *this = (pvrscr_t*) scr;
+
+ pthread_mutex_lock (&this->lock);
+
+ gettimeofday(&this->cur_time, NULL);
+ this->cur_pts = start_vpts;
+
+ pthread_mutex_unlock (&this->lock);
+
+ pvrscr_set_speed (&this->scr, XINE_SPEED_NORMAL);
+}
+
+static int64_t pvrscr_get_current (scr_plugin_t *scr)
+{
+ pvrscr_t *this = (pvrscr_t*) scr;
+
+ struct timeval tv;
+ int64_t pts;
+ double pts_calc;
+ pthread_mutex_lock (&this->lock);
+
+ gettimeofday(&tv, NULL);
+
+ pts_calc = (tv.tv_sec - this->cur_time.tv_sec) * this->speed_factor;
+ pts_calc += (tv.tv_usec - this->cur_time.tv_usec) * this->speed_factor / 1e6;
+
+ pts = this->cur_pts + pts_calc;
+
+ pthread_mutex_unlock (&this->lock);
+
+ return pts;
+}
+
+static void pvrscr_exit (scr_plugin_t *scr)
+{
+ pvrscr_t *this = (pvrscr_t*) scr;
+
+ pthread_mutex_destroy (&this->lock);
+ free(this);
+}
+
+static pvrscr_t* pvrscr_init (void)
+{
+ pvrscr_t *this;
+
+ this = malloc(sizeof(*this));
+ memset(this, 0, sizeof(*this));
+
+ this->scr.interface_version = 2;
+ this->scr.get_priority = pvrscr_get_priority;
+ this->scr.set_speed = pvrscr_set_speed;
+ this->scr.adjust = pvrscr_adjust;
+ this->scr.start = pvrscr_start;
+ this->scr.get_current = pvrscr_get_current;
+ this->scr.exit = pvrscr_exit;
+
+ pthread_mutex_init (&this->lock, NULL);
+
+ pvrscr_speed_tunning(this, 1.0 );
+ pvrscr_set_speed (&this->scr, XINE_SPEED_PAUSE);
+#ifdef SCRLOG
+ printf("input_v4l: scr init complete\n");
#endif
+
+ return this;
+}
+
+/*** END COPY AND PASTE from PVR**************************/
+
+/*** The following is copy and past from net_buf_ctrl ****/
+static void report_progress (xine_stream_t *stream, int p)
+{
+ xine_event_t event;
+ xine_progress_data_t prg;
+
+ if (p == SCR_PAUSED) {
+ prg.description = _("Buffer underrun...");
+ p = 0;
+ } else
+ if (p == SCR_FW) {
+ prg.description = _("Buffer overrun...");
+ p = 100;
+ } else
+ prg.description = _("Adjusting...");
+
+ prg.percent = (p>100)?100:p;
+
+ event.type = XINE_EVENT_PROGRESS;
+ event.data = &prg;
+ event.data_length = sizeof (xine_progress_data_t);
+
+ xine_event_send (stream, &event);
+}
+
+/**** END COPY AND PASTE from net_buf_ctrl ***************/
+
+int rate = 44100; /* Sample rate */
+int dir; /* exact rate == rate --> dir = 0
+ exact rate < rate --> dir = -1
+ exact rate > rate --> dir = 1 */
+int periods = 2; /* Number of periods */
+int periodsize = 2 * 8192; /* Periodsize in bytes */
+int bits = 16;
+
+static int search_by_tuner(v4l_input_plugin_t *this, char *input_source);
+static int search_by_channel(v4l_input_plugin_t *this, char *input_source);
+
+static void v4l_event_handler(v4l_input_plugin_t *this);
+
+/**
+ * Allocate an audio frame.
+ */
+inline static buf_element_t *alloc_aud_frame (v4l_input_plugin_t *this)
+{
+ buf_element_t *frame;
+
+ DBGPRINT("alloc_aud_frame. trying to get lock...\n");
+
+ pthread_mutex_lock (&this->aud_frames_lock) ;
+
+ DBGPRINT("got the lock\n");
+
+ while (!this->aud_frames) {
+ DBGPRINT ("no audio frame available...\n");
+ pthread_cond_wait (&this->aud_frame_freed, &this->aud_frames_lock);
+ }
+
+ frame = this->aud_frames;
+ this->aud_frames = this->aud_frames->next;
+
+ pthread_mutex_unlock (&this->aud_frames_lock);
+
+ DBGPRINT("alloc_vid_frame done\n");
+
+ return frame;
+}
+
+/**
+ * Stores an audio frame.
+ */
+static void store_aud_frame (buf_element_t *frame)
+{
+ v4l_input_plugin_t *this = (v4l_input_plugin_t *) frame->source;
+
+ DBGPRINT("store_aud_frame\n");
+
+ pthread_mutex_lock (&this->aud_frames_lock) ;
+
+ frame->next = this->aud_frames;
+ this->aud_frames = frame;
+
+ pthread_cond_signal (&this->aud_frame_freed);
+ pthread_mutex_unlock (&this->aud_frames_lock);
+}
+
+/**
+ * Allocate a video frame.
+ */
+inline static buf_element_t *alloc_vid_frame (v4l_input_plugin_t *this)
+{
+
+ buf_element_t *frame;
+
+ DBGPRINT("alloc_vid_frame. trying to get lock...\n");
+
+ pthread_mutex_lock (&this->vid_frames_lock) ;
+
+ DBGPRINT("got the lock\n");
- while (!this->frames) {
-#ifdef LOG
- printf ("input_v4l: no frame available...\n");
-#endif
- pthread_cond_wait (&this->frame_freed, &this->frames_lock);
+ while (!this->vid_frames) {
+ DBGPRINT ("no video frame available...\n");
+ pthread_cond_wait (&this->vid_frame_freed, &this->vid_frames_lock);
}
- frame = this->frames;
- this->frames = this->frames->next;
+ frame = this->vid_frames;
+ this->vid_frames = this->vid_frames->next;
- pthread_mutex_unlock (&this->frames_lock);
-
-#ifdef LOG
- printf ("input_v4l: alloc_frame done\n");
-#endif
+ pthread_mutex_unlock (&this->vid_frames_lock);
+
+ DBGPRINT("alloc_vid_frame done\n");
return frame;
}
-static void store_frame (buf_element_t *frame) {
+/**
+ * Stores a video frame.
+ */
+static void store_vid_frame (buf_element_t *frame)
+{
v4l_input_plugin_t *this = (v4l_input_plugin_t *) frame->source;
+
+ DBGPRINT("input_v4l: store_vid_frame\n");
-#ifdef LOG
- printf ("input_v4l: store_frame\n");
-#endif
-
- pthread_mutex_lock (&this->frames_lock) ;
+ pthread_mutex_lock (&this->vid_frames_lock) ;
- frame->next = this->frames;
- this->frames = frame;
+ frame->next = this->vid_frames;
+ this->vid_frames = frame;
- pthread_cond_signal (&this->frame_freed);
+ pthread_cond_signal (&this->vid_frame_freed);
- pthread_mutex_unlock (&this->frames_lock);
+ pthread_mutex_unlock (&this->vid_frames_lock);
}
-static off_t v4l_plugin_read (input_plugin_t *this_gen,
- char *buf, off_t len) {
- return 0;
-}
+static int extract_mrl(v4l_input_plugin_t *this, char *mrl)
+{
+ char *tuner_name = NULL;
+ int frequency = 0;
+ char *locator = NULL;
+ char *begin = NULL;
-int64_t get_time() {
- struct timeval tv;
- gettimeofday(&tv,NULL);
+ if (mrl == NULL) {
+ DBGPRINT("Someone passed an empty mrl\n");
+ return 0;
+ }
- return (int64_t) tv.tv_sec * 90000 + (int64_t) tv.tv_usec * 9 / 100;
-}
+ for (locator = mrl; *locator != '\0' && *locator != '/' ; locator++);
+
+ /* Get tuner name */
+ if (*locator == '/') {
+ begin = ++locator;
+
+ for (; *locator != '\0' && *locator != '/' ; locator++);
+
+ tuner_name = (char *) strndup(begin, locator - begin);
+
+ /* Get frequency, if available */
+ sscanf(locator, "/%d", &frequency);
+ DBGPRINT("v4l: Tuner name: '%s' freq: %d\r\n", tuner_name, frequency);
+ } else {
+ PRINT("v4l: No tuner name given. Expected syntac: v4l:/tuner/frequency\r\n");
+ PRINT("v4l: Using currently tuned settings\r\n");
+ }
+
+ this->frequency = frequency;
+ this->tuner_name = tuner_name;
+ return 1;
+}
-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;
+static int set_frequency(v4l_input_plugin_t *this, unsigned long frequency)
+{
+ int ret = 0;
+ int fd;
+ if (this->video_fd > 0)
+ fd = this->video_fd;
+ else
+ fd = this->radio_fd;
+
+ if (frequency != 0) {
+ if (this->video_tuner.flags & VIDEO_TUNER_LOW) {
+ this->calc_frequency = frequency * 16;
+ } else {
+ this->calc_frequency = (frequency * 16) / 1000;
+ }
+
+ ret = ioctl(fd, VIDIOCSFREQ, &this->calc_frequency);
#ifdef LOG
- printf ("input_v4l: %lld bytes...\n",
- todo);
+ DBGPRINT("IOCTL set frequency (%ld) returned: %d\r\n", frequency, ret);
+ } else {
+ DBGPRINT("v4l: No frequency given. Won't be set\r\n");
+ DBGPRINT("v4l: Syntax is: v4l:/tuner_name/frequency\r\n");
#endif
+ }
+
+ this->frequency = frequency;
+
+ if (ret < 0)
+ return ret;
+ else
+ return 1;
+}
+
+static int set_input_source(v4l_input_plugin_t *this, char *input_source)
+{
+ int ret = 0;
+
+ if ((ret = search_by_channel(this, input_source)) != 1) {
+ ret = search_by_tuner(this, input_source);
+ }
- buf = alloc_frame (this);
+ return ret;
+}
+
+static int search_by_tuner(v4l_input_plugin_t *this, char *input_source)
+{
+ int ret = 0;
+ int fd = 0;
+ int cur_tuner = 0;
+
+ if (this->video_fd > 0)
+ fd = this->video_fd;
+ else
+ fd = this->radio_fd;
- this->gb_buf.frame = this->gb_frame;
+ this->video_tuner.tuner = cur_tuner;
+ ioctl(fd, VIDIOCGCAP, &this->video_cap);
+
+ DBGPRINT("This device has %d channel(s)\r\n", this->video_cap.channels);
+
+ for (ret = ioctl(fd, VIDIOCGTUNER, &this->video_tuner);
+ ret == 0 && this->video_cap.channels > cur_tuner && strstr(this->video_tuner.name, input_source) == NULL;
+ cur_tuner++) {
+
+ this->video_tuner.tuner = cur_tuner;
+
+ DBGPRINT("(%d) V4L device currently set to: \r\n", ret);
+ DBGPRINT("Tuner: %d\r\n", this->video_tuner.tuner);
+ DBGPRINT("Name: %s\r\n", this->video_tuner.name);
+ if (this->video_tuner.flags & VIDEO_TUNER_LOW) {
+ DBGPRINT("Range: %ld - %ld\r\n", this->video_tuner.rangelow / 16, this->video_tuner.rangehigh * 16);
+ } else {
+ DBGPRINT("Range: %ld - %ld\r\n", this->video_tuner.rangelow * 1000 / 16, this->video_tuner.rangehigh * 1000 / 16);
+ }
+ }
+
+ DBGPRINT("(%d) V4L device final: \r\n", ret);
+ DBGPRINT("Tuner: %d\r\n", this->video_tuner.tuner);
+ DBGPRINT("Name: %s\r\n", this->video_tuner.name);
+ if (this->video_tuner.flags & VIDEO_TUNER_LOW) {
+ DBGPRINT("Range: %ld - %ld\r\n", this->video_tuner.rangelow / 16, this->video_tuner.rangehigh * 16);
+ } else {
+ DBGPRINT("Range: %ld - %ld\r\n", this->video_tuner.rangelow * 1000 / 16, this->video_tuner.rangehigh * 1000 / 16);
+ }
+
+ if (strstr(this->video_tuner.name, input_source) == NULL)
+ return -1;
+
+ return 1;
+}
+static int search_by_channel(v4l_input_plugin_t *this, char *input_source)
+{
+ int ret = 0;
+ int fd = 0;
+ this->input = 0;
+
+ if (this->video_fd > 0)
+ fd = this->video_fd;
+ else
+ fd = this->radio_fd;
+
+ /* Tune into channel */
+ ret = ioctl(fd, VIDIOCGCHAN, &this->video_channel);
+ DBGPRINT("(%d) V4L device currently set to:\r\n", ret);
+ DBGPRINT("Channel: %d\r\n", this->video_channel.channel);
+ DBGPRINT("Name: %s\r\n", this->video_channel.name);
+ DBGPRINT("Tuners: %d\r\n", this->video_channel.tuners);
+ DBGPRINT("Flags: %d\r\n", this->video_channel.flags);
+ DBGPRINT("Type: %d\r\n", this->video_channel.type);
+ DBGPRINT("Norm: %d\r\n", this->video_channel.norm);
+
+ if (strlen(input_source) > 0) {
+ while (strstr(this->video_channel.name, input_source) == NULL &&
+ ioctl(fd, VIDIOCGCHAN, &this->video_channel) == 0) {
+
+ DBGPRINT("V4L device currently set to:\r\n");
+ DBGPRINT("Channel: %d\r\n", this->video_channel.channel);
+ DBGPRINT("Name: %s\r\n", this->video_channel.name);
+ DBGPRINT("Tuners: %d\r\n", this->video_channel.tuners);
+ DBGPRINT("Flags: %d\r\n", this->video_channel.flags);
+ DBGPRINT("Type: %d\r\n", this->video_channel.type);
+ DBGPRINT("Norm: %d\r\n", this->video_channel.norm);
+ this->video_channel.channel = ++this->input;
+ }
+
+ if (strstr(this->video_channel.name, input_source) == NULL) {
+ if (this->stream->xine->verbosity >= XINE_VERBOSITY_LOG)
+ PRINT("Tuner name not found\n");
+ return -1;
+ }
+
+ this->tuner_name = input_source;
+ ret = ioctl(fd, VIDIOCSCHAN, &this->input);
+
+ DBGPRINT("(%d) Set channel to %d\r\n", ret, this->input);
+
+ /* FIXME: Don't assume tuner 0 ? */
+
+ this->tuner = 0;
+
+ ret = ioctl(fd, VIDIOCSTUNER, &this->tuner);
+
+ DBGPRINT("(%d) Response on set tuner to %d\r\n", ret, this->tuner);
+
+ this->video_tuner.tuner = this->tuner;
+ } else {
+ PRINT("v4l: Not setting video source. No source given\r\n");
+ }
+ ret = ioctl(fd, VIDIOCGTUNER, &this->video_tuner);
#ifdef LOG
- printf ("input_v4l: VIDIOCMCAPTURE\n");
-#endif
+ DBGPRINT("(%d) Flags %d\r\n", ret, this->video_tuner.flags);
+
+ DBGPRINT("VIDEO_TUNER_PAL %s set\r\n", this->video_tuner.flags & VIDEO_TUNER_PAL ? "" : "not");
+ DBGPRINT("VIDEO_TUNER_NTSC %s set\r\n", this->video_tuner.flags & VIDEO_TUNER_NTSC ? "" : "not");
+ DBGPRINT("VIDEO_TUNER_SECAM %s set\r\n", this->video_tuner.flags & VIDEO_TUNER_SECAM ? "" : "not");
+ DBGPRINT("VIDEO_TUNER_LOW %s set\r\n", this->video_tuner.flags & VIDEO_TUNER_LOW ? "" : "not");
+ DBGPRINT("VIDEO_TUNER_NORM %s set\r\n", this->video_tuner.flags & VIDEO_TUNER_NORM ? "" : "not");
+ DBGPRINT("VIDEO_TUNER_STEREO_ON %s set\r\n", this->video_tuner.flags & VIDEO_TUNER_STEREO_ON ? "" : "not");
+ DBGPRINT("VIDEO_TUNER_RDS_ON %s set\r\n", this->video_tuner.flags & VIDEO_TUNER_RDS_ON ? "" : "not");
+ DBGPRINT("VIDEO_TUNER_MBS_ON %s set\r\n", this->video_tuner.flags & VIDEO_TUNER_MBS_ON ? "" : "not");
+
+ switch (this->video_tuner.mode) {
+ case VIDEO_MODE_PAL:
+ DBGPRINT("The tuner is in PAL mode\r\n");
+ break;
+ case VIDEO_MODE_NTSC:
+ DBGPRINT("The tuner is in NTSC mode\r\n");
+ break;
+ case VIDEO_MODE_SECAM:
+ DBGPRINT("The tuner is in SECAM mode\r\n");
+ break;
+ case VIDEO_MODE_AUTO:
+ DBGPRINT("The tuner is in AUTO mode\r\n");
+ break;
+ }
+#endif
+ return 1;
+}
+int open_radio_capture_device(v4l_input_plugin_t *this)
+{
+ int tuner_found = 0;
+ int i = 0;
- while (ioctl(this->video_fd, VIDIOCMCAPTURE, &this->gb_buf) < 0) {
-#ifdef LOG
- printf("input_v4l: upper while loop\n");
-#endif
- if (errno == EAGAIN) {
-#ifdef LOG
- printf ("input_v4l: cannot sync\n");
-#endif
- continue;
- } else {
- perror("VIDIOCMCAPTURE");
- return NULL;
- }
- }
+ /*
+ * pre-alloc a bunch of frames
+ */
- this->gb_frame = (this->gb_frame + 1) % this->gb_buffers.frames;
+ pthread_mutex_init (&this->vid_frames_lock, NULL);
+ pthread_cond_init (&this->vid_frame_freed, NULL);
+ pthread_mutex_init (&this->aud_frames_lock, NULL);
+ pthread_cond_init (&this->aud_frame_freed, NULL);
- while (ioctl(this->video_fd, VIDIOCSYNC, &this->gb_frame) < 0 &&
- (errno == EAGAIN || errno == EINTR))
- {
-#ifdef LOG
- printf("input_v4l: waiting for videosync\n");
+ DBGPRINT("Opening radio device\n");
+
+ this->radio_fd = open("/dev/v4l/radio0", O_RDWR);
+
+ if (this->radio_fd < 0)
+ return 0;
+
+ DBGPRINT("Device opened, radio %d\n", this->radio_fd);
+
+ if (set_input_source(this, this->tuner_name) > 0)
+ tuner_found = 1;
+
+ this->stream->stream_info[XINE_STREAM_INFO_AUDIO_CHANNELS] = periods;
+ this->stream->stream_info[XINE_STREAM_INFO_AUDIO_BITS] = bits;
+ this->stream->stream_info[XINE_STREAM_INFO_AUDIO_SAMPLERATE] = rate;
+ this->stream->stream_info[XINE_STREAM_INFO_HAS_AUDIO] = 1;
+ this->stream->stream_info[XINE_STREAM_INFO_HAS_VIDEO] = 0;
+
+ /*
+ * Pre allocate some frames for audio and video. This way this hasn't to be
+ * done during capture.
+ */
+ for (i=0; i<NUM_FRAMES; i++) {
+ buf_element_t *frame;
+
+ /* Audio frame */
+ frame = xine_xmalloc (sizeof (buf_element_t));
+
+ frame->decoder_info[1] = periodsize;
+ frame->content = xine_xmalloc(periodsize);
+ frame->type = BUF_AUDIO_RAWPCM;
+ frame->source = this;
+ frame->free_buffer = store_aud_frame;
+ frame->extra_info = xine_xmalloc(sizeof(extra_info_t));
+
+ store_aud_frame(frame);
+ }
+
+ this->audio_only = 1;
+
+ /* Unmute audio off video capture device */
+ ioctl(this->radio_fd, VIDIOCGAUDIO, &this->audio);
+ memcpy(&this->audio_saved, &this->audio, sizeof(this->audio));
+ this->audio.flags &= ~VIDEO_AUDIO_MUTE;
+ this->audio.volume=0x8000;
+ DBGPRINT("Setting audio volume\r\n");
+ ioctl(this->radio_fd, VIDIOCSAUDIO, &this->audio);
+
+ set_frequency(this, this->frequency);
+
+ if (tuner_found)
+ return 1;
+ else
+ return 2;
+}
+
+int close_radio_capture_device(v4l_input_plugin_t *this)
+{
+ if (this->radio_fd > 0)
+ close(this->radio_fd);
+ else
+ /* Radio device probably never opened. So nothing left to cleanup. */
+ return 0;
+
+ this->radio_fd = 0;
+
+ return 1;
+}
+
+/**
+ * Open the video capture device.
+ *
+ * This opens the video capture device and if given, selects a tuner from
+ * which the signal should be grabbed.
+ * @return 1 on success, 0 on failure.
+ */
+int open_video_capture_device(v4l_input_plugin_t *this)
+{
+ int i, j, ret, found = 0;
+ int tuner_found = 0;
+
+ DBGPRINT("Trying to open '%s'\n", this->mrl);
+
+ /*
+ * pre-alloc a bunch of frames
+ */
+
+ pthread_mutex_init (&this->vid_frames_lock, NULL);
+ pthread_cond_init (&this->vid_frame_freed, NULL);
+ pthread_mutex_init (&this->aud_frames_lock, NULL);
+ pthread_cond_init (&this->aud_frame_freed, NULL);
+
+ /* Try to open the video device */
+ this->video_fd = open("/dev/v4l/video0", O_RDWR);
+
+ if (this->video_fd < 0) {
+ DBGPRINT("(%d) Cannot open v4l device: %s\n", this->video_fd,
+ strerror(errno));
+ return 0;
+ }
+ DBGPRINT("Device opened, tv %d\n", this->video_fd);
+
+ /* Get capabilities */
+ if (ioctl(this->video_fd,VIDIOCGCAP,&this->video_cap) < 0) {
+ DBGPRINT ("VIDIOCGCAP ioctl went wrong\n");
+ return 0;
+ }
+
+ if (!(this->video_cap.type & VID_TYPE_CAPTURE)) {
+ /* Capture is not supported by the device. This is a must though! */
+ DBGPRINT("Grab device does not handle capture\n");
+ return 0;
+ }
+
+ /* figure out the resolution */
+ for (j=0; j<NUM_RESOLUTIONS; j++)
+ {
+ if (resolutions[j].width <= this->video_cap.maxwidth
+ && resolutions[j].height <= this->video_cap.maxheight
+ && resolutions[j].width <= MAX_RES)
+ {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found == 0 || resolutions[j].width < this->video_cap.minwidth
+ || resolutions[j].height < this->video_cap.minheight)
+ {
+ /* Looks like the device does not support one of the preset resolutions */
+ DBGPRINT("Grab device does not support any preset resolutions");
+ return 0;
+ }
+
+ this->stream->stream_info[XINE_STREAM_INFO_VIDEO_WIDTH] = resolutions[j].width;
+ this->stream->stream_info[XINE_STREAM_INFO_VIDEO_HEIGHT] = resolutions[j].height;
+ this->stream->stream_info[XINE_STREAM_INFO_AUDIO_CHANNELS] = periods;
+ this->stream->stream_info[XINE_STREAM_INFO_AUDIO_BITS] = bits;
+ this->stream->stream_info[XINE_STREAM_INFO_AUDIO_SAMPLERATE] = rate;
+ this->stream->stream_info[XINE_STREAM_INFO_HAS_AUDIO] = 1;
+ this->stream->stream_info[XINE_STREAM_INFO_HAS_VIDEO] = 1;
+
+ /*
+ * Pre allocate some frames for audio and video. This way this hasn't to be
+ * done during capture.
+ */
+ for (i=0; i<NUM_FRAMES; i++) {
+ buf_element_t *frame;
+
+ /* Video frame */
+ frame = xine_xmalloc (sizeof (buf_element_t));
+
+ frame->decoder_info[0] = resolutions[j].width;
+ frame->decoder_info[1] = resolutions[j].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_vid_frame;
+ frame->extra_info = xine_xmalloc(sizeof(extra_info_t));
+
+ store_vid_frame(frame);
+
+ /* Audio frame */
+ frame = xine_xmalloc (sizeof (buf_element_t));
+
+ frame->decoder_info[1] = periodsize;
+ frame->content = xine_xmalloc(periodsize);
+ frame->type = BUF_AUDIO_RAWPCM;
+ frame->source = this;
+ frame->free_buffer = store_aud_frame;
+ frame->extra_info = xine_xmalloc(sizeof(extra_info_t));
+
+ store_aud_frame(frame);
+ }
+
+ /* Unmute audio off video capture device */
+ ioctl(this->video_fd, VIDIOCGAUDIO, &this->audio);
+ memcpy(&this->audio_saved, &this->audio, sizeof(this->audio));
+ this->audio.flags &= ~VIDEO_AUDIO_MUTE;
+ this->audio.volume=0xD000;
+ DBGPRINT("Setting audio volume\r\n");
+ ioctl(this->video_fd, VIDIOCSAUDIO, &this->audio);
+
+ if (strlen(this->tuner_name) > 0) {
+ /* Tune into source and given frequency */
+ if (set_input_source(this, this->tuner_name) <= 0)
+ return 0;
+ else
+ tuner_found = 1;
+ }
+
+ set_frequency(this, this->frequency);
+
+ /* Test for mmap video access */
+ ret = ioctl(this->video_fd,VIDIOCGMBUF, &this->gb_buffers);
+
+ if (ret < 0) {
+ /* Device driver does not support mmap */
+ /* try to use read based access */
+ struct video_picture pict;
+ int val;
+
+ ioctl(this->video_fd, VIDIOCGPICT, &pict);
+
+ /* 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;
+ DBGPRINT("Grab: no colorspace format found\n");
+ return 0;
+ }
+ else
+ DBGPRINT("Grab: format YUV 4:2:2\n");
+ }
+ else
+ DBGPRINT("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 {
+ /* Good, device driver support mmap. Mmap the memory */
+ DBGPRINT("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) {
+ /* mmap failed. */;
+ perror("mmap");
+ close (this->video_fd);
+ return 0;
+ }
+ 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 = resolutions[j].height;
+ this->gb_buf.width = resolutions[j].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
+ DBGPRINT("(%d) input_v4l: YUV420 should work\n", ret);
+
+ if (ret < 0) {
+ if (errno != EAGAIN) {
+ DBGPRINT(
+ "input_v4l: grab device does not support suitable format\n");
+ } else {
+ DBGPRINT(
+ "input_v4l: grab device does not receive any video signal\n");
+ }
+ close (this->video_fd);
+ return 0;
+ }
+ this->frame_format = this->gb_buf.format;
+ this->use_mmap = 1;
+ }
+
+ switch(this->frame_format) {
+ case VIDEO_PALETTE_YUV420P:
+ this->frame_size =
+ (resolutions[j].width * resolutions[j].height * 3) / 2;
+ break;
+ case VIDEO_PALETTE_YUV422:
+ this->frame_size = resolutions[j].width * resolutions[j].height * 2;
+ break;
+ }
+
+ /* Save dimensions */
+ this->stream->stream_info[XINE_STREAM_INFO_VIDEO_WIDTH] =
+ resolutions[j].width;
+ this->stream->stream_info[XINE_STREAM_INFO_VIDEO_HEIGHT] =
+ resolutions[j].height;
+
+ /* Using deinterlaceing is highly recommended. Setting to true */
+ this->old_interlace =
+ xine_get_param(this->stream, XINE_PARAM_VO_DEINTERLACE);
+ xine_set_param(this->stream, XINE_PARAM_VO_DEINTERLACE, 1);
+
+ /* Strip the vbi / sync signal from the image by zooming in */
+ this->old_zoomx = xine_get_param(this->stream, XINE_PARAM_VO_ZOOM_X);
+ this->old_zoomy = xine_get_param(this->stream, XINE_PARAM_VO_ZOOM_Y);
+
+ xine_set_param(this->stream, XINE_PARAM_VO_ZOOM_X, 103);
+ xine_set_param(this->stream, XINE_PARAM_VO_ZOOM_Y, 103);
+
+ /* If we made it here, everything went ok */
+ this->audio_only = 0;
+ if (tuner_found)
+ return 1;
+ else
+ /* Not a real error, appart that the tuner name is unknown to us */
+ return 2;
+}
+
+/**
+ * Open audio capture device.
+ *
+ * This function opens an alsa capture device. This will be used to capture
+ * audio data from.
+ */
+int open_audio_capture_device(v4l_input_plugin_t *this)
+{
+#ifdef HAVE_ALSA
+ DBGPRINT("Audio Opening PCM Device\n");
+ /* Allocate the snd_pcm_hw_params_t structure on the stack. */
+ snd_pcm_hw_params_alloca(&this->pcm_hwparams);
+
+ /* Open the PCM device. */
+ if (this->audio_only) {
+ /* Open the sound device in blocking mode if we are not capturing video,
+ * otherwise xine gets to many NULL bufs and doesn't seem to handle
+ * them correctly
+ */
+ if (snd_pcm_open(&this->pcm_handle, this->pcm_name, this->pcm_stream,
+ 0) < 0) {
+ PRINT("Audio :( Error opening PCM device %s\n", this->pcm_name);
+ this->audio_capture = 0;
+ }
+ } else
+ if (snd_pcm_open(&this->pcm_handle, this->pcm_name, this->pcm_stream,
+ SND_PCM_NONBLOCK) < 0) {
+ /* Open the sound device in non blocking mode when capturing video data
+ * too, otherwise we will loose videoframes because we keep on waiting
+ * for an audio fragment
+ */
+ PRINT("Audio :( Error opening PCM device %s\n", this->pcm_name);
+ this->audio_capture = 0;
+ }
+
+ /* Get parameters */
+ if (this->audio_capture &&
+ (snd_pcm_hw_params_any(this->pcm_handle, this->pcm_hwparams) < 0)
+ ) {
+ PRINT("Audio :( Can not configure this PCM device.\n");
+ this->audio_capture = 0;
+ }
+
+ /* Set access type */
+ if (this->audio_capture &&
+ (snd_pcm_hw_params_set_access(this->pcm_handle, this->pcm_hwparams,
+ SND_PCM_ACCESS_RW_INTERLEAVED) < 0)
+ ) {
+ PRINT("Audio :( Error setting acces.\n");
+ this->audio_capture = 0;
+ }
+
+ if (this->audio_capture) {
+ if (snd_pcm_hw_params_any(this->pcm_handle, this->pcm_hwparams) < 0) {
+ PRINT("Audio :( Broken configuration for this PCM: No config avail\n"); this->audio_capture = 0;
+ }
+ }
+
+ if (this->audio_capture) {
+ snd_pcm_access_mask_t *mask = alloca(snd_pcm_access_mask_sizeof());
+ snd_pcm_access_mask_none(mask);
+ snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
+ if (snd_pcm_hw_params_set_access_mask(this->pcm_handle,
+ this->pcm_hwparams, mask) < 0) {
+ PRINT("Audio :( Error setting access mask\n");
+ this->audio_capture = 0;
+ }
+ }
+
+ /* Set sample format */
+ if (this->audio_capture &&
+ (snd_pcm_hw_params_set_format(this->pcm_handle, this->pcm_hwparams,
+ SND_PCM_FORMAT_S16_LE) < 0)
+ ) {
+ PRINT("Audio :( Error setting format.\n");
+ this->audio_capture = 0;
+ }
+
+ /* Set sample rate */
+ if (this->audio_capture) {
+ this->exact_rate = snd_pcm_hw_params_set_rate_near(this->pcm_handle,
+ this->pcm_hwparams, rate, &this->dir);
+ if (this->dir != 0) {
+ PRINT("Audio :s The rate %d Hz is not supported by your hardware.\n",
+ rate);
+ PRINT("Audio :s ==> Using %d instead.\n", this->exact_rate);
+ }
+ }
+
+ /* Set number of channels */
+ if (this->audio_capture &&
+ (snd_pcm_hw_params_set_channels(this->pcm_handle,
+ this->pcm_hwparams, 2) < 0)) {
+ PRINT("Audio :( Error setting channels.\n");
+ this->audio_capture = 0;
+ }
+
+ if (this->audio_capture &&
+ (snd_pcm_hw_params_set_periods(this->pcm_handle, this->pcm_hwparams,
+ periods, 0) < 0)) {
+ PRINT("Audio :( Error setting periods.\n");
+ this->audio_capture = 0;
+ }
+
+ /* Set buffersize */
+ if (this->audio_capture &&
+ (snd_pcm_hw_params_set_buffer_size(this->pcm_handle,
+ this->pcm_hwparams,
+ (periodsize * periods) >> 2) < 0)) {
+ PRINT("Audio :( Error setting buffersize.\n");
+ this->audio_capture = 0;
+ }
+
+ /* Apply HW parameter settings */
+ if (this->audio_capture &&
+ (snd_pcm_hw_params(this->pcm_handle, this->pcm_hwparams) < 0)){
+ PRINT("Audio :( Error Setting HW params.\n");
+ this->audio_capture = 0;
+ }
+
+ if (this->audio_capture) {
+ DBGPRINT("Audio Allocating memory for PCM capture :%d\n", periodsize);
+ this->pcm_data = (unsigned char*) malloc(periodsize);
+ } else
+ this->pcm_data = NULL;
+
+ DBGPRINT("Audio :) Device succesfully configured\r\n");
#endif
- }
+ return 0;
+}
- if (this->start_time == 0)
- this->start_time = get_time();
- buf->pts = (get_time() - this->start_time)+50*3600;
+/**
+ * Adjust realtime speed
+ *
+ * If xine is playing at normal speed, tries to adjust xines playing speed to
+ * avoid buffer overrun and buffer underrun
+ */
+static int v4l_adjust_realtime_speed(v4l_input_plugin_t *this, fifo_buffer_t *fifo, int speed)
+{
+ int num_used, num_free;
+ int scr_tunning = this->scr_tunning;
+
+ if (fifo == NULL)
+ return 0;
- /* printf ("grabbing frame #%d\n", frame_num); */
+ num_used = fifo->size(fifo);
+ num_free = NUM_FRAMES - num_used;
+
+ if (!this->audio_only && num_used == 0 && scr_tunning != SCR_PAUSED) {
+ /* Buffer is empty, and we did not pause playback */
+ report_progress(this->stream, SCR_PAUSED);
+
+ if (this->stream->xine->verbosity >= XINE_VERBOSITY_LOG)
+ PRINT("Buffer is empty, pausing playback (used: %d, num_free: %d)\r\n",
+ num_used, num_free);
+
+ this->scr_tunning = SCR_PAUSED;
+ pvrscr_speed_tunning(this->scr, 0.0);
+ this->stream->audio_out->set_property(this->stream->audio_out, AO_PROP_PAUSED, 2);
+ } else
+ if (num_free <= 1 && scr_tunning != SCR_SKIP) {
+ this->scr_tunning = SCR_SKIP;
+ PRINT("Buffer full (used: %d, free: %d)\r\n",
+ num_used, num_free);
+ return 0;
+ } else
+ if (scr_tunning == SCR_PAUSED) {
+ if (2 * num_used > num_free) {
+ /* Playback was paused, but we have normal buffer usage again */
+ if (this->stream->xine->verbosity >= XINE_VERBOSITY_LOG)
+ PRINT("Resuming playback (used: %d, free: %d)\r\n",
+ num_used, num_free);
+
+ this->scr_tunning = 0;
+
+ pvrscr_speed_tunning(this->scr, 1.0);
+ this->stream->audio_out->set_property(this->stream->audio_out, AO_PROP_PAUSED, 0);
+ }
+ } else
+ if (scr_tunning == SCR_SKIP) {
+ if (num_used < 2 * num_free) {
+ DBGPRINT("Resuming from skipping (used: %d, free %d)\r\n",
+ num_used, num_free);
+ this->scr_tunning = 0;
+ } else {
+ return 0;
+ }
+ } else
+ if (speed == XINE_SPEED_NORMAL) {
+ if (num_used > 2 * num_free)
+ /* buffer used > 2/3. Increase playback speed to avoid buffer
+ * overrun */
+ scr_tunning = +1;
+ else if (num_free > 2 * num_used)
+ /* Buffer used < 1/3. Decrease playback speed to avoid buffer
+ * underrun */
+ scr_tunning = -1;
+ else if ((scr_tunning > 0 && num_free > num_used) ||
+ (scr_tunning < 0 && num_used > num_free))
+ /* Buffer usage is ok again. Set playback speed to normal */
+ scr_tunning = 0;
+
+ /* Check if speed adjustment should be changed */
+ if (scr_tunning != this->scr_tunning) {
+ this->scr_tunning = scr_tunning;
+ if (this->stream->xine->verbosity >= XINE_VERBOSITY_LOG)
+ PRINT("scr tunning = %d (used: %d, free: %d)\r\n", scr_tunning, num_used, num_free);
+ pvrscr_speed_tunning(this->scr, 1.0 + (0.01 * scr_tunning));
+ }
+ } else
+ if (this->scr_tunning) {
+ /* Currently speed adjustment is on. But xine is not playing at normal
+ * speed, so there is no reason why we should try to adjust our playback
+ * speed
+ */
+ this->scr_tunning = 0;
+
+ if (this->stream->xine->verbosity >= XINE_VERBOSITY_LOG)
+ PRINT("scr tunning resetting (used: %d, free: %d\r\n", num_used, num_free);
+
+ pvrscr_speed_tunning(this->scr, 1.0);
+ }
+
+ return 1;
+}
+
+/**
+ * Plugin read.
+ * This function is not supported by the plugin.
+ */
+static off_t v4l_plugin_read (input_plugin_t *this_gen,
+ char *buf, off_t len) {
+ DBGPRINT("Read not supported\r\n");
+ return 0;
+}
- ptr = this->video_buf + this->gb_buffers.offsets[this->gb_frame];
+/**
+ * Get time.
+ * Gets a pts time value.
+ */
+inline static int64_t get_time() {
+ struct timeval tv;
+ gettimeofday(&tv,NULL);
- xine_fast_memcpy (buf->content, ptr, this->frame_size);
+ return (int64_t) tv.tv_sec * 90000 + (int64_t) tv.tv_usec * 9 / 100;
+}
-#ifdef LOG
- printf("input_v4l: read block done\n");
+
+/**
+ * Plugin read block
+ * Reads one data block. This is either an audio frame or an video frame
+ */
+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 = NULL;
+ uint8_t *ptr;
+ static char video = 0;
+ int speed = this->stream->xine->clock->speed;
+
+ v4l_event_handler(this);
+
+ if (!this->audio_only) {
+ if (!v4l_adjust_realtime_speed(this, fifo, speed)) {
+ return NULL;
+ }
+ }
+
+ if (!this->audio_only)
+ video = !video;
+ else
+ video = 0;
+
+ DBGPRINT("%lld bytes...\n", todo);
+
+ if (this->start_time == 0)
+ /* Create a start pts value */
+ this->start_time = get_time(); /* this->stream->xine->clock->get_current_time(this->stream->xine->clock); */
+
+ if (video) {
+ /* Capture video */
+ buf = alloc_vid_frame (this);
+ this->gb_buf.frame = this->gb_frame;
+
+ DBGPRINT("input_v4l: VIDIOCMCAPTURE\n");
+
+ while (ioctl(this->video_fd, VIDIOCMCAPTURE, &this->gb_buf) < 0) {
+ DBGPRINT("Upper while loop\n");
+ if (errno == EAGAIN) {
+ DBGPRINT("Cannot sync\n");
+ continue;
+ } else {
+ perror("VIDIOCMCAPTURE");
+ buf->free_buffer(buf);
+ 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))
+ {
+ DBGPRINT("Waiting for videosync\n");
+ }
+
+ /* printf ("grabbing frame #%d\n", frame_num); */
+
+ ptr = this->video_buf + this->gb_buffers.offsets[this->gb_frame];
+ buf->pts = get_time(); /* this->stream->xine->clock->get_current_time(this->stream->xine->clock); */
+ xine_fast_memcpy (buf->content, ptr, this->frame_size);
+ } else {
+
+#ifdef HAVE_ALSA
+ /* Record audio */
+
+ int pcmreturn;
+ if ((pcmreturn = snd_pcm_mmap_readi(this->pcm_handle, this->pcm_data, (periodsize)>> 2)) < 0) {
+ switch (pcmreturn) {
+ case -EAGAIN:
+ /* No data available at the moment */
+ break;
+ case -EBADFD: /* PCM device in wrong state */
+ PRINT("Audio :( PCM is not in the right state\n");
+ break;
+ case -EPIPE: /* Buffer overrun */
+ PRINT("Audio :( Buffer Overrun (lost some samples)\n");
+ /* On buffer overrun we need to re prepare the capturing pcm device */
+ snd_pcm_prepare(this->pcm_handle);
+ break;
+ case -ESTRPIPE: /* Suspend event */
+ PRINT("Audio :( Suspend event occured\n");
+ break;
+ default: /* Unknown */
+ PRINT("Audio :o Unknown error code: %d\n", pcmreturn);
+ snd_pcm_prepare(this->pcm_handle);
+ }
+ } else {
+ /* Succesfully read audio data */
+ if (rate != this->exact_rate)
+ PRINT("HELP: Should pass sample rate %d instead of %d\r\n", this->exact_rate, rate);
+
+ if (this->pts_aud_start)
+ buf = alloc_aud_frame (this);
+
+ /* We want the pts on the start of the sample. As the soundcard starts
+ * sampling a new sample as soon as the read function returned with a
+ * success we will save the current pts and assign the current pts to
+ * that sample when we read it
+ */
+
+ /* Assign start pts to sample */
+ if (buf)
+ buf->pts = this->pts_aud_start;
+
+ /* Save start pts */
+ this->pts_aud_start = get_time(); //this->stream->xine->clock->get_current_time(this->stream->xine->clock);
+
+ if (!buf)
+ /* Skip first sample as we don't have a good pts for this one */
+ return NULL;
+
+ DBGPRINT("Audio: Data read: %d [%d, %d]. Pos: %d\r\n",
+ pcmreturn, (int) (*this->pcm_data), (int) (*(this->pcm_data + periodsize - 3)),
+ (int) this->curpos);
+
+
+ /* Tell decoder the number of bytes we have read */
+ buf->decoder_info[0] = pcmreturn;
+ buf->type = BUF_AUDIO_RAWPCM;
+
+ this->curpos++;
+
+ xine_fast_memcpy(buf->content, this->pcm_data, periodsize);
+ }
#endif
+ }
+
+ DBGPRINT("read block done\n");
- return buf;
+ return buf;
}
+/**
+ * Plugin seek.
+ * Not supported by the plugin.
+ */
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",
+ DBGPRINT("input_v4l: seek %lld bytes, origin %d\n",
offset, origin);
-#endif
return this->curpos;
}
+/**
+ * Plugin get length.
+ * This is a live stream, and as such does not have an known end.
+ */
static off_t v4l_plugin_get_length (input_plugin_t *this_gen) {
/*
@@ -244,14 +1439,33 @@ static off_t v4l_plugin_get_length (input_plugin_t *this_gen) {
return -1;
}
-static uint32_t v4l_plugin_get_capabilities (input_plugin_t *this_gen) {
- return 0;
+/**
+ * Plugin get capabilitiets.
+ * This plugin does not support any special capabilities.
+ */
+static uint32_t v4l_plugin_get_capabilities (input_plugin_t *this_gen)
+{
+ v4l_input_plugin_t *this = (v4l_input_plugin_t *) this_gen;
+
+ if (this->audio_only)
+ return 0x10;
+ else
+ return 0; // 0x10: Has audio only.
}
-static uint32_t v4l_plugin_get_blocksize (input_plugin_t *this_gen) {
+/**
+ * Plugin get block size.
+ * Unsupported by the plugin.
+ */
+static uint32_t v4l_plugin_get_blocksize (input_plugin_t *this_gen)
+{
return 0;
}
+/**
+ * Plugin get current pos.
+ * Unsupported by the plugin.
+ */
static off_t v4l_plugin_get_current_pos (input_plugin_t *this_gen){
v4l_input_plugin_t *this = (v4l_input_plugin_t *) this_gen;
@@ -262,294 +1476,497 @@ static off_t v4l_plugin_get_current_pos (input_plugin_t *this_gen){
return this->curpos;
}
+/**
+ * Event handler.
+ *
+ * Processes events from a frontend. This way frequencies can be changed
+ * without closing the v4l plugin.
+ */
+static void v4l_event_handler (v4l_input_plugin_t *this) {
+ xine_event_t *event;
+
+ while ((event = xine_event_get (this->event_queue))) {
+ xine_set_v4l2_data_t *v4l2_data = event->data;
+
+ switch (event->type) {
+ case XINE_EVENT_SET_V4L2:
+ if( v4l2_data->input != this->input ||
+ v4l2_data->channel != this->channel ||
+ v4l2_data->frequency != this->frequency ) {
+ this->input = v4l2_data->input;
+ this->channel = v4l2_data->channel;
+ this->frequency = v4l2_data->frequency;
+
+ DBGPRINT("Switching to input:%d chan:%d freq:%.2f\n",
+ v4l2_data->input,
+ v4l2_data->channel,
+ (float)v4l2_data->frequency);
+ set_frequency(this, this->frequency);
+
+ xine_demux_flush_engine(this->stream);
+ }
+ break;
+ case XINE_EVENT_MRL_REFERENCE:
+ DBGPRINT("Got new mrl: %s\n", (char *)event->data);
+ extract_mrl(this, event->data);
+ set_frequency(this, this->frequency);
+ xine_demux_flush_engine(this->stream);
+ break;
+/* default:
+
+ DBGPRINT("Got an event, type 0x%08x\n", event->type);
+ */
+ }
+
+ xine_event_free (event);
+ }
+}
+
+/**
+ * Dispose plugin.
+ *
+ * Closes the plugin, restore the V4L device in the initial state (volume) and
+ * frees the allocated memory
+ */
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);
- if (this->video_fd != -1)
- 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;
-}
+ if (this->scr) {
+ this->stream->xine->clock->unregister_scr(this->stream->xine->clock, &this->scr->scr);
+ this->scr->scr.exit(&this->scr->scr);
+ }
-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; */
+ /* Close and free video device */
+ if (this->tuner_name)
+ free(this->tuner_name);
+
+ /* Close video device only if device was openend */
+ if (this->video_fd > 0) {
+
+ /* Restore v4l audio volume */
+ DBGPRINT("Video Restoring audio volume %d\r\n",
+ ioctl(this->video_fd, VIDIOCSAUDIO, &this->audio_saved));
+ ioctl(this->video_fd, VIDIOCSAUDIO, &this->audio_saved);
+
+ /* Unmap memory */
+ if (this->video_buf != NULL &&
+ munmap(this->video_buf, this->gb_buffers.size) != 0) {
+ PRINT("Video :( Could not unmap memory, reason: %s\r\n",
+ strerror(errno));
+ } else
+ DBGPRINT("Video :) Succesfully unmapped memory (size %d)\r\n",
+ this->gb_buffers.size);
+
+ DBGPRINT("Video Closing video filehandler %d\r\n", this->video_fd);
+
+ /* Now close the video device */
+ if (close(this->video_fd) != 0)
+ PRINT("Video :( Error while closing video file handler, "
+ "reason: %s\r\n", strerror(errno));
+ else
+ DBGPRINT("Video :) Device succesfully closed\r\n");
- return INPUT_OPTIONAL_UNSUPPORTED;
-}
+ /* Restore interlace setting */
+ xine_set_param(this->stream, XINE_PARAM_VO_DEINTERLACE,
+ this->old_interlace);
-static int v4l_plugin_open (input_plugin_t *this_gen) {
- v4l_input_plugin_t *this = (v4l_input_plugin_t *) this_gen;
+ /* Restore zoom setting */
+ xine_set_param(this->stream, XINE_PARAM_VO_ZOOM_X, this->old_zoomx);
+ xine_set_param(this->stream, XINE_PARAM_VO_ZOOM_Y, this->old_zoomy);
+ }
+
+ if (this->radio_fd > 0) {
+ close(this->radio_fd);
+ }
+
+#ifdef HAVE_ALSA
+ /* Close audio device */
+ if (this->pcm_handle) {
+ snd_pcm_drop(this->pcm_handle);
+ snd_pcm_close(this->pcm_handle);
+ }
+
+ if (this->pcm_data) {
+ free(this->pcm_data);
+ }
+
+ if (this->pcm_name) {
+ free(this->pcm_name);
+ }
+#endif
- /* v4l_input_class_t *cls = (v4l_input_class_t *) cls_gen; */
- int i, j, ret, found;
+ if (this->event_queue)
+ xine_event_dispose_queue (this->event_queue);
+ DBGPRINT("Freeing allocated audio frames");
+ if (this->aud_frames) {
+ buf_element_t *cur_frame = this->aud_frames;
+ buf_element_t *next_frame = NULL;
+
+ while ((next_frame = cur_frame->next) != NULL) {
#ifdef LOG
- printf ("input_v4l: trying to open '%s'\n", this->mrl);
+ printf("."); fflush(stdout);
#endif
- found = 0;
-
- /*
- * pre-alloc a bunch of frames
- */
-
- pthread_mutex_init (&this->frames_lock, NULL);
- pthread_cond_init (&this->frame_freed, NULL);
-
- this->video_fd = open("/dev/video0", O_RDWR);
- if (this->video_fd < 0) {
+ if (cur_frame->content)
+ free(cur_frame->content);
+
+ if (cur_frame->extra_info)
+ free(cur_frame->extra_info);
+
+ free(cur_frame);
+ cur_frame = next_frame;
+ }
+ }
#ifdef LOG
- printf ("input_v4l: cannot open v4l device\n");
+ printf("\r\n");
#endif
- return 0;
- }
- if (ioctl(this->video_fd,VIDIOCGCAP,&this->video_cap) < 0) {
+ DBGPRINT("Freeing allocated video frames");
+ if (this->vid_frames) {
+ buf_element_t *cur_frame = this->vid_frames;
+ buf_element_t *next_frame = NULL;
+
+ while ((next_frame = cur_frame->next) != NULL) {
#ifdef LOG
- printf ("input_v4l: VIDIOCGCAP ioctl went wrong\n");
+ printf("."); fflush(stdout);
#endif
- return 0;
- }
-
- if (!(this->video_cap.type & VID_TYPE_CAPTURE)) {
+ if (cur_frame->content)
+ free(cur_frame->content);
+
+ if (cur_frame->extra_info)
+ free(cur_frame->extra_info);
+
+ free(cur_frame);
+ cur_frame = next_frame;
+ }
+ }
#ifdef LOG
- printf ("input_v4l: grab device does not handle capture\n");
+ printf("\r\n");
#endif
- return 0;
- }
+
+ free (this);
- /* figure out the resolution */
- for (j=0; j<NUM_RESOLUTIONS; j++)
- {
- if (resolutions[j].width < this->video_cap.maxwidth
- && resolutions[j].height < this->video_cap.maxheight)
- {
- found = 1;
- break;
- }
- }
+ DBGPRINT("plugin Bye bye! \r\n");
+}
- if (found == 0 || resolutions[j].width < this->video_cap.minwidth
- || resolutions[j].height < this->video_cap.minheight)
- {
-#ifdef LOG
- printf ("input_v4l: grab device does not support any preset resolutions");
-#endif
- return 0;
- }
+/**
+ * Get MRL.
+ *
+ * Get the current MRL used by the plugin.
+ */
+static char* v4l_plugin_get_mrl (input_plugin_t *this_gen) {
+ v4l_input_plugin_t *this = (v4l_input_plugin_t *) this_gen;
- for (i=0; i<NUM_FRAMES; i++) {
+ return this->mrl;
+}
- buf_element_t *frame;
+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; */
- frame = xine_xmalloc (sizeof (buf_element_t));
+ return INPUT_OPTIONAL_UNSUPPORTED;
+}
+static int v4l_plugin_radio_open (input_plugin_t *this_gen)
+{
+ v4l_input_plugin_t *this = (v4l_input_plugin_t *) this_gen;
- frame->decoder_info[0] = resolutions[j].width;
- frame->decoder_info[1] = resolutions[j].height;
- frame->content = xine_xmalloc (frame->decoder_info[0] * frame->decoder_info[1] * 3 / 2);
- frame->type = BUF_VIDEO_YUV_FRAMES;
+ if(open_radio_capture_device(this) != 1)
+ return 0;
+
+ open_audio_capture_device(this);
+
+#ifdef HAVE_ALSA
+ this->start_time = 0;
+ this->pts_aud_start = 0;
+ this->curpos = 0;
+ this->event_queue = xine_event_new_queue (this->stream);
+#endif
+
+ return 1;
+}
- frame->source = this;
- frame->free_buffer = store_frame;
- frame->extra_info = xine_xmalloc(sizeof(extra_info_t));
- store_frame (frame);
- }
+static int v4l_plugin_video_open (input_plugin_t *this_gen)
+{
+ v4l_input_plugin_t *this = (v4l_input_plugin_t *) this_gen;
+ int64_t time;
- /* 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;
-#ifdef LOG
- printf ("input_v4l: grab: no colorspace format found\n");
-#endif
- return 0;
- }
-#ifdef LOG
- else
- printf ("input_v4l: grab: format YUV 4:2:2\n");
-#endif
- }
-#ifdef LOG
- else
- printf ("input_v4l: grab: format YUV 4:2:0\n");
+ if(!open_video_capture_device(this))
+ return 0;
+
+ open_audio_capture_device(this);
+
+#ifdef HAVE_ALSA
+ this->pts_aud_start = 0;
#endif
- this->frame_format = pict.palette;
-
- val = 1;
- ioctl(this->video_fd, VIDIOCCAPTURE, &val);
+ this->start_time = 0;
+ this->curpos = 0;
+
+ /* Register our own scr provider */
+ time = this->stream->xine->clock->get_current_time(this->stream->xine->clock);
+ this->scr = pvrscr_init();
+ this->scr->scr.start(&this->scr->scr, time);
+ this->stream->xine->clock->register_scr(this->stream->xine->clock, &this->scr->scr);
+ this->scr_tunning = 0;
+
+ /* enable resample method */
+ this->stream->xine->config->update_num(this->stream->xine->config, "audio.av_sync_method", 1);
+
+ this->event_queue = xine_event_new_queue (this->stream);
+
+ return 1;
+}
- this->use_mmap = 0;
+/**
+ * Create a new instance.
+ *
+ * Creates a new instance of the plugin. Doesn't initialise the V4L device,
+ * does initialise the structure.
+ */
+static input_plugin_t *v4l_class_get_instance (input_class_t *cls_gen,
+ xine_stream_t *stream, const char *data)
+{
+ char *locator = NULL;
- } else {
-#ifdef LOG
- printf ("input_v4l: using mmap, size %d\n", this->gb_buffers.size);
+ /* v4l_input_class_t *cls = (v4l_input_class_t *) cls_gen; */
+ v4l_input_plugin_t *this;
+ char *mrl = strdup(data);
+
+ /* Example mrl: v4l:/Television/62500 */
+
+ if (strncasecmp (mrl, "v4l:/", 5)) {
+ free (mrl);
+ return NULL;
+ }
+
+ if (mrl != NULL) {
+ for (locator = mrl; *locator != '\0' && *locator != '/' ; locator++);
+ } else
+ PRINT("EUhmz, mrl was NULL?\r\n");
+
+ this = (v4l_input_plugin_t *) xine_xmalloc (sizeof (v4l_input_plugin_t));
+
+ extract_mrl(this, mrl);
+
+ this->stream = stream;
+ this->mrl = mrl;
+ this->video_buf = NULL;
+ this->video_fd = -1;
+ this->radio_fd = -1;
+ this->event_queue = NULL;
+ this->scr = NULL;
+#ifdef HAVE_ALSA
+ this->pcm_name = NULL;
+ this->pcm_data = NULL;
+ this->pcm_hwparams = NULL;
+
+ /* Audio */
+ this->pcm_stream = SND_PCM_STREAM_CAPTURE;
+ this->pcm_name = strdup("plughw:0,0");
+ this->audio_capture = 1;
#endif
- 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);
- return 0;
- }
- 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 = resolutions[j].height;
- this->gb_buf.width = resolutions[j].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;
+ pthread_mutex_init (&this->aud_frames_lock, NULL);
+ pthread_cond_init (&this->aud_frame_freed, NULL);
+
+ pthread_mutex_init (&this->vid_frames_lock, NULL);
+ pthread_cond_init (&this->vid_frame_freed, NULL);
+
+ 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;
+}
- ret = ioctl(this->video_fd, VIDIOCMCAPTURE, &this->gb_buf);
- }
-#ifdef LOG
- else
- printf ("input_v4l: YUV420 should work\n");
-#endif
- if (ret < 0) {
-#ifdef LOG
- 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");
- }
-#endif
- close (this->video_fd);
- return 0;
- }
- this->frame_format = this->gb_buf.format;
- this->use_mmap = 1;
- }
+static input_plugin_t *v4l_class_get_video_instance (input_class_t *cls_gen,
+ xine_stream_t *stream, const char *data)
+{
+ int is_ok = 1;
+
+ v4l_input_plugin_t *this = NULL;
+
+ this = (v4l_input_plugin_t *)
+ v4l_class_get_instance (cls_gen, stream, data);
- switch(this->frame_format) {
- case VIDEO_PALETTE_YUV420P:
- this->frame_size = ( resolutions[j].width * resolutions[j].height * 3) / 2;
- break;
- case VIDEO_PALETTE_YUV422:
- this->frame_size = resolutions[j].width * resolutions[j].height * 2;
- break;
- }
+ if (this)
+ this->input_plugin.open = v4l_plugin_video_open;
+ else
+ return NULL;
+
+ /* Try to see if the MRL contains a v4l device we understand */
+ if (is_ok)
+ extract_mrl(this, this->mrl);
- this->stream->stream_info[XINE_STREAM_INFO_VIDEO_WIDTH] = resolutions[j].width;
- this->stream->stream_info[XINE_STREAM_INFO_VIDEO_HEIGHT] = resolutions[j].height;
+ /* Try to open the video device */
+ if (is_ok)
+ this->video_fd = open(VIDEO_DEV, O_RDWR);
+
+ if (is_ok && this->video_fd < 0) {
+ DBGPRINT("(%d) Cannot open v4l device: %s\n", this->video_fd,
+ strerror(errno));
+ xine_log(this->stream->xine, XINE_LOG_MSG,
+ PLUGIN ": Sorry, could not open %s\n", VIDEO_DEV);
+ is_ok = 0;
+ } else
+ DBGPRINT("Device opened, tv %d\n", this->video_fd);
+
+ /* Get capabilities */
+ if (is_ok && ioctl(this->video_fd,VIDIOCGCAP,&this->video_cap) < 0) {
+ xine_log(this->stream->xine, XINE_LOG_MSG,
+ PLUGIN ": Sorry your v4l card doesn't support some features"
+ " needed by xine\n");
+ DBGPRINT ("VIDIOCGCAP ioctl went wrong\n");
+ is_ok = 0;;
+ }
+
+ if (is_ok && !(this->video_cap.type & VID_TYPE_CAPTURE)) {
+ /* Capture is not supported by the device. This is a must though! */
+ xine_log(this->stream->xine, XINE_LOG_MSG,
+ PLUGIN ": Sorry, your v4l card doesn't support frame grabbing."
+ " This is needed by xine though\n");
+
+ DBGPRINT("Grab device does not handle capture\n");
+ is_ok = 0;
+ }
+
+ if (is_ok && set_input_source(this, this->tuner_name) <= 0) {\
+ xine_log(this->stream->xine, XINE_LOG_MSG,
+ PLUGIN ": Could not locate the tuner name [%s] on your v4l card\n",
+ this->tuner_name);
+ is_ok = 0;
+ }
+
+ if (is_ok && this->video_fd > 0) {
+ close(this->video_fd);
+ this->video_fd = -1;
+ }
- this->start_time=0;
+ if (!is_ok) {
+ v4l_plugin_dispose((input_plugin_t *) this);
+ return NULL;
+ }
- return 1;
+
+ return &this->input_plugin;
}
-static input_plugin_t *v4l_class_get_instance (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;
- char *mrl = strdup(data);
- if (strncasecmp (mrl, "v4l:/", 5)) {
- free (mrl);
- return NULL;
- }
+static input_plugin_t *v4l_class_get_radio_instance (input_class_t *cls_gen,
+ xine_stream_t *stream, const char *data)
+{
+ int is_ok = 1;
+ v4l_input_plugin_t *this = NULL;
+
+ if (strstr(data, "Radio") == NULL)
+ return NULL;
+
+ this = (v4l_input_plugin_t *)
+ v4l_class_get_instance (cls_gen, stream, data);
- this = (v4l_input_plugin_t *) xine_xmalloc (sizeof (v4l_input_plugin_t));
+ if (this)
+ this->input_plugin.open = v4l_plugin_radio_open;
+ else
+ return NULL;
- this->stream = stream;
- this->mrl = mrl;
- this->video_fd = -1;
- pthread_mutex_init (&this->frames_lock, NULL);
- pthread_cond_init (&this->frame_freed, NULL);
-
- this->input_plugin.open = v4l_plugin_open;
- 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;
+ if (is_ok)
+ this->radio_fd = open(RADIO_DEV, O_RDWR);
+
+ if (this->radio_fd < 0) {
+ xine_log(this->stream->xine, XINE_LOG_MSG,
+ PLUGIN ": Allthough normally we would be able to handle this MRL,\n"
+ PLUGIN ": I am unable to open the radio device.[%s]\n", RADIO_DEV);
+ is_ok = 0;
+ } else
+ DBGPRINT("Device opened, radio %d\n", this->radio_fd);
+
+ if (is_ok && set_input_source(this, this->tuner_name) <= 0) {
+ xine_log(this->stream->xine, XINE_LOG_MSG,
+ PLUGIN ": Sorry, you Radio device doesn't support this tunername\n");
+ is_ok = 0;
+ }
+
+ if (!is_ok) {
+ v4l_plugin_dispose((input_plugin_t *) this);
+ return NULL;
+ }
- return &this->input_plugin;
+ close(this->radio_fd);
+
+ 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_video_description (input_class_t *this_gen) {
+ return _("v4l tv input plugin");
}
+static char *v4l_class_get_radio_description (input_class_t *this_gen) {
+ return _("v4l radio 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.get_instance = v4l_class_get_instance;
- 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;
+static void *init_video_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.get_instance = v4l_class_get_video_instance;
+ this->input_class.get_identifier = v4l_class_get_identifier;
+ this->input_class.get_description = v4l_class_get_video_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;
+}
- return this;
+static void *init_radio_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.get_instance = v4l_class_get_radio_instance;
+ this->input_class.get_identifier = v4l_class_get_identifier;
+ this->input_class.get_description = v4l_class_get_radio_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;
}
/*
@@ -558,8 +1975,11 @@ static void *init_class (xine_t *xine, void *data) {
plugin_info_t xine_plugin_info[] = {
/* type, API, "name", version, special_info, init_function */
- { PLUGIN_INPUT, 13, "v4l", XINE_VERSION_CODE, NULL, init_class },
+ { PLUGIN_INPUT, 13, "v4l_radio", XINE_VERSION_CODE, NULL, init_radio_class },
+ { PLUGIN_INPUT, 13, "v4l_tv", XINE_VERSION_CODE, NULL, init_video_class },
{ PLUGIN_NONE, 0, "", 0, NULL, NULL }
};
-
+/*
+ * vim:sw=3:sts=3:
+ */
diff --git a/src/libxineadec/Makefile.am b/src/libxineadec/Makefile.am
index f25dfe29d..cac9bffed 100644
--- a/src/libxineadec/Makefile.am
+++ b/src/libxineadec/Makefile.am
@@ -9,6 +9,7 @@ AM_CFLAGS = -DNSF_PLAYER
SUBDIRS = gsm610 nosefart
lib_LTLIBRARIES = \
+ xineplug_decode_pcm.la \
xineplug_decode_adpcm.la \
xineplug_decode_logpcm.la \
xineplug_decode_roqaudio.la \
@@ -21,6 +22,10 @@ xineplug_decode_roqaudio_la_SOURCES = roqaudio.c
xineplug_decode_roqaudio_la_LIBADD = $(XINE_LIB)
xineplug_decode_roqaudio_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
+xineplug_decode_pcm_la_SOURCES = pcm.c
+xineplug_decode_pcm_la_LIBADD = $(XINE_LIB)
+xineplug_decode_pcm_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
+
xineplug_decode_adpcm_la_SOURCES = adpcm.c
xineplug_decode_adpcm_la_LIBADD = $(XINE_LIB)
xineplug_decode_adpcm_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
diff --git a/src/libxineadec/pcm.c b/src/libxineadec/pcm.c
new file mode 100644
index 000000000..58b57bd57
--- /dev/null
+++ b/src/libxineadec/pcm.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2003 J.Asselman <j.asselman@itsec.nl>
+ * ITsec Professional Services
+ *
+ * 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
+ *
+ * Dummy decoder for RAW Recorded PCM data
+ * Sample rate: 44100
+ * Channels: 2 (Stereo)
+ * Resolution: 16
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "xine_internal.h"
+#include "video_out.h"
+#include "audio_out.h"
+#include "buffer.h"
+#include "xineutils.h"
+#include "bswap.h"
+
+#define AUDIOBUFSIZE 128*1024
+
+#define SAMPLERATE 44100
+#define CHANNELS 2
+#define BITS 16
+#define CAPMODE AO_CAP_MODE_STEREO
+
+typedef struct {
+ audio_decoder_class_t decoder_class;
+} pcm_class_t;
+
+typedef struct pcm_decoder_s {
+ audio_decoder_t audio_decoder;
+
+ xine_stream_t *stream;
+
+ unsigned int buf_type;
+
+ unsigned char *buf;
+ int bufsize;
+ int size;
+ char open;
+ int disc;
+} pcm_decoder_t;
+
+static void pcm_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) {\
+ pcm_decoder_t *this = (pcm_decoder_t *) this_gen;
+ audio_buffer_t *aud;
+ char *offset;
+ int bytes_left = buf->decoder_info[1];
+ int bits_per_frame = buf->decoder_info[1] / buf->decoder_info[0];
+
+#ifdef LOG
+ printf(__FILE__ ": decode_data, flags=0x%08x , mem size: %d, frames: %d...\n",
+ buf->decoder_flags, buf->decoder_info[1], buf->decoder_info[0]);
+#endif
+
+ if (this->stream->stream_info[XINE_STREAM_INFO_AUDIO_MODE] == 0) {
+#ifdef LOG
+ printf(__FILE__ ": Someone changed the audio mode. Closing device\r");
+#endif
+ this->stream->audio_out->close(this->stream->audio_out, this->stream);
+ this->open = 0;
+ }
+
+ if (!this->open)
+ this->open = this->stream->audio_out->open(this->stream->audio_out,
+ this->stream, BITS, SAMPLERATE, CAPMODE);
+
+ if (!this->open)
+ /* Snif :'( output still not open */
+ return;
+
+ offset = buf->content;
+
+ while (bytes_left > 0 && (!this->disc || buf->pts)) {
+ int size;
+
+ if (buf->pts)
+ this->disc = 0;
+
+ aud = this->stream->audio_out->get_buffer(this->stream->audio_out);
+
+ if (aud->mem_size == 0) {
+ printf(__FILE__ ": :( Got an audio buffer with size 0!\r\n");
+ return;
+ }
+
+ size = bytes_left > aud->mem_size ? aud->mem_size : bytes_left;
+
+ aud->vpts = buf->pts;
+ aud->num_frames = size / bits_per_frame;
+
+ xine_fast_memcpy(aud->mem, offset, size);
+
+ this->stream->audio_out->put_buffer(this->stream->audio_out, aud, this->stream);
+
+ bytes_left -= size;
+ offset += size;
+
+ buf->pts = 0;
+ }
+}
+
+static void pcm_reset (audio_decoder_t *this_gen) {
+ /* pcm_decoder_t *this = (pcm_decoder_t *) this_gen; */
+}
+
+static void pcm_discontinuity (audio_decoder_t *this_gen)
+{
+ pcm_decoder_t *this = (pcm_decoder_t *) this_gen;
+
+ this->disc = 1;
+}
+
+static void pcm_dispose (audio_decoder_t *this_gen)
+{
+ pcm_decoder_t *this = (pcm_decoder_t *) this_gen;
+
+#ifdef LOG
+ printf(__FILE__ ": Cleaning up\n");
+#endif
+
+ if (this->open)
+ this->stream->audio_out->close (this->stream->audio_out, this->stream);
+
+ this->open = 0;
+
+ if (this->buf)
+ free(this->buf);
+
+ free (this_gen);
+}
+
+/*
+ * PCM decoder class code
+ */
+
+static audio_decoder_t *open_plugin (audio_decoder_class_t *class_gen, xine_stream_t *stream) {
+
+ pcm_decoder_t *this;
+
+ this = (pcm_decoder_t *) malloc (sizeof (pcm_decoder_t));
+ memset(this, 0, sizeof (pcm_decoder_t));
+
+ this->audio_decoder.decode_data = pcm_decode_data;
+ this->audio_decoder.reset = pcm_reset;
+ this->audio_decoder.discontinuity = pcm_discontinuity;
+ this->audio_decoder.dispose = pcm_dispose;
+
+ this->buf = NULL;
+ this->stream = stream;
+ this->open = 0;
+
+ stream->audio_out->open (stream->audio_out, stream,
+ BITS, SAMPLERATE, CAPMODE);
+
+ return &this->audio_decoder;
+}
+
+static char *get_identifier (audio_decoder_class_t *this) {
+ return "PCM";
+}
+
+static char *get_description (audio_decoder_class_t *this) {
+ return "Dummy PCM stream decoder";
+}
+
+static void dispose_class (audio_decoder_class_t *this) {
+ free (this);
+}
+
+static void *init_plugin (xine_t *xine, void *data) {
+
+ pcm_class_t *this ;
+
+ this = (pcm_class_t *) malloc (sizeof (pcm_class_t));
+
+ this->decoder_class.open_plugin = open_plugin;
+ this->decoder_class.get_identifier = get_identifier;
+ this->decoder_class.get_description = get_description;
+ this->decoder_class.dispose = dispose_class;
+
+ return this;
+}
+
+static uint32_t audio_types[] = { BUF_AUDIO_RAWPCM, 0 };
+
+static decoder_info_t dec_info_audio = {
+ audio_types, /* supported types */
+ 10 /* priority */
+};
+
+plugin_info_t xine_plugin_info[] = {
+ /* type, API, "name", version, special_info, init_function */
+ { PLUGIN_AUDIO_DECODER, 13, "pcm", XINE_VERSION_CODE, &dec_info_audio, init_plugin },
+ { PLUGIN_NONE, 0, "", 0, NULL, NULL }
+};
+/*
+ * vim:sw=3:sts=3:
+ */
+
diff --git a/src/xine-engine/buffer.h b/src/xine-engine/buffer.h
index 7f935437e..3976a5530 100644
--- a/src/xine-engine/buffer.h
+++ b/src/xine-engine/buffer.h
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*
- * $Id: buffer.h,v 1.118 2003/05/31 02:18:01 tmmm Exp $
+ * $Id: buffer.h,v 1.119 2003/06/16 16:42:51 holstsn Exp $
*
*
* contents:
@@ -210,6 +210,7 @@ extern "C" {
#define BUF_AUDIO_DV 0x032D0000
#define BUF_AUDIO_WMAV 0x032E0000
#define BUF_AUDIO_SPEEX 0x032F0000
+#define BUF_AUDIO_RAWPCM 0x03300000
/* spu buffer types: */