toxcore/toxav/toxav.c

684 lines
20 KiB
C
Raw Normal View History

2014-02-10 06:06:44 +08:00
/** toxav.c
2014-02-17 09:01:30 +08:00
*
2014-02-10 06:06:44 +08:00
* Copyright (C) 2013 Tox project All Rights Reserved.
*
* This file is part of Tox.
*
* Tox is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tox is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#define __TOX_DEFINED__
typedef struct Messenger Tox;
2014-05-11 00:00:49 +08:00
#define _GNU_SOURCE /* implicit declaration warning */
2014-06-21 07:58:55 +08:00
#include "codec.h"
2014-02-10 06:06:44 +08:00
#include "msi.h"
#include "group.h"
2014-02-10 06:06:44 +08:00
2014-04-28 01:21:26 +08:00
#include "../toxcore/logger.h"
2014-11-18 07:46:46 +08:00
#include "../toxcore/util.h"
2014-04-28 01:21:26 +08:00
2014-05-11 00:00:49 +08:00
#include <assert.h>
2014-02-10 06:06:44 +08:00
#include <stdlib.h>
#include <string.h>
2014-02-16 03:44:33 +08:00
/* Assume 24 fps*/
2014-06-25 06:07:31 +08:00
#define MAX_ENCODE_TIME_US ((1000 / 24) * 1000)
2014-02-10 06:06:44 +08:00
/* call index invalid: true if invalid */
#define cii(c_idx, session) (c_idx < 0 || c_idx >= session->max_calls)
2014-07-27 06:26:58 +08:00
const ToxAvCSettings av_DefaultSettings = {
2014-11-18 07:46:46 +08:00
av_TypeAudio,
2014-07-27 09:26:32 +08:00
2014-07-27 06:26:58 +08:00
500,
1280,
720,
2014-07-27 09:26:32 +08:00
2014-07-27 06:26:58 +08:00
64000,
20,
48000,
1
};
2014-11-18 07:46:46 +08:00
static const uint32_t jbuf_capacity = 6;
2014-02-10 06:06:44 +08:00
static const uint8_t audio_index = 0, video_index = 1;
2014-05-11 00:00:49 +08:00
typedef struct _CallSpecific {
RTPSession *crtps[2]; /** Audio is first and video is second */
2014-11-18 07:46:46 +08:00
CSSession *cs;/** Each call have its own encoders and decoders.
2014-05-11 00:00:49 +08:00
* You can, but don't have to, reuse encoders for
* multiple calls. If you choose to reuse encoders,
* make sure to also reuse encoded payload for every call.
2014-11-18 07:46:46 +08:00
* Decoders have to be unique for each call.
2014-05-11 00:00:49 +08:00
*/
2014-07-03 23:13:11 +08:00
_Bool call_active;
2014-07-05 23:36:12 +08:00
pthread_mutex_t mutex;
2014-08-04 21:50:32 +08:00
} CallSpecific;
2014-02-10 06:06:44 +08:00
struct _ToxAv {
2014-02-17 09:01:30 +08:00
Messenger *messenger;
MSISession *msi_session; /** Main msi session */
CallSpecific *calls; /** Per-call params */
2014-04-28 01:21:26 +08:00
uint32_t max_calls;
2014-11-18 07:46:46 +08:00
/* Decode time measure */
int32_t dectmsscount; /** Measure count */
int32_t dectmsstotal; /** Last cycle total */
int32_t avgdectms; /** Average decoding time in ms */
2014-04-28 01:21:26 +08:00
};
2014-02-17 09:01:30 +08:00
2014-08-01 02:56:32 +08:00
2014-11-18 07:46:46 +08:00
static const MSICSettings *msicsettings_cast (const ToxAvCSettings *from)
2014-07-27 01:29:49 +08:00
{
2014-11-18 07:46:46 +08:00
assert(sizeof(MSICSettings) == sizeof(ToxAvCSettings));
return (const MSICSettings *) from;
2014-07-27 01:29:49 +08:00
}
static const ToxAvCSettings *toxavcsettings_cast (const MSICSettings *from)
2014-07-27 01:29:49 +08:00
{
2014-11-18 07:46:46 +08:00
assert(sizeof(MSICSettings) == sizeof(ToxAvCSettings));
return (const ToxAvCSettings *) from;
2014-02-10 06:06:44 +08:00
2014-07-27 01:29:49 +08:00
}
2014-04-28 01:21:26 +08:00
2014-11-18 07:46:46 +08:00
ToxAv *toxav_new( Tox *messenger, int32_t max_calls)
2014-02-16 03:44:33 +08:00
{
2014-02-17 09:01:30 +08:00
ToxAv *av = calloc ( sizeof(ToxAv), 1);
2014-05-24 22:02:01 +08:00
if (av == NULL) {
LOGGER_WARNING("Allocation failed!");
2014-02-16 03:44:33 +08:00
return NULL;
2014-05-24 22:02:01 +08:00
}
2014-02-16 03:44:33 +08:00
av->messenger = (Messenger *)messenger;
2014-11-18 07:46:46 +08:00
av->msi_session = msi_new(av->messenger, max_calls);
2014-02-10 06:06:44 +08:00
av->msi_session->agent_handler = av;
2014-05-11 00:00:49 +08:00
av->calls = calloc(sizeof(CallSpecific), max_calls);
2014-04-28 01:21:26 +08:00
av->max_calls = max_calls;
2014-02-17 09:01:30 +08:00
2014-02-10 06:06:44 +08:00
return av;
}
2014-02-17 09:01:30 +08:00
void toxav_kill ( ToxAv *av )
{
2014-08-04 06:15:00 +08:00
uint32_t i;
2014-08-01 02:56:32 +08:00
for (i = 0; i < av->max_calls; i ++) {
if ( av->calls[i].crtps[audio_index] )
2014-11-18 07:46:46 +08:00
rtp_kill(av->calls[i].crtps[audio_index], av->msi_session->messenger_handle);
if ( av->calls[i].crtps[video_index] )
2014-11-18 07:46:46 +08:00
rtp_kill(av->calls[i].crtps[video_index], av->msi_session->messenger_handle);
2014-02-17 09:01:30 +08:00
2014-11-18 07:46:46 +08:00
if ( av->calls[i].cs ) cs_kill(av->calls[i].cs);
2014-02-10 06:06:44 +08:00
}
2014-11-18 07:46:46 +08:00
msi_kill(av->msi_session);
2014-05-11 00:00:49 +08:00
free(av->calls);
2014-02-10 06:06:44 +08:00
free(av);
}
uint32_t toxav_do_interval(ToxAv *av)
2014-02-10 06:06:44 +08:00
{
2014-11-18 07:46:46 +08:00
int i = 0;
uint32_t rc = 200 + av->avgdectms; /* Return 200 if no call is active */
2014-11-18 07:46:46 +08:00
for (; i < av->max_calls; i ++) if (av->calls[i].call_active) {
/* This should work. Video payload will always come in greater intervals */
rc = MIN(av->calls[i].cs->audio_decoder_frame_duration, rc);
}
2014-11-26 06:22:44 +08:00
if (rc < av->avgdectms) {
return 0;
} else {
return rc - av->avgdectms;
}
2014-02-10 06:06:44 +08:00
}
void toxav_do(ToxAv *av)
2014-02-10 06:06:44 +08:00
{
2014-11-18 07:46:46 +08:00
msi_do(av->msi_session);
2014-11-18 07:46:46 +08:00
uint64_t start = current_time_monotonic();
2014-11-18 07:46:46 +08:00
uint32_t i = 0;
for (; i < av->max_calls; i ++)
2014-11-18 07:46:46 +08:00
if (av->calls[i].call_active) cs_do(av->calls[i].cs);
2014-11-18 07:46:46 +08:00
uint64_t end = current_time_monotonic();
2014-11-18 07:46:46 +08:00
/* TODO maybe use variable for sizes */
av->dectmsstotal += end - start;
2014-11-18 07:46:46 +08:00
if (++av->dectmsscount == 3) {
av->avgdectms = av->dectmsstotal / 3 + 2 /* NOTE Magic Offset */;
av->dectmsscount = 0;
av->dectmsstotal = 0;
}
2014-02-10 06:06:44 +08:00
}
void toxav_register_callstate_callback ( ToxAv *av, ToxAVCallback cb, ToxAvCallbackID id, void *userdata )
2014-07-08 04:10:10 +08:00
{
2014-11-18 07:46:46 +08:00
msi_register_callback(av->msi_session, (MSICallbackType)cb, (MSICallbackID) id, userdata);
2014-07-08 04:10:10 +08:00
}
void toxav_register_audio_callback(ToxAvAudioCallback cb, void *userdata)
2014-07-08 04:10:10 +08:00
{
2014-11-18 07:46:46 +08:00
cs_register_audio_callback(cb, userdata);
2014-07-08 04:10:10 +08:00
}
void toxav_register_video_callback(ToxAvVideoCallback cb, void *userdata)
2014-11-18 07:46:46 +08:00
{
cs_register_video_callback(cb, userdata);
}
int toxav_call (ToxAv *av,
int32_t *call_index,
int user,
const ToxAvCSettings *csettings,
2014-11-18 07:46:46 +08:00
int ringing_seconds )
2014-02-10 06:06:44 +08:00
{
2014-07-27 01:29:49 +08:00
return msi_invite(av->msi_session, call_index, msicsettings_cast(csettings), ringing_seconds * 1000, user);
2014-02-10 06:06:44 +08:00
}
int toxav_hangup ( ToxAv *av, int32_t call_index )
2014-02-10 06:06:44 +08:00
{
if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) {
2014-11-18 07:46:46 +08:00
return av_ErrorNoCall;
2014-02-10 06:06:44 +08:00
}
2014-02-17 09:01:30 +08:00
2014-04-28 01:21:26 +08:00
if ( av->msi_session->calls[call_index]->state != call_active ) {
2014-11-18 07:46:46 +08:00
return av_ErrorInvalidState;
2014-02-10 06:06:44 +08:00
}
2014-02-17 09:01:30 +08:00
2014-04-28 01:21:26 +08:00
return msi_hangup(av->msi_session, call_index);
2014-02-10 06:06:44 +08:00
}
2014-07-27 09:26:32 +08:00
int toxav_answer ( ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings )
2014-02-10 06:06:44 +08:00
{
if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) {
2014-11-18 07:46:46 +08:00
return av_ErrorNoCall;
2014-02-10 06:06:44 +08:00
}
2014-02-17 09:01:30 +08:00
2014-04-28 01:21:26 +08:00
if ( av->msi_session->calls[call_index]->state != call_starting ) {
2014-11-18 07:46:46 +08:00
return av_ErrorInvalidState;
2014-02-10 06:06:44 +08:00
}
2014-02-17 09:01:30 +08:00
2014-07-27 01:29:49 +08:00
return msi_answer(av->msi_session, call_index, msicsettings_cast(csettings));
2014-02-10 06:06:44 +08:00
}
int toxav_reject ( ToxAv *av, int32_t call_index, const char *reason )
2014-02-10 06:06:44 +08:00
{
if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) {
2014-11-18 07:46:46 +08:00
return av_ErrorNoCall;
2014-02-10 06:06:44 +08:00
}
2014-04-28 01:21:26 +08:00
if ( av->msi_session->calls[call_index]->state != call_starting ) {
2014-11-18 07:46:46 +08:00
return av_ErrorInvalidState;
2014-02-10 06:06:44 +08:00
}
2014-07-24 01:26:55 +08:00
return msi_reject(av->msi_session, call_index, reason);
2014-02-10 06:06:44 +08:00
}
int toxav_cancel ( ToxAv *av, int32_t call_index, int peer_id, const char *reason )
2014-02-10 06:06:44 +08:00
{
if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) {
2014-11-18 07:46:46 +08:00
return av_ErrorNoCall;
2014-02-10 06:06:44 +08:00
}
2014-02-17 09:01:30 +08:00
if ( av->msi_session->calls[call_index]->state != call_inviting ) {
2014-11-18 07:46:46 +08:00
return av_ErrorInvalidState;
}
2014-04-28 01:21:26 +08:00
return msi_cancel(av->msi_session, call_index, peer_id, reason);
2014-02-10 06:06:44 +08:00
}
2014-07-27 09:26:32 +08:00
int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings)
{
if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) {
2014-11-18 07:46:46 +08:00
return av_ErrorNoCall;
}
2014-07-27 01:29:49 +08:00
return msi_change_csettings(av->msi_session, call_index, msicsettings_cast(csettings));
}
int toxav_stop_call ( ToxAv *av, int32_t call_index )
2014-02-10 06:06:44 +08:00
{
if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) {
2014-11-18 07:46:46 +08:00
return av_ErrorNoCall;
2014-02-10 06:06:44 +08:00
}
2014-02-17 09:01:30 +08:00
2014-04-28 01:21:26 +08:00
return msi_stopcall(av->msi_session, call_index);
2014-02-10 06:06:44 +08:00
}
int toxav_prepare_transmission ( ToxAv *av, int32_t call_index, int support_video )
2014-02-10 06:06:44 +08:00
{
2014-07-03 23:13:11 +08:00
if ( !av->msi_session || cii(call_index, av->msi_session) ||
2014-07-27 09:26:32 +08:00
!av->msi_session->calls[call_index] || !av->msi_session->calls[call_index]->csettings_peer ||
av->calls[call_index].call_active) {
LOGGER_ERROR("Error while starting RTP session: invalid call!\n");
2014-11-18 07:46:46 +08:00
return av_ErrorInternal;
2014-02-10 06:06:44 +08:00
}
2014-02-17 09:01:30 +08:00
CallSpecific *call = &av->calls[call_index];
2014-11-18 07:46:46 +08:00
if ( pthread_mutex_init(&call->mutex, NULL) != 0 ) {
LOGGER_WARNING("Failed to init call mutex!");
return av_ErrorInternal;
}
2014-02-17 09:01:30 +08:00
const ToxAvCSettings *c_peer = toxavcsettings_cast
(&av->msi_session->calls[call_index]->csettings_peer[0]);
const ToxAvCSettings *c_self = toxavcsettings_cast
(&av->msi_session->calls[call_index]->csettings_local);
2014-11-18 07:46:46 +08:00
LOGGER_DEBUG(
"Type: %u(s) %u(p)\n"
"Video bitrate: %u(s) %u(p)\n"
"Video height: %u(s) %u(p)\n"
"Video width: %u(s) %u(p)\n"
"Audio bitrate: %u(s) %u(p)\n"
"Audio framedur: %u(s) %u(p)\n"
"Audio sample rate: %u(s) %u(p)\n"
"Audio channels: %u(s) %u(p)\n",
c_self->call_type, c_peer->call_type,
c_self->video_bitrate, c_peer->video_bitrate,
c_self->max_video_height, c_peer->max_video_height,
c_self->max_video_width, c_peer->max_video_width,
c_self->audio_bitrate, c_peer->audio_bitrate,
c_self->audio_frame_duration, c_peer->audio_frame_duration,
c_self->audio_sample_rate, c_peer->audio_sample_rate,
c_self->audio_channels, c_peer->audio_channels );
if ( !(call->cs = cs_new(c_self, c_peer, jbuf_capacity, support_video)) ) {
2014-11-18 07:46:46 +08:00
pthread_mutex_destroy(&call->mutex);
LOGGER_ERROR("Error while starting Codec State!\n");
return av_ErrorInternal;
}
2014-11-18 07:46:46 +08:00
call->cs->agent = av;
call->cs->call_idx = call_index;
call->crtps[audio_index] =
2014-11-18 07:46:46 +08:00
rtp_new(type_audio, av->messenger, av->msi_session->calls[call_index]->peers[0]);
2014-02-17 09:01:30 +08:00
2014-05-11 00:00:49 +08:00
if ( !call->crtps[audio_index] ) {
LOGGER_ERROR("Error while starting audio RTP session!\n");
2014-11-18 07:46:46 +08:00
return av_ErrorInternal;
2014-02-10 06:06:44 +08:00
}
2014-02-17 09:01:30 +08:00
2014-11-18 07:46:46 +08:00
call->crtps[audio_index]->cs = call->cs;
if ( support_video ) {
call->crtps[video_index] =
2014-11-18 07:46:46 +08:00
rtp_new(type_video, av->messenger, av->msi_session->calls[call_index]->peers[0]);
2014-04-28 01:21:26 +08:00
2014-05-11 00:00:49 +08:00
if ( !call->crtps[video_index] ) {
LOGGER_ERROR("Error while starting video RTP session!\n");
2014-07-05 21:11:25 +08:00
goto error;
}
2014-11-18 07:46:46 +08:00
call->crtps[video_index]->cs = call->cs;
}
2014-11-18 07:46:46 +08:00
call->call_active = 1;
return av_ErrorNone;
2014-07-05 21:11:25 +08:00
error:
2014-11-18 07:46:46 +08:00
rtp_kill(call->crtps[audio_index], av->messenger);
rtp_kill(call->crtps[video_index], av->messenger);
cs_kill(call->cs);
pthread_mutex_destroy(&call->mutex);
memset(call, 0, sizeof(CallSpecific));
2014-07-03 23:13:11 +08:00
2014-11-18 07:46:46 +08:00
return av_ErrorInternal;
2014-02-10 06:06:44 +08:00
}
2014-05-17 01:56:40 +08:00
int toxav_kill_transmission ( ToxAv *av, int32_t call_index )
2014-02-10 06:06:44 +08:00
{
if (cii(call_index, av->msi_session)) {
LOGGER_WARNING("Invalid call index: %d", call_index);
2014-11-18 07:46:46 +08:00
return av_ErrorNoCall;
}
2014-06-15 22:36:57 +08:00
CallSpecific *call = &av->calls[call_index];
2014-07-05 23:36:12 +08:00
pthread_mutex_lock(&call->mutex);
if (!call->call_active) {
pthread_mutex_unlock(&call->mutex);
LOGGER_WARNING("Action on inactive call: %d", call_index);
2014-11-18 07:46:46 +08:00
return av_ErrorNoCall;
}
rtp_kill(call->crtps[audio_index], av->messenger);
2014-07-06 00:25:30 +08:00
call->crtps[audio_index] = NULL;
rtp_kill(call->crtps[video_index], av->messenger);
2014-07-06 00:25:30 +08:00
call->crtps[video_index] = NULL;
cs_kill(call->cs);
2014-07-06 00:25:30 +08:00
call->cs = NULL;
call->call_active = 0;
2014-07-05 23:36:12 +08:00
pthread_mutex_unlock(&call->mutex);
pthread_mutex_destroy(&call->mutex);
2014-07-03 23:13:11 +08:00
2014-11-18 07:46:46 +08:00
return av_ErrorNone;
2014-02-10 06:06:44 +08:00
}
static int toxav_send_rtp_payload(ToxAv *av,
CallSpecific *call,
ToxAvCallType type,
2014-11-18 07:46:46 +08:00
const uint8_t *payload,
unsigned int length)
2014-02-10 06:06:44 +08:00
{
2014-11-18 07:46:46 +08:00
if (call->crtps[type - av_TypeAudio]) {
2014-07-06 00:25:30 +08:00
2014-11-18 07:46:46 +08:00
/* Audio */
if (type == av_TypeAudio)
2014-11-18 07:46:46 +08:00
return rtp_send_msg(call->crtps[audio_index], av->messenger, payload, length);
2014-11-18 07:46:46 +08:00
/* Video */
int parts = cs_split_video_payload(call->cs, payload, length);
2014-06-24 04:34:06 +08:00
2014-11-18 07:46:46 +08:00
if (parts == -1) return av_ErrorInternal;
uint16_t part_size;
const uint8_t *iter;
2014-11-18 07:46:46 +08:00
int i;
2014-06-24 04:34:06 +08:00
2014-11-18 07:46:46 +08:00
for (i = 0; i < parts; i++) {
iter = cs_get_split_video_frame(call->cs, &part_size);
2014-06-24 04:34:06 +08:00
if (rtp_send_msg(call->crtps[video_index], av->messenger, iter, part_size) != 0)
2014-11-18 07:46:46 +08:00
return av_ErrorInternal;
}
2014-11-18 07:46:46 +08:00
return av_ErrorNone;
2014-07-03 23:13:11 +08:00
2014-11-18 07:46:46 +08:00
} else return av_ErrorNoRtpSession;
2014-02-16 03:44:33 +08:00
}
int toxav_prepare_video_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, vpx_image_t *input)
2014-02-10 06:06:44 +08:00
{
if (cii(call_index, av->msi_session)) {
LOGGER_WARNING("Invalid call index: %d", call_index);
2014-11-18 07:46:46 +08:00
return av_ErrorNoCall;
}
2014-07-03 23:13:11 +08:00
2014-06-15 22:36:57 +08:00
CallSpecific *call = &av->calls[call_index];
2014-07-05 23:36:12 +08:00
pthread_mutex_lock(&call->mutex);
2014-07-06 00:25:30 +08:00
if (!call->call_active) {
pthread_mutex_unlock(&call->mutex);
LOGGER_WARNING("Action on inactive call: %d", call_index);
2014-11-18 07:46:46 +08:00
return av_ErrorNoCall;
}
2014-11-18 07:46:46 +08:00
if (cs_set_video_encoder_resolution(call->cs, input->d_w, input->d_h) != 0) {
pthread_mutex_unlock(&call->mutex);
2014-11-18 07:46:46 +08:00
return av_ErrorInternal;
}
2014-05-11 00:00:49 +08:00
int rc = vpx_codec_encode(&call->cs->v_encoder, input, call->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US);
2014-05-03 07:46:03 +08:00
if ( rc != VPX_CODEC_OK) {
LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(rc));
2014-07-05 23:36:12 +08:00
pthread_mutex_unlock(&call->mutex);
2014-11-18 07:46:46 +08:00
return av_ErrorInternal;
2014-02-16 03:44:33 +08:00
}
2014-02-17 09:01:30 +08:00
2014-05-11 00:00:49 +08:00
++call->cs->frame_counter;
2014-02-17 09:01:30 +08:00
2014-02-16 03:44:33 +08:00
vpx_codec_iter_t iter = NULL;
const vpx_codec_cx_pkt_t *pkt;
2014-05-11 00:00:49 +08:00
int copied = 0;
2014-02-17 09:01:30 +08:00
2014-05-11 00:00:49 +08:00
while ( (pkt = vpx_codec_get_cx_data(&call->cs->v_encoder, &iter)) ) {
2014-02-16 03:44:33 +08:00
if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
2014-07-06 00:25:30 +08:00
if ( copied + pkt->data.frame.sz > dest_max ) {
2014-07-05 23:36:12 +08:00
pthread_mutex_unlock(&call->mutex);
2014-11-18 07:46:46 +08:00
return av_ErrorPacketTooLarge;
2014-07-05 21:11:25 +08:00
}
memcpy(dest + copied, pkt->data.frame.buf, pkt->data.frame.sz);
2014-05-11 00:00:49 +08:00
copied += pkt->data.frame.sz;
2014-02-16 03:44:33 +08:00
}
}
2014-02-17 09:01:30 +08:00
2014-07-05 23:36:12 +08:00
pthread_mutex_unlock(&call->mutex);
2014-05-11 00:00:49 +08:00
return copied;
2014-02-16 03:44:33 +08:00
}
2014-11-18 07:46:46 +08:00
int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int frame_size)
2014-02-16 03:44:33 +08:00
{
2014-11-18 07:46:46 +08:00
if (cii(call_index, av->msi_session)) {
LOGGER_WARNING("Invalid call index: %d", call_index);
return av_ErrorNoCall;
}
2014-07-03 23:13:11 +08:00
CallSpecific *call = &av->calls[call_index];
pthread_mutex_lock(&call->mutex);
if (!call->call_active) {
pthread_mutex_unlock(&call->mutex);
LOGGER_WARNING("Action on inactive call: %d", call_index);
2014-11-18 07:46:46 +08:00
return av_ErrorNoCall;
}
2014-06-15 22:36:57 +08:00
2014-11-18 07:46:46 +08:00
int rc = toxav_send_rtp_payload(av, call, av_TypeVideo, frame, frame_size);
pthread_mutex_unlock(&call->mutex);
2014-07-06 00:25:30 +08:00
2014-07-05 21:11:25 +08:00
return rc;
2014-05-03 07:46:03 +08:00
}
2014-02-17 09:01:30 +08:00
int toxav_prepare_audio_frame ( ToxAv *av,
int32_t call_index,
uint8_t *dest,
int dest_max,
2014-11-18 07:46:46 +08:00
const int16_t *frame,
int frame_size)
2014-02-16 03:44:33 +08:00
{
2014-07-03 23:13:11 +08:00
if (cii(call_index, av->msi_session) || !av->calls[call_index].call_active) {
LOGGER_WARNING("Action on inactive call: %d", call_index);
2014-11-18 07:46:46 +08:00
return av_ErrorNoCall;
}
2014-07-03 23:13:11 +08:00
CallSpecific *call = &av->calls[call_index];
pthread_mutex_lock(&call->mutex);
2014-06-15 22:36:57 +08:00
2014-07-06 00:25:30 +08:00
if (!call->call_active) {
pthread_mutex_unlock(&call->mutex);
LOGGER_WARNING("Action on inactive call: %d", call_index);
2014-11-18 07:46:46 +08:00
return av_ErrorNoCall;
}
2014-02-17 09:01:30 +08:00
int32_t rc = opus_encode(call->cs->audio_encoder, frame, frame_size, dest, dest_max);
pthread_mutex_unlock(&call->mutex);
2014-07-05 21:11:25 +08:00
2014-05-11 00:00:49 +08:00
if (rc < 0) {
LOGGER_ERROR("Failed to encode payload: %s\n", opus_strerror(rc));
2014-11-18 07:46:46 +08:00
return av_ErrorInternal;
2014-05-11 00:00:49 +08:00
}
2014-02-16 03:44:33 +08:00
2014-05-03 07:46:03 +08:00
return rc;
2014-02-10 06:06:44 +08:00
}
2014-11-18 07:46:46 +08:00
int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *data, unsigned int size)
{
if (size > MAX_CRYPTO_DATA_SIZE)
return av_ErrorInternal;
if (cii(call_index, av->msi_session) || !av->calls[call_index].call_active) {
LOGGER_WARNING("Action on inactive call: %d", call_index);
return av_ErrorNoCall;
}
CallSpecific *call = &av->calls[call_index];
pthread_mutex_lock(&call->mutex);
if (!call->call_active) {
pthread_mutex_unlock(&call->mutex);
LOGGER_WARNING("Action on inactive call: %d", call_index);
return av_ErrorNoCall;
}
int rc = toxav_send_rtp_payload(av, call, av_TypeAudio, data, size);
pthread_mutex_unlock(&call->mutex);
2014-02-16 03:44:33 +08:00
2014-05-03 07:46:03 +08:00
return rc;
2014-02-10 06:06:44 +08:00
}
2014-07-27 09:26:32 +08:00
int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest )
2014-02-10 06:06:44 +08:00
{
2014-06-15 22:36:57 +08:00
if ( peer < 0 || cii(call_index, av->msi_session) || !av->msi_session->calls[call_index]
|| av->msi_session->calls[call_index]->peer_count <= peer )
2014-11-18 07:46:46 +08:00
return av_ErrorInternal;
2014-02-17 09:01:30 +08:00
2014-11-18 07:46:46 +08:00
*dest = *toxavcsettings_cast(&av->msi_session->calls[call_index]->csettings_peer[peer]);
return av_ErrorNone;
2014-02-10 06:06:44 +08:00
}
int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer )
{
2014-06-15 22:36:57 +08:00
if ( peer < 0 || cii(call_index, av->msi_session) || !av->msi_session->calls[call_index]
|| av->msi_session->calls[call_index]->peer_count <= peer )
2014-11-18 07:46:46 +08:00
return av_ErrorInternal;
2014-04-28 01:21:26 +08:00
return av->msi_session->calls[call_index]->peers[peer];
}
2014-07-05 01:28:47 +08:00
ToxAvCallState toxav_get_call_state(ToxAv *av, int32_t call_index)
{
2014-07-05 00:16:53 +08:00
if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] )
return av_CallNonExistant;
2014-07-05 01:28:47 +08:00
2014-07-05 00:16:53 +08:00
return av->msi_session->calls[call_index]->state;
}
2014-11-18 07:46:46 +08:00
int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability )
{
2014-11-18 07:46:46 +08:00
return av->calls[call_index].cs ? av->calls[call_index].cs->capabilities & (CsCapabilities) capability : 0;
/* 0 is error here */
}
2014-11-18 07:46:46 +08:00
Tox *toxav_get_tox(ToxAv *av)
2014-05-17 01:56:40 +08:00
{
return (Tox *)av->messenger;
2014-06-21 07:58:55 +08:00
}
int toxav_set_vad_treshold(ToxAv *av, int32_t call_index, uint32_t treshold)
{
2014-11-18 07:46:46 +08:00
if ( !av->calls[call_index].cs ) return av_ErrorInvalidCodecState;
2014-11-18 07:46:46 +08:00
/* TODO on't use default framedur... */
cs_set_vad_treshold(av->calls[call_index].cs, treshold, av_DefaultSettings.audio_frame_duration);
2014-08-02 01:21:03 +08:00
2014-11-18 07:46:46 +08:00
return av_ErrorNone;
}
2014-11-18 07:46:46 +08:00
int toxav_has_activity(ToxAv *av, int32_t call_index, int16_t *PCM, uint16_t frame_size, float ref)
2014-08-01 02:56:32 +08:00
{
2014-11-18 07:46:46 +08:00
if ( !av->calls[call_index].cs ) return av_ErrorInvalidCodecState;
2014-11-18 07:46:46 +08:00
return cs_calculate_vad(av->calls[call_index].cs, PCM, frame_size, ref);
2014-08-01 02:56:32 +08:00
}
int toxav_get_active_count(ToxAv *av)
2014-07-08 04:10:10 +08:00
{
2014-11-18 07:46:46 +08:00
if (!av) return av_ErrorInternal;
2014-07-08 04:10:10 +08:00
2014-11-18 07:46:46 +08:00
int rc = 0, i = 0;
2014-08-02 01:21:03 +08:00
2014-11-18 07:46:46 +08:00
for (; i < av->max_calls; i ++) if (av->calls[i].call_active) rc++;
2014-07-27 09:26:32 +08:00
2014-11-18 07:46:46 +08:00
return rc;
2014-07-08 04:10:10 +08:00
}
/* Create a new toxav group.
*
* return group number on success.
* return -1 on failure.
2014-11-11 22:48:52 +08:00
*
* Audio data callback format:
* audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata)
*
* Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
*/
int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(Messenger *, int, int, const int16_t *, unsigned int,
uint8_t, unsigned int, void *), void *userdata)
{
Messenger *m = tox;
return add_av_groupchat(m->group_chat_object, audio_callback, userdata);
}
/* Join a AV group (you need to have been invited first.)
*
* returns group number on success
* returns -1 on failure.
2014-11-11 22:48:52 +08:00
*
* Audio data callback format (same as the one for toxav_add_av_groupchat()):
* audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata)
*
* Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
*/
int toxav_join_av_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length,
void (*audio_callback)(Messenger *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *),
void *userdata)
{
Messenger *m = tox;
return join_av_groupchat(m->group_chat_object, friendnumber, data, length, audio_callback, userdata);
}
/* Send audio to the group chat.
*
* return 0 on success.
* return -1 on failure.
*
* Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
*
* Valid number of samples are ((sample rate) * (audio length (Valid ones are: 2.5, 5, 10, 20, 40 or 60 ms)) / 1000)
* Valid number of channels are 1 or 2.
* Valid sample rates are 8000, 12000, 16000, 24000, or 48000.
*
* Recommended values are: samples = 960, channels = 1, sample_rate = 48000
*/
int toxav_group_send_audio(Tox *tox, int groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels,
unsigned int sample_rate)
{
Messenger *m = tox;
return group_send_audio(m->group_chat_object, groupnumber, pcm, samples, channels, sample_rate);
}