From 1b7344741c66107dc14063380ccd34e2f2e009f5 Mon Sep 17 00:00:00 2001 From: sudden6 Date: Tue, 4 Jan 2022 21:05:24 +0100 Subject: [PATCH 01/11] test: add test for multithreaded ToxAV This tests tox_iterate in a separate thread, and the two ToxAV modes. --- CMakeLists.txt | 14 + auto_tests/toxav_mt_test.cc | 506 ++++++++++++++++++++++++++++++++++++ 2 files changed, 520 insertions(+) create mode 100644 auto_tests/toxav_mt_test.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 59ed2ffb7c..d6a0e9fada 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -406,6 +406,19 @@ function(auto_test target) endif() endfunction() +function(auto_test_cc target) + if(AUTOTEST AND NOT (MSVC AND ARGV1 STREQUAL "MSVC_DONT_BUILD")) + add_executable(auto_${target}_test ${CPUFEATURES} + auto_tests/${target}_test.cc) + target_link_modules(auto_${target}_test toxcore misc_tools) + if(NOT ARGV1 STREQUAL "DONT_RUN") + add_test(NAME ${target} COMMAND ${CROSSCOMPILING_EMULATOR} auto_${target}_test) + set_tests_properties(${target} PROPERTIES TIMEOUT "${TEST_TIMEOUT_SECONDS}") + set_property(TEST ${target} PROPERTY ENVIRONMENT "LLVM_PROFILE_FILE=${target}.profraw") + endif() + endif() +endfunction() + auto_test(TCP) auto_test(conference) auto_test(conference_double_invite) @@ -454,6 +467,7 @@ if(BUILD_TOXAV) auto_test(conference_av) auto_test(toxav_basic) auto_test(toxav_many) + auto_test_cc(toxav_mt) endif() ################################################################################ diff --git a/auto_tests/toxav_mt_test.cc b/auto_tests/toxav_mt_test.cc new file mode 100644 index 0000000000..a34858a730 --- /dev/null +++ b/auto_tests/toxav_mt_test.cc @@ -0,0 +1,506 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../testing/misc_tools.h" +#include "../toxav/toxav.h" +#include "../toxcore/tox.h" +#include "check_compat.h" + +// Make lines shorter +using Clock = std::chrono::high_resolution_clock; +using Time_Point = std::chrono::time_point; + +namespace { +// Maximum amount of time in iterations to wait for bootstrapping and friend +// connections to succeed. +constexpr uint32_t MAX_BOOTSTRAP_ITERATIONS = 1000; + +struct Tox_Deleter { + void operator()(Tox *tox) const { tox_kill(tox); } +}; + +using Tox_Ptr = std::unique_ptr; + +struct ToxAV_Deleter { + void operator()(ToxAV *tox) const { toxav_kill(tox); } +}; + +using ToxAV_Ptr = std::unique_ptr; + +static void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, + size_t length, void *userdata) { + if (length == 7 && std::memcmp("gentoo", data, 7) == 0) { + Tox_Err_Friend_Add err; + tox_friend_add_norequest(m, public_key, &err); + ck_assert(err == TOX_ERR_FRIEND_ADD_OK); + } else { + // No other request expected + ck_assert(false); + } +} + +std::vector prepare_network(uint32_t count) { + Tox_Err_New error; + + // Temporary bootstrap node + std::printf("Created 1 instance of Tox as bootstrap node\n"); + Tox_Ptr bootstrap = Tox_Ptr(tox_new_log(nullptr, &error, nullptr)); + ck_assert(error == TOX_ERR_NEW_OK); + + uint8_t bootstrap_key[TOX_PUBLIC_KEY_SIZE]; + tox_self_get_dht_id(bootstrap.get(), bootstrap_key); + const uint16_t bootstrap_port = tox_self_get_udp_port(bootstrap.get(), nullptr); + + std::cout << "Bootstrapping " << std::to_string(count) << " Tox nodes" << std::endl; + + std::vector toxes(count); + + for (auto &tox : toxes) { + tox = Tox_Ptr(tox_new_log(nullptr, &error, nullptr)); + ck_assert(error == TOX_ERR_NEW_OK); + + tox_bootstrap(tox.get(), "localhost", bootstrap_port, bootstrap_key, nullptr); + tox_callback_friend_request(tox.get(), t_accept_friend_request_cb); + } + + // Create fully meshed friend network + for (size_t i = 0; i < toxes.size(); ++i) { + uint8_t address[TOX_ADDRESS_SIZE]; + tox_self_get_address(toxes[i].get(), address); + + for (size_t j = i + 1; j < toxes.size(); ++j) { + Tox_Err_Friend_Add error_add; + tox_friend_add(toxes[j].get(), address, (const uint8_t *)"gentoo", 7, &error_add); + ck_assert(error_add == TOX_ERR_FRIEND_ADD_OK); + } + } + + // temporarily add bootstrap node to end of toxes, so we can iterate all + toxes.push_back(std::move(bootstrap)); + + bool online = false; + + auto bootstrap_start_time = Clock::now(); + + uint32_t bootstrap_iteration; + for (bootstrap_iteration = 0; bootstrap_iteration < MAX_BOOTSTRAP_ITERATIONS; ++bootstrap_iteration) { + for (auto &tox : toxes) { + tox_iterate(tox.get(), nullptr); + } + + if (!online) { + size_t online_cnt = std::count_if(toxes.cbegin(), toxes.cend(), [](const Tox_Ptr &tox) { + return tox_self_get_connection_status(tox.get()); + }); + + if (online_cnt == toxes.size()) { + std::chrono::duration bootstrap_time = Clock::now() - bootstrap_start_time; + std::cout << "Toxes are online, took " << bootstrap_time.count() << "s" << std::endl; + online = true; + } + } + + bool friends_connected = true; + + // Check if the friends are connected to each other, bootstrap node will have empty friends list + for (auto &tox : toxes) { + std::vector friend_list; + friend_list.resize(tox_self_get_friend_list_size(tox.get())); + tox_self_get_friend_list(tox.get(), friend_list.data()); + + for (auto friend_id : friend_list) { + friends_connected &= + tox_friend_get_connection_status(tox.get(), friend_id, nullptr) == TOX_CONNECTION_UDP; + } + } + + if (friends_connected) { + break; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } + + ck_assert(bootstrap_iteration < MAX_BOOTSTRAP_ITERATIONS); + std::chrono::duration mesh_time = Clock::now() - bootstrap_start_time; + std::cout << "Iterations to connect friend mesh: " << std::to_string(bootstrap_iteration) + << std::endl; + std::cout << "Time to connect friend mesh: " << mesh_time.count() << "s" << std::endl; + + // Remove bootstrap node + toxes.pop_back(); + + return toxes; +} + +class AV_State { + public: + explicit AV_State(Tox_Ptr tox, std::string name, bool combined = false) noexcept + : tox_(std::move(tox)), + combined_av_(combined), + stop_threads_{false}, + incomming_{false}, + call_state_{0}, + video_received_{false}, + audio_received_{false}, + name_{name} { + Toxav_Err_New error; + toxAV_ = ToxAV_Ptr(toxav_new(tox_.get(), &error)); + ck_assert(error == TOXAV_ERR_NEW_OK); + + toxav_callback_call(toxAV_.get(), &AV_State::toxav_call_cb, this); + toxav_callback_call_state(toxAV_.get(), &AV_State::toxav_call_state_cb, this); + toxav_callback_video_receive_frame(toxAV_.get(), &AV_State::toxav_receive_video_frame_cb, this); + toxav_callback_audio_receive_frame(toxAV_.get(), &AV_State::toxav_receive_audio_frame_cb, this); + + tox_thread_ = std::thread(&AV_State::tox_iterator, this); + + if (combined) { + av_thread_ = std::thread(&AV_State::toxav_iterator, this, Iteration_Type::TOXAV_BOTH); + } else { + audio_thread_ = std::thread(&AV_State::toxav_iterator, this, Iteration_Type::TOXAV_AUDIO); + video_thread_ = std::thread(&AV_State::toxav_iterator, this, Iteration_Type::TOXAV_VIDEO); + } + } + + ~AV_State() { stop_threads(); } + + ToxAV *get_ToxAV() const { return toxAV_.get(); } + std::mutex &get_tox_loop_lock() { return tox_loop_lock_; } + bool in_call() const { return in_call_.load(); } + uint32_t get_call_state() const { return call_state_.load(); } + void stop_threads() { + if (stop_threads_.exchange(true)) { + // already stopped + return; + } + + tox_thread_.join(); + + if (combined_av_) { + av_thread_.join(); + } else { + audio_thread_.join(); + video_thread_.join(); + } + } + + bool did_receive_audio() const { return audio_received_.load(); } + bool did_receive_video() const { return video_received_.load(); } + + static constexpr uint32_t TEST_A_BITRATE = 48; // In kbit/s + static constexpr uint32_t TEST_V_BITRATE = 4000; // In kbit/s + static constexpr std::chrono::duration AUTO_HANGUP_TIME = std::chrono::seconds(2); + + private: + enum class Iteration_Type { + TOXAV_AUDIO, + TOXAV_VIDEO, + TOXAV_BOTH, + }; + + static void toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, + bool video_enabled, void *user_data) { + AV_State *me = static_cast(user_data); + std::cout << "[" << me->name_ << "] Handling CALL callback" << std::endl; + me->incomming_.store(true); + ck_assert(std::this_thread::get_id() == me->tox_thread_.get_id()); + } + + static void toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, + void *user_data) { + AV_State *me = static_cast(user_data); + ck_assert(std::this_thread::get_id() == me->tox_thread_.get_id()); + ck_assert(state != TOXAV_FRIEND_CALL_STATE_ERROR); + std::cout << "[" << me->name_ << "] State changed to: " << std::to_string(state) << std::endl; + me->call_state_.store(state); + Time_Point tp = me->call_start_.load(); + + if (state != TOXAV_FRIEND_CALL_STATE_NONE && tp == Time_Point()) { + me->call_start_.store(Clock::now()); + me->in_call_.store(true); + } + } + + static void toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, uint16_t width, + uint16_t height, uint8_t const *y, uint8_t const *u, + uint8_t const *v, int32_t ystride, int32_t ustride, + int32_t vstride, void *user_data) { + AV_State *me = static_cast(user_data); + std::cout << "[" << me->name_ << "] Received video payload" << std::endl; + + // toxav.h states that receive events are emitted from their respective threads + if (me->combined_av_) { + ck_assert(std::this_thread::get_id() == me->av_thread_.get_id()); + } else { + ck_assert(std::this_thread::get_id() == me->video_thread_.get_id()); + } + + me->video_received_ = true; + } + + static void toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number, int16_t const *pcm, + size_t sample_count, uint8_t channels, + uint32_t sampling_rate, void *user_data) { + AV_State *me = static_cast(user_data); + std::cout << "[" << me->name_ << "] Received audio payload" << std::endl; + + if (me->combined_av_) { + ck_assert(std::this_thread::get_id() == me->av_thread_.get_id()); + } else { + ck_assert(std::this_thread::get_id() == me->audio_thread_.get_id()); + } + + me->audio_received_ = true; + } + + void tox_iterator() { + while (!stop_threads_.load()) { + uint32_t sleep_ms = 0; + + // Perform this block only while loop lock is held + { + std::lock_guard lock(tox_loop_lock_); + tox_iterate(tox_.get(), this); + + // handle incoming call + if (incomming_.exchange(false)) { + Toxav_Err_Answer answer_err; + toxav_answer(toxAV_.get(), 0, TEST_A_BITRATE, TEST_V_BITRATE, &answer_err); + + if (answer_err != TOXAV_ERR_ANSWER_OK) { + std::printf("toxav_answer failed, Toxav_Err_Answer: %d\n", answer_err); + ck_assert(0); + } + + std::cout << "[" << name_ << "] Answering call" << std::endl; + + call_start_ = Clock::now(); + in_call_.store(true); + } + + if (in_call_.load()) { + uint32_t state = call_state_.load(); + Time_Point tp = call_start_.load(); + std::chrono::duration call_time = Clock::now() - tp; + + if (state == TOXAV_FRIEND_CALL_STATE_FINISHED) { + std::cout << "[" << name_ << "] Call ended by other side after: " << call_time.count() + << "s" << std::endl; + in_call_.store(false); + } else if (tp > Time_Point() && call_time > AV_State::AUTO_HANGUP_TIME) { + std::cout << "[" << name_ << "] Ending call after: " << call_time.count() << "s" + << std::endl; + Toxav_Err_Call_Control cc_err; + toxav_call_control(toxAV_.get(), 0, TOXAV_CALL_CONTROL_CANCEL, &cc_err); + + // Ignore FRIEND_NOT_IN_CALL for the case where the other side hangs up simultaneously + if (cc_err != TOXAV_ERR_CALL_CONTROL_OK && + cc_err != TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL) { + std::printf("toxav_call_control failed: %d\n", cc_err); + ck_assert(0); + } + + in_call_.store(false); + } + } + + // WARNING: This accesses the Tox struct, so it must be inside the lock + tox_iteration_interval(tox_.get()); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms)); + } + } + + void toxav_iterator(Iteration_Type type) { + while (!stop_threads_.load()) { + switch (type) { + case Iteration_Type::TOXAV_AUDIO: + toxav_audio_iterate(toxAV_.get()); + std::this_thread::sleep_for( + std::chrono::milliseconds(toxav_audio_iteration_interval(toxAV_.get()))); + break; + + case Iteration_Type::TOXAV_VIDEO: + toxav_video_iterate(toxAV_.get()); + std::this_thread::sleep_for( + std::chrono::milliseconds(toxav_video_iteration_interval(toxAV_.get()))); + break; + + case Iteration_Type::TOXAV_BOTH: + toxav_iterate(toxAV_.get()); + std::this_thread::sleep_for( + std::chrono::milliseconds(toxav_iteration_interval(toxAV_.get()))); + break; + } + } + } + + std::thread tox_thread_; + std::thread audio_thread_; + std::thread video_thread_; + std::thread av_thread_; + + std::mutex tox_loop_lock_; + + Tox_Ptr tox_; + bool combined_av_; + ToxAV_Ptr toxAV_; + + std::atomic_bool stop_threads_; + std::atomic_bool incomming_; + std::atomic_uint32_t call_state_; + + std::atomic call_start_; + std::atomic_bool in_call_; + + std::atomic_bool video_received_; + std::atomic_bool audio_received_; + std::string name_; +}; + +struct DUMMY_PCM { + static constexpr size_t sample_count = 960; + static constexpr int16_t pcm[sample_count] = {0}; + static constexpr uint8_t channels = 1; + static constexpr uint32_t sampling_rate = 48000; +}; + +struct DUMMY_VIDEO { + // https://en.wikipedia.org/wiki/Graphics_display_resolution#QQVGA size + static constexpr uint16_t width = 160; + static constexpr uint16_t height = 120; + + static constexpr uint8_t y[width * height] = {0}; + static constexpr uint8_t u[width / 2 * height / 2] = {0}; + static constexpr uint8_t v[width / 2 * height / 2] = {0}; +}; + +// FIXME: once we upgrade to C++17, remove these, reason: https://stackoverflow.com/a/28846608 +constexpr std::chrono::duration AV_State::AUTO_HANGUP_TIME; +constexpr int16_t DUMMY_PCM::pcm[]; +constexpr uint8_t DUMMY_VIDEO::y[]; +constexpr uint8_t DUMMY_VIDEO::u[]; +constexpr uint8_t DUMMY_VIDEO::v[]; + +static void test_av(bool combined_av) { + std::cout << "Testing Audio and Video in " << (combined_av ? "combined" : "separate") << " threads" + << std::endl; + auto toxes = prepare_network(2); + + AV_State alice(std::move(toxes[0]), "alice", false); + AV_State bob(std::move(toxes[1]), "bob", false); + + // Let alice call bob + { + std::lock_guard(alice.get_tox_loop_lock()); + Toxav_Err_Call err; + ck_assert( + toxav_call(alice.get_ToxAV(), 0, AV_State::TEST_A_BITRATE, AV_State::TEST_V_BITRATE, &err)); + ck_assert(err == TOXAV_ERR_CALL_OK); + } + + std::cout << "alice started a call" << std::endl; + + auto poll_state = [](AV_State &av, uint32_t expected, uint32_t max_tries, + uint32_t delay_ms) -> bool { + for (uint32_t i = 0; i < max_tries; ++i) { + uint32_t state = av.get_call_state(); + + if (state == expected) { + return true; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms)); + } + + return false; + }; + + // We're starting with full AV on both sides + constexpr uint32_t full_AV_mask = + TOXAV_FRIEND_CALL_STATE_SENDING_A | TOXAV_FRIEND_CALL_STATE_SENDING_V | + TOXAV_FRIEND_CALL_STATE_ACCEPTING_A | TOXAV_FRIEND_CALL_STATE_ACCEPTING_V; + + // Wait 2s until the call is established on both sides + ck_assert(poll_state(alice, full_AV_mask, 20, 100)); + + // TODO: why is Bob's call state not updated? + // ck_assert(poll_state(bob, full_AV_mask, 20, 100)); asserts + + ck_assert(alice.in_call()); + ck_assert(bob.in_call()); + + std::cout << "alice and bob are in the call" << std::endl; + + auto toxav_audio_send_frame_dummy = [](ToxAV *av, Toxav_Err_Send_Frame *error) -> bool { + return toxav_audio_send_frame(av, 0, DUMMY_PCM::pcm, DUMMY_PCM::sample_count, + DUMMY_PCM::channels, DUMMY_PCM::sampling_rate, error); + }; + + auto toxav_video_send_frame_dummy = [](ToxAV *av, Toxav_Err_Send_Frame *error) -> bool { + return toxav_video_send_frame(av, 0, DUMMY_VIDEO::width, DUMMY_VIDEO::height, DUMMY_VIDEO::y, + DUMMY_VIDEO::u, DUMMY_VIDEO::v, error); + }; + + // Send frames from alice to bob + { + std::lock_guard(alice.get_tox_loop_lock()); + Toxav_Err_Send_Frame err; + ck_assert(toxav_audio_send_frame_dummy(alice.get_ToxAV(), &err)); + ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); + + ck_assert(toxav_video_send_frame_dummy(alice.get_ToxAV(), &err)); + ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); + } + + // Send frames from bob to alice + { + std::lock_guard(bob.get_tox_loop_lock()); + Toxav_Err_Send_Frame err; + ck_assert(toxav_audio_send_frame_dummy(bob.get_ToxAV(), &err)); + ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); + + ck_assert(toxav_video_send_frame_dummy(bob.get_ToxAV(), &err)); + ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); + } + + // auto hangup after 2s, wait 3s for this + ck_assert(poll_state(alice, TOXAV_FRIEND_CALL_STATE_FINISHED, 30, 100)); + + // TODO: why is Bobs call state not updated? + // ck_assert(poll_state(bob, TOXAV_FRIEND_CALL_STATE_FINISHED, 30, 100)); fails + + ck_assert(!alice.in_call()); + ck_assert(!bob.in_call()); + + std::cout << "The call ended" << std::endl; + + alice.stop_threads(); + bob.stop_threads(); + + ck_assert(alice.did_receive_audio()); + ck_assert(alice.did_receive_video()); + ck_assert(bob.did_receive_audio()); + ck_assert(bob.did_receive_video()); +} + +} // namespace + +int main(void) { + setvbuf(stdout, nullptr, _IONBF, 0); + + test_av(true); + test_av(false); + + return 0; +} From 84945fee0a885e5f0416a5e89d2b0d2c64056cc4 Mon Sep 17 00:00:00 2001 From: iphydf Date: Tue, 4 Jan 2022 21:17:09 +0000 Subject: [PATCH 02/11] add C++ tests to bazel --- auto_tests/BUILD.bazel | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/auto_tests/BUILD.bazel b/auto_tests/BUILD.bazel index c7a40c8c15..264eee03be 100644 --- a/auto_tests/BUILD.bazel +++ b/auto_tests/BUILD.bazel @@ -20,7 +20,7 @@ flaky_tests = { } [cc_test( - name = src[:-2], + name = src[:src.rindex(".")-len(src)], size = "small", srcs = [src], args = ["$(location %s)" % src], @@ -38,4 +38,4 @@ flaky_tests = { "//c-toxcore/toxcore:DHT_srcs", "//c-toxcore/toxencryptsave", ], -) for src in glob(["*_test.c"])] +) for src in glob(["*_test.c*"])] From a3a1db7a1f432a23d6cec195479433c35fe177c4 Mon Sep 17 00:00:00 2001 From: Maxim Biro Date: Wed, 5 Jan 2022 04:55:39 -0500 Subject: [PATCH 03/11] Fix Windows build failing due to missing std::mutex --- other/docker/windows/get_packages.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/other/docker/windows/get_packages.sh b/other/docker/windows/get_packages.sh index 2c86e17d25..a36320c42b 100644 --- a/other/docker/windows/get_packages.sh +++ b/other/docker/windows/get_packages.sh @@ -25,12 +25,16 @@ if [ "$SUPPORT_ARCH_i686" = "true" ]; then apt-get install -y \ g++-mingw-w64-i686 \ gcc-mingw-w64-i686 + update-alternatives --set i686-w64-mingw32-gcc /usr/bin/i686-w64-mingw32-gcc-posix + update-alternatives --set i686-w64-mingw32-g++ /usr/bin/i686-w64-mingw32-g++-posix fi if [ "$SUPPORT_ARCH_x86_64" = "true" ]; then apt-get install -y \ g++-mingw-w64-x86-64 \ gcc-mingw-w64-x86-64 + update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix + update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix fi # Packages needed for running toxcore tests From d77c6ce0f85f0f8e33553ea534f40786a1910e9e Mon Sep 17 00:00:00 2001 From: Maxim Biro Date: Wed, 5 Jan 2022 07:56:00 -0500 Subject: [PATCH 04/11] Fix build error due to compiler being old --- .travis/cmake-windows.sh | 2 +- other/docker/windows/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis/cmake-windows.sh b/.travis/cmake-windows.sh index ee09cd4021..ee96f70a5d 100644 --- a/.travis/cmake-windows.sh +++ b/.travis/cmake-windows.sh @@ -26,7 +26,7 @@ travis_script() { add_flag -gdwarf-2 # Fix invalid register for .seh_savexmm error - add_flag -fno-asynchronous-unwind-tables + add_c_flag -fno-asynchronous-unwind-tables docker run \ -e ALLOW_TEST_FAILURE=true \ diff --git a/other/docker/windows/Dockerfile b/other/docker/windows/Dockerfile index 37b685e7ff..ff4e34ab83 100644 --- a/other/docker/windows/Dockerfile +++ b/other/docker/windows/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:buster-slim +FROM debian:bullseye-slim # Build-time environment variables ARG VERSION_SODIUM=1.0.18 From c3b8f38147b22e1662a2a6ed3b0a138dce824d82 Mon Sep 17 00:00:00 2001 From: Maxim Biro Date: Sat, 8 Jan 2022 14:31:25 -0500 Subject: [PATCH 05/11] Fix Dockerfile having no workdir set --- other/docker/windows/Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/other/docker/windows/Dockerfile b/other/docker/windows/Dockerfile index ff4e34ab83..8847806d22 100644 --- a/other/docker/windows/Dockerfile +++ b/other/docker/windows/Dockerfile @@ -16,6 +16,8 @@ ENV SUPPORT_ARCH_i686=${SUPPORT_ARCH_i686} ENV SUPPORT_ARCH_x86_64=${SUPPORT_ARCH_x86_64} ENV CROSS_COMPILE=${CROSS_COMPILE} +WORKDIR / + COPY get_packages.sh . RUN sh ./get_packages.sh From ac5a87f04e17d596449838d06edbf59a7cf4517c Mon Sep 17 00:00:00 2001 From: sudden6 Date: Sat, 8 Jan 2022 21:08:21 +0100 Subject: [PATCH 06/11] fix: prevent unlocked access to av->calls This was introduced in 891fe60ea2f7eea220f29b0cc76301004c02a0ab and missed, because iteration_interval() could not be called without av- >mutex unlocked. Now both audio and video thread can. The fix moves calculating the iteration interval, including idle interval into iterate_common(...) which holds the correct mutex. --- toxav/toxav.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/toxav/toxav.c b/toxav/toxav.c index 020c1b6a18..8dd0905281 100644 --- a/toxav/toxav.c +++ b/toxav/toxav.c @@ -108,7 +108,10 @@ struct ToxAV { void *vbcb_user_data; /* keep track of decode times for audio and video */ + + /* Must only be accessed from Audio thread */ DecodeTimeStats audio_stats; + /* Must only be accessed from Video thread */ DecodeTimeStats video_stats; /** ToxAV's own mono_time instance */ Mono_Time *toxav_mono_time; @@ -253,12 +256,12 @@ Tox *toxav_get_tox(const ToxAV *av) uint32_t toxav_audio_iteration_interval(const ToxAV *av) { - return av->calls ? av->audio_stats.interval : IDLE_ITERATION_INTERVAL_MS; + return av->audio_stats.interval; } uint32_t toxav_video_iteration_interval(const ToxAV *av) { - return av->calls ? av->video_stats.interval : IDLE_ITERATION_INTERVAL_MS; + return av->video_stats.interval; } uint32_t toxav_iteration_interval(const ToxAV *av) @@ -276,6 +279,12 @@ uint32_t toxav_iteration_interval(const ToxAV *av) */ static void calc_interval(ToxAV *av, DecodeTimeStats *stats, int32_t frame_time, uint64_t start_time) { + if (av->calls == nullptr) { + // No calls active + stats->interval = IDLE_ITERATION_INTERVAL_MS; + return; + } + stats->interval = frame_time < stats->average ? 0 : (frame_time - stats->average); stats->total += current_time_monotonic(av->m->mono_time) - start_time; @@ -295,15 +304,16 @@ static void iterate_common(ToxAV *av, bool audio) { pthread_mutex_lock(av->mutex); - if (av->calls == nullptr) { - pthread_mutex_unlock(av->mutex); - return; - } - uint64_t start = current_time_monotonic(av->toxav_mono_time); // time until the first audio or video frame is over int32_t frame_time = IDLE_ITERATION_INTERVAL_MS; + DecodeTimeStats *stats = audio ? &av->audio_stats : &av->video_stats; + + if (av->calls == nullptr) { + goto EARLY_RETURN; + } + for (ToxAVCall *i = av->calls[av->calls_head]; i; i = i->next) { if (!i->active) { continue; @@ -341,7 +351,8 @@ static void iterate_common(ToxAV *av, bool audio) } } - DecodeTimeStats *stats = audio ? &av->audio_stats : &av->video_stats; +EARLY_RETURN: + calc_interval(av, stats, frame_time, start); pthread_mutex_unlock(av->mutex); } From 4cf603c9e2208d8a9cb148107a804e9ee1439bde Mon Sep 17 00:00:00 2001 From: sudden6 Date: Sat, 8 Jan 2022 22:35:09 +0100 Subject: [PATCH 07/11] fix: missing lock I don't know what this code is supposed to do, but it's clearly accessing data once with a lock and then the same data without a lock. Add a second lock to fix this. --- toxcore/net_crypto.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/toxcore/net_crypto.c b/toxcore/net_crypto.c index f1e31e59e6..318c569069 100644 --- a/toxcore/net_crypto.c +++ b/toxcore/net_crypto.c @@ -1178,9 +1178,11 @@ static int64_t send_lossless_packet(Net_Crypto *c, int crypt_connection_id, cons if (send_data_packet_helper(c, crypt_connection_id, conn->recv_array.buffer_start, packet_num, data, length) == 0) { Packet_Data *dt1 = nullptr; + pthread_mutex_lock(conn->mutex); if (get_data_pointer(c->log, &conn->send_array, &dt1, packet_num) == 1) { dt1->sent_time = current_time_monotonic(c->mono_time); } + pthread_mutex_unlock(conn->mutex); } else { conn->maximum_speed_reached = 1; LOGGER_DEBUG(c->log, "send_data_packet failed"); From b33bfe1b1a8940ecdb45fe08af57e28e297dcd99 Mon Sep 17 00:00:00 2001 From: sudden6 Date: Sun, 9 Jan 2022 10:27:10 +0100 Subject: [PATCH 08/11] squash, cleanup CMake changes --- CMakeLists.txt | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d6a0e9fada..5d23f4be43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -393,10 +393,10 @@ endif() option(AUTOTEST "Enable autotests (mainly for CI)" OFF) -function(auto_test target) +function(auto_test_common target extension) if(AUTOTEST AND NOT (MSVC AND ARGV1 STREQUAL "MSVC_DONT_BUILD")) add_executable(auto_${target}_test ${CPUFEATURES} - auto_tests/${target}_test.c) + auto_tests/${target}_test.${extension}) target_link_modules(auto_${target}_test toxcore misc_tools) if(NOT ARGV1 STREQUAL "DONT_RUN") add_test(NAME ${target} COMMAND ${CROSSCOMPILING_EMULATOR} auto_${target}_test) @@ -406,17 +406,12 @@ function(auto_test target) endif() endfunction() +function(auto_test target) + auto_test_common(${target} "c" ${ARGN}) +endfunction() + function(auto_test_cc target) - if(AUTOTEST AND NOT (MSVC AND ARGV1 STREQUAL "MSVC_DONT_BUILD")) - add_executable(auto_${target}_test ${CPUFEATURES} - auto_tests/${target}_test.cc) - target_link_modules(auto_${target}_test toxcore misc_tools) - if(NOT ARGV1 STREQUAL "DONT_RUN") - add_test(NAME ${target} COMMAND ${CROSSCOMPILING_EMULATOR} auto_${target}_test) - set_tests_properties(${target} PROPERTIES TIMEOUT "${TEST_TIMEOUT_SECONDS}") - set_property(TEST ${target} PROPERTY ENVIRONMENT "LLVM_PROFILE_FILE=${target}.profraw") - endif() - endif() + auto_test_common(${target} "cc" ${ARGN}) endfunction() auto_test(TCP) From 999ae392451df5b889252d2105d6fae2be67c7a6 Mon Sep 17 00:00:00 2001 From: sudden6 Date: Sun, 9 Jan 2022 10:45:27 +0100 Subject: [PATCH 09/11] squash, avoid copy initialisation --- auto_tests/toxav_mt_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auto_tests/toxav_mt_test.cc b/auto_tests/toxav_mt_test.cc index a34858a730..c1a321d01a 100644 --- a/auto_tests/toxav_mt_test.cc +++ b/auto_tests/toxav_mt_test.cc @@ -360,7 +360,7 @@ class AV_State { std::atomic_bool incomming_; std::atomic_uint32_t call_state_; - std::atomic call_start_; + std::atomic call_start_{}; std::atomic_bool in_call_; std::atomic_bool video_received_; From fdef6db9d1c6bee23f324976242d6275e3c17c58 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Sun, 9 Jan 2022 09:45:51 +0000 Subject: [PATCH 10/11] Restyled by astyle --- auto_tests/toxav_mt_test.cc | 787 +++++++++++++++++++----------------- toxcore/net_crypto.c | 2 + 2 files changed, 417 insertions(+), 372 deletions(-) diff --git a/auto_tests/toxav_mt_test.cc b/auto_tests/toxav_mt_test.cc index c1a321d01a..b01e7aef98 100644 --- a/auto_tests/toxav_mt_test.cc +++ b/auto_tests/toxav_mt_test.cc @@ -18,371 +18,411 @@ using Clock = std::chrono::high_resolution_clock; using Time_Point = std::chrono::time_point; -namespace { +namespace +{ // Maximum amount of time in iterations to wait for bootstrapping and friend // connections to succeed. constexpr uint32_t MAX_BOOTSTRAP_ITERATIONS = 1000; struct Tox_Deleter { - void operator()(Tox *tox) const { tox_kill(tox); } + void operator()(Tox *tox) const + { + tox_kill(tox); + } }; using Tox_Ptr = std::unique_ptr; struct ToxAV_Deleter { - void operator()(ToxAV *tox) const { toxav_kill(tox); } + void operator()(ToxAV *tox) const + { + toxav_kill(tox); + } }; using ToxAV_Ptr = std::unique_ptr; static void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, - size_t length, void *userdata) { - if (length == 7 && std::memcmp("gentoo", data, 7) == 0) { - Tox_Err_Friend_Add err; - tox_friend_add_norequest(m, public_key, &err); - ck_assert(err == TOX_ERR_FRIEND_ADD_OK); - } else { - // No other request expected - ck_assert(false); - } + size_t length, void *userdata) +{ + if (length == 7 && std::memcmp("gentoo", data, 7) == 0) { + Tox_Err_Friend_Add err; + tox_friend_add_norequest(m, public_key, &err); + ck_assert(err == TOX_ERR_FRIEND_ADD_OK); + } else { + // No other request expected + ck_assert(false); + } } -std::vector prepare_network(uint32_t count) { - Tox_Err_New error; +std::vector prepare_network(uint32_t count) +{ + Tox_Err_New error; - // Temporary bootstrap node - std::printf("Created 1 instance of Tox as bootstrap node\n"); - Tox_Ptr bootstrap = Tox_Ptr(tox_new_log(nullptr, &error, nullptr)); - ck_assert(error == TOX_ERR_NEW_OK); + // Temporary bootstrap node + std::printf("Created 1 instance of Tox as bootstrap node\n"); + Tox_Ptr bootstrap = Tox_Ptr(tox_new_log(nullptr, &error, nullptr)); + ck_assert(error == TOX_ERR_NEW_OK); - uint8_t bootstrap_key[TOX_PUBLIC_KEY_SIZE]; - tox_self_get_dht_id(bootstrap.get(), bootstrap_key); - const uint16_t bootstrap_port = tox_self_get_udp_port(bootstrap.get(), nullptr); + uint8_t bootstrap_key[TOX_PUBLIC_KEY_SIZE]; + tox_self_get_dht_id(bootstrap.get(), bootstrap_key); + const uint16_t bootstrap_port = tox_self_get_udp_port(bootstrap.get(), nullptr); - std::cout << "Bootstrapping " << std::to_string(count) << " Tox nodes" << std::endl; + std::cout << "Bootstrapping " << std::to_string(count) << " Tox nodes" << std::endl; - std::vector toxes(count); + std::vector toxes(count); - for (auto &tox : toxes) { - tox = Tox_Ptr(tox_new_log(nullptr, &error, nullptr)); - ck_assert(error == TOX_ERR_NEW_OK); + for (auto &tox : toxes) { + tox = Tox_Ptr(tox_new_log(nullptr, &error, nullptr)); + ck_assert(error == TOX_ERR_NEW_OK); - tox_bootstrap(tox.get(), "localhost", bootstrap_port, bootstrap_key, nullptr); - tox_callback_friend_request(tox.get(), t_accept_friend_request_cb); - } + tox_bootstrap(tox.get(), "localhost", bootstrap_port, bootstrap_key, nullptr); + tox_callback_friend_request(tox.get(), t_accept_friend_request_cb); + } - // Create fully meshed friend network - for (size_t i = 0; i < toxes.size(); ++i) { - uint8_t address[TOX_ADDRESS_SIZE]; - tox_self_get_address(toxes[i].get(), address); + // Create fully meshed friend network + for (size_t i = 0; i < toxes.size(); ++i) { + uint8_t address[TOX_ADDRESS_SIZE]; + tox_self_get_address(toxes[i].get(), address); - for (size_t j = i + 1; j < toxes.size(); ++j) { - Tox_Err_Friend_Add error_add; - tox_friend_add(toxes[j].get(), address, (const uint8_t *)"gentoo", 7, &error_add); - ck_assert(error_add == TOX_ERR_FRIEND_ADD_OK); + for (size_t j = i + 1; j < toxes.size(); ++j) { + Tox_Err_Friend_Add error_add; + tox_friend_add(toxes[j].get(), address, (const uint8_t *)"gentoo", 7, &error_add); + ck_assert(error_add == TOX_ERR_FRIEND_ADD_OK); + } } - } - // temporarily add bootstrap node to end of toxes, so we can iterate all - toxes.push_back(std::move(bootstrap)); + // temporarily add bootstrap node to end of toxes, so we can iterate all + toxes.push_back(std::move(bootstrap)); - bool online = false; + bool online = false; - auto bootstrap_start_time = Clock::now(); + auto bootstrap_start_time = Clock::now(); - uint32_t bootstrap_iteration; - for (bootstrap_iteration = 0; bootstrap_iteration < MAX_BOOTSTRAP_ITERATIONS; ++bootstrap_iteration) { - for (auto &tox : toxes) { - tox_iterate(tox.get(), nullptr); - } + uint32_t bootstrap_iteration; - if (!online) { - size_t online_cnt = std::count_if(toxes.cbegin(), toxes.cend(), [](const Tox_Ptr &tox) { - return tox_self_get_connection_status(tox.get()); - }); + for (bootstrap_iteration = 0; bootstrap_iteration < MAX_BOOTSTRAP_ITERATIONS; ++bootstrap_iteration) { + for (auto &tox : toxes) { + tox_iterate(tox.get(), nullptr); + } - if (online_cnt == toxes.size()) { - std::chrono::duration bootstrap_time = Clock::now() - bootstrap_start_time; - std::cout << "Toxes are online, took " << bootstrap_time.count() << "s" << std::endl; - online = true; - } - } + if (!online) { + size_t online_cnt = std::count_if(toxes.cbegin(), toxes.cend(), [](const Tox_Ptr & tox) { + return tox_self_get_connection_status(tox.get()); + }); - bool friends_connected = true; + if (online_cnt == toxes.size()) { + std::chrono::duration bootstrap_time = Clock::now() - bootstrap_start_time; + std::cout << "Toxes are online, took " << bootstrap_time.count() << "s" << std::endl; + online = true; + } + } - // Check if the friends are connected to each other, bootstrap node will have empty friends list - for (auto &tox : toxes) { - std::vector friend_list; - friend_list.resize(tox_self_get_friend_list_size(tox.get())); - tox_self_get_friend_list(tox.get(), friend_list.data()); - - for (auto friend_id : friend_list) { - friends_connected &= - tox_friend_get_connection_status(tox.get(), friend_id, nullptr) == TOX_CONNECTION_UDP; - } - } + bool friends_connected = true; - if (friends_connected) { - break; - } + // Check if the friends are connected to each other, bootstrap node will have empty friends list + for (auto &tox : toxes) { + std::vector friend_list; + friend_list.resize(tox_self_get_friend_list_size(tox.get())); + tox_self_get_friend_list(tox.get(), friend_list.data()); + + for (auto friend_id : friend_list) { + friends_connected &= + tox_friend_get_connection_status(tox.get(), friend_id, nullptr) == TOX_CONNECTION_UDP; + } + } + + if (friends_connected) { + break; + } - std::this_thread::sleep_for(std::chrono::milliseconds(20)); - } + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } - ck_assert(bootstrap_iteration < MAX_BOOTSTRAP_ITERATIONS); - std::chrono::duration mesh_time = Clock::now() - bootstrap_start_time; - std::cout << "Iterations to connect friend mesh: " << std::to_string(bootstrap_iteration) - << std::endl; - std::cout << "Time to connect friend mesh: " << mesh_time.count() << "s" << std::endl; + ck_assert(bootstrap_iteration < MAX_BOOTSTRAP_ITERATIONS); + std::chrono::duration mesh_time = Clock::now() - bootstrap_start_time; + std::cout << "Iterations to connect friend mesh: " << std::to_string(bootstrap_iteration) + << std::endl; + std::cout << "Time to connect friend mesh: " << mesh_time.count() << "s" << std::endl; - // Remove bootstrap node - toxes.pop_back(); + // Remove bootstrap node + toxes.pop_back(); - return toxes; + return toxes; } -class AV_State { - public: - explicit AV_State(Tox_Ptr tox, std::string name, bool combined = false) noexcept - : tox_(std::move(tox)), - combined_av_(combined), - stop_threads_{false}, - incomming_{false}, - call_state_{0}, - video_received_{false}, - audio_received_{false}, - name_{name} { - Toxav_Err_New error; - toxAV_ = ToxAV_Ptr(toxav_new(tox_.get(), &error)); - ck_assert(error == TOXAV_ERR_NEW_OK); - - toxav_callback_call(toxAV_.get(), &AV_State::toxav_call_cb, this); - toxav_callback_call_state(toxAV_.get(), &AV_State::toxav_call_state_cb, this); - toxav_callback_video_receive_frame(toxAV_.get(), &AV_State::toxav_receive_video_frame_cb, this); - toxav_callback_audio_receive_frame(toxAV_.get(), &AV_State::toxav_receive_audio_frame_cb, this); - - tox_thread_ = std::thread(&AV_State::tox_iterator, this); - - if (combined) { - av_thread_ = std::thread(&AV_State::toxav_iterator, this, Iteration_Type::TOXAV_BOTH); - } else { - audio_thread_ = std::thread(&AV_State::toxav_iterator, this, Iteration_Type::TOXAV_AUDIO); - video_thread_ = std::thread(&AV_State::toxav_iterator, this, Iteration_Type::TOXAV_VIDEO); - } - } - - ~AV_State() { stop_threads(); } - - ToxAV *get_ToxAV() const { return toxAV_.get(); } - std::mutex &get_tox_loop_lock() { return tox_loop_lock_; } - bool in_call() const { return in_call_.load(); } - uint32_t get_call_state() const { return call_state_.load(); } - void stop_threads() { - if (stop_threads_.exchange(true)) { - // already stopped - return; +class AV_State +{ +public: + explicit AV_State(Tox_Ptr tox, std::string name, bool combined = false) noexcept + : tox_(std::move(tox)), + combined_av_(combined), + stop_threads_{false}, + incomming_{false}, + call_state_{0}, + video_received_{false}, + audio_received_{false}, + name_{name} + { + Toxav_Err_New error; + toxAV_ = ToxAV_Ptr(toxav_new(tox_.get(), &error)); + ck_assert(error == TOXAV_ERR_NEW_OK); + + toxav_callback_call(toxAV_.get(), &AV_State::toxav_call_cb, this); + toxav_callback_call_state(toxAV_.get(), &AV_State::toxav_call_state_cb, this); + toxav_callback_video_receive_frame(toxAV_.get(), &AV_State::toxav_receive_video_frame_cb, this); + toxav_callback_audio_receive_frame(toxAV_.get(), &AV_State::toxav_receive_audio_frame_cb, this); + + tox_thread_ = std::thread(&AV_State::tox_iterator, this); + + if (combined) { + av_thread_ = std::thread(&AV_State::toxav_iterator, this, Iteration_Type::TOXAV_BOTH); + } else { + audio_thread_ = std::thread(&AV_State::toxav_iterator, this, Iteration_Type::TOXAV_AUDIO); + video_thread_ = std::thread(&AV_State::toxav_iterator, this, Iteration_Type::TOXAV_VIDEO); + } } - tox_thread_.join(); + ~AV_State() + { + stop_threads(); + } - if (combined_av_) { - av_thread_.join(); - } else { - audio_thread_.join(); - video_thread_.join(); + ToxAV *get_ToxAV() const + { + return toxAV_.get(); } - } - - bool did_receive_audio() const { return audio_received_.load(); } - bool did_receive_video() const { return video_received_.load(); } - - static constexpr uint32_t TEST_A_BITRATE = 48; // In kbit/s - static constexpr uint32_t TEST_V_BITRATE = 4000; // In kbit/s - static constexpr std::chrono::duration AUTO_HANGUP_TIME = std::chrono::seconds(2); - - private: - enum class Iteration_Type { - TOXAV_AUDIO, - TOXAV_VIDEO, - TOXAV_BOTH, - }; - - static void toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, - bool video_enabled, void *user_data) { - AV_State *me = static_cast(user_data); - std::cout << "[" << me->name_ << "] Handling CALL callback" << std::endl; - me->incomming_.store(true); - ck_assert(std::this_thread::get_id() == me->tox_thread_.get_id()); - } - - static void toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, - void *user_data) { - AV_State *me = static_cast(user_data); - ck_assert(std::this_thread::get_id() == me->tox_thread_.get_id()); - ck_assert(state != TOXAV_FRIEND_CALL_STATE_ERROR); - std::cout << "[" << me->name_ << "] State changed to: " << std::to_string(state) << std::endl; - me->call_state_.store(state); - Time_Point tp = me->call_start_.load(); - - if (state != TOXAV_FRIEND_CALL_STATE_NONE && tp == Time_Point()) { - me->call_start_.store(Clock::now()); - me->in_call_.store(true); + std::mutex &get_tox_loop_lock() + { + return tox_loop_lock_; } - } - - static void toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, uint16_t width, - uint16_t height, uint8_t const *y, uint8_t const *u, - uint8_t const *v, int32_t ystride, int32_t ustride, - int32_t vstride, void *user_data) { - AV_State *me = static_cast(user_data); - std::cout << "[" << me->name_ << "] Received video payload" << std::endl; - - // toxav.h states that receive events are emitted from their respective threads - if (me->combined_av_) { - ck_assert(std::this_thread::get_id() == me->av_thread_.get_id()); - } else { - ck_assert(std::this_thread::get_id() == me->video_thread_.get_id()); + bool in_call() const + { + return in_call_.load(); } + uint32_t get_call_state() const + { + return call_state_.load(); + } + void stop_threads() + { + if (stop_threads_.exchange(true)) { + // already stopped + return; + } - me->video_received_ = true; - } - - static void toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number, int16_t const *pcm, - size_t sample_count, uint8_t channels, - uint32_t sampling_rate, void *user_data) { - AV_State *me = static_cast(user_data); - std::cout << "[" << me->name_ << "] Received audio payload" << std::endl; + tox_thread_.join(); - if (me->combined_av_) { - ck_assert(std::this_thread::get_id() == me->av_thread_.get_id()); - } else { - ck_assert(std::this_thread::get_id() == me->audio_thread_.get_id()); + if (combined_av_) { + av_thread_.join(); + } else { + audio_thread_.join(); + video_thread_.join(); + } } - me->audio_received_ = true; - } - - void tox_iterator() { - while (!stop_threads_.load()) { - uint32_t sleep_ms = 0; + bool did_receive_audio() const + { + return audio_received_.load(); + } + bool did_receive_video() const + { + return video_received_.load(); + } - // Perform this block only while loop lock is held - { - std::lock_guard lock(tox_loop_lock_); - tox_iterate(tox_.get(), this); + static constexpr uint32_t TEST_A_BITRATE = 48; // In kbit/s + static constexpr uint32_t TEST_V_BITRATE = 4000; // In kbit/s + static constexpr std::chrono::duration AUTO_HANGUP_TIME = std::chrono::seconds(2); + +private: + enum class Iteration_Type { + TOXAV_AUDIO, + TOXAV_VIDEO, + TOXAV_BOTH, + }; + + static void toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, + bool video_enabled, void *user_data) + { + AV_State *me = static_cast(user_data); + std::cout << "[" << me->name_ << "] Handling CALL callback" << std::endl; + me->incomming_.store(true); + ck_assert(std::this_thread::get_id() == me->tox_thread_.get_id()); + } - // handle incoming call - if (incomming_.exchange(false)) { - Toxav_Err_Answer answer_err; - toxav_answer(toxAV_.get(), 0, TEST_A_BITRATE, TEST_V_BITRATE, &answer_err); + static void toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, + void *user_data) + { + AV_State *me = static_cast(user_data); + ck_assert(std::this_thread::get_id() == me->tox_thread_.get_id()); + ck_assert(state != TOXAV_FRIEND_CALL_STATE_ERROR); + std::cout << "[" << me->name_ << "] State changed to: " << std::to_string(state) << std::endl; + me->call_state_.store(state); + Time_Point tp = me->call_start_.load(); + + if (state != TOXAV_FRIEND_CALL_STATE_NONE && tp == Time_Point()) { + me->call_start_.store(Clock::now()); + me->in_call_.store(true); + } + } - if (answer_err != TOXAV_ERR_ANSWER_OK) { - std::printf("toxav_answer failed, Toxav_Err_Answer: %d\n", answer_err); - ck_assert(0); - } + static void toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, uint16_t width, + uint16_t height, uint8_t const *y, uint8_t const *u, + uint8_t const *v, int32_t ystride, int32_t ustride, + int32_t vstride, void *user_data) + { + AV_State *me = static_cast(user_data); + std::cout << "[" << me->name_ << "] Received video payload" << std::endl; + + // toxav.h states that receive events are emitted from their respective threads + if (me->combined_av_) { + ck_assert(std::this_thread::get_id() == me->av_thread_.get_id()); + } else { + ck_assert(std::this_thread::get_id() == me->video_thread_.get_id()); + } - std::cout << "[" << name_ << "] Answering call" << std::endl; + me->video_received_ = true; + } - call_start_ = Clock::now(); - in_call_.store(true); + static void toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number, int16_t const *pcm, + size_t sample_count, uint8_t channels, + uint32_t sampling_rate, void *user_data) + { + AV_State *me = static_cast(user_data); + std::cout << "[" << me->name_ << "] Received audio payload" << std::endl; + + if (me->combined_av_) { + ck_assert(std::this_thread::get_id() == me->av_thread_.get_id()); + } else { + ck_assert(std::this_thread::get_id() == me->audio_thread_.get_id()); } - if (in_call_.load()) { - uint32_t state = call_state_.load(); - Time_Point tp = call_start_.load(); - std::chrono::duration call_time = Clock::now() - tp; - - if (state == TOXAV_FRIEND_CALL_STATE_FINISHED) { - std::cout << "[" << name_ << "] Call ended by other side after: " << call_time.count() - << "s" << std::endl; - in_call_.store(false); - } else if (tp > Time_Point() && call_time > AV_State::AUTO_HANGUP_TIME) { - std::cout << "[" << name_ << "] Ending call after: " << call_time.count() << "s" - << std::endl; - Toxav_Err_Call_Control cc_err; - toxav_call_control(toxAV_.get(), 0, TOXAV_CALL_CONTROL_CANCEL, &cc_err); - - // Ignore FRIEND_NOT_IN_CALL for the case where the other side hangs up simultaneously - if (cc_err != TOXAV_ERR_CALL_CONTROL_OK && - cc_err != TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL) { - std::printf("toxav_call_control failed: %d\n", cc_err); - ck_assert(0); + me->audio_received_ = true; + } + + void tox_iterator() + { + while (!stop_threads_.load()) { + uint32_t sleep_ms = 0; + + // Perform this block only while loop lock is held + { + std::lock_guard lock(tox_loop_lock_); + tox_iterate(tox_.get(), this); + + // handle incoming call + if (incomming_.exchange(false)) { + Toxav_Err_Answer answer_err; + toxav_answer(toxAV_.get(), 0, TEST_A_BITRATE, TEST_V_BITRATE, &answer_err); + + if (answer_err != TOXAV_ERR_ANSWER_OK) { + std::printf("toxav_answer failed, Toxav_Err_Answer: %d\n", answer_err); + ck_assert(0); + } + + std::cout << "[" << name_ << "] Answering call" << std::endl; + + call_start_ = Clock::now(); + in_call_.store(true); + } + + if (in_call_.load()) { + uint32_t state = call_state_.load(); + Time_Point tp = call_start_.load(); + std::chrono::duration call_time = Clock::now() - tp; + + if (state == TOXAV_FRIEND_CALL_STATE_FINISHED) { + std::cout << "[" << name_ << "] Call ended by other side after: " << call_time.count() + << "s" << std::endl; + in_call_.store(false); + } else if (tp > Time_Point() && call_time > AV_State::AUTO_HANGUP_TIME) { + std::cout << "[" << name_ << "] Ending call after: " << call_time.count() << "s" + << std::endl; + Toxav_Err_Call_Control cc_err; + toxav_call_control(toxAV_.get(), 0, TOXAV_CALL_CONTROL_CANCEL, &cc_err); + + // Ignore FRIEND_NOT_IN_CALL for the case where the other side hangs up simultaneously + if (cc_err != TOXAV_ERR_CALL_CONTROL_OK && + cc_err != TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL) { + std::printf("toxav_call_control failed: %d\n", cc_err); + ck_assert(0); + } + + in_call_.store(false); + } + } + + // WARNING: This accesses the Tox struct, so it must be inside the lock + tox_iteration_interval(tox_.get()); } - in_call_.store(false); - } + std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms)); } - - // WARNING: This accesses the Tox struct, so it must be inside the lock - tox_iteration_interval(tox_.get()); - } - - std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms)); } - } - - void toxav_iterator(Iteration_Type type) { - while (!stop_threads_.load()) { - switch (type) { - case Iteration_Type::TOXAV_AUDIO: - toxav_audio_iterate(toxAV_.get()); - std::this_thread::sleep_for( - std::chrono::milliseconds(toxav_audio_iteration_interval(toxAV_.get()))); - break; - - case Iteration_Type::TOXAV_VIDEO: - toxav_video_iterate(toxAV_.get()); - std::this_thread::sleep_for( - std::chrono::milliseconds(toxav_video_iteration_interval(toxAV_.get()))); - break; - - case Iteration_Type::TOXAV_BOTH: - toxav_iterate(toxAV_.get()); - std::this_thread::sleep_for( - std::chrono::milliseconds(toxav_iteration_interval(toxAV_.get()))); - break; - } + + void toxav_iterator(Iteration_Type type) + { + while (!stop_threads_.load()) { + switch (type) { + case Iteration_Type::TOXAV_AUDIO: + toxav_audio_iterate(toxAV_.get()); + std::this_thread::sleep_for( + std::chrono::milliseconds(toxav_audio_iteration_interval(toxAV_.get()))); + break; + + case Iteration_Type::TOXAV_VIDEO: + toxav_video_iterate(toxAV_.get()); + std::this_thread::sleep_for( + std::chrono::milliseconds(toxav_video_iteration_interval(toxAV_.get()))); + break; + + case Iteration_Type::TOXAV_BOTH: + toxav_iterate(toxAV_.get()); + std::this_thread::sleep_for( + std::chrono::milliseconds(toxav_iteration_interval(toxAV_.get()))); + break; + } + } } - } - std::thread tox_thread_; - std::thread audio_thread_; - std::thread video_thread_; - std::thread av_thread_; + std::thread tox_thread_; + std::thread audio_thread_; + std::thread video_thread_; + std::thread av_thread_; - std::mutex tox_loop_lock_; + std::mutex tox_loop_lock_; - Tox_Ptr tox_; - bool combined_av_; - ToxAV_Ptr toxAV_; + Tox_Ptr tox_; + bool combined_av_; + ToxAV_Ptr toxAV_; - std::atomic_bool stop_threads_; - std::atomic_bool incomming_; - std::atomic_uint32_t call_state_; + std::atomic_bool stop_threads_; + std::atomic_bool incomming_; + std::atomic_uint32_t call_state_; - std::atomic call_start_{}; - std::atomic_bool in_call_; + std::atomic call_start_{}; + std::atomic_bool in_call_; - std::atomic_bool video_received_; - std::atomic_bool audio_received_; - std::string name_; + std::atomic_bool video_received_; + std::atomic_bool audio_received_; + std::string name_; }; struct DUMMY_PCM { - static constexpr size_t sample_count = 960; - static constexpr int16_t pcm[sample_count] = {0}; - static constexpr uint8_t channels = 1; - static constexpr uint32_t sampling_rate = 48000; + static constexpr size_t sample_count = 960; + static constexpr int16_t pcm[sample_count] = {0}; + static constexpr uint8_t channels = 1; + static constexpr uint32_t sampling_rate = 48000; }; struct DUMMY_VIDEO { - // https://en.wikipedia.org/wiki/Graphics_display_resolution#QQVGA size - static constexpr uint16_t width = 160; - static constexpr uint16_t height = 120; + // https://en.wikipedia.org/wiki/Graphics_display_resolution#QQVGA size + static constexpr uint16_t width = 160; + static constexpr uint16_t height = 120; - static constexpr uint8_t y[width * height] = {0}; - static constexpr uint8_t u[width / 2 * height / 2] = {0}; - static constexpr uint8_t v[width / 2 * height / 2] = {0}; + static constexpr uint8_t y[width * height] = {0}; + static constexpr uint8_t u[width / 2 * height / 2] = {0}; + static constexpr uint8_t v[width / 2 * height / 2] = {0}; }; // FIXME: once we upgrade to C++17, remove these, reason: https://stackoverflow.com/a/28846608 @@ -392,115 +432,118 @@ constexpr uint8_t DUMMY_VIDEO::y[]; constexpr uint8_t DUMMY_VIDEO::u[]; constexpr uint8_t DUMMY_VIDEO::v[]; -static void test_av(bool combined_av) { - std::cout << "Testing Audio and Video in " << (combined_av ? "combined" : "separate") << " threads" - << std::endl; - auto toxes = prepare_network(2); - - AV_State alice(std::move(toxes[0]), "alice", false); - AV_State bob(std::move(toxes[1]), "bob", false); - - // Let alice call bob - { - std::lock_guard(alice.get_tox_loop_lock()); - Toxav_Err_Call err; - ck_assert( - toxav_call(alice.get_ToxAV(), 0, AV_State::TEST_A_BITRATE, AV_State::TEST_V_BITRATE, &err)); - ck_assert(err == TOXAV_ERR_CALL_OK); - } +static void test_av(bool combined_av) +{ + std::cout << "Testing Audio and Video in " << (combined_av ? "combined" : "separate") << " threads" + << std::endl; + auto toxes = prepare_network(2); + + AV_State alice(std::move(toxes[0]), "alice", false); + AV_State bob(std::move(toxes[1]), "bob", false); + + // Let alice call bob + { + std::lock_guard(alice.get_tox_loop_lock()); + Toxav_Err_Call err; + ck_assert( + toxav_call(alice.get_ToxAV(), 0, AV_State::TEST_A_BITRATE, AV_State::TEST_V_BITRATE, &err)); + ck_assert(err == TOXAV_ERR_CALL_OK); + } - std::cout << "alice started a call" << std::endl; + std::cout << "alice started a call" << std::endl; - auto poll_state = [](AV_State &av, uint32_t expected, uint32_t max_tries, - uint32_t delay_ms) -> bool { - for (uint32_t i = 0; i < max_tries; ++i) { - uint32_t state = av.get_call_state(); + auto poll_state = [](AV_State & av, uint32_t expected, uint32_t max_tries, + uint32_t delay_ms) -> bool { + for (uint32_t i = 0; i < max_tries; ++i) + { + uint32_t state = av.get_call_state(); - if (state == expected) { - return true; - } + if (state == expected) { + return true; + } - std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms)); - } + std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms)); + } - return false; - }; + return false; + }; - // We're starting with full AV on both sides - constexpr uint32_t full_AV_mask = - TOXAV_FRIEND_CALL_STATE_SENDING_A | TOXAV_FRIEND_CALL_STATE_SENDING_V | - TOXAV_FRIEND_CALL_STATE_ACCEPTING_A | TOXAV_FRIEND_CALL_STATE_ACCEPTING_V; + // We're starting with full AV on both sides + constexpr uint32_t full_AV_mask = + TOXAV_FRIEND_CALL_STATE_SENDING_A | TOXAV_FRIEND_CALL_STATE_SENDING_V | + TOXAV_FRIEND_CALL_STATE_ACCEPTING_A | TOXAV_FRIEND_CALL_STATE_ACCEPTING_V; - // Wait 2s until the call is established on both sides - ck_assert(poll_state(alice, full_AV_mask, 20, 100)); + // Wait 2s until the call is established on both sides + ck_assert(poll_state(alice, full_AV_mask, 20, 100)); - // TODO: why is Bob's call state not updated? - // ck_assert(poll_state(bob, full_AV_mask, 20, 100)); asserts + // TODO: why is Bob's call state not updated? + // ck_assert(poll_state(bob, full_AV_mask, 20, 100)); asserts - ck_assert(alice.in_call()); - ck_assert(bob.in_call()); + ck_assert(alice.in_call()); + ck_assert(bob.in_call()); - std::cout << "alice and bob are in the call" << std::endl; + std::cout << "alice and bob are in the call" << std::endl; - auto toxav_audio_send_frame_dummy = [](ToxAV *av, Toxav_Err_Send_Frame *error) -> bool { - return toxav_audio_send_frame(av, 0, DUMMY_PCM::pcm, DUMMY_PCM::sample_count, - DUMMY_PCM::channels, DUMMY_PCM::sampling_rate, error); - }; + auto toxav_audio_send_frame_dummy = [](ToxAV * av, Toxav_Err_Send_Frame * error) -> bool { + return toxav_audio_send_frame(av, 0, DUMMY_PCM::pcm, DUMMY_PCM::sample_count, + DUMMY_PCM::channels, DUMMY_PCM::sampling_rate, error); + }; - auto toxav_video_send_frame_dummy = [](ToxAV *av, Toxav_Err_Send_Frame *error) -> bool { - return toxav_video_send_frame(av, 0, DUMMY_VIDEO::width, DUMMY_VIDEO::height, DUMMY_VIDEO::y, - DUMMY_VIDEO::u, DUMMY_VIDEO::v, error); - }; + auto toxav_video_send_frame_dummy = [](ToxAV * av, Toxav_Err_Send_Frame * error) -> bool { + return toxav_video_send_frame(av, 0, DUMMY_VIDEO::width, DUMMY_VIDEO::height, DUMMY_VIDEO::y, + DUMMY_VIDEO::u, DUMMY_VIDEO::v, error); + }; - // Send frames from alice to bob - { - std::lock_guard(alice.get_tox_loop_lock()); - Toxav_Err_Send_Frame err; - ck_assert(toxav_audio_send_frame_dummy(alice.get_ToxAV(), &err)); - ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); + // Send frames from alice to bob + { + std::lock_guard(alice.get_tox_loop_lock()); + Toxav_Err_Send_Frame err; + ck_assert(toxav_audio_send_frame_dummy(alice.get_ToxAV(), &err)); + ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); - ck_assert(toxav_video_send_frame_dummy(alice.get_ToxAV(), &err)); - ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); - } + ck_assert(toxav_video_send_frame_dummy(alice.get_ToxAV(), &err)); + ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); + } - // Send frames from bob to alice - { - std::lock_guard(bob.get_tox_loop_lock()); - Toxav_Err_Send_Frame err; - ck_assert(toxav_audio_send_frame_dummy(bob.get_ToxAV(), &err)); - ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); + // Send frames from bob to alice + { + std::lock_guard(bob.get_tox_loop_lock()); + Toxav_Err_Send_Frame err; + ck_assert(toxav_audio_send_frame_dummy(bob.get_ToxAV(), &err)); + ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); - ck_assert(toxav_video_send_frame_dummy(bob.get_ToxAV(), &err)); - ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); - } + ck_assert(toxav_video_send_frame_dummy(bob.get_ToxAV(), &err)); + ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); + } - // auto hangup after 2s, wait 3s for this - ck_assert(poll_state(alice, TOXAV_FRIEND_CALL_STATE_FINISHED, 30, 100)); + // auto hangup after 2s, wait 3s for this + ck_assert(poll_state(alice, TOXAV_FRIEND_CALL_STATE_FINISHED, 30, 100)); - // TODO: why is Bobs call state not updated? - // ck_assert(poll_state(bob, TOXAV_FRIEND_CALL_STATE_FINISHED, 30, 100)); fails + // TODO: why is Bobs call state not updated? + // ck_assert(poll_state(bob, TOXAV_FRIEND_CALL_STATE_FINISHED, 30, 100)); fails - ck_assert(!alice.in_call()); - ck_assert(!bob.in_call()); + ck_assert(!alice.in_call()); + ck_assert(!bob.in_call()); - std::cout << "The call ended" << std::endl; + std::cout << "The call ended" << std::endl; - alice.stop_threads(); - bob.stop_threads(); + alice.stop_threads(); + bob.stop_threads(); - ck_assert(alice.did_receive_audio()); - ck_assert(alice.did_receive_video()); - ck_assert(bob.did_receive_audio()); - ck_assert(bob.did_receive_video()); + ck_assert(alice.did_receive_audio()); + ck_assert(alice.did_receive_video()); + ck_assert(bob.did_receive_audio()); + ck_assert(bob.did_receive_video()); } } // namespace -int main(void) { - setvbuf(stdout, nullptr, _IONBF, 0); +int main(void) +{ + setvbuf(stdout, nullptr, _IONBF, 0); - test_av(true); - test_av(false); + test_av(true); + test_av(false); - return 0; + return 0; } diff --git a/toxcore/net_crypto.c b/toxcore/net_crypto.c index 318c569069..1462ef9a2f 100644 --- a/toxcore/net_crypto.c +++ b/toxcore/net_crypto.c @@ -1179,9 +1179,11 @@ static int64_t send_lossless_packet(Net_Crypto *c, int crypt_connection_id, cons Packet_Data *dt1 = nullptr; pthread_mutex_lock(conn->mutex); + if (get_data_pointer(c->log, &conn->send_array, &dt1, packet_num) == 1) { dt1->sent_time = current_time_monotonic(c->mono_time); } + pthread_mutex_unlock(conn->mutex); } else { conn->maximum_speed_reached = 1; From 38aaaa36f54e102cb6bc78fc6f348d8ce63acb33 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Sun, 9 Jan 2022 09:45:52 +0000 Subject: [PATCH 11/11] Restyled by clang-format --- auto_tests/toxav_mt_test.cc | 787 +++++++++++++++++------------------- 1 file changed, 373 insertions(+), 414 deletions(-) diff --git a/auto_tests/toxav_mt_test.cc b/auto_tests/toxav_mt_test.cc index b01e7aef98..ef400bdd84 100644 --- a/auto_tests/toxav_mt_test.cc +++ b/auto_tests/toxav_mt_test.cc @@ -18,411 +18,373 @@ using Clock = std::chrono::high_resolution_clock; using Time_Point = std::chrono::time_point; -namespace -{ +namespace { // Maximum amount of time in iterations to wait for bootstrapping and friend // connections to succeed. constexpr uint32_t MAX_BOOTSTRAP_ITERATIONS = 1000; struct Tox_Deleter { - void operator()(Tox *tox) const - { - tox_kill(tox); - } + void operator()(Tox *tox) const { tox_kill(tox); } }; using Tox_Ptr = std::unique_ptr; struct ToxAV_Deleter { - void operator()(ToxAV *tox) const - { - toxav_kill(tox); - } + void operator()(ToxAV *tox) const { toxav_kill(tox); } }; using ToxAV_Ptr = std::unique_ptr; static void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, - size_t length, void *userdata) -{ - if (length == 7 && std::memcmp("gentoo", data, 7) == 0) { - Tox_Err_Friend_Add err; - tox_friend_add_norequest(m, public_key, &err); - ck_assert(err == TOX_ERR_FRIEND_ADD_OK); - } else { - // No other request expected - ck_assert(false); - } + size_t length, void *userdata) { + if (length == 7 && std::memcmp("gentoo", data, 7) == 0) { + Tox_Err_Friend_Add err; + tox_friend_add_norequest(m, public_key, &err); + ck_assert(err == TOX_ERR_FRIEND_ADD_OK); + } else { + // No other request expected + ck_assert(false); + } } -std::vector prepare_network(uint32_t count) -{ - Tox_Err_New error; +std::vector prepare_network(uint32_t count) { + Tox_Err_New error; - // Temporary bootstrap node - std::printf("Created 1 instance of Tox as bootstrap node\n"); - Tox_Ptr bootstrap = Tox_Ptr(tox_new_log(nullptr, &error, nullptr)); - ck_assert(error == TOX_ERR_NEW_OK); + // Temporary bootstrap node + std::printf("Created 1 instance of Tox as bootstrap node\n"); + Tox_Ptr bootstrap = Tox_Ptr(tox_new_log(nullptr, &error, nullptr)); + ck_assert(error == TOX_ERR_NEW_OK); - uint8_t bootstrap_key[TOX_PUBLIC_KEY_SIZE]; - tox_self_get_dht_id(bootstrap.get(), bootstrap_key); - const uint16_t bootstrap_port = tox_self_get_udp_port(bootstrap.get(), nullptr); + uint8_t bootstrap_key[TOX_PUBLIC_KEY_SIZE]; + tox_self_get_dht_id(bootstrap.get(), bootstrap_key); + const uint16_t bootstrap_port = tox_self_get_udp_port(bootstrap.get(), nullptr); - std::cout << "Bootstrapping " << std::to_string(count) << " Tox nodes" << std::endl; + std::cout << "Bootstrapping " << std::to_string(count) << " Tox nodes" << std::endl; - std::vector toxes(count); + std::vector toxes(count); - for (auto &tox : toxes) { - tox = Tox_Ptr(tox_new_log(nullptr, &error, nullptr)); - ck_assert(error == TOX_ERR_NEW_OK); + for (auto &tox : toxes) { + tox = Tox_Ptr(tox_new_log(nullptr, &error, nullptr)); + ck_assert(error == TOX_ERR_NEW_OK); - tox_bootstrap(tox.get(), "localhost", bootstrap_port, bootstrap_key, nullptr); - tox_callback_friend_request(tox.get(), t_accept_friend_request_cb); - } + tox_bootstrap(tox.get(), "localhost", bootstrap_port, bootstrap_key, nullptr); + tox_callback_friend_request(tox.get(), t_accept_friend_request_cb); + } - // Create fully meshed friend network - for (size_t i = 0; i < toxes.size(); ++i) { - uint8_t address[TOX_ADDRESS_SIZE]; - tox_self_get_address(toxes[i].get(), address); + // Create fully meshed friend network + for (size_t i = 0; i < toxes.size(); ++i) { + uint8_t address[TOX_ADDRESS_SIZE]; + tox_self_get_address(toxes[i].get(), address); - for (size_t j = i + 1; j < toxes.size(); ++j) { - Tox_Err_Friend_Add error_add; - tox_friend_add(toxes[j].get(), address, (const uint8_t *)"gentoo", 7, &error_add); - ck_assert(error_add == TOX_ERR_FRIEND_ADD_OK); - } + for (size_t j = i + 1; j < toxes.size(); ++j) { + Tox_Err_Friend_Add error_add; + tox_friend_add(toxes[j].get(), address, (const uint8_t *)"gentoo", 7, &error_add); + ck_assert(error_add == TOX_ERR_FRIEND_ADD_OK); } + } - // temporarily add bootstrap node to end of toxes, so we can iterate all - toxes.push_back(std::move(bootstrap)); - - bool online = false; + // temporarily add bootstrap node to end of toxes, so we can iterate all + toxes.push_back(std::move(bootstrap)); - auto bootstrap_start_time = Clock::now(); + bool online = false; - uint32_t bootstrap_iteration; - - for (bootstrap_iteration = 0; bootstrap_iteration < MAX_BOOTSTRAP_ITERATIONS; ++bootstrap_iteration) { - for (auto &tox : toxes) { - tox_iterate(tox.get(), nullptr); - } + auto bootstrap_start_time = Clock::now(); - if (!online) { - size_t online_cnt = std::count_if(toxes.cbegin(), toxes.cend(), [](const Tox_Ptr & tox) { - return tox_self_get_connection_status(tox.get()); - }); + uint32_t bootstrap_iteration; - if (online_cnt == toxes.size()) { - std::chrono::duration bootstrap_time = Clock::now() - bootstrap_start_time; - std::cout << "Toxes are online, took " << bootstrap_time.count() << "s" << std::endl; - online = true; - } - } + for (bootstrap_iteration = 0; bootstrap_iteration < MAX_BOOTSTRAP_ITERATIONS; + ++bootstrap_iteration) { + for (auto &tox : toxes) { + tox_iterate(tox.get(), nullptr); + } - bool friends_connected = true; + if (!online) { + size_t online_cnt = std::count_if(toxes.cbegin(), toxes.cend(), [](const Tox_Ptr &tox) { + return tox_self_get_connection_status(tox.get()); + }); - // Check if the friends are connected to each other, bootstrap node will have empty friends list - for (auto &tox : toxes) { - std::vector friend_list; - friend_list.resize(tox_self_get_friend_list_size(tox.get())); - tox_self_get_friend_list(tox.get(), friend_list.data()); + if (online_cnt == toxes.size()) { + std::chrono::duration bootstrap_time = Clock::now() - bootstrap_start_time; + std::cout << "Toxes are online, took " << bootstrap_time.count() << "s" << std::endl; + online = true; + } + } - for (auto friend_id : friend_list) { - friends_connected &= - tox_friend_get_connection_status(tox.get(), friend_id, nullptr) == TOX_CONNECTION_UDP; - } - } + bool friends_connected = true; - if (friends_connected) { - break; - } + // Check if the friends are connected to each other, bootstrap node will have empty friends list + for (auto &tox : toxes) { + std::vector friend_list; + friend_list.resize(tox_self_get_friend_list_size(tox.get())); + tox_self_get_friend_list(tox.get(), friend_list.data()); + + for (auto friend_id : friend_list) { + friends_connected &= + tox_friend_get_connection_status(tox.get(), friend_id, nullptr) == TOX_CONNECTION_UDP; + } + } - std::this_thread::sleep_for(std::chrono::milliseconds(20)); + if (friends_connected) { + break; } - ck_assert(bootstrap_iteration < MAX_BOOTSTRAP_ITERATIONS); - std::chrono::duration mesh_time = Clock::now() - bootstrap_start_time; - std::cout << "Iterations to connect friend mesh: " << std::to_string(bootstrap_iteration) - << std::endl; - std::cout << "Time to connect friend mesh: " << mesh_time.count() << "s" << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } - // Remove bootstrap node - toxes.pop_back(); + ck_assert(bootstrap_iteration < MAX_BOOTSTRAP_ITERATIONS); + std::chrono::duration mesh_time = Clock::now() - bootstrap_start_time; + std::cout << "Iterations to connect friend mesh: " << std::to_string(bootstrap_iteration) + << std::endl; + std::cout << "Time to connect friend mesh: " << mesh_time.count() << "s" << std::endl; - return toxes; + // Remove bootstrap node + toxes.pop_back(); + + return toxes; } -class AV_State -{ -public: - explicit AV_State(Tox_Ptr tox, std::string name, bool combined = false) noexcept - : tox_(std::move(tox)), - combined_av_(combined), - stop_threads_{false}, - incomming_{false}, - call_state_{0}, - video_received_{false}, - audio_received_{false}, - name_{name} - { - Toxav_Err_New error; - toxAV_ = ToxAV_Ptr(toxav_new(tox_.get(), &error)); - ck_assert(error == TOXAV_ERR_NEW_OK); - - toxav_callback_call(toxAV_.get(), &AV_State::toxav_call_cb, this); - toxav_callback_call_state(toxAV_.get(), &AV_State::toxav_call_state_cb, this); - toxav_callback_video_receive_frame(toxAV_.get(), &AV_State::toxav_receive_video_frame_cb, this); - toxav_callback_audio_receive_frame(toxAV_.get(), &AV_State::toxav_receive_audio_frame_cb, this); - - tox_thread_ = std::thread(&AV_State::tox_iterator, this); - - if (combined) { - av_thread_ = std::thread(&AV_State::toxav_iterator, this, Iteration_Type::TOXAV_BOTH); - } else { - audio_thread_ = std::thread(&AV_State::toxav_iterator, this, Iteration_Type::TOXAV_AUDIO); - video_thread_ = std::thread(&AV_State::toxav_iterator, this, Iteration_Type::TOXAV_VIDEO); - } +class AV_State { + public: + explicit AV_State(Tox_Ptr tox, std::string name, bool combined = false) noexcept + : tox_(std::move(tox)), + combined_av_(combined), + stop_threads_{false}, + incomming_{false}, + call_state_{0}, + video_received_{false}, + audio_received_{false}, + name_{name} { + Toxav_Err_New error; + toxAV_ = ToxAV_Ptr(toxav_new(tox_.get(), &error)); + ck_assert(error == TOXAV_ERR_NEW_OK); + + toxav_callback_call(toxAV_.get(), &AV_State::toxav_call_cb, this); + toxav_callback_call_state(toxAV_.get(), &AV_State::toxav_call_state_cb, this); + toxav_callback_video_receive_frame(toxAV_.get(), &AV_State::toxav_receive_video_frame_cb, this); + toxav_callback_audio_receive_frame(toxAV_.get(), &AV_State::toxav_receive_audio_frame_cb, this); + + tox_thread_ = std::thread(&AV_State::tox_iterator, this); + + if (combined) { + av_thread_ = std::thread(&AV_State::toxav_iterator, this, Iteration_Type::TOXAV_BOTH); + } else { + audio_thread_ = std::thread(&AV_State::toxav_iterator, this, Iteration_Type::TOXAV_AUDIO); + video_thread_ = std::thread(&AV_State::toxav_iterator, this, Iteration_Type::TOXAV_VIDEO); } - - ~AV_State() - { - stop_threads(); + } + + ~AV_State() { stop_threads(); } + + ToxAV *get_ToxAV() const { return toxAV_.get(); } + std::mutex &get_tox_loop_lock() { return tox_loop_lock_; } + bool in_call() const { return in_call_.load(); } + uint32_t get_call_state() const { return call_state_.load(); } + void stop_threads() { + if (stop_threads_.exchange(true)) { + // already stopped + return; } - ToxAV *get_ToxAV() const - { - return toxAV_.get(); - } - std::mutex &get_tox_loop_lock() - { - return tox_loop_lock_; + tox_thread_.join(); + + if (combined_av_) { + av_thread_.join(); + } else { + audio_thread_.join(); + video_thread_.join(); } - bool in_call() const - { - return in_call_.load(); + } + + bool did_receive_audio() const { return audio_received_.load(); } + bool did_receive_video() const { return video_received_.load(); } + + static constexpr uint32_t TEST_A_BITRATE = 48; // In kbit/s + static constexpr uint32_t TEST_V_BITRATE = 4000; // In kbit/s + static constexpr std::chrono::duration AUTO_HANGUP_TIME = std::chrono::seconds(2); + + private: + enum class Iteration_Type { + TOXAV_AUDIO, + TOXAV_VIDEO, + TOXAV_BOTH, + }; + + static void toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, + bool video_enabled, void *user_data) { + AV_State *me = static_cast(user_data); + std::cout << "[" << me->name_ << "] Handling CALL callback" << std::endl; + me->incomming_.store(true); + ck_assert(std::this_thread::get_id() == me->tox_thread_.get_id()); + } + + static void toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, + void *user_data) { + AV_State *me = static_cast(user_data); + ck_assert(std::this_thread::get_id() == me->tox_thread_.get_id()); + ck_assert(state != TOXAV_FRIEND_CALL_STATE_ERROR); + std::cout << "[" << me->name_ << "] State changed to: " << std::to_string(state) << std::endl; + me->call_state_.store(state); + Time_Point tp = me->call_start_.load(); + + if (state != TOXAV_FRIEND_CALL_STATE_NONE && tp == Time_Point()) { + me->call_start_.store(Clock::now()); + me->in_call_.store(true); } - uint32_t get_call_state() const - { - return call_state_.load(); + } + + static void toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, uint16_t width, + uint16_t height, uint8_t const *y, uint8_t const *u, + uint8_t const *v, int32_t ystride, int32_t ustride, + int32_t vstride, void *user_data) { + AV_State *me = static_cast(user_data); + std::cout << "[" << me->name_ << "] Received video payload" << std::endl; + + // toxav.h states that receive events are emitted from their respective threads + if (me->combined_av_) { + ck_assert(std::this_thread::get_id() == me->av_thread_.get_id()); + } else { + ck_assert(std::this_thread::get_id() == me->video_thread_.get_id()); } - void stop_threads() - { - if (stop_threads_.exchange(true)) { - // already stopped - return; - } - tox_thread_.join(); + me->video_received_ = true; + } - if (combined_av_) { - av_thread_.join(); - } else { - audio_thread_.join(); - video_thread_.join(); - } - } - - bool did_receive_audio() const - { - return audio_received_.load(); - } - bool did_receive_video() const - { - return video_received_.load(); - } + static void toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number, int16_t const *pcm, + size_t sample_count, uint8_t channels, + uint32_t sampling_rate, void *user_data) { + AV_State *me = static_cast(user_data); + std::cout << "[" << me->name_ << "] Received audio payload" << std::endl; - static constexpr uint32_t TEST_A_BITRATE = 48; // In kbit/s - static constexpr uint32_t TEST_V_BITRATE = 4000; // In kbit/s - static constexpr std::chrono::duration AUTO_HANGUP_TIME = std::chrono::seconds(2); - -private: - enum class Iteration_Type { - TOXAV_AUDIO, - TOXAV_VIDEO, - TOXAV_BOTH, - }; - - static void toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, - bool video_enabled, void *user_data) - { - AV_State *me = static_cast(user_data); - std::cout << "[" << me->name_ << "] Handling CALL callback" << std::endl; - me->incomming_.store(true); - ck_assert(std::this_thread::get_id() == me->tox_thread_.get_id()); + if (me->combined_av_) { + ck_assert(std::this_thread::get_id() == me->av_thread_.get_id()); + } else { + ck_assert(std::this_thread::get_id() == me->audio_thread_.get_id()); } - static void toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, - void *user_data) - { - AV_State *me = static_cast(user_data); - ck_assert(std::this_thread::get_id() == me->tox_thread_.get_id()); - ck_assert(state != TOXAV_FRIEND_CALL_STATE_ERROR); - std::cout << "[" << me->name_ << "] State changed to: " << std::to_string(state) << std::endl; - me->call_state_.store(state); - Time_Point tp = me->call_start_.load(); - - if (state != TOXAV_FRIEND_CALL_STATE_NONE && tp == Time_Point()) { - me->call_start_.store(Clock::now()); - me->in_call_.store(true); - } - } + me->audio_received_ = true; + } - static void toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, uint16_t width, - uint16_t height, uint8_t const *y, uint8_t const *u, - uint8_t const *v, int32_t ystride, int32_t ustride, - int32_t vstride, void *user_data) - { - AV_State *me = static_cast(user_data); - std::cout << "[" << me->name_ << "] Received video payload" << std::endl; - - // toxav.h states that receive events are emitted from their respective threads - if (me->combined_av_) { - ck_assert(std::this_thread::get_id() == me->av_thread_.get_id()); - } else { - ck_assert(std::this_thread::get_id() == me->video_thread_.get_id()); - } + void tox_iterator() { + while (!stop_threads_.load()) { + uint32_t sleep_ms = 0; - me->video_received_ = true; - } + // Perform this block only while loop lock is held + { + std::lock_guard lock(tox_loop_lock_); + tox_iterate(tox_.get(), this); - static void toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number, int16_t const *pcm, - size_t sample_count, uint8_t channels, - uint32_t sampling_rate, void *user_data) - { - AV_State *me = static_cast(user_data); - std::cout << "[" << me->name_ << "] Received audio payload" << std::endl; - - if (me->combined_av_) { - ck_assert(std::this_thread::get_id() == me->av_thread_.get_id()); - } else { - ck_assert(std::this_thread::get_id() == me->audio_thread_.get_id()); - } + // handle incoming call + if (incomming_.exchange(false)) { + Toxav_Err_Answer answer_err; + toxav_answer(toxAV_.get(), 0, TEST_A_BITRATE, TEST_V_BITRATE, &answer_err); - me->audio_received_ = true; - } + if (answer_err != TOXAV_ERR_ANSWER_OK) { + std::printf("toxav_answer failed, Toxav_Err_Answer: %d\n", answer_err); + ck_assert(0); + } - void tox_iterator() - { - while (!stop_threads_.load()) { - uint32_t sleep_ms = 0; - - // Perform this block only while loop lock is held - { - std::lock_guard lock(tox_loop_lock_); - tox_iterate(tox_.get(), this); - - // handle incoming call - if (incomming_.exchange(false)) { - Toxav_Err_Answer answer_err; - toxav_answer(toxAV_.get(), 0, TEST_A_BITRATE, TEST_V_BITRATE, &answer_err); - - if (answer_err != TOXAV_ERR_ANSWER_OK) { - std::printf("toxav_answer failed, Toxav_Err_Answer: %d\n", answer_err); - ck_assert(0); - } - - std::cout << "[" << name_ << "] Answering call" << std::endl; - - call_start_ = Clock::now(); - in_call_.store(true); - } - - if (in_call_.load()) { - uint32_t state = call_state_.load(); - Time_Point tp = call_start_.load(); - std::chrono::duration call_time = Clock::now() - tp; - - if (state == TOXAV_FRIEND_CALL_STATE_FINISHED) { - std::cout << "[" << name_ << "] Call ended by other side after: " << call_time.count() - << "s" << std::endl; - in_call_.store(false); - } else if (tp > Time_Point() && call_time > AV_State::AUTO_HANGUP_TIME) { - std::cout << "[" << name_ << "] Ending call after: " << call_time.count() << "s" - << std::endl; - Toxav_Err_Call_Control cc_err; - toxav_call_control(toxAV_.get(), 0, TOXAV_CALL_CONTROL_CANCEL, &cc_err); - - // Ignore FRIEND_NOT_IN_CALL for the case where the other side hangs up simultaneously - if (cc_err != TOXAV_ERR_CALL_CONTROL_OK && - cc_err != TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL) { - std::printf("toxav_call_control failed: %d\n", cc_err); - ck_assert(0); - } - - in_call_.store(false); - } - } - - // WARNING: This accesses the Tox struct, so it must be inside the lock - tox_iteration_interval(tox_.get()); - } + std::cout << "[" << name_ << "] Answering call" << std::endl; - std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms)); + call_start_ = Clock::now(); + in_call_.store(true); } - } - void toxav_iterator(Iteration_Type type) - { - while (!stop_threads_.load()) { - switch (type) { - case Iteration_Type::TOXAV_AUDIO: - toxav_audio_iterate(toxAV_.get()); - std::this_thread::sleep_for( - std::chrono::milliseconds(toxav_audio_iteration_interval(toxAV_.get()))); - break; - - case Iteration_Type::TOXAV_VIDEO: - toxav_video_iterate(toxAV_.get()); - std::this_thread::sleep_for( - std::chrono::milliseconds(toxav_video_iteration_interval(toxAV_.get()))); - break; - - case Iteration_Type::TOXAV_BOTH: - toxav_iterate(toxAV_.get()); - std::this_thread::sleep_for( - std::chrono::milliseconds(toxav_iteration_interval(toxAV_.get()))); - break; + if (in_call_.load()) { + uint32_t state = call_state_.load(); + Time_Point tp = call_start_.load(); + std::chrono::duration call_time = Clock::now() - tp; + + if (state == TOXAV_FRIEND_CALL_STATE_FINISHED) { + std::cout << "[" << name_ << "] Call ended by other side after: " << call_time.count() + << "s" << std::endl; + in_call_.store(false); + } else if (tp > Time_Point() && call_time > AV_State::AUTO_HANGUP_TIME) { + std::cout << "[" << name_ << "] Ending call after: " << call_time.count() << "s" + << std::endl; + Toxav_Err_Call_Control cc_err; + toxav_call_control(toxAV_.get(), 0, TOXAV_CALL_CONTROL_CANCEL, &cc_err); + + // Ignore FRIEND_NOT_IN_CALL for the case where the other side hangs up simultaneously + if (cc_err != TOXAV_ERR_CALL_CONTROL_OK && + cc_err != TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL) { + std::printf("toxav_call_control failed: %d\n", cc_err); + ck_assert(0); } + + in_call_.store(false); + } } + + // WARNING: This accesses the Tox struct, so it must be inside the lock + tox_iteration_interval(tox_.get()); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms)); + } + } + + void toxav_iterator(Iteration_Type type) { + while (!stop_threads_.load()) { + switch (type) { + case Iteration_Type::TOXAV_AUDIO: + toxav_audio_iterate(toxAV_.get()); + std::this_thread::sleep_for( + std::chrono::milliseconds(toxav_audio_iteration_interval(toxAV_.get()))); + break; + + case Iteration_Type::TOXAV_VIDEO: + toxav_video_iterate(toxAV_.get()); + std::this_thread::sleep_for( + std::chrono::milliseconds(toxav_video_iteration_interval(toxAV_.get()))); + break; + + case Iteration_Type::TOXAV_BOTH: + toxav_iterate(toxAV_.get()); + std::this_thread::sleep_for( + std::chrono::milliseconds(toxav_iteration_interval(toxAV_.get()))); + break; + } } + } - std::thread tox_thread_; - std::thread audio_thread_; - std::thread video_thread_; - std::thread av_thread_; + std::thread tox_thread_; + std::thread audio_thread_; + std::thread video_thread_; + std::thread av_thread_; - std::mutex tox_loop_lock_; + std::mutex tox_loop_lock_; - Tox_Ptr tox_; - bool combined_av_; - ToxAV_Ptr toxAV_; + Tox_Ptr tox_; + bool combined_av_; + ToxAV_Ptr toxAV_; - std::atomic_bool stop_threads_; - std::atomic_bool incomming_; - std::atomic_uint32_t call_state_; + std::atomic_bool stop_threads_; + std::atomic_bool incomming_; + std::atomic_uint32_t call_state_; - std::atomic call_start_{}; - std::atomic_bool in_call_; + std::atomic call_start_{}; + std::atomic_bool in_call_; - std::atomic_bool video_received_; - std::atomic_bool audio_received_; - std::string name_; + std::atomic_bool video_received_; + std::atomic_bool audio_received_; + std::string name_; }; struct DUMMY_PCM { - static constexpr size_t sample_count = 960; - static constexpr int16_t pcm[sample_count] = {0}; - static constexpr uint8_t channels = 1; - static constexpr uint32_t sampling_rate = 48000; + static constexpr size_t sample_count = 960; + static constexpr int16_t pcm[sample_count] = {0}; + static constexpr uint8_t channels = 1; + static constexpr uint32_t sampling_rate = 48000; }; struct DUMMY_VIDEO { - // https://en.wikipedia.org/wiki/Graphics_display_resolution#QQVGA size - static constexpr uint16_t width = 160; - static constexpr uint16_t height = 120; + // https://en.wikipedia.org/wiki/Graphics_display_resolution#QQVGA size + static constexpr uint16_t width = 160; + static constexpr uint16_t height = 120; - static constexpr uint8_t y[width * height] = {0}; - static constexpr uint8_t u[width / 2 * height / 2] = {0}; - static constexpr uint8_t v[width / 2 * height / 2] = {0}; + static constexpr uint8_t y[width * height] = {0}; + static constexpr uint8_t u[width / 2 * height / 2] = {0}; + static constexpr uint8_t v[width / 2 * height / 2] = {0}; }; // FIXME: once we upgrade to C++17, remove these, reason: https://stackoverflow.com/a/28846608 @@ -432,118 +394,115 @@ constexpr uint8_t DUMMY_VIDEO::y[]; constexpr uint8_t DUMMY_VIDEO::u[]; constexpr uint8_t DUMMY_VIDEO::v[]; -static void test_av(bool combined_av) -{ - std::cout << "Testing Audio and Video in " << (combined_av ? "combined" : "separate") << " threads" - << std::endl; - auto toxes = prepare_network(2); - - AV_State alice(std::move(toxes[0]), "alice", false); - AV_State bob(std::move(toxes[1]), "bob", false); - - // Let alice call bob - { - std::lock_guard(alice.get_tox_loop_lock()); - Toxav_Err_Call err; - ck_assert( - toxav_call(alice.get_ToxAV(), 0, AV_State::TEST_A_BITRATE, AV_State::TEST_V_BITRATE, &err)); - ck_assert(err == TOXAV_ERR_CALL_OK); - } +static void test_av(bool combined_av) { + std::cout << "Testing Audio and Video in " << (combined_av ? "combined" : "separate") + << " threads" << std::endl; + auto toxes = prepare_network(2); - std::cout << "alice started a call" << std::endl; + AV_State alice(std::move(toxes[0]), "alice", false); + AV_State bob(std::move(toxes[1]), "bob", false); - auto poll_state = [](AV_State & av, uint32_t expected, uint32_t max_tries, - uint32_t delay_ms) -> bool { - for (uint32_t i = 0; i < max_tries; ++i) - { - uint32_t state = av.get_call_state(); + // Let alice call bob + { + std::lock_guard(alice.get_tox_loop_lock()); + Toxav_Err_Call err; + ck_assert( + toxav_call(alice.get_ToxAV(), 0, AV_State::TEST_A_BITRATE, AV_State::TEST_V_BITRATE, &err)); + ck_assert(err == TOXAV_ERR_CALL_OK); + } - if (state == expected) { - return true; - } + std::cout << "alice started a call" << std::endl; - std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms)); - } + auto poll_state = [](AV_State &av, uint32_t expected, uint32_t max_tries, + uint32_t delay_ms) -> bool { + for (uint32_t i = 0; i < max_tries; ++i) { + uint32_t state = av.get_call_state(); - return false; - }; + if (state == expected) { + return true; + } - // We're starting with full AV on both sides - constexpr uint32_t full_AV_mask = - TOXAV_FRIEND_CALL_STATE_SENDING_A | TOXAV_FRIEND_CALL_STATE_SENDING_V | - TOXAV_FRIEND_CALL_STATE_ACCEPTING_A | TOXAV_FRIEND_CALL_STATE_ACCEPTING_V; + std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms)); + } - // Wait 2s until the call is established on both sides - ck_assert(poll_state(alice, full_AV_mask, 20, 100)); + return false; + }; - // TODO: why is Bob's call state not updated? - // ck_assert(poll_state(bob, full_AV_mask, 20, 100)); asserts + // We're starting with full AV on both sides + constexpr uint32_t full_AV_mask = + TOXAV_FRIEND_CALL_STATE_SENDING_A | TOXAV_FRIEND_CALL_STATE_SENDING_V | + TOXAV_FRIEND_CALL_STATE_ACCEPTING_A | TOXAV_FRIEND_CALL_STATE_ACCEPTING_V; - ck_assert(alice.in_call()); - ck_assert(bob.in_call()); + // Wait 2s until the call is established on both sides + ck_assert(poll_state(alice, full_AV_mask, 20, 100)); - std::cout << "alice and bob are in the call" << std::endl; + // TODO: why is Bob's call state not updated? + // ck_assert(poll_state(bob, full_AV_mask, 20, 100)); asserts - auto toxav_audio_send_frame_dummy = [](ToxAV * av, Toxav_Err_Send_Frame * error) -> bool { - return toxav_audio_send_frame(av, 0, DUMMY_PCM::pcm, DUMMY_PCM::sample_count, - DUMMY_PCM::channels, DUMMY_PCM::sampling_rate, error); - }; + ck_assert(alice.in_call()); + ck_assert(bob.in_call()); - auto toxav_video_send_frame_dummy = [](ToxAV * av, Toxav_Err_Send_Frame * error) -> bool { - return toxav_video_send_frame(av, 0, DUMMY_VIDEO::width, DUMMY_VIDEO::height, DUMMY_VIDEO::y, - DUMMY_VIDEO::u, DUMMY_VIDEO::v, error); - }; + std::cout << "alice and bob are in the call" << std::endl; - // Send frames from alice to bob - { - std::lock_guard(alice.get_tox_loop_lock()); - Toxav_Err_Send_Frame err; - ck_assert(toxav_audio_send_frame_dummy(alice.get_ToxAV(), &err)); - ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); + auto toxav_audio_send_frame_dummy = [](ToxAV *av, Toxav_Err_Send_Frame *error) -> bool { + return toxav_audio_send_frame(av, 0, DUMMY_PCM::pcm, DUMMY_PCM::sample_count, + DUMMY_PCM::channels, DUMMY_PCM::sampling_rate, error); + }; - ck_assert(toxav_video_send_frame_dummy(alice.get_ToxAV(), &err)); - ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); - } + auto toxav_video_send_frame_dummy = [](ToxAV *av, Toxav_Err_Send_Frame *error) -> bool { + return toxav_video_send_frame(av, 0, DUMMY_VIDEO::width, DUMMY_VIDEO::height, DUMMY_VIDEO::y, + DUMMY_VIDEO::u, DUMMY_VIDEO::v, error); + }; - // Send frames from bob to alice - { - std::lock_guard(bob.get_tox_loop_lock()); - Toxav_Err_Send_Frame err; - ck_assert(toxav_audio_send_frame_dummy(bob.get_ToxAV(), &err)); - ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); + // Send frames from alice to bob + { + std::lock_guard(alice.get_tox_loop_lock()); + Toxav_Err_Send_Frame err; + ck_assert(toxav_audio_send_frame_dummy(alice.get_ToxAV(), &err)); + ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); - ck_assert(toxav_video_send_frame_dummy(bob.get_ToxAV(), &err)); - ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); - } + ck_assert(toxav_video_send_frame_dummy(alice.get_ToxAV(), &err)); + ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); + } + + // Send frames from bob to alice + { + std::lock_guard(bob.get_tox_loop_lock()); + Toxav_Err_Send_Frame err; + ck_assert(toxav_audio_send_frame_dummy(bob.get_ToxAV(), &err)); + ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); + + ck_assert(toxav_video_send_frame_dummy(bob.get_ToxAV(), &err)); + ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); + } - // auto hangup after 2s, wait 3s for this - ck_assert(poll_state(alice, TOXAV_FRIEND_CALL_STATE_FINISHED, 30, 100)); + // auto hangup after 2s, wait 3s for this + ck_assert(poll_state(alice, TOXAV_FRIEND_CALL_STATE_FINISHED, 30, 100)); - // TODO: why is Bobs call state not updated? - // ck_assert(poll_state(bob, TOXAV_FRIEND_CALL_STATE_FINISHED, 30, 100)); fails + // TODO: why is Bobs call state not updated? + // ck_assert(poll_state(bob, TOXAV_FRIEND_CALL_STATE_FINISHED, 30, 100)); fails - ck_assert(!alice.in_call()); - ck_assert(!bob.in_call()); + ck_assert(!alice.in_call()); + ck_assert(!bob.in_call()); - std::cout << "The call ended" << std::endl; + std::cout << "The call ended" << std::endl; - alice.stop_threads(); - bob.stop_threads(); + alice.stop_threads(); + bob.stop_threads(); - ck_assert(alice.did_receive_audio()); - ck_assert(alice.did_receive_video()); - ck_assert(bob.did_receive_audio()); - ck_assert(bob.did_receive_video()); + ck_assert(alice.did_receive_audio()); + ck_assert(alice.did_receive_video()); + ck_assert(bob.did_receive_audio()); + ck_assert(bob.did_receive_video()); } } // namespace -int main(void) -{ - setvbuf(stdout, nullptr, _IONBF, 0); +int main(void) { + setvbuf(stdout, nullptr, _IONBF, 0); - test_av(true); - test_av(false); + test_av(true); + test_av(false); - return 0; + return 0; }