Public header ready to go

This commit is contained in:
mannol 2014-02-15 20:44:33 +01:00
parent 292708c336
commit 393433ce99
26 changed files with 916 additions and 563 deletions

View File

@ -13,8 +13,7 @@ AC_CONFIG_MACRO_DIR([m4])
EXTRA_LT_LDFLAGS=
LIBTOXCORE_LT_VERSION=0:0:0
LIBTOXMSI_LT_VERSION=0:0:0
LIBTOXRTP_LT_VERSION=0:0:0
LIBTOXAV_LT_VERSION=0:0:0
dnl
dnl current:revision:age
dnl
@ -24,12 +23,10 @@ dnl incremented
dnl age: increment if interfaces have been added, set to zero if
dnl interfaces have been removed or changed
TOXCORE_LT_LDFLAGS="-version-info $LIBTOXCORE_LT_VERSION"
TOXMSI_LT_LDFLAGS="-version-info $LIBTOXMSI_LT_VERSION"
TOXRTP_LT_LDFLAGS="-version-info $LIBTOXMSI_LT_VERSION"
TOXAV_LT_LDFLAGS="-version-info $LIBTOXAV_LT_VERSION"
AC_SUBST(TOXCORE_LT_LDFLAGS)
AC_SUBST(TOXMSI_LT_LDFLAGS)
AC_SUBST(TOXRTP_LT_LDFLAGS)
AC_SUBST(TOXAV_LT_LDFLAGS)
if test "x${prefix}" = "xNONE"; then
prefix="${ac_default_prefix}"
@ -39,6 +36,7 @@ BUILD_DHT_BOOTSTRAP_DAEMON="yes"
BUILD_NTOX="yes"
BUILD_TESTS="yes"
BUILD_AV="yes"
BUILD_PHONE="yes"
BUILD_TESTING="yes"
NCURSES_FOUND="no"
@ -64,12 +62,24 @@ AC_ARG_ENABLE([av],
[
if test "x$enableval" = "xno"; then
BUILD_AV="no"
BUILD_PHONE="no"
elif test "x$enableval" = "xyes"; then
BUILD_AV="yes"
fi
]
)
AC_ARG_ENABLE([phone],
[AC_HELP_STRING([--disable-phone], [build test phone (default: auto)]) ],
[
if test "x$enableval" = "xno"; then
BUILD_PHONE="no"
elif test "x$enableval" = "xyes"; then
BUILD_PHONE="yes"
fi
]
)
AC_ARG_ENABLE([tests],
[AC_HELP_STRING([--disable-tests], [build unit tests (default: auto)]) ],
[
@ -339,82 +349,79 @@ AC_C_BIGENDIAN
AC_FUNC_FORK
AC_CHECK_FUNCS([gettimeofday memset socket strchr malloc])
if test "x$BUILD_AV" = "xyes"; then
AX_PTHREAD(
[],
[
AC_MSG_WARN([disabling AV support: required pthread library not found])
BUILD_AV="no"
]
)
fi
AX_PTHREAD(
[],
[
AC_MSG_ERROR([Error: required pthread library not found])
]
)
if test "x$BUILD_AV" = "xyes"; then
if test "x$BUILD_PHONE" = "xyes"; then
PKG_CHECK_MODULES([AVFORMAT], [libavformat],
[],
[
AC_MSG_WARN([disabling AV support $AVFORMAT_PKG_ERRORS])
BUILD_AV="no"
AC_MSG_WARN([disabling phone $AVFORMAT_PKG_ERRORS])
BUILD_PHONE="no"
]
)
fi
if test "x$BUILD_AV" = "xyes"; then
if test "x$BUILD_PHONE" = "xyes"; then
PKG_CHECK_MODULES([AVCODEC], [libavcodec],
[],
[
AC_MSG_WARN([disabling AV support $AVCODEC_PKG_ERRORS])
BUILD_AV="no"
AC_MSG_WARN([disabling phone $AVCODEC_PKG_ERRORS])
BUILD_PHONE="no"
]
)
fi
if test "x$BUILD_AV" = "xyes"; then
if test "x$BUILD_PHONE" = "xyes"; then
PKG_CHECK_MODULES([AVUTIL], [libavutil],
[],
[
AC_MSG_WARN([disabling AV support $AVUTIL_PKG_ERRORS])
BUILD_AV="no"
AC_MSG_WARN([disabling phone $AVUTIL_PKG_ERRORS])
BUILD_PHONE="no"
]
)
fi
if test "x$BUILD_AV" = "xyes"; then
if test "x$BUILD_PHONE" = "xyes"; then
PKG_CHECK_MODULES([AVDEVICE], [libavdevice],
[],
[
AC_MSG_WARN([disabling AV support $AVDEVICE_PKG_ERRORS])
BUILD_AV="no"
AC_MSG_WARN([disabling phone $AVDEVICE_PKG_ERRORS])
BUILD_PHONE="no"
]
)
fi
if test "x$BUILD_AV" = "xyes"; then
if test "x$BUILD_PHONE" = "xyes"; then
PKG_CHECK_MODULES([SWSCALE], [libswscale],
[],
[
AC_MSG_WARN([disabling AV support $SWSCALE_PKG_ERRORS])
BUILD_AV="no"
AC_MSG_WARN([disabling phone $SWSCALE_PKG_ERRORS])
BUILD_PHONE="no"
]
)
fi
if test "x$BUILD_AV" = "xyes"; then
if test "x$BUILD_PHONE" = "xyes"; then
PKG_CHECK_MODULES([SDL], [sdl],
[],
[
AC_MSG_WARN([disabling AV support $SDL_PKG_ERRORS])
BUILD_AV="no"
AC_MSG_WARN([disabling phone $SDL_PKG_ERRORS])
BUILD_PHONE="no"
]
)
fi
if test "x$BUILD_AV" = "xyes"; then
if test "x$BUILD_PHONE" = "xyes"; then
PKG_CHECK_MODULES([OPENAL], [openal],
[],
[
AC_MSG_WARN([disabling AV support $OPENAL_PKG_ERRORS])
BUILD_AV="no"
AC_MSG_WARN([disabling phone $OPENAL_PKG_ERRORS])
BUILD_PHONE="no"
]
)
fi
@ -425,6 +432,18 @@ if test "x$BUILD_AV" = "xyes"; then
[
AC_MSG_WARN([disabling AV support $OPUS_PKG_ERRORS])
BUILD_AV="no"
BUILD_PHONE="no"
]
)
fi
if test "x$BUILD_AV" = "xyes"; then
PKG_CHECK_MODULES([VPX], [vpx],
[],
[
AC_MSG_WARN([disabling AV support $VPX_PKG_ERRORS])
BUILD_AV="no"
BUILD_PHONE="no"
]
)
fi
@ -587,6 +606,7 @@ AM_CONDITIONAL(BUILD_DHT_BOOTSTRAP_DAEMON, test "x$BUILD_DHT_BOOTSTRAP_DAEMON" =
AM_CONDITIONAL(BUILD_TESTS, test "x$BUILD_TESTS" = "xyes")
AM_CONDITIONAL(BUILD_NTOX, test "x$BUILD_NTOX" = "xyes")
AM_CONDITIONAL(BUILD_AV, test "x$BUILD_AV" = "xyes")
AM_CONDITIONAL(BUILD_PHONE, test "x$BUILD_PHONE" = "xyes")
AM_CONDITIONAL(BUILD_TESTING, test "x$BUILD_TESTING" = "xyes")
AM_CONDITIONAL(WIN32, test "x$WIN32" = "xyes")

View File

@ -1,105 +1,43 @@
if BUILD_AV
lib_LTLIBRARIES += libtoxrtp.la \
libtoxmsi.la \
libtoxmedia.la
lib_LTLIBRARIES += libtoxav.la
libtoxav_la_include_HEADERS = ../toxav/toxav.h
libtoxav_la_includedir = $(includedir)/tox
# ****** RTP ****** #
libtoxrtp_la_include_HEADERS = \
../toxav/toxrtp.h
libtoxav_la_SOURCES = ../toxav/rtp.h \
../toxav/rtp.c \
../toxav/msi.h \
../toxav/msi.c \
../toxav/media.h \
../toxav/media.c \
../toxav/toxav.h \
../toxav/toxav.c
libtoxrtp_la_includedir = $(includedir)/tox
libtoxrtp_la_SOURCES = ../toxav/toxrtp.h \
../toxav/toxrtp.c
libtoxav_la_CFLAGS = -I../toxcore \
-I../toxav \
$(NACL_CFLAGS) \
$(OPUS_CFLAGS) \
$(VPX_CFLAGS)
libtoxrtp_la_CFLAGS = -I../toxcore \
-I../toxav \
$(NACL_CFLAGS)
libtoxav_la_LDFLAGS = $(TOXAV_LT_LDFLAGS) \
$(NACL_LDFLAGS) \
$(EXTRA_LT_LDFLAGS)
libtoxav_la_LIBS = $(NACL_LIBS) \
$(OPUS_LIBS) \
$(VPX_LIBS)
libtoxrtp_la_LDFLAGS = $(TOXRTP_LT_LDFLAGS) \
$(NACL_LDFLAGS) \
$(EXTRA_LT_LDFLAGS)
libtoxrtp_la_LIBS = libtoxcore.la \
$(NACL_LIBS)
endif
# ****** MSI ****** #
libtoxmsi_la_include_HEADERS = \
../toxav/toxmsi.h
libtoxmsi_la_includedir = $(includedir)/tox
libtoxmsi_la_SOURCES = ../toxav/toxmsi.h \
../toxav/toxmsi.c
libtoxmsi_la_CFLAGS = -I../toxcore \
-I../toxav \
$(NACL_CFLAGS)
libtoxmsi_la_LDFLAGS = $(TOXMSI_LT_LDFLAGS) \
$(EXTRA_LT_LDFLAGS) \
$(NACL_LDFLAGS)
libtoxmsi_la_LIBS = libtoxcore.la \
$(NACL_LIBS)
# ****** MEDIA ****** #
if BUILD_PHONE
libtoxmedia_la_include_HEADERS = \
../toxav/toxmedia.h
libtoxmedia_la_includedir = $(includedir)/tox
libtoxmedia_la_SOURCES = ../toxav/toxmedia.h \
../toxav/toxmedia.c
libtoxmedia_la_CFLAGS = -I../toxcore \
-I../toxav \
$(AVFORMAT_CFLAGS) \
$(AVCODEC_CFLAGS) \
$(AVUTIL_CFLAGS) \
$(AVDEVICE_CFLAGS) \
$(SWSCALE_CFLAGS) \
$(SDL_CFLAGS) \
$(OPENAL_CFLAGS) \
$(NACL_CFLAGS) \
$(OPUS_CFLAGS)
libtoxmedia_la_LDFLAGS = $(TOXMSI_LT_LDFLAGS) \
$(TOXRTP_LT_LDFLAGS) \
$(EXTRA_LT_LDFLAGS) \
$(NACL_LDFLAGS)
libtoxmedia_la_LIBS = libtoxcore.la \
$(NACL_LDFLAGS) \
$(AVFORMAT_LIBS) \
$(AVCODEC_LIBS) \
$(AVUTIL_LIBS) \
$(AVDEVICE_LIBS) \
$(SWSCALE_LIBS) \
$(SDL_LIBS) \
$(OPENAL_LIBS) \
$(NACL_LIBS) \
$(OPUS_LIBS)
# ***** PHONE ***** #
noinst_PROGRAMS += phone
@ -113,25 +51,17 @@ phone_CFLAGS = -I../toxcore \
$(AVDEVICE_CFLAGS) \
$(SWSCALE_CFLAGS) \
$(SDL_CFLAGS) \
$(OPENAL_CFLAGS) \
$(NACL_CFLAGS) \
$(OPUS_CFLAGS)
$(OPENAL_CFLAGS)
phone_LDADD = libtoxrtp.la \
libtoxmsi.la \
libtoxmedia.la \
phone_LDADD = libtoxav.la \
libtoxcore.la \
$(NACL_LDFLAGS) \
$(AVFORMAT_LIBS) \
$(AVCODEC_LIBS) \
$(AVUTIL_LIBS) \
$(AVDEVICE_LIBS) \
$(SWSCALE_LIBS) \
$(SDL_LIBS) \
$(OPENAL_LIBS) \
$(NACL_LIBS) \
$(OPUS_LIBS)
$(OPENAL_LIBS)
endif

View File

@ -28,13 +28,8 @@
#endif /* HAVE_CONFIG_H */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavdevice/avdevice.h>
#include <libavutil/opt.h>
#include <opus/opus.h>
#include <assert.h>
#include "rtp.h"
@ -206,25 +201,11 @@ int queue(struct jitter_buffer *q, RTPMessage *pk)
int init_video_decoder(CodecState *cs)
{
cs->video_decoder = avcodec_find_decoder(VIDEO_CODEC);
if (!cs->video_decoder) {
if (vpx_codec_dec_init_ver(&cs->v_decoder, VIDEO_CODEC_DECODER_INTERFACE, NULL, 0, VPX_DECODER_ABI_VERSION) != VPX_CODEC_OK) {
fprintf(stderr, "Init video_decoder failed!\n");
return -1;
}
cs->video_decoder_ctx = avcodec_alloc_context3(cs->video_decoder);
if (!cs->video_decoder_ctx) {
fprintf(stderr, "Init video_decoder_ctx failed!\n");
return -1;
}
if (avcodec_open2(cs->video_decoder_ctx, cs->video_decoder, NULL) < 0) {
fprintf(stderr, "Opening video decoder failed!\n");
return -1;
}
return 0;
}
@ -242,97 +223,32 @@ int init_audio_decoder(CodecState *cs, uint32_t audio_channels)
}
int init_video_encoder(CodecState *cs, const char* webcam, const char* video_driver, uint32_t video_bitrate)
int init_video_encoder(CodecState *cs, uint16_t width, uint16_t height, 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");
vpx_codec_enc_cfg_t cfg;
int res = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
if(res) {
printf("Failed to get config: %s\n", vpx_codec_err_to_string(res));
return -1;
}
avformat_find_stream_info(cs->video_format_ctx, NULL);
av_dump_format(cs->video_format_ctx, 0, webcam, 0);
int i;
for (i = 0; i < cs->video_format_ctx->nb_streams; ++i) {
if (cs->video_format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
cs->video_stream = i;
break;
}
}
cs->webcam_decoder_ctx = cs->video_format_ctx->streams[cs->video_stream]->codec;
cs->webcam_decoder = avcodec_find_decoder(cs->webcam_decoder_ctx->codec_id);
if (cs->webcam_decoder == NULL) {
fprintf(stderr, "Unsupported codec!\n");
cfg.rc_target_bitrate = video_bitrate;
cfg.g_w = width;
cfg.g_h = height;
if(vpx_codec_enc_init_ver(&cs->v_encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0, VPX_ENCODER_ABI_VERSION) != VPX_CODEC_OK) {
fprintf(stderr, "Failed to initialize encoder\n");
return -1;
}
if (cs->webcam_decoder_ctx == NULL) {
fprintf(stderr, "Init webcam_decoder_ctx failed!\n");
return -1;
}
if (avcodec_open2(cs->webcam_decoder_ctx, cs->webcam_decoder, NULL) < 0) {
fprintf(stderr, "Opening webcam decoder failed!\n");
return -1;
}
cs->video_encoder = avcodec_find_encoder(VIDEO_CODEC);
if (!cs->video_encoder) {
fprintf(stderr, "Init video_encoder failed!\n");
return -1;
}
cs->video_encoder_ctx = avcodec_alloc_context3(cs->video_encoder);
if (!cs->video_encoder_ctx) {
fprintf(stderr, "Init video_encoder_ctx failed!\n");
return -1;
}
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->profile = 3;
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) {
fprintf(stderr, "Opening video encoder failed!\n");
return -1;
}
return 0;
}
int init_audio_encoder(CodecState *cs)
int init_audio_encoder(CodecState *cs, uint32_t audio_channels)
{
int err = OPUS_OK;
cs->audio_encoder = opus_encoder_create(cs->audio_sample_rate, 1, OPUS_APPLICATION_VOIP, &err);
cs->audio_encoder = opus_encoder_create(cs->audio_sample_rate, audio_channels, OPUS_APPLICATION_AUDIO, &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));
return err == OPUS_OK ? 0 : -1;
}
@ -342,30 +258,26 @@ 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 )
uint16_t video_width,
uint16_t video_height,
uint32_t video_bitrate )
{
CodecState* _retu = av_calloc(sizeof(CodecState), 1);
CodecState* _retu = calloc(sizeof(CodecState), 1);
assert(_retu);
avdevice_register_all();
avcodec_register_all();
av_register_all();
_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) )
if (!video_width || !video_height) {
video_width = 320;
video_height = 240;
}
if ( 0 == init_video_encoder(_retu, video_width, video_height, video_bitrate) )
printf("Video encoder initialized!\n");
if ( 0 == init_audio_encoder(_retu) )
if ( 0 == init_audio_encoder(_retu, audio_channels) )
printf("Audio encoder initialized!\n");
@ -391,7 +303,8 @@ void codec_terminate_session ( CodecState* cs )
opus_decoder_destroy(cs->audio_decoder);
printf("Terminated decoder!\n");
}
/* TODO: Terminate video */
vpx_codec_destroy(&cs->v_decoder);
vpx_codec_destroy(&cs->v_encoder);
}

View File

@ -27,66 +27,27 @@
#include <stdio.h>
#include <math.h>
#include "../toxcore/tox.h"
#include <pthread.h>
/* Video encoding/decoding */
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavdevice/avdevice.h>
#include <libavutil/opt.h>
#include <vpx/vpx_decoder.h>
#include <vpx/vpx_encoder.h>
#include <vpx/vp8dx.h>
#include <vpx/vp8cx.h>
#define VIDEO_CODEC_DECODER_INTERFACE (vpx_codec_vp8_dx())
#define VIDEO_CODEC_ENCODER_INTERFACE (vpx_codec_vp8_cx())
/* Audio encoding/decoding */
#include <opus/opus.h>
/* ffmpeg VP8 codec ID */
#define VIDEO_CODEC AV_CODEC_ID_VP8
/* ffmpeg Opus codec ID */
#define AUDIO_CODEC AV_CODEC_ID_OPUS
/* default video bitrate in bytes/s */
#define VIDEO_BITRATE 10*1000
/* default audio bitrate in bytes/s */
#define AUDIO_BITRATE 64000
/* audio frame duration in miliseconds */
#define AUDIO_FRAME_DURATION 20
/* audio sample rate recommended to be 48kHz for Opus */
#define AUDIO_SAMPLE_RATE 48000
/* the amount of samples in one audio frame */
#define AUDIO_FRAME_SIZE AUDIO_SAMPLE_RATE*AUDIO_FRAME_DURATION/1000
/* the quit event for SDL */
#define FF_QUIT_EVENT (SDL_USEREVENT + 2)
#ifdef __linux__
#define VIDEO_DRIVER "video4linux2"
#define DEFAULT_WEBCAM "/dev/video0"
#endif
#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32)
#define VIDEO_DRIVER "vfwcap"
#define DEFAULT_WEBCAM "0"
#endif
typedef struct _CodecState{
/* video encoding */
AVInputFormat *video_input_format;
AVFormatContext *video_format_ctx;
uint8_t video_stream;
AVCodecContext *webcam_decoder_ctx;
AVCodec *webcam_decoder;
AVCodecContext *video_encoder_ctx;
AVCodec *video_encoder;
vpx_codec_ctx_t v_encoder;
uint32_t frame_counter;
/* video decoding */
AVCodecContext *video_decoder_ctx;
AVCodec *video_decoder;
vpx_codec_ctx_t v_decoder;
/* audio encoding */
OpusEncoder *audio_encoder;
@ -95,11 +56,6 @@ typedef struct _CodecState{
/* audio decoding */
OpusDecoder *audio_decoder;
pthread_mutex_t ctrl_mutex;
uint32_t frame_rate;
} CodecState;
@ -112,13 +68,13 @@ int queue(struct jitter_buffer *q, RTPMessage *pk);
RTPMessage *dequeue(struct jitter_buffer *q, int *success);
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* codec_init_session ( uint32_t audio_bitrate,
uint16_t audio_frame_duration,
uint32_t audio_sample_rate,
uint32_t audio_channels,
uint16_t video_width,
uint16_t video_height,
uint32_t video_bitrate );
void codec_terminate_session(CodecState* cs);

View File

@ -593,7 +593,7 @@ int send_message ( MSISession* session, MSIMessage* msg, uint32_t to )
uint8_t _msg_string_final [MSI_MAXMSG_SIZE];
uint16_t _length = message_to_string ( msg, _msg_string_final );
return m_msi_packet((struct Messenger*) session->messenger_handle, to, _msg_string_final, _length) ? 0 : -1;
return m_msi_packet(session->messenger_handle, to, _msg_string_final, _length) ? 0 : -1;
}
@ -616,7 +616,27 @@ void flush_peer_type ( MSISession* session, MSIMessage* msg, int peer_id ) {
} else {} /* Error */
}
void handle_remote_connection_change(Messenger* messenger, int friend_num, uint8_t status, void* session_p)
{
MSISession* session = session_p;
switch ( status )
{
case 0: /* Went offline */
{
if ( session->call ) {
int i = 0;
for ( ; i < session->call->peer_count; i ++ )
if ( session->call->peers[i] == friend_num ) {
msi_stopcall(session); /* Stop the call for now */
return;
}
}
} break;
default: break;
}
}
/**
* @brief Sends error response to peer.
@ -694,8 +714,8 @@ void* handle_timeout ( void* arg )
}
( *callbacks[MSI_OnTimeout] ) ( _session->agent_handler );
( *callbacks[MSI_OnEnding ] ) ( _session->agent_handler );
( *callbacks[MSI_OnRequestTimeout] ) ( _session->agent_handler );
( *callbacks[MSI_OnEnding ] ) ( _session->agent_handler );
return NULL;
}
@ -774,7 +794,7 @@ int terminate_call ( MSISession* session ) {
/* Check event loop and cancel timed events if there are any
* Notice: This has to be done before possibly
* NOTE: This has to be done before possibly
* locking the mutex the second time
*/
event.timer_release ( session->call->request_timer_id );
@ -797,7 +817,7 @@ int terminate_call ( MSISession* session ) {
pthread_mutex_destroy ( &_call->mutex );
free ( _call );
return 0;
}
@ -1136,7 +1156,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* ua_name ) {
MSISession* msi_init_session ( Messenger* messenger, const uint8_t* ua_name ) {
assert ( messenger );
MSISession* _retu = calloc ( sizeof ( MSISession ), 1 );
@ -1152,8 +1172,10 @@ MSISession* msi_init_session ( Tox* messenger, const uint8_t* ua_name ) {
_retu->call_timeout = 30000; /* default value? */
m_callback_msi_packet((struct Messenger*) messenger, msi_handle_packet, _retu );
m_callback_msi_packet(messenger, msi_handle_packet, _retu );
/* This is called when remote terminates session */
m_callback_connectionstatus_internal_av(messenger, handle_remote_connection_change, _retu);
return _retu;
}
@ -1351,7 +1373,7 @@ int msi_stopcall ( MSISession* session ) {
return -1;
/* just terminate it */
terminate_call ( session );
return 0;

View File

@ -26,9 +26,10 @@
#define __TOXMSI
#include <inttypes.h>
#include "../toxcore/tox.h"
#include <pthread.h>
#include "../toxcore/Messenger.h"
/* define size for call_id */
#define CALL_ID_LEN 12
@ -106,7 +107,7 @@ typedef struct _MSISession {
const uint8_t* ua_name;
void* agent_handler; /* Pointer to an object that is handling msi */
Tox* messenger_handle;
Messenger* messenger_handle;
uint32_t frequ;
uint32_t call_timeout; /* Time of the timeout for some action to end; 0 if infinite */
@ -133,7 +134,7 @@ typedef enum {
/* Protocol */
MSI_OnError,
MSI_OnTimeout
MSI_OnRequestTimeout
} MSICallbackID;
@ -156,7 +157,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* ua_name );
MSISession* msi_init_session ( Messenger* messenger, const uint8_t* ua_name );
/**

View File

@ -44,17 +44,40 @@
#include <unistd.h>
#include <assert.h>
#include <math.h>
#include <pthread.h>
//#include "media.h"
#include "toxav.h"
#include "../toxcore/event.h"
#include "../toxcore/tox.h"
#ifdef TOX_FFMPEG
/* Video encoding/decoding */
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavdevice/avdevice.h>
#include <libavutil/opt.h>
#endif
#include <AL/al.h>
#include <AL/alc.h>
#include <SDL/SDL.h>
#include <SDL/SDL_thread.h>
#include <pthread.h>
#include <opus/opus.h>
#include "media.h"
#include "toxav.h"
#include "../toxcore/event.h"
#include "../toxcore/tox.h"
/* the quit event for SDL */
#define FF_QUIT_EVENT (SDL_USEREVENT + 2)
#ifdef __linux__
#define VIDEO_DRIVER "video4linux2"
#define DEFAULT_WEBCAM "/dev/video0"
#endif
#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32)
#define VIDEO_DRIVER "vfwcap"
#define DEFAULT_WEBCAM "0"
#endif
/* Define client version */
#define _USERAGENT "v.0.3.0"
@ -96,6 +119,13 @@ typedef struct av_session_s {
av_friend_t* _friends;
int _friend_cout;
char _my_public_id[200];
#ifdef TOX_FFMPEG
AVInputFormat *video_input_format;
AVFormatContext *video_format_ctx;
uint8_t video_stream;
AVCodecContext *webcam_decoder_ctx;
AVCodec *webcam_decoder;
#endif
} av_session_t;
@ -236,8 +266,8 @@ static void fraddr_to_str(uint8_t *id_bin, char *id_str)
/*
* How av stuff _should_ look like
*/
int display_received_frame(av_session_t* _phone, AVFrame *r_video_frame)
/*
int display_received_frame(av_session_t* _phone, vpx_image_t *image)
{
CodecState* cs = get_cs_temp(_phone->av);
AVPicture pict;
@ -249,8 +279,8 @@ int display_received_frame(av_session_t* _phone, AVFrame *r_video_frame)
pict.linesize[0] = _phone->video_picture.bmp->pitches[0];
pict.linesize[1] = _phone->video_picture.bmp->pitches[2];
pict.linesize[2] = _phone->video_picture.bmp->pitches[1];
/* Convert the image into YUV format that SDL uses */
*/
/* Convert the image into YUV format that SDL uses *//*
sws_scale(_phone->sws_SDL_r_ctx, (uint8_t const * const *)r_video_frame->data, r_video_frame->linesize, 0,
cs->video_decoder_ctx->height, pict.data, pict.linesize );
@ -263,60 +293,65 @@ int display_received_frame(av_session_t* _phone, AVFrame *r_video_frame)
SDL_DisplayYUVOverlay(_phone->video_picture.bmp, &rect);
return 1;
}
*/
#ifdef TOX_FFMPEG
void *encode_video_thread(void *arg)
{
INFO("Started encode video thread!");
av_session_t* _phone = arg;
_phone->running_encvid = 1;
CodecState *cs = get_cs_temp(_phone->av);
//CodecState *cs = get_cs_temp(_phone->av);
AVPacket pkt1, *packet = &pkt1;
int p = 0;
int got_packet;
//int p = 0;
//int got_packet;
int video_frame_finished;
AVFrame *s_video_frame;
AVFrame *webcam_frame;
s_video_frame = avcodec_alloc_frame();
webcam_frame = avcodec_alloc_frame();
AVPacket enc_video_packet;
//AVPacket enc_video_packet;
uint8_t *buffer;
int numBytes;
/* Determine required buffer size and allocate buffer */
numBytes = avpicture_get_size(PIX_FMT_YUV420P, cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height);
numBytes = avpicture_get_size(PIX_FMT_YUV420P, _phone->webcam_decoder_ctx->width, _phone->webcam_decoder_ctx->height);
buffer = (uint8_t *)av_calloc(numBytes * sizeof(uint8_t),1);
avpicture_fill((AVPicture *)s_video_frame, buffer, PIX_FMT_YUV420P, cs->webcam_decoder_ctx->width,
cs->webcam_decoder_ctx->height);
_phone->sws_ctx = sws_getContext(cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height,
cs->webcam_decoder_ctx->pix_fmt, cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height, PIX_FMT_YUV420P,
avpicture_fill((AVPicture *)s_video_frame, buffer, PIX_FMT_YUV420P, _phone->webcam_decoder_ctx->width,
_phone->webcam_decoder_ctx->height);
_phone->sws_ctx = sws_getContext(_phone->webcam_decoder_ctx->width, _phone->webcam_decoder_ctx->height,
_phone->webcam_decoder_ctx->pix_fmt, _phone->webcam_decoder_ctx->width, _phone->webcam_decoder_ctx->height, PIX_FMT_YUV420P,
SWS_BILINEAR, NULL, NULL, NULL);
vpx_image_t *image =
vpx_img_alloc(NULL, VPX_IMG_FMT_I420, _phone->webcam_decoder_ctx->width, _phone->webcam_decoder_ctx->height, 1);
//uint32_t frame_counter = 0;
while (_phone->running_encvid) {
if (av_read_frame(cs->video_format_ctx, packet) < 0) {
if (av_read_frame(_phone->video_format_ctx, packet) < 0) {
printf("error reading frame\n");
if (cs->video_format_ctx->pb->error != 0)
if (_phone->video_format_ctx->pb->error != 0)
break;
continue;
}
if (packet->stream_index == cs->video_stream) {
if (avcodec_decode_video2(cs->webcam_decoder_ctx, webcam_frame, &video_frame_finished, packet) < 0) {
if (packet->stream_index == _phone->video_stream) {
if (avcodec_decode_video2(_phone->webcam_decoder_ctx, webcam_frame, &video_frame_finished, packet) < 0) {
printf("couldn't decode\n");
continue;
}
av_free_packet(packet);
sws_scale(_phone->sws_ctx, (uint8_t const * const *)webcam_frame->data, webcam_frame->linesize, 0,
cs->webcam_decoder_ctx->height, s_video_frame->data, s_video_frame->linesize);
_phone->webcam_decoder_ctx->height, s_video_frame->data, s_video_frame->linesize);
/* create a new I-frame every 60 frames */
++p;
//++p;
/*
if (p == 60) {
s_video_frame->pict_type = AV_PICTURE_TYPE_BI ;
@ -325,53 +360,66 @@ void *encode_video_thread(void *arg)
p = 0;
} else {
s_video_frame->pict_type = AV_PICTURE_TYPE_P ;
}
}*/
if (video_frame_finished) {
if (avcodec_encode_video2(cs->video_encoder_ctx, &enc_video_packet, s_video_frame, &got_packet) < 0) {
memcpy(image->planes[VPX_PLANE_Y], s_video_frame->data[0], s_video_frame->linesize[0] * _phone->webcam_decoder_ctx->height);
memcpy(image->planes[VPX_PLANE_U], s_video_frame->data[1], s_video_frame->linesize[1] * _phone->webcam_decoder_ctx->height / 2);
memcpy(image->planes[VPX_PLANE_V], s_video_frame->data[2], s_video_frame->linesize[2] * _phone->webcam_decoder_ctx->height / 2);
toxav_send_video (_phone->av, image);
//if (avcodec_encode_video2(cs->video_encoder_ctx, &enc_video_packet, s_video_frame, &got_packet) < 0) {
/*if (vpx_codec_encode(&cs->v_encoder, image, frame_counter, 1, 0, 0) != VPX_CODEC_OK) {
printf("could not encode video frame\n");
continue;
}
++frame_counter;
if (!got_packet) {
continue;
}
vpx_codec_iter_t iter = NULL;
vpx_codec_cx_pkt_t *pkt;
while( (pkt = vpx_codec_get_cx_data(&cs->v_encoder, &iter)) ) {
if (pkt->kind == VPX_CODEC_CX_FRAME_PKT)
toxav_send_rtp_payload(_phone->av, TypeVideo, pkt->data.frame.buf, pkt->data.frame.sz);
}*/
//if (!got_packet) {
// continue;
//}
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");
toxav_send_rtp_payload(_phone->av, TypeVideo, enc_video_packet.data, enc_video_packet.size);
//toxav_send_rtp_payload(_phone->av, TypeVideo, enc_video_packet.data, enc_video_packet.size);
av_free_packet(&enc_video_packet);
//av_free_packet(&enc_video_packet);
}
} else {
av_free_packet(packet);
}
}
vpx_img_free(image);
/* clean up codecs */
pthread_mutex_lock(&cs->ctrl_mutex);
//pthread_mutex_lock(&cs->ctrl_mutex);
av_free(buffer);
av_free(webcam_frame);
av_free(s_video_frame);
sws_freeContext(_phone->sws_ctx);
avcodec_close(cs->webcam_decoder_ctx);
avcodec_close(cs->video_encoder_ctx);
pthread_mutex_unlock(&cs->ctrl_mutex);
//avcodec_close(webcam_decoder_ctx);
//avcodec_close(cs->video_encoder_ctx);
//pthread_mutex_unlock(&cs->ctrl_mutex);
_phone->running_encvid = -1;
pthread_exit ( NULL );
}
#endif
void *encode_audio_thread(void *arg)
{
INFO("Started encode audio thread!");
av_session_t* _phone = arg;
_phone->running_encaud = 1;
unsigned char encoded_data[4096];
int encoded_size = 0;
int ret = 0;
int16_t frame[4096];
int frame_size = AUDIO_FRAME_SIZE;
ALint sample = 0;
@ -383,94 +431,164 @@ void *encode_audio_thread(void *arg)
if (sample >= frame_size) {
alcCaptureSamples((ALCdevice*)_phone->audio_capture_device, frame, frame_size);
encoded_size = toxav_encode_audio(_phone->av, frame, frame_size, encoded_data);
ret = toxav_send_audio(_phone->av, frame, frame_size);
if (encoded_size <= 0) {
printf("Could not encode audio packet\n");
} else {
if ( -1 == toxav_send_rtp_payload(_phone->av, TypeAudio, encoded_data, encoded_size) )
assert(0);
}
if (ret < 0)
printf("Could not encode or send audio packet\n");
} else {
usleep(1000);
}
}
/* clean up codecs *
pthread_mutex_lock(&cs->ctrl_mutex);*/
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_mutex_unlock(&cs->ctrl_mutex);*/
_phone->running_encaud = -1;
pthread_exit ( NULL );
}
void convert_to_rgb(vpx_image_t *img, unsigned char *out)
{
const int w = img->d_w;
const int w2 = w/2;
const int pstride = w*3;
const int h = img->d_h;
const int h2 = h/2;
const int strideY = img->stride[0];
const int strideU = img->stride[1];
const int strideV = img->stride[2];
int posy, posx;
for (posy = 0; posy < h2; posy++) {
unsigned char *dst = out + pstride * (posy * 2);
unsigned char *dst2 = out + pstride * (posy * 2 + 1);
const unsigned char *srcY = img->planes[0] + strideY * posy * 2;
const unsigned char *srcY2 = img->planes[0] + strideY * (posy * 2 + 1);
const unsigned char *srcU = img->planes[1] + strideU * posy;
const unsigned char *srcV = img->planes[2] + strideV * posy;
for (posx = 0; posx < w2; posx++) {
unsigned char Y,U,V;
short R,G,B;
short iR,iG,iB;
U = *(srcU++); V = *(srcV++);
iR = (351 * (V-128)) / 256;
iG = - (179 * (V-128)) / 256 - (86 * (U-128)) / 256;
iB = (444 * (U-128)) / 256;
Y = *(srcY++);
R = Y + iR ; G = Y + iG ; B = Y + iB ;
R = (R<0?0:(R>255?255:R)); G = (G<0?0:(G>255?255:G)); B = (B<0?0:(B>255?255:B));
*(dst++) = R; *(dst++) = G; *(dst++) = B;
Y = *(srcY2++);
R = Y + iR ; G = Y + iG ; B = Y + iB ;
R = (R<0?0:(R>255?255:R)); G = (G<0?0:(G>255?255:G)); B = (B<0?0:(B>255?255:B));
*(dst2++) = R; *(dst2++) = G; *(dst2++) = B;
Y = *(srcY++) ;
R = Y + iR ; G = Y + iG ; B = Y + iB ;
R = (R<0?0:(R>255?255:R)); G = (G<0?0:(G>255?255:G)); B = (B<0?0:(B>255?255:B));
*(dst++) = R; *(dst++) = G; *(dst++) = B;
Y = *(srcY2++);
R = Y + iR ; G = Y + iG ; B = Y + iB ;
R = (R<0?0:(R>255?255:R)); G = (G<0?0:(G>255?255:G)); B = (B<0?0:(B>255?255:B));
*(dst2++) = R; *(dst2++) = G; *(dst2++) = B;
}
}
}
#define mask32(BYTE) (*(uint32_t *)(uint8_t [4]){ [BYTE] = 0xff })
void *decode_video_thread(void *arg)
{
INFO("Started decode video thread!");
av_session_t* _phone = arg;
_phone->running_decvid = 1;
CodecState *cs = get_cs_temp(_phone->av);
cs->video_stream = 0;
//CodecState *cs = get_cs_temp(_phone->av);
//cs->video_stream = 0;
int recved_size;
uint8_t dest[RTP_PAYLOAD_SIZE];
//int recved_size;
//uint8_t dest[RTP_PAYLOAD_SIZE];
int dec_frame_finished;
AVFrame *r_video_frame;
r_video_frame = avcodec_alloc_frame();
AVPacket dec_video_packet;
av_new_packet (&dec_video_packet, 65536);
//int dec_frame_finished;
//AVFrame *r_video_frame;
//r_video_frame = avcodec_alloc_frame();
//AVPacket dec_video_packet;
//av_new_packet (&dec_video_packet, 65536);
int width = 0;
int height = 0;
while (_phone->running_decvid) {
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;
//recved_size = toxav_recv_rtp_payload(_phone->av, TypeVideo, dest);
//if (recved_size) {
vpx_image_t *image;
if (toxav_recv_video(_phone->av, &image) == 0) {
//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);
//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 (image->d_w != width || image->d_h != height) {
width = cs->video_decoder_ctx->width;
height = cs->video_decoder_ctx->height;
width = image->d_w;
height = image->d_h;
printf("w: %d h: %d \n", width, height);
screen = SDL_SetVideoMode(width, height, 0, 0);
if (_phone->video_picture.bmp)
SDL_FreeYUVOverlay(_phone->video_picture.bmp);
//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);
//_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);
}
uint8_t *rgb_image = malloc(width*height*3);
convert_to_rgb(image, rgb_image);
SDL_Surface* img_surface = SDL_CreateRGBSurfaceFrom(rgb_image, width, height, 24, width * 3, mask32(0), mask32(1), mask32(2), 0);
if(SDL_BlitSurface(img_surface, NULL, screen, NULL) == 0)
SDL_UpdateRect(screen, 0, 0, 0, 0);
/*
SDL_LockYUVOverlay(_phone->video_picture.bmp);
memcpy(_phone->video_picture.bmp->pixels[0], image->planes[VPX_PLANE_Y], _phone->video_picture.bmp->pitches[0] * height);
memcpy(_phone->video_picture.bmp->pixels[1], image->planes[VPX_PLANE_V], _phone->video_picture.bmp->pitches[1] * height / 2);
memcpy(_phone->video_picture.bmp->pixels[2], image->planes[VPX_PLANE_U], _phone->video_picture.bmp->pitches[2] * height / 2);
display_received_frame(_phone, r_video_frame);
} else {
SDL_Rect rect;
rect.x = 0;
rect.y = 0;
rect.w = width;
rect.h = height;
SDL_DisplayYUVOverlay(_phone->video_picture.bmp, &rect);*/
free(rgb_image);
//display_received_frame(_phone, image);
} //else {
/* TODO: request the sender to create a new i-frame immediatly */
printf("Bad video packet\n");
}
}
//printf("Bad video packet\n");
//}
//}
usleep(1000);
}
/* clean up codecs */
av_free(r_video_frame);
//av_free(r_video_frame);
pthread_mutex_lock(&cs->ctrl_mutex);
avcodec_close(cs->video_decoder_ctx);
pthread_mutex_unlock(&cs->ctrl_mutex);
//pthread_mutex_lock(&cs->ctrl_mutex);
//avcodec_close(cs->video_decoder_ctx);
//pthread_mutex_unlock(&cs->ctrl_mutex);
_phone->running_decvid = -1;
@ -483,11 +601,11 @@ void *decode_audio_thread(void *arg)
av_session_t* _phone = arg;
_phone->running_decaud = 1;
int recved_size;
uint8_t dest [RTP_PAYLOAD_SIZE];
//int recved_size;
//uint8_t dest [RTP_PAYLOAD_SIZE];
int frame_size = AUDIO_FRAME_SIZE;
int data_size;
//int data_size;
ALCdevice *dev;
ALCcontext *ctx;
@ -507,7 +625,7 @@ void *decode_audio_thread(void *arg)
uint16_t zeros[frame_size];
memset(zeros, 0, frame_size);
opus_int16 PCM[frame_size];
int16_t PCM[frame_size];
int i;
for (i = 0; i < openal_buffers; ++i) {
@ -527,28 +645,15 @@ void *decode_audio_thread(void *arg)
while (_phone->running_decaud) {
alGetSourcei(source, AL_BUFFERS_PROCESSED, &ready);
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);
}
if (ready <= 0)
continue;
dec_frame_len = toxav_recv_audio(_phone->av, frame_size, PCM);
/* Play the packet */
if (dec_frame_len) {
alGetSourcei(source, AL_BUFFERS_PROCESSED, &ready);
if (ready <= 0)
continue;
if (dec_frame_len > 0) {
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);
alBufferData(buffer, AL_FORMAT_MONO16, PCM, dec_frame_len * 2 * 1, 48000);
int error = alGetError();
if (error != AL_NO_ERROR) {
@ -573,16 +678,16 @@ void *decode_audio_thread(void *arg)
ending:
/* clean up codecs * /
pthread_mutex_lock(&cs->ctrl_mutex);
/* clean up codecs */
//pthread_mutex_lock(&cs->ctrl_mutex);
/*
alDeleteSources(1, &source);
alDeleteBuffers(openal_buffers, buffers);
alcMakeContextCurrent(NULL);
alcDestroyContext(ctx);
alcCloseDevice(dev);
pthread_mutex_unlock(&cs->ctrl_mutex); */
*/
//pthread_mutex_unlock(&cs->ctrl_mutex);
_phone->running_decaud = -1;
@ -604,7 +709,7 @@ int phone_startmedia_loop ( ToxAv* arg )
/*
* Rise all threads
*/
#ifdef TOX_FFMPEG
/* Only checks for last peer */
if ( toxav_get_peer_transmission_type(arg, 0) == TypeVideo &&
0 > event.rise(encode_video_thread, toxav_get_agent_handler(arg)) )
@ -612,7 +717,7 @@ int phone_startmedia_loop ( ToxAv* arg )
INFO("Error while starting encode_video_thread()");
return -1;
}
#endif
/* Always send audio */
if ( 0 > event.rise(encode_audio_thread, toxav_get_agent_handler(arg)) )
{
@ -779,7 +884,6 @@ av_session_t* av_init_session()
}
_retu->_friends = NULL;
_retu->av = toxav_new(_retu->_messenger, _retu, _USERAGENT);
const ALchar *_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
@ -821,13 +925,58 @@ av_session_t* av_init_session()
printf("Could not start capture device! %d\n", alcGetError((ALCdevice*)_retu->audio_capture_device));
return 0;
}
uint16_t height = 0, width = 0;
#ifdef TOX_FFMPEG
avdevice_register_all();
avcodec_register_all();
av_register_all();
_retu->video_input_format = av_find_input_format(VIDEO_DRIVER);
if (avformat_open_input(&_retu->video_format_ctx, DEFAULT_WEBCAM, _retu->video_input_format, NULL) != 0) {
fprintf(stderr, "Opening video_input_format failed!\n");
//return -1;
return NULL;
}
avformat_find_stream_info(_retu->video_format_ctx, NULL);
av_dump_format(_retu->video_format_ctx, 0, DEFAULT_WEBCAM, 0);
for (i = 0; i < _retu->video_format_ctx->nb_streams; ++i) {
if (_retu->video_format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
_retu->video_stream = i;
break;
}
}
_retu->webcam_decoder_ctx = _retu->video_format_ctx->streams[_retu->video_stream]->codec;
_retu->webcam_decoder = avcodec_find_decoder(_retu->webcam_decoder_ctx->codec_id);
if (_retu->webcam_decoder == NULL) {
fprintf(stderr, "Unsupported codec!\n");
//return -1;
return NULL;
}
if (_retu->webcam_decoder_ctx == NULL) {
fprintf(stderr, "Init webcam_decoder_ctx failed!\n");
//return -1;
return NULL;
}
if (avcodec_open2(_retu->webcam_decoder_ctx, _retu->webcam_decoder, NULL) < 0) {
fprintf(stderr, "Opening webcam decoder failed!\n");
//return -1;
return NULL;
}
width = _retu->webcam_decoder_ctx->width;
height = _retu->webcam_decoder_ctx->height;
#endif
uint8_t _byte_address[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(_retu->_messenger, _byte_address );
fraddr_to_str( _byte_address, _retu->_my_public_id );
_retu->av = toxav_new(_retu->_messenger, _retu, _USERAGENT, width, height);
/* ------------------ */
@ -842,7 +991,7 @@ av_session_t* av_init_session()
toxav_register_callstate_callback(callback_recv_ending, OnEnding);
toxav_register_callstate_callback(callback_recv_error, OnError);
toxav_register_callstate_callback(callback_requ_timeout, OnTimeout);
toxav_register_callstate_callback(callback_requ_timeout, OnRequestTimeout);
/* ------------------ */
@ -1013,9 +1162,9 @@ void do_phone ( av_session_t* _phone )
{
ToxAvError rc;
if ( _len > 1 && _line[2] == 'v' )
if ( _len > 1 && _line[2] == 'v' ) {
rc = toxav_answer(_phone->av, TypeVideo);
else
} else
rc = toxav_answer(_phone->av, TypeAudio);
if ( rc == ErrorInvalidState ) {

View File

@ -30,10 +30,6 @@
#include <assert.h>
#include <stdlib.h>
#include "../toxcore/util.h"
#include "../toxcore/network.h"
#include "../toxcore/net_crypto.h"
#include "../toxcore/Messenger.h"
#define PAYLOAD_ID_VALUE_OPUS 1
#define PAYLOAD_ID_VALUE_VP8 2
@ -241,8 +237,8 @@ RTPHeader* extract_header ( const uint8_t* payload, int length )
_retu->flags = *_it; ++_it;
/* This indicates if the first 2 bytes are valid.
* Now it my happen that this is out of order but
/* This indicates if the first 2 bits are valid.
* Now it may happen that this is out of order but
* it cuts down chances of parsing some invalid value
*/
@ -299,7 +295,7 @@ RTPHeader* extract_header ( const uint8_t* payload, int length )
* @return RTPExtHeader* Extracted extension header.
* @retval NULL Error occurred while extracting extension header.
*/
RTPExtHeader* extract_ext_header ( const uint8_t* payload, size_t length )
RTPExtHeader* extract_ext_header ( const uint8_t* payload, uint16_t length )
{
const uint8_t* _it = payload;
@ -551,7 +547,7 @@ int rtp_handle_packet ( void* object, IP_Port ip_port, uint8_t* data, uint32_t l
/* Hopefully this goes well
* NOTE: Is this even used?
*/
memcpy(&_msg->from, &ip_port, sizeof(tox_IP_Port));
memcpy(&_msg->from, &ip_port, sizeof(IP_Port));
/* Check if message came in late */
if ( check_late_message(_session, _msg) < 0 ) { /* Not late */
@ -689,7 +685,7 @@ int rtp_release_session_recv ( RTPSession* session )
/**
* @brief Get's oldes message in the list.
* @brief Gets oldest message in the list.
*
* @param session Where the list is.
* @return RTPMessage* The message. You _must_ call rtp_msg_free() to free it.
@ -727,7 +723,7 @@ RTPMessage* rtp_recv_msg ( RTPSession* session )
* @retval -1 On error.
* @retval 0 On success.
*/
int rtp_send_msg ( RTPSession* session, Tox* messenger, const uint8_t* data, uint16_t length )
int rtp_send_msg ( RTPSession* session, Messenger* messenger, const uint8_t* data, uint16_t length )
{
RTPMessage* msg = rtp_new_message (session, data, length);
@ -743,8 +739,8 @@ int rtp_send_msg ( RTPSession* session, Tox* messenger, const uint8_t* data, uin
increase_nonce ( _calculated, msg->header->sequnum );
/* Need to skip 2 bytes that are for sequnum */
int encrypted_length = encrypt_data_symmetric(
(uint8_t*) session->encrypt_key, _calculated, msg->data + 2, msg->length - 2, _send_data + 3 );
int encrypted_length = encrypt_data_symmetric( /* TODO: msg->length - 2 (fix this properly)*/
(uint8_t*) session->encrypt_key, _calculated, msg->data + 2, msg->length, _send_data + 3 );
int full_length = encrypted_length + 3;
@ -752,7 +748,8 @@ int rtp_send_msg ( RTPSession* session, Tox* messenger, const uint8_t* data, uin
_send_data[2] = msg->data[1];
if ( full_length != sendpacket ( ((Messenger*)messenger)->net, *((IP_Port*) &session->dest), _send_data, full_length) ) {
/*if ( full_length != sendpacket ( messenger->net, *((IP_Port*) &session->dest), _send_data, full_length) ) {*/
if ( full_length != send_custom_user_packet(messenger, session->dest, _send_data, full_length) ) {
printf("Rtp error: %s\n", strerror(errno));
return -1;
}
@ -816,30 +813,26 @@ void rtp_free_msg ( RTPSession* session, RTPMessage* msg )
* @retval NULL Error occurred.
*/
RTPSession* rtp_init_session ( int payload_type,
Tox* messenger,
Messenger* messenger,
int friend_num,
const uint8_t* encrypt_key,
const uint8_t* decrypt_key,
const uint8_t* encrypt_nonce,
const uint8_t* decrypt_nonce
)
const uint8_t* decrypt_nonce )
{
Messenger* _messenger_casted = (Messenger*) messenger;
IP_Port _dest = get_friend_ipport(_messenger_casted, friend_num );
/* This should be enough eh? */
if ( _dest.port == 0) {
return NULL;
}
RTPSession* _retu = calloc(1, sizeof(RTPSession));
assert(_retu);
networking_registerhandler(_messenger_casted->net, payload_type, rtp_handle_packet, _retu);
/*networking_registerhandler(messenger->net, payload_type, rtp_handle_packet, _retu);*/
if ( -1 == custom_user_packet_registerhandler(messenger, friend_num, payload_type, rtp_handle_packet, _retu) )
{
fprintf(stderr, "Error setting custom register handler for rtp session\n");
free(_retu);
return NULL;
}
_retu->version = RTP_VERSION; /* It's always 2 */
_retu->padding = 0; /* If some additional data is needed about the packet */
_retu->version = RTP_VERSION; /* It's always 2 */
_retu->padding = 0; /* If some additional data is needed about the packet */
_retu->extension = 0; /* If extension to header is needed */
_retu->cc = 1; /* Amount of contributors */
_retu->csrc = NULL; /* Container */
@ -847,7 +840,7 @@ RTPSession* rtp_init_session ( int payload_type,
_retu->marker = 0;
_retu->payload_type = payload_table[payload_type];
_retu->dest = *((tox_IP_Port*)&_dest);
_retu->dest = friend_num;
_retu->rsequnum = _retu->sequnum = 1;
@ -894,12 +887,12 @@ RTPSession* rtp_init_session ( int payload_type,
* @retval -1 Error occurred.
* @retval 0 Success.
*/
int rtp_terminate_session ( RTPSession* session, Tox* messenger )
int rtp_terminate_session ( RTPSession* session, Messenger* messenger )
{
if ( !session )
return -1;
networking_registerhandler(((Messenger*)messenger)->net, session->prefix, NULL, NULL);
custom_user_packet_registerhandler(messenger, session->dest, session->prefix, NULL, NULL);
free ( session->ext_header );
free ( session->csrc );

View File

@ -28,10 +28,14 @@
#define RTP_VERSION 2
#include <inttypes.h>
#include <pthread.h>
#include "../toxcore/tox.h"
#include "../toxcore/util.h"
#include "../toxcore/network.h"
#include "../toxcore/net_crypto.h"
#include "../toxcore/Messenger.h"
#define MAX_SEQU_NUM 65535
#define MAX_RTP_SIZE 10400
#define MAX_RTP_SIZE 65535
/**
* @brief Standard rtp header
@ -72,7 +76,7 @@ typedef struct _RTPMessage {
uint8_t data[MAX_RTP_SIZE];
uint32_t length;
tox_IP_Port from;
IP_Port from;
struct _RTPMessage* next;
} RTPMessage;
@ -128,7 +132,7 @@ typedef struct _RTPSession {
uint8_t prefix;
pthread_mutex_t mutex;
tox_IP_Port dest;
int dest;
} RTPSession;
@ -164,7 +168,7 @@ RTPMessage* rtp_recv_msg ( RTPSession* session );
* @retval -1 On error.
* @retval 0 On success.
*/
int rtp_send_msg ( RTPSession* session, Tox* messenger, const uint8_t* data, uint16_t length );
int rtp_send_msg ( RTPSession* session, Messenger* messenger, const uint8_t* data, uint16_t length );
/**
@ -192,7 +196,7 @@ void rtp_free_msg ( RTPSession* session, RTPMessage* msg );
* @retval NULL Error occurred.
*/
RTPSession* rtp_init_session ( int payload_type,
Tox* messenger,
Messenger* messenger,
int friend_num,
const uint8_t* encrypt_key,
const uint8_t* decrypt_key,
@ -209,7 +213,7 @@ RTPSession* rtp_init_session ( int payload_type,
* @retval -1 Error occurred.
* @retval 0 Success.
*/
int rtp_terminate_session ( RTPSession* session, Tox* messenger );
int rtp_terminate_session ( RTPSession* session, Messenger* messenger );

View File

@ -26,16 +26,16 @@
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include "toxav.h"
#include "../toxcore/tox.h"
#include "media.h"
#include "rtp.h"
#include "msi.h"
#include "media.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "toxav.h"
#define inline__ inline __attribute__((always_inline))
static const uint8_t audio_index = 0, video_index = 1;
@ -50,7 +50,7 @@ typedef enum {
typedef struct _ToxAv
{
Tox* messenger;
Messenger* messenger;
MSISession* msi_session; /** Main msi session */
@ -89,21 +89,23 @@ typedef struct _ToxAv
ToxAv* toxav_new( Tox* messenger, void* useragent, const char* ua_name )
{
ToxAv* toxav_new( Tox* messenger, void* useragent, const char* ua_name , uint16_t video_width, uint16_t video_height)
{
ToxAv* av = calloc ( sizeof(ToxAv), 1);
av->msi_session = msi_init_session(messenger, (const unsigned char*) ua_name );
if (av == NULL)
return NULL;
av->messenger = (Messenger *)messenger;
av->msi_session = msi_init_session(av->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->cs = codec_init_session(AUDIO_BITRATE, AUDIO_FRAME_DURATION, AUDIO_SAMPLE_RATE, AUDIO_CHANNELS, video_width, video_height, VIDEO_BITRATE);
av->agent_handler = useragent;
@ -273,7 +275,7 @@ inline__ int toxav_send_rtp_payload ( ToxAv* av, ToxAvCallType type, const uint8
else return -1;
}
inline__ int toxav_recv_rtp_payload ( ToxAv* av, ToxAvCallType type, int ready, uint8_t* dest )
inline__ int toxav_recv_rtp_payload ( ToxAv* av, ToxAvCallType type, uint8_t* dest )
{
if ( !dest ) return ErrorInternal;
@ -283,20 +285,19 @@ inline__ int toxav_recv_rtp_payload ( ToxAv* av, ToxAvCallType type, int ready,
if ( type == TypeAudio ) {
message = rtp_recv_msg(av->rtp_sessions[audio_index]);
do {
message = rtp_recv_msg(av->rtp_sessions[audio_index]);
if (message) {
/* push the packet into the queue */
queue(av->j_buf, message);
}
} while(message);
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;
int success = 0;
message = dequeue(av->j_buf, &success);
if ( success == 2) return ErrorAudioPacketLost;
}
else {
message = rtp_recv_msg(av->rtp_sessions[video_index]);
@ -315,19 +316,75 @@ inline__ int toxav_recv_rtp_payload ( ToxAv* av, ToxAvCallType type, int ready,
return 0;
}
inline__ int toxav_decode_audio ( ToxAv* av, const uint8_t* payload, uint16_t length, int frame_size, short int* dest )
inline__ int toxav_recv_video ( ToxAv* av, vpx_image_t **output)
{
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 )
if ( !output ) return ErrorInternal;
uint8_t packet [RTP_PAYLOAD_SIZE];
int recved_size = 0;
do {
recved_size = toxav_recv_rtp_payload(av, TypeVideo, packet);
if (recved_size > 0) {
printf("decode: %s\n", vpx_codec_err_to_string(vpx_codec_decode(&av->cs->v_decoder, packet, recved_size, NULL, 0)));
}
}while (recved_size > 0);
vpx_codec_iter_t iter = NULL;
vpx_image_t *img;
img = vpx_codec_get_frame(&av->cs->v_decoder, &iter);
if (img == NULL)
return ErrorInternal;
return opus_encode(av->cs->audio_encoder, frame, frame_size, dest, RTP_PAYLOAD_SIZE);
*output = img;
return 0;
}
inline__ int toxav_send_video ( ToxAv* av, vpx_image_t *input)
{
if (vpx_codec_encode(&av->cs->v_encoder, input, av->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US) != VPX_CODEC_OK) {
printf("could not encode video frame\n");
return ErrorInternal;
}
++av->cs->frame_counter;
vpx_codec_iter_t iter = NULL;
const vpx_codec_cx_pkt_t *pkt;
int sent = 0;
while( (pkt = vpx_codec_get_cx_data(&av->cs->v_encoder, &iter)) ) {
if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
if (toxav_send_rtp_payload(av, TypeVideo, pkt->data.frame.buf, pkt->data.frame.sz) != -1)
++sent;
}
}
if (sent > 0)
return 0;
return ErrorInternal;
}
inline__ int toxav_recv_audio ( ToxAv* av, int frame_size, int16_t* dest )
{
if ( !dest ) return ErrorInternal;
uint8_t packet [RTP_PAYLOAD_SIZE];
int recved_size = toxav_recv_rtp_payload(av, TypeAudio, packet);
if ( recved_size == ErrorAudioPacketLost ) {
printf("Lost packet\n");
return opus_decode(av->cs->audio_decoder, NULL, 0, dest, frame_size, 1);
} else if ( recved_size ){
return opus_decode(av->cs->audio_decoder, packet, recved_size, dest, frame_size, 0);
} else {
return ErrorInternal;
}
}
inline__ int toxav_send_audio ( ToxAv* av, const int16_t* frame, int frame_size)
{
uint8_t temp_data[RTP_PAYLOAD_SIZE];
int32_t ret = opus_encode(av->cs->audio_encoder, frame, frame_size, temp_data, sizeof(temp_data));
if (ret <= 0)
return ErrorInternal;
return toxav_send_rtp_payload(av, TypeAudio, temp_data, ret);
}
int toxav_get_peer_transmission_type ( ToxAv* av, int peer )
@ -343,10 +400,3 @@ void* toxav_get_agent_handler ( ToxAv* av )
{
return av->agent_handler;
}
/* Only temporary */
void* get_cs_temp(ToxAv* av)
{
return av->cs;
}

View File

@ -27,6 +27,9 @@
#define __TOXAV
#include <inttypes.h>
/* vpx_image_t */
#include <vpx/vpx_image.h>
typedef void* ( *ToxAVCallback ) ( void* arg );
typedef struct _ToxAv ToxAv;
@ -35,7 +38,29 @@ typedef struct _ToxAv ToxAv;
typedef struct Tox Tox;
#endif
#define RTP_PAYLOAD_SIZE 10400
#define RTP_PAYLOAD_SIZE 65535
/* Default video bitrate in bytes/s */
#define VIDEO_BITRATE 10*1000*100
/* Default audio bitrate in bits/s */
#define AUDIO_BITRATE 64000
/* Number of audio channels. */
#define AUDIO_CHANNELS 1
/* Audio frame duration in miliseconds */
#define AUDIO_FRAME_DURATION 20
/* Audio sample rate recommended to be 48kHz for Opus */
#define AUDIO_SAMPLE_RATE 48000
/* The amount of samples in one audio frame */
#define AUDIO_FRAME_SIZE AUDIO_SAMPLE_RATE*AUDIO_FRAME_DURATION/1000
/* Assume 60 fps*/
#define MAX_ENCODE_TIME_US ((1000 / 60) * 1000)
/**
* @brief Callbacks ids that handle the call states
@ -55,7 +80,7 @@ typedef enum {
/* Protocol */
OnError,
OnTimeout
OnRequestTimeout
} ToxAvCallbackID;
@ -86,7 +111,7 @@ typedef enum {
} ToxAvError;
ToxAv* toxav_new(Tox* messenger, void* useragent, const char* ua_name);
ToxAv* toxav_new(Tox* messenger, void* useragent, const char* ua_name, uint16_t video_width, uint16_t video_height) ;
void toxav_kill(ToxAv* av);
void toxav_register_callstate_callback (ToxAVCallback callback, ToxAvCallbackID id);
@ -103,27 +128,25 @@ 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);
/* returns 0 on success */
int toxav_recv_video ( ToxAv* av, vpx_image_t **output);
int toxav_recv_audio( ToxAv* av, int frame_size, int16_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_send_video ( ToxAv* av, vpx_image_t *input);
/* Encode and send audio frame. */
int toxav_send_audio ( ToxAv* av, const int16_t* frame, int frame_size);
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 */

View File

@ -379,6 +379,7 @@ static int get_somewhat_close_nodes(DHT *dht, uint8_t *client_id, Node_format *n
int get_close_nodes(DHT *dht, uint8_t *client_id, Node_format *nodes_list, sa_family_t sa_family, uint8_t is_LAN,
uint8_t want_good)
{
memset(nodes_list, 0, MAX_SENT_NODES * sizeof(Node_format));
#ifdef ENABLE_ASSOC_DHT
if (!dht->assoc)
@ -1249,9 +1250,9 @@ int DHT_delfriend(DHT *dht, uint8_t *client_id)
--dht->num_friends;
if (dht->num_friends != i) {
memcpy( dht->friends_list[i].client_id,
dht->friends_list[dht->num_friends].client_id,
CLIENT_ID_SIZE );
memcpy( &dht->friends_list[i],
&dht->friends_list[dht->num_friends],
sizeof(DHT_Friend) );
}
if (dht->num_friends == 0) {
@ -1553,7 +1554,7 @@ int route_tofriend(DHT *dht, uint8_t *friend_id, uint8_t *packet, uint32_t lengt
IP_Port ip_list[MAX_FRIEND_CLIENTS];
int ip_num = friend_iplist(dht, ip_list, num);
if (ip_num < (MAX_FRIEND_CLIENTS / 2))
if (ip_num < (MAX_FRIEND_CLIENTS / 4))
return 0; /* Reason for that? */
DHT_Friend *friend = &dht->friends_list[num];
@ -2394,7 +2395,6 @@ static int dht_load_state_callback(void *outer, uint8_t *data, uint32_t length,
num = length / sizeof(DHT_Friend);
for (i = 0; i < num; ++i) {
DHT_addfriend(dht, friend_list[i].client_id);
for (j = 0; j < MAX_FRIEND_CLIENTS; ++j) {
Client_data *client = &friend_list[i].client_list[j];

View File

@ -43,6 +43,71 @@ static uint8_t friend_not_valid(Messenger *m, int friendnumber)
return (unsigned int)friendnumber >= m->numfriends;
}
static int add_online_friend(Messenger *m, int friendnumber)
{
if (friend_not_valid(m, friendnumber))
return -1;
IP_Port temp_ip_port = get_friend_ipport(m, friendnumber);
if (temp_ip_port.port == 0)
return -1;
uint32_t i;
for (i = 0; i < m->numonline_friends; ++i) {
if (m->online_friendlist[i].friend_num == (uint32_t)friendnumber)
return 0;
}
Online_Friend *temp;
temp = realloc(m->online_friendlist, sizeof(Online_Friend) * (m->numonline_friends + 1));
if (temp == NULL)
return -1;
m->online_friendlist = temp;
m->online_friendlist[m->numonline_friends].friend_num = friendnumber;
m->online_friendlist[m->numonline_friends].ip_port = temp_ip_port;
++m->numonline_friends;
return 0;
}
static int remove_online_friend(Messenger *m, int friendnumber)
{
uint32_t i;
Online_Friend *temp;
for (i = 0; i < m->numonline_friends; ++i) {
/* Equal */
if (m->online_friendlist[i].friend_num == (uint32_t)friendnumber) {
--m->numonline_friends;
if (m->numonline_friends != i) {
memcpy( &m->online_friendlist[i],
&m->online_friendlist[m->numonline_friends],
sizeof(Online_Friend) );
}
if (m->numonline_friends == 0) {
free(m->online_friendlist);
m->online_friendlist = NULL;
return 0;
}
temp = realloc(m->online_friendlist, sizeof(Online_Friend) * (m->numonline_friends));
if (temp == NULL)
return -1;
m->online_friendlist = temp;
return 0;
}
}
return -1;
}
/* Set the size of the friend list to numfriends.
*
* return -1 if realloc fails.
@ -273,6 +338,9 @@ int m_delfriend(Messenger *m, int friendnumber)
if (friend_not_valid(m, friendnumber))
return -1;
if (m->friendlist[friendnumber].status == FRIEND_ONLINE)
remove_online_friend(m, friendnumber);
onion_delfriend(m->onion_c, m->friendlist[friendnumber].onion_friendnum);
crypto_kill(m->net_crypto, m->friendlist[friendnumber].crypt_connection_id);
free(m->friendlist[friendnumber].statusmessage);
@ -650,12 +718,17 @@ void m_callback_connectionstatus(Messenger *m, void (*function)(Messenger *m, in
m->friend_connectionstatuschange = function;
m->friend_connectionstatuschange_userdata = userdata;
}
void m_callback_connectionstatus_internal_av(Messenger *m, void (*function)(Messenger *m, int, uint8_t, void *),
void *userdata)
{
m->friend_connectionstatuschange_internal = function;
m->friend_connectionstatuschange_internal_userdata = userdata;
}
static void break_files(Messenger *m, int friendnumber);
static void check_friend_connectionstatus(Messenger *m, int friendnumber, uint8_t status)
{
if (!m->friend_connectionstatuschange)
return;
if (status == NOFRIEND)
return;
@ -665,10 +738,19 @@ static void check_friend_connectionstatus(Messenger *m, int friendnumber, uint8_
onion_set_friend_online(m->onion_c, m->friendlist[friendnumber].onion_friendnum, is_online);
if (is_online != was_online) {
if (was_online)
if (was_online) {
break_files(m, friendnumber);
remove_online_friend(m, friendnumber);
} else {
add_online_friend(m, friendnumber);
}
m->friend_connectionstatuschange(m, friendnumber, is_online, m->friend_connectionstatuschange_userdata);
if (m->friend_connectionstatuschange)
m->friend_connectionstatuschange(m, friendnumber, is_online, m->friend_connectionstatuschange_userdata);
if (m->friend_connectionstatuschange_internal)
m->friend_connectionstatuschange_internal(m, friendnumber, is_online,
m->friend_connectionstatuschange_internal_userdata);
}
}
@ -814,6 +896,8 @@ static void group_message_function(Group_Chat *chat, int peer_number, uint8_t *m
if (i == -1)
return;
message[length - 1] = 0; /* Force NULL terminator */
if (m->group_message)
(*m->group_message)(m, i, peer_number, message, length, m->group_message_userdata);
}
@ -826,6 +910,8 @@ static void group_action_function(Group_Chat *chat, int peer_number, uint8_t *ac
if (i == -1)
return;
action[length - 1] = 0; /* Force NULL terminator */
if (m->group_action)
(*m->group_action)(m, i, peer_number, action, length, m->group_action_userdata);
}
@ -1489,6 +1575,60 @@ int m_msi_packet(Messenger *m, int friendnumber, uint8_t *data, uint16_t length)
return write_cryptpacket_id(m, friendnumber, PACKET_ID_MSI, data, length);
}
static int friendnum_from_ip_port(Messenger *m, IP_Port ip_port)
{
uint32_t i;
for (i = 0; i < m->numonline_friends; ++i) {
if (ipport_equal(&m->online_friendlist[i].ip_port, &ip_port))
return m->online_friendlist[i].friend_num;
}
return -1;
}
static int handle_custom_user_packet(void *object, IP_Port source, uint8_t *packet, uint32_t length)
{
Messenger *m = object;
int friend_num = friendnum_from_ip_port(m, source);
if (friend_num == -1)
return 1;
if (m->friendlist[friend_num].packethandlers[packet[0] % TOTAL_USERPACKETS].function)
return m->friendlist[friend_num].packethandlers[packet[0] % TOTAL_USERPACKETS].function(
m->friendlist[friend_num].packethandlers[packet[0] % TOTAL_USERPACKETS].object, source, packet, length);
return 1;
}
int custom_user_packet_registerhandler(Messenger *m, int friendnumber, uint8_t byte, packet_handler_callback cb,
void *object)
{
if (friend_not_valid(m, friendnumber))
return -1;
if (byte < NET_PACKET_CUSTOM_RANGE_START || byte >= NET_PACKET_CUSTOM_RANGE_END)
return -1;
m->friendlist[friendnumber].packethandlers[byte % TOTAL_USERPACKETS].function = cb;
m->friendlist[friendnumber].packethandlers[byte % TOTAL_USERPACKETS].object = object;
networking_registerhandler(m->net, byte, handle_custom_user_packet, m);
return 0;
}
int send_custom_user_packet(Messenger *m, int friendnumber, uint8_t *data, uint32_t length)
{
IP_Port ip_port = get_friend_ipport(m, friendnumber);
if (ip_port.port == 0)
return -1;
return sendpacket(m->net, ip_port, data, length);
}
/* Function to filter out some friend requests*/
static int friend_already_added(uint8_t *client_id, void *data)
{
@ -1841,6 +1981,8 @@ void do_friends(Messenger *m)
m->friendlist[i].file_receiving[filenumber].size = filesize;
m->friendlist[i].file_receiving[filenumber].transferred = 0;
data[data_length - 1] = 0; /* Force NULL terminate file name. */
if (m->file_sendrequest)
(*m->file_sendrequest)(m, i, filenumber, filesize, data + 1 + sizeof(uint64_t), data_length - 1 - sizeof(uint64_t),
m->file_sendrequest_userdata);
@ -2360,6 +2502,12 @@ uint32_t count_friendlist(Messenger *m)
return ret;
}
/* Return the number of online friends in the instance m. */
uint32_t get_num_online_friends(Messenger *m)
{
return m->numonline_friends;
}
/* Copy a list of valid friend IDs into the array out_list.
* If out_list is NULL, returns 0.
* Otherwise, returns the number of elements copied.

View File

@ -155,8 +155,15 @@ typedef struct {
struct File_Transfers file_receiving[MAX_CONCURRENT_FILE_PIPES];
int invited_groups[MAX_INVITED_GROUPS];
uint16_t invited_groups_num;
Packet_Handles packethandlers[TOTAL_USERPACKETS];
} Friend;
typedef struct {
uint32_t friend_num;
IP_Port ip_port;
} Online_Friend;
typedef struct Messenger {
Networking_Core *net;
@ -179,6 +186,9 @@ typedef struct Messenger {
Friend *friendlist;
uint32_t numfriends;
Online_Friend *online_friendlist;
uint32_t numonline_friends;
Group_Chat **chats;
uint32_t numchats;
@ -200,6 +210,8 @@ typedef struct Messenger {
void *friend_statuschange_userdata;
void (*friend_connectionstatuschange)(struct Messenger *m, int, uint8_t, void *);
void *friend_connectionstatuschange_userdata;
void (*friend_connectionstatuschange_internal)(struct Messenger *m, int, uint8_t, void *);
void *friend_connectionstatuschange_internal_userdata;
void (*group_invite)(struct Messenger *m, int, uint8_t *, void *);
void *group_invite_userdata;
@ -450,6 +462,9 @@ void m_callback_read_receipt(Messenger *m, void (*function)(Messenger *m, int, u
* It's assumed that when adding friends, their connection status is offline.
*/
void m_callback_connectionstatus(Messenger *m, void (*function)(Messenger *m, int, uint8_t, void *), void *userdata);
/* Same as previous but for internal A/V core usage only */
void m_callback_connectionstatus_internal_av(Messenger *m, void (*function)(Messenger *m, int, uint8_t, void *),
void *userdata);
/**********GROUP CHATS************/
@ -627,6 +642,22 @@ int m_msi_packet(Messenger *m, int friendnumber, uint8_t *data, uint16_t length)
/**********************************************/
/* Set handlers for custom user packets (RTP packets for example.)
*
* return -1 on failure.
* return 0 on success.
*/
int custom_user_packet_registerhandler(Messenger *m, int friendnumber, uint8_t byte, packet_handler_callback cb,
void *object);
/* High level function to send custom user packets.
*
* return -1 on failure.
* return number of bytes sent on success.
*/
int send_custom_user_packet(Messenger *m, int friendnumber, uint8_t *data, uint32_t length);
/**********************************************/
/* Run this at startup.
* return allocated instance of Messenger on success.
* return 0 if there are problems.
@ -682,6 +713,9 @@ int messenger_load_encrypted(Messenger *m, uint8_t *data, uint32_t length, uint8
* for copy_friendlist. */
uint32_t count_friendlist(Messenger *m);
/* Return the number of online friends in the instance m. */
uint32_t get_num_online_friends(Messenger *m);
/* Copy a list of valid friend IDs into the array out_list.
* If out_list is NULL, returns 0.
* Otherwise, returns the number of elements copied.

View File

@ -31,6 +31,7 @@
#include "event.h"
#include "util.h"
#include "network.h"
#define _GNU_SOURCE

View File

@ -140,6 +140,9 @@ static int friendreq_handlepacket(void *object, uint8_t *source_pubkey, uint8_t
return 1;
addto_receivedlist(fr, source_pubkey);
packet[length - 1] = 0; /* Force NULL terminator. */
(*fr->handle_friendrequest)(source_pubkey, packet + 4, length - 4, fr->handle_friendrequest_userdata);
return 0;
}

View File

@ -75,6 +75,32 @@ static int peer_in_chat(Group_Chat *chat, uint8_t *client_id)
return -1;
}
/* Compares client_id1 and client_id2 with client_id.
*
* return 0 if both are same distance.
* return 1 if client_id1 is closer.
* return 2 if client_id2 is closer.
*/
static int id_closest_groupchats(uint8_t *id, uint8_t *id1, uint8_t *id2)
{
size_t i;
uint8_t distance1, distance2;
for (i = 0; i < CLIENT_ID_SIZE; ++i) {
distance1 = abs(((int8_t *)id)[i] - ((int8_t *)id1)[i]);
distance2 = abs(((int8_t *)id)[i] - ((int8_t *)id2)[i]);
if (distance1 < distance2)
return 1;
if (distance1 > distance2)
return 2;
}
return 0;
}
#define BAD_GROUPNODE_TIMEOUT 30
/*
@ -100,7 +126,7 @@ static int peer_okping(Group_Chat *chat, uint8_t *client_id)
if (id_equal(chat->close[i].client_id, client_id))
return -1;
if (id_closest(chat->self_public_key, chat->close[i].client_id, client_id) == 2)
if (id_closest_groupchats(chat->self_public_key, chat->close[i].client_id, client_id) == 2)
++j;
}
@ -137,7 +163,7 @@ static int add_closepeer(Group_Chat *chat, uint8_t *client_id, IP_Port ip_port)
}
for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) { /* Replace nodes if given one is closer. */
if (id_closest(chat->self_public_key, chat->close[i].client_id, client_id) == 2) {
if (id_closest_groupchats(chat->self_public_key, chat->close[i].client_id, client_id) == 2) {
id_copy(chat->close[i].client_id, client_id);
chat->close[i].ip_port = ip_port;
chat->close[i].last_recv = unix_time();

View File

@ -540,8 +540,10 @@ Networking_Core *new_networking(IP ip, uint16_t port)
addr6->sin6_scope_id = 0;
portptr = &addr6->sin6_port;
} else
} else {
free(temp);
return NULL;
}
if (ip.family == AF_INET6) {
char ipv6only = 0;

View File

@ -129,6 +129,12 @@ typedef int sock_t;
#define NET_PACKET_LAN_DISCOVERY 33 /* LAN discovery packet ID. */
#define NET_PACKET_GROUP_CHATS 48 /* Group chats packet ID. */
/* Range of ids that custom user packets can use. */
#define NET_PACKET_CUSTOM_RANGE_START 64
#define NET_PACKET_CUSTOM_RANGE_END 96
#define TOTAL_USERPACKETS (NET_PACKET_CUSTOM_RANGE_END - NET_PACKET_CUSTOM_RANGE_START)
/* See: docs/Prevent_Tracking.txt and onion.{c, h} */
#define NET_PACKET_ONION_SEND_INITIAL 128
#define NET_PACKET_ONION_SEND_1 129
@ -143,6 +149,9 @@ typedef int sock_t;
#define NET_PACKET_ONION_RECV_2 141
#define NET_PACKET_ONION_RECV_1 142
/* Only used for bootstrap servers */
#define BOOTSTRAP_INFO_PACKET_ID 240
#define TOX_PORTRANGE_FROM 33445
#define TOX_PORTRANGE_TO 33545

View File

@ -24,6 +24,7 @@
#endif
#include "onion.h"
#include "util.h"
#define MAX_ONION_SIZE MAX_DATA_SIZE
@ -36,6 +37,16 @@
#define SEND_2 ONION_SEND_2
#define SEND_1 ONION_SEND_1
/* Change symmetric keys every hour to make paths expire eventually. */
#define KEY_REFRESH_INTERVAL (60 * 60)
static void change_symmetric_key(Onion *onion)
{
if (is_timeout(onion->timestamp, KEY_REFRESH_INTERVAL)) {
new_symmetric_key(onion->secret_symmetric_key);
onion->timestamp = unix_time();
}
}
/* Create and send a onion packet.
*
* nodes is a list of 4 nodes, the packet will route through nodes 0, 1, 2 and the data
@ -126,6 +137,8 @@ static int handle_send_initial(void *object, IP_Port source, uint8_t *packet, ui
if (length <= 1 + SEND_1)
return 1;
change_symmetric_key(onion);
uint8_t plain[MAX_ONION_SIZE];
int len = decrypt_data(packet + 1 + crypto_box_NONCEBYTES, onion->dht->self_secret_key, packet + 1,
@ -170,6 +183,8 @@ static int handle_send_1(void *object, IP_Port source, uint8_t *packet, uint32_t
if (length <= 1 + SEND_2)
return 1;
change_symmetric_key(onion);
uint8_t plain[MAX_ONION_SIZE];
int len = decrypt_data(packet + 1 + crypto_box_NONCEBYTES, onion->dht->self_secret_key, packet + 1,
@ -217,6 +232,8 @@ static int handle_send_2(void *object, IP_Port source, uint8_t *packet, uint32_t
if (length <= 1 + SEND_3)
return 1;
change_symmetric_key(onion);
uint8_t plain[MAX_ONION_SIZE];
int len = decrypt_data(packet + 1 + crypto_box_NONCEBYTES, onion->dht->self_secret_key, packet + 1,
@ -263,6 +280,8 @@ static int handle_recv_3(void *object, IP_Port source, uint8_t *packet, uint32_t
if (length <= 1 + RETURN_3)
return 1;
change_symmetric_key(onion);
uint8_t plain[sizeof(IP_Port) + RETURN_2];
int len = decrypt_data_symmetric(onion->secret_symmetric_key, packet + 1, packet + 1 + crypto_secretbox_NONCEBYTES,
sizeof(IP_Port) + RETURN_2 + crypto_secretbox_MACBYTES, plain);
@ -295,6 +314,8 @@ static int handle_recv_2(void *object, IP_Port source, uint8_t *packet, uint32_t
if (length <= 1 + RETURN_2)
return 1;
change_symmetric_key(onion);
uint8_t plain[sizeof(IP_Port) + RETURN_1];
int len = decrypt_data_symmetric(onion->secret_symmetric_key, packet + 1, packet + 1 + crypto_secretbox_NONCEBYTES,
sizeof(IP_Port) + RETURN_1 + crypto_secretbox_MACBYTES, plain);
@ -327,6 +348,8 @@ static int handle_recv_1(void *object, IP_Port source, uint8_t *packet, uint32_t
if (length <= 1 + RETURN_1)
return 1;
change_symmetric_key(onion);
IP_Port send_to;
int len = decrypt_data_symmetric(onion->secret_symmetric_key, packet + 1, packet + 1 + crypto_secretbox_NONCEBYTES,
@ -358,6 +381,7 @@ Onion *new_onion(DHT *dht)
onion->dht = dht;
onion->net = dht->c->lossless_udp->net;
new_symmetric_key(onion->secret_symmetric_key);
onion->timestamp = unix_time();
networking_registerhandler(onion->net, NET_PACKET_ONION_SEND_INITIAL, &handle_send_initial, onion);
networking_registerhandler(onion->net, NET_PACKET_ONION_SEND_1, &handle_send_1, onion);

View File

@ -29,6 +29,7 @@ typedef struct {
DHT *dht;
Networking_Core *net;
uint8_t secret_symmetric_key[crypto_secretbox_KEYBYTES];
uint64_t timestamp;
} Onion;
#define ONION_RETURN_1 (crypto_secretbox_NONCEBYTES + sizeof(IP_Port) + crypto_secretbox_MACBYTES)

View File

@ -325,7 +325,7 @@ Onion_Announce *new_onion_announce(DHT *dht)
return NULL;
onion_a->dht = dht;
onion_a->net = dht->c->lossless_udp->net;
onion_a->net = dht->net;
new_symmetric_key(onion_a->secret_bytes);
networking_registerhandler(onion_a->net, NET_PACKET_ANNOUNCE_REQUEST, &handle_announce_request, onion_a);

View File

@ -390,6 +390,8 @@ static int handle_fakeid_announce(void *object, uint8_t *source_pubkey, uint8_t
crypto_box_PUBLICKEYBYTES) != 0) {
DHT_delfriend(onion_c->dht, onion_c->friends_list[friend_num].fake_client_id);
onion_c->friends_list[friend_num].last_seen = unix_time();
if (DHT_addfriend(onion_c->dht, data + 1 + sizeof(uint64_t)) == 1) {
return 1;
}
@ -712,6 +714,9 @@ int onion_set_friend_online(Onion_Client *onion_c, int friend_num, uint8_t is_on
if ((uint32_t)friend_num >= onion_c->num_friends)
return -1;
if (is_online == 0 && onion_c->friends_list[friend_num].is_online == 1)
onion_c->friends_list[friend_num].last_seen = unix_time();
onion_c->friends_list[friend_num].is_online = is_online;
/* This should prevent some clock related issues */
@ -767,7 +772,7 @@ static void do_friend(Onion_Client *onion_c, uint16_t friendnum)
}
if (count != MAX_ONION_CLIENTS) {
if (count < rand() % MAX_ONION_CLIENTS) {
if (count < (uint32_t)rand() % MAX_ONION_CLIENTS) {
Node_format nodes_list[MAX_SENT_NODES];
uint32_t num_nodes = get_close_nodes(onion_c->dht, onion_c->friends_list[friendnum].real_client_id, nodes_list,
rand() % 2 ? AF_INET : AF_INET6, 1, 0);
@ -788,6 +793,25 @@ static void do_friend(Onion_Client *onion_c, uint16_t friendnum)
}
}
/* Timeout before which a peer is considered dead and removed from the DHT search. */
#define DEAD_ONION_TIMEOUT (10 * 60)
static void cleanup_friend(Onion_Client *onion_c, uint16_t friendnum)
{
if (friendnum >= onion_c->num_friends)
return;
if (onion_c->friends_list[friendnum].status == 0)
return;
if (onion_c->friends_list[friendnum].is_fake_clientid && !onion_c->friends_list[friendnum].is_online
&& is_timeout(onion_c->friends_list[friendnum].last_seen, DEAD_ONION_TIMEOUT)) {
onion_c->friends_list[friendnum].is_fake_clientid = 0;
DHT_delfriend(onion_c->dht, onion_c->friends_list[friendnum].fake_client_id);
}
}
/* Function to call when onion data packet with contents beginning with byte is received. */
void oniondata_registerhandler(Onion_Client *onion_c, uint8_t byte, oniondata_handler_callback cb, void *object)
{
@ -823,7 +847,7 @@ static void do_announce(Onion_Client *onion_c)
}
if (count != MAX_ONION_CLIENTS) {
if (count < rand() % MAX_ONION_CLIENTS) {
if (count < (uint32_t)rand() % MAX_ONION_CLIENTS) {
Node_format nodes_list[MAX_SENT_NODES];
uint32_t num_nodes = get_close_nodes(onion_c->dht, onion_c->dht->c->self_public_key, nodes_list,
rand() % 2 ? AF_INET : AF_INET6, 1, 0);
@ -845,6 +869,7 @@ void do_onion_client(Onion_Client *onion_c)
for (i = 0; i < onion_c->num_friends; ++i) {
do_friend(onion_c, i);
cleanup_friend(onion_c, i);
}
onion_c->last_run = unix_time();

View File

@ -61,6 +61,8 @@ typedef struct {
uint64_t last_fakeid_dht_sent;
uint64_t last_noreplay;
uint64_t last_seen;
} Onion_Friend;
typedef int (*oniondata_handler_callback)(void *object, uint8_t *source_pubkey, uint8_t *data, uint32_t len);

View File

@ -289,6 +289,13 @@ uint32_t tox_count_friendlist(Tox *tox)
return count_friendlist(m);
}
/* Return the number of online friends in the instance m. */
uint32_t tox_get_num_online_friends(Tox *tox)
{
Messenger *m = tox;
return get_num_online_friends(m);
}
/* Copy a list of valid friend IDs into the array out_list.
* If out_list is NULL, returns 0.
* Otherwise, returns the number of elements copied.

View File

@ -128,6 +128,13 @@ TOX_USERSTATUS;
typedef struct Tox Tox;
#endif
/* NOTE: Strings in Tox are all UTF-8, also the last byte in all strings must be NULL (0).
*
* The length when passing those strings to the core includes that NULL character.
*
* If you send non NULL terminated strings Tox will force NULL terminates them when it receives them.
*/
/* return TOX_FRIEND_ADDRESS_SIZE byte address to give to others.
* format: [client_id (32 bytes)][nospam number (4 bytes)][checksum (2 bytes)]
*/
@ -284,6 +291,9 @@ void tox_set_sends_receipts(Tox *tox, int friendnumber, int yesno);
* for copy_friendlist. */
uint32_t tox_count_friendlist(Tox *tox);
/* Return the number of online friends in the instance m. */
uint32_t tox_get_num_online_friends(Tox *tox);
/* Copy a list of valid friend IDs into the array out_list.
* If out_list is NULL, returns 0.
* Otherwise, returns the number of elements copied.