/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/***************************************************************************
 *            mov_encoder_writer.cc
 *
 *  Sun May 22 12:51:36 CEST 2005
 *  Copyright  2005 Bent Bisballe
 *  deva@aasimon.org
 ****************************************************************************/

/*
 *    This file is part of MIaV.
 *
 *    MIaV 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.
 *
 *    MIaV 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 MIaV; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
 */

/*
 * $Id$
 */

/*
 * $Log$
 * Revision 1.12  2005/07/22 15:59:39  deva
 * *** empty log message ***
 *
 * Revision 1.11  2005/07/09 16:23:15  deva
 * Added audio.
 *
 * Revision 1.10  2005/07/07 12:42:19  deva
 * *** empty log message ***
 *
 * Revision 1.9  2005/07/05 23:15:16  deva
 * *** empty log message ***
 *
 * Revision 1.8  2005/06/30 10:04:35  deva
 * *** empty log message ***
 *
 * Revision 1.7  2005/06/19 20:04:43  deva
 * ImgEncoder now uses the file class for output, through jpeg_mem_dest.
 *
 * Revision 1.6  2005/06/16 21:28:57  deva
 * Rewrote thread object
 * Fixed bug in mov_encoder (pushed read_sem too many times, whihc lead to
 * growing server queue)
 *
 * Revision 1.5  2005/06/14 18:58:35  deva
 * *** empty log message ***
 *
 * Revision 1.4  2005/06/14 12:29:40  deva
 * Incorporated the use of the Info object everywhere... also using the log functionality.
 *
 * Revision 1.3  2005/05/26 21:32:39  deva
 * *** empty log message ***
 *
 * Revision 1.2  2005/05/26 12:48:36  deva
 * *** empty log message ***
 *
 * Revision 1.1  2005/05/22 15:49:22  deva
 * Added multithreaded encoding support.
 *
 */
#include <config.h>
#include "mov_encoder_writer.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <pthread.h>
#include <semaphore.h>

#include <errno.h>

#include <string>
using namespace std;

#include "miav_config.h"

#include <time.h>

MovEncoderWriter::MovEncoderWriter(const char* cpr, 
                                   FramePriorityQueue *q, 
                                   sem_t *s,
                                   pthread_mutex_t *m, 
                                   Info *i)
{
  info = i;
  info->info("MovEncoderWriter");

  // Create path and filename
  char fname[256];
  string *server_root;
  char birthmonth[3];
  char date[32];

  // Get server root
  server_root = config->readString("server_movie_root");

  // Copy the bytes representing the birth month from the cpr
  // [dd][mm][yy]-[nn][nn]
  strncpy(birthmonth, &cpr[2], 2);
  birthmonth[2] = 0;

  // Create date (today) in [yyyy][mm][dd]
  struct tm *ltime;
  time_t t = time(NULL);
  ltime = localtime(&t);
  sprintf(date, "%.4d%.2d%.2d", 
          ltime->tm_year + 1900, 
          ltime->tm_mon, 
          ltime->tm_mday);

  sprintf(fname, "%s/%s/%s/%s-%s-", server_root->c_str(), birthmonth, cpr, cpr, date);

  file = new File(fname, "mpg", info);

  sem = s;
  queue = q;
  frame_number = 0;
  mutex = m;

  running = true;
}

MovEncoderWriter::~MovEncoderWriter()
{
  info->info("~MovEncoderWriter");
  delete file;
}

//#define WRITE_DV 1

void MovEncoderWriter::thread_main()
{
  info->info("MovEncoderWriter::run");

  Frame *frame;

#ifndef WRITE_DV

  write_header();

#endif/*WRITE_DV*/

  while(running) {
    sem_wait(sem);

    // Lock output mutex
    pthread_mutex_lock(mutex);
    frame = queue->top();
    if(frame && frame->number == frame_number) queue->pop();
    //    poolsize = queue->size();
    pthread_mutex_unlock(mutex);
    // Unlock output mutex

    int wrote = 0;
    while(frame && (frame->number == frame_number)) {
      
      int ret = 0;

#ifndef WRITE_DV

      if(frame->number%2 == 1) write_audio_header((unsigned short int)frame->size);
      else write_video_header((unsigned short int)frame->size);

      ret = file->Write(frame->data, frame->size);

#else/*WRITE_DV*/

      if(frame->number%2 == 0) ret = file->Write(frame->data, frame->size);

#endif/*WRITE_DV*/

      frame_number++;
      wrote ++;

      delete frame;

      if(ret == -1) {
        info->error("File write returned -1.");
        return;
      }

      // Lock output mutex
      pthread_mutex_lock(mutex);
      frame = queue->top();
      if(frame && frame->number == frame_number) queue->pop();
      //      poolsize = queue->size();
      pthread_mutex_unlock(mutex);
      // Unlock output mutex
    }
  }

  info->info("MovEncoderWriter::stop");
}

void MovEncoderWriter::write_header()
{
  // PACK
  char pack_start_code[] = {
    0x00, 0x00, 0x01, 0xBA,
  };

  file->Write(pack_start_code, sizeof(pack_start_code));

  char pack_data[] = {
    0x21,             // SCR-32 thru 30, marker bit
    0x00, 0x01,       // SCR-29 thru 15, marker bit
    0x80, 0xF5,       // SCR-14 thru 0, marker bit
    0x80, 0x1B, 0x83  // Marker bit, mux_rate, marker_bit
  };

  file->Write(pack_data, sizeof(pack_data));
  /*
  // SYSTEM
  char system_header_start_code[] = {
    0x00, 0x00, 0x01, 0xBB,
  };

  file->Write(system_header_start_code, sizeof(system_header_start_code));

  char system_data[] = {
    0x00, 0x0C,       // Header length
    0x80, 0x1B, 0x83, // Marker bit, rate_bound,marker_bit
    0x07,             // Audio bound, fixed_flag, CSPS_flag
    0xA1,             // system_audio_lock_flag, system_video_lock_flag
    0xFF,             // Reserved byte
    0xC0,             // Stream id (audio)
    //    0xC0, 0x20,       // '11', STD_buffer_bound_scale, STD_buffer_size_bound
    0xFF, 0xFF,       // '11', STD_buffer_bound_scale, STD_buffer_size_bound
    0xE0,             // Stream id (video)
    //    0xE0, 0x2E        // '11', STD_buffer_bound_scale, STD_buffer_size_bound
    0xFF, 0xFF        // '11', STD_buffer_bound_scale, STD_buffer_size_bound
  };

  file->Write(system_data, sizeof(system_data));

  char padding_header_start_code[] = {
    0x00, 0x00, 0x01, 0xBE
  };

  file->Write(padding_header_start_code, sizeof(padding_header_start_code));

  char padding_data[] = {
    0x00, 0x04,            // Padding length
    0x0F, 0xFF, 0xFF, 0xFF // Padding
  };

  file->Write(padding_data, sizeof(padding_data));
  */
}

void MovEncoderWriter::write_video_header(unsigned short int psize)
{
  // PES Header startcode
  char startcode[] = {
    0x00, 0x00, 0x01
  };

  // Video stream, index = 0
  char streamID[] = {
    0xE0
  };

  char packetsize[] = { 0x00, 0x00 };

  char stuffing_bytes[] = {
    0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0x0F
  };

  file->Write(startcode, sizeof(startcode));

  file->Write(streamID, sizeof(streamID));

  psize +=
    sizeof(stuffing_bytes);
  packetsize[0] = ((char*)&psize)[1];
  packetsize[1] = ((char*)&psize)[0];
  file->Write(packetsize, sizeof(packetsize));

  file->Write(stuffing_bytes, sizeof(stuffing_bytes));
}

void MovEncoderWriter::write_audio_header(unsigned short int psize)
{
  // PES Header startcode
  char startcode[] = {
    0x00, 0x00, 0x01
  };

  // Audio stream, index = 0
  char streamID[] = { 
    0xC0
  };

  char packetsize[] = { 0x00, 0x00 };

  char stuffing_bytes[] = {
    0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF
  };

  char std_buffer[] = {
    0x40, // STD_buffer_scale
    0x20  // STD_buffer_size
  };

  char PTS[] = {
    0x21,       // SCR-32 thru 30, marker bit
    0x00, 0x01, // SCR-29 thru 15, marker bit
    0xCE, 0x37  // SCR-14 thru 0, marker bit
  };

  file->Write(startcode, sizeof(startcode));

  file->Write(streamID, sizeof(streamID));

  psize += 
    sizeof(stuffing_bytes) +
    sizeof(std_buffer) +
    sizeof(PTS);
  packetsize[0] = ((char*)&psize)[1];
  packetsize[1] = ((char*)&psize)[0];
  file->Write(packetsize, sizeof(packetsize));

  file->Write(stuffing_bytes, sizeof(stuffing_bytes));

  file->Write(std_buffer, sizeof(std_buffer));

  file->Write(PTS, sizeof(PTS));

}