toxcore/toxav/toxav.c

563 lines
14 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/>.
*
2014-02-17 09:01:30 +08:00
*
2014-02-16 05:36:15 +08:00
* Report bugs/suggestions at #tox-dev @ freenode.net:6667
2014-02-10 06:06:44 +08:00
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
2014-02-16 03:44:33 +08:00
#include "media.h"
2014-02-10 06:06:44 +08:00
#include "rtp.h"
#include "msi.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
2014-02-16 03:44:33 +08:00
#include "toxav.h"
/* Default video bitrate in bytes/s */
#define VIDEO_BITRATE (10*1000*100)
/* Default audio bitrate in bits/s */
#define AUDIO_BITRATE 64000
/* Assume 60 fps*/
#define MAX_ENCODE_TIME_US ((1000 / 60) * 1000)
2014-02-10 06:06:44 +08:00
#define inline__ inline __attribute__((always_inline))
static const uint8_t audio_index = 0, video_index = 1;
typedef enum {
ts_closing,
ts_running,
ts_closed
2014-02-17 09:01:30 +08:00
2014-02-10 06:06:44 +08:00
} ThreadState;
2014-02-17 09:01:30 +08:00
typedef struct _ToxAv {
Messenger *messenger;
MSISession *msi_session; /** Main msi session */
RTPSession *rtp_sessions[2]; /* Audio is first and video is second */
struct jitter_buffer *j_buf;
CodecState *cs;
void *agent_handler;
2014-02-10 06:06:44 +08:00
} ToxAv;
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, void *userdata, uint16_t video_width, uint16_t video_height)
2014-02-16 03:44:33 +08:00
{
2014-02-17 09:01:30 +08:00
ToxAv *av = calloc ( sizeof(ToxAv), 1);
2014-02-16 03:44:33 +08:00
if (av == NULL)
return NULL;
av->messenger = (Messenger *)messenger;
av->msi_session = msi_init_session(av->messenger);
2014-02-10 06:06:44 +08:00
av->msi_session->agent_handler = av;
2014-02-17 09:01:30 +08:00
2014-02-10 06:06:44 +08:00
av->rtp_sessions[0] = av->rtp_sessions [1] = NULL;
2014-02-16 03:44:33 +08:00
2014-02-10 06:06:44 +08:00
/* NOTE: This should be user defined or? */
av->j_buf = create_queue(20);
2014-02-17 09:01:30 +08:00
av->cs = codec_init_session(AUDIO_BITRATE, AUDIO_FRAME_DURATION, AUDIO_SAMPLE_RATE, AUDIO_CHANNELS, video_width,
video_height, VIDEO_BITRATE);
av->agent_handler = userdata;
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-02-10 06:06:44 +08:00
if ( av->rtp_sessions[audio_index] ) {
rtp_terminate_session(av->rtp_sessions[audio_index], av->msi_session->messenger_handle);
}
2014-02-17 09:01:30 +08:00
2014-02-10 06:06:44 +08:00
if ( av->rtp_sessions[video_index] ) {
rtp_terminate_session(av->rtp_sessions[video_index], av->msi_session->messenger_handle);
}
2014-02-17 09:01:30 +08:00
2014-02-10 06:06:44 +08:00
codec_terminate_session(av->cs);
2014-02-17 09:01:30 +08:00
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
*/
2014-02-17 09:01:30 +08:00
void toxav_register_callstate_callback ( ToxAVCallback callback, ToxAvCallbackID id )
2014-02-10 06:06:44 +08:00
{
msi_register_callback((MSICallback)callback, (MSICallbackID) id);
}
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.
*/
2014-02-17 09:01:30 +08:00
int toxav_call (ToxAv *av, int user, ToxAvCallType call_type, int ringing_seconds )
2014-02-10 06:06:44 +08:00
{
if ( av->msi_session->call ) {
return ErrorAlreadyInCall;
}
2014-02-17 09:01:30 +08:00
2014-02-10 06:06:44 +08:00
return msi_invite(av->msi_session, call_type, ringing_seconds * 1000, user);
}
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.
*/
2014-02-17 09:01:30 +08:00
int toxav_hangup ( ToxAv *av )
2014-02-10 06:06:44 +08:00
{
if ( !av->msi_session->call ) {
return ErrorNoCall;
}
2014-02-17 09:01:30 +08:00
2014-02-10 06:06:44 +08:00
if ( av->msi_session->call->state != call_active ) {
return ErrorInvalidState;
}
2014-02-17 09:01:30 +08:00
2014-02-10 06:06:44 +08:00
return msi_hangup(av->msi_session);
}
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.
*/
2014-02-17 09:01:30 +08:00
int toxav_answer ( ToxAv *av, ToxAvCallType call_type )
2014-02-10 06:06:44 +08:00
{
if ( !av->msi_session->call ) {
return ErrorNoCall;
}
2014-02-17 09:01:30 +08:00
2014-02-10 06:06:44 +08:00
if ( av->msi_session->call->state != call_starting ) {
return ErrorInvalidState;
}
2014-02-17 09:01:30 +08:00
2014-02-10 06:06:44 +08:00
return msi_answer(av->msi_session, call_type);
}
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.
*/
2014-02-17 09:01:30 +08:00
int toxav_reject ( ToxAv *av, const char *reason )
2014-02-10 06:06:44 +08:00
{
if ( !av->msi_session->call ) {
return ErrorNoCall;
}
if ( av->msi_session->call->state != call_starting ) {
return ErrorInvalidState;
}
2014-02-17 09:01:30 +08:00
return msi_reject(av->msi_session, (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.
* @return int
* @retval 0 Success.
* @retval ToxAvError On error.
*/
2014-02-17 09:01:30 +08:00
int toxav_cancel ( ToxAv *av, const char *reason )
2014-02-10 06:06:44 +08:00
{
if ( !av->msi_session->call ) {
return ErrorNoCall;
}
2014-02-17 09:01:30 +08:00
return msi_cancel(av->msi_session, 0, (const uint8_t *)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.
*/
2014-02-17 09:01:30 +08:00
int toxav_stop_call ( ToxAv *av )
2014-02-10 06:06:44 +08:00
{
if ( !av->msi_session->call ) {
return ErrorNoCall;
}
2014-02-17 09:01:30 +08:00
2014-02-10 06:06:44 +08:00
return msi_stopcall(av->msi_session);
}
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.
*/
2014-02-17 09:01:30 +08:00
int toxav_prepare_transmission ( ToxAv *av )
2014-02-10 06:06:44 +08:00
{
assert(av->msi_session);
2014-02-17 09:01:30 +08:00
2014-02-10 06:06:44 +08:00
if ( !av->msi_session || !av->msi_session->call ) {
return ErrorNoCall;
}
2014-02-17 09:01:30 +08:00
2014-02-10 06:06:44 +08:00
av->rtp_sessions[audio_index] = rtp_init_session(
2014-02-17 09:01:30 +08:00
type_audio,
av->messenger,
av->msi_session->call->peers[0],
av->msi_session->call->key_peer,
av->msi_session->call->key_local,
av->msi_session->call->nonce_peer,
av->msi_session->call->nonce_local
2014-02-10 06:06:44 +08:00
);
2014-02-17 09:01:30 +08:00
2014-02-10 06:06:44 +08:00
if ( !av->rtp_sessions[audio_index] ) {
fprintf(stderr, "Error while starting audio RTP session!\n");
return ErrorStartingAudioRtp;
}
2014-02-17 09:01:30 +08:00
av->rtp_sessions[video_index] = rtp_init_session (
type_video,
av->messenger,
av->msi_session->call->peers[0],
av->msi_session->call->key_peer,
av->msi_session->call->key_local,
av->msi_session->call->nonce_peer,
av->msi_session->call->nonce_local
2014-02-10 06:06:44 +08:00
);
2014-02-17 09:01:30 +08:00
2014-02-10 06:06:44 +08:00
if ( !av->rtp_sessions[video_index] ) {
fprintf(stderr, "Error while starting video RTP session!\n");
return ErrorStartingVideoRtp;
}
2014-02-17 09:01:30 +08:00
2014-02-10 06:06:44 +08:00
return ErrorNone;
}
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-02-17 09:01:30 +08:00
int toxav_kill_transmission ( ToxAv *av )
2014-02-10 06:06:44 +08:00
{
/* Both sessions should be active at any time */
if ( !av->rtp_sessions[0] || !av->rtp_sessions[0] )
return ErrorNoTransmission;
2014-02-17 09:01:30 +08:00
2014-02-10 06:06:44 +08:00
if ( -1 == rtp_terminate_session(av->rtp_sessions[audio_index], av->messenger) ) {
fprintf(stderr, "Error while terminating audio RTP session!\n");
return ErrorTerminatingAudioRtp;
}
2014-02-17 09:01:30 +08:00
2014-02-10 06:06:44 +08:00
if ( -1 == rtp_terminate_session(av->rtp_sessions[video_index], av->messenger) ) {
fprintf(stderr, "Error while terminating video RTP session!\n");
return ErrorTerminatingVideoRtp;
}
2014-02-17 09:01:30 +08:00
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.
*/
2014-02-17 09:01:30 +08:00
inline__ int toxav_send_rtp_payload ( ToxAv *av, ToxAvCallType type, const uint8_t *payload, uint16_t length )
2014-02-10 06:06:44 +08:00
{
if ( av->rtp_sessions[type - TypeAudio] )
return rtp_send_msg ( av->rtp_sessions[type - TypeAudio], av->msi_session->messenger_handle, payload, length );
else return -1;
}
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-02-17 09:01:30 +08:00
inline__ int toxav_recv_rtp_payload ( ToxAv *av, ToxAvCallType type, uint8_t *dest )
2014-02-10 06:06:44 +08:00
{
if ( !dest ) return ErrorInternal;
2014-02-17 09:01:30 +08:00
2014-02-10 06:06:44 +08:00
if ( !av->rtp_sessions[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 {
message = rtp_recv_msg(av->rtp_sessions[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 */
queue(av->j_buf, message);
}
2014-02-17 09:01:30 +08:00
} while (message);
2014-02-16 03:44:33 +08:00
int success = 0;
message = dequeue(av->j_buf, &success);
if ( success == 2) return ErrorAudioPacketLost;
2014-02-17 09:01:30 +08:00
} else {
2014-02-10 06:06:44 +08:00
message = rtp_recv_msg(av->rtp_sessions[video_index]);
}
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-02-17 09:01:30 +08:00
inline__ int toxav_recv_video ( ToxAv *av, 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-02-17 09:01:30 +08:00
2014-02-16 03:44:33 +08:00
uint8_t packet [RTP_PAYLOAD_SIZE];
int recved_size = 0;
2014-02-17 09:01:30 +08:00
2014-02-16 03:44:33 +08:00
do {
recved_size = toxav_recv_rtp_payload(av, TypeVideo, packet);
2014-02-17 09:01:30 +08:00
2014-02-16 03:44:33 +08:00
if (recved_size > 0) {
printf("decode: %s\n", vpx_codec_err_to_string(vpx_codec_decode(&av->cs->v_decoder, packet, recved_size, NULL, 0)));
}
2014-02-17 09:01:30 +08:00
} while (recved_size > 0);
2014-02-16 03:44:33 +08:00
vpx_codec_iter_t iter = NULL;
vpx_image_t *img;
img = vpx_codec_get_frame(&av->cs->v_decoder, &iter);
2014-02-17 09:01:30 +08:00
2014-02-16 03:44:33 +08:00
if (img == NULL)
return ErrorInternal;
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.
*/
2014-02-17 09:01:30 +08:00
inline__ int toxav_send_video ( ToxAv *av, vpx_image_t *input)
2014-02-10 06:06:44 +08:00
{
2014-02-16 03:44:33 +08:00
if (vpx_codec_encode(&av->cs->v_encoder, input, av->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US) != VPX_CODEC_OK) {
printf("could not encode video frame\n");
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-02-16 03:44:33 +08:00
++av->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;
int sent = 0;
2014-02-17 09:01:30 +08:00
while ( (pkt = vpx_codec_get_cx_data(&av->cs->v_encoder, &iter)) ) {
2014-02-16 03:44:33 +08:00
if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
if (toxav_send_rtp_payload(av, TypeVideo, pkt->data.frame.buf, pkt->data.frame.sz) != -1)
++sent;
}
}
2014-02-17 09:01:30 +08:00
2014-02-16 03:44:33 +08:00
if (sent > 0)
return 0;
2014-02-17 09:01:30 +08:00
2014-02-16 03:44:33 +08:00
return ErrorInternal;
}
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-02-17 09:01:30 +08:00
inline__ int toxav_recv_audio ( ToxAv *av, int frame_size, int16_t *dest )
2014-02-16 03:44:33 +08:00
{
if ( !dest ) return ErrorInternal;
2014-02-17 09:01:30 +08:00
2014-02-16 03:44:33 +08:00
uint8_t packet [RTP_PAYLOAD_SIZE];
int recved_size = toxav_recv_rtp_payload(av, TypeAudio, packet);
if ( recved_size == ErrorAudioPacketLost ) {
printf("Lost packet\n");
return opus_decode(av->cs->audio_decoder, NULL, 0, dest, frame_size, 1);
2014-02-17 09:01:30 +08:00
} else if ( recved_size ) {
2014-02-16 03:44:33 +08:00
return opus_decode(av->cs->audio_decoder, packet, recved_size, dest, frame_size, 0);
} 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
/**
* @brief Encode and 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-02-17 09:01:30 +08:00
inline__ int toxav_send_audio ( ToxAv *av, const int16_t *frame, int frame_size)
2014-02-16 03:44:33 +08:00
{
uint8_t temp_data[RTP_PAYLOAD_SIZE];
int32_t ret = opus_encode(av->cs->audio_encoder, frame, frame_size, temp_data, sizeof(temp_data));
2014-02-17 09:01:30 +08:00
2014-02-16 03:44:33 +08:00
if (ret <= 0)
return ErrorInternal;
return toxav_send_rtp_payload(av, TypeAudio, temp_data, ret);
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-02-17 09:01:30 +08:00
int toxav_get_peer_transmission_type ( ToxAv *av, int peer )
2014-02-10 06:06:44 +08:00
{
assert(av->msi_session);
2014-02-17 09:01:30 +08:00
2014-02-10 06:06:44 +08:00
if ( peer < 0 || !av->msi_session->call || av->msi_session->call->peer_count <= peer )
return ErrorInternal;
2014-02-17 09:01:30 +08:00
2014-02-10 06:06:44 +08:00
return av->msi_session->call->type_peer[peer];
}
2014-02-16 04:29:41 +08:00
/**
* @brief Get reference to an object that is handling av 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_get_agent_handler ( ToxAv *av )
2014-02-10 06:06:44 +08:00
{
return av->agent_handler;
}