/*
 *  import_mov.c
 *
 *  Copyright (C) Thomas Oestreich - January 2002
 *  updated by Christian Vogelgsang <Vogelgsang@informatik.uni-erlangen.de>
 *
 *  This file is part of transcode, a video stream  processing tool
 *
 *  transcode 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, or (at your option)
 *  any later version.
 *
 *  transcode 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 GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#define MOD_NAME    "import_mov.so"
#define MOD_VERSION "v0.1.3 (2005-12-04)"
#define MOD_CODEC   "(video) * | (audio) *"

#include "transcode.h"
#include "../src/filter.h"

static int verbose_flag = TC_QUIET;
static int capability_flag = TC_CAP_PCM | TC_CAP_RGB | TC_CAP_YUV |
                             TC_CAP_YUV422 | TC_CAP_VID;

#define MOD_PRE mov
#include "import_def.h"

#include <quicktime.h>
#include <colormodels.h>
#include <lqt.h>
#include "magic.h"

#include "aclib/imgconvert.h"

/* movie handles */
static quicktime_t *qt_audio=NULL;
static quicktime_t *qt_video=NULL;

/* row pointer for decode frame */
static unsigned char **row_ptr = NULL;

/* raw or decode frame */
static int rawVideoMode = 0;

/* raw or decode audio */
static int rawAudioMode = 0;

/* frame size */
static int w=0, h=0;

/* number of audio channels */
static int chan=0;

/* number of audio bits */
static int bits=0;

/* number of frames */
static int frames=0;

/* number of audio samples */
static int no_samples=0;

/* import colormodel */
static int qt_cm = 0;


/* ------------------------------------------------------------
 *
 * open stream
 *
 * ------------------------------------------------------------*/

MOD_open
{
  /* audio */
  if(param->flag == TC_AUDIO) {
    int numTrk;
    long rate;
    char *codec;

    param->fd = NULL;

    /* open movie for audio extraction */
    if(qt_audio==NULL) {
      if(NULL == (qt_audio = quicktime_open((char *)vob->audio_in_file,1,0))){
	       tc_log_warn(MOD_NAME, "can't open quicktime!");
	       return(TC_IMPORT_ERROR);
      }
    }

    /* check for audio track */
    numTrk = quicktime_audio_tracks(qt_audio);
    if(numTrk==0) {
      tc_log_warn(MOD_NAME, "AUDIO: --no audio track in quicktime found --");
      no_samples=0;
      return(TC_IMPORT_OK);
    }

    /* extract audio parameters */
    rate   = quicktime_sample_rate(qt_audio, 0);
    chan   = quicktime_track_channels(qt_audio, 0);
    bits   = quicktime_audio_bits(qt_audio, 0);
    codec  = quicktime_audio_compressor(qt_audio, 0);

    /* The total frames */
    no_samples=quicktime_audio_length(qt_audio, 0);

    /* verbose info */
    tc_log_info(MOD_NAME, "codec=%s, rate=%ld Hz, bits=%d,"
                    " channels=%d, samples=%d",
                    codec, rate, bits, chan, no_samples);

    /* check bits */
    if((bits!=8)&&(bits!=16)) {
      tc_log_warn(MOD_NAME, "unsupported sample bits: %d",bits);
      return(TC_IMPORT_ERROR);
    }

    /* check channels */
    if(chan>2) {
      tc_log_warn(MOD_NAME, "too many audio channels: %d",chan);
      return(TC_IMPORT_ERROR);
    }

    /* check codec string */
    if(strlen(codec)==0) {
      tc_log_warn(MOD_NAME, "empty codec in quicktime?");
      return(TC_IMPORT_ERROR);
    }

    /* check if audio compressor is supported */
    if(quicktime_supported_audio(qt_audio, 0)!=0) {
      rawAudioMode = 0;
    }
#if !defined(LIBQUICKTIME_000904)
    /* RAW PCM is directly supported */
    else if(strcasecmp(codec,QUICKTIME_RAW)==0) {
      rawAudioMode = 1;
      tc_log_warn(MOD_NAME, "using RAW audio mode!");
    }
#endif
    /* unsupported codec */
    else {
      tc_log_warn(MOD_NAME, "quicktime audio codec '%s' not supported!",
	      codec);
      return(TC_IMPORT_ERROR);
    }
    return(TC_IMPORT_OK);
  }

  /* video */
  if(param->flag == TC_VIDEO) {
    double fps;
    char *codec;
    int numTrk;

    param->fd = NULL;

    /* open movie for video extraction */
    if(qt_video==NULL)
      if(NULL == (qt_video = quicktime_open((char *)vob->video_in_file,1,0))){
	       tc_log_warn(MOD_NAME,"can't open quicktime!");
	       return(TC_IMPORT_ERROR);
      }

    /* check for audio track */
    numTrk = quicktime_video_tracks(qt_video);
    if(numTrk==0) {
      tc_log_warn(MOD_NAME,"no video track in quicktime found!");
      return(TC_IMPORT_ERROR);
    }

    /* read all video parameter from input file */
    w      =  quicktime_video_width(qt_video, 0);
    h      =  quicktime_video_height(qt_video, 0);
    fps    =  quicktime_frame_rate(qt_video, 0);
    codec  =  quicktime_video_compressor(qt_video, 0);

    //ThOe total frames
    frames=quicktime_video_length(qt_video, 0);

    /* verbose info */
    tc_log_info(MOD_NAME, "VIDEO: codec=%s, fps=%6.3f, width=%d,"
                    " height=%d, frames=%d",
                    codec, fps, w, h, frames);

    /* check codec string */
    if(strlen(codec)==0) {
      tc_log_warn(MOD_NAME, "empty codec in quicktime?");
      return(TC_IMPORT_ERROR);
    }

    /* check if a suitable compressor is available */
    if(quicktime_supported_video(qt_video,0)==0) {
	     tc_log_warn(MOD_NAME, "quicktime codec '%s'"
                             " not supported for RGB!",
		             codec);
	     return(TC_IMPORT_ERROR);
    }


    /* set color model */
    switch(vob->im_v_codec) {
        case CODEC_RGB:
              /* use raw mode when possible */
              /* not working ?*/
              /*if (strcmp(qt_codec, "raw ")) rawVideo=1; */
	      	    /* allocate buffer for row pointers */
	      	    row_ptr = tc_malloc(h*sizeof(char *));
	      	    if(row_ptr==0) {
		        tc_log_error(MOD_NAME,"can't alloc row pointers");
			return(TC_IMPORT_ERROR);
	      	    }

              quicktime_set_cmodel(qt_video, BC_RGB888); qt_cm = BC_RGB888;
              break;

        case CODEC_YUV:
            /* use raw mode when possible */
            /* not working ?*/
            /* if (strcmp(qt_codec, "yv12")) rawVideo=1; */

            /* allocate buffer for row pointers */
            row_ptr = tc_malloc(3*sizeof(char *));
            if(row_ptr==0) {
                tc_log_error(MOD_NAME,"can't alloc row pointers");
                return(TC_IMPORT_ERROR);
            }

            if (!quicktime_reads_cmodel(qt_video, BC_YUV420P, 0)) {
                if (quicktime_reads_cmodel(qt_video, BC_YUVJ420P, 0)) {
                    /* stolen from import_ffmpeg */
                    /* load levels filter */
                    if (!tc_filter_add("levels", "output=16-240:pre=1")) {
                        tc_log_warn(MOD_NAME, "cannot load levels filter. Try -V rgb24.");
                    }
                    quicktime_set_cmodel(qt_video, BC_YUVJ420P);

                } else {
                    tc_log_error(MOD_NAME,"unable to handle colormodel. Try -V rgb24.");
                    return(TC_IMPORT_ERROR);
                }
            } else {
                quicktime_set_cmodel(qt_video, BC_YUV420P);
            }

            qt_cm = BC_YUV420P;
            break;

        case CODEC_YUV422:
            /* allocate buffer for row pointers */
            row_ptr = tc_malloc(3*sizeof(char *));
            if(row_ptr==0) {
                tc_log_error(MOD_NAME,"can't alloc row pointers");
                return(TC_IMPORT_ERROR);
            }

            if (!quicktime_reads_cmodel(qt_video, BC_YUV422P, 0)) {
                tc_log_error(MOD_NAME,"unable to handle colormodel. Try -V rgb24.");
                return(TC_IMPORT_ERROR);
            }

            quicktime_set_cmodel(qt_video, BC_YUV422P); qt_cm = BC_YUV422P;
            break;

        case CODEC_YUY2:
              quicktime_set_cmodel(qt_video, BC_YUV422); qt_cm = BC_YUV422;
              break;

         /* passthrough */
         case CODEC_RAW_RGB:
         case CODEC_RAW_YUV:
         case CODEC_RAW:
              rawVideoMode = 1;
              break;

        default:
            /* unsupported internal format */
            tc_log_warn(MOD_NAME,"unsupported internal video format %x",
                	vob->ex_v_codec);
            return(TC_EXPORT_ERROR);
            break;
       }

    return(TC_IMPORT_OK);
  }

  return(TC_IMPORT_ERROR);
}


/* ------------------------------------------------------------
 *
 * decode  stream
 *
 * ------------------------------------------------------------*/

MOD_decode
{
  /* video */
  if(param->flag == TC_VIDEO) {
    if(rawVideoMode) {
      /* read frame raw */
      param->size = quicktime_read_frame(qt_video, param->buffer, 0);
      if(param->size<=0) {
	if(verbose & TC_DEBUG)
	  tc_log_warn(MOD_NAME,"quicktime read video frame");
	return(TC_IMPORT_ERROR);
      }
    } else {
      /* decode frame */
      unsigned char *mem = param->buffer;

      int iy,sl;

      switch(qt_cm) {
      case BC_RGB888:
              /* setup row pointers for RGB: inverse! */
              sl = w*3;
              for(iy=0;iy<h;iy++){
                  row_ptr[iy] = mem;
                  mem += sl;
              }

              param->size = (h*w) * 3;
              break;

      case BC_YUV420P: {
              /* setup row pointers for YUV420P */
              YUV_INIT_PLANES(row_ptr, mem, IMG_YUV420P, h, w);
              param->size = (h*w*3)/2;
              break;
              }

      case BC_YUV422P: {
	      /* setup row pointers for YUV422P */
              YUV_INIT_PLANES(row_ptr, mem, IMG_YUV422P, h, w);
              param->size = (h*w)*2;
              break;
              }
      }

      /* decode the next frame */
      if(lqt_decode_video(qt_video,row_ptr,0)<0) {
        if(verbose & TC_DEBUG)
          tc_log_warn(MOD_NAME,"can't decode frame");
          return(TC_IMPORT_ERROR);
        }
    }

    //ThOe trust file header and terminate after all frames have been processed.
    if(frames--==0) return(TC_IMPORT_ERROR);
    return(TC_IMPORT_OK);
  }

  /* audio */
  if(param->flag == TC_AUDIO) {

    int bytes_read;

    /* Leave if audio track is empty */
    if (no_samples==0){
      param->size=0;
      return(TC_IMPORT_OK);
    }

    /* raw read mode */
#if !defined(LIBQUICKTIME_000904)
    if(rawAudioMode) {
      bytes_read = quicktime_read_audio(qt_audio,
					param->buffer, param->size, 0);
    } else
#endif
    {
      /* decode audio mode */
      long pos = quicktime_audio_position(qt_audio,0);
      long samples = param->size;
      if(bits==16)
	samples >>= 1;

      /* mono */
      if(chan==1) {
	/* direct copy */
	bytes_read = quicktime_decode_audio(qt_audio,
					    (int16_t *)param->buffer,NULL,
					    samples,0);

	/* check result */
	if(bytes_read<0) {
	  if(verbose & TC_DEBUG)
	    tc_log_warn(MOD_NAME,"reading quicktime audio frame!");
	  return(TC_IMPORT_ERROR);
	}
      }
      /* stereo */
      else {
	int16_t *tgt;
	int16_t *tmp;
	int s,t;

	samples >>= 1;
	tgt = (int16_t *)param->buffer;
	tmp = tc_malloc(samples*sizeof(int16_t));

	/* read first channel into target buffer */
	bytes_read = quicktime_decode_audio(qt_audio,tgt,NULL,samples,0);
	if(bytes_read<0) {
	  if(verbose & TC_DEBUG)
	    tc_log_warn(MOD_NAME,"reading quicktime audio frame!");
	  return(TC_IMPORT_ERROR);
	}

	/* read second channel in temp buffer */
	quicktime_set_audio_position(qt_audio,pos,0);
	bytes_read = quicktime_decode_audio(qt_audio,tmp,NULL,samples,1);
	if(bytes_read<0) {
	  if(verbose & TC_DEBUG)
	    tc_log_warn(MOD_NAME,"reading quicktime audio frame!");
	  return(TC_IMPORT_ERROR);
	}

	/* spread first channel */
	for(s=samples-1;s>=0;s--)
	  tgt[s<<1] = tgt[s];

	/* fill in second channel from temp buffer */
	t = 1;
	for(s=0;s<samples;s++) {
	  tgt[t] = tmp[s];
	  t += 2;
	}

	free(tmp);
      }
      quicktime_set_audio_position(qt_audio,pos+samples,0);
    }

    return(TC_IMPORT_OK);
  }

  return(TC_IMPORT_ERROR);
}

/* ------------------------------------------------------------
 *
 * close stream
 *
 * ------------------------------------------------------------*/

MOD_close
{
  /* free up audio */
  if(param->flag == TC_AUDIO) {
    if(qt_audio!=NULL) {
      quicktime_close(qt_audio);
      qt_audio=NULL;
    }
    return(TC_IMPORT_OK);
  }

  /* free up video */
  if(param->flag == TC_VIDEO) {
    if(qt_video!=NULL) {
      quicktime_close(qt_video);
      qt_video=NULL;
    }
    /* free row pointer */
    if(row_ptr!=0)
      free(row_ptr);

    return(TC_IMPORT_OK);
  }

  return(TC_IMPORT_ERROR);
}
