summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/lib/util/avcomp.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/lib/util/avcomp.c')
-rw-r--r--trunk/src/lib/util/avcomp.c879
1 files changed, 879 insertions, 0 deletions
diff --git a/trunk/src/lib/util/avcomp.c b/trunk/src/lib/util/avcomp.c
new file mode 100644
index 00000000000..0f2af7f68de
--- /dev/null
+++ b/trunk/src/lib/util/avcomp.c
@@ -0,0 +1,879 @@
+/***************************************************************************
+
+ avcomp.c
+
+ Audio/video compression and decompression helpers.
+
+****************************************************************************
+
+ Copyright Aaron Giles
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name 'MAME' nor the names of its contributors may be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+****************************************************************************
+
+ Each frame is compressed as a unit. The raw data is of the form:
+ (all multibyte values are stored in big-endian format)
+
+ +00 = 'chav' (4 bytes) - fixed header data to identify the format
+ +04 = metasize (1 byte) - size of metadata in bytes (max=255 bytes)
+ +05 = channels (1 byte) - number of audio channels
+ +06 = samples (2 bytes) - number of samples per audio stream
+ +08 = width (2 bytes) - width of video data
+ +0A = height (2 bytes) - height of video data
+ +0C = <metadata> - as raw bytes
+ <audio stream 0> - as signed 16-bit samples
+ <audio stream 1> - as signed 16-bit samples
+ ...
+ <video data> - as a raw array of 8-bit YUY data in (Cb,Y,Cr,Y) order
+
+ When compressed, the data is stored as follows:
+ (all multibyte values are stored in big-endian format)
+
+ +00 = metasize (1 byte) - size of metadata in bytes
+ +01 = channels (1 byte) - number of audio channels
+ +02 = samples (2 bytes) - number of samples per audio stream
+ +04 = width (2 bytes) - width of video data
+ +06 = height (2 bytes) - height of video data
+ +08 = audio huffman size (2 bytes) - size of audio huffman tables
+ +0A = str0size (2 bytes) - compressed size of stream 0
+ +0C = str1size (2 bytes) - compressed size of stream 1
+ ...
+ <metadata> - as raw data
+ <audio huffman table> - Huffman table for audio decoding
+ <audio stream 0 data> - Huffman-compressed deltas
+ <audio stream 1 data> - Huffman-compressed deltas
+ <...>
+ <video huffman tables> - Huffman tables for video decoding
+ <video data> - compressed data
+
+****************************************************************************
+
+ Attempted techniques that have not been worthwhile:
+
+ * Attempted to use integer DCTs from the IJG code; even the "slow"
+ variants produce a lot of error and thus kill our compression ratio,
+ since our compression is based on error not bitrate.
+
+ * Tried various other predictors for the lossless video encoding, but
+ none tended to give any significant gain over predicting the
+ previous pixel.
+
+***************************************************************************/
+
+#include "avcomp.h"
+#include "huffman.h"
+#include "chd.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+#define MAX_CHANNELS 4
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+struct _avcomp_state
+{
+ /* video parameters */
+ UINT32 maxwidth, maxheight;
+
+ /* audio parameters */
+ UINT32 maxchannels;
+
+ /* intermediate data */
+ UINT8 * audiodata;
+
+ /* huffman contexts */
+ huffman_context * ycontext;
+ huffman_context * cbcontext;
+ huffman_context * crcontext;
+ huffman_context * audiohicontext;
+ huffman_context * audiolocontext;
+
+ /* configuration data */
+ av_codec_compress_config compress;
+ av_codec_decompress_config decompress;
+};
+
+
+
+/***************************************************************************
+ PROTOTYPES
+***************************************************************************/
+
+/* encoding helpers */
+static avcomp_error encode_audio(avcomp_state *state, int channels, int samples, const UINT8 **source, int sourcexor, UINT8 *dest, UINT8 *sizes);
+static avcomp_error encode_video(avcomp_state *state, int width, int height, const UINT8 *source, UINT32 sstride, UINT32 sxor, UINT8 *dest, UINT32 *complength);
+static avcomp_error encode_video_lossless(avcomp_state *state, int width, int height, const UINT8 *source, UINT32 sstride, UINT32 sxor, UINT8 *dest, UINT32 *complength);
+
+/* decoding helpers */
+static avcomp_error decode_audio(avcomp_state *state, int channels, int samples, const UINT8 *source, UINT8 **dest, UINT32 dxor, const UINT8 *sizes);
+static avcomp_error decode_video(avcomp_state *state, int width, int height, const UINT8 *source, UINT32 complength, UINT8 *dest, UINT32 dstride, UINT32 dxor);
+static avcomp_error decode_video_lossless(avcomp_state *state, int width, int height, const UINT8 *source, UINT32 complength, UINT8 *dest, UINT32 deststride, UINT32 destxor);
+
+
+
+/***************************************************************************
+ IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ avcomp_init - allocate and initialize a
+ new state block for compression or
+ decompression
+-------------------------------------------------*/
+
+avcomp_state *avcomp_init(UINT32 maxwidth, UINT32 maxheight, UINT32 maxchannels)
+{
+ huffman_error hufferr;
+ avcomp_state *state;
+
+ /* error if out of range */
+ if (maxchannels > MAX_CHANNELS)
+ return NULL;
+
+ /* allocate memory for state block */
+ state = (avcomp_state *)malloc(sizeof(*state));
+ if (state == NULL)
+ return NULL;
+
+ /* clear the buffers */
+ memset(state, 0, sizeof(*state));
+
+ /* compute the core info */
+ state->maxwidth = maxwidth;
+ state->maxheight = maxheight;
+ state->maxchannels = maxchannels;
+
+ /* now allocate data buffers */
+ state->audiodata = (UINT8 *)malloc(65536 * state->maxchannels * 2);
+ if (state->audiodata == NULL)
+ goto cleanup;
+
+ /* create huffman contexts */
+ hufferr = huffman_create_context(&state->ycontext, 16);
+ if (hufferr != HUFFERR_NONE)
+ goto cleanup;
+ hufferr = huffman_create_context(&state->cbcontext, 16);
+ if (hufferr != HUFFERR_NONE)
+ goto cleanup;
+ hufferr = huffman_create_context(&state->crcontext, 16);
+ if (hufferr != HUFFERR_NONE)
+ goto cleanup;
+ hufferr = huffman_create_context(&state->audiohicontext, 16);
+ if (hufferr != HUFFERR_NONE)
+ goto cleanup;
+ hufferr = huffman_create_context(&state->audiolocontext, 16);
+ if (hufferr != HUFFERR_NONE)
+ goto cleanup;
+
+ return state;
+
+cleanup:
+ avcomp_free(state);
+ return NULL;
+}
+
+
+/*-------------------------------------------------
+ avcomp_free - free a state block
+-------------------------------------------------*/
+
+void avcomp_free(avcomp_state *state)
+{
+ /* free the data buffers */
+ if (state->audiodata != NULL)
+ free(state->audiodata);
+
+ /* free the contexts */
+ if (state->ycontext != NULL)
+ huffman_free_context(state->ycontext);
+ if (state->cbcontext != NULL)
+ huffman_free_context(state->cbcontext);
+ if (state->crcontext != NULL)
+ huffman_free_context(state->crcontext);
+ if (state->audiohicontext != NULL)
+ huffman_free_context(state->audiohicontext);
+ if (state->audiolocontext != NULL)
+ huffman_free_context(state->audiolocontext);
+
+ free(state);
+}
+
+
+/*-------------------------------------------------
+ avcomp_config_compress - configure compression
+ parameters
+-------------------------------------------------*/
+
+void avcomp_config_compress(avcomp_state *state, const av_codec_compress_config *config)
+{
+ state->compress = *config;
+}
+
+
+/*-------------------------------------------------
+ avcomp_config_decompress - configure
+ decompression parameters
+-------------------------------------------------*/
+
+void avcomp_config_decompress(avcomp_state *state, const av_codec_decompress_config *config)
+{
+ state->decompress = *config;
+}
+
+
+
+/***************************************************************************
+ ENCODING/DECODING FRONTENDS
+***************************************************************************/
+
+/*-------------------------------------------------
+ avcomp_encode_data - encode a block of data
+ into a compressed data stream
+-------------------------------------------------*/
+
+avcomp_error avcomp_encode_data(avcomp_state *state, const UINT8 *source, UINT8 *dest, UINT32 *complength)
+{
+ const UINT8 *metastart, *videostart, *audiostart[MAX_CHANNELS];
+ UINT32 metasize, channels, samples, width, height;
+ UINT32 audioxor, videoxor, videostride;
+ avcomp_error err;
+ UINT32 dstoffs;
+ int chnum;
+
+ /* extract data from source if present */
+ if (source != NULL)
+ {
+ /* validate the header */
+ if (source[0] != 'c' || source[1] != 'h' || source[2] != 'a' || source[3] != 'v')
+ return AVCERR_INVALID_DATA;
+
+ /* extract info from the header */
+ metasize = source[4];
+ channels = source[5];
+ samples = (source[6] << 8) + source[7];
+ width = (source[8] << 8) + source[9];
+ height = (source[10] << 8) + source[11];
+
+ /* determine the start of each piece of data */
+ source += 12;
+ metastart = source;
+ source += metasize;
+ for (chnum = 0; chnum < channels; chnum++)
+ {
+ audiostart[chnum] = source;
+ source += 2 * samples;
+ }
+ videostart = source;
+
+ /* data is assumed to be big-endian already */
+ audioxor = videoxor = 0;
+ videostride = 2 * width;
+ }
+
+ /* otherwise, extract from the state */
+ else
+ {
+ UINT16 betest = 0;
+
+ /* extract metadata information */
+ metastart = state->compress.metadata;
+ metasize = state->compress.metalength;
+ if ((metastart == NULL && metasize != 0) || (metastart != NULL && metasize == 0))
+ return AVCERR_INVALID_CONFIGURATION;
+
+ /* extract audio information */
+ channels = state->compress.channels;
+ samples = state->compress.samples;
+ for (chnum = 0; chnum < channels; chnum++)
+ audiostart[chnum] = (const UINT8 *)state->compress.audio[chnum];
+
+ /* extract video information */
+ videostart = NULL;
+ videostride = width = height = 0;
+ if (state->compress.video != NULL)
+ {
+ videostart = (const UINT8 *)state->compress.video->base;
+ videostride = state->compress.video->rowpixels * 2;
+ width = state->compress.video->width;
+ height = state->compress.video->height;
+ }
+
+ /* data is assumed to be native-endian */
+ *(UINT8 *)&betest = 1;
+ audioxor = videoxor = (betest == 1) ? 1 : 0;
+ }
+
+ /* validate the info from the header */
+ if (width > state->maxwidth || height > state->maxheight)
+ return AVCERR_VIDEO_TOO_LARGE;
+ if (channels > state->maxchannels)
+ return AVCERR_AUDIO_TOO_LARGE;
+
+ /* write the basics to the new header */
+ dest[0] = metasize;
+ dest[1] = channels;
+ dest[2] = samples >> 8;
+ dest[3] = samples;
+ dest[4] = width >> 8;
+ dest[5] = width;
+ dest[6] = height >> 8;
+ dest[7] = height;
+
+ /* starting offsets */
+ dstoffs = 10 + 2 * channels;
+
+ /* copy the metadata first */
+ if (metasize > 0)
+ {
+ memcpy(dest + dstoffs, metastart, metasize);
+ dstoffs += metasize;
+ }
+
+ /* encode the audio channels */
+ if (channels > 0)
+ {
+ /* encode the audio */
+ err = encode_audio(state, channels, samples, audiostart, audioxor, dest + dstoffs, &dest[8]);
+ if (err != AVCERR_NONE)
+ return err;
+
+ /* advance the pointers past the data */
+ dstoffs += (dest[8] << 8) + dest[9];
+ for (chnum = 0; chnum < channels; chnum++)
+ dstoffs += (dest[10 + 2 * chnum] << 8) + dest[11 + 2 * chnum];
+ }
+
+ /* encode the video data */
+ if (width > 0 && height > 0)
+ {
+ UINT32 vidlength = 0;
+
+ /* encode the video */
+ err = encode_video(state, width, height, videostart, videostride, videoxor, dest + dstoffs, &vidlength);
+ if (err != AVCERR_NONE)
+ return err;
+
+ /* advance the pointers past the data */
+ dstoffs += vidlength;
+ }
+
+ /* set the total compression */
+ *complength = dstoffs;
+ return AVCERR_NONE;
+}
+
+
+/*-------------------------------------------------
+ avcomp_decode_data - decode both
+ audio and video from a raw data stream
+-------------------------------------------------*/
+
+avcomp_error avcomp_decode_data(avcomp_state *state, const UINT8 *source, UINT32 complength, UINT8 *dest)
+{
+ UINT8 *metastart, *videostart, *audiostart[MAX_CHANNELS];
+ UINT32 metasize, channels, samples, width, height;
+ UINT32 audioxor, videoxor, videostride;
+ UINT32 srcoffs, totalsize;
+ avcomp_error err;
+ int chnum;
+
+ /* extract info from the header */
+ if (complength < 8)
+ return AVCERR_INVALID_DATA;
+ metasize = source[0];
+ channels = source[1];
+ samples = (source[2] << 8) + source[3];
+ width = (source[4] << 8) + source[5];
+ height = (source[6] << 8) + source[7];
+
+ /* validate the info from the header */
+ if (width > state->maxwidth || height > state->maxheight)
+ return AVCERR_VIDEO_TOO_LARGE;
+ if (channels > state->maxchannels)
+ return AVCERR_AUDIO_TOO_LARGE;
+
+ /* validate that the sizes make sense */
+ if (complength < 10 + 2 * channels)
+ return AVCERR_INVALID_DATA;
+ totalsize = 10 + 2 * channels;
+ totalsize += (source[8] << 8) | source[9];
+ for (chnum = 0; chnum < channels; chnum++)
+ totalsize += (source[10 + 2 * chnum] << 8) | source[11 + 2 * chnum];
+ if (totalsize >= complength)
+ return AVCERR_INVALID_DATA;
+
+ /* starting offsets */
+ srcoffs = 10 + 2 * channels;
+
+ /* if we are decoding raw, set up the output parameters */
+ if (dest != NULL)
+ {
+ /* create a header */
+ dest[0] = 'c';
+ dest[1] = 'h';
+ dest[2] = 'a';
+ dest[3] = 'v';
+ dest[4] = metasize;
+ dest[5] = channels;
+ dest[6] = samples >> 8;
+ dest[7] = samples;
+ dest[8] = width >> 8;
+ dest[9] = width;
+ dest[10] = height >> 8;
+ dest[11] = height;
+
+ /* determine the start of each piece of data */
+ dest += 12;
+ metastart = dest;
+ dest += metasize;
+ for (chnum = 0; chnum < channels; chnum++)
+ {
+ audiostart[chnum] = dest;
+ dest += 2 * samples;
+ }
+ videostart = dest;
+
+ /* data is assumed to be big-endian already */
+ audioxor = videoxor = 0;
+ videostride = 2 * width;
+ }
+
+ /* otherwise, extract from the state */
+ else
+ {
+ UINT16 betest = 0;
+
+ /* determine the start of each piece of data */
+ metastart = state->decompress.metadata;
+ for (chnum = 0; chnum < channels; chnum++)
+ audiostart[chnum] = (UINT8 *)state->decompress.audio[chnum];
+ videostart = (state->decompress.video != NULL) ? (UINT8 *)state->decompress.video->base : NULL;
+ videostride = (state->decompress.video != NULL) ? state->decompress.video->rowpixels * 2 : 0;
+
+ /* data is assumed to be native-endian */
+ *(UINT8 *)&betest = 1;
+ audioxor = videoxor = (betest == 1) ? 1 : 0;
+
+ /* verify against sizes */
+ if (state->decompress.video != NULL && (state->decompress.video->width < width || state->decompress.video->height < height))
+ return AVCERR_VIDEO_TOO_LARGE;
+ for (chnum = 0; chnum < channels; chnum++)
+ if (state->decompress.audio[chnum] != NULL && state->decompress.maxsamples < samples)
+ return AVCERR_AUDIO_TOO_LARGE;
+ if (state->decompress.metadata != NULL && state->decompress.maxmetalength < metasize)
+ return AVCERR_METADATA_TOO_LARGE;
+
+ /* set the output values */
+ if (state->decompress.actsamples != NULL)
+ *state->decompress.actsamples = samples;
+ if (state->decompress.actmetalength != NULL)
+ *state->decompress.actmetalength = metasize;
+ }
+
+ /* copy the metadata first */
+ if (metasize > 0)
+ {
+ if (metastart != NULL)
+ memcpy(metastart, source + srcoffs, metasize);
+ srcoffs += metasize;
+ }
+
+ /* decode the audio channels */
+ if (channels > 0)
+ {
+ /* decode the audio */
+ err = decode_audio(state, channels, samples, source + srcoffs, audiostart, audioxor, &source[8]);
+ if (err != AVCERR_NONE)
+ return err;
+
+ /* advance the pointers past the data */
+ srcoffs += (source[8] << 8) + source[9];
+ for (chnum = 0; chnum < channels; chnum++)
+ srcoffs += (source[10 + 2 * chnum] << 8) + source[11 + 2 * chnum];
+ }
+
+ /* decode the video data */
+ if (width > 0 && height > 0 && videostart != NULL)
+ {
+ /* decode the video */
+ err = decode_video(state, width, height, source + srcoffs, complength - srcoffs, videostart, videostride, videoxor);
+ if (err != AVCERR_NONE)
+ return err;
+ }
+ return AVCERR_NONE;
+}
+
+
+
+/***************************************************************************
+ ENCODING HELPERS
+***************************************************************************/
+
+/*-------------------------------------------------
+ encode_audio - encode raw audio data
+ to the destination
+-------------------------------------------------*/
+
+static avcomp_error encode_audio(avcomp_state *state, int channels, int samples, const UINT8 **source, int sourcexor, UINT8 *dest, UINT8 *sizes)
+{
+ UINT32 size, huffsize, totalsize;
+ huffman_context *contexts[2];
+ huffman_error hufferr;
+ UINT8 *output = dest;
+ int chnum, sampnum;
+ UINT8 *deltabuf;
+
+ /* iterate over channels to compute deltas */
+ deltabuf = state->audiodata;
+ for (chnum = 0; chnum < channels; chnum++)
+ {
+ const UINT8 *srcdata = source[chnum];
+ INT16 prevsample = 0;
+
+ /* extract audio data into hi and lo deltas stored in big-endian order */
+ for (sampnum = 0; sampnum < samples; sampnum++)
+ {
+ INT16 newsample = (srcdata[0 ^ sourcexor] << 8) | srcdata[1 ^ sourcexor];
+ INT16 delta = newsample - prevsample;
+ prevsample = newsample;
+ *deltabuf++ = delta >> 8;
+ *deltabuf++ = delta;
+ srcdata += 2;
+ }
+ }
+
+ /* compute the trees */
+ contexts[0] = state->audiohicontext;
+ contexts[1] = state->audiolocontext;
+ hufferr = huffman_compute_tree_interleaved(2, contexts, state->audiodata, samples * 2, channels, samples * 2, 0);
+ if (hufferr != HUFFERR_NONE)
+ return AVCERR_COMPRESSION_ERROR;
+
+ /* export them to the output */
+ hufferr = huffman_export_tree(state->audiohicontext, output, 256, &size);
+ if (hufferr != HUFFERR_NONE)
+ return AVCERR_COMPRESSION_ERROR;
+ output += size;
+ hufferr = huffman_export_tree(state->audiolocontext, output, 256, &size);
+ if (hufferr != HUFFERR_NONE)
+ return AVCERR_COMPRESSION_ERROR;
+ output += size;
+
+ /* note the size of the two trees */
+ huffsize = output - dest;
+ sizes[0] = huffsize >> 8;
+ sizes[1] = huffsize;
+
+ /* iterate over channels */
+ totalsize = huffsize;
+ for (chnum = 0; chnum < channels; chnum++)
+ {
+ const UINT8 *input = state->audiodata + chnum * samples * 2;
+
+ /* encode the data */
+ hufferr = huffman_encode_data_interleaved(2, contexts, input, samples * 2, 1, 0, 0, output, samples * 2, &size);
+ if (hufferr != HUFFERR_NONE)
+ return AVCERR_COMPRESSION_ERROR;
+ output += size;
+
+ /* store the size of this stream */
+ totalsize += size;
+ if (totalsize >= channels * samples * 2)
+ break;
+ sizes[chnum * 2 + 2] = size >> 8;
+ sizes[chnum * 2 + 3] = size;
+ }
+
+ /* if we ran out of room, throw it all away and just store raw */
+ if (chnum < channels)
+ {
+ memcpy(dest, state->audiodata, channels * samples * 2);
+ size = samples * 2;
+ sizes[0] = sizes[1] = 0;
+ for (chnum = 0; chnum < channels; chnum++)
+ {
+ sizes[chnum * 2 + 2] = size >> 8;
+ sizes[chnum * 2 + 3] = size;
+ }
+ }
+
+ return AVCERR_NONE;
+}
+
+
+/*-------------------------------------------------
+ encode_video - encode raw video data
+ to the destination
+-------------------------------------------------*/
+
+static avcomp_error encode_video(avcomp_state *state, int width, int height, const UINT8 *source, UINT32 sstride, UINT32 sxor, UINT8 *dest, UINT32 *complength)
+{
+ /* only lossless supported at this time */
+ return encode_video_lossless(state, width, height, source, sstride, sxor, dest, complength);
+}
+
+
+/*-------------------------------------------------
+ encode_video_lossless - do a lossless video
+ encoding using deltas and huffman encoding
+-------------------------------------------------*/
+
+static avcomp_error encode_video_lossless(avcomp_state *state, int width, int height, const UINT8 *source, UINT32 sstride, UINT32 sxor, UINT8 *dest, UINT32 *complength)
+{
+ UINT32 srcbytes = width * height * 2;
+ huffman_context *contexts[4];
+ huffman_error hufferr;
+ UINT32 outbytes;
+ UINT8 *output;
+
+ /* set up the output; first byte is 0x80 to indicate lossless encoding */
+ output = dest;
+ *output++ = 0x80;
+
+ /* now encode to the destination using two trees, one for the Y and one for the Cr/Cb */
+ contexts[0] = state->ycontext;
+ contexts[1] = state->cbcontext;
+ contexts[2] = state->ycontext;
+ contexts[3] = state->crcontext;
+
+ /* compute the histograms for the data */
+ hufferr = huffman_deltarle_compute_tree_interleaved(4, contexts, source, width * 2, height, sstride, sxor);
+ if (hufferr != HUFFERR_NONE)
+ return AVCERR_COMPRESSION_ERROR;
+
+ /* export the trees to the data stream */
+ hufferr = huffman_deltarle_export_tree(state->ycontext, output, 256, &outbytes);
+ if (hufferr != HUFFERR_NONE)
+ return AVCERR_COMPRESSION_ERROR;
+ output += outbytes;
+ hufferr = huffman_deltarle_export_tree(state->cbcontext, output, 256, &outbytes);
+ if (hufferr != HUFFERR_NONE)
+ return AVCERR_COMPRESSION_ERROR;
+ output += outbytes;
+ hufferr = huffman_deltarle_export_tree(state->crcontext, output, 256, &outbytes);
+ if (hufferr != HUFFERR_NONE)
+ return AVCERR_COMPRESSION_ERROR;
+ output += outbytes;
+
+ /* encode the data using the trees */
+ hufferr = huffman_deltarle_encode_data_interleaved(4, contexts, source, width * 2, height, sstride, sxor, output, srcbytes, &outbytes);
+ if (hufferr != HUFFERR_NONE)
+ return AVCERR_COMPRESSION_ERROR;
+ output += outbytes;
+
+ /* set the final length */
+ *complength = output - dest;
+ return AVCERR_NONE;
+}
+
+
+
+/***************************************************************************
+ DECODING HELPERS
+***************************************************************************/
+
+/*-------------------------------------------------
+ decode_audio - decode audio from a
+ compressed data stream
+-------------------------------------------------*/
+
+static avcomp_error decode_audio(avcomp_state *state, int channels, int samples, const UINT8 *source, UINT8 **dest, UINT32 dxor, const UINT8 *sizes)
+{
+ huffman_context *contexts[2];
+ UINT32 actsize, huffsize;
+ huffman_error hufferr;
+ int chnum, sampnum;
+ UINT16 size;
+
+ /* if no huffman length, just copy the data */
+ size = (sizes[0] << 8) | sizes[1];
+ if (size == 0)
+ {
+ /* loop over channels */
+ for (chnum = 0; chnum < channels; chnum++)
+ {
+ UINT8 *curdest = dest[chnum];
+
+ /* extract the size of this channel */
+ size = (sizes[chnum * 2 + 2] << 8) | sizes[chnum * 2 + 3];
+
+ /* extract data from the deltas */
+ if (dest[chnum] != NULL)
+ {
+ INT16 prevsample = 0;
+ for (sampnum = 0; sampnum < samples; sampnum++)
+ {
+ INT16 delta = (source[0] << 8) | source[1];
+ INT16 newsample = prevsample + delta;
+ prevsample = newsample;
+
+ curdest[0 ^ dxor] = newsample >> 8;
+ curdest[1 ^ dxor] = newsample;
+ source += 2;
+ curdest += 2;
+ }
+ }
+ else
+ source += size;
+ }
+ return AVCERR_NONE;
+ }
+
+ /* extract the huffman trees */
+ hufferr = huffman_import_tree(state->audiohicontext, source, size, &actsize);
+ if (hufferr != HUFFERR_NONE)
+ return AVCERR_INVALID_DATA;
+ source += actsize;
+ huffsize = actsize;
+
+ hufferr = huffman_import_tree(state->audiolocontext, source, size, &actsize);
+ if (hufferr != HUFFERR_NONE)
+ return AVCERR_INVALID_DATA;
+ source += actsize;
+ huffsize += actsize;
+ if (huffsize != size)
+ return AVCERR_INVALID_DATA;
+
+ /* set up the contexts */
+ contexts[0] = state->audiohicontext;
+ contexts[1] = state->audiolocontext;
+
+ /* now loop over channels and decode their data */
+ for (chnum = 0; chnum < channels; chnum++)
+ {
+ /* extract the size of this channel */
+ size = (sizes[chnum * 2 + 2] << 8) | sizes[chnum * 2 + 3];
+
+ /* decode the data */
+ if (dest[chnum] != NULL)
+ {
+ UINT8 *deltabuf = state->audiodata + chnum * samples * 2;
+ hufferr = huffman_decode_data_interleaved(2, contexts, source, size, deltabuf, samples * 2, 1, 0, 0, &actsize);
+ if (hufferr != HUFFERR_NONE || actsize != size)
+ return AVCERR_INVALID_DATA;
+ }
+
+ /* advance */
+ source += size;
+ }
+
+ /* reassemble audio from the deltas */
+ for (chnum = 0; chnum < channels; chnum++)
+ if (dest[chnum] != NULL)
+ {
+ UINT8 *deltabuf = state->audiodata + chnum * samples * 2;
+ UINT8 *curdest = dest[chnum];
+ INT16 prevsample = 0;
+
+ for (sampnum = 0; sampnum < samples; sampnum++)
+ {
+ INT16 delta = (deltabuf[0] << 8) | deltabuf[1];
+ INT16 newsample = prevsample + delta;
+ prevsample = newsample;
+
+ curdest[0 ^ dxor] = newsample >> 8;
+ curdest[1 ^ dxor] = newsample;
+ deltabuf += 2;
+ curdest += 2;
+ }
+ }
+
+ return AVCERR_NONE;
+}
+
+
+/*-------------------------------------------------
+ decode_video - decode video from a
+ compressed data stream
+-------------------------------------------------*/
+
+static avcomp_error decode_video(avcomp_state *state, int width, int height, const UINT8 *source, UINT32 complength, UINT8 *dest, UINT32 dstride, UINT32 dxor)
+{
+ /* if the high bit of the first byte is set, we decode losslessly */
+ if (source[0] & 0x80)
+ return decode_video_lossless(state, width, height, source, complength, dest, dstride, dxor);
+ else
+ return AVCERR_INVALID_DATA;
+}
+
+
+/*-------------------------------------------------
+ decode_video_lossless - do a lossless video
+ decoding using deltas and huffman encoding
+-------------------------------------------------*/
+
+static avcomp_error decode_video_lossless(avcomp_state *state, int width, int height, const UINT8 *source, UINT32 complength, UINT8 *dest, UINT32 deststride, UINT32 destxor)
+{
+ const UINT8 *sourceend = source + complength;
+ huffman_context *contexts[4];
+ huffman_error hufferr;
+ UINT32 actsize;
+
+ /* skip the first byte */
+ source++;
+
+ /* import the tables */
+ hufferr = huffman_deltarle_import_tree(state->ycontext, source, sourceend - source, &actsize);
+ if (hufferr != HUFFERR_NONE)
+ return AVCERR_INVALID_DATA;
+ source += actsize;
+ hufferr = huffman_deltarle_import_tree(state->cbcontext, source, sourceend - source, &actsize);
+ if (hufferr != HUFFERR_NONE)
+ return AVCERR_INVALID_DATA;
+ source += actsize;
+ hufferr = huffman_deltarle_import_tree(state->crcontext, source, sourceend - source, &actsize);
+ if (hufferr != HUFFERR_NONE)
+ return AVCERR_INVALID_DATA;
+ source += actsize;
+
+ /* set up the decoding contexts */
+ contexts[0] = state->ycontext;
+ contexts[1] = state->cbcontext;
+ contexts[2] = state->ycontext;
+ contexts[3] = state->crcontext;
+
+ /* decode to the destination */
+ hufferr = huffman_deltarle_decode_data_interleaved(4, contexts, source, sourceend - source, dest, width * 2, height, deststride, destxor, &actsize);
+ if (hufferr != HUFFERR_NONE)
+ return AVCERR_INVALID_DATA;
+ if (actsize != sourceend - source)
+ return AVCERR_INVALID_DATA;
+
+ return AVCERR_NONE;
+}