mirror of
				https://github.com/Tha14/toxic.git
				synced 2025-11-04 17:06:51 +01:00 
			
		
		
		
	Merge new AV branch
This commit is contained in:
		
							
								
								
									
										4
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								Makefile
									
									
									
									
									
								
							@@ -59,6 +59,10 @@ $(BUILD_DIR)/toxic: $(OBJ)
 | 
			
		||||
	@echo "  LD    $(@:$(BUILD_DIR)/%=%)"
 | 
			
		||||
	@$(CC) $(CFLAGS) -o $(BUILD_DIR)/toxic $(OBJ) $(LDFLAGS)
 | 
			
		||||
 | 
			
		||||
$(BUILD_DIR)/osx_video.o: $(SRC_DIR)/$(OSX_VIDEO)
 | 
			
		||||
	@echo "  CC    $(@:$(BUILD_DIR)/)osx_video.o"
 | 
			
		||||
	@$(CC) $(CFLAGS) -o $(BUILD_DIR)/osx_video.o -c $(SRC_DIR)/$(OSX_VIDEO)
 | 
			
		||||
 | 
			
		||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
 | 
			
		||||
	@if [ ! -e $(BUILD_DIR) ]; then \
 | 
			
		||||
		mkdir -p $(BUILD_DIR) ;\
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
# Variables for audio call support
 | 
			
		||||
AUDIO_LIBS = libtoxav openal
 | 
			
		||||
AUDIO_CFLAGS = -DAUDIO
 | 
			
		||||
ifneq (, $(findstring device.o, $(OBJ)))
 | 
			
		||||
ifneq (, $(findstring audio_device.o, $(OBJ)))
 | 
			
		||||
    AUDIO_OBJ = audio_call.o
 | 
			
		||||
else
 | 
			
		||||
    AUDIO_OBJ = audio_call.o device.o
 | 
			
		||||
    AUDIO_OBJ = audio_call.o audio_device.o
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Check if we can build audio support
 | 
			
		||||
@@ -18,4 +18,4 @@ else ifneq ($(MAKECMDGOALS), clean)
 | 
			
		||||
    $(warning WARNING -- Toxic will be compiled without audio support)
 | 
			
		||||
    $(warning WARNING -- You need these libraries for audio support)
 | 
			
		||||
    $(warning WARNING -- $(MISSING_AUDIO_LIBS))
 | 
			
		||||
endif
 | 
			
		||||
endif
 | 
			
		||||
@@ -9,7 +9,15 @@ endif
 | 
			
		||||
# Check if we want build audio support
 | 
			
		||||
AUDIO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
 | 
			
		||||
ifneq ($(AUDIO), disabled)
 | 
			
		||||
    -include $(CHECKS_DIR)/av.mk
 | 
			
		||||
    -include $(CHECKS_DIR)/audio.mk
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Check if we want build video support
 | 
			
		||||
VIDEO = $*shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
 | 
			
		||||
ifneq ($(AUDIO), disabled)
 | 
			
		||||
ifneq ($(VIDEO), disabled)
 | 
			
		||||
    -include $(CHECKS_DIR)/video.mk
 | 
			
		||||
endif
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Check if we want build sound notifications support
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										21
									
								
								cfg/checks/video.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								cfg/checks/video.mk
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
# Variables for video call support
 | 
			
		||||
VIDEO_LIBS = libtoxav vpx x11
 | 
			
		||||
VIDEO_CFLAGS = -DVIDEO
 | 
			
		||||
ifneq (, $(findstring video_device.o, $(OBJ)))
 | 
			
		||||
    VIDEO_OBJ = video_call.o
 | 
			
		||||
else
 | 
			
		||||
    VIDEO_OBJ = video_call.o video_device.o
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Check if we can build video support
 | 
			
		||||
CHECK_VIDEO_LIBS = $(shell pkg-config --exists $(VIDEO_LIBS) || echo -n "error")
 | 
			
		||||
ifneq ($(CHECK_VIDEO_LIBS), error)
 | 
			
		||||
    LIBS += $(VIDEO_LIBS)
 | 
			
		||||
    CFLAGS += $(VIDEO_CFLAGS)
 | 
			
		||||
    OBJ += $(VIDEO_OBJ)
 | 
			
		||||
else ifneq ($(MAKECMDGOALS), clean)
 | 
			
		||||
    MISSING_VIDEO_LIBS = $(shell for lib in $(VIDEO_LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done)
 | 
			
		||||
    $(warning WARNING -- Toxic will be compiled without video support)
 | 
			
		||||
    $(warning WARNING -- You will need these libraries for video support)
 | 
			
		||||
    $(warning WARNING -- $(MISSING_VIDEO_LIBS))
 | 
			
		||||
endif
 | 
			
		||||
@@ -8,3 +8,11 @@ LIBS := $(filter-out ncursesw, $(LIBS))
 | 
			
		||||
# OS X ships a usable, recent version of ncurses, but calls it ncurses not ncursesw.
 | 
			
		||||
LDFLAGS += -lncurses -lalut -ltoxav -ltoxcore -ltoxdns -lresolv -lconfig -ltoxencryptsave -g
 | 
			
		||||
CFLAGS += -I/usr/local/opt/freealut/include/AL -I/usr/local/opt/glib/include/glib-2.0 -g
 | 
			
		||||
 | 
			
		||||
OSX_LIBRARIES = -lobjc -lresolv
 | 
			
		||||
OSX_FRAMEWORKS = -framework Foundation -framework CoreFoundation -framework AVFoundation \
 | 
			
		||||
	-framework QuartzCore -framework CoreMedia
 | 
			
		||||
OSX_VIDEO = osx_video.m
 | 
			
		||||
 | 
			
		||||
LDFLAGS += $(OSX_LIBRARIES) $(OSX_FRAMEWORKS)
 | 
			
		||||
OBJ += osx_video.o
 | 
			
		||||
							
								
								
									
										549
									
								
								src/audio_call.c
									
									
									
									
									
								
							
							
						
						
									
										549
									
								
								src/audio_call.c
									
									
									
									
									
								
							@@ -23,12 +23,16 @@
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "audio_call.h"
 | 
			
		||||
#include "device.h"
 | 
			
		||||
#include "audio_device.h"
 | 
			
		||||
#include "chat_commands.h"
 | 
			
		||||
#include "global_commands.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "notify.h"
 | 
			
		||||
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
#include "video_call.h"
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <curses.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
@@ -51,172 +55,169 @@
 | 
			
		||||
 | 
			
		||||
#define cbend pthread_exit(NULL)
 | 
			
		||||
 | 
			
		||||
#define MAX_CALLS 10
 | 
			
		||||
 | 
			
		||||
#define frame_size (av_DefaultSettings.audio_sample_rate * av_DefaultSettings.audio_frame_duration / 1000)
 | 
			
		||||
#define frame_size (CallControl.audio_sample_rate * CallControl.audio_frame_duration / 1000)
 | 
			
		||||
 | 
			
		||||
static int set_call(Call* call, bool start)
 | 
			
		||||
{
 | 
			
		||||
    call->in_idx = -1;
 | 
			
		||||
    call->out_idx = -1;
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
    call->vin_idx = -1;
 | 
			
		||||
    call->vout_idx = -1;
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
    if ( start ) {
 | 
			
		||||
        call->ttas = true;
 | 
			
		||||
 | 
			
		||||
        if (pthread_mutex_init(&call->mutex, NULL) != 0)
 | 
			
		||||
        if ( pthread_mutex_init(&call->mutex, NULL) != 0 )
 | 
			
		||||
            return -1;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        call->ttid = 0;
 | 
			
		||||
        if (pthread_mutex_destroy(&call->mutex) != 0)
 | 
			
		||||
            return -1;
 | 
			
		||||
        if ( pthread_mutex_destroy(&call->mutex) != 0 )
 | 
			
		||||
               return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ASettings {
 | 
			
		||||
    AudioError errors;
 | 
			
		||||
void call_cb                 ( ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data );
 | 
			
		||||
void callstate_cb            ( ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data );
 | 
			
		||||
void receive_audio_frame_cb  ( ToxAV *av, uint32_t friend_number, int16_t const *pcm, size_t sample_count, 
 | 
			
		||||
                                        uint8_t channels, uint32_t sampling_rate, void *user_data );
 | 
			
		||||
void audio_bit_rate_status_cb( ToxAV *av, uint32_t friend_number, 
 | 
			
		||||
                                        bool stable, uint32_t bit_rate, void *user_data );
 | 
			
		||||
void receive_video_frame_cb  ( ToxAV *av, uint32_t friend_number,
 | 
			
		||||
                                        uint16_t width, uint16_t height,
 | 
			
		||||
                                        uint8_t const *y, uint8_t const *u, uint8_t const *v, uint8_t const *a,
 | 
			
		||||
                                        int32_t ystride, int32_t ustride, int32_t vstride, int32_t astride,
 | 
			
		||||
                                        void *user_data );
 | 
			
		||||
void video_bit_rate_status_cb( ToxAV *av, uint32_t friend_number, 
 | 
			
		||||
                                      bool stable, uint32_t bit_rate, void *user_data);
 | 
			
		||||
 | 
			
		||||
    ToxAv *av;
 | 
			
		||||
void callback_recv_invite   ( uint32_t friend_number );
 | 
			
		||||
void callback_recv_ringing  ( uint32_t friend_number );
 | 
			
		||||
void callback_recv_starting ( uint32_t friend_number );
 | 
			
		||||
void callback_recv_ending   ( uint32_t friend_number );
 | 
			
		||||
void callback_call_started  ( uint32_t friend_number );
 | 
			
		||||
void callback_call_canceled ( uint32_t friend_number );
 | 
			
		||||
void callback_call_rejected ( uint32_t friend_number );
 | 
			
		||||
void callback_call_ended    ( uint32_t friend_number );
 | 
			
		||||
void callback_requ_timeout  ( uint32_t friend_number );
 | 
			
		||||
void callback_peer_timeout  ( uint32_t friend_number );
 | 
			
		||||
void callback_media_change  ( uint32_t friend_number );
 | 
			
		||||
 | 
			
		||||
    ToxAvCSettings cs;
 | 
			
		||||
 | 
			
		||||
    Call calls[MAX_CALLS];
 | 
			
		||||
} ASettins;
 | 
			
		||||
 | 
			
		||||
void callback_recv_invite   ( void* av, int32_t call_index, void *arg );
 | 
			
		||||
void callback_recv_ringing  ( void* av, int32_t call_index, void *arg );
 | 
			
		||||
void callback_recv_starting ( void* av, int32_t call_index, void *arg );
 | 
			
		||||
void callback_recv_ending   ( void* av, int32_t call_index, void *arg );
 | 
			
		||||
void callback_call_started  ( void* av, int32_t call_index, void *arg );
 | 
			
		||||
void callback_call_canceled ( void* av, int32_t call_index, void *arg );
 | 
			
		||||
void callback_call_rejected ( void* av, int32_t call_index, void *arg );
 | 
			
		||||
void callback_call_ended    ( void* av, int32_t call_index, void *arg );
 | 
			
		||||
void callback_requ_timeout  ( void* av, int32_t call_index, void *arg );
 | 
			
		||||
void callback_peer_timeout  ( void* av, int32_t call_index, void *arg );
 | 
			
		||||
void callback_media_change  ( void* av, int32_t call_index, void *arg );
 | 
			
		||||
 | 
			
		||||
void write_device_callback( void* agent, int32_t call_index, const int16_t* PCM, uint16_t size, void* arg );
 | 
			
		||||
void write_device_callback( uint32_t friend_number, const int16_t* PCM, uint16_t size );
 | 
			
		||||
 | 
			
		||||
static void print_err (ToxWindow *self, const char *error_str)
 | 
			
		||||
{
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ToxAv *init_audio(ToxWindow *self, Tox *tox)
 | 
			
		||||
ToxAV *init_audio(ToxWindow *self, Tox *tox)
 | 
			
		||||
{
 | 
			
		||||
    ASettins.cs = av_DefaultSettings;
 | 
			
		||||
    ASettins.cs.max_video_height = ASettins.cs.max_video_width = 0;
 | 
			
		||||
    TOXAV_ERR_NEW error;
 | 
			
		||||
    CallControl.audio_errors = ae_None;
 | 
			
		||||
    CallControl.prompt = self;
 | 
			
		||||
    CallControl.pending_call = false;
 | 
			
		||||
 | 
			
		||||
    ASettins.errors = ae_None;
 | 
			
		||||
    CallControl.av = toxav_new(tox, &error);
 | 
			
		||||
 | 
			
		||||
    memset(ASettins.calls, 0, sizeof(ASettins.calls));
 | 
			
		||||
    CallControl.audio_enabled = true;
 | 
			
		||||
    CallControl.audio_bit_rate = 48;
 | 
			
		||||
    CallControl.audio_sample_rate = 48000;
 | 
			
		||||
    CallControl.audio_frame_duration = 10;
 | 
			
		||||
    CallControl.audio_channels = 1;
 | 
			
		||||
 | 
			
		||||
#ifndef VIDEO
 | 
			
		||||
    CallControl.video_enabled = false;
 | 
			
		||||
    CallControl.video_bit_rate = 0;
 | 
			
		||||
    CallControl.video_frame_duration = 0;
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
    /* Streaming stuff from core */
 | 
			
		||||
    memset(CallControl.calls, 0, sizeof(CallControl.calls));
 | 
			
		||||
 | 
			
		||||
    ASettins.av = toxav_new(tox, MAX_CALLS);
 | 
			
		||||
    if ( !CallControl.av ) {
 | 
			
		||||
        CallControl.audio_errors |= ae_StartingCoreAudio;
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init ToxAV");
 | 
			
		||||
 | 
			
		||||
    if ( !ASettins.av ) {
 | 
			
		||||
        ASettins.errors |= ae_StartingCoreAudio;
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( init_devices(ASettins.av) == de_InternalError ) {
 | 
			
		||||
    if ( init_devices(CallControl.av) == de_InternalError ) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init devices");
 | 
			
		||||
        toxav_kill(ASettins.av);
 | 
			
		||||
        return ASettins.av = NULL;
 | 
			
		||||
        toxav_kill(CallControl.av);
 | 
			
		||||
 | 
			
		||||
        return CallControl.av = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    toxav_register_callstate_callback(ASettins.av, callback_call_started, av_OnStart, self);
 | 
			
		||||
    toxav_register_callstate_callback(ASettins.av, callback_call_canceled, av_OnCancel, self);
 | 
			
		||||
    toxav_register_callstate_callback(ASettins.av, callback_call_rejected, av_OnReject, self);
 | 
			
		||||
    toxav_register_callstate_callback(ASettins.av, callback_call_ended, av_OnEnd, self);
 | 
			
		||||
    toxav_register_callstate_callback(ASettins.av, callback_recv_invite, av_OnInvite, self);
 | 
			
		||||
    toxav_callback_call(CallControl.av, call_cb, &CallControl);
 | 
			
		||||
    toxav_callback_call_state(CallControl.av, callstate_cb, &CallControl);
 | 
			
		||||
    toxav_callback_audio_receive_frame(CallControl.av, receive_audio_frame_cb, &CallControl);
 | 
			
		||||
    toxav_callback_audio_bit_rate_status(CallControl.av, audio_bit_rate_status_cb, &CallControl);
 | 
			
		||||
 | 
			
		||||
    toxav_register_callstate_callback(ASettins.av, callback_recv_ringing, av_OnRinging, self);
 | 
			
		||||
    toxav_register_callstate_callback(ASettins.av, callback_recv_starting, av_OnStart, self);
 | 
			
		||||
    toxav_register_callstate_callback(ASettins.av, callback_recv_ending, av_OnEnd, self);
 | 
			
		||||
 | 
			
		||||
    toxav_register_callstate_callback(ASettins.av, callback_requ_timeout, av_OnRequestTimeout, self);
 | 
			
		||||
    toxav_register_callstate_callback(ASettins.av, callback_peer_timeout, av_OnPeerTimeout, self);
 | 
			
		||||
    //toxav_register_callstate_callback(ASettins.av, callback_media_change, av_OnMediaChange, self);
 | 
			
		||||
 | 
			
		||||
    toxav_register_audio_callback(ASettins.av, write_device_callback, NULL);
 | 
			
		||||
 | 
			
		||||
    return ASettins.av;
 | 
			
		||||
    return CallControl.av;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void terminate_audio()
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    for (i = 0; i < MAX_CALLS; ++i)
 | 
			
		||||
        stop_transmission(&ASettins.calls[i], i);
 | 
			
		||||
        stop_transmission(&CallControl.calls[i], i);
 | 
			
		||||
 | 
			
		||||
    if ( ASettins.av )
 | 
			
		||||
        toxav_kill(ASettins.av);
 | 
			
		||||
    if ( CallControl.av )
 | 
			
		||||
        toxav_kill(CallControl.av);
 | 
			
		||||
 | 
			
		||||
    terminate_devices();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void read_device_callback (const int16_t* captured, uint32_t size, void* data)
 | 
			
		||||
void read_device_callback(const int16_t* captured, uint32_t size, void* data)
 | 
			
		||||
{
 | 
			
		||||
    int32_t call_index = *((int32_t*)data); /* TODO: Or pass an array of call_idx's */
 | 
			
		||||
    TOXAV_ERR_SEND_FRAME error;
 | 
			
		||||
    uint32_t friend_number = *((uint32_t*)data); /* TODO: Or pass an array of call_idx's */
 | 
			
		||||
    int64_t sample_count = CallControl.audio_sample_rate * CallControl.audio_frame_duration / 1000;
 | 
			
		||||
 | 
			
		||||
    uint8_t encoded_payload[RTP_PAYLOAD_SIZE];
 | 
			
		||||
    int32_t payload_size = toxav_prepare_audio_frame(ASettins.av, call_index, encoded_payload, RTP_PAYLOAD_SIZE, captured, size);
 | 
			
		||||
    if ( payload_size <= 0 || toxav_send_audio(ASettins.av, call_index, encoded_payload, payload_size) < 0 ) {
 | 
			
		||||
        /*fprintf(stderr, "Could not encode audio packet\n");*/
 | 
			
		||||
    }
 | 
			
		||||
    if ( sample_count <= 0 || toxav_audio_send_frame(CallControl.av, friend_number, 
 | 
			
		||||
                                                     captured, sample_count, 
 | 
			
		||||
                                                     CallControl.audio_channels, 
 | 
			
		||||
                                                     CallControl.audio_sample_rate, &error) == false )
 | 
			
		||||
    {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void write_device_callback(void *agent, int32_t call_index, const int16_t* PCM, uint16_t size, void* arg)
 | 
			
		||||
void write_device_callback(uint32_t friend_number, const int16_t* PCM, uint16_t size)
 | 
			
		||||
{
 | 
			
		||||
    (void)arg;
 | 
			
		||||
    (void)agent;
 | 
			
		||||
 | 
			
		||||
    if (call_index >= 0 && ASettins.calls[call_index].ttas) {
 | 
			
		||||
        ToxAvCSettings csettings = ASettins.cs;
 | 
			
		||||
        toxav_get_peer_csettings(ASettins.av, call_index, 0, &csettings);
 | 
			
		||||
        write_out(ASettins.calls[call_index].out_idx, PCM, size, csettings.audio_channels);
 | 
			
		||||
    }
 | 
			
		||||
    if ( CallControl.calls[friend_number].ttas )
 | 
			
		||||
        write_out(CallControl.calls[friend_number].out_idx, PCM, size, CallControl.audio_channels);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int start_transmission(ToxWindow *self, Call *call)
 | 
			
		||||
{
 | 
			
		||||
    if ( !self || !ASettins.av || self->call_idx == -1 ) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Could not prepare transmission");
 | 
			
		||||
    if ( !self || !CallControl.av ) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare transmission");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Don't provide support for video */
 | 
			
		||||
    if ( 0 != toxav_prepare_transmission(ASettins.av, self->call_idx, 0) ) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Could not prepare transmission");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( !toxav_capability_supported(ASettins.av, self->call_idx, av_AudioDecoding) ||
 | 
			
		||||
         !toxav_capability_supported(ASettins.av, self->call_idx, av_AudioEncoding) )
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    if (set_call(call, true) == -1)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    ToxAvCSettings csettings;
 | 
			
		||||
    toxav_get_peer_csettings(ASettins.av, self->call_idx, 0, &csettings);
 | 
			
		||||
    DeviceError error = open_primary_device(input, &call->in_idx,
 | 
			
		||||
            CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels);
 | 
			
		||||
 | 
			
		||||
    if ( open_primary_device(input, &call->in_idx,
 | 
			
		||||
            csettings.audio_sample_rate, csettings.audio_frame_duration, csettings.audio_channels) != de_None )
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open input device!");
 | 
			
		||||
    if ( error != de_None ) {
 | 
			
		||||
        if ( error == de_FailedStart)
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to start input device");
 | 
			
		||||
 | 
			
		||||
    if ( register_device_callback(self->call_idx, call->in_idx,
 | 
			
		||||
         read_device_callback, &self->call_idx, true) != de_None)
 | 
			
		||||
        if ( error == de_InternalError )
 | 
			
		||||
            line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Internal error with opening input device");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( register_device_callback(self->num, call->in_idx,
 | 
			
		||||
            read_device_callback, &self->num, true) != de_None)
 | 
			
		||||
        /* Set VAD as true for all; TODO: Make it more dynamic */
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to register input handler!");
 | 
			
		||||
 | 
			
		||||
    if ( open_primary_device(output, &call->out_idx,
 | 
			
		||||
            csettings.audio_sample_rate, csettings.audio_frame_duration, csettings.audio_channels) != de_None ) {
 | 
			
		||||
            CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels) != de_None ) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open output device!");
 | 
			
		||||
        call->has_output = 0;
 | 
			
		||||
    }
 | 
			
		||||
@@ -224,22 +225,31 @@ int start_transmission(ToxWindow *self, Call *call)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int stop_transmission(Call *call, int32_t call_index)
 | 
			
		||||
int stop_transmission(Call *call, uint32_t friend_number)
 | 
			
		||||
{
 | 
			
		||||
    if ( call->ttas ) {
 | 
			
		||||
        toxav_kill_transmission(ASettins.av, call_index);
 | 
			
		||||
        call->ttas = false;
 | 
			
		||||
        TOXAV_ERR_CALL_CONTROL error = TOXAV_ERR_CALL_CONTROL_OK;
 | 
			
		||||
 | 
			
		||||
        if ( call->in_idx != -1 )
 | 
			
		||||
            close_device(input, call->in_idx);
 | 
			
		||||
        if ( CallControl.call_state != TOXAV_FRIEND_CALL_STATE_FINISHED )
 | 
			
		||||
            toxav_call_control(CallControl.av, friend_number, TOXAV_CALL_CONTROL_CANCEL, &error); 
 | 
			
		||||
 | 
			
		||||
        if ( call->out_idx != -1 )
 | 
			
		||||
            close_device(output, call->out_idx);
 | 
			
		||||
        if ( error == TOXAV_ERR_CALL_CONTROL_OK ) {
 | 
			
		||||
            call->ttas = false;
 | 
			
		||||
 | 
			
		||||
            if ( call->in_idx != -1 )
 | 
			
		||||
                close_device(input, call->in_idx);
 | 
			
		||||
 | 
			
		||||
            if ( call->out_idx != -1 )
 | 
			
		||||
                close_device(output, call->out_idx);
 | 
			
		||||
 | 
			
		||||
            if ( set_call(call, false) == -1 )
 | 
			
		||||
                return -1;
 | 
			
		||||
 | 
			
		||||
            return 0;
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
        if (set_call(call, false) == -1)
 | 
			
		||||
            return -1;
 | 
			
		||||
 | 
			
		||||
        return 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return -1;
 | 
			
		||||
@@ -255,82 +265,158 @@ int stop_transmission(Call *call, int32_t call_index)
 | 
			
		||||
/*
 | 
			
		||||
 * Callbacks
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define CB_BODY(call_idx, Arg, onFunc) do { ToxWindow* windows = (Arg); int i;\
 | 
			
		||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) if (windows[i].onFunc != NULL) windows[i].onFunc(&windows[i], ASettins.av, call_idx); } while (0)
 | 
			
		||||
 | 
			
		||||
void callback_recv_invite ( void* av, int32_t call_index, void* arg )
 | 
			
		||||
void call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
    CB_BODY(call_index, arg, onInvite);
 | 
			
		||||
    CallControl.pending_call = true;
 | 
			
		||||
    callback_recv_invite(friend_number);
 | 
			
		||||
}
 | 
			
		||||
void callback_recv_ringing ( void* av, int32_t call_index, void* arg )
 | 
			
		||||
 | 
			
		||||
void callstate_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
    CB_BODY(call_index, arg, onRinging);
 | 
			
		||||
}
 | 
			
		||||
void callback_recv_starting ( void* av, int32_t call_index, void* arg )
 | 
			
		||||
{
 | 
			
		||||
    ToxWindow* windows = arg;
 | 
			
		||||
    int i;
 | 
			
		||||
    for (i = 0; i < MAX_WINDOWS_NUM; ++i)
 | 
			
		||||
        if (windows[i].onStarting != NULL && windows[i].call_idx == call_index) {
 | 
			
		||||
            windows[i].onStarting(&windows[i], ASettins.av, call_index);
 | 
			
		||||
            if ( 0 != start_transmission(&windows[i], &ASettins.calls[call_index])) {/* YEAH! */
 | 
			
		||||
                line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0 , "Error starting transmission!");
 | 
			
		||||
    CallControl.call_state = state;
 | 
			
		||||
 | 
			
		||||
    switch ( state ) {
 | 
			
		||||
        case ( TOXAV_FRIEND_CALL_STATE_ERROR ):
 | 
			
		||||
            line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "ToxAV callstate error!");
 | 
			
		||||
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
            callback_video_end(friend_number);
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
            stop_transmission(&CallControl.calls[friend_number], friend_number);
 | 
			
		||||
            callback_call_ended(friend_number);
 | 
			
		||||
            CallControl.pending_call = false;
 | 
			
		||||
 | 
			
		||||
        break;
 | 
			
		||||
        case ( TOXAV_FRIEND_CALL_STATE_FINISHED ):
 | 
			
		||||
            if ( CallControl.pending_call )
 | 
			
		||||
                callback_call_rejected(friend_number);
 | 
			
		||||
            else
 | 
			
		||||
                callback_call_ended(friend_number);
 | 
			
		||||
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
            callback_recv_video_end(friend_number);
 | 
			
		||||
            callback_video_end(friend_number);
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
            stop_transmission(&CallControl.calls[friend_number], friend_number);
 | 
			
		||||
 | 
			
		||||
            /* Reset stored call state after finishing */
 | 
			
		||||
            CallControl.call_state = 0;
 | 
			
		||||
            CallControl.pending_call = false;
 | 
			
		||||
 | 
			
		||||
        break;
 | 
			
		||||
        default:
 | 
			
		||||
            if ( CallControl.pending_call ) {
 | 
			
		||||
                /* Start answered call */
 | 
			
		||||
                callback_call_started(friend_number);
 | 
			
		||||
                CallControl.pending_call = false;
 | 
			
		||||
 | 
			
		||||
            } else {
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
                /* Handle receiving client video call states */
 | 
			
		||||
                if ( state & TOXAV_FRIEND_CALL_STATE_SENDING_V )
 | 
			
		||||
                    callback_recv_video_starting(friend_number);
 | 
			
		||||
                else if ( state & ~TOXAV_FRIEND_CALL_STATE_SENDING_V )
 | 
			
		||||
                    callback_recv_video_end(friend_number);
 | 
			
		||||
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void receive_audio_frame_cb(ToxAV *av, uint32_t friend_number, 
 | 
			
		||||
                                    int16_t const *pcm, size_t sample_count, 
 | 
			
		||||
                                    uint8_t channels, uint32_t sampling_rate, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
    write_device_callback(friend_number, pcm, frame_size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void audio_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, 
 | 
			
		||||
                                    bool stable, uint32_t bit_rate, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
    if ( stable )
 | 
			
		||||
        CallControl.audio_bit_rate = bit_rate;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define CB_BODY(friend_number, onFunc) do { ToxWindow* windows = CallControl.prompt; int i;\
 | 
			
		||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) if ( windows[i].onFunc != NULL && windows[i].num == friend_number )\
 | 
			
		||||
windows[i].onFunc(&windows[i], CallControl.av, friend_number, CallControl.call_state); } while (0)
 | 
			
		||||
 | 
			
		||||
void callback_recv_invite(uint32_t friend_number)
 | 
			
		||||
{
 | 
			
		||||
    CB_BODY(friend_number, onInvite);
 | 
			
		||||
}
 | 
			
		||||
void callback_recv_ringing(uint32_t friend_number)
 | 
			
		||||
{
 | 
			
		||||
    CB_BODY(friend_number, onRinging);
 | 
			
		||||
}
 | 
			
		||||
void callback_recv_starting(uint32_t friend_number)
 | 
			
		||||
{
 | 
			
		||||
    ToxWindow* windows = CallControl.prompt;
 | 
			
		||||
    
 | 
			
		||||
    int i;
 | 
			
		||||
    for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
 | 
			
		||||
        if ( windows[i].onStarting != NULL && windows[i].num == friend_number ) {
 | 
			
		||||
            windows[i].onStarting(&windows[i], CallControl.av, friend_number, CallControl.call_state);
 | 
			
		||||
            if ( 0 != start_transmission(&windows[i], &CallControl.calls[friend_number]) ) /* YEAH! */
 | 
			
		||||
                line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0 , "Error starting transmission!");
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
void callback_recv_ending ( void* av, int32_t call_index, void* arg )
 | 
			
		||||
void callback_recv_ending(uint32_t friend_number)
 | 
			
		||||
{
 | 
			
		||||
    CB_BODY(call_index, arg, onEnding);
 | 
			
		||||
    stop_transmission(&ASettins.calls[call_index], call_index);
 | 
			
		||||
    CB_BODY(friend_number, onEnding);
 | 
			
		||||
}
 | 
			
		||||
void callback_call_started(uint32_t friend_number)
 | 
			
		||||
{
 | 
			
		||||
    ToxWindow* windows = CallControl.prompt;
 | 
			
		||||
 | 
			
		||||
void callback_call_started ( void* av, int32_t call_index, void* arg )
 | 
			
		||||
{
 | 
			
		||||
    ToxWindow* windows = arg;
 | 
			
		||||
    int i;
 | 
			
		||||
    for (i = 0; i < MAX_WINDOWS_NUM; ++i)
 | 
			
		||||
        if (windows[i].onStart != NULL && windows[i].call_idx == call_index) {
 | 
			
		||||
            windows[i].onStart(&windows[i], ASettins.av, call_index);
 | 
			
		||||
            if ( 0 != start_transmission(&windows[i], &ASettins.calls[call_index]) ) {/* YEAH! */
 | 
			
		||||
        if ( windows[i].onStart != NULL && windows[i].num == friend_number ) {
 | 
			
		||||
            windows[i].onStart(&windows[i], CallControl.av, friend_number, CallControl.call_state);
 | 
			
		||||
            if ( 0 != start_transmission(&windows[i], &CallControl.calls[friend_number]) ) {/* YEAH! */
 | 
			
		||||
                line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0, "Error starting transmission!");
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
}
 | 
			
		||||
void callback_call_canceled ( void* av, int32_t call_index, void* arg )
 | 
			
		||||
void callback_call_canceled(uint32_t friend_number)
 | 
			
		||||
{
 | 
			
		||||
    CB_BODY(call_index, arg, onCancel);
 | 
			
		||||
    CB_BODY(friend_number, onCancel);
 | 
			
		||||
}
 | 
			
		||||
void callback_call_rejected(uint32_t friend_number)
 | 
			
		||||
{
 | 
			
		||||
    CB_BODY(friend_number, onReject);
 | 
			
		||||
}
 | 
			
		||||
void callback_call_ended(uint32_t friend_number)
 | 
			
		||||
{
 | 
			
		||||
    CB_BODY(friend_number, onEnd);   
 | 
			
		||||
}
 | 
			
		||||
void callback_requ_timeout(uint32_t friend_number)
 | 
			
		||||
{
 | 
			
		||||
    CB_BODY(friend_number, onRequestTimeout);
 | 
			
		||||
}
 | 
			
		||||
void callback_peer_timeout(uint32_t friend_number)
 | 
			
		||||
{
 | 
			
		||||
    CB_BODY(friend_number, onPeerTimeout);
 | 
			
		||||
 | 
			
		||||
    /* In case call is active */
 | 
			
		||||
    stop_transmission(&ASettins.calls[call_index], call_index);
 | 
			
		||||
}
 | 
			
		||||
void callback_call_rejected ( void* av, int32_t call_index, void* arg )
 | 
			
		||||
{
 | 
			
		||||
    CB_BODY(call_index, arg, onReject);
 | 
			
		||||
}
 | 
			
		||||
void callback_call_ended ( void* av, int32_t call_index, void* arg )
 | 
			
		||||
{
 | 
			
		||||
    CB_BODY(call_index, arg, onEnd);
 | 
			
		||||
    stop_transmission(&ASettins.calls[call_index], call_index);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void callback_requ_timeout ( void* av, int32_t call_index, void* arg )
 | 
			
		||||
{
 | 
			
		||||
    CB_BODY(call_index, arg, onRequestTimeout);
 | 
			
		||||
}
 | 
			
		||||
void callback_peer_timeout ( void* av, int32_t call_index, void* arg )
 | 
			
		||||
{
 | 
			
		||||
    CB_BODY(call_index, arg, onPeerTimeout);
 | 
			
		||||
    stop_transmission(&ASettins.calls[call_index], call_index);
 | 
			
		||||
    callback_video_end(friend_number);
 | 
			
		||||
    callback_recv_video_end(friend_number);
 | 
			
		||||
    stop_transmission(&CallControl.calls[friend_number], friend_number);
 | 
			
		||||
    /* Call is stopped manually since there might be some other
 | 
			
		||||
     * actions that one can possibly take on timeout
 | 
			
		||||
     */
 | 
			
		||||
    toxav_stop_call(ASettins.av, call_index);
 | 
			
		||||
     toxav_call_control(CallControl.av, friend_number, TOXAV_CALL_CONTROL_CANCEL, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// void callback_media_change(void* av, int32_t call_index, void* arg)
 | 
			
		||||
// void callback_media_change(void* av, uint32_t friend_number, void* arg)
 | 
			
		||||
// {
 | 
			
		||||
  /*... TODO cancel all media change requests */
 | 
			
		||||
// }
 | 
			
		||||
@@ -345,33 +431,42 @@ void callback_peer_timeout ( void* av, int32_t call_index, void* arg )
 | 
			
		||||
 */
 | 
			
		||||
void cmd_call(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    TOXAV_ERR_CALL error;
 | 
			
		||||
    const char *error_str;
 | 
			
		||||
 | 
			
		||||
    if (argc != 0) {
 | 
			
		||||
    if ( argc != 0 ) {
 | 
			
		||||
        error_str = "Unknown arguments.";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( !ASettins.av ) {
 | 
			
		||||
        error_str = "Audio not supported!";
 | 
			
		||||
    if ( !CallControl.av ) {
 | 
			
		||||
        error_str = "ToxAV not supported!";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!self->stb->connection) {
 | 
			
		||||
    if ( !self->stb->connection ) {
 | 
			
		||||
        error_str = "Friend is offline.";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ToxAvError error = toxav_call(ASettins.av, &self->call_idx, self->num, &ASettins.cs, 30);
 | 
			
		||||
    if ( CallControl.pending_call ) {
 | 
			
		||||
        error_str = "Already a pending call!";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( error != av_ErrorNone ) {
 | 
			
		||||
        if ( error == av_ErrorAlreadyInCallWithPeer ) error_str = "Already in a call!";
 | 
			
		||||
    toxav_call(CallControl.av, self->num, CallControl.audio_bit_rate, CallControl.video_bit_rate, &error);
 | 
			
		||||
    if ( error != TOXAV_ERR_CALL_OK ) {
 | 
			
		||||
        if ( error == TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL ) error_str = "Already in a call!";
 | 
			
		||||
        else if ( error == TOXAV_ERR_CALL_MALLOC ) error_str = "Memory allocation issue";
 | 
			
		||||
        else if ( error == TOXAV_ERR_CALL_FRIEND_NOT_FOUND ) error_str = "Friend number invalid";
 | 
			
		||||
        else if ( error == TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED ) error_str = "Friend is valid but not currently connected";
 | 
			
		||||
        else error_str = "Internal error!";
 | 
			
		||||
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Calling... idx: %d", self->call_idx);
 | 
			
		||||
    CallControl.pending_call = true;
 | 
			
		||||
    callback_recv_ringing(self->num);
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
on_error:
 | 
			
		||||
@@ -380,29 +475,38 @@ on_error:
 | 
			
		||||
 | 
			
		||||
void cmd_answer(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    TOXAV_ERR_ANSWER error;
 | 
			
		||||
    const char *error_str;
 | 
			
		||||
 | 
			
		||||
    if (argc != 0) {
 | 
			
		||||
    if ( argc != 0 ) {
 | 
			
		||||
        error_str = "Unknown arguments.";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( !ASettins.av ) {
 | 
			
		||||
    if ( !CallControl.av ) {
 | 
			
		||||
        error_str = "Audio not supported!";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ToxAvError error = toxav_answer(ASettins.av, self->call_idx, &ASettins.cs);
 | 
			
		||||
    if ( !CallControl.pending_call ) {
 | 
			
		||||
        error_str = "No incoming call!";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( error != av_ErrorNone ) {
 | 
			
		||||
        if ( error == av_ErrorInvalidState ) error_str = "Cannot answer in invalid state!";
 | 
			
		||||
        else if ( error == av_ErrorNoCall ) error_str = "No incoming call!";
 | 
			
		||||
    toxav_answer(CallControl.av, self->num, CallControl.audio_bit_rate, CallControl.video_bit_rate, &error);
 | 
			
		||||
    if ( error != TOXAV_ERR_ANSWER_OK ) {
 | 
			
		||||
        if ( error == TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING ) error_str = "No incoming call!";
 | 
			
		||||
        else if ( error == TOXAV_ERR_ANSWER_CODEC_INITIALIZATION ) error_str = "Failed to initialize codecs!";
 | 
			
		||||
        else if ( error == TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND ) error_str = "Friend not found!";
 | 
			
		||||
        else if ( error == TOXAV_ERR_ANSWER_INVALID_BIT_RATE ) error_str = "Invalid bit rate!";
 | 
			
		||||
        else error_str = "Internal error!";
 | 
			
		||||
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Callback will print status... */
 | 
			
		||||
    callback_recv_starting(self->num);
 | 
			
		||||
    CallControl.pending_call = false;
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
on_error:
 | 
			
		||||
@@ -413,27 +517,27 @@ void cmd_reject(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
 | 
			
		||||
{
 | 
			
		||||
    const char *error_str;
 | 
			
		||||
 | 
			
		||||
    if (argc != 0) {
 | 
			
		||||
    if ( argc != 0 ) {
 | 
			
		||||
        error_str = "Unknown arguments.";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( !ASettins.av ) {
 | 
			
		||||
    if ( !CallControl.av ) {
 | 
			
		||||
        error_str = "Audio not supported!";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ToxAvError error = toxav_reject(ASettins.av, self->call_idx, "Why not?");
 | 
			
		||||
 | 
			
		||||
    if ( error != av_ErrorNone ) {
 | 
			
		||||
        if ( error == av_ErrorInvalidState ) error_str = "Cannot reject in invalid state!";
 | 
			
		||||
        else if ( error == av_ErrorNoCall ) error_str = "No incoming call!";
 | 
			
		||||
        else error_str = "Internal error!";
 | 
			
		||||
 | 
			
		||||
    if ( !CallControl.pending_call ) {
 | 
			
		||||
        error_str = "No incoming call!";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Manually send a cancel call control because call hasn't started */
 | 
			
		||||
    toxav_call_control(CallControl.av, self->num, TOXAV_CALL_CONTROL_CANCEL, NULL); 
 | 
			
		||||
    CallControl.pending_call = false;
 | 
			
		||||
 | 
			
		||||
    /* Callback will print status... */
 | 
			
		||||
    callback_call_rejected(self->num);
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
on_error:
 | 
			
		||||
@@ -444,36 +548,34 @@ void cmd_hangup(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
 | 
			
		||||
{
 | 
			
		||||
    const char *error_str;
 | 
			
		||||
 | 
			
		||||
    if (argc != 0) {
 | 
			
		||||
    if ( argc != 0 ) {
 | 
			
		||||
        error_str = "Unknown arguments.";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( !ASettins.av ) {
 | 
			
		||||
    if ( !CallControl.av ) {
 | 
			
		||||
        error_str = "Audio not supported!";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ToxAvError error;
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
    callback_video_end(self->num);
 | 
			
		||||
 | 
			
		||||
    if (toxav_get_call_state(ASettins.av, self->call_idx) == av_CallInviting) {
 | 
			
		||||
        error = toxav_cancel(ASettins.av, self->call_idx, self->num,
 | 
			
		||||
                                        "Only those who appreciate small things know the beauty that is life");
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
        stop_sound(self->ringing_sound);
 | 
			
		||||
#endif
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call canceled!");
 | 
			
		||||
    } else {
 | 
			
		||||
        error = toxav_hangup(ASettins.av, self->call_idx);
 | 
			
		||||
#endif /* VIDEO */ 
 | 
			
		||||
 | 
			
		||||
   
 | 
			
		||||
 | 
			
		||||
    if ( CallControl.pending_call ) {
 | 
			
		||||
        /* Manually send a cancel call control because call hasn't started */
 | 
			
		||||
        toxav_call_control(CallControl.av, self->num, TOXAV_CALL_CONTROL_CANCEL, NULL); 
 | 
			
		||||
        callback_call_canceled(self->num);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        stop_transmission(&CallControl.calls[self->num], self->num);
 | 
			
		||||
        callback_call_ended(self->num);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( error != av_ErrorNone ) {
 | 
			
		||||
        if ( error == av_ErrorInvalidState ) error_str = "Cannot hangup in invalid state!";
 | 
			
		||||
        else if ( error == av_ErrorNoCall ) error_str = "No call!";
 | 
			
		||||
        else error_str = "Internal error!";
 | 
			
		||||
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
    CallControl.pending_call = false;
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
on_error:
 | 
			
		||||
@@ -596,28 +698,26 @@ void cmd_ccur_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* If call is active, change device */
 | 
			
		||||
    if ( self->call_idx > -1) {
 | 
			
		||||
        Call* this_call = &ASettins.calls[self->call_idx];
 | 
			
		||||
        if (this_call->ttas) {
 | 
			
		||||
    if ( self->is_call ) {
 | 
			
		||||
        Call* this_call = &CallControl.calls[self->num];
 | 
			
		||||
        if ( this_call->ttas ) {
 | 
			
		||||
 | 
			
		||||
            ToxAvCSettings csettings;
 | 
			
		||||
            toxav_get_peer_csettings(ASettins.av, self->call_idx, 0, &csettings);
 | 
			
		||||
 | 
			
		||||
            if (type == output) {
 | 
			
		||||
            if ( type == output ) {
 | 
			
		||||
                pthread_mutex_lock(&this_call->mutex);
 | 
			
		||||
                close_device(output, this_call->out_idx);
 | 
			
		||||
                this_call->has_output = open_device(output, selection, &this_call->out_idx,
 | 
			
		||||
                    csettings.audio_sample_rate, csettings.audio_frame_duration, csettings.audio_channels)
 | 
			
		||||
                    CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels)
 | 
			
		||||
                    == de_None ? 1 : 0;
 | 
			
		||||
                pthread_mutex_unlock(&this_call->mutex);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                /* TODO: check for failure */
 | 
			
		||||
                close_device(input, this_call->in_idx);
 | 
			
		||||
                open_device(input, selection, &this_call->in_idx, csettings.audio_sample_rate,
 | 
			
		||||
                    csettings.audio_frame_duration, csettings.audio_channels);
 | 
			
		||||
                open_device(input, selection, &this_call->in_idx, CallControl.audio_sample_rate,
 | 
			
		||||
                    CallControl.audio_frame_duration, CallControl.audio_channels);
 | 
			
		||||
                /* Set VAD as true for all; TODO: Make it more dynamic */
 | 
			
		||||
                register_device_callback(self->call_idx, this_call->in_idx, read_device_callback, &self->call_idx, true);
 | 
			
		||||
                register_device_callback(self->num, this_call->in_idx, read_device_callback, &self->num, true);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -655,11 +755,11 @@ void cmd_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /* If call is active, use this_call values */
 | 
			
		||||
    if ( self->call_idx > -1) {
 | 
			
		||||
        Call* this_call = &ASettins.calls[self->call_idx];
 | 
			
		||||
    if ( self->is_call ) {
 | 
			
		||||
        Call* this_call = &CallControl.calls[self->num];
 | 
			
		||||
 | 
			
		||||
        pthread_mutex_lock(&this_call->mutex);
 | 
			
		||||
        if (type == input) {
 | 
			
		||||
        if ( type == input ) {
 | 
			
		||||
            device_mute(type, this_call->in_idx);
 | 
			
		||||
            self->chatwin->infobox.in_is_muted ^= 1;
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -695,8 +795,8 @@ void cmd_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[M
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Call must be active */
 | 
			
		||||
    if ( self->call_idx > -1) {
 | 
			
		||||
        device_set_VAD_treshold(ASettins.calls[self->call_idx].in_idx, value);
 | 
			
		||||
    if ( self->is_call ) {
 | 
			
		||||
        device_set_VAD_treshold(CallControl.calls[self->num].in_idx, value);
 | 
			
		||||
        self->chatwin->infobox.vad_lvl = value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -709,23 +809,6 @@ on_error:
 | 
			
		||||
 | 
			
		||||
void stop_current_call(ToxWindow* self)
 | 
			
		||||
{
 | 
			
		||||
    ToxAvCallState callstate;
 | 
			
		||||
    if ( ASettins.av != NULL && self->call_idx != -1 &&
 | 
			
		||||
       ( callstate = toxav_get_call_state(ASettins.av, self->call_idx) ) != av_CallNonExistent) {
 | 
			
		||||
        switch (callstate)
 | 
			
		||||
        {
 | 
			
		||||
        case av_CallActive:
 | 
			
		||||
        case av_CallHold:
 | 
			
		||||
            toxav_hangup(ASettins.av, self->call_idx);
 | 
			
		||||
            break;
 | 
			
		||||
        case av_CallInviting:
 | 
			
		||||
            toxav_cancel(ASettins.av, self->call_idx, 0, "Not interested anymore");
 | 
			
		||||
            break;
 | 
			
		||||
        case av_CallStarting:
 | 
			
		||||
            toxav_reject(ASettins.av, self->call_idx, "Not interested");
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    TOXAV_ERR_CALL_CONTROL error;
 | 
			
		||||
    toxav_call_control(CallControl.av, self->num, TOXAV_CALL_CONTROL_CANCEL, &error);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,12 +20,14 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef AUDIO_H
 | 
			
		||||
#define AUDIO_H
 | 
			
		||||
#ifndef AUDIO_CALL_H
 | 
			
		||||
#define AUDIO_CALL_H
 | 
			
		||||
 | 
			
		||||
#include <tox/toxav.h>
 | 
			
		||||
 | 
			
		||||
#include "device.h"
 | 
			
		||||
#include "audio_device.h"
 | 
			
		||||
 
 | 
			
		||||
#define MAX_CALLS 10
 | 
			
		||||
 | 
			
		||||
typedef enum _AudioError {
 | 
			
		||||
    ae_None = 0,
 | 
			
		||||
@@ -34,18 +36,61 @@ typedef enum _AudioError {
 | 
			
		||||
    ae_StartingCoreAudio = 1 << 2
 | 
			
		||||
} AudioError;
 | 
			
		||||
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
typedef enum _VideoError {
 | 
			
		||||
    ve_None = 0,
 | 
			
		||||
    ve_StartingCaptureDevice = 1 << 0,
 | 
			
		||||
    ve_StartingOutputDevice = 1 << 1,
 | 
			
		||||
    ve_StartingCoreVideo = 1 << 2
 | 
			
		||||
} VideoError;
 | 
			
		||||
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
typedef struct Call {
 | 
			
		||||
    pthread_t ttid; /* Transmission thread id */
 | 
			
		||||
    bool ttas, has_output; /* Transmission thread active status (0 - stopped, 1- running) */
 | 
			
		||||
    uint32_t in_idx, out_idx;
 | 
			
		||||
    uint32_t in_idx, out_idx; /* Audio Index */
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
    uint32_t vin_idx, vout_idx; /* Video Index */
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
    pthread_mutex_t mutex;
 | 
			
		||||
} Call;
 | 
			
		||||
 | 
			
		||||
struct CallControl {
 | 
			
		||||
    AudioError audio_errors;
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
    VideoError video_errors;
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
    ToxAV *av;
 | 
			
		||||
    ToxWindow *prompt;
 | 
			
		||||
 | 
			
		||||
    Call calls[MAX_CALLS];
 | 
			
		||||
    uint32_t call_state;
 | 
			
		||||
    bool pending_call;
 | 
			
		||||
    bool audio_enabled;
 | 
			
		||||
    bool video_enabled;
 | 
			
		||||
 | 
			
		||||
    uint32_t audio_bit_rate;
 | 
			
		||||
    int32_t audio_frame_duration;
 | 
			
		||||
    uint32_t audio_sample_rate;
 | 
			
		||||
    uint8_t audio_channels;
 | 
			
		||||
    
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
    uint32_t video_bit_rate;
 | 
			
		||||
    int32_t video_frame_duration;
 | 
			
		||||
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
} CallControl;
 | 
			
		||||
 | 
			
		||||
struct CallControl CallControl;
 | 
			
		||||
 | 
			
		||||
/* You will have to pass pointer to first member of 'windows' declared in windows.c */
 | 
			
		||||
ToxAv *init_audio(ToxWindow *self, Tox *tox);
 | 
			
		||||
ToxAV *init_audio(ToxWindow *self, Tox *tox);
 | 
			
		||||
void terminate_audio();
 | 
			
		||||
int start_transmission(ToxWindow *self, Call *call);
 | 
			
		||||
int stop_transmission(Call *call, int call_index);
 | 
			
		||||
int stop_transmission(Call *call, uint32_t friend_number);
 | 
			
		||||
void stop_current_call(ToxWindow *self);
 | 
			
		||||
 | 
			
		||||
#endif /* AUDIO_H */
 | 
			
		||||
#endif /* AUDIO_CALL_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
/*  device.c
 | 
			
		||||
/*  audio_device.c
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2014 Toxic All Rights Reserved.
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "device.h"
 | 
			
		||||
#include "audio_device.h"
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
#include "audio_call.h"
 | 
			
		||||
@@ -57,7 +57,7 @@ typedef struct Device {
 | 
			
		||||
    ALCcontext *ctx;                       /* Device context */
 | 
			
		||||
    DataHandleCallback cb;                 /* Use this to handle data from input device usually */
 | 
			
		||||
    void* cb_data;                         /* Data to be passed to callback */
 | 
			
		||||
    int32_t call_idx;                      /* ToxAv call index */
 | 
			
		||||
    int32_t friend_number;                      /* ToxAV friend number */
 | 
			
		||||
 | 
			
		||||
    uint32_t source, buffers[OPENAL_BUFS]; /* Playback source/buffers */
 | 
			
		||||
    uint32_t ref_count;
 | 
			
		||||
@@ -80,7 +80,7 @@ Device *running[2][MAX_DEVICES] = {{NULL}};     /* Running devices */
 | 
			
		||||
uint32_t primary_device[2];          /* Primary device */
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
static ToxAv* av = NULL;
 | 
			
		||||
static ToxAV* av = NULL;
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
/* q_mutex */
 | 
			
		||||
@@ -95,7 +95,7 @@ bool thread_running = true,
 | 
			
		||||
void* thread_poll(void*);
 | 
			
		||||
/* Meet devices */
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
DeviceError init_devices(ToxAv* av_)
 | 
			
		||||
DeviceError init_devices(ToxAV* av_)
 | 
			
		||||
#else
 | 
			
		||||
DeviceError init_devices()
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
@@ -342,7 +342,7 @@ DeviceError close_device(DeviceType type, uint32_t device_idx)
 | 
			
		||||
    return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DeviceError register_device_callback( int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD)
 | 
			
		||||
DeviceError register_device_callback( int32_t friend_number, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD)
 | 
			
		||||
{
 | 
			
		||||
    if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
 | 
			
		||||
        return de_InvalidSelection;
 | 
			
		||||
@@ -351,7 +351,7 @@ DeviceError register_device_callback( int32_t call_idx, uint32_t device_idx, Dat
 | 
			
		||||
    running[input][device_idx]->cb = callback;
 | 
			
		||||
    running[input][device_idx]->cb_data = data;
 | 
			
		||||
    running[input][device_idx]->enable_VAD = enable_VAD;
 | 
			
		||||
    running[input][device_idx]->call_idx = call_idx;
 | 
			
		||||
    running[input][device_idx]->friend_number = friend_number;
 | 
			
		||||
    unlock;
 | 
			
		||||
 | 
			
		||||
    return de_None;
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
/*  device.h
 | 
			
		||||
/*  audio_device.h
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2014 Toxic All Rights Reserved.
 | 
			
		||||
@@ -26,8 +26,8 @@
 | 
			
		||||
 * Read from running input device(s) via select()/callback combo.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef DEVICE_H
 | 
			
		||||
#define DEVICE_H
 | 
			
		||||
#ifndef AUDIO_DEVICE_H
 | 
			
		||||
#define AUDIO_DEVICE_H
 | 
			
		||||
 | 
			
		||||
#define OPENAL_BUFS 5
 | 
			
		||||
#define MAX_DEVICES 32
 | 
			
		||||
@@ -56,7 +56,7 @@ typedef void (*DataHandleCallback) (const int16_t*, uint32_t size, void* data);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
DeviceError init_devices(ToxAv* av);
 | 
			
		||||
DeviceError init_devices(ToxAV* av);
 | 
			
		||||
#else
 | 
			
		||||
DeviceError init_devices();
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
@@ -64,7 +64,7 @@ DeviceError init_devices();
 | 
			
		||||
DeviceError terminate_devices();
 | 
			
		||||
 | 
			
		||||
/* Callback handles ready data from INPUT device */
 | 
			
		||||
DeviceError register_device_callback(int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD);
 | 
			
		||||
DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD);
 | 
			
		||||
void* get_device_callback_data(uint32_t device_idx);
 | 
			
		||||
 | 
			
		||||
/* toggle device mute */
 | 
			
		||||
@@ -88,4 +88,4 @@ void print_devices(ToxWindow* self, DeviceType type);
 | 
			
		||||
void get_primary_device_name(DeviceType type, char *buf, int size);
 | 
			
		||||
 | 
			
		||||
DeviceError selection_valid(DeviceType type, int32_t selection);
 | 
			
		||||
#endif /* DEVICE_H */
 | 
			
		||||
#endif /* AUDIO_DEVICE_H */
 | 
			
		||||
							
								
								
									
										74
									
								
								src/chat.c
									
									
									
									
									
								
							
							
						
						
									
										74
									
								
								src/chat.c
									
									
									
									
									
								
							@@ -623,32 +623,31 @@ static void chat_onGroupInvite(ToxWindow *self, Tox *m, int32_t friendnumber, ui
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/join\" to join the chat.");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Av Stuff */
 | 
			
		||||
/* AV Stuff */
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
 | 
			
		||||
void chat_onInvite (ToxWindow *self, ToxAv *av, int call_index)
 | 
			
		||||
void chat_onInvite (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
 | 
			
		||||
{
 | 
			
		||||
    if (!self || self->num != toxav_get_peer_id(av, call_index, 0))
 | 
			
		||||
    if (!self || self->num != friend_number)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    /* call_index is set here and reset on call end */
 | 
			
		||||
    /* call is flagged active here */
 | 
			
		||||
    self->is_call = true;
 | 
			
		||||
 | 
			
		||||
    self->call_idx = call_index;
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Incoming audio call! Type: \"/answer\" or \"/reject\"");
 | 
			
		||||
 | 
			
		||||
    if (self->ringing_sound == -1)
 | 
			
		||||
        sound_notify(self, call_incoming, NT_LOOP, &self->ringing_sound);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    if (self->active_box != -1)
 | 
			
		||||
        box_silent_notify2(self, NT_NOFOCUS | NT_WNDALERT_0, self->active_box, "Incoming audio call!");
 | 
			
		||||
    else
 | 
			
		||||
        box_silent_notify(self, NT_NOFOCUS | NT_WNDALERT_0, &self->active_box, self->name, "Incoming audio call!");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void chat_onRinging (ToxWindow *self, ToxAv *av, int call_index)
 | 
			
		||||
void chat_onRinging (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
 | 
			
		||||
{
 | 
			
		||||
    if ( !self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0))
 | 
			
		||||
    if (!self || self->num != friend_number)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ringing...type \"/hangup\" to cancel it.");
 | 
			
		||||
@@ -659,40 +658,44 @@ void chat_onRinging (ToxWindow *self, ToxAv *av, int call_index)
 | 
			
		||||
#endif /* SOUND_NOTIFY */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void chat_onStarting (ToxWindow *self, ToxAv *av, int call_index)
 | 
			
		||||
void chat_onStarting (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
 | 
			
		||||
{
 | 
			
		||||
    if ( !self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0))
 | 
			
		||||
    if (!self || self->num != friend_number)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    init_infobox(self);
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call started! Type: \"/hangup\" to end it.");
 | 
			
		||||
 | 
			
		||||
    /* call is flagged active here */
 | 
			
		||||
    self->is_call = true;
 | 
			
		||||
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
    stop_sound(self->ringing_sound);
 | 
			
		||||
#endif /* SOUND_NOTIFY */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void chat_onEnding (ToxWindow *self, ToxAv *av, int call_index)
 | 
			
		||||
void chat_onEnding (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
 | 
			
		||||
{
 | 
			
		||||
    if (!self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0))
 | 
			
		||||
    if (!self || self->num != friend_number)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    kill_infobox(self);
 | 
			
		||||
    self->call_idx = -1;
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call ended!");
 | 
			
		||||
 | 
			
		||||
    self->is_call = false;
 | 
			
		||||
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
    stop_sound(self->ringing_sound);
 | 
			
		||||
#endif /* SOUND_NOTIFY */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void chat_onError (ToxWindow *self, ToxAv *av, int call_index)
 | 
			
		||||
void chat_onError (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
 | 
			
		||||
{
 | 
			
		||||
    if (!self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0))
 | 
			
		||||
    if (!self || self->num != friend_number)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    self->call_idx = -1;
 | 
			
		||||
    self->is_call = false;
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Error!");
 | 
			
		||||
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
@@ -700,11 +703,14 @@ void chat_onError (ToxWindow *self, ToxAv *av, int call_index)
 | 
			
		||||
#endif /* SOUND_NOTIFY */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void chat_onStart (ToxWindow *self, ToxAv *av, int call_index)
 | 
			
		||||
void chat_onStart (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
 | 
			
		||||
{
 | 
			
		||||
    if ( !self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0))
 | 
			
		||||
    if (!self || self->num != friend_number)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    /* call is flagged active here */
 | 
			
		||||
    self->is_call = true;
 | 
			
		||||
 | 
			
		||||
    init_infobox(self);
 | 
			
		||||
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call started! Type: \"/hangup\" to end it.");
 | 
			
		||||
@@ -714,13 +720,13 @@ void chat_onStart (ToxWindow *self, ToxAv *av, int call_index)
 | 
			
		||||
#endif /* SOUND_NOTIFY */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void chat_onCancel (ToxWindow *self, ToxAv *av, int call_index)
 | 
			
		||||
void chat_onCancel (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
 | 
			
		||||
{
 | 
			
		||||
    if ( !self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0))
 | 
			
		||||
    if (!self || self->num != friend_number)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    self->is_call = false;
 | 
			
		||||
    kill_infobox(self);
 | 
			
		||||
    self->call_idx = -1;
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call canceled!");
 | 
			
		||||
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
@@ -728,39 +734,39 @@ void chat_onCancel (ToxWindow *self, ToxAv *av, int call_index)
 | 
			
		||||
#endif /* SOUND_NOTIFY */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void chat_onReject (ToxWindow *self, ToxAv *av, int call_index)
 | 
			
		||||
void chat_onReject (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
 | 
			
		||||
{
 | 
			
		||||
    if (!self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0))
 | 
			
		||||
    if (!self  || self->num != friend_number)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    self->call_idx = -1;
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Rejected!");
 | 
			
		||||
    self->is_call = false;
 | 
			
		||||
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
    stop_sound(self->ringing_sound);
 | 
			
		||||
#endif /* SOUND_NOTIFY */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void chat_onEnd (ToxWindow *self, ToxAv *av, int call_index)
 | 
			
		||||
void chat_onEnd (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
 | 
			
		||||
{
 | 
			
		||||
    if (!self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0))
 | 
			
		||||
    if (!self || self->num != friend_number)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    kill_infobox(self);
 | 
			
		||||
    self->call_idx = -1;
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call ended!");
 | 
			
		||||
    self->is_call = false;
 | 
			
		||||
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
    stop_sound(self->ringing_sound);
 | 
			
		||||
#endif /* SOUND_NOTIFY */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void chat_onRequestTimeout (ToxWindow *self, ToxAv *av, int call_index)
 | 
			
		||||
void chat_onRequestTimeout (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
 | 
			
		||||
{
 | 
			
		||||
    if (!self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0))
 | 
			
		||||
    if (!self || self->num != friend_number)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    self->call_idx = -1;
 | 
			
		||||
    self->is_call = false;
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No answer!");
 | 
			
		||||
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
@@ -768,13 +774,13 @@ void chat_onRequestTimeout (ToxWindow *self, ToxAv *av, int call_index)
 | 
			
		||||
#endif /* SOUND_NOTIFY */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void chat_onPeerTimeout (ToxWindow *self, ToxAv *av, int call_index)
 | 
			
		||||
void chat_onPeerTimeout (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
 | 
			
		||||
{
 | 
			
		||||
    if (!self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0))
 | 
			
		||||
    if (!self || self->num != friend_number)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    self->is_call = false;
 | 
			
		||||
    kill_infobox(self);
 | 
			
		||||
    self->call_idx = -1;
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer disconnected; call ended!");
 | 
			
		||||
 | 
			
		||||
#ifdef SOUND_NOTIFY
 | 
			
		||||
@@ -1209,7 +1215,7 @@ ToxWindow new_chat(Tox *m, uint32_t friendnum)
 | 
			
		||||
    ret.onRequestTimeout = &chat_onRequestTimeout;
 | 
			
		||||
    ret.onPeerTimeout = &chat_onPeerTimeout;
 | 
			
		||||
 | 
			
		||||
    ret.call_idx = -1;
 | 
			
		||||
    ret.is_call = false;
 | 
			
		||||
    ret.device_selection[0] = ret.device_selection[1] = -1;
 | 
			
		||||
    ret.ringing_sound = -1;
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 
 | 
			
		||||
@@ -120,11 +120,11 @@ void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
 | 
			
		||||
 | 
			
		||||
    if (type == TOX_GROUPCHAT_TYPE_TEXT)
 | 
			
		||||
        groupnum = tox_join_groupchat(m, self->num, (uint8_t *) groupkey, length);
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
/*#ifdef AUDIO
 | 
			
		||||
    else
 | 
			
		||||
        groupnum = toxav_join_av_groupchat(m, self->num, (uint8_t *) groupkey, length,
 | 
			
		||||
                                           NULL, NULL);
 | 
			
		||||
#endif
 | 
			
		||||
#endif*/
 | 
			
		||||
 | 
			
		||||
    if (groupnum == -1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize.");
 | 
			
		||||
 
 | 
			
		||||
@@ -43,4 +43,9 @@ void cmd_mute(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]
 | 
			
		||||
void cmd_sense(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
void cmd_video(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_ccur_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
#endif /* #define CHAT_COMMANDS_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -61,6 +61,10 @@ static struct cmd_func global_commands[] = {
 | 
			
		||||
    { "/lsdev",     cmd_list_devices  },
 | 
			
		||||
    { "/sdev",      cmd_change_device },
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
    { "/lsvdev",    cmd_list_video_devices },
 | 
			
		||||
    { "/svdev" ,    cmd_change_video_device },
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
    { NULL,         NULL              },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -78,6 +82,9 @@ static struct cmd_func chat_commands[] = {
 | 
			
		||||
    { "/mute",      cmd_mute        },
 | 
			
		||||
    { "/sense",     cmd_sense       },
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
    { "/video",     cmd_video       },
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
    { NULL,         NULL            },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1089,23 +1089,22 @@ void disable_chatwin(uint32_t f_num)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
static void friendlist_onAv(ToxWindow *self, ToxAv *av, int call_index)
 | 
			
		||||
static void friendlist_onAV(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
 | 
			
		||||
{
 | 
			
		||||
    int id = toxav_get_peer_id(av, call_index, 0);
 | 
			
		||||
 | 
			
		||||
    if ( id != av_ErrorUnknown && id >= Friends.max_idx)
 | 
			
		||||
    if( friend_number >= Friends.max_idx)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    Tox *m = toxav_get_tox(av);
 | 
			
		||||
 | 
			
		||||
    if (Friends.list[id].chatwin == -1) {
 | 
			
		||||
    if (Friends.list[friend_number].chatwin == -1) {
 | 
			
		||||
        if (get_num_active_windows() < MAX_WINDOWS_NUM) {
 | 
			
		||||
            if (toxav_get_call_state(av, call_index) == av_CallStarting) { /* Only open windows when call is incoming */
 | 
			
		||||
                Friends.list[id].chatwin = add_window(m, new_chat(m, Friends.list[id].num));
 | 
			
		||||
            if(state != TOXAV_FRIEND_CALL_STATE_FINISHED) {
 | 
			
		||||
                Friends.list[friend_number].chatwin = add_window(m, new_chat(m, Friends.list[friend_number].num));
 | 
			
		||||
                set_active_window(Friends.list[friend_number].chatwin);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            char nick[TOX_MAX_NAME_LENGTH];
 | 
			
		||||
            get_nick_truncate(m, nick, Friends.list[id].num);
 | 
			
		||||
            get_nick_truncate(m, nick, Friends.list[friend_number].num);
 | 
			
		||||
            line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio action from: %s!", nick);
 | 
			
		||||
 | 
			
		||||
            const char *errmsg = "* Warning: Too many windows are open.";
 | 
			
		||||
@@ -1137,22 +1136,23 @@ ToxWindow new_friendlist(void)
 | 
			
		||||
    ret.onGroupInvite = &friendlist_onGroupInvite;
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
    ret.onInvite = &friendlist_onAv;
 | 
			
		||||
    ret.onRinging = &friendlist_onAv;
 | 
			
		||||
    ret.onStarting = &friendlist_onAv;
 | 
			
		||||
    ret.onEnding = &friendlist_onAv;
 | 
			
		||||
    ret.onError = &friendlist_onAv;
 | 
			
		||||
    ret.onStart = &friendlist_onAv;
 | 
			
		||||
    ret.onCancel = &friendlist_onAv;
 | 
			
		||||
    ret.onReject = &friendlist_onAv;
 | 
			
		||||
    ret.onEnd = &friendlist_onAv;
 | 
			
		||||
    ret.onRequestTimeout = &friendlist_onAv;
 | 
			
		||||
    ret.onPeerTimeout = &friendlist_onAv;
 | 
			
		||||
    ret.onInvite = &friendlist_onAV;
 | 
			
		||||
    ret.onRinging = &friendlist_onAV;
 | 
			
		||||
    ret.onStarting = &friendlist_onAV;
 | 
			
		||||
    ret.onEnding = &friendlist_onAV;
 | 
			
		||||
    ret.onError = &friendlist_onAV;
 | 
			
		||||
    ret.onStart = &friendlist_onAV;
 | 
			
		||||
    ret.onCancel = &friendlist_onAV;
 | 
			
		||||
    ret.onReject = &friendlist_onAV;
 | 
			
		||||
    ret.onEnd = &friendlist_onAV;
 | 
			
		||||
    ret.onRequestTimeout = &friendlist_onAV;
 | 
			
		||||
    ret.onPeerTimeout = &friendlist_onAV;
 | 
			
		||||
 | 
			
		||||
    ret.call_idx = -1;
 | 
			
		||||
    ret.is_call = false;
 | 
			
		||||
    ret.device_selection[0] = ret.device_selection[1] = -1;
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
    ret.num = -1;
 | 
			
		||||
    ret.active_box = -1;
 | 
			
		||||
 | 
			
		||||
    Help *help = calloc(1, sizeof(Help));
 | 
			
		||||
 
 | 
			
		||||
@@ -344,10 +344,10 @@ void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
 | 
			
		||||
 | 
			
		||||
    if (type == TOX_GROUPCHAT_TYPE_TEXT)
 | 
			
		||||
        groupnum = tox_add_groupchat(m);
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
/*#ifdef AUDIO
 | 
			
		||||
    else
 | 
			
		||||
        groupnum = toxav_add_av_groupchat(m, NULL, NULL);
 | 
			
		||||
#endif
 | 
			
		||||
#endif*/
 | 
			
		||||
 | 
			
		||||
    if (groupnum == -1) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize.");
 | 
			
		||||
 
 | 
			
		||||
@@ -49,4 +49,9 @@ void cmd_list_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_S
 | 
			
		||||
void cmd_change_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
void cmd_list_video_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
void cmd_change_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
#endif /* #define GLOBAL_COMMANDS_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -59,7 +59,7 @@
 | 
			
		||||
#include "help.h"
 | 
			
		||||
#include "notify.h"
 | 
			
		||||
#include "autocomplete.h"
 | 
			
		||||
#include "device.h"
 | 
			
		||||
#include "audio_device.h"
 | 
			
		||||
 | 
			
		||||
extern char *DATA_FILE;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								src/help.c
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								src/help.c
									
									
									
									
									
								
							@@ -167,6 +167,15 @@ static void help_draw_global(ToxWindow *self)
 | 
			
		||||
    wprintw(win, "  /sdev <type> <id>          : Set active device\n");
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
    wattron(win, A_BOLD);
 | 
			
		||||
    wprintw(win, "\n Video:\n");
 | 
			
		||||
    wattroff(win, A_BOLD);
 | 
			
		||||
 | 
			
		||||
    wprintw(win, "  /lsvdev <type>             : List video devices where type: in|out\n");
 | 
			
		||||
    wprintw(win, "  /svdev <type> <id>         : Set active video device\n");
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
    help_draw_bottom_menu(win);
 | 
			
		||||
 | 
			
		||||
    box(win, ACS_VLINE, ACS_HLINE);
 | 
			
		||||
@@ -203,6 +212,13 @@ static void help_draw_chat(ToxWindow *self)
 | 
			
		||||
    wprintw(win, "  /sense <n>                 : VAD sensitivity threshold\n");
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
    wattron(win, A_BOLD);
 | 
			
		||||
    wprintw(win, "\n Video:\n");
 | 
			
		||||
    wattroff(win, A_BOLD);
 | 
			
		||||
    wprintw(win, "  /video                     : Toggle video call\n");
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
    help_draw_bottom_menu(win);
 | 
			
		||||
 | 
			
		||||
    box(win, ACS_VLINE, ACS_HLINE);
 | 
			
		||||
@@ -282,7 +298,9 @@ void help_onKey(ToxWindow *self, wint_t key)
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case 'c':
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
            help_init_window(self, 22, 80);
 | 
			
		||||
#elif AUDIO
 | 
			
		||||
            help_init_window(self, 19, 80);
 | 
			
		||||
#else
 | 
			
		||||
            help_init_window(self, 9, 80);
 | 
			
		||||
@@ -291,7 +309,9 @@ void help_onKey(ToxWindow *self, wint_t key)
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case 'g':
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
            help_init_window(self, 28, 80);
 | 
			
		||||
#elif AUDIO
 | 
			
		||||
            help_init_window(self, 24, 80);
 | 
			
		||||
#else
 | 
			
		||||
            help_init_window(self, 20, 80);
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,7 @@
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
 | 
			
		||||
#include "notify.h"
 | 
			
		||||
#include "device.h"
 | 
			
		||||
#include "audio_device.h"
 | 
			
		||||
#include "settings.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										54
									
								
								src/osx_video.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/osx_video.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
			
		||||
/*  osx_video.h
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2014 Toxic All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of Toxic.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic 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.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic 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 Toxic.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef OSX_VIDEO_H
 | 
			
		||||
#define OSX_VIDEO_H
 | 
			
		||||
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
 | 
			
		||||
#ifdef __OBJC__
 | 
			
		||||
#import <Foundation/Foundation.h>
 | 
			
		||||
#import <AVFoundation/AVFoundation.h>
 | 
			
		||||
#endif /* __OBJC__ */
 | 
			
		||||
 | 
			
		||||
#define RELEASE_CHK(func, obj) if ((obj))\
 | 
			
		||||
 	func((obj));
 | 
			
		||||
 | 
			
		||||
void bgrtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t *rgb, uint16_t width, uint16_t height);
 | 
			
		||||
 | 
			
		||||
#ifdef __OBJC__
 | 
			
		||||
@interface OSXVideo : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate>
 | 
			
		||||
- (instancetype)initWithDeviceNames:(char **)device_names AmtDevices:(int *)size;
 | 
			
		||||
@end
 | 
			
		||||
#endif /* __OBJC__ */
 | 
			
		||||
 | 
			
		||||
int osx_video_init(char **device_names, int *size);
 | 
			
		||||
void osx_video_release();
 | 
			
		||||
/* Start device */
 | 
			
		||||
int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height);
 | 
			
		||||
/* Stop device */
 | 
			
		||||
void osx_video_close_device(uint32_t device_idx);
 | 
			
		||||
/* Read data from device */
 | 
			
		||||
int osx_video_read_device(uint8_t *y, uint8_t *u, uint8_t *v, uint16_t *width, uint16_t *height);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif /* OSX_VIDEO_H */
 | 
			
		||||
							
								
								
									
										308
									
								
								src/osx_video.m
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										308
									
								
								src/osx_video.m
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,308 @@
 | 
			
		||||
/*  osx_video.m
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2014 Toxic All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of Toxic.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic 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.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic 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 Toxic.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifdef __OBJC__
 | 
			
		||||
#include "osx_video.h"
 | 
			
		||||
 | 
			
		||||
#import <Foundation/Foundation.h>
 | 
			
		||||
#import <AVFoundation/AVFoundation.h>
 | 
			
		||||
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "settings.h"
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Helper video format functions
 | 
			
		||||
 */
 | 
			
		||||
static uint8_t rgb_to_y(int r, int g, int b)
 | 
			
		||||
{
 | 
			
		||||
    int y = ((9798 * r + 19235 * g + 3736 * b) >> 15);
 | 
			
		||||
    return y>255? 255 : y<0 ? 0 : y;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint8_t rgb_to_u(int r, int g, int b)
 | 
			
		||||
{
 | 
			
		||||
    int u = ((-5538 * r + -10846 * g + 16351 * b) >> 15) + 128;
 | 
			
		||||
    return u>255? 255 : u<0 ? 0 : u;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint8_t rgb_to_v(int r, int g, int b)
 | 
			
		||||
{
 | 
			
		||||
    int v = ((16351 * r + -13697 * g + -2664 * b) >> 15) + 128;
 | 
			
		||||
    return v>255? 255 : v<0 ? 0 : v;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t *rgb, uint16_t width, uint16_t height)
 | 
			
		||||
{
 | 
			
		||||
    uint16_t x, y;
 | 
			
		||||
    uint8_t *p;
 | 
			
		||||
    uint8_t r, g, b;
 | 
			
		||||
 | 
			
		||||
    for(y = 0; y != height; y += 2) {
 | 
			
		||||
        p = rgb;
 | 
			
		||||
        for(x = 0; x != width; x++) {
 | 
			
		||||
            b = *rgb++;
 | 
			
		||||
            g = *rgb++;
 | 
			
		||||
            r = *rgb++;
 | 
			
		||||
            rgb++;
 | 
			
		||||
 | 
			
		||||
            *plane_y++ = rgb_to_y(r, g, b);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for(x = 0; x != width / 2; x++) {
 | 
			
		||||
            b = *rgb++;
 | 
			
		||||
            g = *rgb++;
 | 
			
		||||
            r = *rgb++;
 | 
			
		||||
            rgb++;
 | 
			
		||||
 | 
			
		||||
            *plane_y++ = rgb_to_y(r, g, b);
 | 
			
		||||
 | 
			
		||||
            b = *rgb++;
 | 
			
		||||
            g = *rgb++;
 | 
			
		||||
            r = *rgb++;
 | 
			
		||||
            rgb++;
 | 
			
		||||
 | 
			
		||||
            *plane_y++ = rgb_to_y(r, g, b);
 | 
			
		||||
 | 
			
		||||
            b = ((int)b + (int)*(rgb - 8) + (int)*p + (int)*(p + 4) + 2) / 4; p++;
 | 
			
		||||
            g = ((int)g + (int)*(rgb - 7) + (int)*p + (int)*(p + 4) + 2) / 4; p++;
 | 
			
		||||
            r = ((int)r + (int)*(rgb - 6) + (int)*p + (int)*(p + 4) + 2) / 4; p++;
 | 
			
		||||
            p++;
 | 
			
		||||
 | 
			
		||||
            *plane_u++ = rgb_to_u(r, g, b);
 | 
			
		||||
            *plane_v++ = rgb_to_v(r, g, b);
 | 
			
		||||
 | 
			
		||||
            p += 4;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
/*
 | 
			
		||||
 * End of helper video format functions
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Implementation for OSXVideo
 | 
			
		||||
 */
 | 
			
		||||
@implementation OSXVideo {
 | 
			
		||||
    dispatch_queue_t _processingQueue;
 | 
			
		||||
    AVCaptureSession *_session;
 | 
			
		||||
    AVCaptureVideoDataOutput *_linkerVideo;
 | 
			
		||||
 | 
			
		||||
    CVImageBufferRef _currentFrame;
 | 
			
		||||
    pthread_mutex_t _frameLock;
 | 
			
		||||
    BOOL _shouldMangleDimensions;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (instancetype)initWithDeviceNames: (char **)device_names AmtDevices: (int *)size {
 | 
			
		||||
    _session = [[AVCaptureSession alloc] init];
 | 
			
		||||
 | 
			
		||||
    NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
 | 
			
		||||
    int i;
 | 
			
		||||
    for (i = 0; i < [devices count]; ++i) {
 | 
			
		||||
        AVCaptureDevice *device = [devices objectAtIndex:i];
 | 
			
		||||
        char *video_input_name;
 | 
			
		||||
        NSString *localizedName = [device localizedName];
 | 
			
		||||
        video_input_name = (char*)malloc(strlen([localizedName cStringUsingEncoding:NSUTF8StringEncoding]) + 1);
 | 
			
		||||
        strcpy(video_input_name, (char*)[localizedName cStringUsingEncoding:NSUTF8StringEncoding]);
 | 
			
		||||
        device_names[i] = video_input_name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( i <= 0 )
 | 
			
		||||
        return nil;
 | 
			
		||||
    *size = i;
 | 
			
		||||
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)dealloc {
 | 
			
		||||
    pthread_mutex_destroy(&_frameLock);
 | 
			
		||||
    [_session release];
 | 
			
		||||
    [_linkerVideo release];
 | 
			
		||||
    dispatch_release(_processingQueue);
 | 
			
		||||
    [super dealloc];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (int)openVideoDeviceIndex: (uint32_t)device_idx Width: (uint16_t *)width Height: (uint16_t *)height {
 | 
			
		||||
    pthread_mutex_init(&_frameLock, NULL);
 | 
			
		||||
    pthread_mutex_lock(&_frameLock);
 | 
			
		||||
    _processingQueue = dispatch_queue_create("Toxic processing queue", DISPATCH_QUEUE_SERIAL);
 | 
			
		||||
    NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
 | 
			
		||||
    AVCaptureDevice *device = [devices objectAtIndex:device_idx];
 | 
			
		||||
    NSError *error = NULL;
 | 
			
		||||
    AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error];
 | 
			
		||||
 | 
			
		||||
    if ( error != NULL ) {
 | 
			
		||||
        [input release];
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [_session beginConfiguration];
 | 
			
		||||
    [_session addInput:input];
 | 
			
		||||
    //_session.sessionPreset = AVCaptureSessionPreset640x480;
 | 
			
		||||
    //*width = 640;
 | 
			
		||||
    //*height = 480;
 | 
			
		||||
    _shouldMangleDimensions = YES;
 | 
			
		||||
    [_session commitConfiguration];
 | 
			
		||||
    [input release];
 | 
			
		||||
    [device release];
 | 
			
		||||
 | 
			
		||||
    /* Obtain device resolution */
 | 
			
		||||
    AVCaptureInputPort *port = [input.ports objectAtIndex:0];
 | 
			
		||||
    CMFormatDescriptionRef format_description = port.formatDescription;
 | 
			
		||||
    if ( format_description ) {
 | 
			
		||||
        CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format_description);
 | 
			
		||||
        *width = dimensions.width;
 | 
			
		||||
        *height = dimensions.height;
 | 
			
		||||
    } else {
 | 
			
		||||
        *width = 0;
 | 
			
		||||
        *height = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _linkerVideo = [[AVCaptureVideoDataOutput alloc] init];
 | 
			
		||||
    [_linkerVideo setSampleBufferDelegate:self queue:_processingQueue];
 | 
			
		||||
    // TODO possibly get a better pixel format
 | 
			
		||||
    if (_shouldMangleDimensions) {
 | 
			
		||||
        [_linkerVideo setVideoSettings:@{(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA),
 | 
			
		||||
                                         (id)kCVPixelBufferWidthKey: @640,
 | 
			
		||||
                                         (id)kCVPixelBufferHeightKey: @480}];
 | 
			
		||||
    } else {
 | 
			
		||||
        [_linkerVideo setVideoSettings:@{(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)}];
 | 
			
		||||
    }
 | 
			
		||||
    [_session addOutput:_linkerVideo];
 | 
			
		||||
    [_session startRunning];
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_unlock(&_frameLock);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)closeVideoDeviceIndex: (uint32_t)device_idx {
 | 
			
		||||
    NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
 | 
			
		||||
    AVCaptureDevice *device = [devices objectAtIndex:device_idx];
 | 
			
		||||
    NSError *error = NULL;
 | 
			
		||||
    AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error];
 | 
			
		||||
    [_session stopRunning];
 | 
			
		||||
    [_session removeOutput:_linkerVideo];
 | 
			
		||||
    [_session removeInput:input];
 | 
			
		||||
    [_linkerVideo release];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
 | 
			
		||||
    pthread_mutex_lock(&_frameLock);
 | 
			
		||||
    CVImageBufferRef img = CMSampleBufferGetImageBuffer(sampleBuffer);
 | 
			
		||||
    if (!img) {
 | 
			
		||||
        NSLog(@"Toxic WARNING: Bad sampleBuffer from AVfoundation!");
 | 
			
		||||
    } else {
 | 
			
		||||
        CVPixelBufferUnlockBaseAddress(_currentFrame, kCVPixelBufferLock_ReadOnly);
 | 
			
		||||
        RELEASE_CHK(CFRelease, _currentFrame);
 | 
			
		||||
 | 
			
		||||
        _currentFrame = (CVImageBufferRef)CFRetain(img);
 | 
			
		||||
        // we're not going to do anything to it, so it's safe to lock it always
 | 
			
		||||
        CVPixelBufferLockBaseAddress(_currentFrame, kCVPixelBufferLock_ReadOnly);
 | 
			
		||||
    }
 | 
			
		||||
    pthread_mutex_unlock(&_frameLock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (int)getVideoFrameY: (uint8_t *)y U: (uint8_t *)u V: (uint8_t *)v Width: (uint16_t *)width Height: (uint16_t *)height {
 | 
			
		||||
    if (!_currentFrame) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_lock(&_frameLock);
 | 
			
		||||
    CFRetain(_currentFrame);
 | 
			
		||||
 | 
			
		||||
    CFTypeID imageType = CFGetTypeID(_currentFrame);
 | 
			
		||||
    if (imageType == CVPixelBufferGetTypeID()) {
 | 
			
		||||
        // TODO maybe handle other formats
 | 
			
		||||
        bgrxtoyuv420(y, u, v, CVPixelBufferGetBaseAddress(_currentFrame), *width, *height);
 | 
			
		||||
    } else if (imageType == CVOpenGLBufferGetTypeID()) {
 | 
			
		||||
        // OpenGL pbuffer
 | 
			
		||||
    } else if (imageType == CVOpenGLTextureGetTypeID()) {
 | 
			
		||||
        // OpenGL Texture (Do we need to handle these?)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    CVPixelBufferRelease(_currentFrame);
 | 
			
		||||
    pthread_mutex_unlock(&_frameLock);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@end
 | 
			
		||||
/*
 | 
			
		||||
 * End of implementation for OSXVideo
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * C-interface for OSXVideo
 | 
			
		||||
 */
 | 
			
		||||
static OSXVideo* _OSXVideo = nil;
 | 
			
		||||
 | 
			
		||||
int osx_video_init(char **device_names, int *size)
 | 
			
		||||
{
 | 
			
		||||
    _OSXVideo = [[OSXVideo alloc] initWithDeviceNames: device_names AmtDevices: size];
 | 
			
		||||
 | 
			
		||||
    if ( _OSXVideo == nil )
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void osx_video_release()
 | 
			
		||||
{
 | 
			
		||||
    [_OSXVideo release];
 | 
			
		||||
    _OSXVideo = nil;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height)
 | 
			
		||||
{
 | 
			
		||||
    if ( _OSXVideo == nil )
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    return [_OSXVideo openVideoDeviceIndex: selection Width: width Height: height];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void osx_video_close_device(uint32_t device_idx)
 | 
			
		||||
{
 | 
			
		||||
    [_OSXVideo closeVideoDeviceIndex: device_idx];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int osx_video_read_device(uint8_t *y, uint8_t *u, uint8_t *v, uint16_t *width, uint16_t *height)
 | 
			
		||||
{
 | 
			
		||||
    if ( _OSXVideo == nil )
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    return [_OSXVideo getVideoFrameY: y U: u V: v Width: width Height: height];
 | 
			
		||||
}
 | 
			
		||||
/*
 | 
			
		||||
 * End of C-interface for OSXVideo
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#endif /* __OBJC__ */
 | 
			
		||||
							
								
								
									
										16
									
								
								src/prompt.c
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								src/prompt.c
									
									
									
									
									
								
							@@ -49,12 +49,13 @@ extern struct Winthread Winthread;
 | 
			
		||||
 | 
			
		||||
extern FriendsList Friends;
 | 
			
		||||
FriendRequests FrndRequests;
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
#define AC_NUM_GLOB_COMMANDS 20
 | 
			
		||||
#elif AUDIO
 | 
			
		||||
#define AC_NUM_GLOB_COMMANDS 18
 | 
			
		||||
#else
 | 
			
		||||
#define AC_NUM_GLOB_COMMANDS 16
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Array of global command names used for tab completion. */
 | 
			
		||||
static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
 | 
			
		||||
@@ -81,6 +82,14 @@ static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
 | 
			
		||||
    { "/sdev"        },
 | 
			
		||||
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
 | 
			
		||||
    { "/lsvdev"      },
 | 
			
		||||
    { "/svdev"       },
 | 
			
		||||
    
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
    
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void kill_prompt_window(ToxWindow *self)
 | 
			
		||||
@@ -510,6 +519,7 @@ ToxWindow new_prompt(void)
 | 
			
		||||
    ToxWindow ret;
 | 
			
		||||
    memset(&ret, 0, sizeof(ret));
 | 
			
		||||
 | 
			
		||||
    ret.num = -1;
 | 
			
		||||
    ret.active = true;
 | 
			
		||||
    ret.is_prompt = true;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,7 @@
 | 
			
		||||
#include "misc_tools.h"
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
    #include "device.h"
 | 
			
		||||
    #include "audio_device.h"
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
#include "settings.h"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								src/toxic.c
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								src/toxic.c
									
									
									
									
									
								
							@@ -54,7 +54,7 @@
 | 
			
		||||
#include "settings.h"
 | 
			
		||||
#include "log.h"
 | 
			
		||||
#include "notify.h"
 | 
			
		||||
#include "device.h"
 | 
			
		||||
#include "audio_device.h"
 | 
			
		||||
#include "message_queue.h"
 | 
			
		||||
#include "execute.h"
 | 
			
		||||
#include "term_mplex.h"
 | 
			
		||||
@@ -65,7 +65,10 @@
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
#include "audio_call.h"
 | 
			
		||||
ToxAv *av;
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
#include "video_call.h"
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
ToxAV *av;
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
#ifndef PACKAGE_DATADIR
 | 
			
		||||
@@ -86,7 +89,7 @@ ToxWindow *prompt = NULL;
 | 
			
		||||
 | 
			
		||||
struct Winthread Winthread;
 | 
			
		||||
struct cqueue_thread cqueue_thread;
 | 
			
		||||
struct audio_thread audio_thread;
 | 
			
		||||
struct av_thread av_thread;
 | 
			
		||||
struct arg_opts arg_opts;
 | 
			
		||||
struct user_settings *user_settings = NULL;
 | 
			
		||||
 | 
			
		||||
@@ -149,6 +152,11 @@ void exit_toxic_success(Tox *m)
 | 
			
		||||
    terminate_notify();
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
    terminate_video();
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
    terminate_audio();
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
@@ -876,16 +884,16 @@ void *thread_cqueue(void *data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
void *thread_audio(void *data)
 | 
			
		||||
void *thread_av(void *data)
 | 
			
		||||
{
 | 
			
		||||
    ToxAv *av = (ToxAv *) data;
 | 
			
		||||
 | 
			
		||||
    ToxAV *av = (ToxAV *) data;
 | 
			
		||||
    
 | 
			
		||||
    while (true) {
 | 
			
		||||
        pthread_mutex_lock(&Winthread.lock);
 | 
			
		||||
        toxav_do(av);
 | 
			
		||||
        toxav_iterate(av);
 | 
			
		||||
        pthread_mutex_unlock(&Winthread.lock);
 | 
			
		||||
 | 
			
		||||
        usleep(toxav_do_interval(av) * 1000);
 | 
			
		||||
        usleep(toxav_iteration_interval(av) * 1000);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#endif  /* AUDIO */
 | 
			
		||||
@@ -1256,9 +1264,14 @@ int main(int argc, char **argv)
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
 | 
			
		||||
    av = init_audio(prompt, m);
 | 
			
		||||
    
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
    init_video(prompt, m);
 | 
			
		||||
 | 
			
		||||
    /* audio thread */
 | 
			
		||||
    if (pthread_create(&audio_thread.tid, NULL, thread_audio, (void *) av) != 0)
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
    /* AV thread */
 | 
			
		||||
    if (pthread_create(&av_thread.tid, NULL, thread_av, (void *) av) != 0)
 | 
			
		||||
        exit_toxic_err("failed in main", FATALERR_THREAD_CREATE);
 | 
			
		||||
 | 
			
		||||
    set_primary_device(input, user_settings->audio_in_dev);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										419
									
								
								src/video_call.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										419
									
								
								src/video_call.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,419 @@
 | 
			
		||||
/*  video_call.c
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2014 Toxic All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of Toxic.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic 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.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic 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 Toxic.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "toxic.h"
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
#include "video_call.h"
 | 
			
		||||
#include "video_device.h"
 | 
			
		||||
#include "chat_commands.h"
 | 
			
		||||
#include "global_commands.h"
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "notify.h"
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <curses.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
 | 
			
		||||
#define default_video_bit_rate 5000
 | 
			
		||||
 | 
			
		||||
void receive_video_frame_cb( ToxAV *av, uint32_t friend_number,
 | 
			
		||||
                                    uint16_t width, uint16_t height,
 | 
			
		||||
                                    uint8_t const *y, uint8_t const *u, uint8_t const *v,
 | 
			
		||||
                                    int32_t ystride, int32_t ustride, int32_t vstride,
 | 
			
		||||
                                    void *user_data );
 | 
			
		||||
void video_bit_rate_status_cb( ToxAV *av, uint32_t friend_number, 
 | 
			
		||||
                                      bool stable, uint32_t bit_rate, void *user_data);
 | 
			
		||||
 | 
			
		||||
static void print_err (ToxWindow *self, const char *error_str)
 | 
			
		||||
{
 | 
			
		||||
    line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ToxAV *init_video(ToxWindow *self, Tox *tox)
 | 
			
		||||
{
 | 
			
		||||
    CallControl.video_errors = ve_None;
 | 
			
		||||
 | 
			
		||||
    CallControl.video_enabled = true;
 | 
			
		||||
    CallControl.video_bit_rate = 0;
 | 
			
		||||
    CallControl.video_frame_duration = 10;
 | 
			
		||||
 | 
			
		||||
    if ( !CallControl.av ) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Video failed to init with ToxAV instance");
 | 
			
		||||
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( init_video_devices(CallControl.av) == vde_InternalError ) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init video devices");
 | 
			
		||||
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    toxav_callback_video_receive_frame(CallControl.av, receive_video_frame_cb, &CallControl);
 | 
			
		||||
    toxav_callback_video_bit_rate_status(CallControl.av, video_bit_rate_status_cb, &CallControl);
 | 
			
		||||
 | 
			
		||||
    return CallControl.av;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void terminate_video()
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    for (i = 0; i < MAX_CALLS; ++i) {
 | 
			
		||||
        Call* this_call = &CallControl.calls[i];
 | 
			
		||||
 | 
			
		||||
        stop_video_transmission(this_call, i);
 | 
			
		||||
 | 
			
		||||
        if( this_call->vout_idx != -1 )
 | 
			
		||||
            close_video_device(vdt_output, this_call->vout_idx);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    terminate_video_devices();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void read_video_device_callback(int16_t width, int16_t height, const uint8_t* y, const uint8_t* u, const uint8_t* v, void* data)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t friend_number = *((uint32_t*)data); /* TODO: Or pass an array of call_idx's */
 | 
			
		||||
    Call* this_call = &CallControl.calls[friend_number];
 | 
			
		||||
    TOXAV_ERR_SEND_FRAME error;
 | 
			
		||||
 | 
			
		||||
    /* Drop frame if video sending is disabled */
 | 
			
		||||
    if ( CallControl.video_bit_rate == 0 || this_call->vin_idx == -1 ) {
 | 
			
		||||
        line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Video frame dropped.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( toxav_video_send_frame(CallControl.av, friend_number, width, height, y, u, v, &error ) == false ) {
 | 
			
		||||
        line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to send video frame");
 | 
			
		||||
 | 
			
		||||
        if ( error == TOXAV_ERR_SEND_FRAME_NULL )
 | 
			
		||||
            line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to capture video frame");
 | 
			
		||||
        else if ( error == TOXAV_ERR_SEND_FRAME_INVALID )
 | 
			
		||||
            line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare video frame");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void write_video_device_callback(uint32_t friend_number, uint16_t width, uint16_t height,
 | 
			
		||||
                                           uint8_t const *y, uint8_t const *u, uint8_t const *v,
 | 
			
		||||
                                           int32_t ystride, int32_t ustride, int32_t vstride,
 | 
			
		||||
                                           void *user_data)
 | 
			
		||||
{
 | 
			
		||||
    write_video_out(width, height, y, u, v, ystride, ustride, vstride, user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int start_video_transmission(ToxWindow *self, ToxAV *av, Call *call)
 | 
			
		||||
{
 | 
			
		||||
    if ( !self || !av) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare transmission");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    CallControl.video_bit_rate = default_video_bit_rate;
 | 
			
		||||
    if ( toxav_video_bit_rate_set(CallControl.av, self->num, CallControl.video_bit_rate, true, NULL) == false ) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set video bit rate");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( open_primary_video_device(vdt_input, &call->vin_idx) != vde_None ) {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open input video device!");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    if ( register_video_device_callback(self->num, call->vin_idx, read_video_device_callback, &self->num) != vde_None )
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to register input video handler!");
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int stop_video_transmission(Call *call, int friend_number)
 | 
			
		||||
{
 | 
			
		||||
    CallControl.video_bit_rate = 0;
 | 
			
		||||
    toxav_video_bit_rate_set(CallControl.av, friend_number, CallControl.video_bit_rate, true, NULL);
 | 
			
		||||
 | 
			
		||||
    if ( call->vin_idx != -1 )
 | 
			
		||||
        close_video_device(vdt_input, call->vin_idx);
 | 
			
		||||
        call->vin_idx = -1;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
/*
 | 
			
		||||
 * End of transmission
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Callbacks
 | 
			
		||||
 */
 | 
			
		||||
void receive_video_frame_cb(ToxAV *av, uint32_t friend_number,
 | 
			
		||||
                                    uint16_t width, uint16_t height,
 | 
			
		||||
                                    uint8_t const *y, uint8_t const *u, uint8_t const *v,
 | 
			
		||||
                                    int32_t ystride, int32_t ustride, int32_t vstride,
 | 
			
		||||
                                    void *user_data)
 | 
			
		||||
{
 | 
			
		||||
    write_video_device_callback(friend_number, width, height, y, u, v, ystride, ustride, vstride, user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void video_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, 
 | 
			
		||||
                                      bool stable, uint32_t bit_rate, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
    if ( stable ) {
 | 
			
		||||
        CallControl.video_bit_rate = bit_rate;
 | 
			
		||||
        toxav_video_bit_rate_set(CallControl.av, friend_number, CallControl.video_bit_rate, false, NULL);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void callback_recv_video_starting(uint32_t friend_number)
 | 
			
		||||
{
 | 
			
		||||
    Call* this_call = &CallControl.calls[friend_number];
 | 
			
		||||
 | 
			
		||||
    if ( this_call->vout_idx != -1 )
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    open_primary_video_device(vdt_output, &this_call->vout_idx);
 | 
			
		||||
}
 | 
			
		||||
void callback_recv_video_end(uint32_t friend_number)
 | 
			
		||||
{   
 | 
			
		||||
    Call* this_call = &CallControl.calls[friend_number];
 | 
			
		||||
 | 
			
		||||
    close_video_device(vdt_output, this_call->vout_idx);
 | 
			
		||||
    this_call->vout_idx = -1;
 | 
			
		||||
}
 | 
			
		||||
void callback_video_starting(uint32_t friend_number)
 | 
			
		||||
{
 | 
			
		||||
    ToxWindow* windows = CallControl.prompt;
 | 
			
		||||
    Call* this_call = &CallControl.calls[friend_number];
 | 
			
		||||
 | 
			
		||||
    TOXAV_ERR_CALL_CONTROL error = TOXAV_ERR_CALL_CONTROL_OK;
 | 
			
		||||
    toxav_call_control(CallControl.av, friend_number, TOXAV_CALL_CONTROL_SHOW_VIDEO, &error);
 | 
			
		||||
 | 
			
		||||
    if (error == TOXAV_ERR_CALL_CONTROL_OK) {
 | 
			
		||||
        int i;
 | 
			
		||||
        for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
 | 
			
		||||
            if ( windows[i].is_call && windows[i].num == friend_number ) {
 | 
			
		||||
                if(0 != start_video_transmission(&windows[i], CallControl.av, this_call)) {
 | 
			
		||||
                    line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0, "Error starting transmission!");
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0, "Video capture starting.");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
void callback_video_end(uint32_t friend_number)
 | 
			
		||||
{
 | 
			
		||||
    ToxWindow* windows = CallControl.prompt;
 | 
			
		||||
 | 
			
		||||
    int i;
 | 
			
		||||
    for (i = 0; i < MAX_WINDOWS_NUM; ++i)
 | 
			
		||||
        if ( windows[i].is_call && windows[i].num == friend_number )
 | 
			
		||||
            line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0, "Video capture ending.");
 | 
			
		||||
 | 
			
		||||
    stop_video_transmission(&CallControl.calls[friend_number], friend_number);
 | 
			
		||||
}
 | 
			
		||||
/*
 | 
			
		||||
 * End of Callbacks
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Commands from chat_commands.h
 | 
			
		||||
 */
 | 
			
		||||
void cmd_video(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    const char *error_str;
 | 
			
		||||
    Call* this_call = &CallControl.calls[self->num];
 | 
			
		||||
 | 
			
		||||
    if ( argc != 0 ) {
 | 
			
		||||
        error_str = "Unknown arguments.";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( !CallControl.av ) {
 | 
			
		||||
        error_str = "ToxAV not supported!";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( !self->stb->connection ) {
 | 
			
		||||
        error_str = "Friend is offline.";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( !self->is_call ) {
 | 
			
		||||
        error_str = "Not in call!";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( this_call->vin_idx == -1 )
 | 
			
		||||
        callback_video_starting(self->num);
 | 
			
		||||
    else
 | 
			
		||||
        callback_video_end(self->num);
 | 
			
		||||
    
 | 
			
		||||
    return;
 | 
			
		||||
on_error:
 | 
			
		||||
    print_err (self, error_str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_list_video_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    const char *error_str;
 | 
			
		||||
 | 
			
		||||
    if ( argc != 1 ) {
 | 
			
		||||
        if ( argc < 1 ) error_str = "Type must be specified!";
 | 
			
		||||
        else error_str = "Only one argument allowed!";
 | 
			
		||||
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    VideoDeviceType type;
 | 
			
		||||
 | 
			
		||||
    if ( strcasecmp(argv[1], "in") == 0 ) /* Input devices */
 | 
			
		||||
        type = vdt_input;
 | 
			
		||||
 | 
			
		||||
    else if ( strcasecmp(argv[1], "out") == 0 ) /* Output devices */
 | 
			
		||||
        type = vdt_output;
 | 
			
		||||
 | 
			
		||||
    else {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    print_video_devices(self, type);
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
on_error:
 | 
			
		||||
    print_err (self, error_str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This changes primary video device only */
 | 
			
		||||
void cmd_change_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    const char *error_str;
 | 
			
		||||
 | 
			
		||||
    if ( argc != 2 ) {
 | 
			
		||||
        if ( argc < 1 ) error_str = "Type must be specified!";
 | 
			
		||||
        else if ( argc < 2 ) error_str = "Must have id!";
 | 
			
		||||
        else error_str = "Only two arguments allowed!";
 | 
			
		||||
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    VideoDeviceType type;
 | 
			
		||||
 | 
			
		||||
    if ( strcmp(argv[1], "in") == 0 ) /* Input devices */
 | 
			
		||||
        type = vdt_input;
 | 
			
		||||
 | 
			
		||||
    else if ( strcmp(argv[1], "out") == 0 ) /* Output devices */
 | 
			
		||||
        type = vdt_output;
 | 
			
		||||
 | 
			
		||||
    else {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    char *end;
 | 
			
		||||
    long int selection = strtol(argv[2], &end, 10);
 | 
			
		||||
 | 
			
		||||
    if ( *end ) {
 | 
			
		||||
        error_str = "Invalid input";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( set_primary_video_device(type, selection) == vde_InvalidSelection ) {
 | 
			
		||||
        error_str = "Invalid selection!";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
on_error:
 | 
			
		||||
    print_err (self, error_str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_ccur_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
 | 
			
		||||
{
 | 
			
		||||
    const char *error_str;
 | 
			
		||||
 | 
			
		||||
    if ( argc != 2 ) {
 | 
			
		||||
        if ( argc < 1 ) error_str = "Type must be specified!";
 | 
			
		||||
        else if ( argc < 2 ) error_str = "Must have id!";
 | 
			
		||||
        else error_str = "Only two arguments allowed!";
 | 
			
		||||
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    VideoDeviceType type;
 | 
			
		||||
 | 
			
		||||
    if ( strcmp(argv[1], "in") == 0 ) /* Input devices */
 | 
			
		||||
        type = vdt_input;
 | 
			
		||||
 | 
			
		||||
    else if ( strcmp(argv[1], "out") == 0 ) /* Output devices */
 | 
			
		||||
        type = vdt_output;
 | 
			
		||||
 | 
			
		||||
    else {
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    char *end;
 | 
			
		||||
    long int selection = strtol(argv[2], &end, 10);
 | 
			
		||||
 | 
			
		||||
    if ( *end ) {
 | 
			
		||||
        error_str = "Invalid input";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( video_selection_valid(type, selection) == vde_InvalidSelection ) {
 | 
			
		||||
        error_str="Invalid selection!";
 | 
			
		||||
        goto on_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* If call is active, change device */
 | 
			
		||||
    if ( self->is_call ) {
 | 
			
		||||
        Call* this_call = &CallControl.calls[self->num];
 | 
			
		||||
        if ( this_call->ttas ) {
 | 
			
		||||
 | 
			
		||||
            if ( type == vdt_output ) {
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                /* TODO: check for failure */
 | 
			
		||||
                close_video_device(vdt_input, this_call->vin_idx);
 | 
			
		||||
                open_video_device(vdt_input, selection, &this_call->vin_idx);
 | 
			
		||||
                register_video_device_callback(self->num, this_call->vin_idx, read_video_device_callback, &self->num);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    self->video_device_selection[type] = selection;
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
    on_error:
 | 
			
		||||
    print_err (self, error_str);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										43
									
								
								src/video_call.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/video_call.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
/*  video_call.h
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2014 Toxic All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of Toxic.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic 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.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic 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 Toxic.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef VIDEO_CALL_H
 | 
			
		||||
#define VIDEO_CALL_H
 | 
			
		||||
 | 
			
		||||
#include <tox/toxav.h>
 | 
			
		||||
 | 
			
		||||
#include "audio_call.h"
 | 
			
		||||
 
 | 
			
		||||
#include "video_device.h"
 | 
			
		||||
 | 
			
		||||
/* You will have to pass pointer to first member of 'windows' declared in windows.c */
 | 
			
		||||
ToxAV *init_video(ToxWindow *self, Tox *tox);
 | 
			
		||||
void terminate_video();
 | 
			
		||||
int start_video_transmission(ToxWindow *self, ToxAV *av, Call *call);
 | 
			
		||||
int stop_video_transmission(Call *call, int friend_number);
 | 
			
		||||
 | 
			
		||||
void callback_recv_video_starting(uint32_t friend_number);
 | 
			
		||||
void callback_recv_video_end(uint32_t friend_number);
 | 
			
		||||
void callback_video_starting(uint32_t friend_number);
 | 
			
		||||
void callback_video_end(uint32_t friend_number);
 | 
			
		||||
 | 
			
		||||
#endif /* VIDEO_CALL_H */
 | 
			
		||||
							
								
								
									
										772
									
								
								src/video_device.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										772
									
								
								src/video_device.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,772 @@
 | 
			
		||||
/*  video_device.c
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2014 Toxic All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of Toxic.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic 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.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic 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 Toxic.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "video_device.h"
 | 
			
		||||
#include "video_call.h"
 | 
			
		||||
 | 
			
		||||
#include <sys/ioctl.h>
 | 
			
		||||
#include <X11/Xlib.h>
 | 
			
		||||
#include <X11/Xutil.h>
 | 
			
		||||
#include <X11/Xos.h>
 | 
			
		||||
 | 
			
		||||
#include <vpx/vpx_image.h>
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <linux/videodev2.h>
 | 
			
		||||
#else /* __OSX__ */
 | 
			
		||||
#import "osx_video.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "line_info.h"
 | 
			
		||||
#include "settings.h"
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
 | 
			
		||||
#define inline__ inline __attribute__((always_inline))
 | 
			
		||||
 | 
			
		||||
extern struct user_settings *user_settings;
 | 
			
		||||
 | 
			
		||||
struct VideoBuffer {
 | 
			
		||||
    void *start;
 | 
			
		||||
    size_t length;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct VideoDevice {
 | 
			
		||||
    VideoDataHandleCallback cb;             /* Use this to handle data from input device usually */
 | 
			
		||||
    void* cb_data;                          /* Data to be passed to callback */
 | 
			
		||||
    int32_t friend_number;                  /* ToxAV friend number */
 | 
			
		||||
    
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
    int fd;                                 /* File descriptor of video device selected/opened */
 | 
			
		||||
    struct v4l2_format fmt;
 | 
			
		||||
    struct VideoBuffer *buffers;
 | 
			
		||||
    uint32_t n_buffers;
 | 
			
		||||
#endif 
 | 
			
		||||
 | 
			
		||||
    uint32_t ref_count;
 | 
			
		||||
    int32_t selection;
 | 
			
		||||
    pthread_mutex_t mutex[1];
 | 
			
		||||
    uint16_t video_width;
 | 
			
		||||
    uint16_t video_height;
 | 
			
		||||
 | 
			
		||||
    vpx_image_t input;
 | 
			
		||||
 | 
			
		||||
    Display *x_display;
 | 
			
		||||
    Window x_window;
 | 
			
		||||
    GC x_gc;
 | 
			
		||||
 | 
			
		||||
} VideoDevice;
 | 
			
		||||
 | 
			
		||||
const char *dvideo_device_names[2];              /* Default device */
 | 
			
		||||
const char *video_devices_names[2][MAX_DEVICES]; /* Container of available devices */
 | 
			
		||||
static int size[2];                        /* Size of above containers */
 | 
			
		||||
VideoDevice *video_devices_running[2][MAX_DEVICES] = {{NULL}};     /* Running devices */
 | 
			
		||||
uint32_t primary_video_device[2];          /* Primary device */
 | 
			
		||||
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
static ToxAV* av = NULL;
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
/* q_mutex */
 | 
			
		||||
#define lock pthread_mutex_lock(&video_mutex);
 | 
			
		||||
#define unlock pthread_mutex_unlock(&video_mutex);
 | 
			
		||||
pthread_mutex_t video_mutex;
 | 
			
		||||
 | 
			
		||||
bool video_thread_running = true,
 | 
			
		||||
      video_thread_paused = true;                /* Thread control */
 | 
			
		||||
 | 
			
		||||
void* video_thread_poll(void*);
 | 
			
		||||
 | 
			
		||||
static void yuv420tobgr(uint16_t width, uint16_t height, const uint8_t *y, 
 | 
			
		||||
                 const uint8_t *u, const uint8_t *v, unsigned int ystride, 
 | 
			
		||||
                 unsigned int ustride, unsigned int vstride, uint8_t *out)
 | 
			
		||||
{
 | 
			
		||||
    unsigned long int i, j;
 | 
			
		||||
    for (i = 0; i < height; ++i) {
 | 
			
		||||
        for (j = 0; j < width; ++j) {
 | 
			
		||||
            uint8_t *point = out + 4 * ((i * width) + j);
 | 
			
		||||
            int t_y = y[((i * ystride) + j)];
 | 
			
		||||
            int t_u = u[(((i / 2) * ustride) + (j / 2))];
 | 
			
		||||
            int t_v = v[(((i / 2) * vstride) + (j / 2))];
 | 
			
		||||
            t_y = t_y < 16 ? 16 : t_y;
 | 
			
		||||
 | 
			
		||||
            int r = (298 * (t_y - 16) + 409 * (t_v - 128) + 128) >> 8;
 | 
			
		||||
            int g = (298 * (t_y - 16) - 100 * (t_u - 128) - 208 * (t_v - 128) + 128) >> 8;
 | 
			
		||||
            int b = (298 * (t_y - 16) + 516 * (t_u - 128) + 128) >> 8;
 | 
			
		||||
 | 
			
		||||
            point[2] = r>255? 255 : r<0 ? 0 : r;
 | 
			
		||||
            point[1] = g>255? 255 : g<0 ? 0 : g;
 | 
			
		||||
            point[0] = b>255? 255 : b<0 ? 0 : b;
 | 
			
		||||
            point[3] = ~0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
static void yuv422to420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, 
 | 
			
		||||
                 uint8_t *input, uint16_t width, uint16_t height)
 | 
			
		||||
{
 | 
			
		||||
    uint8_t *end = input + width * height * 2;
 | 
			
		||||
    while (input != end) {
 | 
			
		||||
        uint8_t *line_end = input + width * 2;
 | 
			
		||||
        while (input != line_end) {
 | 
			
		||||
            *plane_y++ = *input++;
 | 
			
		||||
            *plane_u++ = *input++;
 | 
			
		||||
            *plane_y++ = *input++;
 | 
			
		||||
            *plane_v++ = *input++;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        line_end = input + width * 2;
 | 
			
		||||
        while (input != line_end) {
 | 
			
		||||
            *plane_y++ = *input++;
 | 
			
		||||
            input++;//u
 | 
			
		||||
            *plane_y++ = *input++;
 | 
			
		||||
            input++;//v
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int xioctl(int fh, unsigned long request, void *arg)
 | 
			
		||||
{
 | 
			
		||||
    int r;
 | 
			
		||||
 | 
			
		||||
    do {
 | 
			
		||||
        r = ioctl(fh, request, arg);
 | 
			
		||||
    } while (-1 == r && EINTR == errno);
 | 
			
		||||
 | 
			
		||||
    return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* __linux__ */
 | 
			
		||||
 | 
			
		||||
/* Meet devices */
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
VideoDeviceError init_video_devices(ToxAV* av_)
 | 
			
		||||
#else
 | 
			
		||||
VideoDeviceError init_video_devices()
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
{
 | 
			
		||||
    size[vdt_input] = 0;
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
    for (; size[vdt_input] <= MAX_DEVICES; ++size[vdt_input]) {
 | 
			
		||||
        int fd;
 | 
			
		||||
        char device_address[] = "/dev/videoXX";
 | 
			
		||||
        snprintf(device_address + 10, sizeof(char) * strlen(device_address) - 10, "%i", size[vdt_input]);
 | 
			
		||||
 | 
			
		||||
        fd = open(device_address, O_RDWR | O_NONBLOCK, 0);
 | 
			
		||||
        if ( fd == -1 ) {
 | 
			
		||||
            break;
 | 
			
		||||
        } else {
 | 
			
		||||
            struct v4l2_capability cap;
 | 
			
		||||
            char* video_input_name;
 | 
			
		||||
 | 
			
		||||
            /* Query V4L for capture capabilities */
 | 
			
		||||
            if ( -1 != ioctl(fd, VIDIOC_QUERYCAP, &cap) ) {
 | 
			
		||||
                video_input_name = (char*)malloc(strlen((const char*)cap.card) + strlen(device_address) + 4);
 | 
			
		||||
                strcpy(video_input_name, (char*)cap.card);
 | 
			
		||||
                strcat(video_input_name, " (");
 | 
			
		||||
                strcat(video_input_name, (char*)device_address);
 | 
			
		||||
                strcat(video_input_name, ")");
 | 
			
		||||
            } else {
 | 
			
		||||
                video_input_name = (char*)malloc(strlen(device_address) + 3);
 | 
			
		||||
                strcpy(video_input_name, "(");
 | 
			
		||||
                strcat(video_input_name, device_address);
 | 
			
		||||
                strcat(video_input_name, ")");
 | 
			
		||||
            }
 | 
			
		||||
            video_devices_names[vdt_input][size[vdt_input]] = video_input_name;
 | 
			
		||||
 | 
			
		||||
            close(fd);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#else /* __OSX__ */
 | 
			
		||||
    if( osx_video_init((char**)video_devices_names[vdt_input], &size[vdt_input]) != 0 )
 | 
			
		||||
        return vde_InternalError;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    size[vdt_output] = 1;
 | 
			
		||||
    char* video_output_name = "Toxic Video Receiver";
 | 
			
		||||
    video_devices_names[vdt_output][0] = video_output_name;
 | 
			
		||||
 | 
			
		||||
    // Start poll thread
 | 
			
		||||
    if ( pthread_mutex_init(&video_mutex, NULL) != 0 )
 | 
			
		||||
        return vde_InternalError;
 | 
			
		||||
 | 
			
		||||
    pthread_t thread_id;
 | 
			
		||||
    if ( pthread_create(&thread_id, NULL, video_thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0 )
 | 
			
		||||
        return vde_InternalError;
 | 
			
		||||
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
    av = av_;
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
    return (VideoDeviceError) vde_None;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VideoDeviceError terminate_video_devices()
 | 
			
		||||
{
 | 
			
		||||
    /* Cleanup if needed */
 | 
			
		||||
    video_thread_running = false;
 | 
			
		||||
    usleep(20000);
 | 
			
		||||
 | 
			
		||||
    int i;
 | 
			
		||||
    for (i = 0; i < size[vdt_input]; ++i) {
 | 
			
		||||
        free((void*)video_devices_names[vdt_input][i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( pthread_mutex_destroy(&video_mutex) != 0 )
 | 
			
		||||
        return (VideoDeviceError) vde_InternalError;
 | 
			
		||||
 | 
			
		||||
#ifdef __OSX__
 | 
			
		||||
    osx_video_release();
 | 
			
		||||
#endif /* __OSX__ */
 | 
			
		||||
 | 
			
		||||
    return (VideoDeviceError) vde_None;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VideoDeviceError register_video_device_callback(int32_t friend_number, uint32_t device_idx, 
 | 
			
		||||
                                                VideoDataHandleCallback callback, void* data)
 | 
			
		||||
{
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
    if ( size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx] || !video_devices_running[vdt_input][device_idx]->fd ) 
 | 
			
		||||
        return vde_InvalidSelection;
 | 
			
		||||
#else /* __OSX__ */
 | 
			
		||||
    if ( size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx] ) 
 | 
			
		||||
        return vde_InvalidSelection;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    lock;
 | 
			
		||||
    video_devices_running[vdt_input][device_idx]->cb = callback;
 | 
			
		||||
    video_devices_running[vdt_input][device_idx]->cb_data = data;
 | 
			
		||||
    video_devices_running[vdt_input][device_idx]->friend_number = friend_number;
 | 
			
		||||
    unlock;
 | 
			
		||||
 | 
			
		||||
    return vde_None;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VideoDeviceError set_primary_video_device(VideoDeviceType type, int32_t selection)
 | 
			
		||||
{
 | 
			
		||||
    if ( size[type] <= selection || selection < 0 ) return vde_InvalidSelection;
 | 
			
		||||
   
 | 
			
		||||
    primary_video_device[type] = selection;
 | 
			
		||||
    
 | 
			
		||||
    return vde_None;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VideoDeviceError open_primary_video_device(VideoDeviceType type, uint32_t* device_idx)
 | 
			
		||||
{
 | 
			
		||||
    return open_video_device(type, primary_video_device[type], device_idx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void get_primary_video_device_name(VideoDeviceType type, char *buf, int size)
 | 
			
		||||
{
 | 
			
		||||
    memcpy(buf, dvideo_device_names[type], size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint32_t* device_idx)
 | 
			
		||||
{
 | 
			
		||||
    if ( size[type] <= selection || selection < 0 ) return vde_InvalidSelection;
 | 
			
		||||
    
 | 
			
		||||
    lock;
 | 
			
		||||
    
 | 
			
		||||
    uint32_t i;
 | 
			
		||||
    for (i = 0; i < MAX_DEVICES && video_devices_running[type][i]; ++i);
 | 
			
		||||
    
 | 
			
		||||
    if (i == MAX_DEVICES) { unlock; return vde_AllDevicesBusy; }
 | 
			
		||||
    else *device_idx = i;
 | 
			
		||||
    
 | 
			
		||||
    for (i = 0; i < MAX_DEVICES; i ++) { /* Check if any device has the same selection */
 | 
			
		||||
        if ( video_devices_running[type][i] && video_devices_running[type][i]->selection == selection ) {
 | 
			
		||||
 | 
			
		||||
            video_devices_running[type][*device_idx] = video_devices_running[type][i];            
 | 
			
		||||
            video_devices_running[type][i]->ref_count ++;
 | 
			
		||||
            
 | 
			
		||||
            unlock;
 | 
			
		||||
            return vde_None;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    VideoDevice* device = video_devices_running[type][*device_idx] = calloc(1, sizeof(VideoDevice));
 | 
			
		||||
    device->selection = selection;
 | 
			
		||||
    
 | 
			
		||||
    if ( pthread_mutex_init(device->mutex, NULL) != 0 ) {
 | 
			
		||||
        free(device);
 | 
			
		||||
        unlock;
 | 
			
		||||
        return vde_InternalError;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    if ( type == vdt_input ) {
 | 
			
		||||
        video_thread_paused = true;
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
        /* Open selected device */
 | 
			
		||||
        char device_address[] = "/dev/videoXX";
 | 
			
		||||
        snprintf(device_address + 10 , sizeof(device_address) - 10, "%i", selection);
 | 
			
		||||
 | 
			
		||||
        device->fd = open(device_address, O_RDWR);
 | 
			
		||||
        if ( device->fd == -1 ) {
 | 
			
		||||
            return vde_FailedStart;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Obtain video device capabilities */
 | 
			
		||||
        struct v4l2_capability cap;
 | 
			
		||||
        if ( -1 == xioctl(device->fd, VIDIOC_QUERYCAP, &cap) ) {
 | 
			
		||||
            close(device->fd);
 | 
			
		||||
            free(device);
 | 
			
		||||
 | 
			
		||||
            return vde_FailedStart;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Setup video format */
 | 
			
		||||
        struct v4l2_format fmt;
 | 
			
		||||
        memset(&(fmt), 0, sizeof(fmt));
 | 
			
		||||
 | 
			
		||||
        fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 | 
			
		||||
        fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
 | 
			
		||||
        if( -1 == xioctl(device->fd, VIDIOC_G_FMT, &fmt) ) {
 | 
			
		||||
            close(device->fd);
 | 
			
		||||
            free(device);
 | 
			
		||||
 | 
			
		||||
            return vde_FailedStart;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        device->video_width = fmt.fmt.pix.width;
 | 
			
		||||
        device->video_height = fmt.fmt.pix.height;
 | 
			
		||||
 | 
			
		||||
        /* Request buffers */
 | 
			
		||||
        struct v4l2_requestbuffers req;
 | 
			
		||||
        memset(&(req), 0, sizeof(req));
 | 
			
		||||
        req.count = 4;
 | 
			
		||||
        req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 | 
			
		||||
        req.memory = V4L2_MEMORY_MMAP;
 | 
			
		||||
        if ( -1 == xioctl(device->fd, VIDIOC_REQBUFS, &req) ) {
 | 
			
		||||
            close(device->fd);
 | 
			
		||||
            free(device);
 | 
			
		||||
 | 
			
		||||
            return vde_FailedStart;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ( req.count < 2 ) {
 | 
			
		||||
            close(device->fd);
 | 
			
		||||
            free(device);
 | 
			
		||||
 | 
			
		||||
            return vde_FailedStart;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        device->buffers = calloc(req.count, sizeof(struct VideoBuffer));
 | 
			
		||||
 | 
			
		||||
        for (i = 0; i < req.count; ++i) {
 | 
			
		||||
            struct v4l2_buffer buf;
 | 
			
		||||
            memset(&(buf), 0, sizeof(buf));
 | 
			
		||||
 | 
			
		||||
            buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 | 
			
		||||
            buf.memory = V4L2_MEMORY_MMAP;
 | 
			
		||||
            buf.index = i;
 | 
			
		||||
 | 
			
		||||
            if ( -1 == xioctl(device->fd, VIDIOC_QUERYBUF, &buf) ) {
 | 
			
		||||
                close(device->fd);
 | 
			
		||||
                free(device);
 | 
			
		||||
 | 
			
		||||
                return vde_FailedStart;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            device->buffers[i].length = buf.length;
 | 
			
		||||
            device->buffers[i].start = mmap(NULL /* start anywhere */,
 | 
			
		||||
                          buf.length,
 | 
			
		||||
                          PROT_READ | PROT_WRITE /* required */,
 | 
			
		||||
                          MAP_SHARED /* recommended */,
 | 
			
		||||
                          device->fd, buf.m.offset);
 | 
			
		||||
 | 
			
		||||
            if ( MAP_FAILED == device->buffers[i].start ) {
 | 
			
		||||
                for (i = 0; i < buf.index; ++i)
 | 
			
		||||
                    munmap(device->buffers[i].start, device->buffers[i].length);
 | 
			
		||||
                close(device->fd);
 | 
			
		||||
                free(device);
 | 
			
		||||
 | 
			
		||||
                return vde_FailedStart;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        device->n_buffers = i;
 | 
			
		||||
 | 
			
		||||
        enum v4l2_buf_type type;
 | 
			
		||||
 | 
			
		||||
        for (i = 0; i < device->n_buffers; ++i) {
 | 
			
		||||
            struct v4l2_buffer buf;
 | 
			
		||||
            memset(&(buf), 0, sizeof(buf));
 | 
			
		||||
 | 
			
		||||
            buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 | 
			
		||||
            buf.memory = V4L2_MEMORY_MMAP;
 | 
			
		||||
            buf.index = i;
 | 
			
		||||
 | 
			
		||||
            if ( -1 == xioctl(device->fd, VIDIOC_QBUF, &buf) ) {
 | 
			
		||||
                for (i = 0; i < device->n_buffers; ++i)
 | 
			
		||||
                    munmap(device->buffers[i].start, device->buffers[i].length);
 | 
			
		||||
                close(device->fd);
 | 
			
		||||
                free(device);
 | 
			
		||||
 | 
			
		||||
                return vde_FailedStart;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 | 
			
		||||
 | 
			
		||||
        /* Turn on video stream */
 | 
			
		||||
        if ( -1 == xioctl(device->fd, VIDIOC_STREAMON, &type) ) {
 | 
			
		||||
            close_video_device(vdt_input, *device_idx);
 | 
			
		||||
 | 
			
		||||
            return vde_FailedStart;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
#else /* __OSX__ */
 | 
			
		||||
        if ( osx_video_open_device(selection, &device->video_width, &device->video_height) != 0 ) {
 | 
			
		||||
            free(device);
 | 
			
		||||
 | 
			
		||||
            return vde_FailedStart;
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        /* Create X11 window associated to device */
 | 
			
		||||
        if ( (device->x_display = XOpenDisplay(NULL)) == NULL ) {
 | 
			
		||||
            close_video_device(vdt_input, *device_idx);
 | 
			
		||||
 | 
			
		||||
            return vde_FailedStart;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int screen = DefaultScreen(device->x_display);
 | 
			
		||||
 | 
			
		||||
        if ( !(device->x_window = XCreateSimpleWindow(device->x_display, RootWindow(device->x_display, screen), 0, 0,
 | 
			
		||||
                    device->video_width, device->video_height, 0, BlackPixel(device->x_display, screen), 
 | 
			
		||||
                    BlackPixel(device->x_display, screen))) ) {
 | 
			
		||||
            close_video_device(vdt_input, *device_idx);
 | 
			
		||||
 | 
			
		||||
            return vde_FailedStart;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        XStoreName(device->x_display, device->x_window, "Video Preview");
 | 
			
		||||
        XSelectInput(device->x_display, device->x_window, ExposureMask|ButtonPressMask|KeyPressMask);
 | 
			
		||||
 | 
			
		||||
        if ( (device->x_gc = DefaultGC(device->x_display, screen)) == NULL ) {
 | 
			
		||||
            close_video_device(vdt_input, *device_idx);
 | 
			
		||||
 | 
			
		||||
            return vde_FailedStart;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Disable user from manually closing the X11 window */
 | 
			
		||||
        Atom wm_delete_window = XInternAtom(device->x_display, "WM_DELETE_WINDOW", false);
 | 
			
		||||
        XSetWMProtocols(device->x_display, device->x_window, &wm_delete_window, 1);
 | 
			
		||||
 | 
			
		||||
        XMapWindow(device->x_display, device->x_window);
 | 
			
		||||
        XClearWindow(device->x_display, device->x_window);
 | 
			
		||||
        XMapRaised(device->x_display, device->x_window);
 | 
			
		||||
        XFlush(device->x_display);
 | 
			
		||||
 | 
			
		||||
        vpx_img_alloc(&device->input, VPX_IMG_FMT_I420, device->video_width, device->video_height, 1);
 | 
			
		||||
 | 
			
		||||
        video_thread_paused = false;
 | 
			
		||||
    } else { /* vdt_output */
 | 
			
		||||
 | 
			
		||||
        /* Create X11 window associated to device */
 | 
			
		||||
        if ( (device->x_display = XOpenDisplay(NULL)) == NULL ) {
 | 
			
		||||
            close_video_device(vdt_output, *device_idx);
 | 
			
		||||
 | 
			
		||||
            return vde_FailedStart;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int screen = DefaultScreen(device->x_display);
 | 
			
		||||
 | 
			
		||||
        if ( !(device->x_window = XCreateSimpleWindow(device->x_display, RootWindow(device->x_display, screen), 0, 0,
 | 
			
		||||
                    100, 100, 0, BlackPixel(device->x_display, screen), BlackPixel(device->x_display, screen))) ) {
 | 
			
		||||
            close_video_device(vdt_output, *device_idx);
 | 
			
		||||
 | 
			
		||||
            return vde_FailedStart;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        XStoreName(device->x_display, device->x_window, "Video Receive");
 | 
			
		||||
        XSelectInput(device->x_display, device->x_window, ExposureMask|ButtonPressMask|KeyPressMask);
 | 
			
		||||
 | 
			
		||||
        if ( (device->x_gc = DefaultGC(device->x_display, screen)) == NULL ) {
 | 
			
		||||
            close_video_device(vdt_output, *device_idx);
 | 
			
		||||
 | 
			
		||||
            return vde_FailedStart;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Disable user from manually closing the X11 window */
 | 
			
		||||
        Atom wm_delete_window = XInternAtom(device->x_display, "WM_DELETE_WINDOW", false);
 | 
			
		||||
        XSetWMProtocols(device->x_display, device->x_window, &wm_delete_window, 1);
 | 
			
		||||
 | 
			
		||||
        XMapWindow(device->x_display, device->x_window);
 | 
			
		||||
        XClearWindow(device->x_display, device->x_window);
 | 
			
		||||
        XMapRaised(device->x_display, device->x_window);
 | 
			
		||||
        XFlush(device->x_display);
 | 
			
		||||
 | 
			
		||||
        vpx_img_alloc(&device->input, VPX_IMG_FMT_I420, device->video_width, device->video_height, 1);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    unlock;
 | 
			
		||||
    return vde_None;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__inline VideoDeviceError write_video_out(uint16_t width, uint16_t height,
 | 
			
		||||
                                          uint8_t const *y, uint8_t const *u, uint8_t const *v,
 | 
			
		||||
                                          int32_t ystride, int32_t ustride, int32_t vstride,
 | 
			
		||||
                                          void *user_data)
 | 
			
		||||
{
 | 
			
		||||
    VideoDevice* device = video_devices_running[vdt_output][0];
 | 
			
		||||
 | 
			
		||||
    if ( !device ) return vde_DeviceNotActive;
 | 
			
		||||
 | 
			
		||||
    if( !device->x_window ) return vde_DeviceNotActive;
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_lock(device->mutex);
 | 
			
		||||
 | 
			
		||||
    /* Resize X11 window to correct size */
 | 
			
		||||
    if ( device->video_width != width || device->video_height != height ) {
 | 
			
		||||
        device->video_width = width;
 | 
			
		||||
        device->video_height = height;
 | 
			
		||||
        XResizeWindow(device->x_display, device->x_window, width, height);
 | 
			
		||||
 | 
			
		||||
        vpx_img_free(&device->input);
 | 
			
		||||
        vpx_img_alloc(&device->input, VPX_IMG_FMT_I420, width, height, 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Convert YUV420 data to BGR */
 | 
			
		||||
    ystride = abs(ystride);
 | 
			
		||||
    ustride = abs(ustride);
 | 
			
		||||
    vstride = abs(vstride);
 | 
			
		||||
    uint8_t *img_data = malloc(width * height * 4);
 | 
			
		||||
    yuv420tobgr(width, height, y, u, v, ystride, ustride, vstride, img_data);
 | 
			
		||||
 | 
			
		||||
    /* Allocate image data in X11 */
 | 
			
		||||
    XImage image = {
 | 
			
		||||
        .width = width,
 | 
			
		||||
        .height = height,
 | 
			
		||||
        .depth = 24,
 | 
			
		||||
        .bits_per_pixel = 32,
 | 
			
		||||
        .format = ZPixmap,
 | 
			
		||||
        .byte_order = LSBFirst,
 | 
			
		||||
        .bitmap_unit = 8,
 | 
			
		||||
        .bitmap_bit_order = LSBFirst,
 | 
			
		||||
        .bytes_per_line = width * 4,
 | 
			
		||||
        .red_mask = 0xFF0000,
 | 
			
		||||
        .green_mask = 0xFF00,
 | 
			
		||||
        .blue_mask = 0xFF,
 | 
			
		||||
        .data = (char*)img_data
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /* Render image data */
 | 
			
		||||
    Pixmap pixmap = XCreatePixmap(device->x_display, device->x_window, width, height, 24);
 | 
			
		||||
    XPutImage(device->x_display, pixmap, device->x_gc, &image, 0, 0, 0, 0, width, height);
 | 
			
		||||
    XCopyArea(device->x_display, pixmap, device->x_window, device->x_gc, 0, 0, width, height, 0, 0);
 | 
			
		||||
    XFreePixmap(device->x_display, pixmap);
 | 
			
		||||
    XFlush(device->x_display);
 | 
			
		||||
    free(img_data);
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_unlock(device->mutex);
 | 
			
		||||
    return vde_None;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void* video_thread_poll (void* arg) // TODO: maybe use thread for every input source
 | 
			
		||||
{
 | 
			
		||||
    /*
 | 
			
		||||
     * NOTE: We only need to poll input devices for data.
 | 
			
		||||
     */
 | 
			
		||||
    (void)arg;
 | 
			
		||||
    uint32_t i;
 | 
			
		||||
    
 | 
			
		||||
    while (video_thread_running)
 | 
			
		||||
    {
 | 
			
		||||
        if ( video_thread_paused ) usleep(10000); /* Wait for unpause. */
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            for (i = 0; i < size[vdt_input]; ++i)
 | 
			
		||||
             {
 | 
			
		||||
                lock;
 | 
			
		||||
                if ( video_devices_running[vdt_input][i] != NULL ) 
 | 
			
		||||
                {
 | 
			
		||||
                    /* Obtain frame image data from device buffers */
 | 
			
		||||
                    VideoDevice* device = video_devices_running[vdt_input][i];
 | 
			
		||||
                    uint16_t video_width = device->video_width;
 | 
			
		||||
                    uint16_t video_height = device->video_height;
 | 
			
		||||
                    uint8_t *y = device->input.planes[0];
 | 
			
		||||
                    uint8_t *u = device->input.planes[1];
 | 
			
		||||
                    uint8_t *v = device->input.planes[2];
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
                    struct v4l2_buffer buf;
 | 
			
		||||
                    memset(&(buf), 0, sizeof(buf));
 | 
			
		||||
 | 
			
		||||
                    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 | 
			
		||||
                    buf.memory = V4L2_MEMORY_MMAP;
 | 
			
		||||
 | 
			
		||||
                    if ( -1 == ioctl(device->fd, VIDIOC_DQBUF, &buf) ) {
 | 
			
		||||
                        unlock;
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }   
 | 
			
		||||
 | 
			
		||||
                    void *data = (void*)device->buffers[buf.index].start;
 | 
			
		||||
 | 
			
		||||
                    /* Convert frame image data to YUV420 for ToxAV */
 | 
			
		||||
                    yuv422to420(y, u, v, data, video_width, video_height);
 | 
			
		||||
 | 
			
		||||
#else /* __OSX__*/
 | 
			
		||||
                    if ( osx_video_read_device(y, u, v, &video_width, &video_height) != 0 ) {
 | 
			
		||||
                        unlock;
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
                    /* Send frame data to friend through ToxAV */
 | 
			
		||||
                    if ( device->cb )
 | 
			
		||||
                        device->cb(video_width, video_height, y, u, v, device->cb_data);
 | 
			
		||||
 | 
			
		||||
                    /* Convert YUV420 data to BGR */
 | 
			
		||||
                    uint8_t *img_data = malloc(video_width * video_height * 4);
 | 
			
		||||
                    yuv420tobgr(video_width, video_height, y, u, v, 
 | 
			
		||||
                                video_width, video_width/2, video_width/2, img_data);
 | 
			
		||||
 | 
			
		||||
                    /* Allocate image data in X11 */
 | 
			
		||||
                    XImage image = {
 | 
			
		||||
                        .width = video_width,
 | 
			
		||||
                        .height = video_height,
 | 
			
		||||
                        .depth = 24,
 | 
			
		||||
                        .bits_per_pixel = 32,
 | 
			
		||||
                        .format = ZPixmap,
 | 
			
		||||
                        .byte_order = LSBFirst,
 | 
			
		||||
                        .bitmap_unit = 8,
 | 
			
		||||
                        .bitmap_bit_order = LSBFirst,
 | 
			
		||||
                        .bytes_per_line = video_width * 4,
 | 
			
		||||
                        .red_mask = 0xFF0000,
 | 
			
		||||
                        .green_mask = 0xFF00,
 | 
			
		||||
                        .blue_mask = 0xFF,
 | 
			
		||||
                        .data = (char*)img_data
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    /* Render image data */
 | 
			
		||||
                    Pixmap pixmap = XCreatePixmap(device->x_display, device->x_window, video_width, video_height, 24);
 | 
			
		||||
                    XPutImage(device->x_display, pixmap, device->x_gc, &image, 0, 0, 0, 0, video_width, video_height);
 | 
			
		||||
                    XCopyArea(device->x_display, pixmap, device->x_window, device->x_gc, 0, 0, video_width, video_height, 0, 0);
 | 
			
		||||
                    XFreePixmap(device->x_display, pixmap);
 | 
			
		||||
                    XFlush(device->x_display);
 | 
			
		||||
                    free(img_data);
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
                    if ( -1 == xioctl(device->fd, VIDIOC_QBUF, &buf) ) {
 | 
			
		||||
                        unlock;
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
#endif /* __linux__ */
 | 
			
		||||
                    
 | 
			
		||||
                }
 | 
			
		||||
                unlock;
 | 
			
		||||
            }
 | 
			
		||||
            usleep(1000 * 1000 / 24);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    pthread_exit(NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx)
 | 
			
		||||
{
 | 
			
		||||
    if ( device_idx >= MAX_DEVICES ) return vde_InvalidSelection;
 | 
			
		||||
    
 | 
			
		||||
    lock;
 | 
			
		||||
    VideoDevice *device = video_devices_running[type][device_idx];
 | 
			
		||||
    VideoDeviceError rc = vde_None;
 | 
			
		||||
    
 | 
			
		||||
    if ( !device ) { 
 | 
			
		||||
        unlock;
 | 
			
		||||
        return vde_DeviceNotActive;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    video_devices_running[type][device_idx] = NULL;
 | 
			
		||||
    
 | 
			
		||||
    if ( !device->ref_count ) {
 | 
			
		||||
        
 | 
			
		||||
        if ( type == vdt_input ) {
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
            enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 | 
			
		||||
            if( -1 == xioctl(device->fd, VIDIOC_STREAMOFF, &buf_type) ) {}
 | 
			
		||||
 | 
			
		||||
            int i;
 | 
			
		||||
            for (i = 0; i < device->n_buffers; ++i) {
 | 
			
		||||
                if ( -1 == munmap(device->buffers[i].start, device->buffers[i].length) ) {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            close(device->fd);
 | 
			
		||||
 | 
			
		||||
#else /* __OSX__ */
 | 
			
		||||
            osx_video_close_device(device_idx);
 | 
			
		||||
#endif
 | 
			
		||||
            vpx_img_free(&device->input);
 | 
			
		||||
            XDestroyWindow(device->x_display, device->x_window);
 | 
			
		||||
            XFlush(device->x_display);
 | 
			
		||||
            XCloseDisplay(device->x_display);
 | 
			
		||||
            pthread_mutex_destroy(device->mutex);
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
            free(device->buffers);
 | 
			
		||||
#endif /* __linux__ */
 | 
			
		||||
 | 
			
		||||
            free(device);
 | 
			
		||||
        } else {   
 | 
			
		||||
            vpx_img_free(&device->input);
 | 
			
		||||
            XDestroyWindow(device->x_display, device->x_window);
 | 
			
		||||
            XFlush(device->x_display);
 | 
			
		||||
            XCloseDisplay(device->x_display);
 | 
			
		||||
            pthread_mutex_destroy(device->mutex);
 | 
			
		||||
            free(device); 
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    else device->ref_count--;
 | 
			
		||||
    
 | 
			
		||||
    unlock;
 | 
			
		||||
    return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void print_video_devices(ToxWindow* self, VideoDeviceType type)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < size[type]; ++i)
 | 
			
		||||
        line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d: %s", i, video_devices_names[type][i]);
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VideoDeviceError video_selection_valid(VideoDeviceType type, int32_t selection)
 | 
			
		||||
{
 | 
			
		||||
    return (size[type] <= selection || selection < 0) ? vde_InvalidSelection : vde_None;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										76
									
								
								src/video_device.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								src/video_device.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
			
		||||
/*  video_device.h
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2014 Toxic All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This file is part of Toxic.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic 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.
 | 
			
		||||
 *
 | 
			
		||||
 *  Toxic 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 Toxic.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef VIDEO_DEVICE_H
 | 
			
		||||
#define VIDEO_DEVICE_H
 | 
			
		||||
 | 
			
		||||
#define MAX_DEVICES 32
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include "windows.h"
 | 
			
		||||
 | 
			
		||||
typedef enum VideoDeviceType {
 | 
			
		||||
    vdt_input,
 | 
			
		||||
    vdt_output,
 | 
			
		||||
} VideoDeviceType;
 | 
			
		||||
 | 
			
		||||
typedef enum VideoDeviceError {
 | 
			
		||||
    vde_None,
 | 
			
		||||
    vde_InternalError = -1,
 | 
			
		||||
    vde_InvalidSelection = -2,
 | 
			
		||||
    vde_FailedStart = -3,
 | 
			
		||||
    vde_Busy = -4,
 | 
			
		||||
    vde_AllDevicesBusy = -5,
 | 
			
		||||
    vde_DeviceNotActive = -6,
 | 
			
		||||
    vde_BufferError = -7,
 | 
			
		||||
    vde_UnsupportedMode = -8,
 | 
			
		||||
    vde_CaptureError = -9,
 | 
			
		||||
} VideoDeviceError;
 | 
			
		||||
 | 
			
		||||
typedef void (*VideoDataHandleCallback) (int16_t width, int16_t height, const uint8_t* y, const uint8_t* u, const uint8_t* v, void* data);
 | 
			
		||||
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
VideoDeviceError init_video_devices(ToxAV* av);
 | 
			
		||||
#else
 | 
			
		||||
VideoDeviceError init_video_devices();
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
VideoDeviceError terminate_video_devices();
 | 
			
		||||
 | 
			
		||||
/* Callback handles ready data from INPUT device */
 | 
			
		||||
VideoDeviceError register_video_device_callback(int32_t call_idx, uint32_t device_idx, VideoDataHandleCallback callback, void* data);
 | 
			
		||||
void* get_video_device_callback_data(uint32_t device_idx);
 | 
			
		||||
 | 
			
		||||
VideoDeviceError set_primary_video_device(VideoDeviceType type, int32_t selection);
 | 
			
		||||
VideoDeviceError open_primary_video_device(VideoDeviceType type, uint32_t* device_idx);
 | 
			
		||||
/* Start device */
 | 
			
		||||
VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint32_t* device_idx);
 | 
			
		||||
/* Stop device */
 | 
			
		||||
VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx);
 | 
			
		||||
 | 
			
		||||
/* Write data to device */
 | 
			
		||||
VideoDeviceError write_video_out(uint16_t width, uint16_t height, uint8_t const *y, uint8_t const *u, uint8_t const *v, int32_t ystride, int32_t ustride, int32_t vstride, void *user_data);
 | 
			
		||||
 | 
			
		||||
void print_video_devices(ToxWindow* self, VideoDeviceType type);
 | 
			
		||||
void get_primary_video_device_name(VideoDeviceType type, char *buf, int size);
 | 
			
		||||
 | 
			
		||||
VideoDeviceError video_selection_valid(VideoDeviceType type, int32_t selection);
 | 
			
		||||
#endif /* VIDEO_DEVICE_H */
 | 
			
		||||
@@ -76,7 +76,7 @@ struct cqueue_thread {
 | 
			
		||||
    pthread_t tid;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct audio_thread {
 | 
			
		||||
struct av_thread {
 | 
			
		||||
    pthread_t tid;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -135,24 +135,29 @@ struct ToxWindow {
 | 
			
		||||
 | 
			
		||||
#ifdef AUDIO
 | 
			
		||||
 | 
			
		||||
    void(*onInvite)(ToxWindow *, ToxAv *, int);
 | 
			
		||||
    void(*onRinging)(ToxWindow *, ToxAv *, int);
 | 
			
		||||
    void(*onStarting)(ToxWindow *, ToxAv *, int);
 | 
			
		||||
    void(*onEnding)(ToxWindow *, ToxAv *, int);
 | 
			
		||||
    void(*onError)(ToxWindow *, ToxAv *, int);
 | 
			
		||||
    void(*onStart)(ToxWindow *, ToxAv *, int);
 | 
			
		||||
    void(*onCancel)(ToxWindow *, ToxAv *, int);
 | 
			
		||||
    void(*onReject)(ToxWindow *, ToxAv *, int);
 | 
			
		||||
    void(*onEnd)(ToxWindow *, ToxAv *, int);
 | 
			
		||||
    void(*onRequestTimeout)(ToxWindow *, ToxAv *, int);
 | 
			
		||||
    void(*onPeerTimeout)(ToxWindow *, ToxAv *, int);
 | 
			
		||||
    void(*onWriteDevice)(ToxWindow *, Tox *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int);
 | 
			
		||||
    void(*onInvite)(ToxWindow *, ToxAV *, uint32_t, int);
 | 
			
		||||
    void(*onRinging)(ToxWindow *, ToxAV *, uint32_t, int);
 | 
			
		||||
    void(*onStarting)(ToxWindow *, ToxAV *, uint32_t, int);
 | 
			
		||||
    void(*onEnding)(ToxWindow *, ToxAV *, uint32_t, int);
 | 
			
		||||
    void(*onError)(ToxWindow *, ToxAV *, uint32_t, int);
 | 
			
		||||
    void(*onStart)(ToxWindow *, ToxAV *, uint32_t, int);
 | 
			
		||||
    void(*onCancel)(ToxWindow *, ToxAV *, uint32_t, int);
 | 
			
		||||
    void(*onReject)(ToxWindow *, ToxAV *, uint32_t, int);
 | 
			
		||||
    void(*onEnd)(ToxWindow *, ToxAV *, uint32_t, int);
 | 
			
		||||
    void(*onRequestTimeout)(ToxWindow *, ToxAV *, uint32_t, int);
 | 
			
		||||
    void(*onPeerTimeout)(ToxWindow *, ToxAV *, uint32_t, int);
 | 
			
		||||
    void(*onWriteDevice)(ToxWindow *, Tox *, uint32_t, int, const int16_t *, unsigned int, uint8_t, unsigned int);
 | 
			
		||||
 | 
			
		||||
    int call_idx; /* If in a call will have this index set, otherwise it's -1.
 | 
			
		||||
                   * Don't modify outside av callbacks. */
 | 
			
		||||
    int device_selection[2]; /* -1 if not set, if set uses these selections instead of primary device */
 | 
			
		||||
 | 
			
		||||
    bool is_call;
 | 
			
		||||
    int ringing_sound;
 | 
			
		||||
 | 
			
		||||
#ifdef VIDEO
 | 
			
		||||
 | 
			
		||||
    int video_device_selection[2]; /* -1 if not set, if set uses these selections instead of primary video device */
 | 
			
		||||
    
 | 
			
		||||
#endif /* VIDEO */
 | 
			
		||||
 | 
			
		||||
#endif /* AUDIO */
 | 
			
		||||
 | 
			
		||||
    int active_box; /* For box notify */
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user