#include <stdio.h>
#include <stdlib.h>

/*
 * Code by Riccardo Bernardini (2005).
 */

int dove=0;

char leggi()
{
  int c;

  dove++;
  
  c=fgetc(stdin);

  if (c==EOF)
    exit(0);
  else
    return c;
}

void print_header_info()
{
  char c;
  unsigned int hsize, vsize;
  char *ratio, *rate;

  char *ratio_table[] = { "?", 
			  "1:1", "4:3", "16:9", "2.21:1",
			  "?", "?", "?", "?", "?", "?", "?", 
			  "?", "?", "?", "?" };

  char *rate_table[] = { "?",
			 "23.976", "24", "25",
			 "29.97", "30",  "50",
			 "59.94", "60",
			 "?", "?", "?", "?", "?", "?", "?" };

  c=leggi();
  hsize = c;
  hsize = hsize << 4;
  
  c=leggi();
  hsize |= (c & 0xf0) >> 4;
  vsize  = (c & 0x0f);
  vsize  = vsize << 8;

  c=leggi();
  vsize += ((int) c) & 0xff;

  c=leggi();

  ratio = ratio_table[(c & 0xf0) >> 4];
  rate  = rate_table[c & 0x0f];

  printf(">> H %d %u %u %s %s\n", dove-8, hsize, vsize, rate, ratio);
}

int main(int argc, char **argv)
{
  char c;
  unsigned int ora, min, sec, frame;

  /*
   * The following code implements a finite state automata which finds 
   * strings matching 
   *          0*001.
   *
   * where each char represent an 8 bit value (e.g. '1' represents the 
   * 8 bit sequence "00000001" and '.' represents any 8 bit value).
   *
   * The finite automata has the following states
   *
   *    NO_ZERO:      No zero has been found yet
   *    ONE_ZERO:     Only one zero found
   *    TWO_ZEROS:    Two or more zeros found
   *    CODE_FOUND:   We found the whole string "0*001"
   *
   */

 NO_ZEROS:
  //printf("no zero\n");
  switch(leggi()) {
  case 0:    goto ONE_ZERO;
  default:   goto NO_ZEROS;
  }
  
 ONE_ZERO:
  //printf("one zero\n");
  switch(leggi()) {
  case 0:    goto TWO_ZEROS;
  default:   goto NO_ZEROS;
  }

 TWO_ZEROS:
  //printf("two zero\n");
  switch(leggi()) {
  case 0x01:  goto CODE_FOUND;
  case 0x00:  goto TWO_ZEROS;
  default:    goto NO_ZEROS;
  }

 CODE_FOUND:
  c = leggi();

  /* 
   * We found a start code.  The type of the code found is determined
   * by the value contained in c 
   */

  switch(c) {
  case 0x00: 
    /* Picture.  Parse the picture type */
    leggi(); /* Skip one byte */
    c=(leggi() & 0x38) >> 3;
    switch (c) {
    case 1:
      printf(">> I %d\n", dove-6);
      break;
    case 2:
      printf(">> P %d\n", dove-6);
      break;
    case 3:
      printf(">> B %d\n", dove-6);
      break;
    case 4:
      printf(">> D %d\n", dove-6);
      break;
    default:
      /* Unknown picture type */
      printf(">> F %d (%hx)\n", dove-6, c);
    }
    goto NO_ZEROS;
  case (char) 0xb8: 
    /* GOP.  Parse the timestamp */
    c=leggi();
    ora  = (c & 0x7f) >> 2;
    min  = (c & 0x03) << 4;

    c=leggi();
    min |= c >> 4;
    sec  = (c & 0x07) << 3;

    c=leggi();
    sec |= c >> 5;
    frame = (c & 0x1f) << 1;

    c=leggi();
    frame |= c >> 7;
    printf(">> G %d (%d:%d:%d:%d)\n", dove-8, ora, min, sec, frame);
    goto NO_ZEROS;
  case (char) 0xb2:
    /* User data */ 
    printf(">> U %d\n", dove-4);
    goto NO_ZEROS;
  case (char) 0xb7: 
    /* Sequence end */
    printf(">> E %d\n", dove-4);
    goto NO_ZEROS;
  case (char) 0xb3: 
    /* Header */
    print_header_info();
    goto NO_ZEROS;
  default:
    /* Unhandled.  Just print the code */
    printf(">> # %d (0x%02hhx)\n", dove-4, c);
    goto NO_ZEROS;
  }

  return 0;
}

