From 9b6efb65de1bb148eceab7dcd0f29424013b459f Mon Sep 17 00:00:00 2001 From: cnhenry Date: Thu, 30 Jul 2015 01:49:27 -0500 Subject: [PATCH] Implemented video preview and preparations for ToxAV --- src/audio_call.c | 11 +- src/audio_call.h | 2 + src/audio_device.h | 2 +- src/chat_commands.h | 6 + src/execute.c | 8 ++ src/global_commands.h | 5 + src/prompt.c | 8 ++ src/video_call.c | 302 +++++++++++++++++++++++++++++++++++++----- src/video_call.h | 4 +- src/video_device.c | 174 +++++++++++++++++++----- src/windows.h | 5 + 11 files changed, 447 insertions(+), 80 deletions(-) diff --git a/src/audio_call.c b/src/audio_call.c index d6e6cec..be7ad1b 100644 --- a/src/audio_call.c +++ b/src/audio_call.c @@ -73,8 +73,6 @@ static int set_call(Call* call, bool start) return 0; } -CallControl CallContrl; - 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, @@ -124,18 +122,19 @@ ToxAV *init_audio(ToxWindow *self, Tox *tox) CallContrl.audio_channels = 1; #ifdef VIDEO + if ( !init_video(self, tox, CallContrl.av, &CallContrl) ) { line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init video"); return NULL; } - if (CallContrl.video_enabled == true) { - line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Video enabled"); - } + #else + CallContrl.video_enabled = false; CallContrl.video_bit_rate = 0; CallContrl.video_sample_rate = 0; CallContrl.video_frame_duration = 0; + #endif /* VIDEO */ memset(CallContrl.calls, 0, sizeof(CallContrl.calls)); @@ -343,7 +342,6 @@ void callback_recv_invite ( void* av, uint32_t friend_number, void* arg ) windows[i].onInvite(&windows[i], av, friend_number, cc->call_state); } } - void callback_recv_ringing ( void* av, uint32_t friend_number, void* arg ) { //CB_BODY(friend_number, arg, onRinging); @@ -385,7 +383,6 @@ void callback_recv_ending ( void* av, uint32_t friend_number, void* arg ) stop_transmission(&CallContrl.calls[friend_number], friend_number); } - void callback_call_started ( void* av, uint32_t friend_number, void* arg ) { CallControl* cc = arg; diff --git a/src/audio_call.h b/src/audio_call.h index 5d95314..70b8e61 100644 --- a/src/audio_call.h +++ b/src/audio_call.h @@ -66,6 +66,8 @@ typedef struct CallControl { } CallControl; +CallControl CallContrl; + /* You will have to pass pointer to first member of 'windows' declared in windows.c */ ToxAV *init_audio(ToxWindow *self, Tox *tox); void terminate_audio(); diff --git a/src/audio_device.h b/src/audio_device.h index ca21887..cc1b268 100644 --- a/src/audio_device.h +++ b/src/audio_device.h @@ -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 */ diff --git a/src/chat_commands.h b/src/chat_commands.h index c66b77a..698ab22 100644 --- a/src/chat_commands.h +++ b/src/chat_commands.h @@ -43,4 +43,10 @@ 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_end_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 */ diff --git a/src/execute.c b/src/execute.c index 9e83a83..f8ffc97 100644 --- a/src/execute.c +++ b/src/execute.c @@ -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,10 @@ static struct cmd_func chat_commands[] = { { "/mute", cmd_mute }, { "/sense", cmd_sense }, #endif /* AUDIO */ +#ifdef VIDEO + { "/video", cmd_video }, + { "/endvideo", cmd_end_video }, +#endif /* VIDEO */ { NULL, NULL }, }; diff --git a/src/global_commands.h b/src/global_commands.h index 277f0f1..7749c45 100644 --- a/src/global_commands.h +++ b/src/global_commands.h @@ -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 */ diff --git a/src/prompt.c b/src/prompt.c index 7a189b0..5f95f36 100644 --- a/src/prompt.c +++ b/src/prompt.c @@ -81,6 +81,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) diff --git a/src/video_call.c b/src/video_call.c index 107f484..1d08a0e 100644 --- a/src/video_call.c +++ b/src/video_call.c @@ -7,10 +7,6 @@ #include "line_info.h" #include "notify.h" -#include -#include -#include - #include "assert.h" void receive_video_frame_cb( ToxAV *av, uint32_t friend_number, @@ -21,6 +17,9 @@ void receive_video_frame_cb( ToxAV *av, uint32_t friend_number, void video_bit_rate_status_cb( ToxAV *av, uint32_t friend_number, bool stable, uint32_t bit_rate, void *user_data); +void callback_video_starting ( void* av, uint32_t friend_number, void *arg ); +void callback_video_ending ( void* av, uint32_t friend_number, void *arg ); + static void print_err (ToxWindow *self, const char *error_str) { line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str); @@ -28,36 +27,6 @@ static void print_err (ToxWindow *self, const char *error_str) ToxAV *init_video(ToxWindow *self, Tox *tox, ToxAV *av, CallControl *user_data) { - XInitThreads(); - - Display *display; - if ((display = XOpenDisplay(NULL)) == NULL) { - line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open X11 display"); - return NULL; - } - - int screen; - screen = DefaultScreen(display); - - Window win; - if ((win = XCreateSimpleWindow(display, RootWindow(display, screen), 400, 400, 800, 600, 0, - BlackPixel(display, screen), WhitePixel(display, screen))) == NULL) { - line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create X11 window"); - return NULL; - } - - XSelectInput(display, win, ExposureMask|ButtonPressMask|KeyPressMask); - - GC default_gc; - if ((default_gc = DefaultGC(display, screen)) == NULL) { - line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create X11 graphics context"); - return NULL; - } - - XMapWindow(display, win); - XClearWindow(display, win); - XMapRaised(display, win); - XFlush(display); user_data->video_enabled = true; @@ -77,14 +46,80 @@ ToxAV *init_video(ToxWindow *self, Tox *tox, ToxAV *av, CallControl *user_data) return av; } +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) +{ + TOXAV_ERR_SEND_FRAME error; + int32_t friend_number = *((int32_t*)data); /* TODO: Or pass an array of call_idx's */ + + line_info_add(CallContrl.window, NULL, NULL, NULL, SYS_MSG, 0, 0, "Read video device"); +} + +void write_video_device_callback(void *agent, int32_t friend_number, const int16_t* PCM, uint16_t size, void* arg) +{ + (void)arg; + (void)agent; + + if (friend_number >= 0 && CallContrl.calls[friend_number].ttas) + write_out(CallContrl.calls[friend_number].out_idx, PCM, size, CallContrl.audio_channels); +} + +int start_video_transmission(ToxWindow *self, ToxAV *av, Call *call) +{ + if ( !self || !av) { + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Could not prepare transmission"); + return -1; + } + + //if (set_call(call, true) == -1) + // return -1; + + VideoDeviceError error = open_primary_video_device(input, &call->in_idx); + + if ( error == vde_FailedStart) + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to start input video device"); + + if ( error == vde_InternalError ) + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Internal error with opening input video device"); + + if ( error != vde_None ) + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open input video device!"); + + + if ( register_video_device_callback(self->num, call->in_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!"); + + /* + if ( open_primary_device(output, &call->out_idx, + CallContrl.audio_sample_rate, CallContrl.audio_frame_duration, CallContrl.audio_channels) != de_None ) { + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open output video device!"); + call->has_output = 0; + }*/ + + return 0; +} + +int stop_video_transmission(Call *call, int friend_number) +{ + if ( call->ttas ) { + if ( call->in_idx != -1 ) + close_video_device(input, call->in_idx); + return 0; + } + + return -1; +} + 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) { + CallControl* cc = user_data; } + void video_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, bool stable, uint32_t bit_rate, void *user_data) { @@ -94,8 +129,95 @@ void video_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, cc->video_bit_rate = bit_rate; } + +void callback_video_starting(void* av, uint32_t friend_number, void *arg) +{ + CallControl *cc = (CallControl*)arg; + ToxWindow* windows = cc->window; + + 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 starting"); + if(0 != start_video_transmission(&windows[i], av, &cc->calls[friend_number])) { + line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0, "Error starting transmission!"); + return; + } + } + } +} +void callback_video_ending(void* av, uint32_t friend_number, void *arg) +{ + CallControl *cc = (CallControl*)arg; + ToxWindow* windows = cc->window; + + line_info_add(&windows[friend_number], NULL, NULL, NULL, SYS_MSG, 0, 0, "Video ending"); + + 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 ending"); + } + } + + stop_video_transmission(&cc->calls[friend_number], friend_number); +} + + + + +void cmd_video(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) +{ + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "cmd_video"); + const char *error_str; + + if ( argc != 0 ) { + error_str = "Unknown arguments."; + goto on_error; + } + + if ( !CallContrl.av ) { + error_str = "ToxAV not supported!"; + goto on_error; + } + + if ( !self->stb->connection ) { + error_str = "Friend is offline."; + goto on_error; + } + + callback_video_starting(CallContrl.av, self->num, &CallContrl); + + return; +on_error: + print_err (self, error_str); +} + +void cmd_end_video(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) +{ + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "cmd_end_video"); + const char *error_str; + + if ( argc != 0 ) { + error_str = "Unknown arguments."; + goto on_error; + } + + if ( !CallContrl.av ) { + error_str = "ToxAV not supported!"; + goto on_error; + } + + callback_video_ending(CallContrl.av, self->num, &CallContrl); + + 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]) { + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "List video devices"); const char *error_str; if ( argc != 1 ) { @@ -105,7 +227,7 @@ void cmd_list_video_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, c goto on_error; } - DeviceType type; + VideoDeviceType type; if ( strcasecmp(argv[1], "in") == 0 ) /* Input devices */ type = input; @@ -123,4 +245,114 @@ void cmd_list_video_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, c return; on_error: print_err (self, error_str); +} + +/* This changes primary device only */ +void cmd_change_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) +{ + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Change video device"); + 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 = input; + + else if ( strcmp(argv[1], "out") == 0 ) /* Output devices */ + type = 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]) +{ + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Change current video device"); + 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 = input; + + else if ( strcmp(argv[1], "out") == 0 ) /* Output devices */ + type = 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 ( 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 = &CallContrl.calls[self->num]; + if (this_call->ttas) { + + + if (type == output) { + } + else { + /* TODO: check for failure */ + close_video_device(input, this_call->in_idx); + open_video_device(input, selection, &this_call->in_idx); + register_video_device_callback(self->num, this_call->in_idx, read_video_device_callback, &self->num); + } + } + } + + self->video_device_selection[type] = selection; + + return; + on_error: + print_err (self, error_str); } \ No newline at end of file diff --git a/src/video_call.h b/src/video_call.h index 4f21990..0c3fc98 100644 --- a/src/video_call.h +++ b/src/video_call.h @@ -39,7 +39,7 @@ typedef enum _VideoError { /* You will have to pass pointer to first member of 'windows' declared in windows.c */ ToxAV *init_video(ToxWindow *self, Tox *tox, ToxAV *av, CallControl *user_data); void terminate_video(); -//int start_video_transmission(ToxWindow *self, Call *call); -//int stop_video_transmission(Call *call, int friend_number); +int start_video_transmission(ToxWindow *self, ToxAV *av, Call *call); +int stop_video_transmission(Call *call, int friend_number); #endif /* VIDEO_CALL_H */ \ No newline at end of file diff --git a/src/video_device.c b/src/video_device.c index 1a8815a..b9f74e3 100644 --- a/src/video_device.c +++ b/src/video_device.c @@ -21,10 +21,13 @@ */ #include "video_device.h" - -#ifdef VIDEO #include "video_call.h" -#endif /* VIDEO */ + +#include +#include +#include + +#include #ifdef __linux__ #include @@ -57,9 +60,9 @@ struct VideoBuffer { typedef struct VideoDevice { int fd; /* File descriptor of video device selected/opened */ - 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 */ + 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 */ struct v4l2_format fmt; struct VideoBuffer *buffers; @@ -70,6 +73,13 @@ typedef struct VideoDevice { 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 */ @@ -83,9 +93,9 @@ static ToxAV* av = NULL; #endif /* VIDEO */ /* q_mutex */ -#define lock pthread_mutex_lock(&mutex); -#define unlock pthread_mutex_unlock(&mutex); -pthread_mutex_t 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 */ @@ -114,28 +124,31 @@ VideoDeviceError init_video_devices() #ifdef __linux__ for(int i = 0; i <= MAX_DEVICES; ++i) { + size[vdt_input] = i; int fd; struct v4l2_capability cap; - char *device_address; + + char device_address[] = "/dev/videoXX"; + + snprintf(device_address + 10, sizeof(device_address) - 10, "%i", i); fd = open(device_address, O_RDWR | O_NONBLOCK, 0); - if (fd == -1) - break; - else { - video_devices_names[vdt_input][i] = cap.card; - } - close(fd); - size[vdt_input] = i; + if (fd == -1) { + break; + } else { + video_devices_names[vdt_input][i] = cap.card; + close(fd); + } } #endif /* __linux__ */ /* TODO: Add OSX implementation for listing input video devices */ - size[output] = 0; + size[vdt_output] = 0; /* TODO: List output video devices */ // Start poll thread - if (pthread_mutex_init(&mutex, NULL) != 0) + if (pthread_mutex_init(&video_mutex, NULL) != 0) return vde_InternalError; pthread_t thread_id; @@ -155,12 +168,26 @@ VideoDeviceError terminate_video_devices() video_thread_running = false; usleep(20000); - if (pthread_mutex_destroy(&mutex) != 0) + if (pthread_mutex_destroy(&video_mutex) != 0) return (VideoDeviceError) vde_InternalError; return (VideoDeviceError) vde_None; } +VideoDeviceError register_video_device_callback(int32_t friend_number, uint32_t device_idx, VideoDataHandleCallback callback, void* data) +{ + if (size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx] || video_devices_running[vdt_input][device_idx]->fd == NULL) + return vde_InvalidSelection; + + 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; @@ -212,19 +239,14 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint } if (type == vdt_input) { +#ifdef __linux__ 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; - } - else { - } - - if (type == vdt_input) { -#ifdef __linux__ /* Obtain video device capabilities */ struct v4l2_capability cap; if (-1 == xioctl(device->fd, VIDIOC_QUERYCAP, &cap)) { @@ -236,7 +258,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint memset(&(fmt), 0, sizeof(fmt)); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; if(-1 == xioctl(device->fd, VIDIOC_G_FMT, &fmt)) { return vde_UnsupportedMode; } @@ -308,6 +330,34 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint /*TODO: Add OSX implementation of opening video devices */ + + /* Initialize X11 window associated to device */ + if ((device->x_display = XOpenDisplay(NULL)) == NULL) { + 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), WhitePixel(device->x_display, screen))) == NULL) { + 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) { + return vde_FailedStart; + } + + + 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; } @@ -344,15 +394,42 @@ void* video_thread_poll (void* arg) // TODO: maybe use thread for every input so if (-1 == ioctl(device->fd, VIDIOC_DQBUF, &buf)) { unlock; continue; - } + } void *data = (void*)device->buffers[buf.index].start; - uint8_t *y; - uint8_t *u; - uint8_t *v; - yuv422to420(y, u, v, data, device->video_width, device->video_width); + uint16_t video_width = device->video_width; + uint16_t video_height = device->video_height; + int screen = DefaultScreen(device->x_display); - if ( device->cb ) device->cb(device->video_width, device->video_height, y, u, v, device->cb_data); + yuv422to420(device->input.planes[0], device->input.planes[1], device->input.planes[2], data, video_width, video_height); + uint8_t *img_data = malloc(video_width * video_height * 4); + yuv420tobgr(video_width, video_height, device->input.planes[0], device->input.planes[1], device->input.planes[2], video_width, video_width / 2, video_width / 2, img_data); + + 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 + }; + + 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); + free(img_data); + //XFlush(device->x_display); + // + + //if ( device->cb ) device->cb(device->video_width, device->video_height, device->y, device->u, device->v, device->cb_data); if (-1 == xioctl(device->fd, VIDIOC_QBUF, &buf)) { unlock; @@ -361,7 +438,8 @@ void* video_thread_poll (void* arg) // TODO: maybe use thread for every input so } unlock; } - usleep(5000); + usleep(1000 * 1000 / 24); + //usleep(5000); } } @@ -392,6 +470,10 @@ VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx) } close(device->fd); + vpx_img_free(&device->input); + XFreeGC(device->x_display, device->x_gc); + XDestroyWindow(device->x_display, device->x_window); + XCloseDisplay(device->x_display); } else { @@ -405,6 +487,29 @@ VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx) return rc; } +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; + } + } +} + 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; @@ -424,7 +529,6 @@ void yuv422to420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t * *plane_y++ = *input++; input++;//v } - } } diff --git a/src/windows.h b/src/windows.h index 67dd113..35a6ec6 100644 --- a/src/windows.h +++ b/src/windows.h @@ -151,6 +151,11 @@ struct ToxWindow { 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 */