/* -*- 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"

//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");
    exit(1);
  }

  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");
    switch((int)st) {
    case AVERROR_UNKNOWN     : printf("unknown error\n");
      break;
    case AVERROR_IO          : printf("i/o error\n");
      break;
    case AVERROR_NUMEXPECTED : printf("number syntax expected in filename\n");
      break;
    case AVERROR_INVALIDDATA : printf("invalid data found\n");
      break;
    case AVERROR_NOMEM       : printf("not enough memory\n");
      break;
    case AVERROR_NOFMT       : printf("unknown format\n");
      break;
    case AVERROR_NOTSUPP     : printf("operation not supported\n");
      break;
    }
    exit(1);
  }

  enc_codec = avcodec_find_encoder(CODEC_ID_MPEG2VIDEO);
  if(!enc_codec) {
    printf("Unsupported codec for output stream\n");
    exit(1);
  }
  avcodec_get_context_defaults(&st->codec);
  ecc = &st->codec;
  ecc->codec_id = CODEC_ID_MPEG2VIDEO; 
  ecc->bit_rate = 8192*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) {
    printf("Error while opening codec for stream\n");
    exit(1);
  }

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

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

  dump_format(efc, 0, filename, 1);

  if(av_write_header(efc) < 0) {
    fprintf(stderr, "Could not write header for output file \n");
    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;
  printf("Video decoding\n");
  
  /* find the dvvideo decoder */
  deccodec = avcodec_find_decoder(CODEC_ID_DVVIDEO);
  if (!deccodec) {
    fprintf(stderr, "codec not found\n");
    exit(1);
  }
  
  dcc= avcodec_alloc_context();
  
  /* open it */
  if (avcodec_open(dcc, deccodec) < 0) {
    fprintf(stderr, "could not open codec\n");
    exit(1);
  }
}

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

void MovEncoder::encode(DVFrame *dvframe)
{ 
  int ret;
  AVFrame *rawframe = avcodec_alloc_frame();

  ///////////////////////// DECODE
  uint8_t *ptr;
  int got_picture = 1;
  int len;

  ptr = (uint8_t *)dvframe->frame;
  len = sizeof(dvframe->frame);

  ret = avcodec_decode_video(dcc,//&dfc->streams[0]->codec, 
                             rawframe, &got_picture, ptr, len);

  if(!ret) {
    printf("Decoder fuckup!\n");
    return;
  }
  
  ///////////////////////// ENCODE
  
  ret = avcodec_encode_video(ecc, video_buffer, VIDEO_BUFFER_SIZE, rawframe);
  
  if(!ret) {
    printf("MovEncoder fuckup!\n");
    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);
}