/*--------------------------------------------------------------------------
Copyright (c) 2010-2011, Code Aurora Forum. 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 of Code Aurora 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS 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.
--------------------------------------------------------------------------*/
#include "decoder_driver_test.h"

#define DEBUG_PRINT printf
/************************************************************************/
/*        #DEFINES                          */
/************************************************************************/

#define VOP_START_CODE 0x000001B6
#define SHORT_HEADER_START_CODE 0x00008000
#define H264_START_CODE         0x00000001

/************************************************************************/
/*        STATIC VARIABLES                          */
/************************************************************************/

static int Code_type;
static int total_frames = 0;
static unsigned int header_code = 0;
static pthread_mutex_t read_lock;

static unsigned int read_frame ( unsigned char *dataptr,unsigned int length,
                                 FILE * inputBufferFile
                                );
static int Read_Buffer_From_DAT_File( unsigned char *dataptr, unsigned int length,
                                      FILE * inputBufferFile
                                     );

static unsigned clp2(unsigned x)
{
        x = x - 1;
        x = x | (x >> 1);
        x = x | (x >> 2);
        x = x | (x >> 4);
        x = x | (x >> 8);
        x = x | (x >>16);
        return x + 1;
}

static void* video_thread (void *);
static void* async_thread (void *);

int main (int argc, char **argv)
{
  struct video_decoder_context *decoder_context = NULL;
  char *file_name = NULL;
  FILE *file_ptr = NULL;
  int temp1 =0,temp2 =0;
  int error = 1;
  unsigned int i = 0;

  file_name = argv [1];
  file_ptr = fopen (file_name,"rb");

  if (file_ptr == NULL)
  {
    DEBUG_PRINT("\n File is not located ");
    return -1;
  }


  decoder_context = (struct video_decoder_context *) \
                   calloc (sizeof (struct video_decoder_context),1);
  if (decoder_context == NULL)
  {
    return -1;
  }
  decoder_context->outputBufferFile = NULL;
  decoder_context->inputBufferFile = NULL;
  decoder_context->video_driver_fd = -1;
  decoder_context->inputBufferFile = file_ptr;

  file_ptr = fopen ("/data/output.yuv","wb");
  if (file_ptr == NULL)
  {
    DEBUG_PRINT("\n File can't be created");
    free (decoder_context);
    return -1;
  }
  decoder_context->outputBufferFile = file_ptr;

   switch (atoi(argv[2]))
   {
   case 0:
     DEBUG_PRINT("\n MPEG4 codec selected");
     decoder_context->decoder_format = VDEC_CODECTYPE_MPEG4;
     Code_type = 0;
     break;
   case 1:
     DEBUG_PRINT("\n H.263");
     decoder_context->decoder_format = VDEC_CODECTYPE_H263;
     Code_type = 0;
     break;
   case 2:
     DEBUG_PRINT("\n H.264");
     decoder_context->decoder_format = VDEC_CODECTYPE_H264;
     Code_type = 1;
     break;
   default:
     DEBUG_PRINT("\n Wrong codec type");
     error = -1;
     break;
   }

   if (error != -1)
   {
     temp1 = atoi(argv[3]);
     temp2 = atoi(argv[4]);

     if (((temp1%16) != 0) || ((temp2%16) != 0))
     {
       error = -1;
     }
     else
     {
      decoder_context->video_resoultion.frame_height = temp1;
            decoder_context->video_resoultion.frame_width = temp2;
     }
   }

   switch (atoi(argv[5]))
   {
   case 0:
     DEBUG_PRINT("\n No Sink");
     decoder_context->outputBufferFile = NULL;
     break;
   }

   if ( error != -1 && (init_decoder (decoder_context) == -1 ))
   {
      DEBUG_PRINT("\n Init decoder fails ");
      error = -1;
   }
   DEBUG_PRINT("\n Decoder open successfull");


   /*Allocate input and output buffers*/
   if (error != -1 && (allocate_buffer (VDEC_BUFFER_TYPE_INPUT,
                      decoder_context)== -1))
   {
     DEBUG_PRINT("\n Error in input Buffer allocation");
     error = -1;
   }

   if (error != -1 && (allocate_buffer (VDEC_BUFFER_TYPE_OUTPUT,
                      decoder_context)== -1))
   {
     DEBUG_PRINT("\n Error in output Buffer allocation");
     error = -1;
   }


   if (error != -1 && (start_decoding (decoder_context) == -1))
   {
     DEBUG_PRINT("\n Error in start decoding call");
     error = -1;
   }

   if (error != -1 && (stop_decoding (decoder_context) == -1))
   {
     DEBUG_PRINT("\n Error in stop decoding call");
     error = -1;
   }

   DEBUG_PRINT("\n De-init the decoder");
   if ((deinit_decoder (decoder_context) == -1))
   {
      error = -1;
   }


  (void)free_buffer (VDEC_BUFFER_TYPE_INPUT,decoder_context);
  (void)free_buffer (VDEC_BUFFER_TYPE_OUTPUT,decoder_context);

  if (decoder_context->inputBufferFile != NULL)
  {
   fclose (decoder_context->inputBufferFile);
  }
  if (decoder_context->outputBufferFile != NULL)
  {
    fclose (decoder_context->outputBufferFile);
  }
  DEBUG_PRINT ("\n Total Number of frames decoded %d",total_frames);
  DEBUG_PRINT("\n closing the driver");
  free (decoder_context);

  return error;
}

int init_decoder ( struct video_decoder_context *init_decode )
{
  struct vdec_ioctl_msg ioctl_msg = {NULL,NULL};
  struct video_queue_context *queue_ptr = NULL;
#ifdef MAX_RES_720P
  enum vdec_output_fromat output_format = VDEC_YUV_FORMAT_NV12;
#endif
#ifdef MAX_RES_1080P
  enum vdec_output_fromat output_format  = VDEC_YUV_FORMAT_TILE_4x2;
#endif

  pthread_mutexattr_t init_values;

  DEBUG_PRINT("\n Before calling the open");

  init_decode->video_driver_fd = open ("/dev/msm_vidc_dec", \
                     O_RDWR | O_NONBLOCK);



  if (init_decode->video_driver_fd < 0)
  {
    DEBUG_PRINT("\n Open failed");
    return -1;
  }


  /*Initialize Decoder with codec type and resolution*/
  ioctl_msg.in = &init_decode->decoder_format;
  ioctl_msg.out = NULL;

  if (ioctl (init_decode->video_driver_fd,VDEC_IOCTL_SET_CODEC,
         (void*)&ioctl_msg) < 0)
  {
    DEBUG_PRINT("\n Set codec type failed");
    return -1;
  }

  /*Set the output format*/
  ioctl_msg.in = &output_format;
  ioctl_msg.out = NULL;

  if (ioctl (init_decode->video_driver_fd,VDEC_IOCTL_SET_OUTPUT_FORMAT,
         (void*)&ioctl_msg) < 0)
  {
    DEBUG_PRINT("\n Set output format failed");
    return -1;
  }

  ioctl_msg.in = &init_decode->video_resoultion;
  ioctl_msg.out = NULL;

  if (ioctl (init_decode->video_driver_fd,VDEC_IOCTL_SET_PICRES,
         (void*)&ioctl_msg) < 0)
  {
    DEBUG_PRINT("\n Set Resolution failed");
    return -1;
  }
  DEBUG_PRINT("\n After Set Resolution");

  DEBUG_PRINT("\n Query Input bufffer requirements");
  /*Get the Buffer requirements for input and output ports*/

  init_decode->input_buffer.buffer_type = VDEC_BUFFER_TYPE_INPUT;
  ioctl_msg.in = NULL;
  ioctl_msg.out = &init_decode->input_buffer;

  if (ioctl (init_decode->video_driver_fd,VDEC_IOCTL_GET_BUFFER_REQ,
         (void*)&ioctl_msg) < 0)
  {
    DEBUG_PRINT("\n Requesting for input buffer requirements failed");
    return -1;
  }

  DEBUG_PRINT("\n input Size=%d min count =%d actual count = %d", \
              init_decode->input_buffer.buffer_size,\
              init_decode->input_buffer.mincount,\
              init_decode->input_buffer.actualcount);


  init_decode->input_buffer.buffer_type = VDEC_BUFFER_TYPE_INPUT;
  ioctl_msg.in = &init_decode->input_buffer;
  ioctl_msg.out = NULL;
  init_decode->input_buffer.actualcount = init_decode->input_buffer.mincount + 2;

  if (ioctl (init_decode->video_driver_fd,VDEC_IOCTL_SET_BUFFER_REQ,
         (void*)&ioctl_msg) < 0)
  {
    DEBUG_PRINT("\n Set Buffer Requirements Failed");
    return -1;
  }


  DEBUG_PRINT("\n Query output bufffer requirements");
  init_decode->output_buffer.buffer_type = VDEC_BUFFER_TYPE_OUTPUT;
  ioctl_msg.in = NULL;
  ioctl_msg.out = &init_decode->output_buffer;

  if (ioctl (init_decode->video_driver_fd,VDEC_IOCTL_GET_BUFFER_REQ,
         (void*)&ioctl_msg) < 0)
  {
    DEBUG_PRINT("\n Requesting for output buffer requirements failed");
    return -1;
  }

  DEBUG_PRINT("\n output Size=%d min count =%d actual count = %d", \
              init_decode->output_buffer.buffer_size,\
              init_decode->output_buffer.mincount,\
              init_decode->output_buffer.actualcount);

  /*Create Queue related data structures*/
  queue_ptr = &init_decode->queue_context;
  queue_ptr->commandq_size = 50;
  queue_ptr->dataq_size = 50;

  sem_init(&queue_ptr->sem_message,0, 0);
  sem_init(&init_decode->sem_synchronize,0, 0);

  pthread_mutexattr_init (&init_values);
  pthread_mutex_init (&queue_ptr->mutex,&init_values);
  pthread_mutex_init (&read_lock,&init_values);
  DEBUG_PRINT("\n create Queues");
  queue_ptr->ptr_cmdq = (struct video_msgq*) \
                        calloc (sizeof (struct video_msgq),
                  queue_ptr->commandq_size);
  queue_ptr->ptr_dataq = (struct video_msgq*) \
              calloc (sizeof (struct video_msgq),
                  queue_ptr->dataq_size
                  );

  if ( queue_ptr->ptr_cmdq == NULL ||
     queue_ptr->ptr_dataq == NULL
    )
  {
    return -1;
  }
  DEBUG_PRINT("\n create Threads");
  /*Create two threads*/
    if ( (pthread_create (&init_decode->videothread_id,NULL,video_thread,
            init_decode) < 0) ||
         (pthread_create (&init_decode->asyncthread_id,NULL,async_thread,
            init_decode) < 0)
    )
  {
    return -1;
  }

  return 1;
}



int free_buffer ( enum vdec_buffer buffer_dir,
                  struct video_decoder_context *decode_context
                 )
{
  unsigned int buffercount = 0,i=0;
  struct vdec_bufferpayload **ptemp = NULL;

  if (decode_context == NULL)
  {
    return -1;
  }

  if (buffer_dir == VDEC_BUFFER_TYPE_INPUT && decode_context->ptr_inputbuffer)
  {
      buffercount = decode_context->input_buffer.actualcount;
      ptemp = decode_context->ptr_inputbuffer;

    for (i=0;i<buffercount;i++)
    {
          if (ptemp [i])
      {
            if (ptemp [i]->pmem_fd != -1)
      {
        munmap ( ptemp [i]->bufferaddr,ptemp [i]->mmaped_size);
        ptemp [i]->bufferaddr = NULL;
        close (ptemp [i]->pmem_fd);
      }
      free (ptemp [i]);
      ptemp [i] = NULL;
      }
    }
    free (decode_context->ptr_inputbuffer);
    decode_context->ptr_inputbuffer = NULL;
  }
  else if ( buffer_dir == VDEC_BUFFER_TYPE_OUTPUT )
  {
    buffercount = decode_context->output_buffer.actualcount;
    ptemp = decode_context->ptr_outputbuffer;

        if (decode_context->ptr_respbuffer)
    {
      for (i=0;i<buffercount;i++)
      {
        if (decode_context->ptr_respbuffer [i])
        {
          free (decode_context->ptr_respbuffer[i]);
          decode_context->ptr_respbuffer [i] = NULL;
        }
      }
      free (decode_context->ptr_respbuffer);
      decode_context->ptr_respbuffer = NULL;
    }

    if (ptemp)
    {
      for (i=0;i<buffercount;i++)
      {
        if (ptemp [i])
        {
          if (ptemp [i]->pmem_fd != -1)
          {
            munmap ( ptemp [i]->bufferaddr,ptemp [i]->mmaped_size);
            ptemp [i]->bufferaddr = NULL;
            close (ptemp [i]->pmem_fd);
          }
          free (ptemp [i]);
          ptemp [i] = NULL;
        }
      }
      free (ptemp);
      decode_context->ptr_outputbuffer = NULL;
    }
  }

  return 1;
}

int allocate_buffer ( enum vdec_buffer buffer_dir,
                      struct video_decoder_context *decode_context
                    )
{
  struct vdec_setbuffer_cmd setbuffers;
  struct vdec_bufferpayload **ptemp = NULL;
  struct vdec_ioctl_msg ioctl_msg = {NULL,NULL};
  unsigned int buffercount = 0,i=0,alignedsize=0;
  unsigned int buffersize = 0;

  if ( decode_context == NULL)
  {
    DEBUG_PRINT ("\nallocate_buffer: context is NULL");
    return -1;
  }

  if ( buffer_dir == VDEC_BUFFER_TYPE_INPUT )
  {
        /*Check if buffers are allocated*/
    if (decode_context->ptr_inputbuffer != NULL)
    {
      DEBUG_PRINT ("\nallocate_buffer: decode_context->ptr_inputbuffer is set");
      return -1;
    }

    buffercount = decode_context->input_buffer.actualcount;
    alignedsize = decode_context->input_buffer.alignment;
    buffersize = decode_context->input_buffer.buffer_size;
    buffersize = (buffersize + alignedsize) & (~alignedsize);
  }
  else if (buffer_dir == VDEC_BUFFER_TYPE_OUTPUT)
  {
    /*Check if buffers are allocated*/
    if (decode_context->ptr_outputbuffer != NULL)
    {
      DEBUG_PRINT ("\nallocate_buffer: Double allcoate output");
      return -1;
    }

    buffercount = decode_context->output_buffer.actualcount;
    alignedsize = decode_context->output_buffer.alignment;
    buffersize = decode_context->output_buffer.buffer_size;
    buffersize = (buffersize + alignedsize) & (~alignedsize);

    decode_context->ptr_respbuffer = (struct vdec_output_frameinfo  **)\
    calloc (sizeof (struct vdec_output_frameinfo *),buffercount);

    if (decode_context->ptr_respbuffer == NULL)
    {
      DEBUG_PRINT ("\n Allocate failure ptr_respbuffer");
      return -1;
    }

    for (i=0; i< buffercount; i++)
    {
      decode_context->ptr_respbuffer [i] = (struct vdec_output_frameinfo *)\
      calloc (sizeof (struct vdec_output_frameinfo),buffercount);
      if (decode_context->ptr_respbuffer [i] == NULL)
      {
        DEBUG_PRINT ("\nfailed to allocate vdec_output_frameinfo");
        return -1;
      }
    }
  }
  else
  {
    DEBUG_PRINT ("\nallocate_buffer: Wrong buffer directions");
    return -1;
  }

  ptemp = (struct vdec_bufferpayload **)\
  calloc (sizeof (struct vdec_bufferpayload *),buffercount);

  if (ptemp == NULL)
  {
    DEBUG_PRINT ("\nallocate_buffer: vdec_bufferpayload failure");
    return -1;
  }


  if (buffer_dir == VDEC_BUFFER_TYPE_OUTPUT)
  {
    DEBUG_PRINT ("\nallocate_buffer: OUT");
    decode_context->ptr_outputbuffer = ptemp;
  }
  else
  {
    DEBUG_PRINT ("\nallocate_buffer: IN");
    decode_context->ptr_inputbuffer = ptemp;
  }

  /*Allocate buffer headers*/
  for (i=0; i< buffercount; i++)
  {
    ptemp [i] = (struct vdec_bufferpayload*)\
    calloc (sizeof (struct vdec_bufferpayload),1);

    if (ptemp [i] == NULL)
    {
      DEBUG_PRINT ("\nallocate_buffer: ptemp [i] calloc failure");
      return -1;
    }

    if (buffer_dir == VDEC_BUFFER_TYPE_OUTPUT)
    {
         decode_context->ptr_respbuffer [i]->client_data = \
         (void *) ptemp [i];
    }
    ptemp [i]->pmem_fd = -1;

  }

  for (i=0; i< buffercount; i++)
  {
    ptemp [i]->pmem_fd = open ("/dev/pmem_adsp",O_RDWR);

    if (ptemp [i]->pmem_fd < 0)
    {
      DEBUG_PRINT ("\nallocate_buffer: open pmem_adsp failed");
      return -1;
    }

    ptemp [i]->bufferaddr = mmap(NULL,clp2(buffersize),PROT_READ|PROT_WRITE,
                                 MAP_SHARED,ptemp [i]->pmem_fd,0);
    DEBUG_PRINT ("\n pmem fd = %d virt addr = %p",ptemp [i]->pmem_fd,\
                  ptemp [i]->bufferaddr);
    if (ptemp [i]->bufferaddr == MAP_FAILED)
    {
      ptemp [i]->bufferaddr = NULL;
      DEBUG_PRINT ("\nallocate_buffer: MMAP failed");
      return -1;
    }
    ptemp [i]->buffer_len = buffersize;
    ptemp [i]->mmaped_size = clp2 (buffersize);

    setbuffers.buffer_type = buffer_dir;
    memcpy (&setbuffers.buffer,ptemp [i],sizeof (struct vdec_bufferpayload));

    ioctl_msg.in  = &setbuffers;
    ioctl_msg.out = NULL;

    if (ioctl (decode_context->video_driver_fd,VDEC_IOCTL_SET_BUFFER,
         &ioctl_msg) < 0)
    {
      DEBUG_PRINT ("\nallocate_buffer: Set Buffer IOCTL failed");
      return -1;
    }

  }
  DEBUG_PRINT ("\nallocate_buffer: Success");
  return 1;
}



int start_decoding (struct video_decoder_context *decode_context)
{
  struct vdec_ioctl_msg ioctl_msg = {NULL,NULL};
  struct vdec_input_frameinfo frameinfo;
  struct vdec_fillbuffer_cmd fillbuffer;
  unsigned int i = 0;
  unsigned int data_len =0;

  memset ((unsigned char*)&frameinfo,0,sizeof (struct vdec_input_frameinfo));
  memset ((unsigned char*)&fillbuffer,0,sizeof (struct vdec_fillbuffer_cmd));

  if (decode_context == NULL)
  {
    return -1;
  }

  if (ioctl (decode_context->video_driver_fd,VDEC_IOCTL_CMD_START,
         NULL) < 0)
  {
    DEBUG_PRINT("\n Start failed");
    return -1;
  }

  DEBUG_PRINT("\n Start Issued successfully waiting for Start Done");
  /*Wait for Start command response*/
    sem_wait (&decode_context->sem_synchronize);

  /*Push output Buffers*/
  i = 0;
  while (i < decode_context->output_buffer.mincount)
  {
    fillbuffer.buffer.buffer_len =
                               decode_context->ptr_outputbuffer [i]->buffer_len;
    fillbuffer.buffer.bufferaddr =
                               decode_context->ptr_outputbuffer [i]->bufferaddr;
    fillbuffer.buffer.offset =
                               decode_context->ptr_outputbuffer [i]->offset;
    fillbuffer.buffer.pmem_fd =
                               decode_context->ptr_outputbuffer [i]->pmem_fd;
    fillbuffer.client_data = (void *)decode_context->ptr_respbuffer [i];
    DEBUG_PRINT ("\n Client Data on output = %p",fillbuffer.client_data);
    ioctl_msg.in = &fillbuffer;
    ioctl_msg.out = NULL;

    if (ioctl (decode_context->video_driver_fd,
           VDEC_IOCTL_FILL_OUTPUT_BUFFER,&ioctl_msg) < 0)
    {
      DEBUG_PRINT("\n Decoder frame failed");
      return -1;
    }
    i++;
  }


  /*push input buffers*/
  i = 0;
  while (i < decode_context->input_buffer.mincount)
  {
    DEBUG_PRINT("\n Read  Frame from File");
    data_len = read_frame ( decode_context->ptr_inputbuffer [i]->bufferaddr,
                       decode_context->ptr_inputbuffer [i]->buffer_len,
             decode_context->inputBufferFile);
    if (data_len == 0)
    {
      DEBUG_PRINT("\n Length is zero error");
      return -1;
    }
    DEBUG_PRINT("\n Read  Frame from File szie = %u",data_len);
    frameinfo.bufferaddr =
    decode_context->ptr_inputbuffer [i]->bufferaddr;
    frameinfo.offset = 0;
    frameinfo.pmem_fd = decode_context->ptr_inputbuffer [i]->pmem_fd;
    frameinfo.pmem_offset = decode_context->ptr_inputbuffer [i]->offset;
    frameinfo.datalen = data_len;
    frameinfo.client_data = (struct vdec_bufferpayload *)\
                           decode_context->ptr_inputbuffer [i];
    /*TODO: Time stamp needs to be updated*/
    ioctl_msg.in = &frameinfo;
    ioctl_msg.out = NULL;

    if (ioctl (decode_context->video_driver_fd,VDEC_IOCTL_DECODE_FRAME,
         &ioctl_msg) < 0)
    {
      DEBUG_PRINT("\n Decoder frame failed");
      return -1;
    }
    total_frames++;
    i++;
  }
  DEBUG_PRINT ("\n Wait for EOS");
  /*Wait for EOS or Error condition*/
  sem_wait (&decode_context->sem_synchronize);
  DEBUG_PRINT ("\n Reached EOS");

  return 1;
}

int stop_decoding  (struct video_decoder_context *decode_context)
{
  struct vdec_ioctl_msg ioctl_msg = {NULL,NULL};
  enum vdec_bufferflush flush_dir = VDEC_FLUSH_TYPE_INPUT;

  if (decode_context == NULL)
  {
    return -1;
  }

  ioctl_msg.in = &flush_dir;
  ioctl_msg.out = NULL;

  if (ioctl(decode_context->video_driver_fd,VDEC_IOCTL_CMD_FLUSH,
         &ioctl_msg) < 0)
  {
    DEBUG_PRINT("\n Flush input failed");
  }
  else
  {
       sem_wait (&decode_context->sem_synchronize);
  }

  flush_dir = VDEC_FLUSH_TYPE_OUTPUT;
  ioctl_msg.in = &flush_dir;
  ioctl_msg.out = NULL;

  if (ioctl(decode_context->video_driver_fd,VDEC_IOCTL_CMD_FLUSH,
         &ioctl_msg) < 0)
  {
    DEBUG_PRINT("\n Flush output failed");
  }
  else
  {
     sem_wait (&decode_context->sem_synchronize);
  }

  DEBUG_PRINT("\n Stop VDEC_IOCTL_CMD_STOP");
  if (ioctl(decode_context->video_driver_fd,VDEC_IOCTL_CMD_STOP,
         NULL) < 0)
  {
    DEBUG_PRINT("\n Stop failed");
  }
  else
  {
     sem_wait (&decode_context->sem_synchronize);
  }
  return 1;
}

int deinit_decoder (struct video_decoder_context *init_decode)
{
  if (init_decode == NULL)
  {
    return -1;
  }

  /*Close the driver*/
  if (init_decode->video_driver_fd != -1)
  {
    close (init_decode->video_driver_fd);
  }

  if (init_decode->queue_context.ptr_cmdq)
  {
    free (init_decode->queue_context.ptr_cmdq);
    init_decode->queue_context.ptr_cmdq = NULL;
  }

  if (init_decode->queue_context.ptr_dataq)
  {
    free (init_decode->queue_context.ptr_dataq);
    init_decode->queue_context.ptr_dataq = NULL;
  }

  sem_destroy (&init_decode->queue_context.sem_message);
  sem_destroy (&init_decode->sem_synchronize);

  pthread_mutex_destroy(&init_decode->queue_context.mutex);
  pthread_mutex_destroy (&read_lock);

  return 1;
}

static void* video_thread (void *context)
{
   struct video_decoder_context *decode_context = NULL;
   struct video_msgq *queueitem = NULL;
   struct vdec_ioctl_msg ioctl_msg = {NULL,NULL};
   struct vdec_input_frameinfo frameinfo;
   struct vdec_fillbuffer_cmd fillbuffer;
   struct vdec_output_frameinfo *outputbuffer = NULL;
   struct vdec_bufferpayload *tempbuffer = NULL;
   unsigned int data_len =0;


   if (context == NULL)
   {
     DEBUG_PRINT("\n video thread recieved NULL context");
     return NULL;
   }
   decode_context = (struct video_decoder_context *) context;

   /* Thread function which will accept commands from async thread
    * or main thread
   */
   while (1)
   {
      queueitem = queue_get_cmd (&decode_context ->queue_context);
      if (queueitem != NULL)
      {
        switch (queueitem->cmd)
        {
        case VDEC_MSG_EVT_HW_ERROR:
          DEBUG_PRINT("\n FATAL ERROR ");
          break;
        case VDEC_MSG_RESP_INPUT_FLUSHED:
          DEBUG_PRINT("\n Input Buffer Flushed");
          break;
        case VDEC_MSG_RESP_OUTPUT_FLUSHED:
          DEBUG_PRINT("\n Output buffer Flushed");
          break;
        case VDEC_MSG_RESP_START_DONE:
          DEBUG_PRINT("\n recived start done command");
            sem_post (&decode_context->sem_synchronize);
          break;

        case VDEC_MSG_RESP_STOP_DONE:
          DEBUG_PRINT("\n recieved stop done");
          sem_post (&decode_context->sem_synchronize);
          break;

        case VDEC_MSG_RESP_INPUT_BUFFER_DONE:

          tempbuffer = (struct vdec_bufferpayload *)queueitem->clientdata;
          if (tempbuffer == NULL)
          {
            DEBUG_PRINT("\n FATAL ERROR input buffer address is bad");
            sem_post (&decode_context->sem_synchronize);
            break;
          }
          data_len = read_frame ( tempbuffer->bufferaddr,
                        tempbuffer->buffer_len,
                        decode_context->inputBufferFile
                     );

          if (data_len == 0)
          {
            DEBUG_PRINT ("\n End of stream reached");
            sem_post (&decode_context->sem_synchronize);
            break;
          }

          frameinfo.bufferaddr = tempbuffer->bufferaddr;
          frameinfo.offset = 0;
          frameinfo.pmem_fd = tempbuffer->pmem_fd;
          frameinfo.pmem_offset = tempbuffer->offset;
          frameinfo.datalen = data_len;
          frameinfo.client_data = (struct vdec_bufferpayload *)\
                       tempbuffer;
          /*TODO: Time stamp needs to be updated*/
          ioctl_msg.in = &frameinfo;
          ioctl_msg.out = NULL;
          total_frames++;
          if (ioctl(decode_context->video_driver_fd,VDEC_IOCTL_DECODE_FRAME,
               &ioctl_msg) < 0)
          {
            DEBUG_PRINT("\n Decoder frame failed");
            sem_post (&decode_context->sem_synchronize);
          }
          DEBUG_PRINT("\n Input buffer done send next buffer current value = %d",\
                      total_frames);
          break;

        case VDEC_MSG_RESP_OUTPUT_BUFFER_DONE:

         outputbuffer = (struct vdec_output_frameinfo *)\
                              queueitem->clientdata;
         DEBUG_PRINT("\n Value of client Data in VT %p",queueitem->clientdata);
         if (outputbuffer == NULL || outputbuffer->bufferaddr == NULL ||
                   outputbuffer->client_data == NULL
           )
         {
           DEBUG_PRINT("\n FATAL ERROR output buffer is bad");
           DEBUG_PRINT("\nValues outputbuffer = %p",outputbuffer);
           if (outputbuffer != NULL)
           {
           DEBUG_PRINT("\nValues outputbuffer->bufferaddr = %p",\
                       outputbuffer->bufferaddr);
           DEBUG_PRINT("\nValues outputbuffer->client_data = %p",\
                       outputbuffer->client_data);
           }
           sem_post (&decode_context->sem_synchronize);
           break;
         }


         if (outputbuffer->len == 0)
         {
           DEBUG_PRINT("\n Filled Length is zero Close decoding");
           sem_post (&decode_context->sem_synchronize);
           break;
         }

         if (decode_context->outputBufferFile != NULL)
         {
           fwrite (outputbuffer->bufferaddr,1,outputbuffer->len,
                decode_context->outputBufferFile);
         }

         tempbuffer = (struct vdec_bufferpayload *)\
                     outputbuffer->client_data;

         DEBUG_PRINT("\n recieved output buffer consume outbuffer");
         DEBUG_PRINT("\nValues outputbuffer->bufferaddr = %p",\
                     outputbuffer->bufferaddr);
         DEBUG_PRINT ("\n Vir address of allocated buffer %p",\
                      tempbuffer->bufferaddr);
         fillbuffer.buffer.buffer_len = tempbuffer->buffer_len;
         fillbuffer.buffer.bufferaddr = tempbuffer->bufferaddr;
         fillbuffer.buffer.offset = tempbuffer->offset;
         fillbuffer.buffer.pmem_fd = tempbuffer->pmem_fd;
         fillbuffer.client_data = (void *)outputbuffer;

         ioctl_msg.in = &fillbuffer;
         ioctl_msg.out = NULL;

         if (ioctl (decode_context->video_driver_fd,
              VDEC_IOCTL_FILL_OUTPUT_BUFFER,&ioctl_msg) < 0)
         {
           DEBUG_PRINT("\n Decoder frame failed");
           return NULL;
         }

         break;

        case VDEC_MSG_RESP_FLUSH_INPUT_DONE:
            DEBUG_PRINT("\n Flush input complete");
          sem_post (&decode_context->sem_synchronize);
          break;

        case VDEC_MSG_RESP_FLUSH_OUTPUT_DONE:
          DEBUG_PRINT("\n Flush output complete");
                sem_post (&decode_context->sem_synchronize);
          break;
        }

        if (queueitem->cmd == VDEC_MSG_RESP_STOP_DONE)
        {
          DEBUG_PRINT("\n Playback has ended thread will exit");
          return NULL;
        }
      }
      else
      {
        DEBUG_PRINT("\n Error condition recieved NULL from Queue");
      }

   }
}

static void* async_thread (void *context)
{
  struct video_decoder_context *decode_context = NULL;
  struct vdec_output_frameinfo *outputframe = NULL;
  struct video_msgq queueitem ;
  struct vdec_msginfo vdec_msg;
  struct vdec_ioctl_msg ioctl_msg = {NULL,NULL};
  int result = -1;

  if (context == NULL)
  {
    DEBUG_PRINT("\n aynsc thread recieved NULL context");
    return NULL;
  }
  decode_context = (struct video_decoder_context *) context;
  DEBUG_PRINT("\n Entering the async thread");

  while (1)
  {
    ioctl_msg.in = NULL;

    ioctl_msg.out = (void*)&vdec_msg;
    DEBUG_PRINT ("\n Sizeof vdec_msginfo = %d ",sizeof (vdec_msg));
    DEBUG_PRINT("\n Address of Vdec msg in async thread %p",\
                ioctl_msg.out);
    if (ioctl (decode_context->video_driver_fd,VDEC_IOCTL_GET_NEXT_MSG,\
         (void*)&ioctl_msg) < 0)
    {
      DEBUG_PRINT("\n Error in ioctl read next msg");
    }
    else
    {
      switch (vdec_msg.msgcode)
      {
      case VDEC_MSG_RESP_FLUSH_INPUT_DONE:
      case VDEC_MSG_RESP_FLUSH_OUTPUT_DONE:
      case VDEC_MSG_RESP_START_DONE:
      case VDEC_MSG_RESP_STOP_DONE:
      case VDEC_MSG_EVT_HW_ERROR:
        DEBUG_PRINT("\nioctl read next msg");
        queueitem.cmd = vdec_msg.msgcode;
        queueitem.status = vdec_msg.status_code;
        queueitem.clientdata = NULL;
        break;

      case VDEC_MSG_RESP_INPUT_FLUSHED:
      case VDEC_MSG_RESP_INPUT_BUFFER_DONE:

        queueitem.cmd = vdec_msg.msgcode;
        queueitem.status = vdec_msg.status_code;
        queueitem.clientdata = (void *)\
            vdec_msg.msgdata.input_frame_clientdata;
        break;

      case VDEC_MSG_RESP_OUTPUT_FLUSHED:
      case VDEC_MSG_RESP_OUTPUT_BUFFER_DONE:
        queueitem.cmd = vdec_msg.msgcode;
        queueitem.status = vdec_msg.status_code;
        outputframe = (struct vdec_output_frameinfo *)\
        vdec_msg.msgdata.output_frame.client_data;
        DEBUG_PRINT ("\n Client Data value in %p", \
                     vdec_msg.msgdata.output_frame.client_data);
        outputframe->bufferaddr = vdec_msg.msgdata.output_frame.bufferaddr;
        outputframe->framesize.bottom = \
        vdec_msg.msgdata.output_frame.framesize.bottom;
        outputframe->framesize.left = \
        vdec_msg.msgdata.output_frame.framesize.left;
        outputframe->framesize.right = \
        vdec_msg.msgdata.output_frame.framesize.right;
        outputframe->framesize.top = \
        vdec_msg.msgdata.output_frame.framesize.top;
        outputframe->framesize = vdec_msg.msgdata.output_frame.framesize;
        outputframe->len = vdec_msg.msgdata.output_frame.len;
        outputframe->time_stamp = vdec_msg.msgdata.output_frame.time_stamp;
        queueitem.clientdata = (void *)outputframe;
        DEBUG_PRINT ("\n Client Data value Copy %p",queueitem.clientdata);
       break;

      default:
        DEBUG_PRINT("\nIn Default of get next message %d",vdec_msg.msgcode);
        queueitem.cmd = vdec_msg.msgcode;
        queueitem.status = vdec_msg.status_code;
        queueitem.clientdata = NULL;
        break;
      }
      result = queue_post_cmdq (&decode_context->queue_context,&queueitem);
      while (result == 0)
      {
         result = queue_post_cmdq (&decode_context->queue_context,
                 &queueitem);
      }

      if (result == -1)
      {
        DEBUG_PRINT("\n FATAL ERROR WITH Queue");
      }
    }
    if (vdec_msg.msgcode == VDEC_MSG_RESP_STOP_DONE)
    {
      /*Thread can exit at this point*/
      return NULL;
    }
  }
}


static unsigned int read_frame (unsigned char *dataptr, unsigned int length,
                                FILE * inputBufferFile)
{

  unsigned int readOffset = 0;
  int bytes_read = 0;
  unsigned int code = 0;
  int found = 0;

  DEBUG_PRINT ("\n Inside the readframe");

  if (dataptr == NULL || length == 0)
  {
    DEBUG_PRINT ("\n dataptr = %p length = %u",dataptr,length);
    return 0;
  }

  if (!Code_type)
  {
    /* Start of Critical Section*/
    pthread_mutex_lock(&read_lock);
    do
    {
      //Start codes are always byte aligned.
      bytes_read = fread(&dataptr[readOffset],1, 1,inputBufferFile);
      if( !bytes_read)
      {
        DEBUG_PRINT("\n Bytes read Zero \n");
        break;
      }
      code <<= 8;
      code |= (0x000000FF & dataptr[readOffset]);
      //VOP start code comparision
      if (readOffset>3)
      {
        if(!header_code )
        {
          if( VOP_START_CODE == code)
          {
          DEBUG_PRINT ("\n Found VOP Code");
          header_code = VOP_START_CODE;
          }
          else if ( (0xFFFFFC00 & code) == SHORT_HEADER_START_CODE )
          {
          header_code = SHORT_HEADER_START_CODE;
          }
        }
        if ((header_code == VOP_START_CODE) && (code == VOP_START_CODE))
        {
          //Seek backwards by 4
          fseek(inputBufferFile, -4, SEEK_CUR);
          readOffset-=4;
          found = 1;
          break;

        }
        else if (( header_code == SHORT_HEADER_START_CODE ) &&
        ( SHORT_HEADER_START_CODE == (code & 0xFFFFFC00)))
        {
          //Seek backwards by 4
          fseek(inputBufferFile, -4, SEEK_CUR);
          readOffset-=4;
          found = 1;
          break;
        }
      }
      readOffset++;
    }while (readOffset < length);
    pthread_mutex_unlock(&read_lock);
    /* End of Critical Section*/
    if (found == 1)
    {
      //DEBUG_PRINT ("Found a Frame");
      return (readOffset+1);
    }
    else
    {
      //DEBUG_PRINT ("No Frames detected");
      return 0;
    }
  }
  else
  {

    readOffset = Read_Buffer_From_DAT_File(dataptr,length,inputBufferFile);
    if (total_frames == 0)
    {
      bytes_read = Read_Buffer_From_DAT_File(&dataptr[readOffset],
                                             (length-readOffset),
                                             inputBufferFile);
      readOffset += bytes_read;
    }
    return (readOffset);
  }

}

static int Read_Buffer_From_DAT_File(unsigned char *dataptr, unsigned int length,
                                     FILE * inputBufferFile)
{


  long frameSize=0;
  char temp_buffer[10];
  char temp_byte;
  int bytes_read=0;
  int i=0;
  unsigned char *read_buffer=NULL;
  char c = '1'; //initialize to anything except '\0'(0)
  char inputFrameSize[12];
  int count =0; char cnt =0;
  memset(temp_buffer, 0, sizeof(temp_buffer));

  while (cnt < 10)
  /* Check the input file format, may result in infinite loop */
  {
      count  = fread(&inputFrameSize[cnt],1,1,inputBufferFile);
      if(inputFrameSize[cnt] == '\0' )
        break;
      cnt++;
  }
  inputFrameSize[cnt]='\0';
  frameSize = atoi(inputFrameSize);
  //length = 0;
  DEBUG_PRINT ("\n Frame Size is %d",frameSize);

  /* get the frame length */
  fseek(inputBufferFile, -1, SEEK_CUR);
  bytes_read = fread(dataptr, 1, frameSize,  inputBufferFile);

  if(bytes_read == 0 || bytes_read < frameSize ) {
      return 0;
  }
  return bytes_read;
}