/*
 * h264 SPS / PPS Parser
 *
 * Based on h264.c from ffmpeg (www.ffmpeg.org)
 *
 * Copyright (c) 2003 Michael Niedermayer <michaelni@gmx.at>
 *
 * This file is 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
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include "tvheadend.h"
#include "parsers.h"
#include "parser_h264.h"
#include "bitstream.h"
#include "service.h"

/**
 * H.264 parser, nal escaper
 */
void *
h264_nal_deescape(bitstream_t *bs, const uint8_t *data, int size)
{
  int rbsp_size, i;
  uint8_t *d = malloc(size);
  bs->rdata = d;

  /* Escape 0x000003 into 0x0000 */

  rbsp_size = 0;
  for(i = 1; i < size; i++) {
    if(i + 2 < size && data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 3) {
      d[rbsp_size++] = 0;
      d[rbsp_size++] = 0;
      i += 2;
    } else {
      d[rbsp_size++] = data[i];
    }
  }

  bs->offset = 0;
  bs->len = rbsp_size * 8;
  return d;
}


/**
 * Level to CPB size table
 */

static const int h264_lev2cpbsize[][2] = {
  {10, 175},
  {11, 500},
  {12, 1000},
  {13, 2000},
  {20, 2000},
  {21, 4000},
  {22, 4000},
  {30, 10000},
  {31, 14000},
  {32, 20000},
  {40, 25000},
  {41, 62500},
  {42, 62500},
  {50, 135000},
  {51, 240000},
  {-1, -1},
};



typedef struct h264_private {

  struct {
    int cbpsize;
    int16_t width;
    int16_t height;
    int16_t max_frame_num_bits;
    char mbs_only_flag;
    char aff;
    char fixed_rate;
    int units_in_tick;
    int time_scale;
    uint16_t aspect_num;
    uint16_t aspect_den;
  } sps[256];
  
  struct {
    int sps;
  } pps[256];

} h264_private_t;



static const uint16_t h264_aspect[17][2] = {
 {0, 1},
 {1, 1},
 {12, 11},
 {10, 11},
 {16, 11},
 {40, 33},
 {24, 11},
 {20, 11},
 {32, 11},
 {80, 33},
 {18, 11},
 {15, 11},
 {64, 33},
 {160,99},
 {4, 3},
 {3, 2},
 {2, 1},
};


static uint32_t
gcd(uint32_t a, uint32_t b)
{
  uint32_t r;
  if(a < b) {
    r = a;
    a = b;
    b = r;
  }

  while((r = a % b) != 0) {
    a = b;
    b = r;
  }
  return b;
}


static int 
decode_vui(h264_private_t *p, bitstream_t *bs, int sps_id)
{
  p->sps[sps_id].aspect_num = 0;
  p->sps[sps_id].aspect_den = 1;

  if(read_bits1(bs)) {
    int aspect = read_bits(bs, 8);

    if(aspect == 255) {
      uint16_t num = read_bits(bs, 16);
      uint16_t den = read_bits(bs, 16);
      p->sps[sps_id].aspect_num = num;
      p->sps[sps_id].aspect_den = den;
    } else if(aspect < 17) {
      p->sps[sps_id].aspect_num =  h264_aspect[aspect][0];
      p->sps[sps_id].aspect_den =  h264_aspect[aspect][1];
    }
  }

  if(read_bits1(bs)){      /* overscan_info_present_flag */
    read_bits1(bs);      /* overscan_appropriate_flag */
  }

  if(read_bits1(bs)){      /* video_signal_type_present_flag */
    read_bits(bs, 3);    /* video_format */
    read_bits1(bs);      /* video_full_range_flag */
    if(read_bits1(bs)){  /* colour_description_present_flag */
      read_bits(bs, 8); /* colour_primaries */
      read_bits(bs, 8); /* transfer_characteristics */
      read_bits(bs, 8); /* matrix_coefficients */
    }
  }

  if(read_bits1(bs)){      /* chroma_location_info_present_flag */
    read_golomb_ue(bs);  /* chroma_sample_location_type_top_field */
    read_golomb_ue(bs);  /* chroma_sample_location_type_bottom_field */
  }

  if(!read_bits1(bs)) /* We need timing info */
    return 0;
    

  p->sps[sps_id].units_in_tick = read_bits(bs, 32);
  p->sps[sps_id].time_scale    = read_bits(bs, 32);
  p->sps[sps_id].fixed_rate    = read_bits1(bs);
  return 0;
}



static void 
decode_scaling_list(bitstream_t *bs, int size)
{
  int i, last = 8, next = 8;
  if(!read_bits1(bs))
    return; /* matrix not written */

  for(i=0;i<size;i++){
    if(next)
      next = (last + read_golomb_se(bs)) & 0xff;
    if(!i && !next)
      break;  /* matrix not written */
    last = next ? next : last;
  }
}


int
h264_decode_seq_parameter_set(elementary_stream_t *st, bitstream_t *bs)
{
  int profile_idc, level_idc, poc_type;
  unsigned int sps_id, tmp, i, width, height;
  int cbpsize = -1;
  h264_private_t *p;

  if((p = st->es_priv) == NULL)
    p = st->es_priv = calloc(1, sizeof(h264_private_t));

  profile_idc= read_bits(bs, 8);
  read_bits1(bs);   //constraint_set0_flag
  read_bits1(bs);   //constraint_set1_flag
  read_bits1(bs);   //constraint_set2_flag
  read_bits1(bs);   //constraint_set3_flag
  read_bits(bs, 4); // reserved
  level_idc= read_bits(bs, 8);
  sps_id= read_golomb_ue(bs);

  if(sps_id > 255)
    return -1;

  i = 0;
  while(h264_lev2cpbsize[i][0] != -1) {
    if(h264_lev2cpbsize[i][0] >= level_idc) {
      cbpsize = h264_lev2cpbsize[i][1];
      break;
    }
    i++;
  }
  if(cbpsize < 0)
    return -1;

  p->sps[sps_id].cbpsize = cbpsize * 125; /* Convert from kbit to bytes */


  if(profile_idc >= 100){ //high profile
    if(read_golomb_ue(bs) == 3) //chroma_format_idc
      read_bits1(bs);  //residual_color_transform_flag
    read_golomb_ue(bs);  //bit_depth_luma_minus8
    read_golomb_ue(bs);  //bit_depth_chroma_minus8
    read_bits1(bs);

    if(read_bits1(bs)) {
      /* Scaling matrices */
      decode_scaling_list(bs, 16);
      decode_scaling_list(bs, 16);
      decode_scaling_list(bs, 16);
      decode_scaling_list(bs, 16);
      decode_scaling_list(bs, 16);
      decode_scaling_list(bs, 16);

      decode_scaling_list(bs, 64);
      decode_scaling_list(bs, 64);

    }
  }

  p->sps[sps_id].max_frame_num_bits = read_golomb_ue(bs) + 4;
  poc_type= read_golomb_ue(bs);
 
  if(poc_type == 0){ //FIXME #define
    read_golomb_ue(bs);
  } else if(poc_type == 1){//FIXME #define
    read_bits1(bs);
    read_golomb_se(bs);
    read_golomb_se(bs);
    tmp = read_golomb_ue(bs); /* poc_cycle_length */
    for(i = 0; i < tmp; i++)
      read_golomb_se(bs);

  }else if(poc_type != 2){
    /* Illegal poc */
    return -1;
  }

  tmp = read_golomb_ue(bs);

  read_bits1(bs);

  width = read_golomb_ue(bs) + 1; /* mb width */
  height = read_golomb_ue(bs) + 1; /* mb height */

  p->sps[sps_id].width = width * 16;
  p->sps[sps_id].height = height * 16;

  p->sps[sps_id].mbs_only_flag = read_bits1(bs);
  if(!p->sps[sps_id].mbs_only_flag)
    p->sps[sps_id].aff = read_bits1(bs);

  read_bits1(bs);

  /* CROP */
  if(read_bits1(bs)){
    tmp = read_golomb_ue(bs);
    tmp = read_golomb_ue(bs);
    tmp = read_golomb_ue(bs);
    tmp = read_golomb_ue(bs);
  }

  if(read_bits1(bs)) {
    decode_vui(p, bs, sps_id);
    return 0;
  } else {
    return -1;
  }
}


int
h264_decode_pic_parameter_set(elementary_stream_t *st, bitstream_t *bs)
{
  h264_private_t *p;
  int pps_id, sps_id;

  if((p = st->es_priv) == NULL)
    p = st->es_priv = calloc(1, sizeof(h264_private_t));
  
  pps_id = read_golomb_ue(bs);
  if(pps_id > 255)
    return 0;
  sps_id = read_golomb_ue(bs);
  if(sps_id > 255)
    return -1;

  p->pps[pps_id].sps = sps_id;
  return 0;
}


int
h264_decode_slice_header(elementary_stream_t *st, bitstream_t *bs, int *pkttype,
			 int *isfield)
{
  h264_private_t *p;
  unsigned int slice_type, pps_id, sps_id;

  if((p = st->es_priv) == NULL)
    return -1;

  read_golomb_ue(bs); /* first_mb_in_slice */
  slice_type = read_golomb_ue(bs);
  
  if(slice_type > 4)
    slice_type -= 5;  /* Fixed slice type per frame */

  switch(slice_type) {
  case 0:
    *pkttype = PKT_P_FRAME;
    break;
  case 1:
    *pkttype = PKT_B_FRAME;
    break;
  case 2:
    *pkttype = PKT_I_FRAME;
    break;
  default:
    return -1;
  }

  pps_id = read_golomb_ue(bs);
  if(pps_id > 255)
    return -1;

  sps_id = p->pps[pps_id].sps;
  if(p->sps[sps_id].max_frame_num_bits == 0)
    return -1;

  skip_bits(bs, p->sps[sps_id].max_frame_num_bits);

  int field = 0;
  int timebase = 180000;

  if(!p->sps[sps_id].mbs_only_flag) {
    if(read_bits1(bs)) {
      read_bits1(bs); // bottom field
      field = 1;
    }
  }

  *isfield = field;

  int d = 0;
  if(p->sps[sps_id].time_scale != 0)
    d = timebase * p->sps[sps_id].units_in_tick / p->sps[sps_id].time_scale;

  if(p->sps[sps_id].cbpsize != 0)
    st->es_vbv_size = p->sps[sps_id].cbpsize;

  st->es_vbv_delay = -1;

  if(p->sps[sps_id].width && p->sps[sps_id].height && d && !st->es_buf.sb_err)
    parser_set_stream_vparam(st, p->sps[sps_id].width,
                             p->sps[sps_id].height *
                             (2 - p->sps[sps_id].mbs_only_flag),
                             d);

  if(p->sps[sps_id].aspect_num && p->sps[sps_id].aspect_den) {

    int w = p->sps[sps_id].aspect_num * st->es_width;
    int h = p->sps[sps_id].aspect_den * st->es_height;

    if(w && h) { 
      int d = gcd(w, h);
      st->es_aspect_num = w / d;
      st->es_aspect_den = h / d;
    }

  } else {
    st->es_aspect_num = 0;
    st->es_aspect_den = 1;
  }

  return 0;
}
