int parse_PMT (struct ccx_demuxer *ctx, unsigned char *buf, int len, struct program_info *pinfo) { int must_flush = 0; int ret = 0; unsigned char desc_len = 0; unsigned char *sbuf = buf; unsigned int olen = len; uint32_t crc; uint8_t table_id; uint16_t section_length; uint16_t program_number; uint8_t version_number; uint8_t current_next_indicator; uint8_t section_number; uint8_t last_section_number; uint16_t pi_length; crc = (*(int32_t*)(sbuf+olen-4)); table_id = buf[0]; /* TO-DO: We're currently parsing the PMT making assumptions that there's only one section with table_id=2, but that doesn't have to be the case. There's a sample (friends_tbs.ts) that shows a previous section with table_id = 0xc0. I can't find any place that says that 0xc0 (Program Information Table) must come before table_id = 2, so we should process sections in any order. Check https://github.com/CCExtractor/ccextractor/issues/385 for more info */ if (table_id == 0xC0) { /* * Acc to System Information for Satellite Distribution * of Digital Television for Cable and MMDS (ANSI/SCTE 57 2003 ) * PROGRAM INFORMATION Table found in PMT */ dbg_print(CCX_DMT_PARSE, "PMT: PROGRAM INFORMATION Table need implementation"); // For now, just parse its length and remove it from the buffer unsigned c0length = (buf[1] << 8 | buf[2]) & 0xFFF; // 12 bytes dbg_print(CCX_DMT_PARSE, "Program information table length: %u", c0length); memmove(buf, buf + c0length + 3, len - c0length -3); // First 3 bytes are for the table_id and the length, don't count table_id = buf[0]; // return 0; } else if (table_id == 0xC1) { //SCTE 57 2003 dbg_print(CCX_DMT_PARSE, "PMT: PROGRAM NAME Table need implementation"); unsigned c0length = (buf[1] << 8 | buf[2]) & 0xFFF; // 12 bytes dbg_print(CCX_DMT_PARSE, "Program name message length: %u", c0length); memmove(buf, buf + c0length + 3, len - c0length - 3); // First 3 bytes are for the table_id and the length, don't count table_id = buf[0]; //return 0; } else if(table_id != 0x2) { mprint("Please Report: Unknown table id in PMT expected 0x02 found 0x%X\n", table_id); return 0; } section_length = (((buf[1] & 0x0F) << 8)| buf[2]); if(section_length > (len - 3)) { return 0; //We don't have the full section yet. We will parse again when we have it. } program_number = ((buf[3] << 8) | buf[4]); version_number = (buf[5] & 0x3E) >> 1; current_next_indicator = buf[5] & 0x01; // This table is not active, no need to evaluate if (!current_next_indicator) return 0; memcpy (pinfo->saved_section, buf, len); if (pinfo->analysed_PMT_once == CCX_TRUE && pinfo->version == version_number) { if (pinfo->version == version_number) { /* Same Version number and there was valid or similar CRC last time */ if ( pinfo->valid_crc == CCX_TRUE || pinfo->crc == crc ) return 0; } else if ( (pinfo->version+1)%32 != version_number) mprint("TS PMT:Glitch in version number incremnt"); } pinfo->version = version_number; section_number = buf[6]; last_section_number = buf[7]; if ( last_section_number > 0 ) { mprint("Long PMTs are not supported - skipped.\n"); return 0; } pinfo->pcr_pid = (((buf[8] & 0x1F) << 8) | buf[9]); pi_length = (((buf[10] & 0x0F) << 8) | buf[11]); if( 12 + pi_length > len ) { // If we would support long PMTs, this would be wrong. mprint("program_info_length cannot be longer than the payload_length - skipped\n"); return 0; } buf += 12 + pi_length; len -= (12 + pi_length); unsigned stream_data = section_length - 9 - pi_length - 4; // prev. bytes and CRC dbg_print(CCX_DMT_PARSE, "Read PMT packet (id: %u) program number: %u\n", table_id, program_number); dbg_print(CCX_DMT_PARSE, " section length: %u number: %u last: %u\n", section_length, section_number, last_section_number); dbg_print(CCX_DMT_PARSE, " version_number: %u current_next_indicator: %u\n", version_number, current_next_indicator); dbg_print(CCX_DMT_PARSE, " PCR_PID: %u data length: %u payload_length: %u\n", pinfo->pcr_pid, stream_data, len); if (!pmt_warning_shown && stream_data+4 > len ) { dbg_print (CCX_DMT_GENERIC_NOTICES, "\rWarning: Probably parsing incomplete PMT, expected data longer than available payload.\n"); pmt_warning_shown=1; } // Make a note of the program number for all PIDs, so we can report it later for( unsigned i=0; i < stream_data && (i+4)<len; i+=5) { enum ccx_stream_type stream_type = buf[i]; unsigned elementary_PID = (((buf[i+1] & 0x1F) << 8) | buf[i+2]); unsigned ES_info_length = (((buf[i+3] & 0x0F) << 8) | buf[i+4]); if (ctx->PIDs_programs[elementary_PID]==NULL) { ctx->PIDs_programs[elementary_PID]=(struct PMT_entry *) malloc (sizeof (struct PMT_entry)); if (ctx->PIDs_programs[elementary_PID]==NULL) fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory to process PMT."); } ctx->PIDs_programs[elementary_PID]->elementary_PID=elementary_PID; ctx->PIDs_programs[elementary_PID]->stream_type = stream_type; ctx->PIDs_programs[elementary_PID]->program_number=program_number; ctx->PIDs_programs[elementary_PID]->printable_stream_type=get_printable_stream_type (stream_type); dbg_print(CCX_DMT_PMT, "%6u | %3X (%3u) | %s\n",elementary_PID,stream_type,stream_type, desc[ctx->PIDs_programs[elementary_PID]->printable_stream_type]); process_ccx_mpeg_descriptor (buf+i+5,ES_info_length); i += ES_info_length; } dbg_print(CCX_DMT_PMT, "---\n"); dbg_print(CCX_DMT_VERBOSE, "\nProgram map section (PMT)\n"); for (unsigned i=0; i < stream_data && (i+4)<len; i+=5) { enum ccx_stream_type stream_type = buf[i]; unsigned elementary_PID = (((buf[i+1] & 0x1F) << 8) | buf[i+2]); unsigned ES_info_length = (((buf[i+3] & 0x0F) << 8) | buf[i+4]); if (!ccx_options.print_file_reports || stream_type != CCX_STREAM_TYPE_PRIVATE_MPEG2 || !ES_info_length) { i += ES_info_length; continue; } unsigned char *es_info = buf + i + 5; for (desc_len = 0;(buf + i + 5 + ES_info_length) > es_info; es_info += desc_len) { enum ccx_mpeg_descriptor descriptor_tag = (enum ccx_mpeg_descriptor)(*es_info++); desc_len = (*es_info++); if(descriptor_tag == CCX_MPEG_DSC_DVB_SUBTITLE) { int k = 0; for (int j = 0; j < SUB_STREAMS_CNT; j++) { if (ctx->freport.dvb_sub_pid[j] == 0) k = j; if (ctx->freport.dvb_sub_pid[j] == elementary_PID) { k = j; break; } } ctx->freport.dvb_sub_pid[k] = elementary_PID; } if(IS_VALID_TELETEXT_DESC(descriptor_tag)) { int k = 0; for (int j = 0; j < SUB_STREAMS_CNT; j++) { if (ctx->freport.tlt_sub_pid[j] == 0) k = j; if (ctx->freport.tlt_sub_pid[j] == elementary_PID) { k = j; break; } } ctx->freport.tlt_sub_pid[k] = elementary_PID; } } i += ES_info_length; } for( unsigned i=0; i < stream_data && (i+4)<len; i+=5) { enum ccx_stream_type stream_type = buf[i]; unsigned elementary_PID = (((buf[i+1] & 0x1F) << 8) | buf[i+2]); unsigned ES_info_length = (((buf[i+3] & 0x0F) << 8) | buf[i+4]); if(stream_type == CCX_STREAM_TYPE_PRIVATE_MPEG2 && ES_info_length ) { unsigned char *es_info = buf + i + 5; for (desc_len = 0;(buf + i + 5 + ES_info_length) > es_info ;es_info += desc_len) { void *ptr; enum ccx_mpeg_descriptor descriptor_tag = (enum ccx_mpeg_descriptor)(*es_info++); desc_len = (*es_info++); if(CCX_MPEG_DESC_DATA_COMP == descriptor_tag) { int16_t component_id = 0; if(!IS_FEASIBLE(ctx->codec, ctx->nocodec, CCX_CODEC_ISDB_CC)) continue; if (desc_len < 2) break; component_id = RB16(es_info); if (component_id != 0x08) break; mprint ("*****ISDB subtitles detected\n"); ptr = init_isdb_decoder(); if (ptr == NULL) break; update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_ISDB_CC, program_number, ptr); } if(CCX_MPEG_DSC_DVB_SUBTITLE == descriptor_tag) { struct dvb_config cnf; #ifndef ENABLE_OCR if(ccx_options.write_format != CCX_OF_SPUPNG) { mprint ("DVB subtitles detected, OCR subsystem not present. Use -out=spupng for graphic output\n"); continue; } #endif if (!IS_FEASIBLE(ctx->codec, ctx->nocodec, CCX_CODEC_DVB)) continue; memset((void*)&cnf,0,sizeof(struct dvb_config)); ret = parse_dvb_description(&cnf,es_info,desc_len); if(ret < 0) break; ptr = dvbsub_init_decoder(&cnf); if (ptr == NULL) break; update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_DVB, program_number, ptr); max_dif = 30; } } } else if(stream_type == CCX_STREAM_TYPE_PRIVATE_USER_MPEG2 && ES_info_length ) { //if this any generally used video stream tyoe get clashed with ATSC/SCTE standard //then this code can go in some atsc flag unsigned char *es_info = buf + i + 5; for (desc_len = 0;(buf + i + 5 + ES_info_length) > es_info ;es_info += desc_len) { enum ccx_mpeg_descriptor descriptor_tag = (enum ccx_mpeg_descriptor)(*es_info++); int nb_service; int is_608; int ser_i; desc_len = (*es_info++); if (CCX_MPEG_DSC_CAPTION_SERVICE == descriptor_tag) { nb_service = es_info[0] & 0x1f; for (ser_i = 0; ser_i < nb_service; ser_i++) { dbg_print(CCX_DMT_PMT, "CC SERVICE %d: language (%c%c%c)", nb_service, es_info[1], es_info[2], es_info[3]); is_608 = es_info[4] >> 7; dbg_print(CCX_DMT_PMT, "%s", is_608?" CEA-608":" CEA-708"); dbg_print(CCX_DMT_PMT, "%s", is_608?" CEA-608":" CEA-708"); } } update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_ATSC_CC, program_number, NULL); } }
int parse_PMT (struct lib_ccx_ctx *ctx, unsigned char *buf, int len, int pos) { int must_flush=0; int ret = 0; unsigned char desc_len = 0; if ((ccx_options.ts_forced_cappid || (ccx_options.teletext_mode==CCX_TXT_IN_USE && ccx_options.ts_cappid)) && cap_stream_type!=CCX_STREAM_TYPE_UNKNOWNSTREAM) // Already know what we need, skip return 0; /* We keep a copy of all PMTs, even if not interesting to us for now */ if (pmt_array[pos].last_pmt_payload!=NULL && len == pmt_array[pos].last_pmt_length && !memcmp (buf, pmt_array[pos].last_pmt_payload, len)) { // dbg_print(CCX_DMT_PMT, "PMT hasn't changed, skipping.\n"); return 0; } pmt_array[pos].last_pmt_payload=(unsigned char *) realloc (pmt_array[pos].last_pmt_payload, len+8); // Extra 8 in case memcpy copies dwords, etc if (pmt_array[pos].last_pmt_payload==NULL) fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory to process PMT.\n"); memcpy (pmt_array[pos].last_pmt_payload, buf, len); pmt_array[pos].last_pmt_length = len; unsigned table_id = buf[0]; unsigned section_length = (((buf[1] & 0x0F) << 8) | buf[2]); unsigned program_number = ((buf[3] << 8) | buf[4]); unsigned version_number = (buf[5] & 0x3E) >> 1; unsigned current_next_indicator = buf[5] & 0x01; if (!current_next_indicator) // This table is not active, no need to evaluate return 0; unsigned section_number = buf[6]; unsigned last_section_number = buf[7]; if ( last_section_number > 0 ) { mprint("Long PMTs are not supported - skipped.\n"); return 0; } unsigned PCR_PID = (((buf[8] & 0x1F) << 8) | buf[9]); unsigned pi_length = (((buf[10] & 0x0F) << 8) | buf[11]); if( 12 + pi_length > len ) { // If we would support long PMTs, this would be wrong. mprint("program_info_length cannot be longer than the payload_length - skipped\n"); return 0; } buf += 12 + pi_length; len = tspacket+188-buf; unsigned stream_data = section_length - 9 - pi_length - 4; // prev. bytes and CRC dbg_print(CCX_DMT_PARSE, "Read PMT packet (id: %u) program number: %u\n", table_id, program_number); dbg_print(CCX_DMT_PARSE, " section length: %u number: %u last: %u\n", section_length, section_number, last_section_number); dbg_print(CCX_DMT_PARSE, " version_number: %u current_next_indicator: %u\n", version_number, current_next_indicator); dbg_print(CCX_DMT_PARSE, " PCR_PID: %u data length: %u payload_length: %u\n", PCR_PID, stream_data, len); if (!pmt_warning_shown && stream_data+4 > len ) { dbg_print (CCX_DMT_GENERIC_NOTICES, "\rWarning: Probably parsing incomplete PMT, expected data longer than available payload.\n"); pmt_warning_shown=1; } dbg_print(CCX_DMT_PMT, "\nProgram Map Table for program %u, PMT PID: %u\n", program_number,payload.pid); // Make a note of the program number for all PIDs, so we can report it later for( unsigned i=0; i < stream_data && (i+4)<len; i+=5) { unsigned ccx_stream_type = buf[i]; unsigned elementary_PID = (((buf[i+1] & 0x1F) << 8) | buf[i+2]); unsigned ES_info_length = (((buf[i+3] & 0x0F) << 8) | buf[i+4]); if (ctx->PIDs_programs[elementary_PID]==NULL) { ctx->PIDs_programs[elementary_PID]=(struct PMT_entry *) malloc (sizeof (struct PMT_entry)); if (ctx->PIDs_programs[elementary_PID]==NULL) fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory to process PMT."); } ctx->PIDs_programs[elementary_PID]->elementary_PID=elementary_PID; ctx->PIDs_programs[elementary_PID]->ccx_stream_type=ccx_stream_type; ctx->PIDs_programs[elementary_PID]->program_number=program_number; ctx->PIDs_programs[elementary_PID]->PMT_PID=payload.pid; ctx->PIDs_programs[elementary_PID]->printable_stream_type=get_printable_stream_type (ccx_stream_type); dbg_print(CCX_DMT_PMT, "%6u | %3X (%3u) | %s\n",elementary_PID,ccx_stream_type,ccx_stream_type, desc[ctx->PIDs_programs[elementary_PID]->printable_stream_type]); process_ccx_mpeg_descriptor (buf+i+5,ES_info_length); i += ES_info_length; } dbg_print(CCX_DMT_PMT, "---\n"); unsigned newcappid = 0; unsigned newcap_stream_type = 0; dbg_print(CCX_DMT_VERBOSE, "\nProgram map section (PMT)\n"); for (unsigned i=0; i < stream_data && (i+4)<len; i+=5) { unsigned ccx_stream_type = buf[i]; unsigned elementary_PID = (((buf[i+1] & 0x1F) << 8) | buf[i+2]); unsigned ES_info_length = (((buf[i+3] & 0x0F) << 8) | buf[i+4]); if (!ccx_options.print_file_reports || ccx_stream_type != CCX_STREAM_TYPE_PRIVATE_MPEG2 || !ES_info_length) { i += ES_info_length; continue; } unsigned char *es_info = buf + i + 5; for (desc_len = 0;(buf + i + 5 + ES_info_length) > es_info; es_info += desc_len) { enum ccx_mpeg_descriptor descriptor_tag = (enum ccx_mpeg_descriptor)(*es_info++); desc_len = (*es_info++); if(descriptor_tag == CCX_MPEG_DSC_DVB_SUBTITLE) { int k = 0; for (int j = 0; j < SUB_STREAMS_CNT; j++) { if (ctx->freport.dvb_sub_pid[i] == 0) k = j; if (ctx->freport.dvb_sub_pid[i] == elementary_PID) { k = j; break; } } ctx->freport.dvb_sub_pid[k] = elementary_PID; } if(IS_VALID_TELETEXT_DESC(descriptor_tag)) { int k = 0; for (int j = 0; j < SUB_STREAMS_CNT; j++) { if (ctx->freport.tlt_sub_pid[i] == 0) k = j; if (ctx->freport.tlt_sub_pid[i] == elementary_PID) { k = j; break; } } ctx->freport.tlt_sub_pid[k] = elementary_PID; } } i += ES_info_length; } if (TS_program_number || !ccx_options.ts_autoprogram) { if( payload.pid != pmtpid) { // This isn't the PMT we are interested in (note: If TS_program_number=0 && // autoprogram then we need to check this PMT in case there's a suitable // stream) return 0; } if (program_number != TS_program_number) // Is this the PMT of the program we want? { // Only use PMTs with matching program number dbg_print(CCX_DMT_PARSE,"Reject this PMT packet (pid: %u) program number: %u\n", pmtpid, program_number); return 0; } } for( unsigned i=0; i < stream_data && (i+4)<len; i+=5) { unsigned ccx_stream_type = buf[i]; unsigned elementary_PID = (((buf[i+1] & 0x1F) << 8) | buf[i+2]); unsigned ES_info_length = (((buf[i+3] & 0x0F) << 8) | buf[i+4]); /* There is no information about elementry stream */ /*if(!ES_info_length) continue; */ if (ccx_options.ts_cappid==0 && ccx_stream_type==ccx_options.ts_datastreamtype) // Found a stream with the type the user wants { ccx_options.ts_forced_cappid=1; ccx_options.ts_cappid = newcappid = elementary_PID; cap_stream_type=CCX_STREAM_TYPE_UNKNOWNSTREAM; } if(IS_FEASIBLE(ccx_options.codec,ccx_options.nocodec,CCX_CODEC_DVB) && !ccx_options.ts_cappid && ccx_stream_type == CCX_STREAM_TYPE_PRIVATE_MPEG2 && ES_info_length ) { unsigned char *es_info = buf + i + 5; for (desc_len = 0;(buf + i + 5 + ES_info_length) > es_info ;es_info += desc_len) { enum ccx_mpeg_descriptor descriptor_tag = (enum ccx_mpeg_descriptor)(*es_info++); desc_len = (*es_info++); #ifndef ENABLE_OCR if(ccx_options.write_format != CCX_OF_SPUPNG ) { mprint ("DVB subtitles detected, OCR subsystem not present. Use -out=spupng for graphic output\n"); continue; } #endif if(CCX_MPEG_DSC_DVB_SUBTITLE == descriptor_tag) { struct dvb_config cnf; memset((void*)&cnf,0,sizeof(struct dvb_config)); ret = parse_dvb_description(&cnf,es_info,desc_len); if(ret < 0) break; ccx_dvb_context = dvbsub_init_decoder(&cnf); if (ccx_dvb_context == NULL) break; ccx_options.ts_cappid = newcappid = elementary_PID; cap_stream_type = newcap_stream_type = ccx_stream_type; max_dif = 30; } } } if (IS_FEASIBLE(ccx_options.codec,ccx_options.nocodec,CCX_CODEC_TELETEXT) && (ccx_options.teletext_mode==CCX_TXT_AUTO_NOT_YET_FOUND || (ccx_options.teletext_mode==CCX_TXT_IN_USE && !ccx_options.ts_cappid)) // Want teletext but don't know the PID yet && ES_info_length && ccx_stream_type == CCX_STREAM_TYPE_PRIVATE_MPEG2) // MPEG-2 Packetized Elementary Stream packets containing private data { unsigned char *es_info = buf + i + 5; for (desc_len = 0;(buf + i + 5 + ES_info_length) - es_info ;es_info += desc_len) { enum ccx_mpeg_descriptor descriptor_tag = (enum ccx_mpeg_descriptor)(*es_info++); desc_len = (*es_info++); if(!IS_VALID_TELETEXT_DESC(descriptor_tag)) continue; telxcc_init(ctx); if (!ccx_options.ts_forced_cappid) { ccx_options.ts_cappid = newcappid = elementary_PID; cap_stream_type = newcap_stream_type = ccx_stream_type; } ccx_options.teletext_mode =CCX_TXT_IN_USE; mprint ("VBI/teletext stream ID %u (0x%x) for SID %u (0x%x)\n", elementary_PID, elementary_PID, program_number, program_number); } } if (ccx_options.teletext_mode==CCX_TXT_FORBIDDEN && ccx_stream_type == CCX_STREAM_TYPE_PRIVATE_MPEG2) // MPEG-2 Packetized Elementary Stream packets containing private data { unsigned descriptor_tag = buf[i + 5]; if (descriptor_tag == 0x45) { ccx_options.ts_cappid = newcappid = elementary_PID; cap_stream_type = newcap_stream_type = ccx_stream_type; mprint ("VBI stream ID %u (0x%x) for SID %u (0x%x) - teletext is disabled, will be processed as closed captions.\n", elementary_PID, elementary_PID, program_number, program_number); } } if (ccx_options.ts_forced_cappid && elementary_PID==ccx_options.ts_cappid && cap_stream_type==CCX_STREAM_TYPE_UNKNOWNSTREAM) { // We found the user selected CAPPID in PMT. We make a note of its type and don't // touch anything else if (ccx_stream_type>=0x80 && ccx_stream_type<=0xFF) { if (ccx_options.ts_forced_streamtype==CCX_STREAM_TYPE_UNKNOWNSTREAM) { mprint ("I can't tell the stream type of the manually selected PID.\n"); mprint ("Please pass -streamtype to select manually.\n"); fatal (EXIT_FAILURE, "(user assistance needed)"); } else cap_stream_type = newcap_stream_type = ccx_options.ts_forced_streamtype; } else cap_stream_type = newcap_stream_type = ccx_stream_type; continue; } if ((ccx_stream_type==CCX_STREAM_TYPE_VIDEO_H264 || ccx_stream_type==CCX_STREAM_TYPE_VIDEO_MPEG2) && ccx_options.teletext_mode != CCX_TXT_IN_USE) { newcappid = elementary_PID; newcap_stream_type = ccx_stream_type; } // For the print command below unsigned tmp_stream_type = get_printable_stream_type (ccx_stream_type); dbg_print(CCX_DMT_VERBOSE, " %s stream [0x%02x] - PID: %u\n", desc[tmp_stream_type], ccx_stream_type, elementary_PID); i += ES_info_length; } if (!newcappid && !ccx_options.ts_forced_cappid) { if (!ccx_options.ts_autoprogram) { mprint("No supported stream with caption data found, won't be able to process\n"); mprint("unless a PID is provided manually or packet inspection is enabled.\n"); } else { mprint("No supported stream with caption data found in this program.\n"); } return 0; } if (newcappid != ccx_options.ts_cappid && !ccx_options.ts_forced_cappid) { ccx_options.ts_cappid = newcappid; cap_stream_type = newcap_stream_type; mprint ("Decode captions from program %d - %s stream [0x%02x] - PID: %u\n", program_number , desc[cap_stream_type], cap_stream_type, ccx_options.ts_cappid); if (ccx_options.ts_autoprogram) // Make our program selection official { pmtpid=payload.pid; TS_program_number = program_number; } // If we have data flush it if( ctx->capbuflen > 0 ) must_flush=1; } return must_flush; }