diff --git a/src/lib_ccx/ccx_decoders_708.c b/src/lib_ccx/ccx_decoders_708.c index 38a218025..5fa723cb4 100644 --- a/src/lib_ccx/ccx_decoders_708.c +++ b/src/lib_ccx/ccx_decoders_708.c @@ -1837,7 +1837,8 @@ void dtvcc_process_current_packet(dtvcc_ctx *dtvcc, int len) dtvcc->report->services[service_number] = 1; } - if (service_number > 0 && dtvcc->services_active[service_number - 1]) + // Without an encoder (e.g. --out=report) only record which services are present + if (service_number > 0 && dtvcc->services_active[service_number - 1] && dtvcc->encoder) dtvcc_process_service_block(dtvcc, &dtvcc->decoders[service_number - 1], pos, block_length); pos += block_length; // Skip data diff --git a/src/lib_ccx/ccx_decoders_708_output.c b/src/lib_ccx/ccx_decoders_708_output.c index 569fa5c4e..bf228aa91 100644 --- a/src/lib_ccx/ccx_decoders_708_output.c +++ b/src/lib_ccx/ccx_decoders_708_output.c @@ -136,15 +136,36 @@ void dtvcc_change_pen_attribs(dtvcc_tv_screen *tv, dtvcc_pen_attribs pen_attribs } } -size_t write_utf16_char(unsigned short utf16_char, char *out) +// UTF-16 and UCS-2 are fixed-width: every character, ASCII included, takes 2 bytes +static int is_utf16_charset(const char *charset) { - // Always write 2 bytes for consistent UTF-16BE encoding. - // Previously, this function wrote 1 byte for ASCII characters and 2 bytes - // for non-ASCII, creating an invalid mix that iconv couldn't handle properly. - // This caused garbled output with Japanese/Chinese characters (issue #1451). - out[0] = (unsigned char)(utf16_char >> 8); - out[1] = (unsigned char)(utf16_char & 0xff); - return 2; + const char *names[] = {"UTF-16", "UTF16", "UTF_16", "UCS-2", "UCS2", "UCS_2"}; + char upper[64]; + size_t i; + + for (i = 0; charset[i] && i < sizeof(upper) - 1; i++) + upper[i] = (char)toupper((unsigned char)charset[i]); + upper[i] = '\0'; + + for (i = 0; i < sizeof(names) / sizeof(names[0]); i++) + if (strstr(upper, names[i])) + return 1; + return 0; +} + +size_t write_utf16_char(unsigned short utf16_char, char *out, int use_utf16) +{ + // UTF-16/UCS-2 output needs 2 bytes for every character (issue #1451). Otherwise ASCII + // takes 1 byte, so the output is plain text without a charset and variable-width + // charsets such as EUC-KR work (issue #1065). + if (use_utf16 || (utf16_char >> 8)) + { + out[0] = (unsigned char)(utf16_char >> 8); + out[1] = (unsigned char)(utf16_char & 0xff); + return 2; + } + out[0] = (unsigned char)(utf16_char & 0xff); + return 1; } void dtvcc_write_row(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, int row_index, struct encoder_ctx *encoder, int use_colors) @@ -156,6 +177,7 @@ void dtvcc_write_row(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, i int first, last; int fd = encoder->dtvcc_writers[tv->service_number - 1].fd; + int use_utf16 = writer->charset && is_utf16_charset(writer->charset); dtvcc_pen_color pen_color = dtvcc_default_pen_color; dtvcc_pen_attribs pen_attribs = dtvcc_default_pen_attribs; @@ -175,12 +197,12 @@ void dtvcc_write_row(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, i pen_attribs = tv->pen_attribs[row_index][i]; if (i < first) { - size_t size = write_utf16_char(' ', buf + buf_len); + size_t size = write_utf16_char(' ', buf + buf_len, use_utf16); buf_len += size; } else { - size_t size = write_utf16_char(tv->chars[row_index][i].sym, buf + buf_len); + size_t size = write_utf16_char(tv->chars[row_index][i].sym, buf + buf_len, use_utf16); buf_len += size; } } diff --git a/src/lib_ccx/ccx_decoders_708_output.h b/src/lib_ccx/ccx_decoders_708_output.h index ab9e62935..e9d6c383f 100644 --- a/src/lib_ccx/ccx_decoders_708_output.h +++ b/src/lib_ccx/ccx_decoders_708_output.h @@ -22,7 +22,7 @@ void dtvcc_get_write_interval(dtvcc_tv_screen *tv, int row_index, int *first, in void dtvcc_color_to_hex(int color, unsigned *hR, unsigned *hG, unsigned *hB); void dtvcc_change_pen_colors(dtvcc_tv_screen *tv, dtvcc_pen_color pen_color, int row_index, int column_index, struct encoder_ctx *encoder, size_t *buf_len, int open); void dtvcc_change_pen_attribs(dtvcc_tv_screen *tv, dtvcc_pen_attribs pen_attribs, int row_index, int column_index, struct encoder_ctx *encoder, size_t *buf_len, int open); -size_t write_utf16_char(unsigned short utf16_char, char *out); +size_t write_utf16_char(unsigned short utf16_char, char *out, int use_utf16); void dtvcc_write_row(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, int row_index, struct encoder_ctx *encoder, int use_colors); void dtvcc_write_srt(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struct encoder_ctx *encoder); void dtvcc_write_debug(dtvcc_tv_screen *tv); diff --git a/src/lib_ccx/ccx_decoders_common.c b/src/lib_ccx/ccx_decoders_common.c index 55c6e78ab..f3c4b6502 100644 --- a/src/lib_ccx/ccx_decoders_common.c +++ b/src/lib_ccx/ccx_decoders_common.c @@ -40,6 +40,54 @@ LLONG get_visible_end(struct ccx_common_timing_ctx *ctx, int current_field) return fts; } +#ifdef DISABLE_RUST +// C counterpart of ccxr_process_cc_data() and do_cb_dtvcc_rust(): give every CEA-708 +// block to the C decoder before do_cb() runs, with the same rules as the Rust build +static int process_cc_data_dtvcc(struct lib_cc_decode *ctx, const unsigned char *cc_data, int cc_count) +{ + int ret = -1; + + if (cc_count <= 0 || cc_count > HDCC_MAX_TRIPLETS_PER_SLOT) + return -1; + + for (int j = 0; j < cc_count * 3; j += 3) + { + // validate_cc_data_pair() may change the block, and do_cb() still needs the original + unsigned char block[3] = {cc_data[j], cc_data[j + 1], cc_data[j + 2]}; + if (validate_cc_data_pair(block)) + continue; + ret = 0; + + if (ctx->write_format != CCX_OF_DVDRAW && ctx->write_format != CCX_OF_RAW && + (block[0] == 0xFA || block[0] == 0xFC || block[0] == 0xFD) && (block[1] & 0x7F) == 0 && (block[2] & 0x7F) == 0) + continue; + + unsigned char cc_valid = (block[0] & 4) >> 2; + unsigned char cc_type = block[0] & 3; + if (!(cc_valid || cc_type == 3) || cc_type < 2) + continue; + + // Like do_cb_dtvcc_rust(), the time is taken before current_field is set to 3 + LLONG current_time = ctx->timing ? get_fts(ctx->timing, ctx->current_field) : 0; + int timeok = 1; + ctx->current_field = 3; + if (ctx->extraction_start.set && current_time < ctx->extraction_start.time_in_ms) + timeok = 0; + if (ctx->extraction_end.set && current_time > ctx->extraction_end.time_in_ms) + { + timeok = 0; + ctx->processed_enough = 1; + } + if (timeok && ctx->write_format != CCX_OF_RAW) + { + unsigned char temp[4] = {cc_valid, cc_type, block[1], block[2]}; + dtvcc_process_data(ctx->dtvcc, temp); + } + } + return ret; +} +#endif + int process_cc_data(struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx, unsigned char *cc_data, int cc_count, struct cc_subtitle *sub) { int ret = -1; @@ -50,7 +98,11 @@ int process_cc_data(struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx, return 0; } +#ifndef DISABLE_RUST ret = ccxr_process_cc_data(dec_ctx, cc_data, cc_count); +#else + ret = process_cc_data_dtvcc(dec_ctx, cc_data, cc_count); +#endif for (int j = 0; j < cc_count * 3; j = j + 3) {