/*--------------------------------------------------------------------------
Copyright (c) 2010, 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 "video_encoder_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 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_encoder_context *encoder_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;
  }


  encoder_context = (struct video_encoder_context *) \
                   calloc (sizeof (struct video_encoder_context),1);

  encoder_context->outputBufferFile = NULL;
    encoder_context->inputBufferFile = NULL;
  encoder_context->video_driver_fd = -1;

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

  encoder_context->inputBufferFile = file_ptr;
  encoder_context->input_width = 176;
  encoder_context->input_height = 144;
  encoder_context->codectype = VEN_CODEC_MPEG4;
  encoder_context->fps_num = 60;
  encoder_context->fps_den = 2;
  encoder_context->inputformat = VEN_INPUTFMT_NV12;
  encoder_context->targetbitrate = 128000;

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

   switch (atoi(argv[2]))
   {
   case 0:
     DEBUG_PRINT("\n MPEG4 codec selected");
     encoder_context->codectype = VEN_CODEC_MPEG4;
     Code_type = 0;
     break;
   case 1:
     DEBUG_PRINT("\n H.263");
     encoder_context->codectype = VEN_CODEC_H263;
     Code_type = 0;
     break;
   case 2:
     DEBUG_PRINT("\n H.264");
     encoder_context->codectype = VEN_CODEC_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
     {
      encoder_context->input_width = temp1;
      encoder_context->input_height = temp2;
     }
   }

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

   if (error != -1)
   {
     encoder_context->targetbitrate = atoi (argv[6]);
   }

   if ( error != -1 && (init_encoder (encoder_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 (0,encoder_context)== -1))
   {
     DEBUG_PRINT("\n Error in input Buffer allocation");
     error = -1;
   }

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


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

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

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


  (void)free_buffer (INPUT_BUFFER,encoder_context);
  (void)free_buffer (OUTPUT_BUFFER,encoder_context);

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

  return error;
}

int init_encoder ( struct video_encoder_context *init_decode )
{
  struct venc_ioctl_msg ioctl_msg = {NULL,NULL};
  struct venc_basecfg basecfg;
  struct video_queue_context *queue_ptr = NULL;
  struct venc_ratectrlcfg ratecrl;
  pthread_mutexattr_t init_values;
  struct venc_profile profile;
  struct ven_profilelevel profilelevel;

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

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



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

  basecfg.codectype = init_decode->codectype;
  basecfg.dvs_height = 0;
  basecfg.dvs_width = 0;
  basecfg.fps_den = init_decode->fps_den;
  basecfg.fps_num = init_decode->fps_num;
  basecfg.input_height = init_decode->input_height;
  basecfg.input_width = init_decode->input_width;
  basecfg.inputformat = init_decode->inputformat;
  basecfg.targetbitrate = init_decode->targetbitrate;

  /*Initialize Decoder with codec type and resolution*/
  ioctl_msg.inputparam = &basecfg;
  ioctl_msg.outputparam = NULL;

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

  /*Initialize Decoder with codec type and resolution*/
  DEBUG_PRINT ("\n Switch off rate control");
  ioctl_msg.inputparam = &ratecrl;
  ioctl_msg.outputparam = NULL;
  ratecrl.rcmode = VEN_RC_OFF;
  if (ioctl (init_decode->video_driver_fd,VEN_IOCTL_SET_RATE_CTRL_CFG,
         (void*)&ioctl_msg) < 0)
  {
    DEBUG_PRINT("\n Set rate control failed");
    return -1;
  }

  if (basecfg.codectype == VEN_CODEC_H264)
  {
    DEBUG_PRINT ("\n Set the VEN_IOCTL_SET_CODEC_PROFILE High");
    ioctl_msg.inputparam = &profile;
    ioctl_msg.outputparam = NULL;
    profile.profile = VEN_PROFILE_H264_BASELINE;
    if (ioctl (init_decode->video_driver_fd,VEN_IOCTL_SET_CODEC_PROFILE,
           (void*)&ioctl_msg) < 0)
    {
      DEBUG_PRINT("\n Set VEN_IOCTL_SET_CODEC_PROFILE failed");
      return -1;
    }

    DEBUG_PRINT ("\n Set the VEN_IOCTL_SET_CODEC_PROFILE High");
    ioctl_msg.inputparam = &profilelevel;
    ioctl_msg.outputparam = NULL;
    profilelevel.level = VEN_LEVEL_H264_1p1;
    if (ioctl (init_decode->video_driver_fd,VEN_IOCTL_SET_PROFILE_LEVEL,
           (void*)&ioctl_msg) < 0)
    {
      DEBUG_PRINT("\n Set VEN_IOCTL_SET_CODEC_PROFILE failed");
      return -1;
    }

    if (basecfg.input_width > 720)
    {
      DEBUG_PRINT ("\n Set the VEN_IOCTL_SET_CODEC_PROFILE High");
      ioctl_msg.inputparam = &profile;
      ioctl_msg.outputparam = NULL;
      profile.profile = VEN_PROFILE_H264_HIGH;
      if (ioctl (init_decode->video_driver_fd,VEN_IOCTL_SET_CODEC_PROFILE,
             (void*)&ioctl_msg) < 0)
      {
        DEBUG_PRINT("\n Set VEN_IOCTL_SET_CODEC_PROFILE failed");
        return -1;
      }

      DEBUG_PRINT ("\n Set the VEN_IOCTL_SET_CODEC_PROFILE High");
      ioctl_msg.inputparam = &profilelevel;
      ioctl_msg.outputparam = NULL;
      profilelevel.level = VEN_LEVEL_H264_3p1;
      if (ioctl (init_decode->video_driver_fd,VEN_IOCTL_SET_PROFILE_LEVEL,
             (void*)&ioctl_msg) < 0)
      {
        DEBUG_PRINT("\n Set VEN_IOCTL_SET_CODEC_PROFILE failed");
        return -1;
      }
    }
  }

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



  ioctl_msg.inputparam = NULL;
  ioctl_msg.outputparam = &init_decode->input_buffer;

  if (ioctl (init_decode->video_driver_fd,VEN_IOCTL_GET_INPUT_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", \
              (int)init_decode->input_buffer.datasize,\
              (int)init_decode->input_buffer.mincount,\
              (int)init_decode->input_buffer.actualcount);


  ioctl_msg.inputparam = &init_decode->input_buffer;
  ioctl_msg.outputparam = NULL;
  init_decode->input_buffer.actualcount = init_decode->input_buffer.mincount + 2;

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


  DEBUG_PRINT("\n Query output bufffer requirements");
  ioctl_msg.inputparam = NULL;
  ioctl_msg.outputparam = &init_decode->output_buffer;

  if (ioctl (init_decode->video_driver_fd,VEN_IOCTL_GET_OUTPUT_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", \
              (int)init_decode->output_buffer.datasize,\
              (int)init_decode->output_buffer.mincount,\
              (int)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 ( unsigned int  buffer_dir,
                  struct video_encoder_context *encoder_context
                 )
{
  unsigned int buffercount = 0,i=0;
  struct venc_bufferpayload **ptemp = NULL;

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

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

    for (i=0;i<buffercount;i++)
    {
      if (ptemp [i])
      {
        if (ptemp [i]->fd != -1)
        {
          munmap ( ptemp [i]->pbuffer,ptemp [i]->maped_size);
          ptemp [i]->pbuffer = NULL;
          close (ptemp [i]->fd);
        }
        free (ptemp [i]);
        ptemp [i] = NULL;
      }
    }
    free (encoder_context->ptr_inputbuffer);
    encoder_context->ptr_inputbuffer = NULL;
  }
  else if ( buffer_dir == OUTPUT_BUFFER && encoder_context->ptr_outputbuffer )
  {
    buffercount = encoder_context->output_buffer.actualcount;
    ptemp = encoder_context->ptr_outputbuffer;

    if (ptemp)
    {
      for (i=0;i<buffercount;i++)
      {
        if (ptemp [i])
        {
          if (ptemp [i]->fd != -1)
          {
            munmap ( ptemp [i]->pbuffer,ptemp [i]->maped_size);
            ptemp [i]->pbuffer = NULL;
            close (ptemp [i]->fd);
          }
          free (ptemp [i]);
          ptemp [i] = NULL;
        }
      }
      free (ptemp);
      encoder_context->ptr_outputbuffer = NULL;
    }
  }

  return 1;
}

int allocate_buffer ( unsigned int buffer_dir,
                      struct video_encoder_context *encoder_context
                    )
{
  struct venc_bufferpayload **ptemp = NULL;
  struct venc_ioctl_msg ioctl_msg = {NULL,NULL};
  unsigned int buffercount = 0,i=0,alignedsize=0;
  unsigned int buffersize = 0;

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

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

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

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

  }
  else
  {
    DEBUG_PRINT ("\nallocate_buffer: Wrong buffer directions");
    return -1;
  }

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

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


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

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

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

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

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

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

    ioctl_msg.inputparam  = ptemp [i];
    ioctl_msg.outputparam = NULL;

    if (buffer_dir == OUTPUT_BUFFER)
    {
      if (ioctl (encoder_context->video_driver_fd,VEN_IOCTL_SET_OUTPUT_BUFFER,
           &ioctl_msg) < 0)
      {
        DEBUG_PRINT ("\nallocate_buffer: Set Output Buffer IOCTL failed");
        return -1;
      }
    }
    else
    {
      if (ioctl (encoder_context->video_driver_fd,VEN_IOCTL_SET_INPUT_BUFFER,
           &ioctl_msg) < 0)
      {
        DEBUG_PRINT ("\nallocate_buffer: Set input Buffer IOCTL failed");
        return -1;
      }
    }

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



int start_encoding (struct video_encoder_context *encoder_context)
{
  struct venc_ioctl_msg ioctl_msg = {NULL,NULL};
  struct venc_buffer enc_buffer;
  unsigned int i = 0;
  unsigned int data_len =0;


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

  if (ioctl (encoder_context->video_driver_fd,VEN_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 (&encoder_context->sem_synchronize);

  /*Push output Buffers*/
  i = 0;
  while (i < encoder_context->output_buffer.actualcount)
  {
    enc_buffer.clientdata = (void *)encoder_context->ptr_outputbuffer [i];
    enc_buffer.flags = 0;
    enc_buffer.size = encoder_context->ptr_outputbuffer [i]->nsize;
    enc_buffer.len = 0;
    enc_buffer.ptrbuffer = encoder_context->ptr_outputbuffer [i]->pbuffer;
    enc_buffer.offset = 0;
    enc_buffer.timestamp = 0;

    DEBUG_PRINT ("\n Client Data on output = %p",(void *)enc_buffer.clientdata);
    ioctl_msg.inputparam = &enc_buffer;
    ioctl_msg.outputparam = NULL;

    if (ioctl (encoder_context->video_driver_fd,
           VEN_IOCTL_CMD_FILL_OUTPUT_BUFFER,&ioctl_msg) < 0)
    {
      DEBUG_PRINT("\n fill output frame failed");
      return -1;
    }
    i++;
  }


  /*push input buffers*/
  i = 0;
  while (i < encoder_context->input_buffer.actualcount)
  {
    DEBUG_PRINT("\n Read  Frame from File");

    enc_buffer.clientdata = (void *)encoder_context->ptr_inputbuffer [i];
    enc_buffer.flags = 0;
    enc_buffer.size = encoder_context->ptr_inputbuffer [i]->nsize;
    enc_buffer.len = 0;
    enc_buffer.ptrbuffer = encoder_context->ptr_inputbuffer [i]->pbuffer;
    enc_buffer.offset = 0;
    enc_buffer.timestamp = total_frames *
                ((encoder_context->fps_den * 1000000)/encoder_context->fps_num);
    enc_buffer.len = (encoder_context->input_height *
                     encoder_context->input_width *3)/2;
    data_len = read_frame ( enc_buffer.ptrbuffer,
                            enc_buffer.len,
                            encoder_context->inputBufferFile);
    if (data_len == 0)
    {
      DEBUG_PRINT("\n Length is zero error");
      return -1;
    }
    enc_buffer.len = data_len;
    DEBUG_PRINT("\n Read  Frame from File szie = %d",(int)data_len);

    DEBUG_PRINT ("\n Client Data on output = %p",(void *)enc_buffer.clientdata);
    ioctl_msg.inputparam = &enc_buffer;
    ioctl_msg.outputparam = NULL;

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

  return 1;
}

int stop_encoding  (struct video_encoder_context *encoder_context)
{
  struct venc_ioctl_msg ioctl_msg = {NULL,NULL};
  struct venc_bufferflush buffer_flush;

  if (encoder_context == NULL)
  {
    return -1;
  }
  buffer_flush.flush_mode = VEN_FLUSH_INPUT;
  ioctl_msg.inputparam = &buffer_flush;
  ioctl_msg.outputparam = NULL;

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

  buffer_flush.flush_mode = VEN_FLUSH_OUTPUT;
  ioctl_msg.inputparam = &buffer_flush;
  ioctl_msg.outputparam = NULL;

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

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

int deinit_encoder (struct video_encoder_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_encoder_context *encoder_context = NULL;
   struct video_msgq *queueitem = NULL;
   struct venc_ioctl_msg ioctl_msg = {NULL,NULL};
   struct venc_bufferpayload *tempbuffer = NULL;
   struct venc_buffer enc_buffer;
   unsigned int data_len =0;


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

   /* Thread function which will accept commands from async thread
    * or main thread
   */
   while (1)
   {
      queueitem = queue_get_cmd (&encoder_context ->queue_context);
      if (queueitem != NULL)
      {
        switch (queueitem->cmd)
        {
        case VEN_MSG_START:
          DEBUG_PRINT("\n recived start done command");
            sem_post (&encoder_context->sem_synchronize);
          break;

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

        case VEN_MSG_INPUT_BUFFER_DONE:

          tempbuffer = (struct venc_bufferpayload *)queueitem->clientdata;
          if (tempbuffer == NULL)
          {
            DEBUG_PRINT("\n FATAL ERROR input buffer address is bad");
            sem_post (&encoder_context->sem_synchronize);
            break;
          }
          tempbuffer->filled_len = (encoder_context->input_height *
                             encoder_context->input_width *3)/2;

          data_len = read_frame ( tempbuffer->pbuffer,
                                  tempbuffer->filled_len,
                                  encoder_context->inputBufferFile);

          if (data_len == 0)
          {
            DEBUG_PRINT ("\n End of stream reached");
            sem_post (&encoder_context->sem_synchronize);
            break;
          }
          enc_buffer.clientdata = (void *)tempbuffer;
          enc_buffer.flags = 0;
          enc_buffer.ptrbuffer = tempbuffer->pbuffer;
          enc_buffer.size = tempbuffer->nsize;
          enc_buffer.len = tempbuffer->filled_len;
          enc_buffer.offset = 0;
          enc_buffer.timestamp = total_frames *
                ((encoder_context->fps_den * 1000000)/encoder_context->fps_num);

          /*TODO: Time stamp needs to be updated*/
          ioctl_msg.inputparam = &enc_buffer;
          ioctl_msg.outputparam = NULL;
          total_frames++;
          if (ioctl(encoder_context->video_driver_fd,VEN_IOCTL_CMD_ENCODE_FRAME,
               &ioctl_msg) < 0)
          {
            DEBUG_PRINT("\n Decoder frame failed");
            sem_post (&encoder_context->sem_synchronize);
          }
          DEBUG_PRINT("\n Input buffer done send next buffer current value = %d",\
                      total_frames);
          break;

        case VEN_MSG_OUTPUT_BUFFER_DONE:

          tempbuffer = (struct venc_bufferpayload *)queueitem->clientdata;
          if (tempbuffer == NULL)
          {
            DEBUG_PRINT("\n FATAL ERROR input buffer address is bad");
            sem_post (&encoder_context->sem_synchronize);
            break;
          }

         if (encoder_context->outputBufferFile != NULL)
         {
           fwrite (tempbuffer->pbuffer,1,tempbuffer->filled_len,
                encoder_context->outputBufferFile);
         }


         DEBUG_PRINT("\n recieved output buffer consume outbuffer");
         DEBUG_PRINT("\nValues outputbuffer->bufferaddr = %p",\
                     tempbuffer->pbuffer);
         enc_buffer.clientdata = (void *)tempbuffer;
         enc_buffer.flags = 0;
         enc_buffer.size = tempbuffer->nsize;
         enc_buffer.len = 0;
         enc_buffer.ptrbuffer = tempbuffer->pbuffer;
         enc_buffer.offset = 0;
         enc_buffer.timestamp = 0;

         ioctl_msg.inputparam = &enc_buffer;
         ioctl_msg.outputparam = NULL;

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

         break;

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

        case VEN_MSG_FLUSH_OUPUT_DONE:
          DEBUG_PRINT("\n Flush output complete");
          sem_post (&encoder_context->sem_synchronize);
          break;
         }
      }
      else
      {
        DEBUG_PRINT("\n Error condition recieved NULL from Queue");
      }

      if (queueitem->cmd == VEN_MSG_STOP)
      {
        DEBUG_PRINT("\n Playback has ended thread will exit");
        return NULL;
      }
   }
}

static void* async_thread (void *context)
{
  struct video_encoder_context *encoder_context = NULL;
  struct video_msgq queueitem ;
  struct venc_msg venc_msg;
  struct venc_bufferpayload *tempbuffer = NULL;
  struct venc_ioctl_msg ioctl_msg = {NULL,NULL};
  int result = -1;

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

  while (1)
  {
    ioctl_msg.inputparam = NULL;
    ioctl_msg.outputparam = (void*)&venc_msg;
    DEBUG_PRINT ("\n Sizeof venc_msginfo = %d ",sizeof (venc_msg));
    DEBUG_PRINT("\n Address of Venc msg in async thread %p",\
                ioctl_msg.outputparam);
    if (ioctl (encoder_context->video_driver_fd,VEN_IOCTL_CMD_READ_NEXT_MSG,\
         (void*)&ioctl_msg) < 0)
    {
      DEBUG_PRINT("\n Error in ioctl read next msg");
    }
    else
    {
      switch (venc_msg.msgcode)
      {
      case VEN_MSG_START:
      case VEN_MSG_STOP:
      case VEN_MSG_INDICATION:
        DEBUG_PRINT("\nSTOP/START Indiacation");
        queueitem.cmd = venc_msg.msgcode;
        queueitem.status = venc_msg.statuscode;
        queueitem.clientdata = NULL;
        break;

      case VEN_MSG_INPUT_BUFFER_DONE:
        DEBUG_PRINT("\nINPUT buffer done Indiacation");
        queueitem.cmd = venc_msg.msgcode;
        queueitem.status = venc_msg.statuscode;
        queueitem.clientdata = (void *)venc_msg.buf.clientdata;
        DEBUG_PRINT("\nInput Client data pointer is %p",queueitem.clientdata);
        tempbuffer = (struct venc_bufferpayload *) queueitem.clientdata;
        DEBUG_PRINT ("\n Input Address of tempbuffer %p",tempbuffer);
        tempbuffer->filled_len = venc_msg.buf.len;
        DEBUG_PRINT ("\n Input value of tempbuffer tempbuffer->filled_len %d",(int)tempbuffer->filled_len);
        break;
      case VEN_MSG_OUTPUT_BUFFER_DONE:
        DEBUG_PRINT("\nOUPUT buffer done Indiacation");
        queueitem.cmd = venc_msg.msgcode;
        queueitem.status = venc_msg.statuscode;
        queueitem.clientdata = (void *)venc_msg.buf.clientdata;
        DEBUG_PRINT("\nOutput Client data pointer is %p",queueitem.clientdata);
        tempbuffer = (struct venc_bufferpayload *) queueitem.clientdata;
        DEBUG_PRINT ("\n Output Address of tempbuffer %p",tempbuffer);
        tempbuffer->filled_len = venc_msg.buf.len;
        DEBUG_PRINT ("\n Output value of tempbuffer tempbuffer->filled_len %d",(int)tempbuffer->filled_len);
        break;

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

      if (result == -1)
      {
        DEBUG_PRINT("\n FATAL ERROR WITH Queue");
      }
    }
    if (venc_msg.msgcode == VEN_MSG_STOP)
    {
      /*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 = %d",dataptr,(int)length);
    return 0;
  }

  pthread_mutex_lock(&read_lock);
  bytes_read = fread(&dataptr[readOffset],1,length,inputBufferFile);
  pthread_mutex_unlock(&read_lock);

  return bytes_read;
}
