diff --git a/Makefile b/Makefile
index 392b257..b567ad5 100644
--- a/Makefile
+++ b/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) ;\
diff --git a/cfg/systems/Darwin.mk b/cfg/systems/Darwin.mk
index f33a4be..bb28f26 100644
--- a/cfg/systems/Darwin.mk
+++ b/cfg/systems/Darwin.mk
@@ -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
+OSX_VIDEO = osx_video.m
+
+LDFLAGS += $(OSX_LIBRARIES) $(OSX_FRAMEWORKS)
+OBJ += osx_video.o
\ No newline at end of file
diff --git a/src/osx_video.h b/src/osx_video.h
new file mode 100644
index 0000000..09d0b81
--- /dev/null
+++ b/src/osx_video.h
@@ -0,0 +1,50 @@
+/* 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 .
+ *
+ */
+
+#ifndef OSX_VIDEO_H
+#define OSX_VIDEO_H
+
+#include
+
+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__
+#import
+#import
+
+@interface OSXVideo : NSObject
+- (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 device_idx, uint16_t *width, uint16_t *height);
+/* Stop device */
+void osx_video_close_device(uint32_t device_idx);
+/* Read data from device */
+void osx_video_read_device(uint8_t *y, uint8_t *u, uint8_t *v, uint16_t *width, uint16_t *height);
+
+
+#endif /* OSX_VIDEO_H */
\ No newline at end of file
diff --git a/src/osx_video.m b/src/osx_video.m
new file mode 100644
index 0000000..f183c18
--- /dev/null
+++ b/src/osx_video.m
@@ -0,0 +1,257 @@
+/* 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 .
+ *
+ */
+
+#ifdef __OBJC__
+#include "osx_video.h"
+
+#import
+#import
+
+#include "line_info.h"
+#include "settings.h"
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+/*
+ * 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 bgrtoyuv420(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++;
+ *plane_y++ = rgb_to_y(r, g, b);
+ }
+
+ for(x = 0; x != width / 2; x++) {
+ b = *rgb++;
+ g = *rgb++;
+ r = *rgb++;
+ *plane_y++ = rgb_to_y(r, g, b);
+
+ b = *rgb++;
+ g = *rgb++;
+ r = *rgb++;
+ *plane_y++ = rgb_to_y(r, g, b);
+
+ b = ((int)b + (int)*(rgb - 6) + (int)*p + (int)*(p + 3) + 2) / 4; p++;
+ g = ((int)g + (int)*(rgb - 5) + (int)*p + (int)*(p + 3) + 2) / 4; p++;
+ r = ((int)r + (int)*(rgb - 4) + (int)*p + (int)*(p + 3) + 2) / 4; p++;
+
+ *plane_u++ = rgb_to_u(r, g, b);
+ *plane_v++ = rgb_to_v(r, g, b);
+
+ p += 3;
+ }
+ }
+}
+/*
+ * 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;
+}
+
+- (instancetype)initWithDeviceNames: (char **)device_names AmtDevices: (int *)size {
+ _session = [[AVCaptureSession alloc] init];
+
+ NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
+ int i = 0;
+ for (AVCaptureDevice *device in devices) {
+ 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;
+ ++i;
+ }
+ if ( i <= 0 )
+ return nil;
+ *size = i;
+
+ return self;
+}
+
+- (void)release {
+ [_session release];
+ [super release];
+}
+
+- (int)openVideoDeviceIndex: (uint32_t)device_idx Width: (uint16_t *)width Height: (uint16_t *)height {
+ pthread_mutex_init(&_frameLock, NULL);
+ _processingQueue = dispatch_queue_create("Toxic processing queue", DISPATCH_QUEUE_SERIAL);
+ NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
+ int i = 0;
+ for (AVCaptureDevice *device in devices) {
+ if ( i == device_idx ) {
+ NSError *error = NULL;
+ AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error];
+
+ if ( error != NULL )
+ return -1;
+
+ /* 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;
+ }*/
+
+ [_session beginConfiguration];
+ [_session addInput:input];
+ //session.sessionPreset = AVCaptureSessionPreset640x480;
+ [_session commitConfiguration];
+ [input release];
+
+ break;
+ } else {
+ ++i;
+ }
+ }
+
+ _linkerVideo = [[AVCaptureVideoDataOutput alloc] init];
+ [_linkerVideo setSampleBufferDelegate:self queue:_processingQueue];
+ // TODO possibly get a better pixel format
+ [_linkerVideo setVideoSettings:@{(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)}];
+ [_session addOutput:_linkerVideo];
+
+ [_session startRunning];
+
+ return 0;
+}
+
+- (void)closeVideoDeviceIndex: (uint32_t)device_idx {
+ [_session stopRunning];
+ [_linkerVideo release];
+}
+
+- (void)getVideoFrameY: (uint8_t *)y U: (uint8_t *)u V: (uint8_t *)v Width: (uint16_t *)width Height: (uint16_t *)height {
+ /*CVImageBufferRef currentFrame = NULL;
+
+ 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);*/
+}
+
+@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];
+}
+
+int osx_video_open_device(uint32_t device_idx, uint16_t *width, uint16_t *height)
+{
+ return [_OSXVideo openVideoDeviceIndex: device_idx Width: width Height: height];
+}
+
+void osx_video_close_device(uint32_t device_idx)
+{
+ [_OSXVideo closeVideoDeviceIndex: device_idx];
+}
+
+void osx_video_read_device(uint8_t *y, uint8_t *u, uint8_t *v, uint16_t *width, uint16_t *height)
+{
+ [_OSXVideo getVideoFrameY: y U: u V: v Width: width Height: height];
+}
+/*
+ * End of C-interface for OSXVideo
+ */
+
+#endif /* __OBJC__ */
\ No newline at end of file
diff --git a/src/video_device.c b/src/video_device.c
index 60cb868..600a470 100644
--- a/src/video_device.c
+++ b/src/video_device.c
@@ -36,7 +36,9 @@
#include
#include
#include
-#endif /* __linux__ */
+#else /* __OSX__ */
+#import "osx_video.h"
+#endif
#include "line_info.h"
#include "settings.h"
@@ -60,16 +62,17 @@ 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 */
#ifdef __linux__
+ int fd; /* File descriptor of video device selected/opened */
struct v4l2_format fmt;
struct VideoBuffer *buffers;
uint32_t n_buffers;
#else /* __OSX__ */
+
#endif
uint32_t ref_count;
@@ -205,8 +208,10 @@ VideoDeviceError init_video_devices()
close(fd);
}
}
+
#else /* __OSX__ */
- /* TODO: Add OSX implementation for listing input video devices */
+ if( osx_video_init(video_devices_names[vdt_input], &size[vdt_input]) != 0 )
+ return vde_InternalError;
#endif
@@ -243,14 +248,23 @@ VideoDeviceError terminate_video_devices()
if ( pthread_mutex_destroy(&video_mutex) != 0 )
return (VideoDeviceError) vde_InternalError;
+#ifdef __OSX__
+ void 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;
@@ -435,8 +449,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
}
#else /* __OSX__ */
- /*TODO: Add OSX implementation of opening video devices */
-
+ osx_video_open_device(*device_idx, device->video_width, device->video_height);
#endif
/* Create X11 window associated to device */
@@ -707,14 +720,17 @@ VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx)
if ( -1 == munmap(device->buffers[i].start, device->buffers[i].length) ) {
}
}
+ close(device->fd);
+
#else /* __OSX__ */
+ osx_video_close_device(device_idx);
#endif
- close(device->fd);
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);
#else /* __OSX__ */