Squashed 'external/toxcore/c-toxcore/' changes from 03e9fbf3703..55752a2e2ef
55752a2e2ef fix(toxav): pass video bit rate as kbit Previously we unintentionally made it Mbit. 7e573280a75 docs(toxav): fix docs of toxav.h - fix units to be more readable - use width before height consistently - video -> audio typo 5f88a084e8c fix: friend_connections leak on allocation failure clean up when it only contains connections in the NONE state 6d27a1ae178 fix: wrong comment for closelist ce4f29e8036 cleanup: Fix all `-Wsign-compare` warnings. 4d4251c397f chore: lower cirrus ci timeout drastically 40676284507 fix: events leak that can occur if allocation fails rare in practice, found by fuzzing 9610ac31c5f fix: Return an error instead of crashing on nullptr args in NGC. a57c2c8f956 refactor: Make ToxAV independent of toxcore internals. 5752fc29f86 refactor: Make tox-bootstrapd use bool instead of int df675786eb2 chore: Add release-drafter github action. 03fd7a69dcf chore: Use toktok's cmp instead of upstream. 350c0ba1205 cleanup: Sort apk/apt install commands in Dockerfiles. 8c1bda502cb chore(deps): bump golang.org/x/net ddb9d3210da chore: Upgrade to FreeBSD 14.1 in cirrus build. e9076f45bd3 chore(cmake): set options changes as cache and with force git-subtree-dir: external/toxcore/c-toxcore git-subtree-split: 55752a2e2ef894bfa6d7a2a21a0278e3f2bede7d
This commit is contained in:
@ -42,127 +42,32 @@ cc_library(
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "bwcontroller",
|
||||
srcs = ["bwcontroller.c"],
|
||||
hdrs = ["bwcontroller.h"],
|
||||
name = "toxav",
|
||||
srcs = glob(
|
||||
[
|
||||
"*.c",
|
||||
"*.h",
|
||||
],
|
||||
exclude = ["toxav.h"],
|
||||
),
|
||||
hdrs = ["toxav.h"],
|
||||
visibility = ["//c-toxcore:__subpackages__"],
|
||||
deps = [
|
||||
":ring_buffer",
|
||||
"//c-toxcore/toxcore",
|
||||
"//c-toxcore/toxcore:Messenger",
|
||||
"//c-toxcore/toxcore:ccompat",
|
||||
"//c-toxcore/toxcore:logger",
|
||||
"//c-toxcore/toxcore:mono_time",
|
||||
"//c-toxcore/toxcore:tox",
|
||||
"//c-toxcore/toxcore:util",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "rtp",
|
||||
srcs = ["rtp.c"],
|
||||
hdrs = ["rtp.h"],
|
||||
deps = [
|
||||
":bwcontroller",
|
||||
"//c-toxcore/toxcore:Messenger",
|
||||
"//c-toxcore/toxcore:ccompat",
|
||||
"//c-toxcore/toxcore:logger",
|
||||
"//c-toxcore/toxcore:mono_time",
|
||||
"//c-toxcore/toxcore:tox",
|
||||
"//c-toxcore/toxcore:util",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "rtp_test",
|
||||
size = "small",
|
||||
srcs = ["rtp_test.cc"],
|
||||
deps = [
|
||||
":rtp",
|
||||
"//c-toxcore/toxcore:crypto_core",
|
||||
"@com_google_googletest//:gtest",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "audio",
|
||||
srcs = ["audio.c"],
|
||||
hdrs = ["audio.h"],
|
||||
deps = [
|
||||
":public_api",
|
||||
":rtp",
|
||||
"//c-toxcore/toxcore:ccompat",
|
||||
"//c-toxcore/toxcore:logger",
|
||||
"//c-toxcore/toxcore:mono_time",
|
||||
"//c-toxcore/toxcore:util",
|
||||
"@opus",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "video",
|
||||
srcs = [
|
||||
"msi.c",
|
||||
"video.c",
|
||||
],
|
||||
hdrs = [
|
||||
"msi.h",
|
||||
"video.h",
|
||||
],
|
||||
deps = [
|
||||
":audio",
|
||||
":public_api",
|
||||
":ring_buffer",
|
||||
":rtp",
|
||||
"//c-toxcore/toxcore:Messenger",
|
||||
"//c-toxcore/toxcore:ccompat",
|
||||
"//c-toxcore/toxcore:logger",
|
||||
"//c-toxcore/toxcore:mono_time",
|
||||
"//c-toxcore/toxcore:network",
|
||||
"//c-toxcore/toxcore:util",
|
||||
"@libvpx",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "groupav",
|
||||
srcs = ["groupav.c"],
|
||||
hdrs = ["groupav.h"],
|
||||
deps = [
|
||||
"//c-toxcore/toxcore",
|
||||
"//c-toxcore/toxcore:ccompat",
|
||||
"//c-toxcore/toxcore:group",
|
||||
"//c-toxcore/toxcore:logger",
|
||||
"//c-toxcore/toxcore:mono_time",
|
||||
"//c-toxcore/toxcore:net_crypto",
|
||||
"//c-toxcore/toxcore:network",
|
||||
"//c-toxcore/toxcore:tox",
|
||||
"//c-toxcore/toxcore:util",
|
||||
"@libsodium",
|
||||
"@libvpx",
|
||||
"@opus",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "toxav",
|
||||
srcs = [
|
||||
"toxav.c",
|
||||
"toxav_old.c",
|
||||
],
|
||||
hdrs = [
|
||||
"toxav.h",
|
||||
],
|
||||
visibility = ["//c-toxcore:__subpackages__"],
|
||||
deps = [
|
||||
":groupav",
|
||||
":rtp",
|
||||
":video",
|
||||
"//c-toxcore/toxcore:Messenger",
|
||||
"//c-toxcore/toxcore:ccompat",
|
||||
"//c-toxcore/toxcore:logger",
|
||||
"//c-toxcore/toxcore:mono_time",
|
||||
"//c-toxcore/toxcore:tox",
|
||||
"//c-toxcore/toxcore:util",
|
||||
],
|
||||
)
|
||||
|
||||
sh_library(
|
||||
name = "cimple_files",
|
||||
srcs = glob([
|
||||
|
@ -19,6 +19,7 @@ libtoxav_la_SOURCES = ../toxav/rtp.h \
|
||||
../toxav/ring_buffer.h \
|
||||
../toxav/ring_buffer.c \
|
||||
../toxav/toxav.h \
|
||||
../toxav/toxav_hacks.h \
|
||||
../toxav/toxav.c \
|
||||
../toxav/toxav_old.c
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "../toxcore/ccompat.h"
|
||||
#include "../toxcore/logger.h"
|
||||
#include "../toxcore/mono_time.h"
|
||||
#include "../toxcore/network.h"
|
||||
|
||||
static struct JitterBuffer *jbuf_new(uint32_t capacity);
|
||||
static void jbuf_clear(struct JitterBuffer *q);
|
||||
@ -25,6 +26,8 @@ static bool reconfigure_audio_encoder(const Logger *log, OpusEncoder **e, uint32
|
||||
uint8_t new_ch, uint32_t *old_br, uint32_t *old_sr, uint8_t *old_ch);
|
||||
static bool reconfigure_audio_decoder(ACSession *ac, uint32_t sampling_rate, uint8_t channels);
|
||||
|
||||
|
||||
|
||||
ACSession *ac_new(Mono_Time *mono_time, const Logger *log, ToxAV *av, uint32_t friend_number,
|
||||
toxav_audio_receive_frame_cb *cb, void *cb_data)
|
||||
{
|
||||
@ -150,9 +153,9 @@ void ac_iterate(ACSession *ac)
|
||||
|
||||
ac->lp_channel_count = opus_packet_get_nb_channels(msg->data + 4);
|
||||
|
||||
/* NOTE: even though OPUS supports decoding mono frames with stereo decoder and vice versa,
|
||||
* it didn't work quite well.
|
||||
*/
|
||||
/** NOTE: even though OPUS supports decoding mono frames with stereo decoder and vice versa,
|
||||
* it didn't work quite well.
|
||||
*/
|
||||
if (!reconfigure_audio_decoder(ac, ac->lp_sampling_rate, ac->lp_channel_count)) {
|
||||
LOGGER_WARNING(ac->log, "Failed to reconfigure decoder!");
|
||||
free(msg);
|
||||
@ -273,6 +276,7 @@ static struct JitterBuffer *jbuf_new(uint32_t capacity)
|
||||
q->capacity = capacity;
|
||||
return q;
|
||||
}
|
||||
|
||||
static void jbuf_clear(struct JitterBuffer *q)
|
||||
{
|
||||
while (q->bottom != q->top) {
|
||||
@ -281,6 +285,7 @@ static void jbuf_clear(struct JitterBuffer *q)
|
||||
++q->bottom;
|
||||
}
|
||||
}
|
||||
|
||||
static void jbuf_free(struct JitterBuffer *q)
|
||||
{
|
||||
if (q == nullptr) {
|
||||
@ -291,6 +296,11 @@ static void jbuf_free(struct JitterBuffer *q)
|
||||
free(q->queue);
|
||||
free(q);
|
||||
}
|
||||
|
||||
/*
|
||||
* if -1 is returned the RTPMessage m needs to be free'd by the caller
|
||||
* if 0 is returned the RTPMessage m is stored in the ringbuffer and must NOT be freed by the caller
|
||||
*/
|
||||
static int jbuf_write(const Logger *log, struct JitterBuffer *q, struct RTPMessage *m)
|
||||
{
|
||||
const uint16_t sequnum = m->header.sequnum;
|
||||
@ -319,6 +329,7 @@ static int jbuf_write(const Logger *log, struct JitterBuffer *q, struct RTPMessa
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success)
|
||||
{
|
||||
if (q->top == q->bottom) {
|
||||
|
@ -10,12 +10,16 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "ring_buffer.h"
|
||||
#include "toxav_hacks.h"
|
||||
|
||||
#include "../toxcore/ccompat.h"
|
||||
#include "../toxcore/logger.h"
|
||||
#include "../toxcore/mono_time.h"
|
||||
#include "../toxcore/network.h"
|
||||
#include "../toxcore/tox_private.h"
|
||||
#include "../toxcore/util.h"
|
||||
|
||||
|
||||
#define BWC_PACKET_ID 196
|
||||
#define BWC_SEND_INTERVAL_MS 950 // 0.95s
|
||||
#define BWC_AVG_PKT_COUNT 20
|
||||
@ -38,9 +42,8 @@ typedef struct BWCRcvPkt {
|
||||
struct BWController {
|
||||
m_cb *mcb;
|
||||
void *mcb_user_data;
|
||||
|
||||
Messenger *m;
|
||||
Tox *tox;
|
||||
const Logger *log;
|
||||
uint32_t friend_number;
|
||||
|
||||
BWCCycle cycle;
|
||||
@ -49,6 +52,7 @@ struct BWController {
|
||||
|
||||
uint32_t packet_loss_counted_cycles;
|
||||
Mono_Time *bwc_mono_time;
|
||||
bool bwc_receive_active; /* if this is set to false then incoming bwc packets will not be processed by bwc_handle_data() */
|
||||
};
|
||||
|
||||
struct BWCMessage {
|
||||
@ -56,11 +60,11 @@ struct BWCMessage {
|
||||
uint32_t recv;
|
||||
};
|
||||
|
||||
static int bwc_handle_data(Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object);
|
||||
static int bwc_send_custom_lossy_packet(Tox *tox, int32_t friendnumber, const uint8_t *data, uint32_t length);
|
||||
static void bwc_handle_data(Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length, void *user_data);
|
||||
static void send_update(BWController *bwc);
|
||||
|
||||
BWController *bwc_new(Messenger *m, Tox *tox, uint32_t friendnumber, m_cb *mcb, void *mcb_user_data,
|
||||
|
||||
BWController *bwc_new(const Logger *log, Tox *tox, uint32_t friendnumber, m_cb *mcb, void *mcb_user_data,
|
||||
Mono_Time *bwc_mono_time)
|
||||
{
|
||||
BWController *retu = (BWController *)calloc(1, sizeof(BWController));
|
||||
@ -69,16 +73,18 @@ BWController *bwc_new(Messenger *m, Tox *tox, uint32_t friendnumber, m_cb *mcb,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LOGGER_DEBUG(m->log, "Creating bandwidth controller");
|
||||
LOGGER_DEBUG(log, "Creating bandwidth controller");
|
||||
|
||||
retu->mcb = mcb;
|
||||
retu->mcb_user_data = mcb_user_data;
|
||||
retu->m = m;
|
||||
retu->friend_number = friendnumber;
|
||||
retu->bwc_mono_time = bwc_mono_time;
|
||||
const uint64_t now = current_time_monotonic(bwc_mono_time);
|
||||
retu->cycle.last_sent_timestamp = now;
|
||||
retu->cycle.last_refresh_timestamp = now;
|
||||
retu->tox = tox;
|
||||
retu->log = log;
|
||||
retu->bwc_receive_active = true;
|
||||
retu->rcvpkt.rb = rb_new(BWC_AVG_PKT_COUNT);
|
||||
retu->cycle.lost = 0;
|
||||
retu->cycle.recv = 0;
|
||||
@ -89,7 +95,6 @@ BWController *bwc_new(Messenger *m, Tox *tox, uint32_t friendnumber, m_cb *mcb,
|
||||
rb_write(retu->rcvpkt.rb, &retu->rcvpkt.packet_length_array[i]);
|
||||
}
|
||||
|
||||
m_callback_rtp_packet(m, friendnumber, BWC_PACKET_ID, bwc_handle_data, retu);
|
||||
return retu;
|
||||
}
|
||||
|
||||
@ -99,7 +104,6 @@ void bwc_kill(BWController *bwc)
|
||||
return;
|
||||
}
|
||||
|
||||
m_callback_rtp_packet(bwc->m, bwc->friend_number, BWC_PACKET_ID, nullptr, nullptr);
|
||||
rb_kill(bwc->rcvpkt.rb);
|
||||
free(bwc);
|
||||
}
|
||||
@ -111,7 +115,7 @@ void bwc_add_lost(BWController *bwc, uint32_t bytes_lost)
|
||||
}
|
||||
|
||||
if (bytes_lost > 0) {
|
||||
LOGGER_DEBUG(bwc->m->log, "BWC lost(1): %d", (int)bytes_lost);
|
||||
LOGGER_DEBUG(bwc->log, "BWC lost(1): %d", (int)bytes_lost);
|
||||
bwc->cycle.lost += bytes_lost;
|
||||
send_update(bwc);
|
||||
}
|
||||
@ -135,7 +139,7 @@ static void send_update(BWController *bwc)
|
||||
bwc->packet_loss_counted_cycles = 0;
|
||||
|
||||
if (bwc->cycle.lost != 0) {
|
||||
LOGGER_DEBUG(bwc->m->log, "%p Sent update rcv: %u lost: %u percent: %f %%",
|
||||
LOGGER_DEBUG(bwc->log, "%p Sent update rcv: %u lost: %u percent: %f %%",
|
||||
(void *)bwc, bwc->cycle.recv, bwc->cycle.lost,
|
||||
((double)bwc->cycle.lost / (bwc->cycle.recv + bwc->cycle.lost)) * 100.0);
|
||||
uint8_t bwc_packet[sizeof(struct BWCMessage) + 1];
|
||||
@ -148,13 +152,11 @@ static void send_update(BWController *bwc)
|
||||
offset += net_pack_u32(bwc_packet + offset, bwc->cycle.recv);
|
||||
assert(offset == sizeof(bwc_packet));
|
||||
|
||||
if (bwc_send_custom_lossy_packet(bwc->tox, bwc->friend_number, bwc_packet, sizeof(bwc_packet)) == -1) {
|
||||
char *netstrerror = net_new_strerror(net_error());
|
||||
char *stdstrerror = net_new_strerror(errno);
|
||||
LOGGER_WARNING(bwc->m->log, "BWC send failed (len: %u)! std error: %s, net error %s",
|
||||
(unsigned)sizeof(bwc_packet), stdstrerror, netstrerror);
|
||||
net_kill_strerror(stdstrerror);
|
||||
net_kill_strerror(netstrerror);
|
||||
Tox_Err_Friend_Custom_Packet error;
|
||||
tox_friend_send_lossy_packet(bwc->tox, bwc->friend_number, bwc_packet, sizeof(bwc_packet), &error);
|
||||
|
||||
if (error != TOX_ERR_FRIEND_CUSTOM_PACKET_OK) {
|
||||
LOGGER_WARNING(bwc->log, "BWC send failed: %d", error);
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,11 +168,11 @@ static void send_update(BWController *bwc)
|
||||
|
||||
static int on_update(BWController *bwc, const struct BWCMessage *msg)
|
||||
{
|
||||
LOGGER_DEBUG(bwc->m->log, "%p Got update from peer", (void *)bwc);
|
||||
LOGGER_DEBUG(bwc->log, "%p Got update from peer", (void *)bwc);
|
||||
|
||||
/* Peers sent update too soon */
|
||||
if (bwc->cycle.last_recv_timestamp + BWC_SEND_INTERVAL_MS > current_time_monotonic(bwc->bwc_mono_time)) {
|
||||
LOGGER_INFO(bwc->m->log, "%p Rejecting extra update", (void *)bwc);
|
||||
LOGGER_INFO(bwc->log, "%p Rejecting extra update", (void *)bwc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -180,8 +182,8 @@ static int on_update(BWController *bwc, const struct BWCMessage *msg)
|
||||
|
||||
if (lost != 0 && bwc->mcb != nullptr) {
|
||||
const uint32_t recv = msg->recv;
|
||||
LOGGER_DEBUG(bwc->m->log, "recved: %u lost: %u percentage: %f %%", recv, lost,
|
||||
((double)lost / (recv + lost)) * 100.0);
|
||||
LOGGER_DEBUG(bwc->log, "recved: %u lost: %u percentage: %f %%", recv, lost,
|
||||
((double) lost / (recv + lost)) * 100.0);
|
||||
bwc->mcb(bwc, bwc->friend_number,
|
||||
(float)lost / (recv + lost),
|
||||
bwc->mcb_user_data);
|
||||
@ -190,28 +192,41 @@ static int on_update(BWController *bwc, const struct BWCMessage *msg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* return -1 on failure, 0 on success
|
||||
*
|
||||
*/
|
||||
static int bwc_send_custom_lossy_packet(Tox *tox, int32_t friendnumber, const uint8_t *data, uint32_t length)
|
||||
static void bwc_handle_data(Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length, void *user_data)
|
||||
{
|
||||
Tox_Err_Friend_Custom_Packet error;
|
||||
tox_friend_send_lossy_packet(tox, friendnumber, data, (size_t)length, &error);
|
||||
/* get BWController object from Tox and friend number */
|
||||
ToxAV *toxav = (ToxAV *)tox_get_av_object(tox);
|
||||
|
||||
if (error == TOX_ERR_FRIEND_CUSTOM_PACKET_OK) {
|
||||
return 0;
|
||||
if (toxav == nullptr) {
|
||||
// LOGGER_ERROR(log, "Could not get ToxAV object from Tox");
|
||||
return;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int bwc_handle_data(Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object)
|
||||
{
|
||||
BWController *bwc = (BWController *)object;
|
||||
const Logger *log = toxav_get_logger(toxav);
|
||||
|
||||
if (length - 1 != sizeof(struct BWCMessage)) {
|
||||
return -1;
|
||||
LOGGER_ERROR(log, "Got BWCMessage of insufficient size.");
|
||||
return;
|
||||
}
|
||||
|
||||
const ToxAVCall *call = call_get(toxav, friend_number);
|
||||
|
||||
if (call == nullptr) {
|
||||
LOGGER_ERROR(log, "Could not get ToxAVCall object from ToxAV.");
|
||||
return;
|
||||
}
|
||||
|
||||
/* get Call object from Tox and friend number */
|
||||
BWController *bwc = bwc_controller_get(call);
|
||||
|
||||
if (bwc == nullptr) {
|
||||
LOGGER_WARNING(log, "No session!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bwc->bwc_receive_active) {
|
||||
LOGGER_WARNING(log, "receiving not allowed!");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t offset = 1; // Ignore packet id.
|
||||
@ -220,5 +235,15 @@ static int bwc_handle_data(Messenger *m, uint32_t friend_number, const uint8_t *
|
||||
offset += net_unpack_u32(data + offset, &msg.recv);
|
||||
assert(offset == length);
|
||||
|
||||
return on_update(bwc, &msg);
|
||||
on_update(bwc, &msg);
|
||||
}
|
||||
|
||||
void bwc_allow_receiving(Tox *tox)
|
||||
{
|
||||
tox_callback_friend_lossy_packet_per_pktid(tox, bwc_handle_data, BWC_PACKET_ID);
|
||||
}
|
||||
|
||||
void bwc_stop_receiving(Tox *tox)
|
||||
{
|
||||
tox_callback_friend_lossy_packet_per_pktid(tox, nullptr, BWC_PACKET_ID);
|
||||
}
|
||||
|
@ -5,19 +5,24 @@
|
||||
#ifndef C_TOXCORE_TOXAV_BWCONTROLLER_H
|
||||
#define C_TOXCORE_TOXAV_BWCONTROLLER_H
|
||||
|
||||
#include "../toxcore/Messenger.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../toxcore/logger.h"
|
||||
#include "../toxcore/mono_time.h"
|
||||
#include "../toxcore/tox.h"
|
||||
|
||||
typedef struct BWController BWController;
|
||||
|
||||
typedef void m_cb(BWController *bwc, uint32_t friend_number, float loss, void *user_data);
|
||||
|
||||
BWController *bwc_new(Messenger *m, Tox *tox, uint32_t friendnumber, m_cb *mcb, void *mcb_user_data,
|
||||
Mono_Time *bwc_mono_time);
|
||||
BWController *bwc_new(const Logger *log, Tox *tox, uint32_t friendnumber,
|
||||
m_cb *mcb, void *mcb_user_data, Mono_Time *bwc_mono_time);
|
||||
|
||||
void bwc_kill(BWController *bwc);
|
||||
|
||||
void bwc_add_lost(BWController *bwc, uint32_t bytes_lost);
|
||||
void bwc_add_recv(BWController *bwc, uint32_t recv_bytes);
|
||||
void bwc_allow_receiving(Tox *tox);
|
||||
void bwc_stop_receiving(Tox *tox);
|
||||
|
||||
#endif /* C_TOXCORE_TOXAV_BWCONTROLLER_H */
|
||||
|
@ -476,7 +476,7 @@ int groupchat_enable_av(const Logger *log, Tox *tox, Group_Chats *g_c, uint32_t
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < numpeers; ++i) {
|
||||
for (uint32_t i = 0; i < (uint32_t)numpeers; ++i) {
|
||||
group_av_peer_new(group_av, conference_number, i);
|
||||
}
|
||||
|
||||
@ -508,7 +508,7 @@ int groupchat_disable_av(const Group_Chats *g_c, uint32_t conference_number)
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < numpeers; ++i) {
|
||||
for (uint32_t i = 0; i < (uint32_t)numpeers; ++i) {
|
||||
group_av_peer_delete(group_av, conference_number, group_peer_get_object(g_c, conference_number, i));
|
||||
group_peer_set_object(g_c, conference_number, i, nullptr);
|
||||
}
|
||||
|
462
toxav/msi.c
462
toxav/msi.c
@ -9,8 +9,13 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "toxav_hacks.h"
|
||||
|
||||
#include "../toxcore/ccompat.h"
|
||||
#include "../toxcore/logger.h"
|
||||
#include "../toxcore/net_crypto.h"
|
||||
#include "../toxcore/tox.h"
|
||||
#include "../toxcore/tox_private.h"
|
||||
#include "../toxcore/util.h"
|
||||
|
||||
#define MSI_MAXMSG_SIZE 256
|
||||
@ -55,20 +60,20 @@ typedef struct MSIMessage {
|
||||
} MSIMessage;
|
||||
|
||||
static void msg_init(MSIMessage *dest, MSIRequest request);
|
||||
static void kill_call(const Logger *log, MSICall *call);
|
||||
static int msg_parse_in(const Logger *log, MSIMessage *dest, const uint8_t *data, uint16_t length);
|
||||
static uint8_t *msg_parse_header_out(MSIHeaderID id, uint8_t *dest, const uint8_t *value, uint8_t value_len,
|
||||
uint16_t *length);
|
||||
static int send_message(const Messenger *m, uint32_t friend_number, const MSIMessage *msg);
|
||||
static int send_error(const Messenger *m, uint32_t friend_number, MSIError error);
|
||||
static bool invoke_callback(MSICall *call, MSICallbackID cb);
|
||||
static int send_message(const Logger *log, Tox *tox, uint32_t friend_number, const MSIMessage *msg);
|
||||
static int send_error(const Logger *log, Tox *tox, uint32_t friend_number, MSIError error);
|
||||
static MSICall *get_call(MSISession *session, uint32_t friend_number);
|
||||
static MSICall *new_call(MSISession *session, uint32_t friend_number);
|
||||
static void kill_call(MSICall *call);
|
||||
static void on_peer_status(Messenger *m, uint32_t friend_number, bool is_online, void *user_data);
|
||||
static void handle_init(MSICall *call, const MSIMessage *msg);
|
||||
static void handle_push(MSICall *call, const MSIMessage *msg);
|
||||
static void handle_pop(MSICall *call, const MSIMessage *msg);
|
||||
static void handle_msi_packet(Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *user_data);
|
||||
static bool invoke_callback(const Logger *log, MSICall *call, MSICallbackID cb);
|
||||
static void handle_init(const Logger *log, MSICall *call, const MSIMessage *msg);
|
||||
static void handle_push(const Logger *log, MSICall *call, const MSIMessage *msg);
|
||||
static void handle_pop(const Logger *log, MSICall *call, const MSIMessage *msg);
|
||||
static void handle_msi_packet(Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length,
|
||||
void *user_data);
|
||||
|
||||
/*
|
||||
* Public functions
|
||||
@ -99,43 +104,43 @@ void msi_callback_capabilities(MSISession *session, msi_action_cb *callback)
|
||||
session->capabilities_callback = callback;
|
||||
}
|
||||
|
||||
MSISession *msi_new(Messenger *m)
|
||||
MSISession *msi_new(const Logger *log, Tox *tox)
|
||||
{
|
||||
if (m == nullptr) {
|
||||
if (tox == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MSISession *retu = (MSISession *)calloc(1, sizeof(MSISession));
|
||||
|
||||
if (retu == nullptr) {
|
||||
LOGGER_ERROR(m->log, "Allocation failed! Program might misbehave!");
|
||||
LOGGER_ERROR(log, "Allocation failed! Program might misbehave!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (create_recursive_mutex(retu->mutex) != 0) {
|
||||
LOGGER_ERROR(m->log, "Failed to init mutex! Program might misbehave");
|
||||
LOGGER_ERROR(log, "Failed to init mutex! Program might misbehave");
|
||||
free(retu);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
retu->messenger = m;
|
||||
retu->tox = tox;
|
||||
|
||||
m_callback_msi_packet(m, handle_msi_packet, retu);
|
||||
// register callback
|
||||
tox_callback_friend_lossless_packet_per_pktid(tox, handle_msi_packet, PACKET_ID_MSI);
|
||||
|
||||
/* This is called when remote terminates session */
|
||||
m_callback_connectionstatus_internal_av(m, on_peer_status, retu);
|
||||
|
||||
LOGGER_DEBUG(m->log, "New msi session: %p ", (void *)retu);
|
||||
LOGGER_DEBUG(log, "New msi session: %p ", (void *)retu);
|
||||
return retu;
|
||||
}
|
||||
int msi_kill(MSISession *session, const Logger *log)
|
||||
|
||||
int msi_kill(const Logger *log, Tox *tox, MSISession *session)
|
||||
{
|
||||
if (session == nullptr) {
|
||||
LOGGER_ERROR(log, "Tried to terminate non-existing session");
|
||||
return -1;
|
||||
}
|
||||
|
||||
m_callback_msi_packet(session->messenger, nullptr, nullptr);
|
||||
// UN-register callback
|
||||
tox_callback_friend_lossless_packet_per_pktid(tox, nullptr, PACKET_ID_MSI);
|
||||
|
||||
if (pthread_mutex_trylock(session->mutex) != 0) {
|
||||
LOGGER_ERROR(log, "Failed to acquire lock on msi mutex");
|
||||
@ -149,10 +154,10 @@ int msi_kill(MSISession *session, const Logger *log)
|
||||
MSICall *it = get_call(session, session->calls_head);
|
||||
|
||||
while (it != nullptr) {
|
||||
send_message(session->messenger, it->friend_number, &msg);
|
||||
send_message(log, session->tox, it->friend_number, &msg);
|
||||
MSICall *temp_it = it;
|
||||
it = it->next;
|
||||
kill_call(temp_it); /* This will eventually free session->calls */
|
||||
kill_call(log, temp_it); /* This will eventually free session->calls */
|
||||
}
|
||||
}
|
||||
|
||||
@ -163,21 +168,57 @@ int msi_kill(MSISession *session, const Logger *log)
|
||||
free(session);
|
||||
return 0;
|
||||
}
|
||||
int msi_invite(MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities)
|
||||
|
||||
/*
|
||||
* return true if friend is offline and the call was canceled.
|
||||
*/
|
||||
bool check_peer_offline_status(const Logger *log, const Tox *tox, MSISession *session, uint32_t friend_number)
|
||||
{
|
||||
if (tox == nullptr || session == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Tox_Err_Friend_Query f_con_query_error;
|
||||
const Tox_Connection f_con_status = tox_friend_get_connection_status(tox, friend_number, &f_con_query_error);
|
||||
|
||||
if (f_con_status == TOX_CONNECTION_NONE) {
|
||||
/* Friend is now offline */
|
||||
LOGGER_DEBUG(log, "Friend %d is now offline", friend_number);
|
||||
|
||||
pthread_mutex_lock(session->mutex);
|
||||
MSICall *call = get_call(session, friend_number);
|
||||
|
||||
if (call == nullptr) {
|
||||
pthread_mutex_unlock(session->mutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
invoke_callback(log, call, MSI_ON_PEERTIMEOUT); /* Failure is ignored */
|
||||
kill_call(log, call);
|
||||
pthread_mutex_unlock(session->mutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int msi_invite(const Logger *log, MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities)
|
||||
{
|
||||
LOGGER_DEBUG(log, "msi_invite:session:%p", (void *)session);
|
||||
|
||||
if (session == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOGGER_DEBUG(session->messenger->log, "Session: %p Inviting friend: %u", (void *)session, friend_number);
|
||||
LOGGER_DEBUG(log, "Session: %p Inviting friend: %u", (void *)session, friend_number);
|
||||
|
||||
if (pthread_mutex_trylock(session->mutex) != 0) {
|
||||
LOGGER_ERROR(session->messenger->log, "Failed to acquire lock on msi mutex");
|
||||
LOGGER_ERROR(log, "Failed to acquire lock on msi mutex");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (get_call(session, friend_number) != nullptr) {
|
||||
LOGGER_ERROR(session->messenger->log, "Already in a call");
|
||||
LOGGER_ERROR(log, "Already in a call");
|
||||
pthread_mutex_unlock(session->mutex);
|
||||
return -1;
|
||||
}
|
||||
@ -197,17 +238,18 @@ int msi_invite(MSISession *session, MSICall **call, uint32_t friend_number, uint
|
||||
msg.capabilities.exists = true;
|
||||
msg.capabilities.value = capabilities;
|
||||
|
||||
send_message(temp->session->messenger, temp->friend_number, &msg);
|
||||
send_message(log, temp->session->tox, temp->friend_number, &msg);
|
||||
|
||||
temp->state = MSI_CALL_REQUESTING;
|
||||
|
||||
*call = temp;
|
||||
|
||||
LOGGER_DEBUG(session->messenger->log, "Invite sent");
|
||||
LOGGER_DEBUG(log, "Invite sent");
|
||||
pthread_mutex_unlock(session->mutex);
|
||||
return 0;
|
||||
}
|
||||
int msi_hangup(MSICall *call)
|
||||
|
||||
int msi_hangup(const Logger *log, MSICall *call)
|
||||
{
|
||||
if (call == nullptr || call->session == nullptr) {
|
||||
return -1;
|
||||
@ -215,16 +257,16 @@ int msi_hangup(MSICall *call)
|
||||
|
||||
MSISession *session = call->session;
|
||||
|
||||
LOGGER_DEBUG(session->messenger->log, "Session: %p Hanging up call with friend: %u", (void *)call->session,
|
||||
LOGGER_DEBUG(log, "Session: %p Hanging up call with friend: %u", (void *)call->session,
|
||||
call->friend_number);
|
||||
|
||||
if (pthread_mutex_trylock(session->mutex) != 0) {
|
||||
LOGGER_ERROR(session->messenger->log, "Failed to acquire lock on msi mutex");
|
||||
LOGGER_ERROR(log, "Failed to acquire lock on msi mutex");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (call->state == MSI_CALL_INACTIVE) {
|
||||
LOGGER_ERROR(session->messenger->log, "Call is in invalid state!");
|
||||
LOGGER_ERROR(log, "Call is in invalid state!");
|
||||
pthread_mutex_unlock(session->mutex);
|
||||
return -1;
|
||||
}
|
||||
@ -232,13 +274,14 @@ int msi_hangup(MSICall *call)
|
||||
MSIMessage msg;
|
||||
msg_init(&msg, REQU_POP);
|
||||
|
||||
send_message(session->messenger, call->friend_number, &msg);
|
||||
send_message(log, session->tox, call->friend_number, &msg);
|
||||
|
||||
kill_call(call);
|
||||
kill_call(log, call);
|
||||
pthread_mutex_unlock(session->mutex);
|
||||
return 0;
|
||||
}
|
||||
int msi_answer(MSICall *call, uint8_t capabilities)
|
||||
|
||||
int msi_answer(const Logger *log, MSICall *call, uint8_t capabilities)
|
||||
{
|
||||
if (call == nullptr || call->session == nullptr) {
|
||||
return -1;
|
||||
@ -246,18 +289,18 @@ int msi_answer(MSICall *call, uint8_t capabilities)
|
||||
|
||||
MSISession *session = call->session;
|
||||
|
||||
LOGGER_DEBUG(session->messenger->log, "Session: %p Answering call from: %u", (void *)call->session,
|
||||
LOGGER_DEBUG(log, "Session: %p Answering call from: %u", (void *)call->session,
|
||||
call->friend_number);
|
||||
|
||||
if (pthread_mutex_trylock(session->mutex) != 0) {
|
||||
LOGGER_ERROR(session->messenger->log, "Failed to acquire lock on msi mutex");
|
||||
LOGGER_ERROR(log, "Failed to acquire lock on msi mutex");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (call->state != MSI_CALL_REQUESTED) {
|
||||
/* Though sending in invalid state will not cause anything weird
|
||||
* Its better to not do it like a maniac */
|
||||
LOGGER_ERROR(session->messenger->log, "Call is in invalid state!");
|
||||
LOGGER_ERROR(log, "Call is in invalid state!");
|
||||
pthread_mutex_unlock(session->mutex);
|
||||
return -1;
|
||||
}
|
||||
@ -270,14 +313,15 @@ int msi_answer(MSICall *call, uint8_t capabilities)
|
||||
msg.capabilities.exists = true;
|
||||
msg.capabilities.value = capabilities;
|
||||
|
||||
send_message(session->messenger, call->friend_number, &msg);
|
||||
send_message(log, session->tox, call->friend_number, &msg);
|
||||
|
||||
call->state = MSI_CALL_ACTIVE;
|
||||
pthread_mutex_unlock(session->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
int msi_change_capabilities(MSICall *call, uint8_t capabilities)
|
||||
|
||||
int msi_change_capabilities(const Logger *log, MSICall *call, uint8_t capabilities)
|
||||
{
|
||||
if (call == nullptr || call->session == nullptr) {
|
||||
return -1;
|
||||
@ -285,16 +329,16 @@ int msi_change_capabilities(MSICall *call, uint8_t capabilities)
|
||||
|
||||
MSISession *session = call->session;
|
||||
|
||||
LOGGER_DEBUG(session->messenger->log, "Session: %p Trying to change capabilities to friend %u", (void *)call->session,
|
||||
LOGGER_DEBUG(log, "Session: %p Trying to change capabilities to friend %u", (void *)call->session,
|
||||
call->friend_number);
|
||||
|
||||
if (pthread_mutex_trylock(session->mutex) != 0) {
|
||||
LOGGER_ERROR(session->messenger->log, "Failed to acquire lock on msi mutex");
|
||||
LOGGER_ERROR(log, "Failed to acquire lock on msi mutex");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (call->state != MSI_CALL_ACTIVE) {
|
||||
LOGGER_ERROR(session->messenger->log, "Call is in invalid state!");
|
||||
LOGGER_ERROR(log, "Call is in invalid state!");
|
||||
pthread_mutex_unlock(session->mutex);
|
||||
return -1;
|
||||
}
|
||||
@ -307,7 +351,7 @@ int msi_change_capabilities(MSICall *call, uint8_t capabilities)
|
||||
msg.capabilities.exists = true;
|
||||
msg.capabilities.value = capabilities;
|
||||
|
||||
send_message(call->session->messenger, call->friend_number, &msg);
|
||||
send_message(log, call->session->tox, call->friend_number, &msg);
|
||||
|
||||
pthread_mutex_unlock(session->mutex);
|
||||
return 0;
|
||||
@ -351,10 +395,51 @@ static bool check_enum_high(const Logger *log, const uint8_t *bytes, uint8_t enu
|
||||
return true;
|
||||
}
|
||||
|
||||
static const uint8_t *msg_parse_one(const Logger *log, MSIMessage *dest, const uint8_t *it, int *size_constraint)
|
||||
{
|
||||
switch (*it) {
|
||||
case ID_REQUEST: {
|
||||
if (!check_size(log, it, size_constraint, 1) ||
|
||||
!check_enum_high(log, it, REQU_POP)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
dest->request.value = (MSIRequest)it[2];
|
||||
dest->request.exists = true;
|
||||
return it + 3;
|
||||
}
|
||||
|
||||
case ID_ERROR: {
|
||||
if (!check_size(log, it, size_constraint, 1) ||
|
||||
!check_enum_high(log, it, MSI_E_UNDISCLOSED)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
dest->error.value = (MSIError)it[2];
|
||||
dest->error.exists = true;
|
||||
return it + 3;
|
||||
}
|
||||
|
||||
case ID_CAPABILITIES: {
|
||||
if (!check_size(log, it, size_constraint, 1)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
dest->capabilities.value = it[2];
|
||||
dest->capabilities.exists = true;
|
||||
return it + 3;
|
||||
}
|
||||
|
||||
default: {
|
||||
LOGGER_ERROR(log, "Invalid id byte: %d", *it);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int msg_parse_in(const Logger *log, MSIMessage *dest, const uint8_t *data, uint16_t length)
|
||||
{
|
||||
/* Parse raw data received from socket into MSIMessage struct */
|
||||
|
||||
assert(dest != nullptr);
|
||||
|
||||
if (length == 0 || data[length - 1] != 0) { /* End byte must have value 0 */
|
||||
@ -368,46 +453,10 @@ static int msg_parse_in(const Logger *log, MSIMessage *dest, const uint8_t *data
|
||||
int size_constraint = length;
|
||||
|
||||
while (*it != 0) {/* until end byte is hit */
|
||||
switch (*it) {
|
||||
case ID_REQUEST: {
|
||||
if (!check_size(log, it, &size_constraint, 1) ||
|
||||
!check_enum_high(log, it, REQU_POP)) {
|
||||
return -1;
|
||||
}
|
||||
it = msg_parse_one(log, dest, it, &size_constraint);
|
||||
|
||||
dest->request.value = (MSIRequest)it[2];
|
||||
dest->request.exists = true;
|
||||
it += 3;
|
||||
break;
|
||||
}
|
||||
|
||||
case ID_ERROR: {
|
||||
if (!check_size(log, it, &size_constraint, 1) ||
|
||||
!check_enum_high(log, it, MSI_E_UNDISCLOSED)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
dest->error.value = (MSIError)it[2];
|
||||
dest->error.exists = true;
|
||||
it += 3;
|
||||
break;
|
||||
}
|
||||
|
||||
case ID_CAPABILITIES: {
|
||||
if (!check_size(log, it, &size_constraint, 1)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
dest->capabilities.value = it[2];
|
||||
dest->capabilities.exists = true;
|
||||
it += 3;
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
LOGGER_ERROR(log, "Invalid id byte");
|
||||
return -1;
|
||||
}
|
||||
if (it == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -418,6 +467,7 @@ static int msg_parse_in(const Logger *log, MSIMessage *dest, const uint8_t *data
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t *msg_parse_header_out(MSIHeaderID id, uint8_t *dest, const uint8_t *value, uint8_t value_len,
|
||||
uint16_t *length)
|
||||
{
|
||||
@ -437,11 +487,48 @@ static uint8_t *msg_parse_header_out(MSIHeaderID id, uint8_t *dest, const uint8_
|
||||
|
||||
return dest + value_len; /* Set to next position ready to be written */
|
||||
}
|
||||
static int send_message(const Messenger *m, uint32_t friend_number, const MSIMessage *msg)
|
||||
{
|
||||
/* Parse and send message */
|
||||
assert(m != nullptr);
|
||||
|
||||
/* Send an msi packet.
|
||||
*
|
||||
* return 1 on success
|
||||
* return 0 on failure
|
||||
*/
|
||||
static int m_msi_packet(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length)
|
||||
{
|
||||
// TODO(Zoff): make this better later! -------------------
|
||||
/* we need to prepend 1 byte (packet id) to data
|
||||
* do this without malloc, memcpy and free in the future
|
||||
*/
|
||||
const size_t length_new = (size_t)length + 1;
|
||||
uint8_t *data_new = (uint8_t *)malloc(length_new);
|
||||
|
||||
if (data_new == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
data_new[0] = PACKET_ID_MSI;
|
||||
|
||||
if (length != 0) {
|
||||
memcpy(data_new + 1, data, length);
|
||||
}
|
||||
|
||||
Tox_Err_Friend_Custom_Packet error;
|
||||
tox_friend_send_lossless_packet(tox, friendnumber, data_new, length_new, &error);
|
||||
|
||||
free(data_new);
|
||||
|
||||
if (error == TOX_ERR_FRIEND_CUSTOM_PACKET_OK) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_message(const Logger *log, Tox *tox, uint32_t friend_number, const MSIMessage *msg)
|
||||
{
|
||||
assert(tox != nullptr);
|
||||
|
||||
/* Parse and send message */
|
||||
uint8_t parsed[MSI_MAXMSG_SIZE];
|
||||
|
||||
uint8_t *it = parsed;
|
||||
@ -452,7 +539,7 @@ static int send_message(const Messenger *m, uint32_t friend_number, const MSIMes
|
||||
it = msg_parse_header_out(ID_REQUEST, it, &cast,
|
||||
sizeof(cast), &size);
|
||||
} else {
|
||||
LOGGER_DEBUG(m->log, "Must have request field");
|
||||
LOGGER_DEBUG(log, "Must have request field");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -468,26 +555,27 @@ static int send_message(const Messenger *m, uint32_t friend_number, const MSIMes
|
||||
}
|
||||
|
||||
if (it == parsed) {
|
||||
LOGGER_WARNING(m->log, "Parsing message failed; empty message");
|
||||
LOGGER_WARNING(log, "Parsing message failed; empty message");
|
||||
return -1;
|
||||
}
|
||||
|
||||
*it = 0;
|
||||
++size;
|
||||
|
||||
if (m_msi_packet(m, friend_number, parsed, size)) {
|
||||
LOGGER_DEBUG(m->log, "Sent message");
|
||||
if (m_msi_packet(tox, friend_number, parsed, size) == 1) {
|
||||
LOGGER_DEBUG(log, "Sent message");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
static int send_error(const Messenger *m, uint32_t friend_number, MSIError error)
|
||||
{
|
||||
/* Send error message */
|
||||
assert(m != nullptr);
|
||||
|
||||
LOGGER_DEBUG(m->log, "Sending error: %d to friend: %d", error, friend_number);
|
||||
static int send_error(const Logger *log, Tox *tox, uint32_t friend_number, MSIError error)
|
||||
{
|
||||
assert(tox != nullptr);
|
||||
|
||||
/* Send error message */
|
||||
LOGGER_DEBUG(log, "Sending error: %d to friend: %d", error, friend_number);
|
||||
|
||||
MSIMessage msg;
|
||||
msg_init(&msg, REQU_POP);
|
||||
@ -495,13 +583,14 @@ static int send_error(const Messenger *m, uint32_t friend_number, MSIError error
|
||||
msg.error.exists = true;
|
||||
msg.error.value = error;
|
||||
|
||||
send_message(m, friend_number, &msg);
|
||||
send_message(log, tox, friend_number, &msg);
|
||||
return 0;
|
||||
}
|
||||
static int invoke_callback_inner(MSICall *call, MSICallbackID id)
|
||||
|
||||
static int invoke_callback_inner(const Logger *log, MSICall *call, MSICallbackID id)
|
||||
{
|
||||
MSISession *session = call->session;
|
||||
LOGGER_DEBUG(session->messenger->log, "invoking callback function: %d", id);
|
||||
LOGGER_DEBUG(log, "invoking callback function: %d", id);
|
||||
|
||||
switch (id) {
|
||||
case MSI_ON_INVITE:
|
||||
@ -523,15 +612,16 @@ static int invoke_callback_inner(MSICall *call, MSICallbackID id)
|
||||
return session->capabilities_callback(session->av, call);
|
||||
}
|
||||
|
||||
LOGGER_FATAL(session->messenger->log, "invalid callback id: %d", id);
|
||||
LOGGER_FATAL(log, "invalid callback id: %d", id);
|
||||
return -1;
|
||||
}
|
||||
static bool invoke_callback(MSICall *call, MSICallbackID cb)
|
||||
|
||||
static bool invoke_callback(const Logger *log, MSICall *call, MSICallbackID cb)
|
||||
{
|
||||
assert(call != nullptr);
|
||||
|
||||
if (invoke_callback_inner(call, cb) != 0) {
|
||||
LOGGER_WARNING(call->session->messenger->log,
|
||||
if (invoke_callback_inner(log, call, cb) != 0) {
|
||||
LOGGER_WARNING(log,
|
||||
"Callback state handling failed, sending error");
|
||||
|
||||
/* If no callback present or error happened while handling,
|
||||
@ -546,6 +636,7 @@ static bool invoke_callback(MSICall *call, MSICallbackID cb)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static MSICall *get_call(MSISession *session, uint32_t friend_number)
|
||||
{
|
||||
assert(session != nullptr);
|
||||
@ -556,6 +647,7 @@ static MSICall *get_call(MSISession *session, uint32_t friend_number)
|
||||
|
||||
return session->calls[friend_number];
|
||||
}
|
||||
|
||||
static MSICall *new_call(MSISession *session, uint32_t friend_number)
|
||||
{
|
||||
assert(session != nullptr);
|
||||
@ -607,7 +699,8 @@ static MSICall *new_call(MSISession *session, uint32_t friend_number)
|
||||
session->calls[friend_number] = rc;
|
||||
return rc;
|
||||
}
|
||||
static void kill_call(MSICall *call)
|
||||
|
||||
static void kill_call(const Logger *log, MSICall *call)
|
||||
{
|
||||
/* Assume that session mutex is locked */
|
||||
if (call == nullptr) {
|
||||
@ -616,7 +709,7 @@ static void kill_call(MSICall *call)
|
||||
|
||||
MSISession *session = call->session;
|
||||
|
||||
LOGGER_DEBUG(session->messenger->log, "Killing call: %p", (void *)call);
|
||||
LOGGER_DEBUG(log, "Killing call: %p", (void *)call);
|
||||
|
||||
MSICall *prev = call->prev;
|
||||
MSICall *next = call->next;
|
||||
@ -648,37 +741,12 @@ CLEAR_CONTAINER:
|
||||
free(call);
|
||||
session->calls = nullptr;
|
||||
}
|
||||
static void on_peer_status(Messenger *m, uint32_t friend_number, bool is_online, void *user_data)
|
||||
|
||||
|
||||
static bool try_handle_init(const Logger *log, MSICall *call, const MSIMessage *msg)
|
||||
{
|
||||
MSISession *session = (MSISession *)user_data;
|
||||
|
||||
if (is_online) {
|
||||
// Friend is online.
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER_DEBUG(m->log, "Friend %d is now offline", friend_number);
|
||||
|
||||
pthread_mutex_lock(session->mutex);
|
||||
MSICall *call = get_call(session, friend_number);
|
||||
|
||||
if (call == nullptr) {
|
||||
pthread_mutex_unlock(session->mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
invoke_callback(call, MSI_ON_PEERTIMEOUT); /* Failure is ignored */
|
||||
kill_call(call);
|
||||
pthread_mutex_unlock(session->mutex);
|
||||
}
|
||||
static bool try_handle_init(MSICall *call, const MSIMessage *msg)
|
||||
{
|
||||
assert(call != nullptr);
|
||||
LOGGER_DEBUG(call->session->messenger->log,
|
||||
"Session: %p Handling 'init' friend: %d", (void *)call->session, call->friend_number);
|
||||
|
||||
if (!msg->capabilities.exists) {
|
||||
LOGGER_WARNING(call->session->messenger->log, "Session: %p Invalid capabilities on 'init'", (void *)call->session);
|
||||
LOGGER_WARNING(log, "Session: %p Invalid capabilities on 'init'", (void *)call->session);
|
||||
call->error = MSI_E_INVALID_MESSAGE;
|
||||
return false;
|
||||
}
|
||||
@ -689,7 +757,7 @@ static bool try_handle_init(MSICall *call, const MSIMessage *msg)
|
||||
call->peer_capabilities = msg->capabilities.value;
|
||||
call->state = MSI_CALL_REQUESTED;
|
||||
|
||||
if (!invoke_callback(call, MSI_ON_INVITE)) {
|
||||
if (!invoke_callback(log, call, MSI_ON_INVITE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -704,7 +772,7 @@ static bool try_handle_init(MSICall *call, const MSIMessage *msg)
|
||||
* we can automatically answer the re-call.
|
||||
*/
|
||||
|
||||
LOGGER_INFO(call->session->messenger->log, "Friend is recalling us");
|
||||
LOGGER_INFO(log, "Friend is recalling us");
|
||||
|
||||
MSIMessage out_msg;
|
||||
msg_init(&out_msg, REQU_PUSH);
|
||||
@ -712,7 +780,7 @@ static bool try_handle_init(MSICall *call, const MSIMessage *msg)
|
||||
out_msg.capabilities.exists = true;
|
||||
out_msg.capabilities.value = call->self_capabilities;
|
||||
|
||||
send_message(call->session->messenger, call->friend_number, &out_msg);
|
||||
send_message(log, call->session->tox, call->friend_number, &out_msg);
|
||||
|
||||
/* If peer changed capabilities during re-call they will
|
||||
* be handled accordingly during the next step
|
||||
@ -722,7 +790,7 @@ static bool try_handle_init(MSICall *call, const MSIMessage *msg)
|
||||
|
||||
case MSI_CALL_REQUESTED: // fall-through
|
||||
case MSI_CALL_REQUESTING: {
|
||||
LOGGER_WARNING(call->session->messenger->log, "Session: %p Invalid state on 'init'", (void *)call->session);
|
||||
LOGGER_WARNING(log, "Session: %p Invalid state on 'init'", (void *)call->session);
|
||||
call->error = MSI_E_INVALID_STATE;
|
||||
return false;
|
||||
}
|
||||
@ -730,26 +798,28 @@ static bool try_handle_init(MSICall *call, const MSIMessage *msg)
|
||||
|
||||
return true;
|
||||
}
|
||||
static void handle_init(MSICall *call, const MSIMessage *msg)
|
||||
|
||||
static void handle_init(const Logger *log, MSICall *call, const MSIMessage *msg)
|
||||
{
|
||||
assert(call != nullptr);
|
||||
LOGGER_DEBUG(call->session->messenger->log,
|
||||
LOGGER_DEBUG(log,
|
||||
"Session: %p Handling 'init' friend: %d", (void *)call->session, call->friend_number);
|
||||
|
||||
if (!try_handle_init(call, msg)) {
|
||||
send_error(call->session->messenger, call->friend_number, call->error);
|
||||
kill_call(call);
|
||||
if (!try_handle_init(log, call, msg)) {
|
||||
send_error(log, call->session->tox, call->friend_number, call->error);
|
||||
kill_call(log, call);
|
||||
}
|
||||
}
|
||||
static void handle_push(MSICall *call, const MSIMessage *msg)
|
||||
|
||||
static void handle_push(const Logger *log, MSICall *call, const MSIMessage *msg)
|
||||
{
|
||||
assert(call != nullptr);
|
||||
|
||||
LOGGER_DEBUG(call->session->messenger->log, "Session: %p Handling 'push' friend: %d", (void *)call->session,
|
||||
LOGGER_DEBUG(log, "Session: %p Handling 'push' friend: %d", (void *)call->session,
|
||||
call->friend_number);
|
||||
|
||||
if (!msg->capabilities.exists) {
|
||||
LOGGER_WARNING(call->session->messenger->log, "Session: %p Invalid capabilities on 'push'", (void *)call->session);
|
||||
LOGGER_WARNING(log, "Session: %p Invalid capabilities on 'push'", (void *)call->session);
|
||||
call->error = MSI_E_INVALID_MESSAGE;
|
||||
goto FAILURE;
|
||||
}
|
||||
@ -757,12 +827,11 @@ static void handle_push(MSICall *call, const MSIMessage *msg)
|
||||
switch (call->state) {
|
||||
case MSI_CALL_ACTIVE: {
|
||||
if (call->peer_capabilities != msg->capabilities.value) {
|
||||
/* Only act if capabilities changed */
|
||||
LOGGER_INFO(call->session->messenger->log, "Friend is changing capabilities to: %u", msg->capabilities.value);
|
||||
LOGGER_INFO(log, "Friend is changing capabilities to: %u", msg->capabilities.value);
|
||||
|
||||
call->peer_capabilities = msg->capabilities.value;
|
||||
|
||||
if (!invoke_callback(call, MSI_ON_CAPABILITIES)) {
|
||||
if (!invoke_callback(log, call, MSI_ON_CAPABILITIES)) {
|
||||
goto FAILURE;
|
||||
}
|
||||
}
|
||||
@ -771,13 +840,13 @@ static void handle_push(MSICall *call, const MSIMessage *msg)
|
||||
}
|
||||
|
||||
case MSI_CALL_REQUESTING: {
|
||||
LOGGER_INFO(call->session->messenger->log, "Friend answered our call");
|
||||
LOGGER_INFO(log, "Friend answered our call");
|
||||
|
||||
/* Call started */
|
||||
call->peer_capabilities = msg->capabilities.value;
|
||||
call->state = MSI_CALL_ACTIVE;
|
||||
|
||||
if (!invoke_callback(call, MSI_ON_START)) {
|
||||
if (!invoke_callback(log, call, MSI_ON_START)) {
|
||||
goto FAILURE;
|
||||
}
|
||||
|
||||
@ -786,8 +855,7 @@ static void handle_push(MSICall *call, const MSIMessage *msg)
|
||||
|
||||
case MSI_CALL_INACTIVE: // fall-through
|
||||
case MSI_CALL_REQUESTED: {
|
||||
/* Pushes during initialization state are ignored */
|
||||
LOGGER_WARNING(call->session->messenger->log, "Ignoring invalid push");
|
||||
LOGGER_WARNING(log, "Ignoring invalid push");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -795,76 +863,102 @@ static void handle_push(MSICall *call, const MSIMessage *msg)
|
||||
return;
|
||||
|
||||
FAILURE:
|
||||
send_error(call->session->messenger, call->friend_number, call->error);
|
||||
kill_call(call);
|
||||
send_error(log, call->session->tox, call->friend_number, call->error);
|
||||
kill_call(log, call);
|
||||
}
|
||||
static void handle_pop(MSICall *call, const MSIMessage *msg)
|
||||
|
||||
static void handle_pop(const Logger *log, MSICall *call, const MSIMessage *msg)
|
||||
{
|
||||
assert(call != nullptr);
|
||||
|
||||
LOGGER_DEBUG(call->session->messenger->log, "Session: %p Handling 'pop', friend id: %d", (void *)call->session,
|
||||
LOGGER_DEBUG(log, "Session: %p Handling 'pop', friend id: %d", (void *)call->session,
|
||||
call->friend_number);
|
||||
|
||||
/* callback errors are ignored */
|
||||
|
||||
if (msg->error.exists) {
|
||||
LOGGER_WARNING(call->session->messenger->log, "Friend detected an error: %d", msg->error.value);
|
||||
LOGGER_WARNING(log, "Friend detected an error: %d", msg->error.value);
|
||||
call->error = msg->error.value;
|
||||
invoke_callback(call, MSI_ON_ERROR);
|
||||
invoke_callback(log, call, MSI_ON_ERROR);
|
||||
} else {
|
||||
switch (call->state) {
|
||||
case MSI_CALL_INACTIVE: {
|
||||
LOGGER_FATAL(call->session->messenger->log, "Handling what should be impossible case");
|
||||
LOGGER_FATAL(log, "Handling what should be impossible case");
|
||||
break;
|
||||
}
|
||||
|
||||
case MSI_CALL_ACTIVE: {
|
||||
/* Hangup */
|
||||
LOGGER_INFO(call->session->messenger->log, "Friend hung up on us");
|
||||
invoke_callback(call, MSI_ON_END);
|
||||
LOGGER_INFO(log, "Friend hung up on us");
|
||||
invoke_callback(log, call, MSI_ON_END);
|
||||
break;
|
||||
}
|
||||
|
||||
case MSI_CALL_REQUESTING: {
|
||||
/* Reject */
|
||||
LOGGER_INFO(call->session->messenger->log, "Friend rejected our call");
|
||||
invoke_callback(call, MSI_ON_END);
|
||||
LOGGER_INFO(log, "Friend rejected our call");
|
||||
invoke_callback(log, call, MSI_ON_END);
|
||||
break;
|
||||
}
|
||||
|
||||
case MSI_CALL_REQUESTED: {
|
||||
/* Cancel */
|
||||
LOGGER_INFO(call->session->messenger->log, "Friend canceled call invite");
|
||||
invoke_callback(call, MSI_ON_END);
|
||||
LOGGER_INFO(log, "Friend canceled call invite");
|
||||
invoke_callback(log, call, MSI_ON_END);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kill_call(call);
|
||||
kill_call(log, call);
|
||||
}
|
||||
static void handle_msi_packet(Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *user_data)
|
||||
|
||||
static void handle_msi_packet(Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length,
|
||||
void *user_data)
|
||||
{
|
||||
MSISession *session = (MSISession *)user_data;
|
||||
const ToxAV *toxav = (ToxAV *)tox_get_av_object(tox);
|
||||
|
||||
LOGGER_DEBUG(m->log, "Got msi message");
|
||||
|
||||
MSIMessage msg;
|
||||
|
||||
if (msg_parse_in(m->log, &msg, data, length) == -1) {
|
||||
LOGGER_WARNING(m->log, "Error parsing message");
|
||||
send_error(m, friend_number, MSI_E_INVALID_MESSAGE);
|
||||
if (toxav == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER_DEBUG(m->log, "Successfully parsed message");
|
||||
const Logger *log = toxav_get_logger(toxav);
|
||||
|
||||
if (length < 2) {
|
||||
LOGGER_ERROR(log, "MSI packet is less than 2 bytes in size");
|
||||
// we need more than the ID byte for MSI messages
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t payload_length = (uint16_t)(length - 1);
|
||||
|
||||
// Zoff: do not show the first byte, its always "PACKET_ID_MSI"
|
||||
const uint8_t *data_strip_id_byte = data + 1;
|
||||
|
||||
LOGGER_DEBUG(log, "Got msi message");
|
||||
|
||||
MSISession *session = tox_av_msi_get(toxav);
|
||||
|
||||
if (session == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
MSIMessage msg;
|
||||
|
||||
if (msg_parse_in(log, &msg, data_strip_id_byte, payload_length) == -1) {
|
||||
LOGGER_WARNING(log, "Error parsing message");
|
||||
send_error(log, tox, friend_number, MSI_E_INVALID_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER_DEBUG(log, "Successfully parsed message");
|
||||
|
||||
pthread_mutex_lock(session->mutex);
|
||||
MSICall *call = get_call(session, friend_number);
|
||||
|
||||
if (call == nullptr) {
|
||||
if (msg.request.value != REQU_INIT) {
|
||||
send_error(m, friend_number, MSI_E_STRAY_MESSAGE);
|
||||
send_error(log, tox, friend_number, MSI_E_STRAY_MESSAGE);
|
||||
pthread_mutex_unlock(session->mutex);
|
||||
return;
|
||||
}
|
||||
@ -872,7 +966,7 @@ static void handle_msi_packet(Messenger *m, uint32_t friend_number, const uint8_
|
||||
call = new_call(session, friend_number);
|
||||
|
||||
if (call == nullptr) {
|
||||
send_error(m, friend_number, MSI_E_SYSTEM);
|
||||
send_error(log, tox, friend_number, MSI_E_SYSTEM);
|
||||
pthread_mutex_unlock(session->mutex);
|
||||
return;
|
||||
}
|
||||
@ -880,17 +974,17 @@ static void handle_msi_packet(Messenger *m, uint32_t friend_number, const uint8_
|
||||
|
||||
switch (msg.request.value) {
|
||||
case REQU_INIT: {
|
||||
handle_init(call, &msg);
|
||||
handle_init(log, call, &msg);
|
||||
break;
|
||||
}
|
||||
|
||||
case REQU_PUSH: {
|
||||
handle_push(call, &msg);
|
||||
handle_push(log, call, &msg);
|
||||
break;
|
||||
}
|
||||
|
||||
case REQU_POP: {
|
||||
handle_pop(call, &msg); /* always kills the call */
|
||||
handle_pop(log, call, &msg); /* always kills the call */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
37
toxav/msi.h
37
toxav/msi.h
@ -11,7 +11,6 @@
|
||||
#include "audio.h"
|
||||
#include "video.h"
|
||||
|
||||
#include "../toxcore/Messenger.h"
|
||||
#include "../toxcore/logger.h"
|
||||
|
||||
/**
|
||||
@ -42,22 +41,22 @@ typedef enum MSICapabilities {
|
||||
* Call state identifiers.
|
||||
*/
|
||||
typedef enum MSICallState {
|
||||
MSI_CALL_INACTIVE, /* Default */
|
||||
MSI_CALL_ACTIVE,
|
||||
MSI_CALL_REQUESTING, /* when sending call invite */
|
||||
MSI_CALL_REQUESTED, /* when getting call invite */
|
||||
MSI_CALL_INACTIVE = 0, /* Default */
|
||||
MSI_CALL_ACTIVE = 1,
|
||||
MSI_CALL_REQUESTING = 2, /* when sending call invite */
|
||||
MSI_CALL_REQUESTED = 3, /* when getting call invite */
|
||||
} MSICallState;
|
||||
|
||||
/**
|
||||
* Callbacks ids that handle the states
|
||||
*/
|
||||
typedef enum MSICallbackID {
|
||||
MSI_ON_INVITE, /* Incoming call */
|
||||
MSI_ON_START, /* Call (RTP transmission) started */
|
||||
MSI_ON_END, /* Call that was active ended */
|
||||
MSI_ON_ERROR, /* On protocol error */
|
||||
MSI_ON_PEERTIMEOUT, /* Peer timed out; stop the call */
|
||||
MSI_ON_CAPABILITIES, /* Peer requested capabilities change */
|
||||
MSI_ON_INVITE = 0, /* Incoming call */
|
||||
MSI_ON_START = 1, /* Call (RTP transmission) started */
|
||||
MSI_ON_END = 2, /* Call that was active ended */
|
||||
MSI_ON_ERROR = 3, /* On protocol error */
|
||||
MSI_ON_PEERTIMEOUT = 4, /* Peer timed out; stop the call */
|
||||
MSI_ON_CAPABILITIES = 5, /* Peer requested capabilities change */
|
||||
} MSICallbackID;
|
||||
|
||||
/**
|
||||
@ -96,7 +95,7 @@ typedef struct MSISession {
|
||||
uint32_t calls_head;
|
||||
|
||||
void *av;
|
||||
Messenger *messenger;
|
||||
Tox *tox;
|
||||
|
||||
pthread_mutex_t mutex[1];
|
||||
|
||||
@ -111,11 +110,11 @@ typedef struct MSISession {
|
||||
/**
|
||||
* Start the control session.
|
||||
*/
|
||||
MSISession *msi_new(Messenger *m);
|
||||
MSISession *msi_new(const Logger *log, Tox *tox);
|
||||
/**
|
||||
* Terminate control session. NOTE: all calls will be freed
|
||||
*/
|
||||
int msi_kill(MSISession *session, const Logger *log);
|
||||
int msi_kill(const Logger *log, Tox *tox, MSISession *session);
|
||||
/**
|
||||
* Callback setters.
|
||||
*/
|
||||
@ -128,18 +127,20 @@ void msi_callback_capabilities(MSISession *session, msi_action_cb *callback);
|
||||
/**
|
||||
* Send invite request to friend_number.
|
||||
*/
|
||||
int msi_invite(MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities);
|
||||
int msi_invite(const Logger *log, MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities);
|
||||
/**
|
||||
* Hangup call. NOTE: `call` will be freed
|
||||
*/
|
||||
int msi_hangup(MSICall *call);
|
||||
int msi_hangup(const Logger *log, MSICall *call);
|
||||
/**
|
||||
* Answer call request.
|
||||
*/
|
||||
int msi_answer(MSICall *call, uint8_t capabilities);
|
||||
int msi_answer(const Logger *log, MSICall *call, uint8_t capabilities);
|
||||
/**
|
||||
* Change capabilities of the call.
|
||||
*/
|
||||
int msi_change_capabilities(MSICall *call, uint8_t capabilities);
|
||||
int msi_change_capabilities(const Logger *log, MSICall *call, uint8_t capabilities);
|
||||
|
||||
bool check_peer_offline_status(const Logger *log, const Tox *tox, MSISession *session, uint32_t friend_number);
|
||||
|
||||
#endif /* C_TOXCORE_TOXAV_MSI_H */
|
||||
|
395
toxav/rtp.c
395
toxav/rtp.c
@ -9,12 +9,16 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bwcontroller.h"
|
||||
#include <sodium.h>
|
||||
|
||||
#include "bwcontroller.h"
|
||||
#include "toxav_hacks.h"
|
||||
|
||||
#include "../toxcore/Messenger.h"
|
||||
#include "../toxcore/ccompat.h"
|
||||
#include "../toxcore/logger.h"
|
||||
#include "../toxcore/mono_time.h"
|
||||
#include "../toxcore/net_crypto.h"
|
||||
#include "../toxcore/tox_private.h"
|
||||
#include "../toxcore/util.h"
|
||||
|
||||
/**
|
||||
@ -23,30 +27,15 @@
|
||||
*/
|
||||
#define VIDEO_KEEP_KEYFRAME_IN_BUFFER_FOR_MS 15
|
||||
|
||||
/**
|
||||
* return -1 on failure, 0 on success
|
||||
*
|
||||
*/
|
||||
static int rtp_send_custom_lossy_packet(Tox *tox, int32_t friendnumber, const uint8_t *data, uint32_t length)
|
||||
{
|
||||
Tox_Err_Friend_Custom_Packet error;
|
||||
tox_friend_send_lossy_packet(tox, friendnumber, data, (size_t)length, &error);
|
||||
|
||||
if (error == TOX_ERR_FRIEND_CUSTOM_PACKET_OK) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// allocate_len is NOT including header!
|
||||
static struct RTPMessage *new_message(const struct RTPHeader *header, size_t allocate_len, const uint8_t *data,
|
||||
uint16_t data_length)
|
||||
static struct RTPMessage *new_message(const Logger *log, const struct RTPHeader *header, size_t allocate_len,
|
||||
const uint8_t *data, uint16_t data_length)
|
||||
{
|
||||
assert(allocate_len >= data_length);
|
||||
struct RTPMessage *msg = (struct RTPMessage *)calloc(1, sizeof(struct RTPMessage) + allocate_len);
|
||||
|
||||
if (msg == nullptr) {
|
||||
LOGGER_DEBUG(log, "Could not allocate RTPMessage buffer");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -241,7 +230,7 @@ static struct RTPMessage *process_frame(const Logger *log, struct RTPWorkBufferL
|
||||
}
|
||||
|
||||
/**
|
||||
* @param log A logger.
|
||||
* @param log A pointer to the Logger object.
|
||||
* @param wkbl The list of in-progress frames, i.e. all the slots.
|
||||
* @param slot_id The slot we want to fill the data into.
|
||||
* @param is_keyframe Whether the data is part of a key frame.
|
||||
@ -309,7 +298,7 @@ static bool fill_data_into_slot(const Logger *log, struct RTPWorkBufferList *wkb
|
||||
return slot->received_len == header->data_length_full;
|
||||
}
|
||||
|
||||
static void update_bwc_values(const Logger *log, RTPSession *session, const struct RTPMessage *msg)
|
||||
static void update_bwc_values(RTPSession *session, const struct RTPMessage *msg)
|
||||
{
|
||||
if (session->first_packets_counter < DISMISS_FIRST_LOST_VIDEO_PACKET_COUNT) {
|
||||
++session->first_packets_counter;
|
||||
@ -319,7 +308,7 @@ static void update_bwc_values(const Logger *log, RTPSession *session, const stru
|
||||
bwc_add_recv(session->bwc, data_length_full);
|
||||
|
||||
if (received_length_full < data_length_full) {
|
||||
LOGGER_DEBUG(log, "BWC: full length=%u received length=%d", data_length_full, received_length_full);
|
||||
LOGGER_DEBUG(session->log, "BWC: full length=%u received length=%d", data_length_full, received_length_full);
|
||||
bwc_add_lost(session->bwc, data_length_full - received_length_full);
|
||||
}
|
||||
}
|
||||
@ -347,22 +336,16 @@ static void update_bwc_values(const Logger *log, RTPSession *session, const stru
|
||||
* @retval -1 on error.
|
||||
* @retval 0 on success.
|
||||
*/
|
||||
static int handle_video_packet(RTPSession *session, const struct RTPHeader *header,
|
||||
const uint8_t *incoming_data, uint16_t incoming_data_length, const Logger *log)
|
||||
static int handle_video_packet(const Logger *log, RTPSession *session, const struct RTPHeader *header,
|
||||
const uint8_t *incoming_data, uint16_t incoming_data_length)
|
||||
{
|
||||
// Full frame length in bytes. The frame may be split into multiple packets,
|
||||
// but this value is the complete assembled frame size.
|
||||
const uint32_t full_frame_length = header->data_length_full;
|
||||
|
||||
// Current offset in the frame. If this is the first packet of a multipart
|
||||
// frame or it's not a multipart frame, then this value is 0.
|
||||
const uint32_t offset = header->offset_full; // without header
|
||||
|
||||
// The sender tells us whether this is a key frame.
|
||||
const bool is_keyframe = (header->flags & RTP_KEY_FRAME) != 0;
|
||||
|
||||
LOGGER_DEBUG(log, "-- handle_video_packet -- full lens=%u len=%u offset=%u is_keyframe=%s",
|
||||
(unsigned)incoming_data_length, (unsigned)full_frame_length, (unsigned)offset, is_keyframe ? "K" : ".");
|
||||
LOGGER_DEBUG(log, "wkbl->next_free_entry:003=%d", session->work_buffer_list->next_free_entry);
|
||||
|
||||
const bool is_multipart = full_frame_length != incoming_data_length;
|
||||
@ -387,10 +370,13 @@ static int handle_video_packet(RTPSession *session, const struct RTPHeader *head
|
||||
// get_slot just told us it's full, so process_frame must return non-null.
|
||||
assert(m_new != nullptr);
|
||||
|
||||
LOGGER_DEBUG(log, "-- handle_video_packet -- CALLBACK-001a b0=%d b1=%d", (int)m_new->data[0], (int)m_new->data[1]);
|
||||
update_bwc_values(log, session, m_new);
|
||||
LOGGER_DEBUG(log, "-- handle_video_packet -- CALLBACK-001a b0=%d b1=%d", (int)m_new->data[0],
|
||||
(int)m_new->data[1]);
|
||||
update_bwc_values(session, m_new);
|
||||
// Pass ownership of m_new to the callback.
|
||||
session->mcb(session->m->mono_time, session->cs, m_new);
|
||||
Mono_Time *mt = toxav_get_av_mono_time(session->toxav);
|
||||
assert(mt != nullptr);
|
||||
session->mcb(mt, session->cs, m_new);
|
||||
// Now we no longer own m_new.
|
||||
m_new = nullptr;
|
||||
|
||||
@ -425,9 +411,12 @@ static int handle_video_packet(RTPSession *session, const struct RTPHeader *head
|
||||
struct RTPMessage *m_new = process_frame(log, session->work_buffer_list, slot_id);
|
||||
|
||||
if (m_new != nullptr) {
|
||||
LOGGER_DEBUG(log, "-- handle_video_packet -- CALLBACK-003a b0=%d b1=%d", (int)m_new->data[0], (int)m_new->data[1]);
|
||||
update_bwc_values(log, session, m_new);
|
||||
session->mcb(session->m->mono_time, session->cs, m_new);
|
||||
LOGGER_DEBUG(log, "-- handle_video_packet -- CALLBACK-003a b0=%d b1=%d", (int)m_new->data[0],
|
||||
(int)m_new->data[1]);
|
||||
update_bwc_values(session, m_new);
|
||||
Mono_Time *mt = toxav_get_av_mono_time(session->toxav);
|
||||
assert(mt != nullptr);
|
||||
session->mcb(mt, session->cs, m_new);
|
||||
|
||||
m_new = nullptr;
|
||||
}
|
||||
@ -436,80 +425,113 @@ static int handle_video_packet(RTPSession *session, const struct RTPHeader *head
|
||||
}
|
||||
|
||||
/**
|
||||
* @retval -1 on error.
|
||||
* @retval 0 on success.
|
||||
* receive custom lossypackets and process them. they can be incoming audio or video packets
|
||||
*/
|
||||
static int handle_rtp_packet(Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object)
|
||||
void handle_rtp_packet(Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length, void *user_data)
|
||||
{
|
||||
RTPSession *session = (RTPSession *)object;
|
||||
ToxAV *toxav = (ToxAV *)tox_get_av_object(tox);
|
||||
|
||||
if (session == nullptr || length < RTP_HEADER_SIZE + 1) {
|
||||
LOGGER_WARNING(m->log, "No session or invalid length of received buffer!");
|
||||
return -1;
|
||||
if (toxav == nullptr) {
|
||||
// LOGGER_WARNING(log, "ToxAV is NULL!");
|
||||
return;
|
||||
}
|
||||
|
||||
const Logger *log = toxav_get_logger(toxav);
|
||||
|
||||
if (length < RTP_HEADER_SIZE + 1) {
|
||||
LOGGER_WARNING(log, "Invalid length of received buffer!");
|
||||
return;
|
||||
}
|
||||
|
||||
ToxAVCall *call = call_get(toxav, friend_number);
|
||||
|
||||
if (call == nullptr) {
|
||||
LOGGER_WARNING(log, "ToxAVCall is NULL!");
|
||||
return;
|
||||
}
|
||||
|
||||
RTPSession *session = rtp_session_get(call, data[0]);
|
||||
|
||||
if (session == nullptr) {
|
||||
LOGGER_WARNING(log, "No session!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!session->rtp_receive_active) {
|
||||
LOGGER_WARNING(log, "receiving not allowed!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the packet type.
|
||||
const uint8_t packet_type = data[0];
|
||||
++data;
|
||||
--length;
|
||||
const uint8_t *payload = &data[1];
|
||||
// TODO(Zoff): is this ok?
|
||||
const uint16_t payload_size = (uint16_t)length - 1;
|
||||
|
||||
// Unpack the header.
|
||||
struct RTPHeader header;
|
||||
rtp_header_unpack(data, &header);
|
||||
rtp_header_unpack(payload, &header);
|
||||
|
||||
if (header.pt != packet_type % 128) {
|
||||
LOGGER_WARNING(m->log, "RTPHeader packet type and Tox protocol packet type did not agree: %d != %d",
|
||||
LOGGER_WARNING(log, "RTPHeader packet type and Tox protocol packet type did not agree: %d != %d",
|
||||
header.pt, packet_type % 128);
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (header.pt != session->payload_type % 128) {
|
||||
LOGGER_WARNING(m->log, "RTPHeader packet type does not match this session's payload type: %d != %d",
|
||||
LOGGER_WARNING(log, "RTPHeader packet type does not match this session's payload type: %d != %d",
|
||||
header.pt, session->payload_type % 128);
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((header.flags & RTP_LARGE_FRAME) != 0 && header.offset_full >= header.data_length_full) {
|
||||
LOGGER_ERROR(m->log, "Invalid video packet: frame offset (%u) >= full frame length (%u)",
|
||||
LOGGER_ERROR(log, "Invalid video packet: frame offset (%u) >= full frame length (%u)",
|
||||
(unsigned)header.offset_full, (unsigned)header.data_length_full);
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (header.offset_lower >= header.data_length_lower) {
|
||||
LOGGER_ERROR(m->log, "Invalid old protocol video packet: frame offset (%u) >= full frame length (%u)",
|
||||
LOGGER_ERROR(log, "Invalid old protocol video packet: frame offset (%u) >= full frame length (%u)",
|
||||
(unsigned)header.offset_lower, (unsigned)header.data_length_lower);
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER_DEBUG(m->log, "header.pt %d, video %d", (uint8_t)header.pt, RTP_TYPE_VIDEO % 128);
|
||||
LOGGER_DEBUG(log, "header.pt %d, video %d", (uint8_t)header.pt, RTP_TYPE_VIDEO % 128);
|
||||
|
||||
// The sender uses the new large-frame capable protocol and is sending a
|
||||
// video packet.
|
||||
if ((header.flags & RTP_LARGE_FRAME) != 0 && header.pt == (RTP_TYPE_VIDEO % 128)) {
|
||||
return handle_video_packet(session, &header, data + RTP_HEADER_SIZE, length - RTP_HEADER_SIZE, m->log);
|
||||
handle_video_packet(log, session, &header, &payload[RTP_HEADER_SIZE], payload_size - RTP_HEADER_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
// everything below here is for the old 16 bit protocol ------------------
|
||||
|
||||
if (header.data_length_lower == length - RTP_HEADER_SIZE) {
|
||||
if (header.data_length_lower == payload_size - RTP_HEADER_SIZE) {
|
||||
/* The message is sent in single part */
|
||||
|
||||
/* Message is not late; pick up the latest parameters */
|
||||
session->rsequnum = header.sequnum;
|
||||
session->rtimestamp = header.timestamp;
|
||||
bwc_add_recv(session->bwc, length);
|
||||
bwc_add_recv(session->bwc, payload_size);
|
||||
|
||||
/* Invoke processing of active multiparted message */
|
||||
if (session->mp != nullptr) {
|
||||
session->mcb(session->m->mono_time, session->cs, session->mp);
|
||||
Mono_Time *mt = toxav_get_av_mono_time(session->toxav);
|
||||
assert(mt != nullptr);
|
||||
session->mcb(mt, session->cs, session->mp);
|
||||
session->mp = nullptr;
|
||||
}
|
||||
|
||||
/* The message came in the allowed time;
|
||||
*/
|
||||
|
||||
return session->mcb(session->m->mono_time, session->cs, new_message(&header, length - RTP_HEADER_SIZE,
|
||||
data + RTP_HEADER_SIZE, length - RTP_HEADER_SIZE));
|
||||
session->mp = new_message(log, &header, payload_size - RTP_HEADER_SIZE, &payload[RTP_HEADER_SIZE], payload_size - RTP_HEADER_SIZE);
|
||||
Mono_Time *mt = toxav_get_av_mono_time(session->toxav);
|
||||
assert(mt != nullptr);
|
||||
session->mcb(mt, session->cs, session->mp);
|
||||
session->mp = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
/* The message is sent in multiple parts */
|
||||
@ -527,24 +549,26 @@ static int handle_rtp_packet(Messenger *m, uint32_t friend_number, const uint8_t
|
||||
/* First case */
|
||||
|
||||
/* Make sure we have enough allocated memory */
|
||||
if (session->mp->header.data_length_lower - session->mp->len < length - RTP_HEADER_SIZE ||
|
||||
if (session->mp->header.data_length_lower - session->mp->len < payload_size - RTP_HEADER_SIZE ||
|
||||
session->mp->header.data_length_lower <= header.offset_lower) {
|
||||
/* There happened to be some corruption on the stream;
|
||||
* continue wihtout this part
|
||||
*/
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(session->mp->data + header.offset_lower, data + RTP_HEADER_SIZE,
|
||||
length - RTP_HEADER_SIZE);
|
||||
session->mp->len += length - RTP_HEADER_SIZE;
|
||||
bwc_add_recv(session->bwc, length);
|
||||
memcpy(session->mp->data + header.offset_lower, &payload[RTP_HEADER_SIZE],
|
||||
payload_size - RTP_HEADER_SIZE);
|
||||
session->mp->len += payload_size - RTP_HEADER_SIZE;
|
||||
bwc_add_recv(session->bwc, payload_size);
|
||||
|
||||
if (session->mp->len == session->mp->header.data_length_lower) {
|
||||
/* Received a full message; now push it for the further
|
||||
* processing.
|
||||
*/
|
||||
session->mcb(session->m->mono_time, session->cs, session->mp);
|
||||
Mono_Time *mt = toxav_get_av_mono_time(session->toxav);
|
||||
assert(mt != nullptr);
|
||||
session->mcb(mt, session->cs, session->mp);
|
||||
session->mp = nullptr;
|
||||
}
|
||||
} else {
|
||||
@ -553,17 +577,19 @@ static int handle_rtp_packet(Messenger *m, uint32_t friend_number, const uint8_t
|
||||
/* The received message part is from the old message;
|
||||
* discard it.
|
||||
*/
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Push the previous message for processing */
|
||||
session->mcb(session->m->mono_time, session->cs, session->mp);
|
||||
Mono_Time *mt = toxav_get_av_mono_time(session->toxav);
|
||||
assert(mt != nullptr);
|
||||
session->mcb(mt, session->cs, session->mp);
|
||||
|
||||
session->mp = nullptr;
|
||||
goto NEW_MULTIPARTED;
|
||||
}
|
||||
} else {
|
||||
/* In this case threat the message as if it was received in order
|
||||
/* In this case treat the message as if it was received in order
|
||||
*/
|
||||
/* This is also a point for new multiparted messages */
|
||||
NEW_MULTIPARTED:
|
||||
@ -571,21 +597,21 @@ NEW_MULTIPARTED:
|
||||
/* Message is not late; pick up the latest parameters */
|
||||
session->rsequnum = header.sequnum;
|
||||
session->rtimestamp = header.timestamp;
|
||||
bwc_add_recv(session->bwc, length);
|
||||
bwc_add_recv(session->bwc, payload_size);
|
||||
|
||||
/* Store message.
|
||||
*/
|
||||
session->mp = new_message(&header, header.data_length_lower, data + RTP_HEADER_SIZE, length - RTP_HEADER_SIZE);
|
||||
session->mp = new_message(log, &header, header.data_length_lower, &payload[RTP_HEADER_SIZE], payload_size - RTP_HEADER_SIZE);
|
||||
|
||||
if (session->mp != nullptr) {
|
||||
memmove(session->mp->data + header.offset_lower, session->mp->data, session->mp->len);
|
||||
} else {
|
||||
LOGGER_WARNING(m->log, "new_message() returned a null pointer");
|
||||
return -1;
|
||||
LOGGER_WARNING(log, "new_message() returned a null pointer");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t rtp_header_pack(uint8_t *const rdata, const struct RTPHeader *header)
|
||||
@ -647,24 +673,29 @@ size_t rtp_header_unpack(const uint8_t *data, struct RTPHeader *header)
|
||||
return p - data;
|
||||
}
|
||||
|
||||
RTPSession *rtp_new(int payload_type, Messenger *m, Tox *tox, uint32_t friendnumber,
|
||||
static uint32_t rtp_random_u32(void)
|
||||
{
|
||||
// HINT: uses libsodium function
|
||||
return randombytes_random();
|
||||
}
|
||||
|
||||
RTPSession *rtp_new(const Logger *log, int payload_type, Tox *tox, ToxAV *toxav, uint32_t friendnumber,
|
||||
BWController *bwc, void *cs, rtp_m_cb *mcb)
|
||||
{
|
||||
assert(mcb != nullptr);
|
||||
assert(cs != nullptr);
|
||||
assert(m != nullptr);
|
||||
|
||||
RTPSession *session = (RTPSession *)calloc(1, sizeof(RTPSession));
|
||||
|
||||
if (session == nullptr) {
|
||||
LOGGER_WARNING(m->log, "Alloc failed! Program might misbehave!");
|
||||
LOGGER_WARNING(log, "Alloc failed! Program might misbehave!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
session->work_buffer_list = (struct RTPWorkBufferList *)calloc(1, sizeof(struct RTPWorkBufferList));
|
||||
|
||||
if (session->work_buffer_list == nullptr) {
|
||||
LOGGER_ERROR(m->log, "out of memory while allocating work buffer list");
|
||||
LOGGER_ERROR(log, "out of memory while allocating work buffer list");
|
||||
free(session);
|
||||
return nullptr;
|
||||
}
|
||||
@ -672,11 +703,12 @@ RTPSession *rtp_new(int payload_type, Messenger *m, Tox *tox, uint32_t friendnum
|
||||
// First entry is free.
|
||||
session->work_buffer_list->next_free_entry = 0;
|
||||
|
||||
session->ssrc = payload_type == RTP_TYPE_VIDEO ? 0 : random_u32(m->rng);
|
||||
session->ssrc = payload_type == RTP_TYPE_VIDEO ? 0 : rtp_random_u32(); // Zoff: what is this??
|
||||
session->payload_type = payload_type;
|
||||
session->m = m;
|
||||
session->tox = tox;
|
||||
session->toxav = toxav;
|
||||
session->friend_number = friendnumber;
|
||||
session->rtp_receive_active = true;
|
||||
|
||||
// set NULL just in case
|
||||
session->mp = nullptr;
|
||||
@ -687,26 +719,18 @@ RTPSession *rtp_new(int payload_type, Messenger *m, Tox *tox, uint32_t friendnum
|
||||
session->cs = cs;
|
||||
session->mcb = mcb;
|
||||
|
||||
if (-1 == rtp_allow_receiving(session)) {
|
||||
LOGGER_WARNING(m->log, "Failed to start rtp receiving mode");
|
||||
free(session->work_buffer_list);
|
||||
free(session);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
void rtp_kill(RTPSession *session)
|
||||
void rtp_kill(const Logger *log, RTPSession *session)
|
||||
{
|
||||
if (session == nullptr) {
|
||||
LOGGER_WARNING(log, "No session");
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER_DEBUG(session->m->log, "Terminated RTP session: %p", (void *)session);
|
||||
rtp_stop_receiving(session);
|
||||
|
||||
LOGGER_DEBUG(session->m->log, "Terminated RTP session V3 work_buffer_list->next_free_entry: %d",
|
||||
LOGGER_DEBUG(log, "Terminated RTP session: %p", (void *)session);
|
||||
LOGGER_DEBUG(log, "Terminated RTP session V3 work_buffer_list->next_free_entry: %d",
|
||||
(int)session->work_buffer_list->next_free_entry);
|
||||
|
||||
for (int8_t i = 0; i < session->work_buffer_list->next_free_entry; ++i) {
|
||||
@ -716,36 +740,95 @@ void rtp_kill(RTPSession *session)
|
||||
free(session);
|
||||
}
|
||||
|
||||
int rtp_allow_receiving(RTPSession *session)
|
||||
void rtp_allow_receiving_mark(RTPSession *session)
|
||||
{
|
||||
if (session == nullptr) {
|
||||
return -1;
|
||||
if (session != nullptr) {
|
||||
session->rtp_receive_active = true;
|
||||
}
|
||||
|
||||
if (m_callback_rtp_packet(session->m, session->friend_number, session->payload_type,
|
||||
handle_rtp_packet, session) == -1) {
|
||||
LOGGER_WARNING(session->m->log, "Failed to register rtp receive handler");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOGGER_DEBUG(session->m->log, "Started receiving on session: %p", (void *)session);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtp_stop_receiving(RTPSession *session)
|
||||
void rtp_stop_receiving_mark(RTPSession *session)
|
||||
{
|
||||
if (session == nullptr) {
|
||||
return -1;
|
||||
if (session != nullptr) {
|
||||
session->rtp_receive_active = false;
|
||||
}
|
||||
}
|
||||
|
||||
void rtp_allow_receiving(Tox *tox)
|
||||
{
|
||||
// register callback
|
||||
tox_callback_friend_lossy_packet_per_pktid(tox, handle_rtp_packet, RTP_TYPE_AUDIO);
|
||||
tox_callback_friend_lossy_packet_per_pktid(tox, handle_rtp_packet, RTP_TYPE_VIDEO);
|
||||
}
|
||||
|
||||
void rtp_stop_receiving(Tox *tox)
|
||||
{
|
||||
// UN-register callback
|
||||
tox_callback_friend_lossy_packet_per_pktid(tox, nullptr, RTP_TYPE_AUDIO);
|
||||
tox_callback_friend_lossy_packet_per_pktid(tox, nullptr, RTP_TYPE_VIDEO);
|
||||
}
|
||||
|
||||
static void rtp_send_piece(const Logger *log, Tox *tox, uint32_t friend_number, const struct RTPHeader *header,
|
||||
const uint8_t *data, uint8_t *rdata, uint16_t length)
|
||||
{
|
||||
rtp_header_pack(rdata + 1, header);
|
||||
memcpy(rdata + 1 + RTP_HEADER_SIZE, data, length);
|
||||
|
||||
Tox_Err_Friend_Custom_Packet error;
|
||||
tox_friend_send_lossy_packet(tox, friend_number,
|
||||
rdata, length + RTP_HEADER_SIZE + 1, &error);
|
||||
|
||||
if (error != TOX_ERR_FRIEND_CUSTOM_PACKET_OK) {
|
||||
char *netstrerror = net_new_strerror(net_error());
|
||||
LOGGER_WARNING(log, "RTP send failed (len: %d)! tox error: %d, net error: %s",
|
||||
length + RTP_HEADER_SIZE + 1, error, netstrerror);
|
||||
net_kill_strerror(netstrerror);
|
||||
}
|
||||
}
|
||||
|
||||
static struct RTPHeader rtp_default_header(const RTPSession *session, uint32_t length, bool is_keyframe)
|
||||
{
|
||||
uint16_t length_safe = (uint16_t)length;
|
||||
|
||||
if (length > UINT16_MAX) {
|
||||
length_safe = UINT16_MAX;
|
||||
}
|
||||
|
||||
m_callback_rtp_packet(session->m, session->friend_number, session->payload_type, nullptr, nullptr);
|
||||
struct RTPHeader header = {0};
|
||||
|
||||
LOGGER_DEBUG(session->m->log, "Stopped receiving on session: %p", (void *)session);
|
||||
return 0;
|
||||
if (is_keyframe) {
|
||||
header.flags |= RTP_KEY_FRAME;
|
||||
}
|
||||
|
||||
if (session->payload_type == RTP_TYPE_VIDEO) {
|
||||
header.flags |= RTP_LARGE_FRAME;
|
||||
}
|
||||
|
||||
header.ve = 2; // this is unused in toxav
|
||||
header.pe = 0;
|
||||
header.xe = 0;
|
||||
header.cc = 0;
|
||||
header.ma = 0;
|
||||
header.pt = session->payload_type % 128;
|
||||
header.sequnum = session->sequnum;
|
||||
Mono_Time *mt = toxav_get_av_mono_time(session->toxav);
|
||||
if (mt != nullptr) {
|
||||
header.timestamp = current_time_monotonic(mt);
|
||||
} else {
|
||||
header.timestamp = 0;
|
||||
}
|
||||
header.ssrc = session->ssrc;
|
||||
header.offset_lower = 0;
|
||||
header.data_length_lower = length_safe;
|
||||
header.data_length_full = length; // without header
|
||||
header.offset_lower = 0;
|
||||
header.offset_full = 0;
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a frame of audio or video data, chunked in @ref RTPMessage instances.
|
||||
* @brief Send a frame of audio or video data, chunked in @ref RTPMessage instances.
|
||||
*
|
||||
* @param session The A/V session to send the data for.
|
||||
* @param data A byte array of length @p length.
|
||||
@ -753,77 +836,27 @@ int rtp_stop_receiving(RTPSession *session)
|
||||
* @param is_keyframe Whether this video frame is a key frame. If it is an
|
||||
* audio frame, this parameter is ignored.
|
||||
*/
|
||||
int rtp_send_data(RTPSession *session, const uint8_t *data, uint32_t length,
|
||||
bool is_keyframe, const Logger *log)
|
||||
int rtp_send_data(const Logger *log, RTPSession *session, const uint8_t *data, uint32_t length,
|
||||
bool is_keyframe)
|
||||
{
|
||||
if (session == nullptr) {
|
||||
LOGGER_ERROR(log, "No session!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct RTPHeader header = {0};
|
||||
|
||||
header.ve = 2; // this is unused in toxav
|
||||
|
||||
header.pe = 0;
|
||||
|
||||
header.xe = 0;
|
||||
|
||||
header.cc = 0;
|
||||
|
||||
header.ma = 0;
|
||||
|
||||
header.pt = session->payload_type % 128;
|
||||
|
||||
header.sequnum = session->sequnum;
|
||||
|
||||
header.timestamp = current_time_monotonic(session->m->mono_time);
|
||||
|
||||
header.ssrc = session->ssrc;
|
||||
|
||||
header.offset_lower = 0;
|
||||
|
||||
// here the highest bits gets stripped anyway, no need to do keyframe bit magic here!
|
||||
header.data_length_lower = length;
|
||||
|
||||
if (session->payload_type == RTP_TYPE_VIDEO) {
|
||||
header.flags = RTP_LARGE_FRAME;
|
||||
}
|
||||
|
||||
uint16_t length_safe = (uint16_t)length;
|
||||
|
||||
if (length > UINT16_MAX) {
|
||||
length_safe = UINT16_MAX;
|
||||
}
|
||||
|
||||
header.data_length_lower = length_safe;
|
||||
header.data_length_full = length; // without header
|
||||
header.offset_lower = 0;
|
||||
header.offset_full = 0;
|
||||
|
||||
if (is_keyframe) {
|
||||
header.flags |= RTP_KEY_FRAME;
|
||||
}
|
||||
|
||||
const uint16_t rdata_size = min_u32(length + RTP_HEADER_SIZE + 1, MAX_CRYPTO_DATA_SIZE);
|
||||
VLA(uint8_t, rdata, rdata_size);
|
||||
memset(rdata, 0, rdata_size);
|
||||
rdata[0] = session->payload_type; // packet id == payload_type
|
||||
|
||||
struct RTPHeader header = rtp_default_header(session, length, is_keyframe);
|
||||
|
||||
if (MAX_CRYPTO_DATA_SIZE > (length + RTP_HEADER_SIZE + 1)) {
|
||||
/*
|
||||
* The length is lesser than the maximum allowed length (including header)
|
||||
* Send the packet in single piece.
|
||||
*/
|
||||
rtp_header_pack(rdata + 1, &header);
|
||||
memcpy(rdata + 1 + RTP_HEADER_SIZE, data, length);
|
||||
|
||||
if (-1 == rtp_send_custom_lossy_packet(session->tox, session->friend_number, rdata, rdata_size)) {
|
||||
char *netstrerror = net_new_strerror(net_error());
|
||||
LOGGER_WARNING(session->m->log, "RTP send failed (len: %u)! net error: %s",
|
||||
rdata_size, netstrerror);
|
||||
net_kill_strerror(netstrerror);
|
||||
}
|
||||
assert(length < UINT16_MAX);
|
||||
rtp_send_piece(log, session->tox, session->friend_number, &header, data, rdata, length);
|
||||
} else {
|
||||
/*
|
||||
* The length is greater than the maximum allowed length (including header)
|
||||
@ -833,16 +866,7 @@ int rtp_send_data(RTPSession *session, const uint8_t *data, uint32_t length,
|
||||
uint16_t piece = MAX_CRYPTO_DATA_SIZE - (RTP_HEADER_SIZE + 1);
|
||||
|
||||
while ((length - sent) + RTP_HEADER_SIZE + 1 > MAX_CRYPTO_DATA_SIZE) {
|
||||
rtp_header_pack(rdata + 1, &header);
|
||||
memcpy(rdata + 1 + RTP_HEADER_SIZE, data + sent, piece);
|
||||
|
||||
if (-1 == rtp_send_custom_lossy_packet(session->tox, session->friend_number,
|
||||
rdata, piece + RTP_HEADER_SIZE + 1)) {
|
||||
char *netstrerror = net_new_strerror(net_error());
|
||||
LOGGER_WARNING(session->m->log, "RTP send failed (len: %d)! net error: %s",
|
||||
piece + RTP_HEADER_SIZE + 1, netstrerror);
|
||||
net_kill_strerror(netstrerror);
|
||||
}
|
||||
rtp_send_piece(log, session->tox, session->friend_number, &header, data + sent, rdata, piece);
|
||||
|
||||
sent += piece;
|
||||
header.offset_lower = sent;
|
||||
@ -853,16 +877,7 @@ int rtp_send_data(RTPSession *session, const uint8_t *data, uint32_t length,
|
||||
piece = length - sent;
|
||||
|
||||
if (piece != 0) {
|
||||
rtp_header_pack(rdata + 1, &header);
|
||||
memcpy(rdata + 1 + RTP_HEADER_SIZE, data + sent, piece);
|
||||
|
||||
if (-1 == rtp_send_custom_lossy_packet(session->tox, session->friend_number, rdata,
|
||||
piece + RTP_HEADER_SIZE + 1)) {
|
||||
char *netstrerror = net_new_strerror(net_error());
|
||||
LOGGER_WARNING(session->m->log, "RTP send failed (len: %d)! net error: %s",
|
||||
piece + RTP_HEADER_SIZE + 1, netstrerror);
|
||||
net_kill_strerror(netstrerror);
|
||||
}
|
||||
rtp_send_piece(log, session->tox, session->friend_number, &header, data + sent, rdata, piece);
|
||||
}
|
||||
}
|
||||
|
||||
|
30
toxav/rtp.h
30
toxav/rtp.h
@ -9,7 +9,6 @@
|
||||
|
||||
#include "bwcontroller.h"
|
||||
|
||||
#include "../toxcore/Messenger.h"
|
||||
#include "../toxcore/logger.h"
|
||||
#include "../toxcore/tox.h"
|
||||
|
||||
@ -36,6 +35,11 @@ typedef enum RTP_Type {
|
||||
RTP_TYPE_VIDEO = 193,
|
||||
} RTP_Type;
|
||||
|
||||
#ifndef TOXAV_DEFINED
|
||||
#define TOXAV_DEFINED
|
||||
typedef struct ToxAV ToxAV;
|
||||
#endif /* TOXAV_DEFINED */
|
||||
|
||||
/**
|
||||
* A bit mask (up to 64 bits) specifying features of the current frame affecting
|
||||
* the behaviour of the decoder.
|
||||
@ -157,14 +161,19 @@ typedef struct RTPSession {
|
||||
struct RTPMessage *mp; /* Expected parted message */
|
||||
struct RTPWorkBufferList *work_buffer_list;
|
||||
uint8_t first_packets_counter; /* dismiss first few lost video packets */
|
||||
Messenger *m;
|
||||
const Logger *log;
|
||||
Tox *tox;
|
||||
ToxAV *toxav;
|
||||
uint32_t friend_number;
|
||||
bool rtp_receive_active; /* if this is set to false then incoming rtp packets will not be processed by handle_rtp_packet() */
|
||||
BWController *bwc;
|
||||
void *cs;
|
||||
rtp_m_cb *mcb;
|
||||
} RTPSession;
|
||||
|
||||
|
||||
void handle_rtp_packet(Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length, void *user_data);
|
||||
|
||||
/**
|
||||
* Serialise an RTPHeader to bytes to be sent over the network.
|
||||
*
|
||||
@ -183,13 +192,16 @@ size_t rtp_header_pack(uint8_t *rdata, const struct RTPHeader *header);
|
||||
*/
|
||||
size_t rtp_header_unpack(const uint8_t *data, struct RTPHeader *header);
|
||||
|
||||
RTPSession *rtp_new(int payload_type, Messenger *m, Tox *tox, uint32_t friendnumber,
|
||||
RTPSession *rtp_new(const Logger *log, int payload_type, Tox *tox, ToxAV *toxav, uint32_t friendnumber,
|
||||
BWController *bwc, void *cs, rtp_m_cb *mcb);
|
||||
void rtp_kill(RTPSession *session);
|
||||
int rtp_allow_receiving(RTPSession *session);
|
||||
int rtp_stop_receiving(RTPSession *session);
|
||||
void rtp_kill(const Logger *log, RTPSession *session);
|
||||
void rtp_allow_receiving_mark(RTPSession *session);
|
||||
void rtp_stop_receiving_mark(RTPSession *session);
|
||||
void rtp_allow_receiving(Tox *tox);
|
||||
void rtp_stop_receiving(Tox *tox);
|
||||
|
||||
/**
|
||||
* Send a frame of audio or video data, chunked in @ref RTPMessage instances.
|
||||
* @brief Send a frame of audio or video data, chunked in @ref RTPMessage instances.
|
||||
*
|
||||
* @param session The A/V session to send the data for.
|
||||
* @param data A byte array of length @p length.
|
||||
@ -197,8 +209,8 @@ int rtp_stop_receiving(RTPSession *session);
|
||||
* @param is_keyframe Whether this video frame is a key frame. If it is an
|
||||
* audio frame, this parameter is ignored.
|
||||
*/
|
||||
int rtp_send_data(RTPSession *session, const uint8_t *data, uint32_t length,
|
||||
bool is_keyframe, const Logger *log);
|
||||
int rtp_send_data(const Logger *log, RTPSession *session, const uint8_t *data, uint32_t length,
|
||||
bool is_keyframe);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
339
toxav/toxav.c
339
toxav/toxav.c
@ -12,11 +12,15 @@
|
||||
|
||||
#include "msi.h"
|
||||
#include "rtp.h"
|
||||
#include "toxav_hacks.h"
|
||||
|
||||
#include "../toxcore/Messenger.h"
|
||||
#include "../toxcore/ccompat.h"
|
||||
#include "../toxcore/logger.h"
|
||||
#include "../toxcore/mono_time.h"
|
||||
#include "../toxcore/net_crypto.h"
|
||||
#include "../toxcore/network.h"
|
||||
#include "../toxcore/tox.h"
|
||||
#include "../toxcore/tox_private.h"
|
||||
#include "../toxcore/tox_struct.h"
|
||||
#include "../toxcore/util.h"
|
||||
|
||||
@ -36,7 +40,12 @@
|
||||
// iteration interval that is used when no call is active
|
||||
#define IDLE_ITERATION_INTERVAL_MS 200
|
||||
|
||||
typedef struct ToxAVCall {
|
||||
#ifndef TOXAV_CALL_DEFINED
|
||||
#define TOXAV_CALL_DEFINED
|
||||
typedef struct ToxAVCall ToxAVCall;
|
||||
#endif /* TOXAV_CALL_DEFINED */
|
||||
|
||||
struct ToxAVCall {
|
||||
ToxAV *av;
|
||||
|
||||
pthread_mutex_t mutex_audio[1];
|
||||
@ -63,7 +72,7 @@ typedef struct ToxAVCall {
|
||||
|
||||
struct ToxAVCall *prev;
|
||||
struct ToxAVCall *next;
|
||||
} ToxAVCall;
|
||||
};
|
||||
|
||||
/** Decode time statistics */
|
||||
typedef struct DecodeTimeStats {
|
||||
@ -79,8 +88,8 @@ typedef struct DecodeTimeStats {
|
||||
} DecodeTimeStats;
|
||||
|
||||
struct ToxAV {
|
||||
Logger *log;
|
||||
Tox *tox;
|
||||
Messenger *m;
|
||||
MSISession *msi;
|
||||
|
||||
/* Two-way storage: first is array of calls and second is list of calls with head and tail */
|
||||
@ -111,8 +120,8 @@ struct ToxAV {
|
||||
/* keep track of decode times for audio and video */
|
||||
DecodeTimeStats audio_stats;
|
||||
DecodeTimeStats video_stats;
|
||||
/** ToxAV's own mono_time instance */
|
||||
Mono_Time *toxav_mono_time;
|
||||
|
||||
Mono_Time *toxav_mono_time; // ToxAV's own mono_time instance
|
||||
};
|
||||
|
||||
static void callback_bwc(BWController *bwc, uint32_t friend_number, float loss, void *user_data);
|
||||
@ -127,11 +136,55 @@ static bool audio_bit_rate_invalid(uint32_t bit_rate);
|
||||
static bool video_bit_rate_invalid(uint32_t bit_rate);
|
||||
static bool invoke_call_state_callback(ToxAV *av, uint32_t friend_number, uint32_t state);
|
||||
static ToxAVCall *call_new(ToxAV *av, uint32_t friend_number, Toxav_Err_Call *error);
|
||||
static ToxAVCall *call_get(ToxAV *av, uint32_t friend_number);
|
||||
static ToxAVCall *call_remove(ToxAVCall *call);
|
||||
static bool call_prepare_transmission(ToxAVCall *call);
|
||||
static void call_kill_transmission(ToxAVCall *call);
|
||||
|
||||
MSISession *tox_av_msi_get(const ToxAV *av)
|
||||
{
|
||||
if (av == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return av->msi;
|
||||
}
|
||||
|
||||
ToxAVCall *call_get(ToxAV *av, uint32_t friend_number)
|
||||
{
|
||||
if (av == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Assumes mutex locked */
|
||||
if (av->calls == nullptr || av->calls_tail < friend_number) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return av->calls[friend_number];
|
||||
}
|
||||
|
||||
RTPSession *rtp_session_get(ToxAVCall *call, int payload_type)
|
||||
{
|
||||
if (call == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (payload_type == RTP_TYPE_VIDEO) {
|
||||
return call->video_rtp;
|
||||
} else {
|
||||
return call->audio_rtp;
|
||||
}
|
||||
}
|
||||
|
||||
BWController *bwc_controller_get(const ToxAVCall *call)
|
||||
{
|
||||
if (call == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return call->bwc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief initialize d with default values
|
||||
* @param d struct to be initialized, must not be nullptr
|
||||
@ -155,33 +208,26 @@ ToxAV *toxav_new(Tox *tox, Toxav_Err_New *error)
|
||||
goto RETURN;
|
||||
}
|
||||
|
||||
// TODO(iphydf): Don't rely on toxcore internals.
|
||||
Messenger *m;
|
||||
m = tox->m;
|
||||
|
||||
if (m->msi_packet != nullptr) {
|
||||
rc = TOXAV_ERR_NEW_MULTIPLE;
|
||||
goto RETURN;
|
||||
}
|
||||
|
||||
av = (ToxAV *)calloc(1, sizeof(ToxAV));
|
||||
|
||||
if (av == nullptr) {
|
||||
LOGGER_WARNING(m->log, "Allocation failed!");
|
||||
rc = TOXAV_ERR_NEW_MALLOC;
|
||||
goto RETURN;
|
||||
}
|
||||
|
||||
if (create_recursive_mutex(av->mutex) != 0) {
|
||||
LOGGER_WARNING(m->log, "Mutex creation failed!");
|
||||
rc = TOXAV_ERR_NEW_MALLOC;
|
||||
goto RETURN;
|
||||
}
|
||||
|
||||
av->log = tox->m->log;
|
||||
av->tox = tox;
|
||||
av->m = m;
|
||||
av->msi = msi_new(av->log, av->tox);
|
||||
|
||||
rtp_allow_receiving(av->tox);
|
||||
bwc_allow_receiving(av->tox);
|
||||
|
||||
av->toxav_mono_time = mono_time_new(tox->sys.mem, nullptr, nullptr);
|
||||
av->msi = msi_new(av->m);
|
||||
|
||||
if (av->msi == nullptr) {
|
||||
pthread_mutex_destroy(av->mutex);
|
||||
@ -193,6 +239,9 @@ ToxAV *toxav_new(Tox *tox, Toxav_Err_New *error)
|
||||
init_decode_time_stats(&av->video_stats);
|
||||
av->msi->av = av;
|
||||
|
||||
// save ToxAV object into toxcore
|
||||
tox_set_av_object(av->tox, av);
|
||||
|
||||
msi_callback_invite(av->msi, callback_invite);
|
||||
msi_callback_start(av->msi, callback_start);
|
||||
msi_callback_end(av->msi, callback_end);
|
||||
@ -207,12 +256,15 @@ RETURN:
|
||||
}
|
||||
|
||||
if (rc != TOXAV_ERR_NEW_OK) {
|
||||
free(av);
|
||||
av = nullptr;
|
||||
if (av != nullptr) {
|
||||
free(av);
|
||||
av = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return av;
|
||||
}
|
||||
|
||||
void toxav_kill(ToxAV *av)
|
||||
{
|
||||
if (av == nullptr) {
|
||||
@ -221,8 +273,16 @@ void toxav_kill(ToxAV *av)
|
||||
|
||||
pthread_mutex_lock(av->mutex);
|
||||
|
||||
// unregister callbacks
|
||||
for (uint8_t i = PACKET_ID_RANGE_LOSSY_AV_START; i <= PACKET_ID_RANGE_LOSSY_AV_END; ++i) {
|
||||
tox_callback_friend_lossy_packet_per_pktid(av->tox, nullptr, i);
|
||||
}
|
||||
|
||||
rtp_stop_receiving(av->tox);
|
||||
bwc_stop_receiving(av->tox);
|
||||
|
||||
/* To avoid possible deadlocks */
|
||||
while (av->msi != nullptr && msi_kill(av->msi, av->m->log) != 0) {
|
||||
while (av->msi != nullptr && msi_kill(av->log, av->tox, av->msi) != 0) {
|
||||
pthread_mutex_unlock(av->mutex);
|
||||
pthread_mutex_lock(av->mutex);
|
||||
}
|
||||
@ -243,13 +303,22 @@ void toxav_kill(ToxAV *av)
|
||||
pthread_mutex_unlock(av->mutex);
|
||||
pthread_mutex_destroy(av->mutex);
|
||||
|
||||
// set ToxAV object to NULL in toxcore, to signal ToxAV has been shutdown
|
||||
tox_set_av_object(av->tox, nullptr);
|
||||
|
||||
free(av);
|
||||
}
|
||||
|
||||
Tox *toxav_get_tox(const ToxAV *av)
|
||||
{
|
||||
return av->tox;
|
||||
}
|
||||
|
||||
const Logger *toxav_get_logger(const ToxAV *av)
|
||||
{
|
||||
return av->log;
|
||||
}
|
||||
|
||||
uint32_t toxav_audio_iteration_interval(const ToxAV *av)
|
||||
{
|
||||
return av->calls != nullptr ? av->audio_stats.interval : IDLE_ITERATION_INTERVAL_MS;
|
||||
@ -276,10 +345,11 @@ uint32_t toxav_iteration_interval(const ToxAV *av)
|
||||
static void calc_interval(ToxAV *av, DecodeTimeStats *stats, int32_t frame_time, uint64_t start_time)
|
||||
{
|
||||
stats->interval = frame_time < stats->average ? 0 : (frame_time - stats->average);
|
||||
stats->total += current_time_monotonic(av->m->mono_time) - start_time;
|
||||
stats->total += current_time_monotonic(av->toxav_mono_time) - start_time;
|
||||
|
||||
if (++stats->count == 3) {
|
||||
stats->average = stats->total / 3 + 5; /* NOTE: Magic Offset for precision */
|
||||
/* NOTE: Magic Offset for precision */
|
||||
stats->average = stats->total / 3 + 5;
|
||||
stats->count = 0;
|
||||
stats->total = 0;
|
||||
}
|
||||
@ -300,7 +370,6 @@ static void iterate_common(ToxAV *av, bool audio)
|
||||
}
|
||||
|
||||
const uint64_t start = current_time_monotonic(av->toxav_mono_time);
|
||||
// time until the first audio or video frame is over
|
||||
int32_t frame_time = IDLE_ITERATION_INTERVAL_MS;
|
||||
|
||||
for (ToxAVCall *i = av->calls[av->calls_head]; i != nullptr; i = i->next) {
|
||||
@ -311,6 +380,15 @@ static void iterate_common(ToxAV *av, bool audio)
|
||||
pthread_mutex_lock(i->toxav_call_mutex);
|
||||
pthread_mutex_unlock(av->mutex);
|
||||
|
||||
const uint32_t fid = i->friend_number;
|
||||
const bool is_offline = check_peer_offline_status(av->log, av->tox, i->msi_call->session, fid);
|
||||
|
||||
if (is_offline) {
|
||||
pthread_mutex_unlock(i->toxav_call_mutex);
|
||||
pthread_mutex_lock(av->mutex);
|
||||
break;
|
||||
}
|
||||
|
||||
if (audio) {
|
||||
ac_iterate(i->audio);
|
||||
|
||||
@ -329,8 +407,6 @@ static void iterate_common(ToxAV *av, bool audio)
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t fid = i->friend_number;
|
||||
|
||||
pthread_mutex_unlock(i->toxav_call_mutex);
|
||||
pthread_mutex_lock(av->mutex);
|
||||
|
||||
@ -342,8 +418,10 @@ static void iterate_common(ToxAV *av, bool audio)
|
||||
|
||||
DecodeTimeStats *stats = audio ? &av->audio_stats : &av->video_stats;
|
||||
calc_interval(av, stats, frame_time, start);
|
||||
|
||||
pthread_mutex_unlock(av->mutex);
|
||||
}
|
||||
|
||||
void toxav_audio_iterate(ToxAV *av)
|
||||
{
|
||||
iterate_common(av, true);
|
||||
@ -388,7 +466,7 @@ bool toxav_call(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint
|
||||
call->previous_self_capabilities |= audio_bit_rate > 0 ? MSI_CAP_S_AUDIO : 0;
|
||||
call->previous_self_capabilities |= video_bit_rate > 0 ? MSI_CAP_S_VIDEO : 0;
|
||||
|
||||
if (msi_invite(av->msi, &call->msi_call, friend_number, call->previous_self_capabilities) != 0) {
|
||||
if (msi_invite(av->log, av->msi, &call->msi_call, friend_number, call->previous_self_capabilities) != 0) {
|
||||
call_remove(call);
|
||||
rc = TOXAV_ERR_CALL_SYNC;
|
||||
goto RETURN;
|
||||
@ -405,6 +483,7 @@ RETURN:
|
||||
|
||||
return rc == TOXAV_ERR_CALL_OK;
|
||||
}
|
||||
|
||||
void toxav_callback_call(ToxAV *av, toxav_call_cb *callback, void *user_data)
|
||||
{
|
||||
pthread_mutex_lock(av->mutex);
|
||||
@ -412,6 +491,7 @@ void toxav_callback_call(ToxAV *av, toxav_call_cb *callback, void *user_data)
|
||||
av->ccb_user_data = user_data;
|
||||
pthread_mutex_unlock(av->mutex);
|
||||
}
|
||||
|
||||
bool toxav_answer(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate,
|
||||
Toxav_Err_Answer *error)
|
||||
{
|
||||
@ -420,7 +500,7 @@ bool toxav_answer(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, ui
|
||||
Toxav_Err_Answer rc = TOXAV_ERR_ANSWER_OK;
|
||||
ToxAVCall *call;
|
||||
|
||||
if (!m_friend_exists(av->m, friend_number)) {
|
||||
if (!tox_friend_exists(av->tox, friend_number)) {
|
||||
rc = TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND;
|
||||
goto RETURN;
|
||||
}
|
||||
@ -452,7 +532,7 @@ bool toxav_answer(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, ui
|
||||
call->previous_self_capabilities |= audio_bit_rate > 0 ? MSI_CAP_S_AUDIO : 0;
|
||||
call->previous_self_capabilities |= video_bit_rate > 0 ? MSI_CAP_S_VIDEO : 0;
|
||||
|
||||
if (msi_answer(call->msi_call, call->previous_self_capabilities) != 0) {
|
||||
if (msi_answer(av->log, call->msi_call, call->previous_self_capabilities) != 0) {
|
||||
rc = TOXAV_ERR_ANSWER_SYNC;
|
||||
}
|
||||
|
||||
@ -465,6 +545,7 @@ RETURN:
|
||||
|
||||
return rc == TOXAV_ERR_ANSWER_OK;
|
||||
}
|
||||
|
||||
void toxav_callback_call_state(ToxAV *av, toxav_call_state_cb *callback, void *user_data)
|
||||
{
|
||||
pthread_mutex_lock(av->mutex);
|
||||
@ -472,6 +553,7 @@ void toxav_callback_call_state(ToxAV *av, toxav_call_state_cb *callback, void *u
|
||||
av->scb_user_data = user_data;
|
||||
pthread_mutex_unlock(av->mutex);
|
||||
}
|
||||
|
||||
static Toxav_Err_Call_Control call_control_handle_resume(const ToxAVCall *call)
|
||||
{
|
||||
/* Only act if paused and had media transfer active before */
|
||||
@ -479,12 +561,13 @@ static Toxav_Err_Call_Control call_control_handle_resume(const ToxAVCall *call)
|
||||
return TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
|
||||
}
|
||||
|
||||
if (msi_change_capabilities(call->msi_call, call->previous_self_capabilities) == -1) {
|
||||
if (msi_change_capabilities(call->av->log, call->msi_call,
|
||||
call->previous_self_capabilities) == -1) {
|
||||
return TOXAV_ERR_CALL_CONTROL_SYNC;
|
||||
}
|
||||
|
||||
rtp_allow_receiving(call->audio_rtp);
|
||||
rtp_allow_receiving(call->video_rtp);
|
||||
rtp_allow_receiving_mark(call->audio_rtp);
|
||||
rtp_allow_receiving_mark(call->video_rtp);
|
||||
|
||||
return TOXAV_ERR_CALL_CONTROL_OK;
|
||||
}
|
||||
@ -497,12 +580,12 @@ static Toxav_Err_Call_Control call_control_handle_pause(ToxAVCall *call)
|
||||
|
||||
call->previous_self_capabilities = call->msi_call->self_capabilities;
|
||||
|
||||
if (msi_change_capabilities(call->msi_call, 0) == -1) {
|
||||
if (msi_change_capabilities(call->av->log, call->msi_call, 0) == -1) {
|
||||
return TOXAV_ERR_CALL_CONTROL_SYNC;
|
||||
}
|
||||
|
||||
rtp_stop_receiving(call->audio_rtp);
|
||||
rtp_stop_receiving(call->video_rtp);
|
||||
rtp_stop_receiving_mark(call->audio_rtp);
|
||||
rtp_stop_receiving_mark(call->video_rtp);
|
||||
|
||||
return TOXAV_ERR_CALL_CONTROL_OK;
|
||||
}
|
||||
@ -511,7 +594,7 @@ static Toxav_Err_Call_Control call_control_handle_cancel(ToxAVCall *call)
|
||||
/* Hang up */
|
||||
pthread_mutex_lock(call->toxav_call_mutex);
|
||||
|
||||
if (msi_hangup(call->msi_call) != 0) {
|
||||
if (msi_hangup(call->av->log, call->msi_call) != 0) {
|
||||
pthread_mutex_unlock(call->toxav_call_mutex);
|
||||
return TOXAV_ERR_CALL_CONTROL_SYNC;
|
||||
}
|
||||
@ -531,13 +614,13 @@ static Toxav_Err_Call_Control call_control_handle_mute_audio(const ToxAVCall *ca
|
||||
return TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
|
||||
}
|
||||
|
||||
if (msi_change_capabilities(call->msi_call, call->
|
||||
if (msi_change_capabilities(call->av->log, call->msi_call, call->
|
||||
msi_call->self_capabilities ^ MSI_CAP_R_AUDIO) == -1) {
|
||||
return TOXAV_ERR_CALL_CONTROL_SYNC;
|
||||
|
||||
}
|
||||
|
||||
rtp_stop_receiving(call->audio_rtp);
|
||||
rtp_stop_receiving_mark(call->audio_rtp);
|
||||
return TOXAV_ERR_CALL_CONTROL_OK;
|
||||
}
|
||||
static Toxav_Err_Call_Control call_control_handle_unmute_audio(const ToxAVCall *call)
|
||||
@ -546,12 +629,12 @@ static Toxav_Err_Call_Control call_control_handle_unmute_audio(const ToxAVCall *
|
||||
return TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
|
||||
}
|
||||
|
||||
if (msi_change_capabilities(call->msi_call, call->
|
||||
if (msi_change_capabilities(call->av->log, call->msi_call, call->
|
||||
msi_call->self_capabilities | MSI_CAP_R_AUDIO) == -1) {
|
||||
return TOXAV_ERR_CALL_CONTROL_SYNC;
|
||||
}
|
||||
|
||||
rtp_allow_receiving(call->audio_rtp);
|
||||
rtp_allow_receiving_mark(call->audio_rtp);
|
||||
return TOXAV_ERR_CALL_CONTROL_OK;
|
||||
}
|
||||
static Toxav_Err_Call_Control call_control_handle_hide_video(const ToxAVCall *call)
|
||||
@ -560,12 +643,12 @@ static Toxav_Err_Call_Control call_control_handle_hide_video(const ToxAVCall *ca
|
||||
return TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
|
||||
}
|
||||
|
||||
if (msi_change_capabilities(call->msi_call, call->
|
||||
if (msi_change_capabilities(call->av->log, call->msi_call, call->
|
||||
msi_call->self_capabilities ^ MSI_CAP_R_VIDEO) == -1) {
|
||||
return TOXAV_ERR_CALL_CONTROL_SYNC;
|
||||
}
|
||||
|
||||
rtp_stop_receiving(call->video_rtp);
|
||||
rtp_stop_receiving_mark(call->video_rtp);
|
||||
return TOXAV_ERR_CALL_CONTROL_OK;
|
||||
}
|
||||
static Toxav_Err_Call_Control call_control_handle_show_video(const ToxAVCall *call)
|
||||
@ -574,12 +657,12 @@ static Toxav_Err_Call_Control call_control_handle_show_video(const ToxAVCall *ca
|
||||
return TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
|
||||
}
|
||||
|
||||
if (msi_change_capabilities(call->msi_call, call->
|
||||
if (msi_change_capabilities(call->av->log, call->msi_call, call->
|
||||
msi_call->self_capabilities | MSI_CAP_R_VIDEO) == -1) {
|
||||
return TOXAV_ERR_CALL_CONTROL_SYNC;
|
||||
}
|
||||
|
||||
rtp_allow_receiving(call->video_rtp);
|
||||
rtp_allow_receiving_mark(call->video_rtp);
|
||||
return TOXAV_ERR_CALL_CONTROL_OK;
|
||||
}
|
||||
static Toxav_Err_Call_Control call_control_handle(ToxAVCall *call, Toxav_Call_Control control)
|
||||
@ -611,7 +694,7 @@ static Toxav_Err_Call_Control call_control_handle(ToxAVCall *call, Toxav_Call_Co
|
||||
}
|
||||
static Toxav_Err_Call_Control call_control(ToxAV *av, uint32_t friend_number, Toxav_Call_Control control)
|
||||
{
|
||||
if (!m_friend_exists(av->m, friend_number)) {
|
||||
if (!tox_friend_exists(av->tox, friend_number)) {
|
||||
return TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND;
|
||||
}
|
||||
|
||||
@ -637,13 +720,14 @@ bool toxav_call_control(ToxAV *av, uint32_t friend_number, Toxav_Call_Control co
|
||||
|
||||
return rc == TOXAV_ERR_CALL_CONTROL_OK;
|
||||
}
|
||||
|
||||
bool toxav_audio_set_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t bit_rate,
|
||||
Toxav_Err_Bit_Rate_Set *error)
|
||||
{
|
||||
Toxav_Err_Bit_Rate_Set rc = TOXAV_ERR_BIT_RATE_SET_OK;
|
||||
ToxAVCall *call;
|
||||
|
||||
if (!m_friend_exists(av->m, friend_number)) {
|
||||
if (!tox_friend_exists(av->tox, friend_number)) {
|
||||
rc = TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND;
|
||||
goto RETURN;
|
||||
}
|
||||
@ -662,14 +746,14 @@ bool toxav_audio_set_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t bit_ra
|
||||
goto RETURN;
|
||||
}
|
||||
|
||||
LOGGER_DEBUG(av->m->log, "Setting new audio bitrate to: %d", bit_rate);
|
||||
LOGGER_DEBUG(av->log, "Setting new audio bitrate to: %d", bit_rate);
|
||||
|
||||
if (call->audio_bit_rate == bit_rate) {
|
||||
LOGGER_DEBUG(av->m->log, "Audio bitrate already set to: %d", bit_rate);
|
||||
LOGGER_DEBUG(av->log, "Audio bitrate already set to: %d", bit_rate);
|
||||
} else if (bit_rate == 0) {
|
||||
LOGGER_DEBUG(av->m->log, "Turned off audio sending");
|
||||
LOGGER_DEBUG(av->log, "Turned off audio sending");
|
||||
|
||||
if (msi_change_capabilities(call->msi_call, call->msi_call->
|
||||
if (msi_change_capabilities(av->log, call->msi_call, call->msi_call->
|
||||
self_capabilities ^ MSI_CAP_S_AUDIO) != 0) {
|
||||
pthread_mutex_unlock(av->mutex);
|
||||
rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
|
||||
@ -682,10 +766,10 @@ bool toxav_audio_set_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t bit_ra
|
||||
pthread_mutex_lock(call->toxav_call_mutex);
|
||||
|
||||
if (call->audio_bit_rate == 0) {
|
||||
LOGGER_DEBUG(av->m->log, "Turned on audio sending");
|
||||
LOGGER_DEBUG(av->log, "Turned on audio sending");
|
||||
|
||||
/* The audio has been turned off before this */
|
||||
if (msi_change_capabilities(call->msi_call, call->
|
||||
if (msi_change_capabilities(av->log, call->msi_call, call->
|
||||
msi_call->self_capabilities | MSI_CAP_S_AUDIO) != 0) {
|
||||
pthread_mutex_unlock(call->toxav_call_mutex);
|
||||
pthread_mutex_unlock(av->mutex);
|
||||
@ -693,7 +777,7 @@ bool toxav_audio_set_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t bit_ra
|
||||
goto RETURN;
|
||||
}
|
||||
} else {
|
||||
LOGGER_DEBUG(av->m->log, "Set new audio bit rate %d", bit_rate);
|
||||
LOGGER_DEBUG(av->log, "Set new audio bit rate %d", bit_rate);
|
||||
}
|
||||
|
||||
call->audio_bit_rate = bit_rate;
|
||||
@ -709,13 +793,14 @@ RETURN:
|
||||
|
||||
return rc == TOXAV_ERR_BIT_RATE_SET_OK;
|
||||
}
|
||||
|
||||
bool toxav_video_set_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t bit_rate,
|
||||
Toxav_Err_Bit_Rate_Set *error)
|
||||
{
|
||||
Toxav_Err_Bit_Rate_Set rc = TOXAV_ERR_BIT_RATE_SET_OK;
|
||||
ToxAVCall *call;
|
||||
|
||||
if (!m_friend_exists(av->m, friend_number)) {
|
||||
if (!tox_friend_exists(av->tox, friend_number)) {
|
||||
rc = TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND;
|
||||
goto RETURN;
|
||||
}
|
||||
@ -734,15 +819,15 @@ bool toxav_video_set_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t bit_ra
|
||||
goto RETURN;
|
||||
}
|
||||
|
||||
LOGGER_DEBUG(av->m->log, "Setting new video bitrate to: %d", bit_rate);
|
||||
LOGGER_DEBUG(av->log, "Setting new video bitrate to: %d", bit_rate);
|
||||
|
||||
if (call->video_bit_rate == bit_rate) {
|
||||
LOGGER_DEBUG(av->m->log, "Video bitrate already set to: %d", bit_rate);
|
||||
LOGGER_DEBUG(av->log, "Video bitrate already set to: %d", bit_rate);
|
||||
} else if (bit_rate == 0) {
|
||||
LOGGER_DEBUG(av->m->log, "Turned off video sending");
|
||||
LOGGER_DEBUG(av->log, "Turned off video sending");
|
||||
|
||||
/* Video sending is turned off; notify peer */
|
||||
if (msi_change_capabilities(call->msi_call, call->msi_call->
|
||||
if (msi_change_capabilities(av->log, call->msi_call, call->msi_call->
|
||||
self_capabilities ^ MSI_CAP_S_VIDEO) != 0) {
|
||||
pthread_mutex_unlock(av->mutex);
|
||||
rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
|
||||
@ -754,10 +839,10 @@ bool toxav_video_set_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t bit_ra
|
||||
pthread_mutex_lock(call->toxav_call_mutex);
|
||||
|
||||
if (call->video_bit_rate == 0) {
|
||||
LOGGER_DEBUG(av->m->log, "Turned on video sending");
|
||||
LOGGER_DEBUG(av->log, "Turned on video sending");
|
||||
|
||||
/* The video has been turned off before this */
|
||||
if (msi_change_capabilities(call->msi_call, call->
|
||||
if (msi_change_capabilities(av->log, call->msi_call, call->
|
||||
msi_call->self_capabilities | MSI_CAP_S_VIDEO) != 0) {
|
||||
pthread_mutex_unlock(call->toxav_call_mutex);
|
||||
pthread_mutex_unlock(av->mutex);
|
||||
@ -765,7 +850,7 @@ bool toxav_video_set_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t bit_ra
|
||||
goto RETURN;
|
||||
}
|
||||
} else {
|
||||
LOGGER_DEBUG(av->m->log, "Set new video bit rate %d", bit_rate);
|
||||
LOGGER_DEBUG(av->log, "Set new video bit rate %d", bit_rate);
|
||||
}
|
||||
|
||||
call->video_bit_rate = bit_rate;
|
||||
@ -781,6 +866,7 @@ RETURN:
|
||||
|
||||
return rc == TOXAV_ERR_BIT_RATE_SET_OK;
|
||||
}
|
||||
|
||||
void toxav_callback_audio_bit_rate(ToxAV *av, toxav_audio_bit_rate_cb *callback, void *user_data)
|
||||
{
|
||||
pthread_mutex_lock(av->mutex);
|
||||
@ -788,6 +874,7 @@ void toxav_callback_audio_bit_rate(ToxAV *av, toxav_audio_bit_rate_cb *callback,
|
||||
av->abcb_user_data = user_data;
|
||||
pthread_mutex_unlock(av->mutex);
|
||||
}
|
||||
|
||||
void toxav_callback_video_bit_rate(ToxAV *av, toxav_video_bit_rate_cb *callback, void *user_data)
|
||||
{
|
||||
pthread_mutex_lock(av->mutex);
|
||||
@ -795,13 +882,14 @@ void toxav_callback_video_bit_rate(ToxAV *av, toxav_video_bit_rate_cb *callback,
|
||||
av->vbcb_user_data = user_data;
|
||||
pthread_mutex_unlock(av->mutex);
|
||||
}
|
||||
|
||||
bool toxav_audio_send_frame(ToxAV *av, uint32_t friend_number, const int16_t *pcm, size_t sample_count,
|
||||
uint8_t channels, uint32_t sampling_rate, Toxav_Err_Send_Frame *error)
|
||||
{
|
||||
Toxav_Err_Send_Frame rc = TOXAV_ERR_SEND_FRAME_OK;
|
||||
ToxAVCall *call;
|
||||
|
||||
if (!m_friend_exists(av->m, friend_number)) {
|
||||
if (!tox_friend_exists(av->tox, friend_number)) {
|
||||
rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND;
|
||||
goto RETURN;
|
||||
}
|
||||
@ -859,14 +947,14 @@ bool toxav_audio_send_frame(ToxAV *av, uint32_t friend_number, const int16_t *pc
|
||||
dest + sizeof(sampling_rate), dest_size - sizeof(sampling_rate));
|
||||
|
||||
if (vrc < 0) {
|
||||
LOGGER_WARNING(av->m->log, "Failed to encode frame %s", opus_strerror(vrc));
|
||||
LOGGER_WARNING(av->log, "Failed to encode frame %s", opus_strerror(vrc));
|
||||
pthread_mutex_unlock(call->mutex_audio);
|
||||
rc = TOXAV_ERR_SEND_FRAME_INVALID;
|
||||
goto RETURN;
|
||||
}
|
||||
|
||||
if (rtp_send_data(call->audio_rtp, dest, vrc + sizeof(sampling_rate), false, av->m->log) != 0) {
|
||||
LOGGER_WARNING(av->m->log, "Failed to send audio packet");
|
||||
if (rtp_send_data(av->log, call->audio_rtp, dest, vrc + sizeof(sampling_rate), false) != 0) {
|
||||
LOGGER_WARNING(av->log, "Failed to send audio packet");
|
||||
rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED;
|
||||
}
|
||||
}
|
||||
@ -882,7 +970,7 @@ RETURN:
|
||||
return rc == TOXAV_ERR_SEND_FRAME_OK;
|
||||
}
|
||||
|
||||
static Toxav_Err_Send_Frame send_frames(const Logger *log, ToxAVCall *call)
|
||||
static Toxav_Err_Send_Frame send_frames(const ToxAV *av, ToxAVCall *call)
|
||||
{
|
||||
vpx_codec_iter_t iter = nullptr;
|
||||
|
||||
@ -900,20 +988,15 @@ static Toxav_Err_Send_Frame send_frames(const Logger *log, ToxAVCall *call)
|
||||
const uint32_t frame_length_in_bytes = pkt->data.frame.sz;
|
||||
|
||||
const int res = rtp_send_data(
|
||||
av->log,
|
||||
call->video_rtp,
|
||||
(const uint8_t *)pkt->data.frame.buf,
|
||||
frame_length_in_bytes,
|
||||
is_keyframe,
|
||||
log);
|
||||
|
||||
LOGGER_DEBUG(log, "+ _sending_FRAME_TYPE_==%s bytes=%d frame_len=%d", is_keyframe ? "K" : ".",
|
||||
(int)pkt->data.frame.sz, (int)frame_length_in_bytes);
|
||||
const uint8_t *const buf = (const uint8_t *)pkt->data.frame.buf;
|
||||
LOGGER_DEBUG(log, "+ _sending_FRAME_ b0=%d b1=%d", buf[0], buf[1]);
|
||||
is_keyframe);
|
||||
|
||||
if (res < 0) {
|
||||
char *netstrerror = net_new_strerror(net_error());
|
||||
LOGGER_WARNING(log, "Could not send video frame: %s", netstrerror);
|
||||
LOGGER_WARNING(av->log, "Could not send video frame: %s", netstrerror);
|
||||
net_kill_strerror(netstrerror);
|
||||
return TOXAV_ERR_SEND_FRAME_RTP_FAILED;
|
||||
}
|
||||
@ -930,7 +1013,7 @@ bool toxav_video_send_frame(ToxAV *av, uint32_t friend_number, uint16_t width, u
|
||||
|
||||
int vpx_encode_flags = 0;
|
||||
|
||||
if (!m_friend_exists(av->m, friend_number)) {
|
||||
if (!tox_friend_exists(av->tox, friend_number)) {
|
||||
rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND;
|
||||
goto RETURN;
|
||||
}
|
||||
@ -965,7 +1048,7 @@ bool toxav_video_send_frame(ToxAV *av, uint32_t friend_number, uint16_t width, u
|
||||
goto RETURN;
|
||||
}
|
||||
|
||||
if (vc_reconfigure_encoder(call->video, call->video_bit_rate * 1000, width, height, -1) != 0) {
|
||||
if (vc_reconfigure_encoder(call->video, call->video_bit_rate, width, height, -1) != 0) {
|
||||
pthread_mutex_unlock(call->mutex_video);
|
||||
rc = TOXAV_ERR_SEND_FRAME_INVALID;
|
||||
goto RETURN;
|
||||
@ -974,13 +1057,13 @@ bool toxav_video_send_frame(ToxAV *av, uint32_t friend_number, uint16_t width, u
|
||||
if (call->video_rtp->ssrc < VIDEO_SEND_X_KEYFRAMES_FIRST) {
|
||||
// Key frame flag for first frames
|
||||
vpx_encode_flags = VPX_EFLAG_FORCE_KF;
|
||||
LOGGER_DEBUG(av->m->log, "I_FRAME_FLAG:%d only-i-frame mode", call->video_rtp->ssrc);
|
||||
LOGGER_DEBUG(av->log, "I_FRAME_FLAG:%d only-i-frame mode", call->video_rtp->ssrc);
|
||||
|
||||
++call->video_rtp->ssrc;
|
||||
} else if (call->video_rtp->ssrc == VIDEO_SEND_X_KEYFRAMES_FIRST) {
|
||||
// normal keyframe placement
|
||||
vpx_encode_flags = 0;
|
||||
LOGGER_DEBUG(av->m->log, "I_FRAME_FLAG:%d normal mode", call->video_rtp->ssrc);
|
||||
LOGGER_DEBUG(av->log, "I_FRAME_FLAG:%d normal mode", call->video_rtp->ssrc);
|
||||
|
||||
++call->video_rtp->ssrc;
|
||||
}
|
||||
@ -1009,7 +1092,7 @@ bool toxav_video_send_frame(ToxAV *av, uint32_t friend_number, uint16_t width, u
|
||||
|
||||
if (vrc != VPX_CODEC_OK) {
|
||||
pthread_mutex_unlock(call->mutex_video);
|
||||
LOGGER_ERROR(av->m->log, "Could not encode video frame: %s", vpx_codec_err_to_string(vrc));
|
||||
LOGGER_ERROR(av->log, "Could not encode video frame: %s", vpx_codec_err_to_string(vrc));
|
||||
rc = TOXAV_ERR_SEND_FRAME_INVALID;
|
||||
goto RETURN;
|
||||
}
|
||||
@ -1017,7 +1100,7 @@ bool toxav_video_send_frame(ToxAV *av, uint32_t friend_number, uint16_t width, u
|
||||
|
||||
++call->video->frame_counter;
|
||||
|
||||
rc = send_frames(av->m->log, call);
|
||||
rc = send_frames(av, call);
|
||||
|
||||
pthread_mutex_unlock(call->mutex_video);
|
||||
|
||||
@ -1063,7 +1146,7 @@ static void callback_bwc(BWController *bwc, uint32_t friend_number, float loss,
|
||||
ToxAVCall *call = (ToxAVCall *)user_data;
|
||||
assert(call != nullptr);
|
||||
|
||||
LOGGER_DEBUG(call->av->m->log, "Reported loss of %f%%", (double)loss * 100);
|
||||
LOGGER_DEBUG(call->av->log, "Reported loss of %f%%", (double)loss * 100);
|
||||
|
||||
/* if less than 10% data loss we do nothing! */
|
||||
if (loss < 0.1F) {
|
||||
@ -1075,7 +1158,7 @@ static void callback_bwc(BWController *bwc, uint32_t friend_number, float loss,
|
||||
if (call->video_bit_rate != 0) {
|
||||
if (call->av->vbcb == nullptr) {
|
||||
pthread_mutex_unlock(call->av->mutex);
|
||||
LOGGER_WARNING(call->av->m->log, "No callback to report loss on");
|
||||
LOGGER_WARNING(call->av->log, "No callback to report loss on");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1085,7 +1168,7 @@ static void callback_bwc(BWController *bwc, uint32_t friend_number, float loss,
|
||||
} else if (call->audio_bit_rate != 0) {
|
||||
if (call->av->abcb == nullptr) {
|
||||
pthread_mutex_unlock(call->av->mutex);
|
||||
LOGGER_WARNING(call->av->m->log, "No callback to report loss on");
|
||||
LOGGER_WARNING(call->av->log, "No callback to report loss on");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1096,6 +1179,7 @@ static void callback_bwc(BWController *bwc, uint32_t friend_number, float loss,
|
||||
|
||||
pthread_mutex_unlock(call->av->mutex);
|
||||
}
|
||||
|
||||
static int callback_invite(void *object, MSICall *call)
|
||||
{
|
||||
ToxAV *toxav = (ToxAV *)object;
|
||||
@ -1104,7 +1188,7 @@ static int callback_invite(void *object, MSICall *call)
|
||||
ToxAVCall *av_call = call_new(toxav, call->friend_number, nullptr);
|
||||
|
||||
if (av_call == nullptr) {
|
||||
LOGGER_WARNING(toxav->m->log, "Failed to initialize call...");
|
||||
LOGGER_WARNING(toxav->log, "Failed to initialize call...");
|
||||
pthread_mutex_unlock(toxav->mutex);
|
||||
return -1;
|
||||
}
|
||||
@ -1124,6 +1208,7 @@ static int callback_invite(void *object, MSICall *call)
|
||||
pthread_mutex_unlock(toxav->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int callback_start(void *object, MSICall *call)
|
||||
{
|
||||
ToxAV *toxav = (ToxAV *)object;
|
||||
@ -1152,6 +1237,7 @@ static int callback_start(void *object, MSICall *call)
|
||||
pthread_mutex_unlock(toxav->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int callback_end(void *object, MSICall *call)
|
||||
{
|
||||
ToxAV *toxav = (ToxAV *)object;
|
||||
@ -1167,6 +1253,7 @@ static int callback_end(void *object, MSICall *call)
|
||||
pthread_mutex_unlock(toxav->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int callback_error(void *object, MSICall *call)
|
||||
{
|
||||
ToxAV *toxav = (ToxAV *)object;
|
||||
@ -1182,21 +1269,22 @@ static int callback_error(void *object, MSICall *call)
|
||||
pthread_mutex_unlock(toxav->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int callback_capabilites(void *object, MSICall *call)
|
||||
{
|
||||
ToxAV *toxav = (ToxAV *)object;
|
||||
pthread_mutex_lock(toxav->mutex);
|
||||
|
||||
if ((call->peer_capabilities & MSI_CAP_S_AUDIO) != 0) {
|
||||
rtp_allow_receiving(call->av_call->audio_rtp);
|
||||
rtp_allow_receiving_mark(call->av_call->audio_rtp);
|
||||
} else {
|
||||
rtp_stop_receiving(call->av_call->audio_rtp);
|
||||
rtp_stop_receiving_mark(call->av_call->audio_rtp);
|
||||
}
|
||||
|
||||
if ((call->peer_capabilities & MSI_CAP_S_VIDEO) != 0) {
|
||||
rtp_allow_receiving(call->av_call->video_rtp);
|
||||
rtp_allow_receiving_mark(call->av_call->video_rtp);
|
||||
} else {
|
||||
rtp_stop_receiving(call->av_call->video_rtp);
|
||||
rtp_stop_receiving_mark(call->av_call->video_rtp);
|
||||
}
|
||||
|
||||
invoke_call_state_callback(toxav, call->friend_number, call->peer_capabilities);
|
||||
@ -1204,6 +1292,7 @@ static int callback_capabilites(void *object, MSICall *call)
|
||||
pthread_mutex_unlock(toxav->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool audio_bit_rate_invalid(uint32_t bit_rate)
|
||||
{
|
||||
/* Opus RFC 6716 section-2.1.1 dictates the following:
|
||||
@ -1211,6 +1300,7 @@ static bool audio_bit_rate_invalid(uint32_t bit_rate)
|
||||
*/
|
||||
return bit_rate < 6 || bit_rate > 510;
|
||||
}
|
||||
|
||||
static bool video_bit_rate_invalid(uint32_t bit_rate)
|
||||
{
|
||||
/* https://www.webmproject.org/docs/webm-sdk/structvpx__codec__enc__cfg.html shows the following:
|
||||
@ -1222,6 +1312,7 @@ static bool video_bit_rate_invalid(uint32_t bit_rate)
|
||||
*/
|
||||
return bit_rate > UINT32_MAX;
|
||||
}
|
||||
|
||||
static bool invoke_call_state_callback(ToxAV *av, uint32_t friend_number, uint32_t state)
|
||||
{
|
||||
if (av->scb != nullptr) {
|
||||
@ -1239,12 +1330,17 @@ static ToxAVCall *call_new(ToxAV *av, uint32_t friend_number, Toxav_Err_Call *er
|
||||
Toxav_Err_Call rc = TOXAV_ERR_CALL_OK;
|
||||
ToxAVCall *call = nullptr;
|
||||
|
||||
if (!m_friend_exists(av->m, friend_number)) {
|
||||
Tox_Err_Friend_Query f_con_query_error;
|
||||
Tox_Connection f_con_status = TOX_CONNECTION_NONE;
|
||||
|
||||
if (!tox_friend_exists(av->tox, friend_number)) {
|
||||
rc = TOXAV_ERR_CALL_FRIEND_NOT_FOUND;
|
||||
goto RETURN;
|
||||
}
|
||||
|
||||
if (m_get_friend_connectionstatus(av->m, friend_number) < 1) {
|
||||
f_con_status = tox_friend_get_connection_status(av->tox, friend_number, &f_con_query_error);
|
||||
|
||||
if (f_con_status == TOX_CONNECTION_NONE) {
|
||||
rc = TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED;
|
||||
goto RETURN;
|
||||
}
|
||||
@ -1323,16 +1419,6 @@ RETURN:
|
||||
return call;
|
||||
}
|
||||
|
||||
static ToxAVCall *call_get(ToxAV *av, uint32_t friend_number)
|
||||
{
|
||||
/* Assumes mutex locked */
|
||||
if (av->calls == nullptr || av->calls_tail < friend_number) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return av->calls[friend_number];
|
||||
}
|
||||
|
||||
static ToxAVCall *call_remove(ToxAVCall *call)
|
||||
{
|
||||
if (call == nullptr) {
|
||||
@ -1399,7 +1485,7 @@ static bool call_prepare_transmission(ToxAVCall *call)
|
||||
}
|
||||
|
||||
if (call->active) {
|
||||
LOGGER_WARNING(av->m->log, "Call already active!");
|
||||
LOGGER_WARNING(av->log, "Call already active!");
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1412,43 +1498,37 @@ static bool call_prepare_transmission(ToxAVCall *call)
|
||||
}
|
||||
|
||||
/* Prepare bwc */
|
||||
call->bwc = bwc_new(av->m, av->tox, call->friend_number, callback_bwc, call, av->toxav_mono_time);
|
||||
call->bwc = bwc_new(av->log, av->tox, call->friend_number, callback_bwc, call, av->toxav_mono_time);
|
||||
|
||||
if (call->bwc == nullptr) {
|
||||
LOGGER_ERROR(av->m->log, "Failed to create new bwc");
|
||||
goto FAILURE;
|
||||
}
|
||||
|
||||
{ /* Prepare audio */
|
||||
call->audio = ac_new(av->toxav_mono_time, av->m->log, av, call->friend_number, av->acb, av->acb_user_data);
|
||||
{ /* Prepare audio */
|
||||
call->audio = ac_new(av->toxav_mono_time, av->log, av, call->friend_number, av->acb, av->acb_user_data);
|
||||
|
||||
if (call->audio == nullptr) {
|
||||
LOGGER_ERROR(av->m->log, "Failed to create audio codec session");
|
||||
LOGGER_ERROR(av->log, "Failed to create audio codec session");
|
||||
goto FAILURE;
|
||||
}
|
||||
|
||||
call->audio_rtp = rtp_new(RTP_TYPE_AUDIO, av->m, av->tox, call->friend_number, call->bwc,
|
||||
call->audio_rtp = rtp_new(av->log, RTP_TYPE_AUDIO, av->tox, av, call->friend_number, call->bwc,
|
||||
call->audio, ac_queue_message);
|
||||
|
||||
if (call->audio_rtp == nullptr) {
|
||||
LOGGER_ERROR(av->m->log, "Failed to create audio rtp session");
|
||||
LOGGER_ERROR(av->log, "Failed to create audio rtp session");
|
||||
goto FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
{ /* Prepare video */
|
||||
call->video = vc_new(av->toxav_mono_time, av->m->log, av, call->friend_number, av->vcb, av->vcb_user_data);
|
||||
{ /* Prepare video */
|
||||
call->video = vc_new(av->log, av->toxav_mono_time, av, call->friend_number, av->vcb, av->vcb_user_data);
|
||||
|
||||
if (call->video == nullptr) {
|
||||
LOGGER_ERROR(av->m->log, "Failed to create video codec session");
|
||||
LOGGER_ERROR(av->log, "Failed to create video codec session");
|
||||
goto FAILURE;
|
||||
}
|
||||
|
||||
call->video_rtp = rtp_new(RTP_TYPE_VIDEO, av->m, av->tox, call->friend_number, call->bwc,
|
||||
call->video_rtp = rtp_new(av->log, RTP_TYPE_VIDEO, av->tox, av, call->friend_number, call->bwc,
|
||||
call->video, vc_queue_message);
|
||||
|
||||
if (call->video_rtp == nullptr) {
|
||||
LOGGER_ERROR(av->m->log, "Failed to create video rtp session");
|
||||
LOGGER_ERROR(av->log, "Failed to create video rtp session");
|
||||
goto FAILURE;
|
||||
}
|
||||
}
|
||||
@ -1458,11 +1538,11 @@ static bool call_prepare_transmission(ToxAVCall *call)
|
||||
|
||||
FAILURE:
|
||||
bwc_kill(call->bwc);
|
||||
rtp_kill(call->audio_rtp);
|
||||
rtp_kill(av->log, call->audio_rtp);
|
||||
ac_kill(call->audio);
|
||||
call->audio_rtp = nullptr;
|
||||
call->audio = nullptr;
|
||||
rtp_kill(call->video_rtp);
|
||||
rtp_kill(av->log, call->video_rtp);
|
||||
vc_kill(call->video);
|
||||
call->video_rtp = nullptr;
|
||||
call->video = nullptr;
|
||||
@ -1489,12 +1569,14 @@ static void call_kill_transmission(ToxAVCall *call)
|
||||
|
||||
bwc_kill(call->bwc);
|
||||
|
||||
rtp_kill(call->audio_rtp);
|
||||
const ToxAV *av = call->av;
|
||||
|
||||
rtp_kill(av->log, call->audio_rtp);
|
||||
ac_kill(call->audio);
|
||||
call->audio_rtp = nullptr;
|
||||
call->audio = nullptr;
|
||||
|
||||
rtp_kill(call->video_rtp);
|
||||
rtp_kill(av->log, call->video_rtp);
|
||||
vc_kill(call->video);
|
||||
call->video_rtp = nullptr;
|
||||
call->video = nullptr;
|
||||
@ -1502,3 +1584,12 @@ static void call_kill_transmission(ToxAVCall *call)
|
||||
pthread_mutex_destroy(call->mutex_audio);
|
||||
pthread_mutex_destroy(call->mutex_video);
|
||||
}
|
||||
|
||||
Mono_Time *toxav_get_av_mono_time(const ToxAV *av)
|
||||
{
|
||||
if (av == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return av->toxav_mono_time;
|
||||
}
|
||||
|
@ -73,6 +73,8 @@ typedef struct Tox Tox;
|
||||
#endif /* !TOX_DEFINED */
|
||||
#endif /* !APIGEN_IGNORE */
|
||||
|
||||
#ifndef TOXAV_DEFINED
|
||||
#define TOXAV_DEFINED
|
||||
/**
|
||||
* @brief The ToxAV instance type.
|
||||
*
|
||||
@ -83,6 +85,7 @@ typedef struct Tox Tox;
|
||||
* notifying peers.
|
||||
*/
|
||||
typedef struct ToxAV ToxAV;
|
||||
#endif /* TOXAV_DEFINED */
|
||||
|
||||
/** @{
|
||||
* @brief Creation and destruction
|
||||
@ -246,9 +249,9 @@ typedef enum Toxav_Err_Call {
|
||||
* receiving are both enabled by default.
|
||||
*
|
||||
* @param friend_number The friend number of the friend that should be called.
|
||||
* @param audio_bit_rate Audio bit rate in Kb/sec. Set this to 0 to disable
|
||||
* @param audio_bit_rate Audio bit rate in kbit/sec. Set this to 0 to disable
|
||||
* audio sending.
|
||||
* @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable
|
||||
* @param video_bit_rate Video bit rate in kbit/sec. Set this to 0 to disable
|
||||
* video sending.
|
||||
*/
|
||||
bool toxav_call(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate,
|
||||
@ -315,9 +318,9 @@ typedef enum Toxav_Err_Answer {
|
||||
* enabled by default.
|
||||
*
|
||||
* @param friend_number The friend number of the friend that is calling.
|
||||
* @param audio_bit_rate Audio bit rate in Kb/sec. Set this to 0 to disable
|
||||
* @param audio_bit_rate Audio bit rate in kbit/sec. Set this to 0 to disable
|
||||
* audio sending.
|
||||
* @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable
|
||||
* @param video_bit_rate Video bit rate in kbit/sec. Set this to 0 to disable
|
||||
* video sending.
|
||||
*/
|
||||
bool toxav_answer(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate,
|
||||
@ -597,11 +600,11 @@ bool toxav_audio_send_frame(ToxAV *av, uint32_t friend_number, const int16_t pcm
|
||||
uint8_t channels, uint32_t sampling_rate, Toxav_Err_Send_Frame *error);
|
||||
|
||||
/**
|
||||
* Set the bit rate to be used in subsequent video frames.
|
||||
* Set the bit rate to be used in subsequent audio frames.
|
||||
*
|
||||
* @param friend_number The friend number of the friend for which to set the
|
||||
* bit rate.
|
||||
* @param bit_rate The new audio bit rate in Kb/sec. Set to 0 to disable.
|
||||
* @param bit_rate The new audio bit rate in kbit/sec. Set to 0 to disable.
|
||||
*
|
||||
* @return true on success.
|
||||
*/
|
||||
@ -614,7 +617,7 @@ bool toxav_audio_set_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t bit_ra
|
||||
*
|
||||
* @param friend_number The friend number of the friend for which to set the
|
||||
* bit rate.
|
||||
* @param audio_bit_rate Suggested maximum audio bit rate in Kb/sec.
|
||||
* @param audio_bit_rate Suggested maximum audio bit rate in kbit/sec.
|
||||
*/
|
||||
typedef void toxav_audio_bit_rate_cb(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, void *user_data);
|
||||
|
||||
@ -627,9 +630,10 @@ void toxav_callback_audio_bit_rate(ToxAV *av, toxav_audio_bit_rate_cb *callback,
|
||||
/**
|
||||
* Send a video frame to a friend.
|
||||
*
|
||||
* Y - plane should be of size: `height * width`
|
||||
* U - plane should be of size: `(height/2) * (width/2)`
|
||||
* V - plane should be of size: `(height/2) * (width/2)`
|
||||
* The video frame needs to be planar YUV420.
|
||||
* Y - plane should be of size: `width * height`
|
||||
* U - plane should be of size: `(width/2) * (height/2)`
|
||||
* V - plane should be of size: `(width/2) * (height/2)`
|
||||
*
|
||||
* @param friend_number The friend number of the friend to which to send a video
|
||||
* frame.
|
||||
@ -641,9 +645,9 @@ void toxav_callback_audio_bit_rate(ToxAV *av, toxav_audio_bit_rate_cb *callback,
|
||||
*/
|
||||
bool toxav_video_send_frame(
|
||||
ToxAV *av, uint32_t friend_number, uint16_t width, uint16_t height,
|
||||
const uint8_t y[/*! height * width */],
|
||||
const uint8_t u[/*! height/2 * width/2 */],
|
||||
const uint8_t v[/*! height/2 * width/2 */],
|
||||
const uint8_t y[/*! width * height */],
|
||||
const uint8_t u[/*! width/2 * height/2 */],
|
||||
const uint8_t v[/*! width/2 * height/2 */],
|
||||
Toxav_Err_Send_Frame *error);
|
||||
|
||||
/**
|
||||
@ -651,7 +655,7 @@ bool toxav_video_send_frame(
|
||||
*
|
||||
* @param friend_number The friend number of the friend for which to set the
|
||||
* bit rate.
|
||||
* @param bit_rate The new video bit rate in Kb/sec. Set to 0 to disable.
|
||||
* @param bit_rate The new video bit rate in kbit/sec. Set to 0 to disable.
|
||||
*
|
||||
* @return true on success.
|
||||
*/
|
||||
@ -664,7 +668,7 @@ bool toxav_video_set_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t bit_ra
|
||||
*
|
||||
* @param friend_number The friend number of the friend for which to set the
|
||||
* bit rate.
|
||||
* @param video_bit_rate Suggested maximum video bit rate in Kb/sec.
|
||||
* @param video_bit_rate Suggested maximum video bit rate in kbit/sec.
|
||||
*/
|
||||
typedef void toxav_video_bit_rate_cb(ToxAV *av, uint32_t friend_number, uint32_t video_bit_rate, void *user_data);
|
||||
|
||||
|
35
toxav/toxav_hacks.h
Normal file
35
toxav/toxav_hacks.h
Normal file
@ -0,0 +1,35 @@
|
||||
/* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Copyright © 2016-2018 The TokTok team.
|
||||
* Copyright © 2013-2015 Tox project.
|
||||
*/
|
||||
#ifndef C_TOXCORE_TOXAV_HACKS_H
|
||||
#define C_TOXCORE_TOXAV_HACKS_H
|
||||
|
||||
#include "bwcontroller.h"
|
||||
#include "msi.h"
|
||||
#include "rtp.h"
|
||||
|
||||
#ifndef TOXAV_CALL_DEFINED
|
||||
#define TOXAV_CALL_DEFINED
|
||||
typedef struct ToxAVCall ToxAVCall;
|
||||
#endif /* TOXAV_CALL_DEFINED */
|
||||
|
||||
non_null()
|
||||
ToxAVCall *call_get(ToxAV *av, uint32_t friend_number);
|
||||
|
||||
non_null()
|
||||
RTPSession *rtp_session_get(ToxAVCall *call, int payload_type);
|
||||
|
||||
non_null()
|
||||
MSISession *tox_av_msi_get(const ToxAV *av);
|
||||
|
||||
non_null()
|
||||
BWController *bwc_controller_get(const ToxAVCall *call);
|
||||
|
||||
non_null()
|
||||
Mono_Time *toxav_get_av_mono_time(const ToxAV *av);
|
||||
|
||||
non_null()
|
||||
const Logger *toxav_get_logger(const ToxAV *av);
|
||||
|
||||
#endif /* C_TOXCORE_TOXAV_HACKS_H */
|
@ -143,7 +143,7 @@ static void vc_init_encoder_cfg(const Logger *log, vpx_codec_enc_cfg_t *cfg, int
|
||||
#endif /* 0 */
|
||||
}
|
||||
|
||||
VCSession *vc_new(Mono_Time *mono_time, const Logger *log, ToxAV *av, uint32_t friend_number,
|
||||
VCSession *vc_new(const Logger *log, Mono_Time *mono_time, ToxAV *av, uint32_t friend_number,
|
||||
toxav_video_receive_frame_cb *cb, void *cb_data)
|
||||
{
|
||||
VCSession *vc = (VCSession *)calloc(1, sizeof(VCSession));
|
||||
@ -216,7 +216,7 @@ VCSession *vc_new(Mono_Time *mono_time, const Logger *log, ToxAV *av, uint32_t f
|
||||
|
||||
/* Set encoder to some initial values
|
||||
*/
|
||||
vpx_codec_enc_cfg_t cfg;
|
||||
vpx_codec_enc_cfg_t cfg;
|
||||
vc_init_encoder_cfg(log, &cfg, 1);
|
||||
|
||||
LOGGER_DEBUG(log, "Using VP8 codec for encoder (0.1)");
|
||||
@ -250,6 +250,7 @@ VCSession *vc_new(Mono_Time *mono_time, const Logger *log, ToxAV *av, uint32_t f
|
||||
}
|
||||
|
||||
#endif /* 0 */
|
||||
|
||||
vc->linfts = current_time_monotonic(mono_time);
|
||||
vc->lcfd = 60;
|
||||
vc->vcb = cb;
|
||||
@ -258,12 +259,14 @@ VCSession *vc_new(Mono_Time *mono_time, const Logger *log, ToxAV *av, uint32_t f
|
||||
vc->av = av;
|
||||
vc->log = log;
|
||||
return vc;
|
||||
|
||||
BASE_CLEANUP_1:
|
||||
vpx_codec_destroy(vc->decoder);
|
||||
BASE_CLEANUP:
|
||||
pthread_mutex_destroy(vc->queue_mutex);
|
||||
rb_kill(vc->vbuf_raw);
|
||||
free(vc);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,6 @@ typedef struct VCSession {
|
||||
uint64_t linfts; /* Last received frame time stamp */
|
||||
uint32_t lcfd; /* Last calculated frame duration for incoming video payload */
|
||||
|
||||
const Logger *log;
|
||||
ToxAV *av;
|
||||
uint32_t friend_number;
|
||||
|
||||
@ -42,9 +41,10 @@ typedef struct VCSession {
|
||||
void *vcb_user_data;
|
||||
|
||||
pthread_mutex_t queue_mutex[1];
|
||||
const Logger *log;
|
||||
} VCSession;
|
||||
|
||||
VCSession *vc_new(Mono_Time *mono_time, const Logger *log, ToxAV *av, uint32_t friend_number,
|
||||
VCSession *vc_new(const Logger *log, Mono_Time *mono_time, ToxAV *av, uint32_t friend_number,
|
||||
toxav_video_receive_frame_cb *cb, void *cb_data);
|
||||
void vc_kill(VCSession *vc);
|
||||
void vc_iterate(VCSession *vc);
|
||||
|
Reference in New Issue
Block a user