Exemplo n.º 1
0
LLONG get_data(struct lib_ccx_ctx *ctx, struct wtv_chunked_buffer *cb, struct demuxer_data *data)
{
	static int video_streams[32];
	static int alt_stream; //Stream to use for timestamps if the cc stream has broken timestamps
	static int use_alt_stream = 0;
	static int num_streams = 0;
	struct lib_cc_decode *dec_ctx = update_decoder_list(ctx);

	while(1)
	{
		int bytesread = 0;
		// Read the 32 bytes containing the GUID and length and stream_id info
		get_sized_buffer(ctx->demux_ctx, cb, 32);
		if(cb->buffer == NULL)
			return CCX_EOF;

		uint8_t guid[16];
		memcpy(&guid, cb->buffer, 16); // Read the GUID
		int x;
		for(x=0; x<16; x++)
			dbg_print(CCX_DMT_PARSE, "%02X ", guid[x]);
		dbg_print(CCX_DMT_PARSE, "\n");
		uint32_t len;
		memcpy(&len, cb->buffer+16, 4); // Read the length
		len-=32;
		dbg_print(CCX_DMT_PARSE, "len %X\n", len);
		uint32_t pad;
		pad = len%8==0 ? 0 : 8- (len % 8);  // Calculate the padding to add to the length
		// to get to the next GUID
		dbg_print(CCX_DMT_PARSE, "pad %X\n", pad);
		uint32_t stream_id;
		memcpy(&stream_id, cb->buffer+20, 4);
		stream_id = stream_id & 0x7f;       // Read and calculate the stream_id
		dbg_print(CCX_DMT_PARSE, "stream_id: 0x%X\n", stream_id);

		for(x=0; x<num_streams; x++)
			dbg_print(CCX_DMT_PARSE, "video stream_id: %X\n", video_streams[x]);
		if( !memcmp(guid, WTV_EOF, 16 ))
		{
			// This is the end of the data in this file
			// read the padding at the end of the file
			// and return one more byte
			dbg_print(CCX_DMT_PARSE, "WTV EOF\n");
			uint8_t *parsebuf;
			parsebuf = (uint8_t*)malloc(1024);
			do {
				buffered_read(ctx->demux_ctx, parsebuf, 1024);
				ctx->demux_ctx->past+=1024;
			} while (result==1024);
			ctx->demux_ctx->past+=result;
			free(parsebuf);
			free(cb->buffer);
			cb->buffer=NULL;
			//return one more byte so the final percentage is shown correctly
			*(data->buffer+data->len)=0x00;
			data->len++;
			return CCX_EOF;
		}
		if( !memcmp(guid, WTV_STREAM2, 16 ) )
		{
			// The WTV_STREAM2 GUID appares near the start of the data dir
			// It maps stream_ids to the type of stream
			dbg_print(CCX_DMT_PARSE, "WTV STREAM2\n");
			get_sized_buffer(ctx->demux_ctx, cb, 0xc+16);
			if(cb->buffer==NULL)
				return CCX_EOF;
			static unsigned char stream_type[16];
			memcpy(&stream_type, cb->buffer+0xc, 16); //Read the stream type GUID
			const void *stream_guid;
			if(ccx_options.wtvmpeg2)
				stream_guid = WTV_STREAM_VIDEO; // We want mpeg2 data if the user set -wtvmpeg2
			else
				stream_guid = WTV_STREAM_MSTVCAPTION; // Otherwise we want the MSTV captions stream
			if(!memcmp(stream_type, stream_guid, 16 ) )
			{
				video_streams[num_streams]=stream_id; // We keep a list of stream ids
				num_streams++;                        // Even though there should only be 1
			}
			if (memcmp(stream_type, WTV_STREAM_AUDIO, 16))
				alt_stream = stream_id;
			len-=28;
		}
		if (!memcmp(guid, WTV_TIMING, 16) && ((use_alt_stream < WTV_CC_TIMESTAMP_MAGIC_THRESH && check_stream_id(stream_id, video_streams, num_streams))
					|| (use_alt_stream == WTV_CC_TIMESTAMP_MAGIC_THRESH && stream_id == alt_stream)))
		{
			// The WTV_TIMING GUID contains a timestamp for the given stream_id
			dbg_print(CCX_DMT_PARSE, "WTV TIMING\n");
			get_sized_buffer(ctx->demux_ctx, cb, 0x8+0x8);
			if(cb->buffer==NULL)
				return CCX_EOF;
			int64_t time;
			memcpy(&time, cb->buffer+0x8, 8); // Read the timestamp
			dbg_print(CCX_DMT_PARSE, "TIME: %ld\n", time);
			if (time != -1 && time != WTV_CC_TIMESTAMP_MAGIC) { // Ignore -1 timestamps
				set_current_pts(dec_ctx->timing, time_to_pes_time(time));
				frames_since_ref_time = 0;
				set_fts(dec_ctx->timing);
			}
			else if (time == WTV_CC_TIMESTAMP_MAGIC && stream_id != alt_stream) {
				use_alt_stream++;
				mprint("WARNING: %i WTV_CC_TIMESTAMP_MAGIC detected in cc timestamps. \n", use_alt_stream);
				if (use_alt_stream == WTV_CC_TIMESTAMP_MAGIC_THRESH)
					mprint("WARNING: WTV_CC_TIMESTAMP_MAGIC_THRESH reached. Switching to alt timestamps!\n");
			}
			len-=16;
		}
		if( !memcmp(guid, WTV_DATA, 16 )
				&& check_stream_id(stream_id, video_streams, num_streams) && dec_ctx->timing->current_pts != 0
				&& (ccx_options.wtvmpeg2 || (!ccx_options.wtvmpeg2 && len==2)))
		{
			// This is the data for a stream we want to process
			dbg_print(CCX_DMT_PARSE, "\nWTV DATA\n");
			get_sized_buffer(ctx->demux_ctx, cb, len);
			if(cb->buffer==NULL)
				return CCX_EOF;
			memcpy(data->buffer+data->len, cb->buffer, len);
			data->len+=result;
			bytesread+=(int) len;
			frames_since_ref_time++;
			set_fts(dec_ctx->timing);
			if(pad>0)
			{ //Make sure we skip any padding too, since we are returning here
				skip_sized_buffer(ctx->demux_ctx, cb, pad);
			}
			return bytesread;
		}
		if(len+pad>0)
		{
			// skip any remaining data
			// For any unhandled GUIDs this will be len+pad
			// For others it will just be pad
			skip_sized_buffer(ctx->demux_ctx, cb, len+pad);
		}
	}
}
Exemplo n.º 2
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;
}
Exemplo n.º 3
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;
}
Exemplo n.º 4
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;
}