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