/*
 * mixed functions for Mpxplay's demuxing
 * Original Copyright (c) 2003 Michel Bardiaux
 *
 * This file is originally part of FFmpeg.
 *
 * FFmpeg 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.1 of the License, or (at your option) any later version.
 *
 * FFmpeg 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 FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

//#define MPXPLAY_USE_DEBUGF 1
//#define MPXPLAY_DEBUG_OUTPUT stdout

#include "mpxplay.h"

#include <string.h>
#include <ctype.h>
#include <math.h>
#include "ffutils.h"

#if defined(MPXPLAY_LINK_INFILE_FFMPG)

const AVRational AV_TIME_BASE_Q={1,AV_TIME_BASE};

static const AVCodecTag mpxplay_ff_codec_wav_tags[] = {
    { CODEC_ID_PCM_S16LE,       0x0001 },
    { CODEC_ID_PCM_U8,          0x0001 }, /* must come after s16le in this list */
    { CODEC_ID_PCM_S24LE,       0x0001 },
    { CODEC_ID_PCM_S32LE,       0x0001 },
    { CODEC_ID_ADPCM_MS,        0x0002 },
    { CODEC_ID_PCM_F32LE,       0x0003 },
    { CODEC_ID_PCM_F64LE,       0x0003 }, /* must come after f32le in this list */
    { CODEC_ID_PCM_ALAW,        0x0006 },
    { CODEC_ID_PCM_MULAW,       0x0007 },
    { CODEC_ID_WMAVOICE,        0x000A },
    { CODEC_ID_ADPCM_IMA_WAV,   0x0011 },
    { CODEC_ID_PCM_ZORK,        0x0011 }, /* must come after adpcm_ima_wav in this list */
    { CODEC_ID_ADPCM_YAMAHA,    0x0020 },
    { CODEC_ID_TRUESPEECH,      0x0022 },
    { CODEC_ID_GSM_MS,          0x0031 },
    { CODEC_ID_ADPCM_G726,      0x0045 },
    { CODEC_ID_MP2,             0x0050 },
    { CODEC_ID_MP3,             0x0055 },
    { CODEC_ID_AMR_NB,          0x0057 },
    { CODEC_ID_AMR_WB,          0x0058 },
    { CODEC_ID_ADPCM_IMA_DK4,   0x0061 },  /* rogue format number */
    { CODEC_ID_ADPCM_IMA_DK3,   0x0062 },  /* rogue format number */
    { CODEC_ID_ADPCM_IMA_WAV,   0x0069 },
    { CODEC_ID_VOXWARE,         0x0075 },
    { CODEC_ID_AAC,             0x00ff },
    { CODEC_ID_SIPR,            0x0130 },
    { CODEC_ID_WMAV1,           0x0160 },
    { CODEC_ID_WMAV2,           0x0161 },
    { CODEC_ID_WMAPRO,          0x0162 },
    { CODEC_ID_WMALOSSLESS,     0x0163 },
    { CODEC_ID_ADPCM_CT,        0x0200 },
    { CODEC_ID_ATRAC3,          0x0270 },
    { CODEC_ID_IMC,             0x0401 },
    { CODEC_ID_AC3,             0x2000 },
    { CODEC_ID_DTS,             0x2001 },
    { CODEC_ID_SONIC,           0x2048 },
    { CODEC_ID_SONIC_LS,        0x2048 },
    { CODEC_ID_PCM_MULAW,       0x6c75 },
    { CODEC_ID_AAC,             0x706d },
    { CODEC_ID_AAC,             0x4143 },
    { CODEC_ID_FLAC,            0xF1AC },
    { CODEC_ID_ADPCM_SWF,       ('S'<<8)+'F' },
    { CODEC_ID_VORBIS,          ('V'<<8)+'o' }, //HACK/FIXME, does vorbis in WAV/AVI have an (in)official id?

    /* FIXME: All of the IDs below are not 16 bit and thus illegal. */
    // for NuppelVideo (nuv.c)
    { CODEC_ID_PCM_S16LE, MKTAG('R', 'A', 'W', 'A') },
    { CODEC_ID_MP3,       MKTAG('L', 'A', 'M', 'E') },
    { CODEC_ID_MP3,       MKTAG('M', 'P', '3', ' ') },
    { CODEC_ID_NONE,      0 },
};

static enum CodecID mpxplay_ff_codec_get_id(const AVCodecTag *tags, unsigned int tag)
{
    int i;
    for(i=0; tags[i].id != CODEC_ID_NONE;i++) {
        if(tag == tags[i].tag)
            return tags[i].id;
    }
    for(i=0; tags[i].id != CODEC_ID_NONE; i++) {
        if(   toupper((tag >> 0)&0xFF) == toupper((tags[i].tag >> 0)&0xFF)
           && toupper((tag >> 8)&0xFF) == toupper((tags[i].tag >> 8)&0xFF)
           && toupper((tag >>16)&0xFF) == toupper((tags[i].tag >>16)&0xFF)
           && toupper((tag >>24)&0xFF) == toupper((tags[i].tag >>24)&0xFF))
            return tags[i].id;
    }
    return CODEC_ID_NONE;
}


enum CodecID mpxplay_ff_wav_codec_get_id(unsigned int tag, int bps)
{
    enum CodecID id;
    id = mpxplay_ff_codec_get_id(mpxplay_ff_codec_wav_tags, tag);
    if (id <= 0)
        return id;
    // handle specific u8 codec
    if (id == CODEC_ID_PCM_S16LE && bps == 8)
        id = CODEC_ID_PCM_U8;
    if (id == CODEC_ID_PCM_S16LE && bps == 24)
        id = CODEC_ID_PCM_S24LE;
    if (id == CODEC_ID_PCM_S16LE && bps == 32)
        id = CODEC_ID_PCM_S32LE;
    if (id == CODEC_ID_PCM_F32LE && bps == 64)
        id = CODEC_ID_PCM_F64LE;
    if (id == CODEC_ID_ADPCM_IMA_WAV && bps == 8)
        id = CODEC_ID_PCM_ZORK;
    return id;
}

void mpxplay_ff_get_wav_header(struct mpxplay_bitstreambuf_s *bs, AVCodecContext *codec, int size)
{
 int id;

 id = mpxplay_bitstream_get_le16(bs);
 codec->codec_type = CODEC_TYPE_AUDIO;
 codec->codec_tag = id;
 codec->channels = mpxplay_bitstream_get_le16(bs);
 codec->sample_rate = mpxplay_bitstream_get_le32(bs);
 codec->bit_rate = mpxplay_bitstream_get_le32(bs) * 8;
 codec->block_align = mpxplay_bitstream_get_le16(bs);
 if(size == 14) {  /* We're dealing with plain vanilla WAVEFORMAT */
  codec->bits_per_coded_sample = 8;
 }else
  codec->bits_per_coded_sample = mpxplay_bitstream_get_le16(bs);
 if(size >= 18) {  /* We're obviously dealing with WAVEFORMATEX */
  int cbSize = mpxplay_bitstream_get_le16(bs); /* cbSize */
  size -= 18;
  cbSize = FFMIN(size, cbSize);
  if (cbSize >= 22 && id == 0xfffe) { /* WAVEFORMATEXTENSIBLE */
   codec->bits_per_coded_sample = mpxplay_bitstream_get_le16(bs);
   codec->channel_layout = mpxplay_bitstream_get_le32(bs); /* dwChannelMask */
   id = mpxplay_bitstream_get_le32(bs); /* 4 first bytes of GUID */
   mpxplay_bitstream_skipbytes(bs, 12); /* skip end of GUID */
   cbSize -= 22;
   size -= 22;
  }
  codec->extradata_size = cbSize;
  if(cbSize > 0) {
   codec->extradata = pds_calloc(1,codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
   if(codec->extradata)
    mpxplay_bitstream_readbytes(bs, codec->extradata, codec->extradata_size);
   size -= cbSize;
  }

  if(size > 0)
   mpxplay_bitstream_skipbytes(bs, size);
 }
 codec->codec_id = mpxplay_ff_wav_codec_get_id(id, codec->bits_per_coded_sample);
}

size_t mpxplay_av_strlcpy(char *dst, const char *src, size_t size)
{
 size_t len = 0;
 while(++len < size && *src)
  *dst++ = *src++;
 if(len <= size)
  *dst = 0;
 return len + strlen(src) - 1;
}

size_t mpxplay_av_strlcat(char *dst, const char *src, size_t size)
{
 size_t len = strlen(dst);
 if(size <= len + 1)
  return len + strlen(src);
 return len + mpxplay_av_strlcpy(dst + len, src, size - len);
}

double mpxplay_av_int2dbl(int64_t v)
{
 if(v+v > 0xFFEULL<<52)
  return 0.0/0.0;
 return ldexp(((v&((1LL<<52)-1)) + (1LL<<52)) * (v>>63|1), (v>>52&0x7FF)-1075);
}

float mpxplay_av_int2flt(int32_t v){
    if(v+v > 0xFF000000U)
        return 0.0/0.0;
    return ldexp(((v&0x7FFFFF) + (1<<23)) * (v>>31|1), (v>>23&0xFF)-150);
}

//--------------------------------------------------------------------
//options.c
static AVCodecContext *mpxplay_avcodec_alloc_context(void)
{
 AVCodecContext *s=pds_calloc(1,sizeof(AVCodecContext));
 if(!s)
  return s;
 s->codec_type = CODEC_TYPE_UNKNOWN;
 //s->time_base.num=0;
 //s->time_base.den=1;
 return s;
}

//cutils.c
void mpxplay_ff_dynarray_add(intptr_t **tab_ptr, int *nb_ptr, intptr_t elem)
{
 int nb, nb_alloc;
 intptr_t *tab;

 nb = *nb_ptr;
 tab = *tab_ptr;
 if((nb & (nb - 1)) == 0){
  if(nb == 0)
   nb_alloc = 1;
  else
   nb_alloc = nb * 2;
  tab = pds_realloc(tab, nb_alloc * sizeof(intptr_t));
  *tab_ptr = tab;
 }
 tab[nb++] = elem;
 *nb_ptr = nb;
}

//metadata.c
void mpxplay_av_metadata_free(AVMetadata **pm)
{
 AVMetadata *m= *pm;

 if(m){
  while(m->count--){
   mpxplay_av_free(m->elems[m->count].key);
   mpxplay_av_free(m->elems[m->count].value);
  }
  mpxplay_av_free(m->elems);
 }
 mpxplay_av_freep(pm);
}

//utils.c
void mpxplay_av_streams_and_programs_free(AVFormatContext *s)
{
 int i;
 for(i=0;i<s->nb_streams;i++){
  AVStream *st = s->streams[i];
  if(st){
   //if(st->parser){
   // mpxplay_av_parser_close(st->parser);
   // mpxplay_av_free_packet(&st->cur_pkt);
   //}
   mpxplay_av_metadata_free(&st->metadata);
   mpxplay_av_free(st->index_entries);
   if(st->codec){
    mpxplay_av_free(st->codec->extradata);
    mpxplay_av_free(st->codec);
   }
#if LIBAVFORMAT_VERSION_INT < (53<<16)
   //mpxplay_av_free(st->filename);
#endif
   mpxplay_av_free(st->priv_data);
   mpxplay_av_free(st);
   s->streams[i]=NULL;
  }
 }
 s->nb_streams=0;
 for(i=0;i<s->nb_programs;i++){
  if(s->programs[i]){
#if LIBAVFORMAT_VERSION_INT < (53<<16)
   mpxplay_av_freep(&s->programs[i]->provider_name);
   mpxplay_av_freep(&s->programs[i]->name);
#endif
   mpxplay_av_metadata_free(&s->programs[i]->metadata);
   mpxplay_av_freep(&s->programs[i]->stream_index);
   mpxplay_av_freep(&s->programs[i]);
  }
 }
 mpxplay_av_freep(&s->programs);
 s->nb_programs=0;
}

void mpxplay_av_close_input_stream(AVFormatContext *s)
{
 if(s->iformat && s->iformat->read_close)
  s->iformat->read_close(s);
 mpxplay_av_streams_and_programs_free(s);

 //flush_packet_queue(s);
 mpxplay_av_freep(&s->priv_data);
 /*while(s->nb_chapters--) {
#if LIBAVFORMAT_VERSION_INT < (53<<16)
  mpxplay_av_free(s->chapters[s->nb_chapters]->title);
#endif
  mpxplay_av_metadata_free(&s->chapters[s->nb_chapters]->metadata);
  mpxplay_av_free(s->chapters[s->nb_chapters]);
 }
 mpxplay_av_freep(&s->chapters);*/
 mpxplay_av_metadata_free(&s->metadata);
 //mpxplay_av_free(s); // !!! static in in_ffmpeg.c
}

AVStream *mpxplay_av_new_stream(AVFormatContext *s, int id)
{
 AVStream *st;
 //int i;

#ifdef DEBUG
 av_log(NULL,AV_LOG_DEBUG,"av_new_stream_begin\n");
#endif
 if(s->nb_streams >= MAX_STREAMS)
  return NULL;

 st = pds_calloc(1,sizeof(AVStream));
 if(!st)
  return NULL;

 st->codec= mpxplay_avcodec_alloc_context();
 //if(s->iformat) {
 // no default bitrate if decoding
 // st->codec->bit_rate = 0;
 //}
 st->index = s->nb_streams;
 st->id = id;
 st->start_time = AV_NOPTS_VALUE;
 st->duration = AV_NOPTS_VALUE;
        /* we set the current DTS to 0 so that formats without any timestamps
           but durations get some timestamps, formats with some unknown
           timestamps have their first few packets buffered and the
           timestamps corrected before they are returned to the user */
 //st->cur_dts = 0;
 //st->first_dts = AV_NOPTS_VALUE;
 //st->probe_packets = MAX_PROBE_PACKETS;

 // default pts setting is MPEG-like
 mpxplay_av_set_pts_info(st, 33, 1, 90000); // !!!
 //st->last_IP_pts = AV_NOPTS_VALUE;
 //for(i=0; i<MAX_REORDER_DELAY+1; i++)
 // st->pts_buffer[i]= AV_NOPTS_VALUE;
 //st->reference_dts = AV_NOPTS_VALUE;

 //st->sample_aspect_ratio.num=0;
 //st->sample_aspect_ratio.den=1;

 s->streams[s->nb_streams++] = st;
#ifdef DEBUG
 av_log(NULL,AV_LOG_DEBUG,"av_new_stream_end\n");
#endif
 return st;
}

AVProgram *mpxplay_av_new_program(AVFormatContext *ac, int id)
{
 AVProgram *program=NULL;
 int i;

#ifdef DEBUG
 av_log(NULL, AV_LOG_DEBUG, "new_program: id=0x%04x\n", id);
#endif

 for(i=0; i<ac->nb_programs; i++)
  if(ac->programs[i]->id == id)
   program = ac->programs[i];

 if(!program){
  program = pds_calloc(1,sizeof(AVProgram));
  if(!program)
   return NULL;
  mpxplay_ff_dynarray_add((intptr_t **)&ac->programs, (int *)&ac->nb_programs, (intptr_t)program);
  program->discard = AVDISCARD_NONE;
 }
 program->id = id;

 return program;
}

//avpacket.c ---------------------------------------------------------
void mpxplay_av_destruct_packet(AVPacket *pkt)
{
 if(pkt->data)
  pds_free(pkt->data);
 pkt->data = NULL; pkt->size = 0; pkt->bufsize=0;
}

void mpxplay_av_init_packet(AVPacket *pkt)
{
 pkt->pts   = AV_NOPTS_VALUE;
 pkt->dts   = AV_NOPTS_VALUE;
 pkt->pos   = -1;
 pkt->duration = 0;
 pkt->stream_index = 0;
 pkt->destruct= NULL;
}

// !!! modified by PDS
int mpxplay_av_new_packet(AVPacket *pkt, int size)
{
 uint8_t *data;

 if((unsigned)size > (unsigned)size + FF_INPUT_BUFFER_PADDING_SIZE)
  return AVERROR(ENOMEM);

 if(size>pkt->bufsize){
  mpxplay_av_destruct_packet(pkt);
  data = pds_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE);
  if(!data)
   return AVERROR(ENOMEM);
  pkt->bufsize=size;
 }else
  data=pkt->data;

 memset(data + size, 0, FF_INPUT_BUFFER_PADDING_SIZE);

 mpxplay_av_init_packet(pkt);
 pkt->data = data;
 pkt->size = size;
 pkt->destruct = mpxplay_av_destruct_packet;
 return 0;
}

void mpxplay_av_free_packet(AVPacket *pkt)
{
 if(pkt){
  if(pkt->destruct)
   pkt->destruct(pkt);
  pkt->data = NULL; pkt->size = 0;
 }
}

//aviobuf.c
#if 0 // used at developing only
static int url_resetbuf(ByteIOContext *s, int flags)
{
/*#if LIBAVFORMAT_VERSION_MAJOR < 53
    URLContext *h = s->opaque;
    if ((flags & URL_RDWR) || (h && h->flags != flags && !h->flags & URL_RDWR))
        return AVERROR(EINVAL);
#else
    assert(flags == URL_WRONLY || flags == URL_RDONLY);
#endif*/

    if (flags & URL_WRONLY) {
        s->buf_end = s->buffer + s->buffer_size;
        s->write_flag = 1;
    } else {
        s->buf_end = s->buffer;
        s->write_flag = 0;
    }
    return 0;
}

int init_put_byte(ByteIOContext *s,
                  unsigned char *buffer,
                  int buffer_size,
                  int write_flag,
                  void *opaque,
                  int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),
                  int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),
                  int64_t (*seek)(void *opaque, int64_t offset, int whence))
{
    s->buffer = buffer;
    s->buffer_size = buffer_size;
    s->buf_ptr = buffer;
    s->opaque = opaque;
    s->buf_end=buffer;
    url_resetbuf(s, write_flag ? URL_WRONLY : URL_RDONLY);
    s->write_packet = write_packet;
    s->read_packet = read_packet;
    s->seek = seek;
    s->pos = 0;
    s->must_flush = 0;
    s->eof_reached = 0;
    s->error = 0;
    s->is_streamed = 0;
    s->max_packet_size = 0;
    s->update_checksum= NULL;
    if(!read_packet && !write_flag){
        s->pos = buffer_size;
        s->buf_end = s->buffer + buffer_size;
    }
    s->read_pause = NULL;
    s->read_seek  = NULL;
    return 0;
}
#endif

//-----------------------------------------------------------------
// lzo.c
#ifdef INFFMPG_LINK_LZO

//! Define if we may write up to 12 bytes beyond the output buffer.
#define OUTBUF_PADDED 1
//! Define if we may read up to 8 bytes beyond the input buffer.
#define INBUF_PADDED 1
typedef struct LZOContext {
    const uint8_t *in, *in_end;
    uint8_t *out_start, *out, *out_end;
    int error;
} LZOContext;

/**
 * \brief Reads one byte from the input buffer, avoiding an overrun.
 * \return byte read
 */
static inline int get_byte(LZOContext *c) {
    if (c->in < c->in_end)
        return *c->in++;
    c->error |= AV_LZO_INPUT_DEPLETED;
    return 1;
}

#ifdef INBUF_PADDED
#define GETB(c) (*(c).in++)
#else
#define GETB(c) get_byte(&(c))
#endif

/**
 * \brief Decodes a length value in the coding used by lzo.
 * \param x previous byte value
 * \param mask bits used from x
 * \return decoded length value
 */
static inline int get_len(LZOContext *c, int x, int mask) {
    int cnt = x & mask;
    if (!cnt) {
        while (!(x = get_byte(c))) cnt += 255;
        cnt += mask + x;
    }
    return cnt;
}

//#define UNALIGNED_LOADSTORE
#define BUILTIN_MEMCPY
#ifdef UNALIGNED_LOADSTORE
#define COPY2(d, s) *(uint16_t *)(d) = *(uint16_t *)(s);
#define COPY4(d, s) *(uint32_t *)(d) = *(uint32_t *)(s);
#elif defined(BUILTIN_MEMCPY)
#define COPY2(d, s) memcpy(d, s, 2);
#define COPY4(d, s) memcpy(d, s, 4);
#else
#define COPY2(d, s) (d)[0] = (s)[0]; (d)[1] = (s)[1];
#define COPY4(d, s) (d)[0] = (s)[0]; (d)[1] = (s)[1]; (d)[2] = (s)[2]; (d)[3] = (s)[3];
#endif

/**
 * \brief Copies bytes from input to output buffer with checking.
 * \param cnt number of bytes to copy, must be >= 0
 */
static inline void copy(LZOContext *c, int cnt) {
    register const uint8_t *src = c->in;
    register uint8_t *dst = c->out;
    if (cnt > c->in_end - src) {
        cnt = FFMAX(c->in_end - src, 0);
        c->error |= AV_LZO_INPUT_DEPLETED;
    }
    if (cnt > c->out_end - dst) {
        cnt = FFMAX(c->out_end - dst, 0);
        c->error |= AV_LZO_OUTPUT_FULL;
    }
#if defined(INBUF_PADDED) && defined(OUTBUF_PADDED)
    COPY4(dst, src);
    src += 4;
    dst += 4;
    cnt -= 4;
    if (cnt > 0)
#endif
        memcpy(dst, src, cnt);
    c->in = src + cnt;
    c->out = dst + cnt;
}

static inline void memcpy_backptr(uint8_t *dst, int back, int cnt);

/**
 * \brief Copies previously decoded bytes to current position.
 * \param back how many bytes back we start
 * \param cnt number of bytes to copy, must be >= 0
 *
 * cnt > back is valid, this will copy the bytes we just copied,
 * thus creating a repeating pattern with a period length of back.
 */
static inline void copy_backptr(LZOContext *c, int back, int cnt) {
    register const uint8_t *src = &c->out[-back];
    register uint8_t *dst = c->out;
    if (src < c->out_start || src > dst) {
        c->error |= AV_LZO_INVALID_BACKPTR;
        return;
    }
    if (cnt > c->out_end - dst) {
        cnt = FFMAX(c->out_end - dst, 0);
        c->error |= AV_LZO_OUTPUT_FULL;
    }
    memcpy_backptr(dst, back, cnt);
    c->out = dst + cnt;
}

static inline void memcpy_backptr(uint8_t *dst, int back, int cnt) {
    const uint8_t *src = &dst[-back];
    if (back == 1) {
        memset(dst, *src, cnt);
    } else {
#ifdef OUTBUF_PADDED
        COPY2(dst, src);
        COPY2(dst + 2, src + 2);
        src += 4;
        dst += 4;
        cnt -= 4;
        if (cnt > 0) {
            COPY2(dst, src);
            COPY2(dst + 2, src + 2);
            COPY2(dst + 4, src + 4);
            COPY2(dst + 6, src + 6);
            src += 8;
            dst += 8;
            cnt -= 8;
        }
#endif
        if (cnt > 0) {
            int blocklen = back;
            while (cnt > blocklen) {
                memcpy(dst, src, blocklen);
                dst += blocklen;
                cnt -= blocklen;
                blocklen <<= 1;
            }
            memcpy(dst, src, cnt);
        }
    }
}


int mpxplay_av_lzo1x_decode(void *out, int *outlen, const void *in, int *inlen) {
    int state= 0;
    int x;
    LZOContext c;
    c.in = in;
    c.in_end = (const uint8_t *)in + *inlen;
    c.out = c.out_start = out;
    c.out_end = (uint8_t *)out + * outlen;
    c.error = 0;
    x = GETB(c);
    if (x > 17) {
        copy(&c, x - 17);
        x = GETB(c);
        if (x < 16) c.error |= AV_LZO_ERROR;
    }
    if (c.in > c.in_end)
        c.error |= AV_LZO_INPUT_DEPLETED;
    while (!c.error) {
        int cnt, back;
        if (x > 15) {
            if (x > 63) {
                cnt = (x >> 5) - 1;
                back = (GETB(c) << 3) + ((x >> 2) & 7) + 1;
            } else if (x > 31) {
                cnt = get_len(&c, x, 31);
                x = GETB(c);
                back = (GETB(c) << 6) + (x >> 2) + 1;
            } else {
                cnt = get_len(&c, x, 7);
                back = (1 << 14) + ((x & 8) << 11);
                x = GETB(c);
                back += (GETB(c) << 6) + (x >> 2);
                if (back == (1 << 14)) {
                    if (cnt != 1)
                        c.error |= AV_LZO_ERROR;
                    break;
                }
            }
        } else if(!state){
                cnt = get_len(&c, x, 15);
                copy(&c, cnt + 3);
                x = GETB(c);
                if (x > 15)
                    continue;
                cnt = 1;
                back = (1 << 11) + (GETB(c) << 2) + (x >> 2) + 1;
        } else {
                cnt = 0;
                back = (GETB(c) << 2) + (x >> 2) + 1;
        }
        copy_backptr(&c, back, cnt + 2);
        state=
        cnt = x & 3;
        copy(&c, cnt);
        x = GETB(c);
    }
    *inlen = c.in_end - c.in;
    if (c.in > c.in_end)
        *inlen = 0;
    *outlen = c.out_end - c.out;
    return c.error;
}
#endif // INFFMPG_LINK_LZO

#endif // defined(MPXPLAY_LINK_INFILE_FFMPG)
