/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/***************************************************************************
 *            mov_encoder_thread.cc
 *
 *  Tue May 17 16:00:01 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.
 */
#include <config.h>
#include "mov_encoder_thread.h"
#include <errno.h>
#include "miav_config.h"

MovEncoderThread::MovEncoderThread(const char *clientip, const char *cpr, Info *i)
{
  info = i;
  info->info("MovEncoderThread");

  // Queue
  inputqueue = new ThreadSafeQueueFIFO();

  // Initialize read semaphore
	sem_init(&read_sem, 0, 0);

  video_output_queue = new ThreadSafeQueuePriority(info);
  audio_input_queue = new ThreadSafeQueuePriority(info);
  audio_output_queue = new ThreadSafeQueuePriority(info);

  info->info("video_output_queue: 0x%x", video_output_queue);
  info->info("audio_input_queue: 0x%x", audio_input_queue);
  info->info("audio_output_queue: 0x%x", audio_output_queue);

  block = new FrameVector();

  num_frames_in_block = config->readString("frame_sequence")->length();

  info->info("Frame sequence length %d", num_frames_in_block);

  threads = config->readInt("encoding_threads");
  
  movencodersrunning = true;

  for(int cnt = 0; cnt < threads; cnt++) sem_post(&read_sem);

  // Create the video encoders
  for(int cnt = 0; cnt < threads; cnt++) {
    MovEncoder *movenc = new MovEncoder(&movencodersrunning, &read_sem,
                                        inputqueue,
                                        video_output_queue,
                                        audio_input_queue,
                                        info);
    movenc->run();
    encs.push_back(movenc);
  }

  // Create the audio encoder
  audioenc = new AudioEncoder(audio_input_queue,
                              audio_output_queue,
                              info);
  audioenc->run();

  // Create the multiplexer
  writer = new MovEncoderWriter(clientip, cpr,
                                video_output_queue,
                                audio_output_queue,
                                info);
  writer->run();

  frame_number = 0;
}

//#include <unistd.h>
MovEncoderThread::~MovEncoderThread()
{
  info->info("~MovEncoderThread");

  // First we destroy the movie encoders
  for(int cnt = 0; cnt < threads; cnt++) {
    encs[cnt]->wait_stop();    // Wait for it to stop
    delete encs[cnt];    // Delete it
  }
  info->info("Deleted the movie encoders");


  // Then we destroy the audio encoder
  audioenc->wait_stop();  // Wait for it to stop.
  delete audioenc;  // delete the audio encoder
  info->info("Deleted the audio encoder");


  // Finally we destroy the writer.
  writer->wait_stop(); // Wait for it to stop.
  delete writer;  // delete the writer (end thereby close the file)
  info->info("Deleted the writer");


  // Destroy the semaphore.
  sem_destroy(&read_sem);

  info->info("~MovEncoderThread::done");
}

static int output = 0;
void MovEncoderThread::encode(Frame* frame)
{
  if(output % 250 == 0) // 25 * 24
    info->info("inputqueue: %d\tvideo_outputqueue: %d\taudio_inputqueue: %d\taudio_outputqueue: %d.",
               inputqueue->size(),
               video_output_queue->size(),
               audio_input_queue->size(),
               audio_output_queue->size());
  output++;

  if(frame == NULL) {
    info->info("MovEncoderThread::encode - NULL frame detected.");
    // Terminate
    return;
  }

  frame->number = frame_number;
  block->push_back(frame);

  // Switch frame
  if(block->size() == num_frames_in_block || frame->endOfFrameStream == true) {
    // Wait until a free encoder.
    sem_wait(&read_sem);

    inputqueue->push(block);

    // Start new block
    block = new FrameVector;
  }

  frame_number ++;
}

void MovEncoderThread::setSaveState(n_savestate savestate)
{
  writer->setSaveState(savestate);
}