void write_char(const unsigned char c, ccx_decoder_608_context *context)
{
	if (context->mode != MODE_TEXT)
	{
		struct eia608_screen *use_buffer = get_writing_buffer(context);
		/* printf ("\rWriting char [%c] at %s:%d:%d\n",c,
		use_buffer == &wb->data608->buffer1?"B1":"B2",
		wb->data608->cursor_row,wb->data608->cursor_column); */
		use_buffer->characters[context->cursor_row][context->cursor_column] = c;
		use_buffer->colors[context->cursor_row][context->cursor_column] = context->current_color;
		use_buffer->fonts[context->cursor_row][context->cursor_column] = context->font;
		use_buffer->row_used[context->cursor_row] = 1;

		if (use_buffer->empty)
		{
			if (MODE_POPON != context->mode)
				context->current_visible_start_ms = get_visible_start();
		}
		use_buffer->empty=0;

		if (context->cursor_column<CCX_DECODER_608_SCREEN_WIDTH - 1)
			context->cursor_column++;
		if (context->ts_start_of_current_line == -1)
			context->ts_start_of_current_line = get_fts();
		context->ts_last_char_received = get_fts();
	}
}
void try_to_add_end_credits(struct encoder_ctx *context, struct ccx_s_write *out)
{
	LLONG window, length, st, end;
	if (out->fh == -1)
		return;
	window=get_fts()-context->last_displayed_subs_ms-1;
	if (window < context->endcreditsforatleast.time_in_ms) // Won't happen, window is too short
		return;
	length=context->endcreditsforatmost.time_in_ms > window ?
		window : context->endcreditsforatmost.time_in_ms;

	st=get_fts()-length-1;
	end=get_fts();

	switch (context->write_format)
	{
		case CCX_OF_SRT:
			write_stringz_as_srt(context->end_credits_text, context, st, end);
			break;
        case CCX_OF_WEBVTT:
            write_stringz_as_webvtt(context->end_credits_text, context, st, end);
            break;
		case CCX_OF_SAMI:
			write_stringz_as_sami(context->end_credits_text, context, st, end);
			break;
		case CCX_OF_SMPTETT:
			write_stringz_as_smptett(context->end_credits_text, context, st, end);
			break ;
		default:
			// Do nothing for the rest
			break;
	}
}
int write_xds_string(struct cc_subtitle *sub, struct ccx_decoders_xds_context *ctx, char *p, size_t len)
{
	struct eia608_screen *data = NULL;
	data = (struct eia608_screen *) realloc(sub->data,( sub->nb_data + 1 ) * sizeof(*data));
	if (!data)
	{
		freep(&sub->data);
		sub->nb_data = 0;
		ccx_common_logging.log_ftn("No Memory left");
		return -1;
	}
	else
	{
		sub->data = data;
		data = (struct eia608_screen *)sub->data + sub->nb_data;
		data->format = SFORMAT_XDS;
		data->start_time = ts_start_of_xds;
		data->end_time =  get_fts(ctx->timing, 2);
		data->xds_str = p;
		data->xds_len = len;
		data->cur_xds_packet_class = ctx->cur_xds_packet_class;
		sub->nb_data++;
		sub->type = CC_608;
		sub->got_output = 1;
	}

	return 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;
	return fts;
}
/* 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)
		return fts;
	return minimum_fts+1;
}
/* 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;
}
/* 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;
}
/* 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;
}
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(get_fts()));
}
Exemple #10
0
void dinit_libraries( struct lib_ccx_ctx **ctx)
{
	struct lib_ccx_ctx *lctx = *ctx;
	struct encoder_ctx *enc_ctx;
	struct lib_cc_decode *dec_ctx;
	struct lib_cc_decode *dec_ctx1;
	int i;
	list_for_each_entry_safe(dec_ctx, dec_ctx1, &lctx->dec_ctx_head, list, struct lib_cc_decode)
	{
		LLONG cfts;
		if (dec_ctx->codec == CCX_CODEC_DVB)
			dvbsub_close_decoder(&dec_ctx->private_data);
		//Test memory for teletext
		else if (dec_ctx->codec == CCX_CODEC_TELETEXT)
			telxcc_close(&dec_ctx->private_data, &dec_ctx->dec_sub);
		else if (dec_ctx->codec == CCX_CODEC_ISDB_CC)
			delete_isdb_decoder(&dec_ctx->private_data);

		flush_cc_decode(dec_ctx, &dec_ctx->dec_sub);
		cfts = get_fts(dec_ctx->timing, dec_ctx->current_field);
		enc_ctx = get_encoder_by_pn(lctx, dec_ctx->program_number);
		if (enc_ctx && dec_ctx->dec_sub.got_output == CCX_TRUE)
		{
			encode_sub(enc_ctx, &dec_ctx->dec_sub);
			dec_ctx->dec_sub.got_output = CCX_FALSE;
		}
		list_del(&dec_ctx->list);
		dinit_cc_decode(&dec_ctx);
		if (enc_ctx)
		{
			list_del(&enc_ctx->list);
			dinit_encoder(&enc_ctx, cfts);
		}
	}

	// free EPG memory
	EPG_free(lctx);
	freep(&lctx->freport.data_from_608);
	freep(&lctx->freport.data_from_708);
	ccx_demuxer_delete(&lctx->demux_ctx);
	dinit_decoder_setting(&lctx->dec_global_setting);
	freep(&ccx_options.enc_cfg.output_filename);
	freep(&lctx->basefilename);
	freep(&lctx->pesheaderbuf);
	for(i = 0;i < lctx->num_input_files;i++)
		freep(&lctx->inputfile[i]);
	freep(&lctx->inputfile);
	freep(ctx);
}
int write_cc_bitmap_as_transcript(struct cc_subtitle *sub, struct encoder_ctx *context)
{
	int ret = 0;
#ifdef ENABLE_OCR
	struct cc_bitmap* rect;

	unsigned h1,m1,s1,ms1;
	unsigned h2,m2,s2,ms2;

	LLONG start_time, end_time;

	if (context->prev_start != -1 && (sub->flags & SUB_EOD_MARKER))
	{
		start_time = context->prev_start + context->subs_delay;
		end_time = sub->start_time - 1;
	}
	else if ( !(sub->flags & SUB_EOD_MARKER))
	{
		start_time = sub->start_time + context->subs_delay;
		end_time = sub->end_time - 1;
	}

	if(sub->nb_data == 0 )
		return ret;
	rect = sub->data;

	if ( sub->flags & SUB_EOD_MARKER )
		context->prev_start =  sub->start_time;


	if (rect[0].ocr_text && *(rect[0].ocr_text))
	{
		if (context->prev_start != -1 || !(sub->flags & SUB_EOD_MARKER))
		{
			char *token = NULL;
			token = strtok(rect[0].ocr_text ,"\r\n");
			while (token)
			{

				if (context->transcript_settings->showStartTime)
				{
					char buf1[80];
					if (context->transcript_settings->relativeTimestamp)
					{
						millis_to_date(start_time + context->subs_delay, buf1, context->date_format, context->millis_separator);
						fdprintf(context->out->fh, "%s|", buf1);
					}
					else
					{
						mstotime(start_time + context->subs_delay, &h1, &m1, &s1, &ms1);
						time_t start_time_int = (start_time + context->subs_delay) / 1000;
						int start_time_dec = (start_time + context->subs_delay) % 1000;
						struct tm *start_time_struct = gmtime(&start_time_int);
						strftime(buf1, sizeof(buf1), "%Y%m%d%H%M%S", start_time_struct);
						fdprintf(context->out->fh, "%s%c%03d|", buf1,context->millis_separator,start_time_dec);
					}
				}

				if (context->transcript_settings->showEndTime)
				{
					char buf2[80];
					if (context->transcript_settings->relativeTimestamp)
					{
						millis_to_date(end_time, buf2, context->date_format, context->millis_separator);
						fdprintf(context->out->fh, "%s|", buf2);
					}
					else
					{
						mstotime(get_fts() + context->subs_delay, &h2, &m2, &s2, &ms2);
						time_t end_time_int = end_time / 1000;
						int end_time_dec = end_time % 1000;
						struct tm *end_time_struct = gmtime(&end_time_int);
						strftime(buf2, sizeof(buf2), "%Y%m%d%H%M%S", end_time_struct);
						fdprintf(context->out->fh, "%s%c%03d|", buf2,context->millis_separator,end_time_dec);
					}
				}
				if (context->transcript_settings->showCC)
				{
					fdprintf(context->out->fh,"%s|",language[sub->lang_index]);
				}
				if (context->transcript_settings->showMode)
				{
					fdprintf(context->out->fh,"DVB|");
				}
				fdprintf(context->out->fh,"%s\n",token);
				token = strtok(NULL,"\r\n");

			}

		}
	}
#endif

	sub->nb_data = 0;
	freep(&sub->data);
	return ret;

}
void write_cc_line_as_transcript2(struct eia608_screen *data, struct encoder_ctx *context, int line_number)
{
	int ret = 0;
	unsigned int h1,m1,s1,ms1;
	unsigned int h2,m2,s2,ms2;
	LLONG start_time = data->start_time;
	LLONG end_time = data->end_time;
	if (context->sentence_cap)
	{
		capitalize (line_number,data);
		correct_case(line_number,data);
	}
	int length = get_decoder_str_basic (context->subline, data->characters[line_number], context->trim_subs, context->encoding);
	if (context->encoding!=CCX_ENC_UNICODE)
	{
		dbg_print(CCX_DMT_DECODER_608, "\r");
		dbg_print(CCX_DMT_DECODER_608, "%s\n",context->subline);
	}
	if (length>0)
	{
		if (data->start_time == -1)
		{
			// CFS: Means that the line has characters but we don't have a timestamp for the first one. Since the timestamp
			// is set for example by the write_char function, it possible that we don't have one in empty lines (unclear)
			// For now, let's not consider this a bug as before and just return.
			// fatal (EXIT_BUG_BUG, "Bug in timedtranscript (ts_start_of_current_line==-1). Please report.");
			return;
		}

		if (context->transcript_settings->showStartTime){
			char buf1[80];
			if (context->transcript_settings->relativeTimestamp){
				millis_to_date(start_time + context->subs_delay, buf1, context->date_format, context->millis_separator);
				fdprintf(context->out->fh, "%s|", buf1);
			}
			else {
				mstotime(start_time + context->subs_delay, &h1, &m1, &s1, &ms1);
				time_t start_time_int = (start_time + context->subs_delay) / 1000;
				int start_time_dec = (start_time + context->subs_delay) % 1000;
				struct tm *start_time_struct = gmtime(&start_time_int);
				strftime(buf1, sizeof(buf1), "%Y%m%d%H%M%S", start_time_struct);
				fdprintf(context->out->fh, "%s%c%03d|", buf1,context->millis_separator,start_time_dec);
			}
		}

		if (context->transcript_settings->showEndTime){
			char buf2[80];
			if (context->transcript_settings->relativeTimestamp){
				millis_to_date(end_time, buf2, context->date_format, context->millis_separator);
				fdprintf(context->out->fh, "%s|", buf2);
			}
			else {
				mstotime(get_fts() + context->subs_delay, &h2, &m2, &s2, &ms2);
				time_t end_time_int = (end_time + context->subs_delay) / 1000;
				int end_time_dec = (end_time + context->subs_delay) % 1000;
				struct tm *end_time_struct = gmtime(&end_time_int);
				strftime(buf2, sizeof(buf2), "%Y%m%d%H%M%S", end_time_struct);
				fdprintf(context->out->fh, "%s%c%03d|", buf2,context->millis_separator,end_time_dec);
			}
		}

		if (context->transcript_settings->showCC){
			fdprintf(context->out->fh, "CC%d|", data->my_field == 1 ? data->channel : data->channel + 2); // Data from field 2 is CC3 or 4
		}
		if (context->transcript_settings->showMode){
			const char *mode = "???";
			switch (data->mode)
			{
			case MODE_POPON:
				mode = "POP";
				break;
			case MODE_FAKE_ROLLUP_1:
				mode = "RU1";
				break;
			case MODE_ROLLUP_2:
				mode = "RU2";
				break;
			case MODE_ROLLUP_3:
				mode = "RU3";
				break;
			case MODE_ROLLUP_4:
				mode = "RU4";
				break;
			case MODE_TEXT:
				mode = "TXT";
				break;
			case MODE_PAINTON:
				mode = "PAI";
				break;
			}
			fdprintf(context->out->fh, "%s|", mode);
		}

		ret = write(context->out->fh, context->subline, length);
		if(ret < length)
		{
			mprint("Warning:Loss of data\n");
		}

		ret = write(context->out->fh, encoded_crlf, encoded_crlf_length);
		if(ret <  encoded_crlf_length)
		{
			mprint("Warning:Loss of data\n");
		}
	}
	// fprintf (wb->fh,encoded_crlf);
}
int write_cc_subtitle_as_transcript(struct cc_subtitle *sub, struct encoder_ctx *context)
{
	int length;
	int ret = 0;
	unsigned int h1,m1,s1,ms1;
	unsigned int h2,m2,s2,ms2;
	LLONG start_time;
	LLONG end_time;
	char *str;
	struct cc_subtitle *osub = sub;
	struct cc_subtitle *lsub = sub;

	while(sub)
	{
		if(sub->type == CC_TEXT)
		{
			start_time = sub->start_time;
			end_time = sub->end_time;
		}
		if (context->sentence_cap)
		{
			//TODO capitalize (line_number,data);
			//TODO correct_case(line_number,data);
		}

		str = sub->data;
		length = strlen(str);
		if (context->encoding!=CCX_ENC_UNICODE)
		{
			dbg_print(CCX_DMT_DECODER_608, "\r");
			dbg_print(CCX_DMT_DECODER_608, "%s\n", str);
		}

		if (length>0)
		{
			if (start_time == -1)
			{
				// CFS: Means that the line has characters but we don't have a timestamp for the first one. Since the timestamp
				// is set for example by the write_char function, it possible that we don't have one in empty lines (unclear)
				// For now, let's not consider this a bug as before and just return.
				// fatal (EXIT_BUG_BUG, "Bug in timedtranscript (ts_start_of_current_line==-1). Please report.");
				return 0;
			}

			if (context->transcript_settings->showStartTime){
				char buf1[80];
				if (context->transcript_settings->relativeTimestamp){
					millis_to_date(start_time + context->subs_delay, buf1, context->date_format, context->millis_separator);
					fdprintf(context->out->fh, "%s|", buf1);
				}
				else {
					mstotime(start_time + context->subs_delay, &h1, &m1, &s1, &ms1);
					time_t start_time_int = (start_time + context->subs_delay) / 1000;
					int start_time_dec = (start_time + context->subs_delay) % 1000;
					struct tm *start_time_struct = gmtime(&start_time_int);
					strftime(buf1, sizeof(buf1), "%Y%m%d%H%M%S", start_time_struct);
					fdprintf(context->out->fh, "%s%c%03d|", buf1,context->millis_separator,start_time_dec);
				}
			}

			if (context->transcript_settings->showEndTime){
				char buf2[80];
				if (context->transcript_settings->relativeTimestamp){
					millis_to_date(end_time, buf2, context->date_format, context->millis_separator);
					fdprintf(context->out->fh, "%s|", buf2);
				}
				else {
					mstotime(get_fts() + context->subs_delay, &h2, &m2, &s2, &ms2);
					time_t end_time_int = (end_time + context->subs_delay) / 1000;
					int end_time_dec = (end_time + context->subs_delay) % 1000;
					struct tm *end_time_struct = gmtime(&end_time_int);
					strftime(buf2, sizeof(buf2), "%Y%m%d%H%M%S", end_time_struct);
					fdprintf(context->out->fh, "%s%c%03d|", buf2,context->millis_separator,end_time_dec);
				}
			}

			if (context->transcript_settings->showCC) {
				if(context->in_fileformat == 2 )
					fdprintf(context->out->fh, sub->info);
				else
					//TODO, data->my_field == 1 ? data->channel : data->channel + 2); // Data from field 2 is CC3 or 4
					fdprintf(context->out->fh, "CC?|");
			}
			if (context->transcript_settings->showMode){
				fdprintf(context->out->fh, "%s|", sub->mode);
			}
			ret = write(context->out->fh, str, length);
			if(ret < length)
			{
				mprint("Warning:Loss of data\n");
			}

			ret = write(context->out->fh, encoded_crlf, encoded_crlf_length);
			if(ret <  encoded_crlf_length)
			{
				mprint("Warning:Loss of data\n");
			}
		}

		freep(&sub->data);
		lsub = sub;
		sub = sub->next;
	}

	while(lsub != osub)
	{
		sub = lsub->prev;
		freep(&lsub);
		lsub = sub;
	}

	return ret;
}
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;
}
// 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;
}
// 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;
}
Exemple #17
0
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;
}
Exemple #18
0
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;
}
Exemple #19
0
int write_cc_line(ccx_decoder_608_context *context, struct cc_subtitle *sub)
{
	struct eia608_screen *data;
	LLONG start_time;
	LLONG end_time;
	int i = 0;
	int wrote_something=0;
	int ret = 0;
	data = get_current_visible_buffer(context);

	start_time = context->ts_start_of_current_line + context->subs_delay;
	end_time = get_fts() + context->subs_delay;
	sub->type = CC_608;
	data->format = SFORMAT_CC_LINE;
	data->start_time = 0;
	data->end_time = 0;
	data->mode = context->mode;
	data->channel = context->channel;
	data->my_field = context->my_field;

	//TODO need to put below functionality in encoder context
	ret = get_decoder_line_basic (subline, context->cursor_row, data,context->trim_subs,context->encoding);
	if( ret > 0 )
	{
		sub->data = (struct eia608_screen *) realloc(sub->data,(sub->nb_data +1) * sizeof(*data));
		if (!sub->data)
		{
			ccx_common_logging.log_ftn("No Memory left");
			return 0;
		}
		memcpy(((struct eia608_screen *)sub->data) + sub->nb_data, data, sizeof(*data));
		data = (struct eia608_screen *)sub->data + sub->nb_data;
		sub->nb_data++;

		for(i = 0; i < 15; i++)
		{
			if(i == context->cursor_row)
				data->row_used[i] = 1;
			else
				data->row_used[i] = 0;
		}
		wrote_something = 1;
		if(start_time < end_time)
		{
			int nb_data = sub->nb_data;
			data = (struct eia608_screen *)sub->data;
			for(i = 0;(unsigned) i < sub->nb_data; i++)
			{
				if(!data->start_time)
					break;
				nb_data--;
				data++;
			}
			for(i = 0;(int) i < nb_data; i++)
			{
				data->start_time = start_time + ( ( (end_time - start_time)/nb_data ) * i );
				data->end_time = start_time + ( ( (end_time - start_time)/nb_data ) * (i + 1) );
				data++;
			}
			sub->got_output = 1;
		}
	}
	return wrote_something;

}
// 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;
}
Exemple #21
0
/* 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;
}
int encode_sub(struct encoder_ctx *context, struct cc_subtitle *sub)
{
	int wrote_something = 0;
	int ret = 0;

	if (sub->type == CC_608)
	{
		struct eia608_screen *data = NULL;
		for(data = sub->data; sub->nb_data ; sub->nb_data--,data++)
		{
			// Determine context based on channel. This replaces the code that was above, as this was incomplete (for cases where -12 was used for example)
			if (data->my_field == 2)
				context++;

			new_sentence=1;

			if(data->format == SFORMAT_XDS)
			{
				xds_write_transcript_line_prefix (context->out, data->start_time, data->end_time,data->cur_xds_packet_class);
				if(data->xds_len > 0)
				{
					ret = write (context->out->fh, data->xds_str,data->xds_len);
					if (ret < data->xds_len)
					{
						mprint("WARNING:Loss of data\n");
					}
				}
				freep (&data->xds_str);
				xds_write_transcript_line_suffix (context->out);
				continue;
			}

			if(!data->start_time)
				break;
			switch (context->write_format)
			{
			case CCX_OF_SRT:
				if (!context->startcredits_displayed && context->start_credits_text!=NULL)
					try_to_add_start_credits(context, data->start_time);
				wrote_something = write_cc_buffer_as_srt(data, context);
				break;
            case CCX_OF_WEBVTT:
                if (!context->startcredits_displayed && context->start_credits_text!=NULL)
                    try_to_add_start_credits(context, data->start_time);
                wrote_something = write_cc_buffer_as_webvtt(data, context);
                break;
			case CCX_OF_SAMI:
				if (!context->startcredits_displayed && context->start_credits_text!=NULL)
					try_to_add_start_credits(context, data->start_time);
				wrote_something = write_cc_buffer_as_sami(data, context);
				break;
			case CCX_OF_SMPTETT:
				if (!context->startcredits_displayed && context->start_credits_text!=NULL)
					try_to_add_start_credits(context, data->start_time);
				wrote_something = write_cc_buffer_as_smptett(data, context);
				break;
			case CCX_OF_TRANSCRIPT:
				wrote_something = write_cc_buffer_as_transcript2(data, context);
				break;
			case CCX_OF_SPUPNG:
				wrote_something = write_cc_buffer_as_spupng(data, context);
				break;
			default:
				break;
			}
			if (wrote_something)
				context->last_displayed_subs_ms=get_fts() + context->subs_delay;

			if (context->gui_mode_reports)
				write_cc_buffer_to_gui(sub->data, context);
		}
		freep(&sub->data);
	}
	if(sub->type == CC_BITMAP)
	{
		switch (context->write_format)
		{
		case CCX_OF_SRT:
			if (!context->startcredits_displayed && context->start_credits_text!=NULL)
				try_to_add_start_credits(context, sub->start_time);
			wrote_something = write_cc_bitmap_as_srt(sub, context);
			break;
        case CCX_OF_WEBVTT:
            if (!context->startcredits_displayed && context->start_credits_text!=NULL)
                try_to_add_start_credits(context, sub->start_time);
            wrote_something = write_cc_bitmap_as_webvtt(sub, context);
            break;
		case CCX_OF_SAMI:
			if (!context->startcredits_displayed && context->start_credits_text!=NULL)
				try_to_add_start_credits(context, sub->start_time);
			wrote_something = write_cc_bitmap_as_sami(sub, context);
			break;
		case CCX_OF_SMPTETT:
			if (!context->startcredits_displayed && context->start_credits_text!=NULL)
				try_to_add_start_credits(context, sub->start_time);
			wrote_something = write_cc_bitmap_as_smptett(sub, context);
			break;
		case CCX_OF_TRANSCRIPT:
			wrote_something = write_cc_bitmap_as_transcript(sub, context);
			break;
		case CCX_OF_SPUPNG:
			wrote_something = write_cc_bitmap_as_spupng(sub, context);
			break;
		default:
			break;
		}

	}
	if (sub->type == CC_RAW)
	{
		if (context->send_to_srv)
			net_send_header(sub->data, sub->nb_data);
		else
		{
			ret = write(context->out->fh, sub->data, sub->nb_data);
			if ( ret < sub->nb_data) {
				mprint("WARNING: Loss of data\n");
			}
		}
		sub->nb_data = 0;
	}
	if(sub->type == CC_TEXT)
	{
		switch (context->write_format)
		{
		case CCX_OF_SRT:
			if (!context->startcredits_displayed && context->start_credits_text!=NULL)
				try_to_add_start_credits(context, sub->start_time);
			wrote_something = write_cc_subtitle_as_srt(sub, context);
			break;
        case CCX_OF_WEBVTT:
            if (!context->startcredits_displayed && context->start_credits_text!=NULL)
                try_to_add_start_credits(context, sub->start_time);
            wrote_something = write_cc_subtitle_as_webvtt(sub, context);
            break;
		case CCX_OF_SAMI:
			if (!context->startcredits_displayed && context->start_credits_text!=NULL)
				try_to_add_start_credits(context, sub->start_time);
			wrote_something = write_cc_subtitle_as_sami(sub, context);
			break;
		case CCX_OF_SMPTETT:
			if (!context->startcredits_displayed && context->start_credits_text!=NULL)
				try_to_add_start_credits(context, sub->start_time);
			wrote_something = write_cc_subtitle_as_smptett(sub, context);
			break;
		case CCX_OF_TRANSCRIPT:
			wrote_something = write_cc_subtitle_as_transcript(sub, context);
			break;
		case CCX_OF_SPUPNG:
			wrote_something = write_cc_subtitle_as_spupng(sub, context);
			break;
		default:
			break;
		}
		sub->nb_data = 0;

	}
	if (!sub->nb_data)
		freep(&sub->data);
	return wrote_something;
}
int write_cc_bitmap_as_transcript(struct cc_subtitle *sub, struct encoder_ctx *context)
{
	struct spupng_t *sp = (struct spupng_t *)context->out->spupng_data;
	int x_pos, y_pos, width, height, i;
	int x, y, y_off, x_off, ret;
	uint8_t *pbuf;
	//char *filename;
	struct cc_bitmap* rect;
	png_color *palette = NULL;
	png_byte *alpha = NULL;
#ifdef ENABLE_OCR
	char*str = NULL;
#endif
	//int used;
#ifdef ENABLE_OCR
	unsigned h1,m1,s1,ms1;
	unsigned h2,m2,s2,ms2;
#endif
	LLONG start_time, end_time;
	//char timeline[128];
	int len = 0;

        x_pos = -1;
        y_pos = -1;
        width = 0;
        height = 0;

	if (context->prev_start != -1 && (sub->flags & SUB_EOD_MARKER))
	{
		start_time = context->prev_start + context->subs_delay;
		end_time = sub->start_time - 1;
	}
	else if ( !(sub->flags & SUB_EOD_MARKER))
	{
		start_time = sub->start_time + context->subs_delay;
		end_time = sub->end_time - 1;
	}

	if(sub->nb_data == 0 )
		return 0;
	rect = sub->data;
	for(i = 0;i < sub->nb_data;i++)
	{
		if(x_pos == -1)
		{
			x_pos = rect[i].x;
			y_pos = rect[i].y;
			width = rect[i].w;
			height = rect[i].h;
		}
		else
		{
			if(x_pos > rect[i].x)
			{
				width += (x_pos - rect[i].x);
				x_pos = rect[i].x;
			}

                        if (rect[i].y < y_pos)
                        {
                                height += (y_pos - rect[i].y);
                                y_pos = rect[i].y;
                        }

                        if (rect[i].x + rect[i].w > x_pos + width)
                        {
                                width = rect[i].x + rect[i].w - x_pos;
                        }

                        if (rect[i].y + rect[i].h > y_pos + height)
                        {
                                height = rect[i].y + rect[i].h - y_pos;
                        }

		}
	}
	if ( sub->flags & SUB_EOD_MARKER )
		context->prev_start =  sub->start_time;
	pbuf = (uint8_t*) malloc(width * height);
	memset(pbuf, 0x0, width * height);

	for(i = 0;i < sub->nb_data;i++)
	{
		x_off = rect[i].x - x_pos;
		y_off = rect[i].y - y_pos;
		for (y = 0; y < rect[i].h; y++)
		{
			for (x = 0; x < rect[i].w; x++)
				pbuf[((y + y_off) * width) + x_off + x] = rect[i].data[0][y * rect[i].w + x];

		}
	}
	palette = (png_color*) malloc(rect[0].nb_colors * sizeof(png_color));
	if(!palette)
	{
		ret = -1;
		goto end;
	}
        alpha = (png_byte*) malloc(rect[0].nb_colors * sizeof(png_byte));
        if(!alpha)
        {
                ret = -1;
                goto end;
        }
	/* TODO do rectangle, wise one color table should not be used for all rectangle */
        mapclut_paletee(palette, alpha, (uint32_t *)rect[0].data[1],rect[0].nb_colors);
	quantize_map(alpha, palette, pbuf, width*height, 3, rect[0].nb_colors);
#ifdef ENABLE_OCR
	str = ocr_bitmap(palette,alpha,pbuf,width,height);
	if(str && str[0])
	{
		if (context->prev_start != -1 || !(sub->flags & SUB_EOD_MARKER))
		{
			char *token = NULL;
			token = strtok(str,"\r\n");
			while (token)
			{

				if (ccx_options.transcript_settings.showStartTime)
				{
					char buf1[80];
					if (ccx_options.transcript_settings.relativeTimestamp)
					{
						millis_to_date(start_time + context->subs_delay, buf1);
						fdprintf(context->out->fh, "%s|", buf1);
					}
					else
					{
						mstotime(start_time + context->subs_delay, &h1, &m1, &s1, &ms1);
						time_t start_time_int = (start_time + context->subs_delay) / 1000;
						int start_time_dec = (start_time + context->subs_delay) % 1000;
						struct tm *start_time_struct = gmtime(&start_time_int);
						strftime(buf1, sizeof(buf1), "%Y%m%d%H%M%S", start_time_struct);
						fdprintf(context->out->fh, "%s%c%03d|", buf1,ccx_options.millis_separator,start_time_dec);
					}
				}

				if (ccx_options.transcript_settings.showEndTime)
				{
					char buf2[80];
					if (ccx_options.transcript_settings.relativeTimestamp)
					{
						millis_to_date(end_time, buf2);
						fdprintf(context->out->fh, "%s|", buf2);
					}
					else
					{
						mstotime(get_fts() + context->subs_delay, &h2, &m2, &s2, &ms2);
						time_t end_time_int = end_time / 1000;
						int end_time_dec = end_time % 1000;
						struct tm *end_time_struct = gmtime(&end_time_int);
						strftime(buf2, sizeof(buf2), "%Y%m%d%H%M%S", end_time_struct);
						fdprintf(context->out->fh, "%s%c%03d|", buf2,ccx_options.millis_separator,end_time_dec);
					}
				}
				if (ccx_options.transcript_settings.showCC)
				{
					fdprintf(context->out->fh,"%s|",language[sub->lang_index]);
				}
				if (ccx_options.transcript_settings.showMode)
				{
					fdprintf(context->out->fh,"DVB|");
				}
				fdprintf(context->out->fh,"%s\n",token);
				token = strtok(NULL,"\r\n");

			}

		}
	}
#endif

end:
	sub->nb_data = 0;
	freep(&sub->data);
	freep(&palette);
	freep(&alpha);
	return ret;

}