Example #1
0
/* 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 = ctx->timing->fts_now + ctx->timing->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_static(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;
    }
Example #2
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 lib_cc_decode *ctx, struct bitstream *esstream, struct cc_subtitle *sub)
{
	debug("Read PIC Info\n");

	// We only get here after seeing that start code
	if (next_u32(esstream) != 0x00010000) // LSB first (0x00000100)
		fatal(CCX_COMMON_EXIT_BUG_BUG, "In read_pic_info: next_u32(esstream) != 0x00010000. Please file a bug report in GitHub.\n");

	// 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(ctx, esstream);
	pic_coding_ext(ctx, esstream);

	if (esstream->error)
		return 0;

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

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

	ctx->timing->current_tref = ctx->temporal_reference;
	ctx->timing->current_picture_coding_type = ctx->picture_coding_type;

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

	dbg_print(CCX_DMT_VIDES, "  t:%d r:%d p:%d", ctx->top_field_first,
			ctx->repeat_first_field, ctx->progressive_frame);
	dbg_print(CCX_DMT_VIDES, "  FTS: %s\n", print_mstime_static(get_fts(ctx->timing, ctx->current_field)));

	// 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(ctx->temporal_reference == 0)
	{
		ctx->last_gop_length = ctx->maxtref + 1;
		ctx->maxtref = ctx->temporal_reference;

		// frames_since_ref_time is used in set_fts()

		if( ctx->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 = ctx->frames_since_last_gop; // Should this be 0?
		}
		else
		{
			// No GOP header, use the current values
			fts_at_gop_start = get_fts(ctx->timing, ctx->current_field);
			frames_since_ref_time = 0;
		}

		if (ccx_options.debug_mask & CCX_DMT_TIME)
		{
			dbg_print(CCX_DMT_TIME, "\nNew temporal reference:\n");
			print_debug_timing(ctx->timing);
		}

		ctx->saw_gop_header = 0; // Reset the value
	}

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

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

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

	ctx->total_pulldownframes += extraframe;
	total_frames_count += 1+extraframe;
	ctx->frames_since_last_gop += 1+extraframe;
	frames_since_ref_time += 1+extraframe;

	debug("Read PIC Info - processed\n\n");

	return 1;
}
Example #3
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 lib_cc_decode *ctx, struct bitstream *esstream, struct cc_subtitle *sub)
{
	debug("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(CCX_COMMON_EXIT_BUG_BUG, "gop_header: read_u32(esstream) != 0xB8010000. Please file a bug report in GitHub.\n");

	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 (ctx->has_ccdata_buffered)
		{
			process_hdcc(ctx, sub);
		}

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

		// Report synchronization jumps between GOPs. Warn if there
		// are 20% or more deviation.
		if ( (ccx_options.debug_mask & CCX_DMT_TIME)
				&& ((gtc.ms - gop_time.ms // more than 20% longer
						> ctx->frames_since_last_gop*1000.0/current_fps*1.2)
					||
					(gtc.ms - gop_time.ms // or 20% shorter
					 < ctx->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_static(gop_time.ms));
			mprint("  +  %s (%uF)",
					print_mstime_static((LLONG) (ctx->frames_since_last_gop
							*1000.0/current_fps)),
					ctx->frames_since_last_gop);
			mprint("  !=  (new) %s\n",
					print_mstime_static(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
				ctx->timing->fts_fc_offset = 0;
				// first_gop_time.ms stays unchanged
			}
			else
			{
				ctx->timing->fts_fc_offset = (LLONG) ((total_frames_count+1)
						*1000.0/current_fps);
				// Compensate for those written before
				first_gop_time.ms -= ctx->timing->fts_fc_offset;
			}

			dbg_print(CCX_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),
					ctx->timing->fts_fc_offset);
		}

		gop_time = gtc;

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

		// If we use GOP timing, reconstruct the PTS from the GOP
		if (ccx_options.use_gop_as_pts==1)
		{
			set_current_pts(ctx->timing, gtc.ms*(MPEG_CLOCK_FREQ/1000));
			ctx->timing->current_tref = 0;
			frames_since_ref_time = 0;
			set_fts(ctx->timing);
			fts_at_gop_start = get_fts_max(ctx->timing);
		}
		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(ctx->timing) + (LLONG) (1000.0/current_fps);
		}

		if (ccx_options.debug_mask & CCX_DMT_TIME)
		{
			dbg_print(CCX_DMT_TIME, "\nNew GOP:\n");
			dbg_print(CCX_DMT_TIME, "\nDrop frame flag: %u:\n", drop_frame_flag);
			print_debug_timing(ctx->timing);
		}
	}

	return 1;
}