Expose api functions for enabling and disabling AV in AV groups

A group loaded from a savefile starts with AV disabled.
This commit is contained in:
zugz (tox) 2019-02-10 00:00:00 +00:00
parent 0aad180d1e
commit 49e2406ffa
No known key found for this signature in database
GPG Key ID: 6F2BDA289D04F249
11 changed files with 697 additions and 51 deletions

View File

@ -434,6 +434,7 @@ auto_test(version)
auto_test(save_compatibility)
if(BUILD_TOXAV)
auto_test(conference_av)
auto_test(toxav_basic)
auto_test(toxav_many)
endif()

View File

@ -57,7 +57,7 @@ AUTOTEST_LDADD = \
if BUILD_AV
TESTS += toxav_basic_test toxav_many_test
TESTS += conference_av_test toxav_basic_test toxav_many_test
AUTOTEST_LDADD += libtoxav.la
endif
@ -221,6 +221,10 @@ version_test_LDADD = $(AUTOTEST_LDADD)
if BUILD_AV
conference_av_test_SOURCES = ../auto_tests/conference_av_test.c
conference_av_test_CFLAGS = $(AUTOTEST_CFLAGS)
conference_av_test_LDADD = $(AUTOTEST_LDADD)
toxav_basic_test_SOURCES = ../auto_tests/toxav_basic_test.c
toxav_basic_test_CFLAGS = $(AUTOTEST_CFLAGS)
toxav_basic_test_LDADD = $(AUTOTEST_LDADD) $(AV_LIBS)

View File

@ -0,0 +1,467 @@
/* Auto Tests: Conferences AV.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdint.h>
#include "../toxav/toxav.h"
#include "check_compat.h"
#define NUM_AV_GROUP_TOX 16
#define NUM_AV_DISCONNECT (NUM_AV_GROUP_TOX / 2)
#define NUM_AV_DISABLE (NUM_AV_GROUP_TOX / 2)
typedef struct State {
uint32_t index;
uint64_t clock;
bool invited_next;
uint32_t received_audio_peers[NUM_AV_GROUP_TOX];
uint32_t received_audio_num;
} State;
#include "run_auto_test.h"
static void handle_self_connection_status(
Tox *tox, Tox_Connection connection_status, void *user_data)
{
const State *state = (State *)user_data;
if (connection_status != TOX_CONNECTION_NONE) {
printf("tox #%u: is now connected\n", state->index);
} else {
printf("tox #%u: is now disconnected\n", state->index);
}
}
static void handle_friend_connection_status(
Tox *tox, uint32_t friendnumber, Tox_Connection connection_status, void *user_data)
{
const State *state = (State *)user_data;
if (connection_status != TOX_CONNECTION_NONE) {
printf("tox #%u: is now connected to friend %u\n", state->index, friendnumber);
} else {
printf("tox #%u: is now disconnected from friend %u\n", state->index, friendnumber);
}
}
static void audio_callback(void *tox, uint32_t groupnumber, uint32_t peernumber,
const int16_t *pcm, unsigned int samples, uint8_t channels, uint32_t
sample_rate, void *userdata)
{
if (samples == 0) {
return;
}
State *state = (State *)userdata;
for (uint32_t i = 0; i < state->received_audio_num; ++i) {
if (state->received_audio_peers[i] == peernumber) {
return;
}
}
ck_assert(state->received_audio_num < NUM_AV_GROUP_TOX);
state->received_audio_peers[state->received_audio_num] = peernumber;
++state->received_audio_num;
}
static void handle_conference_invite(
Tox *tox, uint32_t friendnumber, Tox_Conference_Type type,
const uint8_t *data, size_t length, void *user_data)
{
const State *state = (State *)user_data;
ck_assert_msg(type == TOX_CONFERENCE_TYPE_AV, "tox #%u: wrong conference type: %d", state->index, type);
ck_assert_msg(toxav_join_av_groupchat(tox, friendnumber, data, length, audio_callback, user_data) == 0,
"tox #%u: failed to join group", state->index);
}
static void handle_conference_connected(
Tox *tox, uint32_t conference_number, void *user_data)
{
State *state = (State *)user_data;
if (state->invited_next || tox_self_get_friend_list_size(tox) <= 1) {
return;
}
Tox_Err_Conference_Invite err;
tox_conference_invite(tox, 1, 0, &err);
ck_assert_msg(err == TOX_ERR_CONFERENCE_INVITE_OK, "tox #%u failed to invite next friend: err = %d", state->index, err);
printf("tox #%u: invited next friend\n", state->index);
state->invited_next = true;
}
static bool toxes_are_disconnected_from_group(uint32_t tox_count, Tox **toxes,
bool *disconnected)
{
uint32_t num_disconnected = 0;
for (uint32_t i = 0; i < tox_count; ++i) {
num_disconnected += disconnected[i];
}
for (uint32_t i = 0; i < tox_count; i++) {
if (disconnected[i]) {
continue;
}
if (tox_conference_peer_count(toxes[i], 0, nullptr) > tox_count - num_disconnected) {
return false;
}
}
return true;
}
static void disconnect_toxes(uint32_t tox_count, Tox **toxes, State *state,
const bool *disconnect, const bool *exclude)
{
/* Fake a network outage for a set of peers D by iterating only the other
* peers D' until the connections time out according to D', then iterating
* only D until the connections time out according to D. */
VLA(bool, disconnect_now, tox_count);
bool invert = false;
do {
for (uint32_t i = 0; i < tox_count; ++i) {
disconnect_now[i] = exclude[i] || (invert ^ disconnect[i]);
}
do {
for (uint32_t i = 0; i < tox_count; ++i) {
if (!disconnect_now[i]) {
tox_iterate(toxes[i], &state[i]);
state[i].clock += 1000;
}
}
c_sleep(20);
} while (!toxes_are_disconnected_from_group(tox_count, toxes, disconnect_now));
invert = !invert;
} while (invert);
}
static bool all_connected_to_group(uint32_t tox_count, Tox **toxes)
{
for (uint32_t i = 0; i < tox_count; i++) {
if (tox_conference_peer_count(toxes[i], 0, nullptr) < tox_count) {
return false;
}
}
return true;
}
/**
* returns a random index at which a list of booleans is false
* (some such index is required to exist)
*/
static uint32_t random_false_index(bool *list, const uint32_t length)
{
uint32_t index;
do {
index = random_u32() % length;
} while (list[index]);
return index;
}
static bool all_got_audio(State *state, const bool *disabled)
{
uint32_t num_disabled = 0;
for (uint32_t i = 0; i < NUM_AV_GROUP_TOX; ++i) {
num_disabled += disabled[i];
}
for (uint32_t i = 0; i < NUM_AV_GROUP_TOX; ++i) {
if (disabled[i] ^ (state[i].received_audio_num
!= NUM_AV_GROUP_TOX - num_disabled - 1)) {
return false;
}
}
return true;
}
static void reset_received_audio(Tox **toxes, State *state)
{
for (uint32_t j = 0; j < NUM_AV_GROUP_TOX; ++j) {
state[j].received_audio_num = 0;
}
}
#define GROUP_AV_TEST_SAMPLES 960
/* must have
* GROUP_AV_AUDIO_ITERATIONS - NUM_AV_GROUP_TOX >= 2^n >= GROUP_JBUF_SIZE
* for some n, to give messages time to be relayed and to let the jitter
* buffers fill up. */
#define GROUP_AV_AUDIO_ITERATIONS (8 + NUM_AV_GROUP_TOX)
static bool test_audio(Tox **toxes, State *state, const bool *disabled, bool quiet)
{
if (!quiet) {
printf("testing sending and receiving audio\n");
}
int16_t PCM[GROUP_AV_TEST_SAMPLES];
reset_received_audio(toxes, state);
for (uint32_t n = 0; n < GROUP_AV_AUDIO_ITERATIONS; n++) {
for (uint32_t i = 0; i < NUM_AV_GROUP_TOX; ++i) {
if (disabled[i]) {
continue;
}
if (toxav_group_send_audio(toxes[i], 0, PCM, GROUP_AV_TEST_SAMPLES, 1, 48000) != 0) {
if (!quiet) {
ck_abort_msg("#%u failed to send audio", state[i].index);
}
return false;
}
}
iterate_all_wait(NUM_AV_GROUP_TOX, toxes, state, ITERATION_INTERVAL);
if (all_got_audio(state, disabled)) {
return true;
}
}
if (!quiet) {
ck_abort_msg("group failed to receive audio");
}
return false;
}
static void test_eventual_audio(Tox **toxes, State *state, const bool *disabled, uint64_t timeout)
{
uint64_t start = state[0].clock;
while (state[0].clock < start + timeout) {
if (test_audio(toxes, state, disabled, true)
&& test_audio(toxes, state, disabled, true)) {
printf("audio test successful after %d seconds\n", (int)((state[0].clock - start) / 1000));
return;
}
}
printf("audio seems not to be getting through: testing again with errors.\n");
test_audio(toxes, state, disabled, false);
}
static void do_audio(Tox **toxes, State *state, uint32_t iterations)
{
int16_t PCM[GROUP_AV_TEST_SAMPLES];
printf("running audio for %u iterations\n", iterations);
for (uint32_t f = 0; f < iterations; ++f) {
for (uint32_t i = 0; i < NUM_AV_GROUP_TOX; ++i) {
ck_assert_msg(toxav_group_send_audio(toxes[i], 0, PCM, GROUP_AV_TEST_SAMPLES, 1, 48000) == 0,
"#%u failed to send audio", state[i].index);
iterate_all_wait(NUM_AV_GROUP_TOX, toxes, state, ITERATION_INTERVAL);
}
}
}
// should agree with value in groupav.c
#define GROUP_JBUF_DEAD_SECONDS 4
#define JITTER_SETTLE_TIME (GROUP_JBUF_DEAD_SECONDS*1000 + NUM_AV_GROUP_TOX*ITERATION_INTERVAL*(GROUP_AV_AUDIO_ITERATIONS+1))
static void run_conference_tests(Tox **toxes, State *state)
{
bool disabled[NUM_AV_GROUP_TOX] = {0};
test_audio(toxes, state, disabled, false);
/* have everyone send audio for a bit so we can test that the audio
* sequnums dropping to 0 on restart isn't a problem */
do_audio(toxes, state, 20);
printf("letting random toxes timeout\n");
bool disconnected[NUM_AV_GROUP_TOX] = {0};
bool restarting[NUM_AV_GROUP_TOX] = {0};
ck_assert(NUM_AV_DISCONNECT < NUM_AV_GROUP_TOX);
for (uint32_t i = 0; i < NUM_AV_DISCONNECT; ++i) {
uint32_t disconnect = random_false_index(disconnected, NUM_AV_GROUP_TOX);
disconnected[disconnect] = true;
if (i < NUM_AV_DISCONNECT / 2) {
restarting[disconnect] = true;
printf("Restarting #%u\n", state[disconnect].index);
} else {
printf("Disconnecting #%u\n", state[disconnect].index);
}
}
uint8_t *save[NUM_AV_GROUP_TOX];
size_t save_size[NUM_AV_GROUP_TOX];
for (uint32_t i = 0; i < NUM_AV_GROUP_TOX; ++i) {
if (restarting[i]) {
save_size[i] = tox_get_savedata_size(toxes[i]);
ck_assert_msg(save_size[i] != 0, "save is invalid size %u", (unsigned)save_size[i]);
save[i] = (uint8_t *)malloc(save_size[i]);
ck_assert_msg(save[i] != nullptr, "malloc failed");
tox_get_savedata(toxes[i], save[i]);
tox_kill(toxes[i]);
}
}
disconnect_toxes(NUM_AV_GROUP_TOX, toxes, state, disconnected, restarting);
for (uint32_t i = 0; i < NUM_AV_GROUP_TOX; ++i) {
if (restarting[i]) {
struct Tox_Options *const options = tox_options_new(nullptr);
tox_options_set_savedata_type(options, TOX_SAVEDATA_TYPE_TOX_SAVE);
tox_options_set_savedata_data(options, save[i], save_size[i]);
toxes[i] = tox_new_log(options, nullptr, &state[i].index);
tox_options_free(options);
free(save[i]);
set_mono_time_callback(toxes[i], &state[i]);
}
}
printf("reconnecting toxes\n");
do {
iterate_all_wait(NUM_AV_GROUP_TOX, toxes, state, ITERATION_INTERVAL);
} while (!all_connected_to_group(NUM_AV_GROUP_TOX, toxes));
for (uint32_t i = 0; i < NUM_AV_GROUP_TOX; ++i) {
if (restarting[i]) {
ck_assert_msg(toxav_groupchat_enable_av(toxes[i], 0, audio_callback, &state[i]) == 0,
"#%u failed to re-enable av", state[i].index);
}
}
printf("testing audio\n");
/* Allow time for the jitter buffers to reset and for the group to become
* connected enough for lossy messages to get through
* (all_connected_to_group() only checks lossless connectivity, which is a
* looser condition). */
test_eventual_audio(toxes, state, disabled, JITTER_SETTLE_TIME + NUM_AV_GROUP_TOX * 1000);
printf("testing disabling av\n");
ck_assert(NUM_AV_DISABLE < NUM_AV_GROUP_TOX);
for (uint32_t i = 0; i < NUM_AV_DISABLE; ++i) {
uint32_t disable = random_false_index(disabled, NUM_AV_GROUP_TOX);
disabled[disable] = true;
printf("Disabling #%u\n", state[disable].index);
ck_assert_msg(toxav_groupchat_enable_av(toxes[disable], 0, audio_callback, &state[disable]) != 0,
"#%u could enable already enabled av!", state[i].index);
ck_assert_msg(toxav_groupchat_disable_av(toxes[disable], 0) == 0,
"#%u failed to disable av", state[i].index);
}
// Run test without error to clear out messages from now-disabled peers.
test_audio(toxes, state, disabled, true);
printf("testing audio with some peers having disabled their av\n");
test_audio(toxes, state, disabled, false);
for (uint32_t i = 0; i < NUM_AV_DISABLE; ++i) {
if (!disabled[i]) {
continue;
}
disabled[i] = false;
ck_assert_msg(toxav_groupchat_disable_av(toxes[i], 0) != 0,
"#%u could disable already disabled av!", state[i].index);
ck_assert_msg(toxav_groupchat_enable_av(toxes[i], 0, audio_callback, &state[i]) == 0,
"#%u failed to re-enable av", state[i].index);
}
printf("testing audio after re-enabling all av\n");
test_eventual_audio(toxes, state, disabled, JITTER_SETTLE_TIME);
}
static void test_groupav(Tox **toxes, State *state)
{
const time_t test_start_time = time(nullptr);
for (uint32_t i = 0; i < NUM_AV_GROUP_TOX; ++i) {
tox_callback_self_connection_status(toxes[i], &handle_self_connection_status);
tox_callback_friend_connection_status(toxes[i], &handle_friend_connection_status);
tox_callback_conference_invite(toxes[i], &handle_conference_invite);
tox_callback_conference_connected(toxes[i], &handle_conference_connected);
}
ck_assert_msg(toxav_add_av_groupchat(toxes[0], audio_callback, &state[0]) != UINT32_MAX, "failed to create group");
printf("tox #%u: inviting its first friend\n", state[0].index);
ck_assert_msg(tox_conference_invite(toxes[0], 0, 0, nullptr) != 0, "failed to invite friend");
state[0].invited_next = true;
printf("waiting for invitations to be made\n");
uint32_t invited_count = 0;
do {
iterate_all_wait(NUM_AV_GROUP_TOX, toxes, state, ITERATION_INTERVAL);
invited_count = 0;
for (uint32_t i = 0; i < NUM_AV_GROUP_TOX; ++i) {
invited_count += state[i].invited_next;
}
} while (invited_count != NUM_AV_GROUP_TOX - 1);
uint64_t pregroup_clock = state[0].clock;
printf("waiting for all toxes to be in the group\n");
uint32_t fully_connected_count = 0;
do {
fully_connected_count = 0;
iterate_all_wait(NUM_AV_GROUP_TOX, toxes, state, ITERATION_INTERVAL);
for (uint32_t i = 0; i < NUM_AV_GROUP_TOX; ++i) {
Tox_Err_Conference_Peer_Query err;
uint32_t peer_count = tox_conference_peer_count(toxes[i], 0, &err);
if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
peer_count = 0;
}
fully_connected_count += peer_count == NUM_AV_GROUP_TOX;
}
} while (fully_connected_count != NUM_AV_GROUP_TOX);
printf("group connected, took %d seconds\n", (int)((state[0].clock - pregroup_clock) / 1000));
run_conference_tests(toxes, state);
printf("test_many_group succeeded, took %d seconds\n", (int)(time(nullptr) - test_start_time));
}
int main(void)
{
setvbuf(stdout, nullptr, _IONBF, 0);
run_auto_test(NUM_AV_GROUP_TOX, test_groupav, true);
return 0;
}

View File

@ -8,11 +8,8 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdint.h>
#include "../testing/misc_tools.h"
#include "../toxcore/crypto_core.h"
#include "../toxcore/tox.h"
#include "../toxcore/util.h"
#include "check_compat.h"
#define NUM_GROUP_TOX 16
@ -91,7 +88,7 @@ static void handle_conference_connected(
state->invited_next = true;
}
static uint16_t num_recv;
static uint32_t num_recv;
static void handle_conference_message(
Tox *tox, uint32_t groupnumber, uint32_t peernumber, Tox_Message_Type type,
@ -102,15 +99,21 @@ static void handle_conference_message(
}
}
static bool toxes_are_disconnected_from_group(uint32_t tox_count, Tox **toxes, int disconnected_count,
static bool toxes_are_disconnected_from_group(uint32_t tox_count, Tox **toxes,
bool *disconnected)
{
uint32_t num_disconnected = 0;
for (uint32_t i = 0; i < tox_count; ++i) {
num_disconnected += disconnected[i];
}
for (uint32_t i = 0; i < tox_count; i++) {
if (disconnected[i]) {
continue;
}
if (tox_conference_peer_count(toxes[i], 0, nullptr) > tox_count - NUM_DISCONNECT) {
if (tox_conference_peer_count(toxes[i], 0, nullptr) > tox_count - num_disconnected) {
return false;
}
}
@ -118,6 +121,36 @@ static bool toxes_are_disconnected_from_group(uint32_t tox_count, Tox **toxes, i
return true;
}
static void disconnect_toxes(uint32_t tox_count, Tox **toxes, State *state,
const bool *disconnect, const bool *exclude)
{
/* Fake a network outage for a set of peers D by iterating only the other
* peers D' until the connections time out according to D', then iterating
* only D until the connections time out according to D. */
VLA(bool, disconnect_now, tox_count);
bool invert = false;
do {
for (uint32_t i = 0; i < tox_count; ++i) {
disconnect_now[i] = exclude[i] || (invert ^ disconnect[i]);
}
do {
for (uint32_t i = 0; i < tox_count; ++i) {
if (!disconnect_now[i]) {
tox_iterate(toxes[i], &state[i]);
state[i].clock += 1000;
}
}
c_sleep(20);
} while (!toxes_are_disconnected_from_group(tox_count, toxes, disconnect_now));
invert = !invert;
} while (invert);
}
static bool all_connected_to_group(uint32_t tox_count, Tox **toxes)
{
for (uint32_t i = 0; i < tox_count; i++) {
@ -131,8 +164,8 @@ static bool all_connected_to_group(uint32_t tox_count, Tox **toxes)
static bool names_propagated(uint32_t tox_count, Tox **toxes, State *state)
{
for (uint16_t i = 0; i < tox_count; ++i) {
for (uint16_t j = 0; j < tox_count; ++j) {
for (uint32_t i = 0; i < tox_count; ++i) {
for (uint32_t j = 0; j < tox_count; ++j) {
const size_t len = tox_conference_peer_get_name_size(toxes[i], 0, j, nullptr);
if (len != NAMELEN) {
@ -145,9 +178,10 @@ static bool names_propagated(uint32_t tox_count, Tox **toxes, State *state)
}
/* returns a random index at which a list of booleans is false
/**
* returns a random index at which a list of booleans is false
* (some such index is required to exist)
* */
*/
static uint32_t random_false_index(bool *list, const uint32_t length)
{
uint32_t index;
@ -171,7 +205,7 @@ static void run_conference_tests(Tox **toxes, State *state)
ck_assert(NUM_DISCONNECT < NUM_GROUP_TOX);
for (uint16_t i = 0; i < NUM_DISCONNECT; ++i) {
for (uint32_t i = 0; i < NUM_DISCONNECT; ++i) {
uint32_t disconnect = random_false_index(disconnected, NUM_GROUP_TOX);
disconnected[disconnect] = true;
@ -186,7 +220,7 @@ static void run_conference_tests(Tox **toxes, State *state)
uint8_t *save[NUM_GROUP_TOX];
size_t save_size[NUM_GROUP_TOX];
for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) {
if (restarting[i]) {
save_size[i] = tox_get_savedata_size(toxes[i]);
ck_assert_msg(save_size[i] != 0, "save is invalid size %u", (unsigned)save_size[i]);
@ -197,18 +231,9 @@ static void run_conference_tests(Tox **toxes, State *state)
}
}
do {
for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
if (!disconnected[i]) {
tox_iterate(toxes[i], &state[i]);
state[i].clock += 1000;
}
}
disconnect_toxes(NUM_GROUP_TOX, toxes, state, disconnected, restarting);
c_sleep(20);
} while (!toxes_are_disconnected_from_group(NUM_GROUP_TOX, toxes, NUM_DISCONNECT, disconnected));
for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) {
if (restarting[i]) {
struct Tox_Options *const options = tox_options_new(nullptr);
tox_options_set_savedata_type(options, TOX_SAVEDATA_TYPE_TOX_SAVE);
@ -216,13 +241,15 @@ static void run_conference_tests(Tox **toxes, State *state)
toxes[i] = tox_new_log(options, nullptr, &state[i].index);
tox_options_free(options);
free(save[i]);
set_mono_time_callback(toxes[i], &state[i]);
}
}
if (check_name_change_propagation) {
printf("changing names\n");
for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) {
char name[NAMELEN + 1];
snprintf(name, NAMELEN + 1, NEW_NAME_FORMAT_STR, state[i].index);
tox_self_set_name(toxes[i], (const uint8_t *)name, NAMELEN, nullptr);
@ -237,7 +264,7 @@ static void run_conference_tests(Tox **toxes, State *state)
printf("running conference tests\n");
for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) {
tox_callback_conference_message(toxes[i], &handle_conference_message);
iterate_all_wait(NUM_GROUP_TOX, toxes, state, ITERATION_INTERVAL);
@ -259,8 +286,8 @@ static void run_conference_tests(Tox **toxes, State *state)
ck_assert_msg(num_recv == NUM_GROUP_TOX, "failed to recv group messages");
if (check_name_change_propagation) {
for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
for (uint16_t j = 0; j < NUM_GROUP_TOX; ++j) {
for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) {
for (uint32_t j = 0; j < NUM_GROUP_TOX; ++j) {
uint8_t name[NAMELEN];
tox_conference_peer_get_name(toxes[i], 0, j, name, nullptr);
/* Note the toxes will have been reordered */
@ -270,14 +297,14 @@ static void run_conference_tests(Tox **toxes, State *state)
}
}
for (uint16_t k = NUM_GROUP_TOX; k != 0 ; --k) {
for (uint32_t k = NUM_GROUP_TOX; k != 0 ; --k) {
tox_conference_delete(toxes[k - 1], 0, nullptr);
for (uint8_t j = 0; j < 10 || j < NUM_GROUP_TOX; ++j) {
iterate_all_wait(NUM_GROUP_TOX, toxes, state, ITERATION_INTERVAL);
}
for (uint16_t i = 0; i < k - 1; ++i) {
for (uint32_t i = 0; i < k - 1; ++i) {
uint32_t peer_count = tox_conference_peer_count(toxes[i], 0, nullptr);
ck_assert_msg(peer_count == (k - 1), "\n\tBad number of group peers (post check)."
"\n\t\t\tExpected: %u but tox_instance(%u) only has: %u\n\n",
@ -290,7 +317,7 @@ static void test_many_group(Tox **toxes, State *state)
{
const time_t test_start_time = time(nullptr);
for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) {
tox_callback_self_connection_status(toxes[i], &handle_self_connection_status);
tox_callback_friend_connection_status(toxes[i], &handle_friend_connection_status);
tox_callback_conference_invite(toxes[i], &handle_conference_invite);
@ -310,21 +337,21 @@ static void test_many_group(Tox **toxes, State *state)
printf("waiting for invitations to be made\n");
uint16_t invited_count = 0;
uint32_t invited_count = 0;
do {
iterate_all_wait(NUM_GROUP_TOX, toxes, state, ITERATION_INTERVAL);
invited_count = 0;
for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) {
invited_count += state[i].invited_next;
}
} while (invited_count != NUM_GROUP_TOX - 1);
uint64_t pregroup_clock = state[0].clock;
printf("waiting for all toxes to be in the group\n");
uint16_t fully_connected_count = 0;
uint32_t fully_connected_count = 0;
do {
fully_connected_count = 0;
@ -332,7 +359,7 @@ static void test_many_group(Tox **toxes, State *state)
iterate_all_wait(NUM_GROUP_TOX, toxes, state, ITERATION_INTERVAL);
for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) {
Tox_Err_Conference_Peer_Query err;
uint32_t peer_count = tox_conference_peer_count(toxes[i], 0, &err);
@ -353,7 +380,7 @@ static void test_many_group(Tox **toxes, State *state)
fflush(stdout);
} while (fully_connected_count != NUM_GROUP_TOX);
for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) {
for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) {
uint32_t peer_count = tox_conference_peer_count(toxes[i], 0, nullptr);
ck_assert_msg(peer_count == NUM_GROUP_TOX, "\n\tBad number of group peers (pre check)."

View File

@ -48,6 +48,15 @@ static uint64_t get_state_clock_callback(Mono_Time *mono_time, void *user_data)
return state->clock;
}
static void set_mono_time_callback(Tox *tox, State *state)
{
// TODO(iphydf): Don't rely on toxcore internals.
Mono_Time *mono_time = ((Messenger *)tox)->mono_time;
state->clock = current_time_monotonic(mono_time);
mono_time_set_current_time_callback(mono_time, get_state_clock_callback, state);
}
static void run_auto_test(uint32_t tox_count, void test(Tox **toxes, State *state), bool chain)
{
printf("initialising %u toxes\n", tox_count);
@ -59,11 +68,7 @@ static void run_auto_test(uint32_t tox_count, void test(Tox **toxes, State *stat
toxes[i] = tox_new_log(nullptr, nullptr, &state[i].index);
ck_assert_msg(toxes[i], "failed to create %u tox instances", i + 1);
// TODO(iphydf): Don't rely on toxcore internals.
Mono_Time *mono_time = (*(Messenger **)toxes[i])->mono_time;
state[i].clock = current_time_monotonic(mono_time);
mono_time_set_current_time_callback(mono_time, get_state_clock_callback, &state[i]);
set_mono_time_callback(toxes[i], &state[i]);
}
if (chain) {

View File

@ -433,14 +433,19 @@ static int handle_group_audio_packet(void *object, uint32_t groupnumber, uint32_
return 0;
}
/* Convert groupchat to an A/V groupchat.
/* Enable A/V in a groupchat.
*
* return 0 on success.
* return -1 on failure.
*/
static int groupchat_enable_av(const Logger *log, Tox *tox, Group_Chats *g_c, uint32_t groupnumber,
audio_data_cb *audio_callback, void *userdata)
int groupchat_enable_av(const Logger *log, Tox *tox, Group_Chats *g_c, uint32_t groupnumber,
audio_data_cb *audio_callback, void *userdata)
{
if (group_get_type(g_c, groupnumber) != GROUPCHAT_TYPE_AV
|| group_get_object(g_c, groupnumber) != nullptr) {
return -1;
}
Group_AV *group_av = new_group_av(log, tox, g_c, audio_callback, userdata);
if (group_av == nullptr) {
@ -455,10 +460,52 @@ static int groupchat_enable_av(const Logger *log, Tox *tox, Group_Chats *g_c, ui
return -1;
}
int numpeers = group_number_peers(g_c, groupnumber, false);
for (uint32_t i = 0; i < numpeers; ++i) {
group_av_peer_new(group_av, groupnumber, i);
}
group_lossy_packet_registerhandler(g_c, GROUP_AUDIO_PACKET_ID, &handle_group_audio_packet);
return 0;
}
/* Disable A/V in a groupchat.
*
* return 0 on success.
* return -1 on failure.
*/
int groupchat_disable_av(Group_Chats *g_c, uint32_t groupnumber)
{
if (group_get_type(g_c, groupnumber) != GROUPCHAT_TYPE_AV) {
return -1;
}
Group_AV *group_av = (Group_AV *)group_get_object(g_c, groupnumber);
if (group_av == nullptr) {
return -1;
}
int numpeers = group_number_peers(g_c, groupnumber, false);
for (uint32_t i = 0; i < numpeers; ++i) {
group_av_peer_delete(group_av, groupnumber, group_peer_get_object(g_c, groupnumber, i));
group_peer_set_object(g_c, groupnumber, i, nullptr);
}
kill_group_av(group_av);
if (group_set_object(g_c, groupnumber, nullptr) == -1
|| callback_groupchat_peer_new(g_c, groupnumber, nullptr) == -1
|| callback_groupchat_peer_delete(g_c, groupnumber, nullptr) == -1
|| callback_groupchat_delete(g_c, groupnumber, nullptr) == -1) {
return -1;
}
return 0;
}
/* Create a new toxav group.
*
* return group number on success.

View File

@ -59,5 +59,19 @@ int join_av_groupchat(const Logger *log, Tox *tox, Group_Chats *g_c, uint32_t fr
int group_send_audio(Group_Chats *g_c, uint32_t groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels,
uint32_t sample_rate);
/* Enable A/V in a groupchat.
*
* return 0 on success.
* return -1 on failure.
*/
int groupchat_enable_av(const Logger *log, Tox *tox, Group_Chats *g_c, uint32_t groupnumber,
audio_data_cb *audio_callback, void *userdata);
/* Disable A/V in a groupchat.
*
* return 0 on success.
* return -1 on failure.
*/
int groupchat_disable_av(Group_Chats *g_c, uint32_t groupnumber);
#endif // C_TOXCORE_TOXAV_GROUPAV_H

View File

@ -654,6 +654,27 @@ int toxav_join_av_groupchat(Tox *tox, uint32_t friendnumber, const uint8_t *data
int toxav_group_send_audio(Tox *tox, uint32_t groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels,
uint32_t sample_rate);
/* Enable A/V in a groupchat.
*
* return 0 on success.
* return -1 on failure.
*
* Audio data callback format (same as the one for toxav_add_av_groupchat()):
* audio_callback(Tox *tox, uint32_t groupnumber, uint32_t peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, uint32_t sample_rate, void *userdata)
*
* Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
*/
int toxav_groupchat_enable_av(Tox *tox, uint32_t groupnumber,
void (*audio_callback)(void *, uint32_t, uint32_t, const int16_t *, unsigned int, uint8_t, uint32_t, void *),
void *userdata);
/* Disable A/V in a groupchat.
*
* return 0 on success.
* return -1 on failure.
*/
int toxav_groupchat_disable_av(Tox *tox, uint32_t groupnumber);
#ifdef __cplusplus
}
#endif

View File

@ -782,6 +782,27 @@ int toxav_join_av_groupchat(Tox *tox, uint32_t friendnumber, const uint8_t *data
int toxav_group_send_audio(Tox *tox, uint32_t groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels,
uint32_t sample_rate);
/* Enable A/V in a groupchat.
*
* return 0 on success.
* return -1 on failure.
*
* Audio data callback format (same as the one for toxav_add_av_groupchat()):
* audio_callback(Tox *tox, uint32_t groupnumber, uint32_t peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, uint32_t sample_rate, void *userdata)
*
* Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
*/
int toxav_groupchat_enable_av(Tox *tox, uint32_t groupnumber,
void (*audio_callback)(void *, uint32_t, uint32_t, const int16_t *, unsigned int, uint8_t, uint32_t, void *),
void *userdata);
/* Disable A/V in a groupchat.
*
* return 0 on success.
* return -1 on failure.
*/
int toxav_groupchat_disable_av(Tox *tox, uint32_t groupnumber);
#ifdef __cplusplus
}
#endif

View File

@ -80,3 +80,32 @@ int toxav_group_send_audio(Tox *tox, uint32_t groupnumber, const int16_t *pcm, u
Messenger *m = *(Messenger **)tox;
return group_send_audio(m->conferences_object, groupnumber, pcm, samples, channels, sample_rate);
}
/* Enable A/V in a groupchat.
*
* return 0 on success.
* return -1 on failure.
*
* Audio data callback format (same as the one for toxav_add_av_groupchat()):
* audio_callback(Tox *tox, uint32_t groupnumber, uint32_t peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, uint32_t sample_rate, void *userdata)
*
* Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
*/
int toxav_groupchat_enable_av(Tox *tox, uint32_t groupnumber, audio_data_cb *audio_callback, void *userdata)
{
// TODO(iphydf): Don't rely on toxcore internals.
Messenger *m = *(Messenger **)tox;
return groupchat_enable_av(m->log, tox, m->conferences_object, groupnumber, audio_callback, userdata);
}
/* Disable A/V in a groupchat.
*
* return 0 on success.
* return -1 on failure.
*/
int toxav_groupchat_disable_av(Tox *tox, uint32_t groupnumber)
{
// TODO(iphydf): Don't rely on toxcore internals.
Messenger *m = *(Messenger **)tox;
return groupchat_disable_av(m->conferences_object, groupnumber);
}

View File

@ -539,9 +539,7 @@ static int note_peer_active(Group_Chats *g_c, uint32_t groupnumber, uint16_t pee
++g->numpeers;
if (!delete_frozen(g, frozen_index)) {
return -1;
}
delete_frozen(g, frozen_index);
if (g_c->peer_list_changed_callback) {
g_c->peer_list_changed_callback(g_c->m, groupnumber, userdata);
@ -774,6 +772,7 @@ static int freeze_peer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, v
g->frozen = temp;
g->frozen[g->numfrozen] = g->group[peer_index];
g->frozen[g->numfrozen].object = nullptr;
++g->numfrozen;
return delpeer(g_c, groupnumber, peer_index, userdata, true);
@ -2831,6 +2830,12 @@ static unsigned int lossy_packet_not_received(const Group_c *g, int peer_index,
}
/* Does this group type make use of lossy packets? */
static bool type_uses_lossy(uint8_t type)
{
return (type == GROUPCHAT_TYPE_AV);
}
static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uint16_t length, void *userdata)
{
Group_Chats *g_c = (Group_Chats *)object;
@ -2857,6 +2862,10 @@ static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uin
return -1;
}
if (!type_uses_lossy(g->type)) {
return -1;
}
const int index = friend_in_close(g, friendcon_id);
if (index == -1) {
@ -2883,6 +2892,8 @@ static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uin
++lossy_data;
--lossy_length;
send_lossy_all_close(g_c, groupnumber, data + 1 + sizeof(uint16_t), length - (1 + sizeof(uint16_t)), index);
if (g_c->lossy_packethandlers[message_id].function) {
if (g_c->lossy_packethandlers[message_id].function(g->object, groupnumber, peer_index, g->group[peer_index].object,
lossy_data, lossy_length) == -1) {
@ -2892,7 +2903,6 @@ static int handle_lossy(void *object, int friendcon_id, const uint8_t *data, uin
return -1;
}
send_lossy_all_close(g_c, groupnumber, data + 1 + sizeof(uint16_t), length - (1 + sizeof(uint16_t)), index);
return 0;
}
@ -2934,7 +2944,7 @@ int group_peer_set_object(const Group_Chats *g_c, uint32_t groupnumber, int peer
return 0;
}
/* Return the object tide to the group chat previously set by group_set_object.
/* Return the object tied to the group chat previously set by group_set_object.
*
* return NULL on failure.
* return object on success.
@ -2950,7 +2960,7 @@ void *group_get_object(const Group_Chats *g_c, uint32_t groupnumber)
return g->object;
}
/* Return the object tide to the group chat peer previously set by group_peer_set_object.
/* Return the object tied to the group chat peer previously set by group_peer_set_object.
*
* return NULL on failure.
* return object on success.