summaryrefslogtreecommitdiff
path: root/src/input/vcd/xineplug_inp_vcd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/input/vcd/xineplug_inp_vcd.c')
-rw-r--r--src/input/vcd/xineplug_inp_vcd.c1805
1 files changed, 1805 insertions, 0 deletions
diff --git a/src/input/vcd/xineplug_inp_vcd.c b/src/input/vcd/xineplug_inp_vcd.c
new file mode 100644
index 000000000..3084a32c2
--- /dev/null
+++ b/src/input/vcd/xineplug_inp_vcd.c
@@ -0,0 +1,1805 @@
+/*
+ $Id: xineplug_inp_vcd.c,v 1.1 2003/10/13 11:47:11 f1rmb Exp $
+
+ Copyright (C) 2002,2003 Rocky Bernstein <rocky@panix.com>
+
+ Program 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.
+
+ This program 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
+*/
+
+/*
+ These are plugin routines called by the xine engine. See
+ Chapter 4. Extending xine's input
+ http://xinehq.de/index.php/hackersguide/index.php?resource=5.3&action=default#INPUT
+ and the comments in input_plugin.h
+
+ This is what is referred to below a "the xine plugin spec"
+
+ Please don't add any OS-specific code in here - #if defined(__sun)
+ or or #if defined(__linux__) are harbingers of such stuff. It took a
+ great deal of effort to get it *out* of here (or most of it); If you
+ feel the need to do so, you are doing something wrong and breaking
+ modularity.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* Standard includes */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+
+#define SHORT_PLUGIN_NAME "VCDX"
+#define MRL_PREFIX "vcdx://"
+#define MRL_PREFIX_LEN strlen(MRL_PREFIX)
+
+#define xine_config_entry_t xine_cfg_entry_t
+
+/* Xine includes */
+#include "xineutils.h"
+#include "input_plugin.h"
+
+#include "xine-extra.h"
+
+#ifdef HAVE_VCDNAV
+#include <cdio/logging.h>
+#include <cdio/iso9660.h>
+#include <cdio/cd_types.h>
+
+/* libvcd includes */
+#include <libvcd/files.h>
+#include <libvcd/logging.h>
+#else
+#include "cdio/logging.h"
+#include "cdio/iso9660.h"
+#include "cdio/cd_types.h"
+/* libvcd includes */
+#include "libvcd/files.h"
+#include "libvcd/logging.h"
+#endif
+
+#include "vcdplayer.h"
+#include "vcdio.h"
+
+
+/* A xine define. */
+#ifndef BUF_DEMUX_BLOCK
+#define BUF_DEMUX_BLOCK 0x05000000
+#endif
+
+/*
+ Convert an autoplay enumeration into an vcdinfo itemtype enumeration.
+ See definitions in vcdplayer.h and vcdinfo.h to get the below correct.
+*/
+vcdinfo_item_enum_t autoplay2itemtype[]={
+ VCDINFO_ITEM_TYPE_TRACK, /* VCDPLAYER_AUTOPLAY_TRACK */
+ VCDINFO_ITEM_TYPE_ENTRY, /* VCDPLAYER_AUTOPLAY_ENTRY */
+ VCDINFO_ITEM_TYPE_SEGMENT, /* VCDPLAYER_AUTOPLAY_SEGMENT */
+ VCDINFO_ITEM_TYPE_LID /* VCDPLAYER_AUTOPLAY_PBC */
+};
+
+typedef struct
+{
+ char *title_format; /* Format string of GUI display title */
+ char *comment_format; /* Format string of stream comment meta */
+} vcd_config_t;
+
+typedef struct vcd_input_plugin_tag vcd_input_plugin_t;
+
+typedef struct {
+ input_class_t input_class;
+ xine_t *xine;
+ config_values_t *config; /* Pointer to XineRC config file. */
+ vcd_input_plugin_t *ip;
+
+ vcd_config_t v_config; /* config stuff passed to child */
+ xine_mrl_t **mrls; /* list of mrl entries for medium */
+ int num_mrls; /* count of above */
+ char *vcd_device;/* Device name to use when
+ none specified in MRL */
+ /*--------------------------------------------------------------
+ Media resource locator (MRL) info.
+
+ For the below offsets, use play_item + mrl_xxx_offset to get index
+ into "mrls" array
+ ---------------------------------------------------------------*/
+ int mrl_track_offset; /* perhaps -1 for tracks staring with 1*/
+ int mrl_entry_offset; /* num_tracks for entries starting with 0 */
+ int mrl_play_offset; /* num_tracks for entries starting with 0 */
+ int mrl_segment_offset; /* num_tracks + num_entries if segs start 1*/
+
+
+} vcd_input_class_t;
+
+vcd_input_class_t *vcd_class;
+
+struct vcd_input_plugin_tag {
+ input_plugin_t input_plugin; /* input plugin interface as defined by
+ by player. For xine it contains a
+ structure of functions that need
+ to be implemented.
+ */
+ xine_stream_t *stream;
+ xine_event_queue_t *event_queue;
+
+ vcd_input_class_t *class;
+ vcd_config_t v_config; /* Config stuff initially inherited */
+ bool jumped; /* True if we changed tracks or any
+ sort of discontinuity in playing */
+ char *mrl;
+
+ vcdplayer_input_t player ;
+};
+
+vcd_input_plugin_t my_vcd;
+
+/* Prototype definitions */
+static bool vcd_handle_events (void);
+static void vcd_close(vcd_input_class_t *class);
+
+/*
+ If class->vcd_device is NULL or the empty string,
+ Use libcdio to find a CD drive with a VCD in it.
+*/
+static bool
+vcd_get_default_device(vcd_input_class_t *class, bool log_msg_if_fail)
+{
+ if (NULL == class->vcd_device || strlen(class->vcd_device)==0) {
+ char **cd_drives=NULL;
+ cd_drives = cdio_get_devices_with_cap(NULL,
+(CDIO_FS_ANAL_SVCD|CDIO_FS_ANAL_CVD|CDIO_FS_ANAL_VIDEOCD|CDIO_FS_UNKNOWN),
+ true);
+ if (NULL == cd_drives || NULL == cd_drives[0]) {
+ LOG_MSG("%s", _("failed to find a device with a VCD"));
+ return false;
+ }
+ class->vcd_device = strdup(cd_drives[0]);
+ cdio_free_device_list(cd_drives);
+ }
+ return true;
+}
+
+
+static void
+meta_info_assign(int field, xine_stream_t *stream, const char * info)
+{
+ if (NULL != info) {
+ if (stream->meta_info[field])
+ free(stream->meta_info[field]);
+ dbg_print(INPUT_DBG_META, "meta[%d]: %s\n", field, info);
+ stream->meta_info[field] = strdup(info);
+ }
+}
+
+#define stream_info_assign(field, stream, info) \
+ stream->stream_info[field] = info;
+
+/* Set stream information. */
+static void
+vcd_set_meta_info (vcd_input_plugin_t *xine_vcd)
+{
+ vcdinfo_obj_t *obj= xine_vcd->player.vcd;
+ meta_info_assign(XINE_META_INFO_ALBUM, xine_vcd->stream,
+ vcdinfo_get_album_id(obj));
+ meta_info_assign(XINE_META_INFO_ARTIST, xine_vcd->stream,
+ vcdinfo_get_preparer_id(obj));
+ meta_info_assign(XINE_META_INFO_COMMENT, xine_vcd->stream,
+ vcdplayer_format_str(&xine_vcd->player,
+ xine_vcd->v_config.comment_format));
+ meta_info_assign(XINE_META_INFO_GENRE, xine_vcd->stream,
+ vcdinfo_get_format_version_str(obj));
+}
+
+static void
+vcd_force_redisplay (void)
+{
+#if 1
+ my_vcd.stream->xine->clock->adjust_clock(my_vcd.stream->xine->clock,
+ my_vcd.stream->xine->clock->get_current_time(my_vcd.stream->xine->clock)
+ + 30 * 90000 );
+#else
+ /* Alternate method that causes too much disruption... */
+ xine_set_param(my_vcd.stream, XINE_PARAM_VO_ASPECT_RATIO,
+ (xine_get_param(my_vcd.stream, XINE_PARAM_VO_ASPECT_RATIO)));
+#endif
+}
+
+/*! Add another MRL to the MRL list inside "this" to be displayed.
+ mrl is the string name to add; size is the size of the entry in bytes.
+ The number of mrls in "this" is incremented.
+*/
+static void
+vcd_add_mrl_slot(vcd_input_class_t *this, const char *mrl, off_t size,
+ unsigned int *i)
+{
+ dbg_print(INPUT_DBG_MRL, "called to add slot %d: %s, size %u\n",
+ *i, mrl, (unsigned int) size);
+
+ this->mrls[*i] = malloc(sizeof(xine_mrl_t));
+ if (NULL==this->mrls[*i]) {
+ LOG_ERR("Can't malloc %d bytes for MRL slot %d (%s)",
+ sizeof(xine_mrl_t), *i, mrl);
+ return;
+ }
+ this->mrls[*i]->link = NULL;
+ this->mrls[*i]->origin = NULL;
+ this->mrls[*i]->type = mrl_vcd;
+ this->mrls[*i]->size = size * M2F2_SECTOR_SIZE;
+
+ this->mrls[*i]->mrl = (char *) malloc(strlen(mrl) + 1);
+ if (NULL==this->mrls[*i]->mrl) {
+ LOG_ERR("Can't malloc %d bytes for MRL name %s", sizeof(xine_mrl_t), mrl);
+ } else {
+ sprintf(this->mrls[*i]->mrl, "%s", mrl);
+ }
+ (*i)++;
+}
+
+/*!
+ Return the associated mrl_offset for the given type.
+*/
+static int
+vcd_get_mrl_type_offset(vcd_input_plugin_t *inp,
+ vcdinfo_item_enum_t type, int *size)
+{
+ switch (type) {
+ case VCDINFO_ITEM_TYPE_ENTRY:
+ *size = inp->class->mrl_play_offset - inp->class->mrl_entry_offset + 1;
+ return inp->class->mrl_entry_offset;
+ break;
+ case VCDINFO_ITEM_TYPE_SEGMENT:
+ *size = inp->class->num_mrls - inp->class->mrl_segment_offset - 1;
+ return inp->class->mrl_segment_offset;
+ case VCDINFO_ITEM_TYPE_TRACK:
+ *size = inp->class->mrl_entry_offset;
+ return inp->class->mrl_track_offset;
+ case VCDINFO_ITEM_TYPE_LID:
+ /* Play list number (LID) */
+ *size = (inp->player.num_LIDs > 0) ? 1 : 0;
+ return inp->class->mrl_play_offset;
+ case VCDINFO_ITEM_TYPE_NOTFOUND:
+ case VCDINFO_ITEM_TYPE_SPAREID2:
+ default:
+ return -2;
+ }
+}
+
+/*!
+ Create a MRL list inside "class". Any existing MRL list is freed.
+ */
+static bool
+vcd_build_mrl_list(vcd_input_class_t *class, char *vcd_device)
+{
+
+ char mrl[1024];
+ vcdplayer_input_t *player;
+ unsigned int n, i=0;
+ unsigned int num_entries;
+ vcdinfo_obj_t *obj;
+
+ if (NULL == class) {
+ LOG_MSG("%s", _("was passed a null class parameter"));
+ return false;
+ }
+
+ player = &(my_vcd.player);
+
+ /* If VCD already open, we gotta close and stop it. */
+ if (player->opened) {
+ vcd_close(class);
+ }
+
+ if (NULL == vcd_device) {
+ if (!vcd_get_default_device(class, true)) return false;
+ vcd_device = class->vcd_device;
+ }
+
+ if (!vcdio_open(player, vcd_device)) {
+ /* Error should have been logged in vcdio_open. If not do the below:
+ LOG_ERR(player, "%s: %s.\n", _("unable to open"),
+ class->vcd_device, strerror(errno));
+ */
+ return false;
+ }
+
+ obj = player->vcd;
+ num_entries = player->num_entries;
+ class->mrl_track_offset = -1;
+
+ xine_free_mrls(&(class->num_mrls), class->mrls);
+
+ /* Figure out number of MRLs. Calculation would be real simple if
+ didn't have to possibly remove rejected LIDs from list done in the
+ loop below.
+ */
+ class->num_mrls = player->num_tracks + player->num_entries
+ + player->num_segments + player->num_LIDs;
+
+ if (!player->show_rejected && vcdinfo_get_lot(player->vcd)) {
+ /* Remove rejected LIDs from count. */
+ for (n=0; n<player->num_LIDs; n++) {
+ if ( vcdinf_get_lot_offset(vcdinfo_get_lot(player->vcd), n)
+ == PSD_OFS_DISABLED )
+ class->num_mrls--;
+ }
+ }
+
+ class->mrls = calloc(class->num_mrls, sizeof(xine_mrl_t *));
+ if (NULL == class->mrls) {
+ LOG_ERR("Can't calloc %d MRL entries", class->mrls);
+ class->num_mrls = 0;
+ return false;
+ }
+
+ /* Record MRL's for tracks */
+ for (n=1; n<=player->num_tracks; n++) {
+ memset(&mrl, 0, sizeof (mrl));
+ sprintf(mrl, "%s%s@T%u", MRL_PREFIX, vcd_device, n);
+ vcd_add_mrl_slot(class, mrl, player->track[n-1].size, &i);
+ }
+
+ class->mrl_entry_offset = player->num_tracks;
+ class->mrl_play_offset = class->mrl_entry_offset + num_entries - 1;
+
+ /* Record MRL's for entries */
+ if (num_entries > 0) {
+ for (n=0; n<num_entries; n++) {
+ memset(&mrl, 0, sizeof (mrl));
+ sprintf(mrl, "%s%s@E%u", MRL_PREFIX, vcd_device, n);
+ vcd_add_mrl_slot(class, mrl, player->entry[n].size, &i);
+ }
+ }
+
+ /* Record MRL's for LID entries or selection entries*/
+ class->mrl_segment_offset = class->mrl_play_offset;
+ if (vcdinfo_get_lot(player->vcd)) {
+ for (n=0; n<player->num_LIDs; n++) {
+ uint16_t ofs = vcdinf_get_lot_offset(vcdinfo_get_lot(player->vcd), n);
+ if (ofs != PSD_OFS_DISABLED || player->show_rejected) {
+ memset(&mrl, 0, sizeof (mrl));
+ sprintf(mrl, "%s%s@P%u%s", MRL_PREFIX, vcd_device, n+1,
+ ofs == PSD_OFS_DISABLED ? "*" : "");
+ vcd_add_mrl_slot(class, mrl, 0, &i);
+ class->mrl_segment_offset++;
+ }
+ }
+ }
+
+ /* Record MRL's for segments */
+ {
+ segnum_t num_segments = player->num_segments;
+ for (n=0; n<num_segments; n++) {
+ vcdinfo_video_segment_type_t segtype
+ = vcdinfo_get_video_type(obj, n);
+ char c='S';
+ switch (segtype) {
+ {
+ case VCDINFO_FILES_VIDEO_NTSC_STILL:
+ case VCDINFO_FILES_VIDEO_NTSC_STILL2:
+ case VCDINFO_FILES_VIDEO_NTSC_MOTION:
+ c='s';
+ break;
+ case VCDINFO_FILES_VIDEO_PAL_STILL:
+ case VCDINFO_FILES_VIDEO_PAL_STILL2:
+ case VCDINFO_FILES_VIDEO_PAL_MOTION:
+ c='S';
+ break;
+ default: ;
+ }
+ }
+
+ memset(&mrl, 0, sizeof (mrl));
+ sprintf(mrl, "%s%s@%c%u", MRL_PREFIX, vcd_device, c, n);
+ vcd_add_mrl_slot(class, mrl, player->segment[n].size, &i);
+ }
+ }
+
+ dbg_print(INPUT_DBG_MRL,
+ "offsets are track: %d, entry: %d, play: %d seg: %d\n",
+ class->mrl_track_offset, class->mrl_entry_offset,
+ class->mrl_play_offset, class->mrl_segment_offset);
+
+ return true;
+}
+
+/*!
+ parses a MRL which has the format
+
+ vcdx://[vcd_path][@[EPTS]?number]\*?
+
+ Examples
+ vcdx:// - Play (navigate) default device: /dev/cdrom
+ vcdx://@ - same as above
+ vcdx:///dev/cdrom - probably same as above
+ vcdx:///dev/cdrom2 - Play (navigate) /dev/cdrom2
+ vcdx:///dev/cdrom2@ - same as above
+ vcdx:///dev/cdrom2@T1 - Play Track 1 from /dev/cdrom2
+ vcdx:///dev/cdrom@S1 - Play selection id 1 from /dev/cdrom
+ vcdx://dev/cdrom@E0 - Play Entry id 0 from default device
+ vcdx://@P1 - probably same as above.
+ If there is no playback control, MRL will
+ get converted into vcdx://@E0
+ vcdx://@P1* - probably same as above.
+ vcdx://@S0 - Play segment 0 from default device
+ vcdx://@3 - Play track 3 from default device
+ vcdx:///dev/cdrom2@1 - Play track 1 from /dev/cdrom2
+ vcdx:///tmp/ntsc.bin@ - Play default item from /tmp/ntsc.bin
+ vcdx:///tmp/ntsc.bin/@E0 - Play entry 0 of /tmp/ntsc.bin
+
+parameters:
+ mrl : mrl to parse
+ default_vcd_device: name of device to use when none given
+ auto_type : type of selection (entry, track, LID) when none given
+ used_default : true iff auto_type was used.
+
+ */
+static bool
+vcd_parse_mrl(/*in*/ const char *default_vcd_device, /*in*/ char *mrl,
+ /*out*/ char *device_str, /*out*/ vcdinfo_itemid_t *itemid,
+ /*in */ vcdplayer_autoplay_t auto_type,
+ /*out*/ bool *used_default)
+{
+ char type_str[2];
+ int count;
+ char *p;
+ unsigned int num = 0;
+
+ dbg_print(INPUT_DBG_CALL, "called mrl %s\n", mrl);
+
+ type_str[0] ='\0';
+ itemid->type = (vcdinfo_item_enum_t) auto_type;
+ *used_default = false;
+
+ if ( NULL != mrl && !strncasecmp(mrl, MRL_PREFIX, MRL_PREFIX_LEN) )
+ p = &mrl[MRL_PREFIX_LEN];
+ else {
+ return false;
+ }
+
+ count = sscanf (p, "%[^@]@%1[EePpSsTt]%u",
+ device_str, type_str, &num);
+ itemid->num = num;
+
+ switch (count) {
+ case 1:
+ /* Matched device, but nothing beyond that */
+ if (strlen(device_str)!=0 && device_str[0] != ':') {
+ /* See if we have old-style MRL with no type specifier.
+ If so, we assume "track". */
+ count = sscanf (p, "%u", &num);
+ itemid->num = num;
+ if (1==count) {
+ type_str[0] = 'T';
+ }
+
+ break;
+ }
+
+ case 0:
+ case EOF:
+ {
+ /* No device/file given, so use the default device and try again. */
+ if (NULL == default_vcd_device) return false;
+ strcpy(device_str, default_vcd_device);
+ if (p[0] == '@') p++;
+ count = sscanf (p, "%1[EePpSsTt]%u", type_str, &num);
+ type_str[0] = toupper(type_str[0]);
+ itemid->num = num;
+
+ switch (count) {
+ case EOF:
+ /* Default PBC navigation. */
+ return true;
+ case 0:
+ /* See if we have old-style MRL with no type specifier.
+ If so, we assume "track". */
+ count = sscanf (p, "%u", &num);
+ if (1==count) {
+ type_str[0] = 'T';
+ break;
+ }
+ /* Default PBC navigation. */
+ return true;
+ case 1:
+ /* Type given, but no number. Entries start at 0, other things
+ start at 1 */
+ if (type_str[0] == 'P' || type_str[0] == 'T') itemid->num = 1;
+ }
+ }
+ }
+ /* We have some sort of track/selection/entry number */
+ switch (type_str[0]) {
+ case 'E':
+ itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
+ break;
+ case '\0':
+ /* None specified, use config value. */
+ itemid->type = (vcdinfo_item_enum_t) auto_type;
+ *used_default = true;
+ break;
+ case 'P':
+ itemid->type = VCDINFO_ITEM_TYPE_LID;
+ break;
+ case 'S':
+ itemid->type = VCDINFO_ITEM_TYPE_SEGMENT;
+ break;
+ case 'T':
+ itemid->type = VCDINFO_ITEM_TYPE_TRACK;
+ break;
+ default: ;
+ }
+
+ if ( 0==itemid->num
+ && ( (VCDINFO_ITEM_TYPE_LID == itemid->type)
+ || (VCDINFO_ITEM_TYPE_TRACK == itemid->type) ) )
+ itemid->num = 1;
+
+ return true;
+}
+
+/*!
+ From xine plugin spec:
+
+ return capabilities of input source
+*/
+static uint32_t
+vcd_plugin_get_capabilities (input_plugin_t *this_gen)
+{
+
+ uint32_t ret =
+ INPUT_CAP_AUDIOLANG | INPUT_CAP_BLOCK |
+ INPUT_CAP_CHAPTERS | INPUT_CAP_PREVIEW |
+ (my_vcd.player.in_still ? 0: INPUT_CAP_SEEKABLE) |
+ INPUT_CAP_SPULANG;
+
+ dbg_print((INPUT_DBG_CALL|INPUT_DBG_EXT), "returning %d\n", ret);
+ vcd_handle_events();
+ return ret;
+}
+
+# if FINISHED
+/* If needed, will fill out later... */
+static void
+vcd_read_ahead_cb(void *this_gen, xine_cfg_entry_t *entry)
+{
+ return;
+}
+#endif
+
+static void
+vcd_flush_buffers(void)
+{
+ xine_demux_flush_engine(my_vcd.stream);
+}
+
+/*!
+ From xine plugin spec:
+
+ read nlen bytes, return number of bytes read.
+*/
+static off_t
+vcd_plugin_read (input_plugin_t *this_gen, char *buf, const off_t nlen)
+{
+
+ dbg_print((INPUT_DBG_CALL|INPUT_DBG_EXT),
+ "Called with nlen %u\n", (unsigned int) nlen);
+
+ /* FIXME: Tricking the demux_mpeg_block plugin */
+ buf[0] = 0;
+ buf[1] = 0;
+ buf[2] = 0x01;
+ buf[3] = 0xba;
+ return (off_t) 1;
+}
+
+/*!
+ From xine plugin spec:
+
+ read one block, return newly allocated block (or NULL on failure)
+ for blocked input sources len must be == blocksize the fifo
+ parameter is only used to get access to the buffer_pool_alloc
+ function
+*/
+static buf_element_t *
+vcd_plugin_read_block (input_plugin_t *this_gen, fifo_buffer_t *fifo,
+ const off_t nlen)
+{
+ vcdplayer_input_t *this = &my_vcd.player;
+ buf_element_t *buf;
+ uint8_t data[M2F2_SECTOR_SIZE];
+
+ if (fifo == NULL) {
+ dbg_print(INPUT_DBG_CALL, "NULL fifo");
+ return NULL;
+ }
+
+ dbg_print(INPUT_DBG_CALL, "Called with nlen %u\n", (unsigned int) nlen);
+
+ /* Should we change this to <= instead of !=? */
+ if (nlen != M2F2_SECTOR_SIZE) return NULL;
+
+ switch (vcdplayer_read(this, data, nlen)) {
+ case READ_END:
+ /* End reached. Return NULL to indicated this. */
+ return NULL;
+ case READ_ERROR:
+ /* Some sort of error. */
+ return NULL;
+ case READ_STILL_FRAME:
+ {
+ dbg_print(INPUT_DBG_STILL, "Handled still event\n");
+ buf = fifo->buffer_pool_alloc (fifo);
+ buf->type = BUF_CONTROL_NOP;
+ break;
+ }
+
+ default:
+ case READ_BLOCK:
+ /* Read buffer */
+ buf = fifo->buffer_pool_alloc (fifo);
+ buf->type = BUF_DEMUX_BLOCK;
+ }
+
+ buf->content = buf->mem;
+
+ /* Ideally this should probably be nlen. */
+ memcpy (buf->mem, data, M2F2_SECTOR_SIZE);
+
+ return buf;
+}
+
+/*!
+ From xine plugin spec:
+
+ seek position, return new position
+
+ if seeking failed, -1 is returned
+*/
+static off_t
+vcd_plugin_seek (input_plugin_t *this_gen, off_t offset, int origin)
+{
+ return vcdio_seek (&my_vcd.player, offset, origin);
+}
+
+
+/*!
+ From xine plugin spec:
+ return length of input (-1 => unlimited, e.g. stream)
+
+ length size is bytes.
+*/
+
+static vcdinfo_itemid_t old_play_item = {VCDINFO_ITEM_TYPE_NOTFOUND, 0};
+
+static off_t old_get_length = 0;
+static vcdplayer_slider_length_t old_slider_length;
+
+/* This routine is called a bit. Make reasonably fast. */
+static off_t
+vcd_plugin_get_length (input_plugin_t *this_gen) {
+
+ vcd_input_plugin_t *ip= (vcd_input_plugin_t *) this_gen;
+ vcdplayer_input_t *this = &(ip->player);
+
+ int n = this->play_item.num;
+
+ if (this->play_item.num == old_play_item.num
+ && this->play_item.type == old_play_item.type
+ && this->slider_length == old_slider_length)
+ return old_get_length;
+
+ old_slider_length = this->slider_length;
+ old_play_item = this->play_item;
+
+ switch (this->play_item.type) {
+ case VCDINFO_ITEM_TYPE_ENTRY:
+ switch (this->slider_length) {
+ case VCDPLAYER_SLIDER_LENGTH_AUTO:
+ case VCDPLAYER_SLIDER_LENGTH_ENTRY:
+ n += ip->class->mrl_entry_offset;
+ break;
+ case VCDPLAYER_SLIDER_LENGTH_TRACK:
+ n = vcdinfo_get_track(this->vcd, n) + ip->class->mrl_track_offset;
+ break;
+ default:
+ /* FIXME? */
+ return -1;
+ }
+ break;
+ case VCDINFO_ITEM_TYPE_TRACK:
+ n += ip->class->mrl_track_offset;
+ break;
+ case VCDINFO_ITEM_TYPE_SEGMENT:
+ n += ip->class->mrl_segment_offset;
+ break;
+ case VCDINFO_ITEM_TYPE_LID:
+ /* This is the only situation where the size of the current play item
+ is not static. It depends what the current play-item is.
+ */
+ old_get_length = (this->end_lsn - this->origin_lsn) *
+ M2F2_SECTOR_SIZE;
+ return old_get_length;
+ break;
+ case VCDINFO_ITEM_TYPE_NOTFOUND:
+ case VCDINFO_ITEM_TYPE_SPAREID2:
+ default:
+ /* FIXME? */
+ return -1;
+ }
+
+ if (n >= 0 && n < ip->class->num_mrls) {
+ old_get_length = ip->class->mrls[n]->size;
+ dbg_print(INPUT_DBG_MRL, "item: %u, slot %u, size %ld\n",
+ this->play_item.num,
+ (unsigned int) n, (long int) old_get_length);
+ }
+ return old_get_length;
+}
+
+/*!
+ * From xine plugin spec:
+ * get current position in stream.
+ *
+ */
+static off_t
+vcd_plugin_get_current_pos (input_plugin_t *this_gen){
+ // trace_print("Called\n");
+ return (vcd_plugin_seek (this_gen, 0, SEEK_CUR));
+}
+
+
+/*!
+ * From xine plugin spec:
+ * return block size of input source (if supported, 0 otherwise)
+ */
+static uint32_t
+vcd_plugin_get_blocksize (input_plugin_t *this_gen) {
+ dbg_print((INPUT_DBG_CALL|INPUT_DBG_EXT), "called\n");
+
+ return M2F2_SECTOR_SIZE;
+}
+
+/*!
+ From xine plugin spec:
+ ls function
+ return value: NULL => filename is a file, **char=> filename is a dir
+
+-- This list returned forms the entries of the GUI MRL "browser".
+*/
+
+static xine_mrl_t **
+vcd_class_get_dir (input_class_t *this_gen, const char *filename,
+ int *num_files) {
+
+ char intended_vcd_device[1024]="";
+ vcdinfo_itemid_t itemid;
+
+ vcd_input_class_t *class = (vcd_input_class_t *) this_gen;
+ vcdplayer_input_t *player = &my_vcd.player;
+
+ bool used_default;
+
+ if (filename == NULL) {
+ dbg_print((INPUT_DBG_CALL|INPUT_DBG_EXT),
+ "called with NULL\n");
+ if ( class->mrls != NULL && NULL != class->mrls[0] ) goto have_mrls;
+
+ if ( !vcd_build_mrl_list(class, player->current_vcd_device) ) {
+ goto no_mrls;
+ }
+ } else {
+ char *mrl = strdup(filename);
+ dbg_print((INPUT_DBG_CALL|INPUT_DBG_EXT),
+ "called with %s\n", filename);
+ if (!vcd_get_default_device(class, true)) goto no_mrls;
+ if (!vcd_parse_mrl(class->vcd_device, mrl,
+ intended_vcd_device, &itemid,
+ player->default_autoplay, &used_default)) {
+ free (mrl);
+ goto no_mrls;
+ }
+ free (mrl);
+ }
+
+ have_mrls:
+ *num_files = class->num_mrls;
+ return class->mrls;
+
+ no_mrls:
+ *num_files = 0;
+ return NULL;
+}
+
+#define FREE_AND_NULL(ptr) if (NULL != ptr) free(ptr); ptr = NULL;
+
+static void
+vcd_close(vcd_input_class_t *class)
+{
+ xine_free_mrls(&(class->num_mrls), class->mrls);
+ FREE_AND_NULL(my_vcd.mrl);
+ vcdio_close(&my_vcd.player);
+}
+
+
+/*!
+ * From plugin xine spec:
+ * eject/load the media (if it's possible)
+ *
+ * returns 0 for temporary failures
+ */
+static int
+vcd_class_eject_media (input_class_t *this_gen)
+{
+ int ret;
+ CdIo *cdio=vcdinfo_get_cd_image(my_vcd.player.vcd);
+
+ dbg_print((INPUT_DBG_CALL|INPUT_DBG_EXT), "called\n");
+ if (NULL == cdio) return 0;
+
+ ret = cdio_eject_media(&cdio);
+ if ((ret == 0) || (ret == 2)) {
+ vcdio_close(&my_vcd.player);
+ return 1;
+ } else return 0;
+}
+
+/*!
+ * From spec:
+ * return current MRL
+ */
+static char *
+vcd_plugin_get_mrl (input_plugin_t *this_gen)
+{
+ vcd_input_plugin_t *t = (vcd_input_plugin_t *) this_gen;
+ vcdplayer_input_t *this = &my_vcd.player;
+ unsigned int n;
+ int size; /* need something to feed get_mrl_type_offset */
+ int offset;
+
+ if (vcdplayer_pbc_is_on(this)) {
+ n = this->cur_lid;
+ offset = vcd_get_mrl_type_offset(t, VCDINFO_ITEM_TYPE_LID, &size);
+ } else {
+ n = this->play_item.num;
+ offset = vcd_get_mrl_type_offset(t, this->play_item.type, &size);
+ }
+
+ if (-2 == offset) {
+ /* Bad type. */
+ LOG_ERR("%s %d", _("Invalid current entry type"),
+ this->play_item.type);
+ return strdup("");
+ } else {
+ n += offset;
+ if (n < t->class->num_mrls) {
+ dbg_print(INPUT_DBG_CALL, "Called, returning %s\n",
+ t->class->mrls[n]->mrl);
+ return t->class->mrls[n]->mrl;
+ } else {
+ return strdup("");
+ }
+ }
+}
+
+/*!
+ From xine plugin spec:
+
+ return human readable (verbose = 1 line) description for this plugin
+*/
+static char *
+vcd_class_get_description (input_class_t *this_gen)
+{
+ dbg_print((INPUT_DBG_CALL|INPUT_DBG_EXT), "called\n");
+ return _(strdup("Video CD plugin with PBC and support for: (X)VCD, (X)SVCD, HQVCD, CVD ... "));
+}
+
+/*!
+ From xine plugin spec:
+
+ return short, human readable identifier for this plugin
+ this is used for GUI buttons, The identifier must have max. 4 characters
+ characters (max. 5 including terminating \0)
+*/
+static char *
+vcd_class_get_identifier (input_class_t *this_gen) {
+ dbg_print((INPUT_DBG_CALL|INPUT_DBG_EXT), "called\n");
+ return strdup(SHORT_PLUGIN_NAME);
+}
+
+/*
+ Handle a keyboard/mouse event. Return TRUE if this causes a change
+ in the play item.
+*/
+
+static bool
+vcd_handle_events (void)
+{
+ vcdplayer_input_t *this = &my_vcd.player;
+ xine_event_t *event;
+ int digit_entered=0;
+
+ /* What you add to the last input number entry. It accumulates all of
+ the 10_ADD keypresses */
+ static unsigned int number_addend = 0;
+
+ while ((event = xine_event_get(my_vcd.event_queue))) {
+
+ dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EVENT),
+ "processing %d\n", event->type );
+ digit_entered=0;
+
+ switch(event->type) {
+
+ case XINE_EVENT_INPUT_NUMBER_10_ADD:
+ number_addend += 10;
+ dbg_print(INPUT_DBG_EVENT, "10 added to number. Is now: %d\n",
+ number_addend);
+ break;
+
+ /* The method used below is oblivious to XINE_EVENT_INPUT encodings
+ In particular, it does not assume XINE_EVENT_INPUT_NUMBE_9 =
+ XINE_EVENT_INPUT_NUMBER_0 + 9.
+ */
+ case XINE_EVENT_INPUT_NUMBER_9:
+ digit_entered++;
+ case XINE_EVENT_INPUT_NUMBER_8:
+ digit_entered++;
+ case XINE_EVENT_INPUT_NUMBER_7:
+ digit_entered++;
+ case XINE_EVENT_INPUT_NUMBER_6:
+ digit_entered++;
+ case XINE_EVENT_INPUT_NUMBER_5:
+ digit_entered++;
+ case XINE_EVENT_INPUT_NUMBER_4:
+ digit_entered++;
+ case XINE_EVENT_INPUT_NUMBER_3:
+ digit_entered++;
+ case XINE_EVENT_INPUT_NUMBER_2:
+ digit_entered++;
+ case XINE_EVENT_INPUT_NUMBER_1:
+ digit_entered++;
+ case XINE_EVENT_INPUT_NUMBER_0:
+ {
+ number_addend *= 10;
+ number_addend += digit_entered;
+ dbg_print(INPUT_DBG_EVENT,
+ "digit added number is now: %d\n", number_addend);
+ break;
+ }
+ case XINE_EVENT_INPUT_MENU3:
+ dbg_print(INPUT_DBG_EVENT, "menu3 setting debug: %d\n", number_addend);
+ vcdplayer_debug = number_addend;
+ number_addend = 0;
+ break;
+ case XINE_EVENT_INPUT_MENU1:
+ case XINE_EVENT_INPUT_MENU2:
+ case XINE_EVENT_INPUT_NEXT:
+ case XINE_EVENT_INPUT_PREVIOUS:
+ {
+ int num = number_addend;
+ vcdinfo_itemid_t itemid;
+
+ number_addend = 0;
+
+ /* If no number was given it's really the same as 1, not 0. */
+ if (num == 0) num++;
+
+ dbg_print(INPUT_DBG_EVENT,
+ "RETURN/NEXT/PREV/DEFAULT (%d) iteration count %d\n",
+ event->type, num);
+
+ for ( ; num > 0; num--) {
+ itemid = this->play_item;
+ switch (event->type) {
+ case XINE_EVENT_INPUT_MENU1:
+ if (this->return_entry == VCDINFO_INVALID_ENTRY) {
+ LOG_MSG("%s\n", _("selection has no return entry"));
+ return false;
+ }
+ itemid.num = this->return_entry;
+ dbg_print(INPUT_DBG_PBC, "RETURN to %d\n", itemid.num);
+ /* Don't loop around -- doesn't make sense to loop a return*/
+ num = 0;
+ break;
+ case XINE_EVENT_INPUT_MENU2:
+ if (this->default_entry == VCDINFO_INVALID_ENTRY) {
+ LOG_MSG("%s\n", _("selection has no default entry"));
+ return false;
+ }
+ itemid.num = this->default_entry;
+ dbg_print(INPUT_DBG_PBC, "DEFAULT to %d\n", itemid.num);
+ /* Don't loop around -- doesn't make sense to loop a return*/
+ num = 0;
+ break;
+ case XINE_EVENT_INPUT_NEXT:
+ if (this->next_entry == VCDINFO_INVALID_ENTRY) {
+ LOG_MSG("%s\n", _("selection has no next entry"));
+ return false;
+ }
+ itemid.num = this->next_entry;
+ dbg_print(INPUT_DBG_PBC, "NEXT to %d\n", itemid.num);
+ break;
+ case XINE_EVENT_INPUT_PREVIOUS:
+ if (this->prev_entry == VCDINFO_INVALID_ENTRY) {
+ LOG_MSG("%s\n", _("selection has no previous entry"));
+ return false;
+ }
+ itemid.num = this->prev_entry;
+ dbg_print(INPUT_DBG_PBC, "PREVIOUS to %d\n", itemid.num);
+ break;
+ default:
+ LOG_MSG("%s %d\n", _("Unknown event type: "), event->type);
+ }
+ xine_demux_flush_engine(my_vcd.stream);
+ vcdplayer_play(this, itemid);
+ return true;
+ }
+ break;
+ }
+ case XINE_EVENT_INPUT_SELECT:
+ {
+ /* In the future will have to test to see if we are in a menu
+ selection. But if not... */
+ vcdinfo_itemid_t itemid = this->play_item;
+
+ itemid.num = number_addend;
+ number_addend = 0;
+
+ if (vcdplayer_pbc_is_on(this)) {
+ lid_t next_num=vcdplayer_selection2lid(this, itemid.num);
+ if (VCDINFO_INVALID_LID != next_num) {
+ itemid.num = next_num;
+ xine_demux_flush_engine(my_vcd.stream);
+ vcdplayer_play(this, itemid);
+ return true;
+ }
+ }
+ break;
+ }
+ case XINE_EVENT_INPUT_MOUSE_BUTTON:
+ {
+ vcdplayer_send_button_update(this, 1);
+ }
+ break;
+ case XINE_EVENT_INPUT_BUTTON_FORCE:
+ break;
+ case XINE_EVENT_INPUT_MOUSE_MOVE:
+ {
+ vcdplayer_send_button_update(this, 0);
+ }
+ break;
+ case XINE_EVENT_INPUT_UP:
+ dbg_print(INPUT_DBG_EVENT, "Called with up\n");
+ vcdplayer_send_button_update(this, 0);
+ break;
+ case XINE_EVENT_INPUT_DOWN:
+ dbg_print(INPUT_DBG_EVENT, "Called with down\n");
+ vcdplayer_send_button_update(this, 0);
+ break;
+ case XINE_EVENT_INPUT_LEFT:
+ dbg_print(INPUT_DBG_EVENT, "Called with left\n");
+ vcdplayer_send_button_update(this, 0);
+ break;
+ case XINE_EVENT_INPUT_RIGHT:
+ dbg_print(INPUT_DBG_EVENT, "Called with right\n");
+ vcdplayer_send_button_update(this, 0);
+ break;
+ }
+ }
+ return false;
+}
+
+/*!
+ From xine plugin spec:
+
+ request optional data from input plugin.
+*/
+static int
+vcd_get_optional_data (input_plugin_t *this_gen,
+ void *data, int data_type) {
+
+ dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT),
+ "called with %d\n", data_type);
+
+ if (NULL == my_vcd.stream) return INPUT_OPTIONAL_UNSUPPORTED;
+
+ /* Fill this out more fully... */
+ switch(data_type) {
+
+ case INPUT_OPTIONAL_DATA_AUDIOLANG:
+ {
+ int8_t channel;
+ channel = (int8_t) xine_get_audio_channel(my_vcd.stream);
+
+ dbg_print(INPUT_DBG_EXT, "AUDIO CHANNEL = %d\n", channel);
+ if (-1 == channel) {
+ sprintf(data, " %s", "auto");
+ } else {
+ const vcdinfo_obj_t *obj= my_vcd.player.vcd;
+ unsigned int audio_type;
+ unsigned int num_channels;
+ unsigned int track_num = my_vcd.player.cur_track;
+ audio_type = vcdinfo_get_track_audio_type(obj, track_num);
+ num_channels = vcdinfo_audio_type_num_channels(obj, audio_type);
+
+ if (channel >= num_channels) {
+ sprintf(data, "%d ERR", channel);
+ } else {
+ sprintf(data, "%1d", channel);
+ }
+ }
+ return INPUT_OPTIONAL_SUCCESS;
+ }
+
+ case INPUT_OPTIONAL_DATA_SPULANG:
+ {
+ /*uint16_t lang;*/
+ int8_t channel;
+ channel = (int8_t) xine_get_spu_channel(my_vcd.stream);
+ dbg_print(INPUT_DBG_EXT, "SPU CHANNEL = %d\n", channel);
+ if (-1 == channel) {
+ sprintf(data, " %s", "auto");
+ } else {
+ sprintf(data, " %1d", channel);
+ }
+
+ }
+
+ default: ;
+ }
+
+ return INPUT_OPTIONAL_UNSUPPORTED;
+}
+
+/* Array to hold MRLs returned by get_autoplay_list */
+#define MAX_DIR_ENTRIES 250
+
+/*!
+ From xine plugin spec:
+
+ generate autoplay list
+ return value: list of MRLs
+
+-- The list of MRLs returned goes into the playlist.
+ This is called when the SHORT_PLUGIN_NAME button is pressed.
+*/
+
+static char **
+vcd_class_get_autoplay_list (input_class_t *this_gen, int *num_files)
+{
+ vcd_input_class_t *class = (vcd_input_class_t *) this_gen;
+ static char *filelist[MAX_DIR_ENTRIES];
+
+ dbg_print((INPUT_DBG_CALL|INPUT_DBG_EXT), "called\n");
+
+ if ( !vcd_build_mrl_list(class, my_vcd.player.current_vcd_device) ) {
+ *num_files = 0;
+ return NULL;
+ } else {
+ unsigned int i;
+ int size;
+ vcdinfo_item_enum_t itemtype =
+ autoplay2itemtype[my_vcd.player.default_autoplay];
+
+ int offset = vcd_get_mrl_type_offset(&my_vcd, itemtype, &size);
+
+ /* A VCD is not required to have PBC or LID's, default to entry if
+ this is the case...
+ */
+ if (VCDINFO_ITEM_TYPE_LID == itemtype && size==0) {
+ itemtype=VCDINFO_ITEM_TYPE_ENTRY;
+ offset = vcd_get_mrl_type_offset(&my_vcd, itemtype, &size);
+ }
+
+ /* This is because entries start at 0 while other playable units
+ start at 1. Can remove the below when everything has the same
+ origin.
+ */
+ if (VCDINFO_ITEM_TYPE_ENTRY != itemtype) offset++;
+
+ for (i=0; i<size; i++) {
+ if (class->mrls[offset+i] != NULL) {
+ filelist[i] = class->mrls[offset+i]->mrl;
+ dbg_print((INPUT_DBG_MRL), "filelist[%d]: %s\n", i, filelist[i]);
+ } else {
+ filelist[i] = NULL;
+ dbg_print((INPUT_DBG_MRL), "filelist[%d]: NULL\n", i);
+ }
+ }
+ *num_files = i;
+ return filelist;
+ }
+}
+
+/*!
+ Things that need to be done when a stream is closed.
+*/
+static void
+vcd_plugin_dispose(input_plugin_t *this_gen)
+{
+
+ /* Not sure there's much more to do here...
+ In open_plugin we are given a stream which
+ we save...
+ */
+ dbg_print((INPUT_DBG_CALL|INPUT_DBG_EXT), "called\n");
+
+#if 0
+ /*FIXME - this causes termination to in waits and adds sleeps. Why? */
+ free(my_vcd.stream->meta_info[XINE_META_INFO_ALBUM]);
+ free(my_vcd.stream->meta_info[XINE_META_INFO_ARTIST]);
+ free(my_vcd.stream->meta_info[XINE_META_INFO_COMMENT]);
+ free(my_vcd.stream->meta_info[XINE_META_INFO_GENRE]);
+ free(my_vcd.stream->meta_info[XINE_META_INFO_TITLE]);
+#endif
+ my_vcd.stream = NULL;
+
+#if 0
+ vcd_input_plugin_t *t= (vcd_input_plugin_t *) this_gen;
+ vcdplayer_input_t *this = t->v;
+
+ if (NULL==this) return;
+#endif
+}
+
+/* Pointer to vcdimager default log handler. Set by init_input_plugin
+ routine. Perhaps can remove. */
+static vcd_log_handler_t gl_default_vcd_log_handler = NULL;
+static cdio_log_handler_t gl_default_cdio_log_handler = NULL;
+
+/*! This routine is called by libvcd routines on error.
+ Setup is done by init_input_plugin.
+*/
+static void
+vcd_log_handler (vcd_log_level_t level, const char message[])
+{
+ switch (level) {
+ case VCD_LOG_DEBUG:
+ case VCD_LOG_INFO:
+ if (!(vcdplayer_debug & INPUT_DBG_VCDINFO))
+ return;
+ /* Fall through if to warn case */
+ case VCD_LOG_WARN:
+ LOG_MSG("%s", message);
+ break;
+ case VCD_LOG_ERROR:
+ case VCD_LOG_ASSERT:
+ LOG_ERR("%s", message);
+ break;
+ default:
+ LOG_ERR("%s\n%s %d",
+ message,
+ _("The above message had unknown vcdimager log level"),
+ level);
+ }
+
+ /* gl_default_vcd_log_handler (level, message); */
+}
+
+/*! This routine is called by libcdio routines on error.
+ Setup is done by init_input_plugin.
+*/
+static void
+cdio_log_handler (cdio_log_level_t level, const char message[])
+{
+ switch (level) {
+ case CDIO_LOG_DEBUG:
+ case CDIO_LOG_INFO:
+ if (!(vcdplayer_debug & INPUT_DBG_CDIO)) return;
+ /* Fall through if to warn case */
+ default:
+ vcd_log_handler (level, message);
+ }
+}
+
+/*! This routine is when xine is not around.
+ Setup is done by vcd_class_dispose.
+*/
+static void
+uninit_log_handler (vcd_log_level_t level, const char message[])
+{
+ switch (level) {
+ case VCD_LOG_DEBUG:
+ case VCD_LOG_INFO:
+ if (!(vcdplayer_debug & (INPUT_DBG_VCDINFO|INPUT_DBG_CDIO)))
+ return;
+ /* Fall through if to warn case */
+ case VCD_LOG_WARN:
+ fprintf(stderr, "WARN: %s\n", message);
+ break;
+ case VCD_LOG_ERROR:
+ fprintf(stderr, "ERROR: %s\n", message);
+ break;
+ case VCD_LOG_ASSERT:
+ fprintf(stderr, "ASSERT ERROR: %s\n", message);
+ break;
+ default:
+ fprintf(stderr, "UNKNOWN ERROR: %s\n%s %d",
+ message,
+ _("The above message had unknown vcdimager log level"),
+ level);
+ }
+
+ /* gl_default_vcd_log_handler (level, message); */
+}
+
+/*!
+ Things that need to be done the vcdx plugin is closed.
+*/
+static void
+vcd_class_dispose (input_class_t *this_gen) {
+ vcd_input_class_t *class = (vcd_input_class_t *) this_gen;
+
+ gl_default_vcd_log_handler = vcd_log_set_handler (uninit_log_handler);
+ gl_default_cdio_log_handler = cdio_log_set_handler (uninit_log_handler);
+
+ dbg_print((INPUT_DBG_CALL|INPUT_DBG_EXT), "called\n");
+
+ vcd_close(class);
+}
+
+/* Update the xine player title text. */
+static void
+vcd_update_title(void)
+{
+ xine_event_t uevent;
+ xine_ui_data_t data;
+
+ char *title_str;
+
+ title_str = vcdplayer_format_str(&my_vcd.player,
+ my_vcd.v_config.title_format);
+
+ meta_info_assign(XINE_META_INFO_TITLE, my_vcd.stream, title_str);
+ meta_info_assign(XINE_META_INFO_COMMENT, my_vcd.stream,
+ vcdplayer_format_str(&my_vcd.player,
+ my_vcd.v_config.comment_format));
+ stream_info_assign(XINE_STREAM_INFO_VIDEO_HAS_STILL, my_vcd.stream,
+ my_vcd.player.in_still);
+
+ /* Set_str title/chapter display */
+ dbg_print((INPUT_DBG_MRL|INPUT_DBG_CALL),
+ "Changing title to read '%s'\n", title_str);
+ uevent.type = XINE_EVENT_UI_SET_TITLE;
+ uevent.stream = my_vcd.stream;
+ uevent.data = &data;
+ uevent.data_length = sizeof(data);
+
+ memcpy(data.str, title_str, strlen(title_str) + 1);
+ data.str_len = strlen(title_str) + 1;
+
+ xine_event_send(my_vcd.stream, &uevent);
+}
+
+/*
+ No special initialization needed here. All of the initialization
+ is either done in the class or when we have an actual MRL we want
+ to deal with.
+*/
+static int
+vcd_plugin_open (input_plugin_t *this_gen ) {
+ vcd_input_class_t *class = (vcd_input_class_t *) this_gen->input_class;
+
+ gl_default_vcd_log_handler = vcd_log_set_handler (vcd_log_handler);
+ gl_default_cdio_log_handler = cdio_log_set_handler (cdio_log_handler);
+
+ /* actually, this is also done by class initialization. But just in
+ case... */
+ class->ip = &my_vcd;
+
+ return 1;
+}
+
+/*!
+ This basically sets up stream specified by MRL for playing. After this
+ routine is called, xine-lib can read blocks from the thing
+ specified by the MRL, set the position of the thing specified by the
+ MRL, get its size or read its current position...
+
+ See vcdplayer_parses_mrl for the for the format that a valid MRL can take.
+
+ Return values:
+ pointer to input plugin
+ NULL on failure
+
+*/
+static input_plugin_t *
+vcd_class_get_instance (input_class_t *class_gen, xine_stream_t *stream,
+ const char *mrl)
+{
+ vcd_input_class_t *class = (vcd_input_class_t *) class_gen;
+
+ char intended_vcd_device[1024]="";
+ vcdinfo_itemid_t itemid;
+ char *check_mrl=NULL;
+ bool used_default;
+
+ if (mrl == NULL)
+ check_mrl = strdup(MRL_PREFIX);
+ else
+ check_mrl = strdup(mrl);
+
+ dbg_print((INPUT_DBG_CALL|INPUT_DBG_EXT), "called with %s\n", mrl);
+
+ vcd_get_default_device(class, false);
+
+ if (!vcd_parse_mrl(class->vcd_device, check_mrl,
+ intended_vcd_device, &itemid,
+ my_vcd.player.default_autoplay, &used_default)) {
+ dbg_print(INPUT_DBG_MRL, "parsing MRL %s failed", check_mrl);
+ goto free_and_return;
+ }
+
+ free(my_vcd.mrl);
+ my_vcd.mrl = strdup(check_mrl);
+ my_vcd.stream = stream;
+ my_vcd.event_queue = xine_event_new_queue (stream);
+ class->ip = &my_vcd; /* Can probably get rid of this... */
+
+ if (!vcd_build_mrl_list(class, intended_vcd_device)) {
+ goto free_and_return;
+ }
+
+ my_vcd.player.user_data = (void *) class;
+
+ /* Do we set PBC (via LID) on? */
+ my_vcd.player.cur_lid =
+ ( VCDINFO_ITEM_TYPE_LID == itemid.type
+ && my_vcd.player.num_LIDs > itemid.num )
+ ? itemid.num
+ : VCDINFO_INVALID_ENTRY;
+
+ if ( VCDINFO_ITEM_TYPE_LID == itemid.type && used_default) {
+ /* LID was selected automatically but we don't have PBC for this VCD.
+ So silently change LID to track and continue.
+ */
+ itemid.type=VCDINFO_ITEM_TYPE_TRACK;
+ }
+
+ if ( 0==itemid.num
+ && ( (VCDINFO_ITEM_TYPE_LID == itemid.type)
+ || (VCDINFO_ITEM_TYPE_TRACK == itemid.type) ) )
+ itemid.num = 1;
+
+ dbg_print(INPUT_DBG_PBC, "Jumping to NUM >%i<, type >%i<\n",
+ itemid.num, itemid.type);
+
+ vcd_set_meta_info(&my_vcd);
+ vcdplayer_play(&my_vcd.player, itemid);
+
+
+ dbg_print(INPUT_DBG_MRL, "Successfully opened MRL %s.\n",
+ my_vcd.mrl);
+
+ free (check_mrl);
+ return &(my_vcd.input_plugin);
+
+ free_and_return:
+ free (check_mrl);
+ return NULL;
+}
+
+#define VCD_NUM_CALLBACK(fn_name, var) \
+ static void fn_name(void *this_gen, xine_cfg_entry_t *entry) \
+{ \
+\
+ dbg_print(INPUT_DBG_CALL, "Called setting %d\n", entry->num_value); \
+\
+ my_vcd.player.var = entry->num_value; \
+}
+
+#define VCD_ENUM_CALLBACK(fn_name, enum_type, var) \
+ static void fn_name(void *this_gen, xine_cfg_entry_t *entry) \
+{ \
+ dbg_print(INPUT_DBG_CALL, "Called setting %d\n", entry->num_value); \
+\
+ my_vcd.player.var = (enum_type) entry->num_value; \
+}
+
+#define VCD_STR_CALLBACK(fn_name, var) \
+ static void fn_name(void *this_gen, xine_cfg_entry_t *entry) \
+{ \
+\
+ dbg_print(INPUT_DBG_CALL, "Called setting %s\n", entry->str_value); \
+ if ( NULL == entry->str_value ) return; \
+\
+ free(var); \
+ var = strdup(entry->str_value); \
+}
+
+VCD_STR_CALLBACK(vcd_default_dev_changed_cb,
+ my_vcd.class->vcd_device)
+
+VCD_STR_CALLBACK(vcd_title_format_changed_cb,
+ my_vcd.v_config.title_format)
+
+VCD_STR_CALLBACK(vcd_comment_format_changed_cb,
+ my_vcd.v_config.comment_format)
+
+VCD_NUM_CALLBACK(vcd_show_rejected_cb, show_rejected)
+
+VCD_NUM_CALLBACK(vcd_autoadvance_cb, autoadvance)
+
+VCD_ENUM_CALLBACK(vcd_slider_length_cb, vcdplayer_slider_length_t,
+ slider_length)
+
+VCD_ENUM_CALLBACK(vcd_default_autoplay_cb, vcdinfo_item_enum_t,
+ default_autoplay)
+
+static void
+vcd_debug_cb(void *this_gen, xine_cfg_entry_t *entry)
+{
+ dbg_print(INPUT_DBG_CALL, "Called setting %d\n", entry->num_value);
+ vcdplayer_debug = entry->num_value;
+}
+
+static void *
+vcd_init (xine_t *xine, void *data)
+{
+ vcd_input_class_t *class;
+ config_values_t *config;
+
+ dbg_print(INPUT_DBG_CALL, "Called\n");
+
+ class = (vcd_input_class_t *) xine_xmalloc (sizeof (vcd_input_class_t));
+
+ class->xine = xine;
+ class->config = config = xine->config;
+
+ vcd_class = class;
+ class->mrls = NULL;
+
+ if (INPUT_PLUGIN_IFACE_VERSION >= 14) {
+ LOG_MSG("%s%d\n",
+ _("This plugin should be looked over to see if it is compatible with this input plugin API: "), INPUT_PLUGIN_IFACE_VERSION);
+ }
+
+
+#if INPUT_PLUGIN_IFACE_VERSION == 11
+ class->input_class.open_plugin = vcd_class_get_instance;
+#else
+ class->input_class.get_instance = vcd_class_get_instance;
+#endif
+
+ class->input_class.get_identifier = vcd_class_get_identifier;
+ class->input_class.get_description = vcd_class_get_description;
+ class->input_class.get_dir = vcd_class_get_dir;
+ class->input_class.get_autoplay_list = vcd_class_get_autoplay_list;
+ class->input_class.dispose = vcd_class_dispose;
+ class->input_class.eject_media = vcd_class_eject_media;
+
+ memset(&my_vcd, 0, sizeof(my_vcd));
+
+ /*------------------------------------------------------------------
+ Callback functions.
+ ---------------------------------------------------------------------*/
+ my_vcd.player.flush_buffers = &vcd_flush_buffers;
+ my_vcd.player.update_title = &vcd_update_title;
+ my_vcd.player.log_err = (generic_fn) &xine_log_err;
+ my_vcd.player.log_msg = (generic_fn) &xine_log_msg;
+ my_vcd.player.sleep = &xine_usec_sleep;
+ my_vcd.player.force_redisplay = &vcd_force_redisplay;
+ my_vcd.player.handle_events = &vcd_handle_events;
+
+ /*-------------------------------------------------------------
+ Playback control-specific fields
+ --------------------------------------------------------------*/
+
+ my_vcd.player.cur_lid = VCDINFO_INVALID_ENTRY;
+ my_vcd.player.end_lsn = VCDINFO_NULL_LSN;
+
+ my_vcd.player.pdi = -1;
+ my_vcd.player.pxd.psd = NULL;
+
+ /*-----------------------------------
+ Navigation fields
+ ------------------------------------*/
+ my_vcd.player.next_entry = -1;
+ my_vcd.player.prev_entry = -1;
+ my_vcd.player.return_entry = -1;
+ my_vcd.player.default_entry = -1;
+
+ /*--------------------------------------------------------------
+ Configuration variables
+ ---------------------------------------------------------------*/
+
+ {
+ /*Note: these labels have to be listed in the same order as the
+ enumeration vcdplayer_autoplay_t in vcdplayer.h.
+ */
+ static const char *autoplay_modes[] =
+ { "track", "entry", "segment", "playlist", NULL };
+
+ /*Note: these labels have to be listed in the same order as the
+ enumeration vcdplayer_slider_length_t in vcdplayer.h.
+ */
+ static const char *length_reporting_modes[] =
+ { "auto", "track", "entry", NULL };
+
+ my_vcd.player.default_autoplay =
+ config->register_enum(config,
+ "vcdx.autoplay",
+ VCDPLAYER_AUTOPLAY_PBC,
+ (char **) autoplay_modes,
+ _("default type to use on VCD autoplay"),
+_("What play unit to use when none is specified in an MRL, e.g. "
+ "vcdx:// or vcdx:///dev/dvd:"),
+ 0,
+ vcd_default_autoplay_cb, class);
+
+
+ class->vcd_device =
+ config->register_string(config,
+ "vcdx.default_device",
+ "",
+ _("default CD drive used for VCD when none given"),
+_("What to use if no drive specified. If null, we'll scan for CD drives."),
+ 20,
+ vcd_default_dev_changed_cb,
+ (void *) class);
+
+ my_vcd.player.slider_length =
+ config->register_enum(config,
+ "vcdx.length_reporting",
+ VCDPLAYER_SLIDER_LENGTH_AUTO,
+ (char **) length_reporting_modes,
+ _("position slider range"),
+_("The range the stream playback position slider represents when playing."),
+ 0,
+ vcd_slider_length_cb, NULL);
+
+#if READAHEAD_FINISHED
+ my_vcd.player.readahead =
+ config->register_bool(config, "vcdx.use_readahead",
+ (int) false,
+ _("Do we use read-ahead caching?"),
+ _("Class "
+ "may lead to jerky playback on low-end "
+ "machines."),
+ vcd_read_ahead_cb, NULL);
+#endif
+
+ my_vcd.player.autoadvance =
+ config->register_bool(config,
+ "vcdx.autoadvance",
+ (int) true,
+ _("Automatically advance track/entry?"),
+_("If set, we should we automatically advance to the next entry or track. Used only when playback control (PBC) is not on."),
+ 0,
+ vcd_autoadvance_cb,
+ NULL);
+
+ my_vcd.player.show_rejected =
+ config->register_bool(config,
+ "vcdx.show_rejected",
+ (int) false,
+ _("Show 'rejected' LIDs?"),
+"Some playback list IDs (LIDs) are marked not showable, "
+"but you can see then in the MRL list if this is set. Rejected entries "
+"are marked with an asterisk (*) appended in the MRL.",
+ 0,
+ vcd_show_rejected_cb,
+ NULL);
+
+ my_vcd.v_config.title_format =
+ strdup(config->register_string(config,
+ "vcdx.title_format",
+ "%F - %I %N%L%S, disk %c of %C - %v %A",
+ _("format string for display banner"),
+_("Format used in the GUI Title. Similar to the Unix date "
+"command. Format specifiers that start with a percent sign. Specifiers are "
+"%A, %C, %c, %F, %I, %L, %N, %P, %p, %S, %T, %V, %v, and %%."),
+ 0,
+ vcd_title_format_changed_cb,
+ NULL));
+
+ my_vcd.v_config.comment_format =
+ strdup(config->register_string(config,
+ "vcdx.comment_format",
+ "%P - Track %T",
+ _("format string for stream comment field"),
+_("Format used in the GUI Title. Similar to the Unix date "
+"command. Format specifiers that start with a percent sign. Specifiers are "
+"same as the title_format."),
+ 0,
+ vcd_comment_format_changed_cb,
+ NULL));
+
+ vcdplayer_debug =
+ config->register_num(config,
+ "vcdx.debug",
+ 0,
+ _("debug flag mask"),
+ _("This integer when viewed in binary is a debugging mask"),
+ 0,
+ vcd_debug_cb,
+ class);
+ }
+
+ gl_default_vcd_log_handler = vcd_log_set_handler (uninit_log_handler);
+ gl_default_cdio_log_handler = cdio_log_set_handler (uninit_log_handler);
+
+#if INPUT_PLUGIN_IFACE_VERSION != 11
+ my_vcd.input_plugin.open = vcd_plugin_open;
+#endif
+ my_vcd.input_plugin.get_capabilities = vcd_plugin_get_capabilities;
+ my_vcd.input_plugin.read = vcd_plugin_read;
+ my_vcd.input_plugin.read_block = vcd_plugin_read_block;
+ my_vcd.input_plugin.seek = vcd_plugin_seek;
+ my_vcd.input_plugin.get_current_pos = vcd_plugin_get_current_pos;
+ my_vcd.input_plugin.get_length = vcd_plugin_get_length;
+ my_vcd.input_plugin.get_blocksize = vcd_plugin_get_blocksize;
+ my_vcd.input_plugin.get_mrl = vcd_plugin_get_mrl;
+ my_vcd.input_plugin.get_optional_data = vcd_get_optional_data;
+ my_vcd.input_plugin.dispose = vcd_plugin_dispose;
+ my_vcd.input_plugin.input_class = (input_class_t *) class;
+
+ my_vcd.stream = NULL;
+ my_vcd.class = class;
+
+ my_vcd.player.buttonN = 0;
+ my_vcd.player.current_vcd_device = NULL;
+
+ my_vcd.player.opened = false;
+ my_vcd.player.play_item.num = VCDINFO_INVALID_ENTRY;
+ my_vcd.player.play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
+
+ return class;
+}
+
+/*
+ Exported plugin catalog entries.
+
+ All plugins listing only the current API number break when the API
+ number is increased. This is by design.
+
+ Sometimes in the rush to get out a buggy release, the API number is
+ increased without communication let alone a concern for whether it
+ is necessary or how many plugins it might break. And that is
+ precisely when what happened between API release 12 and API
+ 13. Input plugin API numbers 12 and 13 are functionally identical.
+
+ Because of problems like this, we'll just put in a future API
+ release. If the number was increased for a reason that doesn't
+ affect us (such as for nor reason at all), then this plugin will
+ work unmodified that future APIs. If on the other hand there was
+ incompatible change, we are no worse off than if we hadn't entered
+ the next API number since in both cases the plugin is broken.
+ */
+
+const plugin_info_t xine_plugin_info[] = {
+ /* type, API, "name", version, special_info, init_function */
+ { PLUGIN_INPUT, 11, (char *) SHORT_PLUGIN_NAME, XINE_VERSION_CODE,
+ NULL, vcd_init },
+ { PLUGIN_INPUT, 12, (char *) SHORT_PLUGIN_NAME, XINE_VERSION_CODE,
+ NULL, vcd_init },
+ { PLUGIN_INPUT, 13, (char *) SHORT_PLUGIN_NAME, XINE_VERSION_CODE,
+ NULL, vcd_init },
+ { PLUGIN_INPUT, 14, (char *) SHORT_PLUGIN_NAME, XINE_VERSION_CODE,
+ NULL, vcd_init },
+ { PLUGIN_NONE, 0, (char *) "", 0, NULL, NULL }
+};
+
+/*
+ * Local variables:
+ * c-file-style: "gnu"
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ */