
/*  (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 systems standard (a.k.a. 13818-1 or N0801). */

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

/* This too was (obviously) not written for performance. */

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

char vobFilename[MAX_NAME_LENGTHS] = "vts_01_1.vob"; 
char traceFilename[MAX_NAME_LENGTHS] = "vobtrace";  
char rootName[MAX_NAME_LENGTHS] = ""; 
char demuxPath[MAX_NAME_LENGTHS] = "./"; 
int traceFlag = 0;
int tracePayload = 0;
int traceErrors = 0;
int commentaryFlag = 1;
int summaryFlag = 1;
int demuxAllFlag = 1;
int demuxOneId = 0;
char demuxOneFilename[MAX_NAME_LENGTHS] = "-";

int foundGarbageFlag;
int numPesPacketsFound;
int numPacksFound;

#if 0
static void ParseAdaptationField(BitFile *bitFilePtr)
{
   int adaptation_field_length;

   adaptation_field_length = BitFileReadBits(bitFilePtr,8,"adaptation_field_length");
   if(adaptation_field_length > 0) {
      int PCR_flag,OPCR_flag,splicing_point_flag,transport_private_data_flag,adaptation_field_extension_flag;
      int adaptationFieldStartFTell;
      adaptationFieldStartFTell = BitFileFTell(bitFilePtr);
      BitFileReadBits(bitFilePtr,1,"discontinuity_indicator");
      BitFileReadBits(bitFilePtr,1,"random_access_indicator");
      BitFileReadBits(bitFilePtr,1,"elementary_stream_priority_indicator");
      PCR_flag = BitFileReadBits(bitFilePtr,1,"PCR_flag");
      OPCR_flag = BitFileReadBits(bitFilePtr,1,"OPCR_flag");
      splicing_point_flag = BitFileReadBits(bitFilePtr,1,"splicing_point_flag");
      transport_private_data_flag = BitFileReadBits(bitFilePtr,1,"transport_private_data_flag");
      adaptation_field_extension_flag = BitFileReadBits(bitFilePtr,1,"adaptation_field_extension_flag");
      if(PCR_flag) {
         BitFileReadBits(bitFilePtr,32,"program_clock_reference_base");
         BitFileReadBits(bitFilePtr,1,"program_clock_reference_base");
         BitFileReadBits(bitFilePtr,6,"reserved");
         BitFileReadBits(bitFilePtr,9,"program_clock_reference_extension");
      };
      if(OPCR_flag) {
         BitFileReadBits(bitFilePtr,32,"original_program_clock_reference_base");
         BitFileReadBits(bitFilePtr,1,"original_program_clock_reference_base");
         BitFileReadBits(bitFilePtr,6,"reserved");
         BitFileReadBits(bitFilePtr,9,"original_program_clock_reference_extension");
      };
      if(splicing_point_flag) 
         BitFileReadBits(bitFilePtr,8,"splice_countdown");
      if(transport_private_data_flag) {
         int transport_private_data_length;
         transport_private_data_length = BitFileReadBits(bitFilePtr,8,"transport_private_data_length");
         for(;transport_private_data_length;transport_private_data_length--) 
            BitFileReadBits(bitFilePtr,8,"private_data_byte");
      };
      if(adaptation_field_extension_flag) {
         int adaptation_field_extension_length,ltw_flag,piecewise_rate_flag,seamless_splice_flag;
         int adaptationFieldExtensionStartFTell;
         adaptation_field_extension_length = BitFileReadBits(bitFilePtr,8,"adaptation_field_extension_length");
         adaptationFieldExtensionStartFTell = BitFileFTell(bitFilePtr);
         ltw_flag = BitFileReadBits(bitFilePtr,1,"ltw_flag");
         piecewise_rate_flag = BitFileReadBits(bitFilePtr,1,"piecewise_rate_flag");
         seamless_splice_flag = BitFileReadBits(bitFilePtr,1,"seamless_splice_flag");
         BitFileReadBits(bitFilePtr,5,"reserved");
         if(ltw_flag) {
            BitFileReadBits(bitFilePtr,1,"ltw_valid_flag");
            BitFileReadBits(bitFilePtr,15,"ltw_offset");
         };
         if(piecewise_rate_flag) {
            BitFileReadBits(bitFilePtr,2,"reserved");
            BitFileReadBits(bitFilePtr,22,"piecewise_rate");
         };
         if(seamless_splice_flag) {
            BitFileReadBits(bitFilePtr,4,"splice_type");
            BitFileReadBits(bitFilePtr,3,"DTS_next_AU[32..30]");
            BitFileReadBits(bitFilePtr,1,"marker_bit (shoudl be 1)");
            BitFileReadBits(bitFilePtr,15,"DTS_next_AU[29..15]");
            BitFileReadBits(bitFilePtr,1,"marker_bit (shoudl be 1)");
            BitFileReadBits(bitFilePtr,15,"DTS_next_AU[14..0]");
            BitFileReadBits(bitFilePtr,1,"marker_bit (shoudl be 1)");
         };
         for(;BitFileFTell(bitFilePtr)-adaptationFieldExtensionStartFTell < adaptation_field_extension_length;)
            BitFileReadBits(bitFilePtr,8,"reserved (adaptation_field_extension)");
      };
      for(;BitFileFTell(bitFilePtr)-adaptationFieldStartFTell < adaptation_field_length;)
         BitFileReadBits(bitFilePtr,8,"stuffing_byte (adaptation_field) (should be 0xff)");
   };
}

static void ParseAdaptationPayload(BitFile *bitFilePtr)
{

}

static void ParseTransportPacket(BitFile *bitFilePtr)
{
   int pid,adaptation_field_control;

   BitFileReadBits(bitFilePtr,8,"sync_byte (should be 0x47)");
   BitFileReadBits(bitFilePtr,1,"transport_error_indicator");
   BitFileReadBits(bitFilePtr,1,"payload_unit_start_indicator");
   BitFileReadBits(bitFilePtr,1,"transport_priority");
   pid = BitFilePeekBits(bitFilePtr,13,NULL);
   if(pid == 0) 
      BitFileReadBits(bitFilePtr,13,"PID=program association table");
   else if(pid == 1)
      BitFileReadBits(bitFilePtr,13,"PID=conditional access table");
   else if(pid < 0x000f)
      BitFileReadBits(bitFilePtr,13,"PID=reserved");
   else if(pid < 0x1ffe)
      BitFileReadBits(bitFilePtr,13,"PID=network, program_map, elementary, or other");
   else if(pid == 0x1fff)
      BitFileReadBits(bitFilePtr,13,"PID=null packet");
   BitFileReadBits(bitFilePtr,2,"transport_scrambling_control");
   adaptation_field_control = BitFileReadBits(bitFilePtr,2,"adaptation_field_control");
   BitFileReadBits(bitFilePtr,4,"continuity_counter");
   if((adaptation_field_control & 2)) 
      ParseAdaptationField(bitFilePtr);
   if((adaptation_field_control & 1)) 
      ParseAdaptationPayload(bitFilePtr);
}
#endif

static void ParseSystemHeader(BitFile *bitFilePtr)
{
   BitFileReadBits(bitFilePtr,32,"system_header_start_code");  /* should be 0x000001bb */
   BitFileReadBits(bitFilePtr,16,"header_length"); 
   BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)"); 
   BitFileReadBits(bitFilePtr,22,"rate_bound"); 
   BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)"); 
   BitFileReadBits(bitFilePtr,6,"audio_bound"); 
   BitFileReadBits(bitFilePtr,1,"fixed_flag"); 
   BitFileReadBits(bitFilePtr,1,"CSPS_flag"); 
   BitFileReadBits(bitFilePtr,1,"system_audio_lock_flag"); 
   BitFileReadBits(bitFilePtr,1,"system_video_lock_flag"); 
   BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)"); 
   BitFileReadBits(bitFilePtr,5,"video_bound"); 
   BitFileReadBits(bitFilePtr,1,"packet_rate_restriction_flag"); 
   BitFileReadBits(bitFilePtr,8,"reserved_byte"); 
   while(BitFilePeekBits(bitFilePtr,1,NULL) == 1) {
      BitFileReadBits(bitFilePtr,8,"stream_id");
      BitFileReadBits(bitFilePtr,2,"'11'");
      BitFileReadBits(bitFilePtr,1,"P-STD_buffer_bound_scale");
      BitFileReadBits(bitFilePtr,13,"P-STD_buffer_size_bound");
   };

}

static void ParsePackHeader(BitFile *bitFilePtr)
{
   int pack_stuffing_length;

   BitFileReadBits(bitFilePtr,32,"pack_start_code"); /* should be 0x000001ba */
   BitFileReadBits(bitFilePtr,2,"'01'"); 
   BitFileReadBits(bitFilePtr,3,"system_clock_reference_base [32..30]"); 
   BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)"); 
   BitFileReadBits(bitFilePtr,15,"system_clock_reference_base [29..15]"); 
   BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)"); 
   BitFileReadBits(bitFilePtr,15,"system_clock_reference_base [14..0]"); 
   BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)"); 
   BitFileReadBits(bitFilePtr,9,"system_clock_reference_extension"); 
   BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)"); 
   BitFileReadBits(bitFilePtr,22,"program_mux_rate"); 
   BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)"); 
   BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)"); 
   BitFileReadBits(bitFilePtr,5,"reserved"); 
   pack_stuffing_length = BitFileReadBits(bitFilePtr,3,"pack_stuffing_length"); 
   for(;pack_stuffing_length;pack_stuffing_length--) 
      BitFileReadBits(bitFilePtr,8,"stuffing_byte"); 
   if(BitFilePeekBits(bitFilePtr,32,NULL) == 0x000001bb)  /* system_header_start_code */
      ParseSystemHeader(bitFilePtr);
}

typedef struct {
   char parseChoice;
   char *namePtr;
   char *extension;
} Stream_id_consts;

#define NUM_STREAM_IDS (sizeof(stream_id_consts)/sizeof(*stream_id_consts))

Stream_id_consts stream_id_consts[] = {
      {0,"program_stream_map","-proram_map"},  /* stream_id = 0xbc */
      {1,"private_stream_1 (AC3 audio)",".ac3"},
      {2,"padding_stream",NULL},
      {1,"private_stream_2","-private_2"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #00",".mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #01","-audio01.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #02","-audio02.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #03","-audio03.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #04","-audio04.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #05","-audio05.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #06","-audio06.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #07","-audio07.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #08","-audio08.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #09","-audio09.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #10","-audio10.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #11","-audio11.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #12","-audio12.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #13","-audio13.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #14","-audio14.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #15","-audio15.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #16","-audio16.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #17","-audio17.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #18","-audio18.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #19","-audio19.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #20","-audio20.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #21","-audio21.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #22","-audio22.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #23","-audio23.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #24","-audio24.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #25","-audio25.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #26","-audio26.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #27","-audio27.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #28","-audio28.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #29","-audio29.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #30","-audio30.mp3"},
      {0,"ISO/IEC 13818-3 or 11172-3 audio #31","-audio31.mp3"},
      {0,"ISO/IEC 13818-2 or 11172-2 video #00",".mp2"},
      {0,"ISO/IEC 13818-2 or 11172-2 video #01","-video01.mp2"},
      {0,"ISO/IEC 13818-2 or 11172-2 video #02","-video02.mp2"},
      {0,"ISO/IEC 13818-2 or 11172-2 video #03","-video03.mp2"},
      {0,"ISO/IEC 13818-2 or 11172-2 video #04","-video04.mp2"},
      {0,"ISO/IEC 13818-2 or 11172-2 video #05","-video05.mp2"},
      {0,"ISO/IEC 13818-2 or 11172-2 video #06","-video06.mp2"},
      {0,"ISO/IEC 13818-2 or 11172-2 video #07","-video07.mp2"},
      {0,"ISO/IEC 13818-2 or 11172-2 video #08","-video08.mp2"},
      {0,"ISO/IEC 13818-2 or 11172-2 video #09","-video09.mp2"},
      {0,"ISO/IEC 13818-2 or 11172-2 video #10","-video10.mp2"},
      {0,"ISO/IEC 13818-2 or 11172-2 video #11","-video11.mp2"},
      {0,"ISO/IEC 13818-2 or 11172-2 video #12","-video12.mp2"},
      {0,"ISO/IEC 13818-2 or 11172-2 video #13","-video13.mp2"},
      {0,"ISO/IEC 13818-2 or 11172-2 video #14","-video14.mp2"},
      {0,"ISO/IEC 13818-2 or 11172-2 video #15","-video15.mp2"},
      {1,"ECM_stream",".ecm"},
      {1,"EMM_stream",".emm"},
      {1,"ITU-T H.222.0 | ISO/IEC 13818-1 annex A or ISO/IEC 12818-6_DSMCC_stream",".cc"},
      {0,"ISO/IEC_13522_stream",".13522"},
      {0,"ITU-T H.222.1 type A",".222-1-a"},
      {0,"ITU-T H.222.1 type B",".222-1-b"},
      {0,"ITU-T H.222.1 type C",".222-1-c"},
      {0,"ITU-T H.222.1 type D",".222-1-d"},
      {1,"ITU-T H.222.1 type E",".222-1-e"},
      {0,"ancillary_stream","-ancillary"},
      {0,"reserved data stream #0",NULL},
      {0,"reserved data stream #1",NULL},
      {0,"reserved data stream #2",NULL},
      {0,"reserved data stream #3",NULL},
      {1,"program_stream_directory","-prog_dir"},
      {2,"illegal",NULL}};

typedef struct {
   int numBytesFound;
   int numPacketsFound;
   FILE *filePtr;
} Stream_id_stats;

Stream_id_stats stream_id_stats[NUM_STREAM_IDS];

static void DoPacketData(int stream_id,int numBytesThisPacket,BitFile *bitFilePtr,char *commentPtr)
{
   char demuxFilename[sizeof(demuxPath)+sizeof(rootName)+128],dataChar;

   if(demuxAllFlag && stream_id_stats[stream_id-0xbc].filePtr == NULL && stream_id_consts[stream_id-0xbc].extension) {
      strcpy(demuxFilename,demuxPath);
      strcat(demuxFilename,rootName);
      strcat(demuxFilename,stream_id_consts[stream_id-0xbc].extension);
      stream_id_stats[stream_id-0xbc].filePtr = fopen(demuxFilename,"wb");
      if(stream_id_stats[stream_id-0xbc].filePtr == NULL) {
         fprintf(stderr,"ERROR: could not open \"%s\"\n",demuxFilename); 
         exit(-7);
      };
   };
   if(commentaryFlag) {
       if(stream_id_stats[stream_id-0xbc].numPacketsFound == 0)
          printf("found %s\n",stream_id_consts[stream_id-0xbc].namePtr);
   };
   stream_id_stats[stream_id-0xbc].numPacketsFound++;
   stream_id_stats[stream_id-0xbc].numBytesFound += numBytesThisPacket;
   for(;numBytesThisPacket;numBytesThisPacket--) {
      dataChar = BitFileReadBits(bitFilePtr,8,tracePayload?commentPtr:NULL); 
      if(stream_id_stats[stream_id-0xbc].filePtr)
         if(EOF == fputc(dataChar,stream_id_stats[stream_id-0xbc].filePtr)) {
            fprintf(stderr,"ERROR: could not write to demux file (stream_id=0x%x)\n",stream_id);
            exit(-9);
         };
   };
}

static void ParsePesPacket(BitFile *bitFilePtr) 
{
   int stream_id,PES_packet_length, pesPacketDataStartFTell,numDataBytes;
   char stream_id_comment[128],*dataCommentPtr;

   numPesPacketsFound++;
   BitFileReadBits(bitFilePtr,24,"packet_start_code_prefix"); /* should be 0x000001 */
   stream_id = BitFilePeekBits(bitFilePtr,8,NULL); 
   strcpy(stream_id_comment,"stream_id = ");
   strcpy(stream_id_comment+12,stream_id_consts[stream_id-0xbc].namePtr);
   BitFileReadBits(bitFilePtr,8,stream_id_comment); 
   PES_packet_length = BitFileReadBits(bitFilePtr,16,"PES_packet_length"); 
   pesPacketDataStartFTell = BitFileFTell(bitFilePtr);
   if(stream_id_consts[stream_id-0xbc].parseChoice == 0) {
      int PTS_DTS_flags, ESCR_flag, ES_rate_flag, DSM_trick_mode_flag, additional_copy_info_flag;
      int PES_CRC_flag, PES_extension_flag, PES_header_data_length;
      BitFileReadBits(bitFilePtr,2,"'10'"); 
      if(BitFilePeekBits(bitFilePtr,2,NULL) == 0) 
         BitFileReadBits(bitFilePtr,2,"PES_scrambling_control = not scrambled"); 
      else
         BitFileReadBits(bitFilePtr,2,"PES_scrambling_control = user defined"); 
      BitFileReadBits(bitFilePtr,1,"PES_priority"); 
      BitFileReadBits(bitFilePtr,1,"data_alignment_indicator"); 
      if(BitFilePeekBits(bitFilePtr,1,NULL))
         BitFileReadBits(bitFilePtr,1,"Copyright = yes"); 
      else
         BitFileReadBits(bitFilePtr,1,"Copyright = status not specified"); 
      if(BitFilePeekBits(bitFilePtr,1,NULL))
         BitFileReadBits(bitFilePtr,1,"original_or_copy = original"); 
      else
         BitFileReadBits(bitFilePtr,1,"original_or_copy = copy"); 
      PTS_DTS_flags =             BitFileReadBits(bitFilePtr,2,"PTS_DTS_flags"); 
      ESCR_flag =                 BitFileReadBits(bitFilePtr,1,"ESCR_flag"); 
      ES_rate_flag =              BitFileReadBits(bitFilePtr,1,"ES_rate_flag"); 
      DSM_trick_mode_flag =       BitFileReadBits(bitFilePtr,1,"DSM_trick_mode_flag"); 
      additional_copy_info_flag = BitFileReadBits(bitFilePtr,1,"additional_copy_info_flag"); 
      PES_CRC_flag =              BitFileReadBits(bitFilePtr,1,"PES_CRC_flag"); 
      PES_extension_flag =        BitFileReadBits(bitFilePtr,1,"PES_extension_flag"); 
      PES_header_data_length =    BitFileReadBits(bitFilePtr,8,"PES_header_data_length"); 
      if((PTS_DTS_flags&2)) {
         BitFileReadBits(bitFilePtr,4,"'0010'"); 
         BitFileReadBits(bitFilePtr,3,"PTS [32..30]"); 
         BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)"); 
         BitFileReadBits(bitFilePtr,15,"PTS [29..15]"); 
         BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)"); 
         BitFileReadBits(bitFilePtr,15,"PTS [14..0]"); 
         BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)"); 
      };
      if((PTS_DTS_flags&2)) {
         BitFileReadBits(bitFilePtr,4,"'0001'"); 
         BitFileReadBits(bitFilePtr,3,"DTS [32..30]"); 
         BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)"); 
         BitFileReadBits(bitFilePtr,15,"DTS [29..15]"); 
         BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)"); 
         BitFileReadBits(bitFilePtr,15,"DTS [14..0]"); 
         BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)"); 
      };
      if(ESCR_flag) {
         BitFileReadBits(bitFilePtr,2,"reserved"); 
         BitFileReadBits(bitFilePtr,3,"ESCR [32..30]"); 
         BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)"); 
         BitFileReadBits(bitFilePtr,15,"ESCR [29..15]"); 
         BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)"); 
         BitFileReadBits(bitFilePtr,15,"ESCR [14..0]"); 
         BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)"); 
         BitFileReadBits(bitFilePtr,9,"ESCR_extension"); 
         BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)"); 
      };
      if(ES_rate_flag) {
         BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)"); 
         BitFileReadBits(bitFilePtr,22,"ES_rate"); 
         BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)"); 
      };
      if(DSM_trick_mode_flag) {
         int trick_mode_control;
         trick_mode_control = BitFilePeekBits(bitFilePtr,3,"trick_mode_control"); 
         if(trick_mode_control ==  0) {
            trick_mode_control = BitFileReadBits(bitFilePtr,3,"trick_mode_control = fast forward"); 
            BitFileReadBits(bitFilePtr,2,"field_id"); 
            BitFileReadBits(bitFilePtr,1,"intra_slice_refresh"); 
            BitFileReadBits(bitFilePtr,2,"frequency_truncation"); 
         } else if(trick_mode_control ==  1) {
            trick_mode_control = BitFileReadBits(bitFilePtr,3,"trick_mode_control = slow motion"); 
            BitFileReadBits(bitFilePtr,5,"rep_ctrl"); 
         } else if(trick_mode_control ==  2) {
            trick_mode_control = BitFileReadBits(bitFilePtr,3,"trick_mode_control = freeze frame"); 
            BitFileReadBits(bitFilePtr,2,"field_id"); 
            BitFileReadBits(bitFilePtr,3,"reserved"); 
         } else if(trick_mode_control ==  3) {
            trick_mode_control = BitFileReadBits(bitFilePtr,3,"trick_mode_control = fast reverse"); 
            BitFileReadBits(bitFilePtr,2,"field_id"); 
            BitFileReadBits(bitFilePtr,1,"intra_slice_refresh"); 
            BitFileReadBits(bitFilePtr,2,"frequency_truncation"); 
         } else if(trick_mode_control ==  4) {
            trick_mode_control = BitFileReadBits(bitFilePtr,3,"trick_mode_control = slow reverse"); 
            BitFileReadBits(bitFilePtr,5,"rep_ctrl"); 
         } else {
            trick_mode_control = BitFileReadBits(bitFilePtr,3,"trick_mode_control = reserved"); 
            BitFileReadBits(bitFilePtr,5,"reserved"); 
         };
      };
      if(additional_copy_info_flag) {
         BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)"); 
         BitFileReadBits(bitFilePtr,7,"additional_copy_info"); 
      };
      if(PES_CRC_flag) {
         BitFileReadBits(bitFilePtr,16,"previous_PES_packet_CRC"); 
      };
      if(PES_extension_flag) {
         int PES_private_data_flag, pack_header_field_flag, program_packet_sequence_counter_flag;
         int P_STD_buffer_flag, PES_extension_flag_2,privateDataToGo;
         PES_private_data_flag = BitFileReadBits(bitFilePtr,1,"PES_private_data_flag"); 
         pack_header_field_flag = BitFileReadBits(bitFilePtr,1,"pack_header_field_flag"); 
         program_packet_sequence_counter_flag = BitFileReadBits(bitFilePtr,1,"program_packet_sequence_counter_flag"); 
         P_STD_buffer_flag = BitFileReadBits(bitFilePtr,1,"P-STD_buffer_flag"); 
         BitFileReadBits(bitFilePtr,3,"reserved"); 
         PES_extension_flag_2 = BitFileReadBits(bitFilePtr,1,"PES_extension_flag_2"); 
         if(PES_private_data_flag) {
            for(privateDataToGo = 128/4;privateDataToGo;privateDataToGo--) 
               BitFileReadBits(bitFilePtr,32,"PES_private_data"); 
         };
         if(pack_header_field_flag) {
            BitFileReadBits(bitFilePtr,8,"pack_field_length"); 
            ParsePackHeader(bitFilePtr);
         };
         if(program_packet_sequence_counter_flag) {
            BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)"); 
            BitFileReadBits(bitFilePtr,7,"program_packet_sequence_counter"); 
            BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)"); 
            BitFileReadBits(bitFilePtr,1,"MPEG1_MPEG2_identifier"); 
            BitFileReadBits(bitFilePtr,6,"original_stuff_length"); 
         };
         if(P_STD_buffer_flag) {
            BitFileReadBits(bitFilePtr,2,"'01'"); 
            BitFileReadBits(bitFilePtr,1,"P-STD_buffer_scale"); 
            BitFileReadBits(bitFilePtr,13,"P-STD_buffer_size"); 
         };
         if(PES_extension_flag_2) {
            int PES_extension_field_length;
            BitFileReadBits(bitFilePtr,1,"marker_bit (should be 1)"); 
            PES_extension_field_length = BitFileReadBits(bitFilePtr,7,"PES_extension_field_length"); 
            for(;PES_extension_field_length;PES_extension_field_length--) 
               BitFileReadBits(bitFilePtr,8,"reserved"); 
         };
      };
      while(BitFilePeekBits(bitFilePtr,8,NULL) == 0xff) 
         BitFileReadBits(bitFilePtr,8,"stuffing_byte"); 
      dataCommentPtr = "PES_packet_data_byte";
   } else if(stream_id_consts[stream_id-0xbc].parseChoice == 1) {
      dataCommentPtr = "PES_packet_data_byte";
   } else {  /* padding_stream */
      dataCommentPtr = "padding_byte (should be 0xff)";
   };
   numDataBytes = PES_packet_length-(BitFileFTell(bitFilePtr)-pesPacketDataStartFTell)/8;
   DoPacketData(stream_id,numDataBytes,bitFilePtr,dataCommentPtr);
}

static void ParsePack(BitFile *bitFilePtr)
{
   numPacksFound++;
   if(commentaryFlag && (numPacksFound&0x7ff) == 0x7ff) {
      printf(".");
      fflush(stdout);
   }
   ParsePackHeader(bitFilePtr); 
   while(BitFilePeekBits(bitFilePtr,24,NULL) == 0x000001 &&  /* packet_start_code_prefix */
         BitFilePeekBits(bitFilePtr,32,NULL) > 0x000001bc)  
      ParsePesPacket(bitFilePtr);
}

static void ParseVob(BitFile *bitFilePtr)
{
   long bitPositionOfVobStart;

   if(BitFilePeekBits(bitFilePtr,32,NULL) == 0x0000ba01 || 
      BitFilePeekBits(bitFilePtr,32,NULL) == 0xba010000 || 
      BitFilePeekBits(bitFilePtr,32,NULL) == 0x01ba0000) {
       printf("Looks like your words are swapped.\n");
       exit(1);
   };
   for(;;) {  /* for every PES (p-something elementary stream) packet */
      if((BitFileFTell(bitFilePtr)&7) != 0)
         BitFileReadBits(bitFilePtr,8 - (BitFileFTell(bitFilePtr)&7),"fractional byte resync skip");
      for(;;) {  /* for every byte until we find a start code */
         if(BitFilePeekBits(bitFilePtr,32,NULL) == 0x000001ba)  /* pack_start_code */
            break;
         if(BitFilePeekBits(bitFilePtr,32,NULL) == 0x000001b9) {  /* MPEG_program_end_code */
            BitFileReadBits(bitFilePtr,32,"MPEG_program_end_code");
            return;
         };
         if(foundGarbageFlag == 0) {
            foundGarbageFlag = 1;
            if(commentaryFlag) {
               if(BitFileFTell(bitFilePtr) == 0) 
                   printf("The file begins with some garbage.\n");
               else
                   printf("The file has some garbage.\n");
            }
         };
         BitFileReadBits(bitFilePtr,8,traceErrors?"resync skip":NULL);
         if(BitFileEOF(bitFilePtr)) {
            if(commentaryFlag)
                printf("ends without end_code\n");
            return;
         };
      };
      bitPositionOfVobStart = BitFileFTell(bitFilePtr);
      ParsePack(bitFilePtr);
      if(((BitFileFTell(bitFilePtr) - bitPositionOfVobStart)&0x1f))
         BitFileReadBits(bitFilePtr,32-((BitFileFTell(bitFilePtr) - bitPositionOfVobStart)&0x1f),"skip VOB data");
      while(BitFileFTell(bitFilePtr) - bitPositionOfVobStart < 2048*8)
         BitFileReadBits(bitFilePtr,32,"skip VOB data");
   };
}

static void DetermineRootName(void)
{
   int rootNameLength;
   char *slashPtr;

   slashPtr = strrchr(vobFilename,'/'); /* find last slash */
   if(slashPtr)
      strcpy(rootName,slashPtr+1);
   else
      strcpy(rootName,vobFilename);
   rootNameLength = strlen(rootName);
   if(strcasecmp(rootName+rootNameLength-4,".vob") == 0)
      rootName[rootNameLength-4] = '\0';  /* trim root name before optional .vob */
}

static void CloseDemuxFiles(void)
{
   int stream_id; 

   for(stream_id=0;stream_id < NUM_STREAM_IDS;stream_id++) { /* for every stream id */
       if(stream_id_stats[stream_id].filePtr)
          fclose(stream_id_stats[stream_id].filePtr);
   };
}

static void OpenDemuxOneFile(void)
{
   if(demuxOneId == 0)
      return;
   assert(stdout != NULL);
   if(strcmp(demuxOneFilename,"-") == 0)   /* if demux to standard out */
      stream_id_stats[demuxOneId-0xbc].filePtr = stdout;
   else {
      stream_id_stats[demuxOneId-0xbc].filePtr = fopen(demuxOneFilename,"wb");;
      if(stream_id_stats[demuxOneId-0xbc].filePtr == NULL) {
         fprintf(stderr,"ERROR: could not open demux file \"%s\"\n",demuxOneFilename);
         exit(-12);
      };
   };
}

static void PrintSummary(void)
{
   int stream_id; 

   if(commentaryFlag)
      printf("\n\n");
   printf("%d packs found (2048 bytes each)\n",numPacksFound);
   printf("%d PES (packetized elementary stream) packets found\n",numPesPacketsFound);
   printf("\n");
   printf("stream name                          id packets bytes\n");
   printf("------------------------------------ -- ------- ------------\n");
   for(stream_id=0;stream_id < NUM_STREAM_IDS;stream_id++) { /* for every stream id */
      if(stream_id_stats[stream_id].numPacketsFound == 0)  /* if we didn't find any packets of this kind */
         continue;  /* go do another stream */
      printf("%36.36s %x %7d %12d\n",
             stream_id_consts[stream_id].namePtr,stream_id+0xbc,
             stream_id_stats[stream_id].numPacketsFound,
             stream_id_stats[stream_id].numBytesFound);
   };
}

static void PrintHelpStreamId(void)
{
   int stream_id;

   printf("stream_id  name                                 def. exten.\n");
   printf("---------- ------------------------------------ ------------\n");
   for(stream_id=0;stream_id < NUM_STREAM_IDS;stream_id++) {
      printf("0x%2x = %2d %36.36s %12.12s\n",stream_id+0xbc,stream_id+0xbc,stream_id_consts[stream_id].namePtr,
             stream_id_consts[stream_id].extension);
   }; 
   exit(0);
}

static void PrintHelp(void)
{
   printf("usage: vobinfo [options ..]\n"); 
   printf("   --bittrace or --bittrace=filename (default is vts_01_1.vob)\n"); 
   printf("   --vob=filename (current is \"%s\")\n",vobFilename); 
   printf("   --notracepayload\n"); 
   printf("   --notraceerrors\n"); 
   printf("   --help-stream_id\n"); 
   printf("   --nocommentary\n"); 
   printf("   --nosummary\n"); 
   printf("   --demuxall\n"); 
   printf("   --demuxpath=path (default is ./)\n"); 
   printf("   --demuxid=number (default is no demuliplex)\n"); 
   printf("   --demuxfilename=filename (default is -)\n"); 
   printf("\n"); 
   printf("  vobinfo 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(strcasecmp(*argv,"--nocommentary") == 0) {
          commentaryFlag=0;
      } else if(strcasecmp(*argv,"--nosummary") == 0) {
          summaryFlag=0;
      } else if(strcasecmp(*argv,"--tracepayload") == 0) {
          tracePayload=1;
      } else if(strcasecmp(*argv,"--traceerrors") == 0) {
          traceErrors=1;
      } else if(strcasecmp(*argv,"--demuxall") == 0) {
          demuxAllFlag=1;
      } else if(strncasecmp(*argv,"--demuxid=",10) == 0) {
          if(strncasecmp(*argv,"--demuxid=0x",12) == 0)   /* if hex number specified */
             sscanf(*argv+12,"%x",&demuxOneId);
          else
             demuxOneId = atoi(*argv+10);  
          if(demuxOneId == 0 || (demuxOneId >= NUM_STREAM_IDS && demuxOneId < 0xbc) || demuxOneId >= 0xbc+NUM_STREAM_IDS) {
             fprintf(stderr,"bad stream id in %s\n",*argv);
             exit(-11);
          }
          if(demuxOneId < 0xbc)
             demuxOneId += 0xbc;
      } else if(strncasecmp(*argv,"--demuxfilename=",16) == 0) {
          strncpy(demuxOneFilename,&(*argv)[16],sizeof(demuxOneFilename));
          demuxOneFilename[sizeof(demuxOneFilename)-1] = '\0';
      } else if(strcasecmp(*argv,"--help-stream_id") == 0) {
          PrintHelpStreamId();
      } else if(strncasecmp(*argv,"--vob=",6) == 0) {
          strncpy(vobFilename,&(*argv)[6],sizeof(vobFilename));
          vobFilename[sizeof(vobFilename)-1] = '\0';
      } else if(strncasecmp(*argv,"--demuxpath=",12) == 0) {
          strncpy(demuxPath,&(*argv)[12],sizeof(demuxPath));
          demuxPath[sizeof(demuxPath)-1] = '\0';
      } else {
          printf("problem with command arg \"%s\"\n",*argv);
          PrintHelp();
      };
   };
   if(strcmp(demuxOneFilename,"-") == 0) /* if we are demux'ing one to standard out */
      commentaryFlag = summaryFlag = 0;
}

int main(int argc,char *argv[])
{
   BitFile *bitFilePtr;
  
   ReadCommandLineParms(argc,argv);
   DetermineRootName();
   OpenDemuxOneFile();
   bitFilePtr = NewBitFile();
   OpenReadBitFile(bitFilePtr,vobFilename,traceFlag?traceFilename:NULL);   
   ParseVob(bitFilePtr);   
   CloseBitFile(bitFilePtr);
   FreeBitFile(&bitFilePtr);
   if(summaryFlag)
      PrintSummary();
   CloseDemuxFiles();
   return 0;
}





/* end of file */

