diff --git a/toxav/toxmedia.c b/toxav/media.c similarity index 69% rename from toxav/toxmedia.c rename to toxav/media.c index a31b9ab0..e18c1803 100644 --- a/toxav/toxmedia.c +++ b/toxav/media.c @@ -37,9 +37,8 @@ #include #include -#include "toxmsi.h" -#include "toxrtp.h" -#include "toxmedia.h" +#include "rtp.h" +#include "media.h" struct jitter_buffer { RTPMessage **queue; @@ -79,8 +78,6 @@ struct jitter_buffer *create_queue(int capacity) /* 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) { - /* should be stable enough */ - /* TODO: There is already this kind of function in toxrtp.c. * Maybe merge? */ @@ -141,7 +138,6 @@ int empty_queue(struct jitter_buffer *q) { while (q->size > 0) { q->size--; - /* FIXME: */ rtp_free_msg(NULL, q->queue[q->front]); q->front++; @@ -207,64 +203,56 @@ int queue(struct jitter_buffer *q, RTPMessage *pk) return 0; } -int init_receive_audio(codec_state *cs) -{ - int rc; - cs->audio_decoder = opus_decoder_create(48000, 1, &rc ); - - if ( rc != OPUS_OK ){ - 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) +int init_video_decoder(CodecState *cs) { cs->video_decoder = avcodec_find_decoder(VIDEO_CODEC); - + if (!cs->video_decoder) { - printf("Init video_decoder failed\n"); - return 0; + fprintf(stderr, "Init video_decoder failed!\n"); + return -1; } - + cs->video_decoder_ctx = avcodec_alloc_context3(cs->video_decoder); - + if (!cs->video_decoder_ctx) { - printf("Init video_decoder_ctx failed\n"); - return 0; + fprintf(stderr, "Init video_decoder_ctx failed!\n"); + return -1; } - + if (avcodec_open2(cs->video_decoder_ctx, cs->video_decoder, NULL) < 0) { - printf("Opening video decoder failed\n"); - return 0; + fprintf(stderr, "Opening video decoder failed!\n"); + return -1; } - - printf("Init video decoder successful\n"); - return 1; + + return 0; } -int init_send_video(codec_state *cs) +int init_audio_decoder(CodecState *cs, uint32_t audio_channels) { - 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; + } + + return 0; +} - 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; + +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); - av_dump_format(cs->video_format_ctx, 0, DEFAULT_WEBCAM, 0); + av_dump_format(cs->video_format_ctx, 0, webcam, 0); 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); if (cs->webcam_decoder == NULL) { - printf("Unsupported codec\n"); - return 0; + fprintf(stderr, "Unsupported codec!\n"); + return -1; } if (cs->webcam_decoder_ctx == NULL) { - printf("init webcam_decoder_ctx failed\n"); - return 0; + fprintf(stderr, "Init webcam_decoder_ctx failed!\n"); + return -1; } if (avcodec_open2(cs->webcam_decoder_ctx, cs->webcam_decoder, NULL) < 0) { - printf("opening webcam decoder failed\n"); - return 0; + fprintf(stderr, "Opening webcam decoder failed!\n"); + return -1; } cs->video_encoder = avcodec_find_encoder(VIDEO_CODEC); if (!cs->video_encoder) { - printf("init video_encoder failed\n"); - return 0; + fprintf(stderr, "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 0; + fprintf(stderr, "Init video_encoder_ctx failed!\n"); + 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; 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 = VIDEO_BITRATE * 6; + cs->video_encoder_ctx->rc_buffer_size = video_bitrate * 6; cs->video_encoder_ctx->profile = 3; cs->video_encoder_ctx->qmax = 54; 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; if (avcodec_open2(cs->video_encoder_ctx, cs->video_encoder, NULL) < 0) { - printf("opening video encoder failed\n"); - return 0; + fprintf(stderr, "Opening video encoder failed!\n"); + return -1; } - printf("init video encoder successful\n"); - return 1; + return 0; } -int init_send_audio(codec_state *cs) +int init_audio_encoder(CodecState *cs) { - cs->support_send_audio = 0; - int err = OPUS_OK; - cs->audio_bitrate = AUDIO_BITRATE; - cs->audio_encoder = opus_encoder_create(AUDIO_SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP, &err); + cs->audio_encoder = opus_encoder_create(cs->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_COMPLEXITY(10)); err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE)); + /* NOTE: What do we do with this? */ int 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(); avcodec_register_all(); - avdevice_register_all(); av_register_all(); - - pthread_mutex_init(&cs->ctrl_mutex, NULL); - - cs->support_send_video = init_send_video(cs); - cs->support_send_audio = init_send_audio(cs); - - cs->send_audio = 1; - cs->send_video = 1; - - return 1; + + + _retu->audio_bitrate = audio_bitrate; + _retu->audio_sample_rate = audio_sample_rate; + + pthread_mutex_init(&_retu->ctrl_mutex, NULL); + + + /* 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(); - avcodec_register_all(); - avdevice_register_all(); - av_register_all(); - - cs->receive_video = 0; - cs->receive_audio = 0; - - cs->support_receive_video = init_receive_video(cs); - cs->support_receive_audio = init_receive_audio(cs); - - cs->receive_audio = 1; - cs->receive_video = 1; - - return 1; -} \ No newline at end of file + if ( cs->audio_encoder ) { + opus_encoder_destroy(cs->audio_encoder); + printf("Terminated encoder!\n"); + } + + if ( cs->audio_decoder ) { + opus_decoder_destroy(cs->audio_decoder); + printf("Terminated decoder!\n"); + } + + /* TODO: Terminate video */ + +} diff --git a/toxav/toxmedia.h b/toxav/media.h similarity index 83% rename from toxav/toxmedia.h rename to toxav/media.h index 65d1e320..ef2de27c 100644 --- a/toxav/toxmedia.h +++ b/toxav/media.h @@ -27,8 +27,6 @@ #include #include -#include "toxrtp.h" -#include "toxmsi.h" #include "../toxcore/tox.h" /* Video encoding/decoding */ @@ -75,16 +73,7 @@ #define DEFAULT_WEBCAM "0" #endif -typedef struct { - 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; +typedef struct _CodecState{ /* video encoding */ AVInputFormat *video_input_format; @@ -102,19 +91,19 @@ typedef struct { /* audio encoding */ OpusEncoder *audio_encoder; int audio_bitrate; + int audio_sample_rate; /* audio decoding */ OpusDecoder *audio_decoder; - - uint8_t req_video_refresh; pthread_mutex_t ctrl_mutex; uint32_t frame_rate; -} codec_state; +} CodecState; +typedef struct _RTPMessage RTPMessage; struct jitter_buffer *create_queue(int capacity); 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); -int init_encoder(codec_state *cs); -int init_decoder(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 ); +void codec_terminate_session(CodecState* cs); #endif diff --git a/toxav/toxmsi.c b/toxav/msi.c old mode 100755 new mode 100644 similarity index 96% rename from toxav/toxmsi.c rename to toxav/msi.c index d5c35730..014a904f --- a/toxav/toxmsi.c +++ b/toxav/msi.c @@ -29,7 +29,7 @@ #define _BSD_SOURCE -#include "toxmsi.h" +#include "msi.h" #include "../toxcore/util.h" #include "../toxcore/network.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 ); assert ( _retu ); - memset ( _retu, 0, sizeof ( MSIMessage ) ); - if ( type == TYPE_REQUEST ) { 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_str = stringify_error ( errid ); - event.rise ( callbacks[MSI_OnError], session ); + event.rise ( callbacks[MSI_OnError], session->agent_handler ); return 0; } @@ -692,12 +690,12 @@ void* handle_timeout ( void* arg ) /* Cancel all? */ uint16_t _it = 0; 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_OnEnding ] ) ( arg ); + ( *callbacks[MSI_OnTimeout] ) ( _session->agent_handler ); + ( *callbacks[MSI_OnEnding ] ) ( _session->agent_handler ); return NULL; } @@ -829,7 +827,7 @@ int handle_recv_invite ( MSISession* session, MSIMessage* msg ) { send_message ( session, _msg_ringing, msg->friend_id ); free_message ( _msg_ringing ); - event.rise ( callbacks[MSI_OnInvite], session ); + event.rise ( callbacks[MSI_OnInvite], session->agent_handler ); return 1; } @@ -852,7 +850,7 @@ int handle_recv_start ( MSISession* session, MSIMessage* msg ) { flush_peer_type ( session, msg, 0 ); - event.rise ( callbacks[MSI_OnStart], session ); + event.rise ( callbacks[MSI_OnStart], session->agent_handler ); return 1; } @@ -868,7 +866,7 @@ int handle_recv_reject ( MSISession* session, MSIMessage* msg ) { free_message ( _msg_end ); 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 ); return 1; @@ -882,7 +880,7 @@ int handle_recv_cancel ( MSISession* session, MSIMessage* msg ) { terminate_call ( session ); - event.rise ( callbacks[MSI_OnCancel], session ); + event.rise ( callbacks[MSI_OnCancel], session->agent_handler ); return 1; } @@ -899,7 +897,7 @@ int handle_recv_end ( MSISession* session, MSIMessage* msg ) { terminate_call ( session ); - event.rise ( callbacks[MSI_OnEnd], session ); + event.rise ( callbacks[MSI_OnEnd], session->agent_handler ); return 1; } @@ -912,7 +910,7 @@ int handle_recv_ringing ( MSISession* session, MSIMessage* msg ) { return 0; 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; } @@ -950,7 +948,7 @@ int handle_recv_starting ( MSISession* session, MSIMessage* msg ) { 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 ); return 1; @@ -964,7 +962,7 @@ int handle_recv_ending ( MSISession* session, MSIMessage* msg ) { terminate_call ( session ); - event.rise ( callbacks[MSI_OnEnding], session ); + event.rise ( callbacks[MSI_OnEnding], session->agent_handler ); return 1; } @@ -980,7 +978,7 @@ int handle_recv_error ( MSISession* session, MSIMessage* msg ) { terminate_call ( session ); - event.rise ( callbacks[MSI_OnEnding], session ); + event.rise ( callbacks[MSI_OnEnding], session->agent_handler ); return 1; } @@ -1138,14 +1136,13 @@ void msi_register_callback ( MSICallback callback, MSICallbackID id ) * @return MSISession* The created session. * @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 ( user_agent ); MSISession* _retu = calloc ( sizeof ( MSISession ), 1 ); assert ( _retu ); - _retu->user_agent = user_agent; + _retu->ua_name = ua_name; _retu->messenger_handle = messenger; _retu->agent_handler = NULL; @@ -1300,14 +1297,17 @@ int msi_answer ( MSISession* session, MSICallType call_type ) { * @brief Cancel request. * * @param session Control session. - * @param friend_id The friend. + * @param reason Set optional reason header. Pass NULL if none. * @return int */ -int msi_cancel ( MSISession* session, int friend_id ) { +int msi_cancel ( MSISession* session, uint32_t peer, const uint8_t* reason ) { assert ( session ); 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 ); terminate_call ( session ); diff --git a/toxav/toxmsi.h b/toxav/msi.h old mode 100755 new mode 100644 similarity index 94% rename from toxav/toxmsi.h rename to toxav/msi.h index 04987fee..20d6671d --- a/toxav/toxmsi.h +++ b/toxav/msi.h @@ -103,7 +103,7 @@ typedef struct _MSISession { int last_error_id; /* Determine the last error */ 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 */ Tox* messenger_handle; @@ -156,7 +156,7 @@ void msi_register_callback(MSICallback callback, MSICallbackID id); * @return MSISession* The created session. * @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. * * @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 */ -int msi_cancel ( MSISession* session, int friend_id ); +int msi_cancel ( MSISession* session, uint32_t peer, const uint8_t* reason ); /** diff --git a/toxav/phone.c b/toxav/phone.c index d8318ff9..4f078e2b 100755 --- a/toxav/phone.c +++ b/toxav/phone.c @@ -51,9 +51,8 @@ #include #include -#include "toxmsi.h" -#include "toxrtp.h" -#include "toxmedia.h" +#include "media.h" +#include "toxav.h" #include "../toxcore/event.h" #include "../toxcore/tox.h" @@ -75,13 +74,9 @@ typedef struct av_friend_s { } av_friend_t; typedef struct av_session_s { - MSISession* _msi; - RTPSession* _rtp_audio; - RTPSession* _rtp_video; - /* Encoding/decoding/capturing/playing */ + ToxAv* av; - codec_state* cs; VideoPicture video_picture; 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 */ struct SwsContext *sws_ctx; - /**/ - + /* Thread running control */ + int running_decaud, running_encaud, + running_decvid, running_encvid; 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) { - codec_state* cs = _phone->cs; + CodecState* cs = get_cs_temp(_phone->av); AVPicture pict; SDL_LockYUVOverlay(_phone->video_picture.bmp); @@ -268,71 +264,15 @@ int display_received_frame(av_session_t* _phone, AVFrame *r_video_frame) 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) { INFO("Started encode video thread!"); 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; int p = 0; 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, SWS_BILINEAR, NULL, NULL, NULL); - while (cs->send_video) { + while (_phone->running_encvid) { if (av_read_frame(cs->video_format_ctx, packet) < 0) { 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 ( 0 > rtp_send_msg ( _phone->_rtp_video, _phone->_messenger, enc_video_packet.data, enc_video_packet.size) ) { - printf("Failed sending message\n"); - } + toxav_send_rtp_payload(_phone->av, TypeVideo, enc_video_packet.data, enc_video_packet.size); 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->video_encoder_ctx); pthread_mutex_unlock(&cs->ctrl_mutex); + + _phone->running_encvid = -1; + pthread_exit ( NULL ); } @@ -427,8 +368,8 @@ void *encode_audio_thread(void *arg) { INFO("Started encode audio thread!"); av_session_t* _phone = arg; + _phone->running_encaud = 1; - codec_state *cs = _phone->cs; unsigned char encoded_data[4096]; int encoded_size = 0; int16_t frame[4096]; @@ -436,39 +377,46 @@ void *encode_audio_thread(void *arg) ALint sample = 0; 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); if (sample >= 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) { printf("Could not encode audio packet\n"); } 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 { usleep(1000); } } - /* clean up codecs */ - pthread_mutex_lock(&cs->ctrl_mutex); + /* clean up codecs * + pthread_mutex_lock(&cs->ctrl_mutex);*/ alcCaptureStop((ALCdevice*)_phone->audio_capture_device); alcCaptureCloseDevice((ALCdevice*)_phone->audio_capture_device); - pthread_mutex_unlock(&cs->ctrl_mutex); - pthread_exit ( NULL ); + /*pthread_mutex_unlock(&cs->ctrl_mutex);*/ + _phone->running_encaud = -1; + pthread_exit ( NULL ); } void *decode_video_thread(void *arg) { INFO("Started decode video thread!"); 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; - RTPMessage *r_msg; + + int recved_size; + uint8_t dest[RTP_PAYLOAD_SIZE]; + int dec_frame_finished; AVFrame *r_video_frame; r_video_frame = avcodec_alloc_frame(); @@ -477,20 +425,34 @@ void *decode_video_thread(void *arg) int width = 0; int height = 0; - while (cs->receive_video) { - r_msg = rtp_recv_msg ( _phone->_rtp_video ); + while (_phone->running_decvid) { - if (r_msg) { - memcpy(dec_video_packet.data, r_msg->data, r_msg->length); - dec_video_packet.size = r_msg->length; + 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; + avcodec_decode_video2(cs->video_decoder_ctx, r_video_frame, &dec_frame_finished, &dec_video_packet); if (dec_frame_finished) { + + /* Check if size has changed */ if (cs->video_decoder_ctx->width != width || cs->video_decoder_ctx->height != height) { + width = cs->video_decoder_ctx->width; height = cs->video_decoder_ctx->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); @@ -498,18 +460,20 @@ void *decode_video_thread(void *arg) /* TODO: request the sender to create a new i-frame immediatly */ printf("Bad video packet\n"); } - - rtp_free_msg(NULL, r_msg); } usleep(1000); } /* clean up codecs */ - pthread_mutex_lock(&cs->ctrl_mutex); av_free(r_video_frame); + + pthread_mutex_lock(&cs->ctrl_mutex); avcodec_close(cs->video_decoder_ctx); pthread_mutex_unlock(&cs->ctrl_mutex); + + _phone->running_decvid = -1; + pthread_exit ( NULL ); } @@ -517,9 +481,10 @@ void *decode_audio_thread(void *arg) { INFO("Started decode audio thread!"); av_session_t* _phone = arg; - - codec_state *cs = _phone->cs; - RTPMessage *r_msg; + _phone->running_decaud = 1; + + int recved_size; + uint8_t dest [RTP_PAYLOAD_SIZE]; int frame_size = AUDIO_FRAME_SIZE; int data_size; @@ -538,7 +503,7 @@ void *decode_audio_thread(void *arg) alSourcei(source, AL_LOOPING, AL_FALSE); ALuint buffer; - ALint val; + ALint ready; uint16_t zeros[frame_size]; memset(zeros, 0, frame_size); @@ -557,83 +522,58 @@ void *decode_audio_thread(void *arg) goto ending; } - struct jitter_buffer *j_buf = NULL; + int dec_frame_len = 0; - j_buf = create_queue(20); - - int success = 0; - - int dec_frame_len = 0; - - - while (cs->receive_audio) { + while (_phone->running_decaud) { - r_msg = rtp_recv_msg ( _phone->_rtp_audio ); + alGetSourcei(source, AL_BUFFERS_PROCESSED, &ready); - if (r_msg) { - /* push the packet into the queue */ - queue(j_buf, r_msg); + recved_size = toxav_recv_rtp_payload(_phone->av, TypeAudio, ready, dest); + + if ( recved_size == ErrorAudioPacketLost ) { + printf("Lost packet\n"); + 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); } - 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); + /* Play the packet */ + if (dec_frame_len) { + alGetSourcei(source, AL_BUFFERS_PROCESSED, &ready); + + if (ready <= 0) + continue; + + alSourceUnqueueBuffers(source, 1, &buffer); + data_size = av_samples_get_buffer_size(NULL, 1, dec_frame_len, AV_SAMPLE_FMT_S16, 1); + alBufferData(buffer, AL_FORMAT_MONO16, PCM, data_size, 48000); + int error = alGetError(); + + if (error != AL_NO_ERROR) { + fprintf(stderr, "Error setting buffer %d\n", error); + break; } - /* lost packet */ - else if (success == 2) { - printf("Lost packet\n"); - dec_frame_len = opus_decode(cs->audio_decoder, NULL, 0, PCM, frame_size, 1); + alSourceQueueBuffers(source, 1, &buffer); + + if (alGetError() != AL_NO_ERROR) { + fprintf(stderr, "Error: could not buffer audio\n"); + break; } - if (dec_frame_len) { - alGetSourcei(source, AL_BUFFERS_PROCESSED, &val); - - if (val <= 0) - continue; - - alSourceUnqueueBuffers(source, 1, &buffer); - data_size = av_samples_get_buffer_size(NULL, 1, dec_frame_len, AV_SAMPLE_FMT_S16, 1); - alBufferData(buffer, AL_FORMAT_MONO16, PCM, data_size, 48000); - int error = alGetError(); - - if (error != AL_NO_ERROR) { - fprintf(stderr, "Error setting buffer %d\n", error); - break; - } - - alSourceQueueBuffers(source, 1, &buffer); - - if (alGetError() != AL_NO_ERROR) { - fprintf(stderr, "Error: could not buffer audio\n"); - break; - } - - alGetSourcei(source, AL_SOURCE_STATE, &val); - - if (val != AL_PLAYING) - alSourcePlay(source); - - - } - } + alGetSourcei(source, AL_SOURCE_STATE, &ready); + + if (ready != AL_PLAYING) alSourcePlay(source); + } usleep(1000); } ending: - /* clean up codecs */ + /* clean up codecs * / pthread_mutex_lock(&cs->ctrl_mutex); alDeleteSources(1, &source); @@ -642,7 +582,10 @@ ending: alcDestroyContext(ctx); alcCloseDevice(dev); - pthread_mutex_unlock(&cs->ctrl_mutex); + pthread_mutex_unlock(&cs->ctrl_mutex); */ + + _phone->running_decaud = -1; + 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; } - - _phone->_rtp_audio = rtp_init_session ( - 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); - + + toxav_prepare_transmission(arg); + /* * Rise all threads */ /* 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()"); return -1; } /* 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()"); return -1; } /* 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()"); 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()"); return -1; @@ -734,13 +658,13 @@ int phone_startmedia_loop ( av_session_t* _phone ) void* callback_recv_invite ( void* _arg ) { - MSISession* _msi = _arg; - - switch ( _msi->call->type_peer[_msi->call->peer_count - 1] ){ - case type_audio: + assert(_arg); + + switch ( toxav_get_peer_transmission_type(_arg, 0) ){ + case TypeAudio: INFO( "Incoming audio call!"); break; - case type_video: + case TypeVideo: INFO( "Incoming video call!"); break; } @@ -754,8 +678,7 @@ void* callback_recv_ringing ( void* _arg ) } void* callback_recv_starting ( void* _arg ) { - MSISession* _session = _arg; - if ( 0 != phone_startmedia_loop(_session->agent_handler) ){ + if ( 0 != phone_startmedia_loop(_arg) ){ INFO("Starting call failed!"); } else { INFO ("Call started! ( press h to hangup )"); @@ -764,15 +687,21 @@ void* callback_recv_starting ( 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->cs->send_video = 0; - _phone->cs->receive_audio = 0; - _phone->cs->receive_video = 0; + _phone->running_encaud = 0; + _phone->running_decaud = 0; + _phone->running_encvid = 0; + _phone->running_decvid = 0; /* Wait until all threads are done */ - usleep(10000000); + + while ( _phone->running_encaud != -1 || + _phone->running_decaud != -1 || + _phone->running_encvid != -1 || + _phone->running_decvid != -1 ) + + usleep(10000000); INFO ( "Call ended!" ); pthread_exit(NULL); @@ -780,16 +709,15 @@ void* callback_recv_ending ( 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); } void* callback_call_started ( void* _arg ) { - MSISession* _session = _arg; - if ( 0 != phone_startmedia_loop(_session->agent_handler) ){ + if ( 0 != phone_startmedia_loop(_arg) ){ INFO("Starting call failed!"); } else { INFO ("Call started! ( press h to hangup )"); @@ -809,16 +737,23 @@ void* callback_call_rejected ( 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->cs->send_video = 0; - _phone->cs->receive_audio = 0; - _phone->cs->receive_video = 0; + _phone->running_encaud = 0; + _phone->running_decaud = 0; + _phone->running_encvid = 0; + _phone->running_decvid = 0; /* Wait until all threads are done */ - usleep(10000000); + + while ( _phone->running_encaud != -1 || + _phone->running_decaud != -1 || + _phone->running_encvid != -1 || + _phone->running_decvid != -1 ) + + usleep(10000000); + toxav_kill_transmission(_phone->av); INFO ( "Call ended!" ); pthread_exit(NULL); } @@ -844,9 +779,7 @@ av_session_t* av_init_session() } _retu->_friends = NULL; - - _retu->_rtp_audio = NULL; - _retu->_rtp_video = NULL; + _retu->av = toxav_new(_retu->_messenger, _retu, _USERAGENT); const ALchar *_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER); @@ -878,9 +811,7 @@ av_session_t* av_init_session() else { INFO("Selected: %d ( %s )", selection, device_names[selection]); } - - _retu->cs = av_calloc(sizeof(codec_state), 1); - + _retu->audio_capture_device = (struct ALCdevice*)alcCaptureOpenDevice( device_names[selection], AUDIO_SAMPLE_RATE, AL_FORMAT_MONO16, AUDIO_FRAME_SIZE * 4); @@ -890,36 +821,29 @@ av_session_t* av_init_session() printf("Could not start capture device! %d\n", alcGetError((ALCdevice*)_retu->audio_capture_device)); return 0; } - + uint8_t _byte_address[TOX_FRIEND_ADDRESS_SIZE]; tox_get_address(_retu->_messenger, _byte_address ); 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); - msi_register_callback(callback_recv_starting, MSI_OnStarting); - msi_register_callback(callback_recv_ending, MSI_OnEnding); - - msi_register_callback(callback_recv_error, MSI_OnError); - msi_register_callback(callback_requ_timeout, MSI_OnTimeout); + + toxav_register_callstate_callback(callback_call_started, OnStart); + toxav_register_callstate_callback(callback_call_canceled, OnCancel); + 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); + /* ------------------ */ return _retu; @@ -927,17 +851,18 @@ av_session_t* av_init_session() int av_terminate_session(av_session_t* _phone) { - if ( _phone->_msi->call ){ - msi_hangup(_phone->_msi); /* Hangup the phone first */ - } + toxav_hangup(_phone->av); free(_phone->_friends); - msi_terminate_session(_phone->_msi); pthread_mutex_destroy ( &_phone->_mutex ); 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); + toxav_kill(_phone->av); + + free(_phone); printf("\r[i] Quit!\n"); return 0; @@ -1047,22 +972,17 @@ void do_phone ( av_session_t* _phone ) } break; case 'c': { - if ( _phone->_msi->call ){ - INFO("Already in a call"); - break; - } - - MSICallType _ctype; + ToxAvCallType _ctype; if ( _len < 5 ){ INFO("Invalid input; usage: c a/v [friend]"); break; } else if ( _line[2] == 'a' || _line[2] != 'v' ){ /* default and audio */ - _ctype = type_audio; + _ctype = TypeAudio; } else { /* video */ - _ctype = type_video; + _ctype = TypeVideo; } char* _end; @@ -1073,45 +993,41 @@ void do_phone ( av_session_t* _phone ) break; } - /* Set timeout */ - msi_invite ( _phone->_msi, _ctype, 10 * 1000, _friend ); - INFO("Calling friend: %d!", _friend); + if ( toxav_call(_phone->av, _friend, _ctype, 30) == ErrorAlreadyInCall ){ + INFO("Already in a call"); + break; + } + else INFO("Calling friend: %d!", _friend); } break; case 'h': { - if ( !_phone->_msi->call ){ + if ( toxav_hangup(_phone->av) == ErrorNoCall ) { INFO("No call!"); break; } - - msi_hangup(_phone->_msi); - - INFO("Hung up..."); + else INFO("Hung up..."); } break; case 'a': { - - if ( _phone->_msi->call && _phone->_msi->call->state != call_starting ) { - break; - } - + ToxAvError rc; + if ( _len > 1 && _line[2] == 'v' ) - msi_answer(_phone->_msi, type_video); + rc = toxav_answer(_phone->av, TypeVideo); else - msi_answer(_phone->_msi, type_audio); + rc = toxav_answer(_phone->av, TypeAudio); + + if ( rc == ErrorInvalidState ) { + INFO("No call to answer!"); + } } break; case 'r': { - if ( _phone->_msi->call && _phone->_msi->call->state != call_starting ){ - break; - } - - msi_reject(_phone->_msi, NULL); - - INFO("Call Rejected..."); + if ( toxav_reject(_phone->av, "User action") == ErrorInvalidState ) + INFO("No state to cancel!"); + else INFO("Call Rejected..."); } break; case 'q': @@ -1124,7 +1040,6 @@ void do_phone ( av_session_t* _phone ) } default: { - INFO("Invalid command!"); } break; } diff --git a/toxav/toxrtp.c b/toxav/rtp.c old mode 100755 new mode 100644 similarity index 99% rename from toxav/toxrtp.c rename to toxav/rtp.c index d573d403..e23fa132 --- a/toxav/toxrtp.c +++ b/toxav/rtp.c @@ -26,7 +26,7 @@ #include "config.h" #endif /* HAVE_CONFIG_H */ -#include "toxrtp.h" +#include "rtp.h" #include #include @@ -245,6 +245,7 @@ RTPHeader* extract_header ( const uint8_t* payload, int length ) * Now it my happen that this is out of order but * it cuts down chances of parsing some invalid value */ + if ( GET_FLAG_VERSION(_retu) != RTP_VERSION ){ /* Deallocate */ free(_retu); diff --git a/toxav/toxrtp.h b/toxav/rtp.h old mode 100755 new mode 100644 similarity index 100% rename from toxav/toxrtp.h rename to toxav/rtp.h diff --git a/toxav/toxav.c b/toxav/toxav.c new file mode 100644 index 00000000..8757d7fd --- /dev/null +++ b/toxav/toxav.c @@ -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 . + * + * + * 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 +#include +#include + +#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; +} diff --git a/toxav/toxav.h b/toxav/toxav.h new file mode 100644 index 00000000..96a666a2 --- /dev/null +++ b/toxav/toxav.h @@ -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 . + * + * + * 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 + +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 */ \ No newline at end of file