/*
 * Copyright (c) 1997-2004 Alexandros Eleftheriadis, Danny Hong and
 * Yuntai Kyong.
 * 
 * This file is part of Flavor, developed at Columbia University
 * (www.ee.columbia.edu/flavor).
 *
 * Flavor is free software; you can redistribute it and/or modify
 * it under the terms of the Flavor Artistic License as described in
 * the file COPYING.txt. 
 *
 */

/* 
 * Authors:
 * Danny Hong <danny@ee.columbia.edu>
 * Jack Caldwell
 *
 */

/*
 * MPEG-2 Systems (Program Stream)
 *
 * This example reads MPEG-2 Systems PS streams.  It only parses the Systems data
 * and all elementary streams are skipped.
 */


%pragma array=256
%pragma get, trace

%.c{ 
#include  %.c}

///////////////
// PES Types //
///////////////
const unsigned int ST_PSMapTable    = 0x000001BC;
const unsigned int ST_Private1      = 0x000001BD;
const unsigned int ST_Padding       = 0x000001BE;
const unsigned int ST_Private2      = 0x000001BF;
const unsigned int ST_Audio1        = 0x000001C0;
const unsigned int ST_Audio2        = 0x000001DF;
const unsigned int ST_Video1        = 0x000001E0;
const unsigned int ST_Video2        = 0x000001EF;
const unsigned int ST_ECM           = 0x000001F0;
const unsigned int ST_EMM           = 0x000001F1;
const unsigned int ST_DSMCC         = 0x000001F2;
const unsigned int ST_ISO_13522     = 0x000001F3;
const unsigned int ST_ITUT_A        = 0x000001F4;
const unsigned int ST_ITUT_B        = 0x000001F5;
const unsigned int ST_ITUT_C        = 0x000001F6;
const unsigned int ST_ITUT_D        = 0x000001F7;
const unsigned int ST_ITUT_E        = 0x000001F8;
const unsigned int ST_PSDirectory   = 0x000001FF;

/////////////////
// Trick codes //
/////////////////
const unsigned int fast_forward     = 0b000;
const unsigned int slow_motion      = 0b001;
const unsigned int freeze_frame     = 0b010;
const unsigned int fast_reverse     = 0b011;
const unsigned int slow_reverse     = 0b100;

/* 
 * SystemHeader
 *
 */
class SystemHeader 
{
    nextcode(aligned(8), 0x000001BB);   // PS System Header 
    skipbits(32);                       // The next 32 bits must be the system header sc

    unsigned int(16) header_length;
    bit(1) marker = 0b1;
    unsigned int(22) rate_bound;        // UB on the program_mux_rate field
                                        // Should check that rate_bound >= max(program_mux_rate)
    bit(1) marker = 0b1;  
    unsigned int(6) audio_bound;        // UB on the number of active audio streams
    bit(1) fixed_flag;                  // 1 = fixed bit rate
    bit(1) CSPS_flag;                   // 1 = constrained params
    bit(1) system_audio_lock_flag;      // Locked to the STC
    bit(1) system_video_lock_flag;      // Locked to the STC
    bit(1) marker = 0b1;
    unsigned int(5) video_bound;        // UB on the number of video streams 
    bit(1) packet_rate_restriction_flag;
    const bit(7) reserved = 0x7F;
 
    while (nextbits(1) == 0b1) {
        unsigned int(8) stream_id;
        if (stream_id < 0xBC) {
            if (stream_id != 0xB8) {
                if (stream_id != 0xB9) {
                    %g{ 
                    print_error(); %g}
                }
            }
        } 

        bit(2) bit_pattern = 0b11;
        bit(1) P_STD_buffer_bound_scale;
        unsigned int(13) P_STD_buffer_size_bound;
    }
 
    %.c{
    void print_error() { printf("Error in SystemHeader! Wrong stream_id!"); } %.c}
    %.j{
    void print_error() { System.out.println("Error in SystemHeader! Wrong stream_id!"); } %.j}
}

/* 
 * PackHeader
 * 
 */
class PackHeader 
{
    nextcode(aligned(8), 0x000001BA); 
    skipbits(32);             

    bit(2) bit_pattern = 0b01;
    bit(3) system_clock_ref_1;
    bit(1) marker = 0b1;
    bit(15) system_clock_ref_2;
    bit(1) marker = 0b1;
    bit(15) system_clock_ref_3;
    bit(1) marker = 0b1;
    unsigned int(9) system_clock_ref_extension;
    bit(1) marker = 0b1;
    unsigned int(22) program_mux_rate;

    if (program_mux_rate == 0) {
        %g{ 
        print_error(); %g}
    }
    
    bit(1) marker = 0b1;
    bit(1) marker = 0b1;
    bit(5) reserved;
    unsigned int(3) stuffing_len;

    unsigned int i;
    for(i=0; i < stuffing_len; i++)
        bit(8) stuffing_byte = 0xFF;
    if (nextbits(32) == 0x000001BB) 
        SystemHeader system_header;
   
    %.c{
    void print_error() { printf("Error in PackHeader! The program_mux_rate = 0!"); } %.c}
    %.j{
    void print_error() { System.out.println("Error in PackHeader! The program_mux_rate = 0!"); } %.j}
}

/////////////////////////
// Program Descriptors //
/////////////////////////
abstract class BaseProgramDescriptor : unsigned int(8) tag = 0 
{
    unsigned int(8) length;
}

class VideoDescriptor extends BaseProgramDescriptor : unsigned int(8) tag = 2 
{
    bit(1) multiple_frame_rate_flag;
    unsigned int(4) frame_rate_code;
    bit(1) MPEG1_only_flag;
    bit(1) constrained_parameter_flag;
    bit(1) still_picture_flag;
    if (MPEG1_only_flag == 0b1) {
        unsigned int(8) profile_and_level_indication;
        unsigned int(2) chroma_format;
        bit(1) frame_rate_extension_flag;
        bit(5) reserved;
    }
}

class AudioDescriptor extends BaseProgramDescriptor : unsigned int(8) tag = 3 
{
    bit(1) free_format_flag;
    bit(1) ID;
    bit(2) layer;
    bit(1) variable_rate_audio_indicator;
    bit(3) reserved;
}

class HierarchyDescriptor extends BaseProgramDescriptor : unsigned int(8) tag = 4 
{
    bit(4) reserved;
    unsigned int(4) hierarchy_type;
    bit(2) reserved;
    unsigned int(6) hierarchy_layer_index;
    bit(2) reserved;
    unsigned int(6) hierarchy_embedded_layer_index;
    bit(2) reserved;
    unsigned int(6) hierarchy_channel;
}

class RegistrationDescriptor extends BaseProgramDescriptor : unsigned int(8) tag = 5 
{
    unsigned int(32) format_identifier;
    unsigned int bytes_read;
    for (bytes_read=4; bytes_read < length; ) {
        bit(8) additional_identification_info;
        bytes_read += 1;
    }
}

class DataAlignmentDescriptor extends BaseProgramDescriptor : unsigned int(8) tag = 6 
{
    unsigned int(8) alignment_type;
}

class TargetBackgroundGridDescriptor extends BaseProgramDescriptor : unsigned int(8) tag = 7 
{
    unsigned int(14) horizontal_size;
    unsigned int(14) vertical_size;
    unsigned int(4) aspect_ratio_information;
}

class VideoWindowDescriptor extends BaseProgramDescriptor : unsigned int(8) tag = 8 
{
    unsigned int(14) horizontal_offset;
    unsigned int(14) vertical_offset;
    unsigned int(4) window_priority;
}

class ConditionalAccessDescriptor extends BaseProgramDescriptor : unsigned int(8) tag = 9 
{
    unsigned int(16) CA_system_ID;
    bit(3) reserved;
    unsigned int(13) CA_PID;
    unsigned int bytes_read;
    for (bytes_read=4; bytes_read < length; ) {
        unsigned int(8) private_data_byte;
        bytes_read += 1;
    }
}

class LanguageDescriptor extends BaseProgramDescriptor : unsigned int(8) tag = 10 
{
    unsigned int bytes_read;
    for (bytes_read=0; bytes_read < length; ) {
        bit(24) ISO_639_language_code;
        bit(8) audio_type;
        bytes_read += 4;
    }
}

class SystemClockDescriptor extends BaseProgramDescriptor : unsigned int(8) tag = 11 
{
    bit(1) external_clock_reference_indicator;
    bit(1) reserved;
    unsigned int(6) clock_accuracy_indicator;
    unsigned int(3) clock_accuracy_exponent;
    bit(5) reserved;
}

class MultiplexBufferUtilizationDescriptor extends BaseProgramDescriptor : unsigned int(8) tag = 12
{
    bit(1) bound_valid_flag; 
    unsigned int(15) LTW_offset_lower_bound;
    bit(1) reserved;
    unsigned int(15) LTW_offset_upper_bound;
}

class CopyrightDescriptor extends BaseProgramDescriptor : unsigned int(8) tag = 13 
{
    unsigned int(32) copyright_identifier;
    unsigned int bytes_read;
    for (bytes_read=4; bytes_read < length; ) {
        bit(8) additional_copyright_info;
        bytes_read += 1;
    }
}

class MaximumBitrateDescriptor extends BaseProgramDescriptor : unsigned int(8) tag = 14 
{
    bit(2) reserved;
    unsigned int(22) maximum_bitrate;
}

class PrivateDataIndicatorDescriptor extends BaseProgramDescriptor : unsigned int(8) tag = 15 
{
    unsigned int(32) private_data_indicator;
}

class SmoothingBufferDescriptor extends BaseProgramDescriptor : unsigned int(8) tag = 16 
{
    bit(2) reserved;
    unsigned int(22) sb_leak_rate;
    bit(2) reserved;
    unsigned int(22) sb_size;
}

class STDDescriptor extends BaseProgramDescriptor : unsigned int(8) tag = 17 
{
    bit(7) reserved;
    bit(1) leak_valid_flag;
}

class IBPDescriptor extends BaseProgramDescriptor : unsigned int(8) tag = 18 
{
    unsigned int(1) closed_gop_flag;
    unsigned int(1) identical_gop_flag; 
    unsigned int(14) max_gop_length;
}

////////////////////////
// Stream Descriptors //
////////////////////////
abstract class BaseStreamDescriptor : unsigned int(8) type = 0x00 
{
    unsigned int(8) elementary_stream_id;
    unsigned int(16) elementary_stream_info_length;

    unsigned int parsedBytes;
    for (parsedBytes=0; parsedBytes < elementary_stream_info_length; ) {
        BaseProgramDescriptor descriptor;
        parsedBytes += lengthof(descriptor);
    }
}

class MPEG1VideoStreamDescriptor extends BaseStreamDescriptor : unsigned int(8) type = 0x01 {}
class MPEG2VideoStreamDescriptor extends BaseStreamDescriptor : unsigned int(8) type = 0x02 {}
class MPEG1AudioStreamDescriptor extends BaseStreamDescriptor : unsigned int(8) type = 0x03 {}
class MPEG2AudioStreamDescriptor extends BaseStreamDescriptor : unsigned int(8) type = 0x04 {}
class PrivateStreamDescriptor extends BaseStreamDescriptor : unsigned int(8) type = 0x05 {}
class PrivatePESDescriptor extends BaseStreamDescriptor : unsigned int(8) type = 0x06 {}
class MHEGStreamDescriptor extends BaseStreamDescriptor : unsigned int(8) type = 0x07 {}
class DSMCCStreamDescriptor extends BaseStreamDescriptor : unsigned int(8) type = 0x08 {}
class H222_1StreamDescriptor extends BaseStreamDescriptor : unsigned int(8) type = 0x09 {}
class ATypeStreamDescriptor extends BaseStreamDescriptor : unsigned int(8) type = 0x0A {}
class BTypeStreamDescriptor extends BaseStreamDescriptor : unsigned int(8) type = 0x0B {}
class CTypeStreamDescriptor extends BaseStreamDescriptor : unsigned int(8) type = 0x0C {}
class DTypeStreamDescriptor extends BaseStreamDescriptor : unsigned int(8) type = 0x0D {}
class AuxiliaryStreamDescriptor extends BaseStreamDescriptor : unsigned int(8) type = 0x0E {}
class ReservedStreamDescriptor extends BaseStreamDescriptor : unsigned int(8) type = 0x0F .. 0x7F {}
class UserPrivateStreamDescriptor extends BaseStreamDescriptor : unsigned int(8) type = 0x80 .. 0xFF {}

/* If trick_mode '0' was not assigned to fast_forward, then we could have 
 * used polymorphic class inheritance.
 */
class TrickModeControl 
{
    unsigned int(3) trick_mode_control;
    if (trick_mode_control == fast_forward || trick_mode_control == fast_reverse) {
        bit(2) field_id;
        bit(1) intra_slice_refresh;
        bit(2) frequency_truncation;
    }
    else if (trick_mode_control == slow_motion) 
        unsigned int(5) rep_ctrl;
    else if (trick_mode_control == freeze_frame) {
        bit(2) field_id;
        bit(3) reserved;
    }
    else if (trick_mode_control == slow_reverse) 
        unsigned int(5) rep_ctrl;
    else
        bit(5) reserved;
}

/////////
// PES //
/////////
/*
 * PesBase
 *
 * Abstract base class for all PES Packet derivations
 *
 */
abstract class PesBase : const unsigned int(32) st_id = 0 
{
    unsigned int(16) PES_packet_length; 
}

/*
 * PesNonData
 *
 * Abstract base class for all Private2, ECM, EMM, DSMCC, ITUT_E streams 
 *
 */
abstract class PesNonData extends PesBase : const unsigned int(32) st_id = 0 
{
    skipbits(8*PES_packet_length);
}

/*
 * PesData
 *
 * Abstract base class for all PESPacket derivations whose stream id's
 * Range from 0xC0 .. 0xEF
 *
 */
abstract class PesData extends PesBase : const unsigned int(32) st_id = 0 
{
    skipbits(8*PES_packet_length);
}

// The PesPadding is similar to the PesNonData with the exception that the 8 bit padding bytes must have the value 0xFF
class PesPadding extends PesBase : const unsigned int(32) st_id = ST_Padding 
{
    unsigned int i;
    for (i=0; i < PES_packet_length; i++) {
        bit(8) padding_byte = 0xFF;
    }
}

class PesAudio extends PesData : const unsigned int(32) st_id = ST_Audio1 .. ST_Audio2 {}
class PesVideo extends PesData : const unsigned int(32) st_id = ST_Video1 .. ST_Video2 {}
class PesITUT_E extends PesNonData : const unsigned int(32) st_id = ST_ITUT_E {}
class PesPrivate1 extends PesNonData : const unsigned int(32) st_id = ST_Private1 {}
class PesPrivate2 extends PesNonData : const unsigned int(32) st_id = ST_Private2 {}
class PesECM extends PesNonData : const unsigned int(32) st_id = ST_ECM {}
class PesEMM extends PesNonData : const unsigned int(32) st_id = ST_EMM {}
class PesDSMCC extends PesBase : const unsigned int(32) st_id = ST_DSMCC {}

////////////////
// PSMapTable //
////////////////
class PSMapTable extends PesBase : const unsigned int(32) st_id = ST_PSMapTable 
{
    if (PES_packet_length > 1018) {
        %g{ 
        print_error(); %g}
    }

    bit(1) current_next_indicator;
    bit(2) reserved;
    unsigned int(5) program_stream_map_version;
    bit(7) reserved;
    bit(1) marker = 0b1;
    unsigned int(16) program_stream_info_length;

    unsigned int parsed;
    for (parsed=0; parsed < program_stream_info_length; ) {
        BaseProgramDescriptor program_descriptor;
        parsed += lengthof(program_descriptor);
    }
    
    unsigned int(16) elementary_stream_map_length;
    unsigned int stream_map_ct;
    for (stream_map_ct=0; stream_map_ct < elementary_stream_map_length; ) {
        BaseStreamDescriptor stream_descriptor;
        stream_map_ct += lengthof(stream_descriptor);
    }
    
    unsigned int(32) CRC_32;

    %.c{
    void print_error() { printf("Error in PSMapTable! The PES_packet_length is too high!"); } %.c}
    %.j{
    void print_error() { System.out.println("Error in PSMapTable! The PES_packet_length is too high!"); } %.j}
}

/////////////////
// PSDirectory //
/////////////////
class PSDirectory extends PesBase : const unsigned int(32) st_id = ST_PSDirectory 
{
    unsigned int(15) number_of_access_units;
    bit(1) marker = 0b1;
    unsigned int(15) previous_directory_offset_1;
    bit(1) marker = 0b1;
    unsigned int(15) previous_directory_offset_2;
    bit(1) marker = 0b1;
    unsigned int(15) previous_directory_offset_3;
    bit(1) marker = 0b1;
    unsigned int(15) previous_directory_offset_4;
    bit(1) marker = 0b1;
    unsigned int(15) previous_directory_offset_5;
    bit(1) marker = 0b1;
    unsigned int(15) previous_directory_offset_6;
    bit(1) marker = 0b1;

    unsigned int i;
    for (i=0; i < number_of_access_units; i++) {
        unsigned int(8) packet_stream_id;
        int(1) PES_header_position_offset_sign;
        unsigned int(14) PES_header_position_offset_1;
        bit(1) marker = 0b1;
        unsigned int(15) PES_header_position_offset_2;
        bit(1) marker = 0b1;
        unsigned int(15) PES_header_position_offset_3;
        bit(1) marker = 0b1;
        unsigned int(16) reference_offset;
        bit(1) marker = 0b1;
        bit(3) reserved;
        unsigned int(3) PTS_1;
        bit(1) marker = 0b1;
        unsigned int(15) PTS_2;
        bit(1) marker = 0b1;
        unsigned int(15) PTS_3;
        bit(1) marker = 0b1; 
        unsigned int(15) bytes_to_read;
        bit(1) marker = 0b1;
        unsigned int(8) bytes_to_read;
        bit(1) marker = 0b1;
        bit(1) intra_coded_indicator;
        bit(2) coding_parameters_indicator;
        bit(4) reserved;
    }
}

//////////
// Pack //
//////////
class Pack 
{
    PackHeader pack_header;

    while (nextbits(32) >= ST_PSMapTable && nextbits(32) <= ST_PSDirectory)
        PesBase pes;
}

////////////////////
// The main class //
////////////////////
class MPEG2PS 
{
    do {
        Pack p;
    } while (nextbits(32) == 0x000001BA);   // Pack start code
    nextcode(aligned(8), 0x000001B9);       // PS end code
    skipbits(32);             
}