int parse_PAT (struct lib_ccx_ctx *ctx) { int gotpes=0; int is_multiprogram=0; static int warning_program_not_found_shown=0; /* if ((forced_cappid || telext_mode==CCX_TXT_IN_USE) && cap_stream_type!=CCX_STREAM_TYPE_UNKNOWNSTREAM) // Already know what we need, skip return 0; */ if (!payload.pesstart) // Not the first entry. Ignore it, it should not be here. return 0; unsigned pointer_field = *(payload.start); unsigned char *payload_start = payload.start + pointer_field + 1; if (tspacket-payload_start+188<0) // Negative length? Seen it, but impossible return 0; unsigned payload_length = tspacket+188-payload_start; unsigned section_number = payload_start[6]; unsigned last_section_number = payload_start[7]; if (section_number > last_section_number) // Impossible: Defective PAT { dbg_print(CCX_DMT_PAT, "Skipped defective PAT packet, section_number=%u but last_section_number=%u\n", section_number, last_section_number); return gotpes; } if ( last_section_number > 0 ) { dbg_print(CCX_DMT_PAT, "Long PAT packet (%u / %u), skipping.\n", section_number, last_section_number); return gotpes; /* fatal(CCX_COMMON_EXIT_BUG_BUG, "Sorry, long PATs not yet supported!\n"); */ } if (last_pat_payload!=NULL && payload_length == last_pat_length && !memcmp (payload_start, last_pat_payload, payload_length)) { // dbg_print(CCX_DMT_PAT, "PAT hasn't changed, skipping.\n"); return 0; } if (last_pat_payload!=NULL) { mprint ("Notice: PAT changed, clearing all variables.\n"); clear_PMT_array(); if (ccx_options.teletext_mode==CCX_TXT_IN_USE) ccx_options.teletext_mode=CCX_TXT_AUTO_NOT_YET_FOUND; ccx_options.ts_cappid=0; cap_stream_type=CCX_STREAM_TYPE_UNKNOWNSTREAM; memset (ctx->PIDs_seen,0,sizeof (int) *65536); // Forget all we saw if (!tlt_config.user_page) // If the user didn't select a page... tlt_config.page=0; // ..forget whatever we detected. gotpes=1; } last_pat_payload=(unsigned char *) realloc (last_pat_payload, payload_length+8); // Extra 8 in case memcpy copies dwords, etc if (last_pat_payload==NULL) fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory to process PAT.\n"); memcpy (last_pat_payload, payload_start, payload_length); last_pat_length = payload_length; unsigned table_id = payload_start[0]; unsigned section_length = (((payload_start[1] & 0x0F) << 8) | payload_start[2]); unsigned transport_stream_id = ((payload_start[3] << 8) | payload_start[4]); unsigned version_number = (payload_start[5] & 0x3E) >> 1; // Means current OR next (so you can build a long PAT before it // actually is to be in use). unsigned current_next_indicator = payload_start[5] & 0x01; if (!current_next_indicator) // This table is not active, no need to evaluate return 0; payload_start += 8; payload_length = tspacket+188-payload_start; unsigned programm_data = section_length - 5 - 4; // prev. bytes and CRC dbg_print(CCX_DMT_PAT, "Read PAT packet (id: %u) ts-id: 0x%04x\n", table_id, transport_stream_id); dbg_print(CCX_DMT_PAT, " section length: %u number: %u last: %u\n", section_length, section_number, last_section_number); dbg_print(CCX_DMT_PAT, " version_number: %u current_next_indicator: %u\n", version_number, current_next_indicator); if ( programm_data+4 > payload_length ) { fatal(CCX_COMMON_EXIT_BUG_BUG, "Sorry, PAT too long!\n"); } unsigned ts_prog_num = 0; unsigned ts_prog_map_pid = 0; dbg_print(CCX_DMT_PAT, "\nProgram association section (PAT)\n"); ctx->freport.program_cnt=0; for( unsigned i=0; i < programm_data; i+=4) { unsigned program_number = ((payload_start[i] << 8) | payload_start[i+1]); if( !program_number ) continue; ctx->freport.program_cnt++; } is_multiprogram = (ctx->freport.program_cnt>1); for( unsigned i=0; i < programm_data; i+=4) { unsigned program_number = ((payload_start[i] << 8) | payload_start[i+1]); unsigned prog_map_pid = ((payload_start[i+2] << 8) | payload_start[i+3]) & 0x1FFF; dbg_print(CCX_DMT_PAT, " Program number: %u -> PMTPID: %u\n", program_number, prog_map_pid); if( !program_number ) continue; if (!is_multiprogram || (ccx_options.ts_forced_program_selected && program_number == ccx_options.ts_forced_program)) { // If there's just one program we select it unless the user selected // something else anyway. ts_prog_num = program_number; ts_prog_map_pid = prog_map_pid; } // Having an array for PMTs comes from telxcc. int found=0,j; for (j=0;j<pmt_array_length; j++) { if (pmt_array[j].program_number == program_number) { found=1; break; } } if (!found && pmt_array_length < TS_PMT_MAP_SIZE) { pmt_array[pmt_array_length].program_number=program_number; pmt_array[pmt_array_length].PMT_PID=prog_map_pid; pmt_array_length++; } } // for if (is_multiprogram && !ts_prog_num) { // We can only work with "simple" ts files if (ccx_options.ts_forced_program_selected && !warning_program_not_found_shown) { mprint ("\rThe program you selected (%u) wasn't found in the first Program Association Table in the stream.\n",ccx_options.ts_forced_program); mprint ("I will continue reading the stream in case the program appears later.\n\n"); warning_program_not_found_shown=1; } mprint ("\nThis TS file has more than one program. These are the program numbers found: \n"); for( unsigned j=0; j < programm_data; j+=4) { unsigned pn = ((payload_start[j] << 8) | payload_start[j+1]); if (pn) mprint ("%u\n",pn); activity_program_number (pn); } if (!ccx_options.ts_forced_program_selected) { if (!ccx_options.ts_autoprogram) fatal(CCX_COMMON_EXIT_BUG_BUG, "Run ccextractor again with --program-number specifying which program\nto process."); else mprint ("\nThe first program with a suitable CC stream will be selected.\n"); } } // If we found a new PAT reset all TS stream variables if( ts_prog_num != TS_program_number ) { TS_program_number = ts_prog_num; pmtpid = ts_prog_map_pid; if (!ccx_options.ts_forced_cappid) ccx_options.ts_cappid = 0; // Reset caption stream pid // If we have data flush it if( ctx->capbuflen > 0 ) gotpes = 1; } return gotpes; }
// Read ts packets until a complete video PES element can be returned. // The data is read into capbuf and the function returns the number of // bytes read. long ts_readstream(void) { static int prev_ccounter = 0; static int prev_packet = 0; int gotpes = 0; long pespcount=0; // count packets in PES with captions long pcount=0; // count all packets until PES is complete int saw_pesstart = 0; capbuflen = 0; do { pcount++; if( !prev_packet ) { // Exit the loop at EOF if ( !ts_readpacket() ) break; } else prev_packet = 0; // Skip damaged packets, they could do more harm than good if (payload.transport_error) { dbg_print(DMT_VERBOSE, "Packet (pid %u) skipped - transport error.\n", payload.pid); continue; } // Skip packets with no payload. This also fixes the problems // with the continuity counter not being incremented in empty // packets. if ( !payload.length ) { dbg_print(DMT_VERBOSE, "Packet (pid %u) skipped - no payload.\n", payload.pid); continue; } if (cappid == 0) { if (!payload.pesstart) // Not the first entry. Ignore it, it should not be here. continue; } // Check for PAT if( payload.pid == 0 && telext_mode!=TXT_IN_USE) // If teletext is in use, then we don't need to process PAT { if (!payload.pesstart) // Not the first entry. Ignore it, it should not be here. continue; unsigned pointer_field = *(payload.start); unsigned char *payload_start = payload.start + pointer_field + 1; unsigned payload_length = tspacket+188-payload_start; unsigned table_id = payload_start[0]; unsigned section_length = (((payload_start[1] & 0x0F) << 8) | payload_start[2]); unsigned transport_stream_id = ((payload_start[3] << 8) | payload_start[4]); unsigned version_number = (payload_start[5] & 0x3E) >> 1; unsigned current_next_indicator = payload_start[5] & 0x01; unsigned section_number = payload_start[6]; unsigned last_section_number = payload_start[7]; if ( last_section_number > 0 ) { fatal(EXIT_BUG_BUG, "Sorry, long PATs not yet supported!\n"); } if (!current_next_indicator) // This table is not active, no need to evaluate continue; payload_start += 8; payload_length = tspacket+188-payload_start; unsigned programm_data = section_length - 5 - 4; // prev. bytes and CRC dbg_print(DMT_PARSE, "Read PAT packet (id: %u) ts-id: 0x%04x\n", table_id, transport_stream_id); dbg_print(DMT_PARSE, " section length: %u number: %u last: %u\n", section_length, section_number, last_section_number); dbg_print(DMT_PARSE, " version_number: %u current_next_indicator: %u\n", version_number, current_next_indicator); if ( programm_data+4 > payload_length ) { fatal(EXIT_BUG_BUG, "Sorry, PAT too long!\n"); } unsigned ts_prog_num = 0; unsigned ts_prog_map_pid = 0; dbg_print(DMT_VERBOSE, "\nProgram association section (PAT)\n"); for( unsigned i=0; i < programm_data; i+=4) { unsigned program_number = ((payload_start[i] << 8) | payload_start[i+1]); unsigned prog_map_pid = ((payload_start[i+2] << 8) | payload_start[i+3]) & 0x1FFF; dbg_print(DMT_VERBOSE, " Program number: %u -> PMTPID: %u\n", program_number, prog_map_pid); if( program_number != 0 ) { if ( ts_prog_num && ts_prog_num!=program_number && !ts_forced_program_selected) { // We can only work with "simple" ts files mprint ("\nThis TS file has more than one program. These are the program numbers found: \n"); for( unsigned j=0; j < programm_data; j+=4) { unsigned pn = ((payload_start[j] << 8) | payload_start[j+1]); if (pn) mprint ("%u\n",pn); activity_program_number (pn); } fatal(EXIT_BUG_BUG, "Run ccextractor again with --program-number specifying which program to process."); } else { if (!ts_forced_program_selected || program_number == ts_forced_program) { // Otherwise ignore ts_prog_num = program_number; ts_prog_map_pid = prog_map_pid; } } } } // If we found a new PAT reset all TS stream variables if( ts_prog_num != TS_program_number ) { TS_program_number = ts_prog_num; pmtpid = ts_prog_map_pid; cappid = 0; // Reset caption stream pid // If we have data flush it if( capbuflen > 0 ) { gotpes = 1; break; } } continue; } // PID != 0 but no PMT defined yet, ignore the rest of the current // package and continue searching. if ( !pmtpid && telext_mode!=TXT_IN_USE) { dbg_print(DMT_PARSE, "Packet (pid %u) skipped - no PMT pid identified yet.\n", payload.pid); continue; } // Check for PMT (ISO13818-1 / table 2-28) if( payload.pid == pmtpid && telext_mode!=TXT_IN_USE) { if (!payload.pesstart) // Not the first entry. Ignore it, it should not be here. continue; unsigned pointer_field = *(payload.start); unsigned char *payload_start = payload.start + pointer_field + 1; unsigned payload_length = tspacket+188-payload_start; unsigned table_id = payload_start[0]; unsigned section_length = (((payload_start[1] & 0x0F) << 8) | payload_start[2]); unsigned program_number = ((payload_start[3] << 8) | payload_start[4]); if (program_number != TS_program_number) { // Only use PMTs with matching program number dbg_print(DMT_PARSE,"Reject this PMT packet (pid: %u) program number: %u\n", pmtpid, program_number); continue; } unsigned version_number = (payload_start[5] & 0x3E) >> 1; unsigned current_next_indicator = payload_start[5] & 0x01; if (!current_next_indicator) // This table is not active, no need to evaluate continue; unsigned section_number = payload_start[6]; unsigned last_section_number = payload_start[7]; if ( last_section_number > 0 ) { mprint("Long PMTs are not supported - reject!\n"); continue; } unsigned PCR_PID = (((payload_start[8] & 0x1F) << 8) | payload_start[9]); unsigned pi_length = (((payload_start[10] & 0x0F) << 8) | payload_start[11]); if( 12 + pi_length > payload_length ) { // If we would support long PMTs, this would be wrong. mprint("program_info_length cannot be longer than the payload_length - reject!\n"); continue; } payload_start += 12 + pi_length; payload_length = tspacket+188-payload_start; unsigned stream_data = section_length - 9 - pi_length - 4; // prev. bytes and CRC dbg_print(DMT_PARSE, "Read PMT packet (id: %u) program number: %u\n", table_id, program_number); dbg_print(DMT_PARSE, " section length: %u number: %u last: %u\n", section_length, section_number, last_section_number); dbg_print(DMT_PARSE, " version_number: %u current_next_indicator: %u\n", version_number, current_next_indicator); dbg_print(DMT_PARSE, " PCR_PID: %u data length: %u payload_length: %u\n", PCR_PID, stream_data, payload_length); if ( stream_data+4 > payload_length ) { fatal(EXIT_BUG_BUG, "Sorry, PMT to long!\n"); } unsigned newcappid = 0; unsigned newcap_stream_type = 0; dbg_print(DMT_VERBOSE, "\nProgram map section (PMT)\n"); for( unsigned i=0; i < stream_data; i+=5) { unsigned stream_type = payload_start[i]; unsigned elementary_PID = (((payload_start[i+1] & 0x1F) << 8) | payload_start[i+2]); unsigned ES_info_length = (((payload_start[i+3] & 0x0F) << 8) | payload_start[i+4]); if (telext_mode==TXT_AUTO_NOT_YET_FOUND && stream_type == PRIVATE_MPEG2) // MPEG-2 Packetized Elementary Stream packets containing private data { // descriptor_tag: 0x45 = VBI_data_descriptor, 0x46 = VBI_teletext_descriptor, 0x56 = teletext_descriptor unsigned descriptor_tag = payload_start[i + 5]; if ((descriptor_tag == 0x45) || (descriptor_tag == 0x46) || (descriptor_tag == 0x56)) { telxcc_init(); cappid = newcappid = elementary_PID; cap_stream_type = newcap_stream_type = stream_type; telext_mode =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); } } // For the print command below unsigned tmp_stream_type = stream_type; switch (stream_type) { case VIDEO_MPEG2: case VIDEO_H264: // If telext has been detected/selected it has priority over video for subtitles if (telext_mode != TXT_IN_USE) { newcappid = elementary_PID; newcap_stream_type = stream_type; } break; case PRIVATE_MPEG2: case VIDEO_MPEG1: case AUDIO_MPEG1: case AUDIO_MPEG2: case AUDIO_AAC: case VIDEO_MPEG4: case AUDIO_AC3: case AUDIO_DTS: case AUDIO_HDMV_DTS: break; default: tmp_stream_type = UNKNOWNSTREAM; break; } dbg_print(DMT_VERBOSE, " %s stream [0x%02x] - PID: %u\n", desc[tmp_stream_type], stream_type, elementary_PID); i += ES_info_length; } if (!newcappid) { mprint("No supported stream with caption data found - reject!\n"); continue; } if (newcappid != cappid) { cappid = newcappid; cap_stream_type = newcap_stream_type; mprint ("Decode captions from %s stream [0x%02x] - PID: %u\n", desc[cap_stream_type], cap_stream_type, cappid); // If we have data flush it if( capbuflen > 0 ) { gotpes = 1; break; } } continue; } if (PIDs_seen[payload.pid] == 0) { mprint ("\nNew PID found: %u\n", payload.pid); PIDs_seen[payload.pid] = 1; } if (payload.pid==1003 && !hauppauge_warning_shown && !hauppauge_mode) { // TODO: Change this very weak test for something more decent such as size. mprint ("\n\nNote: This TS could be a recording from a Hauppage card. If no captions are detected, try --hauppauge\n\n"); hauppauge_warning_shown=1; } // No caption stream PID defined yet, continue searching. if ( !cappid ) { dbg_print(DMT_PARSE, "Packet (pid %u) skipped - no stream with captions identified yet.\n", payload.pid); continue; } if (hauppauge_mode && payload.pid==HAUPPAGE_CCPID) { // Haup packets processed separately, because we can't mix payloads. So they go in their own buffer // copy payload to capbuf int haup_newcapbuflen = haup_capbuflen + payload.length; if ( haup_newcapbuflen > haup_capbufsize) { haup_capbuf = (unsigned char*)realloc(haup_capbuf, haup_newcapbuflen); if (!haup_capbuf) fatal(EXIT_NOT_ENOUGH_MEMORY, "Out of memory"); haup_capbufsize = haup_newcapbuflen; } memcpy(haup_capbuf+haup_capbuflen, payload.start, payload.length); haup_capbuflen = haup_newcapbuflen; } // Check for PID with captions. Note that in Hauppauge mode we also process the video stream because // we need the timing from its PES header, which isn't included in Hauppauge's packets // CFS: Warning. Most likely mixing payloads. if( payload.pid == cappid) { // Now we got a payload // Video PES start if (payload.pesstart) { // Pretend the previous was smaller prev_ccounter=payload.counter-1; saw_pesstart = 1; } // Discard packets when no pesstart was found. if ( !saw_pesstart ) { dbg_print(DMT_PARSE, "Packet (pid %u) skipped - Did not see pesstart.\n", payload.pid); continue; } // If the buffer is empty we just started this function if (payload.pesstart && capbuflen > 0) { dbg_print(DMT_PARSE, "\nPES finished (%ld bytes/%ld PES packets/%ld total packets)\n", capbuflen, pespcount, pcount); // Keep the data in capbuf to be worked on prev_packet = 1; gotpes = 1; break; } if ( (prev_ccounter==15 ? 0 : prev_ccounter+1) != payload.counter ) { mprint("TS continuity counter not incremented prev/curr %u/%u\n", prev_ccounter, payload.counter); } prev_ccounter = payload.counter; pespcount++; // copy payload to capbuf int newcapbuflen = capbuflen + payload.length; if ( newcapbuflen > capbufsize) { capbuf = (unsigned char*)realloc(capbuf, newcapbuflen); if (!capbuf) fatal(EXIT_NOT_ENOUGH_MEMORY, "Out of memory"); capbufsize = newcapbuflen; } memcpy(capbuf+capbuflen, payload.start, payload.length); capbuflen = newcapbuflen; } //else // if(debug_verbose) // printf("Packet (pid %u) skipped - unused.\n", // payload.pid); // Nothing suitable found, start over } while( !gotpes ); // gotpes==1 never arrives here because of the breaks return capbuflen; }