From c8a9ac21f35525af0551d8017b247398a5ec8ee8 Mon Sep 17 00:00:00 2001 From: cnhenry Date: Thu, 20 Aug 2015 06:39:47 -0500 Subject: [PATCH] Implemented OSX device listing --- Makefile | 4 + cfg/systems/Darwin.mk | 8 ++ src/osx_video.h | 50 ++++++++ src/osx_video.m | 257 ++++++++++++++++++++++++++++++++++++++++++ src/video_device.c | 28 ++++- 5 files changed, 341 insertions(+), 6 deletions(-) create mode 100644 src/osx_video.h create mode 100644 src/osx_video.m 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__ */