コード例 #1
0
/* Helper function for to display debug timing info. */
void print_debug_timing(struct ccx_common_timing_ctx *ctx)
{
	// Avoid wrong "Calc. difference" and "Asynchronous by" numbers
	// for uninitialized min_pts
	LLONG tempmin_pts = (ctx->min_pts==0x01FFFFFFFFLL ? ctx->sync_pts : ctx->min_pts);

	ccx_common_logging.log_ftn("Sync time stamps:  PTS: %s                ",
			print_mstime((ctx->sync_pts)/(MPEG_CLOCK_FREQ/1000)) );
	ccx_common_logging.log_ftn("GOP: %s      \n", print_mstime(gop_time.ms));

	// Length first GOP to last GOP
	LLONG goplenms = (LLONG) (gop_time.ms - first_gop_time.ms);
	// Length at last sync point
	LLONG ptslenms = (unsigned)((ctx->sync_pts-tempmin_pts)/(MPEG_CLOCK_FREQ/1000)
			+ ctx->fts_offset);

	ccx_common_logging.log_ftn("Last               FTS: %s",
			print_mstime(get_fts_max(ctx)));
	ccx_common_logging.log_ftn("      GOP start FTS: %s\n",
			print_mstime(fts_at_gop_start));

	// Times are based on last GOP and/or sync time
	ccx_common_logging.log_ftn("Max FTS diff. to   PTS:       %6lldms              GOP:       %6lldms\n\n",
			get_fts_max(ctx)+(LLONG)(1000.0/current_fps)-ptslenms,
			get_fts_max(ctx)+(LLONG)(1000.0/current_fps)-goplenms);
}
コード例 #2
0
ファイル: timing.cpp プロジェクト: ScandalCorp/ccextractor
/* Helper function for to display debug timing info. */
void print_debug_timing( void )
{
    // Avoid wrong "Calc. difference" and "Asynchronous by" numbers
    // for uninitialized min_pts
    LLONG tempmin_pts = (min_pts==0x01FFFFFFFFLL ? sync_pts : min_pts);

    mprint("Sync time stamps:  PTS: %s                ",
           print_mstime((sync_pts)/(MPEG_CLOCK_FREQ/1000)) );
    mprint("GOP: %s      \n", print_mstime(gop_time.ms));

    // Length first GOP to last GOP
    LLONG goplenms = LLONG(gop_time.ms - first_gop_time.ms);
    // Length at last sync point
    LLONG ptslenms = unsigned((sync_pts-tempmin_pts)/(MPEG_CLOCK_FREQ/1000)
                              + fts_offset);

    mprint("Last               FTS: %s",
           print_mstime(get_fts_max()));
    mprint("      GOP start FTS: %s\n",
           print_mstime(fts_at_gop_start));

    // Times are based on last GOP and/or sync time
    mprint("Max FTS diff. to   PTS:       %6lldms              GOP:       %6lldms\n\n",
           get_fts_max()+LLONG(1000.0/current_fps)-ptslenms,
           get_fts_max()+LLONG(1000.0/current_fps)-goplenms);
}
コード例 #3
0
void set_current_pts(struct ccx_common_timing_ctx *ctx, LLONG pts)
{
	ctx->current_pts = pts;
	if(ctx->pts_set == 0)
		ctx->pts_set = 1;
	dbg_print(CCX_DMT_VIDES, "PTS: %s (%8u)", print_mstime(ctx->current_pts/(MPEG_CLOCK_FREQ/1000)),
				(unsigned) (ctx->current_pts));
	dbg_print(CCX_DMT_VIDES, "  FTS: %s \n",print_mstime(ctx->fts_now));
}
コード例 #4
0
ファイル: output.c プロジェクト: slifty/ccextractor
/* Buffer data with the same FTS and write when a new FTS or data==NULL
 * is encountered */
void writercwtdata (struct lib_cc_decode *ctx, const unsigned char *data, struct cc_subtitle *sub)
{
	static LLONG prevfts = -1;
	LLONG currfts = fts_now + fts_global;
	static uint16_t cbcount = 0;
	static int cbempty=0;
	static unsigned char cbbuffer[0xFFFF*3]; // TODO: use malloc
	static unsigned char cbheader[8+2];

	if ( (prevfts != currfts && prevfts != -1)
			|| data == NULL
			|| cbcount == 0xFFFF)
	{
		// Remove trailing empty or 608 padding caption blocks
		if ( cbcount != 0xFFFF)
		{
			unsigned char cc_valid;
			unsigned char cc_type;
			int storecbcount=cbcount;

			for( int cb = cbcount-1; cb >= 0 ; cb-- )
			{
				cc_valid = (*(cbbuffer+3*cb) & 4) >>2;
				cc_type = *(cbbuffer+3*cb) & 3;

				// The -fullbin option disables pruning of 608 padding blocks
				if ( (cc_valid && cc_type <= 1 // Only skip NTSC padding packets
							&& !ctx->fullbin // Unless we want to keep them
							&& *(cbbuffer+3*cb+1)==0x80
							&& *(cbbuffer+3*cb+2)==0x80)
						|| !(cc_valid || cc_type==3) ) // or unused packets
				{
					cbcount--;
				}
				else
				{
					cb = -1;
				}
			}
			dbg_print(CCX_DMT_CBRAW, "%s Write %d RCWT blocks - skipped %d padding / %d unused blocks.\n",
					print_mstime(prevfts), cbcount, storecbcount - cbcount, cbempty);
		}

		// New FTS, write data header
		// RCWT data header (10 bytes):
		//byte(s)   value   description
		//0-7       FTS     int64_t number with current FTS
		//8-9       blocks  Number of 3 byte data blocks with the same FTS that are
		//                  following this header
		memcpy(cbheader,&prevfts,8);
		memcpy(cbheader+8,&cbcount,2);

		if (cbcount > 0)
		{
			ctx->writedata(cbheader, 10, ctx->context_cc608_field_1, sub);
			ctx->writedata(cbbuffer, 3 * cbcount, ctx->context_cc608_field_1, sub);
		}
		cbcount = 0;
		cbempty = 0;
	}
コード例 #5
0
/* This function returns the current FTS and saves it so it can be used by ctxget_visible_start */
LLONG get_visible_end (struct ccx_common_timing_ctx *ctx, int current_field)
{
	LLONG fts = get_fts(ctx, current_field);
	if (fts > ctx->minimum_fts)
		ctx->minimum_fts = fts;
	ccx_common_logging.debug_ftn(CCX_DMT_DECODER_608, "Visible End time=%s\n", print_mstime(fts));
	return fts;
}
コード例 #6
0
/* This function returns a FTS that is guaranteed to be at least 1 ms later than the end of the previous screen. It shouldn't be needed
   obviously but it guarantees there's no timing overlap */
LLONG get_visible_start (void)
{
	LLONG fts = get_fts();
	if (fts <= minimum_fts)
		fts = minimum_fts+1;
	ccx_common_logging.debug_ftn(CCX_DMT_DECODER_608, "Visible Start time=%s\n", print_mstime(fts));
	return fts;
}
コード例 #7
0
/* This function returns the current FTS and saves it so it can be used by get_visible_start */
LLONG get_visible_end (void)
{
	LLONG fts = get_fts();
	if (fts>minimum_fts)
		minimum_fts=fts;
	ccx_common_logging.debug_ftn(CCX_DMT_DECODER_608, "Visible End time=%s\n", print_mstime(fts));
	return fts;
}
コード例 #8
0
int do_cb (struct lib_cc_decode *ctx, unsigned char *cc_block, struct cc_subtitle *sub)
{
	unsigned char cc_valid = (*cc_block & 4) >>2;
	unsigned char cc_type = *cc_block & 3;

	int timeok = 1;

	if ( ctx->fix_padding
		&& cc_valid==0 && cc_type <= 1 // Only fix NTSC packets
		&& cc_block[1]==0 && cc_block[2]==0 )
	{
		/* Padding */
		cc_valid=1;
		cc_block[1]=0x80;
		cc_block[2]=0x80;
	}

	if ( ctx->write_format!=CCX_OF_RAW && // In raw we cannot skip padding because timing depends on it
		 ctx->write_format!=CCX_OF_DVDRAW &&
		(cc_block[0]==0xFA || cc_block[0]==0xFC || cc_block[0]==0xFD )
		&& (cc_block[1]&0x7F)==0 && (cc_block[2]&0x7F)==0) // CFS: Skip non-data, makes debugging harder.
		return 1;

	// Print raw data with FTS.
	dbg_print(CCX_DMT_CBRAW, "%s   %d   %02X:%c%c:%02X", print_mstime(ctx->timing->fts_now + ctx->timing->fts_global),in_xds_mode,
			cc_block[0], cc_block[1]&0x7f,cc_block[2]&0x7f, cc_block[2]);

	/* In theory the writercwtdata() function could return early and not
	 * go through the 608/708 cases below.  We do that to get accurate
	 * counts for cb_field1, cb_field2 and cb_708.
	 * Note that printdata() and dtvcc_process_data() must not be called for
	 * the CCX_OF_RCWT case. */

	if (cc_valid || cc_type==3)
	{
		ctx->cc_stats[cc_type]++;

		switch (cc_type)
		{
			case 0:
				dbg_print(CCX_DMT_CBRAW, "    %s   ..   ..\n",  debug_608toASC( cc_block, 0));

				ctx->current_field = 1;
				ctx->saw_caption_block = 1;

				if (ctx->extraction_start.set &&
						get_fts(ctx->timing, ctx->current_field) < ctx->extraction_start.time_in_ms)
					timeok = 0;
				if (ctx->extraction_end.set &&
						get_fts(ctx->timing, ctx->current_field) > ctx->extraction_end.time_in_ms)
				{
					timeok = 0;
					ctx->processed_enough=1;
				}
				if (timeok)
				{
					if(ctx->write_format!=CCX_OF_RCWT)
						printdata (ctx, cc_block+1,2,0,0, sub);
					else
						writercwtdata(ctx, cc_block, sub);
				}
				cb_field1++;
				break;
			case 1:
				dbg_print(CCX_DMT_CBRAW, "    ..   %s   ..\n",  debug_608toASC( cc_block, 1));

				ctx->current_field = 2;
				ctx->saw_caption_block = 1;

				if (ctx->extraction_start.set &&
						get_fts(ctx->timing, ctx->current_field) < ctx->extraction_start.time_in_ms)
					timeok = 0;
				if (ctx->extraction_end.set &&
						get_fts(ctx->timing, ctx->current_field) > ctx->extraction_end.time_in_ms)
				{
					timeok = 0;
					ctx->processed_enough=1;
				}
				if (timeok)
				{
					if(ctx->write_format!=CCX_OF_RCWT)
						printdata (ctx, 0,0,cc_block+1,2, sub);
					else
						writercwtdata(ctx, cc_block, sub);
				}
				cb_field2++;
				break;
			case 2: //EIA-708
				// DTVCC packet data
				// Fall through
			case 3: //EIA-708
				dbg_print(CCX_DMT_CBRAW, "    ..   ..   DD\n");

				// DTVCC packet start
				ctx->current_field = 3;

				if (ctx->extraction_start.set &&
						get_fts(ctx->timing, ctx->current_field) < ctx->extraction_start.time_in_ms)
					timeok = 0;
				if (ctx->extraction_end.set &&
						get_fts(ctx->timing, ctx->current_field) > ctx->extraction_end.time_in_ms)
				{
					timeok = 0;
					ctx->processed_enough=1;
				}
				char temp[4];
				temp[0]=cc_valid;
				temp[1]=cc_type;
				temp[2]=cc_block[1];
				temp[3]=cc_block[2];
				if (timeok)
				{
					if (ctx->write_format != CCX_OF_RCWT)
						ccx_dtvcc_process_data(ctx, (const unsigned char *) temp, 4);
					else
						writercwtdata(ctx, cc_block, sub);
				}
				cb_708++;
				// Check for bytes read
				// printf ("Warning: Losing EIA-708 data!\n");
				break;
			default:
				fatal(CCX_COMMON_EXIT_BUG_BUG, "Cannot be reached!");
		} // switch (cc_type)
	} // cc_valid
	else
	{
		dbg_print(CCX_DMT_CBRAW, "    ..   ..   ..\n");
		dbg_print(CCX_DMT_VERBOSE, "Found !(cc_valid || cc_type==3) - ignore this block\n");
	}

	return 1;
}
コード例 #9
0
ファイル: stream_functions.cpp プロジェクト: MrMdR/julapy
/* Read and evaluate the current video PES header. The function returns
 * the length of the payload if successful, otherwise -1 is returned
 * indicating a premature end of file / too small buffer.
 * If sbuflen is
 *    0 .. Read from file into nextheader
 *    >0 .. Use data in nextheader with the length of sbuflen
 */
int read_video_pes_header (unsigned char *nextheader, int *headerlength, int sbuflen)
{
    // Read the next video PES
    // ((nextheader[3]&0xf0)==0xe0)
    unsigned peslen=nextheader[4]<<8 | nextheader[5];
    unsigned payloadlength = 0; // Length of packet data bytes

    if ( !sbuflen )
    {
        // Extension present, get it
        buffered_read (nextheader+6,3);
        past=past+result;
        if (result!=3) {
            // Consider this the end of the show.
            return -1;
        }
    }
    else
    {
	// We need at least 9 bytes to continue
	if( sbuflen < 9 )
	    return -1;
    }
    *headerlength = 6+3;

    unsigned hskip=0;

    // Assume header[8] is right, but double check
    if ( !sbuflen )
    {
        if (nextheader[8] > 0) {
            buffered_read (nextheader+9,nextheader[8]);
            past=past+result;
            if (result!=nextheader[8]) {
                return -1;
            }
        }
    }
    else
    {
	// See if the buffer is big enough
	if( sbuflen < *headerlength + (int)nextheader[8] )
	    return -1;
    }
    *headerlength += (int) nextheader[8];
    int falsepes = 0;
    int pesext = 0;

    // Avoid false positives, check --- not really needed
    if ( (nextheader[7]&0xC0) == 0x80 ) {
        // PTS only
        hskip += 5;
        if( (nextheader[9]&0xF1) != 0x21 || (nextheader[11]&0x01) != 0x01
            || (nextheader[13]&0x01) != 0x01 ) {
            falsepes = 1;
            printf("False PTS\n");
        }
    } else if ( (nextheader[7]&0xC0) == 0xC0 ) {
        // PTS and DTS
        hskip += 10;
        if( (nextheader[9]&0xF1) != 0x31 || (nextheader[11]&0x01) != 0x01
            || (nextheader[13]&0x01) != 0x01
            || (nextheader[14]&0xF1) != 0x11 || (nextheader[16]&0x01) != 0x01
            || (nextheader[18]&0x01) != 0x01 ) {
            falsepes = 1;
            printf("False PTS/DTS\n");
        }
    } else if ( (nextheader[7]&0xC0) == 0x40 ) {
        // Forbidden
        falsepes = 1;
        printf("False PTS/DTS flag\n");
    }
    if ( !falsepes && nextheader[7]&0x20 ) { // ESCR
        if ((nextheader[9+hskip]&0xC4) != 0x04 || !(nextheader[11+hskip]&0x04)
            || !(nextheader[13+hskip]&0x04) || !(nextheader[14+hskip]&0x01) ) {
            falsepes = 1;
            printf("False ESCR\n");
        }
        hskip += 6;
    }
    if ( !falsepes && nextheader[7]&0x10 ) { // ES
        if ( !(nextheader[9+hskip]&0x80) || !(nextheader[11+hskip]&0x01) ) {
            printf("False ES\n");
            falsepes = 1;
        }
        hskip += 3;
    }
    if ( !falsepes && nextheader[7]&0x04) { // add copy info
        if ( !(nextheader[9+hskip]&0x80) ) {
            printf("False add copy info\n");
            falsepes = 1;
        }
        hskip += 1;
    }
    if ( !falsepes && nextheader[7]&0x02) { // PES CRC
        hskip += 2;
    }
    if ( !falsepes && nextheader[7]&0x01) { // PES extension
        if ( (nextheader[9+hskip]&0x0E)!=0x0E ) {
            printf("False PES ext\n");
            falsepes = 1;
        }
        hskip += 1;
        pesext = 1;
    }

    if ( !falsepes ) {
        hskip = nextheader[8];
    }

    if ( !falsepes && nextheader[7]&0x80 ) {
        // Read PTS from byte 9,10,11,12,13

        unsigned bits_9 =  (nextheader[9] & 0x0E) << 29;
        unsigned bits_10 = nextheader[10] << 22;
        unsigned bits_11 = (nextheader[11] & 0xFE) << 14;
        unsigned bits_12 = nextheader[12] << 7;
        unsigned bits_13 = nextheader[13] >> 1;
        current_pts = bits_9 | bits_10 | bits_11 | bits_12 | bits_13;

        if (pts_set==0)
            pts_set=1;

        if (debug_verbose)
            printf("Set PTS: %s (%u)\n",
                   print_mstime((current_pts)/(MPEG_CLOCK_FREQ/1000)),
                   unsigned(current_pts) );
        /* The user data holding the captions seems to come between GOP and
         * the first frame. The sync PTS (sync_pts) (set at picture 0)
         * corresponds to the first frame of the current GOP. */
    }
コード例 #10
0
// Return TRUE if all was read.  FALSE if a problem occured:
// If a bitstream syntax problem occured the bitstream will
// point to after the problem, in case we run out of data the bitstream
// will point to where we want to restart after getting more.
static int read_pic_info(struct bitstream *esstream)
{
    dbg_print(DMT_VERBOSE, "Read PIC Info\n");

    // We only get here after seeing that start code
    if (next_u32(esstream) != 0x00010000) // LSB first (0x00000100)
        fatal(EXIT_BUG_BUG, "Impossible!");

    // If we get here esstream points to the start of a group_start_code
    // should we run out of data in esstream this is where we want to restart
    // after getting more.
    unsigned char *pic_info_start = esstream->pos;

    pic_header(esstream);
    pic_coding_ext(esstream);

    if (esstream->error)
        return 0;

    if (esstream->bitsleft < 0)
    {
        init_bitstream(esstream, pic_info_start, esstream->end);
        return 0;
    }

    // Analyse/use the picture information
    static int maxtref; // Use to remember the temporal reference number

    // A new anchor frame - flush buffered caption data. Might be flushed
    // in GOP header already.
    if (picture_coding_type==I_FRAME || picture_coding_type==P_FRAME)
    {		
//		if (((picture_structure != 0x1) && (picture_structure != 0x2)) ||
//		(temporal_reference != current_tref))
//		{
			// NOTE: process_hdcc() needs to be called before set_fts() as it
			// uses fts_now to re-create the timeline !!!!!
			if (has_ccdata_buffered)
			{
	            process_hdcc();
			}
			anchor_hdcc(temporal_reference);
//		}
    }

    current_tref = temporal_reference;
    current_picture_coding_type = picture_coding_type;

    // We mostly use PTS, but when the GOP mode is enabled do not set
    // the FTS time here.
    if (!use_gop_as_pts)
    {
        set_fts(); // Initialize fts
    }

    dbg_print(DMT_VIDES, "PTS: %s (%8u) - tref: %2d - %s  since tref0/GOP: %2u/%2u",
           print_mstime(current_pts/(MPEG_CLOCK_FREQ/1000)),
           unsigned(current_pts), temporal_reference,
       pict_types[picture_coding_type],
           unsigned(frames_since_ref_time),
           unsigned(frames_since_last_gop));
    dbg_print(DMT_VIDES, "  t:%d r:%d p:%d", top_field_first,
           repeat_first_field, progressive_frame);
    dbg_print(DMT_VIDES, "  FTS: %s\n", print_mstime(get_fts()));

    // Set min_pts/sync_pts according to the current time stamp.
    // Use fts_at_gop_start as reference when a GOP header was seen
    // since the last frame 0. If not this is most probably a
    // TS without GOP headers but with USER DATA after each picture
    // header. Use the current FTS values as reference.
    // Note: If a GOP header was present the reference time is from
    // the beginning of the GOP, otherwise it is now.
    if(temporal_reference == 0)
    {
        last_gop_length = maxtref + 1;
        maxtref = temporal_reference;

        // frames_since_ref_time is used in set_fts()

        if( saw_gop_header )
        {
            // This time (fts_at_gop_start) that was set in the
            // GOP header and it might be off by one GOP. See the comment there.
            frames_since_ref_time = frames_since_last_gop; // Should this be 0?
        }
        else
        {
            // No GOP header, use the current values
            fts_at_gop_start=get_fts();
            frames_since_ref_time = 0;
        }

        if (debug_mask & DMT_TIME)
        {
            dbg_print(DMT_TIME, "\nNew temporal reference:\n");
            print_debug_timing();
        }

        saw_gop_header = 0; // Reset the value
    }

    if ( !saw_gop_header && picture_coding_type==I_FRAME )
    {
        // A new GOP beginns with an I-frame. Lets hope there are
        // never more than one per GOP
        frames_since_last_gop = 0;
    }

    // Set maxtref
    if( temporal_reference > maxtref ) {
        maxtref = temporal_reference;
        if (maxtref+1 > max_gop_length)
            max_gop_length = maxtref+1;
    }

    unsigned extraframe = 0;
    if (repeat_first_field)
    {
        pulldownfields++;
        total_pulldownfields++;
        if ( current_progressive_sequence || !(total_pulldownfields%2) )
            extraframe = 1;
        if ( current_progressive_sequence && top_field_first )
            extraframe = 2;
        dbg_print(DMT_VIDES, "Pulldown: total pd fields: %d - %d extra frames\n",
                   total_pulldownfields, extraframe);
    }

    total_pulldownframes += extraframe;
    total_frames_count += 1+extraframe;
    frames_since_last_gop += 1+extraframe;
    frames_since_ref_time += 1+extraframe;

    dbg_print(DMT_VERBOSE, "Read PIC Info - processed\n\n");

    return 1;
}
コード例 #11
0
// Return TRUE if the data parsing finished, FALSE otherwise.
// estream->pos is advanced. Data is only processed if esstream->error
// is FALSE, parsing can set esstream->error to TRUE.
static int gop_header(struct bitstream *esstream)
{
    dbg_print(DMT_VERBOSE, "GOP header\n");

    if (esstream->error || esstream->bitsleft <= 0)
        return 0;

    // We only get here after seeing that start code
    if (read_u32(esstream) != 0xB8010000) // LSB first (0x000001B8)
        fatal(EXIT_BUG_BUG, "Impossible!");

    unsigned drop_frame_flag = (unsigned) read_bits(esstream,1);
    struct gop_time_code gtc;
    gtc.time_code_hours = (int) read_bits(esstream,5);
    gtc.time_code_minutes = (int) read_bits(esstream,6);
    skip_bits(esstream,1); // Marker bit
    gtc.time_code_seconds = (int) read_bits(esstream,6);
    gtc.time_code_pictures = (int) read_bits(esstream,6);
    gtc.inited = 1;
    calculate_ms_gop_time(&gtc);

    if (esstream->bitsleft < 0)
        return 0;

    if (gop_accepted(&gtc))
    {
        // Do GOP padding during GOP header. The previous GOP and all
        // included captions are written. Use the current GOP time to
        // do the padding.

        // Flush buffered cc blocks before doing the housekeeping
        if (has_ccdata_buffered)
        {
            process_hdcc();
        }

        // Last GOPs pulldown frames
        if ((current_pulldownfields>0) != (pulldownfields>0))
        {
            current_pulldownfields = pulldownfields;
            dbg_print(DMT_VERBOSE, "Pulldown: %s", (pulldownfields ? "on" : "off"));
            if (pulldownfields)
                dbg_print(DMT_VERBOSE, " - %u fields in last GOP", pulldownfields);
            dbg_print(DMT_VERBOSE, "\n");
        }
        pulldownfields = 0;

        // Report synchronization jumps between GOPs. Warn if there
        // are 20% or more deviation.
        if ( (debug_mask & DMT_TIME)
             && ((gtc.ms - gop_time.ms // more than 20% longer
                  > frames_since_last_gop*1000.0/current_fps*1.2)
                 ||
                 (gtc.ms - gop_time.ms // or 20% shorter
                  < frames_since_last_gop*1000.0/current_fps*0.8))
             && first_gop_time.inited )
        {
            mprint("\rWarning: Jump in GOP timing.\n");
            mprint("  (old) %s",
                   print_mstime(gop_time.ms));
            mprint("  +  %s (%uF)",
                   print_mstime(LLONG(frames_since_last_gop
                                      *1000.0/current_fps)),
                   frames_since_last_gop);
            mprint("  !=  (new) %s\n",
                   print_mstime(gtc.ms));
        }

        if (first_gop_time.inited == 0)
        {
            first_gop_time = gtc;

            // It needs to be "+1" because the frame count starts at 0 and we
            // need the length of all frames.
            if ( total_frames_count == 0 )
            {   // If this is the first frame there cannot be an offset
                fts_fc_offset = 0;
                // first_gop_time.ms stays unchanged
            }
            else
            {
                fts_fc_offset = LLONG((total_frames_count+1)
                                      *1000.0/current_fps);
                // Compensate for those written before
                first_gop_time.ms -= fts_fc_offset;
            }

            dbg_print(DMT_TIME, "\nFirst GOP time: %02u:%02u:%02u:%03u %+lldms\n",
                    gtc.time_code_hours,
                    gtc.time_code_minutes, gtc.time_code_seconds,
                    unsigned(1000.0*gtc.time_code_pictures/current_fps),
                    fts_fc_offset);
        }

        gop_time = gtc;

        frames_since_last_gop=0;
        // Indicate that we read a gop header (since last frame number 0)
        saw_gop_header = 1;

        // If we use GOP timing, reconstruct the PTS from the GOP
        if (use_gop_as_pts)
        {
            current_pts = gtc.ms*(MPEG_CLOCK_FREQ/1000);
            if (pts_set==0)
                pts_set=1;
            current_tref = 0;
            frames_since_ref_time = 0;
            set_fts();
            fts_at_gop_start = get_fts_max();
        }
        else
        {
            // FIXME: Wrong when PTS are not increasing but are identical
            // troughout the GOP and then jump to the next time for the
            // next GOP.
            // This effect will also lead to captions being one GOP early
            // for DVD captions.
            fts_at_gop_start = get_fts_max() + LLONG(1000.0/current_fps);
        }

        if (debug_mask & DMT_TIME)
        {
            dbg_print(DMT_TIME, "\nNew GOP:\n");
            dbg_print(DMT_TIME, "\nDrop frame flag: %u:\n", drop_frame_flag);
            print_debug_timing();
        }
    }

    return 1;
}
コード例 #12
0
ファイル: 708.cpp プロジェクト: MrMdR/julapy
int handle_708_C1 (cc708_service_decoder *decoder, unsigned char *data, int data_length)
{
    S_COMMANDS_C1 com=COMMANDS_C1[data[0]-0x80];
    printf ("%s | C1: [%02X]  [%s] [%s] (%d)\n",
        print_mstime(get_fts()),
        data[0],com.name,com.description, com.length);    
    if (com.length>data_length)
    {
        printf ("C1: Warning: Not enough bytes for command.\n");
        return -1;
    }
    switch (com.code)
    {
        case CW0: /* SetCurrentWindow */
        case CW1:
        case CW2:
        case CW3:
        case CW4:
        case CW5:
        case CW6:
        case CW7:
            handle_708_CWx_SetCurrentWindow (decoder, com.code-CW0); /* Window 0 to 7 */
            break;
        case CLW:
            handle_708_CLW_ClearWindows (decoder, data[1]); 
            break;
        case DSW:
            handle_708_DSW_DisplayWindows (decoder, data[1]);
            break;
        case HDW:
            handle_708_HDW_HideWindows (decoder, data[1]);
            break;
        case TGW:
            handle_708_TGW_ToggleWindows (decoder, data[1]);
            break;
        case DLW:
            handle_708_DLW_DeleteWindows (decoder, data[1]);
            break;
        case DLY:
            handle_708_DLY_Delay (decoder, data[1]);
            break;
        case DLC:
            handle_708_DLC_DelayCancel (decoder);
            break;
        case RST:
            cc708_service_reset(decoder);
            break;
        case SPA:
            handle_708_SPA_SetPenAttributes (decoder, data);
            break;
        case SPC:
            handle_708_SPC_SetPenColor (decoder, data);
            break;
        case SPL:
            handle_708_SPL_SetPenLocation (decoder, data);
            break;            
        case RSV93:
        case RSV94:
        case RSV95:
        case RSV96:
            printf ("Warning, found Reserved codes, ignored.\n");
            break;
        case SWA:
            handle_708_SWA_SetWindowAttributes (decoder, data);
            break;            
        case DF0:
        case DF1:
        case DF2:
        case DF3:
        case DF4:
        case DF5:
        case DF6:
        case DF7:
            handle_708_DFx_DefineWindow (decoder, com.code-DF0, data); /* Window 0 to 7 */
            break;            
        default:
            printf ("BUG: Unhandled code in handle_708_C1.\n");
            break;            
    }
    
    return com.length;
}
コード例 #13
0
// C1 Code Set - Captioning Commands Control Codes
int _dtvcc_handle_C1(ccx_dtvcc_ctx *dtvcc,
					 ccx_dtvcc_service_decoder *decoder,
					 unsigned char *data,
					 int data_length)
{
	struct CCX_DTVCC_S_COMMANDS_C1 com = DTVCC_COMMANDS_C1[data[0] - 0x80];
	ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] C1: %s | [%02X]  [%s] [%s] (%d)\n",
			print_mstime(get_fts(dtvcc->timing, 3)),
			data[0], com.name, com.description, com.length);

	if (com.length > data_length)
	{
		ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] C1: Warning: Not enough bytes for command.\n");
		return -1;
	}

	switch (com.code)
	{
		case CCX_DTVCC_C1_CW0: /* SetCurrentWindow */
		case CCX_DTVCC_C1_CW1:
		case CCX_DTVCC_C1_CW2:
		case CCX_DTVCC_C1_CW3:
		case CCX_DTVCC_C1_CW4:
		case CCX_DTVCC_C1_CW5:
		case CCX_DTVCC_C1_CW6:
		case CCX_DTVCC_C1_CW7:
			dtvcc_handle_CWx_SetCurrentWindow(decoder, com.code - CCX_DTVCC_C1_CW0); /* Window 0 to 7 */
			break;
		case CCX_DTVCC_C1_CLW:
			dtvcc_handle_CLW_ClearWindows(decoder, data[1]);
			break;
		case CCX_DTVCC_C1_DSW:
			dtvcc_handle_DSW_DisplayWindows(decoder, data[1], dtvcc->timing);
			break;
		case CCX_DTVCC_C1_HDW:
			dtvcc_handle_HDW_HideWindows(dtvcc, decoder, data[1]);
			break;
		case CCX_DTVCC_C1_TGW:
			dtvcc_handle_TGW_ToggleWindows(dtvcc, decoder, data[1]);
			break;
		case CCX_DTVCC_C1_DLW:
			dtvcc_handle_DLW_DeleteWindows(dtvcc, decoder, data[1]);
			break;
		case CCX_DTVCC_C1_DLY:
			dtvcc_handle_DLY_Delay(decoder, data[1]);
			break;
		case CCX_DTVCC_C1_DLC:
			dtvcc_handle_DLC_DelayCancel(decoder);
			break;
		case CCX_DTVCC_C1_RST:
			dtvcc_handle_RST_Reset(decoder);
			break;
		case CCX_DTVCC_C1_SPA:
			dtvcc_handle_SPA_SetPenAttributes(decoder, data);
			break;
		case CCX_DTVCC_C1_SPC:
			dtvcc_handle_SPC_SetPenColor(decoder, data);
			break;
		case CCX_DTVCC_C1_SPL:
			dtvcc_handle_SPL_SetPenLocation(decoder, data);
			break;
		case CCX_DTVCC_C1_RSV93:
		case CCX_DTVCC_C1_RSV94:
		case CCX_DTVCC_C1_RSV95:
		case CCX_DTVCC_C1_RSV96:
			ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] Warning, found Reserved codes, ignored.\n");
			break;
		case CCX_DTVCC_C1_SWA:
			dtvcc_handle_SWA_SetWindowAttributes(decoder, data);
			break;
		case CCX_DTVCC_C1_DF0:
		case CCX_DTVCC_C1_DF1:
		case CCX_DTVCC_C1_DF2:
		case CCX_DTVCC_C1_DF3:
		case CCX_DTVCC_C1_DF4:
		case CCX_DTVCC_C1_DF5:
		case CCX_DTVCC_C1_DF6:
		case CCX_DTVCC_C1_DF7:
			dtvcc_handle_DFx_DefineWindow(decoder, com.code - CCX_DTVCC_C1_DF0, data, dtvcc->timing); /* Window 0 to 7 */
			break;
		default:
			ccx_common_logging.log_ftn ("[CEA-708] BUG: Unhandled code in _dtvcc_handle_C1.\n");
			break;
	}

	return com.length;
}
コード例 #14
0
ファイル: ccx_decoders_608.c プロジェクト: slifty/ccextractor
/* If wb is NULL, then only XDS will be processed */
int process608(const unsigned char *data, int length, void *private_data, struct cc_subtitle *sub)
{
	struct ccx_decoder_608_report  *report = NULL;
	ccx_decoder_608_context *context = private_data;
	static int textprinted = 0;
	int i;
	if (context)
	{
		report = &context->report;
		context->bytes_processed_608 += length;
	}
	if (!data)
	{
		return -1;
	}
	for (i=0; i < length; i=i+2)
	{
		unsigned char hi, lo;
		int wrote_to_screen=0;

		hi = data[i] & 0x7F; // Get rid of parity bit
		lo = data[i+1] & 0x7F; // Get rid of parity bit

		if (hi==0 && lo==0) // Just padding
			continue;

		// printf ("\r[%02X:%02X]\n",hi,lo);

		if (hi>=0x10 && hi<=0x1e) {
			int ch = (hi<=0x17)? 1 : 2;
			if (context == NULL || context->my_field == 2) // Originally: current_field from sequencing.c. Seems to be just to change channel, so context->my_field seems good.
				ch+=2;

			if(report)
				report->cc_channels[ch - 1] = 1;
		}

		if (hi >= 0x01 && hi <= 0x0E && (context == NULL || context->my_field == 2)) // XDS can only exist in field 2.
		{
			if (context)
				context->channel = 3;
			if (!in_xds_mode)
			{
				ts_start_of_xds=get_fts();
				in_xds_mode=1;
			}
			if(report)
				report->xds=1;
		}
		if (hi == 0x0F && in_xds_mode && (context == NULL || context->my_field == 2)) // End of XDS block
		{
			in_xds_mode=0;
			do_end_of_xds (sub, lo);
			if (context)
				context->channel = context->new_channel; // Switch from channel 3
			continue;
		}
		if (hi>=0x10 && hi<=0x1F) // Non-character code or special/extended char
			// http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/CC_CODES.HTML
			// http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/CC_CHARS.HTML
		{
			// We were writing characters before, start a new line for
			// diagnostic output from disCommand()
			if (textprinted == 1 )
			{
				ccx_common_logging.debug_ftn(CCX_DMT_DECODER_608, "\n");
				textprinted = 0;
			}
			if (!context || context->my_field == 2)
				in_xds_mode=0; // Back to normal (CEA 608-8.6.2)
			if (!context) // Not XDS and we don't have a writebuffer, nothing else would have an effect
				continue;
			if (context->last_c1 == hi && context->last_c2 == lo)
			{
				// Duplicate dual code, discard. Correct to do it only in
				// non-XDS, XDS codes shall not be repeated.
				ccx_common_logging.debug_ftn(CCX_DMT_DECODER_608, "Skipping command %02X,%02X Duplicate\n", hi, lo);
				// Ignore only the first repetition
				context->last_c1=-1;
				context->last_c2 = -1;
				continue;
			}
			context->last_c1 = hi;
			context->last_c2 = lo;
			wrote_to_screen = disCommand(hi, lo, context, sub);
			if(sub->got_output)
				break;
		}
		else
		{
			if (in_xds_mode && (context == NULL || context->my_field == 2))
			{
				process_xds_bytes (hi,lo);
				continue;
			}
			if (!context) // No XDS code after this point, and user doesn't want captions.
				continue;

			context->last_c1 = -1;
			context->last_c2 = -1;

			if (hi>=0x20) // Standard characters (always in pairs)
			{
				// Only print if the channel is active
				if (context->channel != context->my_channel)
					continue;

				if( textprinted == 0 )
				{
					ccx_common_logging.debug_ftn(CCX_DMT_DECODER_608, "\n");
					textprinted = 1;
				}

				handle_single(hi, context);
				handle_single(lo, context);
				wrote_to_screen=1;
				context->last_c1 = 0;
				context->last_c2 = 0;
			}

			if (!textprinted && context->channel == context->my_channel)
			{   // Current FTS information after the characters are shown
				ccx_common_logging.debug_ftn(CCX_DMT_DECODER_608, "Current FTS: %s\n", print_mstime(get_fts()));
				//printf("  N:%u", unsigned(fts_now) );
				//printf("  G:%u", unsigned(fts_global) );
				//printf("  F:%d %d %d %d\n",
				//	   current_field, cb_field1, cb_field2, cb_708 );
			}

			if (wrote_to_screen && context->settings->direct_rollup && // If direct_rollup is enabled and
					(context->mode == MODE_FAKE_ROLLUP_1 || // we are in rollup mode, write now.
					 context->mode == MODE_ROLLUP_2 ||
					 context->mode == MODE_ROLLUP_3 ||
					 context->mode == MODE_ROLLUP_4))
			{
				// We don't increase screenfuls_counter here.
				write_cc_buffer(context, sub);
				context->current_visible_start_ms = get_visible_start();
			}
		}
		if (wrote_to_screen && context->cc_to_stdout)
			fflush (stdout);
	} // for
	return i;
}
コード例 #15
0
ファイル: ccextractor.c プロジェクト: MikaYuoadas/ccextractor
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;
}
コード例 #16
0
int set_fts(struct ccx_common_timing_ctx *ctx)
{
	int pts_jump = 0;

	// ES don't have PTS unless GOP timing is used
	if (!ctx->pts_set && ccx_common_timing_settings.is_elementary_stream)
		return CCX_OK;

	// First check for timeline jump (only when min_pts was set (implies sync_pts)).
	int dif = 0;
	if (ctx->pts_set == 2)
	{
		dif=(int) (ctx->current_pts - ctx->sync_pts)/MPEG_CLOCK_FREQ;

		if (ccx_common_timing_settings.disable_sync_check){
			// Disables sync check. Used for several input formats.
			dif = 0;
		}

		if (dif < -0.2 || dif >= max_dif )
		{
			// ATSC specs: More than 3501 ms means missing component
			ccx_common_logging.log_ftn ("\nWarning: Reference clock has changed abruptly (%d seconds filepos=%lld), attempting to synchronize\n", (int) dif, *ccx_common_timing_settings.file_position);
			ccx_common_logging.log_ftn ("Last sync PTS value: %lld\n",ctx->sync_pts);
			ccx_common_logging.log_ftn ("Current PTS value: %lld\n",ctx->current_pts);
			pts_jump = 1;
			pts_big_change = 1;

			// Discard the gap if it is not on an I-frame or temporal reference zero.
			if(ctx->current_tref != 0 && ctx->current_picture_coding_type != CCX_FRAME_TYPE_I_FRAME)
			{
				ctx->fts_now = ctx->fts_max;
				ccx_common_logging.log_ftn ("Change did not occur on first frame - probably a broken GOP\n");
				return CCX_OK;
			}
		}
	}

	// Set min_pts, fts_offset
	if (ctx->pts_set != 0)
	{
		ctx->pts_set = 2;

		// Use this part only the first time min_pts is set. Later treat
		// it as a reference clock change
		if (ctx->current_pts < ctx->min_pts && !pts_jump)
		{
			// If this is the first GOP, and seq 0 was not encountered yet
			// we might reset min_pts/fts_offset again

			ctx->min_pts = ctx->current_pts;

			// Avoid next async test
			ctx->sync_pts = (LLONG)(ctx->current_pts
					-ctx->current_tref*1000.0/current_fps
					*(MPEG_CLOCK_FREQ/1000));

			if(ctx->current_tref == 0)
			{   // Earliest time in GOP.
				ctx->fts_offset = 0;
			}
			else if ( total_frames_count-frames_since_ref_time == 0 )
			{   // If this is the first frame (PES) there cannot be an offset.
				// This part is also reached for dvr-ms/NTSC (RAW) as
				// total_frames_count = frames_since_ref_time = 0 when
				// this is called for the first time.
				ctx->fts_offset = 0;
			}
			else
			{   // It needs to be "+1" because the current frame is
				// not yet counted.
				ctx->fts_offset = (LLONG)((total_frames_count
							-frames_since_ref_time+1)
						*1000.0/current_fps);
			}
			ccx_common_logging.debug_ftn(CCX_DMT_TIME, "\nFirst sync time    PTS: %s %+lldms (time before this PTS)\n",
					print_mstime(ctx->min_pts/(MPEG_CLOCK_FREQ/1000)),
					ctx->fts_offset );
			ccx_common_logging.debug_ftn(CCX_DMT_TIME, "Total_frames_count %u frames_since_ref_time %u\n",
					total_frames_count, frames_since_ref_time);
		}

		// -nosync disables syncing
		if (pts_jump && !ccx_common_timing_settings.no_sync)
		{
			// The current time in the old time base is calculated using
			// sync_pts (set at the beginning of the last GOP) plus the
			// time of the frames since then.
			ctx->fts_offset = ctx->fts_offset
				+ (ctx->sync_pts - ctx->min_pts)/(MPEG_CLOCK_FREQ/1000)
				+ (LLONG) (frames_since_ref_time*1000/current_fps);
			ctx->fts_max = ctx->fts_offset;

			// Start counting again from here
			ctx->pts_set = 1; // Force min to be set again
			ctx->sync_pts2fts_set = 0; // Make note of the new conversion values

			// Avoid next async test - the gap might have occured on
			// current_tref != 0.
			ctx->sync_pts = (LLONG) (ctx->current_pts
					-ctx->current_tref*1000.0/current_fps
					*(MPEG_CLOCK_FREQ/1000));
			// Set min_pts = sync_pts as this is used for fts_now
			ctx->min_pts = ctx->sync_pts;

			ccx_common_logging.debug_ftn(CCX_DMT_TIME, "\nNew min PTS time: %s %+lldms (time before this PTS)\n",
					print_mstime(ctx->min_pts/(MPEG_CLOCK_FREQ/1000)),
					ctx->fts_offset );
		}
	}

	// Set sync_pts, fts_offset
	if(ctx->current_tref == 0)
		ctx->sync_pts = ctx->current_pts;

	// Reset counters
	cb_field1 = 0;
	cb_field2 = 0;
	cb_708 = 0;

	// Avoid wrong "Calc. difference" and "Asynchronous by" numbers
	// for uninitialized min_pts
	if (1) // CFS: Remove or think decent condition
	{
		if ( ctx->pts_set )
		{
			// If pts_set is TRUE we have min_pts
			ctx->fts_now = (LLONG)((ctx->current_pts - ctx->min_pts)/(MPEG_CLOCK_FREQ/1000)
					+ ctx->fts_offset);
			if (!ctx->sync_pts2fts_set)
			{
				ctx->sync_pts2fts_pts = ctx->current_pts;
				ctx->sync_pts2fts_fts = ctx->fts_now;
				ctx->sync_pts2fts_set = 1;
			}
		}
		else
		{
			// No PTS info at all!!
			ccx_common_logging.log_ftn("Set PTS called without any global timestamp set\n");
			return CCX_EINVAL;
		}
	}
	if ( ctx->fts_now > ctx->fts_max )
	{
		ctx->fts_max = ctx->fts_now;
	}
	return CCX_OK;
}
コード例 #17
0
ファイル: ccextractor.c プロジェクト: Arbitrer/ccextractor
int main(int argc, char *argv[])
{
	struct lib_ccx_ctx *ctx;
	struct lib_cc_decode *dec_ctx = NULL;
	int ret = 0;
	enum ccx_stream_mode_enum stream_mode;

	init_options (&ccx_options);

	parse_configuration(&ccx_options);
	ret = parse_parameters (&ccx_options, argc, argv);
	if (ret == EXIT_NO_INPUT_FILES)
	{
		usage ();
		fatal (EXIT_NO_INPUT_FILES, "(This help screen was shown because there were no input files)\n");
	}
	else if (ret == EXIT_WITH_HELP)
	{
		return EXIT_OK;
	}
	else if (ret != EXIT_OK)
	{
		exit(ret);
	}
	// Initialize libraries
	ctx = init_libraries(&ccx_options);
	if (!ctx && errno == ENOMEM)
		fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory\n");
	else if (!ctx && errno == EINVAL)
		fatal (CCX_COMMON_EXIT_BUG_BUG, "Invalid option to CCextractor Library\n");
	else if (!ctx && errno == EPERM)
		fatal (CCX_COMMON_EXIT_FILE_CREATION_FAILED, "Unable to create Output File\n");
	else if (!ctx && errno == EACCES)
		fatal (CCX_COMMON_EXIT_FILE_CREATION_FAILED, "Unable to create Output File\n");
	else if (!ctx)
		fatal (EXIT_NOT_CLASSIFIED, "Unable to create Library Context %d\n",errno);

	int show_myth_banner = 0;

	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 (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");
		}
	}


	time_t start, final;
	time(&start);

	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))
	{
		prepare_for_new_file(ctx);
		stream_mode = ctx->demux_ctx->get_stream_mode(ctx->demux_ctx);
		// 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 (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;
		}

		/* -----------------------------------------------------------------
		MAIN LOOP
		----------------------------------------------------------------- */
		switch (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:
			case CCX_SM_GXF:
#ifdef ENABLE_FFMPEG
			case CCX_SM_FFMPEG:
#endif
				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);
				break;
			case CCX_SM_MCPOODLESRAW:
				mprint ("\rAnalyzing data in McPoodle raw mode\n");
				raw_loop(ctx);
				break;
			case CCX_SM_RCWT:
				mprint ("\rAnalyzing data in CCExtractor's binary format\n");
				rcwt_loop(ctx);
				break;
			case CCX_SM_MYTH:
				mprint ("\rAnalyzing data in MythTV mode\n");
				show_myth_banner = 1;
				myth_loop(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->mp4_cfg, ctx->inputfile[0]);
				if (ccx_options.print_file_reports)
					print_file_report(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;
		}

#if 0
		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 && 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");
		}
#endif

		list_for_each_entry(dec_ctx, &ctx->dec_ctx_head, list, struct lib_cc_decode)
		{
			mprint("\n");
			dbg_print(CCX_DMT_DECODER_608, "\nTime stamps after last caption block was written:\n");
			dbg_print(CCX_DMT_DECODER_608, "GOP: %s	  \n", print_mstime(gop_time.ms) );
	
			dbg_print(CCX_DMT_DECODER_608, "GOP: %s (%+3dms incl.)\n",
				print_mstime((LLONG)(gop_time.ms
				-first_gop_time.ms
				+get_fts_max(dec_ctx->timing)-fts_at_gop_start)),
				(int)(get_fts_max(dec_ctx->timing)-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(dec_ctx->timing)));

			if (dec_ctx->codec == CCX_CODEC_ATSC_CC)
			{
				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->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]);
			}
			// Add one frame as fts_max marks the beginning of the last frame,
			// but we need the end.
			dec_ctx->timing->fts_global += dec_ctx->timing->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)
				dec_ctx->timing->fts_global += cb_field1*1001/3;
			else if (cb_field2!=0)
				dec_ctx->timing->fts_global += cb_field2*1001/3;
			else
				dec_ctx->timing->fts_global += cb_708*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;
			dec_ctx->timing->fts_now = 0;
			dec_ctx->timing->fts_max = 0;
		}

		if(is_decoder_processed_enough(ctx) == CCX_TRUE)
			break;
	} // file loop
	close_input_file(ctx);

	prepare_for_new_file (ctx); // To reset counters used by handle_end_of_data()


	time (&final);

	long proc_time=(long) (final-start);
	mprint ("\rDone, processing time = %ld seconds\n", proc_time);
#if 0
	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);
	}
#endif
	dbg_print(CCX_DMT_708, "[CEA-708] The 708 decoder was reset [%d] times.\n", ctx->freport.data_from_708->reset_count);

	if (is_decoder_processed_enough(ctx) == CCX_TRUE)
	{
		mprint ("\rNote: Processing was cancelled before all data was processed because\n");
		mprint ("\rone or more user-defined limits were reached.\n");
	} 
	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;
}
コード例 #18
0
ファイル: timing.cpp プロジェクト: ScandalCorp/ccextractor
void set_fts(void)
{
    int pts_jump = 0;

    // ES don't have PTS unless GOP timing is used
    if (!pts_set && stream_mode==SM_ELEMENTARY_OR_NOT_FOUND)
        return;

    // First check for timeline jump (only when min_pts was
    // set (implies sync_pts).
    int dif = 0;
    if (pts_set == 2)
    {
        dif=(int) (current_pts-sync_pts)/MPEG_CLOCK_FREQ;

        // Used to distinguish gaps with missing caption information from
        // jumps in the timeline.  (Currently only used for dvr-ms/NTSC
        // recordings)
        if ( CaptionGap )
            dif = 0;

        // 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 (stream_mode)
        {
            case SM_MCPOODLESRAW:
            case SM_RCWT:
			case SM_MP4:
                dif = 0;
                break;
            default:
                break;
        }

        if (dif < -0.2 || dif >=5 )
        {
            // ATSC specs: More than 3501 ms means missing component
            mprint ("\nWarning: Reference clock has changed abruptly (%d seconds), attempting to synchronize\n", (int) dif);
            mprint ("Last sync PTS value: %lld\n",sync_pts);
            mprint ("Current PTS value: %lld\n",current_pts);
            pts_jump = 1;
            pts_big_change=1;

            // Discard the gap if it is not on an I-frame or temporal reference
            // zero.
            if(current_tref != 0 && current_picture_coding_type != I_FRAME)
            {
                fts_now = fts_max;
                mprint ("Change did not occur on first frame - probably a broken GOP\n");
                return;
            }
        }
    }

    // Set min_pts, fts_offset
    if (pts_set!=0)
    {
        pts_set=2;

        // Use this part only the first time min_pts is set. Later treat
        // it as a reference clock change
        if (current_pts<min_pts && !pts_jump)
        {
            // If this is the first GOP, and seq 0 was not encountered yet
            // we might reset min_pts/fts_offset again

            min_pts=current_pts;

            // Avoid next async test
            sync_pts = LLONG(current_pts
                             -current_tref*1000.0/current_fps
                             *(MPEG_CLOCK_FREQ/1000));

            if(current_tref == 0)
            {   // Earliest time in GOP.
                fts_offset = 0;
            }
            else if ( total_frames_count-frames_since_ref_time == 0 )
            {   // If this is the first frame (PES) there cannot be an offset.
                // This part is also reached for dvr-ms/NTSC (RAW) as 
                // total_frames_count = frames_since_ref_time = 0 when
                // this is called for the first time.
                fts_offset = 0;
            }
            else
            {   // It needs to be "+1" because the current frame is
                // not yet counted.
                fts_offset = LLONG((total_frames_count
                                    -frames_since_ref_time+1)
                                   *1000.0/current_fps);
            }
            dbg_print(DMT_TIME, "\nFirst sync time    PTS: %s %+lldms (time before this PTS)\n",
                   print_mstime(min_pts/(MPEG_CLOCK_FREQ/1000)),
                   fts_offset );
            dbg_print(DMT_TIME, "Total_frames_count %u frames_since_ref_time %u\n",
                   total_frames_count, frames_since_ref_time);
        }

        // -nosync diasbles syncing
        if (pts_jump && !nosync)
        {
            // The current time in the old time base is calculated using
            // sync_pts (set at the beginning of the last GOP) plus the
            // time of the frames since then.
            fts_offset = fts_offset
                + (sync_pts-min_pts)/(MPEG_CLOCK_FREQ/1000)
                + LLONG(frames_since_ref_time*1000/current_fps);
            fts_max = fts_offset;

            // Start counting again from here
            pts_set=1; // Force min to be set again

            // Avoid next async test - the gap might have occured on
            // current_tref != 0.
            sync_pts = LLONG(current_pts
                             -current_tref*1000.0/current_fps
                             *(MPEG_CLOCK_FREQ/1000));
            // Set min_pts = sync_pts as this is used for fts_now
            min_pts = sync_pts;

            dbg_print(DMT_TIME, "\nNew min PTS time: %s %+lldms (time before this PTS)\n",
                   print_mstime(min_pts/(MPEG_CLOCK_FREQ/1000)),
                   fts_offset );
        }
    }

    // Set sync_pts, fts_offset
    if(current_tref == 0)
        sync_pts = current_pts;

    // Reset counters
    cb_field1 = 0;
    cb_field2 = 0;
    cb_708 = 0;

    // Avoid wrong "Calc. difference" and "Asynchronous by" numbers
    // for uninitialized min_pts
	if (1) // CFS: Remove or think decent condition
	{
		if ( pts_set )
		{
			// If pts_set is TRUE we have min_pts
			fts_now = LLONG((current_pts-min_pts)/(MPEG_CLOCK_FREQ/1000)
							+ fts_offset);
		}
		else
		{
			// No PTS info at all!!
			fatal(EXIT_BUG_BUG,
				  "No PTS info. Please write bug report.");
		}
	}
    if ( fts_now > fts_max )
    {
        fts_max = fts_now;
    }
}
コード例 #19
0
ファイル: sequencing.cpp プロジェクト: MrMdR/julapy
int do_cb (unsigned char *cc_block)
{
    unsigned char cc_valid = (*cc_block & 4) >>2;
    unsigned char cc_type = *cc_block & 3;

    int timeok = 1;

    if ( fix_padding
         && cc_valid==0 && cc_type <= 1 // Only fix NTSC packets
         && cc_block[1]==0 && cc_block[2]==0 )
    {
        /* Padding */
        cc_valid=1;
        cc_block[1]=0x80;
        cc_block[2]=0x80;
    }

    // Print raw data with FTS.
    if (debug_cbraw)
        printf("%s  %02X:%02X:%02X", print_mstime(fts_now + fts_global),
               cc_block[0], cc_block[1], cc_block[2]);

    /* In theory the writercwtdata() function could return early and not
     * go through the 608/708 cases below.  We do that to get accurate
     * counts for cb_field1, cb_field2 and cb_708.
     * Note that printdata() and do_708() must not be called for
     * the OF_RCWT case. */

    if (cc_valid || cc_type==3)
    {
        cc_stats[cc_type]++;

        switch (cc_type)
        {
        case 0:
            if (debug_cbraw)
                printf("    %s   ..   ..\n",  debug_608toASC( cc_block, 0));

            current_field=1;
            saw_caption_block = 1;

            if (extraction_start.set && get_fts() < extraction_start.time_in_ms)
                timeok = 0;
            if (extraction_end.set && get_fts() > extraction_end.time_in_ms)
            {
                timeok = 0;
                processed_enough=1;
            }
            if (timeok)
            {
                if(write_format!=OF_RCWT)
                    printdata (cc_block+1,2,0,0);
                else
                    writercwtdata(cc_block);
            }
            cb_field1++;
            break;
        case 1:
            if (debug_cbraw)
                printf("    ..   %s   ..\n",  debug_608toASC( cc_block, 1));

            current_field=2;
            saw_caption_block = 1;

            if (extraction_start.set && get_fts() < extraction_start.time_in_ms)
                timeok = 0;
            if (extraction_end.set && get_fts() > extraction_end.time_in_ms)
            {
                timeok = 0;
                processed_enough=1;
            }
            if (timeok)
            {
                if(write_format!=OF_RCWT)
                    printdata (0,0,cc_block+1,2);
                else
                    writercwtdata(cc_block);
            }
            cb_field2++;
            break;
        case 2: //EIA-708
            // DTVCC packet data
            // Fall through
        case 3: //EIA-708
            if (debug_cbraw)
                printf("    ..   ..   DD\n");

            // DTVCC packet start
            current_field=3;

            if (extraction_start.set && get_fts() < extraction_start.time_in_ms)
                timeok = 0;
            if (extraction_end.set && get_fts() > extraction_end.time_in_ms)
            {
                timeok = 0;
                processed_enough=1;
            }
            char temp[4];
            temp[0]=cc_valid;
            temp[1]=cc_type;
            temp[2]=cc_block[1];
            temp[3]=cc_block[2];
            if (timeok)
            {
                if(write_format!=OF_RCWT)
                   do_708 ((const unsigned char *) temp, 4);
                else
                    writercwtdata(cc_block);
            }
            cb_708++;
            // Check for bytes read
            // printf ("Warning: Losing EIA-708 data!\n");
            break;
        default:
            fatal(EXIT_BUG_BUG, "Cannot be reached!");
        } // switch (cc_type)
    } // cc_valid
    else
    {
        if (debug_cbraw)
            printf("    ..   ..   ..\n");
        if (debug_verbose)
            printf("Found !(cc_valid || cc_type==3) - ignore this block\n");
    }

    return 1;
}