MPEG-TS parser/analyzer

/* Sergey Bodrov 2012, serbod@gmail.com */ #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #define MAX_BUFFER 102400 #define PKT_SIZE 188 /* pids */ #define PAT_PID 0x0000 #define CAT_PID 0x0001 #define TSDT_PID 0x0002 #define NIT_PID 0x0010 #define SDT_PID 0x0011 #define STREAM_TYPE_VIDEO_MPEG1 0x01 #define STREAM_TYPE_VIDEO_MPEG2 0x02 #define STREAM_TYPE_AUDIO_MPEG1 0x03 #define STREAM_TYPE_AUDIO_MPEG2 0x04 #define STREAM_TYPE_PRIVATE_SECTION 0x05 #define STREAM_TYPE_PRIVATE_DATA 0x06 #define STREAM_TYPE_AUDIO_AAC 0x0f #define STREAM_TYPE_AUDIO_AAC_LATM 0x11 #define STREAM_TYPE_VIDEO_MPEG4 0x10 #define STREAM_TYPE_VIDEO_H264 0x1b #define STREAM_TYPE_VIDEO_VC1 0xea #define STREAM_TYPE_VIDEO_DIRAC 0xd1 #define STREAM_TYPE_AUDIO_AC3 0x81 #define STREAM_TYPE_AUDIO_DTS 0x8a # define get_bit(val,bit) ((val & (1 << (bit))) >> (bit)) # define dump_flag(flag,text) if ( (flag) ) printf(" %s", (text)) static int _verbose = 1; inline uint8_t get_8(uint8_t *buf, int *pos) { uint8_t tmp8; tmp8 = buf[*pos]; *pos+=1; return tmp8; } inline uint16_t get_16(uint8_t *buf, int *pos) { uint16_t tmp16; tmp16 = buf[*pos+1] + (buf[*pos] << 8); *pos+=2; return tmp16; } inline uint32_t get_32(uint8_t *buf, int *pos) { uint32_t tmp32; tmp32 = buf[*pos+3] + (buf[*pos+2] << 8) + (buf[*pos+1] << 16) + (buf[*pos] << 24); *pos+=4; return tmp32; } inline char *get_str8(uint8_t *buf, int *pos) { int len; uint8_t tmp8; char *str; len = get_8(buf, pos); if (len < 0) return NULL; str = malloc(len + 1); if (!str) return NULL; memcpy(str, &buf[*pos], len); str[len] = '\0'; *pos+=len; return str; } void dump_bits16(uint16_t n) { int i; //printf("%u=",n); for (i=15; i>=0; i--) { printf("%u", ((n & (1 << i)) >> i)); if ((i % 4) == 0) printf(" "); } } typedef struct { int pid; uint64_t last_pts; uint64_t last_dts; } MpegTsPid; // MPEG-TS transport packet typedef struct { int len; // 8 Number of bytes in the adaptation field immediately following this byte int discont; // 1 Set to 1 if current TS packet is in a discontinuity state int random_access; // 1 Set to 1 if the PES packet in this TS packet starts a video/audio sequence int es_prior; // 1 higher priority) int pcr_flag; // 1 adaptation field does contain a PCR field int opcr_flag; // 1 adaptation field does contain an OPCR field int splice_flag; // 1 splice countdown field in adaptation field int priv_flag; // 1 private data bytes in adaptation field int ext_flag; // 1 adaptation field extension uint64_t pcr; // 33+6+9 Program clock reference (90 kHz base) int pcr_ext; // PCR extension (27 MHz) uint64_t opcr; // 33+6+9 Original Program clock reference. Helps when one TS is copied into another int opcr_ext; // OPCR extension (27 MHz) int splice; // 8 Indicates how many TS packets from this one a splicing point occurs (may be negative) } MpegTsAdaptField; typedef struct { int error_flag; // 1 Transport Error Indicator int payload_flag; // 1 Payload Unit Start Indicator int priority_flag; // 1 Transport Priority int pid; // 13 Packet ID int scramble; // 2 Transport scrambling control (0,1,2,3) int adapt_field; // 2 Adaptation field control (1,2,3) int cc; // 4 Continuity counter MpegTsAdaptField af; } MpegTsPacket; // PSI (Program Specific Information) structures /* SI (system information) section */ typedef struct { int skip_bytes; // 8 skip bytes to payload int table_id; // 8 table ID int syntax_flag; // 1 section syntax indicator int pvt_flag; // 1 private indicator // 2 reserved int len; // 12 section length int len_pos; // .. section length counts from this point // if (syntax_flag == 0), next private data .len-4 bytes // if syntax_flag == 1 then use next fields: int ext_id; // 16 table ID extension // 2 reserved int version; // 5 version number int next_flag; // 1 current next indicator int num; // 8 section number int last_num; // 8 last section number // .. private data .len-9 bytes } MpegTsSiHeader; typedef struct { int tag; // 8 descriptor tag int len; // 8 descriptor data length in bytes uint8_t *data; // raw data } MpegTsDescriptor; /* PAT (program association table */ typedef struct { int num; // 16 program num // 3 reserved, '111' int pid; // 13 packets with this PID are assumed to be PMT tables } MpegTsPatRecord; typedef struct { MpegTsSiHeader *h; // .h.table_id = 0x00 // .h.section_syntax = 1 // .h.ext_id = transport stream id int record_count; // MpegTsPatRecord records[32]; } MpegTsPat; /* PMT (program map table) */ typedef struct { int es_type; // 08 // 03 reserved '111' int es_pid; // 13 // 04 reserved int desc_len; // 12 First two bits must be zero. Entire value may be zero int desc_count; // MpegTsDescriptor desc[8]; } MpegTsPmtRecord; typedef struct { MpegTsSiHeader *h; // .h.table_id = 0x02 // .h.section_syntax = 1 // .h.ext_id = program num // .. // 03 reserved int pcr_pid; // 13 PID of general timecode stream, or 0x1FFF // 4 reserved int info_len; // 12 Sum size of following program descriptors. int record_count; // MpegTsPmtRecord records[32]; } MpegTsPmt; /* SDT (Service Description Table) */ typedef struct { int service_id; // 16 service ID // 6 reserved int schedule; // 1 EIT schedule flag int follow; // 1 EIT present following flag int running_status; // 3 running status (1-stop, 2-starts, 3-pause, 4-running, 5-off-air) int free_ca_mode; // 1 free CA mode (0-not scrambled) int descr_len; // 12 descriptor loop length int descr_count; // descriptors count MpegTsDescriptor desc[8]; // descriptors } MpegTsSdtRecord; #define MAX_SDT_SERVICES 16 typedef struct { MpegTsSiHeader *h; // .table_id = 0x42 // .h.ext_id = transport stream id int orig_nw_id; // 16 original network id // 8 reserved // .. services int services_nb; // number of services MpegTsSdtRecord services[MAX_SDT_SERVICES]; } MpegTsSdt; /* PES (Packetized Elementary Stream */ typedef struct { int stream_id; int len; // Can be zero. If the PES packet length is set to zero, the PES packet can be of any length. A value of zero for the PES packet length can be used only when the PES packet payload is a video elementary stream. int scrambl; // 00 implies not scrambled int prior_flag; int align_flag; // 1 indicates that the PES packet header is immediately followed by the video start code or audio syncword int copyryght_flag; // 1 implies copyrighted int orig_flag; // 1 implies original int pts_dts; // 2 (11 = both present, 10 = only PTS int escr_flag; // 1 ESCR flag int es_rate_flag; // 1 ES rate flag int dsm_trick_mode_flag; // 1 DSM trick mode flag int add_copy_inf_flag; // 1 Additional copy info flag int crc_flag; // 1 CRC flag int ext_flag; // 1 extension flag int data_len; // 8 PES header data length // ..optional fields // Presentation Time Stamp / Decode Time Stamp uint64_t pts; // 5b 001? 32..30 1 / 29..15 1 / 14..00 1 uint64_t dts; // 5b 00?1 32..30 1 / 29..15 1 / 14..00 1 // Elementary Stream Clock Reference, used if the stream and system levels are not synchronized long int escr_base; // 6b 00 32..30 1 29..15 1 14..00 1 ext 1 int escr_ext; // The rate at which data is delivered for this stream, in units of 50 bytes/second int es_rate; // 16 1 es_rate 1 int dsm_trick_mode; int add_copy_info; int prev_crc; // 16 The polynomial used is X^16 + X^12 + X^5 + 1 } MpegTsPes; typedef struct { uint8_t buf[MAX_BUFFER]; int pos; int pids_count; MpegTsPid pids[32]; // stream pids from PAT/PMT MpegTsPid *cur_pid; // current pid uint64_t last_pcr; MpegTsPacket *cur_pkt; // current transport packet } MpegTS; static MpegTS ts; char* get_pid_description(int pid) { switch (pid) { case PAT_PID: return "Program Association Table (PAT)"; case CAT_PID: return "Conditional Access Table (CAT)"; case TSDT_PID: return "Transport Stream Description (TSDT)"; case NIT_PID: return "Network Information Table (NIT)"; case SDT_PID: return "Service Description Table (SDT)"; } return ""; } char* get_pid_name(int pid) { switch (pid) { case 0x0000: return "PAT"; case 0x0001: return "CAT"; case 0x0010: return "NIT,ST"; case 0x0011: return "SDT,BAT,ST"; case 0x0012: return "EIT,ST_CIT"; case 0x0013: return "RST,ST"; case 0x0014: return "TDT,TOT,ST"; case 0x0015: return "netwirk synchronization"; case 0x0016: return "RNT"; case 0x001c: return "inband signaling"; case 0x001d: return "measurement"; case 0x001e: return "DIT"; case 0x001f: return "SIT"; } return ""; } char* get_table_id_name(int pid) { switch (pid) { case 0x00: return "program association"; case 0x01: return "conditional access"; case 0x02: return "program map"; case 0x03: return "transport stream description"; // 0x04 - 0x3f "reserved" case 0x40: return "actual network info"; case 0x41: return "other network info"; case 0x42: return "actual service description"; case 0x46: return "other service description"; case 0x4a: return "bouquet association"; case 0x4e: return "actual event info now"; case 0x4f: return "other event info now"; // 0x50 - 0x5f "event info actual schedule" // 0x60 - 0x6f "event info other schedule" case 0x70: return "time data"; case 0x71: return "running status"; case 0x72: return "stuffing"; case 0x73: return "time offset"; case 0x74: return "application information"; case 0x75: return "container"; case 0x76: return "related content"; case 0x77: return "content id"; case 0x78: return "MPE-FEC"; case 0x79: return "resolution notification"; case 0x7a: return "MPE-IFEC"; // 0x7b - 0x7d "reserved" case 0x7e: return "discontinuity info"; case 0x7f: return "selection info"; // 0x80 - 0xfe "user defined" case 0xff: return "reserved"; } return "reserved"; } char* get_stream_id_name(int stream_id) { switch (stream_id) { case 0x0000: return "reserved"; case 0x0001: return "ISO/IEC 11172-2 (MPEG-1 Video)"; case 0x0002: return "ISO/IEC 13818-2 (MPEG-2 Video)"; case 0x0003: return "ISO/IEC 11172-3 (MPEG-1 Audio)"; case 0x0004: return "ISO/IEC 13818-3 (MPEG-2 Audio)"; case 0x0005: return "ISO/IEC 13818-1 (private section)"; case 0x0006: return "ISO/IEC 13818-1 PES"; case 0x0007: return "ISO/IEC 13522 MHEG"; case 0x0008: return "ITU-T H.222.0 annex A DSM-CC"; case 0x0009: return "ITU-T H.222.1"; case 0x000a: return "ISO/IEC 13818-6 DSM-CC type A"; case 0x000b: return "ISO/IEC 13818-6 DSM-CC type B"; case 0x000c: return "ISO/IEC 13818-6 DSM-CC type C"; case 0x000d: return "ISO/IEC 13818-6 DSM-CC type D"; case 0x000e: return "ISO/IEC 13818-1 (auxiliary)"; case 0x000f: return "ISO/IEC 13818-7 (AAC Audio)"; case 0x0010: return "ISO/IEC 14496-2 (MPEG-4 Video)"; case 0x0011: return "ISO/IEC 14496-3 (AAC LATM Audio)"; case 0x001b: return "ITU-T H.264 (h264 Video)"; case 0x00ea: return "(VC-1 Video)"; case 0x00d1: return "(DIRAC Video)"; case 0x0081: return "(AC3 Audio)"; case 0x008a: return "(DTS Audio)"; case 0x00bd: return "(non-MPEG Audio, subpictures)"; case 0x00be: return "(padding stream)"; case 0x00bf: return "(navigation data)"; //case 0x001e: return ""; //case 0x001f: return ""; default: { if ((stream_id >= 0xc0) && (stream_id <= 0xdf)) return "(AUDIO stream)"; else if ((stream_id >= 0xe0) && (stream_id <= 0xef)) return "(VIDEO stream)"; } } return ""; } int parse_descriptor(uint8_t *buf, int *pos, MpegTsDescriptor *desc) { char *tmp_str; uint8_t tmp8; desc->tag = get_8(buf, pos); desc->len = get_8(buf, pos); if (desc->tag == 0x48) { // service descriptor tmp8 = get_8(buf, pos); printf(" service type=0x%02x", tmp8); tmp_str = get_str8(buf, pos); printf(" author=\"%s\"", tmp_str); free(tmp_str); tmp_str = get_str8(buf, pos); printf(" info=\"%s\"\n", tmp_str); free(tmp_str); return 0; } else { printf(" descriptor tag=0x%02x length=%u \n", desc->tag, desc->len); } //desc->data = malloc(desc->len+1); //memcpy(desc->data, &buf[*pos], desc->len); *pos+=desc->len; return 0; } int parse_si_header(uint8_t *buf, int *pos, MpegTsSiHeader *h) { uint8_t tmp8; uint16_t tmp16; h->skip_bytes = get_8(buf, pos); h->table_id = get_8(buf, pos); // must be 0x42 tmp16 = get_16(buf, pos); h->syntax_flag = get_bit(tmp16, 15); h->len = tmp16 & 0x0fff; // 000 1111 h->len_pos = *pos; if (h->syntax_flag) { h->ext_id = get_16(buf, pos); tmp8 = get_8(buf, pos); h->next_flag = get_bit(tmp8, 0); h->version = tmp8 & 0x3e >> 1; // 0011 1110 h->num = get_8(buf, pos); h->last_num = get_8(buf, pos); } // dump SI header if (_verbose) { // dump header printf(" SI header: skip_bytes=%u table_id=%#02x length=%u", h->skip_bytes, h->table_id, h->len); dump_flag(h->syntax_flag, "syntax"); if (h->syntax_flag) { printf(" ext_id=%u version=%u number=%u last_number=%u", h->ext_id, h->version, h->num, h->last_num); dump_flag(h->next_flag, "next"); } printf("\n"); } return 0; } int parse_pat(uint8_t *buf, int *pos, MpegTsSiHeader *h) { int i, i2, end_pos, sect_end; MpegTsPat pat_data; MpegTsPat *pat = &pat_data; uint16_t tmp16; MpegTsPatRecord *r; // non-verbose output if (!_verbose) { printf("pid=0x%04x [PAT]\n", ts.cur_pkt->pid); } pat->h = h; pat->record_count = 0; sect_end = pat->h->len_pos + pat->h->len - 4; // section length - CRC32 while ((*pos < PKT_SIZE) && (*pos < sect_end)) { r = &pat->records[pat->record_count]; pat->record_count++; r->num = get_16(buf, pos); tmp16 = get_16(buf, pos); r->pid = (tmp16 & 0x1fff); // 0001 1111 // dump record info printf(" PAT program num=%u pmt_pid=0x%04x \n", r->num, r->pid); } } int parse_pmt(uint8_t *buf, int *pos, MpegTsSiHeader *h) { int start_pos = *pos; int i, i2, end_pos, sect_end; MpegTsPmtRecord *r; // single table record MpegTsPmt t_data; // table pointer MpegTsPmt *t = &t_data; // table data uint8_t tmp8; uint16_t tmp16; t->record_count = 0; t->h = h; t->pcr_pid = get_16(buf, pos) & 0x1fff; // 0001 1111 t->info_len = get_16(buf, pos) & 0x0fff; // 0000 1111 // non-verbose output if (!_verbose) { printf("pid=0x%04x [PMT]\n", ts.cur_pkt->pid); } // dump header printf(" PMT pcr_pid=0x%04x", t->pcr_pid); if (t->info_len > 0) printf(" info_len=%u", t->info_len); printf("\n"); sect_end = t->h->len_pos + t->h->len - 4; // section length - CRC32 while ((*pos < PKT_SIZE) && (*pos < sect_end)) { // parse table record r = &t->records[t->record_count]; t->record_count+=1; r->desc_count = 0; r->es_type = get_8(buf, pos); r->es_pid = get_16(buf, pos) & 0x1fff; // 0001 1111 r->desc_len = get_16(buf, pos) & 0x0fff; // 0000 1111 // add program PID to list i2=0; for (i=0; i < ts.pids_count; i++) { if (ts.pids[i].pid == r->es_pid) i2=1; break; } if (i2 == 0) { ts.pids[ts.pids_count].pid = r->es_pid; ts.pids_count++; } // dump table record info printf(" PMT elementary stream type=0x%02x pid=0x%04x", r->es_type, r->es_pid); if (r->desc_len > 0) printf(" desc_len=%u", r->desc_len); printf("\n"); printf(" %s\n", get_stream_id_name(r->es_type)); if (r->desc_len <= (PKT_SIZE - *pos)) { // parse descriptors r->desc_count = 0; end_pos = *pos+r->desc_len; while (*pos < end_pos) { parse_descriptor(buf, pos, &r->desc[r->desc_count]); r->desc_count+=1; } } } return 0; } int parse_sdt(uint8_t *buf, int *pos, MpegTsSiHeader *h) { int start_pos = *pos; int i, i2, end_pos, sect_end; MpegTsSdtRecord *sv; MpegTsSdt sdt_data; MpegTsSdt *sdt = &sdt_data; uint8_t tmp8; uint16_t tmp16; sdt->h = h; sdt->orig_nw_id = get_16(buf, pos); tmp8 = get_8(buf, pos); // reserved field sdt->services_nb = 0; // non-verbose output if (!_verbose) { printf("pid=0x%04x [SDT]\n", ts.cur_pkt->pid); } // dump header printf(" SDT orig_network_id=0x%04x ", sdt->orig_nw_id); printf("\n"); sect_end = sdt->h->len_pos + sdt->h->len - 4; // section length - CRC32 while ((*pos < PKT_SIZE) && (*pos < sect_end)) { // parse service sdt->services_nb++; sv = &sdt->services[sdt->services_nb-1]; sv->service_id = get_16(buf, pos); tmp8 = get_8(buf, pos); sv->schedule = get_bit(tmp8, 1); sv->follow = get_bit(tmp8, 0); tmp16 = get_16(buf, pos); sv->running_status = (tmp16 & 0x7000) >> 13; // 1110 0000 sv->free_ca_mode = get_bit(tmp16, 12); sv->descr_len = tmp16 & 0x0fff; // 0000 1111 // dump service info printf(" SDT service_id=0x%04x running=%u descr_len=%u", sv->service_id, sv->running_status, sv->descr_len); dump_flag(sv->schedule, "schedule"); dump_flag(sv->follow, "follow"); dump_flag(sv->free_ca_mode, "free_ca_mode"); printf("\n"); if (sv->descr_len <= (PKT_SIZE - *pos)) { // parse descriptors sv->descr_count = 0; end_pos = *pos+sv->descr_len; while (*pos < end_pos) { parse_descriptor(buf, pos, &sv->desc[sv->descr_count]); sv->descr_count++; } } } return 0; } uint64_t parse_pes_pts(uint8_t *buf, int *pos) { uint64_t tmp64; uint16_t tmp16; uint8_t tmp8; tmp8 = get_8(buf, pos); tmp64 = ((int64_t)tmp8 >> 1) &0x07 << 30; tmp16 = get_16(buf, pos); tmp64 |= ((int64_t)tmp16 >> 1) << 15; tmp16 = get_16(buf, pos); tmp64 |= ((int64_t)tmp16 >> 1); return tmp64; } int parse_pes(uint8_t *buf, int *pos) { // PES uint16_t tmp16; uint8_t tmp8; MpegTsPes pes_data; MpegTsPes *pes = &pes_data; MpegTsPid *pid; int ii, payload_pos; // check for PES header // Packet start code prefix 3 bytes 0x000001 tmp16 = get_16(buf, pos); tmp8 = get_8(buf, pos); if ((tmp16 == 0 && tmp8 == 1) != 1) { // it's not PES, rewind pos backward *pos-=3; return -1; } // PES detected tmp8 = get_8(buf, pos); pes->stream_id = tmp8; pes->len = get_16(buf, pos);; // dump PES header if (_verbose) { printf(" PES stream_id=%#x length=%u ", pes->stream_id, pes->len); printf(" %s", get_stream_id_name(pes->stream_id)); printf("\n"); } // non-verbose output if (!_verbose) { printf("pid=0x%04x [PES] %s\n", ts.cur_pkt->pid, get_stream_id_name(pes->stream_id)); // PCR if (ts.cur_pkt->adapt_field) { if (ts.cur_pkt->af.pcr_flag) { printf(" PCR=%llu (%+lli)", ts.cur_pkt->af.pcr, (ts.cur_pkt->af.pcr - ts.last_pcr)); if (ts.cur_pkt->af.pcr_ext != 0) printf(" pcr_ext=%u", ts.cur_pkt->af.pcr_ext); printf("\n"); } } } // optional header tmp8 = get_8(buf, pos); ii = (tmp8 & 0xc0) >> 6; //1100 0000; if (ii == 2) { // bin '10' pes->scrambl = (tmp8 & 0x30) >> 4; // 0011 0000 pes->prior_flag = get_bit(tmp8, 3); pes->align_flag = get_bit(tmp8, 2); pes->copyryght_flag = get_bit(tmp8, 1); pes->orig_flag = get_bit(tmp8, 0); tmp8 = get_8(buf, pos); pes->pts_dts = (tmp8 & 0xc0) >> 6; //1100 0000 pes->escr_flag = get_bit(tmp8, 5); pes->es_rate_flag = get_bit(tmp8, 4); pes->dsm_trick_mode_flag = get_bit(tmp8, 3); pes->add_copy_inf_flag = get_bit(tmp8, 2); pes->crc_flag = get_bit(tmp8, 1); pes->ext_flag = get_bit(tmp8, 0); pes->data_len = get_8(buf, pos); payload_pos = *pos + pes->data_len; // dump optional header if (_verbose) { printf(" PES flags:"); dump_flag((pes->scrambl), "scramble"); dump_flag((pes->pts_dts >> 1), "pts"); dump_flag((pes->pts_dts & 0x01), "dts"); dump_flag(pes->prior_flag, "priority"); dump_flag(pes->align_flag, "align"); dump_flag(pes->copyryght_flag, "(c)"); dump_flag(pes->orig_flag, "original"); dump_flag(pes->escr_flag, "ESCR"); dump_flag(pes->es_rate_flag, "ES_rate"); dump_flag(pes->dsm_trick_mode_flag, "DSM_trick_mode"); dump_flag(pes->add_copy_inf_flag, "add_copy_inf"); dump_flag(pes->crc_flag, "CRC"); dump_flag(pes->ext_flag, "ext"); printf("\n"); } pes->pts = pes->dts = 0; if (pes->pts_dts > 1) { // PTS pes->pts = parse_pes_pts(buf, pos); if (_verbose) printf(" PES PTS=%llu", pes->pts); if (!_verbose) printf(" PTS=%llu", pes->pts); if (ts.cur_pid != NULL) { printf(" (%+lli)", (pes->pts - ts.cur_pid->last_pts)); ts.cur_pid->last_pts = pes->pts; } if (!_verbose) printf("\n"); if (pes->pts_dts == 3) { // DTS pes->dts = parse_pes_pts(buf, pos); if (_verbose) printf(" DTS=%llu", pes->dts); if (!_verbose) printf(" DTS=%llu", pes->dts); if (ts.cur_pid != NULL) { printf(" (%+lli)", (pes->dts - ts.cur_pid->last_dts)); ts.cur_pid->last_dts = pes->dts; } if (!_verbose) printf("\n"); } if (_verbose) printf("\n"); } // other PES header data //... // skip to payload *pos = payload_pos; } // PES payload if (*pos+4 <= PKT_SIZE) { // dump payload header 4cc if (_verbose) { printf(" PES payload 0x%08x", get_32(buf, pos)); if (*pos+4 <= PKT_SIZE) { // dump second 4cc printf(" 0x%08x", get_32(buf, pos)); } printf("\n"); } } return 0; } // PCR field value uint64_t parse_pcr(uint8_t *buf, int *pos, int *ext) { // 33 base // 06 reserved // 09 extension uint64_t tmp64 = 0; uint8_t tmp8 = 0; int i; for (i=0; i<4; i++) { tmp64 |= get_8(buf, pos) << (8*(3-i)); } tmp8 = get_8(buf, pos); tmp64 = (tmp64 << 1) | get_bit(tmp8, 7); *ext = (get_bit(tmp8, 0) << 8) | get_8(buf, pos); return tmp64; } int parse_pkt(uint8_t *buf, MpegTsPacket *pkt) { int pos_value = 0; int *pos = &pos_value; int i, ii; uint16_t tmp16; uint8_t tmp8; MpegTsAdaptField *af = &pkt->af; MpegTsSiHeader sih; if (buf[*pos] != 0x47) return -1; ts.cur_pkt = pkt; *pos+=1; tmp16 = get_16(buf, pos); // dump_16(tmp16); printf(" \n"); pkt->error_flag = get_bit(tmp16, 15); pkt->payload_flag = get_bit(tmp16, 14); pkt->priority_flag = get_bit(tmp16, 13); pkt->pid = tmp16 & 0x1fff; // 0x1f = 0001 1111 tmp8 = get_8(buf, pos); pkt->scramble = (tmp8 & 0xc0) >> 6; // 0xc0 = 1100 0000 pkt->adapt_field = (tmp8 & 0x30) >> 4; // 0x30 = 0011 0000 pkt->cc = tmp8 & 0x0f; // 0000 1111 // dump packet if (_verbose) { printf("pid=0x%04x cc=%u", pkt->pid, pkt->cc); dump_flag(pkt->error_flag, "error"); dump_flag(pkt->payload_flag, "PES/SI"); dump_flag(pkt->priority_flag, "priority"); dump_flag(pkt->scramble, "scramble"); dump_flag(pkt->adapt_field > 1, "adapt"); dump_flag(pkt->adapt_field & 1, "payload"); printf(" %s", get_pid_description(pkt->pid)); printf("\n"); } // adaptation field (optional) if (pkt->adapt_field > 1) { af->len = get_8(buf, pos); tmp8 = get_8(buf, pos); af->discont = (tmp8 & ( 1 << 7 )) >> 7; af->random_access = (tmp8 & ( 1 << 6 )) >> 6; af->es_prior = (tmp8 & ( 1 << 5 )) >> 5; af->pcr_flag = (tmp8 & ( 1 << 4 )) >> 4; af->opcr_flag = (tmp8 & ( 1 << 3 )) >> 3; af->splice_flag = (tmp8 & ( 1 << 2 )) >> 2; af->priv_flag = (tmp8 & ( 1 << 1 )) >> 1; af->ext_flag = (tmp8 & ( 1 << 0 )) >> 0; // dump adaptation field if (_verbose) { printf(" adapt_field length=%u", af->len); dump_flag(af->discont, "discontinuity"); dump_flag(af->random_access, "random_access"); dump_flag(af->es_prior, "es_priority"); //dump_flag(af->pcr_flag, "PCR"); //dump_flag(af->opcr_flag, "OPCR"); //dump_flag(af->splice_flag, "splice"); dump_flag(af->priv_flag, "private"); dump_flag(af->ext_flag, "extension"); } // optional header data if (af->pcr_flag) { ts.last_pcr = af->pcr; af->pcr = parse_pcr(buf, pos, &(af->pcr_ext)); if (_verbose) { printf(" PCR=%llu (%+lli)", af->pcr, (af->pcr - ts.last_pcr)); if (af->pcr_ext != 0) printf(" pcr_ext=%u", af->pcr_ext); } } if (af->opcr_flag) { af->opcr = parse_pcr(buf, pos, &(af->opcr_ext)); if (_verbose) { printf(" OPCR=%llu", af->pcr); if (af->opcr_ext != 0) printf(" opcr_ext=%u", af->opcr_ext); } } if (af->splice_flag) { af->splice = get_8(buf, pos); if (_verbose) printf(" splice_count=%i", af->splice); } if (_verbose) printf("\n"); *pos = 4 + 1 + af->len; // skip after adaptation field } ts.cur_pid = NULL; if (pkt->payload_flag) { // packet have payload section // look for PID in PAT table for (i=0; i<ts.pids_count; i++) { if (ts.pids[i].pid == pkt->pid) { ts.cur_pid = &ts.pids[i]; // detect PES header if (parse_pes(buf, pos) == 0) return 0; } } if (pkt->pid > 0x20) { if (parse_pes(buf, pos) == 0) return 0; } tmp8 = buf[*pos+1]; // table_id if (_verbose) printf(" service table name: %s\n", get_table_id_name(tmp8)); parse_si_header(buf, pos, &sih); switch (pkt->pid) { case 0x0000: parse_pat(buf, pos, &sih); break; // PAT table case 0x0011: parse_sdt(buf, pos, &sih); break; // SDT table default: { // other packets if (tmp8 == 0x02) { // PMT table parse_pmt(buf, pos, &sih); return 0; } if (pkt->payload_flag) { // unknown payload tmp16 = get_16(buf, pos); if (_verbose) { printf(" payload header=0x%02x ", tmp16); dump_bits16(tmp16); printf("\n"); } } } } } return 0; } int do_some() { int ir, i, pos47; MpegTsPacket pkt; uint8_t pkt_buf[PKT_SIZE]; ts.last_pcr = 0; ts.pids_count = 0; ir = fread(ts.buf, 1, MAX_BUFFER, stdin); if (ir != MAX_BUFFER) return -1; for (i=0; i<ir; i++) { if (ts.buf[i] == 0x47) { pos47 = i; memcpy(&pkt_buf, &ts.buf[i], sizeof(pkt_buf)); i+=sizeof(pkt_buf)-1; parse_pkt(&pkt_buf[0], &pkt); fflush(stdout); } } } int main(int argc, char**argv) { if (argc > 1) { if ((strcmp(argv[1], "--help") == 0) || (strcmp(argv[1], "-h") ==0)) { printf(" Usage: %s [-b] < input.ts > output.log\n", argv[0]); printf("Options:\n"); printf(" -b - brief mode\n"); return 0; } if (strcmp(argv[1], "-b") == 0) { _verbose=0; } } do_some(); return 0; }