/*
 * LATM / AAC Parser
 *
 * Copyright (C) 2009 Andreas Öman
 *
 * Based on LATM Parser patch sent to ffmpeg-devel@
 * copyright (c) 2009 Paul Kendall <paul@kcbbs.gen.nz>
 *
 * 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 <assert.h>

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

typedef struct latm_private {

  int configured;
  int audio_mux_version_A;
  int frame_length_type;

  int sample_rate_index;
  int channel_config;

} latm_private_t;



static uint32_t
latm_get_value(bitstream_t *bs)
{
    return read_bits(bs, read_bits(bs, 2) * 8);
}


static void
read_audio_specific_config(elementary_stream_t *st, latm_private_t *latm,
			   bitstream_t *bs)
{
  int aot, sr;

  aot = read_bits(bs, 5);

  latm->sample_rate_index = read_bits(bs, 4);

  if(latm->sample_rate_index == 0xf)
    sr = read_bits(bs, 24);
  else
    sr = sri_to_rate(latm->sample_rate_index);

  if(sr == 0)
    return;

  st->es_frame_duration = 1024 * 90000 / sr;

  latm->channel_config = read_bits(bs, 4);

  if (aot == 5) { // AOT_SBR
    if (read_bits(bs, 4) == 0xf) {  // extensionSamplingFrequencyIndex
	skip_bits(bs, 24);
    }
    aot = read_bits(bs, 5);	// this is the main object type (i.e. non-extended)
  }

  if(aot != 2)
    return;

  skip_bits(bs, 1); //framelen_flag
  if(read_bits1(bs))  // depends_on_coder
    skip_bits(bs, 14);

  if(read_bits(bs, 1))  // ext_flag
     skip_bits(bs, 1);  // ext3_flag
}


static void
read_stream_mux_config(elementary_stream_t *st, latm_private_t *latm, bitstream_t *bs)
{
  int audio_mux_version = read_bits(bs, 1);
  latm->audio_mux_version_A = 0;
  if(audio_mux_version)                       // audioMuxVersion
    latm->audio_mux_version_A = read_bits(bs, 1);
  
  if(latm->audio_mux_version_A)
    return;

  if(audio_mux_version)
    latm_get_value(bs);                  // taraFullness

  skip_bits(bs, 1);                         // allStreamSameTimeFraming = 1
  skip_bits(bs, 6);                         // numSubFrames = 0
  skip_bits(bs, 4);                         // numPrograms = 0

  // for each program (which there is only on in DVB)
  skip_bits(bs, 3);                         // numLayer = 0
    
  // for each layer (which there is only on in DVB)
  if(!audio_mux_version)
    read_audio_specific_config(st, latm, bs);
  else {
    return;
#if 0
    uint32_t ascLen = latm_get_value(bs);
    abort(); // ascLen -= read_audio_specific_config(filter, gb);
    skip_bits(bs, ascLen);
#endif
  }

  // these are not needed... perhaps
  latm->frame_length_type = read_bits(bs, 3);
  switch(latm->frame_length_type) {
  case 0:
    read_bits(bs, 8);
    break;
  case 1:
    read_bits(bs, 9);
    break;
  case 3:
  case 4:
  case 5:
    read_bits(bs, 6);                 // celp_table_index
    break;
  case 6:
  case 7:
    read_bits(bs, 1);                 // hvxc_table_index
    break;
  }

  if(read_bits(bs, 1)) {                   // other data?
    if(audio_mux_version)
      latm_get_value(bs);              // other_data_bits
    else {
      int esc;
      do {
	esc = read_bits(bs, 1);
	skip_bits(bs, 8);
      } while (esc);
    }
  }

  if(read_bits(bs, 1))                   // crc present?
    skip_bits(bs, 8);                     // config_crc
  latm->configured = 1;
}

/**
 * Parse AAC LATM
 */
th_pkt_t *
parse_latm_audio_mux_element(service_t *t, elementary_stream_t *st, 
			     const uint8_t *data, int len)
{
  latm_private_t *latm;
  bitstream_t bs, out;
  int slot_len, tmp, i;
  uint8_t *buf;

  init_rbits(&bs, data, len * 8);

  if((latm = st->es_priv) == NULL)
    latm = st->es_priv = calloc(1, sizeof(latm_private_t));

  if(!read_bits1(&bs))
    read_stream_mux_config(st, latm, &bs);

  if(!latm->configured)
    return NULL;

  if(latm->frame_length_type != 0)
    return NULL;

  slot_len = 0;
  do {
    tmp = read_bits(&bs, 8);
    slot_len += tmp;
  } while (tmp == 255);


  if(slot_len * 8 > remaining_bits(&bs))
    return NULL;

  if(st->es_curdts == PTS_UNSET)
    return NULL;

  th_pkt_t *pkt = pkt_alloc(NULL, slot_len + 7, st->es_curdts, st->es_curdts);

  pkt->pkt_commercial = t->s_tt_commercial_advice;
  pkt->pkt_duration = st->es_frame_duration;
  pkt->pkt_sri = latm->sample_rate_index;
  pkt->pkt_channels = latm->channel_config;

  /* 7 bytes of ADTS header */
  init_wbits(&out, pktbuf_ptr(pkt->pkt_payload), 56);

  put_bits(&out, 0xfff, 12); // Sync marker
  put_bits(&out, 0, 1);      // ID 0 = MPEG 4
  put_bits(&out, 0, 2);      // Layer
  put_bits(&out, 1, 1);      // Protection absent
  put_bits(&out, 2, 2);      // AOT
  put_bits(&out, latm->sample_rate_index, 4);
  put_bits(&out, 1, 1);      // Private bit
  put_bits(&out, latm->channel_config, 3);
  put_bits(&out, 1, 1);      // Original
  put_bits(&out, 1, 1);      // Copy
  
  put_bits(&out, 1, 1);      // Copyright identification bit
  put_bits(&out, 1, 1);      // Copyright identification start
  put_bits(&out, slot_len, 13); 
  put_bits(&out, 0, 11);     // Buffer fullness
  put_bits(&out, 0, 2);      // RDB in frame

  assert(remaining_bits(&out) == 0);

  /* AAC RDB */
  buf = pktbuf_ptr(pkt->pkt_payload) + 7;
  for(i = 0; i < slot_len; i++)
    *buf++ = read_bits(&bs, 8);

  st->es_curdts += st->es_frame_duration;

  return pkt;
}
