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; }
int main(int argc, char *argv[]) { char *c; struct encoder_ctx enc_ctx[2]; struct cc_subtitle dec_sub; #ifdef ENABLE_FFMPEG void *ffmpeg_ctx = NULL; #endif struct lib_ccx_ctx *ctx; struct lib_cc_decode *dec_ctx = NULL; init_options (&ccx_options); parse_configuration(&ccx_options); parse_parameters (&ccx_options, argc, argv); // Initialize libraries ctx = init_libraries(&ccx_options); dec_ctx = ctx->dec_ctx; // Prepare write structures init_write(&ctx->wbout1,ccx_options.wbout1.filename); init_write(&ctx->wbout2,ccx_options.wbout2.filename); int show_myth_banner = 0; memset (&cea708services[0],0,CCX_DECODERS_708_MAX_SERVICES*sizeof (int)); // Cannot (yet) be moved because it's needed in parse_parameters. memset (&dec_sub, 0,sizeof(dec_sub)); if (ctx->num_input_files==0 && ccx_options.input_source==CCX_DS_FILE) { usage (); fatal (EXIT_NO_INPUT_FILES, "(This help screen was shown because there were no input files)\n"); } if (ctx->num_input_files>1 && ccx_options.live_stream) { fatal(EXIT_TOO_MANY_INPUT_FILES, "Live stream mode accepts only one input file.\n"); } if (ctx->num_input_files && ccx_options.input_source==CCX_DS_NETWORK) { fatal(EXIT_TOO_MANY_INPUT_FILES, "UDP mode is not compatible with input files.\n"); } if (ccx_options.input_source==CCX_DS_NETWORK || ccx_options.input_source==CCX_DS_TCP) { ccx_options.buffer_input=1; // Mandatory, because each datagram must be read complete. } if (ctx->num_input_files && ccx_options.input_source==CCX_DS_TCP) { fatal(EXIT_TOO_MANY_INPUT_FILES, "TCP mode is not compatible with input files.\n"); } if (ctx->num_input_files > 0) { ctx->wbout1.multiple_files = 1; ctx->wbout1.first_input_file = ctx->inputfile[0]; ctx->wbout2.multiple_files = 1; ctx->wbout2.first_input_file = ctx->inputfile[0]; } // teletext page number out of range if ((tlt_config.page != 0) && ((tlt_config.page < 100) || (tlt_config.page > 899))) { fatal (EXIT_NOT_CLASSIFIED, "Teletext page number could not be lower than 100 or higher than 899\n"); } if (ccx_options.output_filename!=NULL) { // Use the given output file name for the field specified by // the -1, -2 switch. If -12 is used, the filename is used for // field 1. if (ccx_options.extract==2) ctx->wbout2.filename=ccx_options.output_filename; else ctx->wbout1.filename=ccx_options.output_filename; } switch (ccx_options.write_format) { case CCX_OF_RAW: ctx->extension = ".raw"; break; case CCX_OF_SRT: ctx->extension = ".srt"; break; case CCX_OF_SAMI: ctx->extension = ".smi"; break; case CCX_OF_SMPTETT: ctx->extension = ".ttml"; break; case CCX_OF_TRANSCRIPT: ctx->extension = ".txt"; break; case CCX_OF_RCWT: ctx->extension = ".bin"; break; case CCX_OF_SPUPNG: ctx->extension = ".xml"; break; case CCX_OF_NULL: ctx->extension = ""; break; case CCX_OF_DVDRAW: ctx->extension = ".dvdraw"; break; default: fatal (CCX_COMMON_EXIT_BUG_BUG, "write_format doesn't have any legal value, this is a bug.\n"); } params_dump(ctx); // default teletext page if (tlt_config.page > 0) { // dec to BCD, magazine pages numbers are in BCD (ETSI 300 706) tlt_config.page = ((tlt_config.page / 100) << 8) | (((tlt_config.page / 10) % 10) << 4) | (tlt_config.page % 10); } if (ctx->auto_stream==CCX_SM_MCPOODLESRAW && ccx_options.write_format==CCX_OF_RAW) { fatal (EXIT_INCOMPATIBLE_PARAMETERS, "-in=raw can only be used if the output is a subtitle file.\n"); } if (ctx->auto_stream==CCX_SM_RCWT && ccx_options.write_format==CCX_OF_RCWT && ccx_options.output_filename==NULL) { fatal (EXIT_INCOMPATIBLE_PARAMETERS, "CCExtractor's binary format can only be used simultaneously for input and\noutput if the output file name is specified given with -o.\n"); } subline = (unsigned char *) malloc (SUBLINESIZE); switch (ccx_options.input_source) { case CCX_DS_FILE: ctx->basefilename = (char *) malloc (strlen (ctx->inputfile[0])+1); break; case CCX_DS_STDIN: ctx->basefilename = (char *) malloc (strlen (ctx->basefilename_for_stdin)+1); break; case CCX_DS_NETWORK: case CCX_DS_TCP: ctx->basefilename = (char *) malloc (strlen (ctx->basefilename_for_network)+1); break; } if (ctx->basefilename == NULL) fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory\n"); switch (ccx_options.input_source) { case CCX_DS_FILE: strcpy (ctx->basefilename, ctx->inputfile[0]); break; case CCX_DS_STDIN: strcpy (ctx->basefilename, ctx->basefilename_for_stdin); break; case CCX_DS_NETWORK: case CCX_DS_TCP: strcpy (ctx->basefilename, ctx->basefilename_for_network); break; } for (c=ctx->basefilename+strlen (ctx->basefilename)-1; ctx->basefilename && *c!='.'; c--) {;} // Get last . if (*c=='.') *c=0; if (ctx->wbout1.filename==NULL) { ctx->wbout1.filename = (char *) malloc (strlen (ctx->basefilename)+3+strlen (ctx->extension)); ctx->wbout1.filename[0]=0; } if (ctx->wbout2.filename==NULL) { ctx->wbout2.filename = (char *) malloc (strlen (ctx->basefilename)+3+strlen (ctx->extension)); ctx->wbout2.filename[0]=0; } if (ctx->buffer == NULL || ctx->pesheaderbuf==NULL || ctx->wbout1.filename == NULL || ctx->wbout2.filename == NULL || subline==NULL || init_file_buffer() ) { fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory\n"); } if (ccx_options.send_to_srv) { connect_to_srv(ccx_options.srv_addr, ccx_options.srv_port, ccx_options.tcp_desc); } if (ccx_options.write_format!=CCX_OF_NULL) { /* # DVD format uses one raw file for both fields, while Broadcast requires 2 */ if (ccx_options.write_format==CCX_OF_DVDRAW) { if (ctx->wbout1.filename[0]==0) { strcpy (ctx->wbout1.filename,ctx->basefilename); strcat (ctx->wbout1.filename,".raw"); } if (ctx->cc_to_stdout) { ctx->wbout1.fh=STDOUT_FILENO; mprint ("Sending captions to stdout.\n"); } else { mprint ("Creating %s\n", ctx->wbout1.filename); ctx->wbout1.fh=open (ctx->wbout1.filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IREAD | S_IWRITE); if (ctx->wbout1.fh==-1) { fatal(CCX_COMMON_EXIT_FILE_CREATION_FAILED, "Failed\n"); } } } else { if (ctx->cc_to_stdout && ccx_options.extract==12) fatal (EXIT_INCOMPATIBLE_PARAMETERS, "You can't extract both fields to stdout at the same time in broadcast mode."); if (ccx_options.write_format == CCX_OF_SPUPNG && ctx->cc_to_stdout) fatal (EXIT_INCOMPATIBLE_PARAMETERS, "You cannot use -out=spupng with -stdout."); if (ccx_options.extract!=2) { if (ctx->cc_to_stdout) { ctx->wbout1.fh=STDOUT_FILENO; mprint ("Sending captions to stdout.\n"); } else if (!ccx_options.send_to_srv) { if (ctx->wbout1.filename[0]==0) { strcpy (ctx->wbout1.filename,ctx->basefilename); if (ccx_options.extract==12) // _1 only added if there's two files strcat (ctx->wbout1.filename,"_1"); strcat (ctx->wbout1.filename,(const char *) ctx->extension); } mprint ("Creating %s\n", ctx->wbout1.filename); ctx->wbout1.fh=open (ctx->wbout1.filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IREAD | S_IWRITE); if (ctx->wbout1.fh==-1) { fatal(CCX_COMMON_EXIT_FILE_CREATION_FAILED, "Failed (errno=%d)\n", errno); } } switch (ccx_options.write_format) { case CCX_OF_RAW: writeraw(BROADCAST_HEADER, sizeof(BROADCAST_HEADER), &ctx->wbout1); break; case CCX_OF_DVDRAW: break; case CCX_OF_RCWT: if (init_encoder(enc_ctx, &ctx->wbout1)) fatal(EXIT_NOT_ENOUGH_MEMORY, "Not enough memory\n"); set_encoder_subs_delay(enc_ctx, ctx->subs_delay); set_encoder_last_displayed_subs_ms(enc_ctx, ctx->last_displayed_subs_ms); set_encoder_startcredits_displayed(enc_ctx, ctx->startcredits_displayed); break; default: if (!ccx_options.no_bom){ if (ccx_options.encoding == CCX_ENC_UTF_8){ // Write BOM writeraw(UTF8_BOM, sizeof(UTF8_BOM), &ctx->wbout1); } if (ccx_options.encoding == CCX_ENC_UNICODE){ // Write BOM writeraw(LITTLE_ENDIAN_BOM, sizeof(LITTLE_ENDIAN_BOM), &ctx->wbout1); } } if (init_encoder(enc_ctx, &ctx->wbout1)){ fatal(EXIT_NOT_ENOUGH_MEMORY, "Not enough memory\n"); } set_encoder_subs_delay(enc_ctx, ctx->subs_delay); set_encoder_last_displayed_subs_ms(enc_ctx, ctx->last_displayed_subs_ms); set_encoder_startcredits_displayed(enc_ctx, ctx->startcredits_displayed); } } if (ccx_options.extract == 12 && ccx_options.write_format != CCX_OF_RAW) mprint (" and \n"); if (ccx_options.extract!=1) { if (ctx->cc_to_stdout) { ctx->wbout1.fh=STDOUT_FILENO; mprint ("Sending captions to stdout.\n"); } else if(ccx_options.write_format == CCX_OF_RAW && ccx_options.extract == 12) { memcpy(&ctx->wbout2, &ctx->wbout1,sizeof(ctx->wbout1)); } else if (!ccx_options.send_to_srv) { if (ctx->wbout2.filename[0]==0) { strcpy (ctx->wbout2.filename,ctx->basefilename); if (ccx_options.extract==12) // _ only added if there's two files strcat (ctx->wbout2.filename,"_2"); strcat (ctx->wbout2.filename,(const char *) ctx->extension); } mprint ("Creating %s\n", ctx->wbout2.filename); ctx->wbout2.fh=open (ctx->wbout2.filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IREAD | S_IWRITE); if (ctx->wbout2.fh==-1) { fatal(CCX_COMMON_EXIT_FILE_CREATION_FAILED, "Failed\n"); } if(ccx_options.write_format == CCX_OF_RAW) writeraw (BROADCAST_HEADER,sizeof (BROADCAST_HEADER),&ctx->wbout2); } switch (ccx_options.write_format) { case CCX_OF_RAW: case CCX_OF_DVDRAW: break; case CCX_OF_RCWT: if( init_encoder(enc_ctx+1,&ctx->wbout2) ) fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory\n"); set_encoder_subs_delay(enc_ctx+1, ctx->subs_delay); set_encoder_last_displayed_subs_ms(enc_ctx+1, ctx->last_displayed_subs_ms); set_encoder_startcredits_displayed(enc_ctx+1, ctx->startcredits_displayed); break; default: if (!ccx_options.no_bom){ if (ccx_options.encoding == CCX_ENC_UTF_8){ // Write BOM writeraw(UTF8_BOM, sizeof(UTF8_BOM), &ctx->wbout2); } if (ccx_options.encoding == CCX_ENC_UNICODE){ // Write BOM writeraw(LITTLE_ENDIAN_BOM, sizeof(LITTLE_ENDIAN_BOM), &ctx->wbout2); } } if (init_encoder(enc_ctx + 1, &ctx->wbout2)){ fatal(EXIT_NOT_ENOUGH_MEMORY, "Not enough memory\n"); } set_encoder_subs_delay(enc_ctx+1, ctx->subs_delay); set_encoder_last_displayed_subs_ms(enc_ctx+1, ctx->last_displayed_subs_ms); set_encoder_startcredits_displayed(enc_ctx+1, ctx->startcredits_displayed); } } } } if (ccx_options.transcript_settings.xds) { if (ccx_options.write_format != CCX_OF_TRANSCRIPT) { ccx_options.transcript_settings.xds = 0; mprint ("Warning: -xds ignored, XDS can only be exported to transcripts at this time.\n"); } } if (ccx_options.teletext_mode == CCX_TXT_IN_USE) // Here, it would mean it was forced by user telxcc_init(ctx); ctx->fh_out_elementarystream = NULL; if (ccx_options.out_elementarystream_filename!=NULL) { if ((ctx->fh_out_elementarystream = fopen (ccx_options.out_elementarystream_filename,"wb"))==NULL) { fatal(CCX_COMMON_EXIT_FILE_CREATION_FAILED, "Unable to open clean file: %s\n", ccx_options.out_elementarystream_filename); } } // Initialize HDTV caption buffer init_hdcc(); if (ccx_options.line_terminator_lf) encoded_crlf_length = encode_line(encoded_crlf, (unsigned char *) "\n"); else encoded_crlf_length = encode_line(encoded_crlf, (unsigned char *) "\r\n"); encoded_br_length = encode_line(encoded_br, (unsigned char *) "<br>"); time_t start, final; time(&start); dec_ctx->processed_enough=0; if (ccx_options.binary_concat) { ctx->total_inputsize=gettotalfilessize(ctx); if (ctx->total_inputsize==-1) fatal (EXIT_UNABLE_TO_DETERMINE_FILE_SIZE, "Failed to determine total file size.\n"); } #ifndef _WIN32 signal_ctx = ctx; m_signal(SIGINT, sigint_handler); #endif while (switch_to_next_file(ctx, 0) && !dec_ctx->processed_enough) { prepare_for_new_file(ctx); #ifdef ENABLE_FFMPEG close_input_file(ctx); ffmpeg_ctx = init_ffmpeg(ctx->inputfile[0]); if(ffmpeg_ctx) { do { int ret = 0; unsigned char *bptr = ctx->buffer; int len = ff_get_ccframe(ffmpeg_ctx, bptr, 1024); int cc_count = 0; if(len == AVERROR(EAGAIN)) { continue; } else if(len == AVERROR_EOF) break; else if(len == 0) continue; else if(len < 0 ) { mprint("Error extracting Frame\n"); break; } else cc_count = len/3; ret = process_cc_data(dec_ctx, bptr, cc_count, &dec_sub); if(ret >= 0 && dec_sub.got_output) { encode_sub(enc_ctx, &dec_sub); dec_sub.got_output = 0; } }while(1); continue; } else { mprint ("\rFailed to initialized ffmpeg falling back to legacy\n"); } #endif if (ctx->auto_stream == CCX_SM_AUTODETECT) { detect_stream_type(ctx); switch (ctx->stream_mode) { case CCX_SM_ELEMENTARY_OR_NOT_FOUND: mprint ("\rFile seems to be an elementary stream, enabling ES mode\n"); break; case CCX_SM_TRANSPORT: mprint ("\rFile seems to be a transport stream, enabling TS mode\n"); break; case CCX_SM_PROGRAM: mprint ("\rFile seems to be a program stream, enabling PS mode\n"); break; case CCX_SM_ASF: mprint ("\rFile seems to be an ASF, enabling DVR-MS mode\n"); break; case CCX_SM_WTV: mprint ("\rFile seems to be a WTV, enabling WTV mode\n"); break; case CCX_SM_MCPOODLESRAW: mprint ("\rFile seems to be McPoodle raw data\n"); break; case CCX_SM_RCWT: mprint ("\rFile seems to be a raw caption with time data\n"); break; case CCX_SM_MP4: mprint ("\rFile seems to be a MP4\n"); break; #ifdef WTV_DEBUG case CCX_SM_HEX_DUMP: mprint ("\rFile seems to be an hexadecimal dump\n"); break; #endif case CCX_SM_MYTH: case CCX_SM_AUTODETECT: fatal(CCX_COMMON_EXIT_BUG_BUG, "Cannot be reached!"); break; } } else { ctx->stream_mode=ctx->auto_stream; } /* ----------------------------------------------------------------- MAIN LOOP ----------------------------------------------------------------- */ // The myth loop autodetect will only be used with ES or PS streams switch (ccx_options.auto_myth) { case 0: // Use whatever stream mode says break; case 1: // Force stream mode to myth ctx->stream_mode=CCX_SM_MYTH; break; case 2: // autodetect myth files, but only if it does not conflict with // the current stream mode switch (ctx->stream_mode) { case CCX_SM_ELEMENTARY_OR_NOT_FOUND: case CCX_SM_PROGRAM: if ( detect_myth(ctx) ) { ctx->stream_mode=CCX_SM_MYTH; } break; default: // Keep stream_mode break; } break; } // Disable sync check for raw formats - they have the right timeline. // Also true for bin formats, but -nosync might have created a // broken timeline for debug purposes. // Disable too in MP4, specs doesn't say that there can't be a jump switch (ctx->stream_mode) { case CCX_SM_MCPOODLESRAW: case CCX_SM_RCWT: case CCX_SM_MP4: #ifdef WTV_DEBUG case CCX_SM_HEX_DUMP: #endif ccx_common_timing_settings.disable_sync_check = 1; break; default: break; } switch (ctx->stream_mode) { case CCX_SM_ELEMENTARY_OR_NOT_FOUND: if (!ccx_options.use_gop_as_pts) // If !0 then the user selected something ccx_options.use_gop_as_pts = 1; // Force GOP timing for ES ccx_common_timing_settings.is_elementary_stream = 1; case CCX_SM_TRANSPORT: case CCX_SM_PROGRAM: case CCX_SM_ASF: case CCX_SM_WTV: if (!ccx_options.use_gop_as_pts) // If !0 then the user selected something ccx_options.use_gop_as_pts = 0; mprint ("\rAnalyzing data in general mode\n"); general_loop(ctx, &enc_ctx); break; case CCX_SM_MCPOODLESRAW: mprint ("\rAnalyzing data in McPoodle raw mode\n"); raw_loop(ctx, &enc_ctx); break; case CCX_SM_RCWT: mprint ("\rAnalyzing data in CCExtractor's binary format\n"); rcwt_loop(ctx, &enc_ctx); break; case CCX_SM_MYTH: mprint ("\rAnalyzing data in MythTV mode\n"); show_myth_banner = 1; myth_loop(ctx, &enc_ctx); break; case CCX_SM_MP4: mprint ("\rAnalyzing data with GPAC (MP4 library)\n"); close_input_file(ctx); // No need to have it open. GPAC will do it for us processmp4 (ctx, ctx->inputfile[0],&enc_ctx); break; #ifdef WTV_DEBUG case CCX_SM_HEX_DUMP: close_input_file(ctx); // processhex will open it in text mode processhex (ctx, ctx->inputfile[0]); break; #endif case CCX_SM_AUTODETECT: fatal(CCX_COMMON_EXIT_BUG_BUG, "Cannot be reached!"); break; } mprint("\n"); dbg_print(CCX_DMT_DECODER_608, "\nTime stamps after last caption block was written:\n"); dbg_print(CCX_DMT_DECODER_608, "Last time stamps: PTS: %s (%+2dF) ", print_mstime( (LLONG) (sync_pts/(MPEG_CLOCK_FREQ/1000) +frames_since_ref_time*1000.0/current_fps) ), frames_since_ref_time); dbg_print(CCX_DMT_DECODER_608, "GOP: %s \n", print_mstime(gop_time.ms) ); // Blocks since last PTS/GOP time stamp. dbg_print(CCX_DMT_DECODER_608, "Calc. difference: PTS: %s (%+3lldms incl.) ", print_mstime( (LLONG) ((sync_pts-min_pts)/(MPEG_CLOCK_FREQ/1000) + fts_offset + frames_since_ref_time*1000.0/current_fps)), fts_offset + (LLONG) (frames_since_ref_time*1000.0/current_fps) ); dbg_print(CCX_DMT_DECODER_608, "GOP: %s (%+3dms incl.)\n", print_mstime((LLONG)(gop_time.ms -first_gop_time.ms +get_fts_max()-fts_at_gop_start)), (int)(get_fts_max()-fts_at_gop_start)); // When padding is active the CC block time should be within // 1000/29.97 us of the differences. dbg_print(CCX_DMT_DECODER_608, "Max. FTS: %s (without caption blocks since then)\n", print_mstime(get_fts_max())); if (ctx->stat_hdtv) { mprint ("\rCC type 0: %d (%s)\n", dec_ctx->cc_stats[0], cc_types[0]); mprint ("CC type 1: %d (%s)\n", dec_ctx->cc_stats[1], cc_types[1]); mprint ("CC type 2: %d (%s)\n", dec_ctx->cc_stats[2], cc_types[2]); mprint ("CC type 3: %d (%s)\n", dec_ctx->cc_stats[3], cc_types[3]); } mprint ("\nTotal frames time: %s (%u frames at %.2ffps)\n", print_mstime( (LLONG)(total_frames_count*1000/current_fps) ), total_frames_count, current_fps); if (ctx->total_pulldownframes) mprint ("incl. pulldown frames: %s (%u frames at %.2ffps)\n", print_mstime( (LLONG)(ctx->total_pulldownframes*1000/current_fps) ), ctx->total_pulldownframes, current_fps); if (pts_set >= 1 && min_pts != 0x01FFFFFFFFLL) { LLONG postsyncms = (LLONG) (ctx->frames_since_last_gop*1000/current_fps); mprint ("\nMin PTS: %s\n", print_mstime( min_pts/(MPEG_CLOCK_FREQ/1000) - fts_offset)); if (pts_big_change) mprint ("(Reference clock was reset at some point, Min PTS is approximated)\n"); mprint ("Max PTS: %s\n", print_mstime( sync_pts/(MPEG_CLOCK_FREQ/1000) + postsyncms)); mprint ("Length: %s\n", print_mstime( sync_pts/(MPEG_CLOCK_FREQ/1000) + postsyncms - min_pts/(MPEG_CLOCK_FREQ/1000) + fts_offset )); } // dvr-ms files have invalid GOPs if (gop_time.inited && first_gop_time.inited && ctx->stream_mode != CCX_SM_ASF) { mprint ("\nInitial GOP time: %s\n", print_mstime(first_gop_time.ms)); mprint ("Final GOP time: %s%+3dF\n", print_mstime(gop_time.ms), ctx->frames_since_last_gop); mprint ("Diff. GOP length: %s%+3dF", print_mstime(gop_time.ms - first_gop_time.ms), ctx->frames_since_last_gop); mprint (" (%s)\n", print_mstime(gop_time.ms - first_gop_time.ms +(LLONG) ((ctx->frames_since_last_gop)*1000/29.97)) ); } if (ctx->false_pict_header) mprint ("\nNumber of likely false picture headers (discarded): %d\n",ctx->false_pict_header); if (ctx->stat_numuserheaders) mprint("\nTotal user data fields: %d\n", ctx->stat_numuserheaders); if (ctx->stat_dvdccheaders) mprint("DVD-type user data fields: %d\n", ctx->stat_dvdccheaders); if (ctx->stat_scte20ccheaders) mprint("SCTE-20 type user data fields: %d\n", ctx->stat_scte20ccheaders); if (ctx->stat_replay4000headers) mprint("ReplayTV 4000 user data fields: %d\n", ctx->stat_replay4000headers); if (ctx->stat_replay5000headers) mprint("ReplayTV 5000 user data fields: %d\n", ctx->stat_replay5000headers); if (ctx->stat_hdtv) mprint("HDTV type user data fields: %d\n", ctx->stat_hdtv); if (ctx->stat_dishheaders) mprint("Dish Network user data fields: %d\n", ctx->stat_dishheaders); if (ctx->stat_divicom) { mprint("CEA608/Divicom user data fields: %d\n", ctx->stat_divicom); mprint("\n\nNOTE! The CEA 608 / Divicom standard encoding for closed\n"); mprint("caption is not well understood!\n\n"); mprint("Please submit samples to the developers.\n\n\n"); } // Add one frame as fts_max marks the beginning of the last frame, // but we need the end. fts_global += fts_max + (LLONG) (1000.0/current_fps); // CFS: At least in Hauppage mode, cb_field can be responsible for ALL the // timing (cb_fields having a huge number and fts_now and fts_global being 0 all // the time), so we need to take that into account in fts_global before resetting // counters. if (cb_field1!=0) fts_global += cb_field1*1001/3; else fts_global += cb_field2*1001/3; // Reset counters - This is needed if some captions are still buffered // and need to be written after the last file is processed. cb_field1 = 0; cb_field2 = 0; cb_708 = 0; fts_now = 0; fts_max = 0; } // file loop close_input_file(ctx); if (ctx->fh_out_elementarystream!=NULL) fclose (ctx->fh_out_elementarystream); flushbuffer (ctx, &ctx->wbout1, false); flushbuffer (ctx, &ctx->wbout2, false); prepare_for_new_file (ctx); // To reset counters used by handle_end_of_data() telxcc_close(ctx); if (ctx->wbout1.fh!=-1) { if (ccx_options.write_format==CCX_OF_SMPTETT || ccx_options.write_format==CCX_OF_SAMI || ccx_options.write_format==CCX_OF_SRT || ccx_options.write_format==CCX_OF_TRANSCRIPT || ccx_options.write_format==CCX_OF_SPUPNG ) { handle_end_of_data(dec_ctx->context_cc608_field_1, &dec_sub); if (dec_sub.got_output) { encode_sub(enc_ctx,&dec_sub); dec_sub.got_output = 0; } } else if(ccx_options.write_format==CCX_OF_RCWT) { // Write last header and data writercwtdata (dec_ctx, NULL); } dinit_encoder(enc_ctx); } if (ctx->wbout2.fh!=-1) { if (ccx_options.write_format==CCX_OF_SMPTETT || ccx_options.write_format==CCX_OF_SAMI || ccx_options.write_format==CCX_OF_SRT || ccx_options.write_format==CCX_OF_TRANSCRIPT || ccx_options.write_format==CCX_OF_SPUPNG ) { handle_end_of_data(dec_ctx->context_cc608_field_2, &dec_sub); if (dec_sub.got_output) { encode_sub(enc_ctx,&dec_sub); dec_sub.got_output = 0; } } dinit_encoder(enc_ctx+1); } flushbuffer (ctx, &ctx->wbout1,true); flushbuffer (ctx, &ctx->wbout2,true); time (&final); long proc_time=(long) (final-start); mprint ("\rDone, processing time = %ld seconds\n", proc_time); if (proc_time>0) { LLONG ratio=(get_fts_max()/10)/proc_time; unsigned s1=(unsigned) (ratio/100); unsigned s2=(unsigned) (ratio%100); mprint ("Performance (real length/process time) = %u.%02u\n", s1, s2); } dbg_print(CCX_DMT_708, "The 708 decoder was reset [%d] times.\n",resets_708); if (ccx_options.teletext_mode == CCX_TXT_IN_USE) mprint ( "Teletext decoder: %"PRIu32" packets processed, %"PRIu32" SRT frames written.\n", tlt_packet_counter, tlt_frames_produced); if (dec_ctx->processed_enough) { mprint ("\rNote: Processing was cancelled before all data was processed because\n"); mprint ("\rone or more user-defined limits were reached.\n"); } if (ccblocks_in_avc_lost>0) { mprint ("Total caption blocks received: %d\n", ccblocks_in_avc_total); mprint ("Total caption blocks lost: %d\n", ccblocks_in_avc_lost); } mprint ("This is beta software. Report issues to carlos at ccextractor org...\n"); if (show_myth_banner) { mprint ("NOTICE: Due to the major rework in 0.49, we needed to change part of the timing\n"); mprint ("code in the MythTV's branch. Please report results to the address above. If\n"); mprint ("something is broken it will be fixed. Thanks\n"); } dinit_libraries(&ctx); return EXIT_OK; }
// 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; }