toxcore/toxav/toxav.c

870 lines
25 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 */
2014-05-11 00:00:49 +08:00
#define _GNU_SOURCE /* implicit declaration warning */
2014-02-10 06:06:44 +08:00
#include "rtp.h"
2014-06-21 07:58:55 +08:00
#include "codec.h"
2014-02-10 06:06:44 +08:00
#include "msi.h"
2014-05-11 00:00:49 +08:00
#include "toxav.h"
2014-02-10 06:06:44 +08:00
2014-04-28 01:21:26 +08:00
#include "../toxcore/logger.h"
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 60 fps*/
2014-06-25 06:07:31 +08:00
#define MAX_ENCODE_TIME_US ((1000 / 24) * 1000)
#define MAX_VIDEOFRAME_SIZE 0x40000 /* 256KiB */
#define VIDEOFRAME_PIECE_SIZE 0x500 /* 1.25 KiB*/
#define VIDEOFRAME_HEADER_SIZE 0x2
2014-02-10 06:06:44 +08:00
#define inline__ inline __attribute__((always_inline))
/* call index invalid: true if invalid */
#define cii(c_idx, session) (c_idx < 0 || c_idx >= session->max_calls)
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 */
CodecState *cs;/** Each call have its own encoders and decoders.
* 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.
* Decoders have to be unique for each call. FIXME: Now add refcounted encoders and
* reuse them really.
*/
JitterBuffer *j_buf; /** Jitter buffer for audio */
uint32_t frame_limit; /* largest address written to in frame_buf for current input frame*/
uint8_t frame_id, frame_outid; /* id of input and output video frame */
void *frame_buf; /* buffer for split video payloads */
_Bool call_active;
2014-05-11 00:00:49 +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-02-17 09:01:30 +08:00
2014-04-28 01:21:26 +08:00
const ToxAvCodecSettings av_DefaultSettings = {
500,
2014-04-28 01:21:26 +08:00
800,
600,
2014-02-17 09:01:30 +08:00
2014-04-28 01:21:26 +08:00
64000,
20,
48000,
1,
600,
6
};
2014-02-10 06:06:44 +08:00
2014-04-28 01:21:26 +08:00
2014-02-16 04:29:41 +08:00
/**
* @brief Start new A/V session. There can only be one session at the time. If you register more
* it will result in undefined behaviour.
2014-02-17 09:01:30 +08:00
*
2014-02-16 04:29:41 +08:00
* @param messenger The messenger handle.
* @param userdata The agent handling A/V session (i.e. phone).
2014-02-16 04:29:41 +08:00
* @param video_width Width of video frame.
* @param video_height Height of video frame.
* @return ToxAv*
* @retval NULL On error.
*/
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-04-28 01:21:26 +08:00
av->msi_session = msi_init_session(av->messenger, max_calls);
2014-02-10 06:06:44 +08:00
av->msi_session->agent_handler = av;
2014-02-17 09:01:30 +08:00
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-16 04:29:41 +08:00
/**
* @brief Remove A/V session.
2014-02-17 09:01:30 +08:00
*
2014-02-16 04:29:41 +08:00
* @param av Handler.
* @return void
*/
2014-02-17 09:01:30 +08:00
void toxav_kill ( ToxAv *av )
{
2014-02-10 06:06:44 +08:00
msi_terminate_session(av->msi_session);
2014-02-17 09:01:30 +08:00
2014-04-28 01:21:26 +08:00
int i = 0;
2014-02-17 09:01:30 +08:00
2014-04-28 01:21:26 +08:00
for (; i < av->max_calls; i ++) {
if ( av->calls[i].crtps[audio_index] )
2014-05-11 00:00:49 +08:00
rtp_terminate_session(av->calls[i].crtps[audio_index], av->msi_session->messenger_handle);
if ( av->calls[i].crtps[video_index] )
2014-05-11 00:00:49 +08:00
rtp_terminate_session(av->calls[i].crtps[video_index], av->msi_session->messenger_handle);
2014-02-17 09:01:30 +08:00
2014-05-18 05:15:54 +08:00
if ( av->calls[i].j_buf ) terminate_queue(av->calls[i].j_buf);
2014-05-18 05:15:54 +08:00
if ( av->calls[i].cs ) codec_terminate_session(av->calls[i].cs);
2014-02-10 06:06:44 +08:00
}
2014-05-11 00:00:49 +08:00
free(av->calls);
2014-02-10 06:06:44 +08:00
free(av);
}
2014-02-16 04:29:41 +08:00
/**
* @brief Register callback for call state.
2014-02-17 09:01:30 +08:00
*
2014-02-16 04:29:41 +08:00
* @param callback The callback
* @param id One of the ToxAvCallbackID values
* @return void
*/
void toxav_register_callstate_callback ( ToxAVCallback callback, ToxAvCallbackID id, void *userdata )
2014-02-10 06:06:44 +08:00
{
msi_register_callback((MSICallback)callback, (MSICallbackID) id, userdata);
2014-02-10 06:06:44 +08:00
}
2014-02-16 04:29:41 +08:00
/**
* @brief Call user. Use its friend_id.
2014-02-17 09:01:30 +08:00
*
2014-02-16 04:29:41 +08:00
* @param av Handler.
* @param user The user.
* @param call_type Call type.
* @param ringing_seconds Ringing timeout.
* @return int
* @retval 0 Success.
* @retval ToxAvError On error.
*/
int toxav_call (ToxAv *av, int32_t *call_index, int user, ToxAvCallType call_type, int ringing_seconds )
2014-02-10 06:06:44 +08:00
{
2014-04-28 01:21:26 +08:00
return msi_invite(av->msi_session, call_index, call_type, ringing_seconds * 1000, user);
2014-02-10 06:06:44 +08:00
}
2014-02-16 04:29:41 +08:00
/**
* @brief Hangup active call.
2014-02-17 09:01:30 +08:00
*
2014-02-16 04:29:41 +08:00
* @param av Handler.
* @return int
* @retval 0 Success.
* @retval ToxAvError On error.
*/
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-02-10 06:06:44 +08:00
return ErrorNoCall;
}
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-02-10 06:06:44 +08:00
return ErrorInvalidState;
}
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-02-16 04:29:41 +08:00
/**
* @brief Answer incomming call.
2014-02-17 09:01:30 +08:00
*
2014-02-16 04:29:41 +08:00
* @param av Handler.
* @param call_type Answer with...
* @return int
* @retval 0 Success.
* @retval ToxAvError On error.
*/
int toxav_answer ( ToxAv *av, int32_t call_index, ToxAvCallType call_type )
2014-02-10 06:06:44 +08:00
{
if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) {
2014-02-10 06:06:44 +08:00
return ErrorNoCall;
}
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-02-10 06:06:44 +08:00
return ErrorInvalidState;
}
2014-02-17 09:01:30 +08:00
2014-04-28 01:21:26 +08:00
return msi_answer(av->msi_session, call_index, call_type);
2014-02-10 06:06:44 +08:00
}
2014-02-16 04:29:41 +08:00
/**
* @brief Reject incomming call.
2014-02-17 09:01:30 +08:00
*
2014-02-16 04:29:41 +08:00
* @param av Handler.
* @param reason Optional reason. Set NULL if none.
* @return int
* @retval 0 Success.
* @retval ToxAvError On error.
*/
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-02-10 06:06:44 +08:00
return ErrorNoCall;
}
2014-04-28 01:21:26 +08:00
if ( av->msi_session->calls[call_index]->state != call_starting ) {
2014-02-10 06:06:44 +08:00
return ErrorInvalidState;
}
2014-04-28 01:21:26 +08:00
return msi_reject(av->msi_session, call_index, (const uint8_t *) reason);
2014-02-10 06:06:44 +08:00
}
2014-02-16 04:29:41 +08:00
/**
* @brief Cancel outgoing request.
2014-02-17 09:01:30 +08:00
*
2014-02-16 04:29:41 +08:00
* @param av Handler.
* @param reason Optional reason.
2014-03-23 02:33:56 +08:00
* @param peer_id peer friend_id
2014-02-16 04:29:41 +08:00
* @return int
* @retval 0 Success.
* @retval ToxAvError On error.
*/
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-02-10 06:06:44 +08:00
return ErrorNoCall;
}
2014-02-17 09:01:30 +08:00
if ( av->msi_session->calls[call_index]->state != call_inviting ) {
return 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-02-16 04:29:41 +08:00
/**
* @brief Terminate transmission. Note that transmission will be terminated without informing remote peer.
2014-02-17 09:01:30 +08:00
*
2014-02-16 04:29:41 +08:00
* @param av Handler.
* @return int
* @retval 0 Success.
* @retval ToxAvError On error.
*/
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-02-10 06:06:44 +08:00
return ErrorNoCall;
}
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
}
2014-02-16 04:29:41 +08:00
/**
* @brief Must be call before any RTP transmission occurs.
2014-02-17 09:01:30 +08:00
*
2014-02-16 04:29:41 +08:00
* @param av Handler.
* @return int
* @retval 0 Success.
* @retval ToxAvError On error.
*/
int toxav_prepare_transmission ( ToxAv *av, int32_t call_index, ToxAvCodecSettings *codec_settings, int support_video )
2014-02-10 06:06:44 +08:00
{
if ( !av->msi_session || cii(call_index, av->msi_session) ||
!av->msi_session->calls[call_index] || av->calls[call_index].call_active) {
LOGGER_ERROR("Error while starting RTP session: invalid call!\n");
2014-05-03 07:46:03 +08:00
return ErrorInternal;
2014-02-10 06:06:44 +08:00
}
2014-02-17 09:01:30 +08:00
CallSpecific *call = &av->calls[call_index];
call->crtps[audio_index] =
rtp_init_session(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-02-10 06:06:44 +08:00
return ErrorStartingAudioRtp;
}
2014-02-17 09:01:30 +08:00
if ( support_video ) {
call->crtps[video_index] =
rtp_init_session(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");
rtp_terminate_session(call->crtps[audio_index], av->messenger);
return ErrorStartingVideoRtp;
}
call->frame_limit = 0;
call->frame_id = 0;
call->frame_outid = 0;
call->frame_buf = calloc(MAX_VIDEOFRAME_SIZE, 1);
2014-06-24 04:34:06 +08:00
if (!call->frame_buf) {
rtp_terminate_session(call->crtps[audio_index], av->messenger);
rtp_terminate_session(call->crtps[video_index], av->messenger);
LOGGER_WARNING("Frame buffer allocation failed!");
return ErrorInternal;
}
2014-02-10 06:06:44 +08:00
}
if ( !(call->j_buf = create_queue(codec_settings->jbuf_capacity)) ) {
rtp_terminate_session(call->crtps[audio_index], av->messenger);
rtp_terminate_session(call->crtps[video_index], av->messenger);
free(call->frame_buf);
LOGGER_WARNING("Jitter buffer creaton failed!");
return ErrorInternal;
}
if ( (call->cs = codec_init_session(codec_settings->audio_bitrate,
codec_settings->audio_frame_duration,
2014-05-11 00:00:49 +08:00
codec_settings->audio_sample_rate,
codec_settings->audio_channels,
codec_settings->audio_VAD_tolerance,
2014-05-11 00:00:49 +08:00
codec_settings->video_width,
codec_settings->video_height,
codec_settings->video_bitrate) )) {
call->call_active = 1;
return ErrorNone;
}
rtp_terminate_session(call->crtps[audio_index], av->messenger);
rtp_terminate_session(call->crtps[video_index], av->messenger);
free(call->frame_buf);
terminate_queue(call->j_buf);
return ErrorInternal;
2014-02-10 06:06:44 +08:00
}
2014-02-16 04:29:41 +08:00
/**
* @brief Call this at the end of the transmission.
2014-02-17 09:01:30 +08:00
*
2014-02-16 04:29:41 +08:00
* @param av Handler.
* @return int
* @retval 0 Success.
* @retval ToxAvError On error.
*/
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) || !av->calls[call_index].call_active) {
LOGGER_WARNING("Action on inactive call: %d", call_index);
return ErrorNoCall;
}
CallSpecific *call = &av->calls[call_index];
call->call_active = 0;
2014-05-11 00:00:49 +08:00
if ( call->crtps[audio_index] && -1 == rtp_terminate_session(call->crtps[audio_index], av->messenger) ) {
LOGGER_ERROR("Error while terminating audio RTP session!\n");
/*return ErrorTerminatingAudioRtp;*/
2014-02-10 06:06:44 +08:00
}
else call->crtps[audio_index] = NULL;
2014-02-17 09:01:30 +08:00
2014-05-11 00:00:49 +08:00
if ( call->crtps[video_index] && -1 == rtp_terminate_session(call->crtps[video_index], av->messenger) ) {
LOGGER_ERROR("Error while terminating video RTP session!\n");
/*return ErrorTerminatingVideoRtp;*/
2014-02-10 06:06:44 +08:00
}
else call->crtps[video_index] = NULL;
if ( call->j_buf ) {
2014-05-11 00:00:49 +08:00
terminate_queue(call->j_buf);
call->j_buf = NULL;
2014-05-17 01:56:40 +08:00
LOGGER_DEBUG("Terminated j queue");
} else LOGGER_DEBUG("No j queue");
if ( call->cs ) {
2014-05-11 00:00:49 +08:00
codec_terminate_session(call->cs);
call->cs = NULL;
2014-05-17 01:56:40 +08:00
LOGGER_DEBUG("Terminated codec session");
} else LOGGER_DEBUG("No codec session");
2014-02-10 06:06:44 +08:00
return ErrorNone;
}
2014-02-16 04:29:41 +08:00
/**
* @brief Send RTP payload.
2014-02-17 09:01:30 +08:00
*
2014-02-16 04:29:41 +08:00
* @param av Handler.
* @param type Type of payload.
* @param payload The payload.
* @param length Size of it.
* @return int
* @retval 0 Success.
* @retval -1 Failure.
*/
inline__ int toxav_send_rtp_payload ( ToxAv *av, int32_t call_index, ToxAvCallType type, const uint8_t *payload,
unsigned int length )
2014-02-10 06:06:44 +08:00
{
#define send(data, len) rtp_send_msg(av->calls[call_index].crtps[type - TypeAudio], av->msi_session->messenger_handle, data, len)
if (av->calls[call_index].crtps[type - TypeAudio]) {
2014-06-24 04:34:06 +08:00
if (type == TypeAudio) {
return send(payload, length);
} else {
2014-06-24 04:34:06 +08:00
if (length == 0 || length > MAX_VIDEOFRAME_SIZE) {
LOGGER_ERROR("Invalid video frame size: %u\n", length);
return -1;
}
2014-06-24 04:34:06 +08:00
/* number of pieces - 1*/
uint8_t numparts = (length - 1) / VIDEOFRAME_PIECE_SIZE;
uint8_t load[2 + VIDEOFRAME_PIECE_SIZE];
load[0] = av->calls[call_index].frame_outid++;
load[1] = 0;
int i;
2014-06-24 04:34:06 +08:00
for (i = 0; i < numparts; i++) {
memcpy(load + VIDEOFRAME_HEADER_SIZE, payload, VIDEOFRAME_PIECE_SIZE);
payload += VIDEOFRAME_PIECE_SIZE;
if (send(load, VIDEOFRAME_HEADER_SIZE + VIDEOFRAME_PIECE_SIZE) != 0) {
return -1;
}
2014-06-24 04:34:06 +08:00
load[1]++;
}
/* remainder = length % VIDEOFRAME_PIECE_SIZE, VIDEOFRAME_PIECE_SIZE if = 0 */
length = ((length - 1) % VIDEOFRAME_PIECE_SIZE) + 1;
memcpy(load + VIDEOFRAME_HEADER_SIZE, payload, length);
return send(load, VIDEOFRAME_HEADER_SIZE + length);
}
} else {
return -1;
}
#undef send
2014-02-10 06:06:44 +08:00
}
2014-02-16 04:29:41 +08:00
/**
* @brief Receive RTP payload.
2014-02-17 09:01:30 +08:00
*
2014-02-16 04:29:41 +08:00
* @param av Handler.
* @param type Type of the payload.
* @param dest Storage.
* @return int
* @retval ToxAvError On Error.
* @retval >=0 Size of received payload.
*/
2014-05-17 01:56:40 +08:00
inline__ int toxav_recv_rtp_payload ( ToxAv *av, int32_t call_index, ToxAvCallType type, uint8_t *dest )
2014-02-10 06:06:44 +08:00
{
if ( !dest ) return ErrorInternal;
2014-06-15 22:36:57 +08:00
CallSpecific *call = &av->calls[call_index];
2014-05-11 00:00:49 +08:00
if ( !call->crtps[type - TypeAudio] ) return ErrorNoRtpSession;
2014-02-17 09:01:30 +08:00
RTPMessage *message;
2014-02-10 06:06:44 +08:00
if ( type == TypeAudio ) {
2014-02-17 09:01:30 +08:00
2014-02-16 03:44:33 +08:00
do {
2014-05-11 00:00:49 +08:00
message = rtp_recv_msg(call->crtps[audio_index]);
2014-02-17 09:01:30 +08:00
2014-02-16 03:44:33 +08:00
if (message) {
/* push the packet into the queue */
2014-05-11 00:00:49 +08:00
queue(call->j_buf, message);
2014-02-16 03:44:33 +08:00
}
2014-02-17 09:01:30 +08:00
} while (message);
2014-02-16 03:44:33 +08:00
int success = 0;
2014-05-11 00:00:49 +08:00
message = dequeue(call->j_buf, &success);
2014-02-16 03:44:33 +08:00
if ( success == 2) return ErrorAudioPacketLost;
2014-02-17 09:01:30 +08:00
} else {
2014-05-11 00:00:49 +08:00
message = rtp_recv_msg(call->crtps[video_index]);
2014-02-10 06:06:44 +08:00
}
2014-02-17 09:01:30 +08:00
if ( message ) {
2014-02-10 06:06:44 +08:00
memcpy(dest, message->data, message->length);
2014-02-17 09:01:30 +08:00
2014-02-10 06:06:44 +08:00
int length = message->length;
2014-02-17 09:01:30 +08:00
2014-02-10 06:06:44 +08:00
rtp_free_msg(NULL, message);
2014-02-17 09:01:30 +08:00
2014-02-10 06:06:44 +08:00
return length;
}
2014-02-17 09:01:30 +08:00
2014-02-10 06:06:44 +08:00
return 0;
}
2014-02-16 04:29:41 +08:00
/**
* @brief Receive decoded video packet.
2014-02-17 09:01:30 +08:00
*
2014-02-16 04:29:41 +08:00
* @param av Handler.
* @param output Storage.
2014-02-17 09:01:30 +08:00
* @return int
2014-02-16 04:29:41 +08:00
* @retval 0 Success.
* @retval ToxAvError On Error.
*/
2014-05-17 01:56:40 +08:00
inline__ int toxav_recv_video ( ToxAv *av, int32_t call_index, vpx_image_t **output)
2014-02-10 06:06:44 +08:00
{
2014-02-16 03:44:33 +08:00
if ( !output ) return ErrorInternal;
2014-06-15 22:36:57 +08:00
if (cii(call_index, av->msi_session) || !av->calls[call_index].call_active) {
LOGGER_WARNING("Action on inactive call: %d", call_index);
return ErrorNoCall;
}
2014-02-17 09:01:30 +08:00
2014-02-16 03:44:33 +08:00
uint8_t packet [RTP_PAYLOAD_SIZE];
CallSpecific *call = &av->calls[call_index];
int recved_size;
2014-06-21 10:36:16 +08:00
2014-06-24 04:34:06 +08:00
while ((recved_size = toxav_recv_rtp_payload(av, call_index, TypeVideo, packet)) > 0) {
if (recved_size < VIDEOFRAME_HEADER_SIZE) {
continue;
}
2014-06-21 10:36:16 +08:00
2014-06-24 03:58:20 +08:00
uint8_t i = packet[0] - call->frame_id;
2014-02-17 09:01:30 +08:00
2014-06-24 04:34:06 +08:00
if (i == 0) {
/* piece of current frame */
2014-06-24 04:34:06 +08:00
} else if (i > 0 && i < 128) {
/* recieved a piece of a frame ahead, flush current frame and start reading this new frame */
int rc = vpx_codec_decode(&call->cs->v_decoder, call->frame_buf, call->frame_limit, NULL, 0);
call->frame_id = packet[0];
memset(call->frame_buf, 0, call->frame_limit);
call->frame_limit = 0;
2014-06-21 10:36:16 +08:00
if (rc != VPX_CODEC_OK) {
LOGGER_ERROR("Error decoding video: %u %s\n", i, vpx_codec_err_to_string(rc));
}
} else {
/* old packet, dont read */
LOGGER_DEBUG("Old packet: %u\n", i);
continue;
}
2014-06-24 04:34:06 +08:00
if (packet[1] > (MAX_VIDEOFRAME_SIZE - VIDEOFRAME_PIECE_SIZE + 1) /
VIDEOFRAME_PIECE_SIZE) { //TODO, fix this check? not sure
/* packet out of buffer range */
continue;
2014-05-03 07:46:03 +08:00
}
2014-02-17 09:01:30 +08:00
LOGGER_DEBUG("Video Packet: %u %u\n", packet[0], packet[1]);
2014-06-24 04:34:06 +08:00
memcpy(call->frame_buf + packet[1] * VIDEOFRAME_PIECE_SIZE, packet + VIDEOFRAME_HEADER_SIZE,
recved_size - VIDEOFRAME_HEADER_SIZE);
uint32_t limit = packet[1] * VIDEOFRAME_PIECE_SIZE + recved_size - VIDEOFRAME_HEADER_SIZE;
2014-06-24 04:34:06 +08:00
if (limit > call->frame_limit) {
call->frame_limit = limit;
LOGGER_DEBUG("Limit: %u\n", call->frame_limit);
}
}
2014-02-17 09:01:30 +08:00
2014-02-16 03:44:33 +08:00
vpx_codec_iter_t iter = NULL;
vpx_image_t *img;
2014-05-11 00:00:49 +08:00
img = vpx_codec_get_frame(&call->cs->v_decoder, &iter);
2014-02-17 09:01:30 +08:00
2014-02-16 03:44:33 +08:00
*output = img;
return 0;
2014-02-10 06:06:44 +08:00
}
2014-02-16 04:29:41 +08:00
/**
* @brief Encode and send video packet.
2014-02-17 09:01:30 +08:00
*
2014-02-16 04:29:41 +08:00
* @param av Handler.
* @param input The packet.
* @return int
* @retval 0 Success.
* @retval ToxAvError On error.
*/
inline__ int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, int frame_size)
2014-02-10 06:06:44 +08:00
{
if (cii(call_index, av->msi_session) || !av->calls[call_index].call_active) {
LOGGER_WARNING("Action on inactive call: %d", call_index);
return ErrorNoCall;
}
2014-06-15 22:36:57 +08:00
2014-05-11 00:00:49 +08:00
return toxav_send_rtp_payload(av, call_index, TypeVideo, frame, frame_size);
2014-02-16 03:44:33 +08:00
}
2014-05-11 00:00:49 +08:00
/**
* @brief Encode video frame
*
2014-05-11 00:00:49 +08:00
* @param av Handler
* @param dest Where to
* @param dest_max Max size
* @param input What to encode
* @return int
* @retval ToxAvError On error.
* @retval >0 On success
*/
inline__ 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) || !av->calls[call_index].call_active) {
LOGGER_WARNING("Action on inactive call: %d", call_index);
return ErrorNoCall;
}
2014-06-15 22:36:57 +08:00
CallSpecific *call = &av->calls[call_index];
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-02-10 06:06:44 +08:00
return 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-05-03 07:46:03 +08:00
if ( copied + pkt->data.frame.sz > dest_max ) return ErrorPacketTooLarge;
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-05-11 00:00:49 +08:00
return copied;
2014-02-16 03:44:33 +08:00
}
2014-02-16 04:29:41 +08:00
/**
* @brief Receive decoded audio frame.
2014-02-17 09:01:30 +08:00
*
2014-02-16 04:29:41 +08:00
* @param av Handler.
* @param frame_size The size of dest in frames/samples (one frame/sample is 16 bits or 2 bytes
* and corresponds to one sample of audio.)
* @param dest Destination of the raw audio (16 bit signed pcm with AUDIO_CHANNELS channels).
* Make sure it has enough space for frame_size frames/samples.
2014-02-16 04:29:41 +08:00
* @return int
* @retval >=0 Size of received data in frames/samples.
2014-02-16 04:29:41 +08:00
* @retval ToxAvError On error.
*/
2014-05-17 01:56:40 +08:00
inline__ int toxav_recv_audio ( ToxAv *av, int32_t call_index, int frame_size, int16_t *dest )
2014-02-16 03:44:33 +08:00
{
if ( !dest ) return ErrorInternal;
2014-06-15 22:36:57 +08:00
if (cii(call_index, av->msi_session) || !av->calls[call_index].call_active) {
LOGGER_WARNING("Action on inactive call: %d", call_index);
return ErrorNoCall;
}
2014-02-17 09:01:30 +08:00
CallSpecific *call = &av->calls[call_index];
2014-02-17 09:01:30 +08:00
2014-02-16 03:44:33 +08:00
uint8_t packet [RTP_PAYLOAD_SIZE];
2014-04-28 01:21:26 +08:00
int recved_size = toxav_recv_rtp_payload(av, call_index, TypeAudio, packet);
2014-02-16 03:44:33 +08:00
if ( recved_size == ErrorAudioPacketLost ) {
2014-05-11 00:00:49 +08:00
int dec_size = opus_decode(call->cs->audio_decoder, NULL, 0, dest, frame_size, 1);
2014-05-18 05:15:54 +08:00
if ( dec_size < 0 ) {
LOGGER_WARNING("Decoding error: %s", opus_strerror(dec_size));
2014-05-11 00:00:49 +08:00
return ErrorInternal;
} else return dec_size;
2014-02-17 09:01:30 +08:00
} else if ( recved_size ) {
2014-05-11 00:00:49 +08:00
int dec_size = opus_decode(call->cs->audio_decoder, packet, recved_size, dest, frame_size, 0);
2014-05-18 05:15:54 +08:00
if ( dec_size < 0 ) {
LOGGER_WARNING("Decoding error: %s", opus_strerror(dec_size));
2014-05-11 00:00:49 +08:00
return ErrorInternal;
} else return dec_size;
2014-02-16 03:44:33 +08:00
} else {
2014-02-16 04:29:41 +08:00
return 0; /* Nothing received */
2014-02-16 03:44:33 +08:00
}
}
2014-02-16 04:29:41 +08:00
/**
2014-05-03 07:46:03 +08:00
* @brief Send audio frame.
2014-02-17 09:01:30 +08:00
*
2014-02-16 04:29:41 +08:00
* @param av Handler.
* @param frame The frame (raw 16 bit signed pcm with AUDIO_CHANNELS channels audio.)
* @param frame_size Its size in number of frames/samples (one frame/sample is 16 bits or 2 bytes)
* frame size should be AUDIO_FRAME_SIZE.
2014-02-16 04:29:41 +08:00
* @return int
* @retval 0 Success.
* @retval ToxAvError On error.
*/
2014-05-17 01:56:40 +08:00
inline__ int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *frame, int frame_size)
2014-02-16 03:44:33 +08:00
{
if (cii(call_index, av->msi_session) || !av->calls[call_index].call_active) {
LOGGER_WARNING("Action on inactive call: %d", call_index);
return ErrorNoCall;
}
2014-06-15 22:36:57 +08:00
2014-05-03 07:46:03 +08:00
return toxav_send_rtp_payload(av, call_index, TypeAudio, frame, frame_size);
}
2014-02-17 09:01:30 +08:00
2014-05-03 07:46:03 +08:00
/**
* @brief Encode audio frame
*
2014-05-03 07:46:03 +08:00
* @param av Handler
* @param dest dest
* @param dest_max Max dest size
* @param frame The frame
* @param frame_size The frame size
* @return int
* @retval ToxAvError On error.
* @retval >0 On success
*/
inline__ int toxav_prepare_audio_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max,
const int16_t *frame, int frame_size)
2014-02-16 03:44:33 +08:00
{
if (cii(call_index, av->msi_session) || !av->calls[call_index].call_active) {
LOGGER_WARNING("Action on inactive call: %d", call_index);
return ErrorNoCall;
}
2014-06-15 22:36:57 +08:00
2014-05-11 00:00:49 +08:00
int32_t rc = opus_encode(av->calls[call_index].cs->audio_encoder, frame, frame_size, dest, dest_max);
2014-02-17 09:01:30 +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-02-16 03:44:33 +08:00
return 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-02-16 04:29:41 +08:00
/**
* @brief Get peer transmission type. It can either be audio or video.
2014-02-17 09:01:30 +08:00
*
2014-02-16 04:29:41 +08:00
* @param av Handler.
* @param peer The peer
* @return int
* @retval ToxAvCallType On success.
* @retval ToxAvError On error.
*/
2014-05-17 01:56:40 +08:00
int toxav_get_peer_transmission_type ( ToxAv *av, int32_t call_index, int peer )
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-02-10 06:06:44 +08:00
return ErrorInternal;
2014-02-17 09:01:30 +08:00
2014-04-28 01:21:26 +08:00
return av->msi_session->calls[call_index]->type_peer[peer];
2014-02-10 06:06:44 +08:00
}
/**
* @brief Get id of peer participating in conversation
*
* @param av Handler
* @param peer peer index
* @return int
* @retval ToxAvError No peer id
*/
int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer )
{
assert(av->msi_session);
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 )
return ErrorInternal;
2014-04-28 01:21:26 +08:00
return av->msi_session->calls[call_index]->peers[peer];
}
2014-02-16 04:29:41 +08:00
/**
* @brief Is certain capability supported
*
* @param av Handler
* @return int
* @retval 1 Yes.
* @retval 0 No.
*/
inline__ int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability )
{
2014-05-17 01:56:40 +08:00
return av->calls[call_index].cs ? av->calls[call_index].cs->capabilities & (Capabilities) capability : 0;
/* 0 is error here */
}
/**
2014-05-03 07:46:03 +08:00
* @brief Set queue limit
*
2014-05-03 07:46:03 +08:00
* @param av Handler
* @param call_index index
* @param limit the limit
* @return void
*/
inline__ int toxav_set_audio_queue_limit(ToxAv *av, int32_t call_index, uint64_t limit)
{
2014-05-17 01:56:40 +08:00
if ( av->calls[call_index].crtps[audio_index] )
rtp_queue_adjust_limit(av->calls[call_index].crtps[audio_index], limit);
else
return ErrorNoRtpSession;
2014-05-17 01:56:40 +08:00
return ErrorNone;
}
2014-05-03 07:46:03 +08:00
/**
* @brief Set queue limit
*
2014-05-03 07:46:03 +08:00
* @param av Handler
* @param call_index index
* @param limit the limit
* @return void
*/
inline__ int toxav_set_video_queue_limit(ToxAv *av, int32_t call_index, uint64_t limit)
{
2014-05-17 01:56:40 +08:00
if ( av->calls[call_index].crtps[video_index] )
rtp_queue_adjust_limit(av->calls[call_index].crtps[video_index], limit);
else
return ErrorNoRtpSession;
2014-05-17 01:56:40 +08:00
return ErrorNone;
}
2014-05-17 01:56:40 +08:00
inline__ 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_has_activity(ToxAv *av, int32_t call_index, int16_t *PCM, uint16_t frame_size, float ref_energy)
2014-06-21 07:58:55 +08:00
{
if ( !av->calls[call_index].cs ) return ErrorInvalidCodecState;
return energy_VAD(av->calls[call_index].cs, PCM, frame_size, ref_energy);
}