1 /*  video_device.c
2  *
3  *
4  *  Copyright (C) 2014 Toxic All Rights Reserved.
5  *
6  *  This file is part of Toxic.
7  *
8  *  Toxic is free software: you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation, either version 3 of the License, or
11  *  (at your option) any later version.
12  *
13  *  Toxic is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  */
22 
23 #include "video_call.h"
24 #include "video_device.h"
25 
26 #include <sys/ioctl.h>
27 
28 #include <vpx/vpx_image.h>
29 
30 #if defined(__OSX__) || defined(__APPLE__)
31 #import "osx_video.h"
32 #else
33 #include <fcntl.h>
34 #include <sys/mman.h>
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #include <X11/Xlib.h>
38 #include <X11/Xos.h>
39 #include <X11/Xutil.h>
40 #if defined(__OpenBSD__) || defined(__NetBSD__)
41 #include <sys/videoio.h>
42 #else
43 #include <linux/videodev2.h>
44 #endif /* defined(__OpenBSD__) || defined(__NetBSD__) */
45 #endif /* __OSX__ || __APPLE__ */
46 
47 #include "line_info.h"
48 #include "misc_tools.h"
49 #include "settings.h"
50 
51 #include <errno.h>
52 #include <pthread.h>
53 #include <stdbool.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.h>
57 
58 #ifdef VIDEO
59 
60 #define inline__ inline __attribute__((always_inline))
61 
62 extern struct user_settings *user_settings;
63 
64 struct VideoBuffer {
65     void *start;
66     size_t length;
67 };
68 
69 typedef struct VideoDevice {
70     VideoDataHandleCallback cb;             /* Use this to handle data from input device usually */
71     void *cb_data;                          /* Data to be passed to callback */
72     int32_t friend_number;                  /* ToxAV friend number */
73 
74 #if !(defined(__OSX__) || defined(__APPLE__))
75     int fd;                                 /* File descriptor of video device selected/opened */
76     struct v4l2_format fmt;
77     struct VideoBuffer *buffers;
78     uint32_t n_buffers;
79 #endif
80 
81     uint32_t ref_count;
82     int32_t selection;
83     pthread_mutex_t mutex[1];
84     uint16_t video_width;
85     uint16_t video_height;
86 
87     vpx_image_t input;
88 
89     Display *x_display;
90     Window x_window;
91     GC x_gc;
92 
93 } VideoDevice;
94 
95 static const char *dvideo_device_names[2];        /* Default device */
96 static char *video_devices_names[2][MAX_DEVICES]; /* Container of available devices */
97 static int size[2];                               /* Size of above containers */
98 static VideoDevice *video_devices_running[2][MAX_DEVICES] = {{NULL}}; /* Running devices */
99 static uint32_t primary_video_device[2];                              /* Primary device */
100 
101 static ToxAV *av = NULL;
102 
103 /* q_mutex */
104 #define lock pthread_mutex_lock(&video_mutex)
105 #define unlock pthread_mutex_unlock(&video_mutex)
106 static pthread_mutex_t video_mutex;
107 
108 static bool video_thread_running = true;
109 static bool video_thread_paused = true;                /* Thread control */
110 
111 void *video_thread_poll(void *);
112 
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)113 static void yuv420tobgr(uint16_t width, uint16_t height, const uint8_t *y,
114                         const uint8_t *u, const uint8_t *v, unsigned int ystride,
115                         unsigned int ustride, unsigned int vstride, uint8_t *out)
116 {
117     unsigned long int i, j;
118 
119     for (i = 0; i < height; ++i) {
120         for (j = 0; j < width; ++j) {
121             uint8_t *point = out + 4 * ((i * width) + j);
122             int t_y = y[((i * ystride) + j)];
123             int t_u = u[(((i / 2) * ustride) + (j / 2))];
124             int t_v = v[(((i / 2) * vstride) + (j / 2))];
125             t_y = t_y < 16 ? 16 : t_y;
126 
127             int r = (298 * (t_y - 16) + 409 * (t_v - 128) + 128) >> 8;
128             int g = (298 * (t_y - 16) - 100 * (t_u - 128) - 208 * (t_v - 128) + 128) >> 8;
129             int b = (298 * (t_y - 16) + 516 * (t_u - 128) + 128) >> 8;
130 
131             point[2] = r > 255 ? 255 : r < 0 ? 0 : r;
132             point[1] = g > 255 ? 255 : g < 0 ? 0 : g;
133             point[0] = b > 255 ? 255 : b < 0 ? 0 : b;
134             point[3] = ~0;
135         }
136     }
137 }
138 
139 #if !(defined(__OSX__) || defined(__APPLE__))
yuv422to420(uint8_t * plane_y,uint8_t * plane_u,uint8_t * plane_v,uint8_t * input,uint16_t width,uint16_t height)140 static void yuv422to420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v,
141                         uint8_t *input, uint16_t width, uint16_t height)
142 {
143     uint8_t *end = input + width * height * 2;
144 
145     while (input != end) {
146         uint8_t *line_end = input + width * 2;
147 
148         while (input != line_end) {
149             *plane_y++ = *input++;
150             *plane_u++ = *input++;
151             *plane_y++ = *input++;
152             *plane_v++ = *input++;
153         }
154 
155         line_end = input + width * 2;
156 
157         while (input != line_end) {
158             *plane_y++ = *input++;
159             input++;//u
160             *plane_y++ = *input++;
161             input++;//v
162         }
163     }
164 }
165 
xioctl(int fh,unsigned long request,void * arg)166 static int xioctl(int fh, unsigned long request, void *arg)
167 {
168     int r;
169 
170     do {
171         r = ioctl(fh, request, arg);
172     } while (-1 == r && EINTR == errno);
173 
174     return r;
175 }
176 
177 #endif
178 
179 /* Meet devices */
180 #ifdef VIDEO
init_video_devices(ToxAV * av_)181 VideoDeviceError init_video_devices(ToxAV *av_)
182 #else
183 VideoDeviceError init_video_devices(void)
184 #endif /* VIDEO */
185 {
186     size[vdt_input] = 0;
187 
188 #if defined(__OSX__) || defined(__APPLE__)
189 
190     if (osx_video_init(&video_devices_names[vdt_input][0], &size[vdt_input]) != 0) {
191         return vde_InternalError;
192     }
193 
194 #else /* not __OSX__ || __APPLE__ */
195 
196     for (; size[vdt_input] <= MAX_DEVICES; ++size[vdt_input]) {
197         int fd;
198         char device_address[] = "/dev/videoXX";
199         snprintf(device_address + 10, sizeof(char) * strlen(device_address) - 10, "%i", size[vdt_input]);
200 
201         fd = open(device_address, O_RDWR | O_NONBLOCK, 0);
202 
203         if (fd == -1) {
204             break;
205         } else {
206             struct v4l2_capability cap;
207             char *video_input_name;
208 
209             /* Query V4L for capture capabilities */
210             if (-1 != ioctl(fd, VIDIOC_QUERYCAP, &cap)) {
211                 video_input_name = (char *)malloc(strlen((const char *)cap.card) + strlen(device_address) + 4);
212 
213                 if (video_input_name == NULL) {
214                     close(fd);
215                     return vde_InternalError;
216                 }
217 
218                 strcpy(video_input_name, (char *)cap.card);
219                 strcat(video_input_name, " (");
220                 strcat(video_input_name, (char *)device_address);
221                 strcat(video_input_name, ")");
222             } else {
223                 video_input_name = (char *)malloc(strlen(device_address) + 3);
224 
225                 if (video_input_name == NULL) {
226                     close(fd);
227                     return vde_InternalError;
228                 }
229 
230                 strcpy(video_input_name, "(");
231                 strcat(video_input_name, device_address);
232                 strcat(video_input_name, ")");
233             }
234 
235             video_devices_names[vdt_input][size[vdt_input]] = video_input_name;
236 
237             close(fd);
238         }
239     }
240 
241 #endif
242 
243     size[vdt_output] = 1;
244     // TODO(iphydf): String literals are const char *. This may need to be
245     // copied, or if we're not owning any output device names, it should be
246     // const and video_devices_names needs to be split.
247     char *video_output_name = "Toxic Video Receiver";
248     video_devices_names[vdt_output][0] = video_output_name;
249 
250     // Start poll thread
251     if (pthread_mutex_init(&video_mutex, NULL) != 0) {
252         return vde_InternalError;
253     }
254 
255     pthread_t thread_id;
256 
257     if (pthread_create(&thread_id, NULL, video_thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0) {
258         return vde_InternalError;
259     }
260 
261 #ifdef VIDEO
262     av = av_;
263 #endif /* VIDEO */
264 
265     return (VideoDeviceError) vde_None;
266 }
267 
terminate_video_devices(void)268 VideoDeviceError terminate_video_devices(void)
269 {
270     /* Cleanup if needed */
271     lock;
272     video_thread_running = false;
273     unlock;
274 
275     sleep_thread(20000L);
276 
277     int i;
278 
279     for (i = 0; i < size[vdt_input]; ++i) {
280         free(video_devices_names[vdt_input][i]);
281     }
282 
283     if (pthread_mutex_destroy(&video_mutex) != 0) {
284         return (VideoDeviceError) vde_InternalError;
285     }
286 
287 #if defined(__OSX__) || defined(__APPLE__)
288     osx_video_release();
289 #endif /* __OSX__ || __APPLE__ */
290 
291     return (VideoDeviceError) vde_None;
292 }
293 
register_video_device_callback(int32_t friend_number,uint32_t device_idx,VideoDataHandleCallback callback,void * data)294 VideoDeviceError register_video_device_callback(int32_t friend_number, uint32_t device_idx,
295         VideoDataHandleCallback callback, void *data)
296 {
297 #if defined(__OSX__) || defined(__APPLE__)
298 
299     if (size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx]) {
300         return vde_InvalidSelection;
301     }
302 
303 #else /* not __OSX__ || __APPLE__ */
304 
305     if (size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx]
306             || !video_devices_running[vdt_input][device_idx]->fd) {
307         return vde_InvalidSelection;
308     }
309 
310 #endif
311 
312     lock;
313     video_devices_running[vdt_input][device_idx]->cb = callback;
314     video_devices_running[vdt_input][device_idx]->cb_data = data;
315     video_devices_running[vdt_input][device_idx]->friend_number = friend_number;
316     unlock;
317 
318     return vde_None;
319 }
320 
set_primary_video_device(VideoDeviceType type,int32_t selection)321 VideoDeviceError set_primary_video_device(VideoDeviceType type, int32_t selection)
322 {
323     if (size[type] <= selection || selection < 0) {
324         return vde_InvalidSelection;
325     }
326 
327     primary_video_device[type] = selection;
328 
329     return vde_None;
330 }
331 
open_primary_video_device(VideoDeviceType type,uint32_t * device_idx,uint32_t * width,uint32_t * height)332 VideoDeviceError open_primary_video_device(VideoDeviceType type, uint32_t *device_idx,
333         uint32_t *width, uint32_t *height)
334 {
335     return open_video_device(type, primary_video_device[type], device_idx, width, height);
336 }
337 
get_primary_video_device_name(VideoDeviceType type,char * buf,int size)338 void get_primary_video_device_name(VideoDeviceType type, char *buf, int size)
339 {
340     memcpy(buf, dvideo_device_names[type], size);
341 }
342 
open_video_device(VideoDeviceType type,int32_t selection,uint32_t * device_idx,uint32_t * width,uint32_t * height)343 VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint32_t *device_idx,
344                                    uint32_t *width, uint32_t *height)
345 {
346     if (size[type] <= selection || selection < 0) {
347         return vde_InvalidSelection;
348     }
349 
350     lock;
351 
352     uint32_t i, temp_idx = -1;
353 
354     for (i = 0; i < MAX_DEVICES; ++i) {
355         if (!video_devices_running[type][i]) {
356             temp_idx = i;
357             break;
358         }
359     }
360 
361     if (temp_idx == -1) {
362         unlock;
363         return vde_AllDevicesBusy;
364     }
365 
366     for (i = 0; i < MAX_DEVICES; i ++) { /* Check if any device has the same selection */
367         if (video_devices_running[type][i] && video_devices_running[type][i]->selection == selection) {
368 
369             video_devices_running[type][temp_idx] = video_devices_running[type][i];
370             video_devices_running[type][i]->ref_count++;
371 
372             unlock;
373             return vde_None;
374         }
375     }
376 
377     VideoDevice *device = video_devices_running[type][temp_idx] = calloc(1, sizeof(VideoDevice));
378     device->selection = selection;
379 
380     if (pthread_mutex_init(device->mutex, NULL) != 0) {
381         free(device);
382         unlock;
383         return vde_InternalError;
384     }
385 
386     if (type == vdt_input) {
387         video_thread_paused = true;
388 
389 #if defined(__OSX__) || defined(__APPLE__)
390 
391         /* TODO: use requested resolution */
392         if (osx_video_open_device(selection, &device->video_width, &device->video_height) != 0) {
393             free(device);
394             unlock;
395             return vde_FailedStart;
396         }
397 
398 #else /* not __OSX__ || __APPLE__ */
399         /* Open selected device */
400         char device_address[] = "/dev/videoXX";
401         snprintf(device_address + 10, sizeof(device_address) - 10, "%i", selection);
402 
403         device->fd = open(device_address, O_RDWR);
404 
405         if (device->fd == -1) {
406             unlock;
407             return vde_FailedStart;
408         }
409 
410         /* Obtain video device capabilities */
411         struct v4l2_capability cap;
412 
413         if (-1 == xioctl(device->fd, VIDIOC_QUERYCAP, &cap)) {
414             close(device->fd);
415             free(device);
416             unlock;
417             return vde_FailedStart;
418         }
419 
420         /* Setup video format */
421         struct v4l2_format fmt = {0};
422 
423         fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
424         fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
425         fmt.fmt.pix.width = width == NULL ? 0 : *width;
426         fmt.fmt.pix.height = height == NULL ? 0 : *height;
427 
428         if (-1 == xioctl(device->fd, VIDIOC_S_FMT, &fmt)) {
429             close(device->fd);
430             free(device);
431             unlock;
432             return vde_FailedStart;
433         }
434 
435         device->video_width = fmt.fmt.pix.width;
436         device->video_height = fmt.fmt.pix.height;
437 
438         /* Request buffers */
439         struct v4l2_requestbuffers req = {0};
440 
441         req.count = 4;
442         req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
443         req.memory = V4L2_MEMORY_MMAP;
444 
445         if (-1 == xioctl(device->fd, VIDIOC_REQBUFS, &req)) {
446             close(device->fd);
447             free(device);
448             unlock;
449             return vde_FailedStart;
450         }
451 
452         if (req.count < 2) {
453             close(device->fd);
454             free(device);
455             unlock;
456             return vde_FailedStart;
457         }
458 
459         device->buffers = calloc(req.count, sizeof(struct VideoBuffer));
460 
461         for (i = 0; i < req.count; ++i) {
462             struct v4l2_buffer buf = {0};
463 
464             buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
465             buf.memory = V4L2_MEMORY_MMAP;
466             buf.index = i;
467 
468             if (-1 == xioctl(device->fd, VIDIOC_QUERYBUF, &buf)) {
469                 close(device->fd);
470                 free(device);
471                 unlock;
472                 return vde_FailedStart;
473             }
474 
475             device->buffers[i].length = buf.length;
476             device->buffers[i].start = mmap(NULL /* start anywhere */,
477                                             buf.length,
478                                             PROT_READ | PROT_WRITE /* required */,
479                                             MAP_SHARED /* recommended */,
480                                             device->fd, buf.m.offset);
481 
482             if (MAP_FAILED == device->buffers[i].start) {
483                 for (i = 0; i < buf.index; ++i) {
484                     munmap(device->buffers[i].start, device->buffers[i].length);
485                 }
486 
487                 close(device->fd);
488                 free(device);
489                 unlock;
490                 return vde_FailedStart;
491             }
492         }
493 
494         device->n_buffers = i;
495 
496         enum v4l2_buf_type type;
497 
498         for (i = 0; i < device->n_buffers; ++i) {
499             struct v4l2_buffer buf = {0};
500 
501             buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
502             buf.memory = V4L2_MEMORY_MMAP;
503             buf.index = i;
504 
505             if (-1 == xioctl(device->fd, VIDIOC_QBUF, &buf)) {
506                 for (i = 0; i < device->n_buffers; ++i) {
507                     munmap(device->buffers[i].start, device->buffers[i].length);
508                 }
509 
510                 close(device->fd);
511                 free(device);
512                 unlock;
513                 return vde_FailedStart;
514             }
515         }
516 
517         type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
518 
519         /* Turn on video stream */
520         if (-1 == xioctl(device->fd, VIDIOC_STREAMON, &type)) {
521             close_video_device(vdt_input, temp_idx);
522             unlock;
523             return vde_FailedStart;
524         }
525 
526 #endif
527 
528         /* Create X11 window associated to device */
529         if ((device->x_display = XOpenDisplay(NULL)) == NULL) {
530             close_video_device(vdt_input, temp_idx);
531             unlock;
532             return vde_FailedStart;
533         }
534 
535         int screen = DefaultScreen(device->x_display);
536 
537         if (!(device->x_window = XCreateSimpleWindow(device->x_display, RootWindow(device->x_display, screen), 0, 0,
538                                  device->video_width, device->video_height, 0, BlackPixel(device->x_display, screen),
539                                  BlackPixel(device->x_display, screen)))) {
540             close_video_device(vdt_input, temp_idx);
541             unlock;
542             return vde_FailedStart;
543         }
544 
545         XStoreName(device->x_display, device->x_window, "Video Preview");
546         XSelectInput(device->x_display, device->x_window, ExposureMask | ButtonPressMask | KeyPressMask);
547 
548         if ((device->x_gc = DefaultGC(device->x_display, screen)) == NULL) {
549             close_video_device(vdt_input, temp_idx);
550             unlock;
551             return vde_FailedStart;
552         }
553 
554         /* Disable user from manually closing the X11 window */
555         Atom wm_delete_window = XInternAtom(device->x_display, "WM_DELETE_WINDOW", false);
556         XSetWMProtocols(device->x_display, device->x_window, &wm_delete_window, 1);
557 
558         XMapWindow(device->x_display, device->x_window);
559         XClearWindow(device->x_display, device->x_window);
560         XMapRaised(device->x_display, device->x_window);
561         XFlush(device->x_display);
562 
563         vpx_img_alloc(&device->input, VPX_IMG_FMT_I420, device->video_width, device->video_height, 1);
564 
565         if (width != NULL) {
566             *width = device->video_width;
567         }
568 
569         if (height != NULL) {
570             *height = device->video_height;
571         }
572 
573         video_thread_paused = false;
574     } else { /* vdt_output */
575 
576         /* Create X11 window associated to device */
577         if ((device->x_display = XOpenDisplay(NULL)) == NULL) {
578             close_video_device(vdt_output, temp_idx);
579             unlock;
580             return vde_FailedStart;
581         }
582 
583         int screen = DefaultScreen(device->x_display);
584 
585         if (!(device->x_window = XCreateSimpleWindow(device->x_display, RootWindow(device->x_display, screen), 0, 0,
586                                  100, 100, 0, BlackPixel(device->x_display, screen), BlackPixel(device->x_display, screen)))) {
587             close_video_device(vdt_output, temp_idx);
588             unlock;
589             return vde_FailedStart;
590         }
591 
592         XStoreName(device->x_display, device->x_window, "Video Receive");
593         XSelectInput(device->x_display, device->x_window, ExposureMask | ButtonPressMask | KeyPressMask);
594 
595         if ((device->x_gc = DefaultGC(device->x_display, screen)) == NULL) {
596             close_video_device(vdt_output, temp_idx);
597             unlock;
598             return vde_FailedStart;
599         }
600 
601         /* Disable user from manually closing the X11 window */
602         Atom wm_delete_window = XInternAtom(device->x_display, "WM_DELETE_WINDOW", false);
603         XSetWMProtocols(device->x_display, device->x_window, &wm_delete_window, 1);
604 
605         XMapWindow(device->x_display, device->x_window);
606         XClearWindow(device->x_display, device->x_window);
607         XMapRaised(device->x_display, device->x_window);
608         XFlush(device->x_display);
609 
610         vpx_img_alloc(&device->input, VPX_IMG_FMT_I420, device->video_width, device->video_height, 1);
611     }
612 
613     *device_idx = temp_idx;
614     unlock;
615 
616     return vde_None;
617 }
618 
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)619 VideoDeviceError write_video_out(uint16_t width, uint16_t height,
620                                  uint8_t const *y, uint8_t const *u, uint8_t const *v,
621                                  int32_t ystride, int32_t ustride, int32_t vstride,
622                                  void *user_data)
623 {
624     UNUSED_VAR(user_data);
625 
626     VideoDevice *device = video_devices_running[vdt_output][0];
627 
628     if (!device) {
629         return vde_DeviceNotActive;
630     }
631 
632     if (!device->x_window) {
633         return vde_DeviceNotActive;
634     }
635 
636     pthread_mutex_lock(device->mutex);
637 
638     /* Resize X11 window to correct size */
639     if (device->video_width != width || device->video_height != height) {
640         device->video_width = width;
641         device->video_height = height;
642         XResizeWindow(device->x_display, device->x_window, width, height);
643 
644         vpx_img_free(&device->input);
645         vpx_img_alloc(&device->input, VPX_IMG_FMT_I420, width, height, 1);
646     }
647 
648     /* Convert YUV420 data to BGR */
649     ystride = abs(ystride);
650     ustride = abs(ustride);
651     vstride = abs(vstride);
652     uint8_t *img_data = malloc(width * height * 4);
653     yuv420tobgr(width, height, y, u, v, ystride, ustride, vstride, img_data);
654 
655     /* Allocate image data in X11 */
656     XImage image = {
657         .width = width,
658         .height = height,
659         .depth = 24,
660         .bits_per_pixel = 32,
661         .format = ZPixmap,
662         .byte_order = LSBFirst,
663         .bitmap_unit = 8,
664         .bitmap_bit_order = LSBFirst,
665         .bytes_per_line = width * 4,
666         .red_mask = 0xFF0000,
667         .green_mask = 0xFF00,
668         .blue_mask = 0xFF,
669         .data = (char *)img_data
670     };
671 
672     /* Render image data */
673     Pixmap pixmap = XCreatePixmap(device->x_display, device->x_window, width, height, 24);
674     XPutImage(device->x_display, pixmap, device->x_gc, &image, 0, 0, 0, 0, width, height);
675     XCopyArea(device->x_display, pixmap, device->x_window, device->x_gc, 0, 0, width, height, 0, 0);
676     XFreePixmap(device->x_display, pixmap);
677     XFlush(device->x_display);
678     free(img_data);
679 
680     pthread_mutex_unlock(device->mutex);
681     return vde_None;
682 }
683 
video_thread_poll(void * arg)684 void *video_thread_poll(void *arg)  // TODO: maybe use thread for every input source
685 {
686     /*
687      * NOTE: We only need to poll input devices for data.
688      */
689     UNUSED_VAR(arg);
690     uint32_t i;
691 
692     while (1) {
693         lock;
694 
695         if (!video_thread_running) {
696             unlock;
697             break;
698         }
699 
700         unlock;
701 
702         if (video_thread_paused) {
703             sleep_thread(10000L);    /* Wait for unpause. */
704         } else {
705             for (i = 0; i < size[vdt_input]; ++i) {
706                 lock;
707 
708                 if (video_devices_running[vdt_input][i] != NULL) {
709                     /* Obtain frame image data from device buffers */
710                     VideoDevice *device = video_devices_running[vdt_input][i];
711                     uint16_t video_width = device->video_width;
712                     uint16_t video_height = device->video_height;
713                     uint8_t *y = device->input.planes[0];
714                     uint8_t *u = device->input.planes[1];
715                     uint8_t *v = device->input.planes[2];
716 
717 #if defined(__OSX__) || defined(__APPLE__)
718 
719                     if (osx_video_read_device(y, u, v, &video_width, &video_height) != 0) {
720                         unlock;
721                         continue;
722                     }
723 
724 #else /* not __OSX__ || __APPLE__ */
725                     struct v4l2_buffer buf = {0};
726 
727                     buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
728                     buf.memory = V4L2_MEMORY_MMAP;
729 
730                     if (-1 == ioctl(device->fd, VIDIOC_DQBUF, &buf)) {
731                         unlock;
732                         continue;
733                     }
734 
735                     void *data = (void *)device->buffers[buf.index].start;
736 
737                     /* Convert frame image data to YUV420 for ToxAV */
738                     yuv422to420(y, u, v, data, video_width, video_height);
739 
740 #endif
741 
742                     /* Send frame data to friend through ToxAV */
743                     if (device->cb) {
744                         device->cb(video_width, video_height, y, u, v, device->cb_data);
745                     }
746 
747                     /* Convert YUV420 data to BGR */
748                     uint8_t *img_data = malloc(video_width * video_height * 4);
749                     yuv420tobgr(video_width, video_height, y, u, v,
750                                 video_width, video_width / 2, video_width / 2, img_data);
751 
752                     /* Allocate image data in X11 */
753                     XImage image = {
754                         .width = video_width,
755                         .height = video_height,
756                         .depth = 24,
757                         .bits_per_pixel = 32,
758                         .format = ZPixmap,
759                         .byte_order = LSBFirst,
760                         .bitmap_unit = 8,
761                         .bitmap_bit_order = LSBFirst,
762                         .bytes_per_line = video_width * 4,
763                         .red_mask = 0xFF0000,
764                         .green_mask = 0xFF00,
765                         .blue_mask = 0xFF,
766                         .data = (char *)img_data
767                     };
768 
769                     /* Render image data */
770                     Pixmap pixmap = XCreatePixmap(device->x_display, device->x_window, video_width, video_height, 24);
771                     XPutImage(device->x_display, pixmap, device->x_gc, &image, 0, 0, 0, 0, video_width, video_height);
772                     XCopyArea(device->x_display, pixmap, device->x_window, device->x_gc, 0, 0, video_width, video_height, 0, 0);
773                     XFreePixmap(device->x_display, pixmap);
774                     XFlush(device->x_display);
775                     free(img_data);
776 
777 #if !(defined(__OSX__) || defined(__APPLE__))
778 
779                     if (-1 == xioctl(device->fd, VIDIOC_QBUF, &buf)) {
780                         unlock;
781                         continue;
782                     }
783 
784 #endif
785 
786                 }
787 
788                 unlock;
789             }
790 
791             long int sleep_duration = 1000 * 1000 / 24;
792             sleep_thread(sleep_duration);
793         }
794     }
795 
796     pthread_exit(NULL);
797 }
798 
close_video_device(VideoDeviceType type,uint32_t device_idx)799 VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx)
800 {
801     if (device_idx >= MAX_DEVICES) {
802         return vde_InvalidSelection;
803     }
804 
805     lock;
806     VideoDevice *device = video_devices_running[type][device_idx];
807     VideoDeviceError rc = vde_None;
808 
809     if (!device) {
810         unlock;
811         return vde_DeviceNotActive;
812     }
813 
814     video_devices_running[type][device_idx] = NULL;
815 
816     if (!device->ref_count) {
817 
818         if (type == vdt_input) {
819 #if defined(__OSX__) || defined(__APPLE__)
820 
821             osx_video_close_device(device_idx);
822 #else /* not __OSX__ || __APPLE__ */
823             enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
824 
825             if (-1 == xioctl(device->fd, VIDIOC_STREAMOFF, &buf_type)) {}
826 
827             int i;
828 
829             for (i = 0; i < device->n_buffers; ++i) {
830                 if (-1 == munmap(device->buffers[i].start, device->buffers[i].length)) {
831                 }
832             }
833 
834             close(device->fd);
835 
836 #endif
837             vpx_img_free(&device->input);
838             XDestroyWindow(device->x_display, device->x_window);
839             XFlush(device->x_display);
840             XCloseDisplay(device->x_display);
841             pthread_mutex_destroy(device->mutex);
842 
843 #if !(defined(__OSX__) || defined(__APPLE__))
844             free(device->buffers);
845 #endif /* not __OSX__ || __APPLE__ */
846 
847             free(device);
848         } else {
849             vpx_img_free(&device->input);
850             XDestroyWindow(device->x_display, device->x_window);
851             XFlush(device->x_display);
852             XCloseDisplay(device->x_display);
853             pthread_mutex_destroy(device->mutex);
854             free(device);
855         }
856 
857     } else {
858         device->ref_count--;
859     }
860 
861     unlock;
862     return rc;
863 }
864 
print_video_devices(ToxWindow * self,VideoDeviceType type)865 void print_video_devices(ToxWindow *self, VideoDeviceType type)
866 {
867     int i;
868 
869     for (i = 0; i < size[type]; ++i) {
870         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%d: %s", i, video_devices_names[type][i]);
871     }
872 
873     return;
874 }
875 
video_selection_valid(VideoDeviceType type,int32_t selection)876 VideoDeviceError video_selection_valid(VideoDeviceType type, int32_t selection)
877 {
878     return (size[type] <= selection || selection < 0) ? vde_InvalidSelection : vde_None;
879 }
880 
881 #endif /* VIDEO */
882