From 6c11e312aa3100988fb939f5b859f6842b0c8893 Mon Sep 17 00:00:00 2001 From: sudden6 Date: Tue, 4 Jan 2022 20:07:36 +0100 Subject: [PATCH 1/4] fix: use correct sample size in toxav_basic_test --- auto_tests/toxav_basic_test.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/auto_tests/toxav_basic_test.c b/auto_tests/toxav_basic_test.c index 5522cc52e4..c05889b4e3 100644 --- a/auto_tests/toxav_basic_test.c +++ b/auto_tests/toxav_basic_test.c @@ -100,6 +100,12 @@ static void iterate_tox(Tox *bootstrap, Tox *Alice, Tox *Bob) tox_iterate(Bob, nullptr); } +static bool toxav_audio_send_frame_helper(ToxAV *av, uint32_t friend_number, Toxav_Err_Send_Frame *error) +{ + const static int16_t PCM[960] = {0}; + return toxav_audio_send_frame(av, 0, PCM, sizeof(PCM), 1, 48000, nullptr); +} + static void regular_call_flow( Tox *Alice, Tox *Bob, Tox *bootstrap, ToxAV *AliceAV, ToxAV *BobAV, @@ -516,17 +522,15 @@ static void test_av_flows(void) } } - int16_t PCM[5670]; - iterate_tox(bootstrap, Alice, Bob); ck_assert_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_PAUSE); iterate_tox(bootstrap, Alice, Bob); - ck_assert(!toxav_audio_send_frame(AliceAV, 0, PCM, 960, 1, 48000, nullptr)); - ck_assert(!toxav_audio_send_frame(BobAV, 0, PCM, 960, 1, 48000, nullptr)); + ck_assert(!toxav_audio_send_frame_helper(AliceAV, 0, nullptr)); + ck_assert(!toxav_audio_send_frame_helper(BobAV, 0, nullptr)); ck_assert_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_RESUME); iterate_tox(bootstrap, Alice, Bob); - ck_assert(toxav_audio_send_frame(AliceAV, 0, PCM, 960, 1, 48000, nullptr)); - ck_assert(toxav_audio_send_frame(BobAV, 0, PCM, 960, 1, 48000, nullptr)); + ck_assert(toxav_audio_send_frame_helper(AliceAV, 0, nullptr)); + ck_assert(toxav_audio_send_frame_helper(BobAV, 0, nullptr)); iterate_tox(bootstrap, Alice, Bob); { From c9585b9c0719a3645fa3eb113716793594cb3c9c Mon Sep 17 00:00:00 2001 From: sudden6 Date: Tue, 4 Jan 2022 21:05:24 +0100 Subject: [PATCH 2/4] 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 | 513 ++++++++++++++++++++++++++++++++++++ 2 files changed, 527 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..f099c1b7cb --- /dev/null +++ b/auto_tests/toxav_mt_test.cc @@ -0,0 +1,513 @@ +#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 TimePoint = std::chrono::time_point; + +namespace { + +// Whether to write log messages when handling callbacks. +constexpr bool LOG_CALLBACKS = false; + +// Maximum amount of time in iterations to wait for bootstrapping and friend +// connections to succeed. +constexpr uint32_t MAX_BOOTSTRAP_ITERATIONS = 1000; + +struct Tox_Options_Deleter { + void operator()(Tox_Options *options) const { tox_options_free(options); } +}; + +using Tox_Options_Ptr = std::unique_ptr; + +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; + +struct Local_State { + explicit Local_State(Tox_Ptr tox) : tox_(std::move(tox)) {} + + Tox *tox() const { return tox_.get(); } + + private: + Tox_Ptr tox_; +}; + +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) { + ck_assert(tox_friend_add_norequest(m, public_key, nullptr) != (uint32_t) -1); + } +} + + +std::vector prepareNetwork(uint32_t count) { + Tox_Err_New error; + + // Temporary bootstrap node + std::printf("Created 1 instance of Tox as boostrap node\n"); + Tox_Ptr bootstrap = Tox_Ptr(tox_new_log(nullptr, &error, NULL)); + 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)); + + uint32_t bootstrap_iteration = 0; + bool online = false; + + auto bootstrap_start_time = Clock::now(); + + for(; 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) + : tox_(std::move(tox)), + combined_(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() { + stopThreads(); + } + + Tox* getTox() const { return tox_.get(); } + ToxAV *getToxAV() const { return toxAV_.get(); } + std::mutex& getToxLoopLock() {return tox_loop_lock; } + bool inCall() const { return in_call.load();} + uint32_t getCallState() const {return call_state.load();} + void stopThreads() { + if (stop_threads.exchange(true)) { + // already stopped + return; + } + + tox_thread.join(); + if (combined_) { + av_thread.join(); + } else { + audio_thread.join(); + video_thread.join(); + } + } + + bool didReceiveAudio() const {return audio_received.load();} + bool didReceiveVideo() 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); + TimePoint tp = me->call_start.load(); + if (state != TOXAV_FRIEND_CALL_STATE_NONE && tp == TimePoint()) { + 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_) { + 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_) { + 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()) { + // Perform this block only while loop lock is held + { + std::lock_guardlock(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(); + TimePoint 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 > TimePoint() && 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); + } + } + } + + std::this_thread::sleep_for(std::chrono::milliseconds(tox_iteration_interval(tox_.get()))); + } + } + + 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_; + 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[]; + +} // namespace + +void test_av(bool combined) +{ + std::cout << "Testing Audio and Video in" << (combined ? "combined" : "separate") << " threads" << std::endl; + auto toxes = prepareNetwork(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.getToxLoopLock()); + Toxav_Err_Call err; + ck_assert(toxav_call(alice.getToxAV(), 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 { + uint32_t i; + for (i = 0; i < max_tries; ++i) { + uint32_t state = av.getCallState(); + 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, 10, 100)); + + // TODO: why is Bob's call state not updated? + // ck_assert(poll_state(bob, full_AV_mask, 10, 100)); asserts + + ck_assert(alice.inCall()); + ck_assert(bob.inCall()); + + 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.getToxLoopLock()); + Toxav_Err_Send_Frame err; + ck_assert(toxav_audio_send_frame_dummy(alice.getToxAV(), &err)); + ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); + + ck_assert(toxav_video_send_frame_dummy(alice.getToxAV(), &err)); + ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); + } + + // Send frames from bob to alice + { + std::lock_guard(bob.getToxLoopLock()); + Toxav_Err_Send_Frame err; + ck_assert(toxav_audio_send_frame_dummy(bob.getToxAV(), &err)); + ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); + + ck_assert(toxav_video_send_frame_dummy(bob.getToxAV(), &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.inCall()); + ck_assert(!bob.inCall()); + + std::cout << "The call ended" << std::endl; + + alice.stopThreads(); + bob.stopThreads(); + + ck_assert(alice.didReceiveAudio()); + ck_assert(alice.didReceiveVideo()); + ck_assert(bob.didReceiveAudio()); + ck_assert(bob.didReceiveVideo()); +} + +int main(void) +{ + setvbuf(stdout, nullptr, _IONBF, 0); + + test_av(false); + test_av(true); + + return 0; +} From a0e5817ba817fbc211802af79283fce41ab6d5ae Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Tue, 4 Jan 2022 20:09:42 +0000 Subject: [PATCH 3/4] Restyled by astyle --- auto_tests/toxav_mt_test.cc | 601 +++++++++++++++++++----------------- 1 file changed, 326 insertions(+), 275 deletions(-) diff --git a/auto_tests/toxav_mt_test.cc b/auto_tests/toxav_mt_test.cc index f099c1b7cb..1df80acd0c 100644 --- a/auto_tests/toxav_mt_test.cc +++ b/auto_tests/toxav_mt_test.cc @@ -20,7 +20,8 @@ using Clock = std::chrono::high_resolution_clock; using TimePoint = std::chrono::time_point; -namespace { +namespace +{ // Whether to write log messages when handling callbacks. constexpr bool LOG_CALLBACKS = false; @@ -30,30 +31,42 @@ constexpr bool LOG_CALLBACKS = false; constexpr uint32_t MAX_BOOTSTRAP_ITERATIONS = 1000; struct Tox_Options_Deleter { - void operator()(Tox_Options *options) const { tox_options_free(options); } + void operator()(Tox_Options *options) const + { + tox_options_free(options); + } }; using Tox_Options_Ptr = std::unique_ptr; 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; struct Local_State { - explicit Local_State(Tox_Ptr tox) : tox_(std::move(tox)) {} + explicit Local_State(Tox_Ptr tox) : tox_(std::move(tox)) {} - Tox *tox() const { return tox_.get(); } + Tox *tox() const + { + return tox_.get(); + } - private: - Tox_Ptr tox_; +private: + Tox_Ptr tox_; }; static void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, @@ -65,318 +78,351 @@ static void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const } -std::vector prepareNetwork(uint32_t count) { - Tox_Err_New error; +std::vector prepareNetwork(uint32_t count) +{ + Tox_Err_New error; - // Temporary bootstrap node - std::printf("Created 1 instance of Tox as boostrap node\n"); - Tox_Ptr bootstrap = Tox_Ptr(tox_new_log(nullptr, &error, NULL)); - ck_assert(error == TOX_ERR_NEW_OK); + // Temporary bootstrap node + std::printf("Created 1 instance of Tox as boostrap node\n"); + Tox_Ptr bootstrap = Tox_Ptr(tox_new_log(nullptr, &error, NULL)); + 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)); - uint32_t bootstrap_iteration = 0; - bool online = false; + uint32_t bootstrap_iteration = 0; + bool online = false; - auto bootstrap_start_time = Clock::now(); + auto bootstrap_start_time = Clock::now(); - for(; bootstrap_iteration < MAX_BOOTSTRAP_ITERATIONS; ++bootstrap_iteration) { - for(auto& tox : toxes) { - tox_iterate(tox.get(), nullptr); - } + for (; 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) { + 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; - } - } + 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; + 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()); + // 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; - } - } + 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; - } + 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 { +class AV_State +{ public: - explicit AV_State(Tox_Ptr tox, std::string name, bool combined = false) - : tox_(std::move(tox)), - combined_(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); + explicit AV_State(Tox_Ptr tox, std::string name, bool combined = false) + : tox_(std::move(tox)), + combined_(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() { + ~AV_State() + { stopThreads(); } - Tox* getTox() const { return tox_.get(); } - ToxAV *getToxAV() const { return toxAV_.get(); } - std::mutex& getToxLoopLock() {return tox_loop_lock; } - bool inCall() const { return in_call.load();} - uint32_t getCallState() const {return call_state.load();} - void stopThreads() { - if (stop_threads.exchange(true)) { - // already stopped - return; - } - - tox_thread.join(); - if (combined_) { - av_thread.join(); - } else { - audio_thread.join(); - video_thread.join(); - } - } - - bool didReceiveAudio() const {return audio_received.load();} - bool didReceiveVideo() 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); + Tox *getTox() const + { + return tox_.get(); + } + ToxAV *getToxAV() const + { + return toxAV_.get(); + } + std::mutex &getToxLoopLock() + { + return tox_loop_lock; + } + bool inCall() const + { + return in_call.load(); + } + uint32_t getCallState() const + { + return call_state.load(); + } + void stopThreads() + { + if (stop_threads.exchange(true)) { + // already stopped + return; + } + + tox_thread.join(); + + if (combined_) { + av_thread.join(); + } else { + audio_thread.join(); + video_thread.join(); + } + } + + bool didReceiveAudio() const + { + return audio_received.load(); + } + bool didReceiveVideo() 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); - TimePoint tp = me->call_start.load(); - if (state != TOXAV_FRIEND_CALL_STATE_NONE && tp == TimePoint()) { - me->call_start.store(Clock::now()); - me->in_call.store(true); + 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_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_) { - 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_) { - 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()) { - // Perform this block only while loop lock is held - { - std::lock_guardlock(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); + 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); + TimePoint tp = me->call_start.load(); + + if (state != TOXAV_FRIEND_CALL_STATE_NONE && tp == TimePoint()) { + 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_) { + 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 (in_call.load()) { - uint32_t state = call_state.load(); - TimePoint 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 > TimePoint() && 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); + if (me->combined_) { + 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()) { + // Perform this block only while loop lock is held + { + std::lock_guardlock(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); } - in_call.store(false); + + 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(); + TimePoint 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 > TimePoint() && 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); + } } } - } - std::this_thread::sleep_for(std::chrono::milliseconds(tox_iteration_interval(tox_.get()))); + std::this_thread::sleep_for(std::chrono::milliseconds(tox_iteration_interval(tox_.get()))); + } } - } - - 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_; - ToxAV_Ptr toxAV_; + Tox_Ptr tox_; + bool combined_; + 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 { @@ -391,9 +437,9 @@ struct DUMMY_VIDEO { 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 @@ -423,23 +469,28 @@ void test_av(bool combined) 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 { + auto poll_state = [](AV_State & av, uint32_t expected, uint32_t max_tries, uint32_t delay_ms) -> bool { uint32_t i; - for (i = 0; i < max_tries; ++i) { + + for (i = 0; i < max_tries; ++i) + { uint32_t state = av.getCallState(); - if(state == expected) { + + 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; + | 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, 10, 100)); @@ -452,11 +503,11 @@ void test_av(bool combined) 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 { + 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 { + 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); }; From 4893265a390a1d8121f2a7aef9f9ae20067af7a6 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Tue, 4 Jan 2022 20:09:43 +0000 Subject: [PATCH 4/4] Restyled by clang-format --- auto_tests/toxav_mt_test.cc | 784 +++++++++++++++++------------------- 1 file changed, 369 insertions(+), 415 deletions(-) diff --git a/auto_tests/toxav_mt_test.cc b/auto_tests/toxav_mt_test.cc index 1df80acd0c..a321f516ec 100644 --- a/auto_tests/toxav_mt_test.cc +++ b/auto_tests/toxav_mt_test.cc @@ -10,18 +10,15 @@ #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 TimePoint = std::chrono::time_point; -namespace -{ +namespace { // Whether to write log messages when handling callbacks. constexpr bool LOG_CALLBACKS = false; @@ -31,415 +28,371 @@ constexpr bool LOG_CALLBACKS = false; constexpr uint32_t MAX_BOOTSTRAP_ITERATIONS = 1000; struct Tox_Options_Deleter { - void operator()(Tox_Options *options) const - { - tox_options_free(options); - } + void operator()(Tox_Options *options) const { tox_options_free(options); } }; using Tox_Options_Ptr = std::unique_ptr; 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; struct Local_State { - explicit Local_State(Tox_Ptr tox) : tox_(std::move(tox)) {} + explicit Local_State(Tox_Ptr tox) : tox_(std::move(tox)) {} - Tox *tox() const - { - return tox_.get(); - } + Tox *tox() const { return tox_.get(); } -private: - Tox_Ptr tox_; + private: + Tox_Ptr tox_; }; -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) { - ck_assert(tox_friend_add_norequest(m, public_key, nullptr) != (uint32_t) -1); - } +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) { + ck_assert(tox_friend_add_norequest(m, public_key, nullptr) != (uint32_t)-1); + } } +std::vector prepareNetwork(uint32_t count) { + Tox_Err_New error; -std::vector prepareNetwork(uint32_t count) -{ - Tox_Err_New error; + // Temporary bootstrap node + std::printf("Created 1 instance of Tox as boostrap node\n"); + Tox_Ptr bootstrap = Tox_Ptr(tox_new_log(nullptr, &error, NULL)); + ck_assert(error == TOX_ERR_NEW_OK); - // Temporary bootstrap node - std::printf("Created 1 instance of Tox as boostrap node\n"); - Tox_Ptr bootstrap = Tox_Ptr(tox_new_log(nullptr, &error, NULL)); - 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)); - - uint32_t bootstrap_iteration = 0; - 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(); + uint32_t bootstrap_iteration = 0; + bool online = false; - for (; 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()); - }); + auto bootstrap_start_time = Clock::now(); - 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; + for (; bootstrap_iteration < MAX_BOOTSTRAP_ITERATIONS; ++bootstrap_iteration) { + for (auto &tox : toxes) { + tox_iterate(tox.get(), nullptr); + } - // 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) { + size_t online_cnt = std::count_if(toxes.cbegin(), toxes.cend(), [](const Tox_Ptr &tox) { + return tox_self_get_connection_status(tox.get()); + }); - for (auto friend_id : friend_list) { - friends_connected &= tox_friend_get_connection_status(tox.get(), friend_id, nullptr) == TOX_CONNECTION_UDP; - } - } + 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 (friends_connected) { - break; - } + bool friends_connected = true; - std::this_thread::sleep_for(std::chrono::milliseconds(20)); + // 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; } - 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)); + } + + 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) - : tox_(std::move(tox)), - combined_(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) + : tox_(std::move(tox)), + combined_(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() - { - stopThreads(); + } + + ~AV_State() { stopThreads(); } + + Tox *getTox() const { return tox_.get(); } + ToxAV *getToxAV() const { return toxAV_.get(); } + std::mutex &getToxLoopLock() { return tox_loop_lock; } + bool inCall() const { return in_call.load(); } + uint32_t getCallState() const { return call_state.load(); } + void stopThreads() { + if (stop_threads.exchange(true)) { + // already stopped + return; } - Tox *getTox() const - { - return tox_.get(); - } - ToxAV *getToxAV() const - { - return toxAV_.get(); - } - std::mutex &getToxLoopLock() - { - return tox_loop_lock; + tox_thread.join(); + + if (combined_) { + av_thread.join(); + } else { + audio_thread.join(); + video_thread.join(); } - bool inCall() const - { - return in_call.load(); + } + + bool didReceiveAudio() const { return audio_received.load(); } + bool didReceiveVideo() 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); + TimePoint tp = me->call_start.load(); + + if (state != TOXAV_FRIEND_CALL_STATE_NONE && tp == TimePoint()) { + me->call_start.store(Clock::now()); + me->in_call.store(true); } - uint32_t getCallState() 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_) { + 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 stopThreads() - { - if (stop_threads.exchange(true)) { - // already stopped - return; - } - tox_thread.join(); + me->video_received = true; + } - if (combined_) { - av_thread.join(); - } else { - audio_thread.join(); - video_thread.join(); - } - } + 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; - bool didReceiveAudio() const - { - return audio_received.load(); - } - bool didReceiveVideo() const - { - return video_received.load(); + if (me->combined_) { + 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 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()); - } + me->audio_received = true; + } - 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); - TimePoint tp = me->call_start.load(); - - if (state != TOXAV_FRIEND_CALL_STATE_NONE && tp == TimePoint()) { - me->call_start.store(Clock::now()); - me->in_call.store(true); - } - } + void tox_iterator() { + while (!stop_threads.load()) { + // 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_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_) { - 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()); - } + // 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->video_received = 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_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_) { - 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()); - } + std::cout << "[" << name_ << "] Answering call" << std::endl; - me->audio_received = true; - } + call_start = Clock::now(); + in_call.store(true); + } - void tox_iterator() - { - while (!stop_threads.load()) { - // Perform this block only while loop lock is held - { - std::lock_guardlock(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(); - TimePoint 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 > TimePoint() && 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); - } - } + if (in_call.load()) { + uint32_t state = call_state.load(); + TimePoint 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 > TimePoint() && 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); } - std::this_thread::sleep_for(std::chrono::milliseconds(tox_iteration_interval(tox_.get()))); + in_call.store(false); + } } - } + } - 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::this_thread::sleep_for(std::chrono::milliseconds(tox_iteration_interval(tox_.get()))); + } + } + + 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_; - ToxAV_Ptr toxAV_; + Tox_Ptr tox_; + bool combined_; + 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 @@ -449,116 +402,117 @@ constexpr uint8_t DUMMY_VIDEO::y[]; constexpr uint8_t DUMMY_VIDEO::u[]; constexpr uint8_t DUMMY_VIDEO::v[]; -} // namespace +} // namespace -void test_av(bool combined) -{ - std::cout << "Testing Audio and Video in" << (combined ? "combined" : "separate") << " threads" << std::endl; - auto toxes = prepareNetwork(2); +void test_av(bool combined) { + std::cout << "Testing Audio and Video in" << (combined ? "combined" : "separate") << " threads" + << std::endl; + auto toxes = prepareNetwork(2); - AV_State alice(std::move(toxes[0]), "alice", false); - AV_State bob(std::move(toxes[1]), "bob", false); + 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.getToxLoopLock()); - Toxav_Err_Call err; - ck_assert(toxav_call(alice.getToxAV(), 0, AV_State::TEST_A_BITRATE, AV_State::TEST_V_BITRATE, &err)); - ck_assert(err == TOXAV_ERR_CALL_OK); - } + // Let alice call bob + { + std::lock_guard(alice.getToxLoopLock()); + Toxav_Err_Call err; + ck_assert( + toxav_call(alice.getToxAV(), 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 { - uint32_t i; + auto poll_state = [](AV_State &av, uint32_t expected, uint32_t max_tries, + uint32_t delay_ms) -> bool { + uint32_t i; - for (i = 0; i < max_tries; ++i) - { - uint32_t state = av.getCallState(); + for (i = 0; i < max_tries; ++i) { + uint32_t state = av.getCallState(); - 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, 10, 100)); + // Wait 2s until the call is established on both sides + ck_assert(poll_state(alice, full_AV_mask, 10, 100)); - // TODO: why is Bob's call state not updated? - // ck_assert(poll_state(bob, full_AV_mask, 10, 100)); asserts + // TODO: why is Bob's call state not updated? + // ck_assert(poll_state(bob, full_AV_mask, 10, 100)); asserts - ck_assert(alice.inCall()); - ck_assert(bob.inCall()); + ck_assert(alice.inCall()); + ck_assert(bob.inCall()); - 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.getToxLoopLock()); - Toxav_Err_Send_Frame err; - ck_assert(toxav_audio_send_frame_dummy(alice.getToxAV(), &err)); - ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); + // Send frames from alice to bob + { + std::lock_guard(alice.getToxLoopLock()); + Toxav_Err_Send_Frame err; + ck_assert(toxav_audio_send_frame_dummy(alice.getToxAV(), &err)); + ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); - ck_assert(toxav_video_send_frame_dummy(alice.getToxAV(), &err)); - ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); - } + ck_assert(toxav_video_send_frame_dummy(alice.getToxAV(), &err)); + ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); + } - // Send frames from bob to alice - { - std::lock_guard(bob.getToxLoopLock()); - Toxav_Err_Send_Frame err; - ck_assert(toxav_audio_send_frame_dummy(bob.getToxAV(), &err)); - ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); + // Send frames from bob to alice + { + std::lock_guard(bob.getToxLoopLock()); + Toxav_Err_Send_Frame err; + ck_assert(toxav_audio_send_frame_dummy(bob.getToxAV(), &err)); + ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); - ck_assert(toxav_video_send_frame_dummy(bob.getToxAV(), &err)); - ck_assert(err == TOXAV_ERR_SEND_FRAME_OK); - } + ck_assert(toxav_video_send_frame_dummy(bob.getToxAV(), &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.inCall()); - ck_assert(!bob.inCall()); + ck_assert(!alice.inCall()); + ck_assert(!bob.inCall()); - std::cout << "The call ended" << std::endl; + std::cout << "The call ended" << std::endl; - alice.stopThreads(); - bob.stopThreads(); + alice.stopThreads(); + bob.stopThreads(); - ck_assert(alice.didReceiveAudio()); - ck_assert(alice.didReceiveVideo()); - ck_assert(bob.didReceiveAudio()); - ck_assert(bob.didReceiveVideo()); + ck_assert(alice.didReceiveAudio()); + ck_assert(alice.didReceiveVideo()); + ck_assert(bob.didReceiveAudio()); + ck_assert(bob.didReceiveVideo()); } -int main(void) -{ - setvbuf(stdout, nullptr, _IONBF, 0); +int main(void) { + setvbuf(stdout, nullptr, _IONBF, 0); - test_av(false); - test_av(true); + test_av(false); + test_av(true); - return 0; + return 0; }