/*
 * decode.c
 * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
 * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
 *
 * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
 * See http://libmpeg2.sourceforge.net/ for updates.
 *
 * mpeg2dec is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * mpeg2dec is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Modifications for this RISCOS port, Copyright (c) 2002 P.Everett
 * <peter@everett9981.freeserve.co.uk>
 *
 * This software is based on Kino v0.3 by eQ R&D <http://www.eqrd.net>
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "inttypes.h"
#include "mpeg2.h"
#include "mpeg2_inte.h"
#include "idct.h"
#include "timer1.h"
#include "config.h"
#include "ka_hardware.h"
#include "ka_error.h"
#include "ka_log.h"

// the maximum chunk size is determined by vbv_buffer_size
// which is 224K for MP@ML streams.
// (we make no pretenses of decoding anything more than that)
#define CHUNK_SIZE 0xA0000 // 320KB -> 640KB for Eragon.m2ts

#define inuse_fordisplay 0x01
#define inuse_asref      0x02

const mpeg2_info_t* mpeg2_info(mpeg2dec_t* mpeg2dec)
{
  return &(mpeg2dec->info);
}

/*
 * get_frame
 * ---------
 */
static mpeg2_frame_t* get_frame(mpeg2dec_t* mpeg2dec)
{
  mpeg2_frame_t* pframe;

  for (int i = 0; i < MPEG2DEC_MAXFRAMES; i++)
  {
    pframe = &mpeg2dec->frames[i];
    if (pframe->inuse == 0)
    {
      pframe->inuse = inuse_fordisplay;
      pframe->sequence_ref = mpeg2dec->info.sequence_ref;
      return pframe;
    }
  }

  return NULL;
}

static const char* str_states[] =
{ "Wait for Sequence"
, "Wait for Seq/Pic header"
, "Sequence"
, "Start of Group"
, "Picture"
, "Slice"
, "End"
};

static int chunk_transition(mpeg2dec_t * mpeg2dec, int code)
{
  mpeg2_decoder_t * decoder = &mpeg2dec->decoder;

  // ignore error blocks
  if (code == STARTCODE_SEQUENCE_ERROR)
    return 0;

  // are we leaving a given state?
  switch (mpeg2dec->state)
  {
    case STATE_SEQUENCE:
    {
      if ((code != STARTCODE_EXTENSION)
      &&  (code != STARTCODE_USERDATA))
      {
        mpeg2_header_sequence_finalize(mpeg2dec);
        decoder->second_field = 1;

        mpeg2dec->info.sequence = mpeg2dec->sequence;
        if (!mpeg2dec->info.sequence_ref)
          mpeg2dec->state = MPEG2_DECODE_NEW_SEQUENCE;
        mpeg2dec->info.sequence_ref++;
      }
    }
    break;
    case STATE_PICTURE:
    {
      if ((code != STARTCODE_EXTENSION)
      &&  (code != STARTCODE_USERDATA))
      {
        mpeg2_header_matrix_finalize(mpeg2dec);

        decoder->second_field =
            (decoder->picture_structure == FRAME_PICTURE) || !decoder->second_field;

        // start a new frame?
        if ((decoder->picture_structure == FRAME_PICTURE)
        ||  (!decoder->second_field))
        {
          mpeg2dec->state = MPEG2_DECODE_WAIT_FREEFRAME;
        }
        else
          mpeg2dec->state = MPEG2_DECODE_START_SECOND_PICTURE;
      }
    }
    break;
    case STATE_SLICE:
    {
      if ((code < STARTCODE_SLICE_MIN)
      ||  (code > STARTCODE_SLICE_MAX))
      {
        // ensure that any skipped macroblocks at the end of frame are processed
        if (mpeg2dec->current_frame->valid > 0)
          mpeg2_slices_complete(decoder);
        // till we know better
        if (decoder->second_field)
        {
          mpeg2dec->state = MPEG2_DECODE_END_FRAME;
        }
        else
          mpeg2dec->state = STATE_WAIT_HEADER;
      }
    }
    break;
  }

  return 1;
}

/*
 * parse_chunk
 * -----------
 */
static void chunk_parsedata(mpeg2dec_t * mpeg2dec, int code, const uint8_t *buffer, const uint8_t *buffer_end)
{
  mpeg2_decoder_t * decoder = &mpeg2dec->decoder;
  int chunk_state = CHUNK_FORBIDDEN;

  if (buffer >= buffer_end)
    return;

  // ignore error blocks
  if (code == STARTCODE_SEQUENCE_ERROR)
    return;

  // output statistics
  if (config.debug & cfg_printpicturestats)
    stats_header(mpeg2dec, code, buffer);

  // process chunk data
  if (code == STARTCODE_PICTURE)
  {
    // we must at least have found a sequence at some point
    if (mpeg2dec->state != STATE_WAIT_SEQUENCE)
    {
      chunk_state = mpeg2_header_picture(mpeg2dec, buffer);
      if (chunk_state == CHUNK_VALID)
      {
        // Ensure we start with an I/D frame
        if ((decoder->coding_type == B_TYPE) && !mpeg2dec->forward_reference_frame)
          mpeg2dec->state = STATE_WAIT_HEADER;
        else if ((decoder->coding_type == P_TYPE) && !mpeg2dec->backward_reference_frame)
        {
          mpeg2dec->state = STATE_WAIT_HEADER;
        }
        else
        {
          mpeg2dec->state = STATE_PICTURE;
          mpeg2dec->chunk.pts = mpeg2dec->vbv.pts;
          mpeg2dec->vbv.pts = 0;
        }
      }
    }
  }
  else if (code <= STARTCODE_SLICE_MAX)
  {
    if (mpeg2dec->state == STATE_SLICE)
    {
      chunk_state = CHUNK_VALID;
      if (mpeg2dec->current_frame->valid > 0)
      {
        uint32_t t = timer_gettime();
        chunk_state = mpeg2_slice(decoder, code, buffer, buffer_end);
        fn_time[3] += timer_gettime() - t;
      }
    }
  }
  else
  {
    switch (code)
    {
      case STARTCODE_USERDATA:
      {
        if ((mpeg2dec->state == STATE_SEQUENCE)
        ||  (mpeg2dec->state == STATE_PICTURE)
        ||  (mpeg2dec->state == STATE_GOP))
          chunk_state = CHUNK_VALID;
      }
      break;
      case STARTCODE_SEQUENCE_HEADER:
      {
        chunk_state = mpeg2_header_sequence(mpeg2dec, buffer);
        if (chunk_state == CHUNK_VALID)
          mpeg2dec->state = STATE_SEQUENCE;
      }
      break;
      case STARTCODE_EXTENSION:
      {
        // extension may only follow pictures or picture coding extensions
        if (mpeg2dec->state == STATE_PICTURE)
        {
          // picture coding extension is not allowed in MPEG-1
          if (decoder->mpeg1)
            chunk_state = CHUNK_FORBIDDEN;
          else chunk_state = mpeg2_header_extension(mpeg2dec, buffer);
        }
        // or only follow sequences or sequence extensions
        else if (mpeg2dec->state == STATE_SEQUENCE)
          chunk_state = mpeg2_header_extension(mpeg2dec, buffer);
      }
      break;
      case STARTCODE_SEQUENCE_END:
      {
        // we must at least have found a sequence at some point
        if (mpeg2dec->state != STATE_WAIT_SEQUENCE)
        {
          chunk_state = CHUNK_VALID;
          mpeg2dec->state = STATE_WAIT_SEQUENCE;
        }
      }
      break;
      case STARTCODE_GROUP:
      {
        // we must at least have found a sequence at some point
        if (mpeg2dec->state != STATE_WAIT_SEQUENCE)
        {
          chunk_state = CHUNK_VALID;
          mpeg2dec->state = STATE_GOP;
        }
      }
      break;
      default:
      {
        ka_log(ka_log_error | ka_log_video, "Unexpected Chunk 0x%02x", code);
      }
    }
  }

  // take note of any problem
  if (config.debug & cfg_printwarnings)
  {
    if (chunk_state == CHUNK_CORRUPTED)
      ka_log(ka_log_error | ka_log_video, "Chunk 0x%02x is corrupted", code);
    // no need to report chunks belong to picture before first sequence
    else if ((chunk_state == CHUNK_FORBIDDEN) && (mpeg2dec->state != STATE_WAIT_SEQUENCE))
      ka_log(ka_log_error | ka_log_video, "Chunk 0x%02x is forbidden in state '%s'", code, str_states[mpeg2dec->state]);
  }
}

mpeg2_frame_t* mpeg2_currentframe(mpeg2dec_t * mpeg2dec)
{
  return mpeg2dec->current_frame;
}

mpeg2_frame_t* mpeg2_displayframe(mpeg2dec_t * mpeg2dec)
{
  return mpeg2dec->display_frame;
}

void mpeg2_releaseframe(mpeg2dec_t * mpeg2dec, mpeg2_frame_t* pframe)
{
  mpeg2dec = mpeg2dec; // suppress not used warning
  pframe->inuse &= ~inuse_fordisplay; // can still be ref frame
}

/*
 * mpeg2_decode_data
 * -----------------
 */
int mpeg2_decode_videoblock(mpeg2dec_t * mpeg2dec, ka_error_t* pErrorBlock, const uint8_t** pstart, const uint8_t * end, unsigned int pts)
{
  const uint8_t * current = *pstart;
  const uint8_t * start = current;
  uint8_t byte = 0;
  uint32_t shift;

  if (start == NULL)
    return mpeg2_decode_complete(mpeg2dec, pErrorBlock);

  // Restore normal state if reentering the same data block
  switch (mpeg2dec->state)
  {
    case MPEG2_DECODE_NEW_SEQUENCE:
    {
      mpeg2_sequence_t* sequence = &mpeg2dec->sequence;
      int lu_size, cr_size, size;
      uint8_t * alloc;
      int i;

      if (config.debug & cfg_printpicturestats)
        ka_log(ka_log_info, " [state] New Sequence completed");

      // Get size for y u v buffers and this for 3 pictures (for. ref, back ref, current)
      lu_size = sequence->width * sequence->height + 31;
      lu_size &= ~31;

      cr_size = sequence->chroma_width * sequence->chroma_height + 31;
      cr_size &= ~31;

      size = MPEG2DEC_MAXFRAMES * (lu_size + cr_size + cr_size);

      // Free old frame buffer
      mpeg2_free(mpeg2dec->frames[0].base.y);

      // Allocate frame buffer
      alloc = mpeg2_malloc(pErrorBlock, size);
      if (!alloc)
      {
        mpeg2dec->state = MPEG2_DECODE_ERROR;
        return mpeg2dec->state;
      }

      for (i = 0; i < MPEG2DEC_MAXFRAMES; i++)
      {
        mpeg2dec->frames[i].base.y = alloc;
        memset(alloc, 0x00, lu_size);
        alloc += lu_size;
        mpeg2dec->frames[i].base.cb = alloc;
        memset(alloc, 0x80, cr_size);
        alloc += cr_size;
        mpeg2dec->frames[i].base.cr = alloc;
        memset(alloc, 0x80, cr_size);
        alloc += cr_size;
        mpeg2dec->frames[i].valid = -1;
        mpeg2dec->frames[i].inuse = 0;
      }

      mpeg2dec->forward_reference_frame = NULL;
      mpeg2dec->backward_reference_frame = NULL;
      mpeg2dec->current_frame = NULL;
      mpeg2dec->display_frame = NULL;

      mpeg2dec->state = STATE_SEQUENCE;
    }
    break;
    case MPEG2_DECODE_END_FRAME:
    {
      if (config.debug & cfg_printpicturestats)
        ka_log(ka_log_info, " [state] Frame completed");

      mpeg2dec->state = STATE_WAIT_HEADER;
    }
    break;
    case MPEG2_DECODE_WAIT_FREEFRAME:
    {
      mpeg2_frame_t* pframe;

      if (mpeg2dec->decoder.coding_type == B_TYPE)
      {
        pframe = get_frame(mpeg2dec);
        if (pframe == NULL)
          return mpeg2dec->state;

        mpeg2dec->display_frame
          = mpeg2dec->current_frame
          = pframe;
      }
      else
      {
        if ((mpeg2dec->forward_reference_frame)
        &&  (mpeg2dec->forward_reference_frame != mpeg2dec->backward_reference_frame))
        {
          pframe = mpeg2dec->forward_reference_frame;
          pframe->inuse &= ~inuse_asref; // no use as ref anymore, may still be to display
        }
        pframe = get_frame(mpeg2dec);
        if (pframe == NULL)
          return mpeg2dec->state;

        pframe->inuse |= inuse_asref; // used as ref (i.e. the decoder may still use it after the frame is displayed)

        mpeg2dec->display_frame
          = mpeg2dec->forward_reference_frame
          = mpeg2dec->backward_reference_frame;
        mpeg2dec->backward_reference_frame
          = mpeg2dec->current_frame
          = pframe;
        // Eventually use current frame as forward reference too
        // because starting with I B B is legal as long as no skipping
        // and no forward motion vectors are used.
        // Since there is no guarantee on the B frames (and since
        // such a situation will occur after seeking) use backward
        // reference as forward reference to reduce visibility
        // of the invalid block usages.
        if (!mpeg2dec->forward_reference_frame)
          mpeg2dec->forward_reference_frame = mpeg2dec->backward_reference_frame;
      }

      pframe = mpeg2dec->current_frame;
      pframe->temporal_reference = mpeg2dec->decoder.temporal_reference;

      pframe->coding_type = mpeg2dec->decoder.coding_type;
      pframe->display_type = (  (mpeg2dec->decoder.picture_structure == FRAME_PICTURE)
                             && (mpeg2dec->decoder.progressive_frame))
                           ? 2
                           : (mpeg2dec->decoder.top_field_first ? 1 : 0);
      pframe->pts = mpeg2dec->chunk.pts;
      pframe->valid = 1;

      mpeg2dec->state = MPEG2_DECODE_START_FIRST_PICTURE;
      return mpeg2dec->state;
    }
    break;
    case MPEG2_DECODE_START_FIRST_PICTURE:
    {
      if (config.debug & cfg_printpicturestats)
        ka_log(ka_log_info, " [state] Frame started");
    }
    // no break
    case MPEG2_DECODE_START_SECOND_PICTURE:
    {
      if ((config.debug & cfg_printpicturestats)
      &&  mpeg2dec->decoder.second_field)
        ka_log(ka_log_info, " [state] Second field started");
      mpeg2dec->current_frame->duration = (mpeg2dec->nb_fields * mpeg2dec->sequence.frame_period) >> 1;

      // for slices init
      if (mpeg2dec->current_frame->valid > 0)
        mpeg2_slices_init(&mpeg2dec->decoder, &mpeg2dec->forward_reference_frame->base
                     , &mpeg2dec->current_frame->base
                     , &mpeg2dec->backward_reference_frame->base);

      // switch to new state
      mpeg2dec->state = STATE_SLICE;
      // picture without any slice?
      chunk_transition(mpeg2dec, mpeg2dec->chunk.code);
      if (mpeg2dec->state < 0)
        return mpeg2dec->state;
    }
    break;
    case MPEG2_DECODE_ERROR:
    {
      return mpeg2dec->state;
    }
    break;
    default:
    {
      // First entry for this block
      mpeg2dec->vbv.blockid++;
      mpeg2dec->vbv.start = current;
      mpeg2dec->vbv.ptsset = 0;
    }
  }

  shift = mpeg2dec->vbv.shift;
  start = current;

  while(1)
  {
    // Parse an existing chunk, possibly decoded from a previous call for the same data block
    if (mpeg2dec->chunk.start)
    {
      // Parse the chunk
      chunk_parsedata(mpeg2dec, mpeg2dec->chunk.code, mpeg2dec->chunk.start, mpeg2dec->chunk.end - 1);

      if ((mpeg2dec->chunk.blockid == mpeg2dec->vbv.blockid)
      &&  (mpeg2dec->chunk.start != mpeg2dec->chunk.buffer))
      {
        // Chunk was contained within this data block, start scanning from end of the chunk
        current = mpeg2dec->chunk.end;
      }

      mpeg2dec->chunk.ptr = mpeg2dec->chunk.buffer;
      mpeg2dec->chunk.start = NULL;
      mpeg2dec->chunk.code = mpeg2dec->vbv.nextcode;
      if (mpeg2dec->vbv.nextpts)
      {
        mpeg2dec->vbv.pts = mpeg2dec->vbv.nextpts;
        mpeg2dec->vbv.nextpts = 0;
      }
      start = current;
      shift = 0xffffffff;
    }

    // Find start of next chunk
    while (current < end)
    {
      byte = *current++;
      shift = (shift << 8) | byte;

      if ((shift >> 8) == 0x000001)
        break;
    }

    // Stop looping if no start code found
    if ((shift >> 8) != 0x000001)
      break;

    // Found start_code of following chunk
    mpeg2dec->vbv.nextcode = byte;

    // The picture time stamp concerns the first PICTURE chunk started in this data block
    // (not the start of the data but 4 bytes before at the chunk marker)
    // and so till all chunks of the previous block are completely processed
    // the picture time stamp of the previous block must be used
    if ((!mpeg2dec->vbv.ptsset && pts) && (current - mpeg2dec->vbv.start >= 4))
    {
      mpeg2dec->vbv.nextpts = pts;
      mpeg2dec->vbv.ptsset = 1;
    }

    if (mpeg2dec->chunk.ptr != mpeg2dec->chunk.buffer)
    {
      // The data completes a chunk started in the previous data block
      if (mpeg2dec->chunk.ptr + (current - start) > mpeg2dec->chunk.buffer + CHUNK_SIZE)
      {
        // (video buffer overflow)
        ka_error_fill(pErrorBlock, "error6: %s %d %d", "Chunk", (mpeg2dec->chunk.ptr - mpeg2dec->chunk.buffer) + (current - start), CHUNK_SIZE);
        mpeg2dec->state = MPEG2_DECODE_ERROR;
        return mpeg2dec->state;
      }
      memcpy(mpeg2dec->chunk.ptr, start, current - start);
      mpeg2dec->chunk.start = mpeg2dec->chunk.buffer;
      mpeg2dec->chunk.end = mpeg2dec->chunk.ptr + (current - start);
      // Copied data can be freed
      start = current;
    }
    else
    {
      // The data is a complete chunk from this data block
      mpeg2dec->chunk.start = start;
      mpeg2dec->chunk.end = current;
      mpeg2dec->chunk.blockid = mpeg2dec->vbv.blockid;
    }

    // Check for special state changes
    chunk_transition(mpeg2dec, mpeg2dec->chunk.code);
    if (mpeg2dec->state < 0)
    {
      *pstart = start;
      return mpeg2dec->state;
    }
  }

  // Save end of data block in chunk data, either as start or continuation
  if (mpeg2dec->chunk.ptr + (end - start) > mpeg2dec->chunk.buffer + CHUNK_SIZE)
  {
    // (video buffer overflow)
    ka_error_fill(pErrorBlock, "error6: %s %d %d", "Chunk", (mpeg2dec->chunk.ptr - mpeg2dec->chunk.buffer) + (end - start), CHUNK_SIZE);
    mpeg2dec->state = MPEG2_DECODE_ERROR;
    return mpeg2dec->state;
  }
  // If start of new block, assign it current block id
  if (mpeg2dec->chunk.ptr == mpeg2dec->chunk.buffer)
    mpeg2dec->chunk.blockid = mpeg2dec->vbv.blockid;
  memcpy(mpeg2dec->chunk.ptr, start, end - start);
  mpeg2dec->chunk.ptr += end - start;
  mpeg2dec->vbv.shift = shift;
  if (!mpeg2dec->vbv.ptsset && pts)
  {
    mpeg2dec->vbv.nextpts = pts;
    mpeg2dec->vbv.ptsset = 1;
  }

  *pstart = end;

  return MPEG2_DECODE_BLOCKEND;
}

int mpeg2_decode_complete(mpeg2dec_t* mpeg2dec, ka_error_t* pErrorBlock)
{
  pErrorBlock = pErrorBlock; // unused
  // complete existing frame?
  if (mpeg2dec->state == STATE_SLICE)
  {
    // complete picture
    chunk_transition(mpeg2dec, STARTCODE_PICTURE);
    // needs to simulate a second field?
    if (mpeg2dec->state == STATE_WAIT_HEADER)
    {
      mpeg2dec->decoder.second_field = 1;
      mpeg2dec->current_frame->duration = (mpeg2dec->nb_fields * mpeg2dec->sequence.frame_period) >> 1;

      // for slices init
      if (mpeg2dec->current_frame->valid > 0)
        mpeg2_slices_init(&mpeg2dec->decoder, &mpeg2dec->forward_reference_frame->base
                     , &mpeg2dec->current_frame->base
                     , &mpeg2dec->backward_reference_frame->base);

      // switch to new state
      mpeg2dec->state = STATE_SLICE;
      // complete picture
      chunk_transition(mpeg2dec, STARTCODE_PICTURE);
    }

    // Special case, stream contains a single frame
    if ((mpeg2dec->display_frame == NULL)
    &&  (mpeg2dec->current_frame != NULL)
    &&  (  (mpeg2dec->current_frame->coding_type == I_TYPE)
        || (mpeg2dec->current_frame->coding_type == D_TYPE)
        ))
      mpeg2dec->display_frame = mpeg2dec->current_frame;

    if (config.debug & cfg_printpicturestats)
    {
      static const char* stype[5] = {"", "I", "P", "B", "D"};
      ka_log(ka_log_info
           , " [state] Last frame (of type %s) completed, is%s display frame (of type %s)"
           , (mpeg2dec->current_frame != NULL) ? stype[mpeg2dec->current_frame->coding_type] : "-"
           , (mpeg2dec->display_frame == mpeg2dec->current_frame) ? "" : " not"
           , (mpeg2dec->display_frame != NULL) ? stype[mpeg2dec->display_frame->coding_type] : "-"
           );
    }

    // will return MPEG2_DECODE_END_FRAME
    // display_frame is either current B-Frame or what just became the forward reference frame
    return mpeg2dec->state;
  }

  if (mpeg2dec->current_frame != NULL)
  {
    if (mpeg2dec->current_frame->coding_type == B_TYPE)
    {
      // Last decoded frame is B-TYPE: backward reference frame is displayed after it
      if (mpeg2dec->display_frame == mpeg2dec->current_frame)
      {
        mpeg2dec->display_frame = mpeg2dec->backward_reference_frame;

        if (config.debug & cfg_printpicturestats)
          ka_log(ka_log_info, " [state] Add backward reference as final display frame after final decoded B-frame");

        return MPEG2_DECODE_END_FRAME;
      }

      mpeg2dec->current_frame = NULL; // Done
      mpeg2dec->display_frame = NULL;
    }
    else
    {
      // Last decoded frame is a reference frame: that means it is the backward reference frame and is displayed last
      if (mpeg2dec->display_frame != mpeg2dec->current_frame)
      {
        mpeg2dec->display_frame = mpeg2dec->current_frame;

        if (config.debug & cfg_printpicturestats)
          ka_log(ka_log_info, " [state] Add final decoded frame (non-B) as final display frame");

        return MPEG2_DECODE_END_FRAME;
      }

      mpeg2dec->current_frame = NULL; // Done
      mpeg2dec->display_frame = NULL;
    }
  }

  if (mpeg2dec->forward_reference_frame != NULL)
  {
    mpeg2dec->forward_reference_frame->inuse &= ~inuse_asref;
    mpeg2dec->forward_reference_frame = NULL;
  }
  if (mpeg2dec->backward_reference_frame != NULL)
  {
    mpeg2dec->backward_reference_frame->inuse &= ~inuse_asref;
    mpeg2dec->backward_reference_frame = NULL;
  }

  if (config.debug & cfg_printpicturestats)
    ka_log(ka_log_info, " [state] End");

  return MPEG2_DECODE_BLOCKEND;
}

void mpeg2_decode_reset(mpeg2dec_t * mpeg2dec)
{
  if (config.debug & cfg_printpicturestats)
    ka_log(ka_log_info, " [state] Reset");

  // reset chunk buffer
  mpeg2dec->state = (mpeg2dec->info.sequence_ref) ? STATE_WAIT_HEADER : STATE_WAIT_SEQUENCE;
  mpeg2dec->chunk.ptr = mpeg2dec->chunk.buffer;
  mpeg2dec->chunk.start = NULL;
  mpeg2dec->chunk.end = NULL;
  mpeg2dec->chunk.pts = 0;
  mpeg2dec->chunk.code = STARTCODE_SEQUENCE_ERROR;
  mpeg2dec->chunk.blockid = 0;
  mpeg2dec->vbv.start = NULL;
  mpeg2dec->vbv.shift = 0xffffffff;
  mpeg2dec->vbv.pts = 0;
  mpeg2dec->vbv.blockid = 0;
  mpeg2dec->vbv.nextpts = 0;
  mpeg2dec->vbv.nextcode = 0;
  mpeg2dec->vbv.ptsset = 0;

  // invalidate all current data
  mpeg2dec->current_frame = NULL;
  mpeg2dec->display_frame = NULL;
  // Will cause later crashes if we don't check first frame type correctly
  mpeg2dec->forward_reference_frame = NULL;
  mpeg2dec->backward_reference_frame = NULL;
  mpeg2dec->decoder.second_field = 1;
  // mark all frames as free to use
  for (int i = 0; i < MPEG2DEC_MAXFRAMES; i++)
  {
    mpeg2dec->frames[i].inuse = 0;
  }
}

/*
 * mpeg2_setoptions
 * ----------------
 */
void mpeg2_setoptions(mpeg2dec_t* mpeg2dec, int32_t mask, int32_t value)
{
  mpeg2dec->decoder.options &= ~mask;
  mpeg2dec->decoder.options |= value;
}

extern mpeg2_mc_t mc_functions;
extern mpeg2_mc_t mc_functions_arm;
extern mpeg2_mc_t mc_functions_neon;
/*
 * mpeg2_init
 * ----------
 */
mpeg2dec_t * mpeg2_init(ka_error_t* pErrorBlock, uint32_t hardware)
{
  if (hardware & ka_hardware_neon)
  {
    mc_functions = mc_functions_neon;
    idct_block_copy_dc = idct_block_copy_dc_neon;
    idct_block_copy = idct_block_copy_neon;
    idct_block_add_dc = idct_block_add_dc_neon;
    idct_block_add = idct_block_add_neon;
  }
  else
  {
    mc_functions = mc_functions_arm;
    idct_block_copy_dc = idct_block_copy_dc_arm;
    idct_block_copy = idct_block_copy_c;
    idct_block_add_dc = idct_block_add_dc_arm;
    idct_block_add = idct_block_add_c;
  }

  mpeg2dec_t * mpeg2dec = mpeg2_calloc(pErrorBlock, sizeof(*mpeg2dec));
  if (!mpeg2dec)
    return NULL;

  // Allocate frame buffer
  mpeg2dec->chunk.buffer = mpeg2_malloc(pErrorBlock, CHUNK_SIZE);
  if (!mpeg2dec->chunk.buffer)
  {
    mpeg2_free(mpeg2dec);
    return NULL;
  }

  mpeg2dec->info.sequence_ref = 0;
  mpeg2_decode_reset(mpeg2dec);

  return mpeg2dec;
}

/*
 * mpeg2_close
 * -----------
 */
void mpeg2_close(mpeg2dec_t * mpeg2dec)
{
  mpeg2_free(mpeg2dec->frames[0].base.y);
  mpeg2_free(mpeg2dec->chunk.buffer);
  mpeg2_free(mpeg2dec);
}
