diff --git a/cfg/checks/av.mk b/cfg/checks/av.mk index 4d3837e..1ee2cd4 100644 --- a/cfg/checks/av.mk +++ b/cfg/checks/av.mk @@ -1,10 +1,18 @@ # Variables for audio call support AUDIO_LIBS = libtoxav openal +VIDEO_LIBS = libtoxav opencv AUDIO_CFLAGS = -DAUDIO -ifneq (, $(findstring device.o, $(OBJ))) +VIDEO_CFLAGS = -DVIDEO +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 + +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 audio support @@ -19,3 +27,16 @@ else ifneq ($(MAKECMDGOALS), clean) $(warning WARNING -- You need these libraries for audio support) $(warning WARNING -- $(MISSING_AUDIO_LIBS)) 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 need these libraries for video support) + $(warning WARNING -- $(MISSING_VIDEO_LIBS)) +endif \ No newline at end of file diff --git a/opencv_demo/Makefile b/opencv_demo/Makefile new file mode 100644 index 0000000..b8d63f7 --- /dev/null +++ b/opencv_demo/Makefile @@ -0,0 +1,7 @@ +CC=gcc + +capture_playback: capture_playback.c + $(CC) -o capture_playback capture_playback.c -lopencv_core -lopencv_highgui + +clean: + rm -f capture_playback *.o diff --git a/opencv_demo/README.md b/opencv_demo/README.md new file mode 100644 index 0000000..92bc3bd --- /dev/null +++ b/opencv_demo/README.md @@ -0,0 +1 @@ +gcc -o example.exe -I C:/opencv/build/include -L C:/opencv/build/x86/vc12/lib capture_playback.c -lopencv_core2411d -lopencv_highgui2411d -luuid -strmiids -lole32 -loleaut32 \ No newline at end of file diff --git a/opencv_demo/capture_playback.c b/opencv_demo/capture_playback.c new file mode 100644 index 0000000..22c4876 --- /dev/null +++ b/opencv_demo/capture_playback.c @@ -0,0 +1,156 @@ +#include +#include + +#include +#include + +#ifdef __linux__ +#include +#include +#include +#include +#endif /* __linux __ */ + +#ifdef __WIN32 +#include +#include + +#pragma comment(lib, "strmiids.lib") +#endif /* __WIN32 */ + +#define MAX_DEVICES 32 + +int main(int argc, char *argv[]) +{ + const char *device_names[MAX_DEVICES]; + int amt_cameras = 0; + int i = 0; + + #ifdef __linux__ + // Enumerate video capture devices + while(i <= MAX_DEVICES) + { + int fd; + struct v4l2_capability video_cap; + char device_address[256]; + snprintf(device_address, sizeof device_address, "/dev/video%d",i); + + if((fd = open(device_address, O_RDONLY)) == -1) { + break; + } + else { + // Query capture device information + if(ioctl(fd, VIDIOC_QUERYCAP, &video_cap) == -1) + perror("cam_info: Can't get capabilities"); + else { + //printf("Card:\t\t '%s'\n", video_cap.card); + int name_length = sizeof(video_cap.card); + char* name = (char*)malloc(name_length+1); + name = video_cap.card; + device_names[i] = name; + } + close(fd); + amt_cameras = i; + } + ++i; + } + #endif /* __linux__ */ + + #ifdef __WIN32 + HRESULT hr; + CoInitialize(NULL); + ICreateDevEnum *pSysDevEnum = NULL; + hr = CoCreateInstance(&CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, &IID_ICreateDevEnum, (void**)&pSysDevEnum); + if(FAILED(hr)) { + printf("CoCreateInstance failed()\n"); + } + // Obtain a class enumerator for the video compressor category. + IEnumMoniker *pEnumCat = NULL; + hr = pSysDevEnum->lpVtbl->CreateClassEnumerator(pSysDevEnum, &CLSID_VideoInputDeviceCategory, &pEnumCat, 0); + if(hr != S_OK) { + pSysDevEnum->lpVtbl->Release(pSysDevEnum); + printf("CreateClassEnumerator failed()\n"); + } + + IMoniker *pMoniker = NULL; + + ULONG cFetched; + while(pEnumCat->lpVtbl->Next(pEnumCat, 1, &pMoniker, &cFetched) == S_OK && i <= MAX_DEVICES) { + IPropertyBag *pPropBag; + hr = pMoniker->lpVtbl->BindToStorage(pMoniker, 0, 0, &IID_IPropertyBag, (void **)&pPropBag); + if(SUCCEEDED(hr)) { + // To retrieve the filter's friendly name, do the following: + VARIANT varName; + VariantInit(&varName); + hr = pPropBag->lpVtbl->Read(pPropBag, L"FriendlyName", &varName, 0); + if (SUCCEEDED(hr)) { + if(varName.vt == VT_BSTR) { + //printf("friendly name: %ls\n", varName.bstrVal); + int name_length = wcslen(varName.bstrVal); + char* name = (char*)malloc(name_length + 1); + wcstombs(name, varName.bstrVal, name_length + 1); + device_names[i] = name; + //free(name); + } else { + //printf("unfriendly name\n"); + device_names[i] = "Unknown Device"; + } + ++i; + } + + VariantClear(&varName); + pPropBag->lpVtbl->Release(pPropBag); + } + pMoniker->lpVtbl->Release(pMoniker); + } + amt_cameras = i-1; + pEnumCat->lpVtbl->Release(pEnumCat); + pSysDevEnum->lpVtbl->Release(pSysDevEnum); + + #endif /* __WIN32 */ + + // TODO: Windows video capture enumeration + + // Obtain user camera selection + int select_camera = -1; + while(select_camera < 0 || select_camera > amt_cameras || select_camera >= MAX_DEVICES) { + printf("Select a camera from %d cameras...\n", amt_cameras+1); + int i = 0; + // Print all collected devices + while(i <= amt_cameras) + { + printf("%d: %s\n", i, device_names[i]); + ++i; + } + printf("\nCamera #:"); + scanf(" %d", &select_camera); + } + printf("\nPress spacebar to exit.\n\n"); + + // Initialize window + cvNamedWindow("Camera", 1); + // Initialize camera + CvCapture* capture = cvCreateCameraCapture(select_camera); + char key = 0; + // Run until spacebar is pressed + while(key != 32 && capture != NULL) { + // Obtains frame from camera and show it on window + IplImage* frame = cvQueryFrame(capture); + cvShowImage("Camera", frame); + key = cvWaitKey(10); + } + + // Free device list allocations + i = 0; + while(i <= amt_cameras) + { + free(device_names[i]); + ++i; + } + + // Collapse camera and window + cvReleaseCapture(&capture); + cvDestroyWindow("Camera"); + + return 0; +} \ No newline at end of file diff --git a/opencv_demo/opencv_core2411d.dll b/opencv_demo/opencv_core2411d.dll new file mode 100644 index 0000000..8d591fa Binary files /dev/null and b/opencv_demo/opencv_core2411d.dll differ diff --git a/opencv_demo/opencv_highgui2411d.dll b/opencv_demo/opencv_highgui2411d.dll new file mode 100644 index 0000000..9479947 Binary files /dev/null and b/opencv_demo/opencv_highgui2411d.dll differ diff --git a/src/audio_call.c b/src/audio_call.c index 54b865b..b47cea8 100644 --- a/src/audio_call.c +++ b/src/audio_call.c @@ -23,7 +23,7 @@ #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" @@ -69,7 +69,7 @@ static int set_call(Call* call, bool start) else { call->ttid = 0; if (pthread_mutex_destroy(&call->mutex) != 0) - return -1; + return -1; } return 0; diff --git a/src/audio_call.h b/src/audio_call.h index 68509b9..53c85ec 100644 --- a/src/audio_call.h +++ b/src/audio_call.h @@ -25,7 +25,7 @@ #include -#include "device.h" +#include "audio_device.h" typedef enum _AudioError { ae_None = 0, diff --git a/src/device.c b/src/audio_device.c similarity index 99% rename from src/device.c rename to src/audio_device.c index 4a6a8a5..562374b 100644 --- a/src/device.c +++ b/src/audio_device.c @@ -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" diff --git a/src/device.h b/src/audio_device.h similarity index 96% rename from src/device.h rename to src/audio_device.h index aa80a53..9a34e9b 100644 --- a/src/device.h +++ b/src/audio_device.h @@ -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 @@ -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 */ diff --git a/src/chat.c b/src/chat.c index 9eaed52..6627a9e 100644 --- a/src/chat.c +++ b/src/chat.c @@ -101,6 +101,12 @@ static const char chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = { { "/mute" }, { "/sense" }, +#ifdef VIDEO + + { "/enablevid" }, + { "/disablevid" }, + +#endif /* VIDEO */ #endif /* AUDIO */ }; diff --git a/src/chat_commands.h b/src/chat_commands.h index c66b77a..9306310 100644 --- a/src/chat_commands.h +++ b/src/chat_commands.h @@ -41,6 +41,10 @@ void cmd_cancel(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZ void cmd_ccur_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); 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]); +#ifdef VIDEO +void cmd_enablevid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); +void cmd_disablevid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); +#endif /* VIDEO */ #endif /* AUDIO */ #endif /* #define CHAT_COMMANDS_H */ diff --git a/src/execute.c b/src/execute.c index 9e83a83..59248db 100644 --- a/src/execute.c +++ b/src/execute.c @@ -77,6 +77,10 @@ static struct cmd_func chat_commands[] = { { "/hangup", cmd_hangup }, { "/mute", cmd_mute }, { "/sense", cmd_sense }, +#ifdef VIDEO + { "/enablevid", cmd_enablevid }, + { "/disablevid",cmd_disablevid }, +#endif /* VIDEO */ #endif /* AUDIO */ { NULL, NULL }, }; diff --git a/src/groupchat.c b/src/groupchat.c index 57885c9..4675122 100644 --- a/src/groupchat.c +++ b/src/groupchat.c @@ -59,7 +59,7 @@ #include "help.h" #include "notify.h" #include "autocomplete.h" -#include "device.h" +#include "audio_device.h" extern char *DATA_FILE; diff --git a/src/notify.c b/src/notify.c index 8560659..d6d9e57 100644 --- a/src/notify.c +++ b/src/notify.c @@ -31,7 +31,7 @@ #include #include "notify.h" -#include "device.h" +#include "audio_device.h" #include "settings.h" #include "line_info.h" #include "misc_tools.h" diff --git a/src/settings.c b/src/settings.c index 73a293f..95e20a6 100644 --- a/src/settings.c +++ b/src/settings.c @@ -32,7 +32,7 @@ #include "misc_tools.h" #ifdef AUDIO - #include "device.h" + #include "audio_device.h" #endif /* AUDIO */ #include "settings.h" diff --git a/src/toxic.c b/src/toxic.c index 9d14328..e1e548c 100644 --- a/src/toxic.c +++ b/src/toxic.c @@ -54,7 +54,8 @@ #include "settings.h" #include "log.h" #include "notify.h" -#include "device.h" +#include "audio_device.h" +#include "video_device.h" #include "message_queue.h" #include "execute.h" #include "term_mplex.h" @@ -65,6 +66,9 @@ #ifdef AUDIO #include "audio_call.h" +#ifdef VIDEO +#include "video_call.h" +#endif /* VIDEO */ ToxAv *av; #endif /* AUDIO */ @@ -131,6 +135,10 @@ void exit_toxic_success(Tox *m) terminate_audio(); #endif /* AUDIO */ +#ifdef VIDEO + terminate_video(); +#endif /* VIDEO */ + free(DATA_FILE); free(BLOCK_FILE); free(user_settings); @@ -1131,6 +1139,11 @@ int main(int argc, char *argv[]) #ifdef AUDIO av = init_audio(prompt, m); + +#ifdef VIDEO + av = init_video(prompt, m, av); + +#endif /* VIDEO*/ /* audio thread */ if (pthread_create(&audio_thread.tid, NULL, thread_audio, (void *) av) != 0) diff --git a/src/video_call.c b/src/video_call.c new file mode 100644 index 0000000..450ebf1 --- /dev/null +++ b/src/video_call.c @@ -0,0 +1,119 @@ +/* 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 . + * + */ + +#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 +#include +#include +#include +#include +#include +#include + +#define cbend pthread_exit(NULL) + +#define MAX_CALLS 10 + +struct VSettings { + VideoError errors; + + ToxAv *av; + + ToxAvCSettings cs; + + Call calls[MAX_CALLS]; +} VSettins; + +ToxAv *init_video(ToxWindow *self, Tox *tox, ToxAv *av) +{ + VSettins.cs = av_DefaultSettings; + VSettins.cs.max_video_height = 1024; + VSettins.cs.max_video_width = 1024; + + VSettins.errors = ve_None; + + VSettins.av = av; + + memset(VSettins.calls, 0, sizeof(VSettins.calls)); + + if( !VSettins.av ) { + //line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "New toxav instance"); + VSettins.av = toxav_new(tox, MAX_CALLS); + } + + if ( !VSettins.av ) { + VSettins.errors |= ve_StartingCoreVideo; + return NULL; + } + + if ( init_video_devices(VSettins.av) == vde_InternalError ) { + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init video devices"); + //toxav_kill(VSettins.av); + return VSettins.av = NULL; + } + + return VSettins.av; +} + +void terminate_video() +{ + int i; + for (i = 0; i < MAX_CALLS; ++i) + stop_video_transmission(&VSettins.calls[i], i); + + terminate_video_devices(); +} + +int start_video_transmission(ToxWindow *self, Call *call) +{ + + return 0; +} + +int stop_video_transmission(Call *call, int call_index) +{ + + return 0; +} + +/* + * Commands from chat_commands.h + */ +void cmd_enablevid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) +{ + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Video Enabled"); + return; +} + +void cmd_disablevid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) +{ + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Video Disabled"); + return; +} \ No newline at end of file diff --git a/src/video_call.h b/src/video_call.h new file mode 100644 index 0000000..9cb1844 --- /dev/null +++ b/src/video_call.h @@ -0,0 +1,45 @@ +/* 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 . + * + */ + +#ifndef VIDEO_H +#define VIDEO_H + +#include + +#include "audio_call.h" +#include "video_device.h" + +typedef enum _VideoError { + ve_None = 0, + ve_StartingCaptureDevice = 1 << 0, + ve_StartingOutputDevice = 1 << 1, + ve_StartingCoreVideo = 1 << 2 +} 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); +void terminate_video(); +int start_video_transmission(ToxWindow *self, Call *call); +int stop_video_transmission(Call *call, int call_index); +void stop_current_video_call(ToxWindow *self); + + #endif /* VIDEO_H */ \ No newline at end of file diff --git a/src/video_device.c b/src/video_device.c new file mode 100644 index 0000000..0f20e1d --- /dev/null +++ b/src/video_device.c @@ -0,0 +1,326 @@ +/* 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 . + * + */ + +#include "video_device.h" + +#ifdef VIDEO +#include "video_call.h" +#endif + +#include "line_info.h" +#include "settings.h" + +#include +#include +#include + +#ifdef __linux__ +#include +#include +#include +#include +#endif /* __linux __ */ + +#ifdef __WIN32 +#include +#include + +#pragma comment(lib, "strmiids.lib") +#endif /* __WIN32 */ + +#include +#include +#include +#include +#include +#include + +#define inline__ inline __attribute__((always_inline)) + +extern struct user_settings *user_settings; + + typedef struct VideoDevice { + CvCapture* dhndl; + char* window; + IplImage* frame; + int32_t call_idx; + + uint32_t ref_count; + int32_t selection; + pthread_mutex_t mutex[1]; + uint16_t frame_width; + uint16_t frame_height; + } VideoDevice; + +const char *default_video_device_names[2]; +const char *video_device_names[2][MAX_DEVICES]; +static int video_device_size[2]; +VideoDevice *video_device_running[2][MAX_DEVICES] = {{NULL}}; +uint32_t primary_video_device[2]; + +#ifdef VIDEO +static ToxAv* av = NULL; +#endif /* VIDEO */ + +pthread_mutex_t video_mutex; + +bool video_thread_running = true, + video_thread_paused = true; + +void* video_thread_poll(void*); +/* Meet devices */ +#ifdef VIDEO +VideoDeviceError init_video_devices(ToxAv* av_) +#else +VideoDeviceError init_video_devices() +#endif /* AUDIO */ +{ + video_device_size[input] = 0; + uint32_t i; +#ifdef __linux__ + /* Enumerate video capture devices using v4l */ + for(i = 0; i <= MAX_DEVICES; ++i) + { + int fd; + struct v4l2_capability video_cap; + char device_address[256]; + snprintf(device_address, sizeof device_address, "/dev/video%d",i); + + if((fd = open(device_address, O_RDONLY)) == -1) { + break; + } + else { + // Query capture device information + if(ioctl(fd, VIDIOC_QUERYCAP, &video_cap) == -1) + perror("cam_info: Can't get capabilities"); + else { + int name_length = sizeof(video_cap.card); + char* name = (char*)malloc(name_length+1); + name = video_cap.card; + video_device_names[input][i] = name; + } + close(fd); + video_device_size[input] = i; + } + } +#endif /* __linux__ */ + +#ifdef __WIN32 + /* Enumerate video capture devices using win32 api */ + + HRESULT hr; + CoInitialize(NULL); + ICreateDevEnum *pSysDevEnum = NULL; + hr = CoCreateInstance(&CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, &IID_ICreateDevEnum, (void**)&pSysDevEnum); + if(FAILED(hr)) { + printf("CoCreateInstance failed()\n"); + } + // Obtain a class enumerator for the video compressor category. + IEnumMoniker *pEnumCat = NULL; + hr = pSysDevEnum->lpVtbl->CreateClassEnumerator(pSysDevEnum, &CLSID_VideoInputDeviceCategory, &pEnumCat, 0); + if(hr != S_OK) { + pSysDevEnum->lpVtbl->Release(pSysDevEnum); + printf("CreateClassEnumerator failed()\n"); + } + + IMoniker *pMoniker = NULL; + + ULONG cFetched; + i = 0; + while( pEnumCat->lpVtbl->Next(pEnumCat, 1, &pMoniker, &cFetched) == S_OK && i <= MAX_DEVICES ) { + IPropertyBag *pPropBag; + hr = pMoniker->lpVtbl->BindToStorage(pMoniker, 0, 0, &IID_IPropertyBag, (void **)&pPropBag); + if(SUCCEEDED(hr)) { + /* To retrieve the filter's friendly name, do the following: */ + VARIANT varName; + VariantInit(&varName); + hr = pPropBag->lpVtbl->Read(pPropBag, L"FriendlyName", &varName, 0); + if (SUCCEEDED(hr)) { + if(varName.vt == VT_BSTR) { + int name_length = wcslen(varName.bstrVal); + char* name = (char*)malloc(name_length + 1); + wcstombs(name, varName.bstrVal, name_length + 1); + video_device_names[input][i] = name; + } else { + video_device_names[input][i] = "Unknown Device"; + } + ++i; + } + + VariantClear(&varName); + pPropBag->lpVtbl->Release(pPropBag); + } + pMoniker->lpVtbl->Release(pMoniker); + } + video_device_size[input] = i-1; + pEnumCat->lpVtbl->Release(pEnumCat); + pSysDevEnum->lpVtbl->Release(pSysDevEnum); +#endif /* __WIN32 */ + + /* 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); + + if (pthread_mutex_destroy(&video_mutex) != 0) + return (VideoDeviceError) vde_InternalError; + + return (VideoDeviceError) vde_None; +} + +void* video_thread_poll(void* arg) +{ + (void)arg; + uint32_t i; + + while(video_thread_running) + { + if (video_thread_paused) usleep(10000); /* Wait for unpause. */ + else + { + for (i = 0; i < video_device_size[input]; ++i) + { + pthread_mutex_lock(&video_mutex); + + if (video_device_running[input][i] != NULL) + { + /* Capture video frame data of input device */ + video_device_running[input][i]->frame = cvQueryFrame(video_device_running[input][i]->dhndl); + } + + pthread_mutex_unlock(&video_mutex); + } + usleep(5000); + } + } + + pthread_exit(NULL); +} + + +VideoDeviceError set_primary_video_device(DeviceType type, int32_t selection) +{ + if (video_device_size[type] <= selection || selection < 0) return (VideoDeviceError) vde_InvalidSelection; + primary_video_device[type] = selection; + + return (VideoDeviceError) vde_None; +} + +VideoDeviceError open_video_device(DeviceType type, int32_t selection, uint32_t* device_idx) +{ + if (video_device_size[type] <= selection || selection < 0) return vde_InvalidSelection; + + pthread_mutex_lock(&video_mutex); + uint32_t i; + for (i = 0; i < MAX_DEVICES; ++i) { /* Check if any device has the same selection */ + if ( video_device_running[type][i] && video_device_running[type][i]->selection == selection ) { + + video_device_running[type][*device_idx] = video_device_running[type][i]; + video_device_running[type][i]->ref_count++; + + pthread_mutex_unlock(&video_mutex); + return vde_None; + } + } + + VideoDevice* device = video_device_running[type][*device_idx] = calloc(1, sizeof(VideoDevice)); + device->selection = selection; + + if (pthread_mutex_init(device->mutex, NULL) != 0) { + free(device); + pthread_mutex_unlock(&video_mutex); + return vde_InternalError; + } + + if (type == input) { + device->window = video_device_names[input][selection]; + cvNamedWindow(device->window, 1); + device->dhndl = cvCreateCameraCapture(selection); + } + else { + device->dhndl = NULL; + device->window = video_device_names[output][selection]; + if ( device->dhndl || !device->window ) { + free(device); + video_device_running[type][*device_idx] = NULL; + pthread_mutex_unlock(&video_mutex); + return vde_FailedStart; + } + + cvNamedWindow(device->window,1); + } + + if (type == input) { + video_thread_paused = false; + } + + pthread_mutex_unlock(&video_mutex); + return vde_None; +} + +VideoDeviceError close_video_device(DeviceType type, uint32_t device_idx) +{ + if (device_idx >= MAX_DEVICES) return vde_InvalidSelection; + + pthread_mutex_lock(&video_mutex); + VideoDevice* device = video_device_running[type][device_idx]; + VideoDeviceError rc = de_None; + + if (!device) { + pthread_mutex_unlock(&video_mutex); + return vde_DeviceNotActive; + } + + video_device_running[type][device_idx] = NULL; + + if ( !device->ref_count ) { + if (type == input) { + cvReleaseCapture(&device->dhndl); + cvDestroyWindow(device->window); + } + else { + cvDestroyWindow(device->window); + } + + free(device); + } + else device->ref_count--; + + pthread_mutex_unlock(&video_mutex); + return rc; +} \ No newline at end of file diff --git a/src/video_device.h b/src/video_device.h new file mode 100644 index 0000000..1474b85 --- /dev/null +++ b/src/video_device.h @@ -0,0 +1,66 @@ +/* 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 . + * + */ + +/* + * You can have multiple sources (Input devices) but only one output device. + * Pass buffers to output device via write(); + * Read from running input device(s) via select()/callback combo. + */ + +#ifndef VIDEO_DEVICE_H +#define VIDEO_DEVICE_H + +#define MAX_DEVICES 32 +#include +#include "windows.h" + +#include "audio_device.h" + +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_OpenCVError = -9, +} VideoDeviceError; + +#ifdef VIDEO +VideoDeviceError init_video_devices(ToxAv* av); +#else +VideoDeviceError init_video_devices(); +#endif /* VIDEO */ + +VideoDeviceError terminate_video_devices(); + +VideoDeviceError set_primary_video_device(DeviceType type, int32_t selection); +VideoDeviceError open_primary_video_device(DeviceType type); +/* Start device */ +VideoDeviceError open_video_device(DeviceType type, int32_t selection, uint32_t* device_idx); +/* Stop device */ +VideoDeviceError close_video_device(DeviceType type, uint32_t device_idx); + +#endif /* VIDEO_DEVICE_H */ \ No newline at end of file