summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMike Melanson <mike@multimedia.cx>2004-02-05 02:51:14 +0000
committerMike Melanson <mike@multimedia.cx>2004-02-05 02:51:14 +0000
commit6e720988016a766055762cc9f1d97bcf06940444 (patch)
tree4a9b570ccb692725be8eafeb89eb7441f5b7451d /src
parent5964ac85782bc4e48737f36a88fc8f6a9ba8859e (diff)
downloadxine-lib-6e720988016a766055762cc9f1d97bcf06940444.tar.gz
xine-lib-6e720988016a766055762cc9f1d97bcf06940444.tar.bz2
initial commit for Sierra VMD demuxer (no audio yet)
CVS patchset: 6121 CVS date: 2004/02/05 02:51:14
Diffstat (limited to 'src')
-rw-r--r--src/demuxers/Makefile.am3
-rw-r--r--src/demuxers/demux_vmd.c506
2 files changed, 508 insertions, 1 deletions
diff --git a/src/demuxers/Makefile.am b/src/demuxers/Makefile.am
index 97c6bb02f..d11a9f75f 100644
--- a/src/demuxers/Makefile.am
+++ b/src/demuxers/Makefile.am
@@ -111,7 +111,8 @@ xineplug_dmx_pva_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
xineplug_dmx_games_la_SOURCES = group_games.c demux_eawve.c \
demux_idcin.c demux_ipmovie.c demux_roq.c \
demux_vqa.c demux_wc3movie.c demux_str.c \
- demux_film.c demux_smjpeg.c demux_4xm.c
+ demux_film.c demux_smjpeg.c demux_4xm.c \
+ demux_vmd.c
xineplug_dmx_games_la_LIBADD = $(XINE_LIB)
xineplug_dmx_games_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
diff --git a/src/demuxers/demux_vmd.c b/src/demuxers/demux_vmd.c
new file mode 100644
index 000000000..f0461a8f3
--- /dev/null
+++ b/src/demuxers/demux_vmd.c
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2004 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
+ */
+
+/*
+ * Sierra Video and Music Data (.vmd) File Demuxer
+ * by Mike Melanson (melanson@pcisys.net)
+ * For more information on the VMD file format, visit:
+ * http://www.pcisys.net/~melanson/codecs/
+ *
+ * Note that the only way that this demuxer validates by content is by
+ * checking the first 2 bytes, which are 0x2E 0x03 in a Sierra VMD file.
+ * There is a 1/65536 chance of a false positive using this method.
+ *
+ * $Id: demux_vmd.c,v 1.1 2004/02/05 02:51:14 tmmm Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define LOG_MODULE "demux_vmd"
+#define LOG_VERBOSE
+/*
+#define LOG
+*/
+
+#include "xine_internal.h"
+#include "xineutils.h"
+#include "compat.h"
+#include "demux.h"
+#include "bswap.h"
+#include "group_games.h"
+
+#define VMD_HEADER_SIZE 0x330
+#define BYTES_PER_FRAME_RECORD 16
+
+typedef struct {
+ int is_audio_frame;
+ off_t frame_offset;
+ unsigned int frame_size;
+ int64_t pts;
+ int keyframe;
+ unsigned char frame_record[BYTES_PER_FRAME_RECORD];
+} vmd_frame_t;
+
+typedef struct {
+ demux_plugin_t demux_plugin;
+
+ xine_stream_t *stream;
+ fifo_buffer_t *video_fifo;
+ fifo_buffer_t *audio_fifo;
+ input_plugin_t *input;
+ int status;
+
+ off_t data_start;
+ off_t data_size;
+
+ unsigned char bih[sizeof(xine_bmiheader) + VMD_HEADER_SIZE];
+ xine_waveformatex wave;
+
+ unsigned int audio_frames;
+ unsigned int iteration;
+
+ unsigned int frame_count;
+ vmd_frame_t *frame_table;
+ unsigned int current_frame;
+
+ int64_t video_pts_inc;
+ int64_t total_pts;
+
+} demux_vmd_t ;
+
+typedef struct {
+ demux_class_t demux_class;
+} demux_vmd_class_t;
+
+/* returns 1 if the VMD file was opened successfully, 0 otherwise */
+static int open_vmd_file(demux_vmd_t *this) {
+
+ xine_bmiheader *bih = (xine_bmiheader *)this->bih;
+ unsigned char *vmd_header = this->bih + sizeof(xine_bmiheader);
+ off_t toc_offset;
+ unsigned char *raw_frame_table;
+ unsigned int raw_frame_table_size;
+ unsigned char *current_frame_record;
+ off_t current_offset;
+ int i;
+ unsigned int total_frames;
+ int64_t current_video_pts = 0;
+
+ if (_x_demux_read_header(this->input, vmd_header, VMD_HEADER_SIZE) !=
+ VMD_HEADER_SIZE)
+ return 0;
+
+ if (LE_16(&vmd_header[0]) != VMD_HEADER_SIZE - 2)
+ return 0;
+
+ /* file is minimally qualified at this point, proceed to load */
+
+ /* get the actual filesize */
+ if ( !(this->data_size = this->input->get_length(this->input)) )
+ this->data_size = 1;
+
+ bih->biSize = sizeof(xine_bmiheader) + VMD_HEADER_SIZE;
+ bih->biWidth = LE_16(&vmd_header[12]);
+ bih->biHeight = LE_16(&vmd_header[14]);
+ this->wave.nSamplesPerSec = LE_16(&vmd_header[804]);
+ this->wave.nChannels = (vmd_header[811] & 0x80) ? 2 : 1;
+ this->wave.nBlockAlign = LE_16(&vmd_header[806]);
+ if (this->wave.nBlockAlign & 0x8000) {
+ this->wave.nBlockAlign -= 0x8000;
+ this->wave.wBitsPerSample = 16;
+ } else {
+ this->wave.wBitsPerSample = 8;
+ }
+
+ /* decide on a framerate */
+ if (this->wave.nSamplesPerSec) {
+ this->video_pts_inc = 90000;
+ this->video_pts_inc *= this->wave.nBlockAlign;
+ this->video_pts_inc /= this->wave.nSamplesPerSec;
+ } else {
+ this->video_pts_inc = 90000 / 10;
+ }
+
+ /* skip over the offset table and load the table of contents; don't
+ * care about the offset table since demuxer will calculate those
+ * independently */
+ toc_offset = LE_32(&vmd_header[812]);
+ this->frame_count = LE_16(&vmd_header[6]);
+ this->input->seek(this->input, toc_offset + this->frame_count * 6, SEEK_SET);
+
+ /* while we have the toal number of blocks, calculate the total running
+ * time */
+ this->total_pts = this->frame_count;
+ this->total_pts *= this->video_pts_inc;
+ this->total_pts /= 90;
+
+ /* 2 frames for every block reported on disk */
+ this->frame_count *= 2;
+
+ raw_frame_table_size = this->frame_count * BYTES_PER_FRAME_RECORD;
+ raw_frame_table = xine_xmalloc(raw_frame_table_size);
+ if (this->input->read(this->input, raw_frame_table, raw_frame_table_size) !=
+ raw_frame_table_size) {
+ free(raw_frame_table);
+ return 0;
+ }
+
+ this->frame_table = xine_xmalloc(this->frame_count * sizeof(vmd_frame_t));
+
+ current_offset = this->data_start = LE_32(&vmd_header[20]);
+ this->data_size = toc_offset - this->data_start;
+ current_frame_record = raw_frame_table;
+ total_frames = this->frame_count;
+ i = 0;
+ while (total_frames--) {
+ /* if the frame size is 0, do not count the frame and bring the
+ * total frame count down */
+ this->frame_table[i].frame_size = LE_32(&current_frame_record[2]);
+
+ /* this logic is present so that 0-length audio chunks are not
+ * accounted */
+ if (!this->frame_table[i].frame_size) {
+ this->frame_count--; /* one less frame to count */
+ current_frame_record += BYTES_PER_FRAME_RECORD;
+ continue;
+ }
+
+ if (current_frame_record[0] == 0x02) {
+ this->frame_table[i].is_audio_frame = 0;
+ this->frame_table[i].pts = current_video_pts;
+ current_video_pts += this->video_pts_inc;
+ } else {
+ this->frame_table[i].is_audio_frame = 1;
+ this->frame_table[i].pts = 0;
+ }
+ this->frame_table[i].frame_offset = current_offset;
+ current_offset += this->frame_table[i].frame_size;
+ memcpy(this->frame_table[i].frame_record, current_frame_record,
+ BYTES_PER_FRAME_RECORD);
+
+ current_frame_record += BYTES_PER_FRAME_RECORD;
+ i++;
+ }
+
+ free(raw_frame_table);
+ this->current_frame = 0;
+
+ return 1;
+}
+
+static int demux_vmd_send_chunk(demux_plugin_t *this_gen) {
+
+ demux_vmd_t *this = (demux_vmd_t *) this_gen;
+ buf_element_t *buf = NULL;
+ unsigned int remaining_bytes;
+ vmd_frame_t *frame;
+
+ if (this->current_frame >= this->frame_count) {
+ this->status = DEMUX_FINISHED;
+ return this->status;
+ }
+
+ frame = &this->frame_table[this->current_frame];
+ /* position the stream (will probably be there already) */
+ this->input->seek(this->input, frame->frame_offset, SEEK_SET);
+ remaining_bytes = frame->frame_size;
+
+ if (!frame->is_audio_frame) {
+
+ /* send off the frame record first in its own buffer */
+ buf = this->video_fifo->buffer_pool_alloc (this->video_fifo);
+ buf->type = BUF_VIDEO_VMD;
+ buf->extra_info->input_pos = frame->frame_offset - this->data_start;
+ buf->extra_info->input_length = this->data_size;
+ memcpy(buf->content, frame->frame_record, BYTES_PER_FRAME_RECORD);
+ buf->size = BYTES_PER_FRAME_RECORD;
+ buf->pts = frame->pts;
+ buf->extra_info->input_time = buf->pts / 90;
+ this->video_fifo->put(this->video_fifo, buf);
+
+ while (remaining_bytes) {
+ buf = this->video_fifo->buffer_pool_alloc (this->video_fifo);
+ buf->type = BUF_VIDEO_VMD;
+ buf->extra_info->input_pos = frame->frame_offset - this->data_start;
+ buf->extra_info->input_length = this->data_size;
+
+ if (remaining_bytes > buf->max_size)
+ buf->size = buf->max_size;
+ else
+ buf->size = remaining_bytes;
+ remaining_bytes -= buf->size;
+
+ if (!remaining_bytes)
+ buf->decoder_flags |= BUF_FLAG_FRAME_END;
+
+ if (this->input->read(this->input, buf->content, buf->size) !=
+ buf->size) {
+ buf->free_buffer(buf);
+ this->status = DEMUX_FINISHED;
+ break;
+ }
+
+ buf->pts = frame->pts;
+ buf->extra_info->input_time = buf->pts / 90;
+ this->video_fifo->put(this->video_fifo, buf);
+ }
+
+ } else if (frame->is_audio_frame && this->audio_fifo) {
+
+#if 0
+ /* send off the frame record first in its own buffer */
+ buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo);
+ buf->type = BUF_AUDIO_VMD;
+ buf->extra_info->input_pos = frame->frame_offset - this->data_start;
+ buf->extra_info->input_length = this->data_size;
+ memcpy(buf->content, frame->frame_record, BYTES_PER_FRAME_RECORD);
+ buf->size = BYTES_PER_FRAME_RECORD;
+ buf->pts = 0; /* let the engine sort out the audio pts */
+ buf->extra_info->input_time = 0;
+ this->audio_fifo->put(this->audio_fifo, buf);
+
+ while (remaining_bytes) {
+ buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo);
+ buf->type = BUF_AUDIO_VMD;
+ buf->extra_info->input_pos = frame->frame_offset - this->data_start;
+ buf->extra_info->input_length = this->data_size;
+
+ if (remaining_bytes > buf->max_size)
+ buf->size = buf->max_size;
+ else
+ buf->size = remaining_bytes;
+ remaining_bytes -= buf->size;
+
+ if (!remaining_bytes)
+ buf->decoder_flags |= BUF_FLAG_FRAME_END;
+
+ if (this->input->read(this->input, buf->content, buf->size) !=
+ buf->size) {
+ buf->free_buffer(buf);
+ this->status = DEMUX_FINISHED;
+ break;
+ }
+
+ buf->pts = 0; /* let the engine sort out the audio pts */
+ buf->extra_info->input_time = 0;
+ this->audio_fifo->put(this->audio_fifo, buf);
+ }
+#endif
+ }
+
+ this->current_frame++;
+
+ return this->status;
+}
+
+static void demux_vmd_send_headers(demux_plugin_t *this_gen) {
+ demux_vmd_t *this = (demux_vmd_t *) this_gen;
+ buf_element_t *buf;
+ xine_bmiheader *bih = (xine_bmiheader *)this->bih;
+
+ this->video_fifo = this->stream->video_fifo;
+ this->audio_fifo = this->stream->audio_fifo;
+
+ this->status = DEMUX_OK;
+
+ /* load stream information */
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_VIDEO, 1);
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_AUDIO,
+ (this->wave.nSamplesPerSec) ? 1 : 0);
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_WIDTH,
+ bih->biWidth);
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_HEIGHT,
+ bih->biHeight);
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_CHANNELS,
+ this->wave.nChannels);
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_SAMPLERATE,
+ this->wave.nSamplesPerSec);
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_BITS,
+ this->wave.wBitsPerSample);
+
+ /* send start buffers */
+ _x_demux_control_start(this->stream);
+
+ /* send init info to decoders */
+ buf = this->video_fifo->buffer_pool_alloc (this->video_fifo);
+ buf->decoder_flags = BUF_FLAG_HEADER|BUF_FLAG_STDHEADER|BUF_FLAG_FRAME_END;
+ buf->decoder_info[0] = 0;
+ buf->decoder_info[1] = this->video_pts_inc; /* initial duration */
+ memcpy(buf->content, this->bih, sizeof(xine_bmiheader) + VMD_HEADER_SIZE);
+ buf->size = sizeof(xine_bmiheader) + VMD_HEADER_SIZE;
+ buf->type = BUF_VIDEO_VMD;
+ this->video_fifo->put (this->video_fifo, buf);
+
+#if 0
+ if (this->audio_fifo && this->wave.nSamplesPerSec) {
+ buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo);
+ buf->type = BUF_AUDIO_VMD;
+ buf->decoder_flags = BUF_FLAG_HEADER|BUF_FLAG_STDHEADER|BUF_FLAG_FRAME_END;
+ buf->decoder_info[0] = 0;
+ buf->decoder_info[1] = this->wave.nSamplesPerSec;
+ buf->decoder_info[2] = this->wave.wBitsPerSample;
+ buf->decoder_info[3] = this->wave.nChannels;
+ this->wave.nBlockAlign = (this->wave.wBitsPerSample / 8) * this->wave.nChannels;
+ this->wave.nAvgBytesPerSec = this->wave.nBlockAlign * this->wave.nSamplesPerSec;
+ memcpy(buf->content, &this->wave, sizeof(this->wave));
+ buf->size = sizeof(this->wave);
+ this->audio_fifo->put (this->audio_fifo, buf);
+ }
+#endif
+}
+
+static int demux_vmd_seek (demux_plugin_t *this_gen,
+ off_t start_pos, int start_time, int playing) {
+
+ demux_vmd_t *this = (demux_vmd_t *) this_gen;
+
+ /* if thread is not running, initialize demuxer */
+ if( !playing ) {
+ this->status = DEMUX_OK;
+ }
+
+ return this->status;
+}
+
+static void demux_vmd_dispose (demux_plugin_t *this_gen) {
+ demux_vmd_t *this = (demux_vmd_t *) this_gen;
+
+ free(this);
+}
+
+static int demux_vmd_get_status (demux_plugin_t *this_gen) {
+ demux_vmd_t *this = (demux_vmd_t *) this_gen;
+
+ return this->status;
+}
+
+static int demux_vmd_get_stream_length (demux_plugin_t *this_gen) {
+ demux_vmd_t *this = (demux_vmd_t *) this_gen;
+
+ return this->total_pts;
+}
+
+static uint32_t demux_vmd_get_capabilities(demux_plugin_t *this_gen) {
+ return DEMUX_CAP_NOCAP;
+}
+
+static int demux_vmd_get_optional_data(demux_plugin_t *this_gen,
+ void *data, int data_type) {
+ return DEMUX_OPTIONAL_UNSUPPORTED;
+}
+
+static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *stream,
+ input_plugin_t *input) {
+
+ demux_vmd_t *this;
+
+ this = xine_xmalloc (sizeof (demux_vmd_t));
+ this->stream = stream;
+ this->input = input;
+
+ this->demux_plugin.send_headers = demux_vmd_send_headers;
+ this->demux_plugin.send_chunk = demux_vmd_send_chunk;
+ this->demux_plugin.seek = demux_vmd_seek;
+ this->demux_plugin.dispose = demux_vmd_dispose;
+ this->demux_plugin.get_status = demux_vmd_get_status;
+ this->demux_plugin.get_stream_length = demux_vmd_get_stream_length;
+ this->demux_plugin.get_capabilities = demux_vmd_get_capabilities;
+ this->demux_plugin.get_optional_data = demux_vmd_get_optional_data;
+ this->demux_plugin.demux_class = class_gen;
+
+ this->status = DEMUX_FINISHED;
+
+ switch (stream->content_detection_method) {
+
+ case METHOD_BY_EXTENSION: {
+ char *extensions, *mrl;
+
+ mrl = input->get_mrl (input);
+ extensions = class_gen->get_extensions (class_gen);
+
+ if (!_x_demux_check_extension (mrl, extensions)) {
+ free (this);
+ return NULL;
+ }
+ }
+ /* falling through is intended */
+
+ case METHOD_BY_CONTENT:
+ case METHOD_EXPLICIT:
+
+ if (!open_vmd_file(this)) {
+ free (this);
+ return NULL;
+ }
+
+ break;
+
+ default:
+ free (this);
+ return NULL;
+ }
+
+ return &this->demux_plugin;
+}
+
+static char *get_description (demux_class_t *this_gen) {
+ return "Sierra VMD file demux plugin";
+}
+
+static char *get_identifier (demux_class_t *this_gen) {
+ return "VMD";
+}
+
+static char *get_extensions (demux_class_t *this_gen) {
+ return "vmd";
+}
+
+static char *get_mimetypes (demux_class_t *this_gen) {
+ return NULL;
+}
+
+static void class_dispose (demux_class_t *this_gen) {
+ demux_vmd_class_t *this = (demux_vmd_class_t *) this_gen;
+
+ free (this);
+}
+
+void *demux_vmd_init_plugin (xine_t *xine, void *data) {
+ demux_vmd_class_t *this;
+
+ this = xine_xmalloc (sizeof (demux_vmd_class_t));
+
+ this->demux_class.open_plugin = open_plugin;
+ this->demux_class.get_description = get_description;
+ this->demux_class.get_identifier = get_identifier;
+ this->demux_class.get_mimetypes = get_mimetypes;
+ this->demux_class.get_extensions = get_extensions;
+ this->demux_class.dispose = class_dispose;
+
+ return this;
+}