diff --git a/src/608.c b/src/608.c index 3977c5cd..438d299a 100644 --- a/src/608.c +++ b/src/608.c @@ -250,6 +250,187 @@ void handle_text_attr(const unsigned char c1, const unsigned char c2, struct s_c } } +void write_subtitle_file_footer(struct ccx_s_write *out) +{ + switch (ccx_options.write_format) + { + case CCX_OF_SAMI: + sprintf ((char *) str,"\n"); + if (ccx_options.encoding!=CCX_ENC_UNICODE) + { + dbg_print(CCX_DMT_608, "\r%s\n", str); + } + enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str); + write(out->fh, enc_buffer, enc_buffer_used); + break; + case CCX_OF_SMPTETT: + sprintf ((char *) str,"\n"); + if (ccx_options.encoding!=CCX_ENC_UNICODE) + { + dbg_print(CCX_DMT_608, "\r%s\n", str); + } + enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str); + write (out->fh, enc_buffer,enc_buffer_used); + break; + case CCX_OF_SPUPNG: + write_spumux_footer(out); + break; + default: // Nothing to do, no footer on this format + break; + } +} + + +void write_subtitle_file_header(struct ccx_s_write *out) +{ + switch (ccx_options.write_format) + { + case CCX_OF_SRT: // Subrip subtitles have no header + break; + case CCX_OF_SAMI: // This header brought to you by McPoodle's CCASDI + //fprintf_encoded (wb->fh, sami_header); + REQUEST_BUFFER_CAPACITY(strlen (sami_header)*3); + enc_buffer_used=encode_line (enc_buffer,(unsigned char *) sami_header); + write (out->fh, enc_buffer,enc_buffer_used); + break; + case CCX_OF_SMPTETT: // This header brought to you by McPoodle's CCASDI + //fprintf_encoded (wb->fh, sami_header); + REQUEST_BUFFER_CAPACITY(strlen (smptett_header)*3); + enc_buffer_used=encode_line (enc_buffer,(unsigned char *) smptett_header); + write(out->fh, enc_buffer, enc_buffer_used); + break; + case CCX_OF_RCWT: // Write header + write(out->fh, rcwt_header, sizeof(rcwt_header)); + break; + case CCX_OF_SPUPNG: + write_spumux_header(out); + break; + case CCX_OF_TRANSCRIPT: // No header. Fall thru + default: + break; + } +} + +void write_cc_line_as_transcript(struct eia608_screen *data, struct s_context_cc608 *context, int line_number) +{ + unsigned h1,m1,s1,ms1; + unsigned h2,m2,s2,ms2; + if (ccx_options.sentence_cap) + { + capitalize (line_number,data); + correct_case(line_number,data); + } + int length = get_decoder_line_basic (subline, line_number, data); + if (ccx_options.encoding!=CCX_ENC_UNICODE) + { + dbg_print(CCX_DMT_608, "\r"); + dbg_print(CCX_DMT_608, "%s\n",subline); + } + if (length>0) + { + if (context->ts_start_of_current_line == -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 (ccx_options.transcript_settings.showStartTime){ + char buf1[80]; + if (ccx_options.transcript_settings.relativeTimestamp){ + millis_to_date(context->ts_start_of_current_line + subs_delay, buf1); + fdprintf(context->out->fh, "%s|", buf1); + } + else { + mstotime(context->ts_start_of_current_line + subs_delay, &h1, &m1, &s1, &ms1); + time_t start_time_int = (context->ts_start_of_current_line + subs_delay) / 1000; + int start_time_dec = (context->ts_start_of_current_line + 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(get_fts() + subs_delay, buf2); + fdprintf(context->out->fh, "%s|", buf2); + } + else { + mstotime(get_fts() + subs_delay, &h2, &m2, &s2, &ms2); + time_t end_time_int = (get_fts() + subs_delay) / 1000; + int end_time_dec = (get_fts() + 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,ccx_options.millis_separator,end_time_dec); + } + } + + if (ccx_options.transcript_settings.showCC){ + fdprintf(context->out->fh, "CC%d|", context->my_field == 1 ? context->channel : context->channel + 2); // Data from field 2 is CC3 or 4 + } + + if (ccx_options.transcript_settings.showMode){ + const char *mode = "???"; + switch (context->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); + } + + write(context->out->fh, subline, length); + write(context->out->fh, encoded_crlf, encoded_crlf_length); + } + // fprintf (wb->fh,encoded_crlf); +} + +int write_cc_buffer_as_transcript(struct eia608_screen *data, struct s_context_cc608 *context) +{ + int wrote_something = 0; + context->ts_start_of_current_line = context->current_visible_start_ms; + dbg_print(CCX_DMT_608, "\n- - - TRANSCRIPT caption - - -\n"); + + for (int i=0;i<15;i++) + { + if (data->row_used[i]) + { + write_cc_line_as_transcript (data,context, i); + } + wrote_something=1; + } + dbg_print(CCX_DMT_608, "- - - - - - - - - - - -\r\n"); + return wrote_something; +} + + + + +>>>>>>> init struct eia608_screen *get_current_visible_buffer(struct s_context_cc608 *context) { struct eia608_screen *data; diff --git a/src/ccextractor.c b/src/ccextractor.c index e9a1ce9f..05d674e5 100644 --- a/src/ccextractor.c +++ b/src/ccextractor.c @@ -177,6 +177,9 @@ void init_options (struct ccx_s_options *options) /* Networking */ options->udpaddr = 0; options->udpport=0; // Non-zero => Listen for UDP packets on this port, no files. + options->send_to_srv = 0; + options->srv_addr = NULL; + options->srv_port = NULL; options->line_terminator_lf=0; // 0 = CRLF options->noautotimeref=0; // Do NOT set time automatically? options->input_source=CCX_DS_FILE; // Files, stdin or network @@ -420,6 +423,11 @@ int main(int argc, char *argv[]) fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory\n"); } + if (ccx_options.send_to_srv) + { + connect_to_srv(ccx_options.srv_addr, ccx_options.srv_port); + } + if (ccx_options.write_format!=CCX_OF_NULL) { /* # DVD format uses one raw file for both fields, while Broadcast requires 2 */ diff --git a/src/ccextractor.h b/src/ccextractor.h index 4460a820..46acb1ec 100644 --- a/src/ccextractor.h +++ b/src/ccextractor.h @@ -24,6 +24,7 @@ extern int ccblocks_in_avc_lost; // CC blocks found by the AVC code lost due to #include "bitstream.h" #include "constants.h" #include "cc_decoders_common.h" +#include "networking.h" #define TS_PMT_MAP_SIZE 128 @@ -117,6 +118,9 @@ struct ccx_s_options // Options from user parameters /* Networking */ in_addr_t udpaddr; unsigned udpport; // Non-zero => Listen for UDP packets on this port, no files. + unsigned send_to_srv; + char *srv_addr; + char *srv_port; int line_terminator_lf; // 0 = CRLF, 1=LF int noautotimeref; // Do NOT set time automatically? enum ccx_datasource input_source; // Files, stdin or network diff --git a/src/networking.c b/src/networking.c new file mode 100644 index 00000000..02a5bb17 --- /dev/null +++ b/src/networking.c @@ -0,0 +1,424 @@ +#include "ccextractor.h" +#include "networking.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define INT_LEN 10 + +#define PASSW 10 +#define NEW_PRG 12 +#define CC 11 +#define SERV_ERROR 4 +#define WRONG_COMMAND 3 +#define WRONG_PASSW 2 +#define OK 1 + +int srv_sd = -1; + +/* + * Established connection to speciefied addres. + * Returns socked id + */ +int tcp_connect(const char *addr, const char *port); + +/* + * Asks password from stdin, sends it to the server and waits for + * it's response + */ +int ask_passwd(int sd); + +#define BUF_SIZE 20480 +char *buf; +char *buf_end; +void init_buf(); + +/* + * Writes data according to protocol to descriptor + * block format: + * command | lenght | data | \r\n + * 1 byte | INT_LEN bytes | lenght bytes | 2 bytes + */ +ssize_t write_block(int fd, char command, const char *buf, size_t buf_len); + +/* Reads n bytes from descriptor */ +ssize_t readn(int fd, void *vptr, size_t n); + +/* Writes n bytes to descriptor */ +ssize_t writen(int fd, const void *vptr, size_t n); + +/* Convinence functions */ +ssize_t write_byte(int fd, char status); +ssize_t read_byte(int fd, char *status); + +void connect_to_srv(const char *addr, const char *port) +{ + mprint("Connecting to %s:%s\n", addr, port); + + if ((srv_sd = tcp_connect(addr, port)) < 0) + fatal(EXIT_FAILURE, "Unable to connect\n"); + + if (ask_passwd(srv_sd) < 0) + fatal(EXIT_FAILURE, "Unable to connect\n"); + + mprint("Connected to %s:%s\n", addr, port); +} + +void init_buf() +{ + buf = (char *) malloc(BUF_SIZE); + if (NULL == buf) + fatal(EXIT_NOT_ENOUGH_MEMORY, "malloc error(): %s", strerror(errno)); + + buf_end = buf; +} + +void net_append_cc(const char *fmt, ...) +{ + if (NULL == buf) + init_buf(); + + va_list args; + va_start(args, fmt); + + vfprintf(stdout, fmt, args); + + int rc = vsnprintf(buf_end, BUF_SIZE - (buf_end - buf), fmt, args); + if (rc < 0) + { + mprint("net_append_cc() error: can\'t append "); + mprint(fmt, args); + return; + } + + buf_end += rc; + + va_end(args); +} + +void net_append_cc_n(const char *data, size_t len) +{ + assert(data != NULL); + + size_t nleft = BUF_SIZE - (buf_end - buf); + if (nleft < len) + { + mprint("net_append_cc_n() warning: buffer overflow, pruning %zd bytes\n", + nleft); + len = nleft; + } + + memcpy(buf_end, data, len); + + buf_end += len; +} + +void net_send_cc() +{ + assert(srv_sd > 0); + + if (buf_end - buf == 0) + return; + + if (write_block(srv_sd, CC, buf, buf_end - buf) < 0) + { + mprint("Can't send subtitle block\n"); + return; // XXX: store somewhere + } + + buf_end = buf; + + char ok; + read_byte(srv_sd, &ok); + + switch (ok) + { + case OK: + break; + case SERV_ERROR: + mprint("Error on server side\n"); // lol + break; + /* case PASSW: */ + default: + break; + } + + return; +} + +void net_set_new_program(const char *name, size_t len) +{ + assert(name != NULL); + assert(len > 0); + assert(srv_sd > 0); + + if (write_block(srv_sd, NEW_PRG, name, len) < 0) + { + mprint("Can't send new program name to the server\n"); + return; // XXX: store somewhere + } +} + +/* +* command | lenght | data | \r\n +* 1 byte | INT_LEN bytes | lenght bytes | 2 bytes +*/ +ssize_t +write_block(int fd, char command, const char *buf, size_t buf_len) +{ + assert(buf != NULL); + assert(buf_len > 0); + + int rc; + ssize_t nwritten = 0; + + if ((rc = write_byte(fd, command)) < 0) + return -1; + else if (rc != 1) + return 0; + nwritten++; + + char len_str[INT_LEN] = {0}; + snprintf(len_str, INT_LEN, "%d", buf_len); + if ((rc = writen(fd, len_str, INT_LEN)) < 0) + return -1; + else if (rc != INT_LEN) + return 0; + nwritten += rc; + + if ((rc = writen(fd, buf, buf_len)) < 0) + return -1; + else if (rc != buf_len) + return 0; + nwritten += rc; + + if ((rc = write_byte(fd, '\r')) < 0) + return -1; + else if (rc != 1) + return 0; + + nwritten++; + if ((rc = write_byte(fd, '\n')) < 0) + return -1; + else if (rc != 1) + return 0; + nwritten++; + + return nwritten; +} + +int tcp_connect(const char *host, const char *port) +{ + struct addrinfo hints; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + struct addrinfo *ai; + int rc = getaddrinfo(host, port, &hints, &ai); + if (rc != 0) { + mprint("getaddrinfo() error: %s\n", gai_strerror(rc)); + return -1; + } + + struct addrinfo *p; + int sockfd; + /* Try each address until we sucessfully connect */ + for (p = ai; p != NULL; p = p->ai_next) { + sockfd = socket(p->ai_family, SOCK_STREAM, p->ai_protocol); + + if (-1 == sockfd) { + mprint("socket() error: %s\n", strerror(errno)); + if (p->ai_next != NULL) + mprint("trying next addres ..."); + + continue; + } + + if (connect(sockfd, p->ai_addr, p->ai_addrlen) == 0) + break; + + mprint("connect() error: %s\n", strerror(errno)); + if (p->ai_next != NULL) + mprint("trying next addres ..."); + + close(sockfd); + } + + freeaddrinfo(ai); + + if (NULL == p) + return -1; + + return sockfd; +} + +int ask_passwd(int sd) +{ + assert(srv_sd > 0); + + struct termios old, new; + int rc; + size_t len = 0; + char len_str[INT_LEN] = {0}; + int i; + char *pw = NULL; + + char ok; + + do { + do { + if (read_byte(sd, &ok) != 1) + { + fatal(EXIT_FAILURE, "read() error: %s", strerror(errno)); + } + if (OK == ok) + return 1; + } while(ok != PASSW); + + printf("Enter password: "); + fflush(stdout); + + if (tcgetattr(STDIN_FILENO, &old) != 0) + { + mprint("tcgetattr() error: %s\n", strerror(errno)); + } + + new = old; + new.c_lflag &= ~ECHO; + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &new) != 0) + { + mprint("tcgetattr() error: %s\n", strerror(errno)); + } + + rc = getline(&pw, &len, stdin); + rc--; /* -1 for \n */ + + if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &old) != 0) + { + mprint("tcgetattr() error: %s\n", strerror(errno)); + } + + printf("\n"); + fflush(stdout); + + if (write_block(sd, PASSW, pw, rc) < 0) + return -1; + + if (read_byte(sd, &ok) != 1) + return -1; + + if (WRONG_PASSW == ok) + { + printf("Wrong password\n"); + fflush(stdout); + } + + } while(OK != ok); + + return 1; +} + +ssize_t readn(int fd, void *vptr, size_t n) +{ + assert(n >= 0); + + size_t nleft; + ssize_t nread; + char *ptr; + + ptr = vptr; + nleft = n; + while (nleft > 0) + { + if (NULL == vptr) { + char c; + nread = read(fd, &c, 1); + } + else + { + nread = read(fd, ptr, nleft); + } + + if (nread < 0) + { + if (errno == EINTR) + { + nread = 0; + } + else + { + mprint("read() error: %s\n", strerror(errno)); + return -1; + } + } + else if (0 == nread) + { + break; /* EOF */ + } + + nleft -= nread; + ptr += nread; + } + + return n - nleft; +} + +ssize_t writen(int fd, const void *vptr, size_t n) +{ + assert(vptr != NULL); + assert(n > 0); + + size_t nleft; + ssize_t nwritten; + const char *ptr; + + ptr = vptr; + nleft = n; + while (nleft > 0) + { + if ((nwritten = write(fd, ptr, nleft)) < 0) + { + if (errno == EINTR) + { + nwritten = 0; + } + else + { + mprint("write() error: %s\n", strerror(errno)); + return -1; + } + } + else if (0 == nwritten) + { + break; + } + + nleft -= nwritten; + ptr += nwritten; + } + + return n; +} + +ssize_t write_byte(int fd, char ch) +{ + return writen(fd, &ch, 1); +} + +ssize_t read_byte(int fd, char *ch) +{ + assert(ch != 0); + + return readn(fd, ch, 1); +} diff --git a/src/networking.h b/src/networking.h new file mode 100644 index 00000000..fdb3f8ed --- /dev/null +++ b/src/networking.h @@ -0,0 +1,14 @@ +#ifndef NETWORKING_H +#define NETWORKING_H + +#include + +void connect_to_srv(const char *addr, const char *port); + +void net_append_cc(const char *fmt, ...); +void net_append_cc_n(const char *data, size_t len); +void net_send_cc(); + +void net_set_new_program(const char *name, size_t len); + +#endif /* end of include guard: NETWORKING_H */ diff --git a/src/params.c b/src/params.c index 4e8d7dda..fc4bae0b 100644 --- a/src/params.c +++ b/src/params.c @@ -1560,6 +1560,20 @@ void parse_parameters (int argc, char *argv[]) i++; continue; } + + if (strcmp (argv[i],"-sendto")==0 && i