summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libxinevdec/Makefile.am5
-rw-r--r--src/libxinevdec/fli.c651
2 files changed, 655 insertions, 1 deletions
diff --git a/src/libxinevdec/Makefile.am b/src/libxinevdec/Makefile.am
index 1c517aa19..ea84b66cd 100644
--- a/src/libxinevdec/Makefile.am
+++ b/src/libxinevdec/Makefile.am
@@ -9,7 +9,7 @@ libdir = $(XINE_PLUGINDIR)
lib_LTLIBRARIES = xineplug_decode_cinepak.la xineplug_decode_cyuv.la \
xineplug_decode_msvc.la xineplug_decode_roqvideo.la \
- xineplug_decode_svq1.la
+ xineplug_decode_svq1.la xineplug_decode_fli.la
xineplug_decode_cinepak_la_SOURCES = cinepak.c
xineplug_decode_cinepak_la_LDFLAGS = -avoid-version -module
@@ -26,6 +26,9 @@ xineplug_decode_roqvideo_la_LDFLAGS = -avoid-version -module
xineplug_decode_svq1_la_SOURCES = svq1.c svq1_codebooks.h
xineplug_decode_svq1_la_LDFLAGS = -avoid-version -module
+xineplug_decode_fli_la_SOURCES = fli.c
+xineplug_decode_fli_la_LDFLAGS = -avoid-version -module
+
noinst_HEADERS = svq1_codebooks.h
debug:
diff --git a/src/libxinevdec/fli.c b/src/libxinevdec/fli.c
new file mode 100644
index 000000000..fb2e4446e
--- /dev/null
+++ b/src/libxinevdec/fli.c
@@ -0,0 +1,651 @@
+/*
+ * Copyright (C) 2000-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
+ *
+ * FLI Video Decoder by Mike Melanson (melanson@pcisys.net) and
+ * Roberto Togni <rtogni@bresciaonline.it>
+ * For more information on the FLI format, as well as various traps to
+ * avoid when implementing a FLI decoder, visit:
+ * http://www.pcisys.net/~melanson/codecs/
+ *
+ * $Id: fli.c,v 1.1 2002/07/14 01:31:57 tmmm Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "video_out.h"
+#include "buffer.h"
+#include "xine_internal.h"
+#include "xineutils.h"
+#include "bswap.h"
+
+#define VIDEOBUFSIZE 128*1024
+
+#define PALETTE_SIZE (256 * 4)
+
+#define LE_16(x) (le2me_16(*(uint16_t *)(x)))
+#define LE_32(x) (le2me_32(*(uint32_t *)(x)))
+
+#define FLI_256_COLOR 4
+#define FLI_DELTA 7
+#define FLI_COLOR 11
+#define FLI_LC 12
+#define FLI_BLACK 13
+#define FLI_BRUN 15
+#define FLI_COPY 16
+#define FLI_MINI 18
+
+/**************************************************************************
+ * fli specific decode functions
+ *************************************************************************/
+
+typedef struct fli_decoder_s {
+ video_decoder_t video_decoder; /* parent video decoder structure */
+
+ /* these are traditional variables in a video decoder object */
+ vo_instance_t *video_out; /* object that will receive frames */
+ uint64_t video_step; /* frame duration in pts units */
+ int decoder_ok; /* current decoder status */
+ int skipframes;
+
+ unsigned char *buf; /* the accumulated buffer data */
+ int bufsize; /* the maximum size of buf */
+ int size; /* the current size of buf */
+
+ int width; /* the width of a video frame */
+ int height; /* the height of a video frame */
+
+ /* FLI decoding parameters */
+ unsigned char yuv_palette[PALETTE_SIZE];
+ yuv_planes_t yuv_planes;
+ unsigned char *ghost_image;
+
+} fli_decoder_t;
+
+void decode_fli_frame(fli_decoder_t *this) {
+ int stream_ptr = 0;
+ int stream_ptr_after_color_chunk;
+ int pixel_ptr;
+ int palette_ptr1;
+ int palette_ptr2;
+ unsigned char palette_idx1;
+ unsigned char palette_idx2;
+
+ unsigned int frame_size;
+ int num_chunks;
+
+ unsigned int chunk_size;
+ int chunk_type;
+
+ int i, j;
+
+ int color_packets;
+ int color_changes;
+ int color_shift;
+ unsigned r, g, b;
+
+ int lines, x;
+ int compressed_lines;
+ int starting_line;
+ signed short line_packets;
+ int y_ptr;
+ signed char byte_run;
+ int pixel_skip;
+ int update_whole_frame = 0; /* palette change flag */
+ int ghost_pixel_ptr;
+ int ghost_y_ptr;
+
+ frame_size = LE_32(&this->buf[stream_ptr]);
+ stream_ptr += 6; /* skip the magic number */
+ num_chunks = LE_16(&this->buf[stream_ptr]);
+ stream_ptr += 10; /* skip padding */
+
+ /* iterate through the chunks */
+ frame_size -= 16;
+ while ((frame_size > 0) && (num_chunks > 0)) {
+ chunk_size = LE_32(&this->buf[stream_ptr]);
+ stream_ptr += 4;
+ chunk_type = LE_16(&this->buf[stream_ptr]);
+ stream_ptr += 2;
+
+ switch (chunk_type) {
+ case FLI_256_COLOR:
+ case FLI_COLOR:
+ stream_ptr_after_color_chunk = stream_ptr + chunk_size - 6;
+ if (chunk_type == FLI_COLOR)
+ color_shift = 2;
+ else
+ color_shift = 0;
+ /* set up the palette */
+ color_packets = LE_16(&this->buf[stream_ptr]);
+ stream_ptr += 2;
+ palette_ptr1 = 0;
+ for (i = 0; i < color_packets; i++) {
+ /* first byte is how many colors to skip */
+ palette_ptr1 += (this->buf[stream_ptr++] * 4);
+ /* wrap around, for good measure */
+ if (palette_ptr1 >= PALETTE_SIZE)
+ palette_ptr1 = 0;
+ /* next byte indicates how many entries to change */
+ color_changes = this->buf[stream_ptr++];
+ /* if there are 0 color changes, there are actually 256 */
+ if (color_changes == 0)
+ color_changes = 256;
+ for (j = 0; j < color_changes; j++) {
+ r = this->buf[stream_ptr + 0] << color_shift;
+ g = this->buf[stream_ptr + 1] << color_shift;
+ b = this->buf[stream_ptr + 2] << color_shift;
+
+ this->yuv_palette[palette_ptr1++] = COMPUTE_Y(r,g, b);
+ this->yuv_palette[palette_ptr1++] = COMPUTE_U(r,g, b);
+ this->yuv_palette[palette_ptr1++] = COMPUTE_V(r,g, b);
+
+ palette_ptr1++;
+ stream_ptr += 3;
+ }
+ }
+
+ /* color chunks sometimes have weird 16-bit alignment issues;
+ * therefore, take the hardline approach and set the stream_ptr
+ * to the value calculate w.r.t. the size specified by the color
+ * chunk header */
+ stream_ptr = stream_ptr_after_color_chunk;
+
+ /* palette has changed, must update frame */
+ update_whole_frame = 1;
+ break;
+
+ case FLI_DELTA:
+ y_ptr = ghost_y_ptr = 0;
+ compressed_lines = LE_16(&this->buf[stream_ptr]);
+ stream_ptr += 2;
+ while (compressed_lines > 0) {
+ line_packets = LE_16(&this->buf[stream_ptr]);
+ stream_ptr += 2;
+ if (line_packets < 0) {
+ line_packets = -line_packets;
+ y_ptr += (line_packets * this->yuv_planes.row_stride);
+ ghost_y_ptr += (line_packets * this->width);
+ } else {
+ pixel_ptr = y_ptr;
+ ghost_pixel_ptr = ghost_y_ptr;
+ for (i = 0; i < line_packets; i++) {
+ /* account for the skip bytes */
+ pixel_skip = this->buf[stream_ptr++];
+ pixel_ptr += pixel_skip;
+ ghost_pixel_ptr += pixel_skip;
+ byte_run = this->buf[stream_ptr++];
+ if (byte_run < 0) {
+ byte_run = -byte_run;
+ palette_ptr1 = (palette_idx1 = this->buf[stream_ptr++]) * 4;
+ palette_ptr2 = (palette_idx2 = this->buf[stream_ptr++]) * 4;
+ for (j = 0; j < byte_run; j++) {
+ this->ghost_image[ghost_pixel_ptr++] = palette_idx1;
+ this->yuv_planes.y[pixel_ptr] =
+ this->yuv_palette[palette_ptr1 + 0];
+ this->yuv_planes.u[pixel_ptr] =
+ this->yuv_palette[palette_ptr1 + 1];
+ this->yuv_planes.v[pixel_ptr] =
+ this->yuv_palette[palette_ptr1 + 2];
+ pixel_ptr++;
+
+ this->ghost_image[ghost_pixel_ptr++] = palette_idx2;
+ this->yuv_planes.y[pixel_ptr] =
+ this->yuv_palette[palette_ptr2 + 0];
+ this->yuv_planes.u[pixel_ptr] =
+ this->yuv_palette[palette_ptr2 + 1];
+ this->yuv_planes.v[pixel_ptr] =
+ this->yuv_palette[palette_ptr2 + 2];
+ pixel_ptr++;
+ }
+ } else {
+ for (j = 0; j < byte_run * 2; j++) {
+ palette_ptr1 = (palette_idx1 = this->buf[stream_ptr++]) * 4;
+ this->ghost_image[ghost_pixel_ptr++] = palette_idx1;
+ this->yuv_planes.y[pixel_ptr] =
+ this->yuv_palette[palette_ptr1 + 0];
+ this->yuv_planes.u[pixel_ptr] =
+ this->yuv_palette[palette_ptr1 + 1];
+ this->yuv_planes.v[pixel_ptr] =
+ this->yuv_palette[palette_ptr1 + 2];
+ pixel_ptr++;
+ }
+ }
+ }
+
+ // take care of the extra 2 pixels on the C lines
+ this->yuv_planes.u[pixel_ptr] =
+ this->yuv_planes.u[pixel_ptr - 1];
+ this->yuv_planes.u[pixel_ptr + 1] =
+ this->yuv_planes.u[pixel_ptr - 2];
+
+ this->yuv_planes.v[pixel_ptr] =
+ this->yuv_planes.v[pixel_ptr - 1];
+ this->yuv_planes.v[pixel_ptr + 1] =
+ this->yuv_planes.v[pixel_ptr - 2];
+
+ y_ptr += this->yuv_planes.row_stride;
+ ghost_y_ptr += this->width;
+ compressed_lines--;
+ }
+ }
+ break;
+
+ case FLI_LC:
+ /* line compressed */
+ starting_line = LE_16(&this->buf[stream_ptr]);
+ stream_ptr += 2;
+ y_ptr = starting_line * this->yuv_planes.row_stride;
+ ghost_y_ptr = starting_line * this->width;
+
+ compressed_lines = LE_16(&this->buf[stream_ptr]);
+ stream_ptr += 2;
+ while (compressed_lines > 0) {
+ pixel_ptr = y_ptr;
+ ghost_pixel_ptr = ghost_y_ptr;
+ line_packets = this->buf[stream_ptr++];
+ if (line_packets > 0) {
+ for (i = 0; i < line_packets; i++) {
+ /* account for the skip bytes */
+ pixel_skip = this->buf[stream_ptr++];
+ pixel_ptr += pixel_skip;
+ ghost_pixel_ptr += pixel_skip;
+ byte_run = this->buf[stream_ptr++];
+ if (byte_run > 0) {
+ for (j = 0; j < byte_run; j++) {
+ palette_ptr1 = (palette_idx1 = this->buf[stream_ptr++]) * 4;
+ this->ghost_image[ghost_pixel_ptr++] = palette_idx1;
+ this->yuv_planes.y[pixel_ptr] =
+ this->yuv_palette[palette_ptr1 + 0];
+ this->yuv_planes.u[pixel_ptr] =
+ this->yuv_palette[palette_ptr1 + 1];
+ this->yuv_planes.v[pixel_ptr] =
+ this->yuv_palette[palette_ptr1 + 2];
+ pixel_ptr++;
+ }
+ } else {
+ byte_run = -byte_run;
+ palette_ptr1 = (palette_idx1 = this->buf[stream_ptr++]) * 4;
+ for (j = 0; j < byte_run; j++) {
+ this->ghost_image[ghost_pixel_ptr++] = palette_idx1;
+ this->yuv_planes.y[pixel_ptr] =
+ this->yuv_palette[palette_ptr1 + 0];
+ this->yuv_planes.u[pixel_ptr] =
+ this->yuv_palette[palette_ptr1 + 1];
+ this->yuv_planes.v[pixel_ptr] =
+ this->yuv_palette[palette_ptr1 + 2];
+ pixel_ptr++;
+ }
+ }
+ }
+ }
+
+ // take care of the extra 2 pixels on the C lines
+ this->yuv_planes.u[pixel_ptr] =
+ this->yuv_planes.u[pixel_ptr - 1];
+ this->yuv_planes.u[pixel_ptr + 1] =
+ this->yuv_planes.u[pixel_ptr - 2];
+
+ this->yuv_planes.v[pixel_ptr] =
+ this->yuv_planes.v[pixel_ptr - 1];
+ this->yuv_planes.v[pixel_ptr + 1] =
+ this->yuv_planes.v[pixel_ptr - 2];
+
+ y_ptr += this->yuv_planes.row_stride;
+ ghost_y_ptr += this->width;
+ compressed_lines--;
+ }
+ break;
+
+ case FLI_BLACK:
+ /* set the whole frame to color 0 (which is usually black) by
+ * clearing the ghost image and trigger a full frame update */
+ memset(this->ghost_image, 0,
+ this->width * this->height * sizeof(unsigned char));
+ update_whole_frame = 1;
+ break;
+
+ case FLI_BRUN:
+ /* byte run compression */
+ y_ptr = 0;
+ ghost_y_ptr = 0;
+ for (lines = 0; lines < this->height; lines++) {
+ pixel_ptr = y_ptr;
+ ghost_pixel_ptr = ghost_y_ptr;
+ line_packets = this->buf[stream_ptr++];
+ for (i = 0; i < line_packets; i++) {
+ byte_run = this->buf[stream_ptr++];
+ if (byte_run > 0) {
+ palette_ptr1 = (palette_idx1 = this->buf[stream_ptr++]) * 4;
+ for (j = 0; j < byte_run; j++) {
+ this->ghost_image[ghost_pixel_ptr++] = palette_idx1;
+ this->yuv_planes.y[pixel_ptr] =
+ this->yuv_palette[palette_ptr1 + 0];
+ this->yuv_planes.u[pixel_ptr] =
+ this->yuv_palette[palette_ptr1 + 1];
+ this->yuv_planes.v[pixel_ptr] =
+ this->yuv_palette[palette_ptr1 + 2];
+ pixel_ptr++;
+ }
+ } else { /* copy bytes if byte_run < 0 */
+ byte_run = -byte_run;
+ for (j = 0; j < byte_run; j++) {
+ palette_ptr1 = (palette_idx1 = this->buf[stream_ptr++]) * 4;
+ this->ghost_image[ghost_pixel_ptr++] = palette_idx1;
+ this->yuv_planes.y[pixel_ptr] =
+ this->yuv_palette[palette_ptr1 + 0];
+ this->yuv_planes.u[pixel_ptr] =
+ this->yuv_palette[palette_ptr1 + 1];
+ this->yuv_planes.v[pixel_ptr] =
+ this->yuv_palette[palette_ptr1 + 2];
+ pixel_ptr++;
+ }
+ }
+ }
+
+ // take care of the extra 2 pixels on the C lines
+ this->yuv_planes.u[pixel_ptr] =
+ this->yuv_planes.u[pixel_ptr - 1];
+ this->yuv_planes.u[pixel_ptr + 1] =
+ this->yuv_planes.u[pixel_ptr - 2];
+
+ this->yuv_planes.v[pixel_ptr] =
+ this->yuv_planes.v[pixel_ptr - 1];
+ this->yuv_planes.v[pixel_ptr + 1] =
+ this->yuv_planes.v[pixel_ptr - 2];
+
+ y_ptr += this->yuv_planes.row_stride;
+ ghost_y_ptr += this->width;
+ }
+ break;
+
+ case FLI_COPY:
+ /* copy the chunk (uncompressed frame) to the ghost image and
+ * schedule the whole frame to be updated */
+ if (chunk_size - 6 > this->width * this->height) {
+ printf(
+ _("FLI: in chunk FLI_COPY : source data (%d bytes) bigger than" \
+ " image, skipping chunk\n"),
+ chunk_size - 6);
+ break;
+ } else
+ memcpy(this->ghost_image, &this->buf[stream_ptr], chunk_size - 6);
+ stream_ptr += chunk_size - 6;
+ update_whole_frame = 1;
+ break;
+
+ case FLI_MINI:
+ /* some sort of a thumbnail? disregard this chunk... */
+ stream_ptr += chunk_size - 6;
+ break;
+
+ default:
+ printf (_("FLI: Unrecognized chunk type: %d\n"), chunk_type);
+ break;
+ }
+
+ frame_size -= chunk_size;
+ num_chunks--;
+ }
+
+ if (update_whole_frame) {
+
+ pixel_ptr = ghost_pixel_ptr = 0;
+ for (lines = 0; lines < this->height; lines++) {
+ for (x = 0; x < this->width; x++) {
+ palette_ptr1 = this->ghost_image[ghost_pixel_ptr++] * 4;
+ this->yuv_planes.y[pixel_ptr] = this->yuv_palette[palette_ptr1 + 0];
+ this->yuv_planes.u[pixel_ptr] = this->yuv_palette[palette_ptr1 + 1];
+ this->yuv_planes.v[pixel_ptr] = this->yuv_palette[palette_ptr1 + 2];
+ pixel_ptr++;
+ }
+
+ // take care of the extra 2 pixels on the C lines
+ this->yuv_planes.u[pixel_ptr] =
+ this->yuv_planes.u[pixel_ptr - 1];
+ this->yuv_planes.u[pixel_ptr + 1] =
+ this->yuv_planes.u[pixel_ptr - 2];
+
+ // take care of the extra 2 pixels on the C lines
+ this->yuv_planes.v[pixel_ptr] =
+ this->yuv_planes.v[pixel_ptr - 1];
+ this->yuv_planes.v[pixel_ptr + 1] =
+ this->yuv_planes.v[pixel_ptr - 2];
+
+ pixel_ptr += 2;
+ }
+ }
+
+ /* by the end of the chunk, the stream ptr should equal the frame
+ * size (minus 1, possibly); if it doesn't, issue a warning */
+ if ((stream_ptr != this->size) && (stream_ptr != this->size - 1))
+ printf (
+ _(" warning: processed FLI chunk where chunk size = %d\n" \
+ " and final chunk ptr = %d\n"),
+ this->size, stream_ptr);
+}
+
+/**************************************************************************
+ * xine video plugin functions
+ *************************************************************************/
+
+static int fli_can_handle (video_decoder_t *this_gen, int buf_type) {
+
+ return (buf_type == BUF_VIDEO_FLI);
+}
+
+/*
+ * This function is responsible is called to initialize the video decoder
+ * for use. Initialization usually involves setting up the fields in your
+ * private video decoder object.
+ */
+static void fli_init (video_decoder_t *this_gen,
+ vo_instance_t *video_out) {
+ fli_decoder_t *this = (fli_decoder_t *) this_gen;
+
+ /* set our own video_out object to the one that xine gives us */
+ this->video_out = video_out;
+
+ /* indicate that the decoder is not quite ready yet */
+ this->decoder_ok = 0;
+}
+
+/*
+ * This function receives a buffer of data from the demuxer layer and
+ * figures out how to handle it based on its header flags.
+ */
+static void fli_decode_data (video_decoder_t *this_gen,
+ buf_element_t *buf) {
+
+ fli_decoder_t *this = (fli_decoder_t *) this_gen;
+ vo_frame_t *img; /* video out frame */
+
+ /* a video decoder does not care about this flag (?) */
+ if (buf->decoder_flags & BUF_FLAG_PREVIEW)
+ return;
+
+ if (buf->decoder_flags & BUF_FLAG_HEADER) { /* need to initialize */
+ this->video_out->open (this->video_out);
+
+ if(this->buf)
+ free(this->buf);
+
+ this->width = (LE_16(&buf->content[8]) + 3) & ~0x03;
+ this->height = (LE_16(&buf->content[10]) + 3) & ~0x03;
+ this->video_step = buf->decoder_info[1];
+
+ this->ghost_image = xine_xmalloc(this->width * this->height);
+ init_yuv_planes(&this->yuv_planes, this->width, this->height);
+
+ if (this->buf)
+ free (this->buf);
+ this->bufsize = VIDEOBUFSIZE;
+ this->buf = malloc(this->bufsize);
+ this->size = 0;
+
+ this->video_out->open (this->video_out);
+ this->decoder_ok = 1;
+
+ return;
+ } else if (this->decoder_ok) {
+
+ if (this->size + buf->size > this->bufsize) {
+ this->bufsize = this->size + 2 * buf->size;
+ this->buf = realloc (this->buf, this->bufsize);
+ }
+
+ xine_fast_memcpy (&this->buf[this->size], buf->content, buf->size);
+
+ this->size += buf->size;
+
+ if (buf->decoder_flags & BUF_FLAG_FRAMERATE)
+ this->video_step = buf->decoder_info[0];
+
+ if (buf->decoder_flags & BUF_FLAG_FRAME_END) {
+
+ img = this->video_out->get_frame (this->video_out,
+ this->width, this->height,
+ 42, IMGFMT_YUY2, VO_BOTH_FIELDS);
+
+ img->duration = this->video_step;
+ img->pts = buf->pts;
+ img->bad_frame = 0;
+
+ decode_fli_frame(this);
+ yuv444_to_yuy2(&this->yuv_planes, img->base[0]);
+
+/*
+ if (img->copy) {
+
+ int height = abs(this->biHeight);
+ int stride = this->biWidth;
+ uint8_t* src[3];
+
+ src[0] = img->base[0];
+ src[1] = img->base[1];
+ src[2] = img->base[2];
+ while ((height -= 16) >= 0) {
+ img->copy(img, src);
+ src[0] += 16 * stride;
+ src[1] += 4 * stride;
+ src[2] += 4 * stride;
+ }
+ }
+*/
+
+ img->draw(img);
+ img->free(img);
+
+ this->size = 0;
+ }
+ }
+}
+
+/*
+ * This function is called when xine needs to flush the system. Not
+ * sure when or if this is used or even if it needs to do anything.
+ */
+static void fli_flush (video_decoder_t *this_gen) {
+}
+
+/*
+ * This function resets the video decoder.
+ */
+static void fli_reset (video_decoder_t *this_gen) {
+ fli_decoder_t *this = (fli_decoder_t *) this_gen;
+
+ this->size = 0;
+}
+
+/*
+ * This function is called when xine shuts down the decoder. It should
+ * free any memory and release any other resources allocated during the
+ * execution of the decoder.
+ */
+static void fli_close (video_decoder_t *this_gen) {
+ fli_decoder_t *this = (fli_decoder_t *) this_gen;
+
+ if (this->buf) {
+ free (this->buf);
+ this->buf = NULL;
+ }
+
+ free_yuv_planes(&this->yuv_planes);
+ free(this->ghost_image);
+
+ if (this->decoder_ok) {
+ this->decoder_ok = 0;
+ this->video_out->close(this->video_out);
+ }
+}
+
+/*
+ * This function returns the human-readable ID string to identify
+ * this decoder.
+ */
+static char *fli_get_id(void) {
+ return "FLI Video";
+}
+
+/*
+ * This function frees the video decoder instance allocated to the decoder.
+ */
+static void fli_dispose (video_decoder_t *this_gen) {
+ free (this_gen);
+}
+
+/*
+ * This function should be the plugin's only advertised function to the
+ * outside world. It allows xine to query the plugin module for the addresses
+ * to the necessary functions in the video decoder object. The video
+ * decoder object also has a priority field which allows different decoder
+ * plugins for the same buffer types to coexist peacefully. The higher the
+ * priority number, the more precedence a decoder has. E.g., 9 beats 1.
+ */
+video_decoder_t *init_video_decoder_plugin (int iface_version, xine_t *xine) {
+
+ fli_decoder_t *this ;
+
+ if (iface_version != 10) {
+ printf( "fli: plugin doesn't support plugin API version %d.\n"
+ "fli: this means there's a version mismatch between xine and this "
+ "fli: decoder plugin.\nInstalling current plugins should help.\n",
+ iface_version);
+ return NULL;
+ }
+
+ this = (fli_decoder_t *) malloc (sizeof (fli_decoder_t));
+ memset(this, 0, sizeof (fli_decoder_t));
+
+ this->video_decoder.interface_version = iface_version;
+ this->video_decoder.can_handle = fli_can_handle;
+ this->video_decoder.init = fli_init;
+ this->video_decoder.decode_data = fli_decode_data;
+ this->video_decoder.flush = fli_flush;
+ this->video_decoder.reset = fli_reset;
+ this->video_decoder.close = fli_close;
+ this->video_decoder.get_identifier = fli_get_id;
+ this->video_decoder.dispose = fli_dispose;
+ this->video_decoder.priority = 1;
+
+ return (video_decoder_t *) this;
+}