diff -urpN ../gst-plugins-bad-0.10.3/configure.ac ./configure.ac
--- ../gst-plugins-bad-0.10.3/configure.ac	Thu May  4 17:18:42 2006
+++ ./configure.ac	Sun Jul 30 20:31:40 2006
@@ -79,6 +79,7 @@ GST_PLUGINS_ALL="\
   cdxaparse \
   freeze \
   modplug \
+  mve \
   speed \
   qtdemux \
   xingheader \
@@ -632,6 +633,7 @@ gst/cdxaparse/Makefile
 gst/freeze/Makefile
 gst/modplug/Makefile
 gst/modplug/libmodplug/Makefile
+gst/mve/Makefile
 gst/speed/Makefile
 gst/qtdemux/Makefile
 gst/tta/Makefile
diff -urpN ../gst-plugins-bad-0.10.3/gst/mve/Makefile.am ./gst/mve/Makefile.am
--- ../gst-plugins-bad-0.10.3/gst/mve/Makefile.am	Thu Jan  1 01:00:00 1970
+++ ./gst/mve/Makefile.am	Sun Jul 30 20:28:36 2006
@@ -0,0 +1,23 @@
+plugin_LTLIBRARIES = libgstmvedemux.la libgstmvemux.la
+
+libgstmvedemux_la_SOURCES = \
+gstmvedemux.c \
+mveaudiodec.c \
+mvevideodec8.c \
+mvevideodec16.c
+
+libgstmvedemux_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS)
+libgstmvedemux_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS)
+libgstmvedemux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+
+libgstmvemux_la_SOURCES = \
+gstmvemux.c \
+mveaudioenc.c \
+mvevideoenc8.c \
+mvevideoenc16.c
+
+libgstmvemux_la_CFLAGS = $(GST_CFLAGS)
+libgstmvemux_la_LIBADD = $(GST_LIBS)
+libgstmvemux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+
+noinst_HEADERS = gstmvedemux.h gstmvemux.h mve.h
diff -urpN ../gst-plugins-bad-0.10.3/gst/mve/gstmvedemux.c ./gst/mve/gstmvedemux.c
--- ../gst-plugins-bad-0.10.3/gst/mve/gstmvedemux.c	Thu Jan  1 01:00:00 1970
+++ ./gst/mve/gstmvedemux.c	Sun Jul 30 21:23:41 2006
@@ -0,0 +1,1192 @@
+/*
+ * GStreamer demultiplexer plugin for Interplay MVE movie files
+ *
+ * Copyright (C) 2006 Jens Granseuer <jensgr@gmx.net>
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * For more information about the Interplay MVE format, visit:
+ *   http://www.pcisys.net/~melanson/codecs/interplay-mve.txt
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <string.h>
+#include "gstmvedemux.h"
+#include "mve.h"
+
+
+extern int  ipvideo_decode_frame8  (const GstMveDemuxStream *s,
+                                    const unsigned char *data,
+                                    unsigned short len);
+extern int  ipvideo_decode_frame16 (const GstMveDemuxStream *s,
+                                    const unsigned char *data,
+                                    unsigned short len);
+
+extern void ipaudio_uncompress     (short *buffer,
+                                    unsigned short buf_len,
+                                    const unsigned char *data,
+                                    unsigned char channels);
+
+enum MveDemuxState {
+  MVEDEMUX_STATE_INITIAL,      /* initial state, header not read */
+  MVEDEMUX_STATE_NEXT_CHUNK,   /* parsing chunk/segment header */
+  MVEDEMUX_STATE_MOVIE,        /* reading the stream */
+  MVEDEMUX_STATE_SKIP          /* skipping chunk */
+};
+
+GST_DEBUG_CATEGORY (mvedemux_debug);
+
+static GstStaticPadTemplate sink_template =
+GST_STATIC_PAD_TEMPLATE (
+  "sink",
+  GST_PAD_SINK,
+  GST_PAD_ALWAYS,
+  GST_STATIC_CAPS ("video/x-mve")
+);
+
+static GstStaticPadTemplate vidsrc_template =
+GST_STATIC_PAD_TEMPLATE (
+  "video",
+  GST_PAD_SRC,
+  GST_PAD_SOMETIMES,
+  GST_STATIC_CAPS (
+    "video/x-raw-rgb, "                                    \
+      "width = (int) [ 1, MAX ], "                         \
+      "height = (int) [ 1, MAX ], "                        \
+      "framerate = (fraction) [ 0, MAX ], "                \
+      "bpp = (int) 16, "                                   \
+      "depth = (int) 15, "                                 \
+      "endianness = (int) BYTE_ORDER, "                    \
+      "red_mask = (int) 31744, "                           \
+      "green_mask = (int) 992, "                           \
+      "blue_mask = (int) 31; "                             \
+    "video/x-raw-rgb, "                                    \
+      "width = (int) [ 1, MAX ], "                         \
+      "height = (int) [ 1, MAX ], "                        \
+      "framerate = (fraction) [ 0, MAX ], "                \
+      "bpp = (int) 8, "                                    \
+      "depth = (int) 8, "                                  \
+      "endianness = (int) BYTE_ORDER"
+  )
+);
+
+static GstStaticPadTemplate audsrc_template =
+GST_STATIC_PAD_TEMPLATE (
+  "audio",
+  GST_PAD_SRC,
+  GST_PAD_SOMETIMES,
+  GST_STATIC_CAPS (
+    "audio/x-raw-int, "                                    \
+      "width = (int) 8, "                                  \
+      "rate = (int) [ 1, MAX ], "                          \
+      "channels = (int) [ 1, 2 ], "                        \
+      "depth = (int) 8, "                                  \
+      "signed = (boolean) false; "                         \
+    "audio/x-raw-int, "                                    \
+      "width = (int) 16, "                                 \
+      "rate = (int) [ 1, MAX ], "                          \
+      "channels = (int) [ 1, 2 ], "                        \
+      "depth = (int) 16, "                                 \
+      "signed = (boolean) true, "                          \
+      "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }"
+  )
+);
+
+#define MVE_DEFAULT_AUDIO_STREAM 0x01
+
+static GType gst_mve_demux_get_type  (void);
+static void gst_mve_demux_class_init (GstMveDemuxClass *klass);
+static void gst_mve_demux_base_init  (GstMveDemuxClass *klass);
+static void gst_mve_demux_init       (GstMveDemux *mve);
+
+#define GST_MVE_SEGMENT_SIZE(data)    (GST_READ_UINT16_LE (data))
+#define GST_MVE_SEGMENT_TYPE(data)    (GST_READ_UINT8 (data + 2))
+#define GST_MVE_SEGMENT_VERSION(data) (GST_READ_UINT8 (data + 3))
+
+static GstElementClass *parent_class = NULL;
+
+static void
+gst_mve_demux_reset (GstMveDemux *mve)
+{
+  gst_adapter_clear (mve->adapter);
+
+  if (mve->video_stream != NULL)
+  {
+    if (mve->video_stream->pad)
+      gst_element_remove_pad (GST_ELEMENT (mve), mve->video_stream->pad);
+    if (mve->video_stream->caps)
+      gst_caps_unref (mve->video_stream->caps);
+    if (mve->video_stream->palette)
+      gst_buffer_unref (mve->video_stream->palette);
+    g_free (mve->video_stream->code_map);
+    if (mve->video_stream->buffer)
+      gst_buffer_unref (mve->video_stream->buffer);
+    g_free (mve->video_stream);
+    mve->video_stream = NULL;
+  }
+
+  if (mve->audio_stream != NULL)
+  {
+    if (mve->audio_stream->pad)
+      gst_element_remove_pad (GST_ELEMENT (mve), mve->audio_stream->pad);
+    if (mve->audio_stream->caps)
+      gst_caps_unref (mve->audio_stream->caps);
+    if (mve->audio_stream->buffer)
+      gst_buffer_unref (mve->audio_stream->buffer);
+    g_free (mve->audio_stream);
+    mve->audio_stream = NULL;
+  }
+
+  mve->state = MVEDEMUX_STATE_INITIAL;
+  mve->needed_bytes = MVE_PREAMBLE_SIZE;
+  mve->frame_duration = GST_CLOCK_TIME_NONE;
+
+  mve->chunk_size = 0;
+  mve->chunk_offset = 0;
+}
+
+static const GstQueryType *
+gst_mve_demux_get_src_query_types (GstPad *pad)
+{
+  static const GstQueryType src_types[] = {
+    GST_QUERY_POSITION,
+    0
+  };
+
+  return src_types;
+}
+
+static gboolean
+gst_mve_demux_handle_src_query (GstPad *pad, GstQuery *query)
+{
+  gboolean res = FALSE;
+
+  switch (GST_QUERY_TYPE (query)) {
+    case GST_QUERY_POSITION:
+    {
+      GstFormat format;
+      gst_query_parse_position (query, &format, NULL);
+
+      /* we only support TIME */
+      if (format == GST_FORMAT_TIME)
+      {
+        GstMveDemuxStream *s = gst_pad_get_element_private (pad);
+
+        if (s != NULL)
+        {
+          GST_OBJECT_LOCK (s);
+          gst_query_set_position (query, GST_FORMAT_TIME, s->last_ts);
+          GST_OBJECT_UNLOCK (s);
+          res = TRUE;
+        }
+      }
+      break;
+    }
+    default:
+      res = gst_pad_query_default (pad, query);
+  }
+
+  return res;
+}
+
+static GstStateChangeReturn
+gst_mve_demux_change_state (GstElement *element, GstStateChange transition)
+{
+  GstMveDemux *mve = GST_MVE_DEMUX (element);
+
+  if (GST_ELEMENT_CLASS (parent_class)->change_state) {
+    GstStateChangeReturn ret;
+
+    ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+    if (ret != GST_STATE_CHANGE_SUCCESS)
+      return ret;
+  }
+
+  switch (transition) {
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      gst_mve_demux_reset (mve);
+      break;
+    default:
+      break;
+  }
+
+  return GST_STATE_CHANGE_SUCCESS;
+}
+
+static gboolean
+gst_mve_add_stream (GstMveDemux *mve, GstMveDemuxStream *stream, GstTagList *list)
+{
+  GstPadTemplate *templ;
+  gboolean ret = FALSE;
+
+  if (stream->pad == NULL)
+  {
+    if (stream == mve->video_stream)
+    {
+      templ = gst_static_pad_template_get (&vidsrc_template);
+      stream->pad = gst_pad_new_from_template (templ, "video");
+    }
+    else
+    {
+      templ = gst_static_pad_template_get (&audsrc_template);
+      stream->pad = gst_pad_new_from_template (templ, "audio");
+    }
+    gst_object_unref (templ);
+
+    gst_pad_set_query_type_function (stream->pad,
+        GST_DEBUG_FUNCPTR (gst_mve_demux_get_src_query_types));
+    gst_pad_set_query_function (stream->pad,
+        GST_DEBUG_FUNCPTR (gst_mve_demux_handle_src_query));
+    gst_pad_set_element_private (stream->pad, stream);
+
+    GST_DEBUG_OBJECT (mve, "adding pad %s", GST_PAD_NAME (stream->pad));
+    gst_element_add_pad (GST_ELEMENT (mve), stream->pad);
+    ret = TRUE;
+  }
+
+  GST_DEBUG_OBJECT (mve, "setting caps %" GST_PTR_FORMAT, stream->caps);
+  gst_pad_set_caps (stream->pad, stream->caps);
+
+  if (list)
+    gst_element_found_tags_for_pad (GST_ELEMENT (mve), stream->pad, list);
+
+  return ret;
+}
+
+static GstFlowReturn gst_mve_stream_error (GstMveDemux *mve, guint16 req, guint16 avail)
+{
+  GST_ERROR_OBJECT (mve, "wanted to read %d bytes from stream, %d available", req, avail);
+  return GST_FLOW_ERROR;
+}
+
+static GstFlowReturn
+gst_mve_buffer_alloc_for_pad (GstMveDemuxStream *stream,
+                              guint32 size, GstBuffer **buffer)
+{
+  GstFlowReturn ret = gst_pad_alloc_buffer_and_set_caps (
+                         stream->pad, stream->offset,
+                         size, stream->caps, buffer);
+  if (ret == GST_FLOW_OK)
+    GST_BUFFER_TIMESTAMP (*buffer) = stream->last_ts;
+
+  return ret;
+}
+
+static GstFlowReturn
+gst_mve_video_init (GstMveDemux *mve, const guint8 *data)
+{
+  GST_DEBUG_OBJECT (mve, "init video");
+
+  if (mve->video_stream == NULL)
+  {
+    GstMveDemuxStream *stream = g_new0 (GstMveDemuxStream, 1);
+    stream->buffer = NULL;
+    stream->back_buf1 = NULL;
+    stream->back_buf2 = NULL;
+    stream->offset = 0;
+  	stream->width = 0;
+  	stream->height = 0;
+    stream->code_map = NULL;
+    stream->code_map_avail = FALSE;
+    stream->palette = NULL;
+    stream->caps = NULL;
+    stream->last_ts = GST_CLOCK_TIME_NONE;
+    mve->video_stream = stream;
+  }
+
+	return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_mve_video_create_buffer (GstMveDemux *mve, guint8 version, const guint8 *data, guint16 len)
+{
+  GstBuffer *buf;
+  guint16 w, h, n, true_color, bpp;
+  guint required, size;
+
+  GST_DEBUG_OBJECT (mve, "create video buffer");
+
+  if (mve->video_stream == NULL)
+  {
+    GST_ELEMENT_ERROR (mve, STREAM, DECODE, (NULL),
+      ("trying to create video buffer for uninitialized stream"));
+    return GST_FLOW_ERROR;
+  }
+
+  /* need 4 to 8 more bytes */
+  required = (version > 1) ? 8 : (version * 2);
+  if (len < required)
+    return gst_mve_stream_error (mve, required, len);
+
+  w = GST_READ_UINT16_LE (data) << 3;
+  h = GST_READ_UINT16_LE (data + 2)  << 3;
+
+  if (version > 0)
+    n = GST_READ_UINT16_LE (data + 4);
+  else
+    n = 1;
+
+  if (version > 1)
+    true_color = GST_READ_UINT16_LE (data + 6);
+  else
+    true_color = 0;
+
+  bpp = (true_color ? 2 : 1);
+  size = w * h * bpp;
+
+  if (mve->video_stream->buffer != NULL)
+  {
+    GST_DEBUG_OBJECT (mve, "video buffer already created");
+
+    if (GST_BUFFER_SIZE (mve->video_stream->buffer) == size*2)
+      return GST_FLOW_OK;
+
+    GST_DEBUG_OBJECT (mve, "video buffer size has changed");
+    gst_buffer_unref (mve->video_stream->buffer);
+  }
+
+  GST_DEBUG_OBJECT (mve, "allocating video buffer, w:%ld, h:%ld, n:%ld, true_color:%ld",
+                    w, h, n, true_color);
+
+  /* we need a buffer to keep the last 2 frames, since those may be
+     needed for decoding the next one */
+  buf = gst_buffer_new_and_alloc (size * 2);
+
+  mve->video_stream->bpp = bpp;
+  mve->video_stream->width = w;
+  mve->video_stream->height = h;
+  mve->video_stream->buffer = buf;
+  mve->video_stream->back_buf1 = GST_BUFFER_DATA (buf);
+  mve->video_stream->back_buf2 = mve->video_stream->back_buf1 + size;
+  mve->video_stream->max_block_offset = (h - 7) * w - 8;
+  memset (mve->video_stream->back_buf1, 0, size * 2);
+
+  return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_mve_video_palette (GstMveDemux *mve, const guint8 *data, guint16 len)
+{
+  GstBuffer *buf;
+	guint16 start, count;
+  const guint8 *pal;
+  guint32 *pal_ptr;
+  gint i;
+
+  GST_DEBUG_OBJECT (mve, "video palette");
+
+  if (mve->video_stream == NULL)
+  {
+    GST_ERROR_OBJECT (mve, "found palette before video stream was initialized");
+    return GST_FLOW_ERROR;
+  }
+
+  /* need 4 more bytes now, more later */
+  if (len < 4)
+    return gst_mve_stream_error (mve, 4, len);
+
+  len -= 4;
+
+	start = GST_READ_UINT16_LE (data);
+	count = GST_READ_UINT16_LE (data+2);
+  GST_DEBUG_OBJECT (mve, "found palette start:%ld, count:%ld", start, count);
+
+  /* need more bytes */
+  if (len < count * 3)
+    return gst_mve_stream_error (mve, count * 3, len);
+
+  /* make sure we don't exceed the buffer */
+  if (start + count > MVE_PALETTE_COUNT)
+  {
+    GST_ERROR_OBJECT (mve, "palette too large for buffer");
+    return GST_FLOW_ERROR;
+  }
+
+  if (mve->video_stream->palette != NULL)
+  {
+    /* older buffers floating around might still use the old
+       palette, so make sure we can update it */
+    buf = gst_buffer_make_writable (mve->video_stream->palette);
+  }
+  else
+  {
+    buf = gst_buffer_new_and_alloc (MVE_PALETTE_COUNT * 4);
+    memset (GST_BUFFER_DATA (buf), 0, GST_BUFFER_SIZE (buf));
+  }
+
+  mve->video_stream->palette = buf;
+
+  pal = data + 4;
+  pal_ptr = ((guint32 *) GST_BUFFER_DATA (buf)) + start;
+  for (i = 0; i < count; ++i)
+  {
+    /* convert from 6-bit VGA to 8-bit palette */
+    guint8 r, g, b;
+    r = (*pal) << 2; ++pal;
+    g = (*pal) << 2; ++pal;
+    b = (*pal) << 2; ++pal;
+    *pal_ptr = (r << 16) | (g << 8) | (b);
+    ++pal_ptr;
+  }
+  return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_mve_video_palette_compressed (GstMveDemux *mve, const guint8 *data, guint16 len)
+{
+  guint8 mask;
+  gint i, j;
+  guint32 *col;
+
+  GST_DEBUG_OBJECT (mve, "compressed video palette");
+
+  if (mve->video_stream == NULL)
+  {
+    GST_ERROR_OBJECT (mve, "found palette before video stream was initialized");
+    return GST_FLOW_ERROR;
+  }
+
+  if (mve->video_stream->palette == NULL)
+  {
+    GST_ERROR_OBJECT (mve, "no palette available for modification");
+    return GST_FLOW_ERROR;
+  }
+
+  /* need at least 32 more bytes */
+  if (len < 32)
+    return gst_mve_stream_error (mve, 32, len);
+
+  len -= 32;
+
+  for (i = 0; i < 32; ++i)
+  {
+    mask = GST_READ_UINT8 (data);
+    ++data;
+
+    if (mask != 0)
+    {
+      for (j = 0; j < 8; ++j)
+      {
+        if (mask & (1 << j))
+        {
+          guint8 r, g, b;
+
+          /* need 3 more bytes */
+          if (len < 3)
+            return gst_mve_stream_error (mve, 3, len);
+
+          len -= 3;
+
+          r = (*data) << 2; ++data;
+          g = (*data) << 2; ++data;
+          b = (*data) << 2; ++data;
+          col = ((guint32 *) GST_BUFFER_DATA (mve->video_stream->palette)) + i * 8 + j;
+          *col = (r << 16) | (g << 8) | (b);
+        }
+      }
+    }
+  }
+
+  return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_mve_video_code_map (GstMveDemux *mve, const guint8 *data, guint16 len)
+{
+  gint min;
+
+  if (mve->video_stream == NULL || mve->video_stream->code_map == NULL)
+  {
+    GST_WARNING_OBJECT (mve, "video stream not initialized");
+    return GST_FLOW_ERROR;
+  }
+
+  GST_DEBUG_OBJECT (mve, "found code map, size:%ld", len);
+
+  /* decoding is done in 8x8 blocks using 4-bit opcodes */
+  min = (mve->video_stream->width * mve->video_stream->height) / (8 * 8 * 2);
+
+  if (len < min)
+    return gst_mve_stream_error (mve, min, len);
+
+  memcpy (mve->video_stream->code_map, data, min);
+  mve->video_stream->code_map_avail = TRUE;
+  return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_mve_video_data (GstMveDemux *mve, const guint8 *data, guint16 len, GstBuffer **output)
+{
+  GstFlowReturn ret = GST_FLOW_OK;
+	gint16 cur_frame, last_frame;
+	gint16 x_offset, y_offset;
+	gint16 x_size, y_size;
+	guint16 flags;
+  gint dec;
+  GstBuffer *buf = NULL;
+  GstMveDemuxStream *s = mve->video_stream;
+
+  GST_LOG_OBJECT (mve, "video data");
+
+  if (s == NULL)
+  {
+    GST_ELEMENT_ERROR (mve, STREAM, DECODE, (NULL),
+      ("trying to decode video data before stream was initialized"));
+    return GST_FLOW_ERROR;
+  }
+
+  if (GST_CLOCK_TIME_IS_VALID(mve->frame_duration))
+  {
+    if (GST_CLOCK_TIME_IS_VALID (s->last_ts))
+      s->last_ts += mve->frame_duration;
+    else
+      s->last_ts = 0;
+  }
+
+  if (!s->code_map_avail)
+  {
+    GST_ERROR_OBJECT (mve, "no code map available for decoding");
+    return GST_FLOW_ERROR;
+  }
+
+  /* need at least 14 more bytes */
+  if (len < 14)
+    return gst_mve_stream_error (mve, 14, len);
+
+  len -= 14;
+
+	cur_frame = GST_READ_UINT16_LE (data);
+	last_frame = GST_READ_UINT16_LE (data+2);
+	x_offset = GST_READ_UINT16_LE (data+4);
+	y_offset = GST_READ_UINT16_LE (data+6);
+	x_size = GST_READ_UINT16_LE (data+8);
+	y_size = GST_READ_UINT16_LE (data+10);
+	flags = GST_READ_UINT16_LE (data+12);
+  data += 14;
+
+  GST_DEBUG_OBJECT (mve,
+    "video data hot:%d, cold:%d, xoff:%d, yoff:%d, w:%d, h:%d, flags:%x",
+    cur_frame, last_frame, x_offset, y_offset, x_size, y_size, flags);
+
+	if (flags & MVE_VIDEO_DELTA_FRAME)
+  {
+		guint8 *temp = s->back_buf1;
+		s->back_buf1 = s->back_buf2;
+		s->back_buf2 = temp;
+	}
+
+  ret = gst_mve_buffer_alloc_for_pad (s,
+     s->width * s->height * s->bpp, &buf);
+  if (ret != GST_FLOW_OK)
+    return ret;
+
+  if (s->bpp == 2)
+  {
+    dec = ipvideo_decode_frame16 (s, data, len);
+  }
+  else
+  {
+    if (s->palette == NULL)
+    {
+      GST_ELEMENT_ERROR (mve, RESOURCE, FAILED, (NULL),
+        ("no palette available"));
+      goto error;
+    }
+
+    dec = ipvideo_decode_frame8 (s, data, len);
+  }
+  if (dec != 0)
+    goto error;
+
+  memcpy (GST_BUFFER_DATA (buf), s->back_buf1, GST_BUFFER_SIZE (buf));
+  GST_BUFFER_DURATION (buf) = mve->frame_duration;
+  GST_BUFFER_OFFSET_END (buf) = ++s->offset;
+
+  if (s->bpp == 1)
+  {
+    GstCaps *caps;
+
+    /* set the palette on the outgoing buffer */
+    caps = gst_caps_copy (s->caps);
+    gst_caps_set_simple (caps,
+        "palette_data", GST_TYPE_BUFFER, s->palette,
+        NULL);
+    gst_buffer_set_caps (buf, caps);
+    gst_caps_unref (caps);
+  }
+
+  *output = buf;
+  return GST_FLOW_OK;
+
+error:
+  gst_buffer_unref (buf);
+  return GST_FLOW_ERROR;
+}
+
+static GstFlowReturn
+gst_mve_audio_init (GstMveDemux *mve, guint8 version, const guint8 *data, guint16 len)
+{
+  GstMveDemuxStream *stream;
+  guint16 flags;
+  guint32 requested_buffer;
+  GstTagList *list;
+  gchar *name;
+
+  GST_DEBUG_OBJECT (mve, "init audio");
+
+  /* need 8 more bytes */
+  if (len < 8)
+    return gst_mve_stream_error (mve, 8, len);
+
+  if (mve->audio_stream == NULL)
+  {
+    stream = g_new0 (GstMveDemuxStream, 1);
+    stream->offset = 0;
+    stream->last_ts = 0;
+    mve->audio_stream = stream;
+  }
+  else
+  {
+    stream = mve->audio_stream;
+    gst_caps_unref (stream->caps);
+  }
+
+  flags = GST_READ_UINT16_LE (data + 2);
+  stream->sample_rate = GST_READ_UINT16_LE (data + 4);
+  requested_buffer = GST_READ_UINT32_LE (data + 6);
+
+  /* bit 0: 0 = mono, 1 = stereo */
+  stream->n_channels = (flags & MVE_AUDIO_STEREO) + 1;
+  /* bit 1: 0 = 8 bit, 1 = 16 bit */
+  stream->sample_size = (((flags & MVE_AUDIO_16BIT) >> 1) + 1) * 8;
+  /* bit 2: 0 = uncompressed, 1 = compressed */
+  stream->compression = ((version > 0) && (flags & MVE_AUDIO_COMPRESSED)) ?
+                        TRUE : FALSE;
+
+  GST_DEBUG_OBJECT (mve, "audio init, sample_rate:%ld, channels:%ld, "
+                    "bits_per_sample:%ld, compression:%ld, buffer:%ld",
+                    stream->sample_rate, stream->n_channels,
+                    stream->sample_size, stream->compression, requested_buffer);
+
+  stream->caps = gst_caps_from_string ("audio/x-raw-int");
+  if (stream->caps == NULL)
+    return GST_FLOW_ERROR;
+
+  gst_caps_set_simple (stream->caps,
+      "signed", G_TYPE_BOOLEAN, (stream->sample_size == 8) ? FALSE : TRUE,
+      "depth", G_TYPE_INT, stream->sample_size,
+      "width", G_TYPE_INT, stream->sample_size,
+      "channels", G_TYPE_INT, stream->n_channels,
+      "rate", G_TYPE_INT, stream->sample_rate, NULL);
+  if (stream->sample_size > 8)
+  {
+    /* for uncompressed audio we can simply copy the incoming buffer
+       which is always in little endian format */
+    gst_caps_set_simple (stream->caps, "endianness", G_TYPE_INT,
+        (stream->compression ? G_BYTE_ORDER : LITTLE_ENDIAN), NULL);
+  }
+  else if (stream->compression)
+  {
+    GST_WARNING_OBJECT (mve, "compression is only supported for 16-bit samples");
+    stream->compression = FALSE;
+  }
+
+  list = gst_tag_list_new ();
+  name = g_strdup_printf ("Raw %d-bit PCM audio", stream->sample_size);
+  gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
+        GST_TAG_AUDIO_CODEC, name, NULL);
+  g_free (name);
+
+  if (gst_mve_add_stream (mve, stream, list))
+    return gst_pad_push_event (mve->audio_stream->pad,
+              gst_event_new_new_segment (
+                    FALSE, 1.0, GST_FORMAT_TIME,
+                    0, GST_CLOCK_TIME_NONE, 0));
+  else
+    return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_mve_audio_data (GstMveDemux *mve, guint8 type, const guint8 *data, guint16 len, GstBuffer **output)
+{
+  GstFlowReturn ret;
+  GstMveDemuxStream *s = mve->audio_stream;
+  GstBuffer *buf = NULL;
+	guint16 stream_mask;
+	guint16 size;
+
+  GST_LOG_OBJECT (mve, "audio data");
+
+  if (s == NULL)
+  {
+    GST_ELEMENT_ERROR (mve, STREAM, DECODE, (NULL),
+      ("trying to queue samples with no audio stream"));
+    return GST_FLOW_ERROR;
+  }
+
+  /* need at least 6 more bytes */
+  if (len < 6)
+    return gst_mve_stream_error (mve, 6, len);
+
+  len -= 6;
+
+  stream_mask = GST_READ_UINT16_LE (data + 2);
+  size = GST_READ_UINT16_LE (data + 4);
+  data += 6;
+
+  if (stream_mask & MVE_DEFAULT_AUDIO_STREAM)
+  {
+    guint16 n_samples = size / s->n_channels / (s->sample_size/8);
+    GstClockTime duration = (GST_SECOND / s->sample_rate) * n_samples;
+
+    if (type == 8)
+    {
+      guint16 required = (s->compression ? size / 2 + s->n_channels : size);
+
+      if (len < required)
+        return gst_mve_stream_error (mve, required, len);
+
+      ret = gst_mve_buffer_alloc_for_pad (s, size, &buf);
+
+      if (ret != GST_FLOW_OK)
+        return ret;
+
+      if (s->compression)
+        ipaudio_uncompress ((gint16 *) GST_BUFFER_DATA (buf), size,
+                            data, s->n_channels);
+      else
+        memcpy (GST_BUFFER_DATA (buf), data, size);
+
+      GST_BUFFER_DURATION (buf) = duration;
+      GST_BUFFER_OFFSET_END (buf) = s->offset + n_samples;
+
+      GST_DEBUG_OBJECT (mve, "created audio buffer, size:%ld, stream_mask:%lx",
+         size, stream_mask);
+
+      *output = buf;
+    }
+    else
+    {
+      /* silence -
+         don't return a buffer but notify downstream there won't be
+         any data in this chunk */
+      if (mve->audio_stream->pad)
+        gst_pad_push_event (mve->audio_stream->pad,
+          gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_TIME,
+                s->last_ts + duration, GST_CLOCK_TIME_NONE, 0));
+    }
+
+    s->offset += n_samples;
+    s->last_ts += duration;
+  }
+  else
+  {
+    /* alternate audio streams not supported.
+       are there any movies which use them? */
+    if (type == 8)
+      GST_WARNING_OBJECT (mve, "found non-empty alternate audio stream");
+  }
+
+  return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_mve_timer_create (GstMveDemux *mve, const guint8 *data, guint16 len, GstBuffer **buf)
+{
+  guint32 t_rate;
+  guint16 t_subdiv;
+  GstMveDemuxStream *s;
+  GstTagList *list;
+  gint rate_nom, rate_den;
+
+  g_return_val_if_fail (mve->video_stream != NULL, GST_FLOW_ERROR);
+
+  /* need 6 more bytes */
+  if (len < 6)
+    return gst_mve_stream_error (mve, 6, len);
+
+  t_rate = GST_READ_UINT32_LE (data);
+  t_subdiv = GST_READ_UINT16_LE (data + 4);
+
+  GST_DEBUG_OBJECT (mve, "found timer:%ldx%d", t_rate, t_subdiv);
+  mve->frame_duration = t_rate * t_subdiv * GST_USECOND;
+
+  /* now really start rolling... */
+  s = mve->video_stream;
+
+  if ((s->buffer == NULL) || (s->width == 0) || (s->height == 0))
+  {
+    GST_ELEMENT_ERROR (mve, STREAM, DECODE, (NULL),
+      ("missing or invalid create-video-buffer segment (%dx%d)",
+      s->width, s->height));
+    return GST_FLOW_ERROR;
+  }
+
+  if (s->pad != NULL)
+  {
+    if (s->caps != NULL) {
+      gst_caps_unref (s->caps);
+      s->caps = NULL;
+    }
+    if (s->code_map != NULL) {
+      g_free (s->code_map);
+      s->code_map = NULL;
+    }
+    list = NULL;
+  } else {
+    list = gst_tag_list_new ();
+    gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
+          GST_TAG_VIDEO_CODEC, "Raw RGB video", NULL);
+  }
+
+  s->caps = gst_caps_from_string ("video/x-raw-rgb");
+  if (s->caps == NULL)
+    return GST_FLOW_ERROR;
+
+  rate_nom = GST_SECOND / GST_USECOND;
+  rate_den = mve->frame_duration / GST_USECOND;
+
+  gst_caps_set_simple (s->caps,
+      "bpp", G_TYPE_INT, s->bpp * 8,
+      "depth", G_TYPE_INT, (s->bpp == 1) ? 8 : 15,
+      "width", G_TYPE_INT, s->width,
+      "height", G_TYPE_INT, s->height,
+      "framerate", GST_TYPE_FRACTION, rate_nom, rate_den,
+      "endianness", G_TYPE_INT, G_BYTE_ORDER,
+      NULL);
+  if (s->bpp > 1) {
+    gst_caps_set_simple (s->caps,
+        "red_mask", G_TYPE_INT, 0x7C00,    /* 31744 */
+        "green_mask", G_TYPE_INT, 0x03E0,  /*   992 */
+        "blue_mask", G_TYPE_INT, 0x001F,   /*    31 */
+        NULL);
+  }
+
+  s->code_map = g_malloc ((s->width * s->height) / (8 * 8 * 2));
+
+  if (gst_mve_add_stream (mve, s, list))
+    return gst_pad_push_event (s->pad,
+              gst_event_new_new_segment (
+                    FALSE, 1.0, GST_FORMAT_TIME,
+                    0, GST_CLOCK_TIME_NONE, 0));
+  else
+    return GST_FLOW_OK;
+}
+
+static void
+gst_mve_end_chunk (GstMveDemux *mve)
+{
+  GST_LOG_OBJECT (mve, "end of chunk");
+
+  if (mve->video_stream != NULL)
+    mve->video_stream->code_map_avail = FALSE;
+}
+
+/* parse segment */
+static GstFlowReturn
+gst_mve_parse_segment (GstMveDemux *mve, GstMveDemuxStream **stream, GstBuffer **send)
+{
+  GstFlowReturn ret = GST_FLOW_OK;
+  const guint8 *buffer, *data;
+	guint8 type, version;
+	guint16 len;
+
+  buffer = gst_adapter_peek (mve->adapter, mve->needed_bytes);
+
+  type = GST_MVE_SEGMENT_TYPE (buffer);
+
+  /* check whether to handle the segment */
+	if (type < 32)
+  {
+		version = GST_MVE_SEGMENT_VERSION (buffer);
+		len = GST_MVE_SEGMENT_SIZE (buffer);
+		data = buffer + 4;
+
+		switch (type) {
+
+			case MVE_OC_END_OF_CHUNK:
+				gst_mve_end_chunk (mve);
+				break;
+			case MVE_OC_CREATE_TIMER:
+				ret = gst_mve_timer_create (mve, data, len, send);
+        *stream = mve->audio_stream;
+				break;
+			case MVE_OC_AUDIO_BUFFERS:
+				ret = gst_mve_audio_init (mve, version, data, len);
+				break;
+			case MVE_OC_VIDEO_BUFFERS:
+				ret = gst_mve_video_create_buffer (mve, version, data, len);
+				break;
+			case MVE_OC_AUDIO_DATA:
+			case MVE_OC_AUDIO_SILENCE:
+				ret = gst_mve_audio_data (mve, type, data, len, send);
+        *stream = mve->audio_stream;
+				break;
+			case MVE_OC_VIDEO_MODE:
+				ret = gst_mve_video_init (mve, data);
+				break;
+			case MVE_OC_PALETTE:
+				ret = gst_mve_video_palette (mve, data, len);
+				break;
+      case MVE_OC_PALETTE_COMPRESSED:
+        ret = gst_mve_video_palette_compressed (mve, data, len);
+        break;
+			case MVE_OC_CODE_MAP:
+				ret = gst_mve_video_code_map (mve, data, len);
+				break;
+			case MVE_OC_VIDEO_DATA:
+				ret = gst_mve_video_data (mve, data, len, send);
+        *stream = mve->video_stream;
+				break;
+
+			case MVE_OC_END_OF_STREAM:
+			case MVE_OC_PLAY_AUDIO:
+			case MVE_OC_PLAY_VIDEO:
+        /* these are chunks we don't need to handle */
+        GST_LOG_OBJECT (mve, "ignored segment type:0x%02x, version:0x%02x", type, version);
+        break;
+      case 0x13: /* ??? */
+      case 0x14: /* ??? */
+      case 0x15: /* ??? */
+        /* these are chunks we know exist but we don't care about */
+        GST_DEBUG_OBJECT (mve, "known but unhandled segment type:0x%02x, version:0x%02x", type, version);
+        break;
+			default:
+        GST_WARNING_OBJECT (mve, "unhandled segment type:0x%02x, version:0x%02x", type, version);
+				break;
+		}
+	}
+
+  gst_adapter_flush (mve->adapter, mve->needed_bytes);
+	return ret;
+}
+
+static GstFlowReturn
+gst_mve_demux_chain (GstPad *sinkpad, GstBuffer *inbuf)
+{
+  GstMveDemux *mve = GST_MVE_DEMUX (GST_PAD_PARENT (sinkpad));
+  GstFlowReturn ret = GST_FLOW_OK;
+
+  gst_adapter_push (mve->adapter, inbuf);
+
+  GST_DEBUG_OBJECT (mve, "queuing buffer, needed:%ld, available:%ld",
+      mve->needed_bytes, gst_adapter_available (mve->adapter));
+
+  while ((gst_adapter_available (mve->adapter) >= mve->needed_bytes) &&
+      (ret == GST_FLOW_OK)) {
+    GstMveDemuxStream *stream = NULL;
+    GstBuffer *outbuf = NULL;
+
+    switch (mve->state) {
+      case MVEDEMUX_STATE_INITIAL:
+        gst_adapter_flush (mve->adapter, mve->needed_bytes);
+
+        mve->chunk_offset += mve->needed_bytes;
+        mve->needed_bytes = 4;
+        mve->state = MVEDEMUX_STATE_NEXT_CHUNK;
+        break;
+
+      case MVEDEMUX_STATE_NEXT_CHUNK: {
+        const guint8 *data;
+        guint16 size;
+
+        data = gst_adapter_peek (mve->adapter, mve->needed_bytes);
+        size = GST_MVE_SEGMENT_SIZE (data);
+
+        if (mve->chunk_offset >= mve->chunk_size)
+        {
+          /* new chunk, flush buffer and proceed with next segment */
+          guint16 chunk_type = GST_READ_UINT16_LE (data + 2);
+
+          gst_adapter_flush (mve->adapter, mve->needed_bytes);
+          mve->chunk_size = size;
+          mve->chunk_offset = 0;
+
+          if (chunk_type > MVE_CHUNK_END)
+          {
+            GST_WARNING_OBJECT (mve, "skipping unknown chunk type 0x%02x of size:%u",
+                                chunk_type, size);
+            mve->needed_bytes += size;
+            mve->state = MVEDEMUX_STATE_SKIP;
+          }
+          else
+          {
+            GST_DEBUG_OBJECT (mve, "found new chunk type 0x%02x of size:%u",
+                              chunk_type, size);
+          }
+        }
+        else if (mve->chunk_offset <= mve->chunk_size)
+        {
+          /* new segment */
+          GST_DEBUG_OBJECT (mve, "found segment type 0x%02x of size:%u",
+                            GST_MVE_SEGMENT_TYPE (data), size);
+
+          mve->needed_bytes += size;
+          mve->state = MVEDEMUX_STATE_MOVIE;
+        }
+      }
+      break;
+
+      case MVEDEMUX_STATE_MOVIE:
+        ret = gst_mve_parse_segment (mve, &stream, &outbuf);
+
+        if ((ret == GST_FLOW_OK) && (outbuf != NULL))
+        {
+          /* send buffer */
+          GST_DEBUG_OBJECT (mve,
+              "pushing buffer with time %" GST_TIME_FORMAT " (%ld bytes) on pad %s",
+              GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), GST_BUFFER_SIZE (outbuf),
+              GST_PAD_NAME (stream->pad));
+
+          ret = gst_pad_push (stream->pad, outbuf);
+        }
+
+        if (!GST_FLOW_IS_FATAL (ret))
+          ret = GST_FLOW_OK;
+
+        /* update current offset */
+        mve->chunk_offset += mve->needed_bytes;
+
+        mve->state = MVEDEMUX_STATE_NEXT_CHUNK;
+        mve->needed_bytes = 4;
+        break;
+
+      case MVEDEMUX_STATE_SKIP:
+        mve->chunk_offset += mve->needed_bytes;
+        gst_adapter_flush (mve->adapter, mve->needed_bytes);
+        mve->state = MVEDEMUX_STATE_NEXT_CHUNK;
+        mve->needed_bytes = 4;
+        break;
+
+      default:
+        GST_ERROR_OBJECT (mve, "invalid state:%d", mve->state);
+        break;
+    }
+  }
+
+  return ret;
+}
+
+static void
+gst_mve_demux_dispose (GObject *obj)
+{
+  GstMveDemux *mve = GST_MVE_DEMUX (obj);
+
+  if (mve->adapter)
+  {
+    g_object_unref (mve->adapter);
+    mve->adapter = NULL;
+  }
+
+  G_OBJECT_CLASS (parent_class)->dispose (obj);
+}
+
+static gboolean
+plugin_init (GstPlugin *plugin)
+{
+  GST_DEBUG_CATEGORY_INIT (mvedemux_debug, "mvedemux",
+      0, "Interplay MVE movie demuxer");
+
+  return gst_element_register (plugin, "mvedemux",
+			       GST_RANK_PRIMARY,
+			       GST_TYPE_MVE_DEMUX);
+}
+
+static void
+gst_mve_demux_base_init (GstMveDemuxClass *klass)
+{
+  static const GstElementDetails mve_demux_details = {
+    "MVE Demuxer",
+    "Codec/Demuxer",
+    "Demultiplex an Interplay movie (MVE) stream into audio and video",
+    "Jens Granseuer <jensgr@gmx.net>"
+  };
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+  gst_element_class_add_pad_template (element_class,
+    	gst_static_pad_template_get (&sink_template));
+  gst_element_class_add_pad_template (element_class,
+    	gst_static_pad_template_get (&vidsrc_template));
+  gst_element_class_add_pad_template (element_class,
+    	gst_static_pad_template_get (&audsrc_template));
+  gst_element_class_set_details (element_class, &mve_demux_details);
+}
+
+static void
+gst_mve_demux_class_init (GstMveDemuxClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+  parent_class = g_type_class_peek_parent (klass);
+
+  gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_mve_demux_dispose);
+
+  element_class->change_state = GST_DEBUG_FUNCPTR (gst_mve_demux_change_state);
+}
+
+static void
+gst_mve_demux_init (GstMveDemux *mve)
+{
+  mve->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
+  gst_pad_set_chain_function (mve->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_mve_demux_chain));
+  gst_element_add_pad (GST_ELEMENT (mve), mve->sinkpad);
+
+  mve->adapter = gst_adapter_new ();
+  gst_mve_demux_reset (mve);
+}
+
+static GType
+gst_mve_demux_get_type (void)
+{
+  static GType plugin_type = 0;
+
+  if (!plugin_type)
+  {
+    static const GTypeInfo plugin_info =
+    {
+      sizeof (GstMveDemuxClass),
+      (GBaseInitFunc) gst_mve_demux_base_init,
+      NULL,
+      (GClassInitFunc) gst_mve_demux_class_init,
+      NULL,
+      NULL,
+      sizeof (GstMveDemux),
+      0,
+      (GInstanceInitFunc) gst_mve_demux_init,
+    };
+    plugin_type = g_type_register_static (GST_TYPE_ELEMENT,
+	                                        "GstMveDemux",
+	                                        &plugin_info, 0);
+  }
+  return plugin_type;
+}
+
+GST_PLUGIN_DEFINE (
+  GST_VERSION_MAJOR,
+  GST_VERSION_MINOR,
+  "mvedemux",
+  "Interplay MVE movie demuxer",
+  plugin_init,
+  VERSION,
+  "LGPL",
+  "GStreamer",
+  "http://gstreamer.net"
+);
diff -urpN ../gst-plugins-bad-0.10.3/gst/mve/gstmvedemux.h ./gst/mve/gstmvedemux.h
--- ../gst-plugins-bad-0.10.3/gst/mve/gstmvedemux.h	Thu Jan  1 01:00:00 1970
+++ ./gst/mve/gstmvedemux.h	Sun Jul 30 20:22:37 2006
@@ -0,0 +1,105 @@
+/*
+ * GStreamer demultiplexer plugin for Interplay MVE movie files
+ *
+ * Copyright (C) 2006 Jens Granseuer <jensgr@gmx.net>
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_MVE_DEMUX_H__
+#define __GST_MVE_DEMUX_H__
+
+#include <gst/gst.h>
+#include <gst/base/gstadapter.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_MVE_DEMUX \
+  (gst_mve_demux_get_type())
+#define GST_MVE_DEMUX(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MVE_DEMUX,GstMveDemux))
+#define GST_MVE_DEMUX_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MVE_DEMUX,GstMveDemuxClass))
+#define GST_IS_MVE_DEMUX(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MVE_DEMUX))
+#define GST_IS_MVE_DEMUX_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MVE_DEMUX))
+
+typedef struct _GstMveDemux       GstMveDemux;
+typedef struct _GstMveDemuxClass  GstMveDemuxClass;
+typedef struct _GstMveDemuxStream GstMveDemuxStream;
+
+struct _GstMveDemux
+{
+  GstElement element;
+
+  GstPad *sinkpad;
+
+  GstMveDemuxStream *video_stream;
+  GstMveDemuxStream *audio_stream;
+
+  gint state;
+
+  /* time per frame (1/framerate) */
+  GstClockTime frame_duration;
+
+  /* push based variables */
+  guint16 needed_bytes;
+  GstAdapter *adapter;
+  
+  /* size of current chunk */
+  guint32 chunk_size;
+  /* offset in current chunk */
+  guint32 chunk_offset;
+};
+
+struct _GstMveDemuxClass 
+{
+  GstElementClass parent_class;
+};
+
+struct _GstMveDemuxStream {
+  /* shared properties */
+  GstCaps *caps;
+  GstPad *pad;
+  GstClockTime last_ts;
+  gint64 offset;
+
+  /* video properties */
+  guint16 width;
+  guint16 height;
+  guint8 bpp;   /* bytes per pixel */
+  guint8 *code_map;
+  gboolean code_map_avail;
+  guint8 *back_buf1;
+  guint8 *back_buf2;
+  guint32 max_block_offset;
+  GstBuffer *palette;
+  GstBuffer *buffer;
+
+  /* audio properties */
+  guint16 sample_rate;
+  guint16 n_channels;
+  guint16 sample_size;
+  gboolean compression;
+};
+
+GST_DEBUG_CATEGORY_EXTERN (mvedemux_debug);
+#define GST_CAT_DEFAULT mvedemux_debug
+
+G_END_DECLS
+
+#endif /* __GST_MVE_DEMUX_H__ */
diff -urpN ../gst-plugins-bad-0.10.3/gst/mve/gstmvemux.c ./gst/mve/gstmvemux.c
--- ../gst-plugins-bad-0.10.3/gst/mve/gstmvemux.c	Thu Jan  1 01:00:00 1970
+++ ./gst/mve/gstmvemux.c	Sun Jul 30 21:23:19 2006
@@ -0,0 +1,1600 @@
+/*
+ * Interplay MVE multiplexer plugin for GStreamer
+ * Copyright (C) 2006 Jens Granseuer <jensgr@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+gst-launch-0.10 filesrc location=movie.mve ! mvedemux name=d !
+    video/x-raw-rgb ! mvemux quick=true name=m !
+    filesink location=test.mve d. ! audio/x-raw-int ! m.
+*/
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <gst/gst.h>
+#include "gstmvemux.h"
+#include "mve.h"
+
+extern GstFlowReturn mve_encode_frame8  (GstMveMux *mve,
+                                         GstBuffer *frame,
+                                         const guint32 *palette,
+                                         guint16 max_data);
+extern GstFlowReturn mve_encode_frame16 (GstMveMux *mve,
+                                         GstBuffer *frame,
+                                         guint16 max_data);
+extern gint          mve_compress_audio (guint8 *dest,
+                                         const guint8 *src,
+                                         guint16 len,
+                                         guint8 channels);
+
+static const char mve_preamble[] = MVE_PREAMBLE;
+
+enum
+{
+  ARG_0,
+  ARG_AUDIO_COMPRESSION,
+  ARG_VIDEO_QUICK_ENCODING,
+  ARG_VIDEO_SCREEN_WIDTH,
+  ARG_VIDEO_SCREEN_HEIGHT
+};
+
+#define MVEMUX_DEFAULT_COMPRESSION    FALSE
+#define MVEMUX_DEFAULT_SCREEN_WIDTH   640
+#define MVEMUX_DEFAULT_SCREEN_HEIGHT  480
+
+enum MveMuxState {
+  MVEMUX_STATE_INITIAL,     /* initial state */
+  MVEMUX_STATE_CONNECTED,   /* linked, caps set, header not written */
+  MVEMUX_STATE_PREBUFFER,   /* prebuffering audio data */
+  MVEMUX_STATE_MOVIE,       /* writing the movie */
+  MVEMUX_STATE_EOS
+};
+
+static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
+  GST_PAD_SRC,
+  GST_PAD_ALWAYS,
+  GST_STATIC_CAPS ("video/x-mve")
+  );
+
+static GstStaticPadTemplate video_sink_factory =
+  GST_STATIC_PAD_TEMPLATE ("video",
+  GST_PAD_SINK,
+  GST_PAD_REQUEST,
+  GST_STATIC_CAPS (
+    "video/x-raw-rgb, "                      \
+      "width = (int) [ 24, 1600 ], "         \
+      "height = (int) [ 24, 1200 ], "        \
+      "framerate = (fraction) [ 1, MAX ], "  \
+      "bpp = (int) 16, "                     \
+      "depth = (int) 15, "                   \
+      "endianness = (int) BYTE_ORDER, "      \
+      "red_mask = (int) 31744, "             \
+      "green_mask = (int) 992, "             \
+      "blue_mask = (int) 31; "               \
+    "video/x-raw-rgb, "                      \
+      "bpp = (int) 8, "                      \
+      "depth = (int) 8, "                    \
+      "width = (int) [ 24, 1600 ], "         \
+      "height = (int) [ 24, 1200 ], "        \
+      "framerate = (fraction) [ 1, MAX ], "  \
+      "endianness = (int) BYTE_ORDER"
+  ));
+
+static GstStaticPadTemplate audio_sink_factory =
+  GST_STATIC_PAD_TEMPLATE ("audio",
+  GST_PAD_SINK,
+  GST_PAD_REQUEST,
+  GST_STATIC_CAPS (
+    "audio/x-raw-int, "                                    \
+      "width = (int) 8, "                                  \
+      "rate = (int) [ 1, MAX ], "                          \
+      "channels = (int) [ 1, 2 ], "                        \
+      "depth = (int) 8, "                                  \
+      "signed = (boolean) false; "                         \
+    "audio/x-raw-int, "                                    \
+      "width = (int) 16, "                                 \
+      "rate = (int) [ 1, MAX ], "                          \
+      "channels = (int) [ 1, 2 ], "                        \
+      "depth = (int) 16, "                                 \
+      "signed = (boolean) true, "                          \
+      "endianness = (int) BYTE_ORDER"
+  ));
+
+GST_DEBUG_CATEGORY (mvemux_debug);
+
+static GType  gst_mvemux_get_type   (void);
+static void   gst_mvemux_base_init  (GstMveMuxClass *klass);
+static void   gst_mvemux_class_init (GstMveMuxClass *klass);
+static void   gst_mvemux_init       (GstMveMux *mvemux);
+
+static GstElementClass *parent_class = NULL;
+
+static void
+gst_mvemux_reset (GstMveMux *mvemux)
+{
+  mvemux->state = MVEMUX_STATE_INITIAL;
+  mvemux->stream_time = 0;
+  mvemux->stream_offset = 0;
+  mvemux->timer = 0;
+
+  mvemux->frame_duration = GST_CLOCK_TIME_NONE;
+  mvemux->width = 0;
+  mvemux->height = 0;
+  mvemux->screen_width = MVEMUX_DEFAULT_SCREEN_WIDTH;
+  mvemux->screen_height = MVEMUX_DEFAULT_SCREEN_HEIGHT;
+  mvemux->bpp = 0;
+  mvemux->video_frames = 0;
+  mvemux->pal_changed = FALSE;
+  mvemux->pal_first_color = 0;
+  mvemux->pal_colors = MVE_PALETTE_COUNT;
+  mvemux->quick_encoding = TRUE;
+
+  mvemux->bps = 0;
+  mvemux->rate = 0;
+  mvemux->channels = 0;
+  mvemux->compression = MVEMUX_DEFAULT_COMPRESSION;
+  mvemux->next_ts = 0;
+  mvemux->max_ts = 0;
+  mvemux->spf = 0;
+  mvemux->lead_frames = 0;
+  mvemux->audio_frames = 0;
+
+  mvemux->chunk_has_palette = FALSE;
+  mvemux->chunk_has_audio = FALSE;
+
+  mvemux->audio_pad_eos = TRUE;
+  mvemux->video_pad_eos = TRUE;
+
+  g_free (mvemux->chunk_code_map);
+  mvemux->chunk_code_map = NULL;
+
+  if (mvemux->chunk_video != NULL)
+  {
+    g_byte_array_free (mvemux->chunk_video, TRUE);
+    mvemux->chunk_video = NULL;
+  }
+
+  if (mvemux->chunk_audio != NULL)
+  {
+    g_byte_array_free (mvemux->chunk_audio, TRUE);
+    mvemux->chunk_audio = NULL;
+  }
+
+  if (mvemux->last_frame != NULL)
+  {
+    gst_buffer_unref (mvemux->last_frame);
+    mvemux->last_frame = NULL;
+  }
+
+  if (mvemux->second_last_frame != NULL)
+  {
+    gst_buffer_unref (mvemux->second_last_frame);
+    mvemux->second_last_frame = NULL;
+  }
+
+  if (mvemux->audio_buffer != NULL)
+  {
+    g_queue_foreach (mvemux->audio_buffer, (GFunc) gst_mini_object_unref, NULL);
+    g_queue_free (mvemux->audio_buffer);
+  }
+  mvemux->audio_buffer = g_queue_new ();
+
+  if (mvemux->video_buffer != NULL)
+  {
+    g_queue_foreach (mvemux->video_buffer, (GFunc) gst_mini_object_unref, NULL);
+    g_queue_free (mvemux->video_buffer);
+  }
+  mvemux->video_buffer = g_queue_new ();
+}
+
+static void
+gst_mvemux_pad_link (GstPad *pad, GstPad *peer, gpointer data)
+{
+  GstMveMux *mvemux = GST_MVEMUX (data);
+
+  if (pad == mvemux->audiosink) {
+    mvemux->audio_pad_connected = TRUE;
+  } else if (pad == mvemux->videosink) {
+    mvemux->video_pad_connected = TRUE;
+  } else {
+    g_assert_not_reached ();
+  }
+
+  GST_DEBUG_OBJECT (mvemux, "pad '%s' connected", GST_PAD_NAME (pad));
+}
+
+static void
+gst_mvemux_pad_unlink (GstPad *pad, GstPad *peer, gpointer data)
+{
+  GstMveMux *mvemux = GST_MVEMUX (data);
+
+  if (pad == mvemux->audiosink) {
+    mvemux->audio_pad_connected = FALSE;
+  } else if (pad == mvemux->videosink) {
+    mvemux->video_pad_connected = FALSE;
+  } else {
+    g_assert_not_reached ();
+  }
+
+  GST_DEBUG_OBJECT (mvemux, "pad '%s' unlinked", GST_PAD_NAME (pad));
+}
+
+static void
+gst_mvemux_get_property (GObject *object,
+    guint prop_id, GValue *value, GParamSpec *pspec)
+{
+  GstMveMux *mvemux;
+
+  g_return_if_fail (GST_IS_MVEMUX (object));
+  mvemux = GST_MVEMUX (object);
+
+  switch (prop_id) {
+    case ARG_AUDIO_COMPRESSION:
+      g_value_set_boolean (value, mvemux->compression);
+      break;
+    case ARG_VIDEO_QUICK_ENCODING:
+      g_value_set_boolean (value, mvemux->quick_encoding);
+      break;
+    case ARG_VIDEO_SCREEN_WIDTH:
+      g_value_set_uint (value, mvemux->screen_width);
+      break;
+    case ARG_VIDEO_SCREEN_HEIGHT:
+      g_value_set_uint (value, mvemux->screen_height);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_mvemux_set_property (GObject *object,
+    guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+  GstMveMux *mvemux;
+
+  g_return_if_fail (GST_IS_MVEMUX (object));
+  mvemux = GST_MVEMUX (object);
+
+  switch (prop_id) {
+    case ARG_AUDIO_COMPRESSION:
+      mvemux->compression = g_value_get_boolean (value);
+      break;
+    case ARG_VIDEO_QUICK_ENCODING:
+      mvemux->quick_encoding = g_value_get_boolean (value);
+      break;
+    case ARG_VIDEO_SCREEN_WIDTH:
+      mvemux->screen_width = g_value_get_uint (value);
+      break;
+    case ARG_VIDEO_SCREEN_HEIGHT:
+      mvemux->screen_height = g_value_get_uint (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static GstStateChangeReturn
+gst_mvemux_change_state (GstElement *element, GstStateChange transition)
+{
+  GstMveMux *mvemux;
+
+  g_return_val_if_fail (GST_IS_MVEMUX (element), GST_STATE_CHANGE_FAILURE);
+
+  mvemux = GST_MVEMUX (element);
+
+  if (GST_ELEMENT_CLASS (parent_class)->change_state) {
+    GstStateChangeReturn ret;
+
+    ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+    if (ret != GST_STATE_CHANGE_SUCCESS)
+      return ret;
+  }
+
+  switch (transition) {
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      gst_mvemux_reset (mvemux);
+      break;
+    default:
+      break;
+  }
+
+  return GST_STATE_CHANGE_SUCCESS;
+}
+
+static const GstBuffer *
+gst_mvemux_palette_from_buffer (GstBuffer *buf)
+{
+  const GstBuffer *palette = NULL;
+  GstCaps *caps = GST_BUFFER_CAPS (buf);
+
+  if (caps != NULL)
+  {
+    GstStructure *str = gst_caps_get_structure (caps, 0);
+    const GValue *pal = gst_structure_get_value (str, "palette_data");
+
+    if (pal != NULL)
+    {
+      palette = gst_value_get_buffer (pal);
+      if (GST_BUFFER_SIZE (palette) < 256 * 4)
+        palette = NULL;
+    }
+  }
+  return palette;
+}
+
+static GstFlowReturn
+gst_mvemux_palette_from_current_frame (GstMveMux *mvemux, const GstBuffer **pal)
+{
+  GstBuffer *buf = g_queue_peek_head (mvemux->video_buffer);
+
+  /* get palette from buffer */
+  *pal = gst_mvemux_palette_from_buffer (buf);
+  if (*pal == NULL)
+  {
+    GST_ERROR_OBJECT (mvemux, "video buffer has no palette data");
+    return GST_FLOW_ERROR;
+  }
+  return GST_FLOW_OK;
+}
+
+static void
+gst_mvemux_palette_analyze (GstMveMux *mvemux, const GstBuffer *pal,
+                            guint16 *first, guint16 *last)
+{
+  guint i;
+  guint32 *col1;
+
+  col1 = (guint32 *) GST_BUFFER_DATA (pal);
+
+  /* compare current palette against last frame */
+  if (mvemux->last_frame == NULL)
+  {
+    /* ignore 0,0,0 entries but make sure we get
+       at least one color */
+    /* FIXME: is ignoring 0,0,0 safe? possibly depends on player impl */
+    for (i = 0; i < MVE_PALETTE_COUNT; ++i)
+    {
+      if (col1[i] != 0)
+      {
+        *first = i;
+        break;
+      }
+    }
+    if (i == MVE_PALETTE_COUNT)
+    {
+      *first = *last = 0;
+    }
+    else
+    {
+      for (i = MVE_PALETTE_COUNT - 1; i >= 0; --i)
+      {
+        if (col1[i] != 0)
+        {
+          *last = i;
+          break;
+        }
+      }
+    }
+  }
+  else
+  {
+    const GstBuffer *last_pal;
+    guint32 *col2;
+
+    last_pal = gst_mvemux_palette_from_buffer (mvemux->last_frame);
+
+    g_return_if_fail (last_pal != NULL);
+
+    col2 = (guint32 *) GST_BUFFER_DATA (last_pal);
+
+    for (i = 0; i < MVE_PALETTE_COUNT; ++i)
+    {
+      if (col1[i] != col2[i])
+      {
+        *first = i;
+        break;
+      }
+    }
+    for (i = MVE_PALETTE_COUNT - 1; i >= 0; --i)
+    {
+      if (col1[i] != col2[i])
+      {
+        *last = i;
+        break;
+      }
+    }
+  }
+
+  GST_DEBUG_OBJECT (mvemux, "palette first:%d, last:%d",
+                    *first, *last);
+}
+
+static gboolean
+gst_mvemux_palette_changed (GstMveMux *mvemux, const GstBuffer *pal)
+{
+  const GstBuffer *last_pal;
+
+  g_return_val_if_fail (mvemux->last_frame != NULL, TRUE);
+
+  last_pal = gst_mvemux_palette_from_buffer (mvemux->last_frame);
+  if (last_pal == NULL)
+    return TRUE;
+
+  return memcmp (GST_BUFFER_DATA (last_pal), GST_BUFFER_DATA (pal),
+                 MVE_PALETTE_COUNT * 4) != 0;
+}
+
+static GstFlowReturn
+gst_mvemux_push_buffer (GstMveMux *mvemux, GstBuffer *buffer)
+{
+  GST_BUFFER_OFFSET (buffer) = mvemux->stream_offset;
+  mvemux->stream_offset += GST_BUFFER_SIZE (buffer);
+  GST_BUFFER_OFFSET_END (buffer) = mvemux->stream_offset;
+  return gst_pad_push (mvemux->source, buffer);
+}
+
+/* returns TRUE if audio segment is complete */
+static gboolean
+gst_mvemux_audio_data (GstMveMux *mvemux)
+{
+  gboolean complete = FALSE;
+
+  while (!complete)
+  {
+    GstBuffer *buf;
+    GstClockTime buftime;
+    GstClockTime duration;
+    GstClockTime t_needed;
+    gint b_needed;
+    gint len;
+
+    buf = g_queue_peek_head (mvemux->audio_buffer);
+    if (buf == NULL)
+      return (mvemux->audio_pad_eos && mvemux->chunk_audio) ||
+             (mvemux->stream_time + mvemux->frame_duration < mvemux->max_ts);
+
+    buftime = GST_BUFFER_TIMESTAMP (buf);
+    duration = GST_BUFFER_DURATION (buf);
+
+    /* FIXME: adjust buffer timestamps using segment info */
+
+    /* assume continuous buffers on invalid time stamps */
+    if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (buftime)))
+      buftime = mvemux->next_ts;
+
+    if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (duration)))
+      duration = gst_util_uint64_scale_int (mvemux->frame_duration,
+                        GST_BUFFER_SIZE (buf), mvemux->spf);
+
+    if (mvemux->chunk_audio)
+    {
+      b_needed = mvemux->spf - mvemux->chunk_audio->len;
+      t_needed = (gint) gst_util_uint64_scale_int (mvemux->frame_duration,
+                        b_needed, mvemux->spf);
+    }
+    else
+    {
+      b_needed = mvemux->spf;
+      t_needed = mvemux->frame_duration;
+    }
+
+    if (buftime > mvemux->next_ts + t_needed)
+    {
+      /* future buffer - fill chunk with silence */
+      GST_DEBUG_OBJECT (mvemux, "future buffer, inserting silence");
+
+      /* if we already have a chunk started, fill it
+         otherwise we'll simply insert a silence chunk */
+      if (mvemux->chunk_audio)
+      {
+        len = mvemux->chunk_audio->len;
+        g_byte_array_set_size (mvemux->chunk_audio, mvemux->spf);
+        memset (mvemux->chunk_audio->data + len, 0, mvemux->spf - len);
+      }
+      mvemux->next_ts += t_needed;
+      complete = TRUE;
+    }
+    else if (buftime + duration <= mvemux->next_ts)
+    {
+      /* past buffer - drop */
+      GST_DEBUG_OBJECT (mvemux, "dropping past buffer");
+      g_queue_pop_head (mvemux->audio_buffer);
+      gst_buffer_unref (buf);
+    }
+    else
+    {
+      /* our data starts somewhere in this buffer */
+      const guint8 *bufdata = GST_BUFFER_DATA (buf);
+      gint b_available = GST_BUFFER_SIZE (buf);
+      gint align = (mvemux->bps / 8) * mvemux->channels - 1;
+      gint offset;
+
+      if (mvemux->chunk_audio == NULL)
+        mvemux->chunk_audio = g_byte_array_sized_new (mvemux->spf);
+
+      if (buftime >= mvemux->next_ts)
+      {
+        /* insert silence as necessary */
+        len = mvemux->chunk_audio->len;
+        offset = (gint) gst_util_uint64_scale_int (mvemux->spf,
+                 buftime - mvemux->next_ts, mvemux->frame_duration);
+        offset = (offset + align) & ~align;
+
+        if (len < offset)
+        {
+          g_byte_array_set_size (mvemux->chunk_audio, offset);
+          memset (mvemux->chunk_audio->data + len, 0, offset - len);
+          b_needed -= offset - len;
+          mvemux->next_ts += gst_util_uint64_scale_int (mvemux->frame_duration,
+                                offset - len, mvemux->spf);
+        }
+        offset = 0;
+      }
+      else
+      {
+        offset = (gint) gst_util_uint64_scale_int (mvemux->spf,
+                 mvemux->next_ts - buftime, mvemux->frame_duration);
+        offset = (offset + align) & ~align;
+      }
+
+      g_assert (offset <= b_available);
+
+      bufdata += offset;
+      b_available -= offset;
+      if (b_needed > b_available)
+        b_needed = b_available;
+
+      if (mvemux->bps == 8)
+      {
+        g_byte_array_append (mvemux->chunk_audio, bufdata, b_needed);
+      }
+      else
+      {
+        guint i;
+        gint16 *sample = (gint16 *) bufdata;
+        guint8 s[2];
+
+        len = b_needed / 2;
+        for (i = 0; i < len; ++i)
+        {
+          s[0] = (*sample) & 0x00FF;
+          s[1] = ((*sample) & 0xFF00) >> 8;
+          g_byte_array_append (mvemux->chunk_audio, s, 2);
+          ++sample;
+        }
+      }
+
+      mvemux->next_ts += gst_util_uint64_scale_int (mvemux->frame_duration,
+                              b_needed, mvemux->spf);
+
+      if (b_available - b_needed == 0)
+      {
+        /* consumed buffer */
+        GST_LOG_OBJECT (mvemux, "popping consumed buffer");
+        g_queue_pop_head (mvemux->audio_buffer);
+        gst_buffer_unref (buf);
+      }
+
+      complete = (mvemux->chunk_audio->len >= mvemux->spf);
+    }
+
+    if (mvemux->max_ts < mvemux->next_ts)
+      mvemux->max_ts = mvemux->next_ts;
+  }
+
+  return complete;
+}
+
+static GstFlowReturn
+gst_mvemux_start_movie (GstMveMux *mvemux)
+{
+  GstFlowReturn res;
+  GstBuffer *buf;
+
+  GST_DEBUG_OBJECT (mvemux, "writing movie preamble");
+
+  res = gst_pad_alloc_buffer (mvemux->source, 0,
+              MVE_PREAMBLE_SIZE,
+              GST_PAD_CAPS (mvemux->source),
+              &buf);
+
+  if (res != GST_FLOW_OK)
+    return res;
+
+  gst_pad_push_event (mvemux->source,
+      gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0));
+
+  memcpy (GST_BUFFER_DATA (buf), mve_preamble, MVE_PREAMBLE_SIZE);
+  return gst_mvemux_push_buffer (mvemux, buf);
+}
+
+static GstFlowReturn
+gst_mvemux_end_movie (GstMveMux *mvemux)
+{
+  GstFlowReturn res;
+  GstBuffer *buf;
+  guint8 *bufdata;
+
+  GST_DEBUG_OBJECT (mvemux, "writing movie shutdown chunk");
+
+  res = gst_pad_alloc_buffer (mvemux->source, 0, 16,
+              GST_PAD_CAPS (mvemux->source),
+              &buf);
+
+  if (res != GST_FLOW_OK)
+    return res;
+
+  bufdata = GST_BUFFER_DATA (buf);
+
+  GST_WRITE_UINT16_LE (bufdata, 8);       /* shutdown chunk */
+  GST_WRITE_UINT16_LE (bufdata + 2, MVE_CHUNK_SHUTDOWN);
+  GST_WRITE_UINT16_LE (bufdata + 4, 0);   /* end movie segment */
+  bufdata[6] = MVE_OC_END_OF_STREAM;
+  bufdata[7] = 0;
+  GST_WRITE_UINT16_LE (bufdata + 8, 0);   /* end chunk segment */
+  bufdata[10] = MVE_OC_END_OF_CHUNK;
+  bufdata[11] = 0;
+
+  GST_WRITE_UINT16_LE (bufdata + 12, 0);  /* end movie chunk */
+  GST_WRITE_UINT16_LE (bufdata + 14, MVE_CHUNK_END);
+
+  return gst_mvemux_push_buffer (mvemux, buf);
+}
+
+static GstFlowReturn
+gst_mvemux_init_video_chunk (GstMveMux *mvemux, const GstBuffer *pal)
+{
+  GstFlowReturn res;
+  GstBuffer *buf;
+  guint8 *bufdata;
+  guint16 buf_size;
+  guint16 first_col = 0, last_col = 0;
+  guint pal_size = 0;
+
+  GST_DEBUG_OBJECT (mvemux, "init-video chunk w:%d, h:%d, bpp:%d",
+                    mvemux->width, mvemux->height, mvemux->bpp);
+
+  buf_size = 4;      /* chunk header */
+  buf_size += 4 + 6; /* init video mode segment */
+  buf_size += 4 + 8; /* create video buffers segment */
+
+  if (mvemux->bpp == 8)
+  {
+    g_return_val_if_fail (pal != NULL, GST_FLOW_ERROR);
+
+    /* install palette segment */
+    gst_mvemux_palette_analyze (mvemux, pal, &first_col, &last_col);
+    pal_size = (last_col - first_col + 1) * 3;
+    buf_size += 4 + 4 + pal_size;
+  }
+
+  buf_size += 4 + 0; /* end chunk segment */
+
+  res = gst_pad_alloc_buffer (mvemux->source, 0, buf_size,
+              GST_PAD_CAPS (mvemux->source), &buf);
+  if (res != GST_FLOW_OK)
+    return res;
+
+  bufdata = GST_BUFFER_DATA (buf);
+
+  GST_WRITE_UINT16_LE (bufdata, buf_size - 4);
+  GST_WRITE_UINT16_LE (bufdata + 2, MVE_CHUNK_INIT_VIDEO);
+
+  GST_WRITE_UINT16_LE (bufdata + 4, 6);
+  bufdata[6] = MVE_OC_VIDEO_MODE;
+  bufdata[7] = 0;
+  GST_WRITE_UINT16_LE (bufdata + 8, mvemux->screen_width);   /* screen width */
+  GST_WRITE_UINT16_LE (bufdata + 10, mvemux->screen_height); /* screen height */
+  GST_WRITE_UINT16_LE (bufdata + 12, 0);                     /* ??? - flags */
+
+  GST_WRITE_UINT16_LE (bufdata + 14, 8);
+  bufdata[16] = MVE_OC_VIDEO_BUFFERS;
+  bufdata[17] = 2;
+  GST_WRITE_UINT16_LE (bufdata + 18, mvemux->width >> 3);   /* buffer width */
+  GST_WRITE_UINT16_LE (bufdata + 20, mvemux->height >> 3);  /* buffer height */
+  GST_WRITE_UINT16_LE (bufdata + 22, 1);                    /* buffer count */
+  GST_WRITE_UINT16_LE (bufdata + 24, (mvemux->bpp >> 3) - 1); /* true color */
+
+  bufdata += 26;
+
+  if (mvemux->bpp == 8)
+  {
+    /* TODO: check whether we really need to update the entire palette (or at all) */
+    gint i;
+    guint32 *col;
+
+    GST_DEBUG_OBJECT (mvemux, "installing palette");
+
+    GST_WRITE_UINT16_LE (bufdata, 4 + pal_size);
+    bufdata[2] = MVE_OC_PALETTE;
+    bufdata[3] = 0;
+    GST_WRITE_UINT16_LE (bufdata + 4, first_col);                /* first color index */
+    GST_WRITE_UINT16_LE (bufdata + 6, last_col - first_col + 1); /* number of colors */
+
+    bufdata += 8;
+    col = (guint32 *) GST_BUFFER_DATA (pal);
+    for (i = first_col; i <= last_col; ++i)
+    {
+      /* convert from 8-bit palette to 6-bit VGA */
+      guint32 rgb = col[i];
+      (*bufdata) = ((rgb & 0x00FF0000) >> 16) >> 2; ++bufdata;
+      (*bufdata) = ((rgb & 0x0000FF00) >> 8) >> 2; ++bufdata;
+      (*bufdata) = (rgb & 0x000000FF) >> 2; ++bufdata;
+    }
+
+    mvemux->pal_changed = TRUE;
+    mvemux->pal_first_color = first_col;
+    mvemux->pal_colors = last_col - first_col + 1;
+  }
+
+  GST_WRITE_UINT16_LE (bufdata, 0);
+  bufdata[2] = MVE_OC_END_OF_CHUNK;
+  bufdata[3] = 0;
+
+  return gst_mvemux_push_buffer (mvemux, buf);
+}
+
+static GstFlowReturn
+gst_mvemux_init_audio_chunk (GstMveMux *mvemux)
+{
+  GstFlowReturn res;
+  GstBuffer *buf;
+  guint16 buf_size;
+  guint8 *bufdata;
+  guint16 flags = 0;
+  gint align;
+
+  GST_DEBUG_OBJECT (mvemux, "init-audio chunk rate:%d, chan:%d, bps:%d, comp:%d",
+                    mvemux->rate, mvemux->channels, mvemux->bps, mvemux->compression);
+
+  if (G_UNLIKELY (mvemux->bps == 8 && mvemux->compression))
+  {
+    GST_INFO_OBJECT (mvemux, "compression only supported for 16-bit samples, disabling");
+    mvemux->compression = FALSE;
+  }
+
+  /* calculate sample data per frame */
+  align = (mvemux->bps / 8) * mvemux->channels;
+  mvemux->spf = (guint16) (gst_util_uint64_scale_int (
+      align * mvemux->rate, mvemux->frame_duration, GST_SECOND) +
+      align - 1) & ~(align - 1);
+
+  /* prebuffer approx. 1 second of audio data */
+  mvemux->lead_frames = align * mvemux->rate / mvemux->spf;
+  GST_DEBUG_OBJECT (mvemux, "calculated spf:%d, lead frames:%d",
+                    mvemux->spf, mvemux->lead_frames);
+
+  /* chunk header + init video mode segment + end chunk segment */
+  buf_size = 4 + (4 + 10) + 4;
+
+  res = gst_pad_alloc_buffer (mvemux->source, 0, buf_size,
+              GST_PAD_CAPS (mvemux->source), &buf);
+  if (res != GST_FLOW_OK)
+    return res;
+
+  bufdata = GST_BUFFER_DATA (buf);
+
+  if (mvemux->channels == 2)
+    flags |= MVE_AUDIO_STEREO;
+  if (mvemux->bps == 16)
+    flags |= MVE_AUDIO_16BIT;
+  if (mvemux->compression)
+    flags |= MVE_AUDIO_COMPRESSED;
+
+  GST_WRITE_UINT16_LE (bufdata, buf_size - 4);
+  GST_WRITE_UINT16_LE (bufdata + 2, MVE_CHUNK_INIT_AUDIO);
+
+  GST_WRITE_UINT16_LE (bufdata + 4, 10);
+  bufdata[6] = MVE_OC_AUDIO_BUFFERS;
+  bufdata[7] = 1;
+  GST_WRITE_UINT16_LE (bufdata + 8, 0);             /* ??? */
+  GST_WRITE_UINT16_LE (bufdata + 10, flags);        /* flags */
+  GST_WRITE_UINT16_LE (bufdata + 12, mvemux->rate); /* sample rate */
+  GST_WRITE_UINT32_LE (bufdata + 14,                /* minimum audio buffer size */
+      mvemux->spf * mvemux->lead_frames);
+
+  GST_WRITE_UINT16_LE (bufdata + 18, 0);
+  bufdata[20] = MVE_OC_END_OF_CHUNK;
+  bufdata[21] = 0;
+
+  return gst_mvemux_push_buffer (mvemux, buf);
+}
+
+static guint8 *
+gst_mvemux_write_audio_segments (GstMveMux *mvemux, guint8 *data)
+{
+  GByteArray *chunk = mvemux->chunk_audio;
+  guint16 silent_mask;
+
+  GST_LOG_OBJECT (mvemux, "writing audio data");
+
+  /* audio data */
+  if (chunk)
+  {
+    guint16 len = mvemux->compression ?
+                  chunk->len / 2 + mvemux->channels :
+                  chunk->len;
+
+    silent_mask = 0xFFFE;
+
+    GST_WRITE_UINT16_LE (data, 6 + len);
+    data[2] = MVE_OC_AUDIO_DATA;
+    data[3] = 0;
+    GST_WRITE_UINT16_LE (data + 4, mvemux->audio_frames); /* frame number */
+    GST_WRITE_UINT16_LE (data + 6, 0x0001);               /* stream mask */
+    GST_WRITE_UINT16_LE (data + 8, chunk->len);           /* (uncompressed) data length */
+    data += 10;
+
+    if (mvemux->compression)
+      mve_compress_audio (data, chunk->data, len, mvemux->channels);
+    else
+      memcpy (data, chunk->data, chunk->len);
+    data += len;
+
+    g_byte_array_free (chunk, TRUE);
+    mvemux->chunk_audio = NULL;
+  }
+  else
+    silent_mask = 0xFFFF;
+
+  /* audio data (silent) */
+  GST_WRITE_UINT16_LE (data, 6);
+  data[2] = MVE_OC_AUDIO_SILENCE;
+  data[3] = 0;
+  GST_WRITE_UINT16_LE (data + 4, mvemux->audio_frames++); /* frame number */
+  GST_WRITE_UINT16_LE (data + 6, silent_mask);            /* stream mask */
+  GST_WRITE_UINT16_LE (data + 8, mvemux->spf);            /* (imaginary) data length */
+  data += 10;
+
+  return data;
+}
+
+static GstFlowReturn
+gst_mvemux_prebuffer_audio_chunk (GstMveMux *mvemux)
+{
+  GstFlowReturn ret;
+  GstBuffer *chunk;
+  guint16 size;
+  guint8 *data;
+
+  /* calculate chunk size */
+  size = 4;                                   /* chunk header */
+
+  if (mvemux->chunk_audio)
+  {
+    size += 4 + 6 +                           /* audio data */
+            (mvemux->compression ?
+             mvemux->chunk_audio->len / 2 + mvemux->channels :
+             mvemux->chunk_audio->len);
+  }
+  size += 4 + 6;                              /* audio data silent */
+  size += 4;                                  /* end chunk */
+
+  ret = gst_pad_alloc_buffer (mvemux->source, 0, size,
+                              GST_PAD_CAPS (mvemux->source), &chunk);
+  if (ret != GST_FLOW_OK)
+    return ret;
+
+  data = GST_BUFFER_DATA (chunk);
+
+  /* assemble chunk */
+  GST_WRITE_UINT16_LE (data, size - 4);
+  GST_WRITE_UINT16_LE (data + 2, MVE_CHUNK_AUDIO_ONLY);
+  data += 4;
+
+  data = gst_mvemux_write_audio_segments (mvemux, data);
+
+  /* end chunk */
+  GST_WRITE_UINT16_LE (data, 0);
+  data[2] = MVE_OC_END_OF_CHUNK;
+  data[3] = 0;
+
+  if (mvemux->audio_frames >= mvemux->lead_frames)
+    mvemux->state = MVEMUX_STATE_MOVIE;
+
+  mvemux->stream_time += mvemux->frame_duration;
+
+  GST_DEBUG_OBJECT (mvemux, "pushing audio chunk");
+
+  return gst_mvemux_push_buffer (mvemux, chunk);
+}
+
+static GstFlowReturn
+gst_mvemux_push_chunk (GstMveMux *mvemux)
+{
+  GstFlowReturn ret;
+  GstBuffer *chunk;
+  GstBuffer *frame;
+  guint32 size;
+  guint16 cm_size = 0;
+  guint8 *data;
+
+  /* calculate chunk size */
+  size = 4;   /* chunk header */
+
+  if (G_UNLIKELY (mvemux->timer == 0))
+  {
+    /* we need to insert a timer segment */
+    size += 4 + 6;
+  }
+
+  if (mvemux->audio_pad_connected)
+  {
+    if (mvemux->chunk_audio)
+    {
+      size += 4 + 6 +                           /* audio data */
+              (mvemux->compression ?
+               mvemux->chunk_audio->len / 2 + mvemux->channels :
+               mvemux->chunk_audio->len);
+    }
+    size += 4 + 6;                              /* audio data silent */
+  }
+
+  size += 4 + 6;  /* play video */
+  size += 4;      /* play audio; present even if no audio stream */
+  size += 4;      /* end chunk */
+
+  /* we must encode video only after we have the audio side
+     covered, since only then we can tell what size limit
+     the video data must adhere to */
+  frame = g_queue_pop_head (mvemux->video_buffer);
+  if (frame != NULL)
+  {
+    cm_size = (((mvemux->width * mvemux->height) >> 6) + 1) >> 1;
+    size += 4 + cm_size;    /* code map */
+    size += 4 + 14;         /* video data header */
+
+    /* make sure frame is writable since the encoder may want to modify it */
+    frame = gst_buffer_make_writable (frame);
+
+    if (mvemux->bpp == 8) {
+      const GstBuffer *pal = gst_mvemux_palette_from_buffer (frame);
+      if (pal == NULL)
+        ret = GST_FLOW_ERROR;
+      else
+        ret = mve_encode_frame8 (mvemux, frame,
+          (guint32 *) GST_BUFFER_DATA (pal), G_MAXUINT16 - size);
+    } else
+      ret = mve_encode_frame16 (mvemux, frame, G_MAXUINT16 - size);
+
+    if (mvemux->second_last_frame != NULL)
+      gst_buffer_unref (mvemux->second_last_frame);
+    mvemux->second_last_frame = mvemux->last_frame;
+    mvemux->last_frame = frame;
+
+    if (ret != GST_FLOW_OK)
+      return ret;
+
+    size += mvemux->chunk_video->len;
+  }
+
+  if (size > G_MAXUINT16)
+  {
+    GST_ELEMENT_ERROR (mvemux, STREAM, ENCODE, (NULL),
+           ("encoding frame %d failed: maximum block size exceeded (%lu)",
+           mvemux->video_frames+1, size));
+    return GST_FLOW_ERROR;
+  }
+
+  ret = gst_pad_alloc_buffer (mvemux->source, 0, size,
+                              GST_PAD_CAPS (mvemux->source), &chunk);
+  if (ret != GST_FLOW_OK)
+    return ret;
+
+  data = GST_BUFFER_DATA (chunk);
+
+  /* assemble chunk */
+  GST_WRITE_UINT16_LE (data, size - 4);
+  GST_WRITE_UINT16_LE (data + 2, MVE_CHUNK_VIDEO);
+  data += 4;
+
+  if (G_UNLIKELY (mvemux->timer == 0))
+  {
+    /* insert a timer segment */
+    mvemux->timer = mvemux->frame_duration / GST_USECOND / 8;
+
+    GST_WRITE_UINT16_LE (data, 6);
+    data[2] = MVE_OC_CREATE_TIMER;
+    data[3] = 0;
+    GST_WRITE_UINT32_LE (data + 4, mvemux->timer); /* timer rate */
+    GST_WRITE_UINT16_LE (data + 8, 8);             /* timer subdivision */
+    data += 10;
+  }
+
+  /* code map */
+  if (mvemux->chunk_video)
+  {
+    GST_WRITE_UINT16_LE (data, cm_size);
+    data[2] = MVE_OC_CODE_MAP;
+    data[3] = 0;
+    memcpy (data + 4, mvemux->chunk_code_map, cm_size);
+    data += 4 + cm_size;
+  }
+
+  if (mvemux->audio_pad_connected)
+    data = gst_mvemux_write_audio_segments (mvemux, data);
+
+  if (mvemux->chunk_video)
+  {
+    GST_LOG_OBJECT (mvemux, "writing video data");
+
+    /* video data */
+    GST_WRITE_UINT16_LE (data, 14 + mvemux->chunk_video->len);
+    data[2] = MVE_OC_VIDEO_DATA;
+    data[3] = 0;
+    GST_WRITE_UINT16_LE (data + 6, mvemux->video_frames);     /* previous frame */
+    GST_WRITE_UINT16_LE (data + 4, ++mvemux->video_frames);   /* current frame */
+    GST_WRITE_UINT16_LE (data + 8, 0);                        /* x offset */
+    GST_WRITE_UINT16_LE (data + 10, 0);                       /* y offset */
+    GST_WRITE_UINT16_LE (data + 12, mvemux->width >> 3);      /* buffer width */
+    GST_WRITE_UINT16_LE (data + 14, mvemux->height >> 3);     /* buffer height */
+    GST_WRITE_UINT16_LE (data + 16,                           /* flags */
+           (mvemux->video_frames == 1 ? 0 : MVE_VIDEO_DELTA_FRAME));
+    memcpy (data + 18, mvemux->chunk_video->data, mvemux->chunk_video->len);
+    data += 18 + mvemux->chunk_video->len;
+
+    g_byte_array_free (mvemux->chunk_video, TRUE);
+    mvemux->chunk_video = NULL;
+  }
+
+  /* play audio */
+  GST_WRITE_UINT16_LE (data, 0);
+  data[2] = MVE_OC_PLAY_AUDIO;
+  data[3] = 0;
+  data += 4;
+
+  /* play video */
+  GST_WRITE_UINT16_LE (data, 6);
+  data[2] = MVE_OC_PLAY_VIDEO;
+  data[3] = 1;
+  /* this block is only set to non-zero on palette changes in 8-bit mode */
+  if (mvemux->pal_changed)
+  {
+    GST_WRITE_UINT16_LE (data + 4, mvemux->pal_first_color); /* index of first color */
+    GST_WRITE_UINT16_LE (data + 6, mvemux->pal_colors);      /* number of colors */
+    mvemux->pal_changed = FALSE;
+  }
+  else
+  {
+    GST_WRITE_UINT32_LE (data + 4, 0);
+  }
+  GST_WRITE_UINT16_LE (data + 8, 0);    /* ??? */
+  data += 10;
+
+  /* end chunk */
+  GST_WRITE_UINT16_LE (data, 0);
+  data[2] = MVE_OC_END_OF_CHUNK;
+  data[3] = 0;
+
+  mvemux->chunk_has_palette = FALSE;
+  mvemux->chunk_has_audio = FALSE;
+  mvemux->stream_time += mvemux->frame_duration;
+
+  GST_LOG_OBJECT (mvemux, "pushing video chunk");
+
+  return gst_mvemux_push_buffer (mvemux, chunk);
+}
+
+static GstFlowReturn
+gst_mvemux_chain (GstPad *sinkpad, GstBuffer *inbuf)
+{
+  GstMveMux *mvemux = GST_MVEMUX (GST_PAD_PARENT (sinkpad));
+  GstFlowReturn ret = GST_FLOW_OK;
+  const GstBuffer *palette;
+  gboolean audio_ok, video_ok;
+
+  /* need to serialize the buffers */
+  g_mutex_lock (mvemux->lock);
+
+  if (G_LIKELY (inbuf != NULL)) /* TODO: see _sink_event... */
+  {
+    if (sinkpad == mvemux->audiosink)
+      g_queue_push_tail (mvemux->audio_buffer, inbuf);
+    else if (sinkpad == mvemux->videosink)
+      g_queue_push_tail (mvemux->video_buffer, inbuf);
+    else
+      g_assert_not_reached ();
+  }
+
+  /* TODO: this is gross... */
+  if (G_UNLIKELY (mvemux->state == MVEMUX_STATE_INITIAL))
+  {
+    GST_DEBUG_OBJECT (mvemux, "waiting for caps");
+    goto done;
+  }
+
+  /* now actually try to mux something */
+  if (G_UNLIKELY (mvemux->state == MVEMUX_STATE_CONNECTED))
+  {
+    palette = NULL;
+
+    if (mvemux->bpp == 8)
+    {
+      /* we need to add palette info to the init chunk */
+      if (g_queue_is_empty (mvemux->video_buffer))
+        goto done;   /* wait for more data */
+
+      ret = gst_mvemux_palette_from_current_frame (mvemux, &palette);
+      if (ret != GST_FLOW_OK)
+        goto done;
+    }
+
+    gst_mvemux_start_movie (mvemux);
+    gst_mvemux_init_video_chunk (mvemux, palette);
+    mvemux->chunk_has_palette = TRUE;
+
+    if (mvemux->audio_pad_connected)
+    {
+      gst_mvemux_init_audio_chunk (mvemux);
+
+      mvemux->state = MVEMUX_STATE_PREBUFFER;
+    }
+    else mvemux->state = MVEMUX_STATE_MOVIE;
+  }
+
+  while ((mvemux->state == MVEMUX_STATE_PREBUFFER) && (ret == GST_FLOW_OK) &&
+         gst_mvemux_audio_data (mvemux))
+  {
+    ret = gst_mvemux_prebuffer_audio_chunk (mvemux);
+  }
+
+  if (G_LIKELY (mvemux->state >= MVEMUX_STATE_MOVIE))
+  {
+    audio_ok = !mvemux->audio_pad_connected || 
+               !g_queue_is_empty (mvemux->audio_buffer) ||
+               (mvemux->audio_pad_eos && (mvemux->stream_time <= mvemux->max_ts));
+    video_ok = !g_queue_is_empty (mvemux->video_buffer) ||
+               (mvemux->video_pad_eos &&
+               (!mvemux->audio_pad_eos || (mvemux->stream_time <= mvemux->max_ts)));
+
+    while ((ret == GST_FLOW_OK) && audio_ok && video_ok)
+    {
+
+      if (!g_queue_is_empty (mvemux->video_buffer))
+      {
+        if ((mvemux->bpp == 8) && !mvemux->chunk_has_palette)
+        {
+          ret = gst_mvemux_palette_from_current_frame (mvemux, &palette);
+          if (ret != GST_FLOW_OK)
+            goto done;
+
+          if (gst_mvemux_palette_changed (mvemux, palette))
+            gst_mvemux_init_video_chunk (mvemux, palette);
+          mvemux->chunk_has_palette = TRUE;
+        }
+      }
+
+      /* audio data */
+      if (mvemux->audio_pad_connected && !mvemux->chunk_has_audio &&
+          gst_mvemux_audio_data (mvemux))
+        mvemux->chunk_has_audio = TRUE;
+
+      if ((!g_queue_is_empty (mvemux->video_buffer) || mvemux->video_pad_eos) &&
+          (mvemux->chunk_has_audio || !mvemux->audio_pad_connected || mvemux->audio_pad_eos))
+      {
+        ret = gst_mvemux_push_chunk (mvemux);
+      }
+
+      audio_ok = !mvemux->audio_pad_connected || 
+                 !g_queue_is_empty (mvemux->audio_buffer) ||
+                 (mvemux->audio_pad_eos && (mvemux->stream_time <= mvemux->max_ts));
+      video_ok = !g_queue_is_empty (mvemux->video_buffer) ||
+                 (mvemux->video_pad_eos &&
+                 (!mvemux->audio_pad_eos || (mvemux->stream_time <= mvemux->max_ts)));
+    }
+  }
+
+  if (G_UNLIKELY ((mvemux->state == MVEMUX_STATE_EOS) && (ret == GST_FLOW_OK)))
+  {
+    ret = gst_mvemux_end_movie (mvemux);
+    gst_pad_push_event (mvemux->source, gst_event_new_eos ());
+  }
+
+done:
+  g_mutex_unlock (mvemux->lock);
+  return ret;
+}
+
+static gboolean
+gst_mvemux_sink_event (GstPad *pad, GstEvent *event)
+{
+  gboolean res = TRUE;
+  GstMveMux *mvemux = GST_MVEMUX (GST_PAD_PARENT (pad));
+
+  GST_DEBUG_OBJECT (mvemux, "got %s event for pad %s",
+      GST_EVENT_TYPE_NAME (event), GST_PAD_NAME (pad));
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_EOS:
+      if (pad == mvemux->audiosink)
+      {
+        mvemux->audio_pad_eos = TRUE;
+
+        if (mvemux->state == MVEMUX_STATE_PREBUFFER)
+          mvemux->state = MVEMUX_STATE_MOVIE;
+      }
+      else if (pad == mvemux->videosink)
+        mvemux->video_pad_eos = TRUE;
+
+      /* TODO: this is evil */
+      if (mvemux->audio_pad_eos && mvemux->video_pad_eos)
+      {
+        mvemux->state = MVEMUX_STATE_EOS;
+        gst_mvemux_chain (pad, NULL);
+      }
+      gst_event_unref (event);
+      break;
+    case GST_EVENT_NEWSEGMENT:
+      if (pad == mvemux->audiosink)
+      {
+        GstFormat format;
+        gint64 start;
+        gboolean update;
+
+        gst_event_parse_new_segment (event, &update, NULL, &format, &start, NULL, NULL);
+        if ((format == GST_FORMAT_TIME) && update && (start > mvemux->max_ts))
+          mvemux->max_ts = start;
+      }
+      gst_event_unref (event);
+      break;
+    default:
+      res = gst_pad_event_default (pad, event);
+      break;
+  }
+
+  return res;
+}
+
+static gboolean
+gst_mvemux_vidsink_set_caps (GstPad *pad, GstCaps *vscaps)
+{
+  GstMveMux *mvemux;
+  GstStructure *structure;
+  GstClockTime duration;
+  const GValue *fps;
+  gint w, h, bpp;
+  gboolean ret;
+
+  mvemux = GST_MVEMUX (GST_PAD_PARENT (pad));
+
+  GST_DEBUG_OBJECT (mvemux, "video set_caps triggered on %s",
+      GST_PAD_NAME (pad));
+
+  structure = gst_caps_get_structure (vscaps, 0);
+
+  ret = gst_structure_get_int (structure, "width", &w);
+  ret &= gst_structure_get_int (structure, "height", &h);
+  ret &= gst_structure_get_int (structure, "bpp", &bpp);
+  fps = gst_structure_get_value (structure, "framerate");
+  ret &= (fps != NULL && GST_VALUE_HOLDS_FRACTION (fps));
+
+  duration = gst_util_uint64_scale_int (GST_SECOND,
+                 gst_value_get_fraction_denominator (fps),
+                 gst_value_get_fraction_numerator (fps));
+
+  if (!ret)
+    return FALSE;
+
+  /* don't allow changing width, height, bpp, or framerate */
+  if (mvemux->state != MVEMUX_STATE_INITIAL)
+  {
+    if (mvemux->width != w || mvemux->height != h ||
+       mvemux->bpp != bpp || mvemux->frame_duration != duration)
+    {
+      GST_ERROR_OBJECT (mvemux, "caps renegotiation not allowed");
+      return FALSE;
+    }
+  }
+  else
+  {
+    if (w % 8 != 0 || h % 8 != 0)
+    {
+      GST_ERROR_OBJECT (mvemux, "width and height must be multiples of 8");
+      return FALSE;
+    }
+
+    mvemux->width = w;
+    mvemux->height = h;
+    mvemux->bpp = bpp;
+    mvemux->frame_duration = duration;
+
+    if (mvemux->screen_width < w)
+    {
+      GST_INFO_OBJECT (mvemux, "setting suggested screen width to %d", w);
+      mvemux->screen_width = w;
+    }
+    if (mvemux->screen_height < h)
+    {
+      GST_INFO_OBJECT (mvemux, "setting suggested screen height to %d", h);
+      mvemux->screen_height = h;
+    }
+
+    g_free (mvemux->chunk_code_map);
+    mvemux->chunk_code_map = g_malloc ((((w * h) >> 6) + 1) >> 1);
+
+    /* audio caps already initialized? */
+    if (mvemux->bps != 0 || !mvemux->audio_pad_connected)
+      mvemux->state = MVEMUX_STATE_CONNECTED;
+  }
+
+  return TRUE;
+}
+
+static gboolean
+gst_mvemux_audsink_set_caps (GstPad *pad, GstCaps *ascaps)
+{
+  GstMveMux *mvemux;
+  GstStructure *structure;
+  gboolean ret;
+  gint val;
+
+  mvemux = GST_MVEMUX (GST_PAD_PARENT (pad));
+
+  GST_DEBUG_OBJECT (mvemux, "audio set_caps triggered on %s",
+      GST_PAD_NAME (pad));
+
+  /* don't allow caps renegotiation for now */
+  if (mvemux->state != MVEMUX_STATE_INITIAL)
+    return FALSE;
+
+  structure = gst_caps_get_structure (ascaps, 0);
+
+  ret = gst_structure_get_int (structure, "channels", &val);
+  mvemux->channels = val;
+  ret &= gst_structure_get_int (structure, "rate", &val);
+  mvemux->rate = val;
+  ret &= gst_structure_get_int (structure, "width", &val);
+  mvemux->bps = val;
+
+  /* video caps already initialized? */
+  if (mvemux->bpp != 0)
+    mvemux->state = MVEMUX_STATE_CONNECTED;
+
+  return ret;
+}
+
+static GstPad *
+gst_mvemux_request_new_pad (GstElement *element,
+    GstPadTemplate *templ, const gchar *req_name)
+{
+  GstMveMux *mvemux = GST_MVEMUX (element);
+  GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
+  GstPad *pad;
+
+  g_return_val_if_fail (templ != NULL, NULL);
+
+  if (templ->direction != GST_PAD_SINK) {
+    GST_WARNING_OBJECT (mvemux, "request pad is not a SINK pad");
+    return NULL;
+  }
+
+  if (templ == gst_element_class_get_pad_template (klass, "audio")) {
+    if (mvemux->audiosink)
+      return NULL;
+
+    mvemux->audiosink = gst_pad_new_from_template (templ, "audio");
+    gst_pad_set_setcaps_function (mvemux->audiosink, gst_mvemux_audsink_set_caps);
+    mvemux->audio_pad_eos = FALSE;
+    pad = mvemux->audiosink;
+  } else if (templ == gst_element_class_get_pad_template (klass, "video")) {
+    if (mvemux->videosink)
+      return NULL;
+
+    mvemux->videosink = gst_pad_new_from_template (templ, "video");
+    gst_pad_set_setcaps_function (mvemux->videosink, gst_mvemux_vidsink_set_caps);
+    mvemux->video_pad_eos = FALSE;
+    pad = mvemux->videosink;
+  } else {
+    g_assert_not_reached ();
+  }
+
+  gst_pad_set_chain_function (pad, GST_DEBUG_FUNCPTR (gst_mvemux_chain));
+  gst_pad_set_event_function (pad, GST_DEBUG_FUNCPTR (gst_mvemux_sink_event));
+
+  g_signal_connect (pad, "linked", G_CALLBACK (gst_mvemux_pad_link), mvemux);
+  g_signal_connect (pad, "unlinked", G_CALLBACK (gst_mvemux_pad_unlink), mvemux);
+
+  gst_element_add_pad (element, pad);
+  return pad;
+}
+
+static void
+gst_mvemux_release_pad (GstElement *element, GstPad *pad)
+{
+  GstMveMux *mvemux = GST_MVEMUX (element);
+
+  gst_element_remove_pad (element, pad);
+
+  if (pad == mvemux->audiosink)
+  {
+    mvemux->audiosink = NULL;
+    mvemux->audio_pad_connected = FALSE;
+  }
+  else if (pad == mvemux->videosink)
+  {
+    mvemux->videosink = NULL;
+    mvemux->video_pad_connected = FALSE;
+  }
+}
+
+static void
+gst_mvemux_base_init (GstMveMuxClass *klass)
+{
+  static const GstElementDetails gst_mvemux_details =
+      GST_ELEMENT_DETAILS ("MVE Multiplexer",
+      "Codec/Muxer",
+      "Muxes audio and video into an MVE stream",
+      "Jens Granseuer <jensgr@gmx.net>");
+
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&src_factory));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&audio_sink_factory));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&video_sink_factory));
+
+  gst_element_class_set_details (element_class, &gst_mvemux_details);
+}
+
+static void
+gst_mvemux_finalize (GObject *object)
+{
+  GstMveMux *mvemux = GST_MVEMUX (object);
+
+  g_mutex_free (mvemux->lock);
+
+  if (mvemux->audio_buffer)
+  {
+    g_queue_free (mvemux->audio_buffer);
+    mvemux->audio_buffer = NULL;
+  }
+
+  if (mvemux->video_buffer)
+  {
+    g_queue_free (mvemux->video_buffer);
+    mvemux->video_buffer = NULL;
+  }
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_mvemux_class_init (GstMveMuxClass *klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gstelement_class = GST_ELEMENT_CLASS (klass);
+
+  parent_class = g_type_class_peek_parent (klass);
+
+  gobject_class->finalize = gst_mvemux_finalize;
+
+  gobject_class->get_property = gst_mvemux_get_property;
+  gobject_class->set_property = gst_mvemux_set_property;
+
+  g_object_class_install_property (gobject_class, ARG_AUDIO_COMPRESSION,
+      g_param_spec_boolean ("compression", "Audio compression",
+          "Whether to compress audio data", MVEMUX_DEFAULT_COMPRESSION,
+          G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, ARG_VIDEO_QUICK_ENCODING,
+      g_param_spec_boolean ("quick", "Quick encoding",
+          "Whether to disable expensive encoding operations", TRUE,
+          G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, ARG_VIDEO_SCREEN_WIDTH,
+      g_param_spec_uint ("screen-width", "Screen width",
+          "Suggested screen width", 320, 1600,
+          MVEMUX_DEFAULT_SCREEN_WIDTH, G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, ARG_VIDEO_SCREEN_HEIGHT,
+      g_param_spec_uint ("screen-height", "Screen height",
+          "Suggested screen height", 200, 1200,
+          MVEMUX_DEFAULT_SCREEN_HEIGHT, G_PARAM_READWRITE));
+
+  gstelement_class->request_new_pad = gst_mvemux_request_new_pad;
+  gstelement_class->release_pad = gst_mvemux_release_pad;
+
+  gstelement_class->change_state = gst_mvemux_change_state;
+}
+
+static void
+gst_mvemux_init (GstMveMux *mvemux)
+{
+  GstElementClass *klass = GST_ELEMENT_GET_CLASS (mvemux);
+
+  mvemux->source = gst_pad_new_from_template (
+        gst_element_class_get_pad_template (klass, "src"), "src");
+  gst_element_add_pad (GST_ELEMENT (mvemux), mvemux->source);
+
+  mvemux->lock = g_mutex_new ();
+
+  mvemux->audiosink = NULL;
+  mvemux->videosink = NULL;
+  mvemux->audio_pad_connected = FALSE;
+  mvemux->video_pad_connected = FALSE;
+
+  /* audio/video metadata initialisation */
+  mvemux->last_frame = NULL;
+  mvemux->second_last_frame = NULL;
+  mvemux->chunk_code_map = NULL;
+  mvemux->chunk_video = NULL;
+  mvemux->chunk_audio = NULL;
+  mvemux->audio_buffer = NULL;
+  mvemux->video_buffer = NULL;
+
+  gst_mvemux_reset (mvemux);
+}
+
+static GType
+gst_mvemux_get_type (void)
+{
+  static GType mvemux_type = 0;
+
+  if (!mvemux_type) {
+    static const GTypeInfo mvemux_info = {
+      sizeof (GstMveMuxClass),
+      (GBaseInitFunc) gst_mvemux_base_init,
+      NULL,
+      (GClassInitFunc) gst_mvemux_class_init,
+      NULL,
+      NULL,
+      sizeof (GstMveMux),
+      0,
+      (GInstanceInitFunc) gst_mvemux_init,
+    };
+
+    mvemux_type = g_type_register_static (
+        GST_TYPE_ELEMENT, "GstMveMux", &mvemux_info, 0);
+  }
+  return mvemux_type;
+}
+
+static gboolean
+plugin_init (GstPlugin *plugin)
+{
+  GST_DEBUG_CATEGORY_INIT (mvemux_debug, "mvemux",
+      0, "Interplay MVE movie muxer");
+
+  return gst_element_register (plugin, "mvemux",
+			       GST_RANK_NONE,
+			       GST_TYPE_MVEMUX);
+}
+
+GST_PLUGIN_DEFINE (
+  GST_VERSION_MAJOR,
+  GST_VERSION_MINOR,
+  "mvemux",
+  "Interplay MVE movie muxer",
+  plugin_init,
+  VERSION,
+  "LGPL",
+  "GStreamer",
+  "http://gstreamer.net"
+);
diff -urpN ../gst-plugins-bad-0.10.3/gst/mve/gstmvemux.h ./gst/mve/gstmvemux.h
--- ../gst-plugins-bad-0.10.3/gst/mve/gstmvemux.h	Thu Jan  1 01:00:00 1970
+++ ./gst/mve/gstmvemux.h	Sun Jul 30 20:22:37 2006
@@ -0,0 +1,121 @@
+/* 
+ * Interplay MVE muxer plugin for GStreamer
+ * Copyright (C) 2006 Jens Granseuer <jensgr@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_MVEMUX_H__
+#define __GST_MVEMUX_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_MVEMUX \
+  (gst_mvemux_get_type())
+#define GST_MVEMUX(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MVEMUX,GstMveMux))
+#define GST_MVEMUX_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MVEMUX,GstMveMux))
+#define GST_IS_MVEMUX(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MVEMUX))
+#define GST_IS_MVEMUX_CLASS(obj) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MVEMUX))
+
+
+typedef struct _GstMveMux GstMveMux;
+typedef struct _GstMveMuxClass GstMveMuxClass;
+
+struct _GstMveMux {
+  GstElement element;
+  GMutex *lock;
+
+  /* pads */
+  GstPad *source;
+  GstPad *videosink;
+  GstPad *audiosink;
+
+  gboolean audio_pad_connected;
+  gboolean audio_pad_eos;
+  gboolean video_pad_connected;
+  gboolean video_pad_eos;
+
+  guint64 stream_offset;
+  /* audio stream time, really */
+  GstClockTime stream_time;
+  guint timer;
+  gint state;
+
+  /* ticks per frame */
+  GstClockTime frame_duration;
+
+  /* video stream properties */
+  guint16 width, height;
+  guint16 screen_width, screen_height;
+  /* bits per pixel */
+  guint8 bpp;
+  /* previous frames */
+  GstBuffer *last_frame;
+  GstBuffer *second_last_frame;
+  /* number of encoded frames */
+  guint16 video_frames;
+  /* palette handling */
+  gboolean pal_changed;
+  guint16 pal_first_color;
+  guint16 pal_colors;
+  /* whether to use expensive opcodes */
+  gboolean quick_encoding;
+
+  /* audio stream properties */
+  /* bits per sample */
+  guint8 bps;
+  guint32 rate;
+  guint8 channels;
+  gboolean compression;
+  /* current audio stream time */
+  GstClockTime next_ts;
+  /* maximum audio time we know about */
+  GstClockTime max_ts;
+  /* sample bytes per frame */
+  guint16 spf;
+  /* number of frames to use for audio lead-in */
+  guint16 lead_frames;
+  /* number of encoded frames */
+  guint16 audio_frames;
+
+  /* current chunk */
+  guint8 *chunk_code_map;
+  GByteArray *chunk_video;
+  GByteArray *chunk_audio;
+  gboolean chunk_has_palette;
+  gboolean chunk_has_audio;
+
+  /* buffers for incoming data */
+  GQueue *audio_buffer;
+  GQueue *video_buffer;
+};
+
+struct _GstMveMuxClass {
+  GstElementClass parent_class;
+};
+
+GST_DEBUG_CATEGORY_EXTERN (mvemux_debug);
+#define GST_CAT_DEFAULT mvemux_debug
+
+G_END_DECLS
+
+#endif /* __GST_MVEMUX_H__ */
diff -urpN ../gst-plugins-bad-0.10.3/gst/mve/mve.h ./gst/mve/mve.h
--- ../gst-plugins-bad-0.10.3/gst/mve/mve.h	Thu Jan  1 01:00:00 1970
+++ ./gst/mve/mve.h	Sun Jul 30 20:22:37 2006
@@ -0,0 +1,62 @@
+/*
+ * Interplay MVE movie definitions
+ *
+ * Copyright (C) 2006 Jens Granseuer <jensgr@gmx.net>
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __MVE_H__
+#define __MVE_H__
+
+#define MVE_PREAMBLE      "Interplay MVE File\032\000\032\000\000\001\063\021"
+#define MVE_PREAMBLE_SIZE 26
+
+#define MVE_PALETTE_COUNT 256
+
+/* MVE chunk types */
+#define MVE_CHUNK_INIT_AUDIO          0x0000
+#define MVE_CHUNK_AUDIO_ONLY          0x0001
+#define MVE_CHUNK_INIT_VIDEO          0x0002
+#define MVE_CHUNK_VIDEO               0x0003
+#define MVE_CHUNK_SHUTDOWN            0x0004
+#define MVE_CHUNK_END                 0x0005
+
+/* MVE segment opcodes */
+#define MVE_OC_END_OF_STREAM          0x00
+#define MVE_OC_END_OF_CHUNK           0x01
+#define MVE_OC_CREATE_TIMER           0x02
+#define MVE_OC_AUDIO_BUFFERS          0x03
+#define MVE_OC_PLAY_AUDIO             0x04
+#define MVE_OC_VIDEO_BUFFERS          0x05
+#define MVE_OC_PLAY_VIDEO             0x07
+#define MVE_OC_AUDIO_DATA             0x08
+#define MVE_OC_AUDIO_SILENCE          0x09
+#define MVE_OC_VIDEO_MODE             0x0A
+#define MVE_OC_PALETTE                0x0C
+#define MVE_OC_PALETTE_COMPRESSED     0x0D
+#define MVE_OC_CODE_MAP               0x0F
+#define MVE_OC_VIDEO_DATA             0x11
+
+/* audio flags */
+#define MVE_AUDIO_STEREO              0x0001
+#define MVE_AUDIO_16BIT               0x0002
+#define MVE_AUDIO_COMPRESSED          0x0004
+
+/* video flags */
+#define MVE_VIDEO_DELTA_FRAME         0x0001
+
+#endif /* __MVE_H__ */
diff -urpN ../gst-plugins-bad-0.10.3/gst/mve/mveaudiodec.c ./gst/mve/mveaudiodec.c
--- ../gst-plugins-bad-0.10.3/gst/mve/mveaudiodec.c	Thu Jan  1 01:00:00 1970
+++ ./gst/mve/mveaudiodec.c	Sun Jul 30 20:22:37 2006
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2003 The ffmpeg Project, Mike Melanson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Interplay compressed audio codec by Mike Melanson (melanson@pcisys.net)
+ */
+
+#include <gst/gst.h>
+
+static const short delta_table[256] =
+{
+         0,      1,      2,      3,      4,      5,      6,      7,      8,      9,     10,     11,     12,     13,     14,     15,
+        16,     17,     18,     19,     20,     21,     22,     23,     24,     25,     26,     27,     28,     29,     30,     31,
+        32,     33,     34,     35,     36,     37,     38,     39,     40,     41,     42,     43,     47,     51,     56,     61,
+        66,     72,     79,     86,     94,    102,    112,    122,    133,    145,    158,    173,    189,    206,    225,    245,
+       267,    292,    318,    348,    379,    414,    452,    493,    538,    587,    640,    699,    763,    832,    908,    991,
+      1081,   1180,   1288,   1405,   1534,   1673,   1826,   1993,   2175,   2373,   2590,   2826,   3084,   3365,   3672,   4008,
+      4373,   4772,   5208,   5683,   6202,   6767,   7385,   8059,   8794,   9597,  10472,  11428,  12471,  13609,  14851,  16206,
+     17685,  19298,  21060,  22981,  25078,  27367,  29864,  32589, -29973, -26728, -23186, -19322, -15105, -10503,  -5481,     -1,
+         1,      1,   5481,  10503,  15105,  19322,  23186,  26728,  29973, -32589, -29864, -27367, -25078, -22981, -21060, -19298,
+    -17685, -16206, -14851, -13609, -12471, -11428, -10472,  -9597,  -8794,  -8059,  -7385,  -6767,  -6202,  -5683,  -5208,  -4772,
+     -4373,  -4008,  -3672,  -3365,  -3084,  -2826,  -2590,  -2373,  -2175,  -1993,  -1826,  -1673,  -1534,  -1405,  -1288,  -1180,
+     -1081,   -991,   -908,   -832,   -763,   -699,   -640,   -587,   -538,   -493,   -452,   -414,   -379,   -348,   -318,   -292,
+      -267,   -245,   -225,   -206,   -189,   -173,   -158,   -145,   -133,   -122,   -112,   -102,    -94,    -86,    -79,    -72,
+       -66,    -61,    -56,    -51,    -47,    -43,    -42,    -41,    -40,    -39,    -38,    -37,    -36,    -35,    -34,    -33,
+       -32,    -31,    -30,    -29,    -28,    -27,    -26,    -25,    -24,    -23,    -22,    -21,    -20,    -19,    -18,    -17,
+       -16,    -15,    -14,    -13,    -12,    -11,    -10,     -9,     -8,     -7,     -6,     -5,     -4,     -3,     -2,     -1
+};
+
+void
+ipaudio_uncompress (short *buffer, unsigned short buf_len,
+                    const unsigned char *data, unsigned char channels)
+{
+  int i, out = 0;
+  int predictor[2];
+  int channel_number = 0;
+
+  for (i = 0; i < channels; ++i)
+  {
+    predictor[i] = GST_READ_UINT16_LE (data);
+    data += 2;
+    if (predictor[i] & 0x8000)
+      predictor[i] -= 0x10000;
+    buffer[out++] = predictor[i];
+  }
+
+  /* we count in 16-bit ints, so adjust the buffer size */
+  buf_len /= 2;
+  while (out < buf_len)
+  {
+    predictor[channel_number] += delta_table[*data++];
+    if (predictor[channel_number] < -32768)
+      predictor[channel_number] = -32768;
+    else if (predictor[channel_number] > 32767)
+      predictor[channel_number] = 32767;
+    buffer[out++] = predictor[channel_number];
+
+    /* toggle channel */
+    channel_number ^= channels - 1;
+  }
+}
diff -urpN ../gst-plugins-bad-0.10.3/gst/mve/mveaudioenc.c ./gst/mve/mveaudioenc.c
--- ../gst-plugins-bad-0.10.3/gst/mve/mveaudioenc.c	Thu Jan  1 01:00:00 1970
+++ ./gst/mve/mveaudioenc.c	Sun Jul 30 20:22:37 2006
@@ -0,0 +1,121 @@
+/* 
+ * Interplay MVE audio compressor
+ * Copyright (C) 2003, 2004 Alexander Belyakov <abel@krasu.ru>
+ * Copyright (C) 2006 Jens Granseuer <jensgr@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <math.h>
+#include <gst/gst.h>
+
+static const gint32 dec_table[256] = {
+         0,      1,      2,      3,      4,      5,      6,      7,      8,      9,     10,     11,     12,     13,     14,     15,
+        16,     17,     18,     19,     20,     21,     22,     23,     24,     25,     26,     27,     28,     29,     30,     31,
+        32,     33,     34,     35,     36,     37,     38,     39,     40,     41,     42,     43,     47,     51,     56,     61,
+        66,     72,     79,     86,     94,    102,    112,    122,    133,    145,    158,    173,    189,    206,    225,    245,
+       267,    292,    318,    348,    379,    414,    452,    493,    538,    587,    640,    699,    763,    832,    908,    991,
+      1081,   1180,   1288,   1405,   1534,   1673,   1826,   1993,   2175,   2373,   2590,   2826,   3084,   3365,   3672,   4008,
+      4373,   4772,   5208,   5683,   6202,   6767,   7385,   8059,   8794,   9597,  10472,  11428,  12471,  13609,  14851,  16206,
+     17685,  19298,  21060,  22981,  25078,  27367,  29864,  32589,  35563,  38808,  42350,  46214,  50431,  55033,  60055,  65535,
+         1, -65535, -60055, -55033, -50431, -46214, -42350, -38808, -35563, -32589, -29864, -27367, -25078, -22981, -21060, -19298,
+    -17685, -16206, -14851, -13609, -12471, -11428, -10472,  -9597,  -8794,  -8059,  -7385,  -6767,  -6202,  -5683,  -5208,  -4772,
+     -4373,  -4008,  -3672,  -3365,  -3084,  -2826,  -2590,  -2373,  -2175,  -1993,  -1826,  -1673,  -1534,  -1405,  -1288,  -1180,
+     -1081,   -991,   -908,   -832,   -763,   -699,   -640,   -587,   -538,   -493,   -452,   -414,   -379,   -348,   -318,   -292,
+      -267,   -245,   -225,   -206,   -189,   -173,   -158,   -145,   -133,   -122,   -112,   -102,    -94,    -86,    -79,    -72,
+       -66,    -61,    -56,    -51,    -47,    -43,    -42,    -41,    -40,    -39,    -38,    -37,    -36,    -35,    -34,    -33,
+       -32,    -31,    -30,    -29,    -28,    -27,    -26,    -25,    -24,    -23,    -22,    -21,    -20,    -19,    -18,    -17,
+       -16,    -15,    -14,    -13,    -12,    -11,    -10,     -9,     -8,     -7,     -6,     -5,     -4,     -3,     -2,     -1
+};
+
+/* This value could be non-optimal. Without knowledge of the value
+   distribution in the real signal, the actual optimum cannot be evaluated.
+   Should be somewhere between 11.458 and 11.542. */
+static const gdouble DPCM_SCALE = 11.5131;
+
+static gint8
+mve_enc_delta (guint n)
+{
+  if (n < 44)
+    return n;
+  return floor (DPCM_SCALE * log (n));
+}
+
+gint
+mve_compress_audio (guint8 *dest, const guint8 *src, guint16 len, guint8 channels)
+{
+  gint16 prev[2], s;
+  gint delta, real_res;
+  gint cur_chan;
+  guint8 v;
+
+  for (cur_chan = 0; cur_chan < channels; ++cur_chan)
+  {
+    prev[cur_chan] = GST_READ_UINT16_LE (src);
+    GST_WRITE_UINT16_LE (dest, prev[cur_chan]);
+    src += 2;
+    dest += 2;
+    len -= 2;
+  }
+
+  cur_chan = 0;
+  while (len > 0)
+  {
+    s = GST_READ_UINT16_LE (src);
+    src += 2;
+
+    delta = s - prev[cur_chan];
+
+    if (delta >= 0)
+      v = mve_enc_delta (delta);
+    else
+      v = 256 - mve_enc_delta (-delta);
+
+    real_res = dec_table[v] + prev[cur_chan];
+
+    if (real_res < -32768 || real_res > 32767)
+    {
+      /* correct overflow */
+      /* GST_DEBUG ("co:%d + %d = %d -> new v:%d, dec_table:%d will be %d",
+                 prev[cur_chan], dec_table[v], real_res,
+                 v, dec_table[v], prev[cur_chan]+dec_table[v]); */
+      if (s > 0)
+      {
+        if (real_res > 32767) --v;
+      }
+      else
+      {
+        if (real_res < -32768) ++v;
+      }
+
+      real_res = dec_table[v] + prev[cur_chan];
+    }
+
+    if (G_UNLIKELY (abs (real_res - s) > 32767))
+    {
+      GST_ERROR ("sign loss left unfixed in audio stream, deviation:%ld", real_res - s);
+      return -1;
+    }
+
+    *dest++ = v;
+    --len;
+    /* use previous output instead of input. That way output will not go too far from input. */
+    prev[cur_chan] += dec_table[v];
+    cur_chan = channels - 1 - cur_chan;
+  }
+
+  return 0;
+}
diff -urpN ../gst-plugins-bad-0.10.3/gst/mve/mvevideodec16.c ./gst/mve/mvevideodec16.c
--- ../gst-plugins-bad-0.10.3/gst/mve/mvevideodec16.c	Thu Jan  1 01:00:00 1970
+++ ./gst/mve/mvevideodec16.c	Sun Jul 30 20:22:37 2006
@@ -0,0 +1,816 @@
+/*
+ * Interplay MVE Video Decoder (16 bit)
+ * Copyright (C) 2003 the ffmpeg project, Mike Melanson
+ *           (C) 2006 Jens Granseuer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * For more information about the Interplay MVE format, visit:
+ *   http://www.pcisys.net/~melanson/codecs/interplay-mve.txt
+ */
+
+#include "gstmvedemux.h"
+
+#define PIXEL(s) GST_READ_UINT16_LE (s)
+
+#define CHECK_STREAM(l, n) \
+  do { \
+    if (G_UNLIKELY (*(l) < (n))) { \
+      GST_ERROR ("wanted to read %d bytes from stream, %d available", (n), *(l)); \
+      return -1; \
+    } \
+    *(l) -= (n); \
+  } while (0)
+
+/* copy an 8x8 block from the stream to the frame buffer */
+static int
+ipvideo_copy_block (const GstMveDemuxStream *s, unsigned short *frame,
+                    const unsigned short *src, int offset)
+{
+  int i;
+  int frame_offset;
+
+  frame_offset = frame - (unsigned short *) s->back_buf1 + offset;
+
+  if (G_UNLIKELY (frame_offset < 0)) {
+    GST_ERROR ("frame offset < 0 (%ld)", frame_offset);
+    return -1;
+  }
+  else if (G_UNLIKELY (frame_offset > s->max_block_offset)) {
+    GST_ERROR ("frame offset above limit (%ld > %ld)",
+               frame_offset, s->max_block_offset);
+    return -1;
+  }
+
+  for (i = 0; i < 8; ++i)
+  {
+    memcpy (frame, src, 16);
+    frame += s->width;
+    src += s->width;
+  }
+
+  return 0;
+}
+
+static int
+ipvideo_decode_0x2 (const GstMveDemuxStream *s, unsigned short *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  unsigned char B;
+  int x, y;
+  int offset;
+
+  /* copy block from 2 frames ago using a motion vector */
+  CHECK_STREAM (len, 1);
+  B = *(*data)++;
+
+  if (B < 56) {
+    x = 8 + (B % 7);
+    y = B / 7;
+  } else {
+    x = -14 + ((B - 56) % 29);
+    y =   8 + ((B - 56) / 29);
+  }
+  offset = y * s->width + x;
+
+  return ipvideo_copy_block (s, frame, frame + offset, offset);
+}
+
+static int
+ipvideo_decode_0x3 (const GstMveDemuxStream *s, unsigned short *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  unsigned char B;
+  int x, y;
+  int offset;
+
+  /* copy 8x8 block from current frame from an up/left block */
+  CHECK_STREAM (len, 1);
+  B = *(*data)++;
+
+  if (B < 56) {
+    x = -(8 + (B % 7));
+    y = -(B / 7);
+  } else {
+    x = -(-14 + ((B - 56) % 29));
+    y = -(  8 + ((B - 56) / 29));
+  }
+  offset = y * s->width + x;
+
+  return ipvideo_copy_block (s, frame, frame + offset, offset);
+}
+
+static int
+ipvideo_decode_0x4 (const GstMveDemuxStream *s, unsigned short *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  int x, y;
+  unsigned char B;
+  int offset;
+
+  /* copy a block from the previous frame */
+  CHECK_STREAM (len, 1);
+  B = *(*data)++;
+  x = -8 + (B & 0x0F);
+  y = -8 + (B >> 4);
+  offset = y * s->width + x;
+
+  return ipvideo_copy_block (s, frame, frame +
+      ((unsigned short *) s->back_buf2 - (unsigned short *) s->back_buf1) +
+      offset, offset);
+}
+
+static int
+ipvideo_decode_0x5 (const GstMveDemuxStream *s, unsigned short *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  signed char x, y;
+  int offset;
+
+  /* copy a block from the previous frame using an expanded range */
+  CHECK_STREAM (len, 2);
+  x = (signed char) *(*data)++;
+  y = (signed char) *(*data)++;
+  offset = y * s->width + x;
+
+  return ipvideo_copy_block (s, frame, frame +
+      ((unsigned short *) s->back_buf2 - (unsigned short *) s->back_buf1) +
+      offset, offset);
+}
+
+static int
+ipvideo_decode_0x7 (const GstMveDemuxStream *s, unsigned short *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  int x, y;
+  unsigned short P0, P1;
+  unsigned int flags;
+  int bitmask;
+
+  /* 2-color encoding */
+  CHECK_STREAM (len, 4 + 2);
+  P0 = PIXEL (*data); (*data) += 2;
+  P1 = PIXEL (*data); (*data) += 2;
+
+  if (!(P0 & 0x8000)) {
+
+    /* need 8 more bytes from the stream */
+    CHECK_STREAM (len, 8 - 2);
+
+    for (y = 0; y < 8; ++y) {
+      flags = *(*data)++;
+      for (x = 0x01; x <= 0x80; x <<= 1) {
+        if (flags & x)
+          *frame++ = P1;
+        else
+          *frame++ = P0;
+      }
+      frame += s->width - 8;
+    }
+
+  } else {
+    P0 &= ~0x8000;
+
+    /* need 2 more bytes from the stream */
+
+    flags = ((*data)[1] << 8) | (*data)[0];
+    (*data) += 2;
+    bitmask = 0x0001;
+    for (y = 0; y < 8; y += 2) {
+      for (x = 0; x < 8; x += 2, bitmask <<= 1) {
+        if (flags & bitmask) {
+          *(frame + x) = P1;
+          *(frame + x + 1) = P1;
+          *(frame + s->width + x) = P1;
+          *(frame + s->width + x + 1) = P1;
+        } else {
+          *(frame + x) = P0;
+          *(frame + x + 1) = P0;
+          *(frame + s->width + x) = P0;
+          *(frame + s->width + x + 1) = P0;
+        }
+      }
+      frame += s->width * 2;
+    }
+  }
+
+  return 0;
+}
+
+static int
+ipvideo_decode_0x8 (const GstMveDemuxStream *s, unsigned short *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  int x, y;
+  unsigned short P[8];
+  unsigned char B[8];
+  unsigned int flags = 0;
+  unsigned int bitmask = 0;
+  unsigned short P0 = 0, P1 = 0;
+  int lower_half = 0;
+
+  /* 2-color encoding for each 4x4 quadrant, or 2-color encoding on
+   * either top and bottom or left and right halves */
+  CHECK_STREAM (len, 6 + 10);
+
+  P[0] = PIXEL (*data); (*data) += 2;
+  P[1] = PIXEL (*data); (*data) += 2;
+  B[0] = *(*data)++;
+  B[1] = *(*data)++;
+
+  if (!(P[0] & 0x8000)) {
+
+    /* need 18 more bytes */
+    CHECK_STREAM (len, 18 - 10);
+
+    P[2] = PIXEL (*data); (*data) += 2;
+    P[3] = PIXEL (*data); (*data) += 2;
+    B[2] = *(*data)++;
+    B[3] = *(*data)++;
+    P[4] = PIXEL (*data); (*data) += 2;
+    P[5] = PIXEL (*data); (*data) += 2;
+    B[4] = *(*data)++;
+    B[5] = *(*data)++;
+    P[6] = PIXEL (*data); (*data) += 2;
+    P[7] = PIXEL (*data); (*data) += 2;
+    B[6] = *(*data)++;
+    B[7] = *(*data)++;
+
+    flags =
+      ((B[0] & 0xF0) <<  4) | ((B[4] & 0xF0) <<  8) |
+      ((B[0] & 0x0F)      ) | ((B[4] & 0x0F) <<  4) |
+      ((B[1] & 0xF0) << 20) | ((B[5] & 0xF0) << 24) |
+      ((B[1] & 0x0F) << 16) | ((B[5] & 0x0F) << 20);
+    bitmask = 0x00000001;
+    lower_half = 0;  /* still on top half */
+
+    for (y = 0; y < 8; ++y) {
+
+      /* time to reload flags? */
+      if (y == 4) {
+        flags =
+          ((B[2] & 0xF0) <<  4) | ((B[6] & 0xF0) <<  8) |
+          ((B[2] & 0x0F)      ) | ((B[6] & 0x0F) <<  4) |
+          ((B[3] & 0xF0) << 20) | ((B[7] & 0xF0) << 24) |
+          ((B[3] & 0x0F) << 16) | ((B[7] & 0x0F) << 20);
+        bitmask = 0x00000001;
+        lower_half = 2;
+      }
+
+      /* get the pixel values ready for this quadrant */
+      P0 = P[lower_half + 0];
+      P1 = P[lower_half + 1];
+
+      for (x = 0; x < 8; ++x, bitmask <<= 1) {
+        if (x == 4) {
+          P0 = P[lower_half + 4];
+          P1 = P[lower_half + 5];
+        }
+
+        if (flags & bitmask)
+          *frame++ = P1;
+        else
+          *frame++ = P0;
+      }
+      frame += s->width - 8;
+    }
+
+  } else {
+    P[0] &= ~0x8000;
+
+    /* need 10 more bytes */
+    B[2] = *(*data)++;
+    B[3] = *(*data)++;
+    P[2] = PIXEL (*data); (*data) += 2;
+    P[3] = PIXEL (*data); (*data) += 2;
+    B[4] = *(*data)++;
+    B[5] = *(*data)++;
+    B[6] = *(*data)++;
+    B[7] = *(*data)++;
+
+    if (!(P[2] & 0x8000)) {
+      /* vertical split; left & right halves are 2-color encoded */
+
+      flags =
+        ((B[0] & 0xF0) <<  4) | ((B[4] & 0xF0) <<  8) |
+        ((B[0] & 0x0F)      ) | ((B[4] & 0x0F) <<  4) |
+        ((B[1] & 0xF0) << 20) | ((B[5] & 0xF0) << 24) |
+        ((B[1] & 0x0F) << 16) | ((B[5] & 0x0F) << 20);
+      bitmask = 0x00000001;
+
+      for (y = 0; y < 8; ++y) {
+
+        /* time to reload flags? */
+        if (y == 4) {
+          flags =
+            ((B[2] & 0xF0) <<  4) | ((B[6] & 0xF0) <<  8) |
+            ((B[2] & 0x0F)      ) | ((B[6] & 0x0F) <<  4) |
+            ((B[3] & 0xF0) << 20) | ((B[7] & 0xF0) << 24) |
+            ((B[3] & 0x0F) << 16) | ((B[7] & 0x0F) << 20);
+          bitmask = 0x00000001;
+        }
+
+        /* get the pixel values ready for this half */
+        P0 = P[0];
+        P1 = P[1];
+
+        for (x = 0; x < 8; ++x, bitmask <<= 1) {
+          if (x == 4) {
+            P0 = P[2];
+            P1 = P[3];
+          }
+
+          if (flags & bitmask)
+            *frame++ = P1;
+          else
+            *frame++ = P0;
+        }
+        frame += s->width - 8;
+      }
+
+    } else {
+      /* horizontal split; top & bottom halves are 2-color encoded */
+
+      P0 = P[0];
+      P1 = P[1];
+
+      for (y = 0; y < 8; ++y) {
+
+        flags = B[y];
+        if (y == 4) {
+          P0 = P[2] & ~0x8000;
+          P1 = P[3];
+        }
+
+        for (bitmask = 0x01; bitmask <= 0x80; bitmask <<= 1) {
+
+          if (flags & bitmask)
+            *frame++ = P1;
+          else
+            *frame++ = P0;
+        }
+        frame += s->width - 8;
+      }
+    }
+  }
+
+  return 0;
+}
+
+static int
+ipvideo_decode_0x9 (const GstMveDemuxStream *s, unsigned short *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  int x, y;
+  unsigned short P[4];
+  unsigned char B[4];
+  unsigned int flags = 0;
+  int shifter = 0;
+  unsigned short pix;
+
+  /* 4-color encoding */
+  CHECK_STREAM (len, 8 + 4);
+
+  P[0] = PIXEL (*data); (*data) += 2;
+  P[1] = PIXEL (*data); (*data) += 2;
+  P[2] = PIXEL (*data); (*data) += 2;
+  P[3] = PIXEL (*data); (*data) += 2;
+
+  if (!(P[0] & 0x8000) && !(P[2] & 0x8000)) {
+
+    /* 1 of 4 colors for each pixel, need 16 more bytes */
+    CHECK_STREAM (len, 16 - 4);
+
+    for (y = 0; y < 8; ++y) {
+      /* get the next set of 8 2-bit flags */
+      flags = ((*data)[1] << 8) | (*data)[0];
+      (*data) += 2;
+      for (x = 0, shifter = 0; x < 8; ++x, shifter += 2) {
+        *frame++ = P[(flags >> shifter) & 0x03];
+      }
+      frame += s->width - 8;
+    }
+
+  } else if (!(P[0] & 0x8000) && (P[2] & 0x8000)) {
+    P[2] &= ~0x8000;
+
+    /* 1 of 4 colors for each 2x2 block, need 4 more bytes */
+
+    B[0] = *(*data)++;
+    B[1] = *(*data)++;
+    B[2] = *(*data)++;
+    B[3] = *(*data)++;
+    flags = (B[3] << 24) | (B[2] << 16) | (B[1] << 8) | B[0];
+    shifter = 0;
+
+    for (y = 0; y < 8; y += 2) {
+      for (x = 0; x < 8; x += 2, shifter += 2) {
+        pix = P[(flags >> shifter) & 0x03];
+        *(frame + x) = pix;
+        *(frame + x + 1) = pix;
+        *(frame + s->width + x) = pix;
+        *(frame + s->width + x + 1) = pix;
+      }
+      frame += s->width * 2;
+    }
+
+  } else if ((P[0] & 0x8000) && !(P[2] & 0x8000)) {
+    P[0] &= ~0x8000;
+
+    /* 1 of 4 colors for each 2x1 block, need 8 more bytes */
+
+    CHECK_STREAM (len, 8 - 4);
+    for (y = 0; y < 8; ++y) {
+      /* time to reload flags? */
+      if ((y == 0) || (y == 4)) {
+        B[0] = *(*data)++;
+        B[1] = *(*data)++;
+        B[2] = *(*data)++;
+        B[3] = *(*data)++;
+        flags = (B[3] << 24) | (B[2] << 16) | (B[1] << 8) | B[0];
+        shifter = 0;
+      }
+      for (x = 0; x < 8; x += 2, shifter += 2) {
+        pix = P[(flags >> shifter) & 0x03];
+        *(frame + x) = pix;
+        *(frame + x + 1) = pix;
+      }
+      frame += s->width;
+    }
+
+  } else {
+    P[0] &= ~0x8000;
+    P[2] &= ~0x8000;
+
+    /* 1 of 4 colors for each 1x2 block, need 8 more bytes */
+    CHECK_STREAM (len, 8 - 4);
+
+    for (y = 0; y < 8; y += 2) {
+      /* time to reload flags? */
+      if ((y == 0) || (y == 4)) {
+        B[0] = *(*data)++;
+        B[1] = *(*data)++;
+        B[2] = *(*data)++;
+        B[3] = *(*data)++;
+        flags = (B[3] << 24) | (B[2] << 16) | (B[1] << 8) | B[0];
+        shifter = 0;
+      }
+      for (x = 0; x < 8; ++x, shifter += 2) {
+        pix = P[(flags >> shifter) & 0x03];
+        *(frame + x) = pix;
+        *(frame + s->width + x) = pix;
+      }
+      frame += s->width * 2;
+    }
+  }
+
+  return 0;
+}
+
+static int
+ipvideo_decode_0xa (const GstMveDemuxStream *s, unsigned short *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  int x, y;
+  unsigned short P[16];
+  unsigned char B[16];
+  int flags = 0;
+  int shifter = 0;
+  int index;
+  int split;
+  int lower_half;
+
+  /* 4-color encoding for each 4x4 quadrant, or 4-color encoding on
+   * either top and bottom or left and right halves */
+  CHECK_STREAM (len, 8 + 24);
+
+  P[0] = PIXEL (*data); (*data) += 2;
+  P[1] = PIXEL (*data); (*data) += 2;
+  P[2] = PIXEL (*data); (*data) += 2;
+  P[3] = PIXEL (*data); (*data) += 2;
+
+  if (!(P[0] & 0x8000)) {
+
+    /* 4-color encoding for each quadrant; need 40 more bytes */
+    CHECK_STREAM (len, 40 - 24);
+
+    B[0] = *(*data)++;
+    B[1] = *(*data)++;
+    B[2] = *(*data)++;
+    B[3] = *(*data)++;
+    for (y = 4; y < 16; y += 4) {
+      for (x = y; x < y + 4; ++x) {
+        P[x] = PIXEL (*data);
+        (*data) += 2;
+      }
+      for (x = y; x < y + 4; ++x)
+        B[x] = *(*data)++;
+    }
+
+    for (y = 0; y < 8; ++y) {
+
+      lower_half = (y >= 4) ? 4 : 0;
+      flags = (B[y + 8] << 8) | B[y];
+
+      for (x = 0, shifter = 0; x < 8; ++x, shifter += 2) {
+        split = (x >= 4) ? 8 : 0;
+        index = split + lower_half + ((flags >> shifter) & 0x03);
+        *frame++ = P[index];
+      }
+
+      frame += s->width - 8;
+    }
+
+  } else {
+    P[0] &= ~0x8000;
+
+    /* 4-color encoding for either left and right or top and bottom
+     * halves; need 24 more bytes */
+
+    memcpy (&B[0], *data, 8); (*data) += 8;
+    P[4] = PIXEL (*data); (*data) += 2;
+    P[5] = PIXEL (*data); (*data) += 2;
+    P[6] = PIXEL (*data); (*data) += 2;
+    P[7] = PIXEL (*data); (*data) += 2;
+    memcpy (&B[8], *data, 8); (*data) += 8;
+
+    if (!(P[4] & 0x8000)) {
+
+      /* block is divided into left and right halves */
+      for (y = 0; y < 8; ++y) {
+
+        flags = (B[y + 8] << 8) | B[y];
+        split = 0;
+
+        for (x = 0, shifter = 0; x < 8; ++x, shifter += 2) {
+          if (x == 4)
+            split = 4;
+          *frame++ = P[split + ((flags >> shifter) & 0x03)];
+        }
+
+        frame += s->width - 8;
+      }
+
+    } else {
+      P[4] &= ~0x8000;
+
+      /* block is divided into top and bottom halves */
+      split = 0;
+      for (y = 0; y < 8; ++y) {
+
+        flags = (B[y * 2 + 1] << 8) | B[y * 2];
+        if (y == 4)
+          split = 4;
+
+        for (x = 0, shifter = 0; x < 8; ++x, shifter += 2)
+          *frame++ = P[split + ((flags >> shifter) & 0x03)];
+
+        frame += s->width - 8;
+      }
+    }
+  }
+
+  return 0;
+}
+
+static int
+ipvideo_decode_0xb (const GstMveDemuxStream *s, unsigned short *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  int x, y;
+
+  /* 64-color encoding (each pixel in block is a different color) */
+  CHECK_STREAM (len, 128);
+
+  for (y = 0; y < 8; ++y) {
+    for (x = 0; x < 8; ++x) {
+      *frame++ = PIXEL (*data);
+      (*data) += 2;
+    }
+    frame += s->width - 8;
+  }
+
+  return 0;
+}
+
+static int
+ipvideo_decode_0xc (const GstMveDemuxStream *s, unsigned short *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  int x, y;
+  unsigned short pix;
+
+  /* 16-color block encoding: each 2x2 block is a different color */
+  CHECK_STREAM (len, 32);
+
+  for (y = 0; y < 8; y += 2) {
+    for (x = 0; x < 8; x += 2) {
+      pix = PIXEL (*data);
+      (*data) += 2;
+      *(frame + x) = pix;
+      *(frame + x + 1) = pix;
+      *(frame + s->width + x) = pix;
+      *(frame + s->width + x + 1) = pix;
+    }
+    frame += s->width * 2;
+  }
+
+  return 0;
+}
+
+static int
+ipvideo_decode_0xd (const GstMveDemuxStream *s, unsigned short *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  int x, y;
+  unsigned short P[4];
+  unsigned char index = 0;
+
+  /* 4-color block encoding: each 4x4 block is a different color */
+  CHECK_STREAM (len, 8);
+
+  P[0] = PIXEL (*data); (*data) += 2;
+  P[1] = PIXEL (*data); (*data) += 2;
+  P[2] = PIXEL (*data); (*data) += 2;
+  P[3] = PIXEL (*data); (*data) += 2;
+
+  for (y = 0; y < 8; ++y) {
+    if (y < 4)
+      index = 0;
+    else
+      index = 2;
+
+    for (x = 0; x < 8; ++x) {
+      if (x == 4)
+        ++index;
+      *frame++ = P[index];
+    }
+    frame += s->width - 8;
+  }
+
+  return 0;
+}
+
+static int
+ipvideo_decode_0xe (const GstMveDemuxStream *s, unsigned short *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  int x, y;
+  unsigned short pix;
+
+  /* 1-color encoding: the whole block is 1 solid color */
+  CHECK_STREAM (len, 2);
+
+  pix = PIXEL (*data);
+  (*data) += 2;
+
+  for (y = 0; y < 8; ++y) {
+    for (x = 0; x < 8; ++x) {
+      *frame++ = pix;
+    }
+    frame += s->width - 8;
+  }
+
+  return 0;
+}
+
+static int
+ipvideo_decode_0xf (const GstMveDemuxStream *s, unsigned short *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  int x, y;
+  unsigned short P[2];
+
+  /* dithered encoding */
+  CHECK_STREAM (len, 4);
+
+  P[0] = PIXEL (*data); (*data) += 2;
+  P[1] = PIXEL (*data); (*data) += 2;
+
+  for (y = 0; y < 8; ++y) {
+    for (x = 0; x < 4; ++x) {
+      *frame++ = P[y & 1];
+      *frame++ = P[(y & 1) ^ 1];
+    }
+    frame += s->width - 8;
+  }
+
+  return 0;
+}
+
+int
+ipvideo_decode_frame16 (const GstMveDemuxStream *s, const unsigned char *data, unsigned short len)
+{
+  int rc = 0;
+  int x, y, xx, yy;
+  int index = 0;
+  unsigned short offset;
+  unsigned char opcode;
+  unsigned short *frame;
+  const unsigned char *data2;
+  unsigned short len2;
+
+  CHECK_STREAM (&len, 2);
+
+  offset = (data[1] << 8) | data[0];
+  data2 = data + offset;
+  len2 = len - offset + 2;
+  data += 2;
+
+  frame = (unsigned short *) s->back_buf1;
+
+  /* decoding is done in 8x8 blocks */
+  xx = s->width >> 3;
+  yy = s->height >> 3;
+
+  for (y = 0; y < yy; ++y) {
+    for (x = 0; x < xx; ++x) {
+      /* decoding map contains 4 bits of information per 8x8 block */
+      /* bottom nibble first, then top nibble */
+      if (index & 1)
+        opcode = s->code_map[index >> 1] >> 4;
+      else
+        opcode = s->code_map[index >> 1] & 0x0F;
+      ++index;
+
+      /* GST_DEBUG ("block @ (%3d, %3d): encoding 0x%X, data ptr @ %p",
+         x, y, opcode, data); */
+
+      switch (opcode) {
+      case 0x0:
+        /* copy a block from the previous frame */
+        rc = ipvideo_copy_block (s, frame, frame +
+                ((unsigned short *) s->back_buf2 - (unsigned short *) s->back_buf1), 0);
+        break;
+      case 0x1:
+        /* copy block from 2 frames ago; since we switched the back
+         * buffers we don't actually have to do anything here */
+        break;
+      case 0x2:
+        rc = ipvideo_decode_0x2 (s, frame, &data2, &len2);
+        break;
+      case 0x3:
+        rc = ipvideo_decode_0x3 (s, frame, &data2, &len2);
+        break;
+      case 0x4:
+        rc = ipvideo_decode_0x4 (s, frame, &data2, &len2);
+        break;
+      case 0x5:
+        rc = ipvideo_decode_0x5 (s, frame, &data, &len);
+        break;
+      case 0x6:
+        /* mystery opcode? skip multiple blocks? */
+        GST_WARNING ("encountered unsupported opcode 0x6");
+        rc = -1;
+        break;
+      case 0x7:
+        rc = ipvideo_decode_0x7 (s, frame, &data, &len);
+        break;
+      case 0x8:
+        rc = ipvideo_decode_0x8 (s, frame, &data, &len);
+        break;
+      case 0x9:
+        rc = ipvideo_decode_0x9 (s, frame, &data, &len);
+        break;
+      case 0xa:
+        rc = ipvideo_decode_0xa (s, frame, &data, &len);
+        break;
+      case 0xb:
+        rc = ipvideo_decode_0xb (s, frame, &data, &len);
+        break;
+      case 0xc:
+        rc = ipvideo_decode_0xc (s, frame, &data, &len);
+        break;
+      case 0xd:
+        rc = ipvideo_decode_0xd (s, frame, &data, &len);
+        break;
+      case 0xe:
+        rc = ipvideo_decode_0xe (s, frame, &data, &len);
+        break;
+      case 0xf:
+        rc = ipvideo_decode_0xf (s, frame, &data, &len);
+        break;
+      }
+
+      if (rc != 0)
+        return rc;
+
+      frame += 8;
+    }
+    frame += 7 * s->width;
+  }
+
+  return 0;
+}
diff -urpN ../gst-plugins-bad-0.10.3/gst/mve/mvevideodec8.c ./gst/mve/mvevideodec8.c
--- ../gst-plugins-bad-0.10.3/gst/mve/mvevideodec8.c	Thu Jan  1 01:00:00 1970
+++ ./gst/mve/mvevideodec8.c	Sun Jul 30 20:22:37 2006
@@ -0,0 +1,783 @@
+/*
+ * Interplay MVE Video Decoder (8 bit)
+ * Copyright (C) 2003 the ffmpeg project, Mike Melanson
+ *           (C) 2006 Jens Granseuer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * For more information about the Interplay MVE format, visit:
+ *   http://www.pcisys.net/~melanson/codecs/interplay-mve.txt
+ */
+
+#include "gstmvedemux.h"
+
+#define CHECK_STREAM(l, n) \
+  do { \
+    if (G_UNLIKELY (*(l) < (n))) { \
+      GST_ERROR ("wanted to read %d bytes from stream, %d available", (n), *(l)); \
+      return -1; \
+    } \
+    *(l) -= (n); \
+  } while (0)
+
+
+/* copy an 8x8 block from the stream to the frame buffer */
+static int
+ipvideo_copy_block (const GstMveDemuxStream *s, unsigned char *frame,
+                    const unsigned char *src, int offset)
+{
+  int i;
+  long frame_offset;
+
+  frame_offset = frame - s->back_buf1 + offset;
+
+  if (G_UNLIKELY (frame_offset < 0)) {
+    GST_ERROR ("frame offset < 0 (%ld)", frame_offset);
+    return -1;
+  }
+  else if (G_UNLIKELY (frame_offset > s->max_block_offset)) {
+    GST_ERROR ("frame offset above limit (%ld > %ld)",
+               frame_offset, s->max_block_offset);
+    return -1;
+  }
+
+  for (i = 0; i < 8; ++i)
+  {
+    memcpy (frame, src, 8);
+    frame += s->width;
+    src += s->width;
+  }
+
+  return 0;
+}
+
+static int
+ipvideo_decode_0x2 (const GstMveDemuxStream *s, unsigned char *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  unsigned char B;
+  int x, y;
+  int offset;
+
+  /* copy block from 2 frames ago using a motion vector */
+  CHECK_STREAM(len, 1);
+  B = *(*data)++;
+
+  if (B < 56) {
+    x = 8 + (B % 7);
+    y = B / 7;
+  } else {
+    x = -14 + ((B - 56) % 29);
+    y =   8 + ((B - 56) / 29);
+  }
+  offset = y * s->width + x;
+
+  return ipvideo_copy_block (s, frame, frame + offset, offset);
+}
+
+static int
+ipvideo_decode_0x3 (const GstMveDemuxStream *s, unsigned char *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  unsigned char B;
+  int x, y;
+  int offset;
+
+  /* copy 8x8 block from current frame from an up/left block */
+  CHECK_STREAM(len, 1);
+  B = *(*data)++;
+
+  if (B < 56) {
+    x = -(8 + (B % 7));
+    y = -(B / 7);
+  } else {
+    x = -(-14 + ((B - 56) % 29));
+    y = -(  8 + ((B - 56) / 29));
+  }
+  offset = y * s->width + x;
+
+  return ipvideo_copy_block (s, frame, frame + offset, offset);
+}
+
+static int
+ipvideo_decode_0x4 (const GstMveDemuxStream *s, unsigned char *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  unsigned char B;
+  int x, y;
+  int offset;
+
+  /* copy a block from the previous frame */
+  CHECK_STREAM(len, 1);
+  B = *(*data)++;
+  x = -8 + (B & 0x0F);
+  y = -8 + (B >> 4);
+  offset = y * s->width + x;
+
+  return ipvideo_copy_block (s, frame,
+    frame + (s->back_buf2 - s->back_buf1) + offset, offset);
+}
+
+static int
+ipvideo_decode_0x5 (const GstMveDemuxStream *s, unsigned char *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  signed char x, y;
+  int offset;
+
+  /* copy a block from the previous frame using an expanded range */
+  CHECK_STREAM(len, 2);
+
+  x = (signed char)*(*data)++;
+  y = (signed char)*(*data)++;
+  offset = y * s->width + x;
+
+  return ipvideo_copy_block (s, frame,
+    frame + (s->back_buf2 - s->back_buf1) + offset, offset);
+}
+
+static int
+ipvideo_decode_0x7 (const GstMveDemuxStream *s, unsigned char *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  int x, y;
+  unsigned char P0, P1;
+  unsigned int flags;
+  int bitmask;
+
+  /* 2-color encoding */
+  CHECK_STREAM(len, 2 + 2);
+
+  P0 = *(*data)++;
+  P1 = *(*data)++;
+
+  if (P0 <= P1) {
+
+    /* need 8 more bytes from the stream */
+    CHECK_STREAM(len, 8 - 2);
+
+    for (y = 0; y < 8; ++y) {
+      flags = *(*data)++;
+      for (x = 0x01; x <= 0x80; x <<= 1) {
+        if (flags & x)
+          *frame++ = P1;
+        else
+          *frame++ = P0;
+      }
+      frame += s->width - 8;
+    }
+
+  } else {
+
+    /* need 2 more bytes from the stream */
+    flags = ((*data)[1] << 8) | (*data)[0];
+    (*data) += 2;
+    bitmask = 0x0001;
+    for (y = 0; y < 8; y += 2) {
+      for (x = 0; x < 8; x += 2, bitmask <<= 1) {
+        if (flags & bitmask) {
+          *(frame + x) = P1;
+          *(frame + x + 1) = P1;
+          *(frame + s->width + x) = P1;
+          *(frame + s->width + x + 1) = P1;
+        } else {
+          *(frame + x) = P0;
+          *(frame + x + 1) = P0;
+          *(frame + s->width + x) = P0;
+          *(frame + s->width + x + 1) = P0;
+        }
+      }
+      frame += s->width * 2;
+    }
+  }
+
+  return 0;
+}
+
+static int
+ipvideo_decode_0x8 (const GstMveDemuxStream *s, unsigned char *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  int x, y;
+  unsigned char P[8];
+  unsigned char B[8];
+  unsigned int flags = 0;
+  unsigned int bitmask = 0;
+  unsigned char P0 = 0, P1 = 0;
+  int lower_half = 0;
+
+  /* 2-color encoding for each 4x4 quadrant, or 2-color encoding on
+   * either top and bottom or left and right halves */
+  CHECK_STREAM(len, 4 + 8);
+
+  P[0] = (*data)[0];
+  P[1] = (*data)[1];
+  B[0] = (*data)[2];
+  B[1] = (*data)[3];
+  (*data) += 4;
+
+  if (P[0] <= P[1]) {
+
+    /* need 12 more bytes */
+    CHECK_STREAM(len, 12 - 8);
+
+    P[2] = (*data)[0];  P[3] = (*data)[1];
+    B[2] = (*data)[2];  B[3] = (*data)[3];
+    P[4] = (*data)[4];  P[5] = (*data)[5];
+    B[4] = (*data)[6];  B[5] = (*data)[7];
+    P[6] = (*data)[8];  P[7] = (*data)[9];
+    B[6] = (*data)[10]; B[7] = (*data)[11];
+    (*data) += 12;
+
+    flags =
+      ((B[0] & 0xF0) <<  4) | ((B[4] & 0xF0) <<  8) |
+      ((B[0] & 0x0F)      ) | ((B[4] & 0x0F) <<  4) |
+      ((B[1] & 0xF0) << 20) | ((B[5] & 0xF0) << 24) |
+      ((B[1] & 0x0F) << 16) | ((B[5] & 0x0F) << 20);
+    bitmask = 0x00000001;
+    lower_half = 0;  /* still on top half */
+
+    for (y = 0; y < 8; ++y) {
+
+      /* time to reload flags? */
+      if (y == 4) {
+        flags =
+          ((B[2] & 0xF0) <<  4) | ((B[6] & 0xF0) <<  8) |
+          ((B[2] & 0x0F)      ) | ((B[6] & 0x0F) <<  4) |
+          ((B[3] & 0xF0) << 20) | ((B[7] & 0xF0) << 24) |
+          ((B[3] & 0x0F) << 16) | ((B[7] & 0x0F) << 20);
+        bitmask = 0x00000001;
+        lower_half = 2;
+      }
+
+      /* get the pixel values ready for this quadrant */
+      P0 = P[lower_half + 0];
+      P1 = P[lower_half + 1];
+
+      for (x = 0; x < 8; ++x, bitmask <<= 1) {
+        if (x == 4) {
+          P0 = P[lower_half + 4];
+          P1 = P[lower_half + 5];
+        }
+
+        if (flags & bitmask)
+          *frame++ = P1;
+        else
+          *frame++ = P0;
+      }
+      frame += s->width - 8;
+    }
+
+  } else {
+
+    /* need 8 more bytes */
+    B[2] = (*data)[0];  B[3] = (*data)[1];
+    P[2] = (*data)[2];  P[3] = (*data)[3];
+    B[4] = (*data)[4];  B[5] = (*data)[5];
+    B[6] = (*data)[6];  B[7] = (*data)[7];
+    (*data) += 8;
+
+    if (P[2] <= P[3]) {
+
+      /* vertical split; left & right halves are 2-color encoded */
+
+      flags =
+        ((B[0] & 0xF0) <<  4) | ((B[4] & 0xF0) <<  8) |
+        ((B[0] & 0x0F)      ) | ((B[4] & 0x0F) <<  4) |
+        ((B[1] & 0xF0) << 20) | ((B[5] & 0xF0) << 24) |
+        ((B[1] & 0x0F) << 16) | ((B[5] & 0x0F) << 20);
+      bitmask = 0x00000001;
+
+      for (y = 0; y < 8; ++y) {
+
+        /* time to reload flags? */
+        if (y == 4) {
+          flags =
+            ((B[2] & 0xF0) <<  4) | ((B[6] & 0xF0) <<  8) |
+            ((B[2] & 0x0F)      ) | ((B[6] & 0x0F) <<  4) |
+            ((B[3] & 0xF0) << 20) | ((B[7] & 0xF0) << 24) |
+            ((B[3] & 0x0F) << 16) | ((B[7] & 0x0F) << 20);
+          bitmask = 0x00000001;
+        }
+
+        /* get the pixel values ready for this half */
+        P0 = P[0];
+        P1 = P[1];
+
+        for (x = 0; x < 8; ++x, bitmask <<= 1) {
+          if (x == 4) {
+            P0 = P[2];
+            P1 = P[3];
+          }
+
+          if (flags & bitmask)
+            *frame++ = P1;
+          else
+            *frame++ = P0;
+        }
+        frame += s->width - 8;
+      }
+
+    } else {
+
+      /* horizontal split; top & bottom halves are 2-color encoded */
+
+      P0 = P[0];
+      P1 = P[1];
+
+      for (y = 0; y < 8; ++y) {
+
+        flags = B[y];
+        if (y == 4) {
+          P0 = P[2];
+          P1 = P[3];
+        }
+
+        for (bitmask = 0x01; bitmask <= 0x80; bitmask <<= 1) {
+
+          if (flags & bitmask)
+            *frame++ = P1;
+          else
+            *frame++ = P0;
+        }
+        frame += s->width - 8;
+      }
+    }
+  }
+
+  return 0;
+}
+
+static int
+ipvideo_decode_0x9 (const GstMveDemuxStream *s, unsigned char *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  int x, y;
+  unsigned char P[4];
+  unsigned char B[4];
+  unsigned long flags = 0;
+  int shifter = 0;
+  unsigned char pix;
+
+  /* 4-color encoding */
+  CHECK_STREAM(len, 4 + 4);
+
+  P[0] = (*data)[0];
+  P[1] = (*data)[1];
+  P[2] = (*data)[2];
+  P[3] = (*data)[3];
+  (*data) += 4;
+
+  if ((P[0] <= P[1]) && (P[2] <= P[3])) {
+
+    /* 1 of 4 colors for each pixel, need 16 more bytes */
+    CHECK_STREAM(len, 16 - 4);
+
+    for (y = 0; y < 8; ++y) {
+      /* get the next set of 8 2-bit flags */
+      flags = ((*data)[1] << 8) | (*data)[0];
+      (*data) += 2;
+      for (x = 0, shifter = 0; x < 8; ++x, shifter += 2) {
+        *frame++ = P[(flags >> shifter) & 0x03];
+      }
+      frame += s->width - 8;
+    }
+
+  } else if ((P[0] <= P[1]) && (P[2] > P[3])) {
+
+    /* 1 of 4 colors for each 2x2 block, need 4 more bytes */
+    B[0] = (*data)[0];
+    B[1] = (*data)[1];
+    B[2] = (*data)[2];
+    B[3] = (*data)[3];
+    (*data) += 4;
+    flags = (B[3] << 24) | (B[2] << 16) | (B[1] << 8) | B[0];
+    shifter = 0;
+
+    for (y = 0; y < 8; y += 2) {
+      for (x = 0; x < 8; x += 2, shifter += 2) {
+        pix = P[(flags >> shifter) & 0x03];
+        *(frame + x) = pix;
+        *(frame + x + 1) = pix;
+        *(frame + s->width + x) = pix;
+        *(frame + s->width + x + 1) = pix;
+      }
+      frame += s->width * 2;
+    }
+
+  } else if ((P[0] > P[1]) && (P[2] <= P[3])) {
+
+    /* 1 of 4 colors for each 2x1 block, need 8 more bytes */
+    CHECK_STREAM(len, 8 - 4);
+
+    for (y = 0; y < 8; ++y) {
+      /* time to reload flags? */
+      if ((y == 0) || (y == 4)) {
+        B[0] = (*data)[0];
+        B[1] = (*data)[1];
+        B[2] = (*data)[2];
+        B[3] = (*data)[3];
+        (*data) += 4;
+        flags = (B[3] << 24) | (B[2] << 16) | (B[1] << 8) | B[0];
+        shifter = 0;
+      }
+      for (x = 0; x < 8; x += 2, shifter += 2) {
+        pix = P[(flags >> shifter) & 0x03];
+        *(frame + x) = pix;
+        *(frame + x + 1) = pix;
+      }
+      frame += s->width;
+    }
+
+  } else {
+
+    /* 1 of 4 colors for each 1x2 block, need 8 more bytes */
+    CHECK_STREAM(len, 8 - 4);
+
+    for (y = 0; y < 8; y += 2) {
+      /* time to reload flags? */
+      if ((y == 0) || (y == 4)) {
+        B[0] = (*data)[0];
+        B[1] = (*data)[1];
+        B[2] = (*data)[2];
+        B[3] = (*data)[3];
+        (*data) += 4;
+        flags = (B[3] << 24) | (B[2] << 16) | (B[1] << 8) | B[0];
+        shifter = 0;
+      }
+      for (x = 0; x < 8; ++x, shifter += 2) {
+        pix = P[(flags >> shifter) & 0x03];
+        *(frame + x) = pix;
+        *(frame + s->width + x) = pix;
+      }
+      frame += s->width * 2;
+    }
+  }
+
+  return 0;
+}
+
+static int
+ipvideo_decode_0xa (const GstMveDemuxStream *s, unsigned char *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  int x, y;
+  unsigned char P[16];
+  unsigned char B[16];
+  int flags = 0;
+  int shifter = 0;
+  int index;
+  int split;
+  int lower_half;
+
+  /* 4-color encoding for each 4x4 quadrant, or 4-color encoding on
+   * either top and bottom or left and right halves */
+  CHECK_STREAM(len, 8 + 16);
+
+  P[0] = (*data)[0]; P[1] = (*data)[1];
+  P[2] = (*data)[2]; P[3] = (*data)[3];
+  B[0] = (*data)[4]; B[1] = (*data)[5];
+  B[2] = (*data)[6]; B[3] = (*data)[7];
+  (*data) += 8;
+
+  if (P[0] <= P[1]) {
+
+    /* 4-color encoding for each quadrant; need 24 more bytes */
+    CHECK_STREAM(len, 24 - 16);
+
+    for (y = 4; y < 16; y += 4) {
+      for (x = y; x < y + 4; ++x)
+        P[x] = *(*data)++;
+      for (x = y; x < y + 4; ++x)
+        B[x] = *(*data)++;
+    }
+
+    for (y = 0; y < 8; ++y) {
+
+      lower_half = (y >= 4) ? 4 : 0;
+      flags = (B[y + 8] << 8) | B[y];
+
+      for (x = 0, shifter = 0; x < 8; ++x, shifter += 2) {
+        split = (x >= 4) ? 8 : 0;
+        index = split + lower_half + ((flags >> shifter) & 0x03);
+        *frame++ = P[index];
+      }
+
+      frame += s->width - 8;
+    }
+
+  } else {
+
+    /* 4-color encoding for either left and right or top and bottom
+     * halves; need 16 more bytes */
+
+    B[4] = (*data)[0]; B[5] = (*data)[1];
+    B[6] = (*data)[2]; B[7] = (*data)[3];
+    P[4] = (*data)[4]; P[5] = (*data)[5];
+    P[6] = (*data)[6]; P[7] = (*data)[7];
+    (*data) += 8;
+    memcpy (&B[8], *data, 8);
+    (*data) += 8;
+
+    if (P[4] <= P[5]) {
+
+      /* block is divided into left and right halves */
+      for (y = 0; y < 8; ++y) {
+
+        flags = (B[y + 8] << 8) | B[y];
+        split = 0;
+
+        for (x = 0, shifter = 0; x < 8; ++x, shifter += 2) {
+          if (x == 4)
+            split = 4;
+          *frame++ = P[split + ((flags >> shifter) & 0x03)];
+        }
+
+        frame += s->width - 8;
+      }
+
+    } else {
+
+      /* block is divided into top and bottom halves */
+      split = 0;
+      for (y = 0; y < 8; ++y) {
+
+        flags = (B[y * 2 + 1] << 8) | B[y * 2];
+        if (y == 4)
+          split = 4;
+
+        for (x = 0, shifter = 0; x < 8; ++x, shifter += 2)
+          *frame++ = P[split + ((flags >> shifter) & 0x03)];
+
+        frame += s->width - 8;
+      }
+    }
+  }
+
+  return 0;
+}
+
+static int
+ipvideo_decode_0xb (const GstMveDemuxStream *s, unsigned char *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  int y;
+
+  /* 64-color encoding (each pixel in block is a different color) */
+  CHECK_STREAM(len, 64);
+
+  for (y = 0; y < 8; ++y) {
+    memcpy (frame, *data, 8);
+    frame += s->width;
+    (*data) += 8;
+  }
+
+  return 0;
+}
+
+static int
+ipvideo_decode_0xc (const GstMveDemuxStream *s, unsigned char *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  int x, y;
+  unsigned char pix;
+
+  /* 16-color block encoding: each 2x2 block is a different color */
+  CHECK_STREAM(len, 16);
+
+  for (y = 0; y < 8; y += 2) {
+    for (x = 0; x < 8; x += 2) {
+      pix = *(*data)++;
+      *(frame + x) = pix;
+      *(frame + x + 1) = pix;
+      *(frame + s->width + x) = pix;
+      *(frame + s->width + x + 1) = pix;
+    }
+    frame += s->width * 2;
+  }
+
+  return 0;
+}
+
+static int
+ipvideo_decode_0xd (const GstMveDemuxStream *s, unsigned char *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  int x, y;
+  unsigned char P[4];
+  unsigned char index = 0;
+
+  /* 4-color block encoding: each 4x4 block is a different color */
+  CHECK_STREAM(len, 4);
+
+  P[0] = (*data)[0];
+  P[1] = (*data)[1];
+  P[2] = (*data)[2];
+  P[3] = (*data)[3];
+  (*data) += 4;
+
+  for (y = 0; y < 8; ++y) {
+    if (y < 4)
+      index = 0;
+    else
+      index = 2;
+
+    for (x = 0; x < 8; ++x) {
+      if (x == 4)
+        ++index;
+      *frame++ = P[index];
+    }
+    frame += s->width - 8;
+  }
+
+  return 0;
+}
+
+static int
+ipvideo_decode_0xe (const GstMveDemuxStream *s, unsigned char *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  int y;
+  unsigned char pix;
+
+  /* 1-color encoding: the whole block is 1 solid color */
+  CHECK_STREAM(len, 1);
+  pix = *(*data)++;
+
+  for (y = 0; y < 8; ++y) {
+    memset (frame, pix, 8);
+    frame += s->width;
+  }
+
+  return 0;
+}
+
+static int
+ipvideo_decode_0xf (const GstMveDemuxStream *s, unsigned char *frame,
+                    const unsigned char **data, unsigned short *len)
+{
+  int x, y;
+  unsigned char P[2];
+
+  /* dithered encoding */
+  CHECK_STREAM(len, 2);
+
+  P[0] = *(*data)++;
+  P[1] = *(*data)++;
+
+  for (y = 0; y < 8; ++y) {
+    for (x = 0; x < 4; ++x) {
+      *frame++ = P[y & 1];
+      *frame++ = P[(y & 1) ^ 1];
+    }
+    frame += s->width - 8;
+  }
+
+  return 0;
+}
+
+int
+ipvideo_decode_frame8 (const GstMveDemuxStream *s, const unsigned char *data, unsigned short len)
+{
+  int rc = 0;
+  int x, y, xx, yy;
+  int index = 0;
+  unsigned char opcode;
+  unsigned char *frame;
+
+  frame = s->back_buf1;
+
+  /* decoding is done in 8x8 blocks */
+  xx = s->width >> 3;
+  yy = s->height >> 3;
+
+  for (y = 0; y < yy; ++y) {
+    for (x = 0; x < xx; ++x) {
+      /* decoding map contains 4 bits of information per 8x8 block */
+      /* bottom nibble first, then top nibble */
+      if (index & 1)
+        opcode = s->code_map[index >> 1] >> 4;
+      else
+        opcode = s->code_map[index >> 1] & 0x0F;
+      ++index;
+
+      /* GST_DEBUG ("block @ (%3d, %3d): encoding 0x%X, data ptr @ %p",
+         x, y, opcode, data); */
+
+      switch (opcode) {
+      case 0x00:
+        /* copy a block from the previous frame */
+        rc = ipvideo_copy_block (s, frame, frame + (s->back_buf2 - s->back_buf1), 0);
+        break;
+      case 0x01:
+        /* copy block from 2 frames ago; since we switched the back
+         * buffers we don't actually have to do anything here */
+        break;
+      case 0x02:
+        rc = ipvideo_decode_0x2 (s, frame, &data, &len);
+        break;
+      case 0x03:
+        rc = ipvideo_decode_0x3 (s, frame, &data, &len);
+        break;
+      case 0x04:
+        rc = ipvideo_decode_0x4 (s, frame, &data, &len);
+        break;
+      case 0x05:
+        rc = ipvideo_decode_0x5 (s, frame, &data, &len);
+        break;
+      case 0x06:
+        /* mystery opcode? skip multiple blocks? */
+        GST_WARNING ("encountered unsupported opcode 0x6");
+        rc = -1;
+        break;
+      case 0x07:
+        rc = ipvideo_decode_0x7 (s, frame, &data, &len);
+        break;
+      case 0x08:
+        rc = ipvideo_decode_0x8 (s, frame, &data, &len);
+        break;
+      case 0x09:
+        rc = ipvideo_decode_0x9 (s, frame, &data, &len);
+        break;
+      case 0x0a:
+        rc = ipvideo_decode_0xa (s, frame, &data, &len);
+        break;
+      case 0x0b:
+        rc = ipvideo_decode_0xb (s, frame, &data, &len);
+        break;
+      case 0x0c:
+        rc = ipvideo_decode_0xc (s, frame, &data, &len);
+        break;
+      case 0x0d:
+        rc = ipvideo_decode_0xd (s, frame, &data, &len);
+        break;
+      case 0x0e:
+        rc = ipvideo_decode_0xe (s, frame, &data, &len);
+        break;
+      case 0x0f:
+        rc = ipvideo_decode_0xf (s, frame, &data, &len);
+        break;
+      }
+
+      if (rc != 0)
+        return rc;
+
+      frame += 8;
+    }
+    frame += 7 * s->width;
+  }
+
+  return 0;
+}
diff -urpN ../gst-plugins-bad-0.10.3/gst/mve/mvevideoenc16.c ./gst/mve/mvevideoenc16.c
--- ../gst-plugins-bad-0.10.3/gst/mve/mvevideoenc16.c	Thu Jan  1 01:00:00 1970
+++ ./gst/mve/mvevideoenc16.c	Sun Jul 30 20:22:37 2006
@@ -0,0 +1,1588 @@
+/*
+ * Interplay MVE video encoder (16 bit)
+ * Copyright (C) 2006 Jens Granseuer <jensgr@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <stdlib.h>
+#include "gstmvemux.h"
+
+typedef struct _GstMveEncoderData GstMveEncoderData;
+typedef struct _GstMveEncoding GstMveEncoding;
+typedef struct _GstMveApprox GstMveApprox;
+typedef struct _GstMveQuant GstMveQuant;
+
+#define MVE_RMASK   0x7c00
+#define MVE_GMASK   0x03e0
+#define MVE_BMASK   0x001f
+#define MVE_RSHIFT  10
+#define MVE_GSHIFT  5
+#define MVE_BSHIFT  0
+
+#define MVE_RVAL(p)     (((p) & MVE_RMASK) >> MVE_RSHIFT)
+#define MVE_GVAL(p)     (((p) & MVE_GMASK) >> MVE_GSHIFT)
+#define MVE_BVAL(p)     (((p) & MVE_BMASK) >> MVE_BSHIFT)
+#define MVE_COL(r,g,b)  (((r) << MVE_RSHIFT) | ((g) << MVE_GSHIFT) | ((b) << MVE_BSHIFT))
+
+struct _GstMveEncoderData {
+  GstMveMux *mve;
+  /* current position in frame */
+  guint16 x, y;
+
+  /* commonly used quantization results
+     (2 and 4 colors) for the current block */
+  guint16 q2block[64];
+  guint16 q2colors[2];
+  guint32 q2error;
+  gboolean q2available;
+
+  guint16 q4block[64];
+  guint16 q4colors[4];
+  guint32 q4error;
+  gboolean q4available;
+};
+
+struct _GstMveEncoding {
+  guint8 opcode;
+  guint8 size;
+  guint32 (*approx)(GstMveEncoderData *enc, const guint16 *src, GstMveApprox *res);
+};
+
+#define MVE_APPROX_MAX_ERROR  G_MAXUINT32
+
+struct _GstMveApprox {
+  guint32 error;
+  guint8 type;
+  guint8 data[128];  /* max 128 bytes encoded per block */
+  guint16 block[64]; /* block in final image */
+};
+
+struct _GstMveQuant {
+  guint16 col;
+  guint16 r_total, g_total, b_total;
+  guint8 r, g, b;
+  guint8 hits, hits_last;
+  guint32 max_error;
+  guint16 max_miss;
+};
+
+#define mve_median(mve, src) mve_median_sub ((mve), (src), 8, 8, 0)
+#define mve_color_dist(c1, c2) \
+        mve_color_dist_rgb (MVE_RVAL (c1), MVE_GVAL (c1), MVE_BVAL (c1), \
+                            MVE_RVAL (c2), MVE_GVAL (c2), MVE_BVAL (c2));
+
+/* comparison function for qsort() */
+static int
+mve_comp_solution (const void *a, const void *b)
+{
+  const GArray *aa = *((GArray **) a);
+  const GArray *bb = *((GArray **) b);
+
+  if (aa->len <= 1)
+    return G_MAXINT;
+  else if (bb->len <= 1)
+    return G_MININT;
+  else
+    return g_array_index (aa, GstMveApprox, aa->len - 2).error -
+           g_array_index (bb, GstMveApprox, bb->len - 2).error;
+}
+
+static inline guint32 mve_color_dist_rgb (guint8 r1, guint8 g1, guint8 b1,
+                                          guint8 r2, guint8 g2, guint8 b2)
+{
+  /* euclidean distance (minus sqrt) */
+  gint dr = r1 - r2;
+  gint dg = g1 - g2;
+  gint db = b1 - b2;
+  return dr*dr + dg*dg + db*db;
+}
+
+/* compute average color in a sub-block */
+static guint16
+mve_median_sub (const GstMveMux *mve, const guint16 *src, guint w, guint h, guint n)
+{
+  guint x, y;
+  const guint max = w * h, max2 = max >> 1;
+  guint32 r_total = max2, g_total = max2, b_total = max2;
+
+  src += ((n * w) % 8) + (((n * (8 - h)) / (12 - w)) * h * mve->width);
+
+  for (y = 0; y < h; ++y) {
+    for (x = 0; x < w; ++x) {
+      r_total += MVE_RVAL (src[x]);
+      g_total += MVE_GVAL (src[x]);
+      b_total += MVE_BVAL (src[x]);
+    }
+    src += mve->width;
+  }
+
+  return MVE_COL (r_total/max, g_total/max, b_total/max);
+}
+
+static void
+mve_quant_init (const GstMveMux *mve, GstMveQuant *q, guint n_clusters,
+                const guint16 *data, guint w, guint h)
+{
+  guint i;
+  guint x, y;
+  guint16 cols[4];
+  guint16 val[2];
+
+  /* init first cluster with lowest (darkest), second with highest (lightest)
+     color. if we need 4 clusters, fill in first and last color in the block
+     and hope they make for a good distribution */
+  cols[0] = cols[1] = cols[2] = data[0];
+  cols[3] = data[(h - 1) * mve->width + w - 1];
+
+  /* favour red over green and blue */
+  val[0] = val[1] = (MVE_RVAL (data[0]) << 1) + MVE_GVAL (data[0]) + MVE_BVAL (data[0]);
+
+  for (y = 0; y < h; ++y) {
+    for (x = 0; x < w; ++x) {
+      guint16 c = data[x];
+
+      if ((c != cols[0]) && (c != cols[1]))
+      {
+        guint v = (MVE_RVAL (c) << 1) + MVE_GVAL (c) + MVE_BVAL (c);
+
+        if (v < val[0]) {
+          val[0] = v;
+          cols[0] = c;
+        } else if (v > val[1]) {
+          val[1] = v;
+          cols[1] = c;
+        }
+      }
+    }
+    data += mve->width;
+  }
+
+  for (i = 0; i < n_clusters; ++i)
+  {
+    q[i].col = cols[i];
+    q[i].r = MVE_RVAL (cols[i]);
+    q[i].g = MVE_GVAL (cols[i]);
+    q[i].b = MVE_BVAL (cols[i]);
+    q[i].r_total = q[i].g_total = q[i].b_total = 0;
+    q[i].hits = q[i].hits_last = 0;
+    q[i].max_error = 0;
+    q[i].max_miss = 0;
+  }
+}
+
+static gboolean
+mve_quant_update_clusters (GstMveQuant *q, guint n_clusters)
+{
+  gboolean changed = FALSE;
+  guint i;
+
+  for (i = 0; i < n_clusters; ++i) {
+    if (q[i].hits > 0)
+    {
+      guint16 means = MVE_COL (
+          (q[i].r_total + q[i].hits/2) / q[i].hits,
+          (q[i].g_total + q[i].hits/2) / q[i].hits,
+          (q[i].b_total + q[i].hits/2) / q[i].hits
+      );
+
+      if ((means != q[i].col) || (q[i].hits != q[i].hits_last))
+        changed = TRUE;
+
+      q[i].col = means;
+      q[i].r_total = q[i].g_total = q[i].b_total = 0;
+    }
+    else
+    {
+      guint j;
+      guint32 max_err = 0;
+      GstMveQuant *worst = NULL;
+
+      /* try to replace unused cluster with a better representative */
+      for (j = 0; j < n_clusters; ++j) {
+        if (q[j].max_error > max_err) {
+          worst = &q[j];
+          max_err = worst->max_error;
+        }
+      }
+      if (worst) {
+        q[i].col = worst->max_miss;
+        worst->max_error = 0;
+        changed = TRUE;
+      }
+    }
+
+    q[i].r = MVE_RVAL (q[i].col);
+    q[i].g = MVE_GVAL (q[i].col);
+    q[i].b = MVE_BVAL (q[i].col);
+    q[i].hits_last = q[i].hits;
+    q[i].hits = 0;
+  }
+  for (i = 0; i < n_clusters; ++i) {
+    q[i].max_error = 0;
+  }
+
+  return changed;
+}
+
+/* quantize a sub-block using a k-means algorithm */
+static guint32
+mve_quantize (const GstMveMux *mve, const guint16 *src,
+              guint w, guint h, guint n, guint ncols,
+              guint16 *scratch, guint16 *cols)
+{
+  guint x, y, i;
+  GstMveQuant q[4];
+  const guint16 *data;
+  guint16 *dest;
+  guint32 error;
+
+  g_assert (n <= 4 && ncols <= 4);
+
+  src += ((n * w) % 8) + (((n * (8 - h)) / (12 - w)) * h * mve->width);
+  scratch += ((n * w) % 8) + (((n * (8 - h)) / (12 - w)) * h * 8);
+
+  mve_quant_init (mve, q, ncols, src, w, h);
+
+  do {
+    data = src;
+    dest = scratch;
+    error = 0;
+
+    /* for each pixel find the closest cluster */
+    for (y = 0; y < h; ++y) {
+      for (x = 0; x < w; ++x) {
+        guint16 c = data[x];
+        guint8 r = MVE_RVAL (c), g = MVE_GVAL (c), b = MVE_BVAL (c);
+        guint32 minerr = MVE_APPROX_MAX_ERROR, err;
+        GstMveQuant *best;
+
+        for (i = 0; i < ncols; ++i) {
+          err = mve_color_dist_rgb (r, g, b, q[i].r, q[i].g, q[i].b);
+
+          if (err < minerr) {
+            minerr = err;
+            best = &q[i];
+          }
+        }
+
+        ++best->hits;
+        best->r_total += r;
+        best->g_total += g;
+        best->b_total += b;
+
+        if (minerr > best->max_error) {
+          best->max_error = minerr;
+          best->max_miss = c;
+        }
+
+        error += minerr;
+        dest[x] = best->col;
+      }
+      data += mve->width;
+      dest += 8;
+    }
+  } while (mve_quant_update_clusters (q, ncols));
+
+  /* fill cols array with result colors */
+  for (i = 0; i < ncols; ++i)
+    cols[i] = q[i].col;
+
+  return error;
+}
+
+static guint32
+mve_block_error (const GstMveMux *mve, const guint16 *b1, const guint16 *b2, guint32 threshold)
+{
+  /* compute error between two blocks in a frame */
+  guint32 e = 0;
+  guint x, y;
+
+  for (y = 0; y < 8; ++y) {
+    for (x = 0; x <  8; ++x) {
+      e += mve_color_dist (*b1, *b2);
+
+      /* using a threshold to return early gives a huge performance bonus */
+      if (e >= threshold)
+        return MVE_APPROX_MAX_ERROR;
+      ++b1;
+      ++b2;
+    }
+
+    b1 += mve->width - 8;
+    b2 += mve->width - 8;
+  }
+
+  return e;
+}
+
+static guint32
+mve_block_error_packed (const GstMveMux *mve, const guint16 *block, const guint16 *scratch)
+{
+  /* compute error between a block in a frame and a (continuous) scratch pad */
+  guint32 e = 0;
+  guint x, y;
+
+  for (y = 0; y < 8; ++y) {
+    for (x = 0; x <  8; ++x) {
+      e += mve_color_dist (block[x], scratch[x]);
+    }
+    block += mve->width;
+    scratch += 8;
+  }
+
+  return e;
+}
+
+static void
+mve_store_block (const GstMveMux *mve, const guint16 *block, guint16 *scratch)
+{
+  /* copy block from frame to a (continuous) scratch pad */
+  guint y;
+
+  for (y = 0; y < 8; ++y) {
+    memcpy (scratch, block, 16);
+    block += mve->width;
+    scratch += 8;
+  }
+}
+
+static void
+mve_restore_block (const GstMveMux *mve, guint16 *block, const guint16 *scratch)
+{
+  /* copy block from scratch pad to frame */
+  guint y;
+
+  for (y = 0; y < 8; ++y) {
+    memcpy (block, scratch, 16);
+    block += mve->width;
+    scratch += 8;
+  }
+}
+
+
+static guint32
+mve_try_vector (GstMveEncoderData *enc, const guint16 *src,
+                const guint16 *frame, gint pn, GstMveApprox *apx)
+{
+  /* try to locate a similar 8x8 block in the given frame using a motion vector */
+  guint i;
+  gint dx, dy;
+  gint fx, fy;
+  guint32 err;
+
+  apx->error = MVE_APPROX_MAX_ERROR;
+
+  for (i = 0; i < 256; ++i)
+  {
+    if (i < 56) {
+      dx = 8 + (i % 7);
+      dy = i / 7;
+    } else {
+      dx = -14 + ((i - 56) % 29);
+      dy =   8 + ((i - 56) / 29);
+    }
+
+    fx = enc->x + dx * pn;
+    fy = enc->y + dy * pn;
+
+    if ((fx >= 0) && (fy >= 0) && (fx + 8 <= enc->mve->width) && (fy + 8 <= enc->mve->height))
+    {
+      err = mve_block_error (enc->mve, src, frame + fy * enc->mve->width + fx, apx->error);
+      if (err < apx->error)
+      {
+        apx->data[0] = i;
+        mve_store_block (enc->mve, frame + fy * enc->mve->width + fx, apx->block);
+        apx->error = err;
+        if (err == 0)
+          return 0;
+      }
+    }
+  }
+
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x0 (GstMveEncoderData *enc, const guint16 *src, GstMveApprox *apx)
+{
+  /* copy a block from the last frame (0 bytes) */
+  if (enc->mve->last_frame == NULL)
+    return MVE_APPROX_MAX_ERROR;
+
+  mve_store_block (enc->mve, 
+        ((guint16 *) GST_BUFFER_DATA (enc->mve->last_frame)) +
+        enc->y * enc->mve->width + enc->x, apx->block);
+  apx->error = mve_block_error_packed (enc->mve, src, apx->block);
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x1 (GstMveEncoderData *enc, const guint16 *src, GstMveApprox *apx)
+{
+  /* copy a block from the second to last frame (0 bytes) */
+  if (enc->mve->second_last_frame == NULL)
+    return MVE_APPROX_MAX_ERROR;
+
+  mve_store_block (enc->mve,
+        ((guint16 *) GST_BUFFER_DATA (enc->mve->second_last_frame)) +
+        enc->y * enc->mve->width + enc->x, apx->block);
+  apx->error = mve_block_error_packed (enc->mve, src, apx->block);
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x2 (GstMveEncoderData *enc, const guint16 *src, GstMveApprox *apx)
+{
+  /* copy block from 2 frames ago using a motion vector (1 byte) */
+  if (enc->mve->quick_encoding || enc->mve->second_last_frame == NULL)
+    return MVE_APPROX_MAX_ERROR;
+
+  apx->error = mve_try_vector (enc, src,
+          (guint16 *) GST_BUFFER_DATA (enc->mve->second_last_frame),
+          1, apx);
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x3 (GstMveEncoderData *enc, const guint16 *src, GstMveApprox *apx)
+{
+  /* copy 8x8 block from current frame from an up/left block (1 byte) */
+  if (enc->mve->quick_encoding)
+    return MVE_APPROX_MAX_ERROR;
+
+  apx->error = mve_try_vector (enc, src,
+          src - enc->mve->width * enc->y - enc->x, -1, apx);
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x4 (GstMveEncoderData *enc, const guint16 *src, GstMveApprox *apx)
+{
+  /* copy a block from previous frame using a motion vector (-8/-8 to +7/+7) (1 byte) */
+  const GstMveMux *mve = enc->mve;
+  guint32 err;
+  const guint16 *frame;
+  gint x1, x2, xi, y1, y2, yi;
+
+  if (mve->last_frame == NULL)
+    return MVE_APPROX_MAX_ERROR;
+
+  frame = (guint16 *) GST_BUFFER_DATA (mve->last_frame);
+
+  x1 = enc->x - 8;
+  x2 = enc->x + 7;
+  if (x1 < 0)
+    x1 = 0;
+  else if (x2 + 8 > mve->width)
+    x2 = mve->width - 8;
+
+  y1 = enc->y - 8;
+  y2 = enc->y + 7;
+  if (y1 < 0)
+    y1 = 0;
+  else if (y2 + 8 > mve->height)
+    y2 = mve->height - 8;
+
+  apx->error = MVE_APPROX_MAX_ERROR;
+
+  for (yi = y1; yi <= y2; ++yi) {
+    guint yoff = yi * mve->width;
+
+    for (xi = x1; xi <= x2; ++xi) {
+      err = mve_block_error (mve, src, frame + yoff + xi, apx->error);
+      if (err < apx->error)
+      {
+        apx->data[0] = ((xi - enc->x + 8) & 0xF) | ((yi - enc->y + 8) << 4);
+        mve_store_block (mve, frame + yoff + xi, apx->block);
+        apx->error = err;
+        if (err == 0)
+          return 0;
+      }
+    }
+  }
+
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x5 (GstMveEncoderData *enc, const guint16 *src, GstMveApprox *apx)
+{
+  /* copy a block from previous frame using a motion vector
+     (-128/-128 to +127/+127) (2 bytes) */
+  const GstMveMux *mve = enc->mve;
+  guint32 err;
+  const guint16 *frame;
+  gint x1, x2, xi, y1, y2, yi;
+
+  if (mve->quick_encoding || mve->last_frame == NULL)
+    return MVE_APPROX_MAX_ERROR;
+
+  frame = (guint16 *) GST_BUFFER_DATA (mve->last_frame);
+
+  x1 = enc->x - 128;
+  x2 = enc->x + 127;
+  if (x1 < 0)
+    x1 = 0;
+  if (x2 + 8 > mve->width)
+    x2 = mve->width - 8;
+
+  y1 = enc->y - 128;
+  y2 = enc->y + 127;
+  if (y1 < 0)
+    y1 = 0;
+  if (y2 + 8 > mve->height)
+    y2 = mve->height - 8;
+
+  apx->error = MVE_APPROX_MAX_ERROR;
+
+  for (yi = y1; yi <= y2; ++yi) {
+    gint yoff = yi * mve->width;
+
+    for (xi = x1; xi <= x2; ++xi) {
+      err = mve_block_error (mve, src, frame + yoff + xi, apx->error);
+      if (err < apx->error)
+      {
+        apx->data[0] = xi - enc->x;
+        apx->data[1] = yi - enc->y;
+        mve_store_block (mve, frame + yoff + xi, apx->block);
+        apx->error = err;
+        if (err == 0)
+          return 0;
+      }
+    }
+  }
+
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x7a (GstMveEncoderData *enc, const guint16 *src, GstMveApprox *apx)
+{
+  /* 2-color encoding for 2x2 solid blocks (6 bytes) */
+  guint16 p[2];
+  guint16 mean;
+  guint32 e1, e2;
+  guint x, y;
+  guint8 r[2], g[2], b[2], rb, gb, bb;
+  guint16 *block = apx->block;
+  guint16 mask = 0x0001;
+  guint16 flags = 0;
+
+  /* calculate mean colors for the entire block */
+  if (!enc->q2available) {
+    enc->q2error = mve_quantize (enc->mve, src, 8, 8, 0, 2, enc->q2block, enc->q2colors);
+    enc->q2available = TRUE;
+  }
+
+  /* p[0] & 0x8000 */
+  GST_WRITE_UINT16_LE (&apx->data[0], enc->q2colors[0] | 0x8000);
+  GST_WRITE_UINT16_LE (&apx->data[2], enc->q2colors[1]);
+
+  for (x = 0; x < 2; ++x) {
+    r[x] = MVE_RVAL (enc->q2colors[x]);
+    g[x] = MVE_GVAL (enc->q2colors[x]);
+    b[x] = MVE_BVAL (enc->q2colors[x]);
+  }
+
+  /* calculate mean colors for each 2x2 block and map to global colors */
+  for (y = 0; y < 4; ++y) {
+    for (x = 0; x < 4; ++x, mask <<= 1) {
+      p[0] = src[enc->mve->width];
+      p[1] = src[enc->mve->width + 1];
+
+      rb = (MVE_RVAL (src[0]) + MVE_RVAL (src[1]) + MVE_RVAL (p[0]) + MVE_RVAL (p[1]) + 2) / 4;
+      gb = (MVE_GVAL (src[0]) + MVE_GVAL (src[1]) + MVE_GVAL (p[0]) + MVE_GVAL (p[1]) + 2) / 4;
+      bb = (MVE_BVAL (src[0]) + MVE_BVAL (src[1]) + MVE_BVAL (p[0]) + MVE_BVAL (p[1]) + 2) / 4;
+
+      e1 = mve_color_dist_rgb (rb, gb, bb, r[0], g[0], b[0]);
+      e2 = mve_color_dist_rgb (rb, gb, bb, r[1], g[1], b[1]);
+
+      if (e1 > e2) {
+        mean = enc->q2colors[1];
+        flags |= mask;
+      } else {
+        mean = enc->q2colors[0];
+      }
+
+      block[0] = block[1] = block[8] = block[9] = mean;
+
+      src += 2;
+      block += 2;
+    }
+    src += (enc->mve->width * 2) - 8;
+    block += 8;
+  }
+
+  apx->data[4] = flags & 0x00FF;
+  apx->data[5] = (flags & 0xFF00) >> 8;
+
+  apx->error = mve_block_error_packed (enc->mve, src - enc->mve->width * 8, apx->block);
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x7b (GstMveEncoderData *enc, const guint16 *src, GstMveApprox *apx)
+{
+  /* generic 2-color encoding (12 bytes) */
+  guint x, y;
+  guint8 *data = apx->data;
+  guint16 *block = apx->block;
+
+  if (!enc->q2available) {
+    enc->q2error = mve_quantize (enc->mve, src, 8, 8, 0, 2, enc->q2block, enc->q2colors);
+    enc->q2available = TRUE;
+  }
+
+  memcpy (block, enc->q2block, 128);
+
+  /* !(p[0] & 0x8000) */
+  GST_WRITE_UINT16_LE (&data[0], enc->q2colors[0] & ~0x8000);
+  GST_WRITE_UINT16_LE (&data[2], enc->q2colors[1]);
+  data += 4;
+
+  for (y = 0; y < 8; ++y) {
+    guint8 flags = 0;
+
+    for (x = 0x01; x <= 0x80; x <<= 1) {
+      if (*block == enc->q2colors[1])
+        flags |= x;
+      ++block;
+    }
+    *data++ = flags;
+  }
+
+  apx->error = enc->q2error;
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x8a (GstMveEncoderData *enc, const guint16 *src, GstMveApprox *apx)
+{
+  /* 2-color encoding for top and bottom half (16 bytes) */
+  guint16 cols[2];
+  guint32 flags;
+  guint i, x, y, shifter;
+  guint16 *block = apx->block;
+  guint8 *data = apx->data;
+
+  apx->error = 0;
+
+  for (i = 0; i < 2; ++i) {
+    apx->error += mve_quantize (enc->mve, src, 8, 4, i, 2, apx->block, cols);
+
+    flags = 0;
+    shifter = 0;
+
+    /* p0 & 0x8000 && p2 & 0x8000 */
+    GST_WRITE_UINT16_LE (&data[0], cols[0] | 0x8000);
+    GST_WRITE_UINT16_LE (&data[2], cols[1]);
+
+    for (y = 0; y < 4; ++y) {
+      for (x = 0; x < 8; ++x, ++shifter) {
+        if (block[x] == cols[1])
+          flags |= 1 << shifter;
+      }
+      block += 8;
+    }
+    data[4] = flags & 0x000000FF;
+    data[5] = (flags & 0x0000FF00) >> 8;
+    data[6] = (flags & 0x00FF0000) >> 16;
+    data[7] = (flags & 0xFF000000) >> 24;
+    data += 8;
+  }
+
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x8b (GstMveEncoderData *enc, const guint16 *src, GstMveApprox *apx)
+{
+  /* 2-color encoding for left and right half (16 bytes) */
+  guint16 cols[2];
+  guint32 flags;
+  guint i, x, y, shifter;
+  guint16 *block = apx->block;
+  guint8 *data = apx->data;
+
+  apx->error = 0;
+
+  for (i = 0; i < 2; ++i) {
+    apx->error += mve_quantize (enc->mve, src, 4, 8, i, 2, apx->block, cols);
+
+    flags = 0;
+    shifter = 0;
+
+    /* p0 & 0x8000 && !(p2 & 0x8000) */
+    GST_WRITE_UINT16_LE (&data[0], (cols[0] & ~0x8000) | (0x8000 * (i^1)));
+    GST_WRITE_UINT16_LE (&data[2], cols[1]);
+
+    for (y = 0; y < 8; ++y) {
+      for (x = 0; x < 4; ++x, ++shifter) {
+        if (block[x] == cols[1])
+          flags |= 1 << shifter;
+      }
+      block += 8;
+    }
+
+    data[4] = flags & 0x000000FF;
+    data[5] = (flags & 0x0000FF00) >> 8;
+    data[6] = (flags & 0x00FF0000) >> 16;
+    data[7] = (flags & 0xFF000000) >> 24;
+    data += 8;
+    block = apx->block + 4;
+  }
+
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x8c (GstMveEncoderData *enc, const guint16 *src, GstMveApprox *apx)
+{
+  /* 2-color encoding for each 4x4 quadrant (24 bytes) */
+  guint16 cols[2];
+  guint16 flags;
+  guint i, x, y, shifter;
+  guint16 *block;
+  guint8 *data = apx->data;
+
+  apx->error = 0;
+
+  for (i = 0; i < 4; ++i)
+  {
+    apx->error += mve_quantize (enc->mve, src, 4, 4, ((i&1) << 1) | ((i&2) >> 1), 2, apx->block, cols);
+
+    /* !(p0 & 0x8000) */
+    GST_WRITE_UINT16_LE (&data[0], cols[0] & ~0x8000);
+    GST_WRITE_UINT16_LE (&data[2], cols[1]);
+
+    block = apx->block + ((i / 2) * 4) + ((i % 2) * 32);
+    flags = 0;
+    shifter = 0;
+
+    for (y = 0; y < 4; ++y) {
+      for (x = 0; x < 4; ++x, ++shifter) {
+        if (block[x] == cols[1])
+          flags |= 1 << shifter;
+      }
+      block += 8;
+    }
+
+    data[4] = flags & 0x00FF;
+    data[5] = (flags & 0xFF00) >> 8;
+    data += 6;
+  }
+
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x9a (GstMveEncoderData *enc, const guint16 *src, GstMveApprox *apx)
+{
+  /* 4-color encoding for 2x2 solid blocks (12 bytes) */
+  guint16 p[2];
+  guint32 e, emin;
+  guint i, x, y, mean;
+  guint8 r[4], g[4], b[4], rb, gb, bb;
+  guint16 *block = apx->block;
+  guint shifter = 0;
+  guint32 flags = 0;
+
+  /* calculate mean colors for the entire block */
+  if (!enc->q4available) {
+    enc->q4error = mve_quantize (enc->mve, src, 8, 8, 0, 4, enc->q4block, enc->q4colors);
+    enc->q4available = TRUE;
+  }
+
+  /* !(p[0] & 0x8000) && p[2] & 0x8000 */
+  GST_WRITE_UINT16_LE (&apx->data[0], enc->q4colors[0] & ~0x8000);
+  GST_WRITE_UINT16_LE (&apx->data[2], enc->q4colors[1]);
+  GST_WRITE_UINT16_LE (&apx->data[4], enc->q4colors[2] | 0x8000);
+  GST_WRITE_UINT16_LE (&apx->data[6], enc->q4colors[3]);
+
+  for (i = 0; i < 4; ++i) {
+    r[i] = MVE_RVAL (enc->q4colors[i]);
+    g[i] = MVE_GVAL (enc->q4colors[i]);
+    b[i] = MVE_BVAL (enc->q4colors[i]);
+  }
+
+  /* calculate mean colors for each 2x2 block and map to global colors */
+  for (y = 0; y < 4; ++y) {
+    for (x = 0; x < 4; ++x, shifter += 2) {
+      p[0] = src[enc->mve->width];
+      p[1] = src[enc->mve->width + 1];
+
+      rb = (MVE_RVAL (src[0]) + MVE_RVAL (src[1]) + MVE_RVAL (p[0]) + MVE_RVAL (p[1]) + 2) / 4;
+      gb = (MVE_GVAL (src[0]) + MVE_GVAL (src[1]) + MVE_GVAL (p[0]) + MVE_GVAL (p[1]) + 2) / 4;
+      bb = (MVE_BVAL (src[0]) + MVE_BVAL (src[1]) + MVE_BVAL (p[0]) + MVE_BVAL (p[1]) + 2) / 4;
+
+      emin = MVE_APPROX_MAX_ERROR;
+      for (i = 0; i < 4; ++i) {
+        e = mve_color_dist_rgb (rb, gb, bb, r[i], g[i], b[i]);
+        if (e < emin) {
+          emin = e;
+          mean = i;
+        }
+      }
+
+      flags |= mean << shifter;
+      block[0] = block[1] = block[8] = block[9] = enc->q4colors[mean];
+
+      src += 2;
+      block += 2;
+    }
+    src += (enc->mve->width * 2) - 8;
+    block += 8;
+  }
+
+  apx->data[8] = flags & 0x000000FF;
+  apx->data[9] = (flags & 0x0000FF00) >> 8;
+  apx->data[10] = (flags & 0x00FF0000) >> 16;
+  apx->data[11] = (flags & 0xFF000000) >> 24;
+
+  apx->error = mve_block_error_packed (enc->mve, src - 8 * enc->mve->width, apx->block);
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x9b (GstMveEncoderData *enc, const guint16 *src, GstMveApprox *apx)
+{
+  /* 4-color encoding for 2x1 solid blocks (16 bytes) */
+  guint32 e, emin;
+  guint i, x, y, mean;
+  guint8 r[4], g[4], b[4], rb, gb, bb;
+  guint8 *data = apx->data;
+  guint16 *block = apx->block;
+  guint shifter = 0;
+  guint32 flags = 0;
+
+  /* calculate mean colors for the entire block */
+  if (!enc->q4available) {
+    enc->q4error = mve_quantize (enc->mve, src, 8, 8, 0, 4, enc->q4block, enc->q4colors);
+    enc->q4available = TRUE;
+  }
+
+  /* p[0] & 0x8000 && !(p[2] & 0x8000) */
+  GST_WRITE_UINT16_LE (&data[0], enc->q4colors[0] | 0x8000);
+  GST_WRITE_UINT16_LE (&data[2], enc->q4colors[1]);
+  GST_WRITE_UINT16_LE (&data[4], enc->q4colors[2] & ~0x8000);
+  GST_WRITE_UINT16_LE (&data[6], enc->q4colors[3]);
+  data += 8;
+
+  for (i = 0; i < 4; ++i) {
+    r[i] = MVE_RVAL (enc->q4colors[i]);
+    g[i] = MVE_GVAL (enc->q4colors[i]);
+    b[i] = MVE_BVAL (enc->q4colors[i]);
+  }
+
+  /* calculate mean colors for each 2x1 block and map to global colors */
+  for (y = 0; y < 8; ++y) {
+    for (x = 0; x < 4; ++x, shifter += 2) {
+      rb = (MVE_RVAL (src[0]) + MVE_RVAL (src[1]) + 1) / 2;
+      gb = (MVE_GVAL (src[0]) + MVE_GVAL (src[1]) + 1) / 2;
+      bb = (MVE_BVAL (src[0]) + MVE_BVAL (src[1]) + 1) / 2;
+
+      emin = MVE_APPROX_MAX_ERROR;
+      for (i = 0; i < 4; ++i) {
+        e = mve_color_dist_rgb (rb, gb, bb, r[i], g[i], b[i]);
+        if (e < emin) {
+          emin = e;
+          mean = i;
+        }
+      }
+
+      flags |= mean << shifter;
+      block[0] = block[1] = enc->q4colors[mean];
+
+      src += 2;
+      block += 2;
+    }
+
+    if ((y == 3) || (y == 7)) {
+      data[0] = flags & 0x000000FF;
+      data[1] = (flags & 0x0000FF00) >> 8;
+      data[2] = (flags & 0x00FF0000) >> 16;
+      data[3] = (flags & 0xFF000000) >> 24;
+      data += 4;
+
+      flags = 0;
+      shifter = 0;
+    }
+
+    src += enc->mve->width - 8;
+  }
+
+  apx->error = mve_block_error_packed (enc->mve, src - 8 * enc->mve->width, apx->block);
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x9c (GstMveEncoderData *enc, const guint16 *src, GstMveApprox *apx)
+{
+  /* 4-color encoding for 1x2 solid blocks (16 bytes) */
+  guint16 p2;
+  guint32 e, emin;
+  guint i, x, y, mean;
+  guint8 r[4], g[4], b[4], rb, gb, bb;
+  guint8 *data = apx->data;
+  guint16 *block = apx->block;
+  guint shifter = 0;
+  guint32 flags = 0;
+
+  /* calculate mean colors for the entire block */
+  if (!enc->q4available) {
+    enc->q4error = mve_quantize (enc->mve, src, 8, 8, 0, 4, enc->q4block, enc->q4colors);
+    enc->q4available = TRUE;
+  }
+
+  /* p[0] & 0x8000 && p[2] & 0x8000 */
+  GST_WRITE_UINT16_LE (&data[0], enc->q4colors[0] | 0x8000);
+  GST_WRITE_UINT16_LE (&data[2], enc->q4colors[1]);
+  GST_WRITE_UINT16_LE (&data[4], enc->q4colors[2] | 0x8000);
+  GST_WRITE_UINT16_LE (&data[6], enc->q4colors[3]);
+  data += 8;
+
+  for (i = 0; i < 4; ++i) {
+    r[i] = MVE_RVAL (enc->q4colors[i]);
+    g[i] = MVE_GVAL (enc->q4colors[i]);
+    b[i] = MVE_BVAL (enc->q4colors[i]);
+  }
+
+  /* calculate mean colors for each 1x2 block and map to global colors */
+  for (y = 0; y < 4; ++y) {
+    for (x = 0; x < 8; ++x, shifter += 2) {
+      p2 = src[enc->mve->width];
+      rb = (MVE_RVAL (src[0]) + MVE_RVAL (p2) + 1) / 2;
+      gb = (MVE_GVAL (src[0]) + MVE_GVAL (p2) + 1) / 2;
+      bb = (MVE_BVAL (src[0]) + MVE_BVAL (p2) + 1) / 2;
+
+      emin = MVE_APPROX_MAX_ERROR;
+      for (i = 0; i < 4; ++i) {
+        e = mve_color_dist_rgb (rb, gb, bb, r[i], g[i], b[i]);
+        if (e < emin) {
+          emin = e;
+          mean = i;
+        }
+      }
+
+      flags |= mean << shifter;
+      block[0] = block[8] = enc->q4colors[mean];
+
+      ++src;
+      ++block;
+    }
+
+    if ((y == 1) || (y == 3)) {
+      data[0] = flags & 0x000000FF;
+      data[1] = (flags & 0x0000FF00) >> 8;
+      data[2] = (flags & 0x00FF0000) >> 16;
+      data[3] = (flags & 0xFF000000) >> 24;
+      data += 4;
+
+      flags = 0;
+      shifter = 0;
+    }
+
+    src += (enc->mve->width * 2) - 8;
+    block += 8;
+  }
+
+  apx->error = mve_block_error_packed (enc->mve, src - 8 * enc->mve->width, apx->block);
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x9d (GstMveEncoderData *enc, const guint16 *src, GstMveApprox *apx)
+{
+  /* generic 4-color encoding (24 bytes) */
+  guint32 flags = 0;
+  guint shifter = 0;
+  guint i, x, y;
+  guint8 *data = apx->data;
+  guint16 *block = apx->block;
+
+  if (!enc->q4available) {
+    enc->q4error = mve_quantize (enc->mve, src, 8, 8, 0, 4, enc->q4block, enc->q4colors);
+    enc->q4available = TRUE;
+  }
+
+  memcpy (block, enc->q4block, 128);
+
+  /* !(p[0] & 0x8000) && !(p[2] & 0x8000) */
+  GST_WRITE_UINT16_LE (&data[0], enc->q4colors[0] & ~0x8000);
+  GST_WRITE_UINT16_LE (&data[2], enc->q4colors[1]);
+  GST_WRITE_UINT16_LE (&data[4], enc->q4colors[2] & ~0x8000);
+  GST_WRITE_UINT16_LE (&data[6], enc->q4colors[3]);
+  data += 8;
+
+  for (y = 0; y < 8; ++y) {
+    for (x = 0; x < 8; ++x, shifter += 2) {
+
+      for (i = 0; i < 3; ++i) {
+        if (*block == enc->q4colors[i])
+          break;
+      }
+
+      flags |= i << shifter;
+      ++block;
+    }
+
+    data[0] = flags & 0x000000FF;
+    data[1] = (flags & 0x0000FF00) >> 8;
+    data += 2;
+    shifter = 0;
+    flags = 0;
+  }
+
+  apx->error = enc->q4error;
+  return apx->error;
+}
+
+static guint32
+mve_encode_0xaa (GstMveEncoderData *enc, const guint16 *src, GstMveApprox *apx)
+{
+  /* 4-color encoding for top and bottom half (32 bytes) */
+  guint16 cols[4];
+  guint32 flags;
+  guint i, j, x, y, shifter;
+  guint16 *block = apx->block;
+  guint8 *data = apx->data;
+
+  apx->error = 0;
+
+  for (i = 0; i < 2; ++i) {
+    apx->error += mve_quantize (enc->mve, src, 8, 4, i, 4, apx->block, cols);
+
+    flags = 0;
+    shifter = 0;
+
+    /* p0 & 0x8000 && p4 & 0x8000 */
+    GST_WRITE_UINT16_LE (&data[0], cols[0] | 0x8000);
+    GST_WRITE_UINT16_LE (&data[2], cols[1]);
+    GST_WRITE_UINT16_LE (&data[4], cols[2]);
+    GST_WRITE_UINT16_LE (&data[6], cols[3]);
+    data += 8;
+
+    for (y = 0; y < 4; ++y) {
+      for (x = 0; x < 8; ++x, shifter += 2) {
+        for (j = 0; j < 3; ++j) {
+          if (block[x] == cols[j]) break;
+        }
+        flags |= j << shifter;
+      }
+      block += 8;
+
+      if ((y == 1) || (y == 3)) {
+        data[0] = flags & 0x000000FF;
+        data[1] = (flags & 0x0000FF00) >> 8;
+        data[2] = (flags & 0x00FF0000) >> 16;
+        data[3] = (flags & 0xFF000000) >> 24;
+        data += 4;
+        flags = 0;
+        shifter = 0;
+      }
+    }
+  }
+
+  return apx->error;
+}
+
+static guint32
+mve_encode_0xab (GstMveEncoderData *enc, const guint16 *src, GstMveApprox *apx)
+{
+  /* 4-color encoding for left and right half (32 bytes) */
+  guint16 cols[4];
+  guint32 flags;
+  guint i, j, x, y, shifter;
+  guint16 *block = apx->block;
+  guint8 *data = apx->data;
+
+  apx->error = 0;
+
+  for (i = 0; i < 2; ++i) {
+    apx->error += mve_quantize (enc->mve, src, 4, 8, i, 4, apx->block, cols);
+
+    flags = 0;
+    shifter = 0;
+
+    /* p0 & 0x8000 && !(p4 & 0x8000) */
+    GST_WRITE_UINT16_LE (&data[0], (cols[0] & ~0x8000) | (0x8000 * (i^1)));
+    GST_WRITE_UINT16_LE (&data[2], cols[1]);
+    GST_WRITE_UINT16_LE (&data[4], cols[2]);
+    GST_WRITE_UINT16_LE (&data[6], cols[3]);
+    data += 8;
+
+    for (y = 0; y < 8; ++y) {
+      for (x = 0; x < 4; ++x, shifter += 2) {
+        for (j = 0; j < 3; ++j) {
+          if (block[x] == cols[j]) break;
+        }
+        flags |= j << shifter;
+      }
+      block += 8;
+
+      if ((y == 3) || (y == 7)) {
+        data[0] = flags & 0x000000FF;
+        data[1] = (flags & 0x0000FF00) >> 8;
+        data[2] = (flags & 0x00FF0000) >> 16;
+        data[3] = (flags & 0xFF000000) >> 24;
+        data += 4;
+        flags = 0;
+        shifter = 0;
+      }
+    }
+    block = apx->block + 4;
+  }
+
+  return apx->error;
+}
+
+static guint32
+mve_encode_0xac (GstMveEncoderData *enc, const guint16 *src, GstMveApprox *apx)
+{
+  /* 4-color encoding for each 4x4 quadrant (48 bytes) */
+  guint16 cols[4];
+  guint32 flags;
+  guint i, j, x, y, shifter;
+  guint16 *block;
+  guint8 *data = apx->data;
+
+  apx->error = 0;
+
+  for (i = 0; i < 4; ++i)
+  {
+    apx->error += mve_quantize (enc->mve, src, 4, 4, ((i&1) << 1) | ((i&2) >> 1), 4, apx->block, cols);
+
+    /* !(p0 & 0x8000) */
+    GST_WRITE_UINT16_LE (&data[0], cols[0] & ~0x8000);
+    GST_WRITE_UINT16_LE (&data[2], cols[1]);
+    GST_WRITE_UINT16_LE (&data[4], cols[2]);
+    GST_WRITE_UINT16_LE (&data[6], cols[3]);
+
+    block = apx->block + ((i / 2) * 4) + ((i % 2) * 32);
+    flags = 0;
+    shifter = 0;
+
+    for (y = 0; y < 4; ++y) {
+      for (x = 0; x < 4; ++x, shifter += 2) {
+        for (j = 0; j < 3; ++j) {
+          if (block[x] == cols[j]) break;
+        }
+        flags |= j << shifter;
+      }
+      block += 8;
+    }
+
+    data[8] = flags & 0x000000FF;
+    data[9] = (flags & 0x0000FF00) >> 8;
+    data[10] = (flags & 0x00FF0000) >> 16;
+    data[11] = (flags & 0xFF000000) >> 24;
+    data += 12;
+  }
+
+  return apx->error;
+}
+
+static guint32
+mve_encode_0xb (GstMveEncoderData *enc, const guint16 *src, GstMveApprox *apx)
+{
+  /* 64-color encoding (each pixel in block is a different color) (128 bytes) */
+  guint i;
+
+  apx->error = 0;
+
+  mve_store_block (enc->mve, src, apx->block);
+  for (i = 0; i < 64; ++i)
+    GST_WRITE_UINT16_LE (&apx->data[i<<1], apx->block[i]);
+
+  return 0;
+}
+
+static guint32
+mve_encode_0xc (GstMveEncoderData *enc, const guint16 *src, GstMveApprox *apx)
+{
+  /* 16-color block encoding: each 2x2 block is a different color (32 bytes) */
+  guint i = 0, x, y;
+  const guint w = enc->mve->width;
+  guint16 r, g, b;
+
+  /* calculate median color for each 2x2 block */
+  for (y = 0; y < 4; ++y) {
+    for (x = 0; x < 4; ++x) {
+      r = MVE_RVAL (src[0]) + MVE_RVAL (src[1]) +
+          MVE_RVAL (src[w]) + MVE_RVAL (src[w+1]) + 2;
+      g = MVE_GVAL (src[0]) + MVE_GVAL (src[1]) +
+          MVE_GVAL (src[w]) + MVE_GVAL (src[w+1]) + 2;
+      b = MVE_BVAL (src[0]) + MVE_BVAL (src[1]) +
+          MVE_BVAL (src[w]) + MVE_BVAL (src[w+1]) + 2;
+      apx->block[i] = apx->block[i+1] = apx->block[i+2] = apx->block[i+3] =
+              MVE_COL (r >> 2, g >> 2, b >> 2);
+      GST_WRITE_UINT16_LE (&apx->data[i>>1], apx->block[i]);
+
+      i += 4;
+      src += 2;
+    }
+    src += (w * 2) - 8;
+  }
+
+  apx->error = mve_block_error_packed (enc->mve, src - (8 * w), apx->block);
+  return apx->error;
+}
+
+static guint32
+mve_encode_0xd (GstMveEncoderData *enc, const guint16 *src, GstMveApprox *apx)
+{
+  /* 4-color block encoding: each 4x4 block is a different color (8 bytes) */
+  guint i, x, y;
+  guint16 *block;
+
+  /* calculate median color for each 4x4 block */
+  for (i = 0; i < 4; ++i) {
+    guint16 median = mve_median_sub (enc->mve, src, 4, 4, ((i&1) << 1) | ((i&2) >> 1));
+
+    block = apx->block + ((i / 2) * 4) + ((i % 2) * 32);
+    for (y = 0; y < 4; ++y) {
+      for (x = 0; x < 4; ++x) {
+        block[x] = median;
+      }
+      block += 8;
+    }
+
+    GST_WRITE_UINT16_LE (&apx->data[i<<1], median);
+  }
+
+  apx->error = mve_block_error_packed (enc->mve, src, apx->block);
+  return apx->error;
+}
+
+static guint32
+mve_encode_0xe (GstMveEncoderData *enc, const guint16 *src, GstMveApprox *apx)
+{
+  /* 1-color encoding: the whole block is 1 solid color (2 bytes) */
+  guint i;
+  guint16 median = mve_median (enc->mve, src);
+
+  for (i = 0; i < 64; ++i)
+    apx->block[i] = median;
+
+  apx->error = mve_block_error_packed (enc->mve, src, apx->block);
+  GST_WRITE_UINT16_LE (apx->data, median);
+
+  return apx->error;
+}
+
+static guint32
+mve_encode_0xf (GstMveEncoderData *enc, const guint16 *src, GstMveApprox *apx)
+{
+  /* 2 colors dithered encoding (4 bytes) */
+  guint i, x, y;
+  guint32 r[2] = { 0 }, g[2] = { 0 }, b[2] = { 0 };
+  guint16 col[2];
+
+  /* find medians for both colors */
+  for (y = 0; y < 8; ++y) {
+    for (x = 0; x < 8; x += 2) {
+      guint16 p = src[x];
+      r[y & 1] += MVE_RVAL (p);
+      g[y & 1] += MVE_GVAL (p);
+      b[y & 1] += MVE_BVAL (p);
+
+      p = src[x + 1];
+      r[(y & 1)^1] += MVE_RVAL (p);
+      g[(y & 1)^1] += MVE_GVAL (p);
+      b[(y & 1)^1] += MVE_BVAL (p);
+    }
+    src += enc->mve->width;
+  }
+  col[0] = MVE_COL ((r[0]+16)/32, (g[0]+16)/32, (b[0]+16)/32);
+  col[1] = MVE_COL ((r[1]+16)/32, (g[1]+16)/32, (b[1]+16)/32);
+
+  /* store block after encoding */
+  for (i = 0, y = 0; y < 8; ++y) {
+    for (x = 0; x < 4; ++x) {
+      apx->block[i++] = col[y & 1];
+      apx->block[i++] = col[(y & 1) ^ 1];
+    }
+  }
+
+  GST_WRITE_UINT16_LE (&apx->data[0], col[0]);
+  GST_WRITE_UINT16_LE (&apx->data[2], col[1]);
+  apx->error = mve_block_error_packed (enc->mve,
+               src - (8 * enc->mve->width), apx->block);
+  return apx->error;
+}
+
+/* all available encodings in the preferred order,
+   i.e. in ascending encoded size */
+static const GstMveEncoding mve_encodings[] = {
+  { 0x1, 0, mve_encode_0x1 },
+  { 0x0, 0, mve_encode_0x0 },
+  { 0x3, 1, mve_encode_0x3 },
+  { 0x4, 1, mve_encode_0x4 },
+  { 0x2, 1, mve_encode_0x2 },
+  { 0xe, 2, mve_encode_0xe },
+  { 0x5, 2, mve_encode_0x5 },
+  { 0xf, 4, mve_encode_0xf },
+  { 0x7, 6, mve_encode_0x7a },
+  { 0xd, 8, mve_encode_0xd },
+  { 0x7, 12, mve_encode_0x7b },
+  { 0x9, 12, mve_encode_0x9a },
+  { 0x9, 16, mve_encode_0x9b },
+  { 0x9, 16, mve_encode_0x9c },
+  { 0x8, 16, mve_encode_0x8a },
+  { 0x8, 16, mve_encode_0x8b },
+  { 0x8, 24, mve_encode_0x8c },
+  { 0x9, 24, mve_encode_0x9d },
+  { 0xc, 32, mve_encode_0xc },
+  { 0xa, 32, mve_encode_0xaa },
+  { 0xa, 32, mve_encode_0xab },
+  { 0xa, 48, mve_encode_0xac },
+  { 0xb, 128, mve_encode_0xb }
+};
+
+static gboolean
+mve_reorder_solution (GArray **solution, guint16 n)
+{
+  /* do a binary search to find the position to reinsert the modified element */
+  /* the block we need to reconsider is always at position 0 */
+  /* return TRUE if this block only has 1 encoding left and can be dropped */
+  if (mve_comp_solution (&solution[0], &solution[1]) <= 0)
+    return FALSE; /* already sorted */
+
+  else if (solution[0]->len <= 1)
+    /* drop this element from further calculations since we cannot improve here */
+    return TRUE;
+
+  else
+  {
+    /* we know the error value can only get worse, so we can actually start at 1 */
+    guint lower = 1;
+    guint upper = n - 1;
+    gint cmp;
+    guint idx = 0;
+
+    while (upper > lower) {
+      idx = lower + ((upper - lower) / 2);
+
+      cmp = mve_comp_solution (&solution[0], &solution[idx]);
+
+      if (cmp < 0) {
+        upper = idx;
+      } else if (cmp > 0) {
+        lower = ++idx;
+      } else {
+        upper = lower = idx;
+      }
+    }
+
+    if (idx > 0) {
+      /* rearrange array members in new order */
+      GArray *a = solution[0];
+      memcpy (&solution[0], &solution[1], sizeof (GArray *) * idx);
+      solution[idx] = a;
+    }
+  }
+  return FALSE;
+}
+
+static guint32
+gst_mve_find_solution (GArray **approx, guint16 n, guint32 size, guint16 max)
+{
+  /* build an array of approximations we can shuffle around */
+  GstMveApprox *sol_apx;
+  GArray **solution = g_malloc (sizeof (GArray *) * n);
+  GArray **current = solution;
+  memcpy (solution, approx, sizeof (GArray *) * n);
+
+  qsort (solution, n, sizeof (GArray *), mve_comp_solution);
+
+  do {
+    /* array is now sorted by error of the next to optimal approximation;
+       drop optimal approximation for the best block */
+
+    /* unable to reduce size further */
+    if (current[0]->len <= 1)
+      break;
+
+    sol_apx = &g_array_index (current[0], GstMveApprox, current[0]->len - 1);
+    size -= mve_encodings[sol_apx->type].size;
+    g_array_remove_index_fast (current[0], current[0]->len - 1);
+    sol_apx = &g_array_index (current[0], GstMveApprox, current[0]->len - 1);
+    size += mve_encodings[sol_apx->type].size;
+
+    if (mve_reorder_solution (current, n)) {
+      ++current;
+      --n;
+    }
+  } while (size > max);
+
+  g_free (solution);
+
+  return size;
+}
+
+GstFlowReturn
+mve_encode_frame16 (GstMveMux *mve, GstBuffer *frame, guint16 max_data)
+{
+  guint16 *src;
+  GstFlowReturn ret = GST_FLOW_ERROR;
+  guint8 *cm = mve->chunk_code_map;
+  GByteArray *pstream;
+  GArray **approx;
+  GstMveApprox apx;
+  GstMveEncoderData enc;
+  const guint16 blocks = (mve->width * mve->height) / 64;
+  guint32 encoded_size = 2; /* two initial bytes for the offset */
+  guint i = 0, x, y;
+
+  src = (guint16 *) GST_BUFFER_DATA (frame);
+
+  approx = g_malloc (sizeof (GArray *) * blocks);
+
+  enc.mve = mve;
+
+  for (enc.y = 0; enc.y < mve->height; enc.y += 8)
+  {
+    for (enc.x = 0; enc.x < mve->width; enc.x += 8)
+    {
+      guint32 err, last_err = MVE_APPROX_MAX_ERROR;
+      guint type = 0;
+      guint best;
+
+      enc.q2available = enc.q4available = FALSE;
+      approx[i] = g_array_new (FALSE, FALSE, sizeof(GstMveApprox));
+
+      do {
+        err = mve_encodings[type].approx (&enc, src, &apx);
+
+        if (err < last_err)
+        {
+          apx.type = best = type;
+          g_array_append_val (approx[i], apx);
+          last_err = err;
+        }
+
+        ++type;
+      } while (last_err != 0);
+
+      encoded_size += mve_encodings[best].size;
+      ++i;
+      src += 8;
+    }
+    src += 7 * mve->width;
+  }
+
+  /* find best solution with size constraints */
+  GST_DEBUG_OBJECT (mve, "encoded frame %u in %ld bytes (lossless)", mve->video_frames+1, encoded_size);
+
+#if 0
+  /* FIXME */
+  src = (guint16 *) GST_BUFFER_DATA (frame);
+  for (i = 0, y = 0; y < mve->height; y += 8) {
+    for (x = 0; x < mve->width; x += 8, ++i) {
+      GstMveApprox *sol = &g_array_index (approx[i], GstMveApprox, approx[i]->len - 1);
+      guint opcode = mve_encodings[sol->type].opcode;
+      guint j, k;
+
+      if (sol->error > 0)
+        GST_WARNING_OBJECT (mve, "error is %lu for %d/%d (0x%x)", sol->error, x, y, opcode);
+
+      for (j = 0; j < 8; ++j) {
+        guint16 *o = src + j * mve->width;
+        guint16 *c = sol->block + j * 8;
+        if (memcmp (o, c, 16)) {
+          GST_WARNING_OBJECT (mve, "opcode 0x%x (type %d) at %d/%d, line %d:", opcode, sol->type, x, y, j+1);
+          for (k = 0; k < 8; ++k) {
+            o = src + k * mve->width;
+            c = sol->block + k * 8;
+            GST_WARNING_OBJECT (mve, "%d should be: %4d  %4d  %4d  %4d  %4d  %4d  %4d  %4d", k, o[0], o[1], o[2], o[3], o[4], o[5], o[6], o[7]);
+            GST_WARNING_OBJECT (mve, "%d but is   : %4d  %4d  %4d  %4d  %4d  %4d  %4d  %4d", k, c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]);
+          }
+        }
+      }
+      src += 8;
+    }
+    src += 7 * mve->width;
+  }
+#endif
+
+  if (encoded_size > max_data)
+  {
+    encoded_size = gst_mve_find_solution (approx, blocks, encoded_size, max_data);
+    if (encoded_size > max_data)
+    {
+      GST_ERROR_OBJECT (mve, "unable to compress frame to less than %d bytes", encoded_size);
+      for (i = 0; i < blocks; ++i)
+        g_array_free (approx[i], TRUE);
+
+      goto done;
+    }
+    GST_DEBUG_OBJECT (mve, "compressed frame %u to %ld bytes (lossy)", mve->video_frames+1, encoded_size);
+  }
+
+  mve->chunk_video = g_byte_array_sized_new (encoded_size);
+  /* reserve two bytes for the offset pointer we'll fill in later */
+  g_byte_array_set_size (mve->chunk_video, 2);
+
+  pstream = g_byte_array_new ();
+
+  /* encode */
+  src = (guint16 *) GST_BUFFER_DATA (frame);
+  for (i = 0, y = 0; y < mve->height; y += 8) {
+    for (x = 0; x < mve->width; x += 8, ++i) {
+      GstMveApprox *sol = &g_array_index (approx[i], GstMveApprox, approx[i]->len - 1);
+      guint opcode = mve_encodings[sol->type].opcode;
+      GByteArray *dest;
+
+      if (opcode >= 0x2 && opcode <= 0x4)
+        dest = pstream;
+      else
+        dest = mve->chunk_video;
+
+      g_byte_array_append (dest, sol->data, mve_encodings[sol->type].size);
+
+      if (i & 1) {
+        *cm |= opcode << 4;
+        ++cm;
+      } else
+        *cm = opcode;
+
+      /* modify the frame to match the image we actually encoded */
+      if (sol->error > 0)
+        mve_restore_block (mve, src, sol->block);
+
+      src += 8;
+      g_array_free (approx[i], TRUE);
+    }
+    src += 7 * mve->width;
+  }
+
+  /* now update the offset */
+  GST_WRITE_UINT16_LE (mve->chunk_video->data, mve->chunk_video->len);
+  g_byte_array_append (mve->chunk_video, pstream->data, pstream->len);
+  g_byte_array_free (pstream, TRUE);
+
+  ret = GST_FLOW_OK;
+
+done:
+  g_free (approx);
+
+  return ret;
+}
diff -urpN ../gst-plugins-bad-0.10.3/gst/mve/mvevideoenc8.c ./gst/mve/mvevideoenc8.c
--- ../gst-plugins-bad-0.10.3/gst/mve/mvevideoenc8.c	Thu Jan  1 01:00:00 1970
+++ ./gst/mve/mvevideoenc8.c	Sun Jul 30 20:22:37 2006
@@ -0,0 +1,1676 @@
+/*
+ * Interplay MVE video encoder (8 bit)
+ * Copyright (C) 2006 Jens Granseuer <jensgr@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <stdlib.h>
+#include "mve.h"
+#include "gstmvemux.h"
+
+typedef struct _GstMveEncoderData GstMveEncoderData;
+typedef struct _GstMveEncoding GstMveEncoding;
+typedef struct _GstMveApprox GstMveApprox;
+typedef struct _GstMveQuant GstMveQuant;
+
+#define MVE_RMASK   0x00ff0000
+#define MVE_GMASK   0x0000ff00
+#define MVE_BMASK   0x000000ff
+#define MVE_RSHIFT  16
+#define MVE_GSHIFT  8
+#define MVE_BSHIFT  0
+
+#define MVE_RVAL(p)     (((p) & MVE_RMASK) >> MVE_RSHIFT)
+#define MVE_GVAL(p)     (((p) & MVE_GMASK) >> MVE_GSHIFT)
+#define MVE_BVAL(p)     (((p) & MVE_BMASK) >> MVE_BSHIFT)
+#define MVE_COL(r,g,b)  (((r) << MVE_RSHIFT) | ((g) << MVE_GSHIFT) | ((b) << MVE_BSHIFT))
+
+struct _GstMveEncoderData {
+  GstMveMux *mve;
+  /* current position in frame */
+  guint16 x, y;
+
+  /* palette for current frame */
+  const guint32 *palette;
+
+  /* commonly used quantization results
+     (2 and 4 colors) for the current block */
+  guint8 q2block[64];
+  guint8 q2colors[2];
+  guint32 q2error;
+  gboolean q2available;
+
+  guint8 q4block[64];
+  guint8 q4colors[4];
+  guint32 q4error;
+  gboolean q4available;
+};
+
+struct _GstMveEncoding {
+  guint8 opcode;
+  guint8 size;
+  guint32 (*approx)(GstMveEncoderData *enc, const guint8 *src, GstMveApprox *res);
+};
+
+#define MVE_APPROX_MAX_ERROR  G_MAXUINT32
+
+struct _GstMveApprox {
+  guint32 error;
+  guint8 type;
+  guint8 data[64];   /* max 64 bytes encoded per block */
+  guint8 block[64];  /* block in final image */
+};
+
+struct _GstMveQuant {
+  guint32 col;
+  guint16 r_total, g_total, b_total;
+  guint8 r, g, b;
+  guint8 hits, hits_last;
+  guint32 max_error;
+  guint32 max_miss;
+};
+
+#define mve_median(mve, src) mve_median_sub ((mve), (src), 8, 8, 0)
+#define mve_color_dist(c1, c2) \
+        mve_color_dist_rgb (MVE_RVAL (c1), MVE_GVAL (c1), MVE_BVAL (c1), \
+                            MVE_RVAL (c2), MVE_GVAL (c2), MVE_BVAL (c2));
+#define mve_color_dist2(c, r, g, b)  \
+        mve_color_dist_rgb (MVE_RVAL (c), MVE_GVAL (c), MVE_BVAL (c), (r), (g), (b))
+
+
+/* comparison function for qsort() */
+static int
+mve_comp_solution (const void *a, const void *b)
+{
+  const GArray *aa = *((GArray **) a);
+  const GArray *bb = *((GArray **) b);
+
+  if (aa->len <= 1)
+    return G_MAXINT;
+  else if (bb->len <= 1)
+    return G_MININT;
+  else
+    return g_array_index (aa, GstMveApprox, aa->len - 2).error -
+           g_array_index (bb, GstMveApprox, bb->len - 2).error;
+}
+
+static inline guint32 mve_color_dist_rgb (guint8 r1, guint8 g1, guint8 b1,
+                                          guint8 r2, guint8 g2, guint8 b2)
+{
+  /* euclidean distance (minus sqrt) */
+  gint dr = r1 - r2;
+  gint dg = g1 - g2;
+  gint db = b1 - b2;
+  return dr*dr + dg*dg + db*db;
+}
+
+static guint8
+mve_find_pal_color (const guint32 *pal, guint32 col)
+{
+  /* find the closest matching color in the palette */
+  guint i;
+  guint8 best = 0;
+  const guint8 r = MVE_RVAL (col), g = MVE_GVAL (col), b = MVE_BVAL (col);
+  guint32 ebest = MVE_APPROX_MAX_ERROR;
+
+  for (i = 0; i < MVE_PALETTE_COUNT; ++i, ++pal)
+  {
+    guint32 e = mve_color_dist2 (*pal, r, g, b);
+
+    if (e < ebest) {
+      ebest = e;
+      best = i;
+
+      if (ebest == 0)
+        break;
+    }
+  }
+
+  return best;
+}
+
+static guint8
+mve_find_pal_color2 (const guint32 *pal, const guint8 *subset, guint32 col, guint size)
+{
+  /* find the closest matching color in the partial indexed palette */
+  guint i;
+  guint8 best = 0;
+  const guint8 r = MVE_RVAL (col), g = MVE_GVAL (col), b = MVE_BVAL (col);
+  guint32 ebest = MVE_APPROX_MAX_ERROR;
+
+  for (i = 0; i < size; ++i)
+  {
+    guint32 e = mve_color_dist2 (pal[subset[i]], r, g, b);
+
+    if (e < ebest) {
+      ebest = e;
+      best = subset[i];
+
+      if (ebest == 0)
+        break;
+    }
+  }
+
+  return best;
+}
+
+static void
+mve_map_to_palette (const GstMveEncoderData *enc, const guint8 *colors,
+                    const guint8 *data, guint8 *dest, guint w, guint h, guint ncols) {
+  guint x, y;
+
+  for (y = 0; y < h; ++y) {
+    for (x = 0; x < w; ++x) {
+      dest[x] = mve_find_pal_color2 (enc->palette, colors, enc->palette[data[x]], ncols);
+    }
+    data += enc->mve->width;
+    dest += 8;
+  }
+}
+
+/* compute average color in a sub-block */
+static guint8
+mve_median_sub (const GstMveEncoderData *enc, const guint8 *src, guint w, guint h, guint n)
+{
+  guint x, y;
+  const guint max = w * h, max2 = max >> 1;
+  guint32 r_total = max2, g_total = max2, b_total = max2;
+
+  src += ((n * w) % 8) + (((n * (8 - h)) / (12 - w)) * h * enc->mve->width);
+
+  for (y = 0; y < h; ++y) {
+    for (x = 0; x < w; ++x) {
+      guint32 p = enc->palette[src[x]];
+      r_total += MVE_RVAL (p);
+      g_total += MVE_GVAL (p);
+      b_total += MVE_BVAL (p);
+    }
+    src += enc->mve->width;
+  }
+
+  return mve_find_pal_color (enc->palette,
+            MVE_COL (r_total / max, g_total / max, b_total / max));
+}
+
+static void
+mve_quant_init (const GstMveEncoderData *enc, GstMveQuant *q, guint n_clusters,
+                const guint8 *data, guint w, guint h)
+{
+  guint i;
+  guint x, y;
+  guint32 cols[4];
+  guint16 val[2];
+
+  /* init first cluster with lowest (darkest), second with highest (lightest)
+     color. if we need 4 clusters, fill in first and last color in the block
+     and hope they make for a good distribution */
+  cols[0] = cols[1] = cols[2] = enc->palette[data[0]];
+  cols[3] = enc->palette[data[(h - 1) * enc->mve->width + w - 1]];
+
+  /* favour red over green and blue */
+  val[0] = val[1] = (MVE_RVAL (cols[0]) << 1) + MVE_GVAL (cols[0]) + MVE_BVAL (cols[0]);
+
+  for (y = 0; y < h; ++y) {
+    for (x = 0; x < w; ++x) {
+      guint32 c = enc->palette[data[x]];
+
+      if ((c != cols[0]) && (c != cols[1]))
+      {
+        guint v = (MVE_RVAL (c) << 1) + MVE_GVAL (c) + MVE_BVAL (c);
+
+        if (v < val[0]) {
+          val[0] = v;
+          cols[0] = c;
+        } else if (v > val[1]) {
+          val[1] = v;
+          cols[1] = c;
+        }
+      }
+    }
+    data += enc->mve->width;
+  }
+
+  for (i = 0; i < n_clusters; ++i)
+  {
+    q[i].col = cols[i];
+    q[i].r = MVE_RVAL (cols[i]);
+    q[i].g = MVE_GVAL (cols[i]);
+    q[i].b = MVE_BVAL (cols[i]);
+    q[i].r_total = q[i].g_total = q[i].b_total = 0;
+    q[i].hits = q[i].hits_last = 0;
+    q[i].max_error = 0;
+    q[i].max_miss = 0;
+  }
+}
+
+static gboolean
+mve_quant_update_clusters (GstMveQuant *q, guint n_clusters)
+{
+  gboolean changed = FALSE;
+  guint i;
+
+  for (i = 0; i < n_clusters; ++i) {
+    if (q[i].hits > 0)
+    {
+      guint32 means = MVE_COL (
+          (q[i].r_total + q[i].hits/2) / q[i].hits,
+          (q[i].g_total + q[i].hits/2) / q[i].hits,
+          (q[i].b_total + q[i].hits/2) / q[i].hits
+      );
+
+      if ((means != q[i].col) || (q[i].hits != q[i].hits_last))
+        changed = TRUE;
+
+      q[i].col = means;
+      q[i].r_total = q[i].g_total = q[i].b_total = 0;
+    }
+    else
+    {
+      guint j;
+      guint32 max_err = 0;
+      GstMveQuant *worst = NULL;
+
+      /* try to replace unused cluster with a better representative */
+      for (j = 0; j < n_clusters; ++j) {
+        if (q[j].max_error > max_err) {
+          worst = &q[j];
+          max_err = worst->max_error;
+        }
+      }
+      if (worst) {
+        q[i].col = worst->max_miss;
+        worst->max_error = 0;
+        changed = TRUE;
+      }
+    }
+
+    q[i].r = MVE_RVAL (q[i].col);
+    q[i].g = MVE_GVAL (q[i].col);
+    q[i].b = MVE_BVAL (q[i].col);
+    q[i].hits_last = q[i].hits;
+    q[i].hits = 0;
+  }
+  for (i = 0; i < n_clusters; ++i) {
+    q[i].max_error = 0;
+  }
+
+  return changed;
+}
+
+/* quantize a sub-block using a k-means algorithm */
+static guint32
+mve_quantize (const GstMveEncoderData *enc, const guint8 *src,
+              guint w, guint h, guint n, guint ncols,
+              guint8 *dest, guint8 *cols)
+{
+  guint x, y, i;
+  GstMveQuant q[4];
+  const guint8 *data;
+  guint32 error;
+
+  g_assert (n <= 4 && ncols <= 4);
+
+  src += ((n * w) % 8) + (((n * (8 - h)) / (12 - w)) * h * enc->mve->width);
+  dest += ((n * w) % 8) + (((n * (8 - h)) / (12 - w)) * h * 8);
+
+  mve_quant_init (enc, q, ncols, src, w, h);
+
+  do {
+    data = src;
+    error = 0;
+
+    /* for each pixel find the closest cluster */
+    for (y = 0; y < h; ++y) {
+      for (x = 0; x < w; ++x) {
+        guint32 c = enc->palette[data[x]];
+        guint8 r = MVE_RVAL (c), g = MVE_GVAL (c), b = MVE_BVAL (c);
+        guint32 minerr = MVE_APPROX_MAX_ERROR, err;
+        GstMveQuant *best;
+
+        for (i = 0; i < ncols; ++i) {
+          err = mve_color_dist_rgb (r, g, b, q[i].r, q[i].g, q[i].b);
+
+          if (err < minerr) {
+            minerr = err;
+            best = &q[i];
+          }
+        }
+
+        ++best->hits;
+        best->r_total += r;
+        best->g_total += g;
+        best->b_total += b;
+
+        if (minerr > best->max_error) {
+          best->max_error = minerr;
+          best->max_miss = c;
+        }
+
+        error += minerr;
+      }
+      data += enc->mve->width;
+    }
+  } while (mve_quant_update_clusters (q, ncols));
+
+  /* fill cols array with result colors */
+  for (i = 0; i < ncols; ++i)
+    cols[i] = mve_find_pal_color (enc->palette, q[i].col);
+
+  /* make sure we have unique colors in slots 0/1 and 2/3 */
+  if (cols[0] == cols[1])
+      ++cols[1];
+  if ((ncols > 2) && (cols[2] == cols[3]))
+      ++cols[3];
+
+  /* generate the resulting quantized block */
+  mve_map_to_palette (enc, cols, src, dest, w, h, ncols);
+
+  return error;
+}
+
+static guint32
+mve_block_error (const GstMveEncoderData *enc, const guint8 *b1, const guint8 *b2, guint32 threshold)
+{
+  /* compute error between two blocks in a frame */
+  guint32 e = 0;
+  guint x, y;
+
+  for (y = 0; y < 8; ++y) {
+    for (x = 0; x <  8; ++x) {
+      e += mve_color_dist (enc->palette[*b1], enc->palette[*b2]);
+
+      /* using a threshold to return early gives a huge performance bonus */
+      if (e >= threshold)
+        return MVE_APPROX_MAX_ERROR;
+      ++b1;
+      ++b2;
+    }
+
+    b1 += enc->mve->width - 8;
+    b2 += enc->mve->width - 8;
+  }
+
+  return e;
+}
+
+static guint32
+mve_block_error_packed (const GstMveEncoderData *enc, const guint8 *block, const guint8 *scratch)
+{
+  /* compute error between a block in a frame and a (continuous) scratch pad */
+  guint32 e = 0;
+  guint x, y;
+
+  for (y = 0; y < 8; ++y) {
+    for (x = 0; x <  8; ++x) {
+      guint32 c1 = enc->palette[block[x]], c2 = enc->palette[scratch[x]];
+      e += mve_color_dist (c1, c2);
+    }
+    block += enc->mve->width;
+    scratch += 8;
+  }
+
+  return e;
+}
+
+static void
+mve_store_block (const GstMveMux *mve, const guint8 *block, guint8 *scratch)
+{
+  /* copy block from frame to a (continuous) scratch pad */
+  guint y;
+
+  for (y = 0; y < 8; ++y) {
+    memcpy (scratch, block, 8);
+    block += mve->width;
+    scratch += 8;
+  }
+}
+
+static void
+mve_restore_block (const GstMveMux *mve, guint8 *block, const guint8 *scratch)
+{
+  /* copy block from scratch pad to frame */
+  guint y;
+
+  for (y = 0; y < 8; ++y) {
+    memcpy (block, scratch, 8);
+    block += mve->width;
+    scratch += 8;
+  }
+}
+
+
+static guint32
+mve_try_vector (GstMveEncoderData *enc, const guint8 *src,
+                const guint8 *frame, gint pn, GstMveApprox *apx)
+{
+  /* try to locate a similar 8x8 block in the given frame using a motion vector */
+  guint i;
+  gint dx, dy;
+  gint fx, fy;
+  guint32 err;
+
+  apx->error = MVE_APPROX_MAX_ERROR;
+
+  for (i = 0; i < 256; ++i)
+  {
+    if (i < 56) {
+      dx = 8 + (i % 7);
+      dy = i / 7;
+    } else {
+      dx = -14 + ((i - 56) % 29);
+      dy =   8 + ((i - 56) / 29);
+    }
+
+    fx = enc->x + dx * pn;
+    fy = enc->y + dy * pn;
+
+    if ((fx >= 0) && (fy >= 0) && (fx + 8 <= enc->mve->width) && (fy + 8 <= enc->mve->height))
+    {
+      err = mve_block_error (enc, src, frame + fy * enc->mve->width + fx, apx->error);
+      if (err < apx->error)
+      {
+        apx->data[0] = i;
+        mve_store_block (enc->mve, frame + fy * enc->mve->width + fx, apx->block);
+        apx->error = err;
+        if (err == 0)
+          return 0;
+      }
+    }
+  }
+
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x0 (GstMveEncoderData *enc, const guint8 *src, GstMveApprox *apx)
+{
+  /* copy a block from the last frame (0 bytes) */
+  if (enc->mve->last_frame == NULL)
+    return MVE_APPROX_MAX_ERROR;
+
+  mve_store_block (enc->mve,
+        GST_BUFFER_DATA (enc->mve->last_frame) +
+        enc->y * enc->mve->width + enc->x, apx->block);
+  apx->error = mve_block_error_packed (enc, src, apx->block);
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x1 (GstMveEncoderData *enc, const guint8 *src, GstMveApprox *apx)
+{
+  /* copy a block from the second to last frame (0 bytes) */
+  if (enc->mve->second_last_frame == NULL)
+    return MVE_APPROX_MAX_ERROR;
+
+  mve_store_block (enc->mve,
+        GST_BUFFER_DATA (enc->mve->second_last_frame) +
+        enc->y * enc->mve->width + enc->x, apx->block);
+  apx->error = mve_block_error_packed (enc, src, apx->block);
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x2 (GstMveEncoderData *enc, const guint8 *src, GstMveApprox *apx)
+{
+  /* copy block from 2 frames ago using a motion vector (1 byte) */
+  if (enc->mve->quick_encoding || enc->mve->second_last_frame == NULL)
+    return MVE_APPROX_MAX_ERROR;
+
+  apx->error = mve_try_vector (enc, src,
+          GST_BUFFER_DATA (enc->mve->second_last_frame),
+          1, apx);
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x3 (GstMveEncoderData *enc, const guint8 *src, GstMveApprox *apx)
+{
+  /* copy 8x8 block from current frame from an up/left block (1 byte) */
+  if (enc->mve->quick_encoding)
+    return MVE_APPROX_MAX_ERROR;
+
+  apx->error = mve_try_vector (enc, src,
+          src - enc->mve->width * enc->y - enc->x, -1, apx);
+  return apx->error;
+}
+
+
+static guint32
+mve_encode_0x4 (GstMveEncoderData *enc, const guint8 *src, GstMveApprox *apx)
+{
+  /* copy a block from previous frame using a motion vector (-8/-8 to +7/+7) (1 byte) */
+  const GstMveMux *mve = enc->mve;
+  guint32 err;
+  const guint8 *frame;
+  gint x1, x2, xi, y1, y2, yi;
+
+  if (mve->last_frame == NULL)
+    return MVE_APPROX_MAX_ERROR;
+
+  frame = GST_BUFFER_DATA (mve->last_frame);
+
+  x1 = enc->x - 8;
+  x2 = enc->x + 7;
+  if (x1 < 0)
+    x1 = 0;
+  else if (x2 + 8 > mve->width)
+    x2 = mve->width - 8;
+
+  y1 = enc->y - 8;
+  y2 = enc->y + 7;
+  if (y1 < 0)
+    y1 = 0;
+  else if (y2 + 8 > mve->height)
+    y2 = mve->height - 8;
+
+  apx->error = MVE_APPROX_MAX_ERROR;
+
+  for (yi = y1; yi <= y2; ++yi) {
+    guint yoff = yi * mve->width;
+
+    for (xi = x1; xi <= x2; ++xi) {
+      err = mve_block_error (enc, src, frame + yoff + xi, apx->error);
+      if (err < apx->error)
+      {
+        apx->data[0] = ((xi - enc->x + 8) & 0xF) | ((yi - enc->y + 8) << 4);
+        mve_store_block (mve, frame + yoff + xi, apx->block);
+        apx->error = err;
+        if (err == 0)
+          return 0;
+      }
+    }
+  }
+
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x5 (GstMveEncoderData *enc, const guint8 *src, GstMveApprox *apx)
+{
+  /* copy a block from previous frame using a motion vector
+     (-128/-128 to +127/+127) (2 bytes) */
+  const GstMveMux *mve = enc->mve;
+  guint32 err;
+  const guint8 *frame;
+  gint x1, x2, xi, y1, y2, yi;
+
+  if (mve->quick_encoding || mve->last_frame == NULL)
+    return MVE_APPROX_MAX_ERROR;
+
+  frame = GST_BUFFER_DATA (mve->last_frame);
+
+  x1 = enc->x - 128;
+  x2 = enc->x + 127;
+  if (x1 < 0)
+    x1 = 0;
+  if (x2 + 8 > mve->width)
+    x2 = mve->width - 8;
+
+  y1 = enc->y - 128;
+  y2 = enc->y + 127;
+  if (y1 < 0)
+    y1 = 0;
+  if (y2 + 8 > mve->height)
+    y2 = mve->height - 8;
+
+  apx->error = MVE_APPROX_MAX_ERROR;
+
+  for (yi = y1; yi <= y2; ++yi) {
+    gint yoff = yi * mve->width;
+
+    for (xi = x1; xi <= x2; ++xi) {
+      err = mve_block_error (enc, src, frame + yoff + xi, apx->error);
+      if (err < apx->error)
+      {
+        apx->data[0] = xi - enc->x;
+        apx->data[1] = yi - enc->y;
+        mve_store_block (mve, frame + yoff + xi, apx->block);
+        apx->error = err;
+        if (err == 0)
+          return 0;
+      }
+    }
+  }
+
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x7a (GstMveEncoderData *enc, const guint8 *src, GstMveApprox *apx)
+{
+  /* 2-color encoding for 2x2 solid blocks (4 bytes) */
+  guint32 pix[4];
+  guint8 mean;
+  guint32 e1, e2;
+  guint x, y;
+  guint8 r[2], g[2], b[2], rb, gb, bb;
+  guint8 *block = apx->block;
+  guint16 mask = 0x0001;
+  guint16 flags = 0;
+
+  /* calculate mean colors for the entire block */
+  if (!enc->q2available) {
+    enc->q2error = mve_quantize (enc, src, 8, 8, 0, 2, enc->q2block, enc->q2colors);
+    enc->q2available = TRUE;
+  }
+
+  /* p0 > p1 */
+  apx->data[0] = MAX (enc->q2colors[0], enc->q2colors[1]);
+  apx->data[1] = MIN (enc->q2colors[0], enc->q2colors[1]);
+
+  for (x = 0; x < 2; ++x) {
+    r[x] = MVE_RVAL (enc->palette[apx->data[x]]);
+    g[x] = MVE_GVAL (enc->palette[apx->data[x]]);
+    b[x] = MVE_BVAL (enc->palette[apx->data[x]]);
+  }
+
+  /* calculate mean colors for each 2x2 block and map to global colors */
+  for (y = 0; y < 4; ++y) {
+    for (x = 0; x < 4; ++x, mask <<= 1) {
+      pix[0] = enc->palette[src[0]];
+      pix[1] = enc->palette[src[1]];
+      pix[2] = enc->palette[src[enc->mve->width]];
+      pix[3] = enc->palette[src[enc->mve->width + 1]];
+
+      rb = (MVE_RVAL (pix[0]) + MVE_RVAL (pix[1]) + MVE_RVAL (pix[2]) + MVE_RVAL (pix[3]) + 2) / 4;
+      gb = (MVE_GVAL (pix[0]) + MVE_GVAL (pix[1]) + MVE_GVAL (pix[2]) + MVE_GVAL (pix[3]) + 2) / 4;
+      bb = (MVE_BVAL (pix[0]) + MVE_BVAL (pix[1]) + MVE_BVAL (pix[2]) + MVE_BVAL (pix[3]) + 2) / 4;
+
+      e1 = mve_color_dist_rgb (rb, gb, bb, r[0], g[0], b[0]);
+      e2 = mve_color_dist_rgb (rb, gb, bb, r[1], g[1], b[1]);
+
+      if (e1 > e2) {
+        mean = apx->data[1];
+        flags |= mask;
+      } else {
+        mean = apx->data[0];
+      }
+
+      block[0] = block[1] = block[8] = block[9] = mean;
+
+      src += 2;
+      block += 2;
+    }
+    src += (enc->mve->width * 2) - 8;
+    block += 8;
+  }
+
+  apx->data[2] = flags & 0x00FF;
+  apx->data[3] = (flags & 0xFF00) >> 8;
+
+  apx->error = mve_block_error_packed (enc, src - enc->mve->width * 8, apx->block);
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x7b (GstMveEncoderData *enc, const guint8 *src, GstMveApprox *apx)
+{
+  /* generic 2-color encoding (10 bytes) */
+  guint x, y;
+  guint8 *data = apx->data;
+  guint8 *block = apx->block;
+
+  if (!enc->q2available) {
+    enc->q2error = mve_quantize (enc, src, 8, 8, 0, 2, enc->q2block, enc->q2colors);
+    enc->q2available = TRUE;
+  }
+
+  memcpy (block, enc->q2block, 64);
+
+  /* p0 <= p1 */
+  data[0] = MIN (enc->q2colors[0], enc->q2colors[1]);
+  data[1] = MAX (enc->q2colors[0], enc->q2colors[1]);
+  data += 2;
+
+  for (y = 0; y < 8; ++y) {
+    guint8 flags = 0;
+
+    for (x = 0x01; x <= 0x80; x <<= 1) {
+      if (*block == apx->data[1])
+        flags |= x;
+      ++block;
+    }
+    *data++ = flags;
+  }
+
+  apx->error = enc->q2error;
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x8a (GstMveEncoderData *enc, const guint8 *src, GstMveApprox *apx)
+{
+  /* 2-color encoding for top and bottom half (12 bytes) */
+  guint8 cols[2];
+  guint32 flags;
+  guint i, x, y, shifter;
+  guint8 *block = apx->block;
+  guint8 *data = apx->data;
+
+  apx->error = 0;
+
+  for (i = 0; i < 2; ++i) {
+    apx->error += mve_quantize (enc, src, 8, 4, i, 2, apx->block, cols);
+
+    flags = 0;
+    shifter = 0;
+
+    /* p0 > p1 && p2 > p3 */
+    data[0] = MAX (cols[0], cols[1]);
+    data[1] = MIN (cols[0], cols[1]);
+
+    for (y = 0; y < 4; ++y) {
+      for (x = 0; x < 8; ++x, ++shifter) {
+        if (block[x] == data[1])
+          flags |= 1 << shifter;
+      }
+      block += 8;
+    }
+    data[2] = flags & 0x000000FF;
+    data[3] = (flags & 0x0000FF00) >> 8;
+    data[4] = (flags & 0x00FF0000) >> 16;
+    data[5] = (flags & 0xFF000000) >> 24;
+    data += 6;
+  }
+
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x8b (GstMveEncoderData *enc, const guint8 *src, GstMveApprox *apx)
+{
+  /* 2-color encoding for left and right half (12 bytes) */
+  guint8 cols[2];
+  guint32 flags;
+  guint i, x, y, shifter;
+  guint8 *block = apx->block;
+  guint8 *data = apx->data;
+
+  apx->error = 0;
+
+  for (i = 0; i < 2; ++i) {
+    apx->error += mve_quantize (enc, src, 4, 8, i, 2, apx->block, cols);
+
+    flags = 0;
+    shifter = 0;
+
+    /* p0 > p1 && p2 <= p3 */
+    data[i] = MAX (cols[0], cols[1]);
+    data[i^1] = MIN (cols[0], cols[1]);
+
+    for (y = 0; y < 8; ++y) {
+      for (x = 0; x < 4; ++x, ++shifter) {
+        if (block[x] == data[1])
+          flags |= 1 << shifter;
+      }
+      block += 8;
+    }
+
+    data[2] = flags & 0x000000FF;
+    data[3] = (flags & 0x0000FF00) >> 8;
+    data[4] = (flags & 0x00FF0000) >> 16;
+    data[5] = (flags & 0xFF000000) >> 24;
+    data += 6;
+    block = apx->block + 4;
+  }
+
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x8c (GstMveEncoderData *enc, const guint8 *src, GstMveApprox *apx)
+{
+  /* 2-color encoding for each 4x4 quadrant (16 bytes) */
+  guint8 cols[2];
+  guint16 flags;
+  guint i, x, y, shifter;
+  guint8 *block;
+  guint8 *data = apx->data;
+
+  apx->error = 0;
+
+  for (i = 0; i < 4; ++i)
+  {
+    apx->error += mve_quantize (enc, src, 4, 4, ((i&1) << 1) | ((i&2) >> 1), 2, apx->block, cols);
+
+    /* p0 < p1 */
+    if (i == 0) {
+      data[0] = MIN (cols[0], cols[1]);
+      data[1] = MAX (cols[0], cols[1]);
+    } else {
+      data[0] = cols[0];
+      data[1] = cols[1];
+    }
+
+    block = apx->block + ((i / 2) * 4) + ((i % 2) * 32);
+    flags = 0;
+    shifter = 0;
+
+    for (y = 0; y < 4; ++y) {
+      for (x = 0; x < 4; ++x, ++shifter) {
+        if (block[x] == data[1])
+          flags |= 1 << shifter;
+      }
+      block += 8;
+    }
+
+    data[2] = flags & 0x00FF;
+    data[3] = (flags & 0xFF00) >> 8;
+    data += 4;
+  }
+
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x9a (GstMveEncoderData *enc, const guint8 *src, GstMveApprox *apx)
+{
+  /* 4-color encoding for 2x2 solid blocks (8 bytes) */
+  guint32 p[4];
+  guint32 e, emin;
+  guint i, x, y, mean;
+  guint8 r[4], g[4], b[4], rb, gb, bb;
+  guint8 *block = apx->block;
+  guint shifter = 0;
+  guint32 flags = 0;
+
+  /* calculate mean colors for the entire block */
+  if (!enc->q4available) {
+    enc->q4error = mve_quantize (enc, src, 8, 8, 0, 4, enc->q4block, enc->q4colors);
+    enc->q4available = TRUE;
+  }
+
+  /* p0 <= p1 && p2 > p3 */
+  apx->data[0] = MIN (enc->q4colors[0], enc->q4colors[1]);
+  apx->data[1] = MAX (enc->q4colors[0], enc->q4colors[1]);
+  apx->data[2] = MAX (enc->q4colors[2], enc->q4colors[3]);
+  apx->data[3] = MIN (enc->q4colors[2], enc->q4colors[3]);
+
+  for (i = 0; i < 4; ++i) {
+    r[i] = MVE_RVAL (enc->palette[apx->data[i]]);
+    g[i] = MVE_GVAL (enc->palette[apx->data[i]]);
+    b[i] = MVE_BVAL (enc->palette[apx->data[i]]);
+  }
+
+  /* calculate mean colors for each 2x2 block and map to global colors */
+  for (y = 0; y < 4; ++y) {
+    for (x = 0; x < 4; ++x, shifter += 2) {
+      p[0] = enc->palette[src[0]];
+      p[1] = enc->palette[src[1]];
+      p[2] = enc->palette[src[enc->mve->width]];
+      p[3] = enc->palette[src[enc->mve->width + 1]];
+
+      rb = (MVE_RVAL (p[0]) + MVE_RVAL (p[1]) + MVE_RVAL (p[2]) + MVE_RVAL (p[3]) + 2) / 4;
+      gb = (MVE_GVAL (p[0]) + MVE_GVAL (p[1]) + MVE_GVAL (p[2]) + MVE_GVAL (p[3]) + 2) / 4;
+      bb = (MVE_BVAL (p[0]) + MVE_BVAL (p[1]) + MVE_BVAL (p[2]) + MVE_BVAL (p[3]) + 2) / 4;
+
+      emin = MVE_APPROX_MAX_ERROR;
+      for (i = 0; i < 4; ++i) {
+        e = mve_color_dist_rgb (rb, gb, bb, r[i], g[i], b[i]);
+        if (e < emin) {
+          emin = e;
+          mean = i;
+        }
+      }
+
+      flags |= mean << shifter;
+      block[0] = block[1] = block[8] = block[9] = apx->data[mean];
+
+      src += 2;
+      block += 2;
+    }
+    src += (enc->mve->width * 2) - 8;
+    block += 8;
+  }
+
+  apx->data[4] = flags & 0x000000FF;
+  apx->data[5] = (flags & 0x0000FF00) >> 8;
+  apx->data[6] = (flags & 0x00FF0000) >> 16;
+  apx->data[7] = (flags & 0xFF000000) >> 24;
+
+  apx->error = mve_block_error_packed (enc, src - 8 * enc->mve->width, apx->block);
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x9b (GstMveEncoderData *enc, const guint8 *src, GstMveApprox *apx)
+{
+  /* 4-color encoding for 2x1 solid blocks (12 bytes) */
+  guint32 p[2];
+  guint32 e, emin;
+  guint i, x, y, mean;
+  guint8 r[4], g[4], b[4], rb, gb, bb;
+  guint8 *data = apx->data;
+  guint8 *block = apx->block;
+  guint shifter = 0;
+  guint32 flags = 0;
+
+  /* calculate mean colors for the entire block */
+  if (!enc->q4available) {
+    enc->q4error = mve_quantize (enc, src, 8, 8, 0, 4, enc->q4block, enc->q4colors);
+    enc->q4available = TRUE;
+  }
+
+  /* p0 > p1 && p2 <= p3 */
+  data[0] = MAX (enc->q4colors[0], enc->q4colors[1]);
+  data[1] = MIN (enc->q4colors[0], enc->q4colors[1]);
+  data[2] = MIN (enc->q4colors[2], enc->q4colors[3]);
+  data[3] = MAX (enc->q4colors[2], enc->q4colors[3]);
+
+  for (i = 0; i < 4; ++i) {
+    r[i] = MVE_RVAL (enc->palette[data[i]]);
+    g[i] = MVE_GVAL (enc->palette[data[i]]);
+    b[i] = MVE_BVAL (enc->palette[data[i]]);
+  }
+  data += 4;
+
+  /* calculate mean colors for each 2x1 block and map to global colors */
+  for (y = 0; y < 8; ++y) {
+    for (x = 0; x < 4; ++x, shifter += 2) {
+      p[0] = enc->palette[src[0]];
+      p[1] = enc->palette[src[1]];
+      rb = (MVE_RVAL (p[0]) + MVE_RVAL (p[1]) + 1) / 2;
+      gb = (MVE_GVAL (p[0]) + MVE_GVAL (p[1]) + 1) / 2;
+      bb = (MVE_BVAL (p[0]) + MVE_BVAL (p[1]) + 1) / 2;
+
+      emin = MVE_APPROX_MAX_ERROR;
+      for (i = 0; i < 4; ++i) {
+        e = mve_color_dist_rgb (rb, gb, bb, r[i], g[i], b[i]);
+        if (e < emin) {
+          emin = e;
+          mean = i;
+        }
+      }
+
+      flags |= mean << shifter;
+      block[0] = block[1] = apx->data[mean];
+
+      src += 2;
+      block += 2;
+    }
+
+    if ((y == 3) || (y == 7)) {
+      data[0] = flags & 0x000000FF;
+      data[1] = (flags & 0x0000FF00) >> 8;
+      data[2] = (flags & 0x00FF0000) >> 16;
+      data[3] = (flags & 0xFF000000) >> 24;
+      data += 4;
+
+      flags = 0;
+      shifter = 0;
+    }
+
+    src += enc->mve->width - 8;
+  }
+
+  apx->error = mve_block_error_packed (enc, src - 8 * enc->mve->width, apx->block);
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x9c (GstMveEncoderData *enc, const guint8 *src, GstMveApprox *apx)
+{
+  /* 4-color encoding for 1x2 solid blocks (12 bytes) */
+  guint32 p[2];
+  guint32 e, emin;
+  guint i, x, y, mean;
+  guint8 r[4], g[4], b[4], rb, gb, bb;
+  guint8 *data = apx->data;
+  guint8 *block = apx->block;
+  guint shifter = 0;
+  guint32 flags = 0;
+
+  /* calculate mean colors for the entire block */
+  if (!enc->q4available) {
+    enc->q4error = mve_quantize (enc, src, 8, 8, 0, 4, enc->q4block, enc->q4colors);
+    enc->q4available = TRUE;
+  }
+
+  /* p0 > p1 && p2 > p3 */
+  data[0] = MAX (enc->q4colors[0], enc->q4colors[1]);
+  data[1] = MIN (enc->q4colors[0], enc->q4colors[1]);
+  data[2] = MAX (enc->q4colors[2], enc->q4colors[3]);
+  data[3] = MIN (enc->q4colors[2], enc->q4colors[3]);
+
+  for (i = 0; i < 4; ++i) {
+    r[i] = MVE_RVAL (enc->palette[data[i]]);
+    g[i] = MVE_GVAL (enc->palette[data[i]]);
+    b[i] = MVE_BVAL (enc->palette[data[i]]);
+  }
+  data += 4;
+
+  /* calculate mean colors for each 1x2 block and map to global colors */
+  for (y = 0; y < 4; ++y) {
+    for (x = 0; x < 8; ++x, shifter += 2) {
+      p[0] = enc->palette[src[0]];
+      p[1] = enc->palette[src[enc->mve->width]];
+      rb = (MVE_RVAL (p[0]) + MVE_RVAL (p[1]) + 1) / 2;
+      gb = (MVE_GVAL (p[0]) + MVE_GVAL (p[1]) + 1) / 2;
+      bb = (MVE_BVAL (p[0]) + MVE_BVAL (p[1]) + 1) / 2;
+
+      emin = MVE_APPROX_MAX_ERROR;
+      for (i = 0; i < 4; ++i) {
+        e = mve_color_dist_rgb (rb, gb, bb, r[i], g[i], b[i]);
+        if (e < emin) {
+          emin = e;
+          mean = i;
+        }
+      }
+
+      flags |= mean << shifter;
+      block[0] = block[8] = apx->data[mean];
+
+      ++src;
+      ++block;
+    }
+
+    if ((y == 1) || (y == 3)) {
+      data[0] = flags & 0x000000FF;
+      data[1] = (flags & 0x0000FF00) >> 8;
+      data[2] = (flags & 0x00FF0000) >> 16;
+      data[3] = (flags & 0xFF000000) >> 24;
+      data += 4;
+
+      flags = 0;
+      shifter = 0;
+    }
+
+    src += (enc->mve->width * 2) - 8;
+    block += 8;
+  }
+
+  apx->error = mve_block_error_packed (enc, src - 8 * enc->mve->width, apx->block);
+  return apx->error;
+}
+
+static guint32
+mve_encode_0x9d (GstMveEncoderData *enc, const guint8 *src, GstMveApprox *apx)
+{
+  /* generic 4-color encoding (20 bytes) */
+  guint32 flags = 0;
+  guint shifter = 0;
+  guint i, x, y;
+  guint8 *data = apx->data;
+  guint8 *block = apx->block;
+
+  if (!enc->q4available) {
+    enc->q4error = mve_quantize (enc, src, 8, 8, 0, 4, enc->q4block, enc->q4colors);
+    enc->q4available = TRUE;
+  }
+
+  memcpy (block, enc->q4block, 64);
+
+  /* p0 <= p1 && p2 <= p3 */
+  data[0] = MIN (enc->q4colors[0], enc->q4colors[1]);
+  data[1] = MAX (enc->q4colors[0], enc->q4colors[1]);
+  data[2] = MIN (enc->q4colors[2], enc->q4colors[3]);
+  data[3] = MAX (enc->q4colors[2], enc->q4colors[3]);
+  data += 4;
+
+  for (y = 0; y < 8; ++y) {
+    for (x = 0; x < 8; ++x, shifter += 2) {
+
+      for (i = 0; i < 3; ++i) {
+        if (*block == apx->data[i])
+          break;
+      }
+
+      flags |= i << shifter;
+      ++block;
+    }
+
+    data[0] = flags & 0x000000FF;
+    data[1] = (flags & 0x0000FF00) >> 8;
+    data += 2;
+    shifter = 0;
+    flags = 0;
+  }
+
+  apx->error = enc->q4error;
+  return apx->error;
+}
+
+static guint32
+mve_encode_0xaa (GstMveEncoderData *enc, const guint8 *src, GstMveApprox *apx)
+{
+  /* 4-color encoding for top and bottom half (24 bytes) */
+  guint8 cols[4];
+  guint32 flags;
+  guint i, j, x, y, shifter;
+  guint8 *block = apx->block;
+  guint8 *data = apx->data;
+  const guint8 *p;
+
+  apx->error = 0;
+
+  for (i = 0; i < 2; ++i) {
+    apx->error += mve_quantize (enc, src, 8, 4, i, 4, apx->block, cols);
+
+    flags = 0;
+    shifter = 0;
+
+    /* p0 > p1 && p4 > p5 */
+    data[0] = MAX (cols[0], cols[1]);
+    data[1] = MIN (cols[0], cols[1]);
+    data[2] = cols[2];
+    data[3] = cols[3];
+    p = data;
+    data += 4;
+
+    for (y = 0; y < 4; ++y) {
+      for (x = 0; x < 8; ++x, shifter += 2) {
+        for (j = 0; j < 3; ++j) {
+          if (block[x] == p[j]) break;
+        }
+        flags |= j << shifter;
+      }
+      block += 8;
+
+      if ((y == 1) || (y == 3)) {
+        data[0] = flags & 0x000000FF;
+        data[1] = (flags & 0x0000FF00) >> 8;
+        data[2] = (flags & 0x00FF0000) >> 16;
+        data[3] = (flags & 0xFF000000) >> 24;
+        data += 4;
+        flags = 0;
+        shifter = 0;
+      }
+    }
+  }
+
+  return apx->error;
+}
+
+static guint32
+mve_encode_0xab (GstMveEncoderData *enc, const guint8 *src, GstMveApprox *apx)
+{
+  /* 4-color encoding for left and right half (24 bytes) */
+  guint8 cols[4];
+  guint32 flags;
+  guint i, j, x, y, shifter;
+  guint8 *block = apx->block;
+  guint8 *data = apx->data;
+  const guint8 *p;
+
+  apx->error = 0;
+
+  for (i = 0; i < 2; ++i) {
+    apx->error += mve_quantize (enc, src, 4, 8, i, 4, apx->block, cols);
+
+    flags = 0;
+    shifter = 0;
+
+    /* p0 > p1 && p4 <= p5 */
+    data[i] = MAX (cols[0], cols[1]);
+    data[i^1] = MIN (cols[0], cols[1]);
+    data[2] = cols[2];
+    data[3] = cols[3];
+    p = data;
+    data += 4;
+
+    for (y = 0; y < 8; ++y) {
+      for (x = 0; x < 4; ++x, shifter += 2) {
+        for (j = 0; j < 3; ++j) {
+          if (block[x] == p[j]) break;
+        }
+        flags |= j << shifter;
+      }
+      block += 8;
+
+      if ((y == 3) || (y == 7)) {
+        data[0] = flags & 0x000000FF;
+        data[1] = (flags & 0x0000FF00) >> 8;
+        data[2] = (flags & 0x00FF0000) >> 16;
+        data[3] = (flags & 0xFF000000) >> 24;
+        data += 4;
+        flags = 0;
+        shifter = 0;
+      }
+    }
+    block = apx->block + 4;
+  }
+
+  return apx->error;
+}
+
+static guint32
+mve_encode_0xac (GstMveEncoderData *enc, const guint8 *src, GstMveApprox *apx)
+{
+  /* 4-color encoding for each 4x4 quadrant (32 bytes) */
+  guint8 cols[4];
+  guint32 flags;
+  guint i, j, x, y, shifter;
+  guint8 *block;
+  guint8 *data = apx->data;
+
+  apx->error = 0;
+
+  for (i = 0; i < 4; ++i)
+  {
+    apx->error += mve_quantize (enc, src, 4, 4, ((i&1) << 1) | ((i&2) >> 1), 4, apx->block, cols);
+
+    /* p0 <= p1 */
+    data[0] = MIN (cols[0], cols[1]);
+    data[1] = MAX (cols[0], cols[1]);
+    data[2] = cols[2];
+    data[3] = cols[3];
+
+    block = apx->block + ((i / 2) * 4) + ((i % 2) * 32);
+    flags = 0;
+    shifter = 0;
+
+    for (y = 0; y < 4; ++y) {
+      for (x = 0; x < 4; ++x, shifter += 2) {
+        for (j = 0; j < 3; ++j) {
+          if (block[x] == data[j]) break;
+        }
+        flags |= j << shifter;
+      }
+      block += 8;
+    }
+
+    data[4] = flags & 0x000000FF;
+    data[5] = (flags & 0x0000FF00) >> 8;
+    data[6] = (flags & 0x00FF0000) >> 16;
+    data[7] = (flags & 0xFF000000) >> 24;
+    data += 8;
+  }
+
+  return apx->error;
+}
+
+static guint32
+mve_encode_0xb (GstMveEncoderData *enc, const guint8 *src, GstMveApprox *apx)
+{
+  /* 64-color encoding (each pixel in block is a different color) (64 bytes) */
+  mve_store_block (enc->mve, src, apx->block);
+  memcpy (apx->data, apx->block, 64);
+  apx->error = 0;
+
+  return 0;
+}
+
+static guint32
+mve_encode_0xc (GstMveEncoderData *enc, const guint8 *src, GstMveApprox *apx)
+{
+  /* 16-color block encoding: each 2x2 block is a different color (16 bytes) */
+  guint i = 0, x, y;
+  const guint w = enc->mve->width;
+  guint16 r, g, b;
+
+  /* calculate median color for each 2x2 block */
+  for (y = 0; y < 4; ++y) {
+    for (x = 0; x < 4; ++x) {
+      guint32 p = enc->palette[src[0]];
+      r = MVE_RVAL (p) + 2;
+      g = MVE_GVAL (p) + 2;
+      b = MVE_BVAL (p) + 2;
+
+      p = enc->palette[src[1]];
+      r += MVE_RVAL (p);
+      g += MVE_GVAL (p);
+      b += MVE_BVAL (p);
+
+      p = enc->palette[src[w]];
+      r += MVE_RVAL (p);
+      g += MVE_GVAL (p);
+      b += MVE_BVAL (p);
+
+      p = enc->palette[src[w + 1]];
+      r += MVE_RVAL (p);
+      g += MVE_GVAL (p);
+      b += MVE_BVAL (p);
+
+      apx->block[i] = apx->block[i+1] = apx->block[i+2] = apx->block[i+3] =
+      apx->data[i >> 2] =
+              mve_find_pal_color (enc->palette, MVE_COL (r >> 2, g >> 2, b >> 2));
+
+      i += 4;
+      src += 2;
+    }
+    src += (w * 2) - 8;
+  }
+
+  apx->error = mve_block_error_packed (enc, src - (8 * w), apx->block);
+  return apx->error;
+}
+
+static guint32
+mve_encode_0xd (GstMveEncoderData *enc, const guint8 *src, GstMveApprox *apx)
+{
+  /* 4-color block encoding: each 4x4 block is a different color (4 bytes) */
+  guint i, y;
+
+  /* calculate median color for each 4x4 block */
+  for (i = 0; i < 4; ++i) {
+    guint8 median = mve_median_sub (enc, src, 4, 4, ((i&1) << 1) | ((i&2) >> 1));
+    guint8 *block = apx->block + ((i / 2) * 4) + ((i % 2) * 32);
+
+    for (y = 0; y < 4; ++y) {
+      memset (block, median, 4);
+      block += 8;
+    }
+
+    apx->data[i] = median;
+  }
+
+  apx->error = mve_block_error_packed (enc, src, apx->block);
+  return apx->error;
+}
+
+static guint32
+mve_encode_0xe (GstMveEncoderData *enc, const guint8 *src, GstMveApprox *apx)
+{
+  /* 1-color encoding: the whole block is 1 solid color (1 bytes) */
+  guint8 median = mve_median (enc, src);
+
+  memset (apx->block, median, 64);
+
+  apx->data[0] = median;
+  apx->error = mve_block_error_packed (enc, src, apx->block);
+  return apx->error;
+}
+
+static guint32
+mve_encode_0xf (GstMveEncoderData *enc, const guint8 *src, GstMveApprox *apx)
+{
+  /* 2 colors dithered encoding (2 bytes) */
+  guint i, x, y;
+  guint32 r[2] = { 0 }, g[2] = { 0 }, b[2] = { 0 };
+  guint8 col[2];
+
+  /* find medians for both colors */
+  for (y = 0; y < 8; ++y) {
+    for (x = 0; x < 8; x += 2) {
+      guint16 p = src[x];
+      r[y & 1] += MVE_RVAL (p);
+      g[y & 1] += MVE_GVAL (p);
+      b[y & 1] += MVE_BVAL (p);
+
+      p = src[x + 1];
+      r[(y & 1)^1] += MVE_RVAL (p);
+      g[(y & 1)^1] += MVE_GVAL (p);
+      b[(y & 1)^1] += MVE_BVAL (p);
+    }
+    src += enc->mve->width;
+  }
+  col[0] = mve_find_pal_color (enc->palette,
+             MVE_COL ((r[0]+16)/32, (g[0]+16)/32, (b[0]+16)/32));
+  col[1] = mve_find_pal_color (enc->palette,
+             MVE_COL ((r[1]+16)/32, (g[1]+16)/32, (b[1]+16)/32));
+
+  /* store block after encoding */
+  for (i = 0, y = 0; y < 8; ++y) {
+    for (x = 0; x < 4; ++x) {
+      apx->block[i++] = col[y & 1];
+      apx->block[i++] = col[(y & 1) ^ 1];
+    }
+  }
+
+  apx->data[0] = col[0];
+  apx->data[1] = col[1];
+  apx->error = mve_block_error_packed (enc,
+               src - (8 * enc->mve->width), apx->block);
+  return apx->error;
+}
+
+/* all available encodings in the preferred order,
+   i.e. in ascending encoded size */
+static const GstMveEncoding mve_encodings[] = {
+  { 0x1, 0, mve_encode_0x1 },
+  { 0x0, 0, mve_encode_0x0 },
+  { 0xe, 1, mve_encode_0xe },
+  { 0x3, 1, mve_encode_0x3 },
+  { 0x4, 1, mve_encode_0x4 },
+  { 0x2, 1, mve_encode_0x2 },
+  { 0xf, 2, mve_encode_0xf },
+  { 0x5, 2, mve_encode_0x5 },
+  { 0xd, 4, mve_encode_0xd },
+  { 0x7, 4, mve_encode_0x7a },
+  { 0x9, 8, mve_encode_0x9a },
+  { 0x7, 10, mve_encode_0x7b },
+  { 0x8, 12, mve_encode_0x8a },
+  { 0x8, 12, mve_encode_0x8b },
+  { 0x9, 12, mve_encode_0x9b },
+  { 0x9, 12, mve_encode_0x9c },
+  { 0xc, 16, mve_encode_0xc },
+  { 0x8, 16, mve_encode_0x8c },
+  { 0x9, 20, mve_encode_0x9d },
+  { 0xa, 24, mve_encode_0xaa },
+  { 0xa, 24, mve_encode_0xab },
+  { 0xa, 32, mve_encode_0xac },
+  { 0xb, 64, mve_encode_0xb }
+};
+
+static gboolean
+mve_reorder_solution (GArray **solution, guint16 n)
+{
+  /* do a binary search to find the position to reinsert the modified element */
+  /* the block we need to reconsider is always at position 0 */
+  /* return TRUE if this block only has 1 encoding left and can be dropped */
+  if (mve_comp_solution (&solution[0], &solution[1]) <= 0)
+    return FALSE; /* already sorted */
+
+  else if (solution[0]->len <= 1)
+    /* drop this element from further calculations since we cannot improve here */
+    return TRUE;
+
+  else
+  {
+    /* we know the error value can only get worse, so we can actually start at 1 */
+    guint lower = 1;
+    guint upper = n - 1;
+    gint cmp;
+    guint idx = 0;
+
+    while (upper > lower) {
+      idx = lower + ((upper - lower) / 2);
+
+      cmp = mve_comp_solution (&solution[0], &solution[idx]);
+
+      if (cmp < 0) {
+        upper = idx;
+      } else if (cmp > 0) {
+        lower = ++idx;
+      } else {
+        upper = lower = idx;
+      }
+    }
+
+    if (idx > 0) {
+      /* rearrange array members in new order */
+      GArray *a = solution[0];
+      memcpy (&solution[0], &solution[1], sizeof (GArray *) * idx);
+      solution[idx] = a;
+    }
+  }
+  return FALSE;
+}
+
+static guint32
+gst_mve_find_solution (GArray **approx, guint16 n, guint32 size, guint16 max)
+{
+  /* build an array of approximations we can shuffle around */
+  GstMveApprox *sol_apx;
+  GArray **solution = g_malloc (sizeof (GArray *) * n);
+  GArray **current = solution;
+  memcpy (solution, approx, sizeof (GArray *) * n);
+
+  qsort (solution, n, sizeof (GArray *), mve_comp_solution);
+
+  do {
+    /* array is now sorted by error of the next to optimal approximation;
+       drop optimal approximation for the best block */
+
+    /* unable to reduce size further */
+    if (current[0]->len <= 1)
+      break;
+
+    sol_apx = &g_array_index (current[0], GstMveApprox, current[0]->len - 1);
+    size -= mve_encodings[sol_apx->type].size;
+    g_array_remove_index_fast (current[0], current[0]->len - 1);
+    sol_apx = &g_array_index (current[0], GstMveApprox, current[0]->len - 1);
+    size += mve_encodings[sol_apx->type].size;
+
+    if (mve_reorder_solution (current, n)) {
+      ++current;
+      --n;
+    }
+  } while (size > max);
+
+  g_free (solution);
+
+  return size;
+}
+
+GstFlowReturn
+mve_encode_frame8 (GstMveMux *mve, GstBuffer *frame, const guint32 *palette, guint16 max_data)
+{
+  guint8 *src;
+  GstFlowReturn ret = GST_FLOW_ERROR;
+  guint8 *cm = mve->chunk_code_map;
+  GArray **approx;
+  GstMveApprox apx;
+  GstMveEncoderData enc;
+  const guint16 blocks = (mve->width * mve->height) / 64;
+  guint32 encoded_size = 0;
+  guint i = 0, x, y;
+
+  src = GST_BUFFER_DATA (frame);
+
+  approx = g_malloc (sizeof (GArray *) * blocks);
+
+  enc.mve = mve;
+  enc.palette = palette;
+
+  for (enc.y = 0; enc.y < mve->height; enc.y += 8)
+  {
+    for (enc.x = 0; enc.x < mve->width; enc.x += 8)
+    {
+      guint32 err, last_err = MVE_APPROX_MAX_ERROR;
+      guint type = 0;
+      guint best;
+
+      enc.q2available = enc.q4available = FALSE;
+      approx[i] = g_array_new (FALSE, FALSE, sizeof(GstMveApprox));
+
+      do {
+        err = mve_encodings[type].approx (&enc, src, &apx);
+
+        if (err < last_err)
+        {
+          apx.type = best = type;
+          g_array_append_val (approx[i], apx);
+          last_err = err;
+        }
+
+        ++type;
+      } while (last_err != 0);
+
+      encoded_size += mve_encodings[best].size;
+      ++i;
+      src += 8;
+    }
+    src += 7 * mve->width;
+  }
+
+  /* find best solution with size constraints */
+  GST_DEBUG_OBJECT (mve, "encoded frame %u in %ld bytes (lossless)", mve->video_frames+1, encoded_size);
+
+#if 0
+  /* FIXME */
+  src = GST_BUFFER_DATA (frame);
+  for (i = 0, y = 0; y < mve->height; y += 8) {
+    for (x = 0; x < mve->width; x += 8, ++i) {
+      GstMveApprox *sol = &g_array_index (approx[i], GstMveApprox, approx[i]->len - 1);
+      guint opcode = mve_encodings[sol->type].opcode;
+      guint j, k;
+
+      if (sol->error > 0)
+        GST_WARNING_OBJECT (mve, "error is %lu for %d/%d (0x%x)", sol->error, x, y, opcode);
+
+      for (j = 0; j < 8; ++j) {
+        guint8 *o = src + j * mve->width;
+        guint8 *c = sol->block + j * 8;
+        if (memcmp (o, c, 8)) {
+          GST_WARNING_OBJECT (mve, "opcode 0x%x (type %d) at %d/%d, line %d:", opcode, sol->type, x, y, j+1);
+          for (k = 0; k < 8; ++k) {
+            o = src + k * mve->width;
+            c = sol->block + k * 8;
+            GST_WARNING_OBJECT (mve, "%d should be: %4d  %4d  %4d  %4d  %4d  %4d  %4d  %4d", k, o[0], o[1], o[2], o[3], o[4], o[5], o[6], o[7]);
+            GST_WARNING_OBJECT (mve, "%d but is   : %4d  %4d  %4d  %4d  %4d  %4d  %4d  %4d", k, c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]);
+          }
+        }
+      }
+      src += 8;
+    }
+    src += 7 * mve->width;
+  }
+#endif
+
+  if (encoded_size > max_data)
+  {
+    encoded_size = gst_mve_find_solution (approx, blocks, encoded_size, max_data);
+    if (encoded_size > max_data)
+    {
+      GST_ERROR_OBJECT (mve, "unable to compress frame to less than %d bytes", encoded_size);
+      for (i = 0; i < blocks; ++i)
+        g_array_free (approx[i], TRUE);
+
+      goto done;
+    }
+    GST_DEBUG_OBJECT (mve, "compressed frame %u to %ld bytes (lossy)", mve->video_frames+1, encoded_size);
+  }
+
+  mve->chunk_video = g_byte_array_sized_new (encoded_size);
+
+  /* encode */
+  src = GST_BUFFER_DATA (frame);
+  for (i = 0, y = 0; y < mve->height; y += 8) {
+    for (x = 0; x < mve->width; x += 8, ++i) {
+      GstMveApprox *sol = &g_array_index (approx[i], GstMveApprox, approx[i]->len - 1);
+      guint opcode = mve_encodings[sol->type].opcode;
+
+      g_byte_array_append (mve->chunk_video, sol->data, mve_encodings[sol->type].size);
+
+      if (i & 1) {
+        *cm |= opcode << 4;
+        ++cm;
+      } else
+        *cm = opcode;
+
+      /* modify the frame to match the image we actually encoded */
+      if (sol->error > 0)
+        mve_restore_block (mve, src, sol->block);
+
+      src += 8;
+      g_array_free (approx[i], TRUE);
+    }
+    src += 7 * mve->width;
+  }
+
+  ret = GST_FLOW_OK;
+
+done:
+  g_free (approx);
+
+  return ret;
+}
