示例#1
0
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;
}
示例#2
0
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;
}
示例#3
0
// 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;
}