/* # # mpeg parser library # # $Id: mpegparse.c,v 1.3 2005/03/03 10:25:03 nemesis Exp $ # # Copyright (C) 2001-2005 Kees Cook # kees@outflux.net, http://outflux.net/ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # 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. # http://www.gnu.org/copyleft/gpl.html # */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "mpeg2structs.h" #include "mpeg2consts.h" #include "mpegparse-private.h" // general stuff int debug = 0; int dvd_names = 0; // what packets to process int enter_video = 0; // top-level packets to display int no_pack = 0; int no_audio = 0; int no_video = 0; int no_other_streams = 0; // top-level headers to display int no_system_headers = 0; // top-level other things... int no_errors = 0; // video packet display toggles int no_slices = 1; int no_sequence = 0; int no_GOP_headers = 0; int no_picture_headers = 0; // file position options int walk_offsets = 1; off_t begin_at = 0; off_t num_bytes = 0; static struct option long_options[] = { {"help", 0, 0, 'h'}, {"debug", 0, 0, 'd'}, {"dvd-format", 0, 0, 'D'}, {"errors", 0, 0, 'e'}, {"enter-video", 0, 0, 'V'}, {"slices", 0, 0, 's'}, {"audio", 0, 0, 'a'}, {"video", 0, 0, 'v'}, {"pack", 0, 0, 'p'}, {"sequence", 0, 0, 'q'}, {"system", 0, 0, 'y'}, {"other", 0, 0, 'o'}, {"just-frames", 0, 0, 'F'}, {"GOPs", 0, 0, 'g'}, {"frames", 0, 0, 'f'}, {"invert", 0, 0, 'i'}, {"scan-stream", 0, 0, 't'}, {"begin", 0, 0, 'b'}, {"num", 0, 0, 'n'}, {"write", 0, 0, 'w'}, {NULL, 0, 0, 0} }; static char *short_options = "h?dDeVsavpqyoFgfitb:n:w:"; void mpegparse_usage(char *argv[]) { printf("Usage: %s [options ...] MPEG2-PS ...\n", argv[0]); printf("\n"); printf("Processing Toggles\n"); printf("-V/--enter-video Process video packets (forced by -t option)\n"); //printf("-A/--enter-audio Process audio packets\n"); printf("\n"); printf("General Display Toggles\n"); printf("-p/--pack Toggle Pack packet start display\n"); printf("-v/--video Toggle video ES packet start display\n"); printf("-a/--audio Toggle audio ES packet start display\n"); printf ("-o/--other Toggle other stream display (only show video & audio)\n"); printf("-y/--system Toggle system header display\n"); printf("-e/--errors Toggle packet errors\n"); printf("-i/--invert Invert all display toggles\n"); printf("\n"); printf("Video Display Toggles\n"); printf("-s/--slices Toggle slice starts (default off)\n"); printf("-q/--sequence Toggle sequence header display\n"); printf("-g/--GOPs Toggle GOP header display\n"); printf("-f/--frames Toggle Picture header display\n"); //printf("Audio Display Toggles\n"); printf("\n"); printf("Short Cuts\n"); printf ("-F/--just-frames Show only video frames (same as '-Vepvaoyq')\n"); printf("\n"); printf("File Offset Options\n"); printf("-b/--begin=NUM Start reading at offset NUM\n"); printf("-n/--num=NUM Stop after reading NUM bytes\n"); printf ("-t/--scan-stream Scan every byte of the stream instead of using PES offsets\n"); printf (" (this can show erroneous 'start code emulation' codes)\n"); printf("\n"); printf("General Options\n"); printf("-D/--dvd-format Display DVD packet extensions\n"); printf("-d/--debug Report debug info\n"); printf ("-w/--write FILE,0xID[,0xSID] Write PES with id ID (and subid SID) to FILE\n"); printf("-h/-?/--help Display this help\n"); exit(1); } /* utilities for dealing with timecodes */ struct time_code_t { unsigned char drop; // 0-1 unsigned char hours; // 0-23 unsigned char minutes; // 0-59 unsigned char seconds; // 0-59 unsigned char pictures; // 0-59 }; /* void time_code_to_string(struct time_code_t * time_code, char * buf, int len) { if (!time_code) return; snprintf(buf,len,"%02d:%02d:%02d.%02d", time_code->hours, time_code->minutes, time_code->seconds, time_code->pictures); } void time_code_plus_pictures(struct time_code_t * time_code, unsigned char fps, unsigned char pictures) { switch (fps) { case FPS_23_9: #define FPS_23_9 1 // 0001 -- 24000/1001 #define FPS_24 2 // 0010 -- 24 #define FPS_25 3 // 0011 -- 25 #define FPS_29_9 4 // 0100 -- 30000/1001 #define FPS_30 5 // 0101 -- 30 #define FPS_50 6 // 0110 -- 50 #define FPS_59_9 7 // 0111 -- 60000/1001 #define FPS_60 8 // 1000 -- 60 } */ typedef struct { int id; // PES id int sid; // PES sub-id char *filename; // copy of filename FILE *fp; // file handle to write it to } writer_t; int max_pes_writers = 0; writer_t *pes_writers = NULL; FILE *writer_match(int id, int sid) { int i; //fprintf(stderr,"Looking for 0x%02X:0x%02X\n",id,sid); for (i = 0; i < max_pes_writers; i++) { /* fprintf(stderr,"Trying %d: 0x%02X:0x%02X\n",i, pes_writers[i].id, pes_writers[i].sid); */ if (pes_writers[i].id == id && pes_writers[i].sid == sid) { //fprintf(stderr," got it: %d\n",i); return pes_writers[i].fp; } } return NULL; } int writer_add(int id, int sid, char *filename) { FILE *fp; int index; if (writer_match(id, sid)) return -1; if (!(fp = fopen(filename, "w"))) { perror(filename); return -1; } index = max_pes_writers++; if (!(pes_writers = (writer_t *) realloc(pes_writers, sizeof(writer_t) * max_pes_writers))) { perror("realloc"); return -1; } pes_writers[index].id = id; pes_writers[index].sid = sid; pes_writers[index].fp = fp; pes_writers[index].filename = strdup(filename); //fprintf(stderr,"Storing 0x%02X:0x%02X to '%s'\n",id,sid,filename); return 0; } void writer_done() { int i; for (i = 0; i < max_pes_writers; i++) { fclose(pes_writers[i].fp); free(pes_writers[i].filename); } free(pes_writers); pes_writers = NULL; } typedef enum { PACK_NONE, PACK_SPECIAL, PACK_PES_SIMPLE, // packet length == data length PACK_PES_COMPLEX, // crazy headers need skipping } packet_type; typedef enum { SUBID_NONE, // no sub id (sid=0) SUBID_DATA, // first PES data byte } subid_type; // FIXME: // - add "valid scope" flags (eg some markers only valid in Video stream) // - add "struct size" field for auto-forwarding past each marker typedef struct { // the byte value match for the packet tags uint8_t code_match_lo; // low end of the range of matches uint8_t code_match_hi; // high end of the range of matches // what kind of PES is it? packet_type packet; // how do we find the stream sub id subid_type subid_method; enum mpegparse_packet parse_name; // enum for clients } packet_tag_info; packet_tag_info packet_tags[] = { // pic start {0x00, 0x00, PACK_SPECIAL, SUBID_NONE, MPEGPARSE_VIDEO_PICTURE, }, // video slices {0x01, 0xAF, PACK_SPECIAL, SUBID_NONE, MPEGPARSE_VIDEO_SLICE, }, // reserved {0xB0, 0xB1, PACK_SPECIAL, SUBID_NONE, MPEGPARSE_RESERVED, }, // user data, sequences, ext {0xB2, 0xB2, PACK_SPECIAL, SUBID_NONE, MPEGPARSE_USER_DATA, }, {0xB3, 0xB3, PACK_SPECIAL, SUBID_NONE, MPEGPARSE_SEQ_HEADER, }, {0xB4, 0xB4, PACK_SPECIAL, SUBID_NONE, MPEGPARSE_SEQ_ERROR, }, {0xB5, 0xB5, PACK_SPECIAL, SUBID_NONE, MPEGPARSE_EXTENSION_START, }, // reserved {0xB6, 0xB6, PACK_SPECIAL, SUBID_NONE, MPEGPARSE_RESERVED, }, // sequence, gop, ps end {0xB7, 0xB7, PACK_SPECIAL, SUBID_NONE, MPEGPARSE_VIDEO_SEQ_START, }, {0xB8, 0xB8, PACK_SPECIAL, SUBID_NONE, MPEGPARSE_VIDEO_GOP, }, {0xB9, 0xB9, PACK_SPECIAL, SUBID_NONE, MPEGPARSE_PROGRAM_END, }, // pack {0xBA, 0xBA, PACK_SPECIAL, SUBID_NONE, MPEGPARSE_PACK, }, // system: same len as PES {0xBB, 0xBB, PACK_PES_SIMPLE, SUBID_NONE, MPEGPARSE_SYSTEM, }, // PES: prog stream map * {0xBC, 0xBC, PACK_PES_SIMPLE, SUBID_NONE, MPEGPARSE_PES_PS_MAP, }, // PES: priv 1 {0xBD, 0xBD, PACK_PES_COMPLEX, SUBID_DATA, MPEGPARSE_PES_PRIV_1, }, // PES: padding, priv 2 * {0xBE, 0xBE, PACK_PES_SIMPLE, SUBID_NONE, MPEGPARSE_PES_PADDING, }, {0xBF, 0xBF, PACK_PES_SIMPLE, SUBID_NONE, MPEGPARSE_PES_PRIV_2, }, // PES: Audio {0xC0, 0xDF, PACK_PES_COMPLEX, SUBID_NONE, MPEGPARSE_PES_AUDIO, }, // PES: Video {0xE0, 0xEF, PACK_PES_COMPLEX, SUBID_NONE, MPEGPARSE_PES_VIDEO, }, // PES: ecm, emm, dsmcc * {0xF0, 0xF2, PACK_PES_SIMPLE, SUBID_NONE, MPEGPARSE_OTHER, }, // PES: iso 13522/h2221a-d {0xF3, 0xF7, PACK_PES_COMPLEX, SUBID_NONE, MPEGPARSE_PES_ISO, }, // PES: h2221e * {0xF8, 0xF8, PACK_PES_SIMPLE, SUBID_NONE, MPEGPARSE_OTHER, }, // PES: ancillary {0xF9, 0xF9, PACK_PES_COMPLEX, SUBID_NONE, MPEGPARSE_PES_ANCILLARY, }, // PES: reserved {0xFA, 0xFE, PACK_PES_SIMPLE, SUBID_NONE, MPEGPARSE_RESERVED, }, // PES: prog stream dir * {0xFF, 0xFF, PACK_PES_SIMPLE, SUBID_NONE, MPEGPARSE_PES_PS_DIRECTORY, }, // end of list {0, 0, PACK_NONE, SUBID_NONE, MPEGPARSE_END, } }; int mpegparse_handle_options(int argc, char *argv[]) { int c; int option_index = 0; while (1) { c = getopt_long(argc, argv, short_options, long_options, &option_index); if (c == -1) break; switch (c) { case '?': case 'h': mpegparse_usage(argv); break; case 'd': debug = !debug; break; case 'D': dvd_names = !dvd_names; break; case 'e': no_errors = !no_errors; break; case 's': no_slices = !no_slices; break; case 'a': no_audio = !no_audio; break; case 'v': no_video = !no_video; break; case 'p': no_pack = !no_pack; break; case 'q': no_sequence = !no_sequence; break; case 'o': no_other_streams = !no_other_streams; break; case 'y': no_system_headers = !no_system_headers; break; case 'g': no_GOP_headers = !no_GOP_headers; break; case 'f': no_picture_headers = !no_picture_headers; break; case 'F': no_pack = !no_pack; no_video = !no_video; no_audio = !no_audio; no_other_streams = !no_other_streams; no_system_headers = !no_system_headers; no_errors = !no_errors; no_sequence = !no_sequence; enter_video = !enter_video; break; case 'i': no_pack = !no_pack; no_audio = !no_audio; no_video = !no_video; no_other_streams = !no_other_streams; no_system_headers = !no_system_headers; no_slices = !no_slices; no_sequence = !no_sequence; no_GOP_headers = !no_GOP_headers; no_picture_headers = !no_picture_headers; break; case 'V': enter_video = !enter_video; break; case 't': walk_offsets = !walk_offsets; break; case 'n': num_bytes = atoll(optarg); break; case 'b': begin_at = atoll(optarg); break; case 'w': { char *filename; char *id_str; char *sid_str; int id; int sid; filename = strtok(optarg, ","); id_str = strtok(NULL, ","); sid_str = strtok(NULL, ","); if (!filename || !id_str || sscanf(id_str, "%x", &id) != 1 || (sid_str && sscanf(sid_str, "%x", &sid) != 1)) mpegparse_usage(argv); if (!sid_str) sid = 0; if (writer_add(id, sid, filename) < 0) exit(1); break; } default: printf("?? getopt returned character code 0x%x ??\n", c); break; } } return optind; } static char *length_format; uint8_t packet_buffer[65536]; // max size of a double byte uint8_t *packet_start; uint16_t packet_size; struct mpegparse_packet_info * mpegparse_examine_code(mpegparse * parser, uint8_t code, off_t * position) { static char answer[128]; uint8_t bytes[32]; int i; // length variables // HI LO unsigned int length; unsigned int header_len; static char length_str[64]; // pack header variables // //| 0 | 1 | 2 | 3 | 4 | 5 | 6 | // 76543210765432107654321076543210765432107654321076543210 // xxHHHxMMMMMMMMMMMMMMMxLLLLLLLLLLLLLLLxEEEEEEEEExRRRRRRRR //| 7 | 8 | 9 | // 765432107654321076543210 // RRRRRRRRRRRRRRxxrrrrrSSS unsigned long long system_clock_ref_base; // "H"igh, "M"edium, "L"ow bits unsigned long system_clock_ref_ext; // "E" unsigned int program_mux_rate; // "R" unsigned int pack_stuffing_length; // "S" // sequence header variables //|horizontal |vertical |asp|rat|bit rate ||vbv_buff... //| 0 | 1 | 2 | 3 | 4 | 5 | 6 | // 76543210765432107654321076543210765432107654321076543210 // ... |c //| 7 | 8 | 9 | // 765432107654321076543210 int horizontal_size; int vertical_size; int aspect_ratio; int frame_rate; int bit_rate; int constrained; // picture header variables; //|temporal |ty| //| 0 | 1 | 2 | 3 | 4 | 5 | 6 | // 76543210765432107654321076543210765432107654321076543210 int pic_type; int pic_time; // GOP header variables // | 0 | 1 | 2 | 3 | // 7 65432 107654 3 210765 432107 6 543210 // 1 11111 111111 1 111111 111111 1 1 // d hour min m sec pic c b // r a l roken // o r osed // p k int drop; int hour; int min; int sec; int pictures; int closed; int broken; // PES scramble int scramble=0; // file buffer file_buf * fb = NULL; if (!parser) return NULL; fb = parser->fb; // initialize "unknown" packet length header_len = length = 0; length_str[0] = '\0'; // where did the start code begin? parser->info.header_offset = buffer_tell(fb); parser->info.code_offset = parser->info.header_offset - 4; parser->info.header_length = 0; parser->info.data_offset = 0; parser->info.data_length = 0; parser->info.packet = MPEGPARSE_NONE;; parser->info.code = code; // find the packet type for (i = 0; packet_tags[i].packet != PACK_NONE; i++) { if (code >= packet_tags[i].code_match_lo && code <= packet_tags[i].code_match_hi) { FILE *fp; int sid = 0; parser->info.packet = packet_tags[i].parse_name; switch (packet_tags[i].packet) { case PACK_SPECIAL: // actually, nothing special break; case PACK_PES_SIMPLE: case PACK_PES_COMPLEX: if (packet_tags[i].packet == PACK_PES_COMPLEX) { if (buffer_look_ahead(fb, bytes, 5) != FILE_BUF_OKAY) { parser->info.packet = MPEGPARSE_END; return &(parser->info); } // packet_length is 0 and 1 // PES header variables // | 2 | 3 | 4 | // 76 54 3 2 1 0 76 5 4 3 2 1 0 76543210 // 10 scramble pts/dts pes_crc // priority escr extension // alignment es_rate header_data_length // copyright dsm_trick // copy addtl copy if ((bytes[2]>>6) != 0x2) { //printf("PES (0x%02X) header mark != 0x2: 0x%x (is this an MPEG2-PS file?)\n",code,(bytes[2]>>6)); parser->info.packet = MPEGPARSE_NOT_MPEG2; return &(parser->info); } // parse data bits parser->info.header.pes_complex.PES_scrambling_control=((bytes[2]>>4)&0x3); parser->info.header.pes_complex.PES_priority=((bytes[2]>>3)&0x1); parser->info.header.pes_complex.data_alignment_indicator=((bytes[2]>>2)&0x1); parser->info.header.pes_complex.copyright=((bytes[2]>>1)&0x1); parser->info.header.pes_complex.original_or_copy=((bytes[2]>>0)&0x1); parser->info.header.pes_complex.PTS_DTS_flags=((bytes[3]>>7)&0x3); parser->info.header.pes_complex.ESCR_flag=((bytes[3]>>5)&0x1); parser->info.header.pes_complex.ES_rate_flag=((bytes[3]>>4)&0x1); parser->info.header.pes_complex.DSM_trick_mode_flag=((bytes[3]>>3)&0x1); parser->info.header.pes_complex.additional_copy_info_flag=((bytes[3]>>2)&0x1); parser->info.header.pes_complex.PES_CRC_flag=((bytes[3]>>1)&0x1); parser->info.header.pes_complex.PES_extension_flag=((bytes[3]>>0)&0x1); parser->info.header.pes_complex.PES_header_data_length=bytes[4]; // skip PES header data header_len = 5 + bytes[4]; if (dvd_names && packet_tags[i].subid_method == SUBID_DATA && buffer_look_ahead(fb, bytes, header_len + 1) == FILE_BUF_OKAY) { sid = bytes[header_len]; } } else { header_len = 2; if (buffer_look_ahead(fb, bytes, 2) != FILE_BUF_OKAY) { parser->info.packet = MPEGPARSE_END; return &(parser->info); } } length = bytes[1] | (bytes[0] << 8); // "2" comes from the "length" bytes sprintf(length_str, length_format, length, length == 1 ? "" : "s", "marker", buffer_tell(fb) + length + 2); // figure out data length if (header_len) { packet_size = length - header_len + 2; if (dvd_names && packet_tags[i].subid_method == SUBID_DATA) { packet_size--; // used in header header_len++; } } else { packet_size = length; } // save packet settings parser->info.header_length = header_len; parser->info.data_offset = parser->info.header_offset + parser->info.header_length; parser->info.data_length = packet_size; // get position updated for next packet location if (position && walk_offsets) { if (enter_video && (code & PES_TYPE_MASK_video) == PES_TYPE_video) { break; // skip position update } *position = buffer_tell(fb) + length + 2; } break; default: case PACK_NONE: // FIXME: not possible! break; } break; // found a match } } return &(parser->info); /* switch (code) { case PES_TYPE_picture_start: if (no_picture_headers) return NULL; if (scramble) { sprintf(answer," Picture Start (scrambling: %d)",scramble); return answer; } if (buffer_look_ahead(fb,bytes, 2) == FILE_BUF_OKAY) { pic_type = (bytes[1] & PICTURE_CODING_MASK) >> 3; pic_time = (bytes[0] << 2) | ((bytes[1] & 0xC0) >> 6); sprintf(answer, " Picture (%s) Start (%u)", pic_str[pic_type], pic_time); return answer; } else return " Picture Start (incomplete flags)"; case PES_TYPE_user_data_start: if (no_other_streams) return NULL; if (scramble) { sprintf(answer," User Data Start (scrambling: %d)",scramble); return answer; } return " User Data Start"; case PES_TYPE_sequence_header: if (no_sequence) return NULL; if (scramble) { sprintf(answer," Sequence Header (scrambling: %d)",scramble); return answer; } if (buffer_look_ahead(fb,bytes, 8) == FILE_BUF_OKAY) { horizontal_size = (bytes[0] << 4) | ((bytes[1] & 0xF0) >> 4); vertical_size = ((bytes[1] & 0x0F) << 8) | bytes[2]; aspect_ratio = (bytes[3] & 0xF0) >> 4; frame_rate = (bytes[3] & 0x0F); bit_rate = (bytes[4] << 10) | (bytes[5] << 2) | ((bytes[6] & 0xC0) >> 6); constrained = (bytes[7] & 0x04) >> 2; sprintf(answer, " Sequence Header (%dx%d: %s, %sfps, %s%s)", horizontal_size, vertical_size, aspect_str[aspect_ratio], frame_str[frame_rate], speed_str(bit_rate * 400), constrained ? " constrained" : ""); return answer; } else return " Sequence Header (incomplete flags)"; case PES_TYPE_sequence_error: if (no_sequence) return NULL; if (scramble) { sprintf(answer," Sequence Error (scrambling: %d)",scramble); return answer; } return " Sequence Error"; case PES_TYPE_extension_start: if (no_sequence) return NULL; if (scramble) { sprintf(answer," Sequence Extension Start (scrambling: %d)",scramble); return answer; } return " Sequence Extension Start"; case PES_TYPE_sequence_end: if (no_sequence) return NULL; if (scramble) { sprintf(answer," Sequence End (scrambling: %d)",scramble); return answer; } return " Sequence End"; case PES_TYPE_group_start: if (no_GOP_headers) return NULL; if (scramble) { sprintf(answer," GOP Start (scrambling: %d)",scramble); return answer; } if (buffer_look_ahead(fb,bytes, 4) == FILE_BUF_OKAY) { // GOP header variables // | 0 | 1 | 2 | 3 | // 7 65432 107654 3 210765 432107 6 543210 // 1 11111 111111 1 111111 111111 1 1 // d hour min m sec pic c b // r a l roken // o r osed // p k drop = ((bytes[0] & 0x80) > 0); hour = ((bytes[0] & 0x7C) >> 2); min = ((bytes[0] & 0x3) << 4) | ((bytes[1] & 0xF0) >> 4); sec = ((bytes[1] & 0x7) << 3) | ((bytes[2] & 0xE0) >> 5); pictures = ((bytes[2] & 0x1F) << 1) | ((bytes[3] & 0x80) >> 7); closed = ((bytes[3] & 0x40) > 0); broken = ((bytes[3] & 0x20) > 0); sprintf(answer, " GOP Start (%02d:%02d:%02d.%02d%s%s%s)", hour, min, sec, pictures, drop ? " drop" : "", closed ? " closed" : " open", broken ? " broken" : ""); return answer; } else { return " GOP Start (incomplete flags)"; } case PES_TYPE_program_end: if (no_other_streams) return NULL; if (scramble) { sprintf(answer,"Program End (scrambling: %d)",scramble); return answer; } return "Program End"; case PES_TYPE_pack_start: if (no_pack) return NULL; if (scramble) { sprintf(answer,"Pack Start (scrambling: %d)",scramble); return answer; } if (buffer_look_ahead(fb,bytes, 10) == FILE_BUF_OKAY) { //| 0 | 1 | 2 | 3 | 4 | 5 | 6 | // 76543210765432107654321076543210765432107654321076543210 // xxHHHxMMMMMMMMMMMMMMMxLLLLLLLLLLLLLLLxEEEEEEEEExRRRRRRRR // 333 222222222211111 111110000000000 000000000 22111111// bit // 210 987654321098765 432109876543210 876543210 10987654// number // - - - - - - - - - - // 2 2 2 1 1 0 - 0 - 1// left // 7 8 0 2 3 5 3 7 1 4// shift // //| 7 | 8 | 9 | // 765432107654321076543210 // RRRRRRRRRRRRRRxxrrrrrSSS // 11110000000000 00000000 // bit // 32109876543210 43210210 // number // - - - - // 0 - - 0 // left // 6 2 3 0 // shift system_clock_ref_base = (((bytes[0] & 0x38) << 27) | ((bytes[0] & 0x03) << 28) | ((bytes[1] & 0xFF) << 20) | ((bytes[2] & 0xF8) << 12) | ((bytes[2] & 0x03) << 13) | ((bytes[3] & 0xFF) << 5) | ((bytes[4] & 0xF8) >> 3)); system_clock_ref_ext = (((bytes[4] & 0x03) << 7) | ((bytes[5] & 0xFE) >> 1)); program_mux_rate = (((bytes[6] & 0xFF) << 14) | ((bytes[7] & 0xFF) << 6) | ((bytes[8] & 0xFC) >> 2)); pack_stuffing_length = (bytes[9] & 0x07); sprintf(answer, "Pack Start (Mux Rate: %u Stuffing: %u)", // FIXME: too many damn bits // system_clock_ref_base, // system_clock_ref_ext, program_mux_rate, pack_stuffing_length); return answer; } else { return "Pack Start (incomplete flags)"; } case PES_TYPE_system_header: if (no_system_headers) return NULL; if (scramble) { sprintf(answer," System Header (scrambling: %d)%s", scramble,length_str); return answer; } sprintf(answer, " System Header%s", length_str); return answer; case PES_TYPE_program_stream_map: if (no_other_streams) return NULL; sprintf(answer, " Program Stream Map%s", length_str); return answer; case PES_TYPE_private_stream_1: if (scramble) { sprintf(answer," Private Stream 1 (scrambling: %d)%s", scramble,length_str); return answer; } if (dvd_names) { char *audio_type; int raw_id, id; if (no_audio) return NULL; // already done in the header checks above //buffer_look_ahead(fb,bytes,header_len+1); audio_type = "unknown sub stream id"; raw_id = id = bytes[header_len]; if ((raw_id & DVD_AUDIO_TYPE_MASK_sub) == DVD_AUDIO_TYPE_sub) { audio_type = "SPU"; id = (raw_id & (DVD_AUDIO_TYPE_sub - 1)); } else if ((raw_id & DVD_AUDIO_TYPE_MASK_ac3) == DVD_AUDIO_TYPE_ac3) { audio_type = "AC3"; id = (raw_id & (DVD_AUDIO_TYPE_ac3 - 1)); } else if ((raw_id & DVD_AUDIO_TYPE_MASK_pcm) == DVD_AUDIO_TYPE_pcm) { audio_type = "PCM"; id = (raw_id & (DVD_AUDIO_TYPE_pcm - 1)); } sprintf(answer, " DVD Audio 0x%02X (%s %d)%s", raw_id, audio_type, id, length_str); } else { if (no_other_streams) return NULL; sprintf(answer, " Private Stream 1%s", length_str); } return answer; case PES_TYPE_padding_stream: if (no_other_streams) return NULL; sprintf(answer, " Padding Stream%s", length_str); return answer; case PES_TYPE_private_stream_2: if (no_other_streams) return NULL; sprintf(answer, " %s%s", dvd_names ? "DVD Navigation" : "Private Stream 2", length_str); return answer; case PES_TYPE_ECM_stream: if (no_other_streams) return NULL; sprintf(answer, " ECM Stream%s", length_str); return answer; case PES_TYPE_EMM_stream: if (no_other_streams) return NULL; sprintf(answer, " EMM Stream%s", length_str); return answer; case PES_TYPE_DSMCC_stream: if (no_other_streams) return NULL; sprintf(answer, " DSMCC Stream%s", length_str); return answer; case PES_TYPE_ISO_13522: if (no_other_streams) return NULL; if (scramble) { sprintf(answer," ISO13522 Stream (scrambling: %d)%s", scramble,length_str); return answer; } sprintf(answer, " ISO13522 Stream%s", length_str); return answer; case PES_TYPE_H2221A: if (no_other_streams) return NULL; if (scramble) { sprintf(answer," H222.1A (scrambling: %d)%s", scramble,length_str); return answer; } sprintf(answer, " H222.1A%s", length_str); return answer; case PES_TYPE_H2221B: if (no_other_streams) return NULL; if (scramble) { sprintf(answer," H222.1B (scrambling: %d)%s", scramble,length_str); return answer; } sprintf(answer, " H222.1B%s", length_str); return answer; case PES_TYPE_H2221C: if (no_other_streams) return NULL; if (scramble) { sprintf(answer," H222.1C (scrambling: %d)%s", scramble,length_str); return answer; } sprintf(answer, " H222.1C%s", length_str); return answer; case PES_TYPE_H2221D: if (no_other_streams) return NULL; if (scramble) { sprintf(answer," H222.1D (scrambling: %d)%s", scramble,length_str); return answer; } sprintf(answer, " H222.1D%s", length_str); return answer; case PES_TYPE_H2221E: if (no_other_streams) return NULL; sprintf(answer, " H222.1E Stream%s", length_str); return answer; case PES_TYPE_ancillary_stream: if (no_other_streams) return NULL; if (scramble) { sprintf(answer," Ancillary Stream (scrambling: %d)%s", scramble,length_str); return answer; } sprintf(answer, " Ancillary Stream%s", length_str); return answer; case PES_TYPE_program_stream_directory: if (no_other_streams) return NULL; sprintf(answer, " Program Stream Directory%s", length_str); return answer; default: if (code >= 0x01 && code <= 0xAF) { if (no_slices) return NULL; if (scramble) { sprintf(answer," Slice Start (scrambling: %d)%s", scramble,length_str); return answer; } sprintf(answer, " Slice Start%s", length_str); return answer; } if ((code & PES_TYPE_MASK_video) == PES_TYPE_video) { if (no_video) return NULL; if (scramble) { sprintf(answer," Video Stream 0x%02X (scrambling: %d)%s", PES_TYPE_video - (code & PES_TYPE_MASK_video), scramble,length_str); return answer; } sprintf(answer, " Video Stream 0x%02X%s", PES_TYPE_video - (code & PES_TYPE_MASK_video), length_str); return answer; } if ((code & PES_TYPE_MASK_audio) == PES_TYPE_audio) { if (no_audio) return NULL; if (scramble) { sprintf(answer," Audio Stream 0x%02X (scrambling: %d)%s", PES_TYPE_video - (code & PES_TYPE_MASK_video), scramble,length_str); return answer; } sprintf(answer, " Audio Stream 0x%02X%s", PES_TYPE_audio - (code & PES_TYPE_MASK_audio), length_str); return answer; } if ((code >= 0xFA && code <= 0xFE) || (code >= 0xB0 && code <= 0xB1) || (code == 0xB6)) { if (no_errors) return NULL; if (scramble) { sprintf(answer,"Reserved (scrambling: %d)%s", scramble,length_str); return answer; } sprintf(answer, "Reserved%s", length_str); return answer; } if (no_errors) return NULL; if (scramble) { sprintf(answer,"Unknown (scrambling: %d)%s", scramble,length_str); return answer; } sprintf(answer, "Unknown%s", length_str); return answer; } return "IMPOSSIBLE ERROR: no match?!"; */ } mpegparse * mpegparse_open_program_stream(char * filename) { mpegparse * parser = (mpegparse *)malloc(sizeof(mpegparse)); if (!parser) { return NULL; } if (!(parser->fb=buffer_start(NULL,filename,DEFAULT_FILE_BUFFER_SIZE))) { free(parser); return NULL; } parser->info.packet = MPEGPARSE_NONE; return parser; } struct mpegparse_packet_info * mpegparse_find_packet(mpegparse * parser, off_t * location) { uint32_t marker=0xFFFFFFFF; int running=1; if (!parser) return NULL; if (!location) return NULL; // skip forward if (location && *location) { //printf("seeking to %llu\n",*location); buffer_seek(parser->fb,*location); } while (running) { uint8_t byte; //fprintf(stderr,"%08X\n",marker); if ((marker & 0xFFFFFF00) == 0x100) { char *str; *location = 0; //printf("code: 0x%02X @ %llu\n",byte,buffer_tell(parser->fb)); return mpegparse_examine_code(parser, byte, location); } marker <<= 8; if (buffer_get_byte(parser->fb, &byte) < 0) { parser->info.packet = MPEGPARSE_END; return &(parser->info); } else marker |= byte; /* if (num_bytes && buffer_tell(fb) - begin_at > num_bytes) { printf(report_format, buffer_tell(fb), 0x00, "End of Report"); running = 0; } */ } return NULL; } void mpegparse_shutdown(mpegparse * parser) { if (!parser) return; if (parser->fb) buffer_end(parser->fb); free(parser); } /* vi:set ai ts=4 sw=4 expandtab: */