/*
 *  mpegconsts.c:  Video format constants for MPEG and utilities for display
 *                 and conversion to format used for yuv4mpeg
 *
 *  Copyright (C) 2001 Andrew Stevens <andrew.stevens@philips.com>
 *  Copyright (C) 2001 Matthew Marjanovic <maddog@mir.com>
 *
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of version 2 of the GNU General Public License
 *  as published by the Free Software Foundation.
 *
 *  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 <config.h>
#include "mpegconsts.h"
#include "yuv4mpeg.h"
#include "yuv4mpeg_intern.h"

static y4m_ratio_t mpeg_framerates[] = {
  Y4M_FPS_UNKNOWN,
  Y4M_FPS_NTSC_FILM,
  Y4M_FPS_FILM,
  Y4M_FPS_PAL,
  Y4M_FPS_NTSC,
  Y4M_FPS_30,
  Y4M_FPS_PAL_FIELD,
  Y4M_FPS_NTSC_FIELD,
  Y4M_FPS_60
};


#define MPEG_NUM_RATES (sizeof(mpeg_framerates)/sizeof(mpeg_framerates[0]))
const mpeg_framerate_code_t mpeg_num_framerates = MPEG_NUM_RATES;

static const char *framerate_definitions[MPEG_NUM_RATES] = {
  "illegal",
  "24000.0/1001.0 (NTSC 3:2 pulldown converted FILM)",
  "24.0 (NATIVE FILM)",
  "25.0 (PAL/SECAM VIDEO / converted FILM)",
  "30000.0/1001.0 (NTSC VIDEO)",
  "30.0",
  "50.0 (PAL FIELD RATE)",
  "60000.0/1001.0 (NTSC FIELD RATE)",
  "60.0"
};


static const char *mpeg1_aspect_ratio_definitions[] = {
  "1:1 (square pixels)",
  "1:0.6735",
  "1:0.7031 (16:9 Anamorphic PAL/SECAM for 720x578/352x288 images)",
  "1:0.7615",
  "1:0.8055",
  "1:0.8437 (16:9 Anamorphic NTSC for 720x480/352x240 images)",
  "1:0.8935",
  "1:0.9375 (4:3 PAL/SECAM for 720x578/352x288 images)",
  "1:0.9815",
  "1:1.0255",
  "1:1:0695",
  "1:1.1250 (4:3 NTSC for 720x480/352x240 images)",
  "1:1.1575",
  "1:1.2015"
};

static const y4m_ratio_t mpeg1_aspect_ratios[] = {
  Y4M_SAR_MPEG1_1,
  Y4M_SAR_MPEG1_2,
  Y4M_SAR_MPEG1_3,		/* Anamorphic 16:9 PAL */
  Y4M_SAR_MPEG1_4,
  Y4M_SAR_MPEG1_5,
  Y4M_SAR_MPEG1_6,		/* Anamorphic 16:9 NTSC */
  Y4M_SAR_MPEG1_7,
  Y4M_SAR_MPEG1_8,		/* PAL/SECAM 4:3 */
  Y4M_SAR_MPEG1_9,
  Y4M_SAR_MPEG1_10,
  Y4M_SAR_MPEG1_11,
  Y4M_SAR_MPEG1_12,		/* NTSC 4:3 */
  Y4M_SAR_MPEG1_13,
  Y4M_SAR_MPEG1_14,
};

static const char *mpeg2_aspect_ratio_definitions[] = {
  "1:1 pixels",
  "4:3 display",
  "16:9 display",
  "2.21:1 display"
};


static const y4m_ratio_t mpeg2_aspect_ratios[] = {
  Y4M_DAR_MPEG2_1,
  Y4M_DAR_MPEG2_2,
  Y4M_DAR_MPEG2_3,
  Y4M_DAR_MPEG2_4
};

static const char **aspect_ratio_definitions[2] = {
  mpeg1_aspect_ratio_definitions,
  mpeg2_aspect_ratio_definitions
};

static const y4m_ratio_t *mpeg_aspect_ratios[2] = {
  mpeg1_aspect_ratios,
  mpeg2_aspect_ratios
};

const mpeg_aspect_code_t mpeg_num_aspect_ratios[2] = {
  sizeof (mpeg1_aspect_ratios) / sizeof (mpeg1_aspect_ratios[0]),
  sizeof (mpeg2_aspect_ratios) / sizeof (mpeg2_aspect_ratios[0])
};

/*
 * Convert MPEG frame-rate code to corresponding frame-rate
 */

y4m_ratio_t
mpeg_framerate (mpeg_framerate_code_t code)
{
  if (code == 0 || code > mpeg_num_framerates)
    return y4m_fps_UNKNOWN;
  else
    return mpeg_framerates[code];
}

/*
 * Look-up MPEG frame rate code for a (exact) frame rate.
 */


mpeg_framerate_code_t
mpeg_framerate_code (y4m_ratio_t framerate)
{
  mpeg_framerate_code_t i;

  y4m_ratio_reduce (&framerate);
  for (i = 1; i < mpeg_num_framerates; ++i) {
    if (Y4M_RATIO_EQL (framerate, mpeg_framerates[i]))
      return i;
  }
  return 0;
}


/* small enough to distinguish 1/1000 from 1/1001 */
#define MPEG_FPS_TOLERANCE 0.0001


y4m_ratio_t
mpeg_conform_framerate (double fps)
{
  mpeg_framerate_code_t i;
  y4m_ratio_t result;

  /* try to match it to a standard frame rate */
  for (i = 1; i < mpeg_num_framerates; i++) {
    double deviation = 1.0 - (Y4M_RATIO_DBL (mpeg_framerates[i]) / fps);

    if ((deviation > -MPEG_FPS_TOLERANCE) && (deviation < +MPEG_FPS_TOLERANCE))
      return mpeg_framerates[i];
  }
  /* no luck?  just turn it into a ratio (6 decimal place accuracy) */
  result.n = (int) ((fps * 1000000.0) + 0.5);
  result.d = 1000000;
  y4m_ratio_reduce (&result);
  return result;
}



/*
 * Convert MPEG aspect-ratio code to corresponding aspect-ratio
 */

y4m_ratio_t
mpeg_aspect_ratio (int mpeg_version, mpeg_aspect_code_t code)
{
  y4m_ratio_t ratio;

  if (mpeg_version < 1 || mpeg_version > 2)
    return y4m_sar_UNKNOWN;
  if (code == 0 || code > mpeg_num_aspect_ratios[mpeg_version - 1])
    return y4m_sar_UNKNOWN;
  else {
    ratio = mpeg_aspect_ratios[mpeg_version - 1][code - 1];
    y4m_ratio_reduce (&ratio);
    return ratio;
  }
}

/*
 * Look-up corresponding MPEG aspect ratio code given an exact aspect ratio.
 *
 * WARNING: The semantics of aspect ratio coding *changed* between
 * MPEG1 and MPEG2.  In MPEG1 it is the *pixel* aspect ratio. In
 * MPEG2 it is the (far more sensible) aspect ratio of the eventual
 * display.
 *
 */

mpeg_aspect_code_t
mpeg_frame_aspect_code (int mpeg_version, y4m_ratio_t aspect_ratio)
{
  mpeg_aspect_code_t i;
  y4m_ratio_t red_ratio = aspect_ratio;

  y4m_ratio_reduce (&red_ratio);
  if (mpeg_version < 1 || mpeg_version > 2)
    return 0;
  for (i = 1; i < mpeg_num_aspect_ratios[mpeg_version - 1]; ++i) {
    y4m_ratio_t red_entry = mpeg_aspect_ratios[mpeg_version - 1][i - 1];

    y4m_ratio_reduce (&red_entry);
    if (Y4M_RATIO_EQL (red_entry, red_ratio))
      return i;
  }

  return 0;

}



/*
 * Guess the correct MPEG aspect ratio code,
 *  given the true sample aspect ratio and frame size of a video stream
 *  (and the MPEG version, 1 or 2).
 *
 * Returns 0 if it has no good guess.
 *
 */


/* this is big enough to accommodate the difference between 720 and 704 */
#define GUESS_ASPECT_TOLERANCE 0.03

mpeg_aspect_code_t
mpeg_guess_mpeg_aspect_code (int mpeg_version, y4m_ratio_t sampleaspect,
			     int frame_width, int frame_height)
{
  if (Y4M_RATIO_EQL (sampleaspect, y4m_sar_UNKNOWN)) {
    return 0;
  }
  switch (mpeg_version) {
    case 1:
      if (Y4M_RATIO_EQL (sampleaspect, y4m_sar_SQUARE)) {
	return 1;
      } else if (Y4M_RATIO_EQL (sampleaspect, y4m_sar_NTSC_CCIR601)) {
	return 12;
      } else if (Y4M_RATIO_EQL (sampleaspect, y4m_sar_NTSC_16_9)) {
	return 6;
      } else if (Y4M_RATIO_EQL (sampleaspect, y4m_sar_PAL_CCIR601)) {
	return 8;
      } else if (Y4M_RATIO_EQL (sampleaspect, y4m_sar_PAL_16_9)) {
	return 3;
      }
      return 0;
      break;
    case 2:
      if (Y4M_RATIO_EQL (sampleaspect, y4m_sar_SQUARE)) {
	return 1;		/* '1' means square *pixels* in MPEG-2; go figure. */
      } else {
	unsigned int i;
	double true_far;	/* true frame aspect ratio */

	true_far =
	  (double) (sampleaspect.n * frame_width) / (double) (sampleaspect.d * frame_height);
	/* start at '2'... */
	for (i = 2; i < mpeg_num_aspect_ratios[mpeg_version - 1]; i++) {
	  double ratio = true_far / Y4M_RATIO_DBL (mpeg_aspect_ratios[mpeg_version - 1][i - 1]);

	  if ((ratio > (1.0 - GUESS_ASPECT_TOLERANCE)) && (ratio < (1.0 + GUESS_ASPECT_TOLERANCE)))
	    return i;
	}
	return 0;
      }
      break;
    default:
      return 0;
      break;
  }
}




/*
 * Guess the true sample aspect ratio of a video stream,
 *  given the MPEG aspect ratio code and the actual frame size
 *  (and the MPEG version, 1 or 2).
 *
 * Returns y4m_sar_UNKNOWN if it has no good guess.
 *
 */
y4m_ratio_t
mpeg_guess_sample_aspect_ratio (int mpeg_version,
				mpeg_aspect_code_t code, int frame_width, int frame_height)
{
  switch (mpeg_version) {
    case 1:
      /* MPEG-1 codes turn into SAR's, just not quite the right ones.
         For the common/known values, we provide the ratio used in practice,
         otherwise say we don't know. */
      switch (code) {
	case 1:
	  return y4m_sar_SQUARE;
	  break;
	case 3:
	  return y4m_sar_PAL_16_9;
	  break;
	case 6:
	  return y4m_sar_NTSC_16_9;
	  break;
	case 8:
	  return y4m_sar_PAL_CCIR601;
	  break;
	case 12:
	  return y4m_sar_NTSC_CCIR601;
	  break;
	default:
	  return y4m_sar_UNKNOWN;
	  break;
      }
      break;
    case 2:
      /* MPEG-2 codes turn into Frame Aspect Ratios, though not exactly the
         FAR's used in practice.  For common/standard frame sizes, we provide
         the original SAR; otherwise, we say we don't know. */
      if (code == 1) {
	return y4m_sar_SQUARE;	/* '1' means square *pixels* in MPEG-2 */
      } else if ((code >= 2) && (code <= 4)) {
	return y4m_guess_sar (frame_width, frame_height, mpeg2_aspect_ratios[code - 1]);
      } else {
	return y4m_sar_UNKNOWN;
      }
      break;
    default:
      return y4m_sar_UNKNOWN;
      break;
  }
}





/*
 * Look-up MPEG explanatory definition string for frame rate code
 *
 */


const char *
mpeg_framerate_code_definition (mpeg_framerate_code_t code)
{
  if (code == 0 || code >= mpeg_num_framerates)
    return "UNDEFINED: illegal/reserved frame-rate ratio code";

  return framerate_definitions[code];
}

/*
 * Look-up MPEG explanatory definition string aspect ratio code for an
 * aspect ratio code
 *
 */

const char *
mpeg_aspect_code_definition (int mpeg_version, mpeg_aspect_code_t code)
{
  if (mpeg_version < 1 || mpeg_version > 2)
    return "UNDEFINED: illegal MPEG version";

  if (code < 1 || code > mpeg_num_aspect_ratios[mpeg_version - 1])
    return "UNDEFINED: illegal aspect ratio code";

  return aspect_ratio_definitions[mpeg_version - 1][code - 1];
}


/*
 * Look-up explanatory definition of interlace field order code
 *
 */

const char *
mpeg_interlace_code_definition (int yuv4m_interlace_code)
{
  const char *def;

  switch (yuv4m_interlace_code) {
    case Y4M_UNKNOWN:
      def = "unknown";
      break;
    case Y4M_ILACE_NONE:
      def = "none/progressive";
      break;
    case Y4M_ILACE_TOP_FIRST:
      def = "top-field-first";
      break;
    case Y4M_ILACE_BOTTOM_FIRST:
      def = "bottom-field-first";
      break;
    default:
      def = "UNDEFINED: illegal video interlacing type-code!";
      break;
  }
  return def;
}


/* 
 * Local variables:
 *  c-file-style: "stroustrup"
 *  tab-width: 4
 *  indent-tabs-mode: nil
 * End:
 */