├── data ├── locale │ ├── da-DK.ini │ ├── de-DE.ini │ ├── el-GR.ini │ ├── es-ES.ini │ ├── eu-ES.ini │ ├── fi-FI.ini │ ├── fr-FR.ini │ ├── gl-ES.ini │ ├── hr-HR.ini │ ├── hu-HU.ini │ ├── ja-JP.ini │ ├── ko-KR.ini │ ├── nb-NO.ini │ ├── nl-NL.ini │ ├── pl-PL.ini │ ├── pt-BR.ini │ ├── ro-RO.ini │ ├── ru-RU.ini │ ├── sl-SI.ini │ ├── sr-CS.ini │ ├── sr-SP.ini │ ├── sv-SE.ini │ ├── th-TH.ini │ ├── tr-TR.ini │ ├── zh-TW.ini │ └── en-US.ini └── scripts │ └── OBSPythonManager.py ├── .gitignore ├── obs-python-module.h ├── CMakeLists.txt ├── swig ├── CMakeLists.txt └── obspython.i ├── external ├── FindLibobs.cmake └── ObsPluginHelpers.cmake ├── py-obs.h ├── utils.h ├── obs-python-module.c └── py-source.h /data/locale/da-DK.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/locale/de-DE.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/locale/el-GR.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/locale/es-ES.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/locale/eu-ES.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/locale/fi-FI.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/locale/fr-FR.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/locale/gl-ES.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/locale/hr-HR.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/locale/hu-HU.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/locale/ja-JP.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/locale/ko-KR.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/locale/nb-NO.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/locale/nl-NL.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/locale/pl-PL.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/locale/pt-BR.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/locale/ro-RO.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/locale/ru-RU.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/locale/sl-SI.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/locale/sr-CS.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/locale/sr-SP.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/locale/sv-SE.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/locale/th-TH.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/locale/tr-TR.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/locale/zh-TW.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/locale/en-US.ini: -------------------------------------------------------------------------------- 1 | Text="OBS Text" 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /data/scripts/__* 2 | /data/scripts/libobs.py 3 | /swig/libobs_wrap.c 4 | *.pyc 5 | 6 | build/ 7 | builds/ 8 | build32/ 9 | build64/ 10 | debug/ 11 | debug32/ 12 | debug64/ 13 | release/ 14 | release32/ 15 | release64/ 16 | -------------------------------------------------------------------------------- /obs-python-module.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | Copyright (C) 2014 Andrew Skinner 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 17 | ********************************************************************************/ 18 | #pragma once 19 | 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "py-source.h" 26 | 27 | 28 | 29 | OBS_DECLARE_MODULE() 30 | OBS_MODULE_USE_DEFAULT_LOCALE("obs-python", "en-US") 31 | OBS_MODULE_AUTHOR("Andrew Skinner") 32 | 33 | const char * obs_module_name(void){ 34 | return "obs-python"; 35 | } 36 | 37 | 38 | 39 | const char * obs_module_description(void){ 40 | return "obs-python"; 41 | } 42 | 43 | 44 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(obs-python) 3 | 4 | 5 | 6 | set(Python_ADDITIONAL_VERSIONS 3.4) 7 | find_package(PythonLibs 3.4 REQUIRED) 8 | 9 | find_package(LibObs REQUIRED) 10 | find_package(SWIG 2 REQUIRED) 11 | include(${SWIG_USE_FILE}) 12 | include(external/ObsPluginHelpers.cmake) 13 | 14 | 15 | include_directories(${PYTHON_INCLUDE_DIR}) 16 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 17 | include_directories(${CMAKE_CURRENT_BINARY_DIR}) 18 | 19 | 20 | 21 | set(obs-python_SOURCES 22 | obs-python-module.c 23 | py-source.h 24 | py-obs.h) 25 | 26 | 27 | add_library(obs-python SHARED 28 | ${obs-python_SOURCES}) 29 | 30 | 31 | target_link_libraries(obs-python 32 | libobs 33 | ${PYTHON_LIBRARIES}) 34 | 35 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") 36 | 37 | add_definitions( -DSWIG_TYPE_TABLE=obspython ) 38 | add_subdirectory(swig) 39 | 40 | set(SWIG_RUNTIME swig/swigpyrun.h) 41 | add_custom_command(OUTPUT ${SWIG_RUNTIME} 42 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 43 | COMMAND ${SWIG_EXECUTABLE} -python -external-runtime ${SWIG_RUNTIME} 44 | ) 45 | set_source_files_properties( ${SWIG_RUNTIME} PROPERTIES GENERATED TRUE ) 46 | add_custom_target(swig-runtime 47 | DEPENDS ${SWIG_RUNTIME} 48 | ) 49 | 50 | add_dependencies(obs-python swig-runtime) 51 | 52 | 53 | 54 | function(install_plugin_with_data target data_loc) 55 | if(APPLE) 56 | set(_bit_suffix "") 57 | elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) 58 | set(_bit_suffix "64bit/") 59 | else() 60 | set(_bit_suffix "32bit/") 61 | endif() 62 | 63 | set_target_properties(${target} PROPERTIES 64 | PREFIX "") 65 | 66 | add_custom_command(TARGET ${target} POST_BUILD 67 | COMMAND "${CMAKE_COMMAND}" -E copy 68 | "${CMAKE_CFG_INTDIR}/$" 69 | "${EXTERNAL_PLUGIN_OUTPUT_DIR}/${CMAKE_CFG_INTDIR}/obs-plugins/${_bit_suffix}/$" 70 | VERBATIM) 71 | 72 | add_custom_command(TARGET ${target} POST_BUILD 73 | COMMAND "${CMAKE_COMMAND}" -E copy_directory 74 | "${CMAKE_CURRENT_SOURCE_DIR}/${data_loc}" 75 | "${EXTERNAL_PLUGIN_OUTPUT_DIR}/${CMAKE_CFG_INTDIR}/data/obs-plugins/${target}/" 76 | VERBATIM) 77 | endfunction() 78 | 79 | 80 | install_plugin_with_data(obs-python data) 81 | 82 | 83 | -------------------------------------------------------------------------------- /swig/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(obs-python-swig) 3 | 4 | 5 | find_package(SWIG 2 REQUIRED) 6 | find_package(LibObs) 7 | include(${SWIG_USE_FILE}) 8 | include(../external/ObsPluginHelpers.cmake) 9 | 10 | set(Python_ADDITIONAL_VERSIONS 3.4) 11 | find_package(PythonLibs 3.4 REQUIRED) 12 | 13 | include_directories(${PYTHON_INCLUDE_DIR}) 14 | include_directories(${LIBOBS_INCLUDE_DIRS}) 15 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 16 | 17 | 18 | #add_definitions( -DSWIG_TYPE_TABLE=libobs ) 19 | SET_SOURCE_FILES_PROPERTIES(obspython.i PROPERTIES SWIG_FLAGS "-modern") 20 | SET_SOURCE_FILES_PROPERTIES(obspython.i PROPERTIES SWIG_FLAGS "-builtin") 21 | SET_SOURCE_FILES_PROPERTIES(obspython.i PROPERTIES SWIG_FLAGS "-modernargs") 22 | SET_SOURCE_FILES_PROPERTIES(obspython.i PROPERTIES SWIG_FLAGS "-includeall") 23 | SET_SOURCE_FILES_PROPERTIES(obspython.i PROPERTIES SWIG_FLAGS "-importall") 24 | SET_SOURCE_FILES_PROPERTIES(obspython.i PROPERTIES SWIG_FLAGS "-py3") 25 | 26 | 27 | 28 | SWIG_ADD_MODULE(obspython python obspython.i) 29 | SWIG_LINK_LIBRARIES(obspython obs-python libobs ${PYTHON_LIBRARIES} ) 30 | 31 | #if(UNIX) 32 | # if(APPLE) 33 | # install(FILES "${CMAKE_CURRENT_BINARY_DIR}/_obspython.dylib" DESTINATION "data/obs-plugins/obs-python/scripts") 34 | # else(APPLE) 35 | # install(FILES "${CMAKE_CURRENT_BINARY_DIR}/_obspython.so" DESTINATION "data/obs-plugins/obs-python/scripts") 36 | # endif(APPLE) 37 | #endif(UNIX) 38 | # 39 | #if(WIN32) 40 | # install(FILES "${CMAKE_CURRENT_BINARY_DIR}/_obspython.pyd" DESTINATION "data/obs-plugins/obs-python/scripts") 41 | #endif(WIN32) 42 | 43 | 44 | 45 | function(install_plugin_bin_swig target additional_target) 46 | if(APPLE) 47 | set(_bit_suffix "") 48 | elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) 49 | set(_bit_suffix "64bit/") 50 | else() 51 | set(_bit_suffix "32bit/") 52 | endif() 53 | 54 | set_target_properties(${additional_target} PROPERTIES 55 | PREFIX "") 56 | 57 | add_custom_command(TARGET ${additional_target} POST_BUILD 58 | COMMAND "${CMAKE_COMMAND}" -E copy 59 | "${CMAKE_CURRENT_BINARY_DIR}/obspython.py" 60 | "${EXTERNAL_PLUGIN_OUTPUT_DIR}/${CMAKE_CFG_INTDIR}/data/obs-plugins/${target}/${_bit_suffix}/obspython.py" 61 | VERBATIM) 62 | add_custom_command(TARGET ${additional_target} POST_BUILD 63 | COMMAND "${CMAKE_COMMAND}" -E copy 64 | "$" 65 | "${EXTERNAL_PLUGIN_OUTPUT_DIR}/${CMAKE_CFG_INTDIR}/data/obs-plugins/${target}/${_bit_suffix}/$" 66 | VERBATIM) 67 | endfunction() 68 | 69 | 70 | 71 | 72 | install_plugin_bin_swig (obs-python _obspython) 73 | #install(FILES "${CMAKE_CURRENT_BINARY_DIR}/obspython.py" DESTINATION "data/obs-plugins/obs-python/scripts") 74 | #install_external_plugin(obs-python obs-python-swig) 75 | -------------------------------------------------------------------------------- /swig/obspython.i: -------------------------------------------------------------------------------- 1 | %module(threads="1") obspython 2 | %nothread; 3 | %{ 4 | #define SWIG_FILE_WITH_INIT 5 | //#define SWIG_PYTHON_SAFE_CSTRINGS 6 | #define PDEPRECATED_START 7 | #define DEPRECATED_END 8 | #include "graphics/graphics.h" 9 | #include "obs.h" 10 | #include "obs-source.h" 11 | #include "obs-data.h" 12 | #include "obs-properties.h" 13 | #include "obs-module.h" 14 | #include "obs-interaction.h" 15 | #include "util/bmem.h" 16 | %} 17 | 18 | #define DEPRECATED_START 19 | #define DEPRECATED_END 20 | #define EXPORT 21 | 22 | %include "stdint.i" 23 | 24 | %typemap(in) uint8_t *data { 25 | if(!PyByteArray_Check($input)) 26 | { 27 | SWIG_exception_fail(SWIG_TypeError, "Expected a bytearray"); 28 | } 29 | $1 = PyByteArray_AsString($input); 30 | } 31 | 32 | 33 | %typemap(in) uint8_t **data (uint8_t *tmp) 34 | { 35 | if(!PyByteArray_Check($input)) { 36 | SWIG_exception_fail(SWIG_TypeError, "Expected a bytearray"); 37 | } 38 | tmp = PyByteArray_AsString($input); 39 | $1 = &tmp; 40 | } 41 | 42 | 43 | /*buf and alloc are expected*/ 44 | %typemap(in) const char *(Py_ssize_t len, char *buf, PyObject *py_bytes, 45 | int alloc) 46 | { 47 | alloc = SWIG_OLDOBJ; // We don't want swig to attempt to free this mem 48 | /*Python 3 Only*/ 49 | if (!PyUnicode_Check($input)) { 50 | SWIG_exception_fail(SWIG_TypeError, "Expected a string"); 51 | } 52 | 53 | py_bytes = PyUnicode_AsUTF8String($input); 54 | /* Keep this bytes object around cause fck memory managment for now 55 | It would probably be better to use some sort of cache for this, 56 | which would need to be global and have fast insert and search 57 | or 58 | We could force the user to cache the strings themselfs, sit back 59 | back and laugh 60 | */ 61 | Py_INCREF(py_bytes); //never let go jack 62 | 63 | PyBytes_AsStringAndSize(py_bytes, &buf, &len); 64 | $1 = buf; 65 | } 66 | 67 | 68 | 69 | 70 | %ignore obs_source_info; 71 | 72 | /* Used to free when using %newobject */ 73 | %typemap(newfree) char * "bfree($1);"; 74 | 75 | %ignore obs_module_text; 76 | %ignore obs_source_info; 77 | 78 | %ignore obs_register_source_s(const struct obs_source_info *info, size_t size); 79 | %ignore obs_output_set_video(obs_output_t *output, video_t *video); 80 | %ignore obs_output_video(const obs_output_t *output); 81 | %include "graphics/graphics.h" 82 | %include "obs-data.h" 83 | %include "obs-source.h" 84 | %include "obs-properties.h" 85 | %include "obs-interaction.h" 86 | 87 | 88 | /* Declare that this returns something to free */ 89 | %newobject obs_module_get_config_path; 90 | %newobject obs_find_module_file; 91 | %ignore obs_current_module; 92 | %include "obs-module.h" 93 | 94 | 95 | %include "util/bmem.h" 96 | 97 | /*declare these manually because mutex + GIL = deadlocks*/ 98 | %thread; 99 | void obs_enter_graphics(void); //Should only block on entering mutex 100 | %nothread; 101 | %include "obs.h" 102 | 103 | 104 | -------------------------------------------------------------------------------- /external/FindLibobs.cmake: -------------------------------------------------------------------------------- 1 | # This module can be copied and used by external plugins for OBS 2 | # 3 | # Once done these will be defined: 4 | # 5 | # LIBOBS_FOUND 6 | # LIBOBS_INCLUDE_DIRS 7 | # LIBOBS_LIBRARIES 8 | 9 | find_package(PkgConfig QUIET) 10 | if (PKG_CONFIG_FOUND) 11 | pkg_check_modules(_OBS QUIET obs libobs) 12 | endif() 13 | 14 | if(CMAKE_SIZEOF_VOID_P EQUAL 8) 15 | set(_lib_suffix 64) 16 | else() 17 | set(_lib_suffix 32) 18 | endif() 19 | 20 | if(DEFINED CMAKE_BUILD_TYPE) 21 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 22 | set(_build_type_base "debug") 23 | else() 24 | set(_build_type_base "release") 25 | endif() 26 | endif() 27 | 28 | find_path(LIBOBS_INCLUDE_DIR 29 | NAMES obs.h 30 | HINTS 31 | ENV obsPath${_lib_suffix} 32 | ENV obsPath 33 | ${obsPath} 34 | PATHS 35 | /usr/include /usr/local/include /opt/local/include /sw/include 36 | PATH_SUFFIXES 37 | libobs 38 | ) 39 | 40 | function(find_obs_lib base_name repo_build_path lib_name) 41 | string(TOUPPER "${base_name}" base_name_u) 42 | 43 | if(DEFINED _build_type_base) 44 | set(_build_type_${repo_build_path} "${_build_type_base}/${repo_build_path}") 45 | set(_build_type_${repo_build_path}${_lib_suffix} "${_build_type_base}${_lib_suffix}/${repo_build_path}") 46 | endif() 47 | 48 | find_library(${base_name_u}_LIB 49 | NAMES ${_${base_name_u}_LIBRARIES} ${lib_name} lib${lib_name} 50 | HINTS 51 | ENV obsPath${_lib_suffix} 52 | ENV obsPath 53 | ${obsPath} 54 | ${_${base_name_u}_LIBRARY_DIRS} 55 | PATHS 56 | /usr/lib /usr/local/lib /opt/local/lib /sw/lib 57 | PATH_SUFFIXES 58 | lib${_lib_suffix} lib 59 | libs${_lib_suffix} libs 60 | bin${_lib_suffix} bin 61 | ../lib${_lib_suffix} ../lib 62 | ../libs${_lib_suffix} ../libs 63 | ../bin${_lib_suffix} ../bin 64 | # base repo non-msvc-specific search paths 65 | ${_build_type_${repo_build_path}} 66 | ${_build_type_${repo_build_path}${_lib_suffix}} 67 | build/${repo_build_path} 68 | build${_lib_suffix}/${repo_build_path} 69 | # base repo msvc-specific search paths on windows 70 | build${_lib_suffix}/${repo_build_path}/Debug 71 | build${_lib_suffix}/${repo_build_path}/RelWithDebInfo 72 | build/${repo_build_path}/Debug 73 | build/${repo_build_path}/RelWithDebInfo 74 | ) 75 | endfunction() 76 | 77 | find_obs_lib(LIBOBS libobs obs) 78 | 79 | if(MSVC) 80 | find_obs_lib(W32_PTHREADS deps/w32-pthreads w32-pthreads) 81 | endif() 82 | 83 | include(FindPackageHandleStandardArgs) 84 | find_package_handle_standard_args(Libobs DEFAULT_MSG LIBOBS_LIB LIBOBS_INCLUDE_DIR) 85 | mark_as_advanced(LIBOBS_INCLUDE_DIR LIBOBS_LIB) 86 | 87 | if(LIBOBS_FOUND) 88 | if(MSVC) 89 | if (NOT DEFINED W32_PTHREADS_LIB) 90 | message(FATAL_ERROR "Could not find the w32-pthreads library" ) 91 | endif() 92 | 93 | set(W32_PTHREADS_INCLUDE_DIR ${LIBOBS_INCLUDE_DIR}/../deps/w32-pthreads) 94 | endif() 95 | 96 | set(LIBOBS_INCLUDE_DIRS ${LIBOBS_INCLUDE_DIR} ${W32_PTHREADS_INCLUDE_DIR}) 97 | set(LIBOBS_LIBRARIES ${LIBOBS_LIB} ${W32_PTHREADS_LIB}) 98 | include(${LIBOBS_INCLUDE_DIR}/../cmake/external/ObsPluginHelpers.cmake) 99 | 100 | # allows external plugins to easily use/share common dependencies that are often included with libobs (such as FFmpeg) 101 | if(NOT DEFINED INCLUDED_LIBOBS_CMAKE_MODULES) 102 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${LIBOBS_INCLUDE_DIR}/../cmake/Modules/") 103 | set(INCLUDED_LIBOBS_CMAKE_MODULES true) 104 | endif() 105 | else() 106 | message(FATAL_ERROR "Could not find the libobs library" ) 107 | endif() 108 | -------------------------------------------------------------------------------- /py-obs.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | Copyright (C) 2014 Andrew Skinner 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 17 | ********************************************************************************/ 18 | 19 | #include 20 | #include "py-source.h" 21 | 22 | 23 | 24 | 25 | static PyObject* 26 | py_obs_blog(PyObject* self, PyObject* args) 27 | { 28 | UNUSED_PARAMETER(self); 29 | long argLength = PyTuple_Size(args); 30 | if (argLength != 1) { 31 | PyErr_SetString(PyExc_TypeError, "Wrong number of arguments"); 32 | return NULL; 33 | } 34 | PyUnicodeObject* str; 35 | 36 | if (!PyArg_ParseTuple(args, "U", &str)) { 37 | return NULL; 38 | } 39 | 40 | char* cstr =PyUnicode_AsUTF8((PyObject*)str); 41 | 42 | blog(LOG_INFO,"%s",cstr); 43 | 44 | Py_RETURN_NONE; 45 | } 46 | 47 | static PyObject* 48 | py_obs_register_source(PyObject* self, PyObject* args) 49 | { 50 | UNUSED_PARAMETER(self); 51 | long argLength = PyTuple_Size(args); 52 | if (argLength != 1) { 53 | PyErr_SetString(PyExc_TypeError, "Wrong number of arguments"); 54 | return NULL; 55 | } 56 | 57 | PyObject* obj; 58 | 59 | if (!PyArg_ParseTuple(args, "O", &obj)) { 60 | return NULL; 61 | } 62 | 63 | if (!PyObject_TypeCheck(obj,&py_source_type)) { 64 | PyErr_SetString(PyExc_TypeError, "Object is not obspython.Source or subclass of obspython.Source"); 65 | return NULL; 66 | } 67 | 68 | py_source* py_src = (py_source*)obj; 69 | 70 | //updates all the C calls to call the correct python wrapper functions 71 | py_to_obs_source_info(py_src); 72 | 73 | obs_register_source(py_src->py_source_info); 74 | 75 | //Make sure the object stays around. 76 | Py_INCREF(py_src); 77 | 78 | Py_RETURN_NONE; 79 | } 80 | 81 | static PyObject* 82 | py_obs_current_module(PyObject* self, PyObject* args) 83 | { 84 | UNUSED_PARAMETER(self); 85 | UNUSED_PARAMETER(args); 86 | PyObject* obj; 87 | 88 | int SWIG_result = SWIG_OK; 89 | 90 | //Swig types 91 | const char* SWIG_str_obs_module_t = "obs_module_t *"; 92 | 93 | //Python 94 | PyObject* py_swig_obs_module_pointer = NULL; 95 | SWIG_result = libobs_to_py_swig(SWIG_str_obs_module_t, obs_module_pointer, 0, &py_swig_obs_module_pointer); 96 | 97 | if (!SWIG_IsOK(SWIG_result)) { 98 | blog(LOG_INFO, 99 | "%s:l%i \"SWIG Failed to make required python object: '%s' \"", 100 | __func__, 101 | __LINE__, 102 | SWIG_str_obs_module_t); 103 | goto fail; 104 | } 105 | fail: 106 | return py_swig_obs_module_pointer; 107 | } 108 | 109 | 110 | 111 | static PyMethodDef py_obs_methods[] = { 112 | {"blog",py_obs_blog,METH_VARARGS,"Writes to the obs log"}, 113 | {"obs_register_source",py_obs_register_source,METH_VARARGS,"Registers a new source with obs."}, 114 | {"obs_current_module",py_obs_current_module,METH_NOARGS,"Helper function to get the current module pointer"}, 115 | { NULL, NULL, 0, NULL } 116 | }; 117 | 118 | 119 | 120 | 121 | void add_functions_to_py_module(PyObject* module,PyMethodDef* method_list) 122 | { 123 | 124 | PyObject* dict = PyModule_GetDict(module); 125 | PyObject* name = PyModule_GetNameObject(module); 126 | if(dict == NULL || name == NULL) { 127 | return; 128 | } 129 | for(PyMethodDef* ml = method_list; ml->ml_name != NULL; ml++) { 130 | PyObject* func = PyCFunction_NewEx(ml,module,name); 131 | if(func == NULL) { 132 | continue; 133 | } 134 | PyDict_SetItemString(dict, ml->ml_name, func); 135 | Py_DECREF(func); 136 | } 137 | Py_DECREF(name); 138 | } 139 | 140 | 141 | 142 | void* extend_swig_libobs(PyObject* py_swig_libobs) 143 | { 144 | 145 | 146 | if (PyType_Ready(&py_source_type) < 0) { 147 | return NULL; 148 | } 149 | 150 | Py_INCREF(&py_source_type); 151 | 152 | PyModule_AddObject(py_swig_libobs , "obs_source_info", (PyObject*)&py_source_type); 153 | 154 | add_functions_to_py_module(py_swig_libobs,py_obs_methods); 155 | 156 | return NULL; 157 | } 158 | 159 | -------------------------------------------------------------------------------- /external/ObsPluginHelpers.cmake: -------------------------------------------------------------------------------- 1 | # Functions for generating external plugins 2 | 3 | set(EXTERNAL_PLUGIN_OUTPUT_DIR "${CMAKE_BINARY_DIR}/rundir") 4 | 5 | # Fix XCode includes to ignore warnings on system includes 6 | function(target_include_directories_system _target) 7 | if(XCODE) 8 | foreach(_arg ${ARGN}) 9 | if("${_arg}" STREQUAL "PRIVATE" OR "${_arg}" STREQUAL "PUBLIC" OR "${_arg}" STREQUAL "INTERFACE") 10 | set(_scope ${_arg}) 11 | else() 12 | target_compile_options(${_target} ${_scope} -isystem${_arg}) 13 | endif() 14 | endforeach() 15 | else() 16 | target_include_directories(${_target} SYSTEM ${_scope} ${ARGN}) 17 | endif() 18 | endfunction() 19 | 20 | function(install_external_plugin_data_internal target source_dir target_dir) 21 | install(DIRECTORY ${source_dir}/ 22 | DESTINATION "${target}/${target_dir}" 23 | USE_SOURCE_PERMISSIONS) 24 | add_custom_command(TARGET ${target} POST_BUILD 25 | COMMAND "${CMAKE_COMMAND}" -E copy_directory 26 | "${CMAKE_CURRENT_SOURCE_DIR}/${source_dir}" "${EXTERNAL_PLUGIN_OUTPUT_DIR}/$/${target}/${target_dir}" 27 | VERBATIM) 28 | endfunction() 29 | 30 | # Installs data 31 | # 'target' is the destination target project being installed to 32 | # 'data_loc' specifies the directory of the data 33 | function(install_external_plugin_data target data_loc) 34 | install_external_plugin_data_internal(${target} ${data_loc} "data") 35 | endfunction() 36 | 37 | # Installs data in an architecture-specific data directory on windows/linux (data/32bit or data/64bit). Does not apply for mac. 38 | # 'target' is the destination target project being installed to 39 | # 'data_loc' specifies the directory of the data being installed 40 | function(install_external_plugin_arch_data target data_loc) 41 | if(APPLE) 42 | set(_bit_suffix "") 43 | elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) 44 | set(_bit_suffix "/64bit") 45 | else() 46 | set(_bit_suffix "/32bit") 47 | endif() 48 | 49 | install_external_plugin_data_internal(${target} ${data_loc} "data${_bit_suffix}") 50 | endfunction() 51 | 52 | # Installs data in the target's bin directory 53 | # 'target' is the destination target project being installed to 54 | # 'data_loc' specifies the directory of the data being installed 55 | function(install_external_plugin_data_to_bin target data_loc) 56 | if(APPLE) 57 | set(_bit_suffix "") 58 | elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) 59 | set(_bit_suffix "/64bit") 60 | else() 61 | set(_bit_suffix "/32bit") 62 | endif() 63 | 64 | install_external_plugin_data_internal(${target} ${data_loc} "bin${_bit_suffix}") 65 | endfunction() 66 | 67 | # Installs an additional binary to a target 68 | # 'target' is the destination target project being installed to 69 | # 'additional_target' specifies the additional binary 70 | function(install_external_plugin_additional target additional_target) 71 | if(APPLE) 72 | set(_bit_suffix "") 73 | elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) 74 | set(_bit_suffix "64bit/") 75 | else() 76 | set(_bit_suffix "32bit/") 77 | endif() 78 | 79 | set_target_properties(${additional_target} PROPERTIES 80 | PREFIX "") 81 | 82 | install(TARGETS ${additional_target} 83 | LIBRARY DESTINATION "bin" 84 | RUNTIME DESTINATION "bin") 85 | add_custom_command(TARGET ${additional_target} POST_BUILD 86 | COMMAND "${CMAKE_COMMAND}" -E copy 87 | "$" 88 | "${EXTERNAL_PLUGIN_OUTPUT_DIR}/$/${target}/bin/${_bit_suffix}$" 89 | VERBATIM) 90 | endfunction() 91 | 92 | # Installs the binary of the target 93 | # 'target' is the target project being installed 94 | function(install_external_plugin target) 95 | install_external_plugin_additional(${target} ${target}) 96 | endfunction() 97 | 98 | # Installs the binary and data of the target 99 | # 'target' is the destination target project being installed to 100 | function(install_external_plugin_with_data target data_loc) 101 | install_external_plugin(${target}) 102 | install_external_plugin_data(${target} ${data_loc}) 103 | endfunction() 104 | 105 | # Installs an additional binary to the data of a target 106 | # 'target' is the destination target project being installed to 107 | # 'additional_target' specifies the additional binary 108 | function(install_external_plugin_bin_to_data target additional_target) 109 | install(TARGETS ${target} 110 | LIBRARY DESTINATION "data" 111 | RUNTIME DESTINATION "data") 112 | add_custom_command(TARGET ${target} POST_BUILD 113 | COMMAND "${CMAKE_COMMAND}" -E copy 114 | "$" 115 | "${EXTERNAL_PLUGIN_OUTPUT_DIR}/$/${plugin_target}/data/$" 116 | VERBATIM) 117 | endfunction() 118 | 119 | # Installs an additional binary in an architecture-specific data directory on windows/linux (data/32bit or data/64bit). Does not apply for mac. 120 | # 'target' is the destination target project being installed to 121 | # 'additional_target' specifies the additional binary 122 | function(install_external_plugin_bin_to_arch_data target additional_target) 123 | if(APPLE) 124 | set(_bit_suffix "") 125 | elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) 126 | set(_bit_suffix "/64bit") 127 | else() 128 | set(_bit_suffix "/32bit") 129 | endif() 130 | 131 | install(TARGETS ${target} 132 | LIBRARY DESTINATION "data${_bit_suffix}" 133 | RUNTIME DESTINATION "data${_bit_suffix}") 134 | add_custom_command(TARGET ${target} POST_BUILD 135 | COMMAND "${CMAKE_COMMAND}" -E copy 136 | "$" 137 | "${EXTERNAL_PLUGIN_OUTPUT_DIR}/$/${plugin_target}/data${_bit_suffix}/$" 138 | VERBATIM) 139 | endfunction() 140 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | Copyright (C) 2014 Andrew Skinner 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 17 | ********************************************************************************/ 18 | 19 | 20 | #pragma once 21 | 22 | 23 | #include 24 | #include "swig/swigpyrun.h" 25 | 26 | 27 | #include "util/darray.h" 28 | #include "util/dstr.h" 29 | 30 | #if defined ( WIN32 ) 31 | #define __func__ __FUNCTION__ 32 | #else 33 | #include 34 | #endif 35 | 36 | 37 | static bool pyHasError() 38 | { 39 | if (PyErr_Occurred()) { 40 | PyErr_Print(); 41 | return true; 42 | } 43 | return false; 44 | } 45 | 46 | static bool isPyObjectBaseClass(PyObject *obj, const char *type ) 47 | { 48 | 49 | PyObject *tuple = obj->ob_type->tp_bases; 50 | Py_ssize_t len = PyTuple_Size(tuple); 51 | for (int i = 0; i < len; i++) { 52 | PyObject *base = PyTuple_GetItem(tuple, i); 53 | PyObject *name = PyObject_GetAttrString(base, "__name__"); 54 | pyHasError(); 55 | char *cname = NULL; 56 | if (name != NULL) { 57 | cname = PyUnicode_AsUTF8(obj); 58 | } 59 | if (cname != NULL ) { 60 | if (strcmp(cname, type)== 0) { 61 | return true; 62 | } 63 | } 64 | } 65 | return false; 66 | } 67 | 68 | 69 | static void add_to_python_path(char* cstr) 70 | { 71 | //make sure that sys is imported 72 | PyRun_SimpleString("import sys"); 73 | PyObject* path = PySys_GetObject("path"); 74 | pyHasError(); 75 | 76 | 77 | 78 | blog(LOG_INFO, "PATH: %s",cstr); 79 | PyList_Append(path, PyUnicode_FromString(cstr)); 80 | pyHasError(); 81 | 82 | return; 83 | } 84 | 85 | 86 | static void add_enum_to_dict(PyObject *tp_dict, char ** names){ 87 | int i = 0; 88 | while(names[i] != NULL){ 89 | PyDict_SetItemString(tp_dict,names[i],PyLong_FromLong(i)); 90 | i++; 91 | } 92 | 93 | } 94 | 95 | //must be used when GIL lock is held 96 | static inline int py_swig_to_libobs(const char *SWIG_type_str, PyObject *py_in, 97 | void *libobs_out) 98 | { 99 | 100 | swig_type_info *SWIG_type_info = SWIG_TypeQuery(SWIG_type_str); 101 | 102 | if (SWIG_type_info == NULL) { 103 | blog(LOG_INFO, "SWIG could not find type : %s",SWIG_type_str); 104 | return SWIG_ERROR ; 105 | } 106 | 107 | return SWIG_ConvertPtr(py_in, libobs_out, SWIG_type_info, 0); 108 | } 109 | 110 | 111 | static inline int libobs_to_py_swig(const char *SWIG_type_str, void *libobs_in, 112 | int ownership, PyObject **py_out) 113 | { 114 | 115 | swig_type_info *SWIG_type_info = SWIG_TypeQuery(SWIG_type_str); 116 | 117 | if (SWIG_type_info == NULL) { 118 | blog(LOG_INFO, "SWIG could not find type : %s",SWIG_type_str); 119 | return SWIG_ERROR; 120 | } 121 | 122 | *py_out = SWIG_NewPointerObj(libobs_in, SWIG_type_info, ownership); 123 | 124 | if(*py_out == Py_None) { 125 | return SWIG_ERROR; 126 | } 127 | return SWIG_OK; 128 | } 129 | 130 | 131 | static bool is_python_on_path() 132 | { 133 | 134 | char *pypath = getenv("PYTHONPATH"); 135 | if (pypath == NULL) { 136 | blog(LOG_WARNING, 137 | "%s:l%i \"Did not find 'PYTHONPATH' in environment searching trying 'PYTHONHOME'\"", 138 | __func__, 139 | __LINE__ 140 | ); 141 | } else { 142 | return true; 143 | } 144 | 145 | char *pyhome = getenv("PYTHONHOME"); 146 | if (pyhome == NULL) { 147 | blog(LOG_WARNING, 148 | "%s:l%i \"Did not find 'PYTHONHOME' in environment searching trying 'PATH'\"", 149 | __func__, 150 | __LINE__ 151 | ); 152 | } else { 153 | return true; 154 | } 155 | 156 | char *path = getenv("PATH"); 157 | if (path) { 158 | char *python = strstr(path, "Python"); 159 | if (python) { 160 | return true; 161 | } else { 162 | blog(LOG_WARNING, 163 | "%s:l%i \"Did not find python in 'PATH' environment variable.\"", 164 | __func__, 165 | __LINE__ 166 | ); 167 | } 168 | } else { 169 | blog(LOG_WARNING, 170 | "%s:l%i \"Could not get 'PATH' environment variable.\"", 171 | __func__, 172 | __LINE__ 173 | ); 174 | } 175 | return false; 176 | } 177 | 178 | #if defined ( __GNUC__ ) 179 | void *os_dlopen_global(const char *path) 180 | { 181 | struct dstr dylib_name; 182 | 183 | if (!path) 184 | return NULL; 185 | 186 | dstr_init_copy(&dylib_name, path); 187 | if (!dstr_find(&dylib_name, ".so")) 188 | dstr_cat(&dylib_name, ".so"); 189 | 190 | void *res = dlopen(dylib_name.array, RTLD_LAZY | RTLD_GLOBAL); 191 | if (!res) 192 | blog(LOG_ERROR, "os_dlopen(%s->%s): %s\n", 193 | path, dylib_name.array, dlerror()); 194 | 195 | dstr_free(&dylib_name); 196 | return res; 197 | } 198 | #endif 199 | -------------------------------------------------------------------------------- /obs-python-module.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | Copyright (C) 2014 Andrew Skinner 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 17 | ********************************************************************************/ 18 | 19 | #include 20 | 21 | 22 | #include "obs-python-module.h" 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | 29 | #include "utils.h" 30 | #include "py-obs.h" 31 | 32 | 33 | #define STR_HELPER(x) #x 34 | #define STR(x) STR_HELPER(x) 35 | 36 | 37 | // Check windows 38 | #if _WIN32 || _WIN64 39 | #define PYTHON_SHARED_LIBRARY_NAME "python" STR(PY_MAJOR_VERSION)".dll" 40 | #if _WIN64 41 | #define PLUGINARCH "/64bit" 42 | #else 43 | #define PLUGINARCH "/32bit" 44 | #endif 45 | #endif 46 | 47 | 48 | 49 | // Check GCC 50 | #if __GNUC__ 51 | #define PYTHON_SHARED_LIBRARY_NAME "libpython" STR(PY_MAJOR_VERSION) "." STR(PY_MINOR_VERSION) "m.so" 52 | 53 | #if __x86_64__ || __ppc64__ 54 | #define PLUGINARCH "/64bit" 55 | #else 56 | #define PLUGINARCH "/32bit" 57 | #endif 58 | #endif 59 | 60 | 61 | 62 | 63 | bool obs_module_load() 64 | { 65 | blog(LOG_INFO, "obs_module_load"); 66 | 67 | if(!is_python_on_path()){ 68 | blog(LOG_WARNING, 69 | "%s:l%i \"Warning could not detect python environment variables attempting to load shared library anyway\"", 70 | __func__, 71 | __LINE__ 72 | ); 73 | } 74 | 75 | // Manually force python to be loaded 76 | #if __GNUC__ 77 | if(!os_dlopen_global(PYTHON_SHARED_LIBRARY_NAME)){ 78 | #else 79 | if(!os_dlopen(PYTHON_SHARED_LIBRARY_NAME)){ 80 | #endif 81 | blog(LOG_ERROR, 82 | "%s:l%i \"Error Could not load python shared library %s aborting!\"", 83 | __func__, 84 | __LINE__, 85 | PYTHON_SHARED_LIBRARY_NAME 86 | ); 87 | return false; 88 | } 89 | 90 | 91 | Py_Initialize(); 92 | PyEval_InitThreads(); 93 | 94 | 95 | /*Must set arguments for guis to work*/ 96 | wchar_t* argv[] = { L"", NULL }; 97 | int argc = sizeof(argv) / sizeof(wchar_t*) - 1; 98 | 99 | 100 | PySys_SetArgv(argc, argv); 101 | 102 | /* Setup logs to a safe place can be changed by user in OBSPythonManager.py register function*/ 103 | PyRun_SimpleString("import os"); 104 | PyRun_SimpleString("import sys"); 105 | PyRun_SimpleString("os.environ['PYTHONUNBUFFERED'] = '1'"); 106 | /* TODO change to non platform specific */ 107 | PyRun_SimpleString("sys.stdout = open('./stdOut.txt','w',1)"); 108 | PyRun_SimpleString("sys.stderr = open('./stdErr.txt','w',1)"); 109 | PyRun_SimpleString("print(sys.version)"); 110 | 111 | 112 | /*Load manager from file*/ 113 | PyObject* pName = NULL; 114 | PyObject* pModule = NULL; 115 | PyObject* pFunc = NULL; 116 | PyObject* argList = NULL; 117 | 118 | bool ret = false; 119 | 120 | char script[] = "/scripts"; 121 | char arch[] = PLUGINARCH; 122 | const char *data_path = obs_get_module_data_path(obs_current_module()); 123 | char *scripts_path = bzalloc(strlen(data_path)+strlen(script)); 124 | 125 | strcpy(scripts_path,data_path); 126 | strcat(scripts_path,script); 127 | 128 | 129 | //Add the scripts path to env 130 | add_to_python_path(scripts_path); 131 | 132 | bfree(scripts_path); 133 | 134 | 135 | scripts_path = bzalloc(strlen(data_path)+strlen(arch)); 136 | strcpy(scripts_path,data_path); 137 | strcat(scripts_path,arch); 138 | 139 | //Add the plugin obspython arch path to env 140 | add_to_python_path(scripts_path); 141 | 142 | 143 | /* load the obspython library and extend with manually written functions/objects */ 144 | PyObject *py_libobs = PyImport_ImportModule("obspython"); 145 | ret = pyHasError(); 146 | if (ret){ 147 | blog(LOG_INFO, 148 | "%s:l%i \"Error importing '%s/obspython.py' unloading obs-python\"", 149 | __func__, 150 | __LINE__, 151 | scripts_path 152 | ); 153 | goto out; 154 | } 155 | 156 | extend_swig_libobs(py_libobs); 157 | 158 | 159 | //Import the manager script 160 | pName = PyUnicode_FromString("OBSPythonManager"); 161 | pModule = PyImport_Import(pName); 162 | 163 | ret = pyHasError(); 164 | if (ret){ 165 | blog(LOG_INFO, 166 | "%s:l%i \"Error loading '%s/OBSPythonManager.py' unloading obs-python\"", 167 | __func__, 168 | __LINE__, 169 | scripts_path 170 | ); 171 | goto out; 172 | } 173 | 174 | //get the function by name 175 | if(pModule != NULL) { 176 | PyModule_AddObject(pModule,"obspython",py_libobs); 177 | pFunc = PyObject_GetAttr(pModule, PyUnicode_FromString("obs_module_load")); 178 | if(pFunc != NULL) { 179 | argList = Py_BuildValue("()"); 180 | PyObject_CallObject(pFunc,argList); 181 | ret = pyHasError(); 182 | if (ret){ 183 | blog(LOG_INFO, 184 | "%s:l%i \"Error running 'register' function in '%s/OBSPythonManager.py' unloading obs-python\"", 185 | __func__, 186 | __LINE__, 187 | scripts_path 188 | ); 189 | goto out; 190 | } 191 | }else{ 192 | ret = pyHasError(); 193 | blog(LOG_INFO, 194 | "%s:l%i \"Could not find register function in '%s/OBSPythonManager.py' unloading obs-python\"", 195 | __func__, 196 | __LINE__, 197 | scripts_path 198 | ); 199 | } 200 | goto out; 201 | } 202 | 203 | out: 204 | bfree(scripts_path); 205 | Py_XDECREF(pFunc); 206 | Py_XDECREF(argList); 207 | Py_XDECREF(pModule); 208 | Py_XDECREF(pName); 209 | //Release the thread GIL 210 | PyThreadState* pts = PyGILState_GetThisThreadState(); 211 | PyEval_ReleaseThread(pts); 212 | if(!ret){ 213 | return true; 214 | }else{ 215 | obs_module_unload(); 216 | return false; 217 | } 218 | } 219 | 220 | void obs_module_unload() 221 | { 222 | //Shutdown python and call shutdown functions 223 | blog(LOG_INFO, "obs_module_unload"); 224 | 225 | PyGILState_STATE gstate; 226 | gstate = PyGILState_Ensure(); 227 | 228 | UNUSED_PARAMETER(gstate); 229 | 230 | if (Py_IsInitialized()) { 231 | Py_Finalize(); 232 | } 233 | 234 | } 235 | 236 | -------------------------------------------------------------------------------- /data/scripts/OBSPythonManager.py: -------------------------------------------------------------------------------- 1 | import obspython as libobs 2 | import os,traceback,sys 3 | import json 4 | from importlib.machinery import SourceFileLoader 5 | 6 | 7 | class OBSPythonManager(): 8 | def __init__(self,settings,source): 9 | self.source = source 10 | self.width = 0 11 | self.height = 0 12 | self.bpp = 4 13 | 14 | self.SetColour(255,255,255,255) 15 | self.multi = 1 16 | self.rand = 0 17 | self.liveSource = False 18 | LEVELS =1 19 | 20 | 21 | #print(libobs.obs_module_get_config_path(libobs.obs_current_module(), "")) 22 | #print(libobs.obs_find_module_file(libobs.obs_current_module(), "")) 23 | #libobs.obs_data_set_string(settings,"SourceFile",__file__) 24 | 25 | @staticmethod 26 | def create(settings,source): 27 | 28 | #myScene = libobs.obs_scene_create("Fancy Pants") 29 | #mySource = libobs.obs_scene_get_source(myScene) 30 | #libobs.obs_add_source(mySource) 31 | 32 | #libobs.obs_set_output_source(0,libobs.obs_scene_get_source(myScene)) 33 | #libobs.obs_scene_release(myScene) 34 | return OBSPythonManager(settings,source) 35 | def render(self,effect): 36 | #Manager does not render 37 | pass 38 | def tick(self,seconds): 39 | pass 40 | def get_width(self): 41 | return self.width 42 | def get_height(self): 43 | return self.height 44 | def destroy(self): 45 | pass 46 | def get_properties(self): 47 | print("PythonManager get_properties") 48 | self.liveSource = True 49 | 50 | self.props = libobs.obs_properties_create() 51 | 52 | #need to load from the save file or new python managers will wipe our scripts 53 | data = open_json_config_file("PythonScripts.json") 54 | obsdata = libobs.obs_data_create_from_json(json.dumps(data)) 55 | libobs.obs_source_update(self.source,obsdata) 56 | libobs.obs_data_release(obsdata) 57 | 58 | scriptFiles = libobs.obs_properties_add_editable_list(self.props, 59 | "ScriptFiles", 60 | "PythonScriptFiles", 61 | True, 62 | "*.py", 63 | "../../data/obs-plugins/obs-python/scripts") 64 | 65 | libobs.obs_properties_apply_settings(self.props,obsdata) 66 | 67 | return self.props 68 | 69 | def update(self,data): 70 | #Called when a property is changed. 71 | print("PythonManager Update") 72 | #should probably check that script can be loaded and remove it if it cant 73 | #should still work though 74 | return 75 | 76 | def save(self,data): 77 | print("PythonManager Save") 78 | 79 | if self.liveSource: 80 | filename = "PythonScripts.json" 81 | scriptFiles = 'ScriptFiles' 82 | parsed = json.loads(libobs.obs_data_get_json(data)) 83 | 84 | savedArray = parsed[scriptFiles] 85 | try: 86 | currentArray = open_json_config_file(filename)[scriptFiles] 87 | except: 88 | currentArray = [] 89 | 90 | print (currentArray) 91 | toRemove = [] 92 | toAdd = [] 93 | 94 | savedDict = set() 95 | currentDict = set() 96 | 97 | for v in currentArray: 98 | print(v) 99 | currentDict.add(v['value']) 100 | 101 | for v in savedArray: 102 | savedDict.add(v['value']) 103 | 104 | for k in savedDict: 105 | print(k) 106 | if k not in currentDict: 107 | toAdd.append(k) 108 | 109 | for k in currentDict: 110 | print(k) 111 | if k not in savedDict: 112 | toRemove.append(k) 113 | 114 | print (toRemove) 115 | 116 | for script in toAdd: 117 | run_register(script) 118 | 119 | save_json_config_file(parsed,filename) 120 | self.liveSource = False 121 | 122 | return 123 | 124 | 125 | 126 | 127 | 128 | @staticmethod 129 | def get_name(): 130 | return "PythonManager" 131 | 132 | def SetColour(self,r,g,b,a): 133 | for i in range(0,self.width*self.height*self.bpp,self.bpp): 134 | self.pixelbuffer[i] = b #blue 135 | self.pixelbuffer[i+1] = g #green 136 | self.pixelbuffer[i+2] = r #red 137 | self.pixelbuffer[i+3] = a #alpha 138 | 139 | def register(): 140 | 141 | src = libobs.obs_source_info() 142 | src.id = "PythonManager" 143 | src.get_name = OBSPythonManager.get_name 144 | src.create = OBSPythonManager.create 145 | src.video_render = OBSPythonManager.render 146 | src.video_tick = OBSPythonManager.tick 147 | src.get_height = OBSPythonManager.get_height 148 | src.get_width = OBSPythonManager.get_width 149 | src.destroy = OBSPythonManager.destroy 150 | src.get_properties = OBSPythonManager.get_properties 151 | src.update = OBSPythonManager.update 152 | src.save = OBSPythonManager.save 153 | libobs.obs_register_source(src) 154 | 155 | print ("Registered OBSPythonManager") 156 | 157 | 158 | def open_json_config_file(filename): 159 | #get correct config filepath 160 | filepath = libobs.obs_module_get_config_path(libobs.obs_current_module(),filename) 161 | print (filepath) 162 | 163 | try: 164 | fd = open(filepath,'r') 165 | except FileNotFoundError: 166 | dirpath = os.path.dirname(filepath) 167 | if not os.path.exists(dirpath): 168 | os.makedirs(dirpath) 169 | emptyDict = {} 170 | #create the empty file 171 | save_json_config_file(emptyDict,filename) 172 | fd = open(filepath,'r') 173 | data = json.load(fd) 174 | #print(data) 175 | fd.close() 176 | return data 177 | 178 | def save_json_config_file(data,filename): 179 | filepath = libobs.obs_module_get_config_path(libobs.obs_current_module(),filename) 180 | fd = open(filepath,'w+') 181 | json.dump(data,fd) 182 | fd.close() 183 | 184 | def run_register(scriptFile): 185 | try: 186 | scriptName = os.path.basename(scriptFile) 187 | script = SourceFileLoader(scriptName, scriptFile).load_module() 188 | script.register() 189 | print ("Loaded '%s' from '%s'"%(scriptName,scriptFile)) 190 | except Exception as e: 191 | print("Failed to load '%s' from '%s'"%(scriptName,scriptFile), file=sys.stderr) 192 | traceback.print_exc() 193 | 194 | 195 | def register_scripts(): 196 | #Get other scripts from config file 197 | filename = "PythonScripts.json" 198 | data = open_json_config_file(filename) 199 | if not data: 200 | return 201 | for i in range(0,len(data["ScriptFiles"])): 202 | #check that script is not already loaded. 203 | # TODO currently there is no unregister in libobs will need to find another way 204 | # wonder what happens when we just register again Kappa 205 | run_register(data["ScriptFiles"][i]["value"]) 206 | 207 | 208 | def obs_module_load(): 209 | 210 | #log to a file 211 | os.environ['PYTHONUNBUFFERED'] = '1' 212 | sys.stdout = open('./stdOut.txt','w',1) 213 | sys.stderr = open('./stdErr.txt','w',1) 214 | print(sys.version) 215 | 216 | #load python manager 217 | register() 218 | register_scripts() 219 | 220 | 221 | 222 | -------------------------------------------------------------------------------- /py-source.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | Copyright (C) 2015 Andrew Skinner 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 17 | ********************************************************************************/ 18 | #pragma once 19 | 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "utils.h" 26 | #include "obs-python-module.h" 27 | 28 | 29 | #if defined ( WIN32 ) 30 | #define __func__ __FUNCTION__ 31 | #endif 32 | 33 | /*Object Data*/ 34 | typedef struct { 35 | PyObject_HEAD 36 | PyObject* id; 37 | PyObject* type; 38 | PyObject* flags; 39 | PyObject* get_name; 40 | PyObject* create; 41 | PyObject* destroy; 42 | PyObject* get_width; 43 | PyObject* get_height; 44 | PyObject* get_defaults; 45 | PyObject* get_properties; 46 | PyObject* update; 47 | PyObject* activate; 48 | PyObject* deactivate; 49 | PyObject* show; 50 | PyObject* hide; 51 | PyObject* video_tick; 52 | PyObject* video_render; 53 | PyObject* filter_video; 54 | PyObject* filter_audio; 55 | PyObject* enum_sources; 56 | PyObject* save; 57 | PyObject* load; 58 | PyObject* mouse_click; 59 | PyObject* mouse_move; 60 | PyObject* mouse_wheel; 61 | PyObject* focus; 62 | PyObject* key_click; 63 | PyObject* data; 64 | /* C types below this point*/ 65 | struct obs_source_info* py_source_info; 66 | char* name; 67 | } py_source; 68 | 69 | 70 | 71 | //The regestired types 72 | struct python_source { 73 | py_source* source; 74 | struct python_source* next; 75 | struct python_source* prev; 76 | }; 77 | 78 | 79 | struct python_data_pair { 80 | py_source* source; 81 | PyObject* data; 82 | }; 83 | 84 | 85 | 86 | 87 | static void 88 | py_source_dealloc(py_source* self) 89 | { 90 | Py_XDECREF(self->id); 91 | Py_XDECREF(self->data); 92 | Py_XDECREF(self->type); 93 | Py_XDECREF(self->flags); 94 | Py_XDECREF(self->get_name); 95 | Py_XDECREF(self->create); 96 | Py_XDECREF(self->destroy); 97 | Py_XDECREF(self->get_width); 98 | Py_XDECREF(self->get_height); 99 | Py_XDECREF(self->get_defaults); 100 | Py_XDECREF(self->get_properties); 101 | Py_XDECREF(self->update); 102 | Py_XDECREF(self->activate); 103 | Py_XDECREF(self->deactivate); 104 | Py_XDECREF(self->show); 105 | Py_XDECREF(self->hide); 106 | Py_XDECREF(self->video_tick); 107 | Py_XDECREF(self->video_render); 108 | Py_XDECREF(self->filter_video); 109 | Py_XDECREF(self->filter_audio); 110 | Py_XDECREF(self->enum_sources); 111 | Py_XDECREF(self->save); 112 | Py_XDECREF(self->load); 113 | Py_XDECREF(self->mouse_click); 114 | Py_XDECREF(self->mouse_move); 115 | Py_XDECREF(self->mouse_wheel); 116 | Py_XDECREF(self->focus); 117 | Py_XDECREF(self->key_click); 118 | 119 | bfree(self->py_source_info); 120 | bfree(self->name); 121 | 122 | Py_TYPE(self)->tp_free((PyObject*)self); 123 | } 124 | 125 | static PyObject* py_source_new(PyTypeObject* type, PyObject* args, 126 | PyObject* kwds) 127 | { 128 | 129 | UNUSED_PARAMETER(args); 130 | UNUSED_PARAMETER(kwds); 131 | py_source* self; 132 | 133 | 134 | self = (py_source*)type->tp_alloc(type, 0); 135 | 136 | if (self != NULL) { 137 | 138 | self->id = PyUnicode_FromString("python_default"); 139 | if (self->id == NULL) { 140 | Py_DECREF(self); 141 | return NULL; 142 | } 143 | self->type = PyLong_FromLong(OBS_SOURCE_TYPE_INPUT); 144 | if (self->type == NULL) { 145 | Py_DECREF(self); 146 | return NULL; 147 | } 148 | self->flags = PyLong_FromLong(OBS_SOURCE_VIDEO); 149 | if (self->flags == NULL) { 150 | Py_DECREF(self); 151 | return NULL; 152 | } 153 | self->get_name = Py_BuildValue(""); 154 | if (self->get_name == NULL) { 155 | Py_DECREF(self); 156 | return NULL; 157 | } 158 | self->create = Py_BuildValue(""); 159 | if (self->create == NULL) { 160 | Py_DECREF(self); 161 | return NULL; 162 | } 163 | self->destroy = Py_BuildValue(""); 164 | if (self->destroy == NULL) { 165 | Py_DECREF(self); 166 | return NULL; 167 | } 168 | self->data = Py_BuildValue(""); 169 | if (self->data == NULL) { 170 | Py_DECREF(self); 171 | return NULL; 172 | } 173 | 174 | self->get_width = Py_BuildValue(""); 175 | if (self->get_width == NULL) { 176 | Py_DECREF(self); 177 | return NULL; 178 | } 179 | self->get_height = Py_BuildValue(""); 180 | if (self->get_height == NULL) { 181 | Py_DECREF(self); 182 | return NULL; 183 | } 184 | self->get_defaults = Py_BuildValue(""); 185 | if (self->get_defaults == NULL) { 186 | Py_DECREF(self); 187 | return NULL; 188 | } 189 | self->get_properties = Py_BuildValue(""); 190 | if (self->get_properties == NULL) { 191 | Py_DECREF(self); 192 | return NULL; 193 | } 194 | self->update = Py_BuildValue(""); 195 | if (self->update == NULL) { 196 | Py_DECREF(self); 197 | return NULL; 198 | } 199 | self->activate = Py_BuildValue(""); 200 | if (self->activate == NULL) { 201 | Py_DECREF(self); 202 | return NULL; 203 | } 204 | self->deactivate = Py_BuildValue(""); 205 | if (self->deactivate == NULL) { 206 | Py_DECREF(self); 207 | return NULL; 208 | } 209 | self->show = Py_BuildValue(""); 210 | if (self->show == NULL) { 211 | Py_DECREF(self); 212 | return NULL; 213 | } 214 | self->hide = Py_BuildValue(""); 215 | if (self->hide == NULL) { 216 | Py_DECREF(self); 217 | return NULL; 218 | } 219 | self->video_tick = Py_BuildValue(""); 220 | if (self->video_tick == NULL) { 221 | Py_DECREF(self); 222 | return NULL; 223 | } 224 | self->video_render = Py_BuildValue(""); 225 | if (self->video_render == NULL) { 226 | Py_DECREF(self); 227 | return NULL; 228 | } 229 | self->filter_video = Py_BuildValue(""); 230 | if (self->filter_video == NULL) { 231 | Py_DECREF(self); 232 | return NULL; 233 | } 234 | self->filter_audio = Py_BuildValue(""); 235 | if (self->filter_audio == NULL) { 236 | Py_DECREF(self); 237 | return NULL; 238 | } 239 | self->enum_sources = Py_BuildValue(""); 240 | if (self->enum_sources == NULL) { 241 | Py_DECREF(self); 242 | return NULL; 243 | } 244 | self->save = Py_BuildValue(""); 245 | if (self->save == NULL) { 246 | Py_DECREF(self); 247 | return NULL; 248 | } 249 | self->load = Py_BuildValue(""); 250 | if (self->load == NULL) { 251 | Py_DECREF(self); 252 | return NULL; 253 | } 254 | self->mouse_click = Py_BuildValue(""); 255 | if (self->mouse_click == NULL) { 256 | Py_DECREF(self); 257 | return NULL; 258 | } 259 | self->mouse_move = Py_BuildValue(""); 260 | if (self->mouse_move == NULL) { 261 | Py_DECREF(self); 262 | return NULL; 263 | } 264 | self->mouse_wheel = Py_BuildValue(""); 265 | if (self->mouse_wheel == NULL) { 266 | Py_DECREF(self); 267 | return NULL; 268 | } 269 | self->focus = Py_BuildValue(""); 270 | if (self->focus == NULL) { 271 | Py_DECREF(self); 272 | return NULL; 273 | } 274 | self->key_click = Py_BuildValue(""); 275 | if (self->key_click == NULL) { 276 | Py_DECREF(self); 277 | return NULL; 278 | } 279 | 280 | 281 | } 282 | 283 | self->name = bstrdup("Default Python Script"); 284 | self->py_source_info = bzalloc(sizeof(struct obs_source_info)); 285 | return (PyObject*)self; 286 | 287 | } 288 | 289 | 290 | static int 291 | py_source_init(py_source* self, PyObject* args, PyObject* kwds) 292 | { 293 | UNUSED_PARAMETER(self); 294 | UNUSED_PARAMETER(args); 295 | UNUSED_PARAMETER(kwds); 296 | /*Do nothing for now*/ 297 | return 0; 298 | } 299 | 300 | 301 | 302 | /*Method Table*/ 303 | static PyMethodDef py_source_methods[] = { 304 | { NULL } /* Sentinel */ 305 | }; 306 | 307 | /*Member table*/ 308 | static PyMemberDef py_source_members[] = { 309 | {"id",T_OBJECT_EX, offsetof(py_source, id), 0,"id" }, 310 | {"type",T_OBJECT_EX, offsetof(py_source, type), 0,"type" }, 311 | {"flags",T_OBJECT_EX, offsetof(py_source, flags), 0,"flags" }, 312 | {"create",T_OBJECT_EX, offsetof(py_source, create), 0,"create" }, 313 | {"destroy",T_OBJECT_EX, offsetof(py_source, destroy), 0,"destroy" }, 314 | {"get_name",T_OBJECT_EX, offsetof(py_source, get_name), 0,"get_name" }, 315 | {"get_width",T_OBJECT_EX, offsetof(py_source, get_width), 0,"get_width" }, 316 | {"get_height",T_OBJECT_EX, offsetof(py_source, get_height), 0,"get_height" }, 317 | {"get_defaults",T_OBJECT_EX, offsetof(py_source, get_defaults), 0,"get_defaults" }, 318 | {"get_properties",T_OBJECT_EX, offsetof(py_source, get_properties), 0,"get_properties" }, 319 | {"update",T_OBJECT_EX, offsetof(py_source, update), 0,"update" }, 320 | {"activate",T_OBJECT_EX, offsetof(py_source, activate), 0,"activate" }, 321 | {"deactivate",T_OBJECT_EX, offsetof(py_source, deactivate), 0,"deactivate" }, 322 | {"show",T_OBJECT_EX, offsetof(py_source, show), 0,"show" }, 323 | {"hide",T_OBJECT_EX, offsetof(py_source, hide), 0,"hide" }, 324 | {"video_tick",T_OBJECT_EX, offsetof(py_source, video_tick), 0,"video_tick" }, 325 | {"video_render",T_OBJECT_EX, offsetof(py_source, video_render), 0,"video_render" }, 326 | {"filter_video",T_OBJECT_EX, offsetof(py_source, filter_video), 0,"filter video" }, 327 | {"filter_audio",T_OBJECT_EX, offsetof(py_source, filter_audio), 0,"filter video" }, 328 | {"enum_sources",T_OBJECT_EX, offsetof(py_source, enum_sources), 0,"enum_sources" }, 329 | {"save",T_OBJECT_EX, offsetof(py_source, save), 0,"save" }, 330 | {"load",T_OBJECT_EX, offsetof(py_source, load), 0,"load" }, 331 | {"mouse_click",T_OBJECT_EX, offsetof(py_source, mouse_click), 0,"mouse_click" }, 332 | {"mouse_move",T_OBJECT_EX, offsetof(py_source, mouse_move), 0,"mouse_move" }, 333 | {"mouse_wheel",T_OBJECT_EX, offsetof(py_source, mouse_wheel), 0,"mouse_wheel" }, 334 | {"focus",T_OBJECT_EX, offsetof(py_source, focus), 0,"focus" }, 335 | {"key_click",T_OBJECT_EX, offsetof(py_source, key_click), 0,"key_click" }, 336 | { NULL } /* Sentinel */ 337 | }; 338 | 339 | 340 | /*Python Type Object */ 341 | static PyTypeObject py_source_type = { 342 | PyVarObject_HEAD_INIT(NULL, 0) 343 | "obspython.obs_source_info", /*tp_name*/ 344 | sizeof(py_source), /*tp_basicsize*/ 345 | 0, /*tp_itemsize*/ 346 | (destructor)py_source_dealloc, /*tp_dealloc*/ 347 | 0, /*tp_print*/ 348 | 0, /*tp_getattr*/ 349 | 0, /*tp_setattr*/ 350 | 0, /*tp_compare*/ 351 | 0, /*tp_repr*/ 352 | 0, /*tp_as_number*/ 353 | 0, /*tp_as_sequence*/ 354 | 0, /*tp_as_mapping*/ 355 | 0, /*tp_hash */ 356 | 0, /*tp_call*/ 357 | 0, /*tp_str*/ 358 | 0, /*tp_getattro*/ 359 | 0, /*tp_setattro*/ 360 | 0, /*tp_as_buffer*/ 361 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ 362 | "pylibobs Source", /* tp_doc */ 363 | 0, /* tp_traverse */ 364 | 0, /* tp_clear */ 365 | 0, /* tp_richcompare */ 366 | 0, /* tp_weaklistoffset */ 367 | 0, /* tp_iter */ 368 | 0, /* tp_iternext */ 369 | py_source_methods, /* tp_methods */ 370 | py_source_members, /* tp_members */ 371 | 0, /* tp_getset */ 372 | 0, /* tp_base */ 373 | 0, /* tp_dict */ 374 | 0, /* tp_descr_get */ 375 | 0, /* tp_descr_set */ 376 | 0, /* tp_dictoffset */ 377 | (initproc)py_source_init, /* tp_init */ 378 | 0, /* tp_alloc */ 379 | py_source_new, /* tp_new */ 380 | }; 381 | 382 | 383 | 384 | static void* py_source_create(obs_data_t* settings, obs_source_t* source) 385 | { 386 | 387 | const char* id = source->info.id ; 388 | struct python_data_pair* py_data = NULL; 389 | 390 | int SWIG_result = SWIG_OK; 391 | 392 | //Swig types 393 | const char* SWIG_str_obs_data_t = "obs_data_t *"; 394 | const char* SWIG_str_obs_source_t = "obs_source_t *"; 395 | 396 | //Python 397 | PyObject* argList = NULL; 398 | PyObject* data = NULL; 399 | PyObject* py_swig_settings = NULL; 400 | PyObject* py_swig_source = NULL; 401 | 402 | py_source* py_src = source->info.type_data; 403 | 404 | 405 | PyGILState_STATE gstate; 406 | gstate = PyGILState_Ensure(); 407 | 408 | //check python function. 409 | if(!PyCallable_Check(py_src->create)) { 410 | blog(LOG_INFO, "None Callable Create: %s",id); 411 | goto fail; 412 | } 413 | 414 | SWIG_result = libobs_to_py_swig(SWIG_str_obs_data_t,settings,0,&py_swig_settings); 415 | 416 | if (!SWIG_IsOK(SWIG_result)) { 417 | blog(LOG_INFO, 418 | "%s:l%i \"SWIG Failed to make required python object: '%s' for '%s'\"", 419 | __func__, 420 | __LINE__, 421 | SWIG_str_obs_data_t, 422 | id); 423 | goto fail; 424 | } 425 | 426 | SWIG_result = libobs_to_py_swig(SWIG_str_obs_source_t,source,0,&py_swig_source); 427 | 428 | if (!SWIG_IsOK(SWIG_result)) { 429 | blog(LOG_INFO, 430 | "%s:l%i \"SWIG Failed to make required python object: '%s' for '%s'\"", 431 | __func__, 432 | __LINE__, 433 | SWIG_str_obs_source_t, 434 | id); 435 | goto fail; 436 | } 437 | 438 | blog(LOG_INFO, "Source Create"); 439 | argList = Py_BuildValue("(OO)",py_swig_settings,py_swig_source); 440 | data = PyObject_CallObject(py_src->create,argList); 441 | 442 | if(pyHasError()) { 443 | goto fail; 444 | } 445 | 446 | py_data = bzalloc(sizeof(struct python_data_pair)); 447 | py_data->data = data; 448 | py_data->source = py_src; 449 | Py_INCREF(py_src); 450 | blog(LOG_INFO, "Python Create: %s",id); 451 | 452 | fail: 453 | Py_XDECREF(argList); 454 | Py_XDECREF(py_swig_settings); 455 | Py_XDECREF(py_swig_source); 456 | PyGILState_Release(gstate); 457 | return py_data; 458 | } 459 | 460 | 461 | 462 | 463 | 464 | 465 | static void py_source_destroy(void* data) 466 | { 467 | struct python_data_pair* py_pair = data; 468 | py_source* py_src = py_pair->source; 469 | 470 | 471 | 472 | PyObject* argList = NULL; 473 | PyObject* py_data = NULL; 474 | 475 | PyGILState_STATE gstate; 476 | gstate = PyGILState_Ensure(); 477 | 478 | //check python function. 479 | if(!PyCallable_Check(py_src->destroy)) { 480 | blog(LOG_INFO, "None Callable destroy: %s",py_src->py_source_info->id); 481 | goto fail; 482 | } 483 | 484 | py_data = py_pair->data; 485 | argList = Py_BuildValue("(O)",py_data); 486 | PyObject_CallObject(py_src->destroy,argList); 487 | if(pyHasError()){ 488 | blog(LOG_INFO, "Error destroying python object cleanly, this could get messy"); 489 | } 490 | 491 | fail: 492 | 493 | Py_XDECREF(argList); 494 | Py_XDECREF(py_data); 495 | 496 | Py_XDECREF(py_src); 497 | 498 | 499 | PyGILState_Release(gstate); 500 | 501 | bfree(data); 502 | 503 | } 504 | static uint32_t py_source_get_width(void* data) 505 | { 506 | 507 | struct python_data_pair* py_pair = data; 508 | py_source* py_src = py_pair->source; 509 | PyObject* py_data = py_pair->data; 510 | long width = 0; 511 | 512 | //Python 513 | PyObject* argList = NULL; 514 | PyObject* ret = NULL; 515 | 516 | PyGILState_STATE gstate; 517 | gstate = PyGILState_Ensure(); 518 | 519 | //check python function. 520 | if(!PyCallable_Check(py_src->get_width)) { 521 | blog(LOG_INFO, "None Callable get_width: %s",py_src->py_source_info->id); 522 | goto fail; 523 | 524 | } 525 | 526 | argList = Py_BuildValue("(O)",py_data); 527 | ret = PyObject_CallObject(py_src->get_width,argList); 528 | if(pyHasError()){ 529 | goto fail; 530 | } 531 | if(PyLong_Check(ret)) { 532 | width = PyLong_AsLong(ret); 533 | } 534 | 535 | fail: 536 | Py_XDECREF(argList); 537 | Py_XDECREF(ret); 538 | PyGILState_Release(gstate); 539 | 540 | return width; 541 | } 542 | static const char* py_source_get_name(void* data) 543 | { 544 | py_source* py_src = data; 545 | 546 | PyObject *argList = NULL; 547 | PyObject *ret = NULL; 548 | 549 | PyGILState_STATE gstate; 550 | gstate = PyGILState_Ensure(); 551 | 552 | //check python function. 553 | if(!PyCallable_Check(py_src->get_name)) { 554 | blog(LOG_INFO, "None Callable get_name: %s",py_src->py_source_info->id); 555 | goto fail; 556 | } 557 | 558 | argList = Py_BuildValue("()"); 559 | ret = PyObject_CallObject(py_src->get_name,argList); 560 | 561 | if(pyHasError()){ 562 | goto fail; 563 | } 564 | 565 | if(PyUnicode_Check(ret)) { 566 | 567 | char* utf8_name = PyUnicode_AsUTF8(ret); 568 | if(pyHasError() || utf8_name == NULL){ 569 | goto fail; 570 | } 571 | 572 | bfree(py_src->name); 573 | py_src->name = bstrdup(utf8_name); 574 | } 575 | 576 | fail: 577 | Py_XDECREF(argList); 578 | Py_XDECREF(ret); 579 | PyGILState_Release(gstate); 580 | return py_src->name; 581 | } 582 | 583 | static uint32_t py_source_get_height(void* data) 584 | { 585 | 586 | struct python_data_pair* py_pair = data; 587 | py_source* py_src = py_pair->source; 588 | PyObject* py_data = py_pair->data; 589 | long height = 0 ; 590 | 591 | //Python 592 | PyObject* argList = NULL; 593 | PyObject* ret = NULL; 594 | 595 | 596 | PyGILState_STATE gstate; 597 | gstate = PyGILState_Ensure(); 598 | 599 | //check python function. 600 | if(!PyCallable_Check(py_src->get_height)) { 601 | blog(LOG_INFO, "None Callable get_height: %s",py_src->py_source_info->id); 602 | goto fail; 603 | } 604 | 605 | argList = Py_BuildValue("(O)",py_data); 606 | ret = PyObject_CallObject(py_src->get_height,argList); 607 | 608 | if(pyHasError()){ 609 | goto fail; 610 | } 611 | if(PyLong_Check(ret)) { 612 | height = PyLong_AsLong(ret); 613 | } 614 | 615 | fail: 616 | Py_XDECREF(argList); 617 | Py_XDECREF(ret); 618 | PyGILState_Release(gstate); 619 | 620 | return height; 621 | } 622 | static obs_properties_t* py_source_properties(void* data) 623 | { 624 | 625 | struct python_data_pair* py_pair = data; 626 | py_source* py_src = py_pair->source; 627 | PyObject* py_data = py_pair->data; 628 | obs_properties_t* obs_properties = NULL; 629 | 630 | 631 | //SWIG 632 | const char* SWIG_str_obs_properties_t = "obs_properties_t *"; 633 | int SWIG_result = SWIG_OK; 634 | 635 | //Python 636 | PyObject* py_result = NULL; 637 | PyObject* argList = NULL; 638 | 639 | 640 | PyGILState_STATE gstate; 641 | gstate = PyGILState_Ensure(); 642 | 643 | 644 | //Check python function 645 | if(!PyCallable_Check(py_src->get_properties)) { 646 | blog(LOG_INFO, "None Callable get_properties: %s",py_src->py_source_info->id); 647 | goto fail; 648 | } 649 | 650 | argList = Py_BuildValue("(O)",py_data); 651 | py_result = PyObject_CallObject(py_src->get_properties, argList); 652 | 653 | if(pyHasError()){ 654 | goto fail; 655 | } 656 | 657 | SWIG_result = py_swig_to_libobs(SWIG_str_obs_properties_t, py_result, &obs_properties); 658 | if(!SWIG_IsOK(SWIG_result)){ 659 | obs_properties = NULL; 660 | } 661 | 662 | fail: 663 | if( obs_properties == NULL){ 664 | obs_properties = obs_properties_create(); 665 | } 666 | Py_XDECREF(argList); 667 | Py_XDECREF(py_result); 668 | PyGILState_Release(gstate); 669 | return obs_properties; 670 | } 671 | 672 | static void py_source_get_defaults(obs_data_t* settings) { 673 | UNUSED_PARAMETER(settings); 674 | 675 | } 676 | static void py_source_update(void* data, obs_data_t* settings) 677 | { 678 | 679 | struct python_data_pair* py_pair = data; 680 | py_source* py_src = py_pair->source; 681 | PyObject* py_data = py_pair->data; 682 | 683 | //SWIG 684 | const char* SWIG_str_obs_data_t = "obs_data_t *"; 685 | int SWIG_result = SWIG_OK; 686 | 687 | //Python 688 | PyObject* argList = NULL; 689 | PyObject* py_swig_settings = NULL; 690 | 691 | PyGILState_STATE gstate; 692 | gstate = PyGILState_Ensure(); 693 | 694 | //Check python function 695 | if(!PyCallable_Check(py_src->update)) { 696 | blog(LOG_INFO, "None Callable update: %s",py_src->py_source_info->id); 697 | goto fail; 698 | } 699 | 700 | SWIG_result = libobs_to_py_swig(SWIG_str_obs_data_t,settings,0,&py_swig_settings); 701 | 702 | if (!SWIG_IsOK(SWIG_result)) { 703 | blog(LOG_INFO, 704 | "%s:l%i \"SWIG Failed to make required python object: '%s' for '%s'\"", 705 | __func__, 706 | __LINE__, 707 | SWIG_str_obs_data_t, 708 | py_src->py_source_info->id); 709 | goto fail; 710 | } 711 | 712 | argList = Py_BuildValue("(OO)",py_data,py_swig_settings); 713 | PyObject_CallObject(py_src->update,argList); 714 | if(pyHasError()){ 715 | goto fail; 716 | } 717 | 718 | fail: 719 | Py_XDECREF(py_swig_settings); 720 | Py_XDECREF(argList); 721 | PyGILState_Release(gstate); 722 | } 723 | 724 | static void py_source_activate(void* data) 725 | { 726 | 727 | struct python_data_pair* py_pair = data; 728 | py_source* py_src = py_pair->source; 729 | PyObject* py_data = py_pair->data; 730 | 731 | //Python 732 | PyObject* argList = NULL; 733 | 734 | PyGILState_STATE gstate; 735 | gstate = PyGILState_Ensure(); 736 | 737 | //Check python function 738 | if(!PyCallable_Check(py_src->activate)) { 739 | blog(LOG_INFO, "None Callable activate: %s",py_src->py_source_info->id); 740 | goto fail; 741 | } 742 | 743 | argList = Py_BuildValue("(O)",py_data); 744 | PyObject_CallObject(py_src->activate,argList); 745 | if(pyHasError()){ 746 | goto fail; 747 | } 748 | 749 | fail: 750 | Py_XDECREF(argList); 751 | PyGILState_Release(gstate); 752 | } 753 | 754 | static void py_source_deactivate(void* data) 755 | { 756 | 757 | struct python_data_pair* py_pair = data; 758 | py_source* py_src = py_pair->source; 759 | PyObject* py_data = py_pair->data; 760 | 761 | //Python 762 | PyObject* argList = NULL; 763 | 764 | PyGILState_STATE gstate; 765 | gstate = PyGILState_Ensure(); 766 | 767 | //Check python function 768 | if(!PyCallable_Check(py_src->deactivate)) { 769 | blog(LOG_INFO, "None Callable deactivate: %s",py_src->py_source_info->id); 770 | goto fail; 771 | } 772 | 773 | argList = Py_BuildValue("(O)",py_data); 774 | PyObject_CallObject(py_src->deactivate,argList); 775 | if(pyHasError()){ 776 | goto fail; 777 | } 778 | 779 | fail: 780 | Py_XDECREF(argList); 781 | PyGILState_Release(gstate); 782 | } 783 | static void py_source_show(void* data) 784 | { 785 | 786 | struct python_data_pair* py_pair = data; 787 | py_source* py_src = py_pair->source; 788 | PyObject* py_data = py_pair->data; 789 | 790 | //Python 791 | PyObject* argList = NULL; 792 | 793 | PyGILState_STATE gstate; 794 | gstate = PyGILState_Ensure(); 795 | 796 | //Check python function 797 | if(!PyCallable_Check(py_src->show)) { 798 | blog(LOG_INFO, "None Callable show: %s",py_src->py_source_info->id); 799 | goto fail; 800 | } 801 | 802 | argList = Py_BuildValue("(O)",py_data); 803 | PyObject_CallObject(py_src->show,argList); 804 | if(pyHasError()) { 805 | goto fail; 806 | } 807 | 808 | fail: 809 | Py_XDECREF(argList); 810 | PyGILState_Release(gstate); 811 | } 812 | 813 | static void py_source_hide(void* data) 814 | { 815 | 816 | struct python_data_pair* py_pair = data; 817 | py_source* py_src = py_pair->source; 818 | PyObject* py_data = py_pair->data; 819 | 820 | //Python 821 | PyObject* argList = NULL; 822 | 823 | PyGILState_STATE gstate; 824 | gstate = PyGILState_Ensure(); 825 | 826 | //Check python function 827 | if(!PyCallable_Check(py_src->hide)) { 828 | blog(LOG_INFO, "None Callable hide: %s",py_src->py_source_info->id); 829 | goto fail; 830 | } 831 | 832 | argList = Py_BuildValue("(O)",py_data); 833 | PyObject_CallObject(py_src->hide,argList); 834 | 835 | if(pyHasError()){ 836 | goto fail; 837 | } 838 | 839 | fail: 840 | Py_XDECREF(argList); 841 | PyGILState_Release(gstate); 842 | } 843 | static void py_source_video_tick(void* data, float seconds) 844 | { 845 | struct python_data_pair* py_pair = data; 846 | py_source* py_src = py_pair->source; 847 | PyObject* py_data = py_pair->data; 848 | 849 | //Python 850 | PyObject* argList = NULL; 851 | 852 | PyGILState_STATE gstate; 853 | gstate = PyGILState_Ensure(); 854 | 855 | //Check python function 856 | if(!PyCallable_Check(py_src->video_tick)) { 857 | blog(LOG_INFO, "None Callable video_tick: %s",py_src->py_source_info->id); 858 | goto fail; 859 | } 860 | 861 | argList = Py_BuildValue("(Of)",py_data,seconds); 862 | PyObject_CallObject(py_src->video_tick,argList); 863 | 864 | fail: 865 | pyHasError(); 866 | Py_XDECREF(argList); 867 | PyGILState_Release(gstate); 868 | } 869 | static void py_source_video_render(void* data, gs_effect_t* effect) 870 | { 871 | 872 | struct python_data_pair* py_pair = data; 873 | py_source* py_src = py_pair->source; 874 | PyObject* py_data = py_pair->data; 875 | 876 | //SWIG 877 | int SWIG_result = SWIG_OK; 878 | const char *SWIG_str_gs_effect_t = "gs_effect_t *"; 879 | 880 | //Python 881 | PyObject* py_swig_effect = NULL; 882 | PyObject* argList = NULL; 883 | 884 | PyGILState_STATE gstate; 885 | gstate = PyGILState_Ensure(); 886 | 887 | //Check python function 888 | if(!PyCallable_Check(py_src->video_render)) { 889 | blog(LOG_INFO, "None Callable video_render: %s",py_src->py_source_info->id); 890 | PyGILState_Release(gstate); 891 | return ; 892 | } 893 | 894 | /*Create SWIG effect object*/ 895 | SWIG_result = libobs_to_py_swig(SWIG_str_gs_effect_t,effect,0,&py_swig_effect); 896 | if(!SWIG_IsOK(SWIG_result)){ 897 | goto fail; 898 | } 899 | 900 | argList = Py_BuildValue("(OO)",py_data,py_swig_effect); 901 | PyObject_CallObject(py_src->video_render,argList); 902 | if(pyHasError()){ 903 | goto fail; 904 | } 905 | 906 | fail: 907 | Py_XDECREF(argList); 908 | Py_XDECREF(py_swig_effect); 909 | PyGILState_Release(gstate); 910 | 911 | } 912 | static struct obs_source_frame* py_source_filter_video(void* data, 913 | struct obs_source_frame* frame) 914 | { 915 | 916 | struct python_data_pair* py_pair = data; 917 | py_source* py_src = py_pair->source; 918 | 919 | PyObject* py_data = py_pair->data; 920 | struct obs_source_frame* new_frame = NULL; 921 | 922 | 923 | //SWIG 924 | int SWIG_result = SWIG_OK; 925 | const char* SWIG_str_obs_source_frame = "obs_source_frame *"; 926 | 927 | //Python 928 | PyObject* argList = NULL; 929 | PyObject* py_frame = NULL; 930 | PyObject* py_ret = NULL; 931 | 932 | PyGILState_STATE gstate; 933 | gstate = PyGILState_Ensure(); 934 | 935 | //Check python function 936 | if(!PyCallable_Check(py_src->filter_video)) { 937 | blog(LOG_INFO, "None Callable filter_video: %s",py_src->py_source_info->id); 938 | goto fail; 939 | } 940 | 941 | SWIG_result = libobs_to_py_swig(SWIG_str_obs_source_frame, (void*)frame, 0, &py_frame); 942 | 943 | if (!SWIG_IsOK(SWIG_result)) { 944 | blog(LOG_INFO, 945 | "%s:l%i \"SWIG Failed to make required python object: '%s' for '%s'\"", 946 | __func__, 947 | __LINE__, 948 | SWIG_str_obs_source_frame, 949 | py_src->py_source_info->id); 950 | goto fail; 951 | } 952 | 953 | argList = Py_BuildValue("(OO)",py_data,py_frame); 954 | py_ret = PyObject_CallObject(py_src->filter_video,argList); 955 | 956 | if(pyHasError() || py_ret == Py_None ){ 957 | goto fail; 958 | } 959 | 960 | 961 | SWIG_result = py_swig_to_libobs(SWIG_str_obs_source_frame, py_ret, new_frame); 962 | 963 | if (!SWIG_IsOK(SWIG_result)) { 964 | blog(LOG_INFO, 965 | "%s:l%i \"SWIG Failed to convert to libobs object: '%s' for '%s'\"", 966 | __func__, 967 | __LINE__, 968 | SWIG_str_obs_source_frame, 969 | py_src->py_source_info->id); 970 | goto fail; 971 | } 972 | 973 | fail: 974 | if ( new_frame == NULL ){ 975 | new_frame = (void*)frame; 976 | } 977 | Py_XDECREF(py_ret); 978 | Py_XDECREF(argList); 979 | Py_XDECREF(py_frame); 980 | PyGILState_Release(gstate); 981 | return new_frame; 982 | } 983 | static struct obs_audio_data* py_source_filter_audio(void* data, 984 | struct obs_audio_data* audio) 985 | { 986 | 987 | struct python_data_pair* py_pair = data; 988 | py_source* py_src = py_pair->source; 989 | PyObject* py_data = py_pair->data; 990 | 991 | 992 | struct obs_audio_data* new_audio = NULL; 993 | 994 | 995 | //SWIG 996 | int SWIG_result = SWIG_OK; 997 | const char* SWIG_str_obs_audio_data = "obs_audio_data *"; 998 | 999 | //Python 1000 | PyObject* argList = NULL; 1001 | PyObject* py_ret = NULL; 1002 | PyObject* py_audio = NULL; 1003 | 1004 | 1005 | PyGILState_STATE gstate; 1006 | gstate = PyGILState_Ensure(); 1007 | 1008 | //Check python function 1009 | if(!PyCallable_Check(py_src->filter_audio)) { 1010 | blog(LOG_INFO, "None Callable filter_audio: %s",py_src->py_source_info->id); 1011 | goto fail; 1012 | } 1013 | 1014 | SWIG_result = libobs_to_py_swig(SWIG_str_obs_audio_data, (void*)audio, 0, &py_audio); 1015 | 1016 | if (!SWIG_IsOK(SWIG_result)) { 1017 | blog(LOG_INFO, 1018 | "%s:l%i \"SWIG Failed to make required python object: '%s' for '%s'\"", 1019 | __func__, 1020 | __LINE__, 1021 | SWIG_str_obs_audio_data, 1022 | py_src->py_source_info->id); 1023 | goto fail; 1024 | } 1025 | 1026 | argList = Py_BuildValue("(O)",py_data); 1027 | py_ret = PyObject_CallObject(py_src->filter_audio,argList); 1028 | 1029 | if(pyHasError() || py_ret == Py_None){ 1030 | goto fail; 1031 | } 1032 | 1033 | SWIG_result = py_swig_to_libobs(SWIG_str_obs_audio_data, py_ret, new_audio); 1034 | 1035 | if (!SWIG_IsOK(SWIG_result)) { 1036 | blog(LOG_INFO, 1037 | "%s:l%i \"SWIG Failed to convert to libobs object: '%s' for '%s'\"", 1038 | __func__, 1039 | __LINE__, 1040 | SWIG_str_obs_audio_data, 1041 | py_src->py_source_info->id); 1042 | goto fail; 1043 | } 1044 | 1045 | 1046 | fail: 1047 | if(new_audio == NULL){ 1048 | new_audio = (void*)audio; 1049 | } 1050 | Py_XDECREF(argList); 1051 | PyGILState_Release(gstate); 1052 | return new_audio; 1053 | } 1054 | static void py_source_enum_sources(void* data, 1055 | obs_source_enum_proc_t enum_callback,void* param) 1056 | { 1057 | 1058 | UNUSED_PARAMETER(enum_callback); 1059 | UNUSED_PARAMETER(param); 1060 | 1061 | struct python_data_pair* py_pair = data; 1062 | py_source* py_src = py_pair->source; 1063 | PyObject* py_data = py_pair->data; 1064 | 1065 | 1066 | PyObject* argList = NULL; 1067 | 1068 | PyGILState_STATE gstate; 1069 | gstate = PyGILState_Ensure(); 1070 | 1071 | //Check python function 1072 | if(!PyCallable_Check(py_src->enum_sources)) { 1073 | blog(LOG_INFO, "None Callable enum_sources: %s",py_src->py_source_info->id); 1074 | goto fail; 1075 | } 1076 | 1077 | argList = Py_BuildValue("(O)",py_data); 1078 | PyObject_CallObject(py_src->enum_sources,argList); 1079 | pyHasError(); 1080 | 1081 | fail: 1082 | Py_XDECREF(argList); 1083 | PyGILState_Release(gstate); 1084 | 1085 | } 1086 | static void py_source_save(void* data,obs_data_t* settings) 1087 | { 1088 | 1089 | struct python_data_pair* py_pair = data; 1090 | py_source* py_src = py_pair->source; 1091 | PyObject* py_data = py_pair->data; 1092 | 1093 | //SWIG 1094 | const char* SWIG_str_obs_data_t = "obs_data_t *"; 1095 | int SWIG_result = SWIG_OK; 1096 | 1097 | //Python 1098 | PyObject* argList = NULL; 1099 | PyObject* py_swig_settings = NULL; 1100 | 1101 | PyGILState_STATE gstate; 1102 | gstate = PyGILState_Ensure(); 1103 | 1104 | //Check python function 1105 | if(!PyCallable_Check(py_src->save)) { 1106 | blog(LOG_INFO, "None Callable save: %s",py_src->py_source_info->id); 1107 | goto fail; 1108 | } 1109 | 1110 | SWIG_result = libobs_to_py_swig(SWIG_str_obs_data_t, settings, 0, &py_swig_settings); 1111 | 1112 | if (!SWIG_IsOK(SWIG_result)) { 1113 | blog(LOG_INFO, 1114 | "%s:l%i \"SWIG Failed to make required python object: '%s' for '%s'\"", 1115 | __func__, 1116 | __LINE__, 1117 | SWIG_str_obs_data_t, 1118 | py_src->py_source_info->id); 1119 | goto fail; 1120 | } 1121 | 1122 | 1123 | argList = Py_BuildValue("(OO)",py_data,py_swig_settings); 1124 | PyObject_CallObject(py_src->save,argList); 1125 | if(pyHasError()){ 1126 | goto fail; 1127 | } 1128 | 1129 | fail: 1130 | Py_XDECREF(argList); 1131 | Py_XDECREF(py_swig_settings); 1132 | PyGILState_Release(gstate); 1133 | } 1134 | 1135 | static void py_source_load(void* data, obs_data_t* settings) 1136 | { 1137 | struct python_data_pair* py_pair = data; 1138 | py_source* py_src = py_pair->source; 1139 | PyObject* py_data = py_pair->data; 1140 | 1141 | //SWIG 1142 | int SWIG_result = SWIG_OK; 1143 | const char* SWIG_str_obs_data_t = "obs_data_t *"; 1144 | 1145 | //Python 1146 | PyObject* argList = NULL; 1147 | PyObject* py_swig_settings = NULL; 1148 | 1149 | PyGILState_STATE gstate; 1150 | gstate = PyGILState_Ensure(); 1151 | 1152 | //Check python function 1153 | if(!PyCallable_Check(py_src->load)) { 1154 | blog(LOG_INFO, "None Callable load: %s",py_src->py_source_info->id); 1155 | goto fail; 1156 | } 1157 | 1158 | SWIG_result = libobs_to_py_swig(SWIG_str_obs_data_t, settings, 0, &py_swig_settings); 1159 | 1160 | if (!SWIG_IsOK(SWIG_result)) { 1161 | blog(LOG_INFO, 1162 | "%s:l%i \"SWIG Failed to make required python object: '%s' for '%s'\"", 1163 | __func__, 1164 | __LINE__, 1165 | SWIG_str_obs_data_t, 1166 | py_src->py_source_info->id); 1167 | goto fail; 1168 | } 1169 | 1170 | argList = Py_BuildValue("(OO)",py_data,py_swig_settings); 1171 | PyObject_CallObject(py_src->load,argList); 1172 | if(pyHasError()){ 1173 | goto fail; 1174 | } 1175 | 1176 | fail: 1177 | Py_XDECREF(py_swig_settings); 1178 | Py_XDECREF(argList); 1179 | PyGILState_Release(gstate); 1180 | } 1181 | static void py_source_mouse_click(void* data, 1182 | const struct obs_mouse_event* event, 1183 | int32_t type, 1184 | bool mouse_up, 1185 | uint32_t click_count) 1186 | { 1187 | struct python_data_pair* py_pair = data; 1188 | py_source* py_src = py_pair->source; 1189 | PyObject* py_data = py_pair->data; 1190 | 1191 | //SWIG 1192 | const char* SWIG_str_obs_mouse_event = "obs_mouse_event *"; 1193 | int SWIG_result = SWIG_OK; 1194 | 1195 | //Python 1196 | PyObject* argList = NULL; 1197 | PyObject* py_swig_event = NULL; 1198 | 1199 | PyGILState_STATE gstate; 1200 | gstate = PyGILState_Ensure(); 1201 | 1202 | //Check python function 1203 | if(!PyCallable_Check(py_src->mouse_click)) { 1204 | blog(LOG_INFO, "None Callable mouuse_click: %s",py_src->py_source_info->id); 1205 | goto fail; 1206 | } 1207 | 1208 | SWIG_result = libobs_to_py_swig(SWIG_str_obs_mouse_event, (void*)event, 0, &py_swig_event); 1209 | 1210 | if (!SWIG_IsOK(SWIG_result)) { 1211 | blog(LOG_INFO, 1212 | "%s:l%i \"SWIG Failed to make required python object: '%s' for '%s'\"", 1213 | __func__, 1214 | __LINE__, 1215 | SWIG_str_obs_mouse_event, 1216 | py_src->py_source_info->id); 1217 | goto fail; 1218 | } 1219 | 1220 | argList = Py_BuildValue("(Oipi)", py_data, type,mouse_up, click_count); 1221 | PyObject_CallObject(py_src->mouse_click,argList); 1222 | if(pyHasError()){ 1223 | goto fail; 1224 | } 1225 | 1226 | fail: 1227 | Py_XDECREF(argList); 1228 | Py_XDECREF(py_swig_event); 1229 | PyGILState_Release(gstate); 1230 | 1231 | } 1232 | static void py_source_mouse_move(void* data, 1233 | const struct obs_mouse_event* event, 1234 | bool mouse_leave) 1235 | { 1236 | struct python_data_pair* py_pair = data; 1237 | py_source* py_src = py_pair->source; 1238 | PyObject* py_data = py_pair->data; 1239 | 1240 | //SWIG 1241 | const char* SWIG_str_obs_mouse_event = "obs_mouse_event *"; 1242 | int SWIG_result = SWIG_OK; 1243 | 1244 | //Python 1245 | PyObject* argList = NULL; 1246 | PyObject* py_swig_event = NULL; 1247 | 1248 | 1249 | PyGILState_STATE gstate; 1250 | gstate = PyGILState_Ensure(); 1251 | 1252 | //Check python function 1253 | if(!PyCallable_Check(py_src->mouse_move)) { 1254 | blog(LOG_INFO, "None Callable mouse_move: %s",py_src->py_source_info->id); 1255 | goto fail; 1256 | } 1257 | 1258 | SWIG_result = libobs_to_py_swig(SWIG_str_obs_mouse_event, (void*)event, 0, &py_swig_event); 1259 | 1260 | if (!SWIG_IsOK(SWIG_result)) { 1261 | blog(LOG_INFO, 1262 | "%s:l%i \"SWIG Failed to make required python object: '%s' for '%s'\"", 1263 | __func__, 1264 | __LINE__, 1265 | SWIG_str_obs_mouse_event, 1266 | py_src->py_source_info->id); 1267 | goto fail; 1268 | } 1269 | 1270 | argList = Py_BuildValue("(OOO)", py_data, py_swig_event, mouse_leave? Py_True : Py_False); 1271 | PyObject_CallObject(py_src->create,argList); 1272 | if(pyHasError()){ 1273 | goto fail; 1274 | } 1275 | fail: 1276 | Py_XDECREF(py_swig_event); 1277 | Py_XDECREF(argList); 1278 | PyGILState_Release(gstate); 1279 | 1280 | } 1281 | static void py_source_mouse_wheel(void* data, 1282 | const struct obs_mouse_event* event, 1283 | int x_delta, 1284 | int y_delta) 1285 | { 1286 | struct python_data_pair* py_pair = data; 1287 | py_source* py_src = py_pair->source; 1288 | PyObject* py_data = py_pair->data; 1289 | 1290 | //SWIG 1291 | const char* SWIG_str_obs_mouse_event = "obs_mouse_event *"; 1292 | int SWIG_result = SWIG_OK; 1293 | 1294 | //Python 1295 | PyObject* argList = NULL; 1296 | PyObject* py_swig_event = NULL; 1297 | 1298 | PyGILState_STATE gstate; 1299 | gstate = PyGILState_Ensure(); 1300 | 1301 | //Check python function 1302 | if(!PyCallable_Check(py_src->mouse_wheel)) { 1303 | blog(LOG_INFO, "None Callable mouse_wheel: %s",py_src->py_source_info->id); 1304 | goto fail; 1305 | } 1306 | 1307 | SWIG_result = libobs_to_py_swig(SWIG_str_obs_mouse_event, (void*)event, 0, &py_swig_event); 1308 | 1309 | if (!SWIG_IsOK(SWIG_result)) { 1310 | blog(LOG_INFO, 1311 | "%s:l%i \"SWIG Failed to make required python object: '%s' for '%s'\"", 1312 | __func__, 1313 | __LINE__, 1314 | SWIG_str_obs_mouse_event, 1315 | py_src->py_source_info->id); 1316 | goto fail; 1317 | } 1318 | 1319 | argList = Py_BuildValue("(OOii)", py_data, py_swig_event, x_delta, y_delta); 1320 | PyObject_CallObject(py_src->mouse_wheel,argList); 1321 | if(pyHasError()){ 1322 | goto fail; 1323 | } 1324 | 1325 | fail: 1326 | Py_XDECREF(argList); 1327 | Py_XDECREF(py_swig_event); 1328 | PyGILState_Release(gstate); 1329 | } 1330 | static void py_source_focus(void* data,bool focus) 1331 | { 1332 | struct python_data_pair* py_pair = data; 1333 | py_source* py_src = py_pair->source; 1334 | PyObject* py_data = py_pair->data; 1335 | 1336 | //Python 1337 | PyObject* argList = NULL; 1338 | 1339 | PyGILState_STATE gstate; 1340 | gstate = PyGILState_Ensure(); 1341 | 1342 | //Check python function 1343 | if(!PyCallable_Check(py_src->focus)) { 1344 | blog(LOG_INFO, "None Callable focus: %s",py_src->py_source_info->id); 1345 | goto fail; 1346 | } 1347 | 1348 | 1349 | argList = Py_BuildValue("(OO)",py_data, focus ? Py_True : Py_False); 1350 | PyObject_CallObject(py_src->focus,argList); 1351 | if(pyHasError()){ 1352 | goto fail; 1353 | } 1354 | 1355 | fail: 1356 | Py_XDECREF(argList); 1357 | PyGILState_Release(gstate); 1358 | 1359 | } 1360 | static void py_source_key_click(void* data, 1361 | const struct obs_key_event* event, 1362 | bool key_up) 1363 | { 1364 | struct python_data_pair* py_pair = data; 1365 | py_source* py_src = py_pair->source; 1366 | PyObject* py_data = py_pair->data; 1367 | 1368 | //SWIG 1369 | const char* SWIG_str_obs_key_event = "obs_key_event *"; 1370 | int SWIG_result = SWIG_OK; 1371 | 1372 | //Python 1373 | PyObject* argList = NULL; 1374 | PyObject* py_swig_event = NULL; 1375 | 1376 | 1377 | PyGILState_STATE gstate; 1378 | gstate = PyGILState_Ensure(); 1379 | 1380 | //Check python function 1381 | if(!PyCallable_Check(py_src->key_click)) { 1382 | blog(LOG_INFO, "None Callable key_click: %s",py_src->py_source_info->id); 1383 | goto fail; 1384 | } 1385 | 1386 | 1387 | SWIG_result = libobs_to_py_swig(SWIG_str_obs_key_event, (void*)event, 0, &py_swig_event); 1388 | 1389 | if (!SWIG_IsOK(SWIG_result)) { 1390 | blog(LOG_INFO, 1391 | "%s:l%i \"SWIG Failed to make required python object: '%s' for '%s'\"", 1392 | __func__, 1393 | __LINE__, 1394 | SWIG_str_obs_key_event, 1395 | py_src->py_source_info->id); 1396 | goto fail; 1397 | } 1398 | 1399 | argList = Py_BuildValue("(OOO)", py_data, py_swig_event, key_up ? Py_True : Py_False); 1400 | PyObject_CallObject(py_src->key_click,argList); 1401 | if(pyHasError()){ 1402 | goto fail; 1403 | } 1404 | 1405 | fail: 1406 | Py_XDECREF(argList); 1407 | Py_XDECREF(py_swig_event); 1408 | PyGILState_Release(gstate); 1409 | } 1410 | 1411 | 1412 | 1413 | static void py_source_free_type_data(void* data){ 1414 | 1415 | 1416 | PyGILState_STATE gstate; 1417 | gstate = PyGILState_Ensure(); 1418 | //This should mark it ready for python destruction 1419 | Py_XDECREF(data); 1420 | 1421 | PyGILState_Release(gstate); 1422 | } 1423 | 1424 | 1425 | 1426 | //Only called in a python function and by us. 1427 | static void py_to_obs_source_info(py_source* py_info) 1428 | { 1429 | 1430 | /*Should probably check that no other source already has this id*/ 1431 | struct obs_source_info* info = py_info->py_source_info; 1432 | info->id = PyUnicode_AsUTF8(py_info->id); 1433 | info->type = PyLong_AsLong(py_info->type); 1434 | info->output_flags = PyLong_AsLong(py_info->flags); 1435 | 1436 | 1437 | //This function sucks 1438 | info->get_defaults = py_source_get_defaults; 1439 | 1440 | /*The rest of the function pointers point to generic function that use void *data .*/ 1441 | info->create = py_source_create; 1442 | info->destroy = py_source_destroy; 1443 | info->get_name = py_source_get_name; 1444 | info->get_width = py_source_get_width; 1445 | info->get_height = py_source_get_height; 1446 | info->get_properties = py_source_properties; 1447 | info->update = py_source_update; 1448 | info->activate = py_source_activate; 1449 | info->deactivate = py_source_deactivate; 1450 | info->show = py_source_show; 1451 | info->hide = py_source_hide; 1452 | info->video_tick = py_source_video_tick; 1453 | info->video_render = py_source_video_render; 1454 | info->filter_video = py_source_filter_video; 1455 | info->filter_audio = py_source_filter_audio; 1456 | // info->enum_sources = py_source_enum_sources; 1457 | info->save = py_source_save; 1458 | info->load = py_source_load; 1459 | info->mouse_click = py_source_mouse_click; 1460 | info->mouse_move = py_source_mouse_move; 1461 | info->mouse_wheel = py_source_mouse_wheel; 1462 | info->focus = py_source_focus; 1463 | info->key_click = py_source_key_click; 1464 | 1465 | //register the type_data and its corrisponding free 1466 | info->type_data = py_info; 1467 | info->free_type_data = py_source_free_type_data; 1468 | }; 1469 | 1470 | 1471 | --------------------------------------------------------------------------------