/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * RTVideoRec Realtime video recoder and encoder for Linux
 *
 * Copyright (C) 2004  Bent Bisballe
 * Copyright (C) 2004  B. Stultiens
 * Copyright (C) 2004  Koen Otter and Glenn van der Meyden
 *
 * This program 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.
 *
 * This program 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
 */

#include "mov_encoder.h"

#include "debug.h"
//av_alloc_format_context
//av_destruct_packet_nofree

MovEncoder::MovEncoder(const char *filename)
{
  //////////////////// GLOBAL INIT
	av_register_all();

  //////////////////// ENCODE INIT
  AVStream *st;
  AVCodec *enc_codec;

  if(!(efc = av_alloc_format_context())) {
    fprintf(stderr, "Could not alloc output format context\n"); fflush(stderr);
    exit(1);
  } 

  efc->oformat = guess_format("avi", NULL, NULL);
  //  efc->oformat = guess_format("mpeg", NULL, NULL);
  //efc->oformat = guess_format(NULL, filename, NULL);

  if(!(st = av_new_stream(efc, 0))) {
    fprintf(stderr, "Could not alloc stream\n"); fflush(stderr);
    switch((int)st) {
    case AVERROR_UNKNOWN     : fprintf(stderr, "unknown error\n"); fflush(stderr);
      break;
    case AVERROR_IO          : fprintf(stderr, "i/o error\n"); fflush(stderr);
      break;
    case AVERROR_NUMEXPECTED : fprintf(stderr, "number syntax expected in filename\n"); fflush(stderr);
      break;
    case AVERROR_INVALIDDATA : fprintf(stderr, "invalid data found\n"); fflush(stderr);
      break;
    case AVERROR_NOMEM       : fprintf(stderr, "not enough memory\n"); fflush(stderr);
      break;
    case AVERROR_NOFMT       : fprintf(stderr, "unknown format\n"); fflush(stderr);
      break;
    case AVERROR_NOTSUPP     : fprintf(stderr, "operation not supported\n"); fflush(stderr);
      break;
    }
    exit(1);
  }

  //enc_codec = avcodec_find_encoder(CODEC_ID_MPEG4);
  enc_codec = avcodec_find_encoder(CODEC_ID_MPEG2VIDEO);
  if(!enc_codec) {
    fprintf(stderr, "Unsupported codec for output stream\n"); fflush(stderr);
    exit(1);
  }
  avcodec_get_context_defaults(&st->codec);
  ecc = &st->codec;
  //ecc->codec_id = CODEC_ID_MPEG4; 
  ecc->codec_id = CODEC_ID_MPEG2VIDEO; 
  ecc->bit_rate = 8192*1000;
  //ecc->bit_rate = 4096*1000;
  ecc->bit_rate_tolerance = 8000*1000;
  ecc->frame_rate = 25; 
  ecc->frame_rate_base = 1;
  
  ecc->width = 720;
  ecc->height = 576;
  ecc->pix_fmt = PIX_FMT_YUV420P;
  ecc->gop_size = 0;
  ecc->mb_decision = FF_MB_DECISION_SIMPLE;
  ecc->qmin = 2;
  ecc->qmax = 31;
  ecc->mb_qmin = 2;
  ecc->mb_qmax = 31;
  ecc->max_qdiff = 3;
  ecc->qblur = 0.5;
  ecc->qcompress = 0.5;
  ecc->rc_eq = "tex^qComp";
  ecc->debug= 0;
  
  ecc->rc_override_count=0;
  ecc->rc_max_rate = 0;
  ecc->rc_min_rate = 0;
  ecc->rc_buffer_size = 0;
  ecc->rc_buffer_aggressivity = 1.0;
  ecc->rc_initial_cplx= 0;
  ecc->i_quant_factor = -0.8;
  ecc->b_quant_factor = 1.25;
  ecc->i_quant_offset = 0.8;
  ecc->b_quant_offset = 1.25;
  ecc->dct_algo = 0;
  ecc->idct_algo = 0;
  ecc->strict_std_compliance = 0;
  ecc->me_method = ME_EPZS;
  
  if(avcodec_open(&st->codec, enc_codec) < 0) {
    fprintf(stderr, "Error while opening codec for stream\n"); fflush(stderr);
    exit(1);
  }

  if(url_fopen(&efc->pb, filename, URL_RDWR) < 0) {
    fprintf(stderr, "Could not open '%s'\n", filename); fflush(stderr);
    exit(1);
  }

  if(av_set_parameters(efc, NULL) < 0) {
    fprintf(stderr, "%s: Invalid encoding parameters\n", filename); fflush(stderr);
    exit(1);
  }

  dump_format(efc, 0, filename, 1);

  if(av_write_header(efc) < 0) {
    fprintf(stderr, "Could not write header for output file \n"); fflush(stderr);
    exit(1);
  }

  video_buffer = (unsigned char *)av_malloc(VIDEO_BUFFER_SIZE);
  
  av_init_packet(&epkt);

  epkt.stream_index = efc->streams[0]->index;

  //  ecc = &efc->streams[0]->codec;

  //////////////////// DECODE INIT
  AVCodec *deccodec;
  //  AVCodecContext *dcc= NULL;
  fprintf(stderr, "Video decoding\n");
  
  // find the dvvideo decoder
  deccodec = avcodec_find_decoder(CODEC_ID_DVVIDEO);
  if (!deccodec) {
    fprintf(stderr, "codec not found\n"); fflush(stderr);
    exit(1);
  }
  
  dcc= avcodec_alloc_context();
  
  // open it
  if (avcodec_open(dcc, deccodec) < 0) {
    fprintf(stderr, "could not open codec\n"); fflush(stderr);
    exit(1);
  }
}

MovEncoder::~MovEncoder()
{
  av_free(video_buffer); FREE(video_buffer);
  url_fclose(&efc->pb);
}

void MovEncoder::encode(Frame *dvframe)
{
  encode_video(dvframe);
  //  encode_audio(dvframe);
}


void MovEncoder::encode_video(Frame *dvframe)
{
  int16_t audiobuffer[48000];
  int ret;
  AVFrame *rawframe = avcodec_alloc_frame();

  uint8_t *ptr;
  int got_picture = 1;
  int got_sound = 1;
  int len;

  ptr = (uint8_t *)dvframe->data;
  len = dvframe->size;

  ///////////////////////// DECODE VIDEO

  ret = avcodec_decode_video(dcc,//&dfc->streams[0]->codec, 
                             rawframe, &got_picture, ptr, len);
  if(!ret) {
    fprintf(stderr, "Decoder fuckup during video decoding!\n"); fflush(stderr);
    return;
  }

  ///////////////////////// ENCODE VIDEO
  
  ret = avcodec_encode_video(ecc, video_buffer, VIDEO_BUFFER_SIZE, rawframe);
  
  if(!ret) {
    fprintf(stderr, "MovEncoder fuckup during video encoding!\n"); fflush(stderr);
    return;
  }

  epkt.data = video_buffer;
  epkt.size = ret;

  if(ecc->coded_frame) epkt.pts = ecc->coded_frame->pts;

  if(ecc->coded_frame && ecc->coded_frame->key_frame) epkt.flags |= PKT_FLAG_KEY;

  av_write_frame(efc, &epkt);

  av_free(rawframe); FREE(rawframe);
}

#define INBUF_SIZE 48000
void MovEncoder::encode_audio(Frame *dvframe)
{
  uint8_t *decbuf;

  ///////////////////////// DECODE AUDIO
  {
    AVCodec *codec;
    AVCodecContext *c= NULL;
    int out_size, size, len;
    uint8_t inbuf[INBUF_SIZE + FF_INPUT_BUFFER_PADDING_SIZE], *inbuf_ptr;
 
    printf("Audio decoding\n");
     
    /* set end of buffer to 0 (this ensures that no overreading happens for damaged mpeg streams) */
    memset(inbuf + INBUF_SIZE, 0, FF_INPUT_BUFFER_PADDING_SIZE);
 
    /* find the mpeg audio decoder */
    codec = avcodec_find_decoder(CODEC_ID_DVAUDIO);
    if (!codec) {
      fprintf(stderr, "codec not found\n");
      exit(1);
    }
 
    c= avcodec_alloc_context();
 
    /* open it */
    if (avcodec_open(c, codec) < 0) {
      fprintf(stderr, "could not open codec\n");
      exit(1);
    }
     
    decbuf = (uint8_t*)malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE);
 
     
    /* decode until eof */
    size = dvframe->size;
    inbuf_ptr = dvframe->data;

    while (size > 0) {
      len = avcodec_decode_audio(c, (short *)decbuf, &out_size, 
                                 inbuf_ptr, size);
      if (len < 0) {
        fprintf(stderr, "Error while decoding\n");
        exit(1);
      }
      if (out_size > 0) {
        /* if a frame has been decoded, output it */
      }
      size -= len;
      inbuf_ptr += len;
    }
 
    avcodec_close(c);
    av_free(c);
  }
  ///////////////////////// ENCODE AUDIO
  {  
    char filename[]="audio.mp2";
    AVCodec *codec;
    AVCodecContext *c= NULL;
    int frame_size, i, j, out_size, outbuf_size;
    FILE *f;
    short *samples;
    float t, tincr;
    uint8_t *outbuf;
 
    printf("Audio encoding\n");
 
    /* find the MP2 encoder */
    codec = avcodec_find_encoder(CODEC_ID_MP2);
    if (!codec) {
      fprintf(stderr, "codec not found\n");
      exit(1);
    }
 
    c= avcodec_alloc_context();
     
    /* put sample parameters */
    c->bit_rate = 64000;
    c->sample_rate = 44100;
    c->channels = 2;
 
    /* open it */
    if (avcodec_open(c, codec) < 0) {
      fprintf(stderr, "could not open codec\n");
      exit(1);
    }
     
    /* the codec gives us the frame size, in samples */
    frame_size = c->frame_size;
    samples = (short int*)malloc(frame_size * 2 * c->channels);
    outbuf_size = 10000;
    outbuf = (uint8_t*)malloc(outbuf_size);
 
    f = fopen(filename, "a");
    if (!f) {
      fprintf(stderr, "could not open %s\n", filename);
      exit(1);
    }
         
    // encode the sample
    out_size = avcodec_encode_audio(c, outbuf, outbuf_size, (const short int*)decbuf);
    fwrite(outbuf, 1, out_size, f);

    fclose(f);
    free(outbuf);
    free(samples);
 
    avcodec_close(c);
    av_free(c);
  }

  // Don't free outputbuffer until this point!
  free(decbuf);
}