
/*  (C) Copyright 1999 Rick Dean. */
/*  Released under version 2 of the GNU Public License. */                                                                         
/*  <vobTools@fdd.com>  http://fdd.com/vobTools */

/* We borrow the nomenclature from the MPEG2 video standard (a.k.a. 13818-2 or N????). */

/* This program has been tested on only one moved, (which was kind of old and boring) */
/* so there are plenty of bugs left. */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "videobits.h"
#include "bitfile.h"

char vobFilename[MAX_NAME_LENGTHS] = "vts_01_1.vob"; 
char traceFilename[MAX_NAME_LENGTHS] = "videotrace";  
int traceFlag = 0;
int desiredStreamId = 0xbb;

int foundGarbageFlag;
int numPesPacketsFound;
int numPacksFound;

int progressive_sequence;
int horizontal_size;
int vertical_size;
int bit_rate;
int vbv_buffer_size;
int low_delay;

static void ParseSequenceHeader(BitFile *bitFilePtr)
{
   int load_intra_quantiser_matrix,load_non_intra_quantiser_matrix;

   BitFileReadBits(bitFilePtr,32,"sequence_header_code");
   horizontal_size = BitFileReadBits(bitFilePtr,12,"horizontal_size_value");
   vertical_size = BitFileReadBits(bitFilePtr,12,"vertical_size_value");
   BitFileReadBits(bitFilePtr,4,"aspect_ratio_information");
   BitFileReadBits(bitFilePtr,4,"frame_rate_code");
   bit_rate = BitFileReadBits(bitFilePtr,18,"bit_rate_value");
   BitFileReadBits(bitFilePtr,1,"marker_bit (must be 1)");
   vbv_buffer_size = BitFileReadBits(bitFilePtr,10,"vbv_buffer_size_value");
   BitFileReadBits(bitFilePtr,1,"constrained_parameters_flag");
   load_intra_quantiser_matrix = BitFileReadBits(bitFilePtr,1,"load_intra_quantiser_matrix");
   if(load_intra_quantiser_matrix) {
      int matrixIndex;
      for(matrixIndex=0;matrixIndex<64;matrixIndex++)
         BitFileReadBits(bitFilePtr,8,"intra_quantiser_matrix");
   };
   load_non_intra_quantiser_matrix = BitFileReadBits(bitFilePtr,1,"load_non_intra_quantiser_matrix");
   if(load_non_intra_quantiser_matrix) {
      int matrixIndex;
      for(matrixIndex=0;matrixIndex<64;matrixIndex++)
         BitFileReadBits(bitFilePtr,8,"non_intra_quantiser_matrix");
   };
   BitFileReadBits(bitFilePtr,1,"constrained_parameters_flag");
}

static void ParseSequenceExtension(BitFile *bitFilePtr)
{
   BitFileReadBits(bitFilePtr,4,"extension_start_code_identifier = sequence extension");
   BitFileReadBits(bitFilePtr,8,"profile_and_level_indication");
   progressive_sequence = BitFileReadBits(bitFilePtr,1,"progressive_sequence");
   BitFileReadBits(bitFilePtr,2,"chroma_format");
   horizontal_size = (horizontal_size&0xfff) | (BitFileReadBits(bitFilePtr,2,"horizontal_size_extension") << 12);
   vertical_size = (vertical_size&0xfff) | (BitFileReadBits(bitFilePtr,2,"vertical_size_extension") << 12);
   bit_rate = (bit_rate&0xfff) | (BitFileReadBits(bitFilePtr,12,"bit_rate_extension") << 12);
   BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)");
   vbv_buffer_size = (vbv_buffer_size&0x3ff) | (BitFileReadBits(bitFilePtr,8,"vbv_buffer_size_extension") << 10);
   low_delay = BitFileReadBits(bitFilePtr,1,"low_delay");
   BitFileReadBits(bitFilePtr,2,"frame_rate_extension_n");
   BitFileReadBits(bitFilePtr,5,"frame_rate_extension_d");
}

static void ParseSequenceDisplayExtension(BitFile *bitFilePtr)
{
   int colour_description;

   BitFileReadBits(bitFilePtr,4,"extension_start_code_identifier = sequence display");
   BitFileReadBits(bitFilePtr,3,"video_format");
   colour_description = BitFileReadBits(bitFilePtr,1,"colour_description");
   if(colour_description) {
      BitFileReadBits(bitFilePtr,8,"colour_primaries");
      BitFileReadBits(bitFilePtr,8,"transfer_characteristics");
      BitFileReadBits(bitFilePtr,8,"matrix_coefficients");
   };
   BitFileReadBits(bitFilePtr,14,"display_horizontal_size");
   BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)");
   BitFileReadBits(bitFilePtr,14,"display_vertical_size");
}

static void ParseSequenceScalableExtension(BitFile *bitFilePtr)
{
   int scalable_mode;

   BitFileReadBits(bitFilePtr,4,"extension_start_code_identifier = sequence scalable");
   scalable_mode = BitFilePeekBits(bitFilePtr,2,"scalable_mode");
   if(scalable_mode == 1) { /* spatial scalability */
      BitFileReadBits(bitFilePtr,2,"scalable_mode = spatial scalability");
      BitFileReadBits(bitFilePtr,4,"layer_id");
      BitFileReadBits(bitFilePtr,14,"lower_layer_prediction_horizontal_size");
      BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)");
      BitFileReadBits(bitFilePtr,14,"lower_layer_prediction_vertical_size");
      BitFileReadBits(bitFilePtr,5,"horizontal_subsampling_factor_m");
      BitFileReadBits(bitFilePtr,5,"horizontal_subsampling_factor_n");
      BitFileReadBits(bitFilePtr,5,"vertical_subsampling_factor_m");
      BitFileReadBits(bitFilePtr,5,"vertical_subsampling_factor_n");
   } else if(scalable_mode == 3) { /* temporal scalability */
      int picture_mux_enable;
      BitFileReadBits(bitFilePtr,2,"scalable_mode = temporal scalability");
      BitFileReadBits(bitFilePtr,4,"layer_id");
      picture_mux_enable = BitFileReadBits(bitFilePtr,1,"picture_mux_enable");
      if(picture_mux_enable) 
         BitFileReadBits(bitFilePtr,1,"picture_mux_enable");
      BitFileReadBits(bitFilePtr,3,"picture_mux_order");
      BitFileReadBits(bitFilePtr,3,"picture_mux_factor");
   } else if(scalable_mode == 2) { /* SNR scalability */
      BitFileReadBits(bitFilePtr,2,"scalable_mode = SNR");
      BitFileReadBits(bitFilePtr,4,"layer_id");
   } else {
      BitFileReadBits(bitFilePtr,2,"scalable_mode = data partitioning");
      BitFileReadBits(bitFilePtr,4,"layer_id");
   };
}

static void ParseUserData(BitFile *bitFilePtr)
{
   BitFileReadBits(bitFilePtr,32,"user_data_start_code");   /* 0x000001b2 */
   while(BitFilePeekBits(bitFilePtr,24,NULL) != 0x000001)
      BitFileReadBits(bitFilePtr,8,"user_data");
}

static void ParseGroupOfPicturesHeader(BitFile *bitFilePtr)
{
   BitFileReadBits(bitFilePtr,32,"group_start_code");   /* 0x000001?? */
   BitFileReadBits(bitFilePtr,25,"time_code");
   BitFileReadBits(bitFilePtr,1,"closed_gop");
   BitFileReadBits(bitFilePtr,1,"broken_link");
}

static void ParsePictureHeader(BitFile *bitFilePtr)
{
   int picture_coding_type;

   BitFileReadBits(bitFilePtr,32,"picture_start_code");
   BitFileReadBits(bitFilePtr,10,"temporal_reference");
   picture_coding_type = BitFileReadBits(bitFilePtr,3,"picture_coding_type");
   BitFileReadBits(bitFilePtr,16,"vbv_delay");
   if(picture_coding_type == 1 || picture_coding_type == 3) {
      BitFileReadBits(bitFilePtr,1,"full_pel_forward_vector");
      BitFileReadBits(bitFilePtr,3,"forward_f_code");
   };
   if(picture_coding_type == 3) {
      BitFileReadBits(bitFilePtr,1,"full_pel_backward_vector");
      BitFileReadBits(bitFilePtr,3,"backward_f_code");
   };
   for(;;) {
      if(BitFileReadBits(bitFilePtr,1,"extra_bit_picture") == 0)
         break;
      BitFileReadBits(bitFilePtr,8,"extra_information_picture");
   };
}

static void ParseCodingExtension(BitFile *bitFilePtr)
{
   int composite_display_flag;

   BitFileReadBits(bitFilePtr,4,"extension_start_code_identifier = picture coding");   
   BitFileReadBits(bitFilePtr,4,"fcode[0][0]");   
   BitFileReadBits(bitFilePtr,4,"fcode[0][1]");   
   BitFileReadBits(bitFilePtr,4,"fcode[1][0]");   
   BitFileReadBits(bitFilePtr,4,"fcode[1][1]");   
   BitFileReadBits(bitFilePtr,3,"intra_dc_precision");   
   BitFileReadBits(bitFilePtr,3,"picture_structure");   
   BitFileReadBits(bitFilePtr,3,"top_field_first");   
   BitFileReadBits(bitFilePtr,3,"frame_pred_frame_dct");   
   BitFileReadBits(bitFilePtr,3,"concealment_motion_vectors");   
   BitFileReadBits(bitFilePtr,3,"q_scale_type");   
   BitFileReadBits(bitFilePtr,3,"intra_vlc_format");   
   BitFileReadBits(bitFilePtr,3,"alternate_scan");   
   BitFileReadBits(bitFilePtr,3,"repeat_first_field");   
   BitFileReadBits(bitFilePtr,3,"chroma_420_type");   
   BitFileReadBits(bitFilePtr,3,"progressive_frame");   
   composite_display_flag = BitFileReadBits(bitFilePtr,3,"composite_display_flag");   
   if(composite_display_flag) {
      BitFileReadBits(bitFilePtr,3,"v_axis");   
      BitFileReadBits(bitFilePtr,3,"field_sequence");   
      BitFileReadBits(bitFilePtr,3,"sub_carrier");   
      BitFileReadBits(bitFilePtr,3,"burst_amplitude");   
      BitFileReadBits(bitFilePtr,3,"sub_carrier_phase");   
   };
}

static void ParseQuantMatrixExtension(BitFile *bitFilePtr)
{
   int load_intra_quantiser_matrix,load_non_intra_quantiser_matrix;
   int load_chroma_intra_quantiser_matrix,load_chroma_non_intra_quantiser_matrix;

   BitFileReadBits(bitFilePtr,4,"extension_start_code_identifier = quant matrix");   
   load_intra_quantiser_matrix = BitFileReadBits(bitFilePtr,4,"load_intra_quantiser_matrix");   
   if(load_intra_quantiser_matrix) {
      int matrixIndex;
      for(matrixIndex=0;matrixIndex<64;matrixIndex++)
         BitFileReadBits(bitFilePtr,8,"intra_quantiser_matrix");
   }
   load_non_intra_quantiser_matrix = BitFileReadBits(bitFilePtr,1,"load_non_intra_quantiser_matrix");
   if(load_non_intra_quantiser_matrix) {
      int matrixIndex;
      for(matrixIndex=0;matrixIndex<64;matrixIndex++)
         BitFileReadBits(bitFilePtr,8,"non_intra_quantiser_matrix");
   };
   load_chroma_intra_quantiser_matrix = BitFileReadBits(bitFilePtr,4,"load_chroma_intra_quantiser_matrix");   
   if(load_chroma_intra_quantiser_matrix) {
      int matrixIndex;
      for(matrixIndex=0;matrixIndex<64;matrixIndex++)
         BitFileReadBits(bitFilePtr,8,"chroma_intra_quantiser_matrix");
   }
   load_chroma_non_intra_quantiser_matrix = BitFileReadBits(bitFilePtr,1,"load_chroma_non_intra_quantiser_matrix");
   if(load_chroma_non_intra_quantiser_matrix) {
      int matrixIndex;
      for(matrixIndex=0;matrixIndex<64;matrixIndex++)
         BitFileReadBits(bitFilePtr,8,"chroma_non_intra_quantiser_matrix");
   };
}

static void ParsePictureDisplayExtension(BitFile *bitFilePtr)
{
   int frameCenterOffsetIndex;

   BitFileReadBits(bitFilePtr,4,"extension_start_code_identifier = sequence display");   
#if 0
   for(frameCenterOffsetIndex=0;frameCenterOffsetIndex < ;frameCenterOffsetIndex) {
      BitFileReadBits(bitFilePtr,16,"frame_centre_horizontal_offset");   
      BitFileReadBits(bitFilePtr,1,"marker_bit (must be 1)");   
      BitFileReadBits(bitFilePtr,16,"frame_centre_vertical_offset");   
      BitFileReadBits(bitFilePtr,1,"marker_bit (must be 1)");   
   };
#endif
}

static void ParsePictureTemporalScalableExtension(BitFile *bitFilePtr)
{
   BitFileReadBits(bitFilePtr,4,"extension_start_code_identifier = picture temporal scalable");   
   BitFileReadBits(bitFilePtr,2,"referece_select_code");   
   BitFileReadBits(bitFilePtr,10,"forward_temporal_reference");   
   BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)");   
   BitFileReadBits(bitFilePtr,10,"backward_temporal_reference");   
}

static void ParsePictureSpatialScalableExtension(BitFile *bitFilePtr)
{
   BitFileReadBits(bitFilePtr,4,"extension_start_code_identifier = picture spatial scalable");   
   BitFileReadBits(bitFilePtr,10,"lower_layer_temporal_reference");   
   BitFileReadBits(bitFilePtr,1,"marker_bit (must be 1)");   
   BitFileReadBits(bitFilePtr,10,"lower_layer_horizontal_offset");   
   BitFileReadBits(bitFilePtr,1,"marker_bit (must be 1)");   
   BitFileReadBits(bitFilePtr,10,"lower_layer_vertical_offset");   
   BitFileReadBits(bitFilePtr,2,"spatial_temporal_weight_code_table_index");   
   BitFileReadBits(bitFilePtr,1,"lower_layer_progressive_frame");   
   BitFileReadBits(bitFilePtr,1,"lower_layer_deinterlaced_field_select");   
}

static void ParseCopyrightExtension(BitFile *bitFilePtr)
{
   BitFileReadBits(bitFilePtr,4,"extension_start_code_identifier = copyright");   
   BitFileReadBits(bitFilePtr,1,"copyright_flag");   
   BitFileReadBits(bitFilePtr,8,"copyright_identifier");   
   BitFileReadBits(bitFilePtr,1,"original_or_copy");   
   BitFileReadBits(bitFilePtr,7,"reserved");   
   BitFileReadBits(bitFilePtr,1,"marker_bit (must be 1)");   
   BitFileReadBits(bitFilePtr,20,"copyright_number_1");     /* should be 22? */
   BitFileReadBits(bitFilePtr,1,"marker_bit (must be 1)");   
   BitFileReadBits(bitFilePtr,22,"copyright_number_2");   
   BitFileReadBits(bitFilePtr,1,"marker_bit (must be 1)");   
   BitFileReadBits(bitFilePtr,22,"copyright_number_3");   
}

static void ParseExtension(BitFile *bitFilePtr)
{
   int extension_start_code_identifier;

   BitFileReadBits(bitFilePtr,32,"extension_start_code");   
   extension_start_code_identifier = BitFilePeekBits(bitFilePtr,4,NULL);  
   switch(extension_start_code_identifier) {
   case 1:
      ParseSequenceExtension(bitFilePtr);
      break;
   case 2:
      ParseSequenceDisplayExtension(bitFilePtr);
      break;
   case 3:
      ParseQuantMatrixExtension(bitFilePtr);
      break;
   case 4:
      ParseCopyrightExtension(bitFilePtr);
      break;
   case 5:
      ParseSequenceScalableExtension(bitFilePtr);
      break;
   case 7:
      ParsePictureDisplayExtension(bitFilePtr);
      break;
   case 8:
      ParseCodingExtension(bitFilePtr);
      break;
   case 9:
      ParsePictureSpatialScalableExtension(bitFilePtr);
      break;
   case 10:
      ParsePictureTemporalScalableExtension(bitFilePtr);
      break;
   default:
      BitFileReadBits(bitFilePtr,4,"extension_start_code_identifier = reserved");   
      break;
   };
}

static void ParseMacroblock(BitFile *bitFilePtr)
{



}

static void ParsePictureData(BitFile *bitFilePtr)
{
   int extra_bit_slice;

   BitFileReadBits(bitFilePtr,32,"slice_start_code");   
   if(vertical_size > 2800)
      BitFileReadBits(bitFilePtr,3,"slice_vertical_position_extension");   
#if 0
   if(sequence_scalable_extension && scalable_mode == 0) {  /* if data_partitioning */
      BitFileReadBits(bitFilePtr,7,"priority_breakpoint");   
   };
#endif
   BitFileReadBits(bitFilePtr,7,"quantiser_scale_code");   
   extra_bit_slice  = BitFilePeekBits(bitFilePtr,1,NULL);
   if(extra_bit_slice) {  
      BitFileReadBits(bitFilePtr,1,"intra_slice_flag"); /* would be 1 */
      BitFileReadBits(bitFilePtr,1,"intra_slice");   
      BitFileReadBits(bitFilePtr,7,"reserved_bits");   
      for(;;) {
         extra_bit_slice = BitFilePeekBits(bitFilePtr,1,NULL);
         if(extra_bit_slice == 0)    
            break;
         BitFileReadBits(bitFilePtr,1,"extra_bit_slice");  /* would be 1 */ 
         BitFileReadBits(bitFilePtr,8,"extra_infomation_slice");   
      };
   };
   BitFilePeekBits(bitFilePtr,1,"extra_bit_slice");  /* would be 0 */
   while(BitFilePeekBits(bitFilePtr,23,NULL) != 0) {
      ParseMacroblock(bitFilePtr);
   };
}

static void ParseVideo(BitFile *bitFilePtr)
{
   int bitsToSkip;

   for(;;) {  /* for every byte until we find a start code. */
      if(BitFilePeekBits(bitFilePtr,32,NULL) == 0x000001b3) { /* if a sequence_header_code */
         ParseSequenceHeader(bitFilePtr);
      } else if(BitFilePeekBits(bitFilePtr,32,NULL) == 0x000001b5) { /* if a extension_start_code */
         ParseSequenceExtension(bitFilePtr);
      } else if(BitFilePeekBits(bitFilePtr,32,NULL) == 0x00000100) { /* if a picture_start_code */
         ParsePicture(bitFilePtr);
      } else if(BitFilePeekBits(bitFilePtr,32,NULL) == 0x000001b2) { /* if a user_data_start_code */
         ParseUserData(bitFilePtr);
      } else if(BitFilePeekBits(bitFilePtr,32,NULL) == 0x000001b7) { /* if a sequence_end_code */
         BitFileReadBits(bitFilePtr,32,"sequence_end_code");
      } else if(BitFilePeekBits(bitFilePtr,24,NULL) == 0x000001) { /* if a start code */
         BitFileReadBits(bitFilePtr,32,"other start code");
      } else {
         bitsToSkip = 8 - (BitFileFTell(bitFilePtr)&0x7);  /* between 1 and 8 to get us to next byte start */
         BitFileReadBits(bitFilePtr,bitsToSkip,"resyncSkip");
         if(BitFileEOF(bitFilePtr))
            return;
      };
   };
}

static void PrintHelp(void)
{
   printf("usage: videobits [options ..]\n"); 
   printf("   --vob=filename (default is vts_01_1.vob\n"); 
   printf("   --bittrace=filename (default is videotrace\n"); 
   printf("\n"); 
   printf("  videobits version 1.0\n"); 
   printf("  Written by Rick Dean <vobTools@fdd.com>.\n");
   printf("  Copyright (c) 1999 Rick Dean."); 
   printf("  Released under version 2 of the GNU Public License.\n");
   exit(0);
}

static void ReadCommandLineParms(int argc,char *argv[])
{

   argc--;  /* skip name of program */
   argv++;  /* skip name of program */
   for(;argc;argc--,argv++) {  /* for every arg */
      if(strncasecmp(*argv,"--bittrace",10) == 0 && ((*argv)[10] == '\0' || (*argv)[0] == '=')) {
          traceFlag = 1;
          if((*argv)[0] == '=') {
             strncpy(traceFilename,&(*argv)[10],sizeof(traceFilename));
             traceFilename[sizeof(traceFilename)-1] = '\0';
          };
      } else if(strncasecmp(*argv,"--vob=",6) == 0) {
          strncpy(vobFilename,&(*argv)[6],sizeof(vobFilename));
          vobFilename[sizeof(vobFilename)-1] = '\0';
      } else if(strncasecmp(*argv,"--stream_id=",12) == 0) {
          if(strncasecmp(*argv,"--stream_id=0x",14) == 0)   /* if hex number specified */
             sscanf(*argv+12,"%x",&desiredStreamId);
          else
             desiredStreamId = atoi(*argv+10);  
          if(desiredStreamId < 0xbc || desiredStreamId > 0xff) {
             fprintf(stderr,"bad stream_id in %s\n",*argv);
             exit(-11);
          }
      } else {
          printf("problem with command arg \"%s\"\n",*argv);
          PrintHelp();
      };
   };
}

int main(int argc,char *argv[])
{
   BitFile *bitFilePtr;
  
   ReadCommandLineParms(argc,argv);
   bitFilePtr = NewBitFile();
   OpenReadBitFile(bitFilePtr,vobFilename,traceFlag?traceFilename:NULL);   
   ParseVideo(bitFilePtr);
   CloseBitFile(bitFilePtr);
   FreeBitFile(&bitFilePtr);
   return 0;
}





/* end of file */

