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 _dtvcc_window_update_time_show(ccx_dtvcc_window *window, struct ccx_common_timing_ctx *timing) { char buf[128]; window->time_ms_show = get_visible_start(timing, 3); print_mstime2buf(window->time_ms_show, buf); ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] " "[W-%d] show time updated to %s\n", window->number, buf); }
void erase_both_memories(ccx_decoder_608_context *context, struct cc_subtitle *sub) { erase_memory(context, false); // For the visible memory, we write the contents to disk // The currently *visible* buffer is leaving, so now we know its ending // time. Time to actually write it to file. if (write_cc_buffer(context, sub)) context->screenfuls_counter++; context->current_visible_start_ms = get_visible_start(); context->cursor_column = 0; context->cursor_row = 0; context->current_color = context->settings->default_color; context->font = FONT_REGULAR; erase_memory(context, true); }
/* Process GLOBAL CODES */ void handle_command(/*const */ unsigned char c1, const unsigned char c2, ccx_decoder_608_context *context, struct cc_subtitle *sub) { int changes=0; // Handle channel change context->channel = context->new_channel; if (context->channel != context->my_channel) return; enum command_code command = COM_UNKNOWN; if (c1==0x15) c1=0x14; if ((c1==0x14 || c1==0x1C) && c2==0x2C) command = COM_ERASEDISPLAYEDMEMORY; if ((c1==0x14 || c1==0x1C) && c2==0x20) command = COM_RESUMECAPTIONLOADING; if ((c1==0x14 || c1==0x1C) && c2==0x2F) command = COM_ENDOFCAPTION; if ((c1==0x14 || c1==0x1C) && c2==0x22) command = COM_ALARMOFF; if ((c1==0x14 || c1==0x1C) && c2==0x23) command = COM_ALARMON; if ((c1==0x14 || c1==0x1C) && c2==0x24) command = COM_DELETETOENDOFROW; if ((c1==0x17 || c1==0x1F) && c2==0x21) command = COM_TABOFFSET1; if ((c1==0x17 || c1==0x1F) && c2==0x22) command = COM_TABOFFSET2; if ((c1==0x17 || c1==0x1F) && c2==0x23) command = COM_TABOFFSET3; if ((c1==0x14 || c1==0x1C) && c2==0x25) command = COM_ROLLUP2; if ((c1==0x14 || c1==0x1C) && c2==0x26) command = COM_ROLLUP3; if ((c1==0x14 || c1==0x1C) && c2==0x27) command = COM_ROLLUP4; if ((c1==0x14 || c1==0x1C) && c2==0x29) command = COM_RESUMEDIRECTCAPTIONING; if ((c1==0x14 || c1==0x1C) && c2==0x2D) command = COM_CARRIAGERETURN; if ((c1==0x14 || c1==0x1C) && c2==0x2E) command = COM_ERASENONDISPLAYEDMEMORY; if ((c1==0x14 || c1==0x1C) && c2==0x21) command = COM_BACKSPACE; if ((c1==0x14 || c1==0x1C) && c2==0x2b) command = COM_RESUMETEXTDISPLAY; if ((command == COM_ROLLUP2 || command == COM_ROLLUP3 || command == COM_ROLLUP4) && context->settings->force_rollup == 1) command=COM_FAKE_RULLUP1; if ((command == COM_ROLLUP3 || command == COM_ROLLUP4) && context->settings->force_rollup == 2) command=COM_ROLLUP2; else if (command == COM_ROLLUP4 && context->settings->force_rollup == 3) command=COM_ROLLUP3; ccx_common_logging.debug_ftn(CCX_DMT_DECODER_608, "\rCommand begin: %02X %02X (%s)\n", c1, c2, command_type[command]); ccx_common_logging.debug_ftn(CCX_DMT_DECODER_608, "\rCurrent mode: %d Position: %d,%d VisBuf: %d\n", context->mode, context->cursor_row, context->cursor_column, context->visible_buffer); switch (command) { case COM_BACKSPACE: if (context->cursor_column>0) { context->cursor_column--; get_writing_buffer(context)->characters[context->cursor_row][context->cursor_column] = ' '; } break; case COM_TABOFFSET1: if (context->cursor_column<CCX_DECODER_608_SCREEN_WIDTH - 1) context->cursor_column++; break; case COM_TABOFFSET2: context->cursor_column += 2; if (context->cursor_column>CCX_DECODER_608_SCREEN_WIDTH - 1) context->cursor_column = CCX_DECODER_608_SCREEN_WIDTH - 1; break; case COM_TABOFFSET3: context->cursor_column += 3; if (context->cursor_column>CCX_DECODER_608_SCREEN_WIDTH - 1) context->cursor_column = CCX_DECODER_608_SCREEN_WIDTH - 1; break; case COM_RESUMECAPTIONLOADING: context->mode = MODE_POPON; break; case COM_RESUMETEXTDISPLAY: context->mode = MODE_TEXT; break; case COM_FAKE_RULLUP1: case COM_ROLLUP2: case COM_ROLLUP3: case COM_ROLLUP4: if (context->mode == MODE_POPON || context->mode == MODE_PAINTON) { /* CEA-608 C.10 Style Switching (regulatory) [...]if pop-up or paint-on captioning is already present in either memory it shall be erased[...] */ if (write_cc_buffer(context, sub)) context->screenfuls_counter++; erase_memory(context, true); } erase_memory(context, false); // If the reception of data for a row is interrupted by data for the alternate // data channel or for text mode, the display of caption text will resume from the same // cursor position if a roll-up caption command is received and no PAC is given [...] if (context->mode != MODE_TEXT && context->have_cursor_position == 0) { // If no Preamble Address Code is received, the base row shall default to row 15 context->cursor_row = 14; // Default if the previous mode wasn't roll up already. context->cursor_column = 0; context->have_cursor_position = 1; } switch (command) { case COM_FAKE_RULLUP1: context->mode = MODE_FAKE_ROLLUP_1; break; case COM_ROLLUP2: context->mode = MODE_ROLLUP_2; break; case COM_ROLLUP3: context->mode = MODE_ROLLUP_3; break; case COM_ROLLUP4: context->mode = MODE_ROLLUP_4; break; default: // Impossible, but remove compiler warnings break; } break; case COM_CARRIAGERETURN: if (context->mode == MODE_PAINTON) // CR has no effect on painton mode according to zvbis' code break; if (context->mode == MODE_POPON) // CFS: Not sure about this. Is there a valid reason for CR in popup? { context->cursor_column = 0; if (context->cursor_row<15) context->cursor_row++; break; } if (context->output_format == CCX_OF_TRANSCRIPT) { write_cc_line(context,sub); } // In transcript mode, CR doesn't write the whole screen, to avoid // repeated lines. changes = check_roll_up(context); if (changes) { // Only if the roll up would actually cause a line to disappear we write the buffer if (context->output_format != CCX_OF_TRANSCRIPT) { if (write_cc_buffer(context, sub)) context->screenfuls_counter++; if (context->settings->no_rollup) erase_memory(context, true); // Make sure the lines we just wrote aren't written again } } roll_up(context); // The roll must be done anyway of course. context->ts_start_of_current_line = -1; // Unknown. if (changes) context->current_visible_start_ms = get_visible_start(); context->cursor_column = 0; break; case COM_ERASENONDISPLAYEDMEMORY: erase_memory(context, false); break; case COM_ERASEDISPLAYEDMEMORY: // Write it to disk before doing this, and make a note of the new // time it became clear. if (context->output_format == CCX_OF_TRANSCRIPT && (context->mode == MODE_FAKE_ROLLUP_1 || context->mode == MODE_ROLLUP_2 || context->mode == MODE_ROLLUP_3 || context->mode == MODE_ROLLUP_4)) { // In transcript mode we just write the cursor line. The previous lines // should have been written already, so writing everything produces // duplicate lines. write_cc_line(context, sub); } else { if (context->output_format == CCX_OF_TRANSCRIPT) context->ts_start_of_current_line = context->current_visible_start_ms; if (write_cc_buffer(context, sub)) context->screenfuls_counter++; } erase_memory(context, true); context->current_visible_start_ms = get_visible_start(); break; case COM_ENDOFCAPTION: // Switch buffers // The currently *visible* buffer is leaving, so now we know its ending // time. Time to actually write it to file. if (write_cc_buffer(context, sub)) context->screenfuls_counter++; context->visible_buffer = (context->visible_buffer == 1) ? 2 : 1; context->current_visible_start_ms = get_visible_start(); context->cursor_column = 0; context->cursor_row = 0; context->current_color = context->settings->default_color; context->font = FONT_REGULAR; context->mode = MODE_POPON; break; case COM_DELETETOENDOFROW: delete_to_end_of_row(context); break; case COM_ALARMOFF: case COM_ALARMON: // These two are unused according to Robson's, and we wouldn't be able to do anything useful anyway break; case COM_RESUMEDIRECTCAPTIONING: context->mode = MODE_PAINTON; //ccx_common_logging.log_ftn ("\nWarning: Received ResumeDirectCaptioning, this mode is almost impossible.\n"); //ccx_common_logging.log_ftn ("to transcribe to a text file.\n"); break; default: ccx_common_logging.debug_ftn(CCX_DMT_DECODER_608, "\rNot yet implemented.\n"); break; } ccx_common_logging.debug_ftn(CCX_DMT_DECODER_608, "\rCurrent mode: %d Position: %d,%d VisBuf: %d\n", context->mode, context->cursor_row, context->cursor_column, context->visible_buffer); ccx_common_logging.debug_ftn(CCX_DMT_DECODER_608, "\rCommand end: %02X %02X (%s)\n", c1, c2, command_type[command]); }
/* 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; }