diff --git a/CHANGES.md b/CHANGES.md index 764d6cf1..15478f82 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -42,6 +42,8 @@ - @melpon - [ADD] WebSocket の Close を取得できるよう SendOnWsClose を SoraSignalingObserver に追加する - @tnoho +- [ADD] DataChannel のみの接続で type: close がやってきた場合に正しく切断されるようにする + - @melpon - [FIX] HTTP Proxy 利用時の Websocket 初期化で insecure_ メンバ変数が初期化されていなかったのを修正 - @melpon - [FIX] SoraSignalingConfig の client_cert と client_key に渡す必要がある値を、ファイルパスからファイルの内容に修正 diff --git a/include/sora/data_channel.h b/include/sora/data_channel.h index 5446f7b2..ab711de2 100644 --- a/include/sora/data_channel.h +++ b/include/sora/data_channel.h @@ -44,6 +44,7 @@ class DataChannel : public std::enable_shared_from_this { void Close(const webrtc::DataBuffer& disconnect_message, std::function on_close, double disconnect_wait_timeout); + void SetOnClose(std::function on_close); void AddDataChannel( rtc::scoped_refptr data_channel); diff --git a/src/data_channel.cpp b/src/data_channel.cpp index 56270dc5..9b5b1951 100644 --- a/src/data_channel.cpp +++ b/src/data_channel.cpp @@ -69,6 +69,10 @@ void DataChannel::Close(const webrtc::DataBuffer& disconnect_message, auto data_channel = it->second; data_channel->Send(disconnect_message); } +void DataChannel::SetOnClose( + std::function on_close) { + on_close_ = on_close; +} void DataChannel::AddDataChannel( rtc::scoped_refptr data_channel) { diff --git a/src/sora_signaling.cpp b/src/sora_signaling.cpp index 7d09e90c..34eb6774 100644 --- a/src/sora_signaling.cpp +++ b/src/sora_signaling.cpp @@ -1644,6 +1644,23 @@ void SoraSignaling::OnMessage( }, CreateIceError( "Failed to SetOffer in re-offer message via DataChannel")); + } else if (type == "close") { + // グレースフルシャットダウンする + int code = json.at("code").to_number(); + std::string reason = json.at("reason").as_string().c_str(); + // on_close を設定しておいて、あとは DataChannel が閉じるのを待つだけ + dc_->SetOnClose([self = shared_from_this(), code, + reason](boost::system::error_code ec) { + if (code == 1000) { + self->SendOnDisconnect(SoraSignalingErrorCode::CLOSE_SUCCEEDED, + "Succeeded to close DataChannel: reason=" + reason); + } else { + self->SendOnDisconnect( + SoraSignalingErrorCode::CLOSE_SUCCEEDED, + "Failed to close DataChannel: code=" + std::to_string(code) + + " ec=" + ec.message()); + } + }); } return; } diff --git a/test/.testparam.example.json b/test/.testparam.example.json index f24a9f96..1351b057 100644 --- a/test/.testparam.example.json +++ b/test/.testparam.example.json @@ -23,10 +23,34 @@ "capture_width": 1024, "capture_height": 768, "video_bit_rate": 0, - "video_codec_type": "H264", + "video_codec_type": null, "simulcast": false, "use_hardware_encoder": true, "openh264": null, + "ignore_disconnect_websocket": null, + "client_id": null, + + // "data_channels": [ + // { + // // required + // "label": "#test", + // "direction": "sendrecv", + // // optional + // "ordered": null, + // "max_packet_life_time": null, + // "max_retransmits": null, + // "protocol": null, + // "compress": null, + // } + // ], + "data_channels": null, + + // 0 - verbose + // 1 - info + // 2 - warning + // 3 - error + // 4 - none + "log_level": 2, }, "signaling_urls": ["必須"], diff --git a/test/hello.cpp b/test/hello.cpp index 360d3124..c1aff2cd 100644 --- a/test/hello.cpp +++ b/test/hello.cpp @@ -71,6 +71,15 @@ void HelloSora::Run() { config.video_bit_rate = config_.video_bit_rate; config.multistream = true; config.simulcast = config_.simulcast; + if (config_.ignore_disconnect_websocket) { + config.ignore_disconnect_websocket = *config_.ignore_disconnect_websocket; + } + if (!config_.client_id.empty()) { + config.client_id = config_.client_id; + } + if (!config_.data_channels.empty()) { + config.data_channels = config_.data_channels; + } conn_ = sora::SoraSignaling::Create(config); boost::asio::executor_work_guard @@ -142,43 +151,80 @@ int main(int argc, char* argv[]) { config.signaling_urls.push_back(x.as_string().c_str()); } config.channel_id = v.as_object().at("channel_id").as_string().c_str(); - if (auto it = v.as_object().find("role"); it != v.as_object().end()) { - config.role = it->value().as_string(); + boost::json::value x; + auto get = [](const boost::json::value& v, const char* key, + boost::json::value& x) -> bool { + if (auto it = v.as_object().find(key); + it != v.as_object().end() && !it->value().is_null()) { + x = it->value(); + return true; + } + return false; + }; + if (get(v, "role", x)) { + config.role = x.as_string(); + } + if (get(v, "video", x)) { + config.video = x.as_bool(); + } + if (get(v, "audio", x)) { + config.audio = x.as_bool(); + } + if (get(v, "capture_width", x)) { + config.capture_width = x.to_number(); + } + if (get(v, "capture_height", x)) { + config.capture_height = x.to_number(); } - if (auto it = v.as_object().find("video"); it != v.as_object().end()) { - config.video = it->value().as_bool(); + if (get(v, "video_bit_rate", x)) { + config.video_bit_rate = x.to_number(); } - if (auto it = v.as_object().find("audio"); it != v.as_object().end()) { - config.audio = it->value().as_bool(); + if (get(v, "video_codec_type", x)) { + config.video_codec_type = x.as_string(); } - if (auto it = v.as_object().find("capture_width"); - it != v.as_object().end()) { - config.capture_width = boost::json::value_to(it->value()); + if (get(v, "simulcast", x)) { + config.simulcast = x.as_bool(); } - if (auto it = v.as_object().find("capture_height"); - it != v.as_object().end()) { - config.capture_height = boost::json::value_to(it->value()); + if (get(v, "client_id", x)) { + config.client_id = x.as_string(); } - if (auto it = v.as_object().find("video_bit_rate"); - it != v.as_object().end()) { - config.video_bit_rate = boost::json::value_to(it->value()); + if (get(v, "ignore_disconnect_websocket", x)) { + config.ignore_disconnect_websocket = x.as_bool(); } - if (auto it = v.as_object().find("video_codec_type"); - it != v.as_object().end()) { - config.video_codec_type = it->value().as_string(); + if (get(v, "data_channels", x)) { + for (auto&& dc : x.as_array()) { + sora::SoraSignalingConfig::DataChannel data_channel; + data_channel.label = dc.as_object().at("label").as_string(); + data_channel.direction = dc.as_object().at("direction").as_string(); + if (get(dc, "ordered", x)) { + data_channel.ordered = x.as_bool(); + } + if (get(dc, "max_packet_life_time", x)) { + data_channel.max_packet_life_time = x.to_number(); + } + if (get(dc, "max_retransmits", x)) { + data_channel.max_retransmits = x.to_number(); + } + if (get(dc, "protocol", x)) { + data_channel.protocol = x.as_string().c_str(); + } + if (get(dc, "compress", x)) { + data_channel.compress = x.as_bool(); + } + config.data_channels.push_back(data_channel); + } } - if (auto it = v.as_object().find("simulcast"); it != v.as_object().end()) { - config.simulcast = it->value().as_bool(); + if (get(v, "log_level", x)) { + rtc::LogMessage::LogToDebug((rtc::LoggingSeverity)x.to_number()); } sora::SoraClientContextConfig context_config; context_config.get_android_application_context = GetAndroidApplicationContext; - if (auto it = v.as_object().find("use_hardware_encoder"); - it != v.as_object().end()) { - context_config.use_hardware_encoder = it->value().as_bool(); + if (get(v, "use_hardware_encoder", x)) { + context_config.use_hardware_encoder = x.as_bool(); } - if (auto it = v.as_object().find("openh264"); it != v.as_object().end()) { - context_config.openh264 = it->value().as_string(); + if (get(v, "openh264", x)) { + context_config.openh264 = x.as_string(); } auto context = sora::SoraClientContext::Create(context_config); diff --git a/test/hello.h b/test/hello.h index 67b99432..27341f44 100644 --- a/test/hello.h +++ b/test/hello.h @@ -14,6 +14,9 @@ struct HelloSoraConfig { int video_bit_rate = 0; std::string video_codec_type = "H264"; bool simulcast = false; + std::optional ignore_disconnect_websocket; + std::string client_id; + std::vector data_channels; }; class HelloSora : public std::enable_shared_from_this,