diff --git a/CHANGES.md b/CHANGES.md index b817a47..af09f4b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,14 +11,21 @@ ## develop +## 2021.13 + +- [CHANGE] data_channel_messaging を data_channles へ変更する + - @torikizi + ## 2021.12 -- [UPDATE] `CLI11` を `2.1.1` に上げる - - @voluntas +- [UPDATE] `CLI11` を `2.1.2` に上げる + - @voluntas @melpon - [UPDATE] libwebrtc のバージョンを `m94.4606.3.4` に上げる - @voluntas - [FIX] Let's Encrypt な証明書の SSL 接続が失敗する問題を修正する - @melpon +- [ADD] DataChannel メッセージングのデータに時刻とカウンターを追加 + - @melpon ## 2021.11 diff --git a/README.md b/README.md index 256cc5a..cae1e03 100644 --- a/README.md +++ b/README.md @@ -118,8 +118,8 @@ Options: Signaling metadata used in connect message --sora-signaling-notify-metadata TEXT:JSON Value Signaling metadata - --sora-data-channel-messaging TEXT:JSON Value - DataChannel messaging + --sora-data-channels TEXT:JSON Value + DataChannels --fake-network-send-queue-length-packets UINT Queue length in number of packets for sending --fake-network-send-queue-delay-ms INT @@ -186,3 +186,15 @@ limitations under the License. [ゲーム等に使えるフリー声素材配布ページ \- あみたろの声素材工房](https://www14.big.or.jp/~amiami/happy/voice.html) +## 優先実装 + +優先実装とは Sora のライセンスを契約頂いているお客様限定で Momo の実装予定機能を有償にて前倒しで実装することです。 + +### 優先実装が可能な機能一覧 + +**詳細は Discord やメールなどでお気軽にお問い合わせください** + +- Content Hint への対応 +- --fake-video-capture で mjpeg も指定可能にする +- --audio-device 追加 + diff --git a/VERSION b/VERSION index 0874557..8aa26d7 100644 --- a/VERSION +++ b/VERSION @@ -1,7 +1,7 @@ -ZAKURO_VERSION=2021.12 +ZAKURO_VERSION=2021.13 WEBRTC_BUILD_VERSION=94.4606.3.4 BOOST_VERSION=1.77.0 -CLI11_VERSION=2.1.1 +CLI11_VERSION=2.1.2 CMAKE_VERSION=3.21.3 BLEND2D_VERSION=3a0299c9126d19759a483ac3267a52b50ec77141 ASMJIT_VERSION=d0d14ac774977d0060a351f66e35cb57ba0bf59c diff --git a/doc/SUPPORT.md b/doc/SUPPORT.md index b85f999..fee6006 100644 --- a/doc/SUPPORT.md +++ b/doc/SUPPORT.md @@ -8,7 +8,7 @@ 最新の状況などは Discord で共有しています。 -https://discord.gg/eEUZf6j +https://discord.gg/shiguredo ## バグ報告 diff --git a/doc/USE.md b/doc/USE.md index d0fb4bf..85d54ce 100644 --- a/doc/USE.md +++ b/doc/USE.md @@ -171,13 +171,30 @@ zakuro: role: sendrecv multistream: true data-channel-signaling: true - data-channel-messaging: + data-channels: - label: "#test" direction: "sendrecv" # 省略時は 500 (ms) interval: 1000 - # 省略時は 10 (bytes) - size_min: 10 - # 省略時は 10 (bytes) + # 省略時は 16 (bytes) + size_min: 16 + # 省略時は 16 (bytes) size_max: 100 -``` \ No newline at end of file +``` + +### 複数シグナリング URL + +```yaml +zakuro: + instances: + - name: zakuro + vcs: 2 + sora: + signaling-url: + - "wss://sora1.example.com/signaling" + - "wss://sora2.example.com/signaling" + - "wss://sora3.example.com/signaling" + channel-id: sora + role: sendrecv + multistream: true +``` diff --git a/src/scenario_player.h b/src/scenario_player.h index 546a4e0..87e82f5 100644 --- a/src/scenario_player.h +++ b/src/scenario_player.h @@ -1,6 +1,7 @@ #ifndef SCENARIO_PLAYER_H_ #define SCENARIO_PLAYER_H_ +#include #include #include #include @@ -166,8 +167,28 @@ class ScenarioPlayer { } case ScenarioData::OP_SEND_DATA_CHANNEL_MESSAGE: { auto& op = boost::get(opv); - std::string data = config_.binary_pool->Get(op.min_size, op.max_size); + std::string data = + config_.binary_pool->Get(op.min_size - 16, op.max_size - 16); + + // 先頭に8バイトに現在時刻(マイクロ秒単位の UNIX Time)、次の8バイトにカウンターを入れる + char buf[16]; + uint64_t time = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + for (int i = 0; i < 8; i++) { + buf[i] = (char)((time >> ((7 - i) * 8)) & 0xff); + } + auto& counter = dc_counter_[op.label]; + for (int i = 0; i < 8; i++) { + buf[i + 8] = (char)((counter >> ((7 - i) * 8)) & 0xff); + } + data.insert(data.begin(), buf, buf + sizeof(data)); + + RTC_LOG(LS_INFO) << "Send DataChannel unixtime(us)=" << time + << " counter=" << counter; + (*config_.vcs)[client_id]->SendMessage(op.label, data); + counter += 1; break; } case ScenarioData::OP_DISCONNECT: { @@ -199,6 +220,7 @@ class ScenarioPlayer { std::vector client_infos_; VoiceNumberReader voice_reader_; std::map> sub_scenario_; + std::map dc_counter_; }; #endif diff --git a/src/sora/sora_client.cpp b/src/sora/sora_client.cpp index 5d30188..25a5710 100644 --- a/src/sora/sora_client.cpp +++ b/src/sora/sora_client.cpp @@ -343,8 +343,8 @@ void SoraClient::DoSendConnect(bool redirect) { *config_.ignore_disconnect_websocket; } - if (!config_.data_channel_messaging.is_null()) { - json_message["data_channel_messaging"] = config_.data_channel_messaging; + if (!config_.data_channels.is_null()) { + json_message["data_channels"] = config_.data_channels; } ws_->WriteText(boost::json::serialize(json_message), @@ -681,7 +681,26 @@ void SoraClient::OnMessage( << " datasize=" << data.size(); // ハンドリングする必要のあるラベル以外は何もしない - if (label != "signaling" && label != "stats") { + if (label != "signaling" && label != "stats" && label[0] != '#') { + return; + } + + // zakuro の場合、DataChannel メッセージは Fake データだけのはずで、 + // その場合は先頭 16 バイトに特定のデータが入っている。 + if (label[0] == '#') { + if (data.size() < 16) { + return; + } + uint64_t time = 0; + for (int i = 0; i < 8; i++) { + time |= ((uint64_t)data[i] & 0xff) << ((7 - i) * 8); + } + uint64_t counter = 0; + for (int i = 0; i < 8; i++) { + counter |= ((uint64_t)data[i + 8] & 0xff) << ((7 - i) * 8); + } + RTC_LOG(LS_INFO) << "Recv DataChannel unixtime(us)=" << time + << " counter=" << counter; return; } diff --git a/src/sora/sora_client.h b/src/sora/sora_client.h index c18ccef..ecd1964 100644 --- a/src/sora/sora_client.h +++ b/src/sora/sora_client.h @@ -45,7 +45,7 @@ struct SoraClientConfig { int data_channel_signaling_timeout = 180; boost::optional ignore_disconnect_websocket; int disconnect_wait_timeout = 5; - boost::json::value data_channel_messaging; + boost::json::value data_channels; }; class SoraClient : public std::enable_shared_from_this, diff --git a/src/util.cpp b/src/util.cpp index 90e9cb3..6f9409c 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -214,9 +214,9 @@ void Util::ParseArgs(const std::vector& cargs, app.add_option("--sora-signaling-notify-metadata", sora_signaling_notify_metadata, "Signaling metadata") ->check(is_json); - std::string sora_data_channel_messaging; - app.add_option("--sora-data-channel-messaging", sora_data_channel_messaging, - "DataChannel messaging") + std::string sora_data_channels; + app.add_option("--sora-data-channels", sora_data_channels, + "DataChannels") ->check(is_json); // Fake network 系 @@ -344,9 +344,9 @@ void Util::ParseArgs(const std::vector& cargs, config.sora_signaling_notify_metadata = boost::json::parse(sora_signaling_notify_metadata); } - if (!sora_data_channel_messaging.empty()) { - config.sora_data_channel_messaging = - boost::json::parse(sora_data_channel_messaging); + if (!sora_data_channels.empty()) { + config.sora_data_channels = + boost::json::parse(sora_data_channels); } } @@ -518,9 +518,9 @@ std::vector> Util::NodeToArgs(const YAML::Node& inst) { args.push_back("--sora-signaling-notify-metadata"); args.push_back(boost::json::serialize(value)); } - if (sora["data-channel-messaging"]) { - boost::json::value value = NodeToJson(sora["data-channel-messaging"]); - args.push_back("--sora-data-channel-messaging"); + if (sora["data-channels"]) { + boost::json::value value = NodeToJson(sora["data-channels"]); + args.push_back("--sora-data-channels"); args.push_back(boost::json::serialize(value)); } } diff --git a/src/zakuro.cpp b/src/zakuro.cpp index c1c254c..7e043d4 100644 --- a/src/zakuro.cpp +++ b/src/zakuro.cpp @@ -26,31 +26,31 @@ Zakuro::Zakuro(ZakuroConfig config) : config_(std::move(config)) {} -const int MESSAGE_SIZE_MIN = 1; +const int MESSAGE_SIZE_MIN = 16; const int MESSAGE_SIZE_MAX = 256 * 1000; const int BINARY_POOL_SIZE = 1 * 1024 * 1024; -struct DataChannelMessaging { +struct DataChannels { struct Channel { std::string label; int interval = 500; - int size_min = 10; - int size_max = 10; + int size_min = 16; + int size_max = 16; }; std::vector channels; boost::json::value remain; }; -static bool ParseDataChannelMessaging(boost::json::value data_channel_messaging, - DataChannelMessaging& m) { - m = DataChannelMessaging(); - boost::json::value& dcm = data_channel_messaging; - if (!dcm.is_array()) { +static bool ParseDataChannels(boost::json::value data_channels, + DataChannels& m) { + m = DataChannels(); + boost::json::value& dcs = data_channels; + if (!dcs.is_array()) { std::cout << __LINE__ << std::endl; return false; } - for (auto& j : dcm.as_array()) { - DataChannelMessaging::Channel ch; + for (auto& j : dcs.as_array()) { + DataChannels::Channel ch; if (!j.is_object()) { std::cout << __LINE__ << std::endl; @@ -152,7 +152,7 @@ static bool ParseDataChannelMessaging(boost::json::value data_channel_messaging, m.channels.push_back(ch); } } - m.remain = dcm; + m.remain = dcs; return true; } @@ -265,11 +265,11 @@ int Zakuro::Run() { } // DataChannel メッセージング - DataChannelMessaging dcm; - if (!config_.sora_data_channel_messaging.is_null()) { - if (!ParseDataChannelMessaging(config_.sora_data_channel_messaging, dcm)) { + DataChannels dcs; + if (!config_.sora_data_channels.is_null()) { + if (!ParseDataChannels(config_.sora_data_channels, dcs)) { std::cerr << "[" << config_.name - << "] failed to parse DataChannel messaging" << std::endl; + << "] failed to parse DataChannels" << std::endl; return 2; } } @@ -311,7 +311,7 @@ int Zakuro::Run() { sorac_config.ignore_disconnect_websocket = config_.sora_ignore_disconnect_websocket; sorac_config.disconnect_wait_timeout = config_.sora_disconnect_wait_timeout; - sorac_config.data_channel_messaging = dcm.remain; + sorac_config.data_channels = dcs.remain; for (int i = 0; i < config_.vcs; i++) { auto vc = std::unique_ptr( @@ -326,12 +326,12 @@ int Zakuro::Run() { spc.binary_pool.reset(new BinaryPool(BINARY_POOL_SIZE)); // メインのシナリオとは別に、ラベル毎に裏で DataChannel を送信し続けるシナリオを作る - std::vector> dcm_data; - for (const auto& ch : dcm.channels) { + std::vector> dcs_data; + for (const auto& ch : dcs.channels) { ScenarioData sd; sd.Sleep(ch.interval, ch.interval); sd.SendDataChannelMessage(ch.label, ch.size_min, ch.size_max); - dcm_data.push_back(std::make_tuple("scenario-dcm-" + ch.label, sd)); + dcs_data.push_back(std::make_tuple("scenario-dcs-" + ch.label, sd)); } ScenarioPlayer scenario_player(spc); @@ -343,12 +343,12 @@ int Zakuro::Run() { loop_index = 1; } else if (config_.scenario == "") { data.Reconnect(); - for (const auto& d : dcm_data) { + for (const auto& d : dcs_data) { data.PlaySubScenario(std::get<0>(d), std::get<1>(d), 0); } data.Sleep(1000, 5000); data.PlayVoiceNumberClient(); - loop_index = 1 + dcm_data.size(); + loop_index = 1 + dcs_data.size(); } else if (config_.scenario == "reconnect") { data.Reconnect(); data.Sleep(1000, 5000); diff --git a/src/zakuro.h b/src/zakuro.h index f8fc904..bf211fb 100644 --- a/src/zakuro.h +++ b/src/zakuro.h @@ -59,7 +59,7 @@ struct ZakuroConfig { int sora_disconnect_wait_timeout = 5; boost::json::value sora_metadata; boost::json::value sora_signaling_notify_metadata; - boost::json::value sora_data_channel_messaging; + boost::json::value sora_data_channels; webrtc::BuiltInNetworkBehaviorConfig fake_network_send; webrtc::BuiltInNetworkBehaviorConfig fake_network_receive;