The pretty basic adaptive bitrate is *working*

This commit is contained in:
mannol 2015-04-16 02:00:34 +02:00
parent 2465f486ac
commit da6c17222f
6 changed files with 233 additions and 141 deletions

View File

@ -27,6 +27,9 @@
#include "../toxcore/tox.h"
#include "../toxcore/util.h"
#define LOGGING
#include "../toxcore/logger.h"
/* Playing audio data */
#include <portaudio.h>
/* Reading audio */
@ -80,6 +83,7 @@
typedef struct {
bool incoming;
uint32_t state;
uint32_t abitrate;
} CallControl;
struct toxav_thread_data {
@ -91,22 +95,6 @@ struct toxav_thread_data {
const char* vdout = "AV Test"; /* Video output */
PaStream* adout = NULL; /* Audio output */
const char* stringify_state(TOXAV_CALL_STATE s)
{
static const char* strings[] =
{
"NOT SENDING",
"SENDING AUDIO",
"SENDING VIDEO",
"SENDING AUDIO AND VIDEO",
"PAUSED",
"END",
"ERROR"
};
return strings [s];
}
/**
* Callbacks
*/
@ -117,9 +105,20 @@ void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool
}
void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data)
{
printf("Handling CALL STATE callback: %d\n", state);
((CallControl*)user_data)->state = state;
if (state & TOXAV_CALL_STATE_INCREASE_AUDIO_BITRATE) {
uint32_t bitrate = ((CallControl*)user_data)->abitrate;
if (bitrate < 64) {
printf("Changing bitrate to: %d\n", 64);
toxav_set_audio_bit_rate(av, friend_number, 64, 0);
}
} else if (state & TOXAV_CALL_STATE_INCREASE_VIDEO_BITRATE) {
} else {
printf("Handling CALL STATE callback: %d\n", state);
}
}
void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number,
uint16_t width, uint16_t height,
@ -331,24 +330,6 @@ int print_help (const char* name)
int main (int argc, char** argv)
{
RingBuffer* rb = rb_new(4);
int a[5] = {0, 1, 2, 3, 4};
int* x;
rb_write(rb, a + 0);
rb_write(rb, a + 1);
rb_write(rb, a + 2);
rb_write(rb, a + 3);
// rb_write(rb, a + 4);
x = rb_write(rb, a + 4);
while (rb_read(rb, (void**) &x))
// rb_read(rb, (void**)&x);
printf("%d ", *x);
printf("\n");
// int r = 43;
// printf("%d\n", r >= 40 ? 3 : r / 10);
return 0;
Pa_Initialize();
struct stat st;
@ -688,9 +669,11 @@ int main (int argc, char** argv)
memset(&AliceCC, 0, sizeof(CallControl));
memset(&BobCC, 0, sizeof(CallControl));
AliceCC.abitrate = BobCC.abitrate = 8;
{ /* Call */
TOXAV_ERR_CALL rc;
toxav_call(AliceAV, 0, 48, 0, &rc);
toxav_call(AliceAV, 0, AliceCC.abitrate, 0, &rc);
if (rc != TOXAV_ERR_CALL_OK) {
printf("toxav_call failed: %d\n", rc);
@ -703,7 +686,7 @@ int main (int argc, char** argv)
{ /* Answer */
TOXAV_ERR_ANSWER rc;
toxav_answer(BobAV, 0, 48, 0, &rc);
toxav_answer(BobAV, 0, BobCC.abitrate, 0, &rc);
if (rc != TOXAV_ERR_ANSWER_OK) {
printf("toxav_answer failed: %d\n", rc);

View File

@ -293,7 +293,7 @@ void cs_do(CSession *cs)
/* The maximum for 120 ms 48 KHz audio */
int16_t tmp[5760];
while ((msg = jbuf_read(cs->j_buf, &success)) || success == 2) {
if ((msg = jbuf_read(cs->j_buf, &success)) || success == 2) {
LOGGED_UNLOCK(cs->queue_mutex);
if (success == 2) {
@ -327,7 +327,7 @@ void cs_do(CSession *cs)
if (!reconfigure_audio_decoder(cs, cs->last_packet_sampling_rate, cs->last_packet_channel_count)) {
LOGGER_WARNING("Failed to reconfigure decoder!");
rtp_free_msg(NULL, msg);
continue;
goto DONE;
}
rc = opus_decode(cs->audio_decoder, msg->data + 4, msg->length - 4, tmp, 5760, 0);
@ -347,6 +347,7 @@ void cs_do(CSession *cs)
LOGGED_LOCK(cs->queue_mutex);
}
DONE:;
}
/********************* VIDEO *********************/

View File

@ -50,15 +50,17 @@
typedef struct {
uint64_t timestamp; /* in ms */
uint32_t packets_missing;
uint32_t received_packets;
uint32_t expected_packets;
/* ... other stuff in the future */
} RTCPReport;
typedef struct RTCPSession_s {
RTPSession *rtp_session;
uint8_t prefix;
uint64_t last_sent_report_ts;
uint32_t last_missing_packets;
uint32_t last_received_packets;
uint32_t last_expected_packets;
RingBuffer* pl_stats; /* Packet loss stats over time */
@ -66,6 +68,7 @@ typedef struct RTCPSession_s {
/* queue_message() is defined in codec.c */
void queue_message(RTPSession *session, RTPMessage *msg);
RTPHeader *parse_header_in ( const uint8_t *payload, int length );
@ -91,18 +94,12 @@ RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num )
}
retu->version = RTP_VERSION; /* It's always 2 */
retu->padding = 0; /* If some additional data is needed about the packet */
retu->extension = 0; /* If extension to header is needed */
retu->cc = 1; /* Amount of contributors */
retu->csrc = NULL; /* Container */
retu->ssrc = random_int();
retu->marker = 0;
retu->payload_type = payload_type % 128;
retu->tstate = rtp_StateNormal;
retu->m = messenger;
retu->dest = friend_num;
retu->rsequnum = retu->sequnum = 0;
retu->ext_header = NULL; /* When needed allocate */
if ( !(retu->csrc = calloc(1, sizeof(uint32_t))) ) {
LOGGER_WARNING("Alloc failed! Program might misbehave!");
@ -117,15 +114,16 @@ RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num )
/* Initialize rtcp session */
if (!(retu->rtcp = calloc(1, sizeof(RTCPSession)))) {
if (!(retu->rtcp_session = calloc(1, sizeof(RTCPSession)))) {
LOGGER_WARNING("Alloc failed! Program might misbehave!");
free(retu->csrc);
free(retu);
return NULL;
}
retu->rtcp->prefix = 222 + payload_type % 192;
retu->rtcp->pl_stats = rb_new(4);
retu->rtcp_session->prefix = 222 + payload_type % 192;
retu->rtcp_session->pl_stats = rb_new(4);
retu->rtcp_session->rtp_session = retu;
return retu;
}
@ -139,11 +137,11 @@ void rtp_kill ( RTPSession *session )
free ( session->csrc );
void* t;
while (!rb_empty(session->rtcp->pl_stats)) {
rb_read(session->rtcp->pl_stats, (void**) &t);
while (!rb_empty(session->rtcp_session->pl_stats)) {
rb_read(session->rtcp_session->pl_stats, (void**) &t);
free(t);
}
rb_free(session->rtcp->pl_stats);
rb_free(session->rtcp_session->pl_stats);
LOGGER_DEBUG("Terminated RTP session: %p", session);
@ -152,41 +150,49 @@ void rtp_kill ( RTPSession *session )
}
void rtp_do(RTPSession *session)
{
if (!session || !session->rtcp)
if (!session || !session->rtcp_session)
return;
if (current_time_monotonic() - session->rtcp->last_sent_report_ts >= RTCP_REPORT_INTERVAL_MS) {
send_rtcp_report(session->rtcp, session->m, session->dest);
if (current_time_monotonic() - session->rtcp_session->last_sent_report_ts >= RTCP_REPORT_INTERVAL_MS) {
send_rtcp_report(session->rtcp_session, session->m, session->dest);
}
if (rb_full(session->rtcp->pl_stats)) {
if (rb_full(session->rtcp_session->pl_stats)) {
RTCPReport* reports[4];
int i = 0;
for (; rb_read(session->rtcp->pl_stats, (void**) reports + i); i++);
for (; rb_read(session->rtcp_session->pl_stats, (void**) reports + i); i++);
/* Check for timed out reports (> 6 sec) */
uint64_t now = current_time_monotonic();
for (i = 0; i < 4 && now - reports[i]->timestamp < 6000; i ++);
for (; i < 4; i ++) {
rb_write(session->rtcp->pl_stats, reports[i]);
rb_write(session->rtcp_session->pl_stats, reports[i]);
reports[i] = NULL;
}
if (!rb_empty(session->rtcp->pl_stats)) {
if (!rb_empty(session->rtcp_session->pl_stats)) {
for (i = 0; reports[i] != NULL; i ++)
free(reports[i]);
return; /* As some reports are timed out, we need more... */
}
/* We have 4 on-time reports so we can proceed */
uint32_t quality_loss = 0;
uint32_t quality = 100;
for (i = 0; i < 4; i++) {
uint32_t idx = reports[i]->packets_missing * 100 / reports[i]->expected_packets;
quality_loss += idx;
uint32_t idx = reports[i]->received_packets * 100 / reports[i]->expected_packets;
quality = MIN(quality, idx);
free(reports[i]);
}
if (quality_loss > 40) {
LOGGER_DEBUG("Packet loss detected");
if (quality <= 70) {
session->tstate = rtp_StateBad;
LOGGER_WARNING("Stream quality: BAD");
} else if (quality >= 99) {
session->tstate = rtp_StateGood;
LOGGER_DEBUG("Stream quality: GOOD");
} else {
session->tstate = rtp_StateNormal;
LOGGER_DEBUG("Stream quality: NORMAL");
}
}
}
@ -200,8 +206,8 @@ int rtp_start_receiving(RTPSession* session)
LOGGER_WARNING("Failed to register rtp receive handler");
return -1;
}
if (custom_lossy_packet_registerhandler(session->m, session->dest, session->rtcp->prefix,
handle_rtcp_packet, session->rtcp) == -1) {
if (custom_lossy_packet_registerhandler(session->m, session->dest, session->rtcp_session->prefix,
handle_rtcp_packet, session->rtcp_session) == -1) {
LOGGER_WARNING("Failed to register rtcp receive handler");
custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix, NULL, NULL);
return -1;
@ -215,7 +221,7 @@ int rtp_stop_receiving(RTPSession* session)
return -1;
custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix, NULL, NULL);
custom_lossy_packet_registerhandler(session->m, session->dest, session->rtcp->prefix, NULL, NULL); /* RTCP */
custom_lossy_packet_registerhandler(session->m, session->dest, session->rtcp_session->prefix, NULL, NULL); /* RTCP */
return 0;
}
@ -516,16 +522,21 @@ void send_rtcp_report(RTCPSession* session, Messenger* m, int32_t friendnumber)
uint8_t parsed[9];
parsed[0] = session->prefix;
uint32_t packets_missing = htonl(session->last_missing_packets);
uint32_t received_packets = htonl(session->last_received_packets);
uint32_t expected_packets = htonl(session->last_expected_packets);
memcpy(parsed + 1, &packets_missing, 4);
memcpy(parsed + 1, &received_packets, 4);
memcpy(parsed + 5, &expected_packets, 4);
if (-1 == send_custom_lossy_packet(m, friendnumber, parsed, sizeof(parsed)))
LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", sizeof(parsed), strerror(errno));
else
else {
LOGGER_DEBUG("Sent rtcp report: ex: %d rc: %d", session->last_expected_packets, session->last_received_packets);
session->last_received_packets = 0;
session->last_expected_packets = 0;
session->last_sent_report_ts = current_time_monotonic();
}
}
int handle_rtp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length, void *object )
{
@ -545,12 +556,21 @@ int handle_rtp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data,
}
/* Check if message came in late */
if ( msg->header->sequnum > session->rsequnum && msg->header->timestamp > session->rtimestamp ) {
if ( msg->header->sequnum > session->rsequnum || msg->header->timestamp > session->rtimestamp ) {
/* Not late */
if (msg->header->sequnum > session->rsequnum)
session->rtcp_session->last_expected_packets += msg->header->sequnum - session->rsequnum;
else if (msg->header->sequnum < session->rsequnum)
session->rtcp_session->last_expected_packets += (msg->header->sequnum + 65535) - session->rsequnum;
else /* Usual case when transmission starts */
session->rtcp_session->last_expected_packets ++;
session->rsequnum = msg->header->sequnum;
session->rtimestamp = msg->header->timestamp;
}
session->rtcp_session->last_received_packets ++;
queue_message(session, msg);
return 0;
}
@ -562,14 +582,15 @@ int handle_rtcp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data
RTCPSession* session = object;
RTCPReport* report = malloc(sizeof(RTCPReport));
memcpy(&report->packets_missing, data + 1, 4);
memcpy(&report->received_packets, data + 1, 4);
memcpy(&report->expected_packets, data + 5, 4);
report->packets_missing = ntohl(report->packets_missing);
report->received_packets = ntohl(report->received_packets);
report->expected_packets = ntohl(report->expected_packets);
/* This would cause undefined behaviour */
if (report->expected_packets == 0) {
/* Invalid values */
if (report->expected_packets == 0 || report->received_packets > report->expected_packets) {
LOGGER_WARNING("Malformed rtcp report! %d %d", report->expected_packets, report->received_packets);
free(report);
return 0;
}
@ -577,5 +598,7 @@ int handle_rtcp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data
report->timestamp = current_time_monotonic();
free(rb_write(session->pl_stats, report));
LOGGER_DEBUG("Got rtcp report: ex: %d rc: %d", report->expected_packets, report->received_packets);
return 0;
}

View File

@ -44,10 +44,16 @@
/**
* Payload type identifier. Also used as rtp callback prefix.
*/
typedef enum {
enum {
rtp_TypeAudio = 192,
rtp_TypeVideo
} RTPPayloadType;
};
typedef enum {
rtp_StateBad = -1,
rtp_StateNormal,
rtp_StateGood,
} RTPTransmissionState;
/**
* Standard rtp header.
@ -108,10 +114,11 @@ typedef struct {
int dest;
struct RTCPSession_s *rtcp;
struct RTCPSession_s *rtcp_session;
struct CSession_s *cs;
Messenger *m;
RTPTransmissionState tstate;
} RTPSession;
/**
@ -150,5 +157,4 @@ int rtp_send_msg ( RTPSession* session, const uint8_t* data, uint16_t length );
void rtp_free_msg ( RTPSession *session, RTPMessage *msg );
#endif /* RTP_H */

View File

@ -60,6 +60,12 @@ typedef struct ToxAVCall_s {
uint8_t last_self_capabilities;
uint8_t last_peer_capabilities;
/** Quality control */
uint64_t time_audio_good;
uint32_t last_bad_audio_bit_rate;
uint64_t time_video_good;
uint32_t last_bad_video_bit_rate;
struct ToxAVCall_s *prev;
struct ToxAVCall_s *next;
} ToxAVCall;
@ -96,11 +102,13 @@ int callback_end(void* toxav_inst, MSICall* call);
int callback_error(void* toxav_inst, MSICall* call);
int callback_capabilites(void* toxav_inst, MSICall* call);
ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error);
ToxAVCall* call_get(ToxAV* av, uint32_t friend_number);
void call_remove(ToxAVCall* call);
bool audio_bitrate_invalid(uint32_t bitrate);
bool video_bitrate_invalid(uint32_t bitrate);
void invoke_call_state(ToxAV* av, uint32_t friend_number, uint32_t state);
ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error);
ToxAVCall* call_get(ToxAV* av, uint32_t friend_number);
void qc_do(ToxAVCall* call);
void call_remove(ToxAVCall* call);
bool call_prepare_transmission(ToxAVCall* call);
void call_kill_transmission(ToxAVCall* call);
@ -220,8 +228,9 @@ void toxav_iterate(ToxAV* av)
LOGGED_UNLOCK(av->mutex);
cs_do(i->cs);
rtp_do(i->rtps[0]);
rtp_do(i->rtps[1]);
rtp_do(i->rtps[audio_index]);
rtp_do(i->rtps[video_index]);
qc_do(i);
if (i->last_self_capabilities & msi_CapRAudio) /* Receiving audio */
rc = MIN(i->cs->last_packet_frame_duration, rc);
@ -516,8 +525,10 @@ bool toxav_set_audio_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t audio_
goto END;
}
/* NOTE: no need to lock*/
/* Decoding mutex is locked because of quality control */
LOGGED_LOCK(call->mutex_decoding);
call->audio_bit_rate = audio_bit_rate;
LOGGED_UNLOCK(call->mutex_decoding);
LOGGED_UNLOCK(av->mutex);
END:
@ -550,8 +561,10 @@ bool toxav_set_video_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t video_
goto END;
}
/* NOTE: no need to lock*/
/* Decoding mutex is locked because of quality control */
LOGGED_LOCK(call->mutex_decoding);
call->video_bit_rate = video_bit_rate;
LOGGED_UNLOCK(call->mutex_decoding);
LOGGED_UNLOCK(av->mutex);
END:
@ -813,8 +826,7 @@ int callback_start(void* toxav_inst, MSICall* call)
return -1;
}
if (toxav->scb.first)
toxav->scb.first(toxav, call->friend_id, call->peer_capabilities, toxav->scb.second);
invoke_call_state(toxav, call->friend_id, call->peer_capabilities);
LOGGED_UNLOCK(toxav->mutex);
return 0;
@ -825,8 +837,7 @@ int callback_end(void* toxav_inst, MSICall* call)
ToxAV* toxav = toxav_inst;
LOGGED_LOCK(toxav->mutex);
if (toxav->scb.first)
toxav->scb.first(toxav, call->friend_id, TOXAV_CALL_STATE_END, toxav->scb.second);
invoke_call_state(toxav, call->friend_id, TOXAV_CALL_STATE_END);
call_kill_transmission(call->av_call);
call_remove(call->av_call);
@ -840,8 +851,7 @@ int callback_error(void* toxav_inst, MSICall* call)
ToxAV* toxav = toxav_inst;
LOGGED_LOCK(toxav->mutex);
if (toxav->scb.first)
toxav->scb.first(toxav, call->friend_id, TOXAV_CALL_STATE_ERROR, toxav->scb.second);
invoke_call_state(toxav, call->friend_id, TOXAV_CALL_STATE_ERROR);
call_kill_transmission(call->av_call);
call_remove(call->av_call);
@ -857,8 +867,7 @@ int callback_capabilites(void* toxav_inst, MSICall* call)
/* TODO modify cs? */
if (toxav->scb.first)
toxav->scb.first(toxav, call->friend_id, call->peer_capabilities, toxav->scb.second);
invoke_call_state(toxav, call->friend_id, call->peer_capabilities);
LOGGED_UNLOCK(toxav->mutex);
return 0;
@ -878,6 +887,12 @@ bool video_bitrate_invalid(uint32_t bitrate)
return false;
}
void invoke_call_state(ToxAV* av, uint32_t friend_number, uint32_t state)
{
if (av->scb.first)
av->scb.first(av, friend_number, state, av->scb.second);
}
ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error)
{
/* Assumes mutex locked */
@ -968,6 +983,94 @@ ToxAVCall* call_get(ToxAV* av, uint32_t friend_number)
return av->calls[friend_number];
}
void qc_do(ToxAVCall* call)
{
/* Please NOTE: The quality control is rather basic,
* advanced algorithms will be applied in the future
*/
switch(call->rtps[audio_index]->tstate) {
case rtp_StateBad:
LOGGER_DEBUG("Suggesting lower bitrate for audio...");
call->time_audio_good = 0;
call->last_bad_audio_bit_rate = call->audio_bit_rate;
invoke_call_state(call->av, call->friend_id, TOXAV_CALL_STATE_LOWER_AUDIO_BITRATE);
break;
case rtp_StateGood:
if (call->time_audio_good == 0)
call->time_audio_good = current_time_monotonic();
else if (current_time_monotonic() - call->time_audio_good >= 30000 &&
64 > call->audio_bit_rate)
if (call->last_bad_audio_bit_rate > call->audio_bit_rate) {
if (current_time_monotonic() - call->time_audio_good >= 45000)
invoke_call_state(call->av, call->friend_id, TOXAV_CALL_STATE_INCREASE_AUDIO_BITRATE);
call->last_bad_audio_bit_rate = 0;
} else
invoke_call_state(call->av, call->friend_id, TOXAV_CALL_STATE_INCREASE_AUDIO_BITRATE);
break;
case rtp_StateNormal:
call->time_audio_good = 0;
break;
}
switch(call->rtps[video_index]->tstate) {
case rtp_StateBad:
LOGGER_DEBUG("Suggesting lower bitrate for video...");
call->time_video_good = 0;
call->last_bad_video_bit_rate = call->video_bit_rate;
invoke_call_state(call->av, call->friend_id, TOXAV_CALL_STATE_LOWER_VIDEO_BITRATE);
break;
case rtp_StateGood:
if (call->time_video_good == 0)
call->time_video_good = current_time_monotonic();
else if (current_time_monotonic() - call->time_video_good >= 30000)
if (call->last_bad_video_bit_rate > call->video_bit_rate) {
if (current_time_monotonic() - call->time_video_good >= 45000)
invoke_call_state(call->av, call->friend_id, TOXAV_CALL_STATE_INCREASE_VIDEO_BITRATE);
call->last_bad_video_bit_rate = 0;
} else
invoke_call_state(call->av, call->friend_id, TOXAV_CALL_STATE_INCREASE_VIDEO_BITRATE);
break;
case rtp_StateNormal:
call->time_video_good = 0;
break;
}
}
void call_remove(ToxAVCall* call)
{
if (call == NULL)
return;
uint32_t friend_id = call->friend_id;
ToxAV* av = call->av;
ToxAVCall* prev = call->prev;
ToxAVCall* next = call->next;
free(call);
if (prev)
prev->next = next;
else if (next)
av->calls_head = next->friend_id;
else goto CLEAR;
if (next)
next->prev = prev;
else if (prev)
av->calls_tail = prev->friend_id;
else goto CLEAR;
av->calls[friend_id] = NULL;
return;
CLEAR:
av->calls_head = av->calls_tail = 0;
free(av->calls);
av->calls = NULL;
}
bool call_prepare_transmission(ToxAVCall* call)
{
/* Assumes mutex locked */
@ -1089,37 +1192,3 @@ void call_kill_transmission(ToxAVCall* call)
pthread_mutex_destroy(call->mutex_video_sending);
pthread_mutex_destroy(call->mutex_decoding);
}
void call_remove(ToxAVCall* call)
{
if (call == NULL)
return;
uint32_t friend_id = call->friend_id;
ToxAV* av = call->av;
ToxAVCall* prev = call->prev;
ToxAVCall* next = call->next;
free(call);
if (prev)
prev->next = next;
else if (next)
av->calls_head = next->friend_id;
else goto CLEAR;
if (next)
next->prev = prev;
else if (prev)
av->calls_tail = prev->friend_id;
else goto CLEAR;
av->calls[friend_id] = NULL;
return;
CLEAR:
av->calls_head = av->calls_tail = 0;
free(av->calls);
av->calls = NULL;
}

View File

@ -207,21 +207,31 @@ typedef enum TOXAV_CALL_STATE {
* The flag that marks that friend is receiving video.
*/
TOXAV_CALL_STATE_RECEIVING_V = 8,
/**
* The core will never set TOXAV_CALL_STATE_END or TOXAV_CALL_STATE_ERROR
* together with other states.
*/
/**
* The call has finished. This is the final state after which no more state
* transitions can occur for the call.
*/
TOXAV_CALL_STATE_END = 16,
/**
* Sent by the AV core if an error occurred on the remote end.
* AV core suggests you to lower bitrate for audio.
*/
TOXAV_CALL_STATE_ERROR = 32
TOXAV_CALL_STATE_LOWER_AUDIO_BITRATE = 32,
/**
* AV core suggests you to lower bitrate for video.
*/
TOXAV_CALL_STATE_LOWER_VIDEO_BITRATE = 64,
/**
* AV core suggests you to increase bitrate for audio.
*/
TOXAV_CALL_STATE_INCREASE_AUDIO_BITRATE = 128,
/**
* AV core suggests you to increase bitrate for video.
*/
TOXAV_CALL_STATE_INCREASE_VIDEO_BITRATE = 256,
/**
* Set by the AV core if an error occurred on the remote end.
*/
TOXAV_CALL_STATE_ERROR = 32768
} TOXAV_CALL_STATE;
/**
* The function type for the `call_state` callback.