Started adding public API

This commit is contained in:
mannol 2014-02-09 23:06:44 +01:00
parent b30b98aa0b
commit 292708c336
9 changed files with 820 additions and 421 deletions

View File

@ -37,9 +37,8 @@
#include <opus/opus.h> #include <opus/opus.h>
#include <assert.h> #include <assert.h>
#include "toxmsi.h" #include "rtp.h"
#include "toxrtp.h" #include "media.h"
#include "toxmedia.h"
struct jitter_buffer { struct jitter_buffer {
RTPMessage **queue; RTPMessage **queue;
@ -79,8 +78,6 @@ struct jitter_buffer *create_queue(int capacity)
/* returns 1 if 'a' has a higher sequence number than 'b' */ /* returns 1 if 'a' has a higher sequence number than 'b' */
uint8_t sequence_number_older(uint16_t sn_a, uint16_t sn_b, uint32_t ts_a, uint32_t ts_b) uint8_t sequence_number_older(uint16_t sn_a, uint16_t sn_b, uint32_t ts_a, uint32_t ts_b)
{ {
/* should be stable enough */
/* TODO: There is already this kind of function in toxrtp.c. /* TODO: There is already this kind of function in toxrtp.c.
* Maybe merge? * Maybe merge?
*/ */
@ -141,7 +138,6 @@ int empty_queue(struct jitter_buffer *q)
{ {
while (q->size > 0) { while (q->size > 0) {
q->size--; q->size--;
/* FIXME: */
rtp_free_msg(NULL, q->queue[q->front]); rtp_free_msg(NULL, q->queue[q->front]);
q->front++; q->front++;
@ -207,64 +203,56 @@ int queue(struct jitter_buffer *q, RTPMessage *pk)
return 0; return 0;
} }
int init_receive_audio(codec_state *cs)
{
int rc;
cs->audio_decoder = opus_decoder_create(48000, 1, &rc );
if ( rc != OPUS_OK ){ int init_video_decoder(CodecState *cs)
printf("Error while starting audio decoder!\n");
return 0;
}
rc = opus_decoder_init(cs->audio_decoder, 48000, 1);
if ( rc != OPUS_OK ){
printf("Error while starting audio decoder!\n");
return 0;
}
printf("Init audio decoder successful\n");
return 1;
}
int init_receive_video(codec_state *cs)
{ {
cs->video_decoder = avcodec_find_decoder(VIDEO_CODEC); cs->video_decoder = avcodec_find_decoder(VIDEO_CODEC);
if (!cs->video_decoder) { if (!cs->video_decoder) {
printf("Init video_decoder failed\n"); fprintf(stderr, "Init video_decoder failed!\n");
return 0; return -1;
} }
cs->video_decoder_ctx = avcodec_alloc_context3(cs->video_decoder); cs->video_decoder_ctx = avcodec_alloc_context3(cs->video_decoder);
if (!cs->video_decoder_ctx) { if (!cs->video_decoder_ctx) {
printf("Init video_decoder_ctx failed\n"); fprintf(stderr, "Init video_decoder_ctx failed!\n");
return 0; return -1;
} }
if (avcodec_open2(cs->video_decoder_ctx, cs->video_decoder, NULL) < 0) { if (avcodec_open2(cs->video_decoder_ctx, cs->video_decoder, NULL) < 0) {
printf("Opening video decoder failed\n"); fprintf(stderr, "Opening video decoder failed!\n");
return -1;
}
return 0; return 0;
} }
printf("Init video decoder successful\n"); int init_audio_decoder(CodecState *cs, uint32_t audio_channels)
return 1;
}
int init_send_video(codec_state *cs)
{ {
cs->video_input_format = av_find_input_format(VIDEO_DRIVER); int rc;
cs->audio_decoder = opus_decoder_create(cs->audio_sample_rate, audio_channels, &rc );
if ( rc != OPUS_OK ){
fprintf(stderr, "Error while starting audio decoder!\n");
return -1;
}
if (avformat_open_input(&cs->video_format_ctx, DEFAULT_WEBCAM, cs->video_input_format, NULL) != 0) {
printf("opening video_input_format failed\n");
return 0; return 0;
} }
int init_video_encoder(CodecState *cs, const char* webcam, const char* video_driver, uint32_t video_bitrate)
{
cs->video_input_format = av_find_input_format(video_driver);
if (avformat_open_input(&cs->video_format_ctx, webcam, cs->video_input_format, NULL) != 0) {
fprintf(stderr, "Opening video_input_format failed!\n");
return -1;
}
avformat_find_stream_info(cs->video_format_ctx, NULL); avformat_find_stream_info(cs->video_format_ctx, NULL);
av_dump_format(cs->video_format_ctx, 0, DEFAULT_WEBCAM, 0); av_dump_format(cs->video_format_ctx, 0, webcam, 0);
int i; int i;
@ -279,42 +267,42 @@ int init_send_video(codec_state *cs)
cs->webcam_decoder = avcodec_find_decoder(cs->webcam_decoder_ctx->codec_id); cs->webcam_decoder = avcodec_find_decoder(cs->webcam_decoder_ctx->codec_id);
if (cs->webcam_decoder == NULL) { if (cs->webcam_decoder == NULL) {
printf("Unsupported codec\n"); fprintf(stderr, "Unsupported codec!\n");
return 0; return -1;
} }
if (cs->webcam_decoder_ctx == NULL) { if (cs->webcam_decoder_ctx == NULL) {
printf("init webcam_decoder_ctx failed\n"); fprintf(stderr, "Init webcam_decoder_ctx failed!\n");
return 0; return -1;
} }
if (avcodec_open2(cs->webcam_decoder_ctx, cs->webcam_decoder, NULL) < 0) { if (avcodec_open2(cs->webcam_decoder_ctx, cs->webcam_decoder, NULL) < 0) {
printf("opening webcam decoder failed\n"); fprintf(stderr, "Opening webcam decoder failed!\n");
return 0; return -1;
} }
cs->video_encoder = avcodec_find_encoder(VIDEO_CODEC); cs->video_encoder = avcodec_find_encoder(VIDEO_CODEC);
if (!cs->video_encoder) { if (!cs->video_encoder) {
printf("init video_encoder failed\n"); fprintf(stderr, "Init video_encoder failed!\n");
return 0; return -1;
} }
cs->video_encoder_ctx = avcodec_alloc_context3(cs->video_encoder); cs->video_encoder_ctx = avcodec_alloc_context3(cs->video_encoder);
if (!cs->video_encoder_ctx) { if (!cs->video_encoder_ctx) {
printf("init video_encoder_ctx failed\n"); fprintf(stderr, "Init video_encoder_ctx failed!\n");
return 0; return -1;
} }
cs->video_encoder_ctx->bit_rate = VIDEO_BITRATE; cs->video_encoder_ctx->bit_rate = video_bitrate;
cs->video_encoder_ctx->rc_min_rate = cs->video_encoder_ctx->rc_max_rate = cs->video_encoder_ctx->bit_rate; cs->video_encoder_ctx->rc_min_rate = cs->video_encoder_ctx->rc_max_rate = cs->video_encoder_ctx->bit_rate;
av_opt_set_double(cs->video_encoder_ctx->priv_data, "max-intra-rate", 90, 0); av_opt_set_double(cs->video_encoder_ctx->priv_data, "max-intra-rate", 90, 0);
av_opt_set(cs->video_encoder_ctx->priv_data, "quality", "realtime", 0); av_opt_set(cs->video_encoder_ctx->priv_data, "quality", "realtime", 0);
cs->video_encoder_ctx->thread_count = 4; cs->video_encoder_ctx->thread_count = 4;
cs->video_encoder_ctx->rc_buffer_aggressivity = 0.95; cs->video_encoder_ctx->rc_buffer_aggressivity = 0.95;
cs->video_encoder_ctx->rc_buffer_size = VIDEO_BITRATE * 6; cs->video_encoder_ctx->rc_buffer_size = video_bitrate * 6;
cs->video_encoder_ctx->profile = 3; cs->video_encoder_ctx->profile = 3;
cs->video_encoder_ctx->qmax = 54; cs->video_encoder_ctx->qmax = 54;
cs->video_encoder_ctx->qmin = 4; cs->video_encoder_ctx->qmin = 4;
@ -326,66 +314,84 @@ int init_send_video(codec_state *cs)
cs->video_encoder_ctx->height = cs->webcam_decoder_ctx->height; cs->video_encoder_ctx->height = cs->webcam_decoder_ctx->height;
if (avcodec_open2(cs->video_encoder_ctx, cs->video_encoder, NULL) < 0) { if (avcodec_open2(cs->video_encoder_ctx, cs->video_encoder, NULL) < 0) {
printf("opening video encoder failed\n"); fprintf(stderr, "Opening video encoder failed!\n");
return -1;
}
return 0; return 0;
} }
printf("init video encoder successful\n"); int init_audio_encoder(CodecState *cs)
return 1;
}
int init_send_audio(codec_state *cs)
{ {
cs->support_send_audio = 0;
int err = OPUS_OK; int err = OPUS_OK;
cs->audio_bitrate = AUDIO_BITRATE; cs->audio_encoder = opus_encoder_create(cs->audio_sample_rate, 1, OPUS_APPLICATION_VOIP, &err);
cs->audio_encoder = opus_encoder_create(AUDIO_SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP, &err);
err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_bitrate)); err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_bitrate));
err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10)); err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10));
err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE)); err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));
/* NOTE: What do we do with this? */
int nfo; int nfo;
err = opus_encoder_ctl(cs->audio_encoder, OPUS_GET_LOOKAHEAD(&nfo)); err = opus_encoder_ctl(cs->audio_encoder, OPUS_GET_LOOKAHEAD(&nfo));
/* printf("Encoder lookahead delay : %d\n", nfo); */
printf("init audio encoder successful\n");
return 1; return err == OPUS_OK ? 0 : -1;
} }
int init_encoder(codec_state *cs)
CodecState* codec_init_session ( uint32_t audio_bitrate,
uint16_t audio_frame_duration,
uint32_t audio_sample_rate,
uint32_t audio_channels,
uint32_t video_bitrate,
const char* webcam,
const char* webcam_driver )
{ {
CodecState* _retu = av_calloc(sizeof(CodecState), 1);
assert(_retu);
avdevice_register_all(); avdevice_register_all();
avcodec_register_all(); avcodec_register_all();
avdevice_register_all();
av_register_all(); av_register_all();
pthread_mutex_init(&cs->ctrl_mutex, NULL);
cs->support_send_video = init_send_video(cs); _retu->audio_bitrate = audio_bitrate;
cs->support_send_audio = init_send_audio(cs); _retu->audio_sample_rate = audio_sample_rate;
cs->send_audio = 1; pthread_mutex_init(&_retu->ctrl_mutex, NULL);
cs->send_video = 1;
return 1;
/* Encoders */
if ( 0 == init_video_encoder(_retu, webcam, webcam_driver, video_bitrate) )
printf("Video encoder initialized!\n");
if ( 0 == init_audio_encoder(_retu) )
printf("Audio encoder initialized!\n");
/* Decoders */
if ( 0 == init_video_decoder(_retu) )
printf("Video decoder initialized!\n");
if ( 0 == init_audio_decoder(_retu, audio_channels) )
printf("Audio decoder initialized!\n");
return _retu;
} }
int init_decoder(codec_state *cs) void codec_terminate_session ( CodecState* cs )
{ {
avdevice_register_all(); if ( cs->audio_encoder ) {
avcodec_register_all(); opus_encoder_destroy(cs->audio_encoder);
avdevice_register_all(); printf("Terminated encoder!\n");
av_register_all(); }
cs->receive_video = 0; if ( cs->audio_decoder ) {
cs->receive_audio = 0; opus_decoder_destroy(cs->audio_decoder);
printf("Terminated decoder!\n");
cs->support_receive_video = init_receive_video(cs); }
cs->support_receive_audio = init_receive_audio(cs);
/* TODO: Terminate video */
cs->receive_audio = 1;
cs->receive_video = 1;
return 1;
} }

View File

@ -27,8 +27,6 @@
#include <stdio.h> #include <stdio.h>
#include <math.h> #include <math.h>
#include "toxrtp.h"
#include "toxmsi.h"
#include "../toxcore/tox.h" #include "../toxcore/tox.h"
/* Video encoding/decoding */ /* Video encoding/decoding */
@ -75,16 +73,7 @@
#define DEFAULT_WEBCAM "0" #define DEFAULT_WEBCAM "0"
#endif #endif
typedef struct { typedef struct _CodecState{
uint8_t send_audio;
uint8_t receive_audio;
uint8_t send_video;
uint8_t receive_video;
uint8_t support_send_audio;
uint8_t support_send_video;
uint8_t support_receive_audio;
uint8_t support_receive_video;
/* video encoding */ /* video encoding */
AVInputFormat *video_input_format; AVInputFormat *video_input_format;
@ -102,19 +91,19 @@ typedef struct {
/* audio encoding */ /* audio encoding */
OpusEncoder *audio_encoder; OpusEncoder *audio_encoder;
int audio_bitrate; int audio_bitrate;
int audio_sample_rate;
/* audio decoding */ /* audio decoding */
OpusDecoder *audio_decoder; OpusDecoder *audio_decoder;
uint8_t req_video_refresh;
pthread_mutex_t ctrl_mutex; pthread_mutex_t ctrl_mutex;
uint32_t frame_rate; uint32_t frame_rate;
} codec_state; } CodecState;
typedef struct _RTPMessage RTPMessage;
struct jitter_buffer *create_queue(int capacity); struct jitter_buffer *create_queue(int capacity);
int empty_queue(struct jitter_buffer *q); int empty_queue(struct jitter_buffer *q);
@ -123,8 +112,14 @@ int queue(struct jitter_buffer *q, RTPMessage *pk);
RTPMessage *dequeue(struct jitter_buffer *q, int *success); RTPMessage *dequeue(struct jitter_buffer *q, int *success);
int init_encoder(codec_state *cs); CodecState* codec_init_session( uint32_t audio_bitrate,
int init_decoder(codec_state *cs); uint16_t audio_frame_duration,
uint32_t audio_sample_rate,
uint32_t audio_channels,
uint32_t video_bitrate,
const char* webcam,
const char* webcam_driver );
void codec_terminate_session(CodecState* cs);
#endif #endif

44
toxav/toxmsi.c → toxav/msi.c Executable file → Normal file
View File

@ -29,7 +29,7 @@
#define _BSD_SOURCE #define _BSD_SOURCE
#include "toxmsi.h" #include "msi.h"
#include "../toxcore/util.h" #include "../toxcore/util.h"
#include "../toxcore/network.h" #include "../toxcore/network.h"
#include "../toxcore/event.h" #include "../toxcore/event.h"
@ -335,8 +335,6 @@ MSIMessage* msi_new_message ( uint8_t type, const uint8_t* type_id ) {
MSIMessage* _retu = calloc ( sizeof ( MSIMessage ), 1 ); MSIMessage* _retu = calloc ( sizeof ( MSIMessage ), 1 );
assert ( _retu ); assert ( _retu );
memset ( _retu, 0, sizeof ( MSIMessage ) );
if ( type == TYPE_REQUEST ) { if ( type == TYPE_REQUEST ) {
ALLOCATE_HEADER ( _retu->request, type_id, strlen ( (const char*)type_id ) ) ALLOCATE_HEADER ( _retu->request, type_id, strlen ( (const char*)type_id ) )
@ -641,7 +639,7 @@ int handle_error ( MSISession* session, MSICallError errid, uint32_t to ) {
session->last_error_id = errid; session->last_error_id = errid;
session->last_error_str = stringify_error ( errid ); session->last_error_str = stringify_error ( errid );
event.rise ( callbacks[MSI_OnError], session ); event.rise ( callbacks[MSI_OnError], session->agent_handler );
return 0; return 0;
} }
@ -692,12 +690,12 @@ void* handle_timeout ( void* arg )
/* Cancel all? */ /* Cancel all? */
uint16_t _it = 0; uint16_t _it = 0;
for ( ; _it < _peer_count; _it++ ) for ( ; _it < _peer_count; _it++ )
msi_cancel ( arg, _peers[_it] ); msi_cancel ( arg, _peers[_it], (const uint8_t*)"Timeout" );
} }
( *callbacks[MSI_OnTimeout] ) ( arg ); ( *callbacks[MSI_OnTimeout] ) ( _session->agent_handler );
( *callbacks[MSI_OnEnding ] ) ( arg ); ( *callbacks[MSI_OnEnding ] ) ( _session->agent_handler );
return NULL; return NULL;
} }
@ -829,7 +827,7 @@ int handle_recv_invite ( MSISession* session, MSIMessage* msg ) {
send_message ( session, _msg_ringing, msg->friend_id ); send_message ( session, _msg_ringing, msg->friend_id );
free_message ( _msg_ringing ); free_message ( _msg_ringing );
event.rise ( callbacks[MSI_OnInvite], session ); event.rise ( callbacks[MSI_OnInvite], session->agent_handler );
return 1; return 1;
} }
@ -852,7 +850,7 @@ int handle_recv_start ( MSISession* session, MSIMessage* msg ) {
flush_peer_type ( session, msg, 0 ); flush_peer_type ( session, msg, 0 );
event.rise ( callbacks[MSI_OnStart], session ); event.rise ( callbacks[MSI_OnStart], session->agent_handler );
return 1; return 1;
} }
@ -868,7 +866,7 @@ int handle_recv_reject ( MSISession* session, MSIMessage* msg ) {
free_message ( _msg_end ); free_message ( _msg_end );
event.timer_release ( session->call->request_timer_id ); event.timer_release ( session->call->request_timer_id );
event.rise ( callbacks[MSI_OnReject], session ); event.rise ( callbacks[MSI_OnReject], session->agent_handler );
session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout ); session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout );
return 1; return 1;
@ -882,7 +880,7 @@ int handle_recv_cancel ( MSISession* session, MSIMessage* msg ) {
terminate_call ( session ); terminate_call ( session );
event.rise ( callbacks[MSI_OnCancel], session ); event.rise ( callbacks[MSI_OnCancel], session->agent_handler );
return 1; return 1;
} }
@ -899,7 +897,7 @@ int handle_recv_end ( MSISession* session, MSIMessage* msg ) {
terminate_call ( session ); terminate_call ( session );
event.rise ( callbacks[MSI_OnEnd], session ); event.rise ( callbacks[MSI_OnEnd], session->agent_handler );
return 1; return 1;
} }
@ -912,7 +910,7 @@ int handle_recv_ringing ( MSISession* session, MSIMessage* msg ) {
return 0; return 0;
session->call->ringing_timer_id = event.timer_alloc ( handle_timeout, session, session->call->ringing_tout_ms ); session->call->ringing_timer_id = event.timer_alloc ( handle_timeout, session, session->call->ringing_tout_ms );
event.rise ( callbacks[MSI_OnRinging], session ); event.rise ( callbacks[MSI_OnRinging], session->agent_handler );
return 1; return 1;
} }
@ -950,7 +948,7 @@ int handle_recv_starting ( MSISession* session, MSIMessage* msg ) {
flush_peer_type ( session, msg, 0 ); flush_peer_type ( session, msg, 0 );
event.rise ( callbacks[MSI_OnStarting], session ); event.rise ( callbacks[MSI_OnStarting], session->agent_handler );
event.timer_release ( session->call->ringing_timer_id ); event.timer_release ( session->call->ringing_timer_id );
return 1; return 1;
@ -964,7 +962,7 @@ int handle_recv_ending ( MSISession* session, MSIMessage* msg ) {
terminate_call ( session ); terminate_call ( session );
event.rise ( callbacks[MSI_OnEnding], session ); event.rise ( callbacks[MSI_OnEnding], session->agent_handler );
return 1; return 1;
} }
@ -980,7 +978,7 @@ int handle_recv_error ( MSISession* session, MSIMessage* msg ) {
terminate_call ( session ); terminate_call ( session );
event.rise ( callbacks[MSI_OnEnding], session ); event.rise ( callbacks[MSI_OnEnding], session->agent_handler );
return 1; return 1;
} }
@ -1138,14 +1136,13 @@ void msi_register_callback ( MSICallback callback, MSICallbackID id )
* @return MSISession* The created session. * @return MSISession* The created session.
* @retval NULL Error occured. * @retval NULL Error occured.
*/ */
MSISession* msi_init_session ( Tox* messenger, const uint8_t* user_agent ) { MSISession* msi_init_session ( Tox* messenger, const uint8_t* ua_name ) {
assert ( messenger ); assert ( messenger );
assert ( user_agent );
MSISession* _retu = calloc ( sizeof ( MSISession ), 1 ); MSISession* _retu = calloc ( sizeof ( MSISession ), 1 );
assert ( _retu ); assert ( _retu );
_retu->user_agent = user_agent; _retu->ua_name = ua_name;
_retu->messenger_handle = messenger; _retu->messenger_handle = messenger;
_retu->agent_handler = NULL; _retu->agent_handler = NULL;
@ -1300,14 +1297,17 @@ int msi_answer ( MSISession* session, MSICallType call_type ) {
* @brief Cancel request. * @brief Cancel request.
* *
* @param session Control session. * @param session Control session.
* @param friend_id The friend. * @param reason Set optional reason header. Pass NULL if none.
* @return int * @return int
*/ */
int msi_cancel ( MSISession* session, int friend_id ) { int msi_cancel ( MSISession* session, uint32_t peer, const uint8_t* reason ) {
assert ( session ); assert ( session );
MSIMessage* _msg_cancel = msi_new_message ( TYPE_REQUEST, stringify_request ( cancel ) ); MSIMessage* _msg_cancel = msi_new_message ( TYPE_REQUEST, stringify_request ( cancel ) );
send_message ( session, _msg_cancel, friend_id );
if ( reason ) msi_msg_set_reason(_msg_cancel, reason, strlen((const char*)reason));
send_message ( session, _msg_cancel, peer );
free_message ( _msg_cancel ); free_message ( _msg_cancel );
terminate_call ( session ); terminate_call ( session );

9
toxav/toxmsi.h → toxav/msi.h Executable file → Normal file
View File

@ -103,7 +103,7 @@ typedef struct _MSISession {
int last_error_id; /* Determine the last error */ int last_error_id; /* Determine the last error */
const uint8_t* last_error_str; const uint8_t* last_error_str;
const uint8_t* user_agent; const uint8_t* ua_name;
void* agent_handler; /* Pointer to an object that is handling msi */ void* agent_handler; /* Pointer to an object that is handling msi */
Tox* messenger_handle; Tox* messenger_handle;
@ -156,7 +156,7 @@ void msi_register_callback(MSICallback callback, MSICallbackID id);
* @return MSISession* The created session. * @return MSISession* The created session.
* @retval NULL Error occured. * @retval NULL Error occured.
*/ */
MSISession* msi_init_session ( Tox* messenger, const uint8_t* user_agent ); MSISession* msi_init_session ( Tox* messenger, const uint8_t* ua_name );
/** /**
@ -205,10 +205,11 @@ int msi_answer ( MSISession* session, MSICallType call_type );
* @brief Cancel request. * @brief Cancel request.
* *
* @param session Control session. * @param session Control session.
* @param friend_id The friend. * @param peer To which peer.
* @param reason Set optional reason header. Pass NULL if none.
* @return int * @return int
*/ */
int msi_cancel ( MSISession* session, int friend_id ); int msi_cancel ( MSISession* session, uint32_t peer, const uint8_t* reason );
/** /**

View File

@ -51,9 +51,8 @@
#include <pthread.h> #include <pthread.h>
#include <opus/opus.h> #include <opus/opus.h>
#include "toxmsi.h" #include "media.h"
#include "toxrtp.h" #include "toxav.h"
#include "toxmedia.h"
#include "../toxcore/event.h" #include "../toxcore/event.h"
#include "../toxcore/tox.h" #include "../toxcore/tox.h"
@ -75,13 +74,9 @@ typedef struct av_friend_s {
} av_friend_t; } av_friend_t;
typedef struct av_session_s { typedef struct av_session_s {
MSISession* _msi;
RTPSession* _rtp_audio;
RTPSession* _rtp_video;
/* Encoding/decoding/capturing/playing */ /* Encoding/decoding/capturing/playing */
ToxAv* av;
codec_state* cs;
VideoPicture video_picture; VideoPicture video_picture;
struct ALCdevice *audio_capture_device; struct ALCdevice *audio_capture_device;
@ -91,8 +86,9 @@ typedef struct av_session_s {
/* context for converting webcam image format to something the video encoder can use */ /* context for converting webcam image format to something the video encoder can use */
struct SwsContext *sws_ctx; struct SwsContext *sws_ctx;
/**/ /* Thread running control */
int running_decaud, running_encaud,
running_decvid, running_encvid;
pthread_mutex_t _mutex; pthread_mutex_t _mutex;
@ -243,7 +239,7 @@ static void fraddr_to_str(uint8_t *id_bin, char *id_str)
int display_received_frame(av_session_t* _phone, AVFrame *r_video_frame) int display_received_frame(av_session_t* _phone, AVFrame *r_video_frame)
{ {
codec_state* cs = _phone->cs; CodecState* cs = get_cs_temp(_phone->av);
AVPicture pict; AVPicture pict;
SDL_LockYUVOverlay(_phone->video_picture.bmp); SDL_LockYUVOverlay(_phone->video_picture.bmp);
@ -268,71 +264,15 @@ int display_received_frame(av_session_t* _phone, AVFrame *r_video_frame)
return 1; return 1;
} }
int video_encoder_refresh(codec_state *cs, int bps)
{
if (cs->video_encoder_ctx)
avcodec_close(cs->video_encoder_ctx);
cs->video_encoder = avcodec_find_encoder(VIDEO_CODEC);
if (!cs->video_encoder) {
printf("init video_encoder failed\n");
return -1;
}
cs->video_encoder_ctx = avcodec_alloc_context3(cs->video_encoder);
if (!cs->video_encoder_ctx) {
printf("init video_encoder_ctx failed\n");
return -1;
}
cs->video_encoder_ctx->bit_rate = bps;
cs->video_encoder_ctx->rc_min_rate = cs->video_encoder_ctx->rc_max_rate = cs->video_encoder_ctx->bit_rate;
av_opt_set_double(cs->video_encoder_ctx->priv_data, "max-intra-rate", 90, 0);
av_opt_set(cs->video_encoder_ctx->priv_data, "quality", "realtime", 0);
cs->video_encoder_ctx->thread_count = 4;
cs->video_encoder_ctx->rc_buffer_aggressivity = 0.95;
cs->video_encoder_ctx->rc_buffer_size = bps * 6;
cs->video_encoder_ctx->profile = 0;
cs->video_encoder_ctx->qmax = 54;
cs->video_encoder_ctx->qmin = 4;
AVRational myrational = {1, 25};
cs->video_encoder_ctx->time_base = myrational;
cs->video_encoder_ctx->gop_size = 99999;
cs->video_encoder_ctx->pix_fmt = PIX_FMT_YUV420P;
cs->video_encoder_ctx->width = cs->webcam_decoder_ctx->width;
cs->video_encoder_ctx->height = cs->webcam_decoder_ctx->height;
if (avcodec_open2(cs->video_encoder_ctx, cs->video_encoder, NULL) < 0) {
printf("opening video encoder failed\n");
return -1;
}
return 0;
}
int video_decoder_refresh(av_session_t* _phone, int width, int height)
{
printf("need to refresh\n");
screen = SDL_SetVideoMode(width, height, 0, 0);
if (_phone->video_picture.bmp)
SDL_FreeYUVOverlay(_phone->video_picture.bmp);
_phone->video_picture.bmp = SDL_CreateYUVOverlay(width, height, SDL_YV12_OVERLAY, screen);
_phone->sws_SDL_r_ctx = sws_getContext(width, height, _phone->cs->video_decoder_ctx->pix_fmt, width, height, PIX_FMT_YUV420P,
SWS_BILINEAR, NULL, NULL, NULL);
return 1;
}
void *encode_video_thread(void *arg) void *encode_video_thread(void *arg)
{ {
INFO("Started encode video thread!"); INFO("Started encode video thread!");
av_session_t* _phone = arg; av_session_t* _phone = arg;
codec_state *cs = _phone->cs; _phone->running_encvid = 1;
CodecState *cs = get_cs_temp(_phone->av);
AVPacket pkt1, *packet = &pkt1; AVPacket pkt1, *packet = &pkt1;
int p = 0; int p = 0;
int got_packet; int got_packet;
@ -354,7 +294,7 @@ void *encode_video_thread(void *arg)
cs->webcam_decoder_ctx->pix_fmt, cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height, PIX_FMT_YUV420P, cs->webcam_decoder_ctx->pix_fmt, cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height, PIX_FMT_YUV420P,
SWS_BILINEAR, NULL, NULL, NULL); SWS_BILINEAR, NULL, NULL, NULL);
while (cs->send_video) { while (_phone->running_encvid) {
if (av_read_frame(cs->video_format_ctx, packet) < 0) { if (av_read_frame(cs->video_format_ctx, packet) < 0) {
printf("error reading frame\n"); printf("error reading frame\n");
@ -400,9 +340,7 @@ void *encode_video_thread(void *arg)
if (!enc_video_packet.data) fprintf(stderr, "video packet data is NULL\n"); if (!enc_video_packet.data) fprintf(stderr, "video packet data is NULL\n");
if ( 0 > rtp_send_msg ( _phone->_rtp_video, _phone->_messenger, enc_video_packet.data, enc_video_packet.size) ) { toxav_send_rtp_payload(_phone->av, TypeVideo, enc_video_packet.data, enc_video_packet.size);
printf("Failed sending message\n");
}
av_free_packet(&enc_video_packet); av_free_packet(&enc_video_packet);
} }
@ -420,6 +358,9 @@ void *encode_video_thread(void *arg)
avcodec_close(cs->webcam_decoder_ctx); avcodec_close(cs->webcam_decoder_ctx);
avcodec_close(cs->video_encoder_ctx); avcodec_close(cs->video_encoder_ctx);
pthread_mutex_unlock(&cs->ctrl_mutex); pthread_mutex_unlock(&cs->ctrl_mutex);
_phone->running_encvid = -1;
pthread_exit ( NULL ); pthread_exit ( NULL );
} }
@ -427,8 +368,8 @@ void *encode_audio_thread(void *arg)
{ {
INFO("Started encode audio thread!"); INFO("Started encode audio thread!");
av_session_t* _phone = arg; av_session_t* _phone = arg;
_phone->running_encaud = 1;
codec_state *cs = _phone->cs;
unsigned char encoded_data[4096]; unsigned char encoded_data[4096];
int encoded_size = 0; int encoded_size = 0;
int16_t frame[4096]; int16_t frame[4096];
@ -436,28 +377,31 @@ void *encode_audio_thread(void *arg)
ALint sample = 0; ALint sample = 0;
alcCaptureStart((ALCdevice*)_phone->audio_capture_device); alcCaptureStart((ALCdevice*)_phone->audio_capture_device);
while (cs->send_audio) { while (_phone->running_encaud) {
alcGetIntegerv((ALCdevice*)_phone->audio_capture_device, ALC_CAPTURE_SAMPLES, (ALCsizei)sizeof(ALint), &sample); alcGetIntegerv((ALCdevice*)_phone->audio_capture_device, ALC_CAPTURE_SAMPLES, (ALCsizei)sizeof(ALint), &sample);
if (sample >= frame_size) { if (sample >= frame_size) {
alcCaptureSamples((ALCdevice*)_phone->audio_capture_device, frame, frame_size); alcCaptureSamples((ALCdevice*)_phone->audio_capture_device, frame, frame_size);
encoded_size = opus_encode(cs->audio_encoder, frame, frame_size, encoded_data, MAX_RTP_SIZE);
encoded_size = toxav_encode_audio(_phone->av, frame, frame_size, encoded_data);
if (encoded_size <= 0) { if (encoded_size <= 0) {
printf("Could not encode audio packet\n"); printf("Could not encode audio packet\n");
} else { } else {
rtp_send_msg ( _phone->_rtp_audio, _phone->_messenger, encoded_data, encoded_size ); if ( -1 == toxav_send_rtp_payload(_phone->av, TypeAudio, encoded_data, encoded_size) )
assert(0);
} }
} else { } else {
usleep(1000); usleep(1000);
} }
} }
/* clean up codecs */ /* clean up codecs *
pthread_mutex_lock(&cs->ctrl_mutex); pthread_mutex_lock(&cs->ctrl_mutex);*/
alcCaptureStop((ALCdevice*)_phone->audio_capture_device); alcCaptureStop((ALCdevice*)_phone->audio_capture_device);
alcCaptureCloseDevice((ALCdevice*)_phone->audio_capture_device); alcCaptureCloseDevice((ALCdevice*)_phone->audio_capture_device);
pthread_mutex_unlock(&cs->ctrl_mutex); /*pthread_mutex_unlock(&cs->ctrl_mutex);*/
_phone->running_encaud = -1;
pthread_exit ( NULL ); pthread_exit ( NULL );
} }
@ -465,10 +409,14 @@ void *decode_video_thread(void *arg)
{ {
INFO("Started decode video thread!"); INFO("Started decode video thread!");
av_session_t* _phone = arg; av_session_t* _phone = arg;
_phone->running_decvid = 1;
codec_state *cs = _phone->cs; CodecState *cs = get_cs_temp(_phone->av);
cs->video_stream = 0; cs->video_stream = 0;
RTPMessage *r_msg;
int recved_size;
uint8_t dest[RTP_PAYLOAD_SIZE];
int dec_frame_finished; int dec_frame_finished;
AVFrame *r_video_frame; AVFrame *r_video_frame;
r_video_frame = avcodec_alloc_frame(); r_video_frame = avcodec_alloc_frame();
@ -477,20 +425,34 @@ void *decode_video_thread(void *arg)
int width = 0; int width = 0;
int height = 0; int height = 0;
while (cs->receive_video) { while (_phone->running_decvid) {
r_msg = rtp_recv_msg ( _phone->_rtp_video );
recved_size = toxav_recv_rtp_payload(_phone->av, TypeVideo, 1, dest);
if (recved_size) {
memcpy(dec_video_packet.data, dest, recved_size);
dec_video_packet.size = recved_size;
if (r_msg) {
memcpy(dec_video_packet.data, r_msg->data, r_msg->length);
dec_video_packet.size = r_msg->length;
avcodec_decode_video2(cs->video_decoder_ctx, r_video_frame, &dec_frame_finished, &dec_video_packet); avcodec_decode_video2(cs->video_decoder_ctx, r_video_frame, &dec_frame_finished, &dec_video_packet);
if (dec_frame_finished) { if (dec_frame_finished) {
/* Check if size has changed */
if (cs->video_decoder_ctx->width != width || cs->video_decoder_ctx->height != height) { if (cs->video_decoder_ctx->width != width || cs->video_decoder_ctx->height != height) {
width = cs->video_decoder_ctx->width; width = cs->video_decoder_ctx->width;
height = cs->video_decoder_ctx->height; height = cs->video_decoder_ctx->height;
printf("w: %d h: %d \n", width, height); printf("w: %d h: %d \n", width, height);
video_decoder_refresh(_phone, width, height);
screen = SDL_SetVideoMode(width, height, 0, 0);
if (_phone->video_picture.bmp)
SDL_FreeYUVOverlay(_phone->video_picture.bmp);
_phone->video_picture.bmp = SDL_CreateYUVOverlay(width, height, SDL_YV12_OVERLAY, screen);
_phone->sws_SDL_r_ctx = sws_getContext(width, height, cs->video_decoder_ctx->pix_fmt, width, height, PIX_FMT_YUV420P,
SWS_BILINEAR, NULL, NULL, NULL);
} }
display_received_frame(_phone, r_video_frame); display_received_frame(_phone, r_video_frame);
@ -498,18 +460,20 @@ void *decode_video_thread(void *arg)
/* TODO: request the sender to create a new i-frame immediatly */ /* TODO: request the sender to create a new i-frame immediatly */
printf("Bad video packet\n"); printf("Bad video packet\n");
} }
rtp_free_msg(NULL, r_msg);
} }
usleep(1000); usleep(1000);
} }
/* clean up codecs */ /* clean up codecs */
pthread_mutex_lock(&cs->ctrl_mutex);
av_free(r_video_frame); av_free(r_video_frame);
pthread_mutex_lock(&cs->ctrl_mutex);
avcodec_close(cs->video_decoder_ctx); avcodec_close(cs->video_decoder_ctx);
pthread_mutex_unlock(&cs->ctrl_mutex); pthread_mutex_unlock(&cs->ctrl_mutex);
_phone->running_decvid = -1;
pthread_exit ( NULL ); pthread_exit ( NULL );
} }
@ -517,9 +481,10 @@ void *decode_audio_thread(void *arg)
{ {
INFO("Started decode audio thread!"); INFO("Started decode audio thread!");
av_session_t* _phone = arg; av_session_t* _phone = arg;
_phone->running_decaud = 1;
codec_state *cs = _phone->cs; int recved_size;
RTPMessage *r_msg; uint8_t dest [RTP_PAYLOAD_SIZE];
int frame_size = AUDIO_FRAME_SIZE; int frame_size = AUDIO_FRAME_SIZE;
int data_size; int data_size;
@ -538,7 +503,7 @@ void *decode_audio_thread(void *arg)
alSourcei(source, AL_LOOPING, AL_FALSE); alSourcei(source, AL_LOOPING, AL_FALSE);
ALuint buffer; ALuint buffer;
ALint val; ALint ready;
uint16_t zeros[frame_size]; uint16_t zeros[frame_size];
memset(zeros, 0, frame_size); memset(zeros, 0, frame_size);
@ -557,49 +522,28 @@ void *decode_audio_thread(void *arg)
goto ending; goto ending;
} }
struct jitter_buffer *j_buf = NULL;
j_buf = create_queue(20);
int success = 0;
int dec_frame_len = 0; int dec_frame_len = 0;
while (_phone->running_decaud) {
while (cs->receive_audio) { alGetSourcei(source, AL_BUFFERS_PROCESSED, &ready);
r_msg = rtp_recv_msg ( _phone->_rtp_audio ); recved_size = toxav_recv_rtp_payload(_phone->av, TypeAudio, ready, dest);
if (r_msg) { if ( recved_size == ErrorAudioPacketLost ) {
/* push the packet into the queue */
queue(j_buf, r_msg);
}
success = 0;
alGetSourcei(source, AL_BUFFERS_PROCESSED, &val);
/* grab a packet from the queue */
if (val > 0) {
r_msg = dequeue(j_buf, &success);
}
if (success > 0) {
/* good packet */
if (success == 1) {
dec_frame_len = opus_decode(cs->audio_decoder, r_msg->data, r_msg->length, PCM, frame_size, 0);
rtp_free_msg(NULL, r_msg);
}
/* lost packet */
else if (success == 2) {
printf("Lost packet\n"); printf("Lost packet\n");
dec_frame_len = opus_decode(cs->audio_decoder, NULL, 0, PCM, frame_size, 1); dec_frame_len = toxav_decode_audio(_phone->av, NULL, 0, frame_size, PCM);
} else if ( recved_size ) {
dec_frame_len = toxav_decode_audio(_phone->av, dest, recved_size, frame_size, PCM);
} }
if (dec_frame_len) {
alGetSourcei(source, AL_BUFFERS_PROCESSED, &val);
if (val <= 0) /* Play the packet */
if (dec_frame_len) {
alGetSourcei(source, AL_BUFFERS_PROCESSED, &ready);
if (ready <= 0)
continue; continue;
alSourceUnqueueBuffers(source, 1, &buffer); alSourceUnqueueBuffers(source, 1, &buffer);
@ -619,13 +563,9 @@ void *decode_audio_thread(void *arg)
break; break;
} }
alGetSourcei(source, AL_SOURCE_STATE, &val); alGetSourcei(source, AL_SOURCE_STATE, &ready);
if (val != AL_PLAYING) if (ready != AL_PLAYING) alSourcePlay(source);
alSourcePlay(source);
}
} }
usleep(1000); usleep(1000);
@ -642,7 +582,10 @@ ending:
alcDestroyContext(ctx); alcDestroyContext(ctx);
alcCloseDevice(dev); alcCloseDevice(dev);
pthread_mutex_unlock(&cs->ctrl_mutex); pthread_mutex_unlock(&cs->ctrl_mutex); */
_phone->running_decaud = -1;
pthread_exit ( NULL ); pthread_exit ( NULL );
} }
@ -650,61 +593,42 @@ ending:
int phone_startmedia_loop ( av_session_t* _phone ) int phone_startmedia_loop ( ToxAv* arg )
{ {
if ( !_phone ){ if ( !arg ){
return -1; return -1;
} }
_phone->_rtp_audio = rtp_init_session ( toxav_prepare_transmission(arg);
type_audio,
_phone->_messenger,
_phone->_msi->call->peers[0],
_phone->_msi->call->key_peer,
_phone->_msi->call->key_local,
_phone->_msi->call->nonce_peer,
_phone->_msi->call->nonce_local
);
_phone->_rtp_video = rtp_init_session (
type_video,
_phone->_messenger,
_phone->_msi->call->peers[0],
_phone->_msi->call->key_peer,
_phone->_msi->call->key_local,
_phone->_msi->call->nonce_peer,
_phone->_msi->call->nonce_local
);
init_encoder(_phone->cs);
init_decoder(_phone->cs);
/* /*
* Rise all threads * Rise all threads
*/ */
/* Only checks for last peer */ /* Only checks for last peer */
if ( _phone->_msi->call->type_peer[0] == type_video && 0 > event.rise(encode_video_thread, _phone) ) if ( toxav_get_peer_transmission_type(arg, 0) == TypeVideo &&
0 > event.rise(encode_video_thread, toxav_get_agent_handler(arg)) )
{ {
INFO("Error while starting encode_video_thread()"); INFO("Error while starting encode_video_thread()");
return -1; return -1;
} }
/* Always send audio */ /* Always send audio */
if ( 0 > event.rise(encode_audio_thread, _phone) ) if ( 0 > event.rise(encode_audio_thread, toxav_get_agent_handler(arg)) )
{ {
INFO("Error while starting encode_audio_thread()"); INFO("Error while starting encode_audio_thread()");
return -1; return -1;
} }
/* Only checks for last peer */ /* Only checks for last peer */
if ( _phone->_msi->call->type_peer[0] == type_video && 0 > event.rise(decode_video_thread, _phone) ) if ( toxav_get_peer_transmission_type(arg, 0) == TypeVideo &&
0 > event.rise(decode_video_thread, toxav_get_agent_handler(arg)) )
{ {
INFO("Error while starting decode_video_thread()"); INFO("Error while starting decode_video_thread()");
return -1; return -1;
} }
if ( 0 > event.rise(decode_audio_thread, _phone) ) if ( 0 > event.rise(decode_audio_thread, toxav_get_agent_handler(arg)) )
{ {
INFO("Error while starting decode_audio_thread()"); INFO("Error while starting decode_audio_thread()");
return -1; return -1;
@ -734,13 +658,13 @@ int phone_startmedia_loop ( av_session_t* _phone )
void* callback_recv_invite ( void* _arg ) void* callback_recv_invite ( void* _arg )
{ {
MSISession* _msi = _arg; assert(_arg);
switch ( _msi->call->type_peer[_msi->call->peer_count - 1] ){ switch ( toxav_get_peer_transmission_type(_arg, 0) ){
case type_audio: case TypeAudio:
INFO( "Incoming audio call!"); INFO( "Incoming audio call!");
break; break;
case type_video: case TypeVideo:
INFO( "Incoming video call!"); INFO( "Incoming video call!");
break; break;
} }
@ -754,8 +678,7 @@ void* callback_recv_ringing ( void* _arg )
} }
void* callback_recv_starting ( void* _arg ) void* callback_recv_starting ( void* _arg )
{ {
MSISession* _session = _arg; if ( 0 != phone_startmedia_loop(_arg) ){
if ( 0 != phone_startmedia_loop(_session->agent_handler) ){
INFO("Starting call failed!"); INFO("Starting call failed!");
} else { } else {
INFO ("Call started! ( press h to hangup )"); INFO ("Call started! ( press h to hangup )");
@ -764,14 +687,20 @@ void* callback_recv_starting ( void* _arg )
} }
void* callback_recv_ending ( void* _arg ) void* callback_recv_ending ( void* _arg )
{ {
av_session_t* _phone = ((MSISession*)_arg)->agent_handler; av_session_t* _phone = toxav_get_agent_handler(_arg);
_phone->cs->send_audio = 0; _phone->running_encaud = 0;
_phone->cs->send_video = 0; _phone->running_decaud = 0;
_phone->cs->receive_audio = 0; _phone->running_encvid = 0;
_phone->cs->receive_video = 0; _phone->running_decvid = 0;
/* Wait until all threads are done */ /* Wait until all threads are done */
while ( _phone->running_encaud != -1 ||
_phone->running_decaud != -1 ||
_phone->running_encvid != -1 ||
_phone->running_decvid != -1 )
usleep(10000000); usleep(10000000);
INFO ( "Call ended!" ); INFO ( "Call ended!" );
@ -780,16 +709,15 @@ void* callback_recv_ending ( void* _arg )
void* callback_recv_error ( void* _arg ) void* callback_recv_error ( void* _arg )
{ {
MSISession* _session = _arg; /*MSISession* _session = _arg;
INFO( "Error: %s", _session->last_error_str ); INFO( "Error: %s", _session->last_error_str ); */
pthread_exit(NULL); pthread_exit(NULL);
} }
void* callback_call_started ( void* _arg ) void* callback_call_started ( void* _arg )
{ {
MSISession* _session = _arg; if ( 0 != phone_startmedia_loop(_arg) ){
if ( 0 != phone_startmedia_loop(_session->agent_handler) ){
INFO("Starting call failed!"); INFO("Starting call failed!");
} else { } else {
INFO ("Call started! ( press h to hangup )"); INFO ("Call started! ( press h to hangup )");
@ -809,16 +737,23 @@ void* callback_call_rejected ( void* _arg )
} }
void* callback_call_ended ( void* _arg ) void* callback_call_ended ( void* _arg )
{ {
av_session_t* _phone = ((MSISession*)_arg)->agent_handler; av_session_t* _phone = toxav_get_agent_handler(_arg);
_phone->cs->send_audio = 0; _phone->running_encaud = 0;
_phone->cs->send_video = 0; _phone->running_decaud = 0;
_phone->cs->receive_audio = 0; _phone->running_encvid = 0;
_phone->cs->receive_video = 0; _phone->running_decvid = 0;
/* Wait until all threads are done */ /* Wait until all threads are done */
while ( _phone->running_encaud != -1 ||
_phone->running_decaud != -1 ||
_phone->running_encvid != -1 ||
_phone->running_decvid != -1 )
usleep(10000000); usleep(10000000);
toxav_kill_transmission(_phone->av);
INFO ( "Call ended!" ); INFO ( "Call ended!" );
pthread_exit(NULL); pthread_exit(NULL);
} }
@ -844,9 +779,7 @@ av_session_t* av_init_session()
} }
_retu->_friends = NULL; _retu->_friends = NULL;
_retu->av = toxav_new(_retu->_messenger, _retu, _USERAGENT);
_retu->_rtp_audio = NULL;
_retu->_rtp_video = NULL;
const ALchar *_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER); const ALchar *_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
@ -879,8 +812,6 @@ av_session_t* av_init_session()
INFO("Selected: %d ( %s )", selection, device_names[selection]); INFO("Selected: %d ( %s )", selection, device_names[selection]);
} }
_retu->cs = av_calloc(sizeof(codec_state), 1);
_retu->audio_capture_device = _retu->audio_capture_device =
(struct ALCdevice*)alcCaptureOpenDevice( (struct ALCdevice*)alcCaptureOpenDevice(
device_names[selection], AUDIO_SAMPLE_RATE, AL_FORMAT_MONO16, AUDIO_FRAME_SIZE * 4); device_names[selection], AUDIO_SAMPLE_RATE, AL_FORMAT_MONO16, AUDIO_FRAME_SIZE * 4);
@ -897,29 +828,22 @@ av_session_t* av_init_session()
fraddr_to_str( _byte_address, _retu->_my_public_id ); fraddr_to_str( _byte_address, _retu->_my_public_id );
/* Initialize msi */
_retu->_msi = msi_init_session ( _retu->_messenger, (const uint8_t*)_USERAGENT );
if ( !_retu->_msi ) {
fprintf ( stderr, "msi_init_session() failed\n" );
return NULL;
}
_retu->_msi->agent_handler = _retu;
/* ------------------ */ /* ------------------ */
msi_register_callback(callback_call_started, MSI_OnStart);
msi_register_callback(callback_call_canceled, MSI_OnCancel);
msi_register_callback(callback_call_rejected, MSI_OnReject);
msi_register_callback(callback_call_ended, MSI_OnEnd);
msi_register_callback(callback_recv_invite, MSI_OnInvite);
msi_register_callback(callback_recv_ringing, MSI_OnRinging); toxav_register_callstate_callback(callback_call_started, OnStart);
msi_register_callback(callback_recv_starting, MSI_OnStarting); toxav_register_callstate_callback(callback_call_canceled, OnCancel);
msi_register_callback(callback_recv_ending, MSI_OnEnding); toxav_register_callstate_callback(callback_call_rejected, OnReject);
toxav_register_callstate_callback(callback_call_ended, OnEnd);
toxav_register_callstate_callback(callback_recv_invite, OnInvite);
toxav_register_callstate_callback(callback_recv_ringing, OnRinging);
toxav_register_callstate_callback(callback_recv_starting, OnStarting);
toxav_register_callstate_callback(callback_recv_ending, OnEnding);
toxav_register_callstate_callback(callback_recv_error, OnError);
toxav_register_callstate_callback(callback_requ_timeout, OnTimeout);
msi_register_callback(callback_recv_error, MSI_OnError);
msi_register_callback(callback_requ_timeout, MSI_OnTimeout);
/* ------------------ */ /* ------------------ */
return _retu; return _retu;
@ -927,17 +851,18 @@ av_session_t* av_init_session()
int av_terminate_session(av_session_t* _phone) int av_terminate_session(av_session_t* _phone)
{ {
if ( _phone->_msi->call ){ toxav_hangup(_phone->av);
msi_hangup(_phone->_msi); /* Hangup the phone first */
}
free(_phone->_friends); free(_phone->_friends);
msi_terminate_session(_phone->_msi);
pthread_mutex_destroy ( &_phone->_mutex ); pthread_mutex_destroy ( &_phone->_mutex );
Tox* _p = _phone->_messenger; Tox* _p = _phone->_messenger;
_phone->_messenger = NULL; usleep(100000); /* Wait for tox_pool to end */ _phone->_messenger = NULL; usleep(100000); /* Wait for tox_poll to end */
tox_kill(_p); tox_kill(_p);
toxav_kill(_phone->av);
free(_phone);
printf("\r[i] Quit!\n"); printf("\r[i] Quit!\n");
return 0; return 0;
@ -1047,22 +972,17 @@ void do_phone ( av_session_t* _phone )
} break; } break;
case 'c': case 'c':
{ {
if ( _phone->_msi->call ){ ToxAvCallType _ctype;
INFO("Already in a call");
break;
}
MSICallType _ctype;
if ( _len < 5 ){ if ( _len < 5 ){
INFO("Invalid input; usage: c a/v [friend]"); INFO("Invalid input; usage: c a/v [friend]");
break; break;
} }
else if ( _line[2] == 'a' || _line[2] != 'v' ){ /* default and audio */ else if ( _line[2] == 'a' || _line[2] != 'v' ){ /* default and audio */
_ctype = type_audio; _ctype = TypeAudio;
} }
else { /* video */ else { /* video */
_ctype = type_video; _ctype = TypeVideo;
} }
char* _end; char* _end;
@ -1073,45 +993,41 @@ void do_phone ( av_session_t* _phone )
break; break;
} }
/* Set timeout */ if ( toxav_call(_phone->av, _friend, _ctype, 30) == ErrorAlreadyInCall ){
msi_invite ( _phone->_msi, _ctype, 10 * 1000, _friend ); INFO("Already in a call");
INFO("Calling friend: %d!", _friend); break;
}
else INFO("Calling friend: %d!", _friend);
} break; } break;
case 'h': case 'h':
{ {
if ( !_phone->_msi->call ){ if ( toxav_hangup(_phone->av) == ErrorNoCall ) {
INFO("No call!"); INFO("No call!");
break; break;
} }
else INFO("Hung up...");
msi_hangup(_phone->_msi);
INFO("Hung up...");
} break; } break;
case 'a': case 'a':
{ {
ToxAvError rc;
if ( _phone->_msi->call && _phone->_msi->call->state != call_starting ) {
break;
}
if ( _len > 1 && _line[2] == 'v' ) if ( _len > 1 && _line[2] == 'v' )
msi_answer(_phone->_msi, type_video); rc = toxav_answer(_phone->av, TypeVideo);
else else
msi_answer(_phone->_msi, type_audio); rc = toxav_answer(_phone->av, TypeAudio);
if ( rc == ErrorInvalidState ) {
INFO("No call to answer!");
}
} break; } break;
case 'r': case 'r':
{ {
if ( _phone->_msi->call && _phone->_msi->call->state != call_starting ){ if ( toxav_reject(_phone->av, "User action") == ErrorInvalidState )
break; INFO("No state to cancel!");
} else INFO("Call Rejected...");
msi_reject(_phone->_msi, NULL);
INFO("Call Rejected...");
} break; } break;
case 'q': case 'q':
@ -1124,7 +1040,6 @@ void do_phone ( av_session_t* _phone )
} }
default: default:
{ {
INFO("Invalid command!");
} break; } break;
} }

3
toxav/toxrtp.c → toxav/rtp.c Executable file → Normal file
View File

@ -26,7 +26,7 @@
#include "config.h" #include "config.h"
#endif /* HAVE_CONFIG_H */ #endif /* HAVE_CONFIG_H */
#include "toxrtp.h" #include "rtp.h"
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
@ -245,6 +245,7 @@ RTPHeader* extract_header ( const uint8_t* payload, int length )
* Now it my happen that this is out of order but * Now it my happen that this is out of order but
* it cuts down chances of parsing some invalid value * it cuts down chances of parsing some invalid value
*/ */
if ( GET_FLAG_VERSION(_retu) != RTP_VERSION ){ if ( GET_FLAG_VERSION(_retu) != RTP_VERSION ){
/* Deallocate */ /* Deallocate */
free(_retu); free(_retu);

0
toxav/toxrtp.h → toxav/rtp.h Executable file → Normal file
View File

352
toxav/toxav.c Normal file
View File

@ -0,0 +1,352 @@
/** toxav.c
*
* 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/>.
*
*
* Report bugs/suggestions at either #tox-dev @ freenode.net:6667 or
* my email: eniz_vukovic@hotmail.com
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include "toxav.h"
#include "../toxcore/tox.h"
#include "rtp.h"
#include "msi.h"
#include "media.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define inline__ inline __attribute__((always_inline))
static const uint8_t audio_index = 0, video_index = 1;
typedef enum {
ts_closing,
ts_running,
ts_closed
} ThreadState;
typedef struct _ToxAv
{
Tox* messenger;
MSISession* msi_session; /** Main msi session */
RTPSession* rtp_sessions[2]; /* Audio is first and video is second */
/* TODO: Add media session */
struct jitter_buffer* j_buf;
CodecState* cs;
/* TODO: Add media session threads */
void* agent_handler;
} ToxAv;
/********************************************************************************************************************
********************************************************************************************************************
********************************************************************************************************************
********************************************************************************************************************
********************************************************************************************************************
*
*
*
* PUBLIC API FUNCTIONS IMPLEMENTATIONS
*
*
*
********************************************************************************************************************
********************************************************************************************************************
********************************************************************************************************************
********************************************************************************************************************
********************************************************************************************************************/
ToxAv* toxav_new( Tox* messenger, void* useragent, const char* ua_name )
{
ToxAv* av = calloc ( sizeof(ToxAv), 1);
av->msi_session = msi_init_session(messenger, (const unsigned char*) ua_name );
av->msi_session->agent_handler = av;
av->rtp_sessions[0] = av->rtp_sessions [1] = NULL;
av->messenger = messenger;
/* NOTE: This should be user defined or? */
av->j_buf = create_queue(20);
av->cs = codec_init_session(AUDIO_BITRATE, AUDIO_FRAME_DURATION, AUDIO_SAMPLE_RATE, 1, VIDEO_BITRATE, DEFAULT_WEBCAM, VIDEO_DRIVER);
av->agent_handler = useragent;
return av;
}
void toxav_kill ( ToxAv* av )
{
msi_terminate_session(av->msi_session);
if ( av->rtp_sessions[audio_index] ) {
rtp_terminate_session(av->rtp_sessions[audio_index], av->msi_session->messenger_handle);
}
if ( av->rtp_sessions[video_index] ) {
rtp_terminate_session(av->rtp_sessions[video_index], av->msi_session->messenger_handle);
}
codec_terminate_session(av->cs);
free(av);
}
void toxav_register_callstate_callback ( ToxAVCallback callback, ToxAvCallbackID id )
{
msi_register_callback((MSICallback)callback, (MSICallbackID) id);
}
int toxav_call (ToxAv* av, int user, ToxAvCallType call_type, int ringing_seconds )
{
if ( av->msi_session->call ) {
return ErrorAlreadyInCall;
}
return msi_invite(av->msi_session, call_type, ringing_seconds * 1000, user);
}
int toxav_hangup ( ToxAv* av )
{
if ( !av->msi_session->call ) {
return ErrorNoCall;
}
if ( av->msi_session->call->state != call_active ) {
return ErrorInvalidState;
}
return msi_hangup(av->msi_session);
}
int toxav_answer ( ToxAv* av, ToxAvCallType call_type )
{
if ( !av->msi_session->call ) {
return ErrorNoCall;
}
if ( av->msi_session->call->state != call_starting ) {
return ErrorInvalidState;
}
return msi_answer(av->msi_session, call_type);
}
int toxav_reject ( ToxAv* av, const char* reason )
{
if ( !av->msi_session->call ) {
return ErrorNoCall;
}
if ( av->msi_session->call->state != call_starting ) {
return ErrorInvalidState;
}
return msi_reject(av->msi_session, (const uint8_t*) reason);
}
int toxav_cancel ( ToxAv* av, const char* reason )
{
if ( !av->msi_session->call ) {
return ErrorNoCall;
}
return msi_cancel(av->msi_session, 0, (const uint8_t*)reason);
}
/* You can stop the call at any state */
int toxav_stop_call ( ToxAv* av )
{
if ( !av->msi_session->call ) {
return ErrorNoCall;
}
return msi_stopcall(av->msi_session);
}
int toxav_prepare_transmission ( ToxAv* av )
{
assert(av->msi_session);
if ( !av->msi_session || !av->msi_session->call ) {
return ErrorNoCall;
}
av->rtp_sessions[audio_index] = rtp_init_session(
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
);
if ( !av->rtp_sessions[audio_index] ) {
fprintf(stderr, "Error while starting audio RTP session!\n");
return ErrorStartingAudioRtp;
}
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
);
if ( !av->rtp_sessions[video_index] ) {
fprintf(stderr, "Error while starting video RTP session!\n");
return ErrorStartingVideoRtp;
}
return ErrorNone;
}
int toxav_kill_transmission ( ToxAv* av )
{
/* Both sessions should be active at any time */
if ( !av->rtp_sessions[0] || !av->rtp_sessions[0] )
return ErrorNoTransmission;
if ( -1 == rtp_terminate_session(av->rtp_sessions[audio_index], av->messenger) ) {
fprintf(stderr, "Error while terminating audio RTP session!\n");
return ErrorTerminatingAudioRtp;
}
if ( -1 == rtp_terminate_session(av->rtp_sessions[video_index], av->messenger) ) {
fprintf(stderr, "Error while terminating video RTP session!\n");
return ErrorTerminatingVideoRtp;
}
return ErrorNone;
}
inline__ int toxav_send_rtp_payload ( ToxAv* av, ToxAvCallType type, const uint8_t* payload, uint16_t length )
{
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;
}
inline__ int toxav_recv_rtp_payload ( ToxAv* av, ToxAvCallType type, int ready, uint8_t* dest )
{
if ( !dest ) return ErrorInternal;
if ( !av->rtp_sessions[type - TypeAudio] ) return ErrorNoRtpSession;
RTPMessage* message;
if ( type == TypeAudio ) {
message = rtp_recv_msg(av->rtp_sessions[audio_index]);
if (message) {
/* push the packet into the queue */
queue(av->j_buf, message);
}
if (ready) {
int success = 0;
message = dequeue(av->j_buf, &success);
if ( success == 2) return ErrorAudioPacketLost;
}
else return 0;
}
else {
message = rtp_recv_msg(av->rtp_sessions[video_index]);
}
if ( message ) {
memcpy(dest, message->data, message->length);
int length = message->length;
rtp_free_msg(NULL, message);
return length;
}
return 0;
}
inline__ int toxav_decode_audio ( ToxAv* av, const uint8_t* payload, uint16_t length, int frame_size, short int* dest )
{
if ( !dest ) return ErrorInternal;
return opus_decode(av->cs->audio_decoder, payload, length, dest, frame_size, payload ? 0 : 1);
}
inline__ int toxav_encode_audio ( ToxAv* av, const short int* frame, int frame_size, uint8_t* dest )
{
if ( !dest )
return ErrorInternal;
return opus_encode(av->cs->audio_encoder, frame, frame_size, dest, RTP_PAYLOAD_SIZE);
}
int toxav_get_peer_transmission_type ( ToxAv* av, int peer )
{
assert(av->msi_session);
if ( peer < 0 || !av->msi_session->call || av->msi_session->call->peer_count <= peer )
return ErrorInternal;
return av->msi_session->call->type_peer[peer];
}
void* toxav_get_agent_handler ( ToxAv* av )
{
return av->agent_handler;
}
/* Only temporary */
void* get_cs_temp(ToxAv* av)
{
return av->cs;
}

129
toxav/toxav.h Normal file
View File

@ -0,0 +1,129 @@
/** toxav.h
*
* 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/>.
*
*
* Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or
* my email: eniz_vukovic@hotmail.com
*/
#ifndef __TOXAV
#define __TOXAV
#include <inttypes.h>
typedef void* ( *ToxAVCallback ) ( void* arg );
typedef struct _ToxAv ToxAv;
#ifndef __TOX_DEFINED__
#define __TOX_DEFINED__
typedef struct Tox Tox;
#endif
#define RTP_PAYLOAD_SIZE 10400
/**
* @brief Callbacks ids that handle the call states
*/
typedef enum {
/* Requests */
OnInvite,
OnStart,
OnCancel,
OnReject,
OnEnd,
/* Responses */
OnRinging,
OnStarting,
OnEnding,
/* Protocol */
OnError,
OnTimeout
} ToxAvCallbackID;
/**
* @brief Call type identifier.
*/
typedef enum {
TypeAudio = 70,
TypeVideo
} ToxAvCallType;
typedef enum {
ErrorNone = 0,
ErrorInternal = -1, /* Internal error */
ErrorAlreadyInCall = -2, /* Already has an active call */
ErrorNoCall = -3, /* Trying to perform call action while not in a call */
ErrorInvalidState = -4, /* Trying to perform call action while in invalid state*/
ErrorNoRtpSession = -5, /* Trying to perform rtp action on invalid session */
ErrorAudioPacketLost = -6, /* Indicating packet loss */
ErrorStartingAudioRtp = -7, /* Error in toxav_prepare_transmission() */
ErrorStartingVideoRtp = -8 , /* Error in toxav_prepare_transmission() */
ErrorNoTransmission = -9, /* Returned in toxav_kill_transmission() */
ErrorTerminatingAudioRtp = -10, /* Returned in toxav_kill_transmission() */
ErrorTerminatingVideoRtp = -11, /* Returned in toxav_kill_transmission() */
} ToxAvError;
ToxAv* toxav_new(Tox* messenger, void* useragent, const char* ua_name);
void toxav_kill(ToxAv* av);
void toxav_register_callstate_callback (ToxAVCallback callback, ToxAvCallbackID id);
int toxav_call(ToxAv* av, int user, ToxAvCallType call_type, int ringing_seconds);
int toxav_hangup(ToxAv* av);
int toxav_answer(ToxAv* av, ToxAvCallType call_type );
int toxav_reject(ToxAv* av, const char* reason);
int toxav_cancel(ToxAv* av, const char* reason);
int toxav_stop_call(ToxAv* av);
int toxav_prepare_transmission(ToxAv* av);
int toxav_kill_transmission(ToxAv* av);
int toxav_send_rtp_payload(ToxAv* av, ToxAvCallType type, const uint8_t* payload, uint16_t length);
/* Return length of received packet. Returns 0 if nothing recved. Dest has to have
* MAX_RTP_PAYLOAD_SIZE space available. Returns -1 if packet is not ready (ready < 1) for deque.
* For video packets set 'ready' at _any_ value.
*/
int toxav_recv_rtp_payload(ToxAv* av, ToxAvCallType type, int ready, uint8_t* dest);
int toxav_decode_audio( ToxAv* av, const uint8_t* payload, uint16_t length, int frame_size, short int* dest );
/* Please make sure 'dest' has enough storage for RTP_PAYLOAD_SIZE length of data */
int toxav_encode_audio( ToxAv* av, const short int* frame, int frame_size, uint8_t* dest );
int toxav_get_peer_transmission_type ( ToxAv* av, int peer );
void* toxav_get_agent_handler ( ToxAv* av );
/* Use this to get handle of CodecState from ToxAv struct */
void* get_cs_temp( ToxAv* av );
#endif /* __TOXAV */