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;
}
ċ
tsp.c
(28k)
Sergey Bodrov,
17 апр. 2015 г., 4:32
Comments