summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Melanson <mike@multimedia.cx>2002-08-12 00:14:58 +0000
committerMike Melanson <mike@multimedia.cx>2002-08-12 00:14:58 +0000
commitd85a0d15b414e12acebc11bd5a8d0834bd32351d (patch)
treea58988f2e3d9ef94a55ccf1322678cfb4e3f34a6
parentd7c39f75b28f98c3fd91120f1c68e5e298fbc58f (diff)
downloadxine-lib-d85a0d15b414e12acebc11bd5a8d0834bd32351d.tar.gz
xine-lib-d85a0d15b414e12acebc11bd5a8d0834bd32351d.tar.bz2
initial commit for VQA and VOC demuxers
CVS patchset: 2437 CVS date: 2002/08/12 00:14:58
-rw-r--r--src/demuxers/Makefile.am11
-rw-r--r--src/demuxers/demux_voc.c482
-rw-r--r--src/demuxers/demux_vqa.c491
3 files changed, 983 insertions, 1 deletions
diff --git a/src/demuxers/Makefile.am b/src/demuxers/Makefile.am
index ff627746d..4a87f1569 100644
--- a/src/demuxers/Makefile.am
+++ b/src/demuxers/Makefile.am
@@ -29,7 +29,8 @@ lib_LTLIBRARIES = $(ogg_module) $(qt_modules) $(asf_module) xineplug_dmx_avi.la
xineplug_dmx_cda.la xineplug_dmx_film.la \
xineplug_dmx_roq.la xineplug_dmx_fli.la \
xineplug_dmx_smjpeg.la xineplug_dmx_wav.la \
- xineplug_dmx_idcin.la xineplug_dmx_wc3movie.la
+ xineplug_dmx_idcin.la xineplug_dmx_wc3movie.la \
+ xineplug_dmx_vqa.la xineplug_dmx_voc.la
xineplug_dmx_ogg_la_SOURCES = demux_ogg.c
xineplug_dmx_ogg_la_LIBADD = $(OGG_LIBS) \
@@ -104,6 +105,14 @@ xineplug_dmx_wc3movie_la_SOURCES = demux_wc3movie.c
xineplug_dmx_wc3movie_la_LIBADD = $(top_builddir)/src/xine-engine/libxine.la
xineplug_dmx_wc3movie_la_LDFLAGS = -avoid-version -module
+xineplug_dmx_vqa_la_SOURCES = demux_vqa.c
+xineplug_dmx_vqa_la_LIBADD = $(top_builddir)/src/xine-engine/libxine.la
+xineplug_dmx_vqa_la_LDFLAGS = -avoid-version -module
+
+xineplug_dmx_voc_la_SOURCES = demux_voc.c
+xineplug_dmx_voc_la_LIBADD = $(top_builddir)/src/xine-engine/libxine.la
+xineplug_dmx_voc_la_LDFLAGS = -avoid-version -module
+
include_HEADERS = demux.h qtpalette.h
##
diff --git a/src/demuxers/demux_voc.c b/src/demuxers/demux_voc.c
new file mode 100644
index 000000000..6d052f5bf
--- /dev/null
+++ b/src/demuxers/demux_voc.c
@@ -0,0 +1,482 @@
+/*
+ * Copyright (C) 2001-2002 the xine project
+ *
+ * This file is part of xine, a free video player.
+ *
+ * xine is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * xine is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * Creative Voice File Demuxer by Mike Melanson (melanson@pcisys.net)
+ *
+ * $Id: demux_voc.c,v 1.1 2002/08/12 00:14:58 tmmm Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "xine_internal.h"
+#include "xineutils.h"
+#include "demux.h"
+#include "buffer.h"
+#include "bswap.h"
+
+#define LE_16(x) (le2me_16(*(uint16_t *)(x)))
+#define LE_32(x) (le2me_32(*(uint32_t *)(x)))
+
+#define VALID_ENDS "voc"
+#define PCM_BLOCK_ALIGN 1024
+#define VOC_HEADER_SIZE 0x1A
+#define VOC_SIGNATURE "Creative Voice File\x1A"
+#define BLOCK_PREAMBLE_SIZE 4
+
+typedef struct {
+
+ demux_plugin_t demux_plugin;
+
+ xine_t *xine;
+
+ config_values_t *config;
+
+ fifo_buffer_t *video_fifo;
+ fifo_buffer_t *audio_fifo;
+
+ input_plugin_t *input;
+
+ pthread_t thread;
+ int thread_running;
+ pthread_mutex_t mutex;
+ int send_end_buffers;
+ int status;
+
+ unsigned int audio_type;
+ unsigned int audio_sample_rate;
+ unsigned int audio_bits;
+ unsigned int audio_channels;
+
+ off_t data_start;
+ off_t data_size;
+ off_t data_end;
+
+ int seek_flag; /* this is set when a seek just occurred */
+} demux_voc_t;
+
+static void *demux_voc_loop (void *this_gen) {
+
+ demux_voc_t *this = (demux_voc_t *) this_gen;
+ buf_element_t *buf = NULL;
+ unsigned int remaining_sample_bytes;
+ off_t current_file_pos;
+ int64_t current_pts;
+
+ pthread_mutex_lock( &this->mutex );
+ this->seek_flag = 1;
+
+ /* do-while needed to seek after demux finished */
+ do {
+ /* main demuxer loop */
+ while (this->status == DEMUX_OK) {
+
+ /* someone may want to interrupt us */
+ pthread_mutex_unlock( &this->mutex );
+ pthread_mutex_lock( &this->mutex );
+
+ /* just load data chunks from wherever the stream happens to be
+ * pointing; issue a DEMUX_FINISHED status is EOF is reached */
+ remaining_sample_bytes = PCM_BLOCK_ALIGN;
+ current_file_pos = this->input->get_current_pos(this->input);
+
+ current_pts = current_file_pos;
+ current_pts -= this->data_start;
+ current_pts *= 90000;
+ current_pts /= this->audio_sample_rate;
+
+ if (this->seek_flag) {
+ xine_demux_control_newpts(this->xine, current_pts, 0);
+ this->seek_flag = 0;
+ }
+
+ while (remaining_sample_bytes) {
+ buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo);
+ buf->type = this->audio_type;
+ buf->input_pos = current_file_pos;
+ buf->input_length = this->data_end;
+ buf->input_time = current_pts / 90000;
+ buf->pts = current_pts;
+
+ if (remaining_sample_bytes > buf->max_size)
+ buf->size = buf->max_size;
+ else
+ buf->size = remaining_sample_bytes;
+ remaining_sample_bytes -= buf->size;
+
+ if (this->input->read(this->input, buf->content, buf->size) !=
+ buf->size) {
+ buf->free_buffer(buf);
+ this->status = DEMUX_FINISHED;
+ break;
+ }
+
+ if (!remaining_sample_bytes)
+ buf->decoder_flags |= BUF_FLAG_FRAME_END;
+
+ this->audio_fifo->put (this->audio_fifo, buf);
+ }
+ }
+
+ /* wait before sending end buffers: user might want to do a new seek */
+ while(this->send_end_buffers && this->audio_fifo->size(this->audio_fifo) &&
+ this->status != DEMUX_OK){
+ pthread_mutex_unlock( &this->mutex );
+ xine_usec_sleep(100000);
+ pthread_mutex_lock( &this->mutex );
+ }
+
+ } while (this->status == DEMUX_OK);
+
+ printf ("demux_wav: demux loop finished (status: %d)\n",
+ this->status);
+
+ /* seek back to the beginning of the data in preparation for another
+ * start */
+ this->input->seek(this->input, this->data_start, SEEK_SET);
+
+ this->status = DEMUX_FINISHED;
+
+ if (this->send_end_buffers) {
+ xine_demux_control_end(this->xine, BUF_FLAG_END_STREAM);
+ }
+
+ this->thread_running = 0;
+ pthread_mutex_unlock(&this->mutex);
+
+ return NULL;
+}
+
+static int demux_voc_open(demux_plugin_t *this_gen,
+ input_plugin_t *input, int stage) {
+
+ demux_voc_t *this = (demux_voc_t *) this_gen;
+ unsigned char header[VOC_HEADER_SIZE];
+
+ this->input = input;
+
+ switch(stage) {
+ case STAGE_BY_CONTENT: {
+ if ((input->get_capabilities(input) & INPUT_CAP_SEEKABLE) == 0)
+ return DEMUX_CANNOT_HANDLE;
+
+ input->seek(input, 0, SEEK_SET);
+ if (input->read(input, header, VOC_HEADER_SIZE) != VOC_HEADER_SIZE)
+ return DEMUX_CANNOT_HANDLE;
+
+ /* check the signature */
+ if (strncmp(header, VOC_SIGNATURE, strlen(VOC_SIGNATURE)) == 0)
+ return DEMUX_CAN_HANDLE;
+
+ return DEMUX_CANNOT_HANDLE;
+ }
+ break;
+
+ case STAGE_BY_EXTENSION: {
+ char *suffix;
+ char *MRL;
+ char *m, *valid_ends;
+
+ MRL = input->get_mrl (input);
+
+ suffix = strrchr(MRL, '.');
+
+ if(!suffix)
+ return DEMUX_CANNOT_HANDLE;
+
+ xine_strdupa(valid_ends, (this->config->register_string(this->config,
+ "mrl.ends_voc", VALID_ENDS,
+ _("valid mrls ending for voc demuxer"),
+ NULL, NULL, NULL))); while((m = xine_strsep(&valid_ends, ",")) != NULL) {
+
+ while(*m == ' ' || *m == '\t') m++;
+
+ if(!strcasecmp((suffix + 1), m)) {
+ this->input = input;
+ return DEMUX_CAN_HANDLE;
+ }
+ }
+ return DEMUX_CANNOT_HANDLE;
+ }
+ break;
+
+ default:
+ return DEMUX_CANNOT_HANDLE;
+ break;
+ }
+
+ return DEMUX_CANNOT_HANDLE;
+}
+
+static int demux_voc_start (demux_plugin_t *this_gen,
+ fifo_buffer_t *video_fifo,
+ fifo_buffer_t *audio_fifo,
+ off_t start_pos, int start_time) {
+
+ demux_voc_t *this = (demux_voc_t *) this_gen;
+ buf_element_t *buf;
+ int err;
+ unsigned char header[VOC_HEADER_SIZE];
+ unsigned char preamble[BLOCK_PREAMBLE_SIZE];
+ off_t first_block_offset;
+ signed char sample_rate_divisor;
+
+ pthread_mutex_lock(&this->mutex);
+
+ /* if thread is not running, initialize demuxer */
+ if (!this->thread_running) {
+ this->video_fifo = video_fifo;
+ this->audio_fifo = audio_fifo;
+
+ /* load the header */
+ this->input->seek(this->input, 0, SEEK_SET);
+ if (this->input->read(this->input, header, VOC_HEADER_SIZE) !=
+ VOC_HEADER_SIZE) {
+ this->status = DEMUX_FINISHED;
+ pthread_mutex_unlock(&this->mutex);
+ return DEMUX_FINISHED;
+ }
+
+ first_block_offset = LE_16(&header[0x14]);
+ this->input->seek(this->input, first_block_offset, SEEK_SET);
+
+ /* load the block preamble */
+ if (this->input->read(this->input, preamble, BLOCK_PREAMBLE_SIZE) !=
+ BLOCK_PREAMBLE_SIZE) {
+ this->status = DEMUX_FINISHED;
+ pthread_mutex_unlock(&this->mutex);
+ return DEMUX_FINISHED;
+ }
+
+ /* so far, this demuxer only cares about type 1 blocks */
+ if (preamble[0] != 1) {
+ xine_log(this->xine, XINE_LOG_FORMAT,
+ _("unknown VOC block type (0x%02X); please report to xine developers\n"),
+ preamble[0]);
+ this->status = DEMUX_FINISHED;
+ pthread_mutex_unlock(&this->mutex);
+ return DEMUX_FINISHED;
+ }
+
+ /* assemble 24-bit, little endian length */
+ this->data_size = preamble[1] | (preamble[2] << 8) | (preamble[3] << 16);
+
+ /* get the next 2 bytes (re-use preamble bytes) */
+ if (this->input->read(this->input, preamble, 2) != 2) {
+ this->status = DEMUX_FINISHED;
+ pthread_mutex_unlock(&this->mutex);
+ return DEMUX_FINISHED;
+ }
+
+ /* this app only knows how to deal with format 0 data (raw PCM) */
+ if (preamble[1] != 0) {
+ xine_log(this->xine, XINE_LOG_FORMAT,
+ _("unknown VOC compression type (0x%02X); please report to xine developers\n"),
+ preamble[1]);
+ this->status = DEMUX_FINISHED;
+ pthread_mutex_unlock(&this->mutex);
+ return DEMUX_FINISHED;
+ }
+
+ this->audio_type = BUF_AUDIO_LPCM_BE;
+ sample_rate_divisor = preamble[0];
+ this->audio_sample_rate = 256 - (1000000 / sample_rate_divisor);
+ this->data_start = this->input->get_current_pos(this->input);
+ this->data_end = this->data_start + this->data_size;
+ this->audio_bits = 8;
+ this->audio_channels = 1;
+
+ /* print vital stats */
+ xine_log(this->xine, XINE_LOG_FORMAT,
+ _("demux_voc: VOC format 0x%X audio, %d Hz\n"),
+ preamble[1], this->audio_sample_rate);
+
+ /* send start buffers */
+ xine_demux_control_start(this->xine);
+
+ /* send init info to decoders */
+ if (this->audio_fifo && this->audio_type) {
+ buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo);
+ buf->type = this->audio_type;
+ buf->decoder_flags = BUF_FLAG_HEADER;
+ buf->decoder_info[0] = 0;
+ buf->decoder_info[1] = this->audio_sample_rate;
+ buf->decoder_info[2] = this->audio_bits;
+ buf->decoder_info[3] = this->audio_channels;
+ buf->size = 0;
+ this->audio_fifo->put (this->audio_fifo, buf);
+ }
+
+ this->status = DEMUX_OK;
+ this->send_end_buffers = 1;
+ this->thread_running = 1;
+
+ if ((err = pthread_create (&this->thread, NULL, demux_voc_loop, this)) != 0) {
+ printf ("demux_voc: can't create new thread (%s)\n", strerror(err));
+ abort();
+ }
+ }
+
+ pthread_mutex_unlock(&this->mutex);
+
+ return DEMUX_OK;
+}
+
+static int demux_voc_seek (demux_plugin_t *this_gen,
+ off_t start_pos, int start_time) {
+
+ demux_voc_t *this = (demux_voc_t *) this_gen;
+ int status;
+ off_t data_offset;
+
+ pthread_mutex_lock(&this->mutex);
+
+ /* check the boundary offsets */
+ if (start_pos < this->data_start)
+ this->input->seek(this->input, this->data_start, SEEK_SET);
+ else if (start_pos >= this->data_end) {
+ this->status = DEMUX_FINISHED;
+ status = this->status;
+ pthread_mutex_unlock(&this->mutex);
+ return status;
+ } else {
+ /* This function must seek along the block alignment. Determine how
+ * far into the data the requested offset lies, divide the diff
+ * by the block alignment integer-wise, and multiply that by the
+ * block alignment to get the new aligned offset. */
+ data_offset = start_pos - this->data_start;
+ data_offset /= PCM_BLOCK_ALIGN;
+ data_offset *= PCM_BLOCK_ALIGN;
+ data_offset += this->data_start;
+
+ this->input->seek(this->input, data_offset, SEEK_SET);
+ }
+
+ this->seek_flag = 1;
+ status = this->status = DEMUX_OK;
+ xine_demux_flush_engine (this->xine);
+ pthread_mutex_unlock(&this->mutex);
+
+ return status;
+}
+
+static void demux_voc_stop (demux_plugin_t *this_gen) {
+
+ demux_voc_t *this = (demux_voc_t *) this_gen;
+ void *p;
+
+ pthread_mutex_lock( &this->mutex );
+
+ if (!this->thread_running) {
+ pthread_mutex_unlock( &this->mutex );
+ return;
+ }
+
+ /* seek back to the beginning of the data in preparation for another
+ * start */
+ this->input->seek(this->input, this->data_start, SEEK_SET);
+
+ this->send_end_buffers = 0;
+ this->status = DEMUX_FINISHED;
+
+ pthread_mutex_unlock( &this->mutex );
+ pthread_join (this->thread, &p);
+
+ xine_demux_flush_engine(this->xine);
+
+ xine_demux_control_end(this->xine, BUF_FLAG_END_USER);
+}
+
+static void demux_voc_close (demux_plugin_t *this_gen) {
+ demux_voc_t *this = (demux_voc_t *) this_gen;
+
+ pthread_mutex_destroy (&this->mutex);
+ free(this);
+}
+
+static int demux_voc_get_status (demux_plugin_t *this_gen) {
+ demux_voc_t *this = (demux_voc_t *) this_gen;
+
+ return this->status;
+}
+
+static char *demux_voc_get_id(void) {
+ return "VOC";
+}
+
+static char *demux_voc_get_mimetypes(void) {
+ return NULL;
+}
+
+/* return the approximate length in seconds */
+static int demux_voc_get_stream_length (demux_plugin_t *this_gen) {
+
+ demux_voc_t *this = (demux_voc_t *) this_gen;
+
+ return 0;
+}
+
+demux_plugin_t *init_demuxer_plugin(int iface, xine_t *xine) {
+
+ demux_voc_t *this;
+
+ if (iface != 10) {
+ printf (_("demux_voc: plugin doesn't support plugin API version %d.\n"
+ " this means there's a version mismatch between xine and this "
+ " demuxer plugin.\nInstalling current demux plugins should help.\n"),
+ iface);
+ return NULL;
+ }
+
+ this = xine_xmalloc (sizeof (demux_voc_t));
+ this->config = xine->config;
+ this->xine = xine;
+
+ (void*) this->config->register_string(this->config,
+ "mrl.ends_voc", VALID_ENDS,
+ _("valid mrls ending for voc demuxer"),
+ NULL, NULL, NULL);
+
+ this->demux_plugin.interface_version = DEMUXER_PLUGIN_IFACE_VERSION;
+ this->demux_plugin.open = demux_voc_open;
+ this->demux_plugin.start = demux_voc_start;
+ this->demux_plugin.seek = demux_voc_seek;
+ this->demux_plugin.stop = demux_voc_stop;
+ this->demux_plugin.close = demux_voc_close;
+ this->demux_plugin.get_status = demux_voc_get_status;
+ this->demux_plugin.get_identifier = demux_voc_get_id;
+ this->demux_plugin.get_stream_length = demux_voc_get_stream_length;
+ this->demux_plugin.get_mimetypes = demux_voc_get_mimetypes;
+
+ this->status = DEMUX_FINISHED;
+ pthread_mutex_init( &this->mutex, NULL );
+
+ return (demux_plugin_t *) this;
+}
+
diff --git a/src/demuxers/demux_vqa.c b/src/demuxers/demux_vqa.c
new file mode 100644
index 000000000..e173ee9b4
--- /dev/null
+++ b/src/demuxers/demux_vqa.c
@@ -0,0 +1,491 @@
+/*
+ * Copyright (C) 2000-2002 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
+ *
+ * VQA File Demuxer by Mike Melanson (melanson@pcisys.net)
+ * For more information regarding the VQA file format, visit:
+ * http://www.pcisys.net/~melanson/codecs/
+ *
+ * $Id: demux_vqa.c,v 1.1 2002/08/12 00:14:58 tmmm Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "xine_internal.h"
+#include "xineutils.h"
+#include "compat.h"
+#include "demux.h"
+#include "bswap.h"
+
+#define BE_16(x) (be2me_16(*(unsigned short *)(x)))
+#define BE_32(x) (be2me_32(*(unsigned int *)(x)))
+#define LE_16(x) (le2me_16(*(unsigned short *)(x)))
+#define LE_32(x) (le2me_32(*(unsigned int *)(x)))
+
+#define FOURCC_TAG( ch0, ch1, ch2, ch3 ) \
+ ( (long)(unsigned char)(ch3) | \
+ ( (long)(unsigned char)(ch2) << 8 ) | \
+ ( (long)(unsigned char)(ch1) << 16 ) | \
+ ( (long)(unsigned char)(ch0) << 24 ) )
+
+#define FORM_TAG FOURCC_TAG('F', 'O', 'R', 'M')
+#define WVQA_TAG FOURCC_TAG('W', 'Q', 'V', 'A')
+#define VQHD_TAG FOURCC_TAG('V', 'Q', 'H', 'D')
+#define FINF_TAG FOURCC_TAG('F', 'I', 'N', 'F')
+#define SND0_TAG FOURCC_TAG('S', 'N', 'D', '0')
+#define SND2_TAG FOURCC_TAG('S', 'N', 'D', '2')
+#define VQFR_TAG FOURCC_TAG('V', 'Q', 'F', 'R')
+
+#define VQA_HEADER_SIZE 0x2A
+#define VQA_FRAMERATE 15
+#define VQA_PTS_INC (90000 / VQA_FRAMERATE)
+
+#define VALID_ENDS "vqa"
+
+typedef struct {
+ off_t frame_offset;
+ unsigned int frame_size;
+ int64_t audio_pts;
+ int64_t video_pts;
+} vqa_frame_t;
+
+typedef struct {
+
+ demux_plugin_t demux_plugin;
+
+ xine_t *xine;
+
+ config_values_t *config;
+
+ fifo_buffer_t *video_fifo;
+ fifo_buffer_t *audio_fifo;
+
+ input_plugin_t *input;
+
+ pthread_t thread;
+ int thread_running;
+ pthread_mutex_t mutex;
+ int send_end_buffers;
+
+ off_t start;
+ int status;
+
+ unsigned int total_frames;
+ vqa_frame_t *frame_table;
+ unsigned int current_frame;
+ unsigned int last_frame;
+ int total_time;
+
+ unsigned int video_width;
+ unsigned int video_height;
+ unsigned int vector_width;
+ unsigned int vector_height;
+
+ unsigned int audio_sample_rate;
+ unsigned int audio_bits;
+ unsigned int audio_channels;
+} demux_vqa_t ;
+
+static void *demux_vqa_loop (void *this_gen) {
+
+ demux_vqa_t *this = (demux_vqa_t *) this_gen;
+ buf_element_t *buf = NULL;
+ unsigned int i;
+
+ pthread_mutex_lock( &this->mutex );
+
+ /* do-while needed to seek after demux finished */
+ do {
+ /* main demuxer loop */
+ while (this->status == DEMUX_OK) {
+
+ /* someone may want to interrupt us */
+ pthread_mutex_unlock( &this->mutex );
+ pthread_mutex_lock( &this->mutex );
+
+ i = this->current_frame;
+ /* if there is an incongruency between last and current sample, it
+ * must be time to send a new pts */
+ if (this->last_frame + 1 != this->current_frame)
+ xine_demux_control_newpts(this->xine,
+ this->frame_table[i].video_pts, BUF_FLAG_SEEK);
+
+ this->last_frame = this->current_frame;
+ this->current_frame++;
+
+ /* check if all the samples have been sent */
+ if (i >= this->total_frames) {
+ this->status = DEMUX_FINISHED;
+ break;
+ }
+
+ }
+
+ /* wait before sending end buffers: user might want to do a new seek */
+ while(this->send_end_buffers && this->video_fifo->size(this->video_fifo) &&
+ this->status != DEMUX_OK){
+ pthread_mutex_unlock( &this->mutex );
+ xine_usec_sleep(100000);
+ pthread_mutex_lock( &this->mutex );
+ }
+
+ } while (this->status == DEMUX_OK);
+
+ printf ("demux_vqa: demux loop finished (status: %d)\n",
+ this->status);
+
+ this->status = DEMUX_FINISHED;
+
+ if (this->send_end_buffers) {
+ xine_demux_control_end(this->xine, BUF_FLAG_END_STREAM);
+ }
+
+ this->thread_running = 0;
+ pthread_mutex_unlock(&this->mutex);
+
+ return NULL;
+}
+
+static int demux_vqa_open(demux_plugin_t *this_gen, input_plugin_t *input,
+ int stage) {
+ demux_vqa_t *this = (demux_vqa_t *) this_gen;
+ char header[12];
+
+ this->input = input;
+
+ switch(stage) {
+ case STAGE_BY_CONTENT: {
+ if ((input->get_capabilities(input) & INPUT_CAP_SEEKABLE) == 0)
+ return DEMUX_CANNOT_HANDLE;
+
+ input->seek(input, 0, SEEK_SET);
+ if (input->read(input, header, 12) != 12)
+ return DEMUX_CANNOT_HANDLE;
+
+ /* check for the VQA signatures */
+ if ((BE_32(&header[0]) == FORM_TAG) &&
+ (BE_32(&header[8]) == WVQA_TAG))
+ return DEMUX_CAN_HANDLE;
+
+ return DEMUX_CANNOT_HANDLE;
+ }
+ break;
+
+ case STAGE_BY_EXTENSION: {
+ char *suffix;
+ char *MRL;
+ char *m, *valid_ends;
+
+ MRL = input->get_mrl (input);
+
+ suffix = strrchr(MRL, '.');
+
+ if(!suffix)
+ return DEMUX_CANNOT_HANDLE;
+
+ xine_strdupa(valid_ends, (this->config->register_string(this->config,
+ "mrl.ends_vqa", VALID_ENDS,
+ _("valid mrls ending for vqa demuxer"),
+ NULL, NULL, NULL))); while((m = xine_strsep(&valid_ends, ",")) != NULL) {
+
+ while(*m == ' ' || *m == '\t') m++;
+
+ if(!strcasecmp((suffix + 1), m)) {
+ this->input = input;
+ return DEMUX_CAN_HANDLE;
+ }
+ }
+ return DEMUX_CANNOT_HANDLE;
+ }
+ break;
+
+ default:
+ return DEMUX_CANNOT_HANDLE;
+ break;
+
+ }
+
+ return DEMUX_CANNOT_HANDLE;
+}
+
+static int demux_vqa_start (demux_plugin_t *this_gen,
+ fifo_buffer_t *video_fifo,
+ fifo_buffer_t *audio_fifo,
+ off_t start_pos, int start_time) {
+
+ demux_vqa_t *this = (demux_vqa_t *) this_gen;
+ buf_element_t *buf;
+ int err;
+ unsigned char header[VQA_HEADER_SIZE];
+ unsigned char *finf_chunk;
+ int i;
+ off_t last_offset;
+ uint64_t audio_pts_counter = 0;
+ uint64_t video_pts_counter = 0;
+
+ pthread_mutex_lock(&this->mutex);
+
+ /* if thread is not running, initialize demuxer */
+ if (!this->thread_running) {
+ this->video_fifo = video_fifo;
+ this->audio_fifo = audio_fifo;
+
+ /* get the file size (a.k.a., last offset) as reported by the file */
+ this->input->seek(this->input, 4, SEEK_SET);
+ if (this->input->read(this->input, header, 4) != 4) {
+ this->status = DEMUX_FINISHED;
+ pthread_mutex_unlock(&this->mutex);
+ return DEMUX_FINISHED;
+ }
+ last_offset = BE_32(&header[0]);
+
+ /* skip to the VQA header */
+ this->input->seek(this->input, 20, SEEK_SET);
+ if (this->input->read(this->input, header, VQA_HEADER_SIZE)
+ != VQA_HEADER_SIZE) {
+ this->status = DEMUX_FINISHED;
+ pthread_mutex_unlock(&this->mutex);
+ return DEMUX_FINISHED;
+ }
+
+ /* fetch the interesting information */
+ this->total_frames = LE_16(&header[4]);
+ this->video_width = LE_16(&header[6]);
+ this->video_height = LE_16(&header[8]);
+ this->vector_width = header[10];
+ this->vector_height = header[11];
+ this->audio_sample_rate = LE_16(&header[24]);
+
+ /* fetch the chunk table */
+ this->input->seek(this->input, 8, SEEK_CUR); /* skip FINF and length */
+ finf_chunk = xine_xmalloc(this->total_frames * 4);
+ this->frame_table = xine_xmalloc(this->total_frames * sizeof(vqa_frame_t));
+ if (this->input->read(this->input, finf_chunk, this->total_frames * 4) !=
+ this->total_frames * 4) {
+ this->status = DEMUX_FINISHED;
+ pthread_mutex_unlock(&this->mutex);
+ return DEMUX_FINISHED;
+ }
+ for (i = 0; i < this->total_frames; i++) {
+ this->frame_table[i].frame_offset = (LE_32(&finf_chunk[i * 4]) * 2) &
+ 0x03FFFFFF;
+ if (i < this->total_frames - 1)
+ this->frame_table[i].frame_size = LE_32(&finf_chunk[(i + 1) * 4]) * 2 -
+ this->frame_table[i].frame_offset;
+ else
+ this->frame_table[i].frame_size = last_offset -
+ this->frame_table[i].frame_offset;
+
+ /*
+ * VQA files play at a constant rate of 15 frames/second. The file data
+ * begins with 1/15 sec of compressed audio followed by 1 video frame
+ * that will be displayed for 1/15 sec.
+ *
+ * xine pts frame #
+ * -------- = ------- => xine pts = 90000 * frame # / 15
+ * 90000 15
+ *
+ * Thus, each frame has a duration of 90000 / 15 (VQA_PTS_INC, in
+ * this code).
+ *
+ * If this is the first frame in the file, it contains 1/2 sec
+ * of audio and no video. Each successive frame represents 1/15 sec.
+ */
+ if (i == 0) {
+ this->frame_table[i].audio_pts = 0;
+ this->frame_table[i].video_pts = 0;
+ audio_pts_counter += (90000 / 2);
+ } else {
+ this->frame_table[i].audio_pts = audio_pts_counter;
+ this->frame_table[i].video_pts = video_pts_counter;
+ audio_pts_counter += VQA_PTS_INC;
+ video_pts_counter += VQA_PTS_INC;
+ }
+ }
+
+ this->total_time = this->frame_table[this->total_frames - 1].video_pts /
+ 90000;
+
+ /* print vital stats */
+ xine_log (this->xine, XINE_LOG_FORMAT,
+ _("demux_vqa: running time: %d min, %d sec\n"),
+ this->total_time / 60,
+ this->total_time % 60);
+ xine_log (this->xine, XINE_LOG_FORMAT,
+ _("demux_vqa: %dx%d VQA video, %d Hz IMA ADPCM audio\n"),
+ this->video_width,
+ this->video_height,
+ this->audio_sample_rate);
+
+ /* send start buffers */
+ xine_demux_control_start(this->xine);
+
+ /* send init info to decoders */
+ buf = this->video_fifo->buffer_pool_alloc (this->video_fifo);
+ buf->decoder_flags = BUF_FLAG_HEADER;
+ buf->decoder_info[0] = 0;
+ buf->decoder_info[1] = VQA_PTS_INC; /* initial video_step */
+ /* really be a rebel: No structure at all, just put the video width
+ * and height straight into the buffer, BE_16 format */
+ buf->content[0] = (this->video_width >> 8) & 0xFF;
+ buf->content[1] = (this->video_width >> 0) & 0xFF;
+ buf->content[2] = (this->video_height >> 8) & 0xFF;
+ buf->content[3] = (this->video_height >> 0) & 0xFF;
+ buf->size = 4;
+ buf->type = BUF_VIDEO_VQA;
+ this->video_fifo->put (this->video_fifo, buf);
+
+ /* send the vector size to the video decoder */
+ buf = this->video_fifo->buffer_pool_alloc (this->video_fifo);
+ buf->decoder_flags = BUF_FLAG_SPECIAL;
+ buf->decoder_info[1] = BUF_SPECIAL_VQA_VECTOR_SIZE;
+ buf->decoder_info[2] = this->vector_width;
+ buf->decoder_info[3] = this->vector_height;
+ buf->size = 0;
+ buf->type = BUF_VIDEO_VQA;
+ this->video_fifo->put (this->video_fifo, buf);
+
+ if (this->audio_fifo && this->audio_channels) {
+ buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo);
+ buf->type = BUF_AUDIO_VQA_IMA;
+ buf->decoder_flags = BUF_FLAG_HEADER;
+ buf->decoder_info[0] = 0;
+ buf->decoder_info[1] = this->audio_sample_rate;
+ buf->decoder_info[2] = 16; /* bits/samples */
+ buf->decoder_info[3] = 1; /* channels */
+ this->audio_fifo->put (this->audio_fifo, buf);
+ }
+
+ this->current_frame = 0;
+ this->last_frame = 0;
+
+ this->status = DEMUX_OK;
+ this->send_end_buffers = 1;
+ this->thread_running = 1;
+
+ if ((err = pthread_create (&this->thread, NULL, demux_vqa_loop, this)) != 0) {
+ printf ("demux_vqa: can't create new thread (%s)\n", strerror(err));
+ abort();
+ }
+
+ this->status = DEMUX_OK;
+ }
+
+ pthread_mutex_unlock(&this->mutex);
+
+ return DEMUX_OK;
+}
+
+static int demux_vqa_seek (demux_plugin_t *this_gen,
+ off_t start_pos, int start_time) {
+
+ return 0;
+}
+
+static void demux_vqa_stop (demux_plugin_t *this_gen) {
+
+ demux_vqa_t *this = (demux_vqa_t *) this_gen;
+ void *p;
+
+ pthread_mutex_lock( &this->mutex );
+
+ if (!this->thread_running) {
+ pthread_mutex_unlock( &this->mutex );
+ return;
+ }
+
+ this->send_end_buffers = 0;
+ this->status = DEMUX_FINISHED;
+
+ pthread_mutex_unlock( &this->mutex );
+ pthread_join (this->thread, &p);
+
+ xine_demux_flush_engine(this->xine);
+
+ xine_demux_control_end(this->xine, BUF_FLAG_END_USER);
+}
+
+static void demux_vqa_close (demux_plugin_t *this) {
+ free(this);
+}
+
+static int demux_vqa_get_status (demux_plugin_t *this_gen) {
+ demux_vqa_t *this = (demux_vqa_t *) this_gen;
+
+ return this->status;
+}
+
+static char *demux_vqa_get_id(void) {
+ return "VQA";
+}
+
+static int demux_vqa_get_stream_length (demux_plugin_t *this_gen) {
+
+ return 0;
+}
+
+static char *demux_vqa_get_mimetypes(void) {
+ return NULL;
+}
+
+
+demux_plugin_t *init_demuxer_plugin(int iface, xine_t *xine) {
+ demux_vqa_t *this;
+
+ if (iface != 10) {
+ printf (_("demux_vqa: plugin doesn't support plugin API version %d.\n"
+ " this means there's a version mismatch between xine and this "
+ " demuxer plugin.\nInstalling current demux plugins should help.\n"),
+ iface);
+ return NULL;
+ }
+
+ this = (demux_vqa_t *) xine_xmalloc(sizeof(demux_vqa_t));
+ this->config = xine->config;
+ this->xine = xine;
+
+ (void *) this->config->register_string(this->config,
+ "mrl.ends_vqa", VALID_ENDS,
+ _("valid mrls ending for vqa demuxer"), NULL, NULL, NULL);
+
+ this->demux_plugin.interface_version = DEMUXER_PLUGIN_IFACE_VERSION;
+ this->demux_plugin.open = demux_vqa_open;
+ this->demux_plugin.start = demux_vqa_start;
+ this->demux_plugin.seek = demux_vqa_seek;
+ this->demux_plugin.stop = demux_vqa_stop;
+ this->demux_plugin.close = demux_vqa_close;
+ this->demux_plugin.get_status = demux_vqa_get_status;
+ this->demux_plugin.get_identifier = demux_vqa_get_id;
+ this->demux_plugin.get_stream_length = demux_vqa_get_stream_length;
+ this->demux_plugin.get_mimetypes = demux_vqa_get_mimetypes;
+
+ this->status = DEMUX_FINISHED;
+ pthread_mutex_init(&this->mutex, NULL);
+
+ return &this->demux_plugin;
+}
+