// Phase 6 signaling test: a client offers a session to a server over a real // localhost TCP connection and receives the server's answer. #include "screencast/network/signaling.h" #include #include #include #include #include #include #include #include namespace { [[noreturn]] void fail(const char* what) { std::fprintf(stderr, "test_signaling: FAIL: %s\n", what); std::abort(); } void check(bool condition, const char* what) { if (!condition) { fail(what); } } void check_result(const char* what, const sc::NetworkResult>& result) { if (sc::is_network_error(result)) { std::fprintf(stderr, "test_signaling: FAIL: %s: %s\n", what, sc::network_error(result).message.c_str()); std::abort(); } } } // namespace int main() { // Bind the server to the first free port in a small range. std::unique_ptr server; uint16_t server_port = 0; for (uint16_t port = 45940; port < 45960; ++port) { auto server_result = sc::SignalingFactory::create_server(port); if (!sc::is_network_error(server_result)) { server = std::move(sc::network_value(server_result)); server_port = port; break; } } check(server != nullptr, "server listens"); check(server_port != 0, "server port"); constexpr uint16_t advertised_rtp_port = 45999; // The receiver side of the handshake: reply to offers with an answer. server->on_message([&](const sc::SignalingMessage& message) { const sc::SessionOffer* offer = std::get_if(&message); if (offer == nullptr) { return; } sc::SessionAnswer answer; answer.session_id = offer->session_id; answer.rtp_endpoint = sc::Endpoint{"", advertised_rtp_port}; server->send(answer); }); // The sender side: connect, offer, and wait for the answer. auto client_result = sc::SignalingFactory::create_client(); check_result("client create", client_result); std::unique_ptr client = std::move(sc::network_value(client_result)); check(client->connect(sc::Endpoint{"127.0.0.1", server_port}), "client connect"); std::promise answer_promise; auto answer_future = answer_promise.get_future(); std::atomic answered{false}; client->on_message([&](const sc::SignalingMessage& message) { if (const sc::SessionAnswer* answer = std::get_if(&message)) { if (!answered.exchange(true)) { answer_promise.set_value(*answer); } } }); sc::SessionOffer offer; offer.session_id = "test-session-0001"; offer.codec_name = "h264"; offer.frame_rate_num = 25; offer.frame_rate_den = 1; client->send(offer); check(answer_future.wait_for(std::chrono::seconds(5)) == std::future_status::ready, "answer received"); const sc::SessionAnswer answer = answer_future.get(); check(answer.session_id == "test-session-0001", "session id echoes"); check(answer.rtp_endpoint.port == advertised_rtp_port, "rtp port in answer"); // PLI: the receiver side asks for a keyframe mid-session; the client // must receive it with the session id intact. std::promise pli_promise; auto pli_future = pli_promise.get_future(); std::atomic pli_seen{false}; client->on_message([&](const sc::SignalingMessage& message) { if (const sc::SessionPli* pli = std::get_if(&message)) { if (!pli_seen.exchange(true)) { pli_promise.set_value(*pli); } } }); sc::SessionPli pli; pli.session_id = "test-session-0001"; server->send(pli); check(pli_future.wait_for(std::chrono::seconds(5)) == std::future_status::ready, "pli received"); const sc::SessionPli received_pli = pli_future.get(); check(received_pli.session_id == "test-session-0001", "pli session id echoes"); client->disconnect(); server->disconnect(); std::puts("test_signaling: all checks passed"); return 0; }