예제 #1
0
int parse_PAT (struct lib_ccx_ctx *ctx)
{
	int gotpes=0;
	int is_multiprogram=0;
	static int warning_program_not_found_shown=0;

	/* if ((forced_cappid || telext_mode==CCX_TXT_IN_USE) && cap_stream_type!=CCX_STREAM_TYPE_UNKNOWNSTREAM) // Already know what we need, skip
		return 0;  */

    if (!payload.pesstart)
        // Not the first entry. Ignore it, it should not be here.
        return 0;

    unsigned pointer_field = *(payload.start);
    unsigned char *payload_start = payload.start + pointer_field + 1;
	if (tspacket-payload_start+188<0) // Negative length? Seen it, but impossible
		return 0;
    unsigned payload_length = tspacket+188-payload_start;
	unsigned section_number = payload_start[6];
    unsigned last_section_number = payload_start[7];
	if (section_number > last_section_number) // Impossible: Defective PAT
	{
		dbg_print(CCX_DMT_PAT, "Skipped defective PAT packet, section_number=%u but last_section_number=%u\n",
			section_number, last_section_number);
		return gotpes;
	}
    if ( last_section_number > 0 )
    {
		dbg_print(CCX_DMT_PAT, "Long PAT packet (%u / %u), skipping.\n",
			section_number, last_section_number);
		return gotpes;
        /* fatal(CCX_COMMON_EXIT_BUG_BUG,
              "Sorry, long PATs not yet supported!\n"); */
    }

	if (last_pat_payload!=NULL && payload_length == last_pat_length &&
		!memcmp (payload_start, last_pat_payload, payload_length))
	{
		// dbg_print(CCX_DMT_PAT, "PAT hasn't changed, skipping.\n");
		return 0;
	}

	if (last_pat_payload!=NULL)
	{
		mprint ("Notice: PAT changed, clearing all variables.\n");
		clear_PMT_array();
		if (ccx_options.teletext_mode==CCX_TXT_IN_USE)
			ccx_options.teletext_mode=CCX_TXT_AUTO_NOT_YET_FOUND;
		ccx_options.ts_cappid=0;
		cap_stream_type=CCX_STREAM_TYPE_UNKNOWNSTREAM;
		memset (ctx->PIDs_seen,0,sizeof (int) *65536); // Forget all we saw
		if (!tlt_config.user_page) // If the user didn't select a page...
			tlt_config.page=0; // ..forget whatever we detected.

		gotpes=1;
	}

	last_pat_payload=(unsigned char *) realloc (last_pat_payload, payload_length+8); // Extra 8 in case memcpy copies dwords, etc
	if (last_pat_payload==NULL)
		fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory to process PAT.\n");
	memcpy (last_pat_payload, payload_start, payload_length);
	last_pat_length = payload_length;


    unsigned table_id = payload_start[0];
    unsigned section_length = (((payload_start[1] & 0x0F) << 8)
                               | payload_start[2]);
    unsigned transport_stream_id = ((payload_start[3] << 8)
                                    | payload_start[4]);
    unsigned version_number = (payload_start[5] & 0x3E) >> 1;

	// Means current OR next (so you can build a long PAT before it
	// actually is to be in use).
    unsigned current_next_indicator = payload_start[5] & 0x01;



    if (!current_next_indicator)
        // This table is not active, no need to evaluate
        return 0;

    payload_start += 8;
    payload_length = tspacket+188-payload_start;

    unsigned programm_data = section_length - 5 - 4; // prev. bytes and CRC

    dbg_print(CCX_DMT_PAT, "Read PAT packet (id: %u) ts-id: 0x%04x\n",
           table_id, transport_stream_id);
    dbg_print(CCX_DMT_PAT, "  section length: %u  number: %u  last: %u\n",
           section_length, section_number, last_section_number);
    dbg_print(CCX_DMT_PAT, "  version_number: %u  current_next_indicator: %u\n",
           version_number, current_next_indicator);

    if ( programm_data+4 > payload_length )
    {
        fatal(CCX_COMMON_EXIT_BUG_BUG,
              "Sorry, PAT too long!\n");
    }

    unsigned ts_prog_num = 0;
    unsigned ts_prog_map_pid = 0;
    dbg_print(CCX_DMT_PAT, "\nProgram association section (PAT)\n");

	ctx->freport.program_cnt=0;
	for( unsigned i=0; i < programm_data; i+=4)
	{
        unsigned program_number = ((payload_start[i] << 8)
			| payload_start[i+1]);
		if( !program_number )
			continue;
		ctx->freport.program_cnt++;
	}

	is_multiprogram = (ctx->freport.program_cnt>1);

    for( unsigned i=0; i < programm_data; i+=4)
    {
        unsigned program_number = ((payload_start[i] << 8)
                                   | payload_start[i+1]);
        unsigned prog_map_pid = ((payload_start[i+2] << 8)
                                 | payload_start[i+3]) & 0x1FFF;

        dbg_print(CCX_DMT_PAT, "  Program number: %u  -> PMTPID: %u\n",
                    program_number, prog_map_pid);

		if( !program_number )
			continue;

		if (!is_multiprogram || (ccx_options.ts_forced_program_selected && program_number == ccx_options.ts_forced_program))
		{
			// If there's just one program we select it unless the user selected
			// something else anyway.
			ts_prog_num = program_number;
			ts_prog_map_pid = prog_map_pid;
		}

		// Having an array for PMTs comes from telxcc.
		int found=0,j;
		for (j=0;j<pmt_array_length; j++)
		{
			if (pmt_array[j].program_number == program_number)
			{
				found=1;
				break;
			}
		}
		if (!found && pmt_array_length < TS_PMT_MAP_SIZE)
		{
			pmt_array[pmt_array_length].program_number=program_number;
			pmt_array[pmt_array_length].PMT_PID=prog_map_pid;
			pmt_array_length++;
		}
    } // for

	if (is_multiprogram && !ts_prog_num)
	{
        // We can only work with "simple" ts files
		if (ccx_options.ts_forced_program_selected && !warning_program_not_found_shown)
		{
			mprint ("\rThe program you selected (%u) wasn't found in the first Program Association Table in the stream.\n",ccx_options.ts_forced_program);
			mprint ("I will continue reading the stream in case the program appears later.\n\n");
			warning_program_not_found_shown=1;
		}
		mprint ("\nThis TS file has more than one program. These are the program numbers found: \n");
		for( unsigned j=0; j < programm_data; j+=4)
		{
			unsigned pn = ((payload_start[j] << 8)
                           | payload_start[j+1]);
			if (pn)
				mprint ("%u\n",pn);
			activity_program_number (pn);
		}
		if (!ccx_options.ts_forced_program_selected)
		{
			if (!ccx_options.ts_autoprogram)
				fatal(CCX_COMMON_EXIT_BUG_BUG, "Run ccextractor again with --program-number specifying which program\nto process.");
			else
				mprint ("\nThe first program with a suitable CC stream will be selected.\n");
		}
	}

    // If we found a new PAT reset all TS stream variables
    if( ts_prog_num != TS_program_number )
    {
        TS_program_number = ts_prog_num;
        pmtpid = ts_prog_map_pid;
		if (!ccx_options.ts_forced_cappid)
			ccx_options.ts_cappid = 0; // Reset caption stream pid
        // If we have data flush it
        if( ctx->capbuflen > 0 )
            gotpes = 1;
    }
	return gotpes;
}
예제 #2
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;
}