/*
 * Copyright 1993 by the University of Pennsylvania
 *
 * Permission to use, copy, and distribute for non-commercial purposes,
 * is hereby granted without fee, providing that the above copyright
 * notice appear in all copies and that both the copyright notice and this
 * permission notice appear in supporting documentation.
 *
 * The software may be modified for your own purposes, but modified versions
 * may not be distributed.
 *
 * This software is provided "as is" without any expressed or implied warranty.
 */

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include "global.h"
#include "mpeg_encode.h"

FILE * open_file();
/************************************************************************
   mpeg_encode - read video data and encode it and write encoded data
                 and statistics data. The parameter should be checked
                 before call this routine.
                 This returns int value 0:no error -1: error with errnum.
 *************************************************************************/
int
  mpeg_encode(N, M, quant, dctnum, 
              CCIR, width, height,
              start_frame, nfrms,
              in_fname, file_type,
	      out_fname, stat_fname)
int N;      /* Intra frame interval */
int M;      /* Intra/Pred frame interval */    
int quant;  /* quantization scale */
int dctnum; /* Beta, Number of DCT coefficients in HP class */
int CCIR;   /* 0: RGB, 1: CCIR video data format */
int width;  /* width of video data */
int height; /* height of video data */
int start_frame;      /* encode start frame number */
int nfrms;            /* number of frames to be coded */
char *in_fname;  /* video data file/device name */
int file_type;   /* 0 = file, 1 = tape */
char *out_fname; /* mpeg data file/device name */
char *stat_fname;/* stat data file name */
{
/*--------------------------- define valuables --------------------------*/
  int i,j,k;
  FRAME *first_frame  = NULL, 
        *last_frame   = NULL,        /* I or P frame */ 
        **inner_frame = NULL;        /* 1D array of B frame */
  FRAME **first_recon_frame = NULL,
        **last_recon_frame  = NULL;  /* reconstructed frame data */
  int pic_count = 1;                 /* counter for frame */
  int fdin;
  FILE *fdstat = NULL;
  FRAME_STAT **FS;
  int error = 0;
/*************************************************************************
  working display init
 *************************************************************************/
  if (Xon)
    if(working_update(2,NULL,nfrms)) return -1;
/*************************************************************************
  file opening process
 *************************************************************************/
  if((fdin = open_video_data(in_fname))==-1)  return -1;
  if(open_mpeg_file(out_fname,dctnum)==-1)  goto file_close_jump_label;
  if(strlen(stat_fname)&&(fdstat = open_file(stat_fname,"w"))==NULL) 
    goto file_close_jump_label;
  write_stat_header(fdstat, N, M, quant, dctnum, 
                            start_frame, nfrms, height, width);
/*************************************************************************/
/*------------------- skip to starting point on tape --------------------*/
/*************************************************************************/
  if(start_frame !=1) {
    if (Xon)
      if(working_update(1,"Skipping Frame(s) ...... ",start_frame -1))
	goto file_close_jump_label;
    if(skip(fdin,start_frame - 1, width, height, CCIR,file_type)) {
    file_close_jump_label: /* interrupt process jump routine */
      close_video_data(fdin);
      close_mpeg_file();
      return -1;
    }    
  }
/**************************************************************************/
/*                     Main MPEG Coding Loop                              */
/**************************************************************************/
/*------------------- Allocate memory for arrays -------------------------*/  
  first_recon_frame = (FRAME **)malloc((NUM_P+1)*sizeof(struct FRAME*));
  for(i=0;i<NUM_P+1;i++) 
    first_recon_frame[i] = make_frame(width,height);
  
  last_recon_frame  = (FRAME **)malloc((NUM_P+1)*sizeof(struct FRAME*));
  for(i=0;i<NUM_P+1;i++) 
    last_recon_frame[i]  = make_frame(width,height);
  
  inner_frame = (FRAME **)malloc((M - 1)*sizeof(struct FRAME *));
/*------------------- Allocate memory for statistics ----------------------*/
  FS = (FRAME_STAT **)malloc(M*sizeof(FRAME_STAT*));
/*------------------- put start header ------------------------------------*/
  put_sequence_start_code(16*(width/16),16*(height/16));        /* 95 bits */
/*------------------- read one frame and perform I frame coding -----------*/
#ifdef FREQUENT_UPDATE
  if (Xon)
    if(working_update(1,"Loading Frame ...... (1/1)",0)) 
#else
  if (Xon)
    if(working_update(1,"Loading Frame ...... ",0)) 
#endif
    {error = 1;goto interrupt_jump_label;}
  if((last_frame = load_frame(fdin,width,height,CCIR,file_type)) == NULL) 
    {error = 1;goto interrupt_jump_label;}
#ifdef DEBUG
  fprintf(stdout,"ENCODING frame # %d\n",pic_count);
#endif
  if (Xon)
    if(working_update(2,NULL,0))     {error = 1;goto interrupt_jump_label;}
  FS[0] = do_intra(last_frame,last_recon_frame,quant,dctnum,height,width);
  if(FS[0]==NULL)     {error = 1;goto interrupt_jump_label;}
  FS[0]->frame = 1;
#ifdef DISPSTAT
  disp_stat(FS[0]);
#endif
/*------------------- Stat data write -----------------------------*/
  write_stat(fdstat,FS[0]);
/*------------------- Encoded data write -----------------------------*/
  free(FS[0]);
  pic_count=1;                   /* reset picture gcounter */
/*------------------------------- Main loop ------------------------------*/
  while(pic_count < nfrms) {
    first_frame = last_frame;
/*----------- copy last_recon_frames into first_recon_frames -------------*/
    for(i=0;i<NUM_P+1;i++)
      copy_frame(first_recon_frame[i],last_recon_frame[i]);
    /* read B frames */
    if (Xon)
      if(working_update(1,"Loading Frame(s) ......",M)) {error = 1;break;}
    for(i=0;i<M-1;i++) {
      if (Xon)
	if(working_update(1,NULL,0)) {error = 1;break;}
      if((inner_frame[i]=load_frame(fdin,width,height,CCIR,file_type))==NULL) 
        {error = 1;break;}
      pic_count++;
    }
    /* load last frame */
    if (Xon)
      if(working_update(1,NULL,0)) {error = 1;break;}
    if((last_frame = load_frame(fdin,width,height,CCIR,file_type)) == NULL) 
      {error = 1;break;}
    pic_count++;
#ifdef DEBUG
    fprintf(stderr,"Frame read complete -> %d\n",pic_count);   
    /*** create last_recon_frame ***/
    fprintf(stdout,"ENCODING frame # %d\n",pic_count);
#endif
    if ((pic_count-1)%N == 0) {            /* so I frame */ 
      if (Xon)
	if(working_update(2,NULL,0)) {error = 1;break;}
      FS[M-1]= do_intra(last_frame,last_recon_frame,quant,dctnum,height,width);
      if(FS[M-1]==NULL) {error = 1;break;}
      FS[M-1]->frame = pic_count;
    }
    else {                                 /* obviously, P frame */
      if (Xon)
	if(working_update(2,NULL,0)) {error = 1;break;}
      FS[M-1] = do_pred(last_frame,last_recon_frame,quant,dctnum,height,width);
      if(FS[M-1]==NULL) {error = 1;break;}
      FS[M-1]->frame = pic_count;
    }

    /*** perform B coding over inner frames ***/
    for(i=0;i<M-1;i++) {                  /* B frames */
#ifdef DEBUG
      fprintf(stdout,"ENCODEING frame # %d\n",pic_count + i - M + 1);
#endif
      if (Xon)
	if(working_update(2,NULL,0)) {error = 1;break;}
      FS[i] = do_interpolate(inner_frame[i],first_recon_frame[0]
                   ,last_recon_frame[0],quant,dctnum,height,width);
      if(FS[i]==NULL) {error = 1;break;}
      FS[i]->frame = start_frame + pic_count - M + i;
    }
    /*** release allocated memory ***/
    free_frame(first_frame);
    first_frame = NULL;
    for(i=0;i<M-2;i++) {
      free_frame(inner_frame[i]);
      inner_frame[i] = NULL;
    }
    /*** write data to file ***/
    for(i=0;i<M;i++) {
#ifdef DISPSTAT
      disp_stat(FS[i]);
#endif
      write_stat(fdstat,FS[i]);
      free(FS[i]);
    }
  }
/*-------------------------- Put sequence end code ----------------------*/
  if(error==0) put_sequence_end_code();
/*-------------------------- Exit and file close ------------------------*/
  interrupt_jump_label:
  close_mpeg_file();
  close_video_data(fdin);
  close_file(fdstat);
/*--------------------- release allocated memory -------------------------*/
  free(FS);
  for(i=0;i<NUM_P;i++) {
    free_frame(first_recon_frame[i]);
    free_frame(last_recon_frame[i]);
  }
  free(first_recon_frame);
  free(last_recon_frame);
  if(pic_count != 1) {
    free_frame(first_frame);
    free_frame(last_frame);
    for(i=0;i<M-1;i++)
      free_frame(inner_frame[i]);
  }
  free(inner_frame);
#ifdef DEBUG
  fprintf(stderr, "Complete!\n");
#endif
  return error; /* complete encoding without error */
}


