diff options
author | Stefan Holst <holstsn@users.sourceforge.net> | 2003-06-16 16:42:51 +0000 |
---|---|---|
committer | Stefan Holst <holstsn@users.sourceforge.net> | 2003-06-16 16:42:51 +0000 |
commit | f85e001864873b52e96f906d44340d2b22c344a4 (patch) | |
tree | e2d185c09984a4d901564b03875dca16859ce152 | |
parent | 168e5d883fba0eaa89fa44febe5b2642d51d4d44 (diff) | |
download | xine-lib-f85e001864873b52e96f906d44340d2b22c344a4.tar.gz xine-lib-f85e001864873b52e96f906d44340d2b22c344a4.tar.bz2 |
Patch from Jeroen Asselman:
- fixes mimetype in realaudio demuxer
- big improvement of v4l input and associated
demuxer. Including sound capture using alsa
and a/v sync. Now radio is supported as well.
CVS patchset: 5054
CVS date: 2003/06/16 16:42:51
-rw-r--r-- | src/demuxers/demux_real.c | 4 | ||||
-rw-r--r-- | src/demuxers/demux_yuv_frames.c | 107 | ||||
-rw-r--r-- | src/input/Makefile.am | 6 | ||||
-rw-r--r-- | src/input/input_v4l.c | 2060 | ||||
-rw-r--r-- | src/libxineadec/Makefile.am | 5 | ||||
-rw-r--r-- | src/libxineadec/pcm.c | 221 | ||||
-rw-r--r-- | src/xine-engine/buffer.h | 3 |
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: */ |