From 1243c961ef9281e67814ccbb6d1075caf97d1a49 Mon Sep 17 00:00:00 2001 From: Daniel Caujolle-Bert Date: Thu, 6 Dec 2001 23:53:20 +0000 Subject: Add audio cd support. CVS patchset: 1172 CVS date: 2001/12/06 23:53:20 --- misc/xine-lib.spec.in | 6 + src/demuxers/Makefile.am | 5 +- src/demuxers/demux_cda.c | 303 ++++++++++++++++ src/input/Makefile.am | 6 +- src/input/input_cda.c | 919 +++++++++++++++++++++++++++++++++++++++++++++++ src/input/input_plugin.h | 25 +- 6 files changed, 1250 insertions(+), 14 deletions(-) create mode 100644 src/demuxers/demux_cda.c create mode 100644 src/input/input_cda.c diff --git a/misc/xine-lib.spec.in b/misc/xine-lib.spec.in index f6aafb155..5b2715eea 100644 --- a/misc/xine-lib.spec.in +++ b/misc/xine-lib.spec.in @@ -213,6 +213,8 @@ rm -rf $RPM_BUILD_ROOT %{prefix}/lib/xine/plugins/xineplug_inp_stdin_fifo.so %{prefix}/lib/xine/plugins/xineplug_inp_vcd.la %{prefix}/lib/xine/plugins/xineplug_inp_vcd.so +%{prefix}/lib/xine/plugins/xineplug_inp_cda.la +%{prefix}/lib/xine/plugins/xineplug_inp_cda.so # demuxer plugins %{prefix}/lib/xine/plugins/xineplug_dmx_asf.la %{prefix}/lib/xine/plugins/xineplug_dmx_asf.so @@ -232,6 +234,8 @@ rm -rf $RPM_BUILD_ROOT %{prefix}/lib/xine/plugins/xineplug_dmx_mpeg_ts.so @HAVE_ZLIB_TRUE@%{prefix}/lib/xine/plugins/xineplug_dmx_qt.la @HAVE_ZLIB_TRUE@%{prefix}/lib/xine/plugins/xineplug_dmx_qt.so +%{prefix}/lib/xine/plugins/xineplug_dmx_cda.la +%{prefix}/lib/xine/plugins/xineplug_dmx_cda.so # decoder plugins %{prefix}/lib/xine/plugins/xineplug_decode_a52.la %{prefix}/lib/xine/plugins/xineplug_decode_a52.so @@ -327,6 +331,8 @@ rm -rf $RPM_BUILD_ROOT %{prefix}/share/doc/xine/xine-lib-API/* %changelog +* Thu Dec 6 2001 Daniel Caujolle-Bert +- Add cda plugins. * Wed Nov 14 2001 Daniel Caujolle-Bert - fixed dxr3 header files inclusion, aalib deps: thanks to Andrew Meredith . * Mon Oct 29 2001 Matthias Dahl diff --git a/src/demuxers/Makefile.am b/src/demuxers/Makefile.am index 1dd074214..e1d311d88 100644 --- a/src/demuxers/Makefile.am +++ b/src/demuxers/Makefile.am @@ -22,7 +22,7 @@ lib_LTLIBRARIES = $(ogg_module) $(qt_modules) xineplug_dmx_avi.la \ xineplug_dmx_mpeg_block.la xineplug_dmx_mpeg.la \ xineplug_dmx_mpeg_elem.la xineplug_dmx_mpeg_audio.la \ xineplug_dmx_mpeg_pes.la xineplug_dmx_mpeg_ts.la \ - xineplug_dmx_asf.la + xineplug_dmx_asf.la xineplug_dmx_cda.la xineplug_dmx_ogg_la_SOURCES = demux_ogg.c xineplug_dmx_ogg_la_LIBADD = @OGG_LIBS@ @@ -57,6 +57,9 @@ xineplug_dmx_qt_la_LIBADD = -lz xineplug_dmx_asf_la_SOURCES = demux_asf.c xineplug_dmx_asf_la_LDFLAGS = -avoid-version -module +xineplug_dmx_cda_la_SOURCES = demux_cda.c +xineplug_dmx_cda_la_LDFLAGS = -avoid-version -module + include_HEADERS = demux.h ## diff --git a/src/demuxers/demux_cda.c b/src/demuxers/demux_cda.c new file mode 100644 index 000000000..df4de96be --- /dev/null +++ b/src/demuxers/demux_cda.c @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2000-2001 the xine project + * + * This file is part of xine, a unix 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 + * + * $Id: demux_cda.c,v 1.1 2001/12/06 23:53:20 f1rmb Exp $ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "xine_internal.h" +#include "xineutils.h" +#include "compat.h" +#include "demux.h" + +#define DEMUX_CDA_IFACE_VERSION 3 + +typedef struct { + + demux_plugin_t demux_plugin; + + config_values_t *config; + + fifo_buffer_t *video_fifo; + fifo_buffer_t *audio_fifo; + + input_plugin_t *input; + + pthread_t thread; + + off_t start; + + int status; + int send_end_buffers; + int blocksize; + +} demux_cda_t ; + +/* + * + */ +static int demux_cda_next (demux_cda_t *this) { + buf_element_t *buf; + int pos, len; + + buf = this->input->read_block(this->input, this->audio_fifo, this->blocksize); + + pos = this->input->get_current_pos(this->input); + len = this->input->get_length(this->input); + + buf->PTS = 0; + buf->SCR = 0; + buf->input_pos = this->input->get_current_pos(this->input); + buf->input_time = buf->input_pos / this->blocksize; + buf->type = BUF_AUDIO_MPEG; /* Fake */ + + if(this->audio_fifo) + this->audio_fifo->put(this->audio_fifo, buf); + + return ((pos < len)); +} + +/* + * + */ +static void *demux_cda_loop (void *this_gen) { + demux_cda_t *this = (demux_cda_t *) this_gen; + buf_element_t *buf; + + this->send_end_buffers = 1; + + this->input->seek(this->input, this->start, SEEK_SET); + + do { + + if (!demux_cda_next(this)) + this->status = DEMUX_FINISHED; + + } while (this->status == DEMUX_OK) ; + + this->status = DEMUX_FINISHED; + + if (this->send_end_buffers) { + buf = this->video_fifo->buffer_pool_alloc (this->video_fifo); + buf->type = BUF_CONTROL_END; + buf->decoder_info[0] = 0; /* stream finished */ + this->video_fifo->put (this->video_fifo, buf); + + if(this->audio_fifo) { + buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); + buf->type = BUF_CONTROL_END; + buf->decoder_info[0] = 0; /* stream finished */ + this->audio_fifo->put (this->audio_fifo, buf); + } + } + + pthread_exit(NULL); +} + +/* + * + */ +static void demux_cda_stop (demux_plugin_t *this_gen) { + demux_cda_t *this = (demux_cda_t *) this_gen; + buf_element_t *buf; + void *p; + + if (this->status != DEMUX_OK) { + printf ("demux_cda: stop...ignored\n"); + return; + } + + /* Force stop */ + this->input->stop(this->input); + + this->send_end_buffers = 0; + this->status = DEMUX_FINISHED; + + pthread_cancel(this->thread); + pthread_join(this->thread, &p); + + this->video_fifo->clear(this->video_fifo); + if (this->audio_fifo) + this->audio_fifo->clear(this->audio_fifo); + + buf = this->video_fifo->buffer_pool_alloc (this->video_fifo); + buf->type = BUF_CONTROL_END; + buf->decoder_info[0] = 1; /* forced */ + this->video_fifo->put (this->video_fifo, buf); + + if(this->audio_fifo) { + buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); + buf->type = BUF_CONTROL_END; + buf->decoder_info[0] = 1; /* forced */ + this->audio_fifo->put (this->audio_fifo, buf); + } + +} + +/* + * + */ +static int demux_cda_get_status (demux_plugin_t *this_gen) { + demux_cda_t *this = (demux_cda_t *) this_gen; + + return this->status; +} + +/* + * + */ +static void demux_cda_start (demux_plugin_t *this_gen, + fifo_buffer_t *video_fifo, + fifo_buffer_t *audio_fifo, + off_t start_pos, int start_time) { + demux_cda_t *this = (demux_cda_t *) this_gen; + buf_element_t *buf; + int err; + + this->video_fifo = video_fifo; + this->audio_fifo = audio_fifo; + + this->status = DEMUX_OK; + this->start = start_pos; + + this->blocksize = this->input->get_blocksize(this->input); + + buf = this->video_fifo->buffer_pool_alloc (this->video_fifo); + buf->type = BUF_CONTROL_START; + this->video_fifo->put (this->video_fifo, buf); + + if(this->audio_fifo) { + buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); + buf->type = BUF_CONTROL_START; + this->audio_fifo->put (this->audio_fifo, buf); + } + + /* + * now start demuxing + */ + + if ((err = pthread_create (&this->thread, + NULL, demux_cda_loop, this)) != 0) { + fprintf (stderr, "demux_cda: can't create new thread (%s)\n", strerror(err)); + exit(1); + } +} + +/* + * + */ +static int demux_cda_open(demux_plugin_t *this_gen, input_plugin_t *input, int stage) { + demux_cda_t *this = (demux_cda_t *) this_gen; + + switch(stage) { + + case STAGE_BY_CONTENT: + return DEMUX_CANNOT_HANDLE; + break; + + case STAGE_BY_EXTENSION: { + char *media; + char *MRL = input->get_mrl(input); + + media = strstr(MRL, "://"); + if(media) { + if(!strncasecmp(MRL, "cda", 3)) { + this->input = input; + return DEMUX_CAN_HANDLE; + } + } + } + break; + + default: + return DEMUX_CANNOT_HANDLE; + break; + } + + return DEMUX_CANNOT_HANDLE; +} + +/* + * + */ +static char *demux_cda_get_id(void) { + return "CDA"; +} + +/* + * + */ +static char *demux_cda_get_mimetypes(void) { + return "audio/cda: CD Audio"; +} + +/* + * + */ +static void demux_cda_close (demux_plugin_t *this) { + +} + +/* + * + */ +static int demux_cda_get_stream_length (demux_plugin_t *this_gen) { + + return 0; +} + +/* + * + */ +demux_plugin_t *init_demuxer_plugin(int iface, xine_t *xine) { + demux_cda_t *this; + + if (iface != 6) { + printf( "demux_cda: plugin doesn't support plugin API version %d.\n" + "demux_cda: this means there's a version mismatch between xine and this " + "demux_cda: demuxer plugin.\nInstalling current demux plugins should help.\n", + iface); + return NULL; + } + + this = (demux_cda_t *) xine_xmalloc(sizeof(demux_cda_t)); + this->config = xine->config; + + this->demux_plugin.interface_version = DEMUX_CDA_IFACE_VERSION; + this->demux_plugin.open = demux_cda_open; + this->demux_plugin.start = demux_cda_start; + this->demux_plugin.stop = demux_cda_stop; + this->demux_plugin.close = demux_cda_close; + this->demux_plugin.get_status = demux_cda_get_status; + this->demux_plugin.get_identifier = demux_cda_get_id; + this->demux_plugin.get_stream_length = demux_cda_get_stream_length; + this->demux_plugin.get_mimetypes = demux_cda_get_mimetypes; + + return &this->demux_plugin; +} diff --git a/src/input/Makefile.am b/src/input/Makefile.am index 5f478f398..752025027 100644 --- a/src/input/Makefile.am +++ b/src/input/Makefile.am @@ -17,9 +17,10 @@ libdir = $(XINE_PLUGINDIR) if HAVE_CDROM_IOCTLS in_dvd = xineplug_inp_dvd.la in_vcd = xineplug_inp_vcd.la +in_cda = xineplug_inp_cda.la endif -lib_LTLIBRARIES = xineplug_inp_file.la $(in_dvd) $(in_vcd) \ +lib_LTLIBRARIES = xineplug_inp_file.la $(in_dvd) $(in_vcd) $(in_cda) \ xineplug_inp_stdin_fifo.la xineplug_inp_net.la \ xineplug_inp_rtp.la xineplug_inp_http.la @@ -44,6 +45,9 @@ xineplug_inp_rtp_la_LDFLAGS = -avoid-version -module xineplug_inp_http_la_SOURCES = input_http.c xineplug_inp_http_la_LDFLAGS = -avoid-version -module +xineplug_inp_cda_la_SOURCES = input_cda.c +xineplug_inp_cda_la_LDFLAGS = -avoid-version -module + include_HEADERS = input_plugin.h noinst_HEADERS = dvd_udf.h read_cache.h diff --git a/src/input/input_cda.c b/src/input/input_cda.c new file mode 100644 index 000000000..4dd7914bd --- /dev/null +++ b/src/input/input_cda.c @@ -0,0 +1,919 @@ +/* + * Copyright (C) 2000-2001 the xine project + * + * This file is part of xine, a unix 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 + * + * $Id: input_cda.c,v 1.1 2001/12/06 23:53:20 f1rmb Exp $ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LINUX_CDROM_H +# include +#endif +#ifdef HAVE_SYS_CDIO_H +# include +/* TODO: not clean yet */ +# if defined (__FreeBSD__) +# include +# endif +#endif +#if ! defined (HAVE_LINUX_CDROM_H) && ! defined (HAVE_SYS_CDIO_H) +#error "you need to add cdrom / CDA support for your platform to input_cda and configure.in" +#endif + +#include "xine_internal.h" +#include "xineutils.h" +#include "input_plugin.h" + +/* +#define DEBUG_DISC +#define DEBUG_POS +*/ + +#define CDROM "/dev/cdaudio" + +/* for type */ +#define CDAUDIO 1 +#define CDDATA 2 + +/* for status */ +#define CDA_NO 1 +#define CDA_PLAY 2 +#define CDA_PAUSE 3 +#define CDA_STOP 4 +#define CDA_EJECT 5 +#define CDA_COMPLETE 6 + +#define CDA_BLOCKSIZE 75 + +typedef struct { + int type; + int length; + int start; + int track; +} trackinfo_t; + +typedef struct { + int fd; + char *device_name; + int cur_track; + int cur_pos; + int status; + int num_tracks; + int first_track; + int length; + trackinfo_t *track; +} cdainfo_t; + +typedef struct { + + input_plugin_t input_plugin; + + config_values_t *config; + xine_t *xine; + + uint32_t speed; + + char *mrl; + + cdainfo_t *cda; + + char *filelist[100]; + int mrls_allocated_entries; + mrl_t **mrls; + +} cda_input_plugin_t; + +/* Func proto */ +static void _cda_stop_cd(cdainfo_t *); + +/* + * ******************************** PRIVATES *************************************** + */ + +/* + * Get CDA status (pos, cur track, status) + * This function was grabbed and adapted from workbone (thanks to this team). + */ +static int _cda_get_status_cd(cdainfo_t *cda) { + struct cdrom_subchnl sc; + int cur_pos_abs = 0; + int cur_frame = 0; + int cur_track; + int cur_ntracks; + int cur_cdlen; + int cur_index; + int cur_pos_rel = 0; + int cur_tracklen; + + if(cda == NULL || cda->fd < 0) + return 0; + + cur_track = cda->cur_track; + cur_ntracks = cda->num_tracks; + cur_cdlen = cda->length; + + sc.cdsc_format = CDROM_MSF; + + if(ioctl(cda->fd, CDROMSUBCHNL, &sc)) { + fprintf(stderr, "input_cda: ioctl(CDROMSUBCHNL) failed: %s.\n", strerror(errno)); + return 0; + } + + switch (sc.cdsc_audiostatus) { + case CDROM_AUDIO_PLAY: + cda->status = CDA_PLAY; + + __get_pos: + + cur_pos_abs = sc.cdsc_absaddr.msf.minute * 60 + sc.cdsc_absaddr.msf.second; + cur_frame = cur_pos_abs * 75 + sc.cdsc_absaddr.msf.frame; + + if(cur_track < 1 + || cur_frame < cda->track[cur_track-1].start + || cur_frame >= (cur_track >= cur_ntracks ? + (cur_cdlen + 1) * 75 : + cda->track[cur_track].start)) { + cur_track = 0; + + while (cur_track < cur_ntracks && cur_frame >= cda->track[cur_track].start) + cur_track++; + } + + if(cur_track >= 1 && sc.cdsc_trk > cda->track[cur_track-1].track) + cur_track++; + + cur_index = sc.cdsc_ind; + + __get_posrel: + + if(cur_track >= 1 && cur_track <= cur_ntracks) { + cur_pos_rel = (cur_frame - cda->track[cur_track-1].start) / 75; + if(cur_pos_rel < 0) + cur_pos_rel = -cur_pos_rel; + } + + if(cur_pos_abs < 0) + cur_pos_abs = cur_frame = 0; + + if(cur_track < 1) + cur_tracklen = cda->length; + else + cur_tracklen = cda->track[cur_track-1].length; + break; + + case CDROM_AUDIO_PAUSED: + cda->status = CDA_PAUSE; + goto __get_pos; + break; + + case CDROM_AUDIO_COMPLETED: + cda->status = CDA_COMPLETE; + break; + + case CDROM_AUDIO_NO_STATUS: + cda->status = CDA_STOP; + goto __get_posrel; + } + + if(cur_track == cda->cur_track) + cda->cur_pos = cur_pos_rel; + else { + if(cda->status == CDA_PLAY) { + _cda_stop_cd(cda); + cda->status = CDA_STOP; + cda->cur_pos = cda->track[cda->cur_track - 1].length; + } + } + +#ifdef DEBUG_DISC + printf("Current Track = %d\n", cda->cur_track); + printf("Current pos in track = %d (%02d:%02d:%02d)\n", cda->cur_pos, + (cda->cur_pos / (60 * 60)), + ((cda->cur_pos / 60) % 60), + (cda->cur_pos %60)); + printf("Current status: "); + switch(cda->status) { + case CDA_NO: + printf("NO CD\n"); + break; + case CDA_PLAY: + printf("PLAY\n"); + break; + case CDA_PAUSE: + printf("PAUSE\n"); + break; + case CDA_STOP: + printf("STOP\n"); + break; + case CDA_EJECT: + printf("EJECT\n"); + break; + case CDA_COMPLETE: + printf("COMPLETE\n"); + break; + } +#endif + return 1; +} + +/* + * Play a time chunk (in secs); + */ +static int _cda_play_chunk_cd(cdainfo_t *cda, int start, int end) { + struct cdrom_msf msf; + + if(cda == NULL || cda->fd < 0) + return 0; + + end--; + + if(start >= end) + start = end - 1; + + msf.cdmsf_min0 = start / (60*75); + msf.cdmsf_sec0 = (start % (60*75)) / 75; + msf.cdmsf_frame0 = start % 75; + msf.cdmsf_min1 = end / (60*75); + msf.cdmsf_sec1 = (end % (60*75)) / 75; + msf.cdmsf_frame1 = end % 75; + + if (ioctl(cda->fd, CDROMSTART)) { + fprintf(stderr, "input_cda: ioctl(CDROMSTART) failed: %s.\n", strerror(errno)); + return 0; + } + + if(ioctl(cda->fd, CDROMPLAYMSF, &msf)) { + fprintf(stderr, "input_cda: ioctl(CDROMPLAYMSF) failed: %s.\n", strerror(errno)); + return 0; + } + + return 1; +} + +/* + * Open audio cd device FD. + */ +static int _cda_open_cd(cdainfo_t *cda) { + + if(cda == NULL) + return 0; + + if((cda->fd = open(cda->device_name, 0)) < 0) { + + if(errno == EACCES) { + fprintf(stderr, "input_cda: No rights to open %s.\n", cda->device_name); + } + else if(errno != ENXIO) { + fprintf(stderr, "input_cda: open() failed: %s.\n", strerror(errno)); + } + + return 0; + } + + return 1; +} + +/* + * Close opened audio cd fd. + */ +static void _cda_close_cd(cdainfo_t *cda) { + + if(cda == NULL) + return; + + if(cda->fd >= 0) { + close(cda->fd); + cda->fd = -1; + } +} + +/* + * Stop audio cd. + */ +static void _cda_stop_cd(cdainfo_t *cda) { + + if (cda->fd < 0) + return; + + if(cda->status != CDA_STOP) { + ioctl(cda->fd, CDROMSTOP); + _cda_get_status_cd(cda); + } +} + +/* + * Pause audio cd. + */ +static void _cda_pause_cd(cdainfo_t *cda) { + + if (cda->fd < 0) + return; + + if(cda->status == CDA_PLAY) { + ioctl(cda->fd, CDROMPAUSE); + _cda_get_status_cd(cda); + } +} + +/* + * Resume audio cd. + */ +static void _cda_resume_cd(cdainfo_t *cda) { + + if (cda->fd < 0) + return; + + if(cda->status == CDA_PAUSE) { + ioctl(cda->fd, CDROMRESUME); + _cda_get_status_cd(cda); + } +} + +/* + * Eject audio cd. + */ +static int _cda_eject_cd(cdainfo_t *cda) { + int err, status; + + if(cda->fd < 0) + _cda_open_cd(cda); + +#if defined (__linux__) + if((status = ioctl(cda->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT)) > 0) { + switch(status) { + case CDS_TRAY_OPEN: + if((err = ioctl(cda->fd, CDROMCLOSETRAY)) != 0) { + fprintf(stderr, "input_cda: ioctl(CDROMCLOSETRAY) failed: %s\n", strerror(errno)); + } + break; + case CDS_DISC_OK: + if((err = ioctl(cda->fd, CDROMEJECT)) != 0) { + fprintf(stderr, "input_cda: ioctl(CDROMEJECT) failed: %s\n", strerror(errno)); + } + break; + } + } + else { + fprintf(stderr, "input_cda: ioctl(CDROM_DRIVE_STATUS) failed: %s\n", strerror(errno)); + _cda_close_cd(cda); + return 0; + } +#elif defined (__FreeBSD__) + if(ioctl(cda->fd, CDIOCALLOW) == -1) { + fprintf(stderr, "input_cda: ioctl(CDROMALLOW) failed: %s\n", strerror(errno)); + } + else { + if(ioctl(cda->fd, CDIOCEJECT) == -1) { + fprintf(stderr, "input_cda: ioctl(CDROMEJECT) failed: %s\n", strerror(errno)); + } + } +#elif defined (__sun) + if((err = ioctl(cda->fd, CDROMEJECT)) != 0) { + fprintf(stderr, "input_cda: ioctl(CDROMEJECT) failed: %s\n", strerror(errno)); + } +#endif + + _cda_close_cd(cda); + + return 1; +} + +/* + * Read cd table of content. + */ +static int _cda_read_toc_cd(cdainfo_t *cda) { + struct cdrom_tochdr hdr; + struct cdrom_tocentry entry; + int i, pos; + + if(ioctl(cda->fd, CDROMREADTOCHDR, &hdr)) { + fprintf(stderr, "input_cda: ioctl(CDROMREADTOCHDR) failed: %s.\n", strerror(errno)); + return 0; + } + + cda->first_track = hdr.cdth_trk0; + cda->num_tracks = hdr.cdth_trk1; + + if(cda->track) + cda->track = (trackinfo_t *) realloc(cda->track, (cda->num_tracks + 1) * sizeof(trackinfo_t)); + else + cda->track = (trackinfo_t *) malloc((cda->num_tracks + 1) * sizeof(trackinfo_t)); + + for(i = 0; i <= cda->num_tracks; i++) { + if(i == cda->num_tracks) + entry.cdte_track = CDROM_LEADOUT; + else + entry.cdte_track = i + 1; + + entry.cdte_format = CDROM_MSF; + + if(ioctl(cda->fd, CDROMREADTOCENTRY, &entry)) { + fprintf(stderr, "input_cda: ioctl(CDROMREADTOCENTRY) failed: %s.\n", strerror(errno)); + return 0; + } + cda->track[i].track = i + 1; + cda->track[i].type = (entry.cdte_ctrl & CDROM_DATA_TRACK) ? CDDATA : CDAUDIO; + cda->track[i].length = entry.cdte_addr.msf.minute * 60 + entry.cdte_addr.msf.second; + cda->track[i].start = cda->track[i].length * 75 + entry.cdte_addr.msf.frame; + } + + /* compute real track length */ + pos = cda->track[0].length; + for(i = 0; i < cda->num_tracks; i++) { + cda->track[i].length = cda->track[i+1].length - pos; + pos = cda->track[i+1].length; + if(cda->track[i].type == CDDATA) + cda->track[i].length = (cda->track[i + 1].start - cda->track[i].start) * 2; + } + + cda->length = cda->track[cda->num_tracks].length; + +#ifdef DEBUG_DISC + printf("Disc have %d track(s), first track is %d, length %d (%02d:%02d:%02d)\n", + cda->num_tracks, cda->first_track, + cda->length, (cda->length / (60 * 60)), ((cda->length / 60) % 60), (cda->length %60)); + for(i = 0; i < cda->num_tracks; i++) { + printf("Track %2d, %s type, length %3d seconds(%02d:%02d:%02d), start at %3d secs\n", + i, + ((cda->track[i].type == CDDATA)?"DATA":"AUDIO"), + cda->track[i].length, + (cda->track[i].length / (60 * 60)), + ((cda->track[i].length / 60) % 60), + (cda->track[i].length %60), + cda->track[i].start); + } +#endif + + return 1; +} + +/* + * + */ +static void _cda_play_track_to_track_from_pos(cdainfo_t *cda, + int start_track, int pos, int end_track) { + int start; + int end; + + if(cda == NULL || cda->fd < 0) + return; + + _cda_get_status_cd(cda); + + start = start_track - 1; + end = end_track - 1; + + if(start >= cda->num_tracks) + end = cda->length * 75; + else + end = cda->track[(end_track - 1)].start - 1; + + if(_cda_play_chunk_cd(cda, cda->track[start].start + (pos * 75), end)) + _cda_get_status_cd(cda); + +} + +/* + * Some frontends functions to _cda_play_track_to_track_from_pos() + */ +static void _cda_play_track_to_track(cdainfo_t *cda, int start_track, int end_track) { + _cda_play_track_to_track_from_pos(cda, start_track, 0, end_track); +} +static void _cda_play_track_from_pos(cdainfo_t *cda, int track, int pos) { + _cda_play_track_to_track_from_pos(cda, track, pos, track + 1); +} +static void _cda_play_track(cdainfo_t *cda, int track) { + _cda_play_track_to_track(cda, track, track + 1); +} + +/* + * + */ +static void _cda_free_cda(cdainfo_t *cda) { + + if(cda == NULL) + return; + + _cda_close_cd(cda); + + if(cda->device_name) + free(cda->device_name); + if(cda->track) + free(cda->track); + free(cda); +} + +/* + * *************************** END OF PRIVATES ************************************ + */ + +/* + * + */ +static int cda_plugin_open (input_plugin_t *this_gen, char *mrl) { + cda_input_plugin_t *this = (cda_input_plugin_t *) this_gen; + char *filename; + + this->mrl = mrl; + + if(strncasecmp (mrl, "cda://", 6)) + return 0; + + if(!_cda_open_cd(this->cda)) { + _cda_free_cda(this->cda); + return 0; + } + + if(!_cda_read_toc_cd(this->cda)) { + _cda_free_cda(this->cda); + return 0; + } + + filename = (char *) &mrl[6]; + + if(sscanf(filename, "%d", &this->cda->cur_track) != 1) { + fprintf(stderr, "input_cda: malformed MRL. Use cda://\n"); + _cda_free_cda(this->cda); + return 0; + } + + if((!this->cda->cur_track) || (this->cda->cur_track > this->cda->num_tracks)) { + fprintf(stderr, "input_cda: invalid track %d (valid range: 1 .. %d)\n", + this->cda->cur_track, this->cda->num_tracks - 1); + _cda_free_cda(this->cda); + return 0; + } + + return 1; +} + +/* + * + */ +static buf_element_t *cda_plugin_read_block (input_plugin_t *this_gen, + fifo_buffer_t *fifo, off_t nlen) { + cda_input_plugin_t *this = (cda_input_plugin_t *) this_gen; + buf_element_t *buf; + unsigned char buffer[nlen]; + + /* Check if speed has changed */ + if(this->xine->speed != this->speed) { + int old_status = this->cda->status; + this->speed = this->xine->speed; + if((this->speed == SPEED_PAUSE) && this->cda->status == CDA_PLAY) { + _cda_pause_cd(this->cda); + } + else { + if(old_status == CDA_PAUSE) { + _cda_resume_cd(this->cda); + } + } + } + + memset(&buffer, 'X', sizeof(buffer)); + + buf = fifo->buffer_pool_alloc(fifo); + buf->content = buf->mem; + buf->type = BUF_DEMUX_BLOCK; + memcpy(buf->mem, buffer, nlen); + + return buf; +} + +/* + * + */ +static off_t cda_plugin_read (input_plugin_t *this_gen, char *buf, off_t nlen) { + cda_input_plugin_t *this = (cda_input_plugin_t *) this_gen; + char *buffer[nlen]; + + _cda_get_status_cd(this->cda); + + /* Dummy */ + memset(&buffer, 'X', sizeof(buf)); + memcpy(buf, buffer, nlen); + + return nlen; +} + +/* + * + */ +static off_t cda_plugin_seek (input_plugin_t *this_gen, off_t offset, int origin) { + cda_input_plugin_t *this = (cda_input_plugin_t *) this_gen; + + switch (origin) { + case SEEK_SET: + _cda_play_track_from_pos(this->cda, this->cda->cur_track, (int) (offset/CDA_BLOCKSIZE)); + break; + + default: + fprintf (stderr, "input_cda: error seek to origin %d not implemented!\n", + origin); + return 0; + } + + return offset; +} + +/* + * Return current length; + */ +static off_t cda_plugin_get_length (input_plugin_t *this_gen) { + cda_input_plugin_t *this = (cda_input_plugin_t *) this_gen; + + return (this->cda->track[this->cda->cur_track-1].length * CDA_BLOCKSIZE); +} + +/* + * Return current pos. + */ +static off_t cda_plugin_get_current_pos (input_plugin_t *this_gen){ + cda_input_plugin_t *this = (cda_input_plugin_t *) this_gen; + + _cda_get_status_cd(this->cda); + +#ifdef DEBUG_POS + printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b(%02d:%02d:%02d) (%d)%02d", + (this->cda->cur_pos / (60 * 60)), + ((this->cda->cur_pos / 60) % 60), + (this->cda->cur_pos %60), + this->cda->cur_track-1, + this->cda->track[this->cda->cur_track-1].length); +#endif + + return (this->cda->cur_pos * CDA_BLOCKSIZE); +} + +/* + * Get plugin capabilities. + */ +static uint32_t cda_plugin_get_capabilities (input_plugin_t *this_gen) { + + return INPUT_CAP_SEEKABLE | INPUT_CAP_AUTOPLAY | INPUT_CAP_GET_DIR; +} + +/* + * Get (pseudo) blocksize. + */ +static uint32_t cda_plugin_get_blocksize (input_plugin_t *this_gen) { + + return CDA_BLOCKSIZE; +} + +/* + * Eject current media. + */ +static int cda_plugin_eject_media (input_plugin_t *this_gen) { + cda_input_plugin_t *this = (cda_input_plugin_t *) this_gen; + + return (_cda_eject_cd(this->cda)); +} + +/* + * Close plugin. + */ +static void cda_plugin_close(input_plugin_t *this_gen) { + cda_input_plugin_t *this = (cda_input_plugin_t *) this_gen; + + _cda_stop_cd(this->cda); +} + +/* + * Plugin stop. + */ +static void cda_plugin_stop (input_plugin_t *this_gen) { + cda_input_plugin_t *this = (cda_input_plugin_t *) this_gen; + + _cda_stop_cd(this->cda); + _cda_close_cd(this->cda); +} + +/* + * + */ +static char *cda_plugin_get_description (input_plugin_t *this_gen) { + return "cd audio plugin as shipped with xine"; +} + +/* + * + */ +static char *cda_plugin_get_identifier (input_plugin_t *this_gen) { + return "CDA"; +} + +/* + * Get dir. + */ +static mrl_t **cda_plugin_get_dir (input_plugin_t *this_gen, + char *filename, int *nEntries) { + cda_input_plugin_t *this = (cda_input_plugin_t *) this_gen; + int i; + + *nEntries = 0; + + if(filename) + return NULL; + + if(!_cda_open_cd(this->cda)) { + _cda_free_cda(this->cda); + return NULL; + } + + if(!_cda_read_toc_cd(this->cda)) { + _cda_free_cda(this->cda); + return NULL; + } + + _cda_close_cd(this->cda); + + if(!this->cda->num_tracks) + return NULL; + + *nEntries = this->cda->num_tracks; + + for(i=1; i <= this->cda->num_tracks; i++) { + char mrl[1024]; + + memset(&mrl, 0, sizeof (mrl)); + sprintf(mrl, "cda://%d",i); + + if((i-1) >= this->mrls_allocated_entries) { + ++this->mrls_allocated_entries; + /* note: 1 extra pointer for terminating NULL */ + this->mrls = realloc(this->mrls, (this->mrls_allocated_entries+1) * sizeof(mrl_t*)); + this->mrls[(i-1)] = (mrl_t *) xine_xmalloc(sizeof(mrl_t)); + } + else { + memset(this->mrls[(i-1)], 0, sizeof(mrl_t)); + } + + if(this->mrls[(i-1)]->mrl) { + this->mrls[(i-1)]->mrl = (char *) + realloc(this->mrls[(i-1)]->mrl, strlen(mrl) + 1); + } + else { + this->mrls[(i-1)]->mrl = (char *) xine_xmalloc(strlen(mrl) + 1); + } + + this->mrls[i-1]->origin = NULL; + sprintf(this->mrls[i-1]->mrl, "%s", mrl); + this->mrls[i-1]->link = NULL; + this->mrls[i-1]->type = (0 | mrl_cda); + this->mrls[i-1]->size = this->cda->track[i-1].length; + } + + /* + * Freeing exceeded mrls if exists. + */ + while(this->mrls_allocated_entries > *nEntries) { + MRL_ZERO(this->mrls[this->mrls_allocated_entries - 1]); + free(this->mrls[this->mrls_allocated_entries--]); + } + + this->mrls[*nEntries] = NULL; + + return this->mrls; +} + +/* + * Get autoplay. + */ +static char **cda_plugin_get_autoplay_list (input_plugin_t *this_gen, int *nFiles) { + cda_input_plugin_t *this = (cda_input_plugin_t *) this_gen; + int i; + + + *nFiles = 0; + + if(!_cda_open_cd(this->cda)) { + _cda_free_cda(this->cda); + return NULL; + } + + if(!_cda_read_toc_cd(this->cda)) { + _cda_free_cda(this->cda); + return NULL; + } + + _cda_close_cd(this->cda); + + if(!this->cda->num_tracks) + return NULL; + + *nFiles = this->cda->num_tracks; + + for(i = 1; i <= this->cda->num_tracks; i++) + sprintf (this->filelist[i-1], "cda://%d",i); + + this->filelist[i-1] = NULL; + + return this->filelist; +} + +/* + * Return current MRL. + */ +static char* cda_plugin_get_mrl (input_plugin_t *this_gen) { + cda_input_plugin_t *this = (cda_input_plugin_t *) this_gen; + + return this->mrl; +} + +/* + * Get optional data. + */ +static int cda_plugin_get_optional_data (input_plugin_t *this_gen, void *data, int data_type) { + + return INPUT_OPTIONAL_UNSUPPORTED; +} + +/* + * Initialize plugin. + */ +input_plugin_t *init_input_plugin (int iface, xine_t *xine) { + cda_input_plugin_t *this; + config_values_t *config; + int i; + + if (iface != 5) { + printf("cda input plugin doesn't support plugin API version %d.\n" + "PLUGIN DISABLED.\n" + "This means there's a version mismatch between xine and this input" + "plugin.\nInstalling current input plugins should help.\n", + iface); + return NULL; + } + + this = (cda_input_plugin_t *) xine_xmalloc(sizeof(cda_input_plugin_t)); + config = xine->config; + + for (i = 0; i < 100; i++) { + this->filelist[i] = (char *) xine_xmalloc (256); + } + + this->input_plugin.interface_version = INPUT_PLUGIN_IFACE_VERSION; + this->input_plugin.get_capabilities = cda_plugin_get_capabilities; + this->input_plugin.open = cda_plugin_open; + this->input_plugin.read = cda_plugin_read; + this->input_plugin.read_block = cda_plugin_read_block; + this->input_plugin.seek = cda_plugin_seek; + this->input_plugin.get_current_pos = cda_plugin_get_current_pos; + this->input_plugin.get_length = cda_plugin_get_length; + this->input_plugin.get_blocksize = cda_plugin_get_blocksize; + this->input_plugin.eject_media = cda_plugin_eject_media; + this->input_plugin.close = cda_plugin_close; + this->input_plugin.stop = cda_plugin_stop; + this->input_plugin.get_identifier = cda_plugin_get_identifier; + this->input_plugin.get_description = cda_plugin_get_description; + this->input_plugin.get_dir = cda_plugin_get_dir; + this->input_plugin.get_mrl = cda_plugin_get_mrl; + this->input_plugin.get_autoplay_list = cda_plugin_get_autoplay_list; + this->input_plugin.get_optional_data = cda_plugin_get_optional_data; + this->input_plugin.is_branch_possible = NULL; + + this->xine = xine; + this->config = config; + + this->mrl = NULL; + + this->cda = (cdainfo_t *) xine_xmalloc(sizeof(cdainfo_t)); + this->cda->cur_track = -1; + this->cda->cur_pos = -1; + + this->cda->device_name = strdup(config->register_string(config, "input.cda_device", CDROM, + "path to your local cd audio device file", + NULL, NULL, NULL)); + + this->mrls = (mrl_t **) xine_xmalloc(sizeof(mrl_t*)); + this->mrls_allocated_entries = 0; + + return (input_plugin_t *) this; +} diff --git a/src/input/input_plugin.h b/src/input/input_plugin.h index a820a6186..ea702126e 100644 --- a/src/input/input_plugin.h +++ b/src/input/input_plugin.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: input_plugin.h,v 1.18 2001/12/01 22:38:31 guenter Exp $ + * $Id: input_plugin.h,v 1.19 2001/12/06 23:53:20 f1rmb Exp $ */ #ifndef HAVE_INPUT_PLUGIN_H @@ -49,17 +49,18 @@ extern void *xmalloc(size_t); #define mrl_net (1 << 2) #define mrl_rtp (1 << 3) #define mrl_stdin (1 << 4) -#define mrl_file (1 << 5) -#define mrl_file_fifo (1 << 6) -#define mrl_file_chardev (1 << 7) -#define mrl_file_directory (1 << 8) -#define mrl_file_blockdev (1 << 9) -#define mrl_file_normal (1 << 10) -#define mrl_file_symlink (1 << 11) -#define mrl_file_sock (1 << 12) -#define mrl_file_exec (1 << 13) -#define mrl_file_backup (1 << 14) -#define mrl_file_hidden (1 << 15) +#define mrl_cda (1 << 5) +#define mrl_file (1 << 6) +#define mrl_file_fifo (1 << 7) +#define mrl_file_chardev (1 << 8) +#define mrl_file_directory (1 << 9) +#define mrl_file_blockdev (1 << 10) +#define mrl_file_normal (1 << 11) +#define mrl_file_symlink (1 << 12) +#define mrl_file_sock (1 << 13) +#define mrl_file_exec (1 << 14) +#define mrl_file_backup (1 << 15) +#define mrl_file_hidden (1 << 16) /* * Freeing/zeroing all of entries of given mrl. -- cgit v1.2.3