diff --git a/linux/build b/linux/build index 385a8d61b..2700f7a58 100755 --- a/linux/build +++ b/linux/build @@ -95,7 +95,7 @@ if [ "$WITHOUT_RUST" = true ]; then echo "Building without rust files..." BLD_FLAGS="$BLD_FLAGS -DDISABLE_RUST" else - BLD_LINKER="$BLD_LINKER ./libccx_rust.a" + BLD_LINKER="$BLD_LINKER ./libccx_rust.a -lnanomsg" echo "Checking for cargo..." if ! [ -x "$(command -v cargo)" ]; then echo 'Error: cargo is not installed.' >&2 diff --git a/mac/build.command b/mac/build.command index 3d47d2533..cab51a78d 100755 --- a/mac/build.command +++ b/mac/build.command @@ -52,7 +52,7 @@ SRC_FREETYPE="../src/thirdparty/freetype/autofit/autofit.c \ ../src/thirdparty/freetype/type42/type42.c \ ../src/thirdparty/freetype/winfonts/winfnt.c" BLD_SOURCES="../src/ccextractor.c $SRC_API $SRC_CCX $SRC_LIB_HASH $SRC_LIBPNG $SRC_PROTOBUF $SRC_UTF8 $SRC_ZLIB $SRC_ZVBI $SRC_FREETYPE" -BLD_LINKER="-lm -liconv -lpthread -ldl `pkg-config --libs --silence-errors tesseract` `pkg-config --libs --silence-errors gpac`" +BLD_LINKER="-lm -liconv -lpthread -ldl -lnanomsg `pkg-config --libs --silence-errors tesseract` `pkg-config --libs --silence-errors gpac`" [[ $1 = "OCR" ]] && BLD_LINKER="$BLD_LINKER `pkg-config --libs --silence-errors tesseract` `pkg-config --libs --silence-errors lept`" ./pre-build.sh diff --git a/src/lib_ccx/ccx_share.c b/src/lib_ccx/ccx_share.c index e5026b9e2..e8c6865a2 100644 --- a/src/lib_ccx/ccx_share.c +++ b/src/lib_ccx/ccx_share.c @@ -15,19 +15,40 @@ #include ccx_share_service_ctx ccx_share_ctx; +#ifndef DISABLE_RUST +extern void ccxr_sub_entry_msg_cleanup_c(CcxSubEntryMessage *msg); +extern void ccxr_sub_entry_msg_print_c(const CcxSubEntryMessage *msg); +extern void ccxr_sub_entries_cleanup_c(ccx_sub_entries *entries); +extern void ccxr_sub_entries_print_c(const ccx_sub_entries *entries); +extern ccx_share_status ccxr_share_start_c(const char *stream_name); +extern ccx_share_status ccxr_share_stop_c(void); +extern ccx_share_status _ccxr_share_send_c(const CcxSubEntryMessage *msg); +extern ccx_share_status ccxr_share_send_c(const struct cc_subtitle *sub); +extern ccx_share_status ccxr_share_stream_done_c(const char *stream_name); +extern ccx_share_status _ccxr_share_sub_to_entries_c(const struct cc_subtitle *sub, ccx_sub_entries *entries); + +#endif void ccx_sub_entry_msg_cleanup(CcxSubEntryMessage *msg) { +#ifndef DISABLE_RUST + return ccxr_sub_entry_msg_cleanup_c(msg); +#else + for (int i = 0; i < msg->n_lines; i++) { free(msg->lines[i]); } free(msg->lines); free(msg->stream_name); +#endif } void ccx_sub_entry_msg_print(CcxSubEntryMessage *msg) { +#ifndef DISABLE_RUST + return ccxr_sub_entry_msg_print_c(msg); +#else if (!msg) { dbg_print(CCX_DMT_SHARE, "[share] print(!msg)\n"); @@ -55,6 +76,7 @@ void ccx_sub_entry_msg_print(CcxSubEntryMessage *msg) } dbg_print(CCX_DMT_SHARE, "[share] %s\n", msg->lines[i]); } +#endif } void ccx_sub_entries_init(ccx_sub_entries *entries) @@ -65,6 +87,9 @@ void ccx_sub_entries_init(ccx_sub_entries *entries) void ccx_sub_entries_cleanup(ccx_sub_entries *entries) { +#ifndef DISABLE_RUST + return ccxr_sub_entries_cleanup_c(entries); +#else for (int i = 0; i < entries->count; i++) { ccx_sub_entry_msg_cleanup(entries->messages + i); @@ -72,19 +97,27 @@ void ccx_sub_entries_cleanup(ccx_sub_entries *entries) free(entries->messages); entries->messages = NULL; entries->count = 0; +#endif } void ccx_sub_entries_print(ccx_sub_entries *entries) { +#ifndef DISABLE_RUST + return ccxr_sub_entries_print_c(entries); +#else dbg_print(CCX_DMT_SHARE, "[share] ccx_sub_entries_print (%u entries)\n", entries->count); for (int i = 0; i < entries->count; i++) { ccx_sub_entry_msg_print(entries->messages + i); } +#endif } ccx_share_status ccx_share_start(const char *stream_name) // TODO add stream { +#ifndef DISABLE_RUST + return ccxr_share_start_c(stream_name); +#else dbg_print(CCX_DMT_SHARE, "[share] ccx_share_start: starting service\n"); // TODO for multiple files we have to move creation to ccx_share_init ccx_share_ctx.nn_sock = nn_socket(AF_SP, NN_PUB); @@ -121,18 +154,26 @@ ccx_share_status ccx_share_start(const char *stream_name) // TODO add stream sleep(1); // We have to sleep a while, because it takes some time for subscribers to subscribe return CCX_SHARE_OK; +#endif } ccx_share_status ccx_share_stop() { +#ifndef DISABLE_RUST + return ccxr_share_stop_c(); +#else dbg_print(CCX_DMT_SHARE, "[share] ccx_share_stop: stopping service\n"); nn_shutdown(ccx_share_ctx.nn_sock, ccx_share_ctx.nn_binder); free(ccx_share_ctx.stream_name); return CCX_SHARE_OK; +#endif } ccx_share_status ccx_share_send(struct cc_subtitle *sub) { +#ifndef DISABLE_RUST + return ccxr_share_send_c(sub); +#else dbg_print(CCX_DMT_SHARE, "[share] ccx_share_send: sending\n"); ccx_sub_entries entries; ccx_sub_entries_init(&entries); @@ -154,10 +195,14 @@ ccx_share_status ccx_share_send(struct cc_subtitle *sub) ccx_sub_entries_cleanup(&entries); return CCX_SHARE_OK; +#endif } ccx_share_status _ccx_share_send(CcxSubEntryMessage *msg) { +#ifndef DISABLE_RUST + return _ccxr_share_send_c(msg); +#else dbg_print(CCX_DMT_SHARE, "[share] _ccx_share_send\n"); size_t len = ccx_sub_entry_message__get_packed_size(msg); void *buf = malloc(len); @@ -175,10 +220,14 @@ ccx_share_status _ccx_share_send(CcxSubEntryMessage *msg) free(buf); dbg_print(CCX_DMT_SHARE, "[share] _ccx_share_send: sent\n"); return CCX_SHARE_OK; +#endif } ccx_share_status ccx_share_stream_done(char *stream_name) { +#ifndef DISABLE_RUST + return ccxr_share_stream_done_c(stream_name); +#else CcxSubEntryMessage msg = CCX_SUB_ENTRY_MESSAGE__INIT; msg.eos = 1; msg.stream_name = strdup(stream_name); @@ -197,10 +246,14 @@ ccx_share_status ccx_share_stream_done(char *stream_name) ccx_sub_entry_msg_cleanup(&msg); return CCX_SHARE_OK; +#endif } ccx_share_status _ccx_share_sub_to_entries(struct cc_subtitle *sub, ccx_sub_entries *entries) { +#ifndef DISABLE_RUST + return _ccxr_share_sub_to_entries_c(sub, entries); +#else dbg_print(CCX_DMT_SHARE, "\n[share] _ccx_share_sub_to_entry\n"); if (sub->type == CC_608) { @@ -295,6 +348,7 @@ ccx_share_status _ccx_share_sub_to_entries(struct cc_subtitle *sub, ccx_sub_entr dbg_print(CCX_DMT_SHARE, "[share] done\n"); return CCX_SHARE_OK; +#endif } ccx_share_status ccx_share_launch_translator(char *langs, char *auth) diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock index 053d5d436..68c774fcd 100644 --- a/src/rust/Cargo.lock +++ b/src/rust/Cargo.lock @@ -60,6 +60,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "anyhow" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" + [[package]] name = "approx" version = "0.5.1" @@ -143,12 +149,27 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "bytes" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" + [[package]] name = "camino" version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +[[package]] +name = "cc" +version = "1.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333" +dependencies = [ + "shlex", +] + [[package]] name = "ccx_rust" version = "0.1.0" @@ -160,6 +181,7 @@ dependencies = [ "iconv", "leptonica-sys", "lib_ccxr", + "libc", "log", "num-integer", "palette", @@ -238,6 +260,15 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +[[package]] +name = "cmake" +version = "0.1.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c682c223677e0e5b6b7f63a64b9351844c3f1b1678a68b7ee617e30fb082620e" +dependencies = [ + "cc", +] + [[package]] name = "colorchoice" version = "1.0.2" @@ -314,14 +345,20 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + [[package]] name = "find-crate" version = "0.6.3" @@ -331,6 +368,12 @@ dependencies = [ "toml", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -340,6 +383,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + [[package]] name = "glob" version = "0.3.1" @@ -469,7 +518,14 @@ dependencies = [ "bitflags 2.6.0", "crc32fast", "derive_more", + "lazy_static", + "libc", + "nanomsg", + "nanomsg-sys", "num_enum", + "prost", + "prost-build", + "prost-types", "strum 0.26.3", "strum_macros 0.26.4", "thiserror", @@ -479,9 +535,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.158" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libloading" @@ -517,6 +573,34 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "multimap" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" + +[[package]] +name = "nanomsg" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617e0160fba522f8667df7bc79f3b4a74a0e3968d08023ebb3ce717a5f3bd3ac" +dependencies = [ + "libc", + "nanomsg-sys", +] + +[[package]] +name = "nanomsg-sys" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78aa3ccb6d007dfecb4f7070725c4b1670a87677babb6621cb0c8cce9cfdc004" +dependencies = [ + "cmake", + "gcc", + "libc", + "pkg-config", +] + [[package]] name = "nom" version = "7.1.3" @@ -620,6 +704,16 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap", +] + [[package]] name = "phf" version = "0.11.2" @@ -703,6 +797,58 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prost" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f3e5beed80eb580c68e2c600937ac2c4eedabdfd5ef1e5b7ea4f3fba84497b" +dependencies = [ + "heck 0.5.0", + "itertools", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn 2.0.75", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 2.0.75", +] + +[[package]] +name = "prost-types" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2f1e56baa61e93533aebc21af4d2134b70f66275e0fcdf3cbe43d77ff7e8fc" +dependencies = [ + "prost", +] + [[package]] name = "quote" version = "1.0.36" @@ -785,15 +931,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -920,6 +1066,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "termcolor" version = "1.4.1" diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml index da5e88652..b87687f3f 100644 --- a/src/rust/Cargo.toml +++ b/src/rust/Cargo.toml @@ -28,6 +28,7 @@ cfg-if = "1.0.0" num-integer = "0.1.45" lib_ccxr = { path = "lib_ccxr" } url = "2.5.2" +libc = "0.2.169" [build-dependencies] bindgen = "0.64.0" diff --git a/src/rust/lib_ccxr/Cargo.lock b/src/rust/lib_ccxr/Cargo.lock index 886ba28db..cf0274446 100644 --- a/src/rust/lib_ccxr/Cargo.lock +++ b/src/rust/lib_ccxr/Cargo.lock @@ -2,18 +2,57 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" + [[package]] name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "bytes" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" + +[[package]] +name = "cc" +version = "1.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333" +dependencies = [ + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cmake" +version = "0.1.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c682c223677e0e5b6b7f63a64b9351844c3f1b1678a68b7ee617e30fb082620e" +dependencies = [ + "cc", +] + [[package]] name = "convert_case" version = "0.4.0" @@ -51,12 +90,40 @@ dependencies = [ "syn", ] +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -66,6 +133,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + [[package]] name = "hashbrown" version = "0.14.5" @@ -90,20 +163,35 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "lib_ccxr" version = "0.1.0" @@ -111,7 +199,14 @@ dependencies = [ "bitflags", "crc32fast", "derive_more", + "lazy_static", + "libc", + "nanomsg", + "nanomsg-sys", "num_enum", + "prost", + "prost-build", + "prost-types", "strum", "strum_macros", "thiserror", @@ -119,12 +214,58 @@ dependencies = [ "url", ] +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "multimap" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" + +[[package]] +name = "nanomsg" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617e0160fba522f8667df7bc79f3b4a74a0e3968d08023ebb3ce717a5f3bd3ac" +dependencies = [ + "libc", + "nanomsg-sys", +] + +[[package]] +name = "nanomsg-sys" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78aa3ccb6d007dfecb4f7070725c4b1670a87677babb6621cb0c8cce9cfdc004" +dependencies = [ + "cmake", + "gcc", + "libc", + "pkg-config", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -164,12 +305,38 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "petgraph" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c94eb96835f05ec51384814c9b2daef83f68486f67a0e2e9680e0f698dca808e" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + [[package]] name = "powerfmt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "prettyplease" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -189,6 +356,58 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prost" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f3e5beed80eb580c68e2c600937ac2c4eedabdfd5ef1e5b7ea4f3fba84497b" +dependencies = [ + "heck", + "itertools", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2f1e56baa61e93533aebc21af4d2134b70f66275e0fcdf3cbe43d77ff7e8fc" +dependencies = [ + "prost", +] + [[package]] name = "quote" version = "1.0.36" @@ -198,6 +417,35 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + [[package]] name = "rustc_version" version = "0.4.0" @@ -207,6 +455,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.38.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "rustversion" version = "1.0.17" @@ -239,6 +500,12 @@ dependencies = [ "syn", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "strum" version = "0.26.3" @@ -260,15 +527,28 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.75" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys", +] + [[package]] name = "thiserror" version = "1.0.63" @@ -384,6 +664,79 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "winnow" version = "0.5.40" diff --git a/src/rust/lib_ccxr/Cargo.toml b/src/rust/lib_ccxr/Cargo.toml index 939010e14..c52bc34f5 100644 --- a/src/rust/lib_ccxr/Cargo.toml +++ b/src/rust/lib_ccxr/Cargo.toml @@ -15,6 +15,12 @@ strum = "0.26.3" strum_macros = "0.26.4" crc32fast = "1.3.2" num_enum = "0.6.1" +libc = "0.2.169" +nanomsg = "0.7.2" +prost = "0.13.4" +prost-types = "0.13.4" +lazy_static = "1.5.0" +nanomsg-sys = "0.7.2" [features] default = [ @@ -30,3 +36,6 @@ enable_ffmpeg = [] debug_out = [] debug = [] with_libcurl = [] + +[build-dependencies] +prost-build = "0.13.4" diff --git a/src/rust/lib_ccxr/src/lib.rs b/src/rust/lib_ccxr/src/lib.rs index 9f32678db..18c565fdc 100644 --- a/src/rust/lib_ccxr/src/lib.rs +++ b/src/rust/lib_ccxr/src/lib.rs @@ -5,3 +5,4 @@ pub mod subtitle; pub mod teletext; pub mod time; pub mod util; +pub mod share; \ No newline at end of file diff --git a/src/rust/lib_ccxr/src/share/ccxr_sub_entry_message.rs b/src/rust/lib_ccxr/src/share/ccxr_sub_entry_message.rs new file mode 100644 index 000000000..2cabcdc56 --- /dev/null +++ b/src/rust/lib_ccxr/src/share/ccxr_sub_entry_message.rs @@ -0,0 +1,16 @@ +// This file is @generated by prost-build. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CcxSubEntryMessage { + #[prost(int32, required, tag = "1")] + pub eos: i32, + #[prost(string, required, tag = "2")] + pub stream_name: ::prost::alloc::string::String, + #[prost(int64, required, tag = "3")] + pub counter: i64, + #[prost(int64, required, tag = "4")] + pub start_time: i64, + #[prost(int64, required, tag = "5")] + pub end_time: i64, + #[prost(string, repeated, tag = "7")] + pub lines: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +} diff --git a/src/rust/lib_ccxr/src/share/mod.rs b/src/rust/lib_ccxr/src/share/mod.rs new file mode 100644 index 000000000..8938af992 --- /dev/null +++ b/src/rust/lib_ccxr/src/share/mod.rs @@ -0,0 +1,3 @@ +pub mod share; +pub mod ccxr_sub_entry_message; +mod tests; diff --git a/src/rust/lib_ccxr/src/share/share.rs b/src/rust/lib_ccxr/src/share/share.rs new file mode 100644 index 000000000..c8759be03 --- /dev/null +++ b/src/rust/lib_ccxr/src/share/share.rs @@ -0,0 +1,510 @@ +use std::{ptr, thread, time::Duration, ffi::{CString, CStr}}; +use std::any::Any; +use std::cmp::PartialEq; +use std::io::Write; + +extern crate libc; +use std::os::raw::{c_char, c_int, c_void}; +use std::sync::{Mutex, Once}; +use std::thread::sleep; +use crate::util::log::{debug, info, DebugMessageFlag}; +use crate::share::ccxr_sub_entry_message::CcxSubEntryMessage; +use nanomsg::{Endpoint, Protocol, Socket}; +use lazy_static::lazy_static; +use prost::Message; + +// use crate::bindings::{cc_subtitle, ccx_output_format}; + +pub const CCX_DECODER_608_SCREEN_ROWS: usize = 15; +pub const CCX_DECODER_608_SCREEN_WIDTH: usize = 32; + +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum CcxEia608Format { + SFormatCcScreen = 0, + SFormatCcLine = 1, + SFormatXds = 2, +} + +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum CcModes { + ModePopOn = 0, + ModeRollUp2 = 1, + ModeRollUp3 = 2, + ModeRollUp4 = 3, + ModeText = 4, + ModePaintOn = 5, + ModeFakeRollUp1 = 100, +} + + +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum CcxDecoder608ColorCode { + White = 0, + Green = 1, + Blue = 2, + Cyan = 3, + Red = 4, + Yellow = 5, + Magenta = 6, + UserDefined = 7, + Black = 8, + Transparent = 9, + Max = 10, +} + +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum FontBits { + Normal = 0, + Italics = 1, + Underline = 2, + UnderlineItalics = 3, +} + + +#[repr(C)] +#[derive(Debug, Clone)] +pub struct Eia608Screen { + pub format: CcxEia608Format, + pub characters: [[c_char; CCX_DECODER_608_SCREEN_WIDTH + 1]; CCX_DECODER_608_SCREEN_ROWS], + pub colors: [[CcxDecoder608ColorCode; CCX_DECODER_608_SCREEN_WIDTH + 1]; CCX_DECODER_608_SCREEN_ROWS], + pub fonts: [[FontBits; CCX_DECODER_608_SCREEN_WIDTH + 1]; CCX_DECODER_608_SCREEN_ROWS], + pub row_used: [c_int; CCX_DECODER_608_SCREEN_ROWS], + pub empty: c_int, + pub start_time: i64, + pub end_time: i64, + pub mode: CcModes, + pub channel: c_int, + pub my_field: c_int, + pub xds_str: *mut c_char, + pub xds_len: usize, + pub cur_xds_packet_class: c_int, +} + +impl Default for Eia608Screen { + fn default() -> Self { + Self { + format: CcxEia608Format::SFormatCcScreen, + characters: [[0; CCX_DECODER_608_SCREEN_WIDTH + 1]; CCX_DECODER_608_SCREEN_ROWS], + colors: [[CcxDecoder608ColorCode::Black; CCX_DECODER_608_SCREEN_WIDTH + 1]; CCX_DECODER_608_SCREEN_ROWS], + fonts: [[FontBits::Normal; CCX_DECODER_608_SCREEN_WIDTH + 1]; CCX_DECODER_608_SCREEN_ROWS], + row_used: [0; CCX_DECODER_608_SCREEN_ROWS], + empty: 1, + start_time: 0, + end_time: 0, + mode: CcModes::ModePopOn, + channel: 0, + my_field: 0, + xds_str: std::ptr::null_mut(), + xds_len: 0, + cur_xds_packet_class: 0, + } + } +} + +impl Eia608Screen { + pub fn new() -> Self { + Self::default() + } + + pub fn set_xds_str(&mut self, xds: &str) { + let c_string = CString::new(xds).expect("CString::new failed"); + self.xds_str = c_string.into_raw(); + self.xds_len = xds.len(); + } + + pub fn free_xds_str(&mut self) { + if !self.xds_str.is_null() { + unsafe { + CString::from_raw(self.xds_str); + } + self.xds_str = std::ptr::null_mut(); + self.xds_len = 0; + } + } +} + +impl Drop for Eia608Screen { + fn drop(&mut self) { + self.free_xds_str(); + } +} + + +pub type subdatatype = ::std::os::raw::c_uint; +pub type LLONG = i64; +pub const subtype_CC_BITMAP: subtype = 0; +pub const subtype_CC_608: subtype = 1; +pub const subtype_CC_TEXT: subtype = 2; +pub const subtype_CC_RAW: subtype = 3; +pub type subtype = ::std::os::raw::c_uint; + +pub type ccx_encoding_type = ::std::os::raw::c_uint; + +pub struct CcSubtitle { + #[doc = " A generic data which contain data according to decoder\n @warn decoder cant output multiple types of data"] + pub data: *mut ::std::os::raw::c_void, + pub datatype: subdatatype, + #[doc = " number of data"] + pub nb_data: ::std::os::raw::c_uint, + #[doc = " type of subtitle"] + pub type_: subtype, + #[doc = " Encoding type of Text, must be ignored in case of subtype as bitmap or cc_screen"] + pub enc_type: ccx_encoding_type, + pub start_time: LLONG, + pub end_time: LLONG, + pub flags: ::std::os::raw::c_int, + pub lang_index: ::std::os::raw::c_int, + #[doc = " flag to tell that decoder has given output"] + pub got_output: ::std::os::raw::c_int, + pub mode: [::std::os::raw::c_char; 5usize], + pub info: [::std::os::raw::c_char; 4usize], + #[doc = " Used for DVB end time in ms"] + pub time_out: ::std::os::raw::c_int, + pub next: *mut CcSubtitle, + pub prev: *mut CcSubtitle, +} + + +#[derive(Debug)] +pub enum CcxShareStatus { + Ok = 0, + Fail, +} +impl PartialEq for CcxShareStatus { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (CcxShareStatus::Ok, CcxShareStatus::Ok) => true, + (CcxShareStatus::Fail, CcxShareStatus::Fail) => true, + _ => false, + } + } +} +pub struct CcxShareServiceCtx { + counter: i64, + stream_name: Option, + nn_sock: Option, + nn_binder: Option, +} + +pub struct CcxSubEntries { + pub messages: Vec, +} +impl CcxShareServiceCtx { + fn new() -> Self { + CcxShareServiceCtx { + counter: 0, + stream_name: None, + nn_sock: None, + nn_binder: None, + } + } +} + + +lazy_static! { + pub static ref CCX_SHARE_CTX: Mutex = Mutex::new(CcxShareServiceCtx::new()); +} + + + +pub fn ccxr_sub_entry_msg_init(msg: &mut CcxSubEntryMessage) { + msg.eos = 0; + msg.stream_name = "".parse().unwrap(); + msg.counter = 0; + msg.start_time = 0; + msg.end_time = 0; + msg.lines.clear(); +} + + +pub fn ccxr_sub_entry_msg_cleanup(msg: &mut CcxSubEntryMessage) { + msg.lines.clear(); + msg.stream_name = "".parse().unwrap(); +} + +pub fn ccxr_sub_entry_msg_print(msg: &CcxSubEntryMessage) { + if msg.lines.is_empty() { + debug!(msg_type = DebugMessageFlag::SHARE; "[share] no lines allocated"); + return; + } + + debug!(msg_type = DebugMessageFlag::SHARE; "\n[share] sub msg #{}", msg.counter); + if !msg.stream_name.is_empty() { + debug!(msg_type = DebugMessageFlag::SHARE; "[share] name: {}", msg.stream_name); + } else { + debug!(msg_type = DebugMessageFlag::SHARE; "[share] name: None"); + } + debug!(msg_type = DebugMessageFlag::SHARE; "[share] start: {}", msg.start_time); + debug!(msg_type = DebugMessageFlag::SHARE; "[share] end: {}", msg.end_time); + debug!(msg_type = DebugMessageFlag::SHARE; "[share] lines count: {}", msg.lines.len()); + + if msg.lines.is_empty() { + debug!(msg_type = DebugMessageFlag::SHARE; "[share] no lines allocated"); + return; + } + for (i, line) in msg.lines.iter().enumerate() { + if !line.is_empty() { + debug!(msg_type = DebugMessageFlag::SHARE; "[share] line[{}]: {}", i, line); + } else { + debug!(msg_type = DebugMessageFlag::SHARE; "[share] line[{}] is not allocated", i); + } + } +} + + + +pub fn ccxr_sub_entries_cleanup(entries: &mut CcxSubEntries) { + entries.messages.clear(); +} + +pub fn ccxr_sub_entries_print(entries: &CcxSubEntries) { + eprintln!("[share] ccxr_sub_entries_print ({} entries)", entries.messages.len()); + for message in &entries.messages { + ccxr_sub_entry_msg_print(message); + } +} + +pub fn ccxr_share_start(stream_name: &str) -> CcxShareStatus { + debug!(msg_type = DebugMessageFlag::SHARE; "[share] ccx_share_start: starting service\n"); + let mut ctx = CCX_SHARE_CTX.lock().unwrap(); + ctx.nn_sock = Some(Socket::new(Protocol::Pub).unwrap()); + if (ctx.nn_sock.is_none()) { + debug!(msg_type = DebugMessageFlag::SHARE; "[share] ccx_share_start: can't nn_socket()\n"); + return CcxShareStatus::Fail; + } + /// TODO translating this line + /// if (!ccx_options.sharing_url) + /// { + /// ccx_options.sharing_url = "tcp://*:3269"; + /// } + /// dbg_print(CCX_DMT_SHARE, "[share] ccx_share_start: url=%s\n", ccx_options.sharing_url); + let sharing_url = "tcp://*:3269"; // Default URL if none is provided + debug!(msg_type = DebugMessageFlag::SHARE; "[share] ccx_share_start: url={}", sharing_url); + let binder = match ctx.nn_sock.as_mut().unwrap().bind(sharing_url) { + Ok(endpoint) => endpoint, + Err(err) => { + eprintln!("[share] ccx_share_start: can't bind to URL: {}", err); + return CcxShareStatus::Fail; + } + }; + ctx.nn_binder = Some(binder); + if let Err(err) = ctx.nn_sock.as_mut().unwrap().set_linger(-1) { + eprintln!("[share] ccx_share_start: can't set linger option: {}", err); + return CcxShareStatus::Fail; + } + ctx.stream_name = Some(stream_name.parse().unwrap()); + sleep(Duration::from_secs(1)); + + CcxShareStatus::Ok +} + + +pub fn ccxr_share_stop() -> CcxShareStatus { + let mut ctx = CCX_SHARE_CTX.lock().unwrap(); + + debug!(msg_type = DebugMessageFlag::SHARE; "[share] ccx_share_stop: stopping service"); + + + if let Some(mut binder) = ctx.nn_binder.take() { + if let Err(err) = binder.shutdown() { + debug!(msg_type = DebugMessageFlag::SHARE; "[share] ccx_share_stop: error during shutdown: {}", err); + return CcxShareStatus::Fail; + } + } + ctx.stream_name = None; + ctx.nn_sock = None; + ctx.nn_binder = None; + + CcxShareStatus::Ok +} + + +pub fn ccxr_share_send(sub: &CcSubtitle) -> CcxShareStatus { + debug!(msg_type = DebugMessageFlag::SHARE; "[share] ccx_share_send: sending"); + + let mut entries = CcxSubEntries { messages: Vec::new() }; + ccxr_sub_entries_cleanup(&mut entries); + + if ccxr_share_sub_to_entries(sub, &mut entries) == CcxShareStatus::Fail { + debug!(msg_type = DebugMessageFlag::SHARE; "[share] failed to convert subtitle to entries"); + return CcxShareStatus::Fail; + } + + ccxr_sub_entries_print(&entries); + debug!(msg_type = DebugMessageFlag::SHARE; "[share] entry obtained:"); + + for i in 0..entries.messages.len() { + debug!(msg_type = DebugMessageFlag::SHARE; "[share] ccx_share_send: sending entry {}", i); + + if entries.messages[i].lines.is_empty() { + debug!(msg_type = DebugMessageFlag::SHARE; "[share] skipping empty message"); + continue; + } + + if _ccxr_share_send(&entries.messages[i]) != CcxShareStatus::Ok { + debug!(msg_type = DebugMessageFlag::SHARE; "[share] can't send message"); + ccxr_sub_entries_cleanup(&mut entries); + return CcxShareStatus::Fail; + } + } + + ccxr_sub_entries_cleanup(&mut entries); + + CcxShareStatus::Ok +} + +pub fn ccxr_sub_entry_message_get_packed_size(message: &CcxSubEntryMessage) -> usize { + message.encoded_len() +} + +pub fn ccxr_sub_entry_message_pack(message: &CcxSubEntryMessage, buf: &mut Vec) -> Result<(), prost::EncodeError> { + message.encode(buf) +} + + +pub fn _ccxr_share_send(msg: &CcxSubEntryMessage) -> CcxShareStatus { + debug!(msg_type = DebugMessageFlag::SHARE; "[share] _ccx_share_send"); + let len = ccxr_sub_entry_message_get_packed_size(msg); + let mut buf = Vec::with_capacity(len); + debug!(msg_type = DebugMessageFlag::SHARE; "[share] _ccx_share_send: packing"); + ccxr_sub_entry_message_pack(msg, &mut buf); + debug!(msg_type = DebugMessageFlag::SHARE; "[share] _ccx_share_send: sending"); + let mut ctx = CCX_SHARE_CTX.lock().unwrap(); + if let Some(sock) = ctx.nn_sock.as_mut() { + match sock.write(&buf) { + Ok(sent) => { + if sent != buf.len() { + debug!( + msg_type = DebugMessageFlag::SHARE; + "[share] _ccx_share_send: len={} sent={}", buf.len(), sent + ); + return CcxShareStatus::Fail; + } + } + Err(err) => { + eprintln!("[share] _ccx_share_send: failed to send message: {}", err); + return CcxShareStatus::Fail; + } + } + } else { + eprintln!("[share] _ccx_share_send: no active socket available"); + return CcxShareStatus::Fail; + } + + debug!(msg_type = DebugMessageFlag::SHARE; "[share] _ccx_share_send: sent"); + CcxShareStatus::Ok +} + + +pub fn ccxr_share_stream_done(stream_name: &str) -> CcxShareStatus { + let mut msg = CcxSubEntryMessage { + eos: 1, + stream_name: stream_name.parse().unwrap(), + counter: 0, + start_time: 0, + end_time: 0, + lines: Vec::new(), + }; + + + let mut ctx = CCX_SHARE_CTX.lock().unwrap(); + + if _ccxr_share_send(&msg) != CcxShareStatus::Ok { + ccxr_sub_entry_msg_cleanup(&mut msg); + debug!(msg_type = DebugMessageFlag::SHARE; "[share] ccx_share_stream_done: can't send message"); + return CcxShareStatus::Fail; + } + debug!(msg_type = DebugMessageFlag::SHARE; "[share] ccx_share_stream_done: message sent successfully"); + CcxShareStatus::Ok +} + +pub fn ccxr_share_sub_to_entries(sub: &CcSubtitle, entries: &mut CcxSubEntries) -> CcxShareStatus { + unsafe { + let mut ctx = CCX_SHARE_CTX.lock().unwrap(); + + debug!(msg_type = DebugMessageFlag::SHARE; "[share] _ccx_share_sub_to_entries"); + + if sub.type_ == subtype_CC_608 { + debug!(msg_type = DebugMessageFlag::SHARE; "[share] CC_608"); + + let data_ptr = sub.data as *const Eia608Screen; + let mut nb_data = sub.nb_data; + + while nb_data > 0 { + let data = &*data_ptr.add(sub.nb_data as usize - nb_data as usize); + + debug!(msg_type = DebugMessageFlag::SHARE; "[share] data item"); + + if data.format == CcxEia608Format::SFormatXds { + debug!(msg_type = DebugMessageFlag::SHARE; "[share] XDS. Skipping"); + nb_data -= 1; + continue; + } + + if data.start_time == 0 { + debug!(msg_type = DebugMessageFlag::SHARE; "[share] No start time. Skipping"); + break; + } + + entries.messages.push(CcxSubEntryMessage { + eos: 0, + stream_name: String::new(), + counter: ctx.counter + 1, + start_time: data.start_time, + end_time: data.end_time, + lines: Vec::new(), + }); + + let entry_index = entries.messages.len() - 1; + let entry = &mut entries.messages[entry_index]; + + for row in 0..CCX_DECODER_608_SCREEN_ROWS { + if data.row_used[row] != 0 { + let characters = CStr::from_ptr(data.characters[row].as_ptr()) + .to_string_lossy() + .to_string(); + entry.lines.push(characters); + } + } + + if entry.lines.is_empty() { + debug!(msg_type = DebugMessageFlag::SHARE; "[share] buffer is empty"); + entries.messages.pop(); + return CcxShareStatus::Ok; + } + + debug!( + msg_type = DebugMessageFlag::SHARE; + "[share] Copied {} lines", entry.lines.len() + ); + + ctx.counter += 1; + nb_data -= 1; + + debug!(msg_type = DebugMessageFlag::SHARE; "[share] item done"); + } + } else { + match sub.type_ { + subtype_CC_BITMAP => { + debug!(msg_type = DebugMessageFlag::SHARE; "[share] CC_BITMAP. Skipping"); + } + subtype_CC_RAW => { + debug!(msg_type = DebugMessageFlag::SHARE; "[share] CC_RAW. Skipping"); + } + subtype_CC_TEXT => { + debug!(msg_type = DebugMessageFlag::SHARE; "[share] CC_TEXT. Skipping"); + } + _ => { + debug!(msg_type = DebugMessageFlag::SHARE; "[share] Unknown subtitle type"); + } + } + } + + debug!(msg_type = DebugMessageFlag::SHARE; "[share] done"); + CcxShareStatus::Ok + } +} diff --git a/src/rust/lib_ccxr/src/share/tests.rs b/src/rust/lib_ccxr/src/share/tests.rs new file mode 100644 index 000000000..5eee95493 --- /dev/null +++ b/src/rust/lib_ccxr/src/share/tests.rs @@ -0,0 +1,268 @@ +mod test { + use libc::c_char; + use crate::share::share::*; + use crate::share::ccxr_sub_entry_message::*; + use std::sync::Once; + use crate::util::log::{set_logger, CCExtractorLogger, DebugMessageFlag, DebugMessageMask, OutputTarget}; + + + use super::*; + + static INIT: Once = Once::new(); + + fn initialize_logger() { + INIT.call_once(|| { + set_logger(CCExtractorLogger::new( + OutputTarget::Stdout, + DebugMessageMask::new(DebugMessageFlag::VERBOSE, DebugMessageFlag::VERBOSE), + false, + )) + .ok(); + }); + } + #[test] + fn test_ffi_sub_entry_msg_cleanup() { + let mut msg = CcxSubEntryMessage { + eos: 0, + stream_name: "test".to_string(), + counter: 0, + start_time: 0, + end_time: 0, + lines: vec!["test".to_string()], + }; + + unsafe { + ccxr_sub_entry_msg_cleanup(&mut *(&mut msg as *mut CcxSubEntryMessage)); + } + + assert!(msg.lines.is_empty()); + assert_eq!(msg.stream_name, ""); + } + + #[test] + fn test_ffi_sub_entry_msg_print() { + let msg = CcxSubEntryMessage { + eos: 0, + stream_name: "test".to_string(), + counter: 0, + start_time: 0, + end_time: 0, + lines: vec![ + "test".to_string(), + "test".to_string(), + "test".to_string(), + ], + }; + + unsafe { + ccxr_sub_entry_msg_print(&*(&msg as *const CcxSubEntryMessage)); + } + } + #[test] + fn test_ffi_sub_entries_init() { + let mut entries = CcxSubEntries { + messages: vec![CcxSubEntryMessage { + eos: 0, + counter: 1, + stream_name: "Test".parse().unwrap(), + start_time: 0, + end_time: 0, + lines: vec![], + }], + }; + + unsafe { + ccxr_sub_entries_cleanup(&mut *(&mut entries as *mut CcxSubEntries)); + } + + assert!(entries.messages.is_empty()); + } + + #[test] + fn test_ffi_sub_entries_cleanup() { + let mut entries = CcxSubEntries { + messages: vec![CcxSubEntryMessage { + eos: 0, + counter: 1, + stream_name: "Test".parse().unwrap(), + start_time: 0, + end_time: 0, + lines: vec![], + }], + }; + + unsafe { + ccxr_sub_entries_cleanup(&mut *(&mut entries as *mut CcxSubEntries)); + } + + assert!(entries.messages.is_empty()); + } + + #[test] + fn test_ffi_sub_entries_print() { + let entries = CcxSubEntries { + messages: vec![CcxSubEntryMessage { + eos: 0, + counter: 1, + stream_name: "Test".parse().unwrap(), + start_time: 0, + end_time: 0, + lines: vec![], + }], + }; + + unsafe { + ccxr_sub_entries_print(&*(&entries as *const CcxSubEntries)); + } + + } + #[test] + fn test_ccxr_share_stop() { + let status = ccxr_share_stop(); + assert_eq!(status, CcxShareStatus::Ok); + } + + #[test] + fn test_ccxr_share_send() { + initialize_logger(); + + let sub = CcSubtitle { + data: std::ptr::null_mut(), + datatype: 1, + nb_data: 0, + type_: 1, + enc_type: 1, + start_time: 0, + end_time: 0, + flags: 0, + lang_index: 0, + got_output: 0, + mode: [0; 5], + info: [0; 4], + time_out: 0, + next: std::ptr::null_mut(), + prev: std::ptr::null_mut(), + + }; + let status = ccxr_share_send(&sub); + assert!(matches!(status, CcxShareStatus::Ok | CcxShareStatus::Fail)); + } + + #[test] + fn test_ccxr_share_stream_done() { + let status = ccxr_share_stream_done("test_stream"); + assert_eq!(status, CcxShareStatus::Ok); + } + #[test] + fn test_ccxr_share_send_c() { + initialize_logger(); + let msg = CcxSubEntryMessage { + eos: 0, + stream_name: "test_stream".to_string(), + counter: 1, + start_time: 0, + end_time: 0, + lines: vec!["Line 1".to_string(), "Line 2".to_string()], + }; + + let status = unsafe { _ccxr_share_send(&*(&msg as *const CcxSubEntryMessage)) }; + assert!(matches!(status, CcxShareStatus::Ok | CcxShareStatus::Fail)); + } + + + const CCX_DECODER_608_SCREEN_WIDTH: usize = 32; + const CCX_DECODER_608_SCREEN_ROWS: usize = 15; + + #[test] + fn test_ccxr_share_sub_to_entries() { + const NUM_ROWS: usize = CCX_DECODER_608_SCREEN_ROWS; + let mut screen = Eia608Screen::new(); + screen.row_used[0] = 1; + screen.row_used[2] = 1; + screen.row_used[4] = 1; + + let row_0_content = b"Hello, World!"; + let row_2_content = b"Subtitle line 2"; + let row_4_content = b"Subtitle line 3"; + + for (i, &ch) in row_0_content.iter().enumerate() { + screen.characters[0][i] = ch as c_char; + } + for (i, &ch) in row_2_content.iter().enumerate() { + screen.characters[2][i] = ch as c_char; + } + for (i, &ch) in row_4_content.iter().enumerate() { + screen.characters[4][i] = ch as c_char; + } + + screen.start_time = 1000; + screen.end_time = 2000; + screen.mode = CcModes::ModePaintOn; + screen.channel = 1; + screen.my_field = 42; + + let sub = CcSubtitle { + data: &screen as *const _ as *mut ::std::os::raw::c_void, + datatype: 1, + nb_data: 1, + type_: subtype_CC_608, + enc_type: 1, + start_time: 0, + end_time: 0, + flags: 0, + lang_index: 0, + got_output: 1, + mode: [b'M' as c_char, b'O' as c_char, b'D' as c_char, b'E' as c_char, 0], + info: [b'I' as c_char, b'N' as c_char, b'F' as c_char, 0], + time_out: 0, + next: std::ptr::null_mut(), + prev: std::ptr::null_mut(), + }; + let mut entries = CcxSubEntries { messages: Vec::new() }; + let status = ccxr_share_sub_to_entries(&sub, &mut entries); + assert_eq!(status, CcxShareStatus::Ok, "Function should return OK status"); + assert_eq!(entries.messages.len(), 1, "There should be one entry in messages"); + + let message = &entries.messages[0]; + assert_eq!(message.start_time, 1000, "Start time should match input"); + assert_eq!(message.end_time, 2000, "End time should match input"); + assert_eq!(message.lines.len(), 3, "There should be 3 lines of content"); + + assert_eq!(message.lines[0], "Hello, World!", "First line content mismatch"); + assert_eq!(message.lines[1], "Subtitle line 2", "Second line content mismatch"); + assert_eq!(message.lines[2], "Subtitle line 3", "Third line content mismatch"); + } + + #[test] + fn test_ccxr_share_sub_to_entries_empty_rows() { + let mut screen = Eia608Screen::new(); + + screen.start_time = 1000; + screen.end_time = 2000; + + let sub = CcSubtitle { + data: &screen as *const _ as *mut ::std::os::raw::c_void, + datatype: 1, + nb_data: 1, + type_: subtype_CC_608, + enc_type: 1, + start_time: 0, + end_time: 0, + flags: 0, + lang_index: 0, + got_output: 1, + mode: [b'M' as c_char, b'O' as c_char, b'D' as c_char, b'E' as c_char, 0], + info: [b'I' as c_char, b'N' as c_char, b'F' as c_char, 0], + time_out: 0, + next: std::ptr::null_mut(), + prev: std::ptr::null_mut(), + }; + + let mut entries = CcxSubEntries { messages: Vec::new() }; + + let status = ccxr_share_sub_to_entries(&sub, &mut entries); + + assert_eq!(status, CcxShareStatus::Ok, "Function should return OK status"); + assert_eq!(entries.messages.len(), 0, "There should be no messages for empty rows"); + } +} diff --git a/src/rust/src/libccxr_exports/mod.rs b/src/rust/src/libccxr_exports/mod.rs index c2f64a258..638e5621b 100644 --- a/src/rust/src/libccxr_exports/mod.rs +++ b/src/rust/src/libccxr_exports/mod.rs @@ -1,6 +1,8 @@ //! Provides C-FFI functions that are direct equivalent of functions available in C. pub mod time; +pub mod share; + use crate::ccx_options; use lib_ccxr::util::log::*; use lib_ccxr::util::{bits::*, levenshtein::*}; diff --git a/src/rust/src/libccxr_exports/share.rs b/src/rust/src/libccxr_exports/share.rs new file mode 100644 index 000000000..57ae74391 --- /dev/null +++ b/src/rust/src/libccxr_exports/share.rs @@ -0,0 +1,115 @@ +use std::ffi::CStr; +use lib_ccxr::share::share::*; +use lib_ccxr::share::ccxr_sub_entry_message::*; +use lib_ccxr::util::log::{debug, info, DebugMessageFlag}; +/// C-compatible function to clean up a `CcxSubEntryMessage`. +#[no_mangle] +pub extern "C" fn ccxr_sub_entry_msg_cleanup_c(msg: *mut CcxSubEntryMessage) { + unsafe { + if msg.is_null() { + return; + } + let msg = &mut *msg; + ccxr_sub_entry_msg_cleanup(msg); + } +} + +/// C-compatible function to print a `CcxSubEntryMessage`. +#[no_mangle] +pub unsafe extern "C" fn ccxr_sub_entry_msg_print_c(msg: *const CcxSubEntryMessage) { + if msg.is_null() { + debug!(msg_type = DebugMessageFlag::SHARE; "[share] print(!msg)\n"); + return; + } + + let msg = &*msg; + + // Call the main Rust function + ccxr_sub_entry_msg_print(msg); +} + + +#[no_mangle] +pub unsafe extern "C" fn ccxr_sub_entries_cleanup_c(entries: *mut CcxSubEntries) { + if entries.is_null() { + return; + } + let entries = &mut *entries; + ccxr_sub_entries_cleanup(entries); +} + +#[no_mangle] +pub unsafe extern "C" fn ccxr_sub_entries_print_c(entries: *const CcxSubEntries) { + if entries.is_null() { + debug!(msg_type = DebugMessageFlag::SHARE; "[share] ccxr_sub_entries_print (null entries)\n"); + return; + } + let entries = &*entries; + ccxr_sub_entries_print(entries); +} + +/// C-compatible function to start the sharing service. +#[no_mangle] +pub unsafe extern "C" fn ccxr_share_start_c(stream_name: *const libc::c_char) -> CcxShareStatus { + if stream_name.is_null() { + return ccxr_share_start("unknown"); + } + + let c_str = CStr::from_ptr(stream_name); + let stream_name = match c_str.to_str() { + Ok(name) => name, + Err(_) => return CcxShareStatus::Fail, + }; + + ccxr_share_start(stream_name) +} +#[no_mangle] +pub unsafe extern "C" fn ccxr_share_stop_c() -> CcxShareStatus { + ccxr_share_stop() +} + +#[no_mangle] +pub unsafe extern "C" fn _ccxr_share_send_c(msg: *const CcxSubEntryMessage) -> CcxShareStatus { + if msg.is_null() { + return CcxShareStatus::Fail; + } + _ccxr_share_send(&*msg) +} + +#[no_mangle] +pub unsafe extern "C" fn ccxr_share_send_c(sub: *const CcSubtitle) -> CcxShareStatus { + if sub.is_null() { + return CcxShareStatus::Fail; + } + ccxr_share_send(&*sub) +} + +#[no_mangle] +pub unsafe extern "C" fn ccxr_share_stream_done_c(stream_name: *const libc::c_char) -> CcxShareStatus { + if stream_name.is_null() { + return CcxShareStatus::Fail; + } + + let c_str = CStr::from_ptr(stream_name); + match c_str.to_str() { + Ok(name) => ccxr_share_stream_done(name), + Err(_) => CcxShareStatus::Fail, + } +} + +/// C-compatible function to convert subtitles to sub-entry messages. +#[no_mangle] +pub unsafe extern "C" fn _ccxr_share_sub_to_entries_c( + sub: *const CcSubtitle, + entries: *mut CcxSubEntries, +) -> CcxShareStatus { + if sub.is_null() || entries.is_null() { + return CcxShareStatus::Fail; + } + + // Dereference the pointers safely + let sub_ref = &*sub; + let entries_ref = &mut *entries; + + ccxr_share_sub_to_entries(sub_ref, entries_ref) +}