Imported Upstream version 0.13.2+dsfg1
This commit is contained in:
commit
fb3990e9e5
2036 changed files with 287360 additions and 0 deletions
380
libobs/CMakeLists.txt
Normal file
380
libobs/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,380 @@
|
|||
project(libobs)
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
find_package(FFmpeg REQUIRED
|
||||
COMPONENTS avformat avutil swscale swresample
|
||||
OPTIONAL_COMPONENTS avcodec)
|
||||
include_directories(${FFMPEG_INCLUDE_DIRS})
|
||||
|
||||
if (NOT "${FFMPEG_AVCODEC_LIBRARIES}" STREQUAL "")
|
||||
list(REMOVE_ITEM FFMPEG_LIBRARIES ${FFMPEG_AVCODEC_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(UNIX)
|
||||
find_package(DBus QUIET)
|
||||
else()
|
||||
set(HAVE_DBUS "0")
|
||||
endif()
|
||||
|
||||
find_package(ImageMagick QUIET COMPONENTS MagickCore)
|
||||
|
||||
if(NOT ImageMagick_MagickCore_FOUND AND NOT FFMPEG_AVCODEC_FOUND)
|
||||
message(FATAL_ERROR "Either MagickCore or Libavcodec is required, but both were not found")
|
||||
endif()
|
||||
|
||||
option(LIBOBS_PREFER_IMAGEMAGICK "Prefer ImageMagick over ffmpeg for image loading" OFF)
|
||||
|
||||
if(NOT FFMPEG_AVCODEC_FOUND OR (ImageMagick_MagickCore_FOUND AND LIBOBS_PREFER_IMAGEMAGICK))
|
||||
message(STATUS "Using ImageMagick for image loading in libobs")
|
||||
|
||||
set(libobs_image_loading_SOURCES
|
||||
graphics/graphics-magick.c)
|
||||
set(libobs_image_loading_LIBRARIES
|
||||
${ImageMagick_LIBRARIES})
|
||||
|
||||
include_directories(${ImageMagick_INCLUDE_DIRS})
|
||||
else()
|
||||
message(STATUS "Using libavcodec for image loading in libobs")
|
||||
|
||||
set(libobs_image_loading_SOURCES
|
||||
graphics/graphics-ffmpeg.c)
|
||||
set(libobs_image_loading_LIBRARIES
|
||||
${FFMPEG_AVCODEC_LIBRARIES})
|
||||
endif()
|
||||
|
||||
find_package(ZLIB REQUIRED)
|
||||
|
||||
include_directories(SYSTEM ${ZLIB_INCLUDE_DIR})
|
||||
|
||||
add_definitions(-DLIBOBS_EXPORTS)
|
||||
|
||||
include_directories(${OBS_JANSSON_INCLUDE_DIRS})
|
||||
|
||||
if(WIN32)
|
||||
set(libobs_PLATFORM_SOURCES
|
||||
obs-win-crash-handler.c
|
||||
obs-windows.c
|
||||
util/threading-windows.c
|
||||
util/pipe-windows.c
|
||||
util/platform-windows.c)
|
||||
set(libobs_PLATFORM_HEADERS
|
||||
util/threading-windows.h
|
||||
util/windows/win-version.h
|
||||
util/windows/ComPtr.hpp
|
||||
util/windows/CoTaskMemPtr.hpp
|
||||
util/windows/HRError.hpp
|
||||
util/windows/WinHandle.hpp)
|
||||
set(libobs_PLATFORM_DEPS winmm)
|
||||
if(MSVC)
|
||||
set(libobs_PLATFORM_DEPS
|
||||
${libobs_PLATFORM_DEPS}
|
||||
w32-pthreads)
|
||||
endif()
|
||||
elseif(APPLE)
|
||||
set(libobs_PLATFORM_SOURCES
|
||||
obs-cocoa.c
|
||||
util/threading-posix.c
|
||||
util/pipe-posix.c
|
||||
util/platform-nix.c
|
||||
util/platform-cocoa.m)
|
||||
set(libobs_PLATFORM_HEADERS
|
||||
util/threading-posix.h)
|
||||
|
||||
set_source_files_properties(${libobs_PLATFORM_SOURCES}
|
||||
PROPERTIES
|
||||
LANGUAGE C
|
||||
COMPILE_FLAGS "-fobjc-arc")
|
||||
|
||||
find_library(COCOA Cocoa)
|
||||
mark_as_advanced(COCOA)
|
||||
include_directories(${COCOA})
|
||||
|
||||
find_library(APPKIT AppKit)
|
||||
mark_as_advanced(APPKIT)
|
||||
include_directories(${APPKIT})
|
||||
|
||||
find_library(IOKIT IOKit)
|
||||
mark_as_advanced(IOKIT)
|
||||
include_directories(${IOKIT})
|
||||
|
||||
find_library(CARBON Carbon)
|
||||
mark_as_advanced(CARBON)
|
||||
include_directories(${CARBON})
|
||||
|
||||
set(libobs_PLATFORM_DEPS
|
||||
${COCOA}
|
||||
${APPKIT}
|
||||
${IOKIT}
|
||||
${CARBON})
|
||||
elseif(UNIX)
|
||||
set(libobs_PLATFORM_SOURCES
|
||||
obs-nix.c
|
||||
util/threading-posix.c
|
||||
util/pipe-posix.c
|
||||
util/platform-nix.c)
|
||||
set(libobs_PLATFORM_HEADERS
|
||||
util/threading-posix.h)
|
||||
|
||||
if(DBUS_FOUND)
|
||||
set(libobs_PLATFORM_SOURCES ${libobs_PLATFORM_SOURCES}
|
||||
util/platform-nix-dbus.c)
|
||||
include_directories(${DBUS_INCLUDE_DIRS})
|
||||
set(libobs_PLATFORM_DEPS
|
||||
${libobs_PLATFORM_DEPS}
|
||||
${DBUS_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
||||
# use the sysinfo compatibility library on bsd
|
||||
find_package(Libsysinfo REQUIRED)
|
||||
include_directories(${SYSINFO_INCLUDE_DIRS})
|
||||
set(libobs_PLATFORM_DEPS
|
||||
${libobs_PLATFORM_DEPS}
|
||||
${SYSINFO_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /SAFESEH:NO")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /EHc-")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHc-")
|
||||
endif()
|
||||
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/obsconfig.h.in"
|
||||
"${CMAKE_BINARY_DIR}/config/obsconfig.h")
|
||||
|
||||
set(libobs_config_HEADERS
|
||||
"${CMAKE_BINARY_DIR}/config/obsconfig.h"
|
||||
obs-config.h)
|
||||
|
||||
set(libobs_callback_SOURCES
|
||||
callback/calldata.c
|
||||
callback/decl.c
|
||||
callback/signal.c
|
||||
callback/proc.c)
|
||||
set(libobs_callback_HEADERS
|
||||
callback/calldata.h
|
||||
callback/decl.h
|
||||
callback/proc.h
|
||||
callback/signal.h)
|
||||
|
||||
set(libobs_graphics_SOURCES
|
||||
${libobs_image_loading_SOURCES}
|
||||
graphics/quat.c
|
||||
graphics/effect-parser.c
|
||||
graphics/axisang.c
|
||||
graphics/vec4.c
|
||||
graphics/vec2.c
|
||||
graphics/libnsgif/libnsgif.c
|
||||
graphics/texture-render.c
|
||||
graphics/image-file.c
|
||||
graphics/bounds.c
|
||||
graphics/matrix3.c
|
||||
graphics/matrix4.c
|
||||
graphics/vec3.c
|
||||
graphics/graphics.c
|
||||
graphics/shader-parser.c
|
||||
graphics/plane.c
|
||||
graphics/effect.c
|
||||
graphics/math-extra.c
|
||||
graphics/graphics-imports.c)
|
||||
set(libobs_graphics_HEADERS
|
||||
graphics/plane.h
|
||||
graphics/quat.h
|
||||
graphics/input.h
|
||||
graphics/axisang.h
|
||||
graphics/shader-parser.h
|
||||
graphics/effect.h
|
||||
graphics/math-defs.h
|
||||
graphics/matrix4.h
|
||||
graphics/graphics.h
|
||||
graphics/graphics-internal.h
|
||||
graphics/libnsgif/libnsgif.h
|
||||
graphics/device-exports.h
|
||||
graphics/image-file.h
|
||||
graphics/vec2.h
|
||||
graphics/vec4.h
|
||||
graphics/matrix3.h
|
||||
graphics/vec3.h
|
||||
graphics/math-extra.h
|
||||
graphics/bounds.h
|
||||
graphics/effect-parser.h)
|
||||
|
||||
set(libobs_mediaio_SOURCES
|
||||
media-io/video-io.c
|
||||
media-io/video-fourcc.c
|
||||
media-io/video-matrices.c
|
||||
media-io/audio-io.c
|
||||
media-io/video-frame.c
|
||||
media-io/format-conversion.c
|
||||
media-io/audio-resampler-ffmpeg.c
|
||||
media-io/video-scaler-ffmpeg.c
|
||||
media-io/media-remux.c)
|
||||
set(libobs_mediaio_HEADERS
|
||||
media-io/media-io-defs.h
|
||||
media-io/video-io.h
|
||||
media-io/audio-io.h
|
||||
media-io/audio-math.h
|
||||
media-io/video-frame.h
|
||||
media-io/format-conversion.h
|
||||
media-io/audio-resampler.h
|
||||
media-io/video-scaler.h
|
||||
media-io/media-remux.h
|
||||
media-io/frame-rate.h)
|
||||
|
||||
set(libobs_util_SOURCES
|
||||
util/array-serializer.c
|
||||
util/file-serializer.c
|
||||
util/base.c
|
||||
util/platform.c
|
||||
util/cf-lexer.c
|
||||
util/bmem.c
|
||||
util/config-file.c
|
||||
util/lexer.c
|
||||
util/dstr.c
|
||||
util/utf8.c
|
||||
util/crc32.c
|
||||
util/text-lookup.c
|
||||
util/cf-parser.c
|
||||
util/profiler.c)
|
||||
set(libobs_util_HEADERS
|
||||
util/array-serializer.h
|
||||
util/file-serializer.h
|
||||
util/utf8.h
|
||||
util/crc32.h
|
||||
util/base.h
|
||||
util/text-lookup.h
|
||||
util/vc/vc_inttypes.h
|
||||
util/vc/vc_stdbool.h
|
||||
util/vc/vc_stdint.h
|
||||
util/bmem.h
|
||||
util/c99defs.h
|
||||
util/util_uint128.h
|
||||
util/cf-parser.h
|
||||
util/threading.h
|
||||
util/pipe.h
|
||||
util/cf-lexer.h
|
||||
util/darray.h
|
||||
util/circlebuf.h
|
||||
util/dstr.h
|
||||
util/serializer.h
|
||||
util/config-file.h
|
||||
util/lexer.h
|
||||
util/platform.h
|
||||
util/profiler.h
|
||||
util/profiler.hpp)
|
||||
|
||||
set(libobs_libobs_SOURCES
|
||||
${libobs_PLATFORM_SOURCES}
|
||||
obs-audio-controls.c
|
||||
obs-avc.c
|
||||
obs-encoder.c
|
||||
obs-service.c
|
||||
obs-source.c
|
||||
obs-source-transition.c
|
||||
obs-output.c
|
||||
obs-output-delay.c
|
||||
obs.c
|
||||
obs-properties.c
|
||||
obs-data.c
|
||||
obs-hotkey.c
|
||||
obs-hotkey-name-map.c
|
||||
obs-module.c
|
||||
obs-display.c
|
||||
obs-view.c
|
||||
obs-scene.c
|
||||
obs-audio.c
|
||||
obs-video.c)
|
||||
set(libobs_libobs_HEADERS
|
||||
${libobs_PLATFORM_HEADERS}
|
||||
obs-audio-controls.h
|
||||
obs-defs.h
|
||||
obs-avc.h
|
||||
obs-encoder.h
|
||||
obs-service.h
|
||||
obs-internal.h
|
||||
obs.h
|
||||
obs-ui.h
|
||||
obs-properties.h
|
||||
obs-data.h
|
||||
obs-interaction.h
|
||||
obs-hotkey.h
|
||||
obs-hotkeys.h
|
||||
obs-module.h
|
||||
obs-scene.h
|
||||
obs-source.h
|
||||
obs-output.h
|
||||
obs-ffmpeg-compat.h
|
||||
obs.hpp)
|
||||
|
||||
set(libobs_SOURCES
|
||||
${libobs_callback_SOURCES}
|
||||
${libobs_graphics_SOURCES}
|
||||
${libobs_mediaio_SOURCES}
|
||||
${libobs_util_SOURCES}
|
||||
${libobs_libobs_SOURCES})
|
||||
|
||||
set(libobs_HEADERS
|
||||
${libobs_config_HEADERS}
|
||||
${libobs_callback_HEADERS}
|
||||
${libobs_graphics_HEADERS}
|
||||
${libobs_mediaio_HEADERS}
|
||||
${libobs_util_HEADERS}
|
||||
${libobs_libobs_HEADERS})
|
||||
|
||||
source_group("callback\\Source Files" FILES ${libobs_callback_SOURCES})
|
||||
source_group("callback\\Header Files" FILES ${libobs_callback_HEADERS})
|
||||
source_group("graphics\\Source Files" FILES ${libobs_graphics_SOURCES})
|
||||
source_group("graphics\\Header Files" FILES ${libobs_graphics_HEADERS})
|
||||
source_group("libobs\\Source Files" FILES ${libobs_libobs_SOURCES})
|
||||
source_group("libobs\\Header Files" FILES ${libobs_libobs_HEADERS})
|
||||
source_group("media-io\\Source Files" FILES ${libobs_mediaio_SOURCES})
|
||||
source_group("media-io\\Header Files" FILES ${libobs_mediaio_HEADERS})
|
||||
source_group("util\\Source Files" FILES ${libobs_util_SOURCES})
|
||||
source_group("util\\Header Files" FILES ${libobs_util_HEADERS})
|
||||
|
||||
|
||||
add_library(libobs SHARED ${libobs_SOURCES} ${libobs_HEADERS})
|
||||
|
||||
set_target_properties(libobs PROPERTIES
|
||||
OUTPUT_NAME obs
|
||||
VERSION "0"
|
||||
SOVERSION "0")
|
||||
target_compile_definitions(libobs
|
||||
PUBLIC
|
||||
HAVE_OBSCONFIG_H)
|
||||
|
||||
if(NOT MSVC)
|
||||
target_compile_options(libobs
|
||||
PUBLIC
|
||||
-mmmx
|
||||
-msse
|
||||
-msse2)
|
||||
endif()
|
||||
|
||||
|
||||
target_compile_options(libobs
|
||||
PUBLIC
|
||||
"${THREADS_DEFINITIONS}")
|
||||
target_include_directories(libobs
|
||||
PUBLIC
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
|
||||
"$<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/config>"
|
||||
"$<INSTALL_INTERFACE:${OBS_INCLUDE_DESTINATION}>")
|
||||
target_link_libraries(libobs
|
||||
PRIVATE
|
||||
${libobs_PLATFORM_DEPS}
|
||||
${libobs_image_loading_LIBRARIES}
|
||||
${OBS_JANSSON_IMPORT}
|
||||
${FFMPEG_LIBRARIES}
|
||||
${ZLIB_LIBRARIES}
|
||||
PUBLIC
|
||||
${THREADS_LIBRARIES})
|
||||
|
||||
install_obs_core(libobs EXPORT LibObs)
|
||||
install_obs_data(libobs data libobs)
|
||||
install_obs_headers(${libobs_HEADERS})
|
||||
|
||||
obs_install_additional(libobs)
|
||||
26
libobs/LibObsConfig.cmake.in
Normal file
26
libobs/LibObsConfig.cmake.in
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# - Config file for the LibObs package
|
||||
# It defines the following variables
|
||||
# LIBOBS_INCLUDE_DIRS
|
||||
# LIBOBS_LIBRARIES
|
||||
# LIBOBS_PLUGIN_DESTINATION{,32,64}
|
||||
# LIBOBS_PLUGIN_DATA_DESTINATION
|
||||
|
||||
set(LIBOBS_PLUGIN_DESTINATION "@CONF_PLUGIN_DEST@")
|
||||
set(LIBOBS_PLUGIN_DESTINATION32 "@CONF_PLUGIN_DEST32@")
|
||||
set(LIBOBS_PLUGIN_DESTINATION64 "@CONF_PLUGIN_DEST64@")
|
||||
set(LIBOBS_INCLUDE_DIRS "@CONF_INCLUDE_DIRS@")
|
||||
set(LIBOBS_PLUGIN_DATA_DESTINATION "@CONF_PLUGIN_DATA_DEST@")
|
||||
|
||||
# Cleanup possible relative paths
|
||||
get_filename_component(LIBOBS_PLUGIN_DESTINATION "${LIBOBS_PLUGIN_DESTINATION}" ABSOLUTE)
|
||||
get_filename_component(LIBOBS_PLUGIN_DESTINATION32 "${LIBOBS_PLUGIN_DESTINATION32}" ABSOLUTE)
|
||||
get_filename_component(LIBOBS_PLUGIN_DESTINATION64 "${LIBOBS_PLUGIN_DESTINATION64}" ABSOLUTE)
|
||||
get_filename_component(LIBOBS_INCLUDE_DIRS "${LIBOBS_INCLUDE_DIRS}" ABSOLUTE)
|
||||
get_filename_component(LIBOBS_PLUGIN_DATA_DESTINATION "${LIBOBS_PLUGIN_DATA_DESTINATION}" ABSOLUTE)
|
||||
|
||||
if(NOT TARGET libobs)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/LibObsTarget.cmake")
|
||||
endif()
|
||||
|
||||
set(LIBOBS_LIBRARIES libobs)
|
||||
|
||||
10
libobs/LibObsConfigVersion.cmake.in
Normal file
10
libobs/LibObsConfigVersion.cmake.in
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
set(PACKAGE_VERSION "@OBS_VERSION@")
|
||||
|
||||
if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
|
||||
set(PACKAGE_VERSION_COMPATIBLE FALSE)
|
||||
else()
|
||||
set(PACKAGE_VERSION_COMPATIBLE TRUE)
|
||||
if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
|
||||
set(PACKAGE_VERSION_EXACT TRUE)
|
||||
endif()
|
||||
endif()
|
||||
250
libobs/callback/calldata.c
Normal file
250
libobs/callback/calldata.c
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
* Copyright (c) 2013 Hugh Bailey <obs.jim@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "../util/bmem.h"
|
||||
#include "../util/base.h"
|
||||
|
||||
#include "calldata.h"
|
||||
|
||||
/*
|
||||
* Uses a data stack. Probably more complex than it should be, but reduces
|
||||
* fetching.
|
||||
*
|
||||
* Stack format is:
|
||||
* [size_t param1_name_size]
|
||||
* [char[] param1_name]
|
||||
* [size_t param1_data_size]
|
||||
* [uint8_t[] param1_data]
|
||||
* [size_t param2_name_size]
|
||||
* [char[] param2_name]
|
||||
* [size_t param2_data_size]
|
||||
* [uint8_t[] param2_data]
|
||||
* [...]
|
||||
* [size_t 0]
|
||||
*
|
||||
* Strings and string sizes always include the null terminator to allow for
|
||||
* direct referencing.
|
||||
*/
|
||||
|
||||
static inline void cd_serialize(uint8_t **pos, void *ptr, size_t size)
|
||||
{
|
||||
memcpy(ptr, *pos, size);
|
||||
*pos += size;
|
||||
}
|
||||
|
||||
static inline size_t cd_serialize_size(uint8_t **pos)
|
||||
{
|
||||
size_t size = 0;
|
||||
memcpy(&size, *pos, sizeof(size_t));
|
||||
*pos += sizeof(size_t);
|
||||
return size;
|
||||
}
|
||||
|
||||
static inline const char *cd_serialize_string(uint8_t **pos)
|
||||
{
|
||||
size_t size = cd_serialize_size(pos);
|
||||
const char *str = (const char *)*pos;
|
||||
|
||||
*pos += size;
|
||||
|
||||
return (size != 0) ? str : NULL;
|
||||
}
|
||||
|
||||
static bool cd_getparam(const calldata_t *data, const char *name,
|
||||
uint8_t **pos)
|
||||
{
|
||||
size_t name_size;
|
||||
|
||||
if (!data->size)
|
||||
return false;
|
||||
|
||||
*pos = data->stack;
|
||||
|
||||
name_size = cd_serialize_size(pos);
|
||||
while (name_size != 0) {
|
||||
const char *param_name = (const char *)*pos;
|
||||
size_t param_size;
|
||||
|
||||
*pos += name_size;
|
||||
if (strcmp(param_name, name) == 0)
|
||||
return true;
|
||||
|
||||
param_size = cd_serialize_size(pos);
|
||||
*pos += param_size;
|
||||
|
||||
name_size = cd_serialize_size(pos);
|
||||
}
|
||||
|
||||
*pos -= sizeof(size_t);
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void cd_copy_string(uint8_t **pos, const char *str, size_t len)
|
||||
{
|
||||
if (!len)
|
||||
len = strlen(str)+1;
|
||||
|
||||
memcpy(*pos, &len, sizeof(size_t));
|
||||
*pos += sizeof(size_t);
|
||||
memcpy(*pos, str, len);
|
||||
*pos += len;
|
||||
}
|
||||
|
||||
static inline void cd_copy_data(uint8_t **pos, const void *in, size_t size)
|
||||
{
|
||||
memcpy(*pos, &size, sizeof(size_t));
|
||||
*pos += sizeof(size_t);
|
||||
|
||||
if (size) {
|
||||
memcpy(*pos, in, size);
|
||||
*pos += size;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void cd_set_first_param(calldata_t *data, const char *name,
|
||||
const void *in, size_t size)
|
||||
{
|
||||
uint8_t *pos;
|
||||
size_t capacity;
|
||||
size_t name_len = strlen(name)+1;
|
||||
|
||||
capacity = sizeof(size_t)*3 + name_len + size;
|
||||
data->size = capacity;
|
||||
|
||||
if (capacity < 128)
|
||||
capacity = 128;
|
||||
|
||||
data->capacity = capacity;
|
||||
data->stack = bmalloc(capacity);
|
||||
|
||||
pos = data->stack;
|
||||
cd_copy_string(&pos, name, name_len);
|
||||
cd_copy_data(&pos, in, size);
|
||||
memset(pos, 0, sizeof(size_t));
|
||||
}
|
||||
|
||||
static inline bool cd_ensure_capacity(calldata_t *data, uint8_t **pos,
|
||||
size_t new_size)
|
||||
{
|
||||
size_t offset;
|
||||
size_t new_capacity;
|
||||
|
||||
if (new_size < data->capacity)
|
||||
return true;
|
||||
if (data->fixed) {
|
||||
blog(LOG_ERROR, "Tried to go above fixed calldata stack size!");
|
||||
return false;
|
||||
}
|
||||
|
||||
offset = *pos - data->stack;
|
||||
|
||||
new_capacity = data->capacity * 2;
|
||||
if (new_capacity < new_size)
|
||||
new_capacity = new_size;
|
||||
|
||||
data->stack = brealloc(data->stack, new_capacity);
|
||||
data->capacity = new_capacity;
|
||||
|
||||
*pos = data->stack + offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
bool calldata_get_data(const calldata_t *data, const char *name, void *out,
|
||||
size_t size)
|
||||
{
|
||||
uint8_t *pos;
|
||||
size_t data_size;
|
||||
|
||||
if (!data || !name || !*name)
|
||||
return false;
|
||||
|
||||
if (!cd_getparam(data, name, &pos))
|
||||
return false;
|
||||
|
||||
data_size = cd_serialize_size(&pos);
|
||||
if (data_size != size)
|
||||
return false;
|
||||
|
||||
memcpy(out, pos, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
void calldata_set_data(calldata_t *data, const char *name, const void *in,
|
||||
size_t size)
|
||||
{
|
||||
uint8_t *pos = NULL;
|
||||
|
||||
if (!data || !name || !*name)
|
||||
return;
|
||||
|
||||
if (!data->fixed && !data->stack) {
|
||||
cd_set_first_param(data, name, in, size);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cd_getparam(data, name, &pos)) {
|
||||
size_t cur_size;
|
||||
memcpy(&cur_size, pos, sizeof(size_t));
|
||||
|
||||
if (cur_size < size) {
|
||||
size_t offset = size - cur_size;
|
||||
size_t bytes = data->size;
|
||||
|
||||
if (!cd_ensure_capacity(data, &pos, bytes + offset))
|
||||
return;
|
||||
memmove(pos+offset, pos, bytes - (pos - data->stack));
|
||||
data->size += offset;
|
||||
|
||||
} else if (cur_size > size) {
|
||||
size_t offset = cur_size - size;
|
||||
size_t bytes = data->size - offset;
|
||||
|
||||
memmove(pos, pos+offset, bytes - (pos - data->stack));
|
||||
data->size -= offset;
|
||||
}
|
||||
|
||||
cd_copy_data(&pos, in, size);
|
||||
|
||||
} else {
|
||||
size_t name_len = strlen(name)+1;
|
||||
size_t offset = name_len + size + sizeof(size_t)*2;
|
||||
if (!cd_ensure_capacity(data, &pos, data->size + offset))
|
||||
return;
|
||||
data->size += offset;
|
||||
|
||||
cd_copy_string(&pos, name, 0);
|
||||
cd_copy_data(&pos, in, size);
|
||||
memset(pos, 0, sizeof(size_t));
|
||||
}
|
||||
}
|
||||
|
||||
bool calldata_get_string(const calldata_t *data, const char *name,
|
||||
const char **str)
|
||||
{
|
||||
uint8_t *pos;
|
||||
if (!data || !name || !*name)
|
||||
return false;
|
||||
|
||||
if (!cd_getparam(data, name, &pos))
|
||||
return false;
|
||||
|
||||
*str = cd_serialize_string(&pos);
|
||||
return true;
|
||||
}
|
||||
198
libobs/callback/calldata.h
Normal file
198
libobs/callback/calldata.h
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* Copyright (c) 2013 Hugh Bailey <obs.jim@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
#include "../util/c99defs.h"
|
||||
#include "../util/bmem.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Procedure call data structure
|
||||
*
|
||||
* This is used to store parameters (and return value) sent to/from signals,
|
||||
* procedures, and callbacks.
|
||||
*/
|
||||
|
||||
enum call_param_type {
|
||||
CALL_PARAM_TYPE_VOID,
|
||||
CALL_PARAM_TYPE_INT,
|
||||
CALL_PARAM_TYPE_FLOAT,
|
||||
CALL_PARAM_TYPE_BOOL,
|
||||
CALL_PARAM_TYPE_PTR,
|
||||
CALL_PARAM_TYPE_STRING
|
||||
};
|
||||
|
||||
#define CALL_PARAM_IN (1<<0)
|
||||
#define CALL_PARAM_OUT (1<<1)
|
||||
|
||||
struct calldata {
|
||||
uint8_t *stack;
|
||||
size_t size; /* size of the stack, in bytes */
|
||||
size_t capacity; /* capacity of the stack, in bytes */
|
||||
bool fixed; /* fixed size (using call stack) */
|
||||
};
|
||||
|
||||
typedef struct calldata calldata_t;
|
||||
|
||||
static inline void calldata_init(struct calldata *data)
|
||||
{
|
||||
memset(data, 0, sizeof(struct calldata));
|
||||
}
|
||||
|
||||
static inline void calldata_clear(struct calldata *data);
|
||||
|
||||
static inline void calldata_init_fixed(struct calldata *data, uint8_t *stack,
|
||||
size_t size)
|
||||
{
|
||||
data->stack = stack;
|
||||
data->capacity = size;
|
||||
data->fixed = true;
|
||||
data->size = 0;
|
||||
calldata_clear(data);
|
||||
}
|
||||
|
||||
static inline void calldata_free(struct calldata *data)
|
||||
{
|
||||
if (!data->fixed)
|
||||
bfree(data->stack);
|
||||
}
|
||||
|
||||
EXPORT bool calldata_get_data(const calldata_t *data, const char *name,
|
||||
void *out, size_t size);
|
||||
EXPORT void calldata_set_data(calldata_t *data, const char *name,
|
||||
const void *in, size_t new_size);
|
||||
|
||||
static inline void calldata_clear(struct calldata *data)
|
||||
{
|
||||
if (data->stack) {
|
||||
data->size = sizeof(size_t);
|
||||
memset(data->stack, 0, sizeof(size_t));
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* NOTE: 'get' functions return true only if paramter exists, and is the
|
||||
* same type. They return false otherwise. */
|
||||
|
||||
static inline bool calldata_get_int(const calldata_t *data, const char *name,
|
||||
long long *val)
|
||||
{
|
||||
return calldata_get_data(data, name, val, sizeof(*val));
|
||||
}
|
||||
|
||||
static inline bool calldata_get_float (const calldata_t *data, const char *name,
|
||||
double *val)
|
||||
{
|
||||
return calldata_get_data(data, name, val, sizeof(*val));
|
||||
}
|
||||
|
||||
static inline bool calldata_get_bool (const calldata_t *data, const char *name,
|
||||
bool *val)
|
||||
{
|
||||
return calldata_get_data(data, name, val, sizeof(*val));
|
||||
}
|
||||
|
||||
static inline bool calldata_get_ptr (const calldata_t *data, const char *name,
|
||||
void *p_ptr)
|
||||
{
|
||||
return calldata_get_data(data, name, p_ptr, sizeof(p_ptr));
|
||||
}
|
||||
|
||||
EXPORT bool calldata_get_string(const calldata_t *data, const char *name,
|
||||
const char **str);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* call if you know your data is valid */
|
||||
|
||||
static inline long long calldata_int(const calldata_t *data, const char *name)
|
||||
{
|
||||
long long val = 0;
|
||||
calldata_get_int(data, name, &val);
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline double calldata_float(const calldata_t *data, const char *name)
|
||||
{
|
||||
double val = 0.0;
|
||||
calldata_get_float(data, name, &val);
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline bool calldata_bool(const calldata_t *data, const char *name)
|
||||
{
|
||||
bool val = false;
|
||||
calldata_get_bool(data, name, &val);
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void *calldata_ptr(const calldata_t *data, const char *name)
|
||||
{
|
||||
void *val = NULL;
|
||||
calldata_get_ptr(data, name, &val);
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline const char *calldata_string(const calldata_t *data,
|
||||
const char *name)
|
||||
{
|
||||
const char *val = NULL;
|
||||
calldata_get_string(data, name, &val);
|
||||
return val;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static inline void calldata_set_int (calldata_t *data, const char *name,
|
||||
long long val)
|
||||
{
|
||||
calldata_set_data(data, name, &val, sizeof(val));
|
||||
}
|
||||
|
||||
static inline void calldata_set_float (calldata_t *data, const char *name,
|
||||
double val)
|
||||
{
|
||||
calldata_set_data(data, name, &val, sizeof(val));
|
||||
}
|
||||
|
||||
static inline void calldata_set_bool (calldata_t *data, const char *name,
|
||||
bool val)
|
||||
{
|
||||
calldata_set_data(data, name, &val, sizeof(val));
|
||||
}
|
||||
|
||||
static inline void calldata_set_ptr (calldata_t *data, const char *name,
|
||||
void *ptr)
|
||||
{
|
||||
calldata_set_data(data, name, &ptr, sizeof(ptr));
|
||||
}
|
||||
|
||||
static inline void calldata_set_string(calldata_t *data, const char *name,
|
||||
const char *str)
|
||||
{
|
||||
if (str)
|
||||
calldata_set_data(data, name, str, strlen(str)+1);
|
||||
else
|
||||
calldata_set_data(data, name, NULL, 0);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
239
libobs/callback/decl.c
Normal file
239
libobs/callback/decl.c
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Hugh Bailey <obs.jim@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "../util/cf-parser.h"
|
||||
#include "decl.h"
|
||||
|
||||
static inline void err_specifier_exists(struct cf_parser *cfp,
|
||||
const char *storage)
|
||||
{
|
||||
cf_adderror(cfp, "'$1' specifier already exists", LEX_ERROR,
|
||||
storage, NULL, NULL);
|
||||
}
|
||||
|
||||
static inline void err_reserved_name(struct cf_parser *cfp, const char *name)
|
||||
{
|
||||
cf_adderror(cfp, "'$1' is a reserved name", LEX_ERROR,
|
||||
name, NULL, NULL);
|
||||
}
|
||||
|
||||
static inline void err_existing_name(struct cf_parser *cfp, const char *name)
|
||||
{
|
||||
cf_adderror(cfp, "'$1' already exists", LEX_ERROR, name, NULL, NULL);
|
||||
}
|
||||
|
||||
static bool is_in_out_specifier(struct cf_parser *cfp, struct strref *name,
|
||||
uint32_t *type)
|
||||
{
|
||||
if (strref_cmp(name, "in") == 0) {
|
||||
if (*type & CALL_PARAM_IN)
|
||||
err_specifier_exists(cfp, "in");
|
||||
|
||||
*type |= CALL_PARAM_IN;
|
||||
|
||||
} else if (strref_cmp(name, "out") == 0) {
|
||||
if (*type & CALL_PARAM_OUT)
|
||||
err_specifier_exists(cfp, "out");
|
||||
|
||||
*type |= CALL_PARAM_OUT;
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define TYPE_OR_STORAGE "type or storage specifier"
|
||||
|
||||
static bool get_type(struct strref *ref, enum call_param_type *type,
|
||||
bool is_return)
|
||||
{
|
||||
if (strref_cmp(ref, "int") == 0)
|
||||
*type = CALL_PARAM_TYPE_INT;
|
||||
else if (strref_cmp(ref, "float") == 0)
|
||||
*type = CALL_PARAM_TYPE_FLOAT;
|
||||
else if (strref_cmp(ref, "bool") == 0)
|
||||
*type = CALL_PARAM_TYPE_BOOL;
|
||||
else if (strref_cmp(ref, "ptr") == 0)
|
||||
*type = CALL_PARAM_TYPE_PTR;
|
||||
else if (strref_cmp(ref, "string") == 0)
|
||||
*type = CALL_PARAM_TYPE_STRING;
|
||||
else if (is_return && strref_cmp(ref, "void") == 0)
|
||||
*type = CALL_PARAM_TYPE_VOID;
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_reserved_name(const char *str)
|
||||
{
|
||||
return (strcmp(str, "int") == 0) ||
|
||||
(strcmp(str, "float") == 0) ||
|
||||
(strcmp(str, "bool") == 0) ||
|
||||
(strcmp(str, "ptr") == 0) ||
|
||||
(strcmp(str, "string") == 0) ||
|
||||
(strcmp(str, "void") == 0) ||
|
||||
(strcmp(str, "return") == 0);
|
||||
}
|
||||
|
||||
static bool name_exists(struct decl_info *decl, const char *name)
|
||||
{
|
||||
for (size_t i = 0; i < decl->params.num; i++) {
|
||||
const char *param_name = decl->params.array[i].name;
|
||||
|
||||
if (strcmp(name, param_name) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int parse_param(struct cf_parser *cfp, struct decl_info *decl)
|
||||
{
|
||||
struct strref ref;
|
||||
int code;
|
||||
struct decl_param param = {0};
|
||||
|
||||
/* get stprage specifiers */
|
||||
code = cf_next_name_ref(cfp, &ref, TYPE_OR_STORAGE, ",");
|
||||
if (code != PARSE_SUCCESS)
|
||||
return code;
|
||||
|
||||
while (is_in_out_specifier(cfp, &ref, ¶m.flags)) {
|
||||
code = cf_next_name_ref(cfp, &ref, TYPE_OR_STORAGE, ",");
|
||||
if (code != PARSE_SUCCESS)
|
||||
return code;
|
||||
}
|
||||
|
||||
/* parameters not marked with specifers are input parameters */
|
||||
if (param.flags == 0)
|
||||
param.flags = CALL_PARAM_IN;
|
||||
|
||||
if (!get_type(&ref, ¶m.type, false)) {
|
||||
cf_adderror_expecting(cfp, "type");
|
||||
cf_go_to_token(cfp, ",", ")");
|
||||
return PARSE_CONTINUE;
|
||||
}
|
||||
|
||||
/* name */
|
||||
code = cf_next_name(cfp, ¶m.name, "parameter name", ",");
|
||||
if (code != PARSE_SUCCESS)
|
||||
return code;
|
||||
|
||||
if (name_exists(decl, param.name))
|
||||
err_existing_name(cfp, param.name);
|
||||
|
||||
if (is_reserved_name(param.name))
|
||||
err_reserved_name(cfp, param.name);
|
||||
|
||||
da_push_back(decl->params, ¶m);
|
||||
return PARSE_SUCCESS;
|
||||
}
|
||||
|
||||
static void parse_params(struct cf_parser *cfp, struct decl_info *decl)
|
||||
{
|
||||
struct cf_token peek;
|
||||
int code;
|
||||
|
||||
if (!cf_peek_valid_token(cfp, &peek))
|
||||
return;
|
||||
|
||||
while (peek.type == CFTOKEN_NAME) {
|
||||
code = parse_param(cfp, decl);
|
||||
if (code == PARSE_EOF)
|
||||
return;
|
||||
|
||||
if (code != PARSE_CONTINUE && !cf_next_valid_token(cfp))
|
||||
return;
|
||||
|
||||
if (cf_token_is(cfp, ")"))
|
||||
break;
|
||||
else if (cf_token_should_be(cfp, ",", ",", NULL) == PARSE_EOF)
|
||||
return;
|
||||
|
||||
if (!cf_peek_valid_token(cfp, &peek))
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cf_token_is(cfp, ")"))
|
||||
cf_next_token_should_be(cfp, ")", NULL, NULL);
|
||||
}
|
||||
|
||||
static void print_errors(struct cf_parser *cfp, const char *decl_string)
|
||||
{
|
||||
char *errors = error_data_buildstring(&cfp->error_list);
|
||||
|
||||
if (errors) {
|
||||
blog(LOG_WARNING, "Errors/warnings for '%s':\n\n%s",
|
||||
decl_string, errors);
|
||||
|
||||
bfree(errors);
|
||||
}
|
||||
}
|
||||
|
||||
bool parse_decl_string(struct decl_info *decl, const char *decl_string)
|
||||
{
|
||||
struct cf_parser cfp;
|
||||
struct strref ret_type;
|
||||
struct decl_param ret_param = {0};
|
||||
int code;
|
||||
bool success;
|
||||
|
||||
decl->decl_string = decl_string;
|
||||
ret_param.flags = CALL_PARAM_OUT;
|
||||
|
||||
cf_parser_init(&cfp);
|
||||
if (!cf_parser_parse(&cfp, decl_string, "declaraion"))
|
||||
goto fail;
|
||||
|
||||
code = cf_get_name_ref(&cfp, &ret_type, "return type", NULL);
|
||||
if (code == PARSE_EOF)
|
||||
goto fail;
|
||||
|
||||
if (!get_type(&ret_type, &ret_param.type, true))
|
||||
cf_adderror_expecting(&cfp, "return type");
|
||||
|
||||
code = cf_next_name(&cfp, &decl->name, "function name", "(");
|
||||
if (code == PARSE_EOF)
|
||||
goto fail;
|
||||
|
||||
if (is_reserved_name(decl->name))
|
||||
err_reserved_name(&cfp, decl->name);
|
||||
|
||||
code = cf_next_token_should_be(&cfp, "(", "(", NULL);
|
||||
if (code == PARSE_EOF)
|
||||
goto fail;
|
||||
|
||||
parse_params(&cfp, decl);
|
||||
|
||||
fail:
|
||||
success = !error_data_has_errors(&cfp.error_list);
|
||||
|
||||
if (success && ret_param.type != CALL_PARAM_TYPE_VOID) {
|
||||
ret_param.name = bstrdup("return");
|
||||
da_push_back(decl->params, &ret_param);
|
||||
}
|
||||
|
||||
if (!success)
|
||||
decl_info_free(decl);
|
||||
|
||||
print_errors(&cfp, decl_string);
|
||||
|
||||
cf_parser_free(&cfp);
|
||||
return success;
|
||||
}
|
||||
61
libobs/callback/decl.h
Normal file
61
libobs/callback/decl.h
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Hugh Bailey <obs.jim@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "calldata.h"
|
||||
#include "../util/darray.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct decl_param {
|
||||
char *name;
|
||||
enum call_param_type type;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
static inline void decl_param_free(struct decl_param *param)
|
||||
{
|
||||
if (param)
|
||||
bfree(param->name);
|
||||
memset(param, 0, sizeof(struct decl_param));
|
||||
}
|
||||
|
||||
struct decl_info {
|
||||
char *name;
|
||||
const char *decl_string;
|
||||
DARRAY(struct decl_param) params;
|
||||
};
|
||||
|
||||
static inline void decl_info_free(struct decl_info *decl)
|
||||
{
|
||||
if (decl) {
|
||||
for (size_t i = 0; i < decl->params.num; i++)
|
||||
decl_param_free(decl->params.array+i);
|
||||
da_free(decl->params);
|
||||
|
||||
bfree(decl->name);
|
||||
memset(decl, 0, sizeof(struct decl_info));
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT bool parse_decl_string(struct decl_info *decl, const char *decl_string);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
90
libobs/callback/proc.c
Normal file
90
libobs/callback/proc.c
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (c) 2013 Hugh Bailey <obs.jim@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "../util/darray.h"
|
||||
|
||||
#include "decl.h"
|
||||
#include "proc.h"
|
||||
|
||||
struct proc_info {
|
||||
struct decl_info func;
|
||||
void *data;
|
||||
proc_handler_proc_t callback;
|
||||
};
|
||||
|
||||
static inline void proc_info_free(struct proc_info *pi)
|
||||
{
|
||||
decl_info_free(&pi->func);
|
||||
}
|
||||
|
||||
struct proc_handler {
|
||||
/* TODO: replace with hash table lookup? */
|
||||
DARRAY(struct proc_info) procs;
|
||||
};
|
||||
|
||||
proc_handler_t *proc_handler_create(void)
|
||||
{
|
||||
struct proc_handler *handler = bmalloc(sizeof(struct proc_handler));
|
||||
da_init(handler->procs);
|
||||
return handler;
|
||||
}
|
||||
|
||||
void proc_handler_destroy(proc_handler_t *handler)
|
||||
{
|
||||
if (handler) {
|
||||
for (size_t i = 0; i < handler->procs.num; i++)
|
||||
proc_info_free(handler->procs.array+i);
|
||||
da_free(handler->procs);
|
||||
bfree(handler);
|
||||
}
|
||||
}
|
||||
|
||||
void proc_handler_add(proc_handler_t *handler, const char *decl_string,
|
||||
proc_handler_proc_t proc, void *data)
|
||||
{
|
||||
if (!handler) return;
|
||||
|
||||
struct proc_info pi;
|
||||
memset(&pi, 0, sizeof(struct proc_info));
|
||||
|
||||
if (!parse_decl_string(&pi.func, decl_string)) {
|
||||
blog(LOG_ERROR, "Function declaration invalid: %s",
|
||||
decl_string);
|
||||
return;
|
||||
}
|
||||
|
||||
pi.callback = proc;
|
||||
pi.data = data;
|
||||
|
||||
da_push_back(handler->procs, &pi);
|
||||
}
|
||||
|
||||
bool proc_handler_call(proc_handler_t *handler, const char *name,
|
||||
calldata_t *params)
|
||||
{
|
||||
if (!handler) return false;
|
||||
|
||||
for (size_t i = 0; i < handler->procs.num; i++) {
|
||||
struct proc_info *info = handler->procs.array+i;
|
||||
|
||||
if (strcmp(info->func.name, name) == 0) {
|
||||
info->callback(info->data, params);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
54
libobs/callback/proc.h
Normal file
54
libobs/callback/proc.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (c) 2013 Hugh Bailey <obs.jim@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../util/c99defs.h"
|
||||
|
||||
#include "calldata.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Procedure handler
|
||||
*
|
||||
* This handler is used to allow access to one or more procedures that can be
|
||||
* added and called without having to have direct access to declarations or
|
||||
* procedure callback pointers.
|
||||
*/
|
||||
|
||||
struct proc_handler;
|
||||
typedef struct proc_handler proc_handler_t;
|
||||
typedef void (*proc_handler_proc_t)(void*, calldata_t*);
|
||||
|
||||
EXPORT proc_handler_t *proc_handler_create(void);
|
||||
EXPORT void proc_handler_destroy(proc_handler_t *handler);
|
||||
|
||||
EXPORT void proc_handler_add(proc_handler_t *handler, const char *decl_string,
|
||||
proc_handler_proc_t proc, void *data);
|
||||
|
||||
/**
|
||||
* Calls a function in a procedure handler. Returns false if the named
|
||||
* procedure is not found.
|
||||
*/
|
||||
EXPORT bool proc_handler_call(proc_handler_t *handler, const char *name,
|
||||
calldata_t *params);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
268
libobs/callback/signal.c
Normal file
268
libobs/callback/signal.c
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2014 Hugh Bailey <obs.jim@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "../util/darray.h"
|
||||
#include "../util/threading.h"
|
||||
|
||||
#include "decl.h"
|
||||
#include "signal.h"
|
||||
|
||||
struct signal_callback {
|
||||
signal_callback_t callback;
|
||||
void *data;
|
||||
bool remove;
|
||||
};
|
||||
|
||||
struct signal_info {
|
||||
struct decl_info func;
|
||||
DARRAY(struct signal_callback) callbacks;
|
||||
pthread_mutex_t mutex;
|
||||
bool signalling;
|
||||
|
||||
struct signal_info *next;
|
||||
};
|
||||
|
||||
static inline struct signal_info *signal_info_create(struct decl_info *info)
|
||||
{
|
||||
pthread_mutexattr_t attr;
|
||||
struct signal_info *si;
|
||||
|
||||
if (pthread_mutexattr_init(&attr) != 0)
|
||||
return NULL;
|
||||
if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0)
|
||||
return NULL;
|
||||
|
||||
si = bmalloc(sizeof(struct signal_info));
|
||||
|
||||
si->func = *info;
|
||||
si->next = NULL;
|
||||
si->signalling = false;
|
||||
da_init(si->callbacks);
|
||||
|
||||
if (pthread_mutex_init(&si->mutex, &attr) != 0) {
|
||||
blog(LOG_ERROR, "Could not create signal");
|
||||
|
||||
decl_info_free(&si->func);
|
||||
bfree(si);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return si;
|
||||
}
|
||||
|
||||
static inline void signal_info_destroy(struct signal_info *si)
|
||||
{
|
||||
if (si) {
|
||||
pthread_mutex_destroy(&si->mutex);
|
||||
decl_info_free(&si->func);
|
||||
da_free(si->callbacks);
|
||||
bfree(si);
|
||||
}
|
||||
}
|
||||
|
||||
static inline size_t signal_get_callback_idx(struct signal_info *si,
|
||||
signal_callback_t callback, void *data)
|
||||
{
|
||||
for (size_t i = 0; i < si->callbacks.num; i++) {
|
||||
struct signal_callback *sc = si->callbacks.array+i;
|
||||
|
||||
if (sc->callback == callback && sc->data == data)
|
||||
return i;
|
||||
}
|
||||
|
||||
return DARRAY_INVALID;
|
||||
}
|
||||
|
||||
struct signal_handler {
|
||||
struct signal_info *first;
|
||||
pthread_mutex_t mutex;
|
||||
};
|
||||
|
||||
static struct signal_info *getsignal(signal_handler_t *handler,
|
||||
const char *name, struct signal_info **p_last)
|
||||
{
|
||||
struct signal_info *signal, *last= NULL;
|
||||
|
||||
signal = handler->first;
|
||||
while (signal != NULL) {
|
||||
if (strcmp(signal->func.name, name) == 0)
|
||||
break;
|
||||
|
||||
last = signal;
|
||||
signal = signal->next;
|
||||
}
|
||||
|
||||
if (p_last)
|
||||
*p_last = last;
|
||||
return signal;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
signal_handler_t *signal_handler_create(void)
|
||||
{
|
||||
struct signal_handler *handler = bmalloc(sizeof(struct signal_handler));
|
||||
handler->first = NULL;
|
||||
|
||||
if (pthread_mutex_init(&handler->mutex, NULL) != 0) {
|
||||
blog(LOG_ERROR, "Couldn't create signal handler!");
|
||||
bfree(handler);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return handler;
|
||||
}
|
||||
|
||||
void signal_handler_destroy(signal_handler_t *handler)
|
||||
{
|
||||
if (handler) {
|
||||
struct signal_info *sig = handler->first;
|
||||
while (sig != NULL) {
|
||||
struct signal_info *next = sig->next;
|
||||
signal_info_destroy(sig);
|
||||
sig = next;
|
||||
}
|
||||
|
||||
pthread_mutex_destroy(&handler->mutex);
|
||||
bfree(handler);
|
||||
}
|
||||
}
|
||||
|
||||
bool signal_handler_add(signal_handler_t *handler, const char *signal_decl)
|
||||
{
|
||||
struct decl_info func = {0};
|
||||
struct signal_info *sig, *last;
|
||||
bool success = true;
|
||||
|
||||
if (!parse_decl_string(&func, signal_decl)) {
|
||||
blog(LOG_ERROR, "Signal declaration invalid: %s", signal_decl);
|
||||
return false;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&handler->mutex);
|
||||
|
||||
sig = getsignal(handler, func.name, &last);
|
||||
if (sig) {
|
||||
blog(LOG_WARNING, "Signal declaration '%s' exists", func.name);
|
||||
decl_info_free(&func);
|
||||
success = false;
|
||||
} else {
|
||||
sig = signal_info_create(&func);
|
||||
if (!last)
|
||||
handler->first = sig;
|
||||
else
|
||||
last->next = sig;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&handler->mutex);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void signal_handler_connect(signal_handler_t *handler, const char *signal,
|
||||
signal_callback_t callback, void *data)
|
||||
{
|
||||
struct signal_info *sig, *last;
|
||||
struct signal_callback cb_data = {callback, data, false};
|
||||
size_t idx;
|
||||
|
||||
if (!handler)
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&handler->mutex);
|
||||
sig = getsignal(handler, signal, &last);
|
||||
pthread_mutex_unlock(&handler->mutex);
|
||||
|
||||
if (!sig) {
|
||||
blog(LOG_WARNING, "signal_handler_connect: "
|
||||
"signal '%s' not found", signal);
|
||||
return;
|
||||
}
|
||||
|
||||
/* -------------- */
|
||||
|
||||
pthread_mutex_lock(&sig->mutex);
|
||||
|
||||
idx = signal_get_callback_idx(sig, callback, data);
|
||||
if (idx == DARRAY_INVALID)
|
||||
da_push_back(sig->callbacks, &cb_data);
|
||||
|
||||
pthread_mutex_unlock(&sig->mutex);
|
||||
}
|
||||
|
||||
static inline struct signal_info *getsignal_locked(signal_handler_t *handler,
|
||||
const char *name)
|
||||
{
|
||||
struct signal_info *sig;
|
||||
|
||||
if (!handler)
|
||||
return NULL;
|
||||
|
||||
pthread_mutex_lock(&handler->mutex);
|
||||
sig = getsignal(handler, name, NULL);
|
||||
pthread_mutex_unlock(&handler->mutex);
|
||||
|
||||
return sig;
|
||||
}
|
||||
|
||||
void signal_handler_disconnect(signal_handler_t *handler, const char *signal,
|
||||
signal_callback_t callback, void *data)
|
||||
{
|
||||
struct signal_info *sig = getsignal_locked(handler, signal);
|
||||
size_t idx;
|
||||
|
||||
if (!sig)
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&sig->mutex);
|
||||
|
||||
idx = signal_get_callback_idx(sig, callback, data);
|
||||
if (idx != DARRAY_INVALID) {
|
||||
if (sig->signalling)
|
||||
sig->callbacks.array[idx].remove = true;
|
||||
else
|
||||
da_erase(sig->callbacks, idx);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&sig->mutex);
|
||||
}
|
||||
|
||||
void signal_handler_signal(signal_handler_t *handler, const char *signal,
|
||||
calldata_t *params)
|
||||
{
|
||||
struct signal_info *sig = getsignal_locked(handler, signal);
|
||||
|
||||
if (!sig)
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&sig->mutex);
|
||||
sig->signalling = true;
|
||||
|
||||
for (size_t i = 0; i < sig->callbacks.num; i++) {
|
||||
struct signal_callback *cb = sig->callbacks.array+i;
|
||||
if (!cb->remove)
|
||||
cb->callback(cb->data, params);
|
||||
}
|
||||
|
||||
for (size_t i = sig->callbacks.num; i > 0; i--) {
|
||||
struct signal_callback *cb = sig->callbacks.array+i-1;
|
||||
if (cb->remove)
|
||||
da_erase(sig->callbacks, i-1);
|
||||
}
|
||||
|
||||
sig->signalling = false;
|
||||
pthread_mutex_unlock(&sig->mutex);
|
||||
}
|
||||
68
libobs/callback/signal.h
Normal file
68
libobs/callback/signal.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (c) 2013 Hugh Bailey <obs.jim@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../util/c99defs.h"
|
||||
|
||||
#include "calldata.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Signal handler
|
||||
*
|
||||
* This is used to create a signal handler which can broadcast events
|
||||
* to one or more callbacks connected to a signal.
|
||||
*/
|
||||
|
||||
struct signal_handler;
|
||||
typedef struct signal_handler signal_handler_t;
|
||||
typedef void (*signal_callback_t)(void*, calldata_t*);
|
||||
|
||||
EXPORT signal_handler_t *signal_handler_create(void);
|
||||
EXPORT void signal_handler_destroy(signal_handler_t *handler);
|
||||
|
||||
EXPORT bool signal_handler_add(signal_handler_t *handler,
|
||||
const char *signal_decl);
|
||||
|
||||
static inline bool signal_handler_add_array(signal_handler_t *handler,
|
||||
const char **signal_decls)
|
||||
{
|
||||
bool success = true;
|
||||
if (!signal_decls)
|
||||
return false;
|
||||
|
||||
while (*signal_decls)
|
||||
if (!signal_handler_add(handler, *(signal_decls++)))
|
||||
success = false;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
EXPORT void signal_handler_connect(signal_handler_t *handler,
|
||||
const char *signal, signal_callback_t callback, void *data);
|
||||
EXPORT void signal_handler_disconnect(signal_handler_t *handler,
|
||||
const char *signal, signal_callback_t callback, void *data);
|
||||
|
||||
EXPORT void signal_handler_signal(signal_handler_t *handler, const char *signal,
|
||||
calldata_t *params);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
139
libobs/data/bicubic_scale.effect
Normal file
139
libobs/data/bicubic_scale.effect
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* bicubic sharper (better for downscaling)
|
||||
* note - this shader is adapted from the GPL bsnes shader, very good stuff
|
||||
* there.
|
||||
*/
|
||||
|
||||
uniform float4x4 ViewProj;
|
||||
uniform texture2d image;
|
||||
uniform float4x4 color_matrix;
|
||||
uniform float3 color_range_min = {0.0, 0.0, 0.0};
|
||||
uniform float3 color_range_max = {1.0, 1.0, 1.0};
|
||||
uniform float2 base_dimension_i;
|
||||
|
||||
sampler_state textureSampler {
|
||||
Filter = Linear;
|
||||
AddressU = Clamp;
|
||||
AddressV = Clamp;
|
||||
};
|
||||
|
||||
struct VertData {
|
||||
float4 pos : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
VertData VSDefault(VertData v_in)
|
||||
{
|
||||
VertData vert_out;
|
||||
vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj);
|
||||
vert_out.uv = v_in.uv;
|
||||
return vert_out;
|
||||
}
|
||||
|
||||
float weight(float x)
|
||||
{
|
||||
float ax = abs(x);
|
||||
|
||||
/* Sharper version. May look better in some cases. */
|
||||
const float B = 0.0;
|
||||
const float C = 0.75;
|
||||
|
||||
if (ax < 1.0)
|
||||
return (pow(x, 2.0) *
|
||||
((12.0 - 9.0 * B - 6.0 * C) * ax +
|
||||
(-18.0 + 12.0 * B + 6.0 * C)) +
|
||||
(6.0 - 2.0 * B))
|
||||
/ 6.0;
|
||||
else if ((ax >= 1.0) && (ax < 2.0))
|
||||
return (pow(x, 2.0) *
|
||||
((-B - 6.0 * C) * ax + (6.0 * B + 30.0 * C)) +
|
||||
(-12.0 * B - 48.0 * C) * ax +
|
||||
(8.0 * B + 24.0 * C))
|
||||
/ 6.0;
|
||||
else
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
float4 weight4(float x)
|
||||
{
|
||||
return float4(
|
||||
weight(x - 2.0),
|
||||
weight(x - 1.0),
|
||||
weight(x),
|
||||
weight(x + 1.0));
|
||||
}
|
||||
|
||||
float4 pixel(float xpos, float ypos)
|
||||
{
|
||||
return image.Sample(textureSampler, float2(xpos, ypos));
|
||||
}
|
||||
|
||||
float4 get_line(float ypos, float4 xpos, float4 linetaps)
|
||||
{
|
||||
return
|
||||
pixel(xpos.r, ypos) * linetaps.r +
|
||||
pixel(xpos.g, ypos) * linetaps.g +
|
||||
pixel(xpos.b, ypos) * linetaps.b +
|
||||
pixel(xpos.a, ypos) * linetaps.a;
|
||||
}
|
||||
|
||||
float4 DrawBicubic(VertData v_in)
|
||||
{
|
||||
float2 stepxy = base_dimension_i;
|
||||
float2 pos = v_in.uv + stepxy * 0.5;
|
||||
float2 f = frac(pos / stepxy);
|
||||
|
||||
float4 rowtaps = weight4(1.0 - f.x);
|
||||
float4 coltaps = weight4(1.0 - f.y);
|
||||
|
||||
/* make sure all taps added together is exactly 1.0, otherwise some
|
||||
* (very small) distortion can occur */
|
||||
rowtaps /= rowtaps.r + rowtaps.g + rowtaps.b + rowtaps.a;
|
||||
coltaps /= coltaps.r + coltaps.g + coltaps.b + coltaps.a;
|
||||
|
||||
float2 xystart = (-1.5 - f) * stepxy + pos;
|
||||
float4 xpos = float4(
|
||||
xystart.x,
|
||||
xystart.x + stepxy.x,
|
||||
xystart.x + stepxy.x * 2.0,
|
||||
xystart.x + stepxy.x * 3.0
|
||||
);
|
||||
|
||||
return
|
||||
get_line(xystart.y , xpos, rowtaps) * coltaps.r +
|
||||
get_line(xystart.y + stepxy.y , xpos, rowtaps) * coltaps.g +
|
||||
get_line(xystart.y + stepxy.y * 2.0, xpos, rowtaps) * coltaps.b +
|
||||
get_line(xystart.y + stepxy.y * 3.0, xpos, rowtaps) * coltaps.a;
|
||||
}
|
||||
|
||||
float4 PSDrawBicubicRGBA(VertData v_in) : TARGET
|
||||
{
|
||||
return DrawBicubic(v_in);
|
||||
}
|
||||
|
||||
float4 PSDrawBicubicMatrix(VertData v_in) : TARGET
|
||||
{
|
||||
float4 rgba = DrawBicubic(v_in);
|
||||
float4 yuv;
|
||||
|
||||
yuv.xyz = clamp(rgba.xyz, color_range_min, color_range_max);
|
||||
return saturate(mul(float4(yuv.xyz, 1.0), color_matrix));
|
||||
}
|
||||
|
||||
technique Draw
|
||||
{
|
||||
pass
|
||||
{
|
||||
vertex_shader = VSDefault(v_in);
|
||||
pixel_shader = PSDrawBicubicRGBA(v_in);
|
||||
}
|
||||
}
|
||||
|
||||
technique DrawMatrix
|
||||
{
|
||||
pass
|
||||
{
|
||||
vertex_shader = VSDefault(v_in);
|
||||
pixel_shader = PSDrawBicubicMatrix(v_in);
|
||||
}
|
||||
}
|
||||
84
libobs/data/bilinear_lowres_scale.effect
Normal file
84
libobs/data/bilinear_lowres_scale.effect
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* bilinear low res scaling, samples 9 pixels of a larger image to scale to a
|
||||
* low resolution image below half size
|
||||
*/
|
||||
|
||||
uniform float4x4 ViewProj;
|
||||
uniform texture2d image;
|
||||
uniform float4x4 color_matrix;
|
||||
uniform float3 color_range_min = {0.0, 0.0, 0.0};
|
||||
uniform float3 color_range_max = {1.0, 1.0, 1.0};
|
||||
uniform float2 base_dimension_i;
|
||||
|
||||
sampler_state textureSampler {
|
||||
Filter = Linear;
|
||||
AddressU = Clamp;
|
||||
AddressV = Clamp;
|
||||
};
|
||||
|
||||
struct VertData {
|
||||
float4 pos : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
VertData VSDefault(VertData v_in)
|
||||
{
|
||||
VertData vert_out;
|
||||
vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj);
|
||||
vert_out.uv = v_in.uv;
|
||||
return vert_out;
|
||||
}
|
||||
|
||||
float4 pixel(float2 uv)
|
||||
{
|
||||
return image.Sample(textureSampler, uv);
|
||||
}
|
||||
|
||||
float4 DrawLowresBilinear(VertData v_in)
|
||||
{
|
||||
float2 stepxy = base_dimension_i;
|
||||
float4 out_color;
|
||||
|
||||
out_color = pixel(v_in.uv);
|
||||
out_color += pixel(v_in.uv + float2(-stepxy.x, -stepxy.y));
|
||||
out_color += pixel(v_in.uv + float2(-stepxy.x, 0.0));
|
||||
out_color += pixel(v_in.uv + float2(-stepxy.x, stepxy.y));
|
||||
out_color += pixel(v_in.uv + float2( 0.0, -stepxy.y));
|
||||
out_color += pixel(v_in.uv + float2( 0.0, stepxy.y));
|
||||
out_color += pixel(v_in.uv + float2( stepxy.x, -stepxy.y));
|
||||
out_color += pixel(v_in.uv + float2( stepxy.x, 0.0));
|
||||
out_color += pixel(v_in.uv + float2( stepxy.x, stepxy.y));
|
||||
return out_color / float4(9.0, 9.0, 9.0, 9.0);
|
||||
}
|
||||
|
||||
float4 PSDrawLowresBilinearRGBA(VertData v_in) : TARGET
|
||||
{
|
||||
return DrawLowresBilinear(v_in);
|
||||
}
|
||||
|
||||
float4 PSDrawLowresBilinearMatrix(VertData v_in) : TARGET
|
||||
{
|
||||
float4 yuv = DrawLowresBilinear(v_in);
|
||||
|
||||
yuv.xyz = clamp(yuv.xyz, color_range_min, color_range_max);
|
||||
return saturate(mul(float4(yuv.xyz, 1.0), color_matrix));
|
||||
}
|
||||
|
||||
technique Draw
|
||||
{
|
||||
pass
|
||||
{
|
||||
vertex_shader = VSDefault(v_in);
|
||||
pixel_shader = PSDrawLowresBilinearRGBA(v_in);
|
||||
}
|
||||
}
|
||||
|
||||
technique DrawMatrix
|
||||
{
|
||||
pass
|
||||
{
|
||||
vertex_shader = VSDefault(v_in);
|
||||
pixel_shader = PSDrawLowresBilinearMatrix(v_in);
|
||||
}
|
||||
}
|
||||
|
||||
54
libobs/data/default.effect
Normal file
54
libobs/data/default.effect
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
uniform float4x4 ViewProj;
|
||||
uniform float4x4 color_matrix;
|
||||
uniform float3 color_range_min = {0.0, 0.0, 0.0};
|
||||
uniform float3 color_range_max = {1.0, 1.0, 1.0};
|
||||
uniform texture2d image;
|
||||
|
||||
sampler_state def_sampler {
|
||||
Filter = Linear;
|
||||
AddressU = Clamp;
|
||||
AddressV = Clamp;
|
||||
};
|
||||
|
||||
struct VertInOut {
|
||||
float4 pos : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
VertInOut VSDefault(VertInOut vert_in)
|
||||
{
|
||||
VertInOut vert_out;
|
||||
vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);
|
||||
vert_out.uv = vert_in.uv;
|
||||
return vert_out;
|
||||
}
|
||||
|
||||
float4 PSDrawBare(VertInOut vert_in) : TARGET
|
||||
{
|
||||
return image.Sample(def_sampler, vert_in.uv);
|
||||
}
|
||||
|
||||
float4 PSDrawMatrix(VertInOut vert_in) : TARGET
|
||||
{
|
||||
float4 yuv = image.Sample(def_sampler, vert_in.uv);
|
||||
yuv.xyz = clamp(yuv.xyz, color_range_min, color_range_max);
|
||||
return saturate(mul(float4(yuv.xyz, 1.0), color_matrix));
|
||||
}
|
||||
|
||||
technique Draw
|
||||
{
|
||||
pass
|
||||
{
|
||||
vertex_shader = VSDefault(vert_in);
|
||||
pixel_shader = PSDrawBare(vert_in);
|
||||
}
|
||||
}
|
||||
|
||||
technique DrawMatrix
|
||||
{
|
||||
pass
|
||||
{
|
||||
vertex_shader = VSDefault(vert_in);
|
||||
pixel_shader = PSDrawMatrix(vert_in);
|
||||
}
|
||||
}
|
||||
55
libobs/data/default_rect.effect
Normal file
55
libobs/data/default_rect.effect
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
uniform float4x4 ViewProj;
|
||||
uniform float4x4 color_matrix;
|
||||
uniform float3 color_range_min = {0.0, 0.0, 0.0};
|
||||
uniform float3 color_range_max = {1.0, 1.0, 1.0};
|
||||
uniform texture_rect image;
|
||||
|
||||
sampler_state def_sampler {
|
||||
Filter = Linear;
|
||||
AddressU = Clamp;
|
||||
AddressV = Clamp;
|
||||
};
|
||||
|
||||
struct VertInOut {
|
||||
float4 pos : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
VertInOut VSDefault(VertInOut vert_in)
|
||||
{
|
||||
VertInOut vert_out;
|
||||
vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);
|
||||
vert_out.uv = vert_in.uv;
|
||||
return vert_out;
|
||||
}
|
||||
|
||||
float4 PSDrawBare(VertInOut vert_in) : TARGET
|
||||
{
|
||||
return image.Sample(def_sampler, vert_in.uv);
|
||||
}
|
||||
|
||||
float4 PSDrawMatrix(VertInOut vert_in) : TARGET
|
||||
{
|
||||
float4 yuv = image.Sample(def_sampler, vert_in.uv);
|
||||
yuv.xyz = clamp(yuv.xyz, color_range_min, color_range_max);
|
||||
return saturate(mul(float4(yuv.xyz, 1.0), color_matrix));
|
||||
}
|
||||
|
||||
technique Draw
|
||||
{
|
||||
pass
|
||||
{
|
||||
vertex_shader = VSDefault(vert_in);
|
||||
pixel_shader = PSDrawBare(vert_in);
|
||||
}
|
||||
}
|
||||
|
||||
technique DrawMatrix
|
||||
{
|
||||
pass
|
||||
{
|
||||
vertex_shader = VSDefault(vert_in);
|
||||
pixel_shader = PSDrawMatrix(vert_in);
|
||||
}
|
||||
}
|
||||
|
||||
380
libobs/data/format_conversion.effect
Normal file
380
libobs/data/format_conversion.effect
Normal file
|
|
@ -0,0 +1,380 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2014 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
//#define DEBUGGING
|
||||
|
||||
uniform float4x4 ViewProj;
|
||||
|
||||
uniform float u_plane_offset;
|
||||
uniform float v_plane_offset;
|
||||
|
||||
uniform float width;
|
||||
uniform float height;
|
||||
uniform float width_i;
|
||||
uniform float height_i;
|
||||
uniform float width_d2;
|
||||
uniform float height_d2;
|
||||
uniform float width_d2_i;
|
||||
uniform float height_d2_i;
|
||||
uniform float input_width;
|
||||
uniform float input_height;
|
||||
uniform float input_width_i;
|
||||
uniform float input_height_i;
|
||||
uniform float input_width_i_d2;
|
||||
uniform float input_height_i_d2;
|
||||
|
||||
uniform texture2d image;
|
||||
|
||||
sampler_state def_sampler {
|
||||
Filter = Linear;
|
||||
AddressU = Clamp;
|
||||
AddressV = Clamp;
|
||||
};
|
||||
|
||||
struct VertInOut {
|
||||
float4 pos : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
VertInOut VSDefault(VertInOut vert_in)
|
||||
{
|
||||
VertInOut vert_out;
|
||||
vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);
|
||||
vert_out.uv = vert_in.uv;
|
||||
return vert_out;
|
||||
}
|
||||
|
||||
/* used to prevent internal GPU precision issues width fmod in particular */
|
||||
#define PRECISION_OFFSET 0.2
|
||||
|
||||
float4 PSNV12(VertInOut vert_in) : TARGET
|
||||
{
|
||||
float v_mul = floor(vert_in.uv.y * input_height);
|
||||
|
||||
float byte_offset = floor((v_mul + vert_in.uv.x) * width) * 4.0;
|
||||
byte_offset += PRECISION_OFFSET;
|
||||
|
||||
float2 sample_pos[4];
|
||||
|
||||
if (byte_offset < u_plane_offset) {
|
||||
#ifdef DEBUGGING
|
||||
return float4(1.0, 1.0, 1.0, 1.0);
|
||||
#endif
|
||||
|
||||
float lum_u = floor(fmod(byte_offset, width)) * width_i;
|
||||
float lum_v = floor(byte_offset * width_i) * height_i;
|
||||
|
||||
/* move to texel centers to sample the 4 pixels properly */
|
||||
lum_u += width_i * 0.5;
|
||||
lum_v += height_i * 0.5;
|
||||
|
||||
sample_pos[0] = float2(lum_u, lum_v);
|
||||
sample_pos[1] = float2(lum_u += width_i, lum_v);
|
||||
sample_pos[2] = float2(lum_u += width_i, lum_v);
|
||||
sample_pos[3] = float2(lum_u + width_i, lum_v);
|
||||
|
||||
float4x4 out_val = float4x4(
|
||||
image.Sample(def_sampler, sample_pos[0]),
|
||||
image.Sample(def_sampler, sample_pos[1]),
|
||||
image.Sample(def_sampler, sample_pos[2]),
|
||||
image.Sample(def_sampler, sample_pos[3])
|
||||
);
|
||||
|
||||
return transpose(out_val)[1];
|
||||
} else {
|
||||
#ifdef DEBUGGING
|
||||
return float4(0.5, 0.2, 0.5, 0.2);
|
||||
#endif
|
||||
|
||||
float new_offset = byte_offset - u_plane_offset;
|
||||
|
||||
float ch_u = floor(fmod(new_offset, width)) * width_i;
|
||||
float ch_v = floor(new_offset * width_i) * height_d2_i;
|
||||
float width_i2 = width_i*2.0;
|
||||
|
||||
/* move to the borders of each set of 4 pixels to force it
|
||||
* to do bilinear averaging */
|
||||
ch_u += width_i;
|
||||
ch_v += height_i;
|
||||
|
||||
sample_pos[0] = float2(ch_u, ch_v);
|
||||
sample_pos[1] = float2(ch_u + width_i2, ch_v);
|
||||
|
||||
return float4(
|
||||
image.Sample(def_sampler, sample_pos[0]).rb,
|
||||
image.Sample(def_sampler, sample_pos[1]).rb
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
float4 PSPlanar420(VertInOut vert_in) : TARGET
|
||||
{
|
||||
float v_mul = floor(vert_in.uv.y * input_height);
|
||||
|
||||
float byte_offset = floor((v_mul + vert_in.uv.x) * width) * 4.0;
|
||||
byte_offset += PRECISION_OFFSET;
|
||||
|
||||
float2 sample_pos[4];
|
||||
|
||||
if (byte_offset < u_plane_offset) {
|
||||
#ifdef DEBUGGING
|
||||
return float4(1.0, 1.0, 1.0, 1.0);
|
||||
#endif
|
||||
|
||||
float lum_u = floor(fmod(byte_offset, width)) * width_i;
|
||||
float lum_v = floor(byte_offset * width_i) * height_i;
|
||||
|
||||
/* move to texel centers to sample the 4 pixels properly */
|
||||
lum_u += width_i * 0.5;
|
||||
lum_v += height_i * 0.5;
|
||||
|
||||
sample_pos[0] = float2(lum_u, lum_v);
|
||||
sample_pos[1] = float2(lum_u += width_i, lum_v);
|
||||
sample_pos[2] = float2(lum_u += width_i, lum_v);
|
||||
sample_pos[3] = float2(lum_u + width_i, lum_v);
|
||||
|
||||
} else {
|
||||
#ifdef DEBUGGING
|
||||
return ((byte_offset < v_plane_offset) ?
|
||||
float4(0.5, 0.5, 0.5, 0.5) :
|
||||
float4(0.2, 0.2, 0.2, 0.2));
|
||||
#endif
|
||||
|
||||
float new_offset = byte_offset -
|
||||
((byte_offset < v_plane_offset) ?
|
||||
u_plane_offset : v_plane_offset);
|
||||
|
||||
float ch_u = floor(fmod(new_offset, width_d2)) * width_d2_i;
|
||||
float ch_v = floor(new_offset * width_d2_i) * height_d2_i;
|
||||
float width_i2 = width_i*2.0;
|
||||
|
||||
/* move to the borders of each set of 4 pixels to force it
|
||||
* to do bilinear averaging */
|
||||
ch_u += width_i;
|
||||
ch_v += height_i;
|
||||
|
||||
sample_pos[0] = float2(ch_u, ch_v);
|
||||
sample_pos[1] = float2(ch_u += width_i2, ch_v);
|
||||
sample_pos[2] = float2(ch_u += width_i2, ch_v);
|
||||
sample_pos[3] = float2(ch_u + width_i2, ch_v);
|
||||
}
|
||||
|
||||
float4x4 out_val = float4x4(
|
||||
image.Sample(def_sampler, sample_pos[0]),
|
||||
image.Sample(def_sampler, sample_pos[1]),
|
||||
image.Sample(def_sampler, sample_pos[2]),
|
||||
image.Sample(def_sampler, sample_pos[3])
|
||||
);
|
||||
|
||||
out_val = transpose(out_val);
|
||||
|
||||
if (byte_offset < u_plane_offset)
|
||||
return out_val[1];
|
||||
else if (byte_offset < v_plane_offset)
|
||||
return out_val[0];
|
||||
else
|
||||
return out_val[2];
|
||||
}
|
||||
|
||||
float4 PSPlanar444(VertInOut vert_in) : TARGET
|
||||
{
|
||||
float v_mul = floor(vert_in.uv.y * input_height);
|
||||
|
||||
float byte_offset = floor((v_mul + vert_in.uv.x) * width) * 4.0;
|
||||
byte_offset += PRECISION_OFFSET;
|
||||
|
||||
float new_byte_offset = byte_offset;
|
||||
|
||||
if (byte_offset >= v_plane_offset)
|
||||
new_byte_offset -= v_plane_offset;
|
||||
else if (byte_offset >= u_plane_offset)
|
||||
new_byte_offset -= u_plane_offset;
|
||||
|
||||
float2 sample_pos[4];
|
||||
|
||||
float u_val = floor(fmod(new_byte_offset, width)) * width_i;
|
||||
float v_val = floor(new_byte_offset * width_i) * height_i;
|
||||
|
||||
/* move to texel centers to sample the 4 pixels properly */
|
||||
u_val += width_i * 0.5;
|
||||
v_val += height_i * 0.5;
|
||||
|
||||
sample_pos[0] = float2(u_val, v_val);
|
||||
sample_pos[1] = float2(u_val += width_i, v_val);
|
||||
sample_pos[2] = float2(u_val += width_i, v_val);
|
||||
sample_pos[3] = float2(u_val + width_i, v_val);
|
||||
|
||||
float4x4 out_val = float4x4(
|
||||
image.Sample(def_sampler, sample_pos[0]),
|
||||
image.Sample(def_sampler, sample_pos[1]),
|
||||
image.Sample(def_sampler, sample_pos[2]),
|
||||
image.Sample(def_sampler, sample_pos[3])
|
||||
);
|
||||
|
||||
out_val = transpose(out_val);
|
||||
|
||||
if (byte_offset < u_plane_offset)
|
||||
return out_val[1];
|
||||
else if (byte_offset < v_plane_offset)
|
||||
return out_val[0];
|
||||
else
|
||||
return out_val[2];
|
||||
}
|
||||
|
||||
float4 PSPacked422_Reverse(VertInOut vert_in, int u_pos, int v_pos,
|
||||
int y0_pos, int y1_pos) : TARGET
|
||||
{
|
||||
float y = vert_in.uv.y;
|
||||
float odd = floor(fmod(width * vert_in.uv.x + PRECISION_OFFSET, 2.0));
|
||||
float x = floor(width_d2 * vert_in.uv.x + PRECISION_OFFSET) *
|
||||
width_d2_i;
|
||||
|
||||
x += input_width_i_d2;
|
||||
|
||||
float4 texel = image.Sample(def_sampler, float2(x, y));
|
||||
return float4(odd > 0.5 ? texel[y1_pos] : texel[y0_pos],
|
||||
texel[u_pos], texel[v_pos], 1.0);
|
||||
}
|
||||
|
||||
float GetOffsetColor(float offset)
|
||||
{
|
||||
float2 uv;
|
||||
|
||||
offset += PRECISION_OFFSET;
|
||||
uv.x = floor(fmod(offset, input_width)) * input_width_i;
|
||||
uv.y = floor(offset * input_width_i) * input_height_i;
|
||||
|
||||
uv.xy += float2(input_width_i_d2, input_height_i_d2);
|
||||
|
||||
return image.Sample(def_sampler, uv).r;
|
||||
}
|
||||
|
||||
float4 PSPlanar420_Reverse(VertInOut vert_in) : TARGET
|
||||
{
|
||||
float x = vert_in.uv.x;
|
||||
float y = vert_in.uv.y;
|
||||
float x_offset = floor(x * width + PRECISION_OFFSET);
|
||||
float y_offset = floor(y * height + PRECISION_OFFSET);
|
||||
|
||||
float lum_offset = y_offset * width + x_offset + PRECISION_OFFSET;
|
||||
lum_offset = floor(lum_offset);
|
||||
|
||||
float ch_offset = floor(y_offset * 0.5 + PRECISION_OFFSET) * width_d2 +
|
||||
(x_offset * 0.5) + PRECISION_OFFSET;
|
||||
ch_offset = floor(ch_offset);
|
||||
|
||||
return float4(
|
||||
GetOffsetColor(lum_offset),
|
||||
GetOffsetColor(u_plane_offset + ch_offset),
|
||||
GetOffsetColor(v_plane_offset + ch_offset),
|
||||
1.0
|
||||
);
|
||||
}
|
||||
|
||||
float4 PSNV12_Reverse(VertInOut vert_in) : TARGET
|
||||
{
|
||||
float x = vert_in.uv.x;
|
||||
float y = vert_in.uv.y;
|
||||
float x_offset = floor(x * width + PRECISION_OFFSET);
|
||||
float y_offset = floor(y * height + PRECISION_OFFSET);
|
||||
|
||||
float lum_offset = y_offset * width + x_offset + PRECISION_OFFSET;
|
||||
lum_offset = floor(lum_offset);
|
||||
|
||||
float ch_offset = floor(y_offset * 0.5 + PRECISION_OFFSET) * width_d2 +
|
||||
(x_offset * 0.5);
|
||||
ch_offset = floor(ch_offset * 2.0 + PRECISION_OFFSET);
|
||||
|
||||
return float4(
|
||||
GetOffsetColor(lum_offset),
|
||||
GetOffsetColor(u_plane_offset + ch_offset),
|
||||
GetOffsetColor(u_plane_offset + ch_offset + 1.0),
|
||||
1.0
|
||||
);
|
||||
}
|
||||
|
||||
technique Planar420
|
||||
{
|
||||
pass
|
||||
{
|
||||
vertex_shader = VSDefault(vert_in);
|
||||
pixel_shader = PSPlanar420(vert_in);
|
||||
}
|
||||
}
|
||||
|
||||
technique Planar444
|
||||
{
|
||||
pass
|
||||
{
|
||||
vertex_shader = VSDefault(vert_in);
|
||||
pixel_shader = PSPlanar444(vert_in);
|
||||
}
|
||||
}
|
||||
|
||||
technique NV12
|
||||
{
|
||||
pass
|
||||
{
|
||||
vertex_shader = VSDefault(vert_in);
|
||||
pixel_shader = PSNV12(vert_in);
|
||||
}
|
||||
}
|
||||
|
||||
technique UYVY_Reverse
|
||||
{
|
||||
pass
|
||||
{
|
||||
vertex_shader = VSDefault(vert_in);
|
||||
pixel_shader = PSPacked422_Reverse(vert_in, 2, 0, 1, 3);
|
||||
}
|
||||
}
|
||||
|
||||
technique YUY2_Reverse
|
||||
{
|
||||
pass
|
||||
{
|
||||
vertex_shader = VSDefault(vert_in);
|
||||
pixel_shader = PSPacked422_Reverse(vert_in, 1, 3, 2, 0);
|
||||
}
|
||||
}
|
||||
|
||||
technique YVYU_Reverse
|
||||
{
|
||||
pass
|
||||
{
|
||||
vertex_shader = VSDefault(vert_in);
|
||||
pixel_shader = PSPacked422_Reverse(vert_in, 3, 1, 2, 0);
|
||||
}
|
||||
}
|
||||
|
||||
technique I420_Reverse
|
||||
{
|
||||
pass
|
||||
{
|
||||
vertex_shader = VSDefault(vert_in);
|
||||
pixel_shader = PSPlanar420_Reverse(vert_in);
|
||||
}
|
||||
}
|
||||
|
||||
technique NV12_Reverse
|
||||
{
|
||||
pass
|
||||
{
|
||||
vertex_shader = VSDefault(vert_in);
|
||||
pixel_shader = PSNV12_Reverse(vert_in);
|
||||
}
|
||||
}
|
||||
139
libobs/data/lanczos_scale.effect
Normal file
139
libobs/data/lanczos_scale.effect
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* lanczos sharper
|
||||
* note - this shader is adapted from the GPL bsnes shader, very good stuff
|
||||
* there.
|
||||
*/
|
||||
|
||||
uniform float4x4 ViewProj;
|
||||
uniform texture2d image;
|
||||
uniform float4x4 color_matrix;
|
||||
uniform float3 color_range_min = {0.0, 0.0, 0.0};
|
||||
uniform float3 color_range_max = {1.0, 1.0, 1.0};
|
||||
uniform float2 base_dimension_i;
|
||||
|
||||
sampler_state textureSampler
|
||||
{
|
||||
AddressU = Clamp;
|
||||
AddressV = Clamp;
|
||||
Filter = Linear;
|
||||
};
|
||||
|
||||
struct VertData {
|
||||
float4 pos : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
VertData VSDefault(VertData v_in)
|
||||
{
|
||||
VertData vert_out;
|
||||
vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj);
|
||||
vert_out.uv = v_in.uv;
|
||||
return vert_out;
|
||||
}
|
||||
|
||||
float sinc(float x)
|
||||
{
|
||||
const float PIval = 3.1415926535897932384626433832795;
|
||||
return sin(x * PIval) / (x * PIval);
|
||||
}
|
||||
|
||||
float weight(float x, float radius)
|
||||
{
|
||||
float ax = abs(x);
|
||||
if (x == 0.0)
|
||||
return 1.0;
|
||||
else if (ax < radius)
|
||||
return sinc(x) * sinc(x / radius);
|
||||
else
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
float3 weight3(float x)
|
||||
{
|
||||
return float3(
|
||||
weight(x * 2.0 + 0.0 * 2.0 - 3.0, 3.0),
|
||||
weight(x * 2.0 + 1.0 * 2.0 - 3.0, 3.0),
|
||||
weight(x * 2.0 + 2.0 * 2.0 - 3.0, 3.0));
|
||||
}
|
||||
|
||||
float4 pixel(float xpos, float ypos)
|
||||
{
|
||||
return image.Sample(textureSampler, float2(xpos, ypos));
|
||||
}
|
||||
|
||||
float4 get_line(float ypos, float3 xpos1, float3 xpos2, float3 rowtap1,
|
||||
float3 rowtap2)
|
||||
{
|
||||
return
|
||||
pixel(xpos1.r, ypos) * rowtap1.r +
|
||||
pixel(xpos1.g, ypos) * rowtap2.r +
|
||||
pixel(xpos1.b, ypos) * rowtap1.g +
|
||||
pixel(xpos2.r, ypos) * rowtap2.g +
|
||||
pixel(xpos2.g, ypos) * rowtap1.b +
|
||||
pixel(xpos2.b, ypos) * rowtap2.b;
|
||||
}
|
||||
|
||||
float4 DrawLanczos(VertData v_in)
|
||||
{
|
||||
float2 stepxy = base_dimension_i;
|
||||
float2 pos = v_in.uv + stepxy * 0.5;
|
||||
float2 f = frac(pos / stepxy);
|
||||
|
||||
float3 rowtap1 = weight3((1.0 - f.x) / 2.0);
|
||||
float3 rowtap2 = weight3((1.0 - f.x) / 2.0 + 0.5);
|
||||
float3 coltap1 = weight3((1.0 - f.y) / 2.0);
|
||||
float3 coltap2 = weight3((1.0 - f.y) / 2.0 + 0.5);
|
||||
|
||||
/* make sure all taps added together is exactly 1.0, otherwise some
|
||||
* (very small) distortion can occur */
|
||||
float suml = rowtap1.r + rowtap1.g + rowtap1.b + rowtap2.r + rowtap2.g + rowtap2.b;
|
||||
float sumc = coltap1.r + coltap1.g + coltap1.b + coltap2.r + coltap2.g + coltap2.b;
|
||||
rowtap1 /= suml;
|
||||
rowtap2 /= suml;
|
||||
coltap1 /= sumc;
|
||||
coltap2 /= sumc;
|
||||
|
||||
float2 xystart = (-2.5 - f) * stepxy + pos;
|
||||
float3 xpos1 = float3(xystart.x , xystart.x + stepxy.x , xystart.x + stepxy.x * 2.0);
|
||||
float3 xpos2 = float3(xystart.x + stepxy.x * 3.0, xystart.x + stepxy.x * 4.0, xystart.x + stepxy.x * 5.0);
|
||||
|
||||
return
|
||||
get_line(xystart.y , xpos1, xpos2, rowtap1, rowtap2) * coltap1.r +
|
||||
get_line(xystart.y + stepxy.y , xpos1, xpos2, rowtap1, rowtap2) * coltap2.r +
|
||||
get_line(xystart.y + stepxy.y * 2.0, xpos1, xpos2, rowtap1, rowtap2) * coltap1.g +
|
||||
get_line(xystart.y + stepxy.y * 3.0, xpos1, xpos2, rowtap1, rowtap2) * coltap2.g +
|
||||
get_line(xystart.y + stepxy.y * 4.0, xpos1, xpos2, rowtap1, rowtap2) * coltap1.b +
|
||||
get_line(xystart.y + stepxy.y * 5.0, xpos1, xpos2, rowtap1, rowtap2) * coltap2.b;
|
||||
}
|
||||
|
||||
float4 PSDrawLanczosRGBA(VertData v_in) : TARGET
|
||||
{
|
||||
return DrawLanczos(v_in);
|
||||
}
|
||||
|
||||
float4 PSDrawLanczosMatrix(VertData v_in) : TARGET
|
||||
{
|
||||
float4 rgba = DrawLanczos(v_in);
|
||||
float4 yuv;
|
||||
|
||||
yuv.xyz = clamp(rgba.xyz, color_range_min, color_range_max);
|
||||
return saturate(mul(float4(yuv.xyz, 1.0), color_matrix));
|
||||
}
|
||||
|
||||
technique Draw
|
||||
{
|
||||
pass
|
||||
{
|
||||
vertex_shader = VSDefault(v_in);
|
||||
pixel_shader = PSDrawLanczosRGBA(v_in);
|
||||
}
|
||||
}
|
||||
|
||||
technique DrawMatrix
|
||||
{
|
||||
pass
|
||||
{
|
||||
vertex_shader = VSDefault(v_in);
|
||||
pixel_shader = PSDrawLanczosMatrix(v_in);
|
||||
}
|
||||
}
|
||||
35
libobs/data/opaque.effect
Normal file
35
libobs/data/opaque.effect
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
uniform float4x4 ViewProj;
|
||||
uniform texture2d image;
|
||||
|
||||
sampler_state def_sampler {
|
||||
Filter = Linear;
|
||||
AddressU = Clamp;
|
||||
AddressV = Clamp;
|
||||
};
|
||||
|
||||
struct VertInOut {
|
||||
float4 pos : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
VertInOut VSDefault(VertInOut vert_in)
|
||||
{
|
||||
VertInOut vert_out;
|
||||
vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);
|
||||
vert_out.uv = vert_in.uv;
|
||||
return vert_out;
|
||||
}
|
||||
|
||||
float4 PSDraw(VertInOut vert_in) : TARGET
|
||||
{
|
||||
return float4(image.Sample(def_sampler, vert_in.uv).rgb, 1.0);
|
||||
}
|
||||
|
||||
technique Draw
|
||||
{
|
||||
pass
|
||||
{
|
||||
vertex_shader = VSDefault(vert_in);
|
||||
pixel_shader = PSDraw(vert_in);
|
||||
}
|
||||
}
|
||||
54
libobs/data/solid.effect
Normal file
54
libobs/data/solid.effect
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
uniform float4x4 ViewProj;
|
||||
uniform float4 color = {1.0, 1.0, 1.0, 1.0};
|
||||
|
||||
struct SolidVertInOut {
|
||||
float4 pos : POSITION;
|
||||
};
|
||||
|
||||
SolidVertInOut VSSolid(SolidVertInOut vert_in)
|
||||
{
|
||||
SolidVertInOut vert_out;
|
||||
vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);
|
||||
return vert_out;
|
||||
}
|
||||
|
||||
float4 PSSolid(SolidVertInOut vert_in) : TARGET
|
||||
{
|
||||
return color;
|
||||
}
|
||||
|
||||
struct SolidColoredVertInOut {
|
||||
float4 pos : POSITION;
|
||||
float4 color : COLOR;
|
||||
};
|
||||
|
||||
SolidColoredVertInOut VSSolidColored(SolidColoredVertInOut vert_in)
|
||||
{
|
||||
SolidColoredVertInOut vert_out;
|
||||
vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);
|
||||
vert_out.color = vert_in.color;
|
||||
return vert_out;
|
||||
}
|
||||
|
||||
float4 PSSolidColored(SolidColoredVertInOut vert_in) : TARGET
|
||||
{
|
||||
return vert_in.color * color;
|
||||
}
|
||||
|
||||
technique Solid
|
||||
{
|
||||
pass
|
||||
{
|
||||
vertex_shader = VSSolid(vert_in);
|
||||
pixel_shader = PSSolid(vert_in);
|
||||
}
|
||||
}
|
||||
|
||||
technique SolidColored
|
||||
{
|
||||
pass
|
||||
{
|
||||
vertex_shader = VSSolidColored(vert_in);
|
||||
pixel_shader = PSSolidColored(vert_in);
|
||||
}
|
||||
}
|
||||
38
libobs/graphics/axisang.c
Normal file
38
libobs/graphics/axisang.c
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "axisang.h"
|
||||
#include "quat.h"
|
||||
|
||||
void axisang_from_quat(struct axisang *dst, const struct quat *q)
|
||||
{
|
||||
float len, leni;
|
||||
|
||||
len = q->x*q->x + q->y*q->y + q->z*q->z;
|
||||
if (!close_float(len, 0.0f, EPSILON)) {
|
||||
leni = 1.0f/sqrtf(len);
|
||||
dst->x = q->x * leni;
|
||||
dst->y = q->y * leni;
|
||||
dst->z = q->z * leni;
|
||||
dst->w = acosf(q->w)*2.0f;
|
||||
} else {
|
||||
dst->x = 0.0f;
|
||||
dst->y = 0.0f;
|
||||
dst->z = 0.0f;
|
||||
dst->w = 0.0f;
|
||||
}
|
||||
}
|
||||
64
libobs/graphics/axisang.h
Normal file
64
libobs/graphics/axisang.h
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../util/c99defs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct quat;
|
||||
|
||||
struct axisang {
|
||||
union {
|
||||
struct {float x, y, z, w;};
|
||||
float ptr[4];
|
||||
};
|
||||
};
|
||||
|
||||
static inline void axisang_zero(struct axisang *dst)
|
||||
{
|
||||
dst->x = 0.0f;
|
||||
dst->y = 0.0f;
|
||||
dst->z = 0.0f;
|
||||
dst->w = 0.0f;
|
||||
}
|
||||
|
||||
static inline void axisang_copy(struct axisang *dst, struct axisang *aa)
|
||||
{
|
||||
dst->x = aa->x;
|
||||
dst->y = aa->y;
|
||||
dst->z = aa->z;
|
||||
dst->w = aa->w;
|
||||
}
|
||||
|
||||
static inline void axisang_set(struct axisang *dst, float x, float y, float z,
|
||||
float w)
|
||||
{
|
||||
dst->x = x;
|
||||
dst->y = y;
|
||||
dst->z = z;
|
||||
dst->w = w;
|
||||
}
|
||||
|
||||
EXPORT void axisang_from_quat(struct axisang *dst, const struct quat *q);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
3
libobs/graphics/basemath.hpp
Normal file
3
libobs/graphics/basemath.hpp
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
/* TODO: C++ math wrappers */
|
||||
321
libobs/graphics/bounds.c
Normal file
321
libobs/graphics/bounds.c
Normal file
|
|
@ -0,0 +1,321 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "bounds.h"
|
||||
#include "matrix3.h"
|
||||
#include "matrix4.h"
|
||||
#include "plane.h"
|
||||
|
||||
void bounds_move(struct bounds *dst, const struct bounds *b,
|
||||
const struct vec3 *v)
|
||||
{
|
||||
vec3_add(&dst->min, &b->min, v);
|
||||
vec3_add(&dst->max, &b->max, v);
|
||||
}
|
||||
|
||||
void bounds_scale(struct bounds *dst, const struct bounds *b,
|
||||
const struct vec3 *v)
|
||||
{
|
||||
vec3_mul(&dst->min, &b->min, v);
|
||||
vec3_mul(&dst->max, &b->max, v);
|
||||
}
|
||||
|
||||
void bounds_merge(struct bounds *dst, const struct bounds *b1,
|
||||
const struct bounds *b2)
|
||||
{
|
||||
vec3_min(&dst->min, &b1->min, &b2->min);
|
||||
vec3_max(&dst->max, &b1->max, &b2->max);
|
||||
}
|
||||
|
||||
void bounds_merge_point(struct bounds *dst, const struct bounds *b,
|
||||
const struct vec3 *v)
|
||||
{
|
||||
vec3_min(&dst->min, &b->min, v);
|
||||
vec3_max(&dst->max, &b->max, v);
|
||||
}
|
||||
|
||||
void bounds_get_point(struct vec3 *dst, const struct bounds *b,
|
||||
unsigned int i)
|
||||
{
|
||||
if (i > 8)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Note:
|
||||
* 0 = min.x,min.y,min.z
|
||||
* 1 = min.x,min.y,MAX.z
|
||||
* 2 = min.x,MAX.y,min.z
|
||||
* 3 = min.x,MAX.y,MAX.z
|
||||
* 4 = MAX.x,min.y,min.z
|
||||
* 5 = MAX.x,min.y,MAX.z
|
||||
|
||||
* 6 = MAX.x,MAX.y,min.z
|
||||
* 7 = MAX.x,MAX.y,MAX.z
|
||||
*/
|
||||
|
||||
if(i > 3) {dst->x = b->max.x; i -= 4;}
|
||||
else {dst->x = b->min.x;}
|
||||
|
||||
if(i > 1) {dst->y = b->max.y; i -= 2;}
|
||||
else {dst->y = b->min.y;}
|
||||
|
||||
dst->z = (i == 1) ? b->max.z : b->min.z;
|
||||
}
|
||||
|
||||
void bounds_get_center(struct vec3 *dst, const struct bounds *b)
|
||||
{
|
||||
vec3_sub(dst, &b->max, &b->min);
|
||||
vec3_mulf(dst, dst, 0.5f);
|
||||
vec3_add(dst, dst, &b->min);
|
||||
}
|
||||
|
||||
void bounds_transform(struct bounds *dst, const struct bounds *b,
|
||||
const struct matrix4 *m)
|
||||
{
|
||||
struct bounds temp;
|
||||
bool b_init = false;
|
||||
int i;
|
||||
|
||||
memset(&temp, 0, sizeof(temp));
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
struct vec3 p;
|
||||
bounds_get_point(&p, b, i);
|
||||
vec3_transform(&p, &p, m);
|
||||
|
||||
if (!b_init) {
|
||||
vec3_copy(&temp.min, &p);
|
||||
vec3_copy(&temp.max, &p);
|
||||
b_init = true;
|
||||
} else {
|
||||
if (p.x < temp.min.x)
|
||||
temp.min.x = p.x;
|
||||
else if (p.x > temp.max.x)
|
||||
temp.max.x = p.x;
|
||||
|
||||
if (p.y < temp.min.y)
|
||||
temp.min.y = p.y;
|
||||
else if (p.y > temp.max.y)
|
||||
temp.max.y = p.y;
|
||||
|
||||
if (p.z < temp.min.z)
|
||||
temp.min.z = p.z;
|
||||
else if (p.z > temp.max.z)
|
||||
temp.max.z = p.z;
|
||||
}
|
||||
}
|
||||
|
||||
bounds_copy(dst, &temp);
|
||||
}
|
||||
|
||||
void bounds_transform3x4(struct bounds *dst, const struct bounds *b,
|
||||
const struct matrix3 *m)
|
||||
{
|
||||
struct bounds temp;
|
||||
bool b_init = false;
|
||||
int i;
|
||||
|
||||
memset(&temp, 0, sizeof(temp));
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
struct vec3 p;
|
||||
bounds_get_point(&p, b, i);
|
||||
vec3_transform3x4(&p, &p, m);
|
||||
|
||||
if (!b_init) {
|
||||
vec3_copy(&temp.min, &p);
|
||||
vec3_copy(&temp.max, &p);
|
||||
b_init = true;
|
||||
} else {
|
||||
if (p.x < temp.min.x)
|
||||
temp.min.x = p.x;
|
||||
else if (p.x > temp.max.x)
|
||||
temp.max.x = p.x;
|
||||
|
||||
if (p.y < temp.min.y)
|
||||
temp.min.y = p.y;
|
||||
else if (p.y > temp.max.y)
|
||||
temp.max.y = p.y;
|
||||
|
||||
if (p.z < temp.min.z)
|
||||
temp.min.z = p.z;
|
||||
else if (p.z > temp.max.z)
|
||||
temp.max.z = p.z;
|
||||
}
|
||||
}
|
||||
|
||||
bounds_copy(dst, &temp);
|
||||
}
|
||||
|
||||
bool bounds_intersection_ray(const struct bounds *b, const struct vec3 *orig,
|
||||
const struct vec3 *dir, float *t)
|
||||
{
|
||||
float t_max = M_INFINITE;
|
||||
float t_min = -M_INFINITE;
|
||||
struct vec3 center, max_offset, box_offset;
|
||||
int i;
|
||||
|
||||
bounds_get_center(¢er, b);
|
||||
vec3_sub(&max_offset, &b->max, ¢er);
|
||||
vec3_sub(&box_offset, ¢er, orig);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
float e = box_offset.ptr[i];
|
||||
float f = dir->ptr[i];
|
||||
|
||||
if (fabsf(f) > 0.0f) {
|
||||
float fi = 1.0f/f;
|
||||
float t1 = (e+max_offset.ptr[i])*fi;
|
||||
float t2 = (e-max_offset.ptr[i])*fi;
|
||||
if (t1 > t2) {
|
||||
if (t2 > t_min) t_min = t2;
|
||||
if (t1 < t_max) t_max = t1;
|
||||
} else {
|
||||
if (t1 > t_min) t_min = t1;
|
||||
if (t2 < t_max) t_max = t2;
|
||||
}
|
||||
if (t_min > t_max)
|
||||
return false;
|
||||
if (t_max < 0.0f)
|
||||
return false;
|
||||
} else if ((-e - max_offset.ptr[i]) > 0.0f ||
|
||||
(-e + max_offset.ptr[i]) < 0.0f) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
*t = (t_min > 0.0f) ? t_min : t_max;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bounds_intersection_line(const struct bounds *b, const struct vec3 *p1,
|
||||
const struct vec3 *p2, float *t)
|
||||
{
|
||||
struct vec3 dir;
|
||||
float length;
|
||||
|
||||
vec3_sub(&dir, p2, p1);
|
||||
length = vec3_len(&dir);
|
||||
if (length <= TINY_EPSILON)
|
||||
return false;
|
||||
|
||||
vec3_mulf(&dir, &dir, 1.0f/length);
|
||||
|
||||
if (!bounds_intersection_ray(b, p1, &dir, t))
|
||||
return false;
|
||||
|
||||
*t /= length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bounds_plane_test(const struct bounds *b, const struct plane *p)
|
||||
{
|
||||
struct vec3 vmin, vmax;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (p->dir.ptr[i] >= 0.0f) {
|
||||
vmin.ptr[i] = b->min.ptr[i];
|
||||
vmax.ptr[i] = b->max.ptr[i];
|
||||
} else {
|
||||
vmin.ptr[i] = b->max.ptr[i];
|
||||
vmax.ptr[i] = b->min.ptr[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (vec3_plane_dist(&vmin, p) > 0.0f)
|
||||
return BOUNDS_OUTSIDE;
|
||||
|
||||
if (vec3_plane_dist(&vmax, p) >= 0.0f)
|
||||
return BOUNDS_PARTIAL;
|
||||
|
||||
return BOUNDS_INSIDE;
|
||||
}
|
||||
|
||||
bool bounds_under_plane(const struct bounds *b, const struct plane *p)
|
||||
{
|
||||
struct vec3 vmin;
|
||||
|
||||
vmin.x = (p->dir.x < 0.0f) ? b->max.x : b->min.x;
|
||||
vmin.y = (p->dir.y < 0.0f) ? b->max.y : b->min.y;
|
||||
vmin.z = (p->dir.z < 0.0f) ? b->max.z : b->min.z;
|
||||
|
||||
return (vec3_dot(&vmin, &p->dir) <= p->dist);
|
||||
}
|
||||
|
||||
bool bounds_intersects(const struct bounds *b, const struct bounds *test,
|
||||
float epsilon)
|
||||
{
|
||||
return ((b->min.x - test->max.x) <= epsilon) &&
|
||||
((test->min.x - b->max.x) <= epsilon) &&
|
||||
((b->min.y - test->max.y) <= epsilon) &&
|
||||
((test->min.y - b->max.y) <= epsilon) &&
|
||||
((b->min.z - test->max.z) <= epsilon) &&
|
||||
((test->min.z - b->max.z) <= epsilon);
|
||||
}
|
||||
|
||||
bool bounds_intersects_obb(const struct bounds *b, const struct bounds *test,
|
||||
const struct matrix4 *m, float epsilon)
|
||||
{
|
||||
struct bounds b_tr, test_tr;
|
||||
struct matrix4 m_inv;
|
||||
|
||||
matrix4_inv(&m_inv, m);
|
||||
|
||||
bounds_transform(&b_tr, b, m);
|
||||
bounds_transform(&test_tr, test, &m_inv);
|
||||
|
||||
return bounds_intersects(b, &test_tr, epsilon) &&
|
||||
bounds_intersects(&b_tr, test, epsilon);
|
||||
}
|
||||
|
||||
bool bounds_intersects_obb3x4(const struct bounds *b, const struct bounds *test,
|
||||
const struct matrix3 *m, float epsilon)
|
||||
{
|
||||
struct bounds b_tr, test_tr;
|
||||
struct matrix3 m_inv;
|
||||
|
||||
matrix3_transpose(&m_inv, m);
|
||||
|
||||
bounds_transform3x4(&b_tr, b, m);
|
||||
bounds_transform3x4(&test_tr, test, &m_inv);
|
||||
|
||||
return bounds_intersects(b, &test_tr, epsilon) &&
|
||||
bounds_intersects(&b_tr, test, epsilon);
|
||||
}
|
||||
|
||||
static inline float vec3or_offset_len(const struct bounds *b,
|
||||
const struct vec3 *v)
|
||||
{
|
||||
struct vec3 temp1, temp2;
|
||||
vec3_sub(&temp1, &b->max, &b->min);
|
||||
vec3_abs(&temp2, v);
|
||||
return vec3_dot(&temp1, &temp2);
|
||||
}
|
||||
|
||||
float bounds_min_dist(const struct bounds *b, const struct plane *p)
|
||||
{
|
||||
struct vec3 center;
|
||||
float vec_len = vec3or_offset_len(b, &p->dir) * 0.5f;
|
||||
float center_dist;
|
||||
|
||||
bounds_get_center(¢er, b);
|
||||
center_dist = vec3_plane_dist(¢er, p);
|
||||
|
||||
return p->dist + center_dist - vec_len;
|
||||
}
|
||||
137
libobs/graphics/bounds.h
Normal file
137
libobs/graphics/bounds.h
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "math-defs.h"
|
||||
#include "vec3.h"
|
||||
|
||||
/*
|
||||
* Axis Aligned Bounding Box
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define BOUNDS_MAX_X 1
|
||||
#define BOUNDS_MAX_Y 2
|
||||
#define BOUNDS_MAX_Z 4
|
||||
|
||||
#define BOUNDS_OUTSIDE 1
|
||||
#define BOUNDS_INSIDE 2
|
||||
#define BOUNDS_PARTIAL 3
|
||||
|
||||
struct bounds {
|
||||
struct vec3 min, max;
|
||||
};
|
||||
|
||||
static inline void bounds_zero(struct bounds *dst)
|
||||
{
|
||||
vec3_zero(&dst->min);
|
||||
vec3_zero(&dst->max);
|
||||
}
|
||||
|
||||
static inline void bounds_copy(struct bounds *dst, const struct bounds *b)
|
||||
{
|
||||
vec3_copy(&dst->min, &b->min);
|
||||
vec3_copy(&dst->max, &b->max);
|
||||
}
|
||||
|
||||
EXPORT void bounds_move(struct bounds *dst, const struct bounds *b,
|
||||
const struct vec3 *v);
|
||||
|
||||
EXPORT void bounds_scale(struct bounds *dst, const struct bounds *b,
|
||||
const struct vec3 *v);
|
||||
|
||||
EXPORT void bounds_merge(struct bounds *dst, const struct bounds *b1,
|
||||
const struct bounds *b2);
|
||||
EXPORT void bounds_merge_point(struct bounds *dst, const struct bounds *b,
|
||||
const struct vec3 *v);
|
||||
|
||||
EXPORT void bounds_get_point(struct vec3 *dst, const struct bounds *b,
|
||||
unsigned int i);
|
||||
EXPORT void bounds_get_center(struct vec3 *dst, const struct bounds *b);
|
||||
|
||||
/**
|
||||
* Note: transforms as OBB, then converts back to AABB, which can result in
|
||||
* the actual size becoming larger than it originally was.
|
||||
*/
|
||||
EXPORT void bounds_transform(struct bounds *dst, const struct bounds *b,
|
||||
const struct matrix4 *m);
|
||||
EXPORT void bounds_transform3x4(struct bounds *dst, const struct bounds *b,
|
||||
const struct matrix3 *m);
|
||||
|
||||
EXPORT bool bounds_intersection_ray(const struct bounds *b,
|
||||
const struct vec3 *orig, const struct vec3 *dir, float *t);
|
||||
EXPORT bool bounds_intersection_line(const struct bounds *b,
|
||||
const struct vec3 *p1, const struct vec3 *p2, float *t);
|
||||
|
||||
EXPORT bool bounds_plane_test(const struct bounds *b, const struct plane *p);
|
||||
EXPORT bool bounds_under_plane(const struct bounds *b,
|
||||
const struct plane *p);
|
||||
|
||||
static inline bool bounds_inside(const struct bounds *b,
|
||||
const struct bounds *test)
|
||||
{
|
||||
return test->min.x >= b->min.x &&
|
||||
test->min.y >= b->min.y &&
|
||||
test->min.z >= b->min.z &&
|
||||
test->max.x <= b->max.x &&
|
||||
test->max.y <= b->max.y &&
|
||||
test->max.z <= b->max.z;
|
||||
}
|
||||
|
||||
static inline bool bounds_vec3_inside(const struct bounds *b,
|
||||
const struct vec3 *v)
|
||||
{
|
||||
return v->x >= (b->min.x-EPSILON) &&
|
||||
v->x <= (b->max.x+EPSILON) &&
|
||||
v->y >= (b->min.y-EPSILON) &&
|
||||
v->y <= (b->max.y+EPSILON) &&
|
||||
v->z >= (b->min.z-EPSILON) &&
|
||||
v->z <= (b->max.z+EPSILON);
|
||||
}
|
||||
|
||||
EXPORT bool bounds_intersects(const struct bounds *b,
|
||||
const struct bounds *test, float epsilon);
|
||||
EXPORT bool bounds_intersects_obb(const struct bounds *b,
|
||||
const struct bounds *test, const struct matrix4 *m,
|
||||
float epsilon);
|
||||
EXPORT bool bounds_intersects_obb3x4(const struct bounds *b,
|
||||
const struct bounds *test, const struct matrix3 *m,
|
||||
float epsilon);
|
||||
|
||||
static inline bool bounds_intersects_ray(const struct bounds *b,
|
||||
const struct vec3 *orig, const struct vec3 *dir)
|
||||
{
|
||||
float t;
|
||||
return bounds_intersection_ray(b, orig, dir, &t);
|
||||
}
|
||||
|
||||
static inline bool bounds_intersects_line(const struct bounds *b,
|
||||
const struct vec3 *p1, const struct vec3 *p2)
|
||||
{
|
||||
float t;
|
||||
return bounds_intersection_line(b, p1, p2, &t);
|
||||
}
|
||||
|
||||
EXPORT float bounds_min_dist(const struct bounds *b, const struct plane *p);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
148
libobs/graphics/device-exports.h
Normal file
148
libobs/graphics/device-exports.h
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013-2014 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <util/c99defs.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
EXPORT const char *device_get_name(void);
|
||||
EXPORT int device_get_type(void);
|
||||
EXPORT bool device_enum_adapters(
|
||||
bool (*callback)(void *param, const char *name, uint32_t id),
|
||||
void *param);
|
||||
EXPORT const char *device_preprocessor_name(void);
|
||||
EXPORT int device_create(gs_device_t **device, uint32_t adapter);
|
||||
EXPORT void device_destroy(gs_device_t *device);
|
||||
EXPORT void device_enter_context(gs_device_t *device);
|
||||
EXPORT void device_leave_context(gs_device_t *device);
|
||||
EXPORT gs_swapchain_t *device_swapchain_create(gs_device_t *device,
|
||||
const struct gs_init_data *data);
|
||||
EXPORT void device_resize(gs_device_t *device, uint32_t x, uint32_t y);
|
||||
EXPORT void device_get_size(const gs_device_t *device, uint32_t *x, uint32_t *y);
|
||||
EXPORT uint32_t device_get_width(const gs_device_t *device);
|
||||
EXPORT uint32_t device_get_height(const gs_device_t *device);
|
||||
EXPORT gs_texture_t *device_texture_create(gs_device_t *device, uint32_t width,
|
||||
uint32_t height, enum gs_color_format color_format,
|
||||
uint32_t levels, const uint8_t **data, uint32_t flags);
|
||||
EXPORT gs_texture_t *device_cubetexture_create(gs_device_t *device,
|
||||
uint32_t size, enum gs_color_format color_format,
|
||||
uint32_t levels, const uint8_t **data, uint32_t flags);
|
||||
EXPORT gs_texture_t *device_voltexture_create(gs_device_t *device,
|
||||
uint32_t width, uint32_t height, uint32_t depth,
|
||||
enum gs_color_format color_format, uint32_t levels,
|
||||
const uint8_t **data, uint32_t flags);
|
||||
EXPORT gs_zstencil_t *device_zstencil_create(gs_device_t *device,
|
||||
uint32_t width, uint32_t height,
|
||||
enum gs_zstencil_format format);
|
||||
EXPORT gs_stagesurf_t *device_stagesurface_create(gs_device_t *device,
|
||||
uint32_t width, uint32_t height,
|
||||
enum gs_color_format color_format);
|
||||
EXPORT gs_samplerstate_t *device_samplerstate_create(gs_device_t *device,
|
||||
const struct gs_sampler_info *info);
|
||||
EXPORT gs_shader_t *device_vertexshader_create(gs_device_t *device,
|
||||
const char *shader, const char *file,
|
||||
char **error_string);
|
||||
EXPORT gs_shader_t *device_pixelshader_create(gs_device_t *device,
|
||||
const char *shader, const char *file,
|
||||
char **error_string);
|
||||
EXPORT gs_vertbuffer_t *device_vertexbuffer_create(gs_device_t *device,
|
||||
struct gs_vb_data *data, uint32_t flags);
|
||||
EXPORT gs_indexbuffer_t *device_indexbuffer_create(gs_device_t *device,
|
||||
enum gs_index_type type, void *indices, size_t num,
|
||||
uint32_t flags);
|
||||
EXPORT enum gs_texture_type device_get_texture_type(
|
||||
const gs_texture_t *texture);
|
||||
EXPORT void device_load_vertexbuffer(gs_device_t *device,
|
||||
gs_vertbuffer_t *vertbuffer);
|
||||
EXPORT void device_load_indexbuffer(gs_device_t *device,
|
||||
gs_indexbuffer_t *indexbuffer);
|
||||
EXPORT void device_load_texture(gs_device_t *device, gs_texture_t *tex,
|
||||
int unit);
|
||||
EXPORT void device_load_samplerstate(gs_device_t *device,
|
||||
gs_samplerstate_t *samplerstate, int unit);
|
||||
EXPORT void device_load_vertexshader(gs_device_t *device,
|
||||
gs_shader_t *vertshader);
|
||||
EXPORT void device_load_pixelshader(gs_device_t *device,
|
||||
gs_shader_t *pixelshader);
|
||||
EXPORT void device_load_default_samplerstate(gs_device_t *device, bool b_3d,
|
||||
int unit);
|
||||
EXPORT gs_shader_t *device_get_vertex_shader(const gs_device_t *device);
|
||||
EXPORT gs_shader_t *device_get_pixel_shader(const gs_device_t *device);
|
||||
EXPORT gs_texture_t *device_get_render_target(const gs_device_t *device);
|
||||
EXPORT gs_zstencil_t *device_get_zstencil_target(const gs_device_t *device);
|
||||
EXPORT void device_set_render_target(gs_device_t *device, gs_texture_t *tex,
|
||||
gs_zstencil_t *zstencil);
|
||||
EXPORT void device_set_cube_render_target(gs_device_t *device,
|
||||
gs_texture_t *cubetex,
|
||||
int side, gs_zstencil_t *zstencil);
|
||||
EXPORT void device_copy_texture(gs_device_t *device, gs_texture_t *dst,
|
||||
gs_texture_t *src);
|
||||
EXPORT void device_copy_texture_region(gs_device_t *device,
|
||||
gs_texture_t *dst, uint32_t dst_x, uint32_t dst_y,
|
||||
gs_texture_t *src, uint32_t src_x, uint32_t src_y,
|
||||
uint32_t src_w, uint32_t src_h);
|
||||
EXPORT void device_stage_texture(gs_device_t *device, gs_stagesurf_t *dst,
|
||||
gs_texture_t *src);
|
||||
EXPORT void device_begin_scene(gs_device_t *device);
|
||||
EXPORT void device_draw(gs_device_t *device, enum gs_draw_mode draw_mode,
|
||||
uint32_t start_vert, uint32_t num_verts);
|
||||
EXPORT void device_end_scene(gs_device_t *device);
|
||||
EXPORT void device_load_swapchain(gs_device_t *device,
|
||||
gs_swapchain_t *swapchain);
|
||||
EXPORT void device_clear(gs_device_t *device, uint32_t clear_flags,
|
||||
const struct vec4 *color, float depth, uint8_t stencil);
|
||||
EXPORT void device_present(gs_device_t *device);
|
||||
EXPORT void device_flush(gs_device_t *device);
|
||||
EXPORT void device_set_cull_mode(gs_device_t *device, enum gs_cull_mode mode);
|
||||
EXPORT enum gs_cull_mode device_get_cull_mode(const gs_device_t *device);
|
||||
EXPORT void device_enable_blending(gs_device_t *device, bool enable);
|
||||
EXPORT void device_enable_depth_test(gs_device_t *device, bool enable);
|
||||
EXPORT void device_enable_stencil_test(gs_device_t *device, bool enable);
|
||||
EXPORT void device_enable_stencil_write(gs_device_t *device, bool enable);
|
||||
EXPORT void device_enable_color(gs_device_t *device, bool red, bool green,
|
||||
bool blue, bool alpha);
|
||||
EXPORT void device_blend_function(gs_device_t *device, enum gs_blend_type src,
|
||||
enum gs_blend_type dest);
|
||||
EXPORT void device_blend_function_separate(gs_device_t *device,
|
||||
enum gs_blend_type src_c, enum gs_blend_type dest_c,
|
||||
enum gs_blend_type src_a, enum gs_blend_type dest_a);
|
||||
EXPORT void device_depth_function(gs_device_t *device, enum gs_depth_test test);
|
||||
EXPORT void device_stencil_function(gs_device_t *device,
|
||||
enum gs_stencil_side side, enum gs_depth_test test);
|
||||
EXPORT void device_stencil_op(gs_device_t *device, enum gs_stencil_side side,
|
||||
enum gs_stencil_op_type fail, enum gs_stencil_op_type zfail,
|
||||
enum gs_stencil_op_type zpass);
|
||||
EXPORT void device_set_viewport(gs_device_t *device, int x, int y, int width,
|
||||
int height);
|
||||
EXPORT void device_get_viewport(const gs_device_t *device,
|
||||
struct gs_rect *rect);
|
||||
EXPORT void device_set_scissor_rect(gs_device_t *device,
|
||||
const struct gs_rect *rect);
|
||||
EXPORT void device_ortho(gs_device_t *device, float left, float right,
|
||||
float top, float bottom, float znear, float zfar);
|
||||
EXPORT void device_frustum(gs_device_t *device, float left, float right,
|
||||
float top, float bottom, float znear, float zfar);
|
||||
EXPORT void device_projection_push(gs_device_t *device);
|
||||
EXPORT void device_projection_pop(gs_device_t *device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
1512
libobs/graphics/effect-parser.c
Normal file
1512
libobs/graphics/effect-parser.c
Normal file
File diff suppressed because it is too large
Load diff
283
libobs/graphics/effect-parser.h
Normal file
283
libobs/graphics/effect-parser.h
Normal file
|
|
@ -0,0 +1,283 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../util/darray.h"
|
||||
#include "../util/cf-parser.h"
|
||||
#include "graphics.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct dstr;
|
||||
|
||||
/*
|
||||
* The effect parser takes an effect file and converts it into individual
|
||||
* shaders for each technique's pass. It automatically writes all dependent
|
||||
* structures/functions/parameters to the shader and builds shader text for
|
||||
* each shader component of each pass.
|
||||
*/
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* effect parser var data */
|
||||
|
||||
struct ep_var {
|
||||
char *type, *name, *mapping;
|
||||
bool uniform;
|
||||
};
|
||||
|
||||
static inline void ep_var_init(struct ep_var *epv)
|
||||
{
|
||||
memset(epv, 0, sizeof(struct ep_var));
|
||||
}
|
||||
|
||||
static inline void ep_var_free(struct ep_var *epv)
|
||||
{
|
||||
bfree(epv->type);
|
||||
bfree(epv->name);
|
||||
bfree(epv->mapping);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* effect parser param data */
|
||||
|
||||
struct ep_param {
|
||||
char *type, *name;
|
||||
DARRAY(uint8_t) default_val;
|
||||
DARRAY(char*) properties;
|
||||
struct gs_effect_param *param;
|
||||
bool is_const, is_property, is_uniform, is_texture, written;
|
||||
int writeorder, array_count;
|
||||
};
|
||||
|
||||
extern void ep_param_writevar(struct dstr *dst, struct darray *use_params);
|
||||
|
||||
static inline void ep_param_init(struct ep_param *epp,
|
||||
char *type, char *name,
|
||||
bool is_property, bool is_const, bool is_uniform)
|
||||
{
|
||||
epp->type = type;
|
||||
epp->name = name;
|
||||
epp->is_property = is_property;
|
||||
epp->is_const = is_const;
|
||||
epp->is_uniform = is_uniform;
|
||||
epp->is_texture = (astrcmp_n(epp->type, "texture", 7) == 0);
|
||||
epp->written = false;
|
||||
epp->writeorder = false;
|
||||
epp->array_count = 0;
|
||||
da_init(epp->default_val);
|
||||
da_init(epp->properties);
|
||||
}
|
||||
|
||||
static inline void ep_param_free(struct ep_param *epp)
|
||||
{
|
||||
bfree(epp->type);
|
||||
bfree(epp->name);
|
||||
da_free(epp->default_val);
|
||||
da_free(epp->properties);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* effect parser struct data */
|
||||
|
||||
struct ep_struct {
|
||||
char *name;
|
||||
DARRAY(struct ep_var) vars; /* struct ep_var */
|
||||
bool written;
|
||||
};
|
||||
|
||||
static inline bool ep_struct_mapped(struct ep_struct *eps)
|
||||
{
|
||||
if (eps->vars.num > 0)
|
||||
return eps->vars.array[0].mapping != NULL;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void ep_struct_init(struct ep_struct *eps)
|
||||
{
|
||||
memset(eps, 0, sizeof(struct ep_struct));
|
||||
}
|
||||
|
||||
static inline void ep_struct_free(struct ep_struct *eps)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
bfree(eps->name);
|
||||
for (i = 0; i < eps->vars.num; i++)
|
||||
ep_var_free(eps->vars.array+i);
|
||||
da_free(eps->vars);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* effect parser sampler data */
|
||||
|
||||
struct ep_sampler {
|
||||
char *name;
|
||||
DARRAY(char*) states;
|
||||
DARRAY(char*) values;
|
||||
|
||||
bool written;
|
||||
};
|
||||
|
||||
static inline void ep_sampler_init(struct ep_sampler *eps)
|
||||
{
|
||||
memset(eps, 0, sizeof(struct ep_sampler));
|
||||
}
|
||||
|
||||
static inline void ep_sampler_free(struct ep_sampler *eps)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < eps->states.num; i++)
|
||||
bfree(eps->states.array[i]);
|
||||
for (i = 0; i < eps->values.num; i++)
|
||||
bfree(eps->values.array[i]);
|
||||
|
||||
bfree(eps->name);
|
||||
da_free(eps->states);
|
||||
da_free(eps->values);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* effect parser pass data */
|
||||
|
||||
struct ep_pass {
|
||||
char *name;
|
||||
DARRAY(struct cf_token) vertex_program;
|
||||
DARRAY(struct cf_token) fragment_program;
|
||||
struct gs_effect_pass *pass;
|
||||
};
|
||||
|
||||
static inline void ep_pass_init(struct ep_pass *epp)
|
||||
{
|
||||
memset(epp, 0, sizeof(struct ep_pass));
|
||||
}
|
||||
|
||||
static inline void ep_pass_free(struct ep_pass *epp)
|
||||
{
|
||||
bfree(epp->name);
|
||||
da_free(epp->vertex_program);
|
||||
da_free(epp->fragment_program);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* effect parser technique data */
|
||||
|
||||
struct ep_technique {
|
||||
char *name;
|
||||
DARRAY(struct ep_pass) passes; /* struct ep_pass */
|
||||
};
|
||||
|
||||
static inline void ep_technique_init(struct ep_technique *ept)
|
||||
{
|
||||
memset(ept, 0, sizeof(struct ep_technique));
|
||||
}
|
||||
|
||||
static inline void ep_technique_free(struct ep_technique *ept)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < ept->passes.num; i++)
|
||||
ep_pass_free(ept->passes.array+i);
|
||||
|
||||
bfree(ept->name);
|
||||
da_free(ept->passes);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* effect parser function data */
|
||||
|
||||
struct ep_func {
|
||||
char *name, *ret_type, *mapping;
|
||||
struct dstr contents;
|
||||
DARRAY(struct ep_var) param_vars;
|
||||
DARRAY(const char*) func_deps;
|
||||
DARRAY(const char*) struct_deps;
|
||||
DARRAY(const char*) param_deps;
|
||||
DARRAY(const char*) sampler_deps;
|
||||
bool written;
|
||||
};
|
||||
|
||||
static inline void ep_func_init(struct ep_func *epf, char *ret_type,
|
||||
char *name)
|
||||
{
|
||||
memset(epf, 0, sizeof(struct ep_func));
|
||||
epf->name = name;
|
||||
epf->ret_type = ret_type;
|
||||
}
|
||||
|
||||
static inline void ep_func_free(struct ep_func *epf)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < epf->param_vars.num; i++)
|
||||
ep_var_free(epf->param_vars.array+i);
|
||||
|
||||
bfree(epf->name);
|
||||
bfree(epf->ret_type);
|
||||
bfree(epf->mapping);
|
||||
dstr_free(&epf->contents);
|
||||
da_free(epf->param_vars);
|
||||
da_free(epf->func_deps);
|
||||
da_free(epf->struct_deps);
|
||||
da_free(epf->param_deps);
|
||||
da_free(epf->sampler_deps);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
struct effect_parser {
|
||||
gs_effect_t *effect;
|
||||
|
||||
DARRAY(struct ep_param) params;
|
||||
DARRAY(struct ep_struct) structs;
|
||||
DARRAY(struct ep_func) funcs;
|
||||
DARRAY(struct ep_sampler) samplers;
|
||||
DARRAY(struct ep_technique) techniques;
|
||||
|
||||
/* internal vars */
|
||||
DARRAY(struct cf_lexer) files;
|
||||
DARRAY(struct cf_token) tokens;
|
||||
struct gs_effect_pass *cur_pass;
|
||||
|
||||
struct cf_parser cfp;
|
||||
};
|
||||
|
||||
static inline void ep_init(struct effect_parser *ep)
|
||||
{
|
||||
da_init(ep->params);
|
||||
da_init(ep->structs);
|
||||
da_init(ep->funcs);
|
||||
da_init(ep->samplers);
|
||||
da_init(ep->techniques);
|
||||
da_init(ep->files);
|
||||
da_init(ep->tokens);
|
||||
|
||||
ep->cur_pass = NULL;
|
||||
cf_parser_init(&ep->cfp);
|
||||
}
|
||||
|
||||
extern void ep_free(struct effect_parser *ep);
|
||||
|
||||
extern bool ep_parse(struct effect_parser *ep, gs_effect_t *effect,
|
||||
const char *effect_string, const char *file);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
380
libobs/graphics/effect.c
Normal file
380
libobs/graphics/effect.c
Normal file
|
|
@ -0,0 +1,380 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "effect.h"
|
||||
#include "graphics-internal.h"
|
||||
#include "vec2.h"
|
||||
#include "vec3.h"
|
||||
#include "vec4.h"
|
||||
|
||||
void gs_effect_actually_destroy(gs_effect_t *effect)
|
||||
{
|
||||
effect_free(effect);
|
||||
bfree(effect);
|
||||
}
|
||||
|
||||
void gs_effect_destroy(gs_effect_t *effect)
|
||||
{
|
||||
if (effect) {
|
||||
if (!effect->cached)
|
||||
gs_effect_actually_destroy(effect);
|
||||
}
|
||||
}
|
||||
|
||||
gs_technique_t *gs_effect_get_technique(const gs_effect_t *effect,
|
||||
const char *name)
|
||||
{
|
||||
if (!effect) return NULL;
|
||||
|
||||
for (size_t i = 0; i < effect->techniques.num; i++) {
|
||||
struct gs_effect_technique *tech = effect->techniques.array+i;
|
||||
if (strcmp(tech->name, name) == 0)
|
||||
return tech;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gs_technique_t *gs_effect_get_current_technique(const gs_effect_t *effect)
|
||||
{
|
||||
if (!effect) return NULL;
|
||||
|
||||
return effect->cur_technique;
|
||||
}
|
||||
|
||||
bool gs_effect_loop(gs_effect_t *effect, const char *name)
|
||||
{
|
||||
if (!effect) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!effect->looping) {
|
||||
gs_technique_t *tech;
|
||||
|
||||
if (!!gs_get_effect()) {
|
||||
blog(LOG_WARNING, "gs_effect_loop: An effect is "
|
||||
"already active");
|
||||
return false;
|
||||
}
|
||||
|
||||
tech = gs_effect_get_technique(effect, name);
|
||||
if (!tech) {
|
||||
blog(LOG_WARNING, "gs_effect_loop: Technique '%s' "
|
||||
"not found.", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
gs_technique_begin(tech);
|
||||
|
||||
effect->looping = true;
|
||||
} else {
|
||||
gs_technique_end_pass(effect->cur_technique);
|
||||
}
|
||||
|
||||
if (!gs_technique_begin_pass(effect->cur_technique,
|
||||
effect->loop_pass++)) {
|
||||
gs_technique_end(effect->cur_technique);
|
||||
effect->looping = false;
|
||||
effect->loop_pass = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t gs_technique_begin(gs_technique_t *tech)
|
||||
{
|
||||
if (!tech) return 0;
|
||||
|
||||
tech->effect->cur_technique = tech;
|
||||
tech->effect->graphics->cur_effect = tech->effect;
|
||||
|
||||
return tech->passes.num;
|
||||
}
|
||||
|
||||
void gs_technique_end(gs_technique_t *tech)
|
||||
{
|
||||
if (!tech) return;
|
||||
|
||||
struct gs_effect *effect = tech->effect;
|
||||
struct gs_effect_param *params = effect->params.array;
|
||||
size_t i;
|
||||
|
||||
gs_load_vertexshader(NULL);
|
||||
gs_load_pixelshader(NULL);
|
||||
|
||||
tech->effect->cur_technique = NULL;
|
||||
tech->effect->graphics->cur_effect = NULL;
|
||||
|
||||
for (i = 0; i < effect->params.num; i++) {
|
||||
struct gs_effect_param *param = params+i;
|
||||
|
||||
da_free(param->cur_val);
|
||||
param->changed = false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void reset_params(struct darray *shaderparams)
|
||||
{
|
||||
struct pass_shaderparam *params = shaderparams->array;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < shaderparams->num; i++)
|
||||
params[i].eparam->changed = false;
|
||||
}
|
||||
|
||||
static void upload_shader_params(struct darray *pass_params, bool changed_only)
|
||||
{
|
||||
struct pass_shaderparam *params = pass_params->array;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < pass_params->num; i++) {
|
||||
struct pass_shaderparam *param = params+i;
|
||||
struct gs_effect_param *eparam = param->eparam;
|
||||
gs_sparam_t *sparam = param->sparam;
|
||||
|
||||
if (changed_only && !eparam->changed)
|
||||
continue;
|
||||
|
||||
if (!eparam->cur_val.num) {
|
||||
if (eparam->default_val.num)
|
||||
da_copy(eparam->cur_val, eparam->default_val);
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
gs_shader_set_val(sparam, eparam->cur_val.array,
|
||||
eparam->cur_val.num);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void upload_parameters(struct gs_effect *effect,
|
||||
bool changed_only)
|
||||
{
|
||||
struct darray *vshader_params, *pshader_params;
|
||||
|
||||
if (!effect->cur_pass)
|
||||
return;
|
||||
|
||||
vshader_params = &effect->cur_pass->vertshader_params.da;
|
||||
pshader_params = &effect->cur_pass->pixelshader_params.da;
|
||||
|
||||
upload_shader_params(vshader_params, changed_only);
|
||||
upload_shader_params(pshader_params, changed_only);
|
||||
reset_params(vshader_params);
|
||||
reset_params(pshader_params);
|
||||
}
|
||||
|
||||
void gs_effect_update_params(gs_effect_t *effect)
|
||||
{
|
||||
if (effect)
|
||||
upload_parameters(effect, true);
|
||||
}
|
||||
|
||||
bool gs_technique_begin_pass(gs_technique_t *tech, size_t idx)
|
||||
{
|
||||
struct gs_effect_pass *passes;
|
||||
struct gs_effect_pass *cur_pass;
|
||||
|
||||
if (!tech || idx >= tech->passes.num)
|
||||
return false;
|
||||
|
||||
passes = tech->passes.array;
|
||||
cur_pass = passes+idx;
|
||||
|
||||
tech->effect->cur_pass = cur_pass;
|
||||
gs_load_vertexshader(cur_pass->vertshader);
|
||||
gs_load_pixelshader(cur_pass->pixelshader);
|
||||
upload_parameters(tech->effect, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gs_technique_begin_pass_by_name(gs_technique_t *tech,
|
||||
const char *name)
|
||||
{
|
||||
if (!tech)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < tech->passes.num; i++) {
|
||||
struct gs_effect_pass *pass = tech->passes.array+i;
|
||||
if (strcmp(pass->name, name) == 0) {
|
||||
gs_technique_begin_pass(tech, i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void clear_tex_params(struct darray *in_params)
|
||||
{
|
||||
struct pass_shaderparam *params = in_params->array;
|
||||
|
||||
for (size_t i = 0; i < in_params->num; i++) {
|
||||
struct pass_shaderparam *param = params+i;
|
||||
struct gs_shader_param_info info;
|
||||
|
||||
gs_shader_get_param_info(param->sparam, &info);
|
||||
if (info.type == GS_SHADER_PARAM_TEXTURE)
|
||||
gs_shader_set_texture(param->sparam, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void gs_technique_end_pass(gs_technique_t *tech)
|
||||
{
|
||||
if (!tech) return;
|
||||
|
||||
struct gs_effect_pass *pass = tech->effect->cur_pass;
|
||||
if (!pass)
|
||||
return;
|
||||
|
||||
clear_tex_params(&pass->vertshader_params.da);
|
||||
clear_tex_params(&pass->pixelshader_params.da);
|
||||
tech->effect->cur_pass = NULL;
|
||||
}
|
||||
|
||||
size_t gs_effect_get_num_params(const gs_effect_t *effect)
|
||||
{
|
||||
return effect ? effect->params.num : 0;
|
||||
}
|
||||
|
||||
gs_eparam_t *gs_effect_get_param_by_idx(const gs_effect_t *effect, size_t param)
|
||||
{
|
||||
if (!effect) return NULL;
|
||||
|
||||
struct gs_effect_param *params = effect->params.array;
|
||||
if (param >= effect->params.num)
|
||||
return NULL;
|
||||
|
||||
return params+param;
|
||||
}
|
||||
|
||||
gs_eparam_t *gs_effect_get_param_by_name(const gs_effect_t *effect,
|
||||
const char *name)
|
||||
{
|
||||
if (!effect) return NULL;
|
||||
|
||||
struct gs_effect_param *params = effect->params.array;
|
||||
|
||||
for (size_t i = 0; i < effect->params.num; i++) {
|
||||
struct gs_effect_param *param = params+i;
|
||||
|
||||
if (strcmp(param->name, name) == 0)
|
||||
return param;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gs_eparam_t *gs_effect_get_viewproj_matrix(const gs_effect_t *effect)
|
||||
{
|
||||
return effect ? effect->view_proj : NULL;
|
||||
}
|
||||
|
||||
gs_eparam_t *gs_effect_get_world_matrix(const gs_effect_t *effect)
|
||||
{
|
||||
return effect ? effect->world : NULL;
|
||||
}
|
||||
|
||||
void gs_effect_get_param_info(const gs_eparam_t *param,
|
||||
struct gs_effect_param_info *info)
|
||||
{
|
||||
if (!param)
|
||||
return;
|
||||
|
||||
info->name = param->name;
|
||||
info->type = param->type;
|
||||
}
|
||||
|
||||
static inline void effect_setval_inline(gs_eparam_t *param,
|
||||
const void *data, size_t size)
|
||||
{
|
||||
bool size_changed;
|
||||
|
||||
if (!param) {
|
||||
blog(LOG_ERROR, "effect_setval_inline: invalid param");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
blog(LOG_ERROR, "effect_setval_inline: invalid data");
|
||||
return;
|
||||
}
|
||||
|
||||
size_changed = param->cur_val.num != size;
|
||||
|
||||
if (size_changed)
|
||||
da_resize(param->cur_val, size);
|
||||
|
||||
if (size_changed || memcmp(param->cur_val.array, data, size) != 0) {
|
||||
memcpy(param->cur_val.array, data, size);
|
||||
param->changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void gs_effect_set_bool(gs_eparam_t *param, bool val)
|
||||
{
|
||||
int b_val = (int)val;
|
||||
effect_setval_inline(param, &b_val, sizeof(int));
|
||||
}
|
||||
|
||||
void gs_effect_set_float(gs_eparam_t *param, float val)
|
||||
{
|
||||
effect_setval_inline(param, &val, sizeof(float));
|
||||
}
|
||||
|
||||
void gs_effect_set_int(gs_eparam_t *param, int val)
|
||||
{
|
||||
effect_setval_inline(param, &val, sizeof(int));
|
||||
}
|
||||
|
||||
void gs_effect_set_matrix4(gs_eparam_t *param, const struct matrix4 *val)
|
||||
{
|
||||
effect_setval_inline(param, val, sizeof(struct matrix4));
|
||||
}
|
||||
|
||||
void gs_effect_set_vec2(gs_eparam_t *param, const struct vec2 *val)
|
||||
{
|
||||
effect_setval_inline(param, val, sizeof(struct vec2));
|
||||
}
|
||||
|
||||
void gs_effect_set_vec3(gs_eparam_t *param, const struct vec3 *val)
|
||||
{
|
||||
effect_setval_inline(param, val, sizeof(float) * 3);
|
||||
}
|
||||
|
||||
void gs_effect_set_vec4(gs_eparam_t *param, const struct vec4 *val)
|
||||
{
|
||||
effect_setval_inline(param, val, sizeof(struct vec4));
|
||||
}
|
||||
|
||||
void gs_effect_set_texture(gs_eparam_t *param, gs_texture_t *val)
|
||||
{
|
||||
effect_setval_inline(param, &val, sizeof(gs_texture_t*));
|
||||
}
|
||||
|
||||
void gs_effect_set_val(gs_eparam_t *param, const void *val, size_t size)
|
||||
{
|
||||
effect_setval_inline(param, val, size);
|
||||
}
|
||||
|
||||
void gs_effect_set_default(gs_eparam_t *param)
|
||||
{
|
||||
effect_setval_inline(param, param->default_val.array,
|
||||
param->default_val.num);
|
||||
}
|
||||
187
libobs/graphics/effect.h
Normal file
187
libobs/graphics/effect.h
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "effect-parser.h"
|
||||
#include "graphics.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Effects introduce a means of bundling together shader text into one
|
||||
* file with shared functions and parameters. This is done because often
|
||||
* shaders must be duplicated when you need to alter minor aspects of the code
|
||||
* that cannot be done via constants. Effects allow developers to easily
|
||||
* switch shaders and set constants that can be used between shaders.
|
||||
*
|
||||
* Effects are built via the effect parser, and shaders are automatically
|
||||
* generated for each technique's pass.
|
||||
*/
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
enum effect_section {
|
||||
EFFECT_PARAM,
|
||||
EFFECT_TECHNIQUE,
|
||||
EFFECT_SAMPLER,
|
||||
EFFECT_PASS
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
struct gs_effect_param {
|
||||
char *name;
|
||||
enum effect_section section;
|
||||
|
||||
enum gs_shader_param_type type;
|
||||
|
||||
bool changed;
|
||||
DARRAY(uint8_t) cur_val;
|
||||
DARRAY(uint8_t) default_val;
|
||||
|
||||
gs_effect_t *effect;
|
||||
|
||||
/*char *full_name;
|
||||
float scroller_min, scroller_max, scroller_inc, scroller_mul;*/
|
||||
};
|
||||
|
||||
static inline void effect_param_init(struct gs_effect_param *param)
|
||||
{
|
||||
memset(param, 0, sizeof(struct gs_effect_param));
|
||||
}
|
||||
|
||||
static inline void effect_param_free(struct gs_effect_param *param)
|
||||
{
|
||||
bfree(param->name);
|
||||
//bfree(param->full_name);
|
||||
da_free(param->cur_val);
|
||||
da_free(param->default_val);
|
||||
}
|
||||
|
||||
EXPORT void effect_param_parse_property(gs_eparam_t *param,
|
||||
const char *property);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
struct pass_shaderparam {
|
||||
struct gs_effect_param *eparam;
|
||||
gs_sparam_t *sparam;
|
||||
};
|
||||
|
||||
struct gs_effect_pass {
|
||||
char *name;
|
||||
enum effect_section section;
|
||||
|
||||
gs_shader_t *vertshader;
|
||||
gs_shader_t *pixelshader;
|
||||
DARRAY(struct pass_shaderparam) vertshader_params;
|
||||
DARRAY(struct pass_shaderparam) pixelshader_params;
|
||||
};
|
||||
|
||||
static inline void effect_pass_init(struct gs_effect_pass *pass)
|
||||
{
|
||||
memset(pass, 0, sizeof(struct gs_effect_pass));
|
||||
}
|
||||
|
||||
static inline void effect_pass_free(struct gs_effect_pass *pass)
|
||||
{
|
||||
bfree(pass->name);
|
||||
da_free(pass->vertshader_params);
|
||||
da_free(pass->pixelshader_params);
|
||||
gs_shader_destroy(pass->vertshader);
|
||||
gs_shader_destroy(pass->pixelshader);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
struct gs_effect_technique {
|
||||
char *name;
|
||||
enum effect_section section;
|
||||
struct gs_effect *effect;
|
||||
|
||||
DARRAY(struct gs_effect_pass) passes;
|
||||
};
|
||||
|
||||
static inline void effect_technique_init(struct gs_effect_technique *t)
|
||||
{
|
||||
memset(t, 0, sizeof(struct gs_effect_technique));
|
||||
}
|
||||
|
||||
static inline void effect_technique_free(struct gs_effect_technique *t)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < t->passes.num; i++)
|
||||
effect_pass_free(t->passes.array+i);
|
||||
da_free(t->passes);
|
||||
bfree(t->name);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
struct gs_effect {
|
||||
bool processing;
|
||||
bool cached;
|
||||
char *effect_path, *effect_dir;
|
||||
|
||||
DARRAY(struct gs_effect_param) params;
|
||||
DARRAY(struct gs_effect_technique) techniques;
|
||||
|
||||
struct gs_effect_technique *cur_technique;
|
||||
struct gs_effect_pass *cur_pass;
|
||||
|
||||
gs_eparam_t *view_proj, *world, *scale;
|
||||
graphics_t *graphics;
|
||||
|
||||
struct gs_effect *next;
|
||||
|
||||
size_t loop_pass;
|
||||
bool looping;
|
||||
};
|
||||
|
||||
static inline void effect_init(gs_effect_t *effect)
|
||||
{
|
||||
memset(effect, 0, sizeof(struct gs_effect));
|
||||
}
|
||||
|
||||
static inline void effect_free(gs_effect_t *effect)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < effect->params.num; i++)
|
||||
effect_param_free(effect->params.array+i);
|
||||
for (i = 0; i < effect->techniques.num; i++)
|
||||
effect_technique_free(effect->techniques.array+i);
|
||||
|
||||
da_free(effect->params);
|
||||
da_free(effect->techniques);
|
||||
|
||||
bfree(effect->effect_path);
|
||||
bfree(effect->effect_dir);
|
||||
effect->effect_path = NULL;
|
||||
effect->effect_dir = NULL;
|
||||
}
|
||||
|
||||
EXPORT void effect_upload_params(gs_effect_t *effect, bool changed_only);
|
||||
EXPORT void effect_upload_shader_params(gs_effect_t *effect,
|
||||
gs_shader_t *shader, struct darray *pass_params,
|
||||
bool changed_only);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
227
libobs/graphics/graphics-ffmpeg.c
Normal file
227
libobs/graphics/graphics-ffmpeg.c
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
#include "graphics.h"
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libswscale/swscale.h>
|
||||
|
||||
#include "../obs-ffmpeg-compat.h"
|
||||
|
||||
struct ffmpeg_image {
|
||||
const char *file;
|
||||
AVFormatContext *fmt_ctx;
|
||||
AVCodecContext *decoder_ctx;
|
||||
AVCodec *decoder;
|
||||
AVStream *stream;
|
||||
int stream_idx;
|
||||
|
||||
int cx, cy;
|
||||
enum AVPixelFormat format;
|
||||
};
|
||||
|
||||
static bool ffmpeg_image_open_decoder_context(struct ffmpeg_image *info)
|
||||
{
|
||||
int ret = av_find_best_stream(info->fmt_ctx, AVMEDIA_TYPE_VIDEO,
|
||||
-1, 1, NULL, 0);
|
||||
if (ret < 0) {
|
||||
blog(LOG_WARNING, "Couldn't find video stream in file '%s': %s",
|
||||
info->file, av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
info->stream_idx = ret;
|
||||
info->stream = info->fmt_ctx->streams[ret];
|
||||
info->decoder_ctx = info->stream->codec;
|
||||
info->decoder = avcodec_find_decoder(info->decoder_ctx->codec_id);
|
||||
|
||||
if (!info->decoder) {
|
||||
blog(LOG_WARNING, "Failed to find decoder for file '%s'",
|
||||
info->file);
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = avcodec_open2(info->decoder_ctx, info->decoder, NULL);
|
||||
if (ret < 0) {
|
||||
blog(LOG_WARNING, "Failed to open video codec for file '%s': "
|
||||
"%s", info->file, av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ffmpeg_image_free(struct ffmpeg_image *info)
|
||||
{
|
||||
avcodec_close(info->decoder_ctx);
|
||||
avformat_close_input(&info->fmt_ctx);
|
||||
}
|
||||
|
||||
static bool ffmpeg_image_init(struct ffmpeg_image *info, const char *file)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!file || !*file)
|
||||
return false;
|
||||
|
||||
memset(info, 0, sizeof(struct ffmpeg_image));
|
||||
info->file = file;
|
||||
info->stream_idx = -1;
|
||||
|
||||
ret = avformat_open_input(&info->fmt_ctx, file, NULL, NULL);
|
||||
if (ret < 0) {
|
||||
blog(LOG_WARNING, "Failed to open file '%s': %s",
|
||||
info->file, av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = avformat_find_stream_info(info->fmt_ctx, NULL);
|
||||
if (ret < 0) {
|
||||
blog(LOG_WARNING, "Could not find stream info for file '%s':"
|
||||
" %s", info->file, av_err2str(ret));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!ffmpeg_image_open_decoder_context(info))
|
||||
goto fail;
|
||||
|
||||
info->cx = info->decoder_ctx->width;
|
||||
info->cy = info->decoder_ctx->height;
|
||||
info->format = info->decoder_ctx->pix_fmt;
|
||||
return true;
|
||||
|
||||
fail:
|
||||
ffmpeg_image_free(info);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ffmpeg_image_reformat_frame(struct ffmpeg_image *info,
|
||||
AVFrame *frame, uint8_t *out, int linesize)
|
||||
{
|
||||
struct SwsContext *sws_ctx = NULL;
|
||||
int ret = 0;
|
||||
|
||||
if (info->format == AV_PIX_FMT_RGBA ||
|
||||
info->format == AV_PIX_FMT_BGRA ||
|
||||
info->format == AV_PIX_FMT_BGR0) {
|
||||
|
||||
if (linesize != frame->linesize[0]) {
|
||||
int min_line = linesize < frame->linesize[0] ?
|
||||
linesize : frame->linesize[0];
|
||||
|
||||
for (int y = 0; y < info->cy; y++)
|
||||
memcpy(out + y * linesize,
|
||||
frame->data[0] + y * frame->linesize[0],
|
||||
min_line);
|
||||
} else {
|
||||
memcpy(out, frame->data[0], linesize * info->cy);
|
||||
}
|
||||
|
||||
} else {
|
||||
sws_ctx = sws_getContext(info->cx, info->cy, info->format,
|
||||
info->cx, info->cy, AV_PIX_FMT_BGRA,
|
||||
SWS_POINT, NULL, NULL, NULL);
|
||||
if (!sws_ctx) {
|
||||
blog(LOG_WARNING, "Failed to create scale context "
|
||||
"for '%s'", info->file);
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = sws_scale(sws_ctx, (const uint8_t *const*)frame->data,
|
||||
frame->linesize, 0, info->cy, &out, &linesize);
|
||||
sws_freeContext(sws_ctx);
|
||||
|
||||
if (ret < 0) {
|
||||
blog(LOG_WARNING, "sws_scale failed for '%s': %s",
|
||||
info->file, av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
info->format = AV_PIX_FMT_BGRA;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ffmpeg_image_decode(struct ffmpeg_image *info, uint8_t *out,
|
||||
int linesize)
|
||||
{
|
||||
AVPacket packet = {0};
|
||||
bool success = false;
|
||||
AVFrame *frame = av_frame_alloc();
|
||||
int got_frame = 0;
|
||||
int ret;
|
||||
|
||||
if (!frame) {
|
||||
blog(LOG_WARNING, "Failed to create frame data for '%s'",
|
||||
info->file);
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = av_read_frame(info->fmt_ctx, &packet);
|
||||
if (ret < 0) {
|
||||
blog(LOG_WARNING, "Failed to read image frame from '%s': %s",
|
||||
info->file, av_err2str(ret));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
while (!got_frame) {
|
||||
ret = avcodec_decode_video2(info->decoder_ctx, frame,
|
||||
&got_frame, &packet);
|
||||
if (ret < 0) {
|
||||
blog(LOG_WARNING, "Failed to decode frame for '%s': %s",
|
||||
info->file, av_err2str(ret));
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
success = ffmpeg_image_reformat_frame(info, frame, out, linesize);
|
||||
|
||||
fail:
|
||||
av_free_packet(&packet);
|
||||
av_frame_free(&frame);
|
||||
return success;
|
||||
}
|
||||
|
||||
void gs_init_image_deps(void)
|
||||
{
|
||||
av_register_all();
|
||||
}
|
||||
|
||||
void gs_free_image_deps(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline enum gs_color_format convert_format(enum AVPixelFormat format)
|
||||
{
|
||||
switch ((int)format) {
|
||||
case AV_PIX_FMT_RGBA: return GS_RGBA;
|
||||
case AV_PIX_FMT_BGRA: return GS_BGRA;
|
||||
case AV_PIX_FMT_BGR0: return GS_BGRX;
|
||||
}
|
||||
|
||||
return GS_BGRX;
|
||||
}
|
||||
|
||||
uint8_t *gs_create_texture_file_data(const char *file,
|
||||
enum gs_color_format *format,
|
||||
uint32_t *cx_out, uint32_t *cy_out)
|
||||
{
|
||||
struct ffmpeg_image image;
|
||||
uint8_t *data = NULL;
|
||||
|
||||
if (ffmpeg_image_init(&image, file)) {
|
||||
data = bmalloc(image.cx * image.cy * 4);
|
||||
|
||||
if (ffmpeg_image_decode(&image, data, image.cx * 4)) {
|
||||
*format = convert_format(image.format);
|
||||
*cx_out = (uint32_t)image.cx;
|
||||
*cy_out = (uint32_t)image.cy;
|
||||
} else {
|
||||
bfree(data);
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
ffmpeg_image_free(&image);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
192
libobs/graphics/graphics-imports.c
Normal file
192
libobs/graphics/graphics-imports.c
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "../util/base.h"
|
||||
#include "../util/dstr.h"
|
||||
#include "../util/platform.h"
|
||||
#include "graphics-internal.h"
|
||||
|
||||
#define GRAPHICS_IMPORT(func) \
|
||||
do { \
|
||||
exports->func = os_dlsym(module, #func); \
|
||||
if (!exports->func) { \
|
||||
success = false; \
|
||||
blog(LOG_ERROR, "Could not load function '%s' from " \
|
||||
"module '%s'", #func, module_name); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define GRAPHICS_IMPORT_OPTIONAL(func) \
|
||||
do { \
|
||||
exports->func = os_dlsym(module, #func); \
|
||||
} while (false)
|
||||
|
||||
bool load_graphics_imports(struct gs_exports *exports, void *module,
|
||||
const char *module_name)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
GRAPHICS_IMPORT(device_get_name);
|
||||
GRAPHICS_IMPORT(device_get_type);
|
||||
GRAPHICS_IMPORT_OPTIONAL(device_enum_adapters);
|
||||
GRAPHICS_IMPORT(device_preprocessor_name);
|
||||
GRAPHICS_IMPORT(device_create);
|
||||
GRAPHICS_IMPORT(device_destroy);
|
||||
GRAPHICS_IMPORT(device_enter_context);
|
||||
GRAPHICS_IMPORT(device_leave_context);
|
||||
GRAPHICS_IMPORT(device_swapchain_create);
|
||||
GRAPHICS_IMPORT(device_resize);
|
||||
GRAPHICS_IMPORT(device_get_size);
|
||||
GRAPHICS_IMPORT(device_get_width);
|
||||
GRAPHICS_IMPORT(device_get_height);
|
||||
GRAPHICS_IMPORT(device_texture_create);
|
||||
GRAPHICS_IMPORT(device_cubetexture_create);
|
||||
GRAPHICS_IMPORT(device_voltexture_create);
|
||||
GRAPHICS_IMPORT(device_zstencil_create);
|
||||
GRAPHICS_IMPORT(device_stagesurface_create);
|
||||
GRAPHICS_IMPORT(device_samplerstate_create);
|
||||
GRAPHICS_IMPORT(device_vertexshader_create);
|
||||
GRAPHICS_IMPORT(device_pixelshader_create);
|
||||
GRAPHICS_IMPORT(device_vertexbuffer_create);
|
||||
GRAPHICS_IMPORT(device_indexbuffer_create);
|
||||
GRAPHICS_IMPORT(device_get_texture_type);
|
||||
GRAPHICS_IMPORT(device_load_vertexbuffer);
|
||||
GRAPHICS_IMPORT(device_load_indexbuffer);
|
||||
GRAPHICS_IMPORT(device_load_texture);
|
||||
GRAPHICS_IMPORT(device_load_samplerstate);
|
||||
GRAPHICS_IMPORT(device_load_vertexshader);
|
||||
GRAPHICS_IMPORT(device_load_pixelshader);
|
||||
GRAPHICS_IMPORT(device_load_default_samplerstate);
|
||||
GRAPHICS_IMPORT(device_get_vertex_shader);
|
||||
GRAPHICS_IMPORT(device_get_pixel_shader);
|
||||
GRAPHICS_IMPORT(device_get_render_target);
|
||||
GRAPHICS_IMPORT(device_get_zstencil_target);
|
||||
GRAPHICS_IMPORT(device_set_render_target);
|
||||
GRAPHICS_IMPORT(device_set_cube_render_target);
|
||||
GRAPHICS_IMPORT(device_copy_texture_region);
|
||||
GRAPHICS_IMPORT(device_copy_texture);
|
||||
GRAPHICS_IMPORT(device_stage_texture);
|
||||
GRAPHICS_IMPORT(device_begin_scene);
|
||||
GRAPHICS_IMPORT(device_draw);
|
||||
GRAPHICS_IMPORT(device_load_swapchain);
|
||||
GRAPHICS_IMPORT(device_end_scene);
|
||||
GRAPHICS_IMPORT(device_clear);
|
||||
GRAPHICS_IMPORT(device_present);
|
||||
GRAPHICS_IMPORT(device_flush);
|
||||
GRAPHICS_IMPORT(device_set_cull_mode);
|
||||
GRAPHICS_IMPORT(device_get_cull_mode);
|
||||
GRAPHICS_IMPORT(device_enable_blending);
|
||||
GRAPHICS_IMPORT(device_enable_depth_test);
|
||||
GRAPHICS_IMPORT(device_enable_stencil_test);
|
||||
GRAPHICS_IMPORT(device_enable_stencil_write);
|
||||
GRAPHICS_IMPORT(device_enable_color);
|
||||
GRAPHICS_IMPORT(device_blend_function);
|
||||
GRAPHICS_IMPORT(device_blend_function_separate);
|
||||
GRAPHICS_IMPORT(device_depth_function);
|
||||
GRAPHICS_IMPORT(device_stencil_function);
|
||||
GRAPHICS_IMPORT(device_stencil_op);
|
||||
GRAPHICS_IMPORT(device_set_viewport);
|
||||
GRAPHICS_IMPORT(device_get_viewport);
|
||||
GRAPHICS_IMPORT(device_set_scissor_rect);
|
||||
GRAPHICS_IMPORT(device_ortho);
|
||||
GRAPHICS_IMPORT(device_frustum);
|
||||
GRAPHICS_IMPORT(device_projection_push);
|
||||
GRAPHICS_IMPORT(device_projection_pop);
|
||||
|
||||
GRAPHICS_IMPORT(gs_swapchain_destroy);
|
||||
|
||||
GRAPHICS_IMPORT(gs_texture_destroy);
|
||||
GRAPHICS_IMPORT(gs_texture_get_width);
|
||||
GRAPHICS_IMPORT(gs_texture_get_height);
|
||||
GRAPHICS_IMPORT(gs_texture_get_color_format);
|
||||
GRAPHICS_IMPORT(gs_texture_map);
|
||||
GRAPHICS_IMPORT(gs_texture_unmap);
|
||||
GRAPHICS_IMPORT_OPTIONAL(gs_texture_is_rect);
|
||||
GRAPHICS_IMPORT(gs_texture_get_obj);
|
||||
|
||||
GRAPHICS_IMPORT(gs_cubetexture_destroy);
|
||||
GRAPHICS_IMPORT(gs_cubetexture_get_size);
|
||||
GRAPHICS_IMPORT(gs_cubetexture_get_color_format);
|
||||
|
||||
GRAPHICS_IMPORT(gs_voltexture_destroy);
|
||||
GRAPHICS_IMPORT(gs_voltexture_get_width);
|
||||
GRAPHICS_IMPORT(gs_voltexture_get_height);
|
||||
GRAPHICS_IMPORT(gs_voltexture_get_depth);
|
||||
GRAPHICS_IMPORT(gs_voltexture_get_color_format);
|
||||
|
||||
GRAPHICS_IMPORT(gs_stagesurface_destroy);
|
||||
GRAPHICS_IMPORT(gs_stagesurface_get_width);
|
||||
GRAPHICS_IMPORT(gs_stagesurface_get_height);
|
||||
GRAPHICS_IMPORT(gs_stagesurface_get_color_format);
|
||||
GRAPHICS_IMPORT(gs_stagesurface_map);
|
||||
GRAPHICS_IMPORT(gs_stagesurface_unmap);
|
||||
|
||||
GRAPHICS_IMPORT(gs_zstencil_destroy);
|
||||
|
||||
GRAPHICS_IMPORT(gs_samplerstate_destroy);
|
||||
|
||||
GRAPHICS_IMPORT(gs_vertexbuffer_destroy);
|
||||
GRAPHICS_IMPORT(gs_vertexbuffer_flush);
|
||||
GRAPHICS_IMPORT(gs_vertexbuffer_get_data);
|
||||
|
||||
GRAPHICS_IMPORT(gs_indexbuffer_destroy);
|
||||
GRAPHICS_IMPORT(gs_indexbuffer_flush);
|
||||
GRAPHICS_IMPORT(gs_indexbuffer_get_data);
|
||||
GRAPHICS_IMPORT(gs_indexbuffer_get_num_indices);
|
||||
GRAPHICS_IMPORT(gs_indexbuffer_get_type);
|
||||
|
||||
GRAPHICS_IMPORT(gs_shader_destroy);
|
||||
GRAPHICS_IMPORT(gs_shader_get_num_params);
|
||||
GRAPHICS_IMPORT(gs_shader_get_param_by_idx);
|
||||
GRAPHICS_IMPORT(gs_shader_get_param_by_name);
|
||||
GRAPHICS_IMPORT(gs_shader_get_viewproj_matrix);
|
||||
GRAPHICS_IMPORT(gs_shader_get_world_matrix);
|
||||
GRAPHICS_IMPORT(gs_shader_get_param_info);
|
||||
GRAPHICS_IMPORT(gs_shader_set_bool);
|
||||
GRAPHICS_IMPORT(gs_shader_set_float);
|
||||
GRAPHICS_IMPORT(gs_shader_set_int);
|
||||
GRAPHICS_IMPORT(gs_shader_set_matrix3);
|
||||
GRAPHICS_IMPORT(gs_shader_set_matrix4);
|
||||
GRAPHICS_IMPORT(gs_shader_set_vec2);
|
||||
GRAPHICS_IMPORT(gs_shader_set_vec3);
|
||||
GRAPHICS_IMPORT(gs_shader_set_vec4);
|
||||
GRAPHICS_IMPORT(gs_shader_set_texture);
|
||||
GRAPHICS_IMPORT(gs_shader_set_val);
|
||||
GRAPHICS_IMPORT(gs_shader_set_default);
|
||||
|
||||
/* OSX/Cocoa specific functions */
|
||||
#ifdef __APPLE__
|
||||
GRAPHICS_IMPORT_OPTIONAL(device_texture_create_from_iosurface);
|
||||
GRAPHICS_IMPORT_OPTIONAL(gs_texture_rebind_iosurface);
|
||||
|
||||
/* win32 specific functions */
|
||||
#elif _WIN32
|
||||
GRAPHICS_IMPORT(device_gdi_texture_available);
|
||||
GRAPHICS_IMPORT(device_shared_texture_available);
|
||||
GRAPHICS_IMPORT_OPTIONAL(device_get_duplicator_monitor_info);
|
||||
GRAPHICS_IMPORT_OPTIONAL(device_duplicator_create);
|
||||
GRAPHICS_IMPORT_OPTIONAL(gs_duplicator_destroy);
|
||||
GRAPHICS_IMPORT_OPTIONAL(gs_duplicator_update_frame);
|
||||
GRAPHICS_IMPORT_OPTIONAL(gs_duplicator_get_texture);
|
||||
GRAPHICS_IMPORT_OPTIONAL(device_texture_create_gdi);
|
||||
GRAPHICS_IMPORT_OPTIONAL(gs_texture_get_dc);
|
||||
GRAPHICS_IMPORT_OPTIONAL(gs_texture_release_dc);
|
||||
GRAPHICS_IMPORT_OPTIONAL(device_texture_open_shared);
|
||||
#endif
|
||||
|
||||
return success;
|
||||
}
|
||||
300
libobs/graphics/graphics-internal.h
Normal file
300
libobs/graphics/graphics-internal.h
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../util/threading.h"
|
||||
#include "../util/darray.h"
|
||||
#include "graphics.h"
|
||||
#include "matrix3.h"
|
||||
#include "matrix4.h"
|
||||
|
||||
struct gs_exports {
|
||||
const char *(*device_get_name)(void);
|
||||
int (*device_get_type)(void);
|
||||
bool (*device_enum_adapters)(
|
||||
bool (*callback)(void*, const char*, uint32_t),
|
||||
void*);
|
||||
const char *(*device_preprocessor_name)(void);
|
||||
int (*device_create)(gs_device_t **device, uint32_t adapter);
|
||||
void (*device_destroy)(gs_device_t *device);
|
||||
void (*device_enter_context)(gs_device_t *device);
|
||||
void (*device_leave_context)(gs_device_t *device);
|
||||
gs_swapchain_t *(*device_swapchain_create)(gs_device_t *device,
|
||||
const struct gs_init_data *data);
|
||||
void (*device_resize)(gs_device_t *device, uint32_t x, uint32_t y);
|
||||
void (*device_get_size)(const gs_device_t *device,
|
||||
uint32_t *x, uint32_t *y);
|
||||
uint32_t (*device_get_width)(const gs_device_t *device);
|
||||
uint32_t (*device_get_height)(const gs_device_t *device);
|
||||
gs_texture_t *(*device_texture_create)(gs_device_t *device,
|
||||
uint32_t width, uint32_t height,
|
||||
enum gs_color_format color_format, uint32_t levels,
|
||||
const uint8_t **data, uint32_t flags);
|
||||
gs_texture_t *(*device_cubetexture_create)(gs_device_t *device,
|
||||
uint32_t size, enum gs_color_format color_format,
|
||||
uint32_t levels, const uint8_t **data, uint32_t flags);
|
||||
gs_texture_t *(*device_voltexture_create)(gs_device_t *device,
|
||||
uint32_t width, uint32_t height, uint32_t depth,
|
||||
enum gs_color_format color_format, uint32_t levels,
|
||||
const uint8_t **data, uint32_t flags);
|
||||
gs_zstencil_t *(*device_zstencil_create)(gs_device_t *device,
|
||||
uint32_t width, uint32_t height,
|
||||
enum gs_zstencil_format format);
|
||||
gs_stagesurf_t *(*device_stagesurface_create)(gs_device_t *device,
|
||||
uint32_t width, uint32_t height,
|
||||
enum gs_color_format color_format);
|
||||
gs_samplerstate_t *(*device_samplerstate_create)(gs_device_t *device,
|
||||
const struct gs_sampler_info *info);
|
||||
gs_shader_t *(*device_vertexshader_create)(gs_device_t *device,
|
||||
const char *shader, const char *file,
|
||||
char **error_string);
|
||||
gs_shader_t *(*device_pixelshader_create)(gs_device_t *device,
|
||||
const char *shader, const char *file,
|
||||
char **error_string);
|
||||
gs_vertbuffer_t *(*device_vertexbuffer_create)(gs_device_t *device,
|
||||
struct gs_vb_data *data, uint32_t flags);
|
||||
gs_indexbuffer_t *(*device_indexbuffer_create)(gs_device_t *device,
|
||||
enum gs_index_type type, void *indices, size_t num,
|
||||
uint32_t flags);
|
||||
enum gs_texture_type (*device_get_texture_type)(
|
||||
const gs_texture_t *texture);
|
||||
void (*device_load_vertexbuffer)(gs_device_t *device,
|
||||
gs_vertbuffer_t *vertbuffer);
|
||||
void (*device_load_indexbuffer)(gs_device_t *device,
|
||||
gs_indexbuffer_t *indexbuffer);
|
||||
void (*device_load_texture)(gs_device_t *device, gs_texture_t *tex,
|
||||
int unit);
|
||||
void (*device_load_samplerstate)(gs_device_t *device,
|
||||
gs_samplerstate_t *samplerstate, int unit);
|
||||
void (*device_load_vertexshader)(gs_device_t *device,
|
||||
gs_shader_t *vertshader);
|
||||
void (*device_load_pixelshader)(gs_device_t *device,
|
||||
gs_shader_t *pixelshader);
|
||||
void (*device_load_default_samplerstate)(gs_device_t *device,
|
||||
bool b_3d, int unit);
|
||||
gs_shader_t *(*device_get_vertex_shader)(const gs_device_t *device);
|
||||
gs_shader_t *(*device_get_pixel_shader)(const gs_device_t *device);
|
||||
gs_texture_t *(*device_get_render_target)(const gs_device_t *device);
|
||||
gs_zstencil_t *(*device_get_zstencil_target)(const gs_device_t *device);
|
||||
void (*device_set_render_target)(gs_device_t *device, gs_texture_t *tex,
|
||||
gs_zstencil_t *zstencil);
|
||||
void (*device_set_cube_render_target)(gs_device_t *device,
|
||||
gs_texture_t *cubetex, int side, gs_zstencil_t *zstencil);
|
||||
void (*device_copy_texture)(gs_device_t *device, gs_texture_t *dst,
|
||||
gs_texture_t *src);
|
||||
void (*device_copy_texture_region)(gs_device_t *device,
|
||||
gs_texture_t *dst, uint32_t dst_x, uint32_t dst_y,
|
||||
gs_texture_t *src, uint32_t src_x, uint32_t src_y,
|
||||
uint32_t src_w, uint32_t src_h);
|
||||
void (*device_stage_texture)(gs_device_t *device, gs_stagesurf_t *dst,
|
||||
gs_texture_t *src);
|
||||
void (*device_begin_scene)(gs_device_t *device);
|
||||
void (*device_draw)(gs_device_t *device, enum gs_draw_mode draw_mode,
|
||||
uint32_t start_vert, uint32_t num_verts);
|
||||
void (*device_end_scene)(gs_device_t *device);
|
||||
void (*device_load_swapchain)(gs_device_t *device,
|
||||
gs_swapchain_t *swaphchain);
|
||||
void (*device_clear)(gs_device_t *device, uint32_t clear_flags,
|
||||
const struct vec4 *color, float depth, uint8_t stencil);
|
||||
void (*device_present)(gs_device_t *device);
|
||||
void (*device_flush)(gs_device_t *device);
|
||||
void (*device_set_cull_mode)(gs_device_t *device,
|
||||
enum gs_cull_mode mode);
|
||||
enum gs_cull_mode (*device_get_cull_mode)(const gs_device_t *device);
|
||||
void (*device_enable_blending)(gs_device_t *device, bool enable);
|
||||
void (*device_enable_depth_test)(gs_device_t *device, bool enable);
|
||||
void (*device_enable_stencil_test)(gs_device_t *device, bool enable);
|
||||
void (*device_enable_stencil_write)(gs_device_t *device, bool enable);
|
||||
void (*device_enable_color)(gs_device_t *device, bool red, bool green,
|
||||
bool blue, bool alpha);
|
||||
void (*device_blend_function)(gs_device_t *device,
|
||||
enum gs_blend_type src, enum gs_blend_type dest);
|
||||
void (*device_blend_function_separate)(gs_device_t *device,
|
||||
enum gs_blend_type src_c, enum gs_blend_type dest_c,
|
||||
enum gs_blend_type src_a, enum gs_blend_type dest_a);
|
||||
void (*device_depth_function)(gs_device_t *device,
|
||||
enum gs_depth_test test);
|
||||
void (*device_stencil_function)(gs_device_t *device,
|
||||
enum gs_stencil_side side, enum gs_depth_test test);
|
||||
void (*device_stencil_op)(gs_device_t *device,
|
||||
enum gs_stencil_side side,
|
||||
enum gs_stencil_op_type fail,
|
||||
enum gs_stencil_op_type zfail,
|
||||
enum gs_stencil_op_type zpass);
|
||||
void (*device_set_viewport)(gs_device_t *device, int x, int y,
|
||||
int width, int height);
|
||||
void (*device_get_viewport)(const gs_device_t *device,
|
||||
struct gs_rect *rect);
|
||||
void (*device_set_scissor_rect)(gs_device_t *device,
|
||||
const struct gs_rect *rect);
|
||||
void (*device_ortho)(gs_device_t *device, float left, float right,
|
||||
float top, float bottom, float znear, float zfar);
|
||||
void (*device_frustum)(gs_device_t *device, float left, float right,
|
||||
float top, float bottom, float znear, float zfar);
|
||||
void (*device_projection_push)(gs_device_t *device);
|
||||
void (*device_projection_pop)(gs_device_t *device);
|
||||
|
||||
void (*gs_swapchain_destroy)(gs_swapchain_t *swapchain);
|
||||
|
||||
void (*gs_texture_destroy)(gs_texture_t *tex);
|
||||
uint32_t (*gs_texture_get_width)(const gs_texture_t *tex);
|
||||
uint32_t (*gs_texture_get_height)(const gs_texture_t *tex);
|
||||
enum gs_color_format (*gs_texture_get_color_format)(
|
||||
const gs_texture_t *tex);
|
||||
bool (*gs_texture_map)(gs_texture_t *tex, uint8_t **ptr,
|
||||
uint32_t *linesize);
|
||||
void (*gs_texture_unmap)(gs_texture_t *tex);
|
||||
bool (*gs_texture_is_rect)(const gs_texture_t *tex);
|
||||
void *(*gs_texture_get_obj)(const gs_texture_t *tex);
|
||||
|
||||
void (*gs_cubetexture_destroy)(gs_texture_t *cubetex);
|
||||
uint32_t (*gs_cubetexture_get_size)(const gs_texture_t *cubetex);
|
||||
enum gs_color_format (*gs_cubetexture_get_color_format)(
|
||||
const gs_texture_t *cubetex);
|
||||
|
||||
void (*gs_voltexture_destroy)(gs_texture_t *voltex);
|
||||
uint32_t (*gs_voltexture_get_width)(const gs_texture_t *voltex);
|
||||
uint32_t (*gs_voltexture_get_height)(const gs_texture_t *voltex);
|
||||
uint32_t (*gs_voltexture_get_depth)(const gs_texture_t *voltex);
|
||||
enum gs_color_format (*gs_voltexture_get_color_format)(
|
||||
const gs_texture_t *voltex);
|
||||
|
||||
void (*gs_stagesurface_destroy)(gs_stagesurf_t *stagesurf);
|
||||
uint32_t (*gs_stagesurface_get_width)(const gs_stagesurf_t *stagesurf);
|
||||
uint32_t (*gs_stagesurface_get_height)(const gs_stagesurf_t *stagesurf);
|
||||
enum gs_color_format (*gs_stagesurface_get_color_format)(
|
||||
const gs_stagesurf_t *stagesurf);
|
||||
bool (*gs_stagesurface_map)(gs_stagesurf_t *stagesurf,
|
||||
uint8_t **data, uint32_t *linesize);
|
||||
void (*gs_stagesurface_unmap)(gs_stagesurf_t *stagesurf);
|
||||
|
||||
void (*gs_zstencil_destroy)(gs_zstencil_t *zstencil);
|
||||
|
||||
void (*gs_samplerstate_destroy)(gs_samplerstate_t *samplerstate);
|
||||
|
||||
void (*gs_vertexbuffer_destroy)(gs_vertbuffer_t *vertbuffer);
|
||||
void (*gs_vertexbuffer_flush)(gs_vertbuffer_t *vertbuffer);
|
||||
struct gs_vb_data *(*gs_vertexbuffer_get_data)(
|
||||
const gs_vertbuffer_t *vertbuffer);
|
||||
|
||||
void (*gs_indexbuffer_destroy)(gs_indexbuffer_t *indexbuffer);
|
||||
void (*gs_indexbuffer_flush)(gs_indexbuffer_t *indexbuffer);
|
||||
void *(*gs_indexbuffer_get_data)(const gs_indexbuffer_t *indexbuffer);
|
||||
size_t (*gs_indexbuffer_get_num_indices)(
|
||||
const gs_indexbuffer_t *indexbuffer);
|
||||
enum gs_index_type (*gs_indexbuffer_get_type)(
|
||||
const gs_indexbuffer_t *indexbuffer);
|
||||
|
||||
void (*gs_shader_destroy)(gs_shader_t *shader);
|
||||
int (*gs_shader_get_num_params)(const gs_shader_t *shader);
|
||||
gs_sparam_t *(*gs_shader_get_param_by_idx)(gs_shader_t *shader,
|
||||
uint32_t param);
|
||||
gs_sparam_t *(*gs_shader_get_param_by_name)(gs_shader_t *shader,
|
||||
const char *name);
|
||||
gs_sparam_t *(*gs_shader_get_viewproj_matrix)(
|
||||
const gs_shader_t *shader);
|
||||
gs_sparam_t *(*gs_shader_get_world_matrix)(const gs_shader_t *shader);
|
||||
void (*gs_shader_get_param_info)(const gs_sparam_t *param,
|
||||
struct gs_shader_param_info *info);
|
||||
void (*gs_shader_set_bool)(gs_sparam_t *param, bool val);
|
||||
void (*gs_shader_set_float)(gs_sparam_t *param, float val);
|
||||
void (*gs_shader_set_int)(gs_sparam_t *param, int val);
|
||||
void (*gs_shader_set_matrix3)(gs_sparam_t *param,
|
||||
const struct matrix3 *val);
|
||||
void (*gs_shader_set_matrix4)(gs_sparam_t *param,
|
||||
const struct matrix4 *val);
|
||||
void (*gs_shader_set_vec2)(gs_sparam_t *param, const struct vec2 *val);
|
||||
void (*gs_shader_set_vec3)(gs_sparam_t *param, const struct vec3 *val);
|
||||
void (*gs_shader_set_vec4)(gs_sparam_t *param, const struct vec4 *val);
|
||||
void (*gs_shader_set_texture)(gs_sparam_t *param, gs_texture_t *val);
|
||||
void (*gs_shader_set_val)(gs_sparam_t *param, const void *val,
|
||||
size_t size);
|
||||
void (*gs_shader_set_default)(gs_sparam_t *param);
|
||||
|
||||
#ifdef __APPLE__
|
||||
/* OSX/Cocoa specific functions */
|
||||
gs_texture_t *(*device_texture_create_from_iosurface)(gs_device_t *dev,
|
||||
void *iosurf);
|
||||
bool (*gs_texture_rebind_iosurface)(gs_texture_t *texture,
|
||||
void *iosurf);
|
||||
|
||||
#elif _WIN32
|
||||
bool (*device_gdi_texture_available)(void);
|
||||
bool (*device_shared_texture_available)(void);
|
||||
|
||||
bool (*device_get_duplicator_monitor_info)(gs_device_t *device,
|
||||
int monitor_idx, struct gs_monitor_info *monitor_info);
|
||||
|
||||
gs_duplicator_t *(*device_duplicator_create)(gs_device_t *device,
|
||||
int monitor_idx);
|
||||
void (*gs_duplicator_destroy)(gs_duplicator_t *duplicator);
|
||||
|
||||
bool (*gs_duplicator_update_frame)(gs_duplicator_t *duplicator);
|
||||
gs_texture_t *(*gs_duplicator_get_texture)(gs_duplicator_t *duplicator);
|
||||
|
||||
gs_texture_t *(*device_texture_create_gdi)(gs_device_t *device,
|
||||
uint32_t width, uint32_t height);
|
||||
|
||||
void *(*gs_texture_get_dc)(gs_texture_t *gdi_tex);
|
||||
void (*gs_texture_release_dc)(gs_texture_t *gdi_tex);
|
||||
|
||||
gs_texture_t *(*device_texture_open_shared)(gs_device_t *device,
|
||||
uint32_t handle);
|
||||
#endif
|
||||
};
|
||||
|
||||
struct blend_state {
|
||||
bool enabled;
|
||||
enum gs_blend_type src_c;
|
||||
enum gs_blend_type dest_c;
|
||||
enum gs_blend_type src_a;
|
||||
enum gs_blend_type dest_a;
|
||||
};
|
||||
|
||||
struct graphics_subsystem {
|
||||
void *module;
|
||||
gs_device_t *device;
|
||||
struct gs_exports exports;
|
||||
|
||||
DARRAY(struct gs_rect) viewport_stack;
|
||||
|
||||
DARRAY(struct matrix4) matrix_stack;
|
||||
size_t cur_matrix;
|
||||
|
||||
struct matrix4 projection;
|
||||
struct gs_effect *cur_effect;
|
||||
|
||||
gs_vertbuffer_t *sprite_buffer;
|
||||
|
||||
bool using_immediate;
|
||||
struct gs_vb_data *vbd;
|
||||
gs_vertbuffer_t *immediate_vertbuffer;
|
||||
DARRAY(struct vec3) verts;
|
||||
DARRAY(struct vec3) norms;
|
||||
DARRAY(uint32_t) colors;
|
||||
DARRAY(struct vec2) texverts[16];
|
||||
|
||||
pthread_mutex_t effect_mutex;
|
||||
struct gs_effect *first_effect;
|
||||
|
||||
pthread_mutex_t mutex;
|
||||
volatile long ref;
|
||||
|
||||
struct blend_state cur_blend_state;
|
||||
DARRAY(struct blend_state) blend_state_stack;
|
||||
};
|
||||
63
libobs/graphics/graphics-magick.c
Normal file
63
libobs/graphics/graphics-magick.c
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#include "graphics.h"
|
||||
|
||||
#define MAGICKCORE_QUANTUM_DEPTH 16
|
||||
#define MAGICKCORE_HDRI_ENABLE 0
|
||||
#include <magick/MagickCore.h>
|
||||
|
||||
void gs_init_image_deps()
|
||||
{
|
||||
MagickCoreGenesis(NULL, MagickTrue);
|
||||
}
|
||||
|
||||
void gs_free_image_deps()
|
||||
{
|
||||
MagickCoreTerminus();
|
||||
}
|
||||
|
||||
uint8_t *gs_create_texture_file_data(const char *file,
|
||||
enum gs_color_format *format,
|
||||
uint32_t *cx_out, uint32_t *cy_out)
|
||||
{
|
||||
uint8_t *data = NULL;
|
||||
ImageInfo *info;
|
||||
ExceptionInfo *exception;
|
||||
Image *image;
|
||||
|
||||
if (!file || !*file)
|
||||
return NULL;
|
||||
|
||||
info = CloneImageInfo(NULL);
|
||||
exception = AcquireExceptionInfo();
|
||||
|
||||
strcpy(info->filename, file);
|
||||
image = ReadImage(info, exception);
|
||||
if (image) {
|
||||
size_t cx = image->magick_columns;
|
||||
size_t cy = image->magick_rows;
|
||||
data = bmalloc(cx * cy * 4);
|
||||
|
||||
ExportImagePixels(image, 0, 0, cx, cy, "BGRA", CharPixel,
|
||||
data, exception);
|
||||
if (exception->severity != UndefinedException) {
|
||||
blog(LOG_WARNING, "magickcore warning/error getting "
|
||||
"pixels from file '%s': %s", file,
|
||||
exception->reason);
|
||||
bfree(data);
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
*format = GS_BGRA;
|
||||
*cx_out = (uint32_t)cx;
|
||||
*cy_out = (uint32_t)cy;
|
||||
DestroyImage(image);
|
||||
|
||||
} else if (exception->severity != UndefinedException) {
|
||||
blog(LOG_WARNING, "magickcore warning/error reading file "
|
||||
"'%s': %s", file, exception->reason);
|
||||
}
|
||||
|
||||
DestroyImageInfo(info);
|
||||
DestroyExceptionInfo(exception);
|
||||
|
||||
return data;
|
||||
}
|
||||
2566
libobs/graphics/graphics.c
Normal file
2566
libobs/graphics/graphics.c
Normal file
File diff suppressed because it is too large
Load diff
808
libobs/graphics/graphics.h
Normal file
808
libobs/graphics/graphics.h
Normal file
|
|
@ -0,0 +1,808 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../util/bmem.h"
|
||||
#include "input.h"
|
||||
#ifdef __APPLE__
|
||||
#include <objc/objc-runtime.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is an API-independent graphics subsystem wrapper.
|
||||
*
|
||||
* This allows the use of OpenGL and different Direct3D versions through
|
||||
* one shared interface.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define GS_MAX_TEXTURES 8
|
||||
|
||||
struct vec2;
|
||||
struct vec3;
|
||||
struct vec4;
|
||||
struct quat;
|
||||
struct axisang;
|
||||
struct plane;
|
||||
struct matrix3;
|
||||
struct matrix4;
|
||||
|
||||
enum gs_draw_mode {
|
||||
GS_POINTS,
|
||||
GS_LINES,
|
||||
GS_LINESTRIP,
|
||||
GS_TRIS,
|
||||
GS_TRISTRIP
|
||||
};
|
||||
|
||||
enum gs_color_format {
|
||||
GS_UNKNOWN,
|
||||
GS_A8,
|
||||
GS_R8,
|
||||
GS_RGBA,
|
||||
GS_BGRX,
|
||||
GS_BGRA,
|
||||
GS_R10G10B10A2,
|
||||
GS_RGBA16,
|
||||
GS_R16,
|
||||
GS_RGBA16F,
|
||||
GS_RGBA32F,
|
||||
GS_RG16F,
|
||||
GS_RG32F,
|
||||
GS_R16F,
|
||||
GS_R32F,
|
||||
GS_DXT1,
|
||||
GS_DXT3,
|
||||
GS_DXT5
|
||||
};
|
||||
|
||||
enum gs_zstencil_format {
|
||||
GS_ZS_NONE,
|
||||
GS_Z16,
|
||||
GS_Z24_S8,
|
||||
GS_Z32F,
|
||||
GS_Z32F_S8X24
|
||||
};
|
||||
|
||||
enum gs_index_type {
|
||||
GS_UNSIGNED_SHORT,
|
||||
GS_UNSIGNED_LONG
|
||||
};
|
||||
|
||||
enum gs_cull_mode {
|
||||
GS_BACK,
|
||||
GS_FRONT,
|
||||
GS_NEITHER
|
||||
};
|
||||
|
||||
enum gs_blend_type {
|
||||
GS_BLEND_ZERO,
|
||||
GS_BLEND_ONE,
|
||||
GS_BLEND_SRCCOLOR,
|
||||
GS_BLEND_INVSRCCOLOR,
|
||||
GS_BLEND_SRCALPHA,
|
||||
GS_BLEND_INVSRCALPHA,
|
||||
GS_BLEND_DSTCOLOR,
|
||||
GS_BLEND_INVDSTCOLOR,
|
||||
GS_BLEND_DSTALPHA,
|
||||
GS_BLEND_INVDSTALPHA,
|
||||
GS_BLEND_SRCALPHASAT
|
||||
};
|
||||
|
||||
enum gs_depth_test {
|
||||
GS_NEVER,
|
||||
GS_LESS,
|
||||
GS_LEQUAL,
|
||||
GS_EQUAL,
|
||||
GS_GEQUAL,
|
||||
GS_GREATER,
|
||||
GS_NOTEQUAL,
|
||||
GS_ALWAYS
|
||||
};
|
||||
|
||||
enum gs_stencil_side {
|
||||
GS_STENCIL_FRONT=1,
|
||||
GS_STENCIL_BACK,
|
||||
GS_STENCIL_BOTH
|
||||
};
|
||||
|
||||
enum gs_stencil_op_type {
|
||||
GS_KEEP,
|
||||
GS_ZERO,
|
||||
GS_REPLACE,
|
||||
GS_INCR,
|
||||
GS_DECR,
|
||||
GS_INVERT
|
||||
};
|
||||
|
||||
enum gs_cube_sides {
|
||||
GS_POSITIVE_X,
|
||||
GS_NEGATIVE_X,
|
||||
GS_POSITIVE_Y,
|
||||
GS_NEGATIVE_Y,
|
||||
GS_POSITIVE_Z,
|
||||
GS_NEGATIVE_Z
|
||||
};
|
||||
|
||||
enum gs_sample_filter {
|
||||
GS_FILTER_POINT,
|
||||
GS_FILTER_LINEAR,
|
||||
GS_FILTER_ANISOTROPIC,
|
||||
GS_FILTER_MIN_MAG_POINT_MIP_LINEAR,
|
||||
GS_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT,
|
||||
GS_FILTER_MIN_POINT_MAG_MIP_LINEAR,
|
||||
GS_FILTER_MIN_LINEAR_MAG_MIP_POINT,
|
||||
GS_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR,
|
||||
GS_FILTER_MIN_MAG_LINEAR_MIP_POINT,
|
||||
};
|
||||
|
||||
enum gs_address_mode {
|
||||
GS_ADDRESS_CLAMP,
|
||||
GS_ADDRESS_WRAP,
|
||||
GS_ADDRESS_MIRROR,
|
||||
GS_ADDRESS_BORDER,
|
||||
GS_ADDRESS_MIRRORONCE
|
||||
};
|
||||
|
||||
enum gs_texture_type {
|
||||
GS_TEXTURE_2D,
|
||||
GS_TEXTURE_3D,
|
||||
GS_TEXTURE_CUBE
|
||||
};
|
||||
|
||||
struct gs_monitor_info {
|
||||
int rotation_degrees;
|
||||
long x;
|
||||
long y;
|
||||
long cx;
|
||||
long cy;
|
||||
};
|
||||
|
||||
struct gs_tvertarray {
|
||||
size_t width;
|
||||
void *array;
|
||||
};
|
||||
|
||||
struct gs_vb_data {
|
||||
size_t num;
|
||||
struct vec3 *points;
|
||||
struct vec3 *normals;
|
||||
struct vec3 *tangents;
|
||||
uint32_t *colors;
|
||||
|
||||
size_t num_tex;
|
||||
struct gs_tvertarray *tvarray;
|
||||
};
|
||||
|
||||
static inline struct gs_vb_data *gs_vbdata_create(void)
|
||||
{
|
||||
return (struct gs_vb_data*)bzalloc(sizeof(struct gs_vb_data));
|
||||
}
|
||||
|
||||
static inline void gs_vbdata_destroy(struct gs_vb_data *data)
|
||||
{
|
||||
uint32_t i;
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
bfree(data->points);
|
||||
bfree(data->normals);
|
||||
bfree(data->tangents);
|
||||
bfree(data->colors);
|
||||
for (i = 0; i < data->num_tex; i++)
|
||||
bfree(data->tvarray[i].array);
|
||||
bfree(data->tvarray);
|
||||
bfree(data);
|
||||
}
|
||||
|
||||
struct gs_sampler_info {
|
||||
enum gs_sample_filter filter;
|
||||
enum gs_address_mode address_u;
|
||||
enum gs_address_mode address_v;
|
||||
enum gs_address_mode address_w;
|
||||
int max_anisotropy;
|
||||
uint32_t border_color;
|
||||
};
|
||||
|
||||
struct gs_display_mode {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t bits;
|
||||
uint32_t freq;
|
||||
};
|
||||
|
||||
struct gs_rect {
|
||||
int x;
|
||||
int y;
|
||||
int cx;
|
||||
int cy;
|
||||
};
|
||||
|
||||
/* wrapped opaque data types */
|
||||
|
||||
struct gs_texture;
|
||||
struct gs_stage_surface;
|
||||
struct gs_zstencil_buffer;
|
||||
struct gs_vertex_buffer;
|
||||
struct gs_index_buffer;
|
||||
struct gs_sampler_state;
|
||||
struct gs_shader;
|
||||
struct gs_swap_chain;
|
||||
struct gs_texrender;
|
||||
struct gs_shader_param;
|
||||
struct gs_effect;
|
||||
struct gs_effect_technique;
|
||||
struct gs_effect_pass;
|
||||
struct gs_effect_param;
|
||||
struct gs_device;
|
||||
struct graphics_subsystem;
|
||||
|
||||
typedef struct gs_texture gs_texture_t;
|
||||
typedef struct gs_stage_surface gs_stagesurf_t;
|
||||
typedef struct gs_zstencil_buffer gs_zstencil_t;
|
||||
typedef struct gs_vertex_buffer gs_vertbuffer_t;
|
||||
typedef struct gs_index_buffer gs_indexbuffer_t;
|
||||
typedef struct gs_sampler_state gs_samplerstate_t;
|
||||
typedef struct gs_swap_chain gs_swapchain_t;
|
||||
typedef struct gs_texture_render gs_texrender_t;
|
||||
typedef struct gs_shader gs_shader_t;
|
||||
typedef struct gs_shader_param gs_sparam_t;
|
||||
typedef struct gs_effect gs_effect_t;
|
||||
typedef struct gs_effect_technique gs_technique_t;
|
||||
typedef struct gs_effect_param gs_eparam_t;
|
||||
typedef struct gs_device gs_device_t;
|
||||
typedef struct graphics_subsystem graphics_t;
|
||||
|
||||
/* ---------------------------------------------------
|
||||
* shader functions
|
||||
* --------------------------------------------------- */
|
||||
|
||||
enum gs_shader_param_type {
|
||||
GS_SHADER_PARAM_UNKNOWN,
|
||||
GS_SHADER_PARAM_BOOL,
|
||||
GS_SHADER_PARAM_FLOAT,
|
||||
GS_SHADER_PARAM_INT,
|
||||
GS_SHADER_PARAM_STRING,
|
||||
GS_SHADER_PARAM_VEC2,
|
||||
GS_SHADER_PARAM_VEC3,
|
||||
GS_SHADER_PARAM_VEC4,
|
||||
GS_SHADER_PARAM_MATRIX4X4,
|
||||
GS_SHADER_PARAM_TEXTURE,
|
||||
};
|
||||
|
||||
struct gs_shader_param_info {
|
||||
enum gs_shader_param_type type;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
enum gs_shader_type {
|
||||
GS_SHADER_VERTEX,
|
||||
GS_SHADER_PIXEL,
|
||||
};
|
||||
|
||||
EXPORT void gs_shader_destroy(gs_shader_t *shader);
|
||||
|
||||
EXPORT int gs_shader_get_num_params(const gs_shader_t *shader);
|
||||
EXPORT gs_sparam_t *gs_shader_get_param_by_idx(gs_shader_t *shader,
|
||||
uint32_t param);
|
||||
EXPORT gs_sparam_t *gs_shader_get_param_by_name(gs_shader_t *shader,
|
||||
const char *name);
|
||||
|
||||
EXPORT gs_sparam_t *gs_shader_get_viewproj_matrix(const gs_shader_t *shader);
|
||||
EXPORT gs_sparam_t *gs_shader_get_world_matrix(const gs_shader_t *shader);
|
||||
|
||||
EXPORT void gs_shader_get_param_info(const gs_sparam_t *param,
|
||||
struct gs_shader_param_info *info);
|
||||
EXPORT void gs_shader_set_bool(gs_sparam_t *param, bool val);
|
||||
EXPORT void gs_shader_set_float(gs_sparam_t *param, float val);
|
||||
EXPORT void gs_shader_set_int(gs_sparam_t *param, int val);
|
||||
EXPORT void gs_shader_set_matrix3(gs_sparam_t *param, const struct matrix3 *val);
|
||||
EXPORT void gs_shader_set_matrix4(gs_sparam_t *param, const struct matrix4 *val);
|
||||
EXPORT void gs_shader_set_vec2(gs_sparam_t *param, const struct vec2 *val);
|
||||
EXPORT void gs_shader_set_vec3(gs_sparam_t *param, const struct vec3 *val);
|
||||
EXPORT void gs_shader_set_vec4(gs_sparam_t *param, const struct vec4 *val);
|
||||
EXPORT void gs_shader_set_texture(gs_sparam_t *param, gs_texture_t *val);
|
||||
EXPORT void gs_shader_set_val(gs_sparam_t *param, const void *val, size_t size);
|
||||
EXPORT void gs_shader_set_default(gs_sparam_t *param);
|
||||
|
||||
/* ---------------------------------------------------
|
||||
* effect functions
|
||||
* --------------------------------------------------- */
|
||||
|
||||
/*enum gs_effect_property_type {
|
||||
GS_EFFECT_NONE,
|
||||
GS_EFFECT_BOOL,
|
||||
GS_EFFECT_FLOAT,
|
||||
GS_EFFECT_COLOR,
|
||||
GS_EFFECT_TEXTURE
|
||||
};*/
|
||||
|
||||
struct gs_effect_param_info {
|
||||
const char *name;
|
||||
enum gs_shader_param_type type;
|
||||
|
||||
/* const char *full_name;
|
||||
enum gs_effect_property_type prop_type;
|
||||
|
||||
float min, max, inc, mul; */
|
||||
};
|
||||
|
||||
EXPORT void gs_effect_destroy(gs_effect_t *effect);
|
||||
|
||||
EXPORT gs_technique_t *gs_effect_get_technique(const gs_effect_t *effect,
|
||||
const char *name);
|
||||
|
||||
EXPORT gs_technique_t *gs_effect_get_current_technique(
|
||||
const gs_effect_t *effect);
|
||||
|
||||
EXPORT size_t gs_technique_begin(gs_technique_t *technique);
|
||||
EXPORT void gs_technique_end(gs_technique_t *technique);
|
||||
EXPORT bool gs_technique_begin_pass(gs_technique_t *technique, size_t pass);
|
||||
EXPORT bool gs_technique_begin_pass_by_name(gs_technique_t *technique,
|
||||
const char *name);
|
||||
EXPORT void gs_technique_end_pass(gs_technique_t *technique);
|
||||
|
||||
EXPORT size_t gs_effect_get_num_params(const gs_effect_t *effect);
|
||||
EXPORT gs_eparam_t *gs_effect_get_param_by_idx(const gs_effect_t *effect,
|
||||
size_t param);
|
||||
EXPORT gs_eparam_t *gs_effect_get_param_by_name(const gs_effect_t *effect,
|
||||
const char *name);
|
||||
|
||||
/** Helper function to simplify effect usage. Use with a while loop that
|
||||
* contains drawing functions. Automatically handles techniques, passes, and
|
||||
* unloading. */
|
||||
EXPORT bool gs_effect_loop(gs_effect_t *effect, const char *name);
|
||||
|
||||
/** used internally */
|
||||
EXPORT void gs_effect_update_params(gs_effect_t *effect);
|
||||
|
||||
EXPORT gs_eparam_t *gs_effect_get_viewproj_matrix(const gs_effect_t *effect);
|
||||
EXPORT gs_eparam_t *gs_effect_get_world_matrix(const gs_effect_t *effect);
|
||||
|
||||
EXPORT void gs_effect_get_param_info(const gs_eparam_t *param,
|
||||
struct gs_effect_param_info *info);
|
||||
EXPORT void gs_effect_set_bool(gs_eparam_t *param, bool val);
|
||||
EXPORT void gs_effect_set_float(gs_eparam_t *param, float val);
|
||||
EXPORT void gs_effect_set_int(gs_eparam_t *param, int val);
|
||||
EXPORT void gs_effect_set_matrix4(gs_eparam_t *param,
|
||||
const struct matrix4 *val);
|
||||
EXPORT void gs_effect_set_vec2(gs_eparam_t *param, const struct vec2 *val);
|
||||
EXPORT void gs_effect_set_vec3(gs_eparam_t *param, const struct vec3 *val);
|
||||
EXPORT void gs_effect_set_vec4(gs_eparam_t *param, const struct vec4 *val);
|
||||
EXPORT void gs_effect_set_texture(gs_eparam_t *param, gs_texture_t *val);
|
||||
EXPORT void gs_effect_set_val(gs_eparam_t *param, const void *val, size_t size);
|
||||
EXPORT void gs_effect_set_default(gs_eparam_t *param);
|
||||
|
||||
/* ---------------------------------------------------
|
||||
* texture render helper functions
|
||||
* --------------------------------------------------- */
|
||||
|
||||
EXPORT gs_texrender_t *gs_texrender_create(enum gs_color_format format,
|
||||
enum gs_zstencil_format zsformat);
|
||||
EXPORT void gs_texrender_destroy(gs_texrender_t *texrender);
|
||||
EXPORT bool gs_texrender_begin(gs_texrender_t *texrender, uint32_t cx,
|
||||
uint32_t cy);
|
||||
EXPORT void gs_texrender_end(gs_texrender_t *texrender);
|
||||
EXPORT void gs_texrender_reset(gs_texrender_t *texrender);
|
||||
EXPORT gs_texture_t *gs_texrender_get_texture(const gs_texrender_t *texrender);
|
||||
|
||||
/* ---------------------------------------------------
|
||||
* graphics subsystem
|
||||
* --------------------------------------------------- */
|
||||
|
||||
#define GS_BUILD_MIPMAPS (1<<0)
|
||||
#define GS_DYNAMIC (1<<1)
|
||||
#define GS_RENDER_TARGET (1<<2)
|
||||
#define GS_GL_DUMMYTEX (1<<3) /**<< texture with no allocated texture data */
|
||||
|
||||
/* ---------------- */
|
||||
/* global functions */
|
||||
|
||||
#define GS_SUCCESS 0
|
||||
#define GS_ERROR_FAIL -1
|
||||
#define GS_ERROR_MODULE_NOT_FOUND -2
|
||||
#define GS_ERROR_NOT_SUPPORTED -3
|
||||
|
||||
struct gs_window {
|
||||
#if defined(_WIN32)
|
||||
void *hwnd;
|
||||
#elif defined(__APPLE__)
|
||||
__unsafe_unretained id view;
|
||||
#elif defined(__linux__) || defined(__FreeBSD__)
|
||||
/* I'm not sure how portable defining id to uint32_t is. */
|
||||
uint32_t id;
|
||||
void* display;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct gs_init_data {
|
||||
struct gs_window window;
|
||||
uint32_t cx, cy;
|
||||
uint32_t num_backbuffers;
|
||||
enum gs_color_format format;
|
||||
enum gs_zstencil_format zsformat;
|
||||
uint32_t adapter;
|
||||
};
|
||||
|
||||
#define GS_DEVICE_OPENGL 1
|
||||
#define GS_DEVICE_DIRECT3D_11 2
|
||||
|
||||
EXPORT const char *gs_get_device_name(void);
|
||||
EXPORT int gs_get_device_type(void);
|
||||
EXPORT void gs_enum_adapters(
|
||||
bool (*callback)(void *param, const char *name, uint32_t id),
|
||||
void *param);
|
||||
|
||||
EXPORT int gs_create(graphics_t **graphics, const char *module,
|
||||
uint32_t adapter);
|
||||
EXPORT void gs_destroy(graphics_t *graphics);
|
||||
|
||||
EXPORT void gs_enter_context(graphics_t *graphics);
|
||||
EXPORT void gs_leave_context(void);
|
||||
EXPORT graphics_t *gs_get_context(void);
|
||||
|
||||
EXPORT void gs_matrix_push(void);
|
||||
EXPORT void gs_matrix_pop(void);
|
||||
EXPORT void gs_matrix_identity(void);
|
||||
EXPORT void gs_matrix_transpose(void);
|
||||
EXPORT void gs_matrix_set(const struct matrix4 *matrix);
|
||||
EXPORT void gs_matrix_get(struct matrix4 *dst);
|
||||
EXPORT void gs_matrix_mul(const struct matrix4 *matrix);
|
||||
EXPORT void gs_matrix_rotquat(const struct quat *rot);
|
||||
EXPORT void gs_matrix_rotaa(const struct axisang *rot);
|
||||
EXPORT void gs_matrix_translate(const struct vec3 *pos);
|
||||
EXPORT void gs_matrix_scale(const struct vec3 *scale);
|
||||
EXPORT void gs_matrix_rotaa4f(float x, float y, float z, float angle);
|
||||
EXPORT void gs_matrix_translate3f(float x, float y, float z);
|
||||
EXPORT void gs_matrix_scale3f(float x, float y, float z);
|
||||
|
||||
EXPORT void gs_render_start(bool b_new);
|
||||
EXPORT void gs_render_stop(enum gs_draw_mode mode);
|
||||
EXPORT gs_vertbuffer_t *gs_render_save(void);
|
||||
EXPORT void gs_vertex2f(float x, float y);
|
||||
EXPORT void gs_vertex3f(float x, float y, float z);
|
||||
EXPORT void gs_normal3f(float x, float y, float z);
|
||||
EXPORT void gs_color(uint32_t color);
|
||||
EXPORT void gs_texcoord(float x, float y, int unit);
|
||||
EXPORT void gs_vertex2v(const struct vec2 *v);
|
||||
EXPORT void gs_vertex3v(const struct vec3 *v);
|
||||
EXPORT void gs_normal3v(const struct vec3 *v);
|
||||
EXPORT void gs_color4v(const struct vec4 *v);
|
||||
EXPORT void gs_texcoord2v(const struct vec2 *v, int unit);
|
||||
|
||||
EXPORT input_t *gs_get_input(void);
|
||||
EXPORT gs_effect_t *gs_get_effect(void);
|
||||
|
||||
EXPORT gs_effect_t *gs_effect_create_from_file(const char *file,
|
||||
char **error_string);
|
||||
EXPORT gs_effect_t *gs_effect_create(const char *effect_string,
|
||||
const char *filename, char **error_string);
|
||||
|
||||
EXPORT gs_shader_t *gs_vertexshader_create_from_file(const char *file,
|
||||
char **error_string);
|
||||
EXPORT gs_shader_t *gs_pixelshader_create_from_file(const char *file,
|
||||
char **error_string);
|
||||
|
||||
EXPORT gs_texture_t *gs_texture_create_from_file(const char *file);
|
||||
EXPORT uint8_t *gs_create_texture_file_data(const char *file,
|
||||
enum gs_color_format *format, uint32_t *cx, uint32_t *cy);
|
||||
|
||||
#define GS_FLIP_U (1<<0)
|
||||
#define GS_FLIP_V (1<<1)
|
||||
|
||||
/**
|
||||
* Draws a 2D sprite
|
||||
*
|
||||
* If width or height is 0, the width or height of the texture will be used.
|
||||
* The flip value specifies whether the texture shoudl be flipped on the U or V
|
||||
* axis with GS_FLIP_U and GS_FLIP_V.
|
||||
*/
|
||||
EXPORT void gs_draw_sprite(gs_texture_t *tex, uint32_t flip, uint32_t width,
|
||||
uint32_t height);
|
||||
|
||||
EXPORT void gs_draw_cube_backdrop(gs_texture_t *cubetex, const struct quat *rot,
|
||||
float left, float right, float top, float bottom, float znear);
|
||||
|
||||
/** sets the viewport to current swap chain size */
|
||||
EXPORT void gs_reset_viewport(void);
|
||||
|
||||
/** sets default screen-sized orthographich mode */
|
||||
EXPORT void gs_set_2d_mode(void);
|
||||
/** sets default screen-sized perspective mode */
|
||||
EXPORT void gs_set_3d_mode(double fovy, double znear, double zvar);
|
||||
|
||||
EXPORT void gs_viewport_push(void);
|
||||
EXPORT void gs_viewport_pop(void);
|
||||
|
||||
EXPORT void gs_texture_set_image(gs_texture_t *tex, const uint8_t *data,
|
||||
uint32_t linesize, bool invert);
|
||||
EXPORT void gs_cubetexture_set_image(gs_texture_t *cubetex, uint32_t side,
|
||||
const void *data, uint32_t linesize, bool invert);
|
||||
|
||||
EXPORT void gs_perspective(float fovy, float aspect, float znear, float zfar);
|
||||
|
||||
EXPORT void gs_blend_state_push(void);
|
||||
EXPORT void gs_blend_state_pop(void);
|
||||
EXPORT void gs_reset_blend_state(void);
|
||||
|
||||
/* -------------------------- */
|
||||
/* library-specific functions */
|
||||
|
||||
EXPORT gs_swapchain_t *gs_swapchain_create(const struct gs_init_data *data);
|
||||
|
||||
EXPORT void gs_resize(uint32_t x, uint32_t y);
|
||||
EXPORT void gs_get_size(uint32_t *x, uint32_t *y);
|
||||
EXPORT uint32_t gs_get_width(void);
|
||||
EXPORT uint32_t gs_get_height(void);
|
||||
|
||||
EXPORT gs_texture_t *gs_texture_create(uint32_t width, uint32_t height,
|
||||
enum gs_color_format color_format, uint32_t levels,
|
||||
const uint8_t **data, uint32_t flags);
|
||||
EXPORT gs_texture_t *gs_cubetexture_create(uint32_t size,
|
||||
enum gs_color_format color_format, uint32_t levels,
|
||||
const uint8_t **data, uint32_t flags);
|
||||
EXPORT gs_texture_t *gs_voltexture_create(uint32_t width, uint32_t height,
|
||||
uint32_t depth, enum gs_color_format color_format,
|
||||
uint32_t levels, const uint8_t **data, uint32_t flags);
|
||||
|
||||
EXPORT gs_zstencil_t *gs_zstencil_create(uint32_t width, uint32_t height,
|
||||
enum gs_zstencil_format format);
|
||||
|
||||
EXPORT gs_stagesurf_t *gs_stagesurface_create(uint32_t width, uint32_t height,
|
||||
enum gs_color_format color_format);
|
||||
|
||||
EXPORT gs_samplerstate_t *gs_samplerstate_create(
|
||||
const struct gs_sampler_info *info);
|
||||
|
||||
EXPORT gs_shader_t *gs_vertexshader_create(const char *shader,
|
||||
const char *file, char **error_string);
|
||||
EXPORT gs_shader_t *gs_pixelshader_create(const char *shader,
|
||||
const char *file, char **error_string);
|
||||
|
||||
EXPORT gs_vertbuffer_t *gs_vertexbuffer_create(struct gs_vb_data *data,
|
||||
uint32_t flags);
|
||||
EXPORT gs_indexbuffer_t *gs_indexbuffer_create(enum gs_index_type type,
|
||||
void *indices, size_t num, uint32_t flags);
|
||||
|
||||
EXPORT enum gs_texture_type gs_get_texture_type(const gs_texture_t *texture);
|
||||
|
||||
EXPORT void gs_load_vertexbuffer(gs_vertbuffer_t *vertbuffer);
|
||||
EXPORT void gs_load_indexbuffer(gs_indexbuffer_t *indexbuffer);
|
||||
EXPORT void gs_load_texture(gs_texture_t *tex, int unit);
|
||||
EXPORT void gs_load_samplerstate(gs_samplerstate_t *samplerstate, int unit);
|
||||
EXPORT void gs_load_vertexshader(gs_shader_t *vertshader);
|
||||
EXPORT void gs_load_pixelshader(gs_shader_t *pixelshader);
|
||||
|
||||
EXPORT void gs_load_default_samplerstate(bool b_3d, int unit);
|
||||
|
||||
EXPORT gs_shader_t *gs_get_vertex_shader(void);
|
||||
EXPORT gs_shader_t *gs_get_pixel_shader(void);
|
||||
|
||||
EXPORT gs_texture_t *gs_get_render_target(void);
|
||||
EXPORT gs_zstencil_t *gs_get_zstencil_target(void);
|
||||
|
||||
EXPORT void gs_set_render_target(gs_texture_t *tex, gs_zstencil_t *zstencil);
|
||||
EXPORT void gs_set_cube_render_target(gs_texture_t *cubetex, int side,
|
||||
gs_zstencil_t *zstencil);
|
||||
|
||||
EXPORT void gs_copy_texture(gs_texture_t *dst, gs_texture_t *src);
|
||||
EXPORT void gs_copy_texture_region(
|
||||
gs_texture_t *dst, uint32_t dst_x, uint32_t dst_y,
|
||||
gs_texture_t *src, uint32_t src_x, uint32_t src_y,
|
||||
uint32_t src_w, uint32_t src_h);
|
||||
EXPORT void gs_stage_texture(gs_stagesurf_t *dst, gs_texture_t *src);
|
||||
|
||||
EXPORT void gs_begin_scene(void);
|
||||
EXPORT void gs_draw(enum gs_draw_mode draw_mode, uint32_t start_vert,
|
||||
uint32_t num_verts);
|
||||
EXPORT void gs_end_scene(void);
|
||||
|
||||
#define GS_CLEAR_COLOR (1<<0)
|
||||
#define GS_CLEAR_DEPTH (1<<1)
|
||||
#define GS_CLEAR_STENCIL (1<<2)
|
||||
|
||||
EXPORT void gs_load_swapchain(gs_swapchain_t *swapchain);
|
||||
EXPORT void gs_clear(uint32_t clear_flags, const struct vec4 *color,
|
||||
float depth, uint8_t stencil);
|
||||
EXPORT void gs_present(void);
|
||||
EXPORT void gs_flush(void);
|
||||
|
||||
EXPORT void gs_set_cull_mode(enum gs_cull_mode mode);
|
||||
EXPORT enum gs_cull_mode gs_get_cull_mode(void);
|
||||
|
||||
EXPORT void gs_enable_blending(bool enable);
|
||||
EXPORT void gs_enable_depth_test(bool enable);
|
||||
EXPORT void gs_enable_stencil_test(bool enable);
|
||||
EXPORT void gs_enable_stencil_write(bool enable);
|
||||
EXPORT void gs_enable_color(bool red, bool green, bool blue, bool alpha);
|
||||
|
||||
EXPORT void gs_blend_function(enum gs_blend_type src, enum gs_blend_type dest);
|
||||
EXPORT void gs_blend_function_separate(
|
||||
enum gs_blend_type src_c, enum gs_blend_type dest_c,
|
||||
enum gs_blend_type src_a, enum gs_blend_type dest_a);
|
||||
EXPORT void gs_depth_function(enum gs_depth_test test);
|
||||
|
||||
EXPORT void gs_stencil_function(enum gs_stencil_side side,
|
||||
enum gs_depth_test test);
|
||||
EXPORT void gs_stencil_op(enum gs_stencil_side side,
|
||||
enum gs_stencil_op_type fail,
|
||||
enum gs_stencil_op_type zfail,
|
||||
enum gs_stencil_op_type zpass);
|
||||
|
||||
EXPORT void gs_set_viewport(int x, int y, int width, int height);
|
||||
EXPORT void gs_get_viewport(struct gs_rect *rect);
|
||||
EXPORT void gs_set_scissor_rect(const struct gs_rect *rect);
|
||||
|
||||
EXPORT void gs_ortho(float left, float right, float top, float bottom,
|
||||
float znear, float zfar);
|
||||
EXPORT void gs_frustum(float left, float right, float top, float bottom,
|
||||
float znear, float zfar);
|
||||
|
||||
EXPORT void gs_projection_push(void);
|
||||
EXPORT void gs_projection_pop(void);
|
||||
|
||||
EXPORT void gs_swapchain_destroy(gs_swapchain_t *swapchain);
|
||||
|
||||
EXPORT void gs_texture_destroy(gs_texture_t *tex);
|
||||
EXPORT uint32_t gs_texture_get_width(const gs_texture_t *tex);
|
||||
EXPORT uint32_t gs_texture_get_height(const gs_texture_t *tex);
|
||||
EXPORT enum gs_color_format gs_texture_get_color_format(
|
||||
const gs_texture_t *tex);
|
||||
EXPORT bool gs_texture_map(gs_texture_t *tex, uint8_t **ptr,
|
||||
uint32_t *linesize);
|
||||
EXPORT void gs_texture_unmap(gs_texture_t *tex);
|
||||
/** special-case function (GL only) - specifies whether the texture is a
|
||||
* GL_TEXTURE_RECTANGLE type, which doesn't use normalized texture
|
||||
* coordinates, doesn't support mipmapping, and requires address clamping */
|
||||
EXPORT bool gs_texture_is_rect(const gs_texture_t *tex);
|
||||
/**
|
||||
* Gets a pointer to the context-specific object associated with the texture.
|
||||
* For example, for GL, this is a GLuint*. For D3D11, ID3D11Texture2D*.
|
||||
*/
|
||||
EXPORT void *gs_texture_get_obj(gs_texture_t *tex);
|
||||
|
||||
EXPORT void gs_cubetexture_destroy(gs_texture_t *cubetex);
|
||||
EXPORT uint32_t gs_cubetexture_get_size(const gs_texture_t *cubetex);
|
||||
EXPORT enum gs_color_format gs_cubetexture_get_color_format(
|
||||
const gs_texture_t *cubetex);
|
||||
|
||||
EXPORT void gs_voltexture_destroy(gs_texture_t *voltex);
|
||||
EXPORT uint32_t gs_voltexture_get_width(const gs_texture_t *voltex);
|
||||
EXPORT uint32_t gs_voltexture_get_height(const gs_texture_t *voltex);
|
||||
EXPORT uint32_t gs_voltexture_get_depth(const gs_texture_t *voltex);
|
||||
EXPORT enum gs_color_format gs_voltexture_get_color_format(
|
||||
const gs_texture_t *voltex);
|
||||
|
||||
EXPORT void gs_stagesurface_destroy(gs_stagesurf_t *stagesurf);
|
||||
EXPORT uint32_t gs_stagesurface_get_width(const gs_stagesurf_t *stagesurf);
|
||||
EXPORT uint32_t gs_stagesurface_get_height(const gs_stagesurf_t *stagesurf);
|
||||
EXPORT enum gs_color_format gs_stagesurface_get_color_format(
|
||||
const gs_stagesurf_t *stagesurf);
|
||||
EXPORT bool gs_stagesurface_map(gs_stagesurf_t *stagesurf, uint8_t **data,
|
||||
uint32_t *linesize);
|
||||
EXPORT void gs_stagesurface_unmap(gs_stagesurf_t *stagesurf);
|
||||
|
||||
EXPORT void gs_zstencil_destroy(gs_zstencil_t *zstencil);
|
||||
|
||||
EXPORT void gs_samplerstate_destroy(gs_samplerstate_t *samplerstate);
|
||||
|
||||
EXPORT void gs_vertexbuffer_destroy(gs_vertbuffer_t *vertbuffer);
|
||||
EXPORT void gs_vertexbuffer_flush(gs_vertbuffer_t *vertbuffer);
|
||||
EXPORT struct gs_vb_data *gs_vertexbuffer_get_data(
|
||||
const gs_vertbuffer_t *vertbuffer);
|
||||
|
||||
EXPORT void gs_indexbuffer_destroy(gs_indexbuffer_t *indexbuffer);
|
||||
EXPORT void gs_indexbuffer_flush(gs_indexbuffer_t *indexbuffer);
|
||||
EXPORT void *gs_indexbuffer_get_data(const gs_indexbuffer_t *indexbuffer);
|
||||
EXPORT size_t gs_indexbuffer_get_num_indices(
|
||||
const gs_indexbuffer_t *indexbuffer);
|
||||
EXPORT enum gs_index_type gs_indexbuffer_get_type(
|
||||
const gs_indexbuffer_t *indexbuffer);
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
/** platform specific function for creating (GL_TEXTURE_RECTANGLE) textures
|
||||
* from shared surface resources */
|
||||
EXPORT gs_texture_t *gs_texture_create_from_iosurface(void *iosurf);
|
||||
EXPORT bool gs_texture_rebind_iosurface(gs_texture_t *texture,
|
||||
void *iosurf);
|
||||
|
||||
#elif _WIN32
|
||||
|
||||
EXPORT bool gs_gdi_texture_available(void);
|
||||
EXPORT bool gs_shared_texture_available(void);
|
||||
|
||||
struct gs_duplicator;
|
||||
typedef struct gs_duplicator gs_duplicator_t;
|
||||
|
||||
/**
|
||||
* Gets information about the monitor at the specific index, returns false
|
||||
* when there is no monitor at the specified index
|
||||
*/
|
||||
EXPORT bool gs_get_duplicator_monitor_info(int monitor_idx,
|
||||
struct gs_monitor_info *monitor_info);
|
||||
|
||||
/** creates a windows 8+ output duplicator (monitor capture) */
|
||||
EXPORT gs_duplicator_t *gs_duplicator_create(int monitor_idx);
|
||||
EXPORT void gs_duplicator_destroy(gs_duplicator_t *duplicator);
|
||||
|
||||
EXPORT bool gs_duplicator_update_frame(gs_duplicator_t *duplicator);
|
||||
EXPORT gs_texture_t *gs_duplicator_get_texture(gs_duplicator_t *duplicator);
|
||||
|
||||
/** creates a windows GDI-lockable texture */
|
||||
EXPORT gs_texture_t *gs_texture_create_gdi(uint32_t width, uint32_t height);
|
||||
|
||||
EXPORT void *gs_texture_get_dc(gs_texture_t *gdi_tex);
|
||||
EXPORT void gs_texture_release_dc(gs_texture_t *gdi_tex);
|
||||
|
||||
/** creates a windows shared texture from a texture handle */
|
||||
EXPORT gs_texture_t *gs_texture_open_shared(uint32_t handle);
|
||||
#endif
|
||||
|
||||
/* inline functions used by modules */
|
||||
|
||||
static inline uint32_t gs_get_format_bpp(enum gs_color_format format)
|
||||
{
|
||||
switch (format) {
|
||||
case GS_A8: return 8;
|
||||
case GS_R8: return 8;
|
||||
case GS_RGBA: return 32;
|
||||
case GS_BGRX: return 32;
|
||||
case GS_BGRA: return 32;
|
||||
case GS_R10G10B10A2: return 32;
|
||||
case GS_RGBA16: return 64;
|
||||
case GS_R16: return 16;
|
||||
case GS_RGBA16F: return 64;
|
||||
case GS_RGBA32F: return 128;
|
||||
case GS_RG16F: return 32;
|
||||
case GS_RG32F: return 64;
|
||||
case GS_R16F: return 16;
|
||||
case GS_R32F: return 32;
|
||||
case GS_DXT1: return 4;
|
||||
case GS_DXT3: return 8;
|
||||
case GS_DXT5: return 8;
|
||||
case GS_UNKNOWN: return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool gs_is_compressed_format(enum gs_color_format format)
|
||||
{
|
||||
return (format == GS_DXT1 || format == GS_DXT3 || format == GS_DXT5);
|
||||
}
|
||||
|
||||
static inline uint32_t gs_get_total_levels(uint32_t width, uint32_t height)
|
||||
{
|
||||
uint32_t size = width > height ? width : height;
|
||||
uint32_t num_levels = 0;
|
||||
|
||||
while (size > 1) {
|
||||
size /= 2;
|
||||
num_levels++;
|
||||
}
|
||||
|
||||
return num_levels;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
326
libobs/graphics/image-file.c
Normal file
326
libobs/graphics/image-file.c
Normal file
|
|
@ -0,0 +1,326 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2016 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "image-file.h"
|
||||
#include "../util/base.h"
|
||||
#include "../util/platform.h"
|
||||
|
||||
#define blog(level, format, ...) \
|
||||
blog(level, "%s: " format, __FUNCTION__, __VA_ARGS__)
|
||||
|
||||
static void *bi_def_bitmap_create(int width, int height)
|
||||
{
|
||||
return bmalloc(width * height * 4);
|
||||
}
|
||||
|
||||
static void bi_def_bitmap_set_opaque(void *bitmap, bool opaque)
|
||||
{
|
||||
UNUSED_PARAMETER(bitmap);
|
||||
UNUSED_PARAMETER(opaque);
|
||||
}
|
||||
|
||||
static bool bi_def_bitmap_test_opaque(void *bitmap)
|
||||
{
|
||||
UNUSED_PARAMETER(bitmap);
|
||||
return false;
|
||||
}
|
||||
|
||||
static unsigned char *bi_def_bitmap_get_buffer(void *bitmap)
|
||||
{
|
||||
return (unsigned char*)bitmap;
|
||||
}
|
||||
|
||||
static void bi_def_bitmap_destroy(void *bitmap)
|
||||
{
|
||||
bfree(bitmap);
|
||||
}
|
||||
|
||||
static void bi_def_bitmap_modified(void *bitmap)
|
||||
{
|
||||
UNUSED_PARAMETER(bitmap);
|
||||
}
|
||||
|
||||
static inline int get_full_decoded_gif_size(gs_image_file_t *image)
|
||||
{
|
||||
return image->gif.width * image->gif.height * 4 * image->gif.frame_count;
|
||||
}
|
||||
|
||||
static bool init_animated_gif(gs_image_file_t *image, const char *path)
|
||||
{
|
||||
bool is_animated_gif = true;
|
||||
gif_result result;
|
||||
uint64_t max_size;
|
||||
size_t size;
|
||||
FILE *file;
|
||||
|
||||
image->bitmap_callbacks.bitmap_create = bi_def_bitmap_create;
|
||||
image->bitmap_callbacks.bitmap_destroy = bi_def_bitmap_destroy;
|
||||
image->bitmap_callbacks.bitmap_get_buffer = bi_def_bitmap_get_buffer;
|
||||
image->bitmap_callbacks.bitmap_modified = bi_def_bitmap_modified;
|
||||
image->bitmap_callbacks.bitmap_set_opaque = bi_def_bitmap_set_opaque;
|
||||
image->bitmap_callbacks.bitmap_test_opaque = bi_def_bitmap_test_opaque;
|
||||
|
||||
gif_create(&image->gif, &image->bitmap_callbacks);
|
||||
|
||||
file = os_fopen(path, "rb");
|
||||
if (!file) {
|
||||
blog(LOG_WARNING, "Failed to open file '%s'", path);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
size = (size_t)os_ftelli64(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
image->gif_data = bmalloc(size);
|
||||
fread(image->gif_data, 1, size, file);
|
||||
|
||||
do {
|
||||
result = gif_initialise(&image->gif, size, image->gif_data);
|
||||
if (result < 0) {
|
||||
blog(LOG_WARNING, "Failed to initialize gif '%s', "
|
||||
"possible file corruption", path);
|
||||
goto fail;
|
||||
}
|
||||
} while (result != GIF_OK);
|
||||
|
||||
if (image->gif.width > 4096 || image->gif.height > 4096) {
|
||||
blog(LOG_WARNING, "Bad texture dimensions (%dx%d) in '%s'",
|
||||
image->gif.width, image->gif.height, path);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
max_size = (uint64_t)image->gif.width * (uint64_t)image->gif.height *
|
||||
(uint64_t)image->gif.frame_count * 4LLU;
|
||||
|
||||
if ((uint64_t)get_full_decoded_gif_size(image) != max_size) {
|
||||
blog(LOG_WARNING, "Gif '%s' overflowed maximum pointer size",
|
||||
path);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
image->is_animated_gif = (image->gif.frame_count > 1 && result >= 0);
|
||||
if (image->is_animated_gif) {
|
||||
gif_decode_frame(&image->gif, 0);
|
||||
|
||||
image->animation_frame_cache = bzalloc(
|
||||
image->gif.frame_count * sizeof(uint8_t*));
|
||||
image->animation_frame_data = bzalloc(
|
||||
get_full_decoded_gif_size(image));
|
||||
|
||||
for (unsigned int i = 0; i < image->gif.frame_count; i++) {
|
||||
if (gif_decode_frame(&image->gif, i) != GIF_OK)
|
||||
blog(LOG_WARNING, "Couldn't decode frame %u "
|
||||
"of '%s'", i, path);
|
||||
}
|
||||
|
||||
gif_decode_frame(&image->gif, 0);
|
||||
|
||||
image->cx = (uint32_t)image->gif.width;
|
||||
image->cy = (uint32_t)image->gif.height;
|
||||
image->format = GS_RGBA;
|
||||
} else {
|
||||
gif_finalise(&image->gif);
|
||||
bfree(image->gif_data);
|
||||
image->gif_data = NULL;
|
||||
is_animated_gif = false;
|
||||
goto not_animated;
|
||||
}
|
||||
|
||||
image->loaded = true;
|
||||
|
||||
fail:
|
||||
if (!image->loaded)
|
||||
gs_image_file_free(image);
|
||||
not_animated:
|
||||
if (file)
|
||||
fclose(file);
|
||||
|
||||
return is_animated_gif;
|
||||
}
|
||||
|
||||
void gs_image_file_init(gs_image_file_t *image, const char *file)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
if (!image)
|
||||
return;
|
||||
|
||||
memset(image, 0, sizeof(*image));
|
||||
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
len = strlen(file);
|
||||
|
||||
if (len > 4 && strcmp(file + len - 4, ".gif") == 0) {
|
||||
if (init_animated_gif(image, file))
|
||||
return;
|
||||
}
|
||||
|
||||
image->texture_data = gs_create_texture_file_data(file,
|
||||
&image->format, &image->cx, &image->cy);
|
||||
|
||||
image->loaded = !!image->texture_data;
|
||||
if (!image->loaded) {
|
||||
blog(LOG_WARNING, "Failed to load file '%s'", file);
|
||||
gs_image_file_free(image);
|
||||
}
|
||||
}
|
||||
|
||||
void gs_image_file_free(gs_image_file_t *image)
|
||||
{
|
||||
if (!image)
|
||||
return;
|
||||
|
||||
if (image->loaded) {
|
||||
if (image->is_animated_gif) {
|
||||
gif_finalise(&image->gif);
|
||||
bfree(image->animation_frame_cache);
|
||||
bfree(image->animation_frame_data);
|
||||
}
|
||||
|
||||
gs_texture_destroy(image->texture);
|
||||
}
|
||||
|
||||
bfree(image->texture_data);
|
||||
bfree(image->gif_data);
|
||||
memset(image, 0, sizeof(*image));
|
||||
}
|
||||
|
||||
void gs_image_file_init_texture(gs_image_file_t *image)
|
||||
{
|
||||
if (!image->loaded)
|
||||
return;
|
||||
|
||||
if (image->is_animated_gif) {
|
||||
image->texture = gs_texture_create(
|
||||
image->cx, image->cy, image->format, 1,
|
||||
(const uint8_t**)&image->gif.frame_image,
|
||||
GS_DYNAMIC);
|
||||
|
||||
} else {
|
||||
image->texture = gs_texture_create(
|
||||
image->cx, image->cy, image->format, 1,
|
||||
(const uint8_t**)&image->texture_data, 0);
|
||||
bfree(image->texture_data);
|
||||
image->texture_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint64_t get_time(gs_image_file_t *image, int i)
|
||||
{
|
||||
uint64_t val = (uint64_t)image->gif.frames[i].frame_delay * 10000000ULL;
|
||||
if (!val)
|
||||
val = 100000000;
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline int calculate_new_frame(gs_image_file_t *image,
|
||||
uint64_t elapsed_time_ns, int loops)
|
||||
{
|
||||
int new_frame = image->cur_frame;
|
||||
|
||||
image->cur_time += elapsed_time_ns;
|
||||
for (;;) {
|
||||
uint64_t t = get_time(image, new_frame);
|
||||
if (image->cur_time <= t)
|
||||
break;
|
||||
|
||||
image->cur_time -= t;
|
||||
if ((unsigned int)++new_frame == image->gif.frame_count) {
|
||||
if (!loops || ++image->cur_loop < loops) {
|
||||
new_frame = 0;
|
||||
} else if (image->cur_loop == loops) {
|
||||
new_frame--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new_frame;
|
||||
}
|
||||
|
||||
static void decode_new_frame(gs_image_file_t *image, int new_frame)
|
||||
{
|
||||
if (!image->animation_frame_cache[new_frame]) {
|
||||
int last_frame;
|
||||
|
||||
/* if looped, decode frame 0 */
|
||||
last_frame = (new_frame < image->last_decoded_frame) ?
|
||||
0 : image->last_decoded_frame + 1;
|
||||
|
||||
/* decode missed frames */
|
||||
for (int i = last_frame; i < new_frame; i++) {
|
||||
if (gif_decode_frame(&image->gif, i) != GIF_OK)
|
||||
return;
|
||||
}
|
||||
|
||||
/* decode actual desired frame */
|
||||
if (gif_decode_frame(&image->gif, new_frame) == GIF_OK) {
|
||||
size_t pos = new_frame * image->gif.width *
|
||||
image->gif.height * 4;
|
||||
image->animation_frame_cache[new_frame] =
|
||||
image->animation_frame_data + pos;
|
||||
|
||||
memcpy(image->animation_frame_cache[new_frame],
|
||||
image->gif.frame_image,
|
||||
image->gif.width *
|
||||
image->gif.height * 4);
|
||||
|
||||
image->last_decoded_frame = new_frame;
|
||||
}
|
||||
}
|
||||
|
||||
image->cur_frame = new_frame;
|
||||
}
|
||||
|
||||
bool gs_image_file_tick(gs_image_file_t *image, uint64_t elapsed_time_ns)
|
||||
{
|
||||
int loops;
|
||||
|
||||
if (!image->is_animated_gif || !image->loaded)
|
||||
return false;
|
||||
|
||||
loops = image->gif.loop_count;
|
||||
if (loops >= 0xFFFF)
|
||||
loops = 0;
|
||||
|
||||
if (!loops || image->cur_loop < loops) {
|
||||
int new_frame = calculate_new_frame(image, elapsed_time_ns,
|
||||
loops);
|
||||
|
||||
if (new_frame != image->cur_frame) {
|
||||
decode_new_frame(image, new_frame);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void gs_image_file_update_texture(gs_image_file_t *image)
|
||||
{
|
||||
if (!image->is_animated_gif || !image->loaded)
|
||||
return;
|
||||
|
||||
if (image->animation_frame_cache[image->cur_frame]) {
|
||||
gs_texture_set_image(image->texture,
|
||||
image->animation_frame_cache[image->cur_frame],
|
||||
image->gif.width * 4, false);
|
||||
}
|
||||
}
|
||||
53
libobs/graphics/image-file.h
Normal file
53
libobs/graphics/image-file.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2016 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "graphics.h"
|
||||
#include "libnsgif/libnsgif.h"
|
||||
|
||||
struct gs_image_file {
|
||||
gs_texture_t *texture;
|
||||
enum gs_color_format format;
|
||||
uint32_t cx;
|
||||
uint32_t cy;
|
||||
bool is_animated_gif;
|
||||
bool frame_updated;
|
||||
bool loaded;
|
||||
|
||||
gif_animation gif;
|
||||
uint8_t *gif_data;
|
||||
uint8_t **animation_frame_cache;
|
||||
uint8_t *animation_frame_data;
|
||||
uint64_t cur_time;
|
||||
int cur_frame;
|
||||
int cur_loop;
|
||||
int last_decoded_frame;
|
||||
|
||||
uint8_t *texture_data;
|
||||
gif_bitmap_callback_vt bitmap_callbacks;
|
||||
};
|
||||
|
||||
typedef struct gs_image_file gs_image_file_t;
|
||||
|
||||
EXPORT void gs_image_file_init(gs_image_file_t *image, const char *file);
|
||||
EXPORT void gs_image_file_free(gs_image_file_t *image);
|
||||
|
||||
EXPORT void gs_image_file_init_texture(gs_image_file_t *image);
|
||||
EXPORT bool gs_image_file_tick(gs_image_file_t *image,
|
||||
uint64_t elapsed_time_ns);
|
||||
EXPORT void gs_image_file_update_texture(gs_image_file_t *image);
|
||||
152
libobs/graphics/input.h
Normal file
152
libobs/graphics/input.h
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* TODO: incomplete/may not be necessary */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define KBC_ESCAPE 0x0
|
||||
#define KBC_1 0x1
|
||||
#define KBC_2 0x2
|
||||
#define KBC_3 0x3
|
||||
#define KBC_4 0x4
|
||||
#define KBC_5 0x5
|
||||
#define KBC_6 0x6
|
||||
#define KBC_7 0x7
|
||||
#define KBC_8 0x8
|
||||
#define KBC_9 0x9
|
||||
#define KBC_0 0xA
|
||||
#define KBC_MINUS 0xB
|
||||
#define KBC_EQUALS 0xC
|
||||
#define KBC_BACK 0xD
|
||||
#define KBC_TAB 0xE
|
||||
#define KBC_Q 0xF
|
||||
#define KBC_W 0x10
|
||||
#define KBC_E 0x11
|
||||
#define KBC_R 0x12
|
||||
#define KBC_T 0x13
|
||||
#define KBC_Y 0x14
|
||||
#define KBC_U 0x15
|
||||
#define KBC_I 0x16
|
||||
#define KBC_O 0x17
|
||||
#define KBC_P 0x18
|
||||
#define KBC_LBRACKET 0x19
|
||||
#define KBC_RBRACKET 0x1A
|
||||
#define KBC_RETURN 0x1B
|
||||
#define KBC_LCONTROL 0x1C
|
||||
#define KBC_A 0x1D
|
||||
#define KBC_S 0x1E
|
||||
#define KBC_D 0x1F
|
||||
#define KBC_F 0x20
|
||||
#define KBC_G 0x21
|
||||
#define KBC_H 0x22
|
||||
#define KBC_J 0x23
|
||||
#define KBC_K 0x24
|
||||
#define KBC_L 0x25
|
||||
#define KBC_SEMICOLON 0x26
|
||||
#define KBC_APOSTROPHE 0x27
|
||||
#define KBC_TILDE 0x28
|
||||
#define KBC_LSHIFT 0x29
|
||||
#define KBC_BACKSLASH 0x2A
|
||||
#define KBC_Z 0x2B
|
||||
#define KBC_X 0x2C
|
||||
#define KBC_C 0x2D
|
||||
#define KBC_V 0x2E
|
||||
#define KBC_B 0x2F
|
||||
#define KBC_N 0x30
|
||||
#define KBC_M 0x31
|
||||
#define KBC_COMMA 0x32
|
||||
#define KBC_PERIOD 0x33
|
||||
#define KBC_SLASH 0x34
|
||||
#define KBC_RSHIFT 0x35
|
||||
#define KBC_MULTIPLY 0x36
|
||||
#define KBC_LALT 0x37
|
||||
#define KBC_SPACE 0x38
|
||||
#define KBC_CAPSLOCK 0x39
|
||||
#define KBC_F1 0x3A
|
||||
#define KBC_F2 0x3B
|
||||
#define KBC_F3 0x3C
|
||||
#define KBC_F4 0x3D
|
||||
#define KBC_F5 0x3E
|
||||
#define KBC_F6 0x3F
|
||||
#define KBC_F7 0x40
|
||||
#define KBC_F8 0x41
|
||||
#define KBC_F9 0x42
|
||||
#define KBC_F10 0x43
|
||||
#define KBC_NUMLOCK 0x44
|
||||
#define KBC_SCROLLLOCK 0x45
|
||||
#define KBC_NUMPAD7 0x46
|
||||
#define KBC_NUMPAD8 0x47
|
||||
#define KBC_NUMPAD9 0x48
|
||||
#define KBC_SUBTRACT 0x49
|
||||
#define KBC_NUMPAD4 0x4A
|
||||
#define KBC_NUMPAD5 0x4B
|
||||
#define KBC_NUMPAD6 0x4C
|
||||
#define KBC_ADD 0x4D
|
||||
#define KBC_NUMPAD1 0x4E
|
||||
#define KBC_NUMPAD2 0x4F
|
||||
#define KBC_NUMPAD3 0x50
|
||||
#define KBC_NUMPAD0 0x51
|
||||
#define KBC_DECIMAL 0x52
|
||||
#define KBC_F11 0x53
|
||||
#define KBC_F12 0x54
|
||||
#define KBC_NUMPADENTER 0x55
|
||||
#define KBC_RCONTROL 0x56
|
||||
#define KBC_DIVIDE 0x57
|
||||
#define KBC_SYSRQ 0x58
|
||||
#define KBC_RALT 0x59
|
||||
#define KBC_PAUSE 0x5A
|
||||
#define KBC_HOME 0x5B
|
||||
#define KBC_UP 0x5C
|
||||
#define KBC_PAGEDOWN 0x5D
|
||||
#define KBC_LEFT 0x5E
|
||||
#define KBC_RIGHT 0x5F
|
||||
#define KBC_END 0x60
|
||||
#define KBC_DOWN 0x61
|
||||
#define KBC_PAGEUP 0x62
|
||||
#define KBC_INSERT 0x63
|
||||
#define KBC_DELETE 0x64
|
||||
|
||||
#define MOUSE_LEFTBUTTON 0x65
|
||||
#define MOUSE_MIDDLEBUTTON 0x66
|
||||
#define MOUSE_RIGHTBUTTON 0x67
|
||||
#define MOUSE_WHEEL 0x68
|
||||
#define MOUSE_MOVE 0x69
|
||||
|
||||
#define KBC_CONTROL 0xFFFFFFFE
|
||||
#define KBC_ALT 0xFFFFFFFD
|
||||
#define KBC_SHIFT 0xFFFFFFFC
|
||||
|
||||
#define STATE_LBUTTONDOWN (1<<0)
|
||||
#define STATE_RBUTTONDOWN (1<<1)
|
||||
#define STATE_MBUTTONDOWN (1<<2)
|
||||
#define STATE_X4BUTTONDOWN (1<<3)
|
||||
#define STATE_X5BUTTONDOWN (1<<4)
|
||||
|
||||
/* wrapped opaque data types */
|
||||
struct input_subsystem;
|
||||
typedef struct input_subsystem input_t;
|
||||
|
||||
EXPORT int input_getbuttonstate(input_t *input, uint32_t button);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
1274
libobs/graphics/libnsgif/libnsgif.c
Normal file
1274
libobs/graphics/libnsgif/libnsgif.c
Normal file
File diff suppressed because it is too large
Load diff
142
libobs/graphics/libnsgif/libnsgif.h
Normal file
142
libobs/graphics/libnsgif/libnsgif.h
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright 2004 Richard Wilson <richard.wilson@netsurf-browser.org>
|
||||
* Copyright 2008 Sean Fox <dyntryx@gmail.com>
|
||||
*
|
||||
* This file is part of NetSurf's libnsgif, http://www.netsurf-browser.org/
|
||||
* Licenced under the MIT License,
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* Progressive animated GIF file decoding (interface).
|
||||
*/
|
||||
|
||||
#ifndef _LIBNSGIF_H_
|
||||
#define _LIBNSGIF_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
/* Error return values
|
||||
*/
|
||||
typedef enum {
|
||||
GIF_WORKING = 1,
|
||||
GIF_OK = 0,
|
||||
GIF_INSUFFICIENT_FRAME_DATA = -1,
|
||||
GIF_FRAME_DATA_ERROR = -2,
|
||||
GIF_INSUFFICIENT_DATA = -3,
|
||||
GIF_DATA_ERROR = -4,
|
||||
GIF_INSUFFICIENT_MEMORY = -5,
|
||||
GIF_FRAME_NO_DISPLAY = -6,
|
||||
GIF_END_OF_FRAME = -7
|
||||
} gif_result;
|
||||
|
||||
/* Maximum LZW bits available
|
||||
*/
|
||||
#define GIF_MAX_LZW 12
|
||||
|
||||
/* The GIF frame data
|
||||
*/
|
||||
typedef struct gif_frame {
|
||||
bool display; /**< whether the frame should be displayed/animated */
|
||||
unsigned int frame_delay; /**< delay (in 100th second intervals) before animating the frame */
|
||||
/** Internal members are listed below
|
||||
*/
|
||||
unsigned int frame_pointer; /**< offset (in bytes) to the GIF frame data */
|
||||
bool virgin; /**< whether the frame has previously been used */
|
||||
bool opaque; /**< whether the frame is totally opaque */
|
||||
bool redraw_required; /**< whether a forcable screen redraw is required */
|
||||
unsigned char disposal_method; /**< how the previous frame should be disposed; affects plotting */
|
||||
bool transparency; /**< whether we acknoledge transparency */
|
||||
unsigned char transparency_index; /**< the index designating a transparent pixel */
|
||||
unsigned int redraw_x; /**< x co-ordinate of redraw rectangle */
|
||||
unsigned int redraw_y; /**< y co-ordinate of redraw rectangle */
|
||||
unsigned int redraw_width; /**< width of redraw rectangle */
|
||||
unsigned int redraw_height; /**< height of redraw rectangle */
|
||||
} gif_frame;
|
||||
|
||||
/* API for Bitmap callbacks
|
||||
*/
|
||||
typedef void* (*gif_bitmap_cb_create)(int width, int height);
|
||||
typedef void (*gif_bitmap_cb_destroy)(void *bitmap);
|
||||
typedef unsigned char* (*gif_bitmap_cb_get_buffer)(void *bitmap);
|
||||
typedef void (*gif_bitmap_cb_set_opaque)(void *bitmap, bool opaque);
|
||||
typedef bool (*gif_bitmap_cb_test_opaque)(void *bitmap);
|
||||
typedef void (*gif_bitmap_cb_modified)(void *bitmap);
|
||||
|
||||
/* The Bitmap callbacks function table
|
||||
*/
|
||||
typedef struct gif_bitmap_callback_vt {
|
||||
gif_bitmap_cb_create bitmap_create; /**< Create a bitmap. */
|
||||
gif_bitmap_cb_destroy bitmap_destroy; /**< Free a bitmap. */
|
||||
gif_bitmap_cb_get_buffer bitmap_get_buffer; /**< Return a pointer to the pixel data in a bitmap. */
|
||||
/** Members below are optional
|
||||
*/
|
||||
gif_bitmap_cb_set_opaque bitmap_set_opaque; /**< Sets whether a bitmap should be plotted opaque. */
|
||||
gif_bitmap_cb_test_opaque bitmap_test_opaque; /**< Tests whether a bitmap has an opaque alpha channel. */
|
||||
gif_bitmap_cb_modified bitmap_modified; /**< The bitmap image has changed, so flush any persistant cache. */
|
||||
} gif_bitmap_callback_vt;
|
||||
|
||||
/* The GIF animation data
|
||||
*/
|
||||
typedef struct gif_animation {
|
||||
gif_bitmap_callback_vt bitmap_callbacks; /**< callbacks for bitmap functions */
|
||||
unsigned char *gif_data; /**< pointer to GIF data */
|
||||
unsigned int width; /**< width of GIF (may increase during decoding) */
|
||||
unsigned int height; /**< heigth of GIF (may increase during decoding) */
|
||||
unsigned int frame_count; /**< number of frames decoded */
|
||||
unsigned int frame_count_partial; /**< number of frames partially decoded */
|
||||
gif_frame *frames; /**< decoded frames */
|
||||
int decoded_frame; /**< current frame decoded to bitmap */
|
||||
void *frame_image; /**< currently decoded image; stored as bitmap from bitmap_create callback */
|
||||
int loop_count; /**< number of times to loop animation */
|
||||
gif_result current_error; /**< current error type, or 0 for none*/
|
||||
/** Internal members are listed below
|
||||
*/
|
||||
unsigned int buffer_position; /**< current index into GIF data */
|
||||
unsigned int buffer_size; /**< total number of bytes of GIF data available */
|
||||
unsigned int frame_holders; /**< current number of frame holders */
|
||||
unsigned int background_index; /**< index in the colour table for the background colour */
|
||||
unsigned int aspect_ratio; /**< image aspect ratio (ignored) */
|
||||
unsigned int colour_table_size; /**< size of colour table (in entries) */
|
||||
bool global_colours; /**< whether the GIF has a global colour table */
|
||||
unsigned int *global_colour_table; /**< global colour table */
|
||||
unsigned int *local_colour_table; /**< local colour table */
|
||||
|
||||
|
||||
/* General LZW values. They are NO LONGER shared for all GIFs being decoded BECAUSE
|
||||
THAT IS A TERRIBLE IDEA TO SAVE 10Kb or so per GIF.
|
||||
*/
|
||||
unsigned char buf[4];
|
||||
unsigned char *direct;
|
||||
|
||||
int table[2][(1 << GIF_MAX_LZW)];
|
||||
unsigned char stack[(1 << GIF_MAX_LZW) * 2];
|
||||
unsigned char *stack_pointer;
|
||||
int code_size, set_code_size;
|
||||
int max_code, max_code_size;
|
||||
int clear_code, end_code;
|
||||
int curbit, lastbit, last_byte;
|
||||
int firstcode, oldcode;
|
||||
bool zero_data_block;
|
||||
bool get_done;
|
||||
|
||||
/* Whether to clear the decoded image rather than plot
|
||||
*/
|
||||
bool clear_image;
|
||||
} gif_animation;
|
||||
|
||||
void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks);
|
||||
gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data);
|
||||
gif_result gif_decode_frame(gif_animation *gif, unsigned int frame);
|
||||
void gif_finalise(gif_animation *gif);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
45
libobs/graphics/math-defs.h
Normal file
45
libobs/graphics/math-defs.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../util/c99defs.h"
|
||||
#include <math.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.1415926535897932384626433832795f
|
||||
#endif
|
||||
|
||||
#define RAD(val) ((val)*0.0174532925199432957692369076848f)
|
||||
#define DEG(val) ((val)*57.295779513082320876798154814105f)
|
||||
#define LARGE_EPSILON 1e-2f
|
||||
#define EPSILON 1e-4f
|
||||
#define TINY_EPSILON 1e-5f
|
||||
#define M_INFINITE 3.4e38f
|
||||
|
||||
static inline bool close_float(float f1, float f2, float precision)
|
||||
{
|
||||
return fabsf(f1-f2) <= precision;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
133
libobs/graphics/math-extra.c
Normal file
133
libobs/graphics/math-extra.c
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "vec2.h"
|
||||
#include "vec3.h"
|
||||
#include "math-defs.h"
|
||||
#include "math-extra.h"
|
||||
|
||||
void polar_to_cart(struct vec3 *dst, const struct vec3 *v)
|
||||
{
|
||||
struct vec3 cart;
|
||||
float sinx = cosf(v->x);
|
||||
float sinx_z = v->z * sinx;
|
||||
|
||||
cart.x = sinx_z * sinf(v->y);
|
||||
cart.z = sinx_z * cosf(v->y);
|
||||
cart.y = v->z * sinf(v->x);
|
||||
|
||||
vec3_copy(dst, &cart);
|
||||
}
|
||||
|
||||
void cart_to_polar(struct vec3 *dst, const struct vec3 *v)
|
||||
{
|
||||
struct vec3 polar;
|
||||
polar.z = vec3_len(v);
|
||||
|
||||
if (close_float(polar.z, 0.0f, EPSILON)) {
|
||||
vec3_zero(&polar);
|
||||
} else {
|
||||
polar.x = asinf(v->y / polar.z);
|
||||
polar.y = atan2f(v->x, v->z);
|
||||
}
|
||||
|
||||
vec3_copy(dst, &polar);
|
||||
}
|
||||
|
||||
void norm_to_polar(struct vec2 *dst, const struct vec3 *norm)
|
||||
{
|
||||
dst->x = atan2f(norm->x, norm->z);
|
||||
dst->y = asinf(norm->y);
|
||||
}
|
||||
|
||||
void polar_to_norm(struct vec3 *dst, const struct vec2 *polar)
|
||||
{
|
||||
float sinx = sinf(polar->x);
|
||||
|
||||
dst->x = sinx * cosf(polar->y);
|
||||
dst->y = sinx * sinf(polar->y);
|
||||
dst->z = cosf(polar->x);
|
||||
}
|
||||
|
||||
float calc_torquef(float val1, float val2, float torque, float min_adjust,
|
||||
float t)
|
||||
{
|
||||
float out = val1;
|
||||
float dist;
|
||||
bool over;
|
||||
|
||||
if (close_float(val1, val2, EPSILON))
|
||||
return val1;
|
||||
|
||||
dist = (val2-val1)*torque;
|
||||
over = dist > 0.0f;
|
||||
|
||||
if (over) {
|
||||
if (dist < min_adjust) /* prevents from going too slow */
|
||||
dist = min_adjust;
|
||||
out += dist*t; /* add torque */
|
||||
if (out > val2) /* clamp if overshoot */
|
||||
out = val2;
|
||||
} else {
|
||||
if (dist > -min_adjust)
|
||||
dist = -min_adjust;
|
||||
out += dist*t;
|
||||
if (out < val2)
|
||||
out = val2;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void calc_torque(struct vec3 *dst, const struct vec3 *v1,
|
||||
const struct vec3 *v2, float torque, float min_adjust,
|
||||
float t)
|
||||
{
|
||||
struct vec3 line, dir;
|
||||
float orig_dist, torque_dist, adjust_dist;
|
||||
|
||||
if (vec3_close(v1, v2, EPSILON)) {
|
||||
vec3_copy(dst, v1);
|
||||
return;
|
||||
}
|
||||
|
||||
vec3_sub(&line, v2, v1);
|
||||
orig_dist = vec3_len(&line);
|
||||
vec3_mulf(&dir, &line, 1.0f/orig_dist);
|
||||
|
||||
torque_dist = orig_dist*torque; /* use distance to determine speed */
|
||||
if (torque_dist < min_adjust) /* prevent from going too slow */
|
||||
torque_dist = min_adjust;
|
||||
|
||||
adjust_dist = torque_dist*t;
|
||||
|
||||
if (adjust_dist <= (orig_dist-LARGE_EPSILON)) {
|
||||
vec3_mulf(dst, &dir, adjust_dist);
|
||||
vec3_add(dst, dst, v1); /* add torque */
|
||||
} else {
|
||||
vec3_copy(dst, v2); /* clamp if overshoot */
|
||||
}
|
||||
}
|
||||
|
||||
float rand_float(int positive_only)
|
||||
{
|
||||
if (positive_only)
|
||||
return (float)((double)rand()/(double)RAND_MAX);
|
||||
else
|
||||
return (float)(((double)rand()/(double)RAND_MAX*2.0)-1.0);
|
||||
}
|
||||
63
libobs/graphics/math-extra.h
Normal file
63
libobs/graphics/math-extra.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../util/c99defs.h"
|
||||
|
||||
/*
|
||||
* A few general math functions that I couldn't really decide where to put.
|
||||
*
|
||||
* Polar/Cart conversion, torque functions (for smooth movement), percentage,
|
||||
* random floats.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct vec2;
|
||||
struct vec3;
|
||||
|
||||
EXPORT void polar_to_cart(struct vec3 *dst, const struct vec3 *v);
|
||||
EXPORT void cart_to_polar(struct vec3 *dst, const struct vec3 *v);
|
||||
|
||||
EXPORT void norm_to_polar(struct vec2 *dst, const struct vec3 *norm);
|
||||
EXPORT void polar_to_norm(struct vec3 *dst, const struct vec2 *polar);
|
||||
|
||||
EXPORT float calc_torquef(float val1, float val2, float torque,
|
||||
float min_adjust, float t);
|
||||
|
||||
EXPORT void calc_torque(struct vec3 *dst, const struct vec3 *v1,
|
||||
const struct vec3 *v2, float torque, float min_adjust,
|
||||
float t);
|
||||
|
||||
static inline float get_percentage(float start, float end, float mid)
|
||||
{
|
||||
return (mid-start) / (end-start);
|
||||
}
|
||||
|
||||
static inline float get_percentagei(int start, int end, int mid)
|
||||
{
|
||||
return (float)(mid-start) / (float)(end-start);
|
||||
}
|
||||
|
||||
EXPORT float rand_float(int positive_only);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
145
libobs/graphics/matrix3.c
Normal file
145
libobs/graphics/matrix3.c
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "matrix3.h"
|
||||
#include "matrix4.h"
|
||||
#include "plane.h"
|
||||
#include "quat.h"
|
||||
|
||||
|
||||
void matrix3_from_quat(struct matrix3 *dst, const struct quat *q)
|
||||
{
|
||||
float norm = quat_dot(q, q);
|
||||
float s = (norm > 0.0f) ? (2.0f/norm) : 0.0f;
|
||||
|
||||
float xx = q->x * q->x * s;
|
||||
float yy = q->y * q->y * s;
|
||||
float zz = q->z * q->z * s;
|
||||
float xy = q->x * q->y * s;
|
||||
float xz = q->x * q->z * s;
|
||||
float yz = q->y * q->z * s;
|
||||
float wx = q->w * q->x * s;
|
||||
float wy = q->w * q->y * s;
|
||||
float wz = q->w * q->z * s;
|
||||
|
||||
vec3_set(&dst->x, 1.0f - (yy + zz), xy + wz, xz - wy);
|
||||
vec3_set(&dst->y, xy - wz, 1.0f - (xx + zz), yz + wx);
|
||||
vec3_set(&dst->z, xz + wy, yz - wx, 1.0f - (xx + yy));
|
||||
vec3_zero(&dst->t);
|
||||
}
|
||||
|
||||
void matrix3_from_axisang(struct matrix3 *dst, const struct axisang *aa)
|
||||
{
|
||||
struct quat q;
|
||||
quat_from_axisang(&q, aa);
|
||||
matrix3_from_quat(dst, &q);
|
||||
}
|
||||
|
||||
void matrix3_from_matrix4(struct matrix3 *dst, const struct matrix4 *m)
|
||||
{
|
||||
dst->x.m = m->x.m;
|
||||
dst->y.m = m->y.m;
|
||||
dst->z.m = m->z.m;
|
||||
dst->t.m = m->t.m;
|
||||
dst->x.w = 0.0f;
|
||||
dst->y.w = 0.0f;
|
||||
dst->z.w = 0.0f;
|
||||
dst->t.w = 0.0f;
|
||||
}
|
||||
|
||||
void matrix3_mul(struct matrix3 *dst, const struct matrix3 *m1,
|
||||
const struct matrix3 *m2)
|
||||
{
|
||||
if (dst == m2) {
|
||||
struct matrix3 temp;
|
||||
vec3_rotate(&temp.x, &m1->x, m2);
|
||||
vec3_rotate(&temp.y, &m1->y, m2);
|
||||
vec3_rotate(&temp.z, &m1->z, m2);
|
||||
vec3_transform3x4(&temp.t, &m1->t, m2);
|
||||
matrix3_copy(dst, &temp);
|
||||
} else {
|
||||
vec3_rotate(&dst->x, &m1->x, m2);
|
||||
vec3_rotate(&dst->y, &m1->y, m2);
|
||||
vec3_rotate(&dst->z, &m1->z, m2);
|
||||
vec3_transform3x4(&dst->t, &m1->t, m2);
|
||||
}
|
||||
}
|
||||
|
||||
void matrix3_rotate(struct matrix3 *dst, const struct matrix3 *m,
|
||||
const struct quat *q)
|
||||
{
|
||||
struct matrix3 temp;
|
||||
matrix3_from_quat(&temp, q);
|
||||
matrix3_mul(dst, m, &temp);
|
||||
}
|
||||
|
||||
void matrix3_rotate_aa(struct matrix3 *dst, const struct matrix3 *m,
|
||||
const struct axisang *aa)
|
||||
{
|
||||
struct matrix3 temp;
|
||||
matrix3_from_axisang(&temp, aa);
|
||||
matrix3_mul(dst, m, &temp);
|
||||
}
|
||||
|
||||
void matrix3_scale(struct matrix3 *dst, const struct matrix3 *m,
|
||||
const struct vec3 *v)
|
||||
{
|
||||
vec3_mul(&dst->x, &m->x, v);
|
||||
vec3_mul(&dst->y, &m->y, v);
|
||||
vec3_mul(&dst->z, &m->z, v);
|
||||
vec3_mul(&dst->t, &m->t, v);
|
||||
}
|
||||
|
||||
void matrix3_transpose(struct matrix3 *dst, const struct matrix3 *m)
|
||||
{
|
||||
__m128 tmp1, tmp2;
|
||||
vec3_rotate(&dst->t, &m->t, m);
|
||||
vec3_neg(&dst->t, &dst->t);
|
||||
|
||||
tmp1 = _mm_movelh_ps(m->x.m, m->y.m);
|
||||
tmp2 = _mm_movehl_ps(m->y.m, m->x.m);
|
||||
dst->x.m = _mm_shuffle_ps(tmp1, m->z.m, _MM_SHUFFLE(3, 0, 2, 0));
|
||||
dst->y.m = _mm_shuffle_ps(tmp1, m->z.m, _MM_SHUFFLE(3, 1, 3, 1));
|
||||
dst->z.m = _mm_shuffle_ps(tmp2, m->z.m, _MM_SHUFFLE(3, 2, 2, 0));
|
||||
}
|
||||
|
||||
void matrix3_inv(struct matrix3 *dst, const struct matrix3 *m)
|
||||
{
|
||||
struct matrix4 m4;
|
||||
matrix4_from_matrix3(&m4, m);
|
||||
matrix4_inv((struct matrix4*)dst, &m4);
|
||||
dst->t.w = 0.0f;
|
||||
}
|
||||
|
||||
void matrix3_mirror(struct matrix3 *dst, const struct matrix3 *m,
|
||||
const struct plane *p)
|
||||
{
|
||||
vec3_mirrorv(&dst->x, &m->x, &p->dir);
|
||||
vec3_mirrorv(&dst->y, &m->y, &p->dir);
|
||||
vec3_mirrorv(&dst->z, &m->z, &p->dir);
|
||||
vec3_mirror(&dst->t, &m->t, p);
|
||||
}
|
||||
|
||||
void matrix3_mirrorv(struct matrix3 *dst, const struct matrix3 *m,
|
||||
const struct vec3 *v)
|
||||
{
|
||||
vec3_mirrorv(&dst->x, &m->x, v);
|
||||
vec3_mirrorv(&dst->y, &m->y, v);
|
||||
vec3_mirrorv(&dst->z, &m->z, v);
|
||||
vec3_mirrorv(&dst->t, &m->t, v);
|
||||
}
|
||||
108
libobs/graphics/matrix3.h
Normal file
108
libobs/graphics/matrix3.h
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vec3.h"
|
||||
#include "axisang.h"
|
||||
|
||||
/* 3x4 Matrix */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct matrix4;
|
||||
|
||||
struct matrix3 {
|
||||
struct vec3 x;
|
||||
struct vec3 y;
|
||||
struct vec3 z;
|
||||
struct vec3 t;
|
||||
};
|
||||
|
||||
static inline void matrix3_copy(struct matrix3 *dst, const struct matrix3 *m)
|
||||
{
|
||||
vec3_copy(&dst->x, &m->x);
|
||||
vec3_copy(&dst->y, &m->y);
|
||||
vec3_copy(&dst->z, &m->z);
|
||||
vec3_copy(&dst->t, &m->t);
|
||||
}
|
||||
|
||||
static inline void matrix3_identity(struct matrix3 *dst)
|
||||
{
|
||||
vec3_zero(&dst->x);
|
||||
vec3_zero(&dst->y);
|
||||
vec3_zero(&dst->z);
|
||||
vec3_zero(&dst->t);
|
||||
dst->x.x = dst->y.y = dst->z.z = 1.0f;
|
||||
}
|
||||
|
||||
EXPORT void matrix3_from_quat(struct matrix3 *dst, const struct quat *q);
|
||||
EXPORT void matrix3_from_axisang(struct matrix3 *dst,
|
||||
const struct axisang *aa);
|
||||
EXPORT void matrix3_from_matrix4(struct matrix3 *dst, const struct matrix4 *m);
|
||||
|
||||
EXPORT void matrix3_mul(struct matrix3 *dst, const struct matrix3 *m1,
|
||||
const struct matrix3 *m2);
|
||||
static inline void matrix3_translate(struct matrix3 *dst,
|
||||
const struct matrix3 *m, const struct vec3 *v)
|
||||
{
|
||||
vec3_sub(&dst->t, &m->t, v);
|
||||
}
|
||||
|
||||
EXPORT void matrix3_rotate(struct matrix3 *dst, const struct matrix3 *m,
|
||||
const struct quat *q);
|
||||
EXPORT void matrix3_rotate_aa(struct matrix3 *dst, const struct matrix3 *m,
|
||||
const struct axisang *aa);
|
||||
EXPORT void matrix3_scale(struct matrix3 *dst, const struct matrix3 *m,
|
||||
const struct vec3 *v);
|
||||
EXPORT void matrix3_transpose(struct matrix3 *dst, const struct matrix3 *m);
|
||||
EXPORT void matrix3_inv(struct matrix3 *dst, const struct matrix3 *m);
|
||||
|
||||
EXPORT void matrix3_mirror(struct matrix3 *dst, const struct matrix3 *m,
|
||||
const struct plane *p);
|
||||
EXPORT void matrix3_mirrorv(struct matrix3 *dst, const struct matrix3 *m,
|
||||
const struct vec3 *v);
|
||||
|
||||
static inline void matrix3_translate3f(struct matrix3 *dst,
|
||||
const struct matrix3 *m, float x, float y, float z)
|
||||
{
|
||||
struct vec3 v;
|
||||
vec3_set(&v, x, y, z);
|
||||
matrix3_translate(dst, m, &v);
|
||||
}
|
||||
|
||||
static inline void matrix3_rotate_aa4f(struct matrix3 *dst,
|
||||
const struct matrix3 *m, float x, float y, float z, float rot)
|
||||
{
|
||||
struct axisang aa;
|
||||
axisang_set(&aa, x, y, z, rot);
|
||||
matrix3_rotate_aa(dst, m, &aa);
|
||||
}
|
||||
|
||||
static inline void matrix3_scale3f(struct matrix3 *dst,
|
||||
const struct matrix3 *m, float x, float y, float z)
|
||||
{
|
||||
struct vec3 v;
|
||||
vec3_set(&v, x, y, z);
|
||||
matrix3_scale(dst, m, &v);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
285
libobs/graphics/matrix4.c
Normal file
285
libobs/graphics/matrix4.c
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "math-defs.h"
|
||||
#include "matrix4.h"
|
||||
#include "matrix3.h"
|
||||
#include "quat.h"
|
||||
|
||||
void matrix4_from_matrix3(struct matrix4 *dst, const struct matrix3 *m)
|
||||
{
|
||||
dst->x.m = m->x.m;
|
||||
dst->y.m = m->y.m;
|
||||
dst->z.m = m->z.m;
|
||||
dst->t.m = m->t.m;
|
||||
dst->t.w = 1.0f;
|
||||
}
|
||||
|
||||
void matrix4_from_quat(struct matrix4 *dst, const struct quat *q)
|
||||
{
|
||||
float norm = quat_dot(q, q);
|
||||
float s = (norm > 0.0f) ? (2.0f/norm) : 0.0f;
|
||||
|
||||
float xx = q->x * q->x * s;
|
||||
float yy = q->y * q->y * s;
|
||||
float zz = q->z * q->z * s;
|
||||
float xy = q->x * q->y * s;
|
||||
float xz = q->x * q->z * s;
|
||||
float yz = q->y * q->z * s;
|
||||
float wx = q->w * q->x * s;
|
||||
float wy = q->w * q->y * s;
|
||||
float wz = q->w * q->z * s;
|
||||
|
||||
vec4_set(&dst->x, 1.0f - (yy + zz), xy + wz, xz - wy, 0.0f);
|
||||
vec4_set(&dst->y, xy - wz, 1.0f - (xx + zz), yz + wx, 0.0f);
|
||||
vec4_set(&dst->z, xz + wy, yz - wx, 1.0f - (xx + yy), 0.0f);
|
||||
vec4_set(&dst->t, 0.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
void matrix4_from_axisang(struct matrix4 *dst, const struct axisang *aa)
|
||||
{
|
||||
struct quat q;
|
||||
quat_from_axisang(&q, aa);
|
||||
matrix4_from_quat(dst, &q);
|
||||
}
|
||||
|
||||
void matrix4_mul(struct matrix4 *dst, const struct matrix4 *m1,
|
||||
const struct matrix4 *m2)
|
||||
{
|
||||
const struct vec4 *m1v = (const struct vec4*)m1;
|
||||
const float *m2f = (const float*)m2;
|
||||
struct vec4 out[4];
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
for (j=0; j<4; j++) {
|
||||
struct vec4 temp;
|
||||
vec4_set(&temp, m2f[j], m2f[j+4], m2f[j+8], m2f[j+12]);
|
||||
out[i].ptr[j] = vec4_dot(&m1v[i], &temp);
|
||||
}
|
||||
}
|
||||
|
||||
matrix4_copy(dst, (struct matrix4*)out);
|
||||
}
|
||||
|
||||
static inline void get_3x3_submatrix(float *dst, const struct matrix4 *m,
|
||||
int i, int j)
|
||||
{
|
||||
const float *mf = (const float *)m;
|
||||
int ti, tj, idst, jdst;
|
||||
|
||||
for (ti = 0; ti < 4; ti++) {
|
||||
if (ti < i)
|
||||
idst = ti;
|
||||
else if (ti > i)
|
||||
idst = ti-1;
|
||||
else
|
||||
continue;
|
||||
|
||||
for (tj = 0; tj < 4; tj++) {
|
||||
if (tj < j)
|
||||
jdst = tj;
|
||||
else if (tj > j)
|
||||
jdst = tj-1;
|
||||
else
|
||||
continue;
|
||||
|
||||
dst[(idst*3) + jdst] = mf[(ti*4) + tj];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline float get_3x3_determinant(const float *m)
|
||||
{
|
||||
return (m[0] * ((m[4]*m[8]) - (m[7]*m[5]))) -
|
||||
(m[1] * ((m[3]*m[8]) - (m[6]*m[5]))) +
|
||||
(m[2] * ((m[3]*m[7]) - (m[6]*m[4])));
|
||||
}
|
||||
|
||||
float matrix4_determinant(const struct matrix4 *m)
|
||||
{
|
||||
const float *mf = (const float *)m;
|
||||
float det, result = 0.0f, i = 1.0f;
|
||||
float m3x3[9];
|
||||
int n;
|
||||
|
||||
for (n = 0; n < 4; n++, i *= -1.0f) {
|
||||
get_3x3_submatrix(m3x3, m, 0, n);
|
||||
|
||||
det = get_3x3_determinant(m3x3);
|
||||
result += mf[n] * det * i;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void matrix4_translate3v(struct matrix4 *dst, const struct matrix4 *m,
|
||||
const struct vec3 *v)
|
||||
{
|
||||
struct matrix4 temp;
|
||||
vec4_set(&temp.x, 1.0f, 0.0f, 0.0f, 0.0f);
|
||||
vec4_set(&temp.y, 0.0f, 1.0f, 0.0f, 0.0f);
|
||||
vec4_set(&temp.z, 0.0f, 0.0f, 1.0f, 0.0f);
|
||||
vec4_from_vec3(&temp.t, v);
|
||||
|
||||
matrix4_mul(dst, m, &temp);
|
||||
}
|
||||
|
||||
void matrix4_translate4v(struct matrix4 *dst, const struct matrix4 *m,
|
||||
const struct vec4 *v)
|
||||
{
|
||||
struct matrix4 temp;
|
||||
vec4_set(&temp.x, 1.0f, 0.0f, 0.0f, 0.0f);
|
||||
vec4_set(&temp.y, 0.0f, 1.0f, 0.0f, 0.0f);
|
||||
vec4_set(&temp.z, 0.0f, 0.0f, 1.0f, 0.0f);
|
||||
vec4_copy(&temp.t, v);
|
||||
|
||||
matrix4_mul(dst, m, &temp);
|
||||
}
|
||||
|
||||
void matrix4_rotate(struct matrix4 *dst, const struct matrix4 *m,
|
||||
const struct quat *q)
|
||||
{
|
||||
struct matrix4 temp;
|
||||
matrix4_from_quat(&temp, q);
|
||||
matrix4_mul(dst, m, &temp);
|
||||
}
|
||||
|
||||
void matrix4_rotate_aa(struct matrix4 *dst, const struct matrix4 *m,
|
||||
const struct axisang *aa)
|
||||
{
|
||||
struct matrix4 temp;
|
||||
matrix4_from_axisang(&temp, aa);
|
||||
matrix4_mul(dst, m, &temp);
|
||||
}
|
||||
|
||||
void matrix4_scale(struct matrix4 *dst, const struct matrix4 *m,
|
||||
const struct vec3 *v)
|
||||
{
|
||||
struct matrix4 temp;
|
||||
vec4_set(&temp.x, v->x, 0.0f, 0.0f, 0.0f);
|
||||
vec4_set(&temp.y, 0.0f, v->y, 0.0f, 0.0f);
|
||||
vec4_set(&temp.z, 0.0f, 0.0f, v->z, 0.0f);
|
||||
vec4_set(&temp.t, 0.0f, 0.0f, 0.0f, 1.0f);
|
||||
matrix4_mul(dst, m, &temp);
|
||||
}
|
||||
|
||||
void matrix4_translate3v_i(struct matrix4 *dst, const struct vec3 *v,
|
||||
const struct matrix4 *m)
|
||||
{
|
||||
struct matrix4 temp;
|
||||
vec4_set(&temp.x, 1.0f, 0.0f, 0.0f, 0.0f);
|
||||
vec4_set(&temp.y, 0.0f, 1.0f, 0.0f, 0.0f);
|
||||
vec4_set(&temp.z, 0.0f, 0.0f, 1.0f, 0.0f);
|
||||
vec4_from_vec3(&temp.t, v);
|
||||
|
||||
matrix4_mul(dst, &temp, m);
|
||||
}
|
||||
|
||||
void matrix4_translate4v_i(struct matrix4 *dst, const struct vec4 *v,
|
||||
const struct matrix4 *m)
|
||||
{
|
||||
struct matrix4 temp;
|
||||
vec4_set(&temp.x, 1.0f, 0.0f, 0.0f, 0.0f);
|
||||
vec4_set(&temp.y, 0.0f, 1.0f, 0.0f, 0.0f);
|
||||
vec4_set(&temp.z, 0.0f, 0.0f, 1.0f, 0.0f);
|
||||
vec4_copy(&temp.t, v);
|
||||
|
||||
matrix4_mul(dst, &temp, m);
|
||||
}
|
||||
|
||||
void matrix4_rotate_i(struct matrix4 *dst, const struct quat *q,
|
||||
const struct matrix4 *m)
|
||||
{
|
||||
struct matrix4 temp;
|
||||
matrix4_from_quat(&temp, q);
|
||||
matrix4_mul(dst, &temp, m);
|
||||
}
|
||||
|
||||
void matrix4_rotate_aa_i(struct matrix4 *dst, const struct axisang *aa,
|
||||
const struct matrix4 *m)
|
||||
{
|
||||
struct matrix4 temp;
|
||||
matrix4_from_axisang(&temp, aa);
|
||||
matrix4_mul(dst, &temp, m);
|
||||
}
|
||||
|
||||
void matrix4_scale_i(struct matrix4 *dst, const struct vec3 *v,
|
||||
const struct matrix4 *m)
|
||||
{
|
||||
struct matrix4 temp;
|
||||
vec4_set(&temp.x, v->x, 0.0f, 0.0f, 0.0f);
|
||||
vec4_set(&temp.y, 0.0f, v->y, 0.0f, 0.0f);
|
||||
vec4_set(&temp.z, 0.0f, 0.0f, v->z, 0.0f);
|
||||
vec4_set(&temp.t, 0.0f, 0.0f, 0.0f, 1.0f);
|
||||
matrix4_mul(dst, &temp, m);
|
||||
}
|
||||
|
||||
bool matrix4_inv(struct matrix4 *dst, const struct matrix4 *m)
|
||||
{
|
||||
struct vec4 *dstv = (struct vec4 *)dst;
|
||||
float det = matrix4_determinant(m);
|
||||
float m3x3[9];
|
||||
int i, j, sign;
|
||||
|
||||
if (dst == m) {
|
||||
struct matrix4 temp = *m;
|
||||
return matrix4_inv(dst, &temp);
|
||||
}
|
||||
|
||||
dstv = (struct vec4 *)dst;
|
||||
det = matrix4_determinant(m);
|
||||
|
||||
if (fabs(det) < 0.0005f)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
for (j = 0; j < 4; j++) {
|
||||
sign = 1 - ((i+j) % 2) * 2;
|
||||
get_3x3_submatrix(m3x3, m, i, j);
|
||||
dstv[j].ptr[i] = get_3x3_determinant(m3x3) *
|
||||
(float)sign / det;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void matrix4_transpose(struct matrix4 *dst, const struct matrix4 *m)
|
||||
{
|
||||
struct matrix4 temp;
|
||||
|
||||
/* TODO: Add SSE */
|
||||
temp.x.x = m->x.x;
|
||||
temp.x.y = m->y.x;
|
||||
temp.x.z = m->z.x;
|
||||
temp.x.w = m->t.x;
|
||||
temp.y.x = m->x.y;
|
||||
temp.y.y = m->y.y;
|
||||
temp.y.z = m->z.y;
|
||||
temp.y.w = m->t.y;
|
||||
temp.z.x = m->x.z;
|
||||
temp.z.y = m->y.z;
|
||||
temp.z.z = m->z.z;
|
||||
temp.z.w = m->t.z;
|
||||
temp.t.x = m->x.w;
|
||||
temp.t.y = m->y.w;
|
||||
temp.t.z = m->z.w;
|
||||
temp.t.w = m->t.w;
|
||||
|
||||
matrix4_copy(dst, &temp);
|
||||
}
|
||||
116
libobs/graphics/matrix4.h
Normal file
116
libobs/graphics/matrix4.h
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vec3.h"
|
||||
#include "vec4.h"
|
||||
#include "axisang.h"
|
||||
|
||||
/* 4x4 Matrix */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct matrix3;
|
||||
|
||||
struct matrix4 {
|
||||
struct vec4 x, y, z, t;
|
||||
};
|
||||
|
||||
static inline void matrix4_copy(struct matrix4 *dst, const struct matrix4 *m)
|
||||
{
|
||||
dst->x.m = m->x.m;
|
||||
dst->y.m = m->y.m;
|
||||
dst->z.m = m->z.m;
|
||||
dst->t.m = m->t.m;
|
||||
}
|
||||
|
||||
static inline void matrix4_identity(struct matrix4 *dst)
|
||||
{
|
||||
vec4_zero(&dst->x);
|
||||
vec4_zero(&dst->y);
|
||||
vec4_zero(&dst->z);
|
||||
vec4_zero(&dst->t);
|
||||
dst->x.x = 1.0f;
|
||||
dst->y.y = 1.0f;
|
||||
dst->z.z = 1.0f;
|
||||
dst->t.w = 1.0f;
|
||||
}
|
||||
|
||||
EXPORT void matrix4_from_matrix3(struct matrix4 *dst, const struct matrix3 *m);
|
||||
EXPORT void matrix4_from_quat(struct matrix4 *dst, const struct quat *q);
|
||||
EXPORT void matrix4_from_axisang(struct matrix4 *dst,
|
||||
const struct axisang *aa);
|
||||
|
||||
EXPORT void matrix4_mul(struct matrix4 *dst, const struct matrix4 *m1,
|
||||
const struct matrix4 *m2);
|
||||
|
||||
EXPORT float matrix4_determinant(const struct matrix4 *m);
|
||||
|
||||
EXPORT void matrix4_translate3v(struct matrix4 *dst, const struct matrix4 *m,
|
||||
const struct vec3 *v);
|
||||
EXPORT void matrix4_translate4v(struct matrix4 *dst, const struct matrix4 *m,
|
||||
const struct vec4 *v);
|
||||
EXPORT void matrix4_rotate(struct matrix4 *dst, const struct matrix4 *m,
|
||||
const struct quat *q);
|
||||
EXPORT void matrix4_rotate_aa(struct matrix4 *dst, const struct matrix4 *m,
|
||||
const struct axisang *aa);
|
||||
EXPORT void matrix4_scale(struct matrix4 *dst, const struct matrix4 *m,
|
||||
const struct vec3 *v);
|
||||
EXPORT bool matrix4_inv(struct matrix4 *dst, const struct matrix4 *m);
|
||||
EXPORT void matrix4_transpose(struct matrix4 *dst, const struct matrix4 *m);
|
||||
|
||||
EXPORT void matrix4_translate3v_i(struct matrix4 *dst, const struct vec3 *v,
|
||||
const struct matrix4 *m);
|
||||
EXPORT void matrix4_translate4v_i(struct matrix4 *dst, const struct vec4 *v,
|
||||
const struct matrix4 *m);
|
||||
EXPORT void matrix4_rotate_i(struct matrix4 *dst, const struct quat *q,
|
||||
const struct matrix4 *m);
|
||||
EXPORT void matrix4_rotate_aa_i(struct matrix4 *dst, const struct axisang *aa,
|
||||
const struct matrix4 *m);
|
||||
EXPORT void matrix4_scale_i(struct matrix4 *dst, const struct vec3 *v,
|
||||
const struct matrix4 *m);
|
||||
|
||||
static inline void matrix4_translate3f(struct matrix4 *dst,
|
||||
const struct matrix4 *m, float x, float y, float z)
|
||||
{
|
||||
struct vec3 v;
|
||||
vec3_set(&v, x, y, z);
|
||||
matrix4_translate3v(dst, m, &v);
|
||||
}
|
||||
|
||||
static inline void matrix4_rotate_aa4f(struct matrix4 *dst,
|
||||
const struct matrix4 *m, float x, float y, float z, float rot)
|
||||
{
|
||||
struct axisang aa;
|
||||
axisang_set(&aa, x, y, z, rot);
|
||||
matrix4_rotate_aa(dst, m, &aa);
|
||||
}
|
||||
|
||||
static inline void matrix4_scale3f(struct matrix4 *dst,
|
||||
const struct matrix4 *m, float x, float y, float z)
|
||||
{
|
||||
struct vec3 v;
|
||||
vec3_set(&v, x, y, z);
|
||||
matrix4_scale(dst, m, &v);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
162
libobs/graphics/plane.c
Normal file
162
libobs/graphics/plane.c
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "../util/c99defs.h"
|
||||
#include "matrix3.h"
|
||||
#include "plane.h"
|
||||
|
||||
void plane_from_tri(struct plane *dst,
|
||||
const struct vec3 *v1,
|
||||
const struct vec3 *v2,
|
||||
const struct vec3 *v3)
|
||||
{
|
||||
struct vec3 temp;
|
||||
|
||||
vec3_sub(&temp, v2, v1);
|
||||
vec3_sub(&dst->dir, v3, v1);
|
||||
vec3_cross(&dst->dir, &temp, &dst->dir);
|
||||
vec3_norm(&dst->dir, &dst->dir);
|
||||
dst->dist = vec3_dot(v1, &dst->dir);
|
||||
}
|
||||
|
||||
void plane_transform(struct plane *dst, const struct plane *p,
|
||||
const struct matrix4 *m)
|
||||
{
|
||||
struct vec3 temp;
|
||||
|
||||
vec3_zero(&temp);
|
||||
|
||||
vec3_transform(&dst->dir, &p->dir, m);
|
||||
vec3_norm(&dst->dir, &dst->dir);
|
||||
|
||||
vec3_transform(&temp, &temp, m);
|
||||
dst->dist = p->dist - vec3_dot(&dst->dir, &temp);
|
||||
}
|
||||
|
||||
void plane_transform3x4(struct plane *dst, const struct plane *p,
|
||||
const struct matrix3 *m)
|
||||
{
|
||||
struct vec3 temp;
|
||||
|
||||
vec3_transform3x4(&dst->dir, &p->dir, m);
|
||||
vec3_norm(&dst->dir, &dst->dir);
|
||||
|
||||
vec3_transform3x4(&temp, &m->t, m);
|
||||
dst->dist = p->dist - vec3_dot(&dst->dir, &temp);
|
||||
}
|
||||
|
||||
bool plane_intersection_ray(const struct plane *p, const struct vec3 *orig,
|
||||
const struct vec3 *dir, float *t)
|
||||
{
|
||||
float c = vec3_dot(&p->dir, dir);
|
||||
|
||||
if (fabsf(c) < EPSILON) {
|
||||
*t = 0.0f;
|
||||
return false;
|
||||
} else {
|
||||
*t = (p->dist - vec3_dot(&p->dir, orig)) / c;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool plane_intersection_line(const struct plane *p, const struct vec3 *v1,
|
||||
const struct vec3 *v2, float *t)
|
||||
{
|
||||
float p1_dist, p2_dist, p1_abs_dist, dist2;
|
||||
bool p1_over, p2_over;
|
||||
|
||||
p1_dist = vec3_plane_dist(v1, p);
|
||||
p2_dist = vec3_plane_dist(v2, p);
|
||||
|
||||
if (close_float(p1_dist, 0.0f, EPSILON)) {
|
||||
if (close_float(p2_dist, 0.0f, EPSILON))
|
||||
return false;
|
||||
|
||||
*t = 0.0f;
|
||||
return true;
|
||||
} else if (close_float(p2_dist, 0.0f, EPSILON)) {
|
||||
*t = 1.0f;
|
||||
return true;
|
||||
}
|
||||
|
||||
p1_over = (p1_dist > 0.0f);
|
||||
p2_over = (p2_dist > 0.0f);
|
||||
|
||||
if (p1_over == p2_over)
|
||||
return false;
|
||||
|
||||
p1_abs_dist = fabsf(p1_dist);
|
||||
dist2 = p1_abs_dist + fabsf(p2_dist);
|
||||
if (dist2 < EPSILON)
|
||||
return false;
|
||||
|
||||
*t = p1_abs_dist / dist2;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool plane_tri_inside(const struct plane *p,
|
||||
const struct vec3 *v1,
|
||||
const struct vec3 *v2,
|
||||
const struct vec3 *v3,
|
||||
float precision)
|
||||
{
|
||||
/* bit 1: part or all is behind the plane */
|
||||
/* bit 2: part or all is in front of the plane */
|
||||
int sides = 0;
|
||||
float d1 = vec3_plane_dist(v1, p);
|
||||
float d2 = vec3_plane_dist(v2, p);
|
||||
float d3 = vec3_plane_dist(v3, p);
|
||||
|
||||
if (d1 >= precision)
|
||||
sides = 2;
|
||||
else if (d1 <= -precision)
|
||||
sides = 1;
|
||||
|
||||
if (d2 >= precision)
|
||||
sides |= 2;
|
||||
else if (d2 <= -precision)
|
||||
sides |= 1;
|
||||
|
||||
if (d3 >= precision)
|
||||
sides |= 2;
|
||||
else if (d3 <= -precision)
|
||||
sides |= 1;
|
||||
|
||||
return sides;
|
||||
}
|
||||
|
||||
bool plane_line_inside(const struct plane *p, const struct vec3 *v1,
|
||||
const struct vec3 *v2, float precision)
|
||||
{
|
||||
/* bit 1: part or all is behind the plane */
|
||||
/* bit 2: part or all is in front of the plane */
|
||||
int sides = 0;
|
||||
float d1 = vec3_plane_dist(v1, p);
|
||||
float d2 = vec3_plane_dist(v2, p);
|
||||
|
||||
if (d1 >= precision)
|
||||
sides = 2;
|
||||
else if (d1 <= -precision)
|
||||
sides = 1;
|
||||
|
||||
if (d2 >= precision)
|
||||
sides |= 2;
|
||||
else if (d2 <= -precision)
|
||||
sides |= 1;
|
||||
|
||||
return sides;
|
||||
}
|
||||
101
libobs/graphics/plane.h
Normal file
101
libobs/graphics/plane.h
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "math-defs.h"
|
||||
#include "vec3.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct matrix3;
|
||||
struct matrix4;
|
||||
|
||||
struct plane {
|
||||
struct vec3 dir;
|
||||
float dist;
|
||||
};
|
||||
|
||||
static inline void plane_copy(struct plane *dst, const struct plane *p)
|
||||
{
|
||||
vec3_copy(&dst->dir, &p->dir);
|
||||
dst->dist = p->dist;
|
||||
}
|
||||
|
||||
static inline void plane_set(struct plane *dst, const struct vec3 *dir,
|
||||
float dist)
|
||||
{
|
||||
vec3_copy(&dst->dir, dir);
|
||||
dst->dist = dist;
|
||||
}
|
||||
|
||||
static inline void plane_setf(struct plane *dst, float a, float b, float c,
|
||||
float d)
|
||||
{
|
||||
vec3_set(&dst->dir, a, b, c);
|
||||
dst->dist = d;
|
||||
}
|
||||
|
||||
EXPORT void plane_from_tri(struct plane *dst,
|
||||
const struct vec3 *v1,
|
||||
const struct vec3 *v2,
|
||||
const struct vec3 *v3);
|
||||
|
||||
EXPORT void plane_transform(struct plane *dst, const struct plane *p,
|
||||
const struct matrix4 *m);
|
||||
EXPORT void plane_transform3x4(struct plane *dst, const struct plane *p,
|
||||
const struct matrix3 *m);
|
||||
|
||||
EXPORT bool plane_intersection_ray(const struct plane *p,
|
||||
const struct vec3 *orig, const struct vec3 *dir, float *t);
|
||||
EXPORT bool plane_intersection_line(const struct plane *p,
|
||||
const struct vec3 *v1, const struct vec3 *v2, float *t);
|
||||
|
||||
EXPORT bool plane_tri_inside(const struct plane *p,
|
||||
const struct vec3 *v1,
|
||||
const struct vec3 *v2,
|
||||
const struct vec3 *v3,
|
||||
float precision);
|
||||
|
||||
EXPORT bool plane_line_inside(const struct plane *p, const struct vec3 *v1,
|
||||
const struct vec3 *v2, float precision);
|
||||
|
||||
static inline bool plane_close(const struct plane *p1, const struct plane *p2,
|
||||
float precision)
|
||||
{
|
||||
return vec3_close(&p1->dir, &p2->dir, precision) &&
|
||||
close_float(p1->dist, p2->dist, precision);
|
||||
}
|
||||
|
||||
static inline bool plane_coplanar(const struct plane *p1,
|
||||
const struct plane *p2, float precision)
|
||||
{
|
||||
float cos_angle = vec3_dot(&p1->dir, &p2->dir);
|
||||
|
||||
if (close_float(cos_angle, 1.0f, precision))
|
||||
return close_float(p1->dist, p2->dist, precision);
|
||||
else if (close_float(cos_angle, -1.0f, precision))
|
||||
return close_float(-p1->dist, p2->dist, precision);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
222
libobs/graphics/quat.c
Normal file
222
libobs/graphics/quat.c
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "quat.h"
|
||||
#include "vec3.h"
|
||||
#include "matrix3.h"
|
||||
#include "matrix4.h"
|
||||
#include "axisang.h"
|
||||
|
||||
static inline void quat_vec3(struct vec3 *v, const struct quat *q)
|
||||
{
|
||||
v->m = q->m;
|
||||
v->w = 0.0f;
|
||||
}
|
||||
|
||||
void quat_mul(struct quat *dst, const struct quat *q1, const struct quat *q2)
|
||||
{
|
||||
struct vec3 q1axis, q2axis;
|
||||
struct vec3 temp1, temp2;
|
||||
|
||||
quat_vec3(&q1axis, q1);
|
||||
quat_vec3(&q2axis, q2);
|
||||
|
||||
vec3_mulf(&temp1, &q2axis, q1->w);
|
||||
vec3_mulf(&temp2, &q1axis, q2->w);
|
||||
vec3_add(&temp1, &temp1, &temp2);
|
||||
vec3_cross(&temp2, &q1axis, &q2axis);
|
||||
vec3_add((struct vec3 *)dst, &temp1, &temp2);
|
||||
|
||||
dst->w = (q1->w * q2->w) - vec3_dot(&q1axis, &q2axis);
|
||||
}
|
||||
|
||||
void quat_from_axisang(struct quat *dst, const struct axisang *aa)
|
||||
{
|
||||
float halfa = aa->w * 0.5f;
|
||||
float sine = sinf(halfa);
|
||||
|
||||
dst->x = aa->x * sine;
|
||||
dst->y = aa->y * sine;
|
||||
dst->z = aa->z * sine;
|
||||
dst->w = cosf(halfa);
|
||||
}
|
||||
|
||||
struct f4x4 {
|
||||
float ptr[4][4];
|
||||
};
|
||||
|
||||
void quat_from_matrix3(struct quat *dst, const struct matrix3 *m)
|
||||
{
|
||||
quat_from_matrix4(dst, (const struct matrix4*)m);
|
||||
}
|
||||
|
||||
void quat_from_matrix4(struct quat *dst, const struct matrix4 *m)
|
||||
{
|
||||
float tr = (m->x.x + m->y.y + m->z.z);
|
||||
float inv_half;
|
||||
float four_d;
|
||||
int i,j,k;
|
||||
|
||||
if (tr > 0.0f) {
|
||||
four_d = sqrtf(tr + 1.0f);
|
||||
dst->w = four_d * 0.5f;
|
||||
|
||||
inv_half = 0.5f / four_d;
|
||||
dst->x = (m->y.z - m->z.y) * inv_half;
|
||||
dst->y = (m->z.x - m->x.z) * inv_half;
|
||||
dst->z = (m->x.y - m->y.x) * inv_half;
|
||||
} else {
|
||||
struct f4x4 *val = (struct f4x4*)m;
|
||||
|
||||
i = (m->x.x > m->y.y) ? 0 : 1;
|
||||
|
||||
if (m->z.z > val->ptr[i][i])
|
||||
i = 2;
|
||||
|
||||
j = (i+1) % 3;
|
||||
k = (i+2) % 3;
|
||||
|
||||
/* ---------------------------------- */
|
||||
|
||||
four_d = sqrtf((val->ptr[i][i] - val->ptr[j][j] -
|
||||
val->ptr[k][k]) + 1.0f);
|
||||
|
||||
dst->ptr[i] = four_d * 0.5f;
|
||||
|
||||
inv_half = 0.5f / four_d;
|
||||
dst->ptr[j] = (val->ptr[i][j] + val->ptr[j][i]) * inv_half;
|
||||
dst->ptr[k] = (val->ptr[i][k] + val->ptr[k][i]) * inv_half;
|
||||
dst->w = (val->ptr[j][k] - val->ptr[k][j]) * inv_half;
|
||||
}
|
||||
}
|
||||
|
||||
void quat_get_dir(struct vec3 *dst, const struct quat *q)
|
||||
{
|
||||
struct matrix3 m;
|
||||
matrix3_from_quat(&m, q);
|
||||
vec3_copy(dst, &m.z);
|
||||
}
|
||||
|
||||
void quat_set_look_dir(struct quat *dst, const struct vec3 *dir)
|
||||
{
|
||||
struct vec3 new_dir;
|
||||
struct quat xz_rot, yz_rot;
|
||||
bool xz_valid;
|
||||
bool yz_valid;
|
||||
struct axisang aa;
|
||||
|
||||
vec3_norm(&new_dir, dir);
|
||||
vec3_neg(&new_dir, &new_dir);
|
||||
|
||||
quat_identity(&xz_rot);
|
||||
quat_identity(&yz_rot);
|
||||
|
||||
xz_valid = close_float(new_dir.x, 0.0f, EPSILON) ||
|
||||
close_float(new_dir.z, 0.0f, EPSILON);
|
||||
yz_valid = close_float(new_dir.y, 0.0f, EPSILON);
|
||||
|
||||
if (xz_valid) {
|
||||
axisang_set(&aa, 0.0f, 1.0f, 0.0f,
|
||||
atan2f(new_dir.x, new_dir.z));
|
||||
|
||||
quat_from_axisang(&xz_rot, &aa);
|
||||
}
|
||||
if (yz_valid) {
|
||||
axisang_set(&aa, -1.0f, 0.0f, 0.0f, asinf(new_dir.y));
|
||||
quat_from_axisang(&yz_rot, &aa);
|
||||
}
|
||||
|
||||
if (!xz_valid)
|
||||
quat_copy(dst, &yz_rot);
|
||||
else if (!yz_valid)
|
||||
quat_copy(dst, &xz_rot);
|
||||
else
|
||||
quat_mul(dst, &xz_rot, &yz_rot);
|
||||
}
|
||||
|
||||
void quat_log(struct quat *dst, const struct quat *q)
|
||||
{
|
||||
float angle = acosf(q->w);
|
||||
float sine = sinf(angle);
|
||||
float w = q->w;
|
||||
|
||||
quat_copy(dst, q);
|
||||
dst->w = 0.0f;
|
||||
|
||||
if ((fabsf(w) < 1.0f) && (fabsf(sine) >= EPSILON)) {
|
||||
sine = angle/sine;
|
||||
quat_mulf(dst, dst, sine);
|
||||
}
|
||||
}
|
||||
|
||||
void quat_exp(struct quat *dst, const struct quat *q)
|
||||
{
|
||||
float length = sqrtf(q->x*q->x + q->y*q->y + q->z*q->z);
|
||||
float sine = sinf(length);
|
||||
|
||||
quat_copy(dst, q);
|
||||
sine = (length > EPSILON) ? (sine/length) : 1.0f;
|
||||
quat_mulf(dst, dst, sine);
|
||||
dst->w = cosf(length);
|
||||
}
|
||||
|
||||
void quat_interpolate(struct quat *dst, const struct quat *q1,
|
||||
const struct quat *q2, float t)
|
||||
{
|
||||
float dot = quat_dot(q1, q2);
|
||||
float anglef = acosf(dot);
|
||||
float sine, sinei, sinet, sineti;
|
||||
struct quat temp;
|
||||
|
||||
if (anglef >= EPSILON) {
|
||||
sine = sinf(anglef);
|
||||
sinei = 1/sine;
|
||||
sinet = sinf(anglef*t)*sinei;
|
||||
sineti = sinf(anglef*(1.0f-t))*sinei;
|
||||
|
||||
quat_mulf(&temp, q1, sineti);
|
||||
quat_mulf(dst, q2, sinet);
|
||||
quat_add(dst, &temp, dst);
|
||||
} else {
|
||||
quat_sub(&temp, q2, q1);
|
||||
quat_mulf(&temp, &temp, t);
|
||||
quat_add(dst, &temp, q1);
|
||||
}
|
||||
}
|
||||
|
||||
void quat_get_tangent(struct quat *dst, const struct quat *prev,
|
||||
const struct quat *q, const struct quat *next)
|
||||
{
|
||||
struct quat temp;
|
||||
|
||||
quat_sub(&temp, q, prev);
|
||||
quat_add(&temp, &temp, next);
|
||||
quat_sub(&temp, &temp, q);
|
||||
quat_mulf(dst, &temp, 0.5f);
|
||||
}
|
||||
|
||||
void quat_interpolate_cubic(struct quat *dst,
|
||||
const struct quat *q1, const struct quat *q2,
|
||||
const struct quat *m1, const struct quat *m2,
|
||||
float t)
|
||||
{
|
||||
struct quat temp1, temp2;
|
||||
|
||||
quat_interpolate(&temp1, q1, q2, t);
|
||||
quat_interpolate(&temp2, m1, m2, t);
|
||||
quat_interpolate(dst, &temp1, &temp2, 2.0f*(1.0f-t)*t);
|
||||
}
|
||||
184
libobs/graphics/quat.h
Normal file
184
libobs/graphics/quat.h
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../util/c99defs.h"
|
||||
#include "math-defs.h"
|
||||
#include "vec3.h"
|
||||
#include <xmmintrin.h>
|
||||
|
||||
/*
|
||||
* Quaternion math
|
||||
*
|
||||
* Generally used to represent rotational data more than anything. Allows
|
||||
* for efficient and correct rotational interpolation without suffering from
|
||||
* things like gimbal lock.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct matrix3;
|
||||
struct matrix4;
|
||||
struct axisang;
|
||||
|
||||
struct quat {
|
||||
union {
|
||||
struct {float x, y, z, w;};
|
||||
float ptr[4];
|
||||
__m128 m;
|
||||
};
|
||||
};
|
||||
|
||||
static inline void quat_identity(struct quat *q)
|
||||
{
|
||||
q->m = _mm_setzero_ps();
|
||||
q->w = 1.0f;
|
||||
}
|
||||
|
||||
static inline void quat_set(struct quat *dst, float x, float y, float z,
|
||||
float w)
|
||||
{
|
||||
dst->m = _mm_set_ps(x, y, z, w);
|
||||
}
|
||||
|
||||
static inline void quat_copy(struct quat *dst, const struct quat *q)
|
||||
{
|
||||
dst->m = q->m;
|
||||
}
|
||||
|
||||
static inline void quat_add(struct quat *dst, const struct quat *q1,
|
||||
const struct quat *q2)
|
||||
{
|
||||
dst->m = _mm_add_ps(q1->m, q2->m);
|
||||
}
|
||||
|
||||
static inline void quat_sub(struct quat *dst, const struct quat *q1,
|
||||
const struct quat *q2)
|
||||
{
|
||||
dst->m = _mm_sub_ps(q1->m, q2->m);
|
||||
}
|
||||
|
||||
EXPORT void quat_mul(struct quat *dst, const struct quat *q1,
|
||||
const struct quat *q2);
|
||||
|
||||
static inline void quat_addf(struct quat *dst, const struct quat *q,
|
||||
float f)
|
||||
{
|
||||
dst->m = _mm_add_ps(q->m, _mm_set1_ps(f));
|
||||
}
|
||||
|
||||
static inline void quat_subf(struct quat *dst, const struct quat *q,
|
||||
float f)
|
||||
{
|
||||
dst->m = _mm_sub_ps(q->m, _mm_set1_ps(f));
|
||||
}
|
||||
|
||||
static inline void quat_mulf(struct quat *dst, const struct quat *q,
|
||||
float f)
|
||||
{
|
||||
dst->m = _mm_mul_ps(q->m, _mm_set1_ps(f));
|
||||
}
|
||||
|
||||
static inline void quat_divf(struct quat *dst, const struct quat *q,
|
||||
float f)
|
||||
{
|
||||
dst->m = _mm_div_ps(q->m, _mm_set1_ps(f));
|
||||
}
|
||||
|
||||
static inline float quat_dot(const struct quat *q1, const struct quat *q2)
|
||||
{
|
||||
struct vec3 add;
|
||||
__m128 mul = _mm_mul_ps(q1->m, q2->m);
|
||||
add.m = _mm_add_ps(_mm_movehl_ps(mul, mul), mul);
|
||||
add.m = _mm_add_ps(_mm_shuffle_ps(add.m, add.m, 0x55), add.m);
|
||||
return add.x;
|
||||
}
|
||||
|
||||
static inline void quat_inv(struct quat *dst, const struct quat *q)
|
||||
{
|
||||
dst->x = -q->x;
|
||||
dst->y = -q->y;
|
||||
dst->z = -q->z;
|
||||
}
|
||||
|
||||
static inline void quat_neg(struct quat *dst, const struct quat *q)
|
||||
{
|
||||
dst->x = -q->x;
|
||||
dst->y = -q->y;
|
||||
dst->z = -q->z;
|
||||
dst->w = -q->w;
|
||||
}
|
||||
|
||||
static inline float quat_len(const struct quat *q)
|
||||
{
|
||||
float dot_val = quat_dot(q, q);
|
||||
return (dot_val > 0.0f) ? sqrtf(dot_val) : 0.0f;
|
||||
}
|
||||
|
||||
static inline float quat_dist(const struct quat *q1, const struct quat *q2)
|
||||
{
|
||||
struct quat temp;
|
||||
float dot_val;
|
||||
|
||||
quat_sub(&temp, q1, q2);
|
||||
dot_val = quat_dot(&temp, &temp);
|
||||
return (dot_val > 0.0f) ? sqrtf(dot_val) : 0.0f;
|
||||
}
|
||||
|
||||
static inline void quat_norm(struct quat *dst, const struct quat *q)
|
||||
{
|
||||
float dot_val = quat_dot(q, q);
|
||||
dst->m = (dot_val > 0.0f) ?
|
||||
_mm_mul_ps(q->m, _mm_set1_ps(1.0f/sqrtf(dot_val))) :
|
||||
_mm_setzero_ps();
|
||||
}
|
||||
|
||||
static inline bool quat_close(const struct quat *q1, const struct quat *q2,
|
||||
float epsilon)
|
||||
{
|
||||
struct quat test;
|
||||
quat_sub(&test, q1, q2);
|
||||
return test.x < epsilon &&
|
||||
test.y < epsilon &&
|
||||
test.z < epsilon &&
|
||||
test.w < epsilon;
|
||||
}
|
||||
|
||||
EXPORT void quat_from_axisang(struct quat *dst, const struct axisang *aa);
|
||||
EXPORT void quat_from_matrix3(struct quat *dst, const struct matrix3 *m);
|
||||
EXPORT void quat_from_matrix4(struct quat *dst, const struct matrix4 *m);
|
||||
|
||||
EXPORT void quat_get_dir(struct vec3 *dst, const struct quat *q);
|
||||
EXPORT void quat_set_look_dir(struct quat *dst, const struct vec3 *dir);
|
||||
|
||||
EXPORT void quat_log(struct quat *dst, const struct quat *q);
|
||||
EXPORT void quat_exp(struct quat *dst, const struct quat *q);
|
||||
|
||||
EXPORT void quat_interpolate(struct quat *dst, const struct quat *q1,
|
||||
const struct quat *q2, float t);
|
||||
EXPORT void quat_get_tangent(struct quat *dst, const struct quat *prev,
|
||||
const struct quat *q, const struct quat *next);
|
||||
EXPORT void quat_interpolate_cubic(struct quat *dst, const struct quat *q1,
|
||||
const struct quat *q2, const struct quat *m1,
|
||||
const struct quat *m2, float t);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
693
libobs/graphics/shader-parser.c
Normal file
693
libobs/graphics/shader-parser.c
Normal file
|
|
@ -0,0 +1,693 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "../util/platform.h"
|
||||
#include "shader-parser.h"
|
||||
|
||||
enum gs_shader_param_type get_shader_param_type(const char *type)
|
||||
{
|
||||
if (strcmp(type, "float") == 0)
|
||||
return GS_SHADER_PARAM_FLOAT;
|
||||
else if (strcmp(type, "float2") == 0)
|
||||
return GS_SHADER_PARAM_VEC2;
|
||||
else if (strcmp(type, "float3") == 0)
|
||||
return GS_SHADER_PARAM_VEC3;
|
||||
else if (strcmp(type, "float4") == 0)
|
||||
return GS_SHADER_PARAM_VEC4;
|
||||
else if (astrcmp_n(type, "texture", 7) == 0)
|
||||
return GS_SHADER_PARAM_TEXTURE;
|
||||
else if (strcmp(type, "float4x4") == 0)
|
||||
return GS_SHADER_PARAM_MATRIX4X4;
|
||||
else if (strcmp(type, "bool") == 0)
|
||||
return GS_SHADER_PARAM_BOOL;
|
||||
else if (strcmp(type, "int") == 0)
|
||||
return GS_SHADER_PARAM_INT;
|
||||
else if (strcmp(type, "string") == 0)
|
||||
return GS_SHADER_PARAM_STRING;
|
||||
|
||||
return GS_SHADER_PARAM_UNKNOWN;
|
||||
}
|
||||
|
||||
enum gs_sample_filter get_sample_filter(const char *filter)
|
||||
{
|
||||
if (astrcmpi(filter, "Anisotropy") == 0)
|
||||
return GS_FILTER_ANISOTROPIC;
|
||||
|
||||
else if (astrcmpi(filter, "Point") == 0 ||
|
||||
strcmp(filter, "MIN_MAG_MIP_POINT") == 0)
|
||||
return GS_FILTER_POINT;
|
||||
|
||||
else if (astrcmpi(filter, "Linear") == 0 ||
|
||||
strcmp(filter, "MIN_MAG_MIP_LINEAR") == 0)
|
||||
return GS_FILTER_LINEAR;
|
||||
|
||||
else if (strcmp(filter, "MIN_MAG_POINT_MIP_LINEAR") == 0)
|
||||
return GS_FILTER_MIN_MAG_POINT_MIP_LINEAR;
|
||||
|
||||
else if (strcmp(filter, "MIN_POINT_MAG_LINEAR_MIP_POINT") == 0)
|
||||
return GS_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT;
|
||||
|
||||
else if (strcmp(filter, "MIN_POINT_MAG_MIP_LINEAR") == 0)
|
||||
return GS_FILTER_MIN_POINT_MAG_MIP_LINEAR;
|
||||
|
||||
else if (strcmp(filter, "MIN_LINEAR_MAG_MIP_POINT") == 0)
|
||||
return GS_FILTER_MIN_LINEAR_MAG_MIP_POINT;
|
||||
|
||||
else if (strcmp(filter, "MIN_LINEAR_MAG_POINT_MIP_LINEAR") == 0)
|
||||
return GS_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR;
|
||||
|
||||
else if (strcmp(filter, "MIN_MAG_LINEAR_MIP_POINT") == 0)
|
||||
return GS_FILTER_MIN_MAG_LINEAR_MIP_POINT;
|
||||
|
||||
return GS_FILTER_LINEAR;
|
||||
}
|
||||
|
||||
extern enum gs_address_mode get_address_mode(const char *mode)
|
||||
{
|
||||
if (astrcmpi(mode, "Wrap") == 0 || astrcmpi(mode, "Repeat") == 0)
|
||||
return GS_ADDRESS_WRAP;
|
||||
else if (astrcmpi(mode, "Clamp") == 0 || astrcmpi(mode, "None") == 0)
|
||||
return GS_ADDRESS_CLAMP;
|
||||
else if (astrcmpi(mode, "Mirror") == 0)
|
||||
return GS_ADDRESS_MIRROR;
|
||||
else if (astrcmpi(mode, "Border") == 0)
|
||||
return GS_ADDRESS_BORDER;
|
||||
else if (astrcmpi(mode, "MirrorOnce") == 0)
|
||||
return GS_ADDRESS_MIRRORONCE;
|
||||
|
||||
return GS_ADDRESS_CLAMP;
|
||||
}
|
||||
|
||||
void shader_sampler_convert(struct shader_sampler *ss,
|
||||
struct gs_sampler_info *info)
|
||||
{
|
||||
size_t i;
|
||||
memset(info, 0, sizeof(struct gs_sampler_info));
|
||||
|
||||
for (i = 0; i < ss->states.num; i++) {
|
||||
const char *state = ss->states.array[i];
|
||||
const char *value = ss->values.array[i];
|
||||
|
||||
if (astrcmpi(state, "Filter") == 0)
|
||||
info->filter = get_sample_filter(value);
|
||||
else if (astrcmpi(state, "AddressU") == 0)
|
||||
info->address_u = get_address_mode(value);
|
||||
else if (astrcmpi(state, "AddressV") == 0)
|
||||
info->address_v = get_address_mode(value);
|
||||
else if (astrcmpi(state, "AddressW") == 0)
|
||||
info->address_w = get_address_mode(value);
|
||||
else if (astrcmpi(state, "MaxAnisotropy") == 0)
|
||||
info->max_anisotropy = (int)strtol(value, NULL, 10);
|
||||
else if (astrcmpi(state, "BorderColor") == 0)
|
||||
info->border_color = (*value == '#') ?
|
||||
strtol(value + 1, NULL, 16) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static int sp_parse_sampler_state_item(struct shader_parser *sp,
|
||||
struct shader_sampler *ss)
|
||||
{
|
||||
int ret;
|
||||
char *state = NULL, *value = NULL;
|
||||
|
||||
ret = cf_next_name(&sp->cfp, &state, "state name", ";");
|
||||
if (ret != PARSE_SUCCESS) goto fail;
|
||||
|
||||
ret = cf_next_token_should_be(&sp->cfp, "=", ";", NULL);
|
||||
if (ret != PARSE_SUCCESS) goto fail;
|
||||
|
||||
ret = cf_next_name(&sp->cfp, &value, "value name", ";");
|
||||
if (ret != PARSE_SUCCESS) goto fail;
|
||||
|
||||
ret = cf_next_token_should_be(&sp->cfp, ";", ";", NULL);
|
||||
if (ret != PARSE_SUCCESS) goto fail;
|
||||
|
||||
da_push_back(ss->states, &state);
|
||||
da_push_back(ss->values, &value);
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
bfree(state);
|
||||
bfree(value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sp_parse_sampler_state(struct shader_parser *sp)
|
||||
{
|
||||
struct shader_sampler ss;
|
||||
struct cf_token peek;
|
||||
shader_sampler_init(&ss);
|
||||
|
||||
if (cf_next_name(&sp->cfp, &ss.name, "name", ";") != PARSE_SUCCESS)
|
||||
goto error;
|
||||
if (cf_next_token_should_be(&sp->cfp, "{", ";", NULL) != PARSE_SUCCESS)
|
||||
goto error;
|
||||
|
||||
if (!cf_peek_valid_token(&sp->cfp, &peek))
|
||||
goto error;
|
||||
|
||||
while (strref_cmp(&peek.str, "}") != 0) {
|
||||
int ret = sp_parse_sampler_state_item(sp, &ss);
|
||||
if (ret == PARSE_EOF)
|
||||
goto error;
|
||||
|
||||
if (!cf_peek_valid_token(&sp->cfp, &peek))
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (cf_next_token_should_be(&sp->cfp, "}", ";", NULL) != PARSE_SUCCESS)
|
||||
goto error;
|
||||
if (cf_next_token_should_be(&sp->cfp, ";", NULL, NULL) != PARSE_SUCCESS)
|
||||
goto error;
|
||||
|
||||
da_push_back(sp->samplers, &ss);
|
||||
return;
|
||||
|
||||
error:
|
||||
shader_sampler_free(&ss);
|
||||
}
|
||||
|
||||
static inline int sp_parse_struct_var(struct shader_parser *sp,
|
||||
struct shader_var *var)
|
||||
{
|
||||
int code;
|
||||
|
||||
/* -------------------------------------- */
|
||||
/* variable type */
|
||||
|
||||
if (!cf_next_valid_token(&sp->cfp)) return PARSE_EOF;
|
||||
|
||||
if (cf_token_is(&sp->cfp, ";")) return PARSE_CONTINUE;
|
||||
if (cf_token_is(&sp->cfp, "}")) return PARSE_BREAK;
|
||||
|
||||
code = cf_token_is_type(&sp->cfp, CFTOKEN_NAME, "type name", ";");
|
||||
if (code != PARSE_SUCCESS)
|
||||
return code;
|
||||
|
||||
cf_copy_token(&sp->cfp, &var->type);
|
||||
|
||||
/* -------------------------------------- */
|
||||
/* variable name */
|
||||
|
||||
if (!cf_next_valid_token(&sp->cfp)) return PARSE_EOF;
|
||||
|
||||
if (cf_token_is(&sp->cfp, ";")) return PARSE_UNEXPECTED_CONTINUE;
|
||||
if (cf_token_is(&sp->cfp, "}")) return PARSE_UNEXPECTED_BREAK;
|
||||
|
||||
code = cf_token_is_type(&sp->cfp, CFTOKEN_NAME, "variable name",
|
||||
";");
|
||||
if (code != PARSE_SUCCESS)
|
||||
return code;
|
||||
|
||||
cf_copy_token(&sp->cfp, &var->name);
|
||||
|
||||
/* -------------------------------------- */
|
||||
/* variable mapping if any (POSITION, TEXCOORD, etc) */
|
||||
|
||||
if (!cf_next_valid_token(&sp->cfp)) return PARSE_EOF;
|
||||
|
||||
if (cf_token_is(&sp->cfp, ":")) {
|
||||
if (!cf_next_valid_token(&sp->cfp)) return PARSE_EOF;
|
||||
|
||||
if (cf_token_is(&sp->cfp, ";"))
|
||||
return PARSE_UNEXPECTED_CONTINUE;
|
||||
if (cf_token_is(&sp->cfp, "}"))
|
||||
return PARSE_UNEXPECTED_BREAK;
|
||||
|
||||
code = cf_token_is_type(&sp->cfp, CFTOKEN_NAME,
|
||||
"mapping name", ";");
|
||||
if (code != PARSE_SUCCESS)
|
||||
return code;
|
||||
|
||||
cf_copy_token(&sp->cfp, &var->mapping);
|
||||
|
||||
if (!cf_next_valid_token(&sp->cfp)) return PARSE_EOF;
|
||||
}
|
||||
|
||||
/* -------------------------------------- */
|
||||
|
||||
if (!cf_token_is(&sp->cfp, ";")) {
|
||||
if (!cf_go_to_valid_token(&sp->cfp, ";", "}"))
|
||||
return PARSE_EOF;
|
||||
return PARSE_CONTINUE;
|
||||
}
|
||||
|
||||
return PARSE_SUCCESS;
|
||||
}
|
||||
|
||||
static void sp_parse_struct(struct shader_parser *sp)
|
||||
{
|
||||
struct shader_struct ss;
|
||||
shader_struct_init(&ss);
|
||||
|
||||
if (cf_next_name(&sp->cfp, &ss.name, "name", ";") != PARSE_SUCCESS)
|
||||
goto error;
|
||||
if (cf_next_token_should_be(&sp->cfp, "{", ";", NULL) != PARSE_SUCCESS)
|
||||
goto error;
|
||||
|
||||
/* get structure variables */
|
||||
while (true) {
|
||||
bool do_break = false;
|
||||
struct shader_var var;
|
||||
|
||||
shader_var_init(&var);
|
||||
|
||||
switch (sp_parse_struct_var(sp, &var)) {
|
||||
|
||||
case PARSE_UNEXPECTED_CONTINUE:
|
||||
cf_adderror_syntax_error(&sp->cfp);
|
||||
case PARSE_CONTINUE:
|
||||
shader_var_free(&var);
|
||||
continue;
|
||||
|
||||
case PARSE_UNEXPECTED_BREAK:
|
||||
cf_adderror_syntax_error(&sp->cfp);
|
||||
case PARSE_BREAK:
|
||||
shader_var_free(&var);
|
||||
do_break = true;
|
||||
break;
|
||||
|
||||
case PARSE_EOF:
|
||||
shader_var_free(&var);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (do_break)
|
||||
break;
|
||||
|
||||
da_push_back(ss.vars, &var);
|
||||
}
|
||||
|
||||
if (cf_next_token_should_be(&sp->cfp, ";", NULL, NULL) != PARSE_SUCCESS)
|
||||
goto error;
|
||||
|
||||
da_push_back(sp->structs, &ss);
|
||||
return;
|
||||
|
||||
error:
|
||||
shader_struct_free(&ss);
|
||||
}
|
||||
|
||||
static inline int sp_check_for_keyword(struct shader_parser *sp,
|
||||
const char *keyword, bool *val)
|
||||
{
|
||||
bool new_val = cf_token_is(&sp->cfp, keyword);
|
||||
if (new_val) {
|
||||
if (!cf_next_valid_token(&sp->cfp))
|
||||
return PARSE_EOF;
|
||||
|
||||
if (new_val && *val)
|
||||
cf_adderror(&sp->cfp, "'$1' keyword already specified",
|
||||
LEX_WARNING, keyword, NULL, NULL);
|
||||
*val = new_val;
|
||||
|
||||
return PARSE_CONTINUE;
|
||||
}
|
||||
|
||||
return PARSE_SUCCESS;
|
||||
}
|
||||
|
||||
static inline int sp_parse_func_param(struct shader_parser *sp,
|
||||
struct shader_var *var)
|
||||
{
|
||||
int code;
|
||||
bool is_uniform = false;
|
||||
|
||||
if (!cf_next_valid_token(&sp->cfp))
|
||||
return PARSE_EOF;
|
||||
|
||||
code = sp_check_for_keyword(sp, "uniform", &is_uniform);
|
||||
if (code == PARSE_EOF)
|
||||
return PARSE_EOF;
|
||||
|
||||
var->var_type = is_uniform ? SHADER_VAR_UNIFORM : SHADER_VAR_NONE;
|
||||
|
||||
code = cf_get_name(&sp->cfp, &var->type, "type", ")");
|
||||
if (code != PARSE_SUCCESS)
|
||||
return code;
|
||||
|
||||
code = cf_next_name(&sp->cfp, &var->name, "name", ")");
|
||||
if (code != PARSE_SUCCESS)
|
||||
return code;
|
||||
|
||||
if (!cf_next_valid_token(&sp->cfp))
|
||||
return PARSE_EOF;
|
||||
|
||||
if (cf_token_is(&sp->cfp, ":")) {
|
||||
code = cf_next_name(&sp->cfp, &var->mapping,
|
||||
"mapping specifier", ")");
|
||||
if (code != PARSE_SUCCESS)
|
||||
return code;
|
||||
|
||||
if (!cf_next_valid_token(&sp->cfp))
|
||||
return PARSE_EOF;
|
||||
}
|
||||
|
||||
return PARSE_SUCCESS;
|
||||
}
|
||||
|
||||
static bool sp_parse_func_params(struct shader_parser *sp,
|
||||
struct shader_func *func)
|
||||
{
|
||||
struct cf_token peek;
|
||||
int code;
|
||||
|
||||
cf_token_clear(&peek);
|
||||
|
||||
if (!cf_peek_valid_token(&sp->cfp, &peek))
|
||||
return false;
|
||||
|
||||
if (*peek.str.array == ')') {
|
||||
cf_next_token(&sp->cfp);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
do {
|
||||
struct shader_var var;
|
||||
shader_var_init(&var);
|
||||
|
||||
if (!cf_token_is(&sp->cfp, "(") && !cf_token_is(&sp->cfp, ","))
|
||||
cf_adderror_syntax_error(&sp->cfp);
|
||||
|
||||
code = sp_parse_func_param(sp, &var);
|
||||
if (code != PARSE_SUCCESS) {
|
||||
shader_var_free(&var);
|
||||
|
||||
if (code == PARSE_CONTINUE)
|
||||
goto exit;
|
||||
else if (code == PARSE_EOF)
|
||||
return false;
|
||||
}
|
||||
|
||||
da_push_back(func->params, &var);
|
||||
} while (!cf_token_is(&sp->cfp, ")"));
|
||||
|
||||
exit:
|
||||
return true;
|
||||
}
|
||||
|
||||
static void sp_parse_function(struct shader_parser *sp, char *type, char *name)
|
||||
{
|
||||
struct shader_func func;
|
||||
|
||||
shader_func_init(&func, type, name);
|
||||
if (!sp_parse_func_params(sp, &func))
|
||||
goto error;
|
||||
|
||||
if (!cf_next_valid_token(&sp->cfp))
|
||||
goto error;
|
||||
|
||||
/* if function is mapped to something, for example COLOR */
|
||||
if (cf_token_is(&sp->cfp, ":")) {
|
||||
char *mapping = NULL;
|
||||
int errorcode = cf_next_name(&sp->cfp, &mapping, "mapping",
|
||||
"{");
|
||||
if (errorcode != PARSE_SUCCESS)
|
||||
goto error;
|
||||
|
||||
func.mapping = mapping;
|
||||
|
||||
if (!cf_next_valid_token(&sp->cfp))
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!cf_token_is(&sp->cfp, "{")) {
|
||||
cf_adderror_expecting(&sp->cfp, "{");
|
||||
goto error;
|
||||
}
|
||||
|
||||
func.start = sp->cfp.cur_token;
|
||||
if (!cf_pass_pair(&sp->cfp, '{', '}'))
|
||||
goto error;
|
||||
|
||||
/* it is established that the current token is '}' if we reach this */
|
||||
cf_next_token(&sp->cfp);
|
||||
|
||||
func.end = sp->cfp.cur_token;
|
||||
da_push_back(sp->funcs, &func);
|
||||
return;
|
||||
|
||||
error:
|
||||
shader_func_free(&func);
|
||||
}
|
||||
|
||||
/* parses "array[count]" */
|
||||
static bool sp_parse_param_array(struct shader_parser *sp,
|
||||
struct shader_var *param)
|
||||
{
|
||||
if (!cf_next_valid_token(&sp->cfp))
|
||||
return false;
|
||||
|
||||
if (sp->cfp.cur_token->type != CFTOKEN_NUM ||
|
||||
!valid_int_str(sp->cfp.cur_token->str.array,
|
||||
sp->cfp.cur_token->str.len))
|
||||
return false;
|
||||
|
||||
param->array_count =(int)strtol(sp->cfp.cur_token->str.array, NULL, 10);
|
||||
|
||||
if (cf_next_token_should_be(&sp->cfp, "]", ";", NULL) == PARSE_EOF)
|
||||
return false;
|
||||
|
||||
if (!cf_next_valid_token(&sp->cfp))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline int sp_parse_param_assign_intfloat(struct shader_parser *sp,
|
||||
struct shader_var *param, bool is_float)
|
||||
{
|
||||
int code;
|
||||
bool is_negative = false;
|
||||
|
||||
if (!cf_next_valid_token(&sp->cfp))
|
||||
return PARSE_EOF;
|
||||
|
||||
if (cf_token_is(&sp->cfp, "-")) {
|
||||
is_negative = true;
|
||||
|
||||
if (!cf_next_token(&sp->cfp))
|
||||
return PARSE_EOF;
|
||||
}
|
||||
|
||||
code = cf_token_is_type(&sp->cfp, CFTOKEN_NUM, "numeric value", ";");
|
||||
if (code != PARSE_SUCCESS)
|
||||
return code;
|
||||
|
||||
if (is_float) {
|
||||
float f = (float)os_strtod(sp->cfp.cur_token->str.array);
|
||||
if (is_negative) f = -f;
|
||||
da_push_back_array(param->default_val, &f, sizeof(float));
|
||||
} else {
|
||||
long l = strtol(sp->cfp.cur_token->str.array, NULL, 10);
|
||||
if (is_negative) l = -l;
|
||||
da_push_back_array(param->default_val, &l, sizeof(long));
|
||||
}
|
||||
|
||||
return PARSE_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* parses assignment for float1, float2, float3, float4, and any combination
|
||||
* for float3x3, float4x4, etc
|
||||
*/
|
||||
static inline int sp_parse_param_assign_float_array(struct shader_parser *sp,
|
||||
struct shader_var *param)
|
||||
{
|
||||
const char *float_type = param->type+5;
|
||||
int float_count = 0, code, i;
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
if (float_type[0] < '1' || float_type[0] > '4')
|
||||
cf_adderror(&sp->cfp, "Invalid row count", LEX_ERROR,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
float_count = float_type[0]-'0';
|
||||
|
||||
if (float_type[1] == 'x') {
|
||||
if (float_type[2] < '1' || float_type[2] > '4')
|
||||
cf_adderror(&sp->cfp, "Invalid column count",
|
||||
LEX_ERROR, NULL, NULL, NULL);
|
||||
|
||||
float_count *= float_type[2]-'0';
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
code = cf_next_token_should_be(&sp->cfp, "{", ";", NULL);
|
||||
if (code != PARSE_SUCCESS) return code;
|
||||
|
||||
for (i = 0; i < float_count; i++) {
|
||||
char *next = ((i+1) < float_count) ? "," : "}";
|
||||
|
||||
code = sp_parse_param_assign_intfloat(sp, param, true);
|
||||
if (code != PARSE_SUCCESS) return code;
|
||||
|
||||
code = cf_next_token_should_be(&sp->cfp, next, ";", NULL);
|
||||
if (code != PARSE_SUCCESS) return code;
|
||||
}
|
||||
|
||||
return PARSE_SUCCESS;
|
||||
}
|
||||
|
||||
static int sp_parse_param_assignment_val(struct shader_parser *sp,
|
||||
struct shader_var *param)
|
||||
{
|
||||
if (strcmp(param->type, "int") == 0)
|
||||
return sp_parse_param_assign_intfloat(sp, param, false);
|
||||
else if (strcmp(param->type, "float") == 0)
|
||||
return sp_parse_param_assign_intfloat(sp, param, true);
|
||||
else if (astrcmp_n(param->type, "float", 5) == 0)
|
||||
return sp_parse_param_assign_float_array(sp, param);
|
||||
|
||||
cf_adderror(&sp->cfp, "Invalid type '$1' used for assignment",
|
||||
LEX_ERROR, param->type, NULL, NULL);
|
||||
|
||||
return PARSE_CONTINUE;
|
||||
}
|
||||
|
||||
static inline bool sp_parse_param_assign(struct shader_parser *sp,
|
||||
struct shader_var *param)
|
||||
{
|
||||
if (sp_parse_param_assignment_val(sp, param) != PARSE_SUCCESS)
|
||||
return false;
|
||||
|
||||
if (!cf_next_valid_token(&sp->cfp))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void sp_parse_param(struct shader_parser *sp,
|
||||
char *type, char *name, bool is_const, bool is_uniform)
|
||||
{
|
||||
struct shader_var param;
|
||||
shader_var_init_param(¶m, type, name, is_uniform, is_const);
|
||||
|
||||
if (cf_token_is(&sp->cfp, ";"))
|
||||
goto complete;
|
||||
if (cf_token_is(&sp->cfp, "[") && !sp_parse_param_array(sp, ¶m))
|
||||
goto error;
|
||||
if (cf_token_is(&sp->cfp, "=") && !sp_parse_param_assign(sp, ¶m))
|
||||
goto error;
|
||||
if (!cf_token_is(&sp->cfp, ";"))
|
||||
goto error;
|
||||
|
||||
complete:
|
||||
da_push_back(sp->params, ¶m);
|
||||
return;
|
||||
|
||||
error:
|
||||
shader_var_free(¶m);
|
||||
}
|
||||
|
||||
static bool sp_get_var_specifiers(struct shader_parser *sp,
|
||||
bool *is_const, bool *is_uniform)
|
||||
{
|
||||
while(true) {
|
||||
int code = sp_check_for_keyword(sp, "const", is_const);
|
||||
if (code == PARSE_EOF)
|
||||
return false;
|
||||
else if (code == PARSE_CONTINUE)
|
||||
continue;
|
||||
|
||||
code = sp_check_for_keyword(sp, "uniform", is_uniform);
|
||||
if (code == PARSE_EOF)
|
||||
return false;
|
||||
else if (code == PARSE_CONTINUE)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void report_invalid_func_keyword(struct shader_parser *sp,
|
||||
const char *name, bool val)
|
||||
{
|
||||
if (val)
|
||||
cf_adderror(&sp->cfp, "'$1' keyword cannot be used with a "
|
||||
"function", LEX_ERROR,
|
||||
name, NULL, NULL);
|
||||
}
|
||||
|
||||
static void sp_parse_other(struct shader_parser *sp)
|
||||
{
|
||||
bool is_const = false, is_uniform = false;
|
||||
char *type = NULL, *name = NULL;
|
||||
|
||||
if (!sp_get_var_specifiers(sp, &is_const, &is_uniform))
|
||||
goto error;
|
||||
|
||||
if (cf_get_name(&sp->cfp, &type, "type", ";") != PARSE_SUCCESS)
|
||||
goto error;
|
||||
if (cf_next_name(&sp->cfp, &name, "name", ";") != PARSE_SUCCESS)
|
||||
goto error;
|
||||
|
||||
if (!cf_next_valid_token(&sp->cfp))
|
||||
goto error;
|
||||
|
||||
if (cf_token_is(&sp->cfp, "(")) {
|
||||
report_invalid_func_keyword(sp, "const", is_const);
|
||||
report_invalid_func_keyword(sp, "uniform", is_uniform);
|
||||
|
||||
sp_parse_function(sp, type, name);
|
||||
return;
|
||||
} else {
|
||||
sp_parse_param(sp, type, name, is_const, is_uniform);
|
||||
return;
|
||||
}
|
||||
|
||||
error:
|
||||
bfree(type);
|
||||
bfree(name);
|
||||
}
|
||||
|
||||
bool shader_parse(struct shader_parser *sp, const char *shader,
|
||||
const char *file)
|
||||
{
|
||||
if (!cf_parser_parse(&sp->cfp, shader, file))
|
||||
return false;
|
||||
|
||||
while (sp->cfp.cur_token && sp->cfp.cur_token->type != CFTOKEN_NONE) {
|
||||
if (cf_token_is(&sp->cfp, ";") ||
|
||||
is_whitespace(*sp->cfp.cur_token->str.array)) {
|
||||
sp->cfp.cur_token++;
|
||||
|
||||
} else if (cf_token_is(&sp->cfp, "struct")) {
|
||||
sp_parse_struct(sp);
|
||||
|
||||
} else if (cf_token_is(&sp->cfp, "sampler_state")) {
|
||||
sp_parse_sampler_state(sp);
|
||||
|
||||
} else if (cf_token_is(&sp->cfp, "{")) {
|
||||
cf_adderror(&sp->cfp, "Unexpected code segment",
|
||||
LEX_ERROR, NULL, NULL, NULL);
|
||||
cf_pass_pair(&sp->cfp, '{', '}');
|
||||
|
||||
} else {
|
||||
/* parameters and functions */
|
||||
sp_parse_other(sp);
|
||||
}
|
||||
}
|
||||
|
||||
return !error_data_has_errors(&sp->cfp.error_list);
|
||||
}
|
||||
278
libobs/graphics/shader-parser.h
Normal file
278
libobs/graphics/shader-parser.h
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../util/cf-parser.h"
|
||||
#include "graphics.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
EXPORT enum gs_shader_param_type get_shader_param_type(const char *type);
|
||||
EXPORT enum gs_sample_filter get_sample_filter(const char *filter);
|
||||
EXPORT enum gs_address_mode get_address_mode(const char *address_mode);
|
||||
|
||||
/*
|
||||
* Shader Parser
|
||||
*
|
||||
* Parses a shader and extracts data such as shader constants, samplers,
|
||||
* and vertex input information. Also allows the reformatting of shaders for
|
||||
* different libraries. This is usually used only by graphics libraries,
|
||||
*/
|
||||
|
||||
enum shader_var_type {
|
||||
SHADER_VAR_NONE,
|
||||
SHADER_VAR_UNIFORM,
|
||||
SHADER_VAR_CONST
|
||||
};
|
||||
|
||||
struct shader_var {
|
||||
char *type;
|
||||
char *name;
|
||||
char *mapping;
|
||||
enum shader_var_type var_type;
|
||||
int array_count;
|
||||
size_t gl_sampler_id; /* optional: used/parsed by GL */
|
||||
|
||||
DARRAY(uint8_t) default_val;
|
||||
};
|
||||
|
||||
static inline void shader_var_init(struct shader_var *sv)
|
||||
{
|
||||
memset(sv, 0, sizeof(struct shader_var));
|
||||
}
|
||||
|
||||
static inline void shader_var_init_param(struct shader_var *sv,
|
||||
char *type, char *name, bool is_uniform,
|
||||
bool is_const)
|
||||
{
|
||||
if (is_uniform)
|
||||
sv->var_type = SHADER_VAR_UNIFORM;
|
||||
else if (is_const)
|
||||
sv->var_type = SHADER_VAR_CONST;
|
||||
else
|
||||
sv->var_type = SHADER_VAR_NONE;
|
||||
|
||||
sv->type = type;
|
||||
sv->name = name;
|
||||
sv->mapping = NULL;
|
||||
sv->array_count = 0;
|
||||
da_init(sv->default_val);
|
||||
}
|
||||
|
||||
static inline void shader_var_free(struct shader_var *sv)
|
||||
{
|
||||
bfree(sv->type);
|
||||
bfree(sv->name);
|
||||
bfree(sv->mapping);
|
||||
da_free(sv->default_val);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
struct shader_sampler {
|
||||
char *name;
|
||||
DARRAY(char*) states;
|
||||
DARRAY(char*) values;
|
||||
};
|
||||
|
||||
static inline void shader_sampler_init(struct shader_sampler *ss)
|
||||
{
|
||||
memset(ss, 0, sizeof(struct shader_sampler));
|
||||
}
|
||||
|
||||
static inline void shader_sampler_free(struct shader_sampler *ss)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < ss->states.num; i++)
|
||||
bfree(ss->states.array[i]);
|
||||
for (i = 0; i < ss->values.num; i++)
|
||||
bfree(ss->values.array[i]);
|
||||
|
||||
bfree(ss->name);
|
||||
da_free(ss->states);
|
||||
da_free(ss->values);
|
||||
}
|
||||
|
||||
EXPORT void shader_sampler_convert(struct shader_sampler *ss,
|
||||
struct gs_sampler_info *info);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
struct shader_struct {
|
||||
char *name;
|
||||
DARRAY(struct shader_var) vars;
|
||||
};
|
||||
|
||||
static inline void shader_struct_init(struct shader_struct *ss)
|
||||
{
|
||||
memset(ss, 0, sizeof(struct shader_struct));
|
||||
}
|
||||
|
||||
static inline void shader_struct_free(struct shader_struct *ss)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < ss->vars.num; i++)
|
||||
shader_var_free(ss->vars.array+i);
|
||||
|
||||
bfree(ss->name);
|
||||
da_free(ss->vars);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
struct shader_func {
|
||||
char *name;
|
||||
char *return_type;
|
||||
char *mapping;
|
||||
DARRAY(struct shader_var) params;
|
||||
|
||||
struct cf_token *start, *end;
|
||||
};
|
||||
|
||||
static inline void shader_func_init(struct shader_func *sf,
|
||||
char *return_type, char *name)
|
||||
{
|
||||
da_init(sf->params);
|
||||
|
||||
sf->return_type = return_type;
|
||||
sf->mapping = NULL;
|
||||
sf->name = name;
|
||||
sf->start = NULL;
|
||||
sf->end = NULL;
|
||||
}
|
||||
|
||||
static inline void shader_func_free(struct shader_func *sf)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < sf->params.num; i++)
|
||||
shader_var_free(sf->params.array+i);
|
||||
|
||||
bfree(sf->name);
|
||||
bfree(sf->return_type);
|
||||
bfree(sf->mapping);
|
||||
da_free(sf->params);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
struct shader_parser {
|
||||
struct cf_parser cfp;
|
||||
|
||||
DARRAY(struct shader_var) params;
|
||||
DARRAY(struct shader_struct) structs;
|
||||
DARRAY(struct shader_sampler) samplers;
|
||||
DARRAY(struct shader_func) funcs;
|
||||
};
|
||||
|
||||
static inline void shader_parser_init(struct shader_parser *sp)
|
||||
{
|
||||
cf_parser_init(&sp->cfp);
|
||||
|
||||
da_init(sp->params);
|
||||
da_init(sp->structs);
|
||||
da_init(sp->samplers);
|
||||
da_init(sp->funcs);
|
||||
}
|
||||
|
||||
static inline void shader_parser_free(struct shader_parser *sp)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < sp->params.num; i++)
|
||||
shader_var_free(sp->params.array+i);
|
||||
for (i = 0; i < sp->structs.num; i++)
|
||||
shader_struct_free(sp->structs.array+i);
|
||||
for (i = 0; i < sp->samplers.num; i++)
|
||||
shader_sampler_free(sp->samplers.array+i);
|
||||
for (i = 0; i < sp->funcs.num; i++)
|
||||
shader_func_free(sp->funcs.array+i);
|
||||
|
||||
cf_parser_free(&sp->cfp);
|
||||
da_free(sp->params);
|
||||
da_free(sp->structs);
|
||||
da_free(sp->samplers);
|
||||
da_free(sp->funcs);
|
||||
}
|
||||
|
||||
EXPORT bool shader_parse(struct shader_parser *sp, const char *shader,
|
||||
const char *file);
|
||||
|
||||
static inline char *shader_parser_geterrors(struct shader_parser *sp)
|
||||
{
|
||||
return error_data_buildstring(&sp->cfp.error_list);
|
||||
}
|
||||
|
||||
static inline struct shader_var *shader_parser_getparam(
|
||||
struct shader_parser *sp, const char *param_name)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < sp->params.num; i++) {
|
||||
struct shader_var *param = sp->params.array+i;
|
||||
if (strcmp(param->name, param_name) == 0)
|
||||
return param;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct shader_struct *shader_parser_getstruct(
|
||||
struct shader_parser *sp, const char *struct_name)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < sp->structs.num; i++) {
|
||||
struct shader_struct *st = sp->structs.array+i;
|
||||
if (strcmp(st->name, struct_name) == 0)
|
||||
return st;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct shader_sampler *shader_parser_getsampler(
|
||||
struct shader_parser *sp, const char *sampler_name)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < sp->samplers.num; i++) {
|
||||
struct shader_sampler *sampler = sp->samplers.array+i;
|
||||
if (strcmp(sampler->name, sampler_name) == 0)
|
||||
return sampler;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct shader_func *shader_parser_getfunc(
|
||||
struct shader_parser *sp, const char *func_name)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < sp->funcs.num; i++) {
|
||||
struct shader_func *func = sp->funcs.array+i;
|
||||
if (strcmp(func->name, func_name) == 0)
|
||||
return func;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
139
libobs/graphics/texture-render.c
Normal file
139
libobs/graphics/texture-render.c
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
/*
|
||||
* This is a set of helper functions to more easily render to textures
|
||||
* without having to duplicate too much code.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include "graphics.h"
|
||||
|
||||
struct gs_texture_render {
|
||||
gs_texture_t *target, *prev_target;
|
||||
gs_zstencil_t *zs, *prev_zs;
|
||||
|
||||
uint32_t cx, cy;
|
||||
|
||||
enum gs_color_format format;
|
||||
enum gs_zstencil_format zsformat;
|
||||
|
||||
bool rendered;
|
||||
};
|
||||
|
||||
gs_texrender_t *gs_texrender_create(enum gs_color_format format,
|
||||
enum gs_zstencil_format zsformat)
|
||||
{
|
||||
struct gs_texture_render *texrender;
|
||||
texrender = bzalloc(sizeof(struct gs_texture_render));
|
||||
texrender->format = format;
|
||||
texrender->zsformat = zsformat;
|
||||
|
||||
return texrender;
|
||||
}
|
||||
|
||||
void gs_texrender_destroy(gs_texrender_t *texrender)
|
||||
{
|
||||
if (texrender) {
|
||||
gs_texture_destroy(texrender->target);
|
||||
gs_zstencil_destroy(texrender->zs);
|
||||
bfree(texrender);
|
||||
}
|
||||
}
|
||||
|
||||
static bool texrender_resetbuffer(gs_texrender_t *texrender, uint32_t cx,
|
||||
uint32_t cy)
|
||||
{
|
||||
if (!texrender)
|
||||
return false;
|
||||
|
||||
gs_texture_destroy(texrender->target);
|
||||
gs_zstencil_destroy(texrender->zs);
|
||||
|
||||
texrender->target = NULL;
|
||||
texrender->zs = NULL;
|
||||
texrender->cx = cx;
|
||||
texrender->cy = cy;
|
||||
|
||||
texrender->target = gs_texture_create(cx, cy, texrender->format,
|
||||
1, NULL, GS_RENDER_TARGET);
|
||||
if (!texrender->target)
|
||||
return false;
|
||||
|
||||
if (texrender->zsformat != GS_ZS_NONE) {
|
||||
texrender->zs = gs_zstencil_create(cx, cy, texrender->zsformat);
|
||||
if (!texrender->zs) {
|
||||
gs_texture_destroy(texrender->target);
|
||||
texrender->target = NULL;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gs_texrender_begin(gs_texrender_t *texrender, uint32_t cx, uint32_t cy)
|
||||
{
|
||||
if (!texrender || texrender->rendered)
|
||||
return false;
|
||||
|
||||
if (!cx || !cy)
|
||||
return false;
|
||||
|
||||
if (texrender->cx != cx || texrender->cy != cy)
|
||||
if (!texrender_resetbuffer(texrender, cx, cy))
|
||||
return false;
|
||||
|
||||
gs_viewport_push();
|
||||
gs_projection_push();
|
||||
gs_matrix_push();
|
||||
gs_matrix_identity();
|
||||
|
||||
texrender->prev_target = gs_get_render_target();
|
||||
texrender->prev_zs = gs_get_zstencil_target();
|
||||
gs_set_render_target(texrender->target, texrender->zs);
|
||||
|
||||
gs_set_viewport(0, 0, texrender->cx, texrender->cy);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void gs_texrender_end(gs_texrender_t *texrender)
|
||||
{
|
||||
if (!texrender)
|
||||
return;
|
||||
|
||||
gs_set_render_target(texrender->prev_target, texrender->prev_zs);
|
||||
|
||||
gs_matrix_pop();
|
||||
gs_projection_pop();
|
||||
gs_viewport_pop();
|
||||
|
||||
texrender->rendered = true;
|
||||
}
|
||||
|
||||
void gs_texrender_reset(gs_texrender_t *texrender)
|
||||
{
|
||||
if (texrender)
|
||||
texrender->rendered = false;
|
||||
}
|
||||
|
||||
gs_texture_t *gs_texrender_get_texture(const gs_texrender_t *texrender)
|
||||
{
|
||||
return texrender ? texrender->target : NULL;
|
||||
}
|
||||
52
libobs/graphics/vec2.c
Normal file
52
libobs/graphics/vec2.c
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include <math.h>
|
||||
#include "math-extra.h"
|
||||
#include "math-defs.h"
|
||||
#include "vec2.h"
|
||||
|
||||
void vec2_abs(struct vec2 *dst, const struct vec2 *v)
|
||||
{
|
||||
vec2_set(dst, fabsf(v->x), fabsf(v->y));
|
||||
}
|
||||
|
||||
void vec2_floor(struct vec2 *dst, const struct vec2 *v)
|
||||
{
|
||||
vec2_set(dst, floorf(v->x), floorf(v->y));
|
||||
}
|
||||
|
||||
void vec2_ceil(struct vec2 *dst, const struct vec2 *v)
|
||||
{
|
||||
vec2_set(dst, ceilf(v->x), ceilf(v->y));
|
||||
}
|
||||
|
||||
int vec2_close(const struct vec2 *v1, const struct vec2 *v2, float epsilon)
|
||||
{
|
||||
return close_float(v1->x, v2->x, epsilon) &&
|
||||
close_float(v1->y, v2->y, epsilon);
|
||||
}
|
||||
|
||||
void vec2_norm(struct vec2 *dst, const struct vec2 *v)
|
||||
{
|
||||
float len = vec2_len(v);
|
||||
|
||||
if (len > 0.0f) {
|
||||
len = 1.0f / len;
|
||||
vec2_mulf(dst, v, len);
|
||||
}
|
||||
}
|
||||
169
libobs/graphics/vec2.h
Normal file
169
libobs/graphics/vec2.h
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../util/c99defs.h"
|
||||
#include <math.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct vec2 {
|
||||
union {
|
||||
struct {
|
||||
float x, y;
|
||||
};
|
||||
float ptr[2];
|
||||
};
|
||||
};
|
||||
|
||||
static inline void vec2_zero(struct vec2 *dst)
|
||||
{
|
||||
dst->x = 0.0f;
|
||||
dst->y = 0.0f;
|
||||
}
|
||||
|
||||
static inline void vec2_set(struct vec2 *dst, float x, float y)
|
||||
{
|
||||
dst->x = x;
|
||||
dst->y = y;
|
||||
}
|
||||
|
||||
static inline void vec2_copy(struct vec2 *dst, const struct vec2 *v)
|
||||
{
|
||||
dst->x = v->x;
|
||||
dst->y = v->y;
|
||||
}
|
||||
|
||||
static inline void vec2_add(struct vec2 *dst, const struct vec2 *v1,
|
||||
const struct vec2 *v2)
|
||||
{
|
||||
vec2_set(dst, v1->x+v2->x, v1->y+v2->y);
|
||||
}
|
||||
|
||||
static inline void vec2_sub(struct vec2 *dst, const struct vec2 *v1,
|
||||
const struct vec2 *v2)
|
||||
{
|
||||
vec2_set(dst, v1->x-v2->x, v1->y-v2->y);
|
||||
}
|
||||
|
||||
static inline void vec2_mul(struct vec2 *dst, const struct vec2 *v1,
|
||||
const struct vec2 *v2)
|
||||
{
|
||||
vec2_set(dst, v1->x*v2->x, v1->y*v2->y);
|
||||
}
|
||||
|
||||
static inline void vec2_div(struct vec2 *dst, const struct vec2 *v1,
|
||||
const struct vec2 *v2)
|
||||
{
|
||||
vec2_set(dst, v1->x/v2->x, v1->y/v2->y);
|
||||
}
|
||||
|
||||
static inline void vec2_addf(struct vec2 *dst, const struct vec2 *v,
|
||||
float f)
|
||||
{
|
||||
vec2_set(dst, v->x+f, v->y+f);
|
||||
}
|
||||
|
||||
static inline void vec2_subf(struct vec2 *dst, const struct vec2 *v,
|
||||
float f)
|
||||
{
|
||||
vec2_set(dst, v->x-f, v->y-f);
|
||||
}
|
||||
|
||||
static inline void vec2_mulf(struct vec2 *dst, const struct vec2 *v,
|
||||
float f)
|
||||
{
|
||||
vec2_set(dst, v->x*f, v->y*f);
|
||||
}
|
||||
|
||||
static inline void vec2_divf(struct vec2 *dst, const struct vec2 *v,
|
||||
float f)
|
||||
{
|
||||
vec2_set(dst, v->x/f, v->y/f);
|
||||
}
|
||||
|
||||
static inline void vec2_neg(struct vec2 *dst, const struct vec2 *v)
|
||||
{
|
||||
vec2_set(dst, -v->x, -v->y);
|
||||
}
|
||||
|
||||
static inline float vec2_dot(const struct vec2 *v1, const struct vec2 *v2)
|
||||
{
|
||||
return (v1->x+v2->x) * (v1->y+v2->y);
|
||||
}
|
||||
|
||||
static inline float vec2_len(const struct vec2 *v)
|
||||
{
|
||||
return sqrtf(v->x*v->x + v->y*v->y);
|
||||
}
|
||||
|
||||
static inline float vec2_dist(const struct vec2 *v1, const struct vec2 *v2)
|
||||
{
|
||||
struct vec2 temp;
|
||||
vec2_sub(&temp, v1, v2);
|
||||
return vec2_len(&temp);
|
||||
}
|
||||
|
||||
static inline void vec2_minf(struct vec2 *dst, const struct vec2 *v,
|
||||
float val)
|
||||
{
|
||||
if (v->x < val)
|
||||
dst->x = val;
|
||||
if (v->y < val)
|
||||
dst->y = val;
|
||||
}
|
||||
|
||||
static inline void vec2_min(struct vec2 *dst, const struct vec2 *v,
|
||||
const struct vec2 *min_v)
|
||||
{
|
||||
if (v->x < min_v->x)
|
||||
dst->x = min_v->x;
|
||||
if (v->y < min_v->y)
|
||||
dst->y = min_v->y;
|
||||
}
|
||||
|
||||
static inline void vec2_maxf(struct vec2 *dst, const struct vec2 *v,
|
||||
float val)
|
||||
{
|
||||
if (v->x > val)
|
||||
dst->x = val;
|
||||
if (v->y > val)
|
||||
dst->y = val;
|
||||
}
|
||||
|
||||
static inline void vec2_max(struct vec2 *dst, const struct vec2 *v,
|
||||
const struct vec2 *max_v)
|
||||
{
|
||||
if (v->x > max_v->x)
|
||||
dst->x = max_v->x;
|
||||
if (v->y > max_v->y)
|
||||
dst->y = max_v->y;
|
||||
}
|
||||
|
||||
EXPORT void vec2_abs(struct vec2 *dst, const struct vec2 *v);
|
||||
EXPORT void vec2_floor(struct vec2 *dst, const struct vec2 *v);
|
||||
EXPORT void vec2_ceil(struct vec2 *dst, const struct vec2 *v);
|
||||
EXPORT int vec2_close(const struct vec2 *v1, const struct vec2 *v2,
|
||||
float epsilon);
|
||||
EXPORT void vec2_norm(struct vec2 *dst, const struct vec2 *v);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
91
libobs/graphics/vec3.c
Normal file
91
libobs/graphics/vec3.c
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "vec3.h"
|
||||
#include "vec4.h"
|
||||
#include "quat.h"
|
||||
#include "axisang.h"
|
||||
#include "plane.h"
|
||||
#include "matrix3.h"
|
||||
#include "math-extra.h"
|
||||
|
||||
void vec3_from_vec4(struct vec3 *dst, const struct vec4 *v)
|
||||
{
|
||||
dst->m = v->m;
|
||||
dst->w = 0.0f;
|
||||
}
|
||||
|
||||
float vec3_plane_dist(const struct vec3 *v, const struct plane *p)
|
||||
{
|
||||
return vec3_dot(v, &p->dir) - p->dist;
|
||||
}
|
||||
|
||||
void vec3_rotate(struct vec3 *dst, const struct vec3 *v,
|
||||
const struct matrix3 *m)
|
||||
{
|
||||
struct vec3 temp;
|
||||
vec3_copy(&temp, v);
|
||||
|
||||
dst->x = vec3_dot(&temp, &m->x);
|
||||
dst->y = vec3_dot(&temp, &m->y);
|
||||
dst->z = vec3_dot(&temp, &m->z);
|
||||
dst->w = 0.0f;
|
||||
}
|
||||
|
||||
void vec3_transform(struct vec3 *dst, const struct vec3 *v,
|
||||
const struct matrix4 *m)
|
||||
{
|
||||
struct vec4 v4;
|
||||
vec4_from_vec3(&v4, v);
|
||||
vec4_transform(&v4, &v4, m);
|
||||
vec3_from_vec4(dst, &v4);
|
||||
}
|
||||
|
||||
void vec3_transform3x4(struct vec3 *dst, const struct vec3 *v,
|
||||
const struct matrix3 *m)
|
||||
{
|
||||
struct vec3 temp;
|
||||
vec3_sub(&temp, v, &m->t);
|
||||
|
||||
dst->x = vec3_dot(&temp, &m->x);
|
||||
dst->y = vec3_dot(&temp, &m->y);
|
||||
dst->z = vec3_dot(&temp, &m->z);
|
||||
dst->w = 0.0f;
|
||||
}
|
||||
|
||||
void vec3_mirror(struct vec3 *dst, const struct vec3 *v, const struct plane *p)
|
||||
{
|
||||
struct vec3 temp;
|
||||
vec3_mulf(&temp, &p->dir, vec3_plane_dist(v, p) * 2.0f);
|
||||
vec3_sub(dst, v, &temp);
|
||||
}
|
||||
|
||||
void vec3_mirrorv(struct vec3 *dst, const struct vec3 *v,
|
||||
const struct vec3 *vec)
|
||||
{
|
||||
struct vec3 temp;
|
||||
vec3_mulf(&temp, vec, vec3_dot(v, vec) * 2.0f);
|
||||
vec3_sub(dst, v, &temp);
|
||||
}
|
||||
|
||||
void vec3_rand(struct vec3 *dst, int positive_only)
|
||||
{
|
||||
dst->x = rand_float(positive_only);
|
||||
dst->y = rand_float(positive_only);
|
||||
dst->z = rand_float(positive_only);
|
||||
dst->w = 0.0f;
|
||||
}
|
||||
244
libobs/graphics/vec3.h
Normal file
244
libobs/graphics/vec3.h
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "math-defs.h"
|
||||
#include "vec4.h"
|
||||
#include <xmmintrin.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct plane;
|
||||
struct matrix3;
|
||||
struct matrix4;
|
||||
struct quat;
|
||||
|
||||
struct vec3 {
|
||||
union {
|
||||
struct {
|
||||
float x, y, z, w;
|
||||
};
|
||||
float ptr[4];
|
||||
__m128 m;
|
||||
};
|
||||
};
|
||||
|
||||
static inline void vec3_zero(struct vec3 *v)
|
||||
{
|
||||
v->m = _mm_setzero_ps();
|
||||
}
|
||||
|
||||
static inline void vec3_set(struct vec3 *dst, float x, float y, float z)
|
||||
{
|
||||
dst->m = _mm_set_ps(0.0f, z, y, x);
|
||||
}
|
||||
|
||||
static inline void vec3_copy(struct vec3 *dst, const struct vec3 *v)
|
||||
{
|
||||
dst->m = v->m;
|
||||
}
|
||||
|
||||
EXPORT void vec3_from_vec4(struct vec3 *dst, const struct vec4 *v);
|
||||
|
||||
static inline void vec3_add(struct vec3 *dst, const struct vec3 *v1,
|
||||
const struct vec3 *v2)
|
||||
{
|
||||
dst->m = _mm_add_ps(v1->m, v2->m);
|
||||
dst->w = 0.0f;
|
||||
}
|
||||
|
||||
static inline void vec3_sub(struct vec3 *dst, const struct vec3 *v1,
|
||||
const struct vec3 *v2)
|
||||
{
|
||||
dst->m = _mm_sub_ps(v1->m, v2->m);
|
||||
dst->w = 0.0f;
|
||||
}
|
||||
|
||||
static inline void vec3_mul(struct vec3 *dst, const struct vec3 *v1,
|
||||
const struct vec3 *v2)
|
||||
{
|
||||
dst->m = _mm_mul_ps(v1->m, v2->m);
|
||||
}
|
||||
|
||||
static inline void vec3_div(struct vec3 *dst, const struct vec3 *v1,
|
||||
const struct vec3 *v2)
|
||||
{
|
||||
dst->m = _mm_div_ps(v1->m, v2->m);
|
||||
dst->w = 0.0f;
|
||||
}
|
||||
|
||||
static inline void vec3_addf(struct vec3 *dst, const struct vec3 *v,
|
||||
float f)
|
||||
{
|
||||
dst->m = _mm_add_ps(v->m, _mm_set1_ps(f));
|
||||
dst->w = 0.0f;
|
||||
}
|
||||
|
||||
static inline void vec3_subf(struct vec3 *dst, const struct vec3 *v,
|
||||
float f)
|
||||
{
|
||||
dst->m = _mm_sub_ps(v->m, _mm_set1_ps(f));
|
||||
dst->w = 0.0f;
|
||||
}
|
||||
|
||||
static inline void vec3_mulf(struct vec3 *dst, const struct vec3 *v,
|
||||
float f)
|
||||
{
|
||||
dst->m = _mm_mul_ps(v->m, _mm_set1_ps(f));
|
||||
}
|
||||
|
||||
static inline void vec3_divf(struct vec3 *dst, const struct vec3 *v,
|
||||
float f)
|
||||
{
|
||||
dst->m = _mm_div_ps(v->m, _mm_set1_ps(f));
|
||||
dst->w = 0.0f;
|
||||
}
|
||||
|
||||
static inline float vec3_dot(const struct vec3 *v1, const struct vec3 *v2)
|
||||
{
|
||||
struct vec3 add;
|
||||
__m128 mul = _mm_mul_ps(v1->m, v2->m);
|
||||
add.m = _mm_add_ps(_mm_movehl_ps(mul, mul), mul);
|
||||
add.m = _mm_add_ps(_mm_shuffle_ps(add.m, add.m, 0x55), add.m);
|
||||
return add.x;
|
||||
}
|
||||
|
||||
static inline void vec3_cross(struct vec3 *dst, const struct vec3 *v1,
|
||||
const struct vec3 *v2)
|
||||
{
|
||||
__m128 s1v1 = _mm_shuffle_ps(v1->m, v1->m, _MM_SHUFFLE(3, 0, 2, 1));
|
||||
__m128 s1v2 = _mm_shuffle_ps(v2->m, v2->m, _MM_SHUFFLE(3, 1, 0, 2));
|
||||
__m128 s2v1 = _mm_shuffle_ps(v1->m, v1->m, _MM_SHUFFLE(3, 1, 0, 2));
|
||||
__m128 s2v2 = _mm_shuffle_ps(v2->m, v2->m, _MM_SHUFFLE(3, 0, 2, 1));
|
||||
dst->m = _mm_sub_ps(_mm_mul_ps(s1v1, s1v2), _mm_mul_ps(s2v1, s2v2));
|
||||
}
|
||||
|
||||
static inline void vec3_neg(struct vec3 *dst, const struct vec3 *v)
|
||||
{
|
||||
dst->x = -v->x;
|
||||
dst->y = -v->y;
|
||||
dst->z = -v->z;
|
||||
dst->w = 0.0f;
|
||||
}
|
||||
|
||||
static inline float vec3_len(const struct vec3 *v)
|
||||
{
|
||||
float dot_val = vec3_dot(v, v);
|
||||
return (dot_val > 0.0f) ? sqrtf(dot_val) : 0.0f;
|
||||
}
|
||||
|
||||
static inline float vec3_dist(const struct vec3 *v1, const struct vec3 *v2)
|
||||
{
|
||||
struct vec3 temp;
|
||||
float dot_val;
|
||||
|
||||
vec3_sub(&temp, v1, v2);
|
||||
dot_val = vec3_dot(&temp, &temp);
|
||||
return (dot_val > 0.0f) ? sqrtf(dot_val) : 0.0f;
|
||||
}
|
||||
|
||||
static inline void vec3_norm(struct vec3 *dst, const struct vec3 *v)
|
||||
{
|
||||
float dot_val = vec3_dot(v, v);
|
||||
dst->m = (dot_val > 0.0f) ?
|
||||
_mm_mul_ps(v->m, _mm_set1_ps(1.0f/sqrtf(dot_val))) :
|
||||
_mm_setzero_ps();
|
||||
}
|
||||
|
||||
static inline bool vec3_close(const struct vec3 *v1, const struct vec3 *v2,
|
||||
float epsilon)
|
||||
{
|
||||
struct vec3 test;
|
||||
vec3_sub(&test, v1, v2);
|
||||
return test.x < epsilon && test.y < epsilon && test.z < epsilon;
|
||||
}
|
||||
|
||||
static inline void vec3_min(struct vec3 *dst, const struct vec3 *v1,
|
||||
const struct vec3 *v2)
|
||||
{
|
||||
dst->m = _mm_min_ps(v1->m, v2->m);
|
||||
dst->w = 0.0f;
|
||||
}
|
||||
|
||||
static inline void vec3_minf(struct vec3 *dst, const struct vec3 *v,
|
||||
float f)
|
||||
{
|
||||
dst->m = _mm_min_ps(v->m, _mm_set1_ps(f));
|
||||
dst->w = 0.0f;
|
||||
}
|
||||
|
||||
static inline void vec3_max(struct vec3 *dst, const struct vec3 *v1,
|
||||
const struct vec3 *v2)
|
||||
{
|
||||
dst->m = _mm_max_ps(v1->m, v2->m);
|
||||
dst->w = 0.0f;
|
||||
}
|
||||
|
||||
static inline void vec3_maxf(struct vec3 *dst, const struct vec3 *v,
|
||||
float f)
|
||||
{
|
||||
dst->m = _mm_max_ps(v->m, _mm_set1_ps(f));
|
||||
dst->w = 0.0f;
|
||||
}
|
||||
|
||||
static inline void vec3_abs(struct vec3 *dst, const struct vec3 *v)
|
||||
{
|
||||
dst->x = fabsf(v->x);
|
||||
dst->y = fabsf(v->y);
|
||||
dst->z = fabsf(v->z);
|
||||
dst->w = 0.0f;
|
||||
}
|
||||
|
||||
static inline void vec3_floor(struct vec3 *dst, const struct vec3 *v)
|
||||
{
|
||||
dst->x = floorf(v->x);
|
||||
dst->y = floorf(v->y);
|
||||
dst->z = floorf(v->z);
|
||||
dst->w = 0.0f;
|
||||
}
|
||||
|
||||
static inline void vec3_ceil(struct vec3 *dst, const struct vec3 *v)
|
||||
{
|
||||
dst->x = ceilf(v->x);
|
||||
dst->y = ceilf(v->y);
|
||||
dst->z = ceilf(v->z);
|
||||
dst->w = 0.0f;
|
||||
}
|
||||
|
||||
EXPORT float vec3_plane_dist(const struct vec3 *v, const struct plane *p);
|
||||
|
||||
EXPORT void vec3_transform(struct vec3 *dst, const struct vec3 *v,
|
||||
const struct matrix4 *m);
|
||||
|
||||
EXPORT void vec3_rotate(struct vec3 *dst, const struct vec3 *v,
|
||||
const struct matrix3 *m);
|
||||
EXPORT void vec3_transform3x4(struct vec3 *dst, const struct vec3 *v,
|
||||
const struct matrix3 *m);
|
||||
|
||||
EXPORT void vec3_mirror(struct vec3 *dst, const struct vec3 *v,
|
||||
const struct plane *p);
|
||||
EXPORT void vec3_mirrorv(struct vec3 *dst, const struct vec3 *v,
|
||||
const struct vec3 *vec);
|
||||
|
||||
EXPORT void vec3_rand(struct vec3 *dst, int positive_only);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
42
libobs/graphics/vec4.c
Normal file
42
libobs/graphics/vec4.c
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "vec4.h"
|
||||
#include "vec3.h"
|
||||
#include "matrix4.h"
|
||||
|
||||
void vec4_from_vec3(struct vec4 *dst, const struct vec3 *v)
|
||||
{
|
||||
dst->m = v->m;
|
||||
dst->w = 1.0f;
|
||||
}
|
||||
|
||||
void vec4_transform(struct vec4 *dst, const struct vec4 *v,
|
||||
const struct matrix4 *m)
|
||||
{
|
||||
struct vec4 temp;
|
||||
struct matrix4 transpose;
|
||||
|
||||
matrix4_transpose(&transpose, m);
|
||||
|
||||
temp.x = vec4_dot(&transpose.x, v);
|
||||
temp.y = vec4_dot(&transpose.y, v);
|
||||
temp.z = vec4_dot(&transpose.z, v);
|
||||
temp.w = vec4_dot(&transpose.t, v);
|
||||
|
||||
vec4_copy(dst, &temp);
|
||||
}
|
||||
253
libobs/graphics/vec4.h
Normal file
253
libobs/graphics/vec4.h
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "math-defs.h"
|
||||
#include <xmmintrin.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct vec3;
|
||||
struct matrix4;
|
||||
|
||||
struct vec4 {
|
||||
union {
|
||||
struct {
|
||||
float x, y, z, w;
|
||||
};
|
||||
float ptr[4];
|
||||
__m128 m;
|
||||
};
|
||||
};
|
||||
|
||||
static inline void vec4_zero(struct vec4 *v)
|
||||
{
|
||||
v->m = _mm_setzero_ps();
|
||||
}
|
||||
|
||||
static inline void vec4_set(struct vec4 *dst, float x, float y, float z,
|
||||
float w)
|
||||
{
|
||||
dst->m = _mm_set_ps(w, z, y, x);
|
||||
}
|
||||
|
||||
static inline void vec4_copy(struct vec4 *dst, const struct vec4 *v)
|
||||
{
|
||||
dst->m = v->m;
|
||||
}
|
||||
|
||||
EXPORT void vec4_from_vec3(struct vec4 *dst, const struct vec3 *v);
|
||||
|
||||
static inline void vec4_add(struct vec4 *dst, const struct vec4 *v1,
|
||||
const struct vec4 *v2)
|
||||
{
|
||||
dst->m = _mm_add_ps(v1->m, v2->m);
|
||||
}
|
||||
|
||||
static inline void vec4_sub(struct vec4 *dst, const struct vec4 *v1,
|
||||
const struct vec4 *v2)
|
||||
{
|
||||
dst->m = _mm_sub_ps(v1->m, v2->m);
|
||||
}
|
||||
|
||||
static inline void vec4_mul(struct vec4 *dst, const struct vec4 *v1,
|
||||
const struct vec4 *v2)
|
||||
{
|
||||
dst->m = _mm_mul_ps(v1->m, v2->m);
|
||||
}
|
||||
|
||||
static inline void vec4_div(struct vec4 *dst, const struct vec4 *v1,
|
||||
const struct vec4 *v2)
|
||||
{
|
||||
dst->m = _mm_div_ps(v1->m, v2->m);
|
||||
}
|
||||
|
||||
static inline void vec4_addf(struct vec4 *dst, const struct vec4 *v,
|
||||
float f)
|
||||
{
|
||||
dst->m = _mm_add_ps(v->m, _mm_set1_ps(f));
|
||||
}
|
||||
|
||||
static inline void vec4_subf(struct vec4 *dst, const struct vec4 *v,
|
||||
float f)
|
||||
{
|
||||
dst->m = _mm_sub_ps(v->m, _mm_set1_ps(f));
|
||||
}
|
||||
|
||||
static inline void vec4_mulf(struct vec4 *dst, const struct vec4 *v,
|
||||
float f)
|
||||
{
|
||||
dst->m = _mm_mul_ps(v->m, _mm_set1_ps(f));
|
||||
}
|
||||
|
||||
static inline void vec4_divf(struct vec4 *dst, const struct vec4 *v,
|
||||
float f)
|
||||
{
|
||||
dst->m = _mm_div_ps(v->m, _mm_set1_ps(f));
|
||||
}
|
||||
|
||||
static inline float vec4_dot(const struct vec4 *v1, const struct vec4 *v2)
|
||||
{
|
||||
struct vec4 add;
|
||||
__m128 mul = _mm_mul_ps(v1->m, v2->m);
|
||||
add.m = _mm_add_ps(_mm_movehl_ps(mul, mul), mul);
|
||||
add.m = _mm_add_ps(_mm_shuffle_ps(add.m, add.m, 0x55), add.m);
|
||||
return add.x;
|
||||
}
|
||||
|
||||
static inline void vec4_neg(struct vec4 *dst, const struct vec4 *v)
|
||||
{
|
||||
dst->x = -v->x;
|
||||
dst->y = -v->y;
|
||||
dst->z = -v->z;
|
||||
dst->w = -v->w;
|
||||
}
|
||||
|
||||
static inline float vec4_len(const struct vec4 *v)
|
||||
{
|
||||
float dot_val = vec4_dot(v, v);
|
||||
return (dot_val > 0.0f) ? sqrtf(dot_val) : 0.0f;
|
||||
}
|
||||
|
||||
static inline float vec4_dist(const struct vec4 *v1, const struct vec4 *v2)
|
||||
{
|
||||
struct vec4 temp;
|
||||
float dot_val;
|
||||
|
||||
vec4_sub(&temp, v1, v2);
|
||||
dot_val = vec4_dot(&temp, &temp);
|
||||
return (dot_val > 0.0f) ? sqrtf(dot_val) : 0.0f;
|
||||
}
|
||||
|
||||
static inline void vec4_norm(struct vec4 *dst, const struct vec4 *v)
|
||||
{
|
||||
float dot_val = vec4_dot(v, v);
|
||||
dst->m = (dot_val > 0.0f) ?
|
||||
_mm_mul_ps(v->m, _mm_set1_ps(1.0f/sqrtf(dot_val))) :
|
||||
_mm_setzero_ps();
|
||||
}
|
||||
|
||||
static inline int vec4_close(const struct vec4 *v1, const struct vec4 *v2,
|
||||
float epsilon)
|
||||
{
|
||||
struct vec4 test;
|
||||
vec4_sub(&test, v1, v2);
|
||||
return test.x < epsilon &&
|
||||
test.y < epsilon &&
|
||||
test.z < epsilon &&
|
||||
test.w < epsilon;
|
||||
}
|
||||
|
||||
static inline void vec4_min(struct vec4 *dst, const struct vec4 *v1,
|
||||
const struct vec4 *v2)
|
||||
{
|
||||
dst->m = _mm_min_ps(v1->m, v2->m);
|
||||
}
|
||||
|
||||
static inline void vec4_minf(struct vec4 *dst, const struct vec4 *v,
|
||||
float f)
|
||||
{
|
||||
dst->m = _mm_min_ps(v->m, _mm_set1_ps(f));
|
||||
}
|
||||
|
||||
static inline void vec4_max(struct vec4 *dst, const struct vec4 *v1,
|
||||
const struct vec4 *v2)
|
||||
{
|
||||
dst->m = _mm_max_ps(v1->m, v2->m);
|
||||
}
|
||||
|
||||
static inline void vec4_maxf(struct vec4 *dst, const struct vec4 *v,
|
||||
float f)
|
||||
{
|
||||
dst->m = _mm_max_ps(v->m, _mm_set1_ps(f));
|
||||
}
|
||||
|
||||
static inline void vec4_abs(struct vec4 *dst, const struct vec4 *v)
|
||||
{
|
||||
dst->x = fabsf(v->x);
|
||||
dst->y = fabsf(v->y);
|
||||
dst->z = fabsf(v->z);
|
||||
dst->w = fabsf(v->w);
|
||||
}
|
||||
|
||||
static inline void vec4_floor(struct vec4 *dst, const struct vec4 *v)
|
||||
{
|
||||
dst->x = floorf(v->x);
|
||||
dst->y = floorf(v->y);
|
||||
dst->z = floorf(v->z);
|
||||
dst->w = floorf(v->w);
|
||||
}
|
||||
|
||||
static inline void vec4_ceil(struct vec4 *dst, const struct vec4 *v)
|
||||
{
|
||||
dst->x = ceilf(v->x);
|
||||
dst->y = ceilf(v->y);
|
||||
dst->z = ceilf(v->z);
|
||||
dst->w = ceilf(v->w);
|
||||
}
|
||||
|
||||
static inline uint32_t vec4_to_rgba(const struct vec4 *src)
|
||||
{
|
||||
uint32_t val;
|
||||
val = (uint32_t)((double)src->x * 255.0);
|
||||
val |= (uint32_t)((double)src->y * 255.0) << 8;
|
||||
val |= (uint32_t)((double)src->z * 255.0) << 16;
|
||||
val |= (uint32_t)((double)src->w * 255.0) << 24;
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline uint32_t vec4_to_bgra(const struct vec4 *src)
|
||||
{
|
||||
uint32_t val;
|
||||
val = (uint32_t)((double)src->z * 255.0);
|
||||
val |= (uint32_t)((double)src->y * 255.0) << 8;
|
||||
val |= (uint32_t)((double)src->x * 255.0) << 16;
|
||||
val |= (uint32_t)((double)src->w * 255.0) << 24;
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void vec4_from_rgba(struct vec4 *dst, uint32_t rgba)
|
||||
{
|
||||
dst->x = (float)((double)(rgba&0xFF) * (1.0/255.0));
|
||||
rgba >>= 8;
|
||||
dst->y = (float)((double)(rgba&0xFF) * (1.0/255.0));
|
||||
rgba >>= 8;
|
||||
dst->z = (float)((double)(rgba&0xFF) * (1.0/255.0));
|
||||
rgba >>= 8;
|
||||
dst->w = (float)((double)(rgba&0xFF) * (1.0/255.0));
|
||||
}
|
||||
|
||||
static inline void vec4_from_bgra(struct vec4 *dst, uint32_t bgra)
|
||||
{
|
||||
dst->z = (float)((double)(bgra&0xFF) * (1.0/255.0));
|
||||
bgra >>= 8;
|
||||
dst->y = (float)((double)(bgra&0xFF) * (1.0/255.0));
|
||||
bgra >>= 8;
|
||||
dst->x = (float)((double)(bgra&0xFF) * (1.0/255.0));
|
||||
bgra >>= 8;
|
||||
dst->w = (float)((double)(bgra&0xFF) * (1.0/255.0));
|
||||
}
|
||||
|
||||
EXPORT void vec4_transform(struct vec4 *dst, const struct vec4 *v,
|
||||
const struct matrix4 *m);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
501
libobs/media-io/audio-io.c
Normal file
501
libobs/media-io/audio-io.c
Normal file
|
|
@ -0,0 +1,501 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include <math.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "../util/threading.h"
|
||||
#include "../util/darray.h"
|
||||
#include "../util/circlebuf.h"
|
||||
#include "../util/platform.h"
|
||||
#include "../util/profiler.h"
|
||||
|
||||
#include "audio-io.h"
|
||||
#include "audio-resampler.h"
|
||||
|
||||
extern profiler_name_store_t *obs_get_profiler_name_store(void);
|
||||
|
||||
/* #define DEBUG_AUDIO */
|
||||
|
||||
#define nop() do {int invalid = 0;} while(0)
|
||||
|
||||
struct audio_input {
|
||||
struct audio_convert_info conversion;
|
||||
audio_resampler_t *resampler;
|
||||
|
||||
audio_output_callback_t callback;
|
||||
void *param;
|
||||
};
|
||||
|
||||
static inline void audio_input_free(struct audio_input *input)
|
||||
{
|
||||
audio_resampler_destroy(input->resampler);
|
||||
}
|
||||
|
||||
struct audio_mix {
|
||||
DARRAY(struct audio_input) inputs;
|
||||
float buffer[MAX_AUDIO_CHANNELS][AUDIO_OUTPUT_FRAMES];
|
||||
};
|
||||
|
||||
struct audio_output {
|
||||
struct audio_output_info info;
|
||||
size_t block_size;
|
||||
size_t channels;
|
||||
size_t planes;
|
||||
|
||||
pthread_t thread;
|
||||
os_event_t *stop_event;
|
||||
|
||||
bool initialized;
|
||||
|
||||
audio_input_callback_t input_cb;
|
||||
void *input_param;
|
||||
pthread_mutex_t input_mutex;
|
||||
struct audio_mix mixes[MAX_AUDIO_MIXES];
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* the following functions are used to calculate frame offsets based upon
|
||||
* timestamps. this will actually work accurately as long as you handle the
|
||||
* values correctly */
|
||||
|
||||
static inline double ts_to_frames(const audio_t *audio, uint64_t ts)
|
||||
{
|
||||
double audio_offset_d = (double)ts;
|
||||
audio_offset_d /= 1000000000.0;
|
||||
audio_offset_d *= (double)audio->info.samples_per_sec;
|
||||
|
||||
return audio_offset_d;
|
||||
}
|
||||
|
||||
static inline double positive_round(double val)
|
||||
{
|
||||
return floor(val+0.5);
|
||||
}
|
||||
|
||||
static int64_t ts_diff_frames(const audio_t *audio, uint64_t ts1, uint64_t ts2)
|
||||
{
|
||||
double diff = ts_to_frames(audio, ts1) - ts_to_frames(audio, ts2);
|
||||
return (int64_t)positive_round(diff);
|
||||
}
|
||||
|
||||
static int64_t ts_diff_bytes(const audio_t *audio, uint64_t ts1, uint64_t ts2)
|
||||
{
|
||||
return ts_diff_frames(audio, ts1, ts2) * (int64_t)audio->block_size;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static inline uint64_t min_uint64(uint64_t a, uint64_t b)
|
||||
{
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
static inline size_t min_size(size_t a, size_t b)
|
||||
{
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
#ifndef CLAMP
|
||||
#define CLAMP(val, minval, maxval) \
|
||||
((val > maxval) ? maxval : ((val < minval) ? minval : val))
|
||||
#endif
|
||||
|
||||
static bool resample_audio_output(struct audio_input *input,
|
||||
struct audio_data *data)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
if (input->resampler) {
|
||||
uint8_t *output[MAX_AV_PLANES];
|
||||
uint32_t frames;
|
||||
uint64_t offset;
|
||||
|
||||
memset(output, 0, sizeof(output));
|
||||
|
||||
success = audio_resampler_resample(input->resampler,
|
||||
output, &frames, &offset,
|
||||
(const uint8_t *const *)data->data,
|
||||
data->frames);
|
||||
|
||||
for (size_t i = 0; i < MAX_AV_PLANES; i++)
|
||||
data->data[i] = output[i];
|
||||
data->frames = frames;
|
||||
data->timestamp -= offset;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static inline void do_audio_output(struct audio_output *audio,
|
||||
size_t mix_idx, uint64_t timestamp, uint32_t frames)
|
||||
{
|
||||
struct audio_mix *mix = &audio->mixes[mix_idx];
|
||||
struct audio_data data;
|
||||
|
||||
pthread_mutex_lock(&audio->input_mutex);
|
||||
|
||||
for (size_t i = mix->inputs.num; i > 0; i--) {
|
||||
struct audio_input *input = mix->inputs.array+(i-1);
|
||||
|
||||
for (size_t i = 0; i < audio->planes; i++)
|
||||
data.data[i] = (uint8_t*)mix->buffer[i];
|
||||
data.frames = frames;
|
||||
data.timestamp = timestamp;
|
||||
|
||||
if (resample_audio_output(input, &data))
|
||||
input->callback(input->param, mix_idx, &data);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&audio->input_mutex);
|
||||
}
|
||||
|
||||
static inline void clamp_audio_output(struct audio_output *audio, size_t bytes)
|
||||
{
|
||||
size_t float_size = bytes / sizeof(float);
|
||||
|
||||
for (size_t mix_idx = 0; mix_idx < MAX_AUDIO_MIXES; mix_idx++) {
|
||||
struct audio_mix *mix = &audio->mixes[mix_idx];
|
||||
|
||||
/* do not process mixing if a specific mix is inactive */
|
||||
if (!mix->inputs.num)
|
||||
continue;
|
||||
|
||||
for (size_t plane = 0; plane < audio->planes; plane++) {
|
||||
float *mix_data = mix->buffer[plane];
|
||||
float *mix_end = &mix_data[float_size];
|
||||
|
||||
while (mix_data < mix_end) {
|
||||
float val = *mix_data;
|
||||
val = (val > 1.0f) ? 1.0f : val;
|
||||
val = (val < -1.0f) ? -1.0f : val;
|
||||
*(mix_data++) = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void input_and_output(struct audio_output *audio,
|
||||
uint64_t audio_time, uint64_t prev_time)
|
||||
{
|
||||
size_t bytes = AUDIO_OUTPUT_FRAMES * audio->block_size;
|
||||
struct audio_output_data data[MAX_AUDIO_MIXES];
|
||||
uint32_t active_mixes = 0;
|
||||
uint64_t new_ts = 0;
|
||||
bool success;
|
||||
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
#ifdef DEBUG_AUDIO
|
||||
blog(LOG_DEBUG, "audio_time: %llu, prev_time: %llu, bytes: %lu",
|
||||
audio_time, prev_time, bytes);
|
||||
#endif
|
||||
|
||||
/* get mixers */
|
||||
pthread_mutex_lock(&audio->input_mutex);
|
||||
for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {
|
||||
if (audio->mixes[i].inputs.num)
|
||||
active_mixes |= (1 << i);
|
||||
}
|
||||
pthread_mutex_unlock(&audio->input_mutex);
|
||||
|
||||
/* clear mix buffers */
|
||||
for (size_t mix_idx = 0; mix_idx < MAX_AUDIO_MIXES; mix_idx++) {
|
||||
struct audio_mix *mix = &audio->mixes[mix_idx];
|
||||
|
||||
memset(mix->buffer[0], 0, AUDIO_OUTPUT_FRAMES *
|
||||
MAX_AUDIO_CHANNELS * sizeof(float));
|
||||
|
||||
for (size_t i = 0; i < audio->planes; i++)
|
||||
data[mix_idx].data[i] = mix->buffer[i];
|
||||
}
|
||||
|
||||
/* get new audio data */
|
||||
success = audio->input_cb(audio->input_param, prev_time, audio_time,
|
||||
&new_ts, active_mixes, data);
|
||||
if (!success)
|
||||
return;
|
||||
|
||||
/* clamps audio data to -1.0..1.0 */
|
||||
clamp_audio_output(audio, bytes);
|
||||
|
||||
/* output */
|
||||
for (size_t i = 0; i < MAX_AUDIO_MIXES; i++)
|
||||
do_audio_output(audio, i, new_ts, AUDIO_OUTPUT_FRAMES);
|
||||
}
|
||||
|
||||
static void *audio_thread(void *param)
|
||||
{
|
||||
struct audio_output *audio = param;
|
||||
size_t rate = audio->info.samples_per_sec;
|
||||
uint64_t samples = 0;
|
||||
uint64_t start_time = os_gettime_ns();
|
||||
uint64_t prev_time = start_time;
|
||||
uint64_t audio_time = prev_time;
|
||||
uint32_t audio_wait_time =
|
||||
(uint32_t)(audio_frames_to_ns(rate, AUDIO_OUTPUT_FRAMES) /
|
||||
1000000);
|
||||
|
||||
os_set_thread_name("audio-io: audio thread");
|
||||
|
||||
const char *audio_thread_name =
|
||||
profile_store_name(obs_get_profiler_name_store(),
|
||||
"audio_thread(%s)", audio->info.name);
|
||||
|
||||
while (os_event_try(audio->stop_event) == EAGAIN) {
|
||||
uint64_t cur_time;
|
||||
|
||||
os_sleep_ms(audio_wait_time);
|
||||
|
||||
profile_start(audio_thread_name);
|
||||
|
||||
cur_time = os_gettime_ns();
|
||||
while (audio_time <= cur_time) {
|
||||
samples += AUDIO_OUTPUT_FRAMES;
|
||||
audio_time = start_time +
|
||||
audio_frames_to_ns(rate, samples);
|
||||
|
||||
input_and_output(audio, audio_time, prev_time);
|
||||
prev_time = audio_time;
|
||||
}
|
||||
|
||||
profile_end(audio_thread_name);
|
||||
|
||||
profile_reenable_thread();
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static size_t audio_get_input_idx(const audio_t *audio, size_t mix_idx,
|
||||
audio_output_callback_t callback, void *param)
|
||||
{
|
||||
const struct audio_mix *mix = &audio->mixes[mix_idx];
|
||||
|
||||
for (size_t i = 0; i < mix->inputs.num; i++) {
|
||||
struct audio_input *input = mix->inputs.array+i;
|
||||
|
||||
if (input->callback == callback && input->param == param)
|
||||
return i;
|
||||
}
|
||||
|
||||
return DARRAY_INVALID;
|
||||
}
|
||||
|
||||
static inline bool audio_input_init(struct audio_input *input,
|
||||
struct audio_output *audio)
|
||||
{
|
||||
if (input->conversion.format != audio->info.format ||
|
||||
input->conversion.samples_per_sec != audio->info.samples_per_sec ||
|
||||
input->conversion.speakers != audio->info.speakers) {
|
||||
struct resample_info from = {
|
||||
.format = audio->info.format,
|
||||
.samples_per_sec = audio->info.samples_per_sec,
|
||||
.speakers = audio->info.speakers
|
||||
};
|
||||
|
||||
struct resample_info to = {
|
||||
.format = input->conversion.format,
|
||||
.samples_per_sec = input->conversion.samples_per_sec,
|
||||
.speakers = input->conversion.speakers
|
||||
};
|
||||
|
||||
input->resampler = audio_resampler_create(&to, &from);
|
||||
if (!input->resampler) {
|
||||
blog(LOG_ERROR, "audio_input_init: Failed to "
|
||||
"create resampler");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
input->resampler = NULL;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool audio_output_connect(audio_t *audio, size_t mi,
|
||||
const struct audio_convert_info *conversion,
|
||||
audio_output_callback_t callback, void *param)
|
||||
{
|
||||
bool success = false;
|
||||
|
||||
if (!audio || mi >= MAX_AUDIO_MIXES) return false;
|
||||
|
||||
pthread_mutex_lock(&audio->input_mutex);
|
||||
|
||||
if (audio_get_input_idx(audio, mi, callback, param) == DARRAY_INVALID) {
|
||||
struct audio_mix *mix = &audio->mixes[mi];
|
||||
struct audio_input input;
|
||||
input.callback = callback;
|
||||
input.param = param;
|
||||
|
||||
if (conversion) {
|
||||
input.conversion = *conversion;
|
||||
} else {
|
||||
input.conversion.format = audio->info.format;
|
||||
input.conversion.speakers = audio->info.speakers;
|
||||
input.conversion.samples_per_sec =
|
||||
audio->info.samples_per_sec;
|
||||
}
|
||||
|
||||
if (input.conversion.format == AUDIO_FORMAT_UNKNOWN)
|
||||
input.conversion.format = audio->info.format;
|
||||
if (input.conversion.speakers == SPEAKERS_UNKNOWN)
|
||||
input.conversion.speakers = audio->info.speakers;
|
||||
if (input.conversion.samples_per_sec == 0)
|
||||
input.conversion.samples_per_sec =
|
||||
audio->info.samples_per_sec;
|
||||
|
||||
success = audio_input_init(&input, audio);
|
||||
if (success)
|
||||
da_push_back(mix->inputs, &input);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&audio->input_mutex);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void audio_output_disconnect(audio_t *audio, size_t mix_idx,
|
||||
audio_output_callback_t callback, void *param)
|
||||
{
|
||||
if (!audio || mix_idx >= MAX_AUDIO_MIXES) return;
|
||||
|
||||
pthread_mutex_lock(&audio->input_mutex);
|
||||
|
||||
size_t idx = audio_get_input_idx(audio, mix_idx, callback, param);
|
||||
if (idx != DARRAY_INVALID) {
|
||||
struct audio_mix *mix = &audio->mixes[mix_idx];
|
||||
audio_input_free(mix->inputs.array+idx);
|
||||
da_erase(mix->inputs, idx);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&audio->input_mutex);
|
||||
}
|
||||
|
||||
static inline bool valid_audio_params(const struct audio_output_info *info)
|
||||
{
|
||||
return info->format && info->name && info->samples_per_sec > 0 &&
|
||||
info->speakers > 0;
|
||||
}
|
||||
|
||||
int audio_output_open(audio_t **audio, struct audio_output_info *info)
|
||||
{
|
||||
struct audio_output *out;
|
||||
pthread_mutexattr_t attr;
|
||||
bool planar = is_audio_planar(info->format);
|
||||
|
||||
if (!valid_audio_params(info))
|
||||
return AUDIO_OUTPUT_INVALIDPARAM;
|
||||
|
||||
out = bzalloc(sizeof(struct audio_output));
|
||||
if (!out)
|
||||
goto fail;
|
||||
|
||||
memcpy(&out->info, info, sizeof(struct audio_output_info));
|
||||
out->channels = get_audio_channels(info->speakers);
|
||||
out->planes = planar ? out->channels : 1;
|
||||
out->input_cb = info->input_callback;
|
||||
out->input_param= info->input_param;
|
||||
out->block_size = (planar ? 1 : out->channels) *
|
||||
get_audio_bytes_per_channel(info->format);
|
||||
|
||||
if (pthread_mutexattr_init(&attr) != 0)
|
||||
goto fail;
|
||||
if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0)
|
||||
goto fail;
|
||||
if (pthread_mutex_init(&out->input_mutex, &attr) != 0)
|
||||
goto fail;
|
||||
if (os_event_init(&out->stop_event, OS_EVENT_TYPE_MANUAL) != 0)
|
||||
goto fail;
|
||||
if (pthread_create(&out->thread, NULL, audio_thread, out) != 0)
|
||||
goto fail;
|
||||
|
||||
out->initialized = true;
|
||||
*audio = out;
|
||||
return AUDIO_OUTPUT_SUCCESS;
|
||||
|
||||
fail:
|
||||
audio_output_close(out);
|
||||
return AUDIO_OUTPUT_FAIL;
|
||||
}
|
||||
|
||||
void audio_output_close(audio_t *audio)
|
||||
{
|
||||
void *thread_ret;
|
||||
|
||||
if (!audio)
|
||||
return;
|
||||
|
||||
if (audio->initialized) {
|
||||
os_event_signal(audio->stop_event);
|
||||
pthread_join(audio->thread, &thread_ret);
|
||||
}
|
||||
|
||||
for (size_t mix_idx = 0; mix_idx < MAX_AUDIO_MIXES; mix_idx++) {
|
||||
struct audio_mix *mix = &audio->mixes[mix_idx];
|
||||
|
||||
for (size_t i = 0; i < mix->inputs.num; i++)
|
||||
audio_input_free(mix->inputs.array+i);
|
||||
|
||||
da_free(mix->inputs);
|
||||
}
|
||||
|
||||
os_event_destroy(audio->stop_event);
|
||||
bfree(audio);
|
||||
}
|
||||
|
||||
const struct audio_output_info *audio_output_get_info(const audio_t *audio)
|
||||
{
|
||||
return audio ? &audio->info : NULL;
|
||||
}
|
||||
|
||||
bool audio_output_active(const audio_t *audio)
|
||||
{
|
||||
if (!audio) return false;
|
||||
|
||||
for (size_t mix_idx = 0; mix_idx < MAX_AUDIO_MIXES; mix_idx++) {
|
||||
const struct audio_mix *mix = &audio->mixes[mix_idx];
|
||||
|
||||
if (mix->inputs.num != 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t audio_output_get_block_size(const audio_t *audio)
|
||||
{
|
||||
return audio ? audio->block_size : 0;
|
||||
}
|
||||
|
||||
size_t audio_output_get_planes(const audio_t *audio)
|
||||
{
|
||||
return audio ? audio->planes : 0;
|
||||
}
|
||||
|
||||
size_t audio_output_get_channels(const audio_t *audio)
|
||||
{
|
||||
return audio ? audio->channels : 0;
|
||||
}
|
||||
|
||||
uint32_t audio_output_get_sample_rate(const audio_t *audio)
|
||||
{
|
||||
return audio ? audio->info.samples_per_sec : 0;
|
||||
}
|
||||
226
libobs/media-io/audio-io.h
Normal file
226
libobs/media-io/audio-io.h
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "media-io-defs.h"
|
||||
#include "../util/c99defs.h"
|
||||
#include "../util/util_uint128.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MAX_AUDIO_MIXES 4
|
||||
#define MAX_AUDIO_CHANNELS 2
|
||||
#define AUDIO_OUTPUT_FRAMES 1024
|
||||
|
||||
/*
|
||||
* Base audio output component. Use this to create an audio output track
|
||||
* for the media.
|
||||
*/
|
||||
|
||||
struct audio_output;
|
||||
typedef struct audio_output audio_t;
|
||||
|
||||
enum audio_format {
|
||||
AUDIO_FORMAT_UNKNOWN,
|
||||
|
||||
AUDIO_FORMAT_U8BIT,
|
||||
AUDIO_FORMAT_16BIT,
|
||||
AUDIO_FORMAT_32BIT,
|
||||
AUDIO_FORMAT_FLOAT,
|
||||
|
||||
AUDIO_FORMAT_U8BIT_PLANAR,
|
||||
AUDIO_FORMAT_16BIT_PLANAR,
|
||||
AUDIO_FORMAT_32BIT_PLANAR,
|
||||
AUDIO_FORMAT_FLOAT_PLANAR,
|
||||
};
|
||||
|
||||
enum speaker_layout {
|
||||
SPEAKERS_UNKNOWN,
|
||||
SPEAKERS_MONO,
|
||||
SPEAKERS_STEREO,
|
||||
SPEAKERS_2POINT1,
|
||||
SPEAKERS_QUAD,
|
||||
SPEAKERS_4POINT1,
|
||||
SPEAKERS_5POINT1,
|
||||
SPEAKERS_5POINT1_SURROUND,
|
||||
SPEAKERS_7POINT1,
|
||||
SPEAKERS_7POINT1_SURROUND,
|
||||
SPEAKERS_SURROUND,
|
||||
};
|
||||
|
||||
struct audio_data {
|
||||
uint8_t *data[MAX_AV_PLANES];
|
||||
uint32_t frames;
|
||||
uint64_t timestamp;
|
||||
};
|
||||
|
||||
struct audio_output_data {
|
||||
float *data[MAX_AUDIO_CHANNELS];
|
||||
};
|
||||
|
||||
typedef bool (*audio_input_callback_t)(void *param,
|
||||
uint64_t start_ts, uint64_t end_ts, uint64_t *new_ts,
|
||||
uint32_t active_mixers, struct audio_output_data *mixes);
|
||||
|
||||
struct audio_output_info {
|
||||
const char *name;
|
||||
|
||||
uint32_t samples_per_sec;
|
||||
enum audio_format format;
|
||||
enum speaker_layout speakers;
|
||||
|
||||
audio_input_callback_t input_callback;
|
||||
void *input_param;
|
||||
};
|
||||
|
||||
struct audio_convert_info {
|
||||
uint32_t samples_per_sec;
|
||||
enum audio_format format;
|
||||
enum speaker_layout speakers;
|
||||
};
|
||||
|
||||
static inline uint32_t get_audio_channels(enum speaker_layout speakers)
|
||||
{
|
||||
switch (speakers) {
|
||||
case SPEAKERS_MONO: return 1;
|
||||
case SPEAKERS_STEREO: return 2;
|
||||
case SPEAKERS_2POINT1: return 3;
|
||||
case SPEAKERS_SURROUND:
|
||||
case SPEAKERS_QUAD: return 4;
|
||||
case SPEAKERS_4POINT1: return 5;
|
||||
case SPEAKERS_5POINT1:
|
||||
case SPEAKERS_5POINT1_SURROUND: return 6;
|
||||
case SPEAKERS_7POINT1: return 8;
|
||||
case SPEAKERS_7POINT1_SURROUND: return 8;
|
||||
case SPEAKERS_UNKNOWN: return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline size_t get_audio_bytes_per_channel(enum audio_format format)
|
||||
{
|
||||
switch (format) {
|
||||
case AUDIO_FORMAT_U8BIT:
|
||||
case AUDIO_FORMAT_U8BIT_PLANAR:
|
||||
return 1;
|
||||
|
||||
case AUDIO_FORMAT_16BIT:
|
||||
case AUDIO_FORMAT_16BIT_PLANAR:
|
||||
return 2;
|
||||
|
||||
case AUDIO_FORMAT_FLOAT:
|
||||
case AUDIO_FORMAT_FLOAT_PLANAR:
|
||||
case AUDIO_FORMAT_32BIT:
|
||||
case AUDIO_FORMAT_32BIT_PLANAR:
|
||||
return 4;
|
||||
|
||||
case AUDIO_FORMAT_UNKNOWN:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool is_audio_planar(enum audio_format format)
|
||||
{
|
||||
switch (format) {
|
||||
case AUDIO_FORMAT_U8BIT:
|
||||
case AUDIO_FORMAT_16BIT:
|
||||
case AUDIO_FORMAT_32BIT:
|
||||
case AUDIO_FORMAT_FLOAT:
|
||||
return false;
|
||||
|
||||
case AUDIO_FORMAT_U8BIT_PLANAR:
|
||||
case AUDIO_FORMAT_FLOAT_PLANAR:
|
||||
case AUDIO_FORMAT_16BIT_PLANAR:
|
||||
case AUDIO_FORMAT_32BIT_PLANAR:
|
||||
return true;
|
||||
|
||||
case AUDIO_FORMAT_UNKNOWN:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline size_t get_audio_planes(enum audio_format format,
|
||||
enum speaker_layout speakers)
|
||||
{
|
||||
return (is_audio_planar(format) ? get_audio_channels(speakers) : 1);
|
||||
}
|
||||
|
||||
static inline size_t get_audio_size(enum audio_format format,
|
||||
enum speaker_layout speakers, uint32_t frames)
|
||||
{
|
||||
bool planar = is_audio_planar(format);
|
||||
|
||||
return (planar ? 1 : get_audio_channels(speakers)) *
|
||||
get_audio_bytes_per_channel(format) *
|
||||
frames;
|
||||
}
|
||||
|
||||
static inline uint64_t audio_frames_to_ns(size_t sample_rate,
|
||||
uint64_t frames)
|
||||
{
|
||||
util_uint128_t val;
|
||||
val = util_mul64_64(frames, 1000000000ULL);
|
||||
val = util_div128_32(val, (uint32_t)sample_rate);
|
||||
return val.low;
|
||||
}
|
||||
|
||||
static inline uint64_t ns_to_audio_frames(size_t sample_rate,
|
||||
uint64_t frames)
|
||||
{
|
||||
util_uint128_t val;
|
||||
val = util_mul64_64(frames, sample_rate);
|
||||
val = util_div128_32(val, 1000000000);
|
||||
return val.low;
|
||||
}
|
||||
|
||||
#define AUDIO_OUTPUT_SUCCESS 0
|
||||
#define AUDIO_OUTPUT_INVALIDPARAM -1
|
||||
#define AUDIO_OUTPUT_FAIL -2
|
||||
|
||||
EXPORT int audio_output_open(audio_t **audio, struct audio_output_info *info);
|
||||
EXPORT void audio_output_close(audio_t *audio);
|
||||
|
||||
typedef void (*audio_output_callback_t)(void *param, size_t mix_idx,
|
||||
struct audio_data *data);
|
||||
|
||||
EXPORT bool audio_output_connect(audio_t *video, size_t mix_idx,
|
||||
const struct audio_convert_info *conversion,
|
||||
audio_output_callback_t callback, void *param);
|
||||
EXPORT void audio_output_disconnect(audio_t *video, size_t mix_idx,
|
||||
audio_output_callback_t callback, void *param);
|
||||
|
||||
EXPORT bool audio_output_active(const audio_t *audio);
|
||||
|
||||
EXPORT size_t audio_output_get_block_size(const audio_t *audio);
|
||||
EXPORT size_t audio_output_get_planes(const audio_t *audio);
|
||||
EXPORT size_t audio_output_get_channels(const audio_t *audio);
|
||||
EXPORT uint32_t audio_output_get_sample_rate(const audio_t *audio);
|
||||
EXPORT const struct audio_output_info *audio_output_get_info(
|
||||
const audio_t *audio);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
43
libobs/media-io/audio-math.h
Normal file
43
libobs/media-io/audio-math.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2015 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../util/c99defs.h"
|
||||
#include <math.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <float.h>
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4056)
|
||||
#pragma warning(disable : 4756)
|
||||
#endif
|
||||
|
||||
static inline float mul_to_db(const float mul)
|
||||
{
|
||||
return (mul == 0.0f) ? -INFINITY : (20.0f * log10f(mul));
|
||||
}
|
||||
|
||||
static inline float db_to_mul(const float db)
|
||||
{
|
||||
return isfinite((double)db) ? powf(10.0f, db / 20.0f) : 0.0f;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
173
libobs/media-io/audio-resampler-ffmpeg.c
Normal file
173
libobs/media-io/audio-resampler-ffmpeg.c
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "../util/bmem.h"
|
||||
#include "audio-resampler.h"
|
||||
#include "audio-io.h"
|
||||
#include <libavutil/avutil.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libswresample/swresample.h>
|
||||
|
||||
struct audio_resampler {
|
||||
struct SwrContext *context;
|
||||
bool opened;
|
||||
|
||||
uint32_t input_freq;
|
||||
uint64_t input_layout;
|
||||
enum AVSampleFormat input_format;
|
||||
|
||||
uint8_t *output_buffer[MAX_AV_PLANES];
|
||||
uint64_t output_layout;
|
||||
enum AVSampleFormat output_format;
|
||||
int output_size;
|
||||
uint32_t output_ch;
|
||||
uint32_t output_freq;
|
||||
uint32_t output_planes;
|
||||
};
|
||||
|
||||
static inline enum AVSampleFormat convert_audio_format(enum audio_format format)
|
||||
{
|
||||
switch (format) {
|
||||
case AUDIO_FORMAT_UNKNOWN: return AV_SAMPLE_FMT_S16;
|
||||
case AUDIO_FORMAT_U8BIT: return AV_SAMPLE_FMT_U8;
|
||||
case AUDIO_FORMAT_16BIT: return AV_SAMPLE_FMT_S16;
|
||||
case AUDIO_FORMAT_32BIT: return AV_SAMPLE_FMT_S32;
|
||||
case AUDIO_FORMAT_FLOAT: return AV_SAMPLE_FMT_FLT;
|
||||
case AUDIO_FORMAT_U8BIT_PLANAR: return AV_SAMPLE_FMT_U8P;
|
||||
case AUDIO_FORMAT_16BIT_PLANAR: return AV_SAMPLE_FMT_S16P;
|
||||
case AUDIO_FORMAT_32BIT_PLANAR: return AV_SAMPLE_FMT_S32P;
|
||||
case AUDIO_FORMAT_FLOAT_PLANAR: return AV_SAMPLE_FMT_FLTP;
|
||||
}
|
||||
|
||||
/* shouldn't get here */
|
||||
return AV_SAMPLE_FMT_S16;
|
||||
}
|
||||
|
||||
static inline uint64_t convert_speaker_layout(enum speaker_layout layout)
|
||||
{
|
||||
switch (layout) {
|
||||
case SPEAKERS_UNKNOWN: return 0;
|
||||
case SPEAKERS_MONO: return AV_CH_LAYOUT_MONO;
|
||||
case SPEAKERS_STEREO: return AV_CH_LAYOUT_STEREO;
|
||||
case SPEAKERS_2POINT1: return AV_CH_LAYOUT_2_1;
|
||||
case SPEAKERS_QUAD: return AV_CH_LAYOUT_QUAD;
|
||||
case SPEAKERS_4POINT1: return AV_CH_LAYOUT_4POINT1;
|
||||
case SPEAKERS_5POINT1: return AV_CH_LAYOUT_5POINT1;
|
||||
case SPEAKERS_5POINT1_SURROUND: return AV_CH_LAYOUT_5POINT1_BACK;
|
||||
case SPEAKERS_7POINT1: return AV_CH_LAYOUT_7POINT1;
|
||||
case SPEAKERS_7POINT1_SURROUND: return AV_CH_LAYOUT_7POINT1_WIDE_BACK;
|
||||
case SPEAKERS_SURROUND: return AV_CH_LAYOUT_SURROUND;
|
||||
}
|
||||
|
||||
/* shouldn't get here */
|
||||
return 0;
|
||||
}
|
||||
|
||||
audio_resampler_t *audio_resampler_create(const struct resample_info *dst,
|
||||
const struct resample_info *src)
|
||||
{
|
||||
struct audio_resampler *rs = bzalloc(sizeof(struct audio_resampler));
|
||||
int errcode;
|
||||
|
||||
rs->opened = false;
|
||||
rs->input_freq = src->samples_per_sec;
|
||||
rs->input_layout = convert_speaker_layout(src->speakers);
|
||||
rs->input_format = convert_audio_format(src->format);
|
||||
rs->output_size = 0;
|
||||
rs->output_ch = get_audio_channels(dst->speakers);
|
||||
rs->output_freq = dst->samples_per_sec;
|
||||
rs->output_layout = convert_speaker_layout(dst->speakers);
|
||||
rs->output_format = convert_audio_format(dst->format);
|
||||
rs->output_planes = is_audio_planar(dst->format) ? rs->output_ch : 1;
|
||||
|
||||
rs->context = swr_alloc_set_opts(NULL,
|
||||
rs->output_layout, rs->output_format, dst->samples_per_sec,
|
||||
rs->input_layout, rs->input_format, src->samples_per_sec,
|
||||
0, NULL);
|
||||
|
||||
if (!rs->context) {
|
||||
blog(LOG_ERROR, "swr_alloc_set_opts failed");
|
||||
audio_resampler_destroy(rs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
errcode = swr_init(rs->context);
|
||||
if (errcode != 0) {
|
||||
blog(LOG_ERROR, "avresample_open failed: error code %d",
|
||||
errcode);
|
||||
audio_resampler_destroy(rs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
void audio_resampler_destroy(audio_resampler_t *rs)
|
||||
{
|
||||
if (rs) {
|
||||
if (rs->context)
|
||||
swr_free(&rs->context);
|
||||
if (rs->output_buffer[0])
|
||||
av_freep(&rs->output_buffer[0]);
|
||||
|
||||
bfree(rs);
|
||||
}
|
||||
}
|
||||
|
||||
bool audio_resampler_resample(audio_resampler_t *rs,
|
||||
uint8_t *output[], uint32_t *out_frames, uint64_t *ts_offset,
|
||||
const uint8_t *const input[], uint32_t in_frames)
|
||||
{
|
||||
if (!rs) return false;
|
||||
|
||||
struct SwrContext *context = rs->context;
|
||||
int ret;
|
||||
|
||||
int64_t delay = swr_get_delay(context, rs->input_freq);
|
||||
int estimated = (int)av_rescale_rnd(
|
||||
delay + (int64_t)in_frames,
|
||||
(int64_t)rs->output_freq, (int64_t)rs->input_freq,
|
||||
AV_ROUND_UP);
|
||||
|
||||
*ts_offset = (uint64_t)swr_get_delay(context, 1000000000);
|
||||
|
||||
/* resize the buffer if bigger */
|
||||
if (estimated > rs->output_size) {
|
||||
if (rs->output_buffer[0])
|
||||
av_freep(&rs->output_buffer[0]);
|
||||
|
||||
av_samples_alloc(rs->output_buffer, NULL, rs->output_ch,
|
||||
estimated, rs->output_format, 0);
|
||||
|
||||
rs->output_size = estimated;
|
||||
}
|
||||
|
||||
ret = swr_convert(context,
|
||||
rs->output_buffer, rs->output_size,
|
||||
(const uint8_t**)input, in_frames);
|
||||
|
||||
if (ret < 0) {
|
||||
blog(LOG_ERROR, "swr_convert failed: %d", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < rs->output_planes; i++)
|
||||
output[i] = rs->output_buffer[i];
|
||||
|
||||
*out_frames = (uint32_t)ret;
|
||||
return true;
|
||||
}
|
||||
46
libobs/media-io/audio-resampler.h
Normal file
46
libobs/media-io/audio-resampler.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../util/c99defs.h"
|
||||
#include "audio-io.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct audio_resampler;
|
||||
typedef struct audio_resampler audio_resampler_t;
|
||||
|
||||
struct resample_info {
|
||||
uint32_t samples_per_sec;
|
||||
enum audio_format format;
|
||||
enum speaker_layout speakers;
|
||||
};
|
||||
|
||||
EXPORT audio_resampler_t *audio_resampler_create(const struct resample_info *dst,
|
||||
const struct resample_info *src);
|
||||
EXPORT void audio_resampler_destroy(audio_resampler_t *resampler);
|
||||
|
||||
EXPORT bool audio_resampler_resample(audio_resampler_t *resampler,
|
||||
uint8_t *output[], uint32_t *out_frames, uint64_t *ts_offset,
|
||||
const uint8_t *const input[], uint32_t in_frames);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
324
libobs/media-io/format-conversion.c
Normal file
324
libobs/media-io/format-conversion.c
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "format-conversion.h"
|
||||
#include <xmmintrin.h>
|
||||
#include <emmintrin.h>
|
||||
|
||||
/* ...surprisingly, if I don't use a macro to force inlining, it causes the
|
||||
* CPU usage to boost by a tremendous amount in debug builds. */
|
||||
|
||||
#define get_m128_32_0(val) (*((uint32_t*)&val))
|
||||
#define get_m128_32_1(val) (*(((uint32_t*)&val)+1))
|
||||
|
||||
#define pack_shift(lum_plane, lum_pos0, lum_pos1, line1, line2, mask, sh) \
|
||||
do { \
|
||||
__m128i pack_val = _mm_packs_epi32( \
|
||||
_mm_srli_si128(_mm_and_si128(line1, mask), sh), \
|
||||
_mm_srli_si128(_mm_and_si128(line2, mask), sh)); \
|
||||
pack_val = _mm_packus_epi16(pack_val, pack_val); \
|
||||
\
|
||||
*(uint32_t*)(lum_plane+lum_pos0) = get_m128_32_0(pack_val); \
|
||||
*(uint32_t*)(lum_plane+lum_pos1) = get_m128_32_1(pack_val); \
|
||||
} while (false)
|
||||
|
||||
#define pack_val(lum_plane, lum_pos0, lum_pos1, line1, line2, mask) \
|
||||
do { \
|
||||
__m128i pack_val = _mm_packs_epi32( \
|
||||
_mm_and_si128(line1, mask), \
|
||||
_mm_and_si128(line2, mask)); \
|
||||
pack_val = _mm_packus_epi16(pack_val, pack_val); \
|
||||
\
|
||||
*(uint32_t*)(lum_plane+lum_pos0) = get_m128_32_0(pack_val); \
|
||||
*(uint32_t*)(lum_plane+lum_pos1) = get_m128_32_1(pack_val); \
|
||||
} while (false)
|
||||
|
||||
#define pack_ch_1plane(uv_plane, chroma_pos, line1, line2, uv_mask) \
|
||||
do { \
|
||||
__m128i add_val = _mm_add_epi64( \
|
||||
_mm_and_si128(line1, uv_mask), \
|
||||
_mm_and_si128(line2, uv_mask)); \
|
||||
__m128i avg_val = _mm_add_epi64( \
|
||||
add_val, \
|
||||
_mm_shuffle_epi32(add_val, _MM_SHUFFLE(2, 3, 0, 1))); \
|
||||
avg_val = _mm_srai_epi16(avg_val, 2); \
|
||||
avg_val = _mm_shuffle_epi32(avg_val, _MM_SHUFFLE(3, 1, 2, 0)); \
|
||||
avg_val = _mm_packus_epi16(avg_val, avg_val); \
|
||||
\
|
||||
*(uint32_t*)(uv_plane+chroma_pos) = get_m128_32_0(avg_val); \
|
||||
} while (false)
|
||||
|
||||
#define pack_ch_2plane(u_plane, v_plane, chroma_pos, line1, line2, uv_mask) \
|
||||
do { \
|
||||
uint32_t packed_vals; \
|
||||
\
|
||||
__m128i add_val = _mm_add_epi64( \
|
||||
_mm_and_si128(line1, uv_mask), \
|
||||
_mm_and_si128(line2, uv_mask)); \
|
||||
__m128i avg_val = _mm_add_epi64( \
|
||||
add_val, \
|
||||
_mm_shuffle_epi32(add_val, _MM_SHUFFLE(2, 3, 0, 1))); \
|
||||
avg_val = _mm_srai_epi16(avg_val, 2); \
|
||||
avg_val = _mm_shuffle_epi32(avg_val, _MM_SHUFFLE(3, 1, 2, 0)); \
|
||||
avg_val = _mm_shufflelo_epi16(avg_val, _MM_SHUFFLE(3, 1, 2, 0)); \
|
||||
avg_val = _mm_packus_epi16(avg_val, avg_val); \
|
||||
\
|
||||
packed_vals = get_m128_32_0(avg_val); \
|
||||
\
|
||||
*(uint16_t*)(u_plane+chroma_pos) = (uint16_t)(packed_vals); \
|
||||
*(uint16_t*)(v_plane+chroma_pos) = (uint16_t)(packed_vals>>16); \
|
||||
} while (false)
|
||||
|
||||
|
||||
static FORCE_INLINE uint32_t min_uint32(uint32_t a, uint32_t b)
|
||||
{
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
void compress_uyvx_to_i420(
|
||||
const uint8_t *input, uint32_t in_linesize,
|
||||
uint32_t start_y, uint32_t end_y,
|
||||
uint8_t *output[], const uint32_t out_linesize[])
|
||||
{
|
||||
uint8_t *lum_plane = output[0];
|
||||
uint8_t *u_plane = output[1];
|
||||
uint8_t *v_plane = output[2];
|
||||
uint32_t width = min_uint32(in_linesize, out_linesize[0]);
|
||||
uint32_t y;
|
||||
|
||||
__m128i lum_mask = _mm_set1_epi32(0x0000FF00);
|
||||
__m128i uv_mask = _mm_set1_epi16(0x00FF);
|
||||
|
||||
for (y = start_y; y < end_y; y += 2) {
|
||||
uint32_t y_pos = y * in_linesize;
|
||||
uint32_t chroma_y_pos = (y>>1) * out_linesize[1];
|
||||
uint32_t lum_y_pos = y * out_linesize[0];
|
||||
uint32_t x;
|
||||
|
||||
for (x = 0; x < width; x += 4) {
|
||||
const uint8_t *img = input + y_pos + x*4;
|
||||
uint32_t lum_pos0 = lum_y_pos + x;
|
||||
uint32_t lum_pos1 = lum_pos0 + out_linesize[0];
|
||||
|
||||
__m128i line1 = _mm_load_si128((const __m128i*)img);
|
||||
__m128i line2 = _mm_load_si128(
|
||||
(const __m128i*)(img + in_linesize));
|
||||
|
||||
pack_shift(lum_plane, lum_pos0, lum_pos1,
|
||||
line1, line2, lum_mask, 1);
|
||||
pack_ch_2plane(u_plane, v_plane,
|
||||
chroma_y_pos + (x>>1),
|
||||
line1, line2, uv_mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void compress_uyvx_to_nv12(
|
||||
const uint8_t *input, uint32_t in_linesize,
|
||||
uint32_t start_y, uint32_t end_y,
|
||||
uint8_t *output[], const uint32_t out_linesize[])
|
||||
{
|
||||
uint8_t *lum_plane = output[0];
|
||||
uint8_t *chroma_plane = output[1];
|
||||
uint32_t width = min_uint32(in_linesize, out_linesize[0]);
|
||||
uint32_t y;
|
||||
|
||||
__m128i lum_mask = _mm_set1_epi32(0x0000FF00);
|
||||
__m128i uv_mask = _mm_set1_epi16(0x00FF);
|
||||
|
||||
for (y = start_y; y < end_y; y += 2) {
|
||||
uint32_t y_pos = y * in_linesize;
|
||||
uint32_t chroma_y_pos = (y>>1) * out_linesize[1];
|
||||
uint32_t lum_y_pos = y * out_linesize[0];
|
||||
uint32_t x;
|
||||
|
||||
for (x = 0; x < width; x += 4) {
|
||||
const uint8_t *img = input + y_pos + x*4;
|
||||
uint32_t lum_pos0 = lum_y_pos + x;
|
||||
uint32_t lum_pos1 = lum_pos0 + out_linesize[0];
|
||||
|
||||
__m128i line1 = _mm_load_si128((const __m128i*)img);
|
||||
__m128i line2 = _mm_load_si128(
|
||||
(const __m128i*)(img + in_linesize));
|
||||
|
||||
pack_shift(lum_plane, lum_pos0, lum_pos1,
|
||||
line1, line2, lum_mask, 1);
|
||||
pack_ch_1plane(chroma_plane, chroma_y_pos + x,
|
||||
line1, line2, uv_mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void convert_uyvx_to_i444(
|
||||
const uint8_t *input, uint32_t in_linesize,
|
||||
uint32_t start_y, uint32_t end_y,
|
||||
uint8_t *output[], const uint32_t out_linesize[])
|
||||
{
|
||||
uint8_t *lum_plane = output[0];
|
||||
uint8_t *u_plane = output[1];
|
||||
uint8_t *v_plane = output[2];
|
||||
uint32_t width = min_uint32(in_linesize, out_linesize[0]);
|
||||
uint32_t y;
|
||||
|
||||
__m128i lum_mask = _mm_set1_epi32(0x0000FF00);
|
||||
__m128i u_mask = _mm_set1_epi32(0x000000FF);
|
||||
__m128i v_mask = _mm_set1_epi32(0x00FF0000);
|
||||
|
||||
for (y = start_y; y < end_y; y += 2) {
|
||||
uint32_t y_pos = y * in_linesize;
|
||||
uint32_t lum_y_pos = y * out_linesize[0];
|
||||
uint32_t x;
|
||||
|
||||
for (x = 0; x < width; x += 4) {
|
||||
const uint8_t *img = input + y_pos + x*4;
|
||||
uint32_t lum_pos0 = lum_y_pos + x;
|
||||
uint32_t lum_pos1 = lum_pos0 + out_linesize[0];
|
||||
|
||||
__m128i line1 = _mm_load_si128((const __m128i*)img);
|
||||
__m128i line2 = _mm_load_si128(
|
||||
(const __m128i*)(img + in_linesize));
|
||||
|
||||
pack_shift(lum_plane, lum_pos0, lum_pos1,
|
||||
line1, line2, lum_mask, 1);
|
||||
pack_val(u_plane, lum_pos0, lum_pos1,
|
||||
line1, line2, u_mask);
|
||||
pack_shift(v_plane, lum_pos0, lum_pos1,
|
||||
line1, line2, v_mask, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void decompress_420(
|
||||
const uint8_t *const input[], const uint32_t in_linesize[],
|
||||
uint32_t start_y, uint32_t end_y,
|
||||
uint8_t *output, uint32_t out_linesize)
|
||||
{
|
||||
uint32_t start_y_d2 = start_y/2;
|
||||
uint32_t width_d2 = min_uint32(in_linesize[0], out_linesize)/2;
|
||||
uint32_t height_d2 = end_y/2;
|
||||
uint32_t y;
|
||||
|
||||
for (y = start_y_d2; y < height_d2; y++) {
|
||||
const uint8_t *chroma0 = input[1] + y * in_linesize[1];
|
||||
const uint8_t *chroma1 = input[2] + y * in_linesize[2];
|
||||
register const uint8_t *lum0, *lum1;
|
||||
register uint32_t *output0, *output1;
|
||||
uint32_t x;
|
||||
|
||||
lum0 = input[0] + y * 2 * in_linesize[0];
|
||||
lum1 = lum0 + in_linesize[0];
|
||||
output0 = (uint32_t*)(output + y * 2 * in_linesize[0]);
|
||||
output1 = (uint32_t*)((uint8_t*)output0 + in_linesize[0]);
|
||||
|
||||
for (x = 0; x < width_d2; x++) {
|
||||
uint32_t out;
|
||||
out = (*(chroma0++) << 8) | (*(chroma1++) << 16);
|
||||
|
||||
*(output0++) = *(lum0++) | out;
|
||||
*(output0++) = *(lum0++) | out;
|
||||
|
||||
*(output1++) = *(lum1++) | out;
|
||||
*(output1++) = *(lum1++) | out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void decompress_nv12(
|
||||
const uint8_t *const input[], const uint32_t in_linesize[],
|
||||
uint32_t start_y, uint32_t end_y,
|
||||
uint8_t *output, uint32_t out_linesize)
|
||||
{
|
||||
uint32_t start_y_d2 = start_y/2;
|
||||
uint32_t width_d2 = min_uint32(in_linesize[0], out_linesize)/2;
|
||||
uint32_t height_d2 = end_y/2;
|
||||
uint32_t y;
|
||||
|
||||
for (y = start_y_d2; y < height_d2; y++) {
|
||||
const uint16_t *chroma;
|
||||
register const uint8_t *lum0, *lum1;
|
||||
register uint32_t *output0, *output1;
|
||||
uint32_t x;
|
||||
|
||||
chroma = (const uint16_t*)(input[1] + y * in_linesize[1]);
|
||||
lum0 = input[0] + y * 2 * in_linesize[0];
|
||||
lum1 = lum0 + in_linesize[0];
|
||||
output0 = (uint32_t*)(output + y * 2 * out_linesize);
|
||||
output1 = (uint32_t*)((uint8_t*)output0 + out_linesize);
|
||||
|
||||
for (x = 0; x < width_d2; x++) {
|
||||
uint32_t out = *(chroma++) << 8;
|
||||
|
||||
*(output0++) = *(lum0++) | out;
|
||||
*(output0++) = *(lum0++) | out;
|
||||
|
||||
*(output1++) = *(lum1++) | out;
|
||||
*(output1++) = *(lum1++) | out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void decompress_422(
|
||||
const uint8_t *input, uint32_t in_linesize,
|
||||
uint32_t start_y, uint32_t end_y,
|
||||
uint8_t *output, uint32_t out_linesize,
|
||||
bool leading_lum)
|
||||
{
|
||||
uint32_t width_d2 = min_uint32(in_linesize, out_linesize)/2;
|
||||
uint32_t y;
|
||||
|
||||
register const uint32_t *input32;
|
||||
register const uint32_t *input32_end;
|
||||
register uint32_t *output32;
|
||||
|
||||
if (leading_lum) {
|
||||
for (y = start_y; y < end_y; y++) {
|
||||
input32 = (const uint32_t*)(input + y*in_linesize);
|
||||
input32_end = input32 + width_d2;
|
||||
output32 = (uint32_t*)(output + y*out_linesize);
|
||||
|
||||
while(input32 < input32_end) {
|
||||
register uint32_t dw = *input32;
|
||||
|
||||
output32[0] = dw;
|
||||
dw &= 0xFFFFFF00;
|
||||
dw |= (uint8_t)(dw>>16);
|
||||
output32[1] = dw;
|
||||
|
||||
output32 += 2;
|
||||
input32++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (y = start_y; y < end_y; y++) {
|
||||
input32 = (const uint32_t*)(input + y*in_linesize);
|
||||
input32_end = input32 + width_d2;
|
||||
output32 = (uint32_t*)(output + y*out_linesize);
|
||||
|
||||
while (input32 < input32_end) {
|
||||
register uint32_t dw = *input32;
|
||||
|
||||
output32[0] = dw;
|
||||
dw &= 0xFFFF00FF;
|
||||
dw |= (dw>>16) & 0xFF00;
|
||||
output32[1] = dw;
|
||||
|
||||
output32 += 2;
|
||||
input32++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
63
libobs/media-io/format-conversion.h
Normal file
63
libobs/media-io/format-conversion.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../util/c99defs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Functions for converting to and from packed 444 YUV
|
||||
*/
|
||||
|
||||
EXPORT void compress_uyvx_to_i420(
|
||||
const uint8_t *input, uint32_t in_linesize,
|
||||
uint32_t start_y, uint32_t end_y,
|
||||
uint8_t *output[], const uint32_t out_linesize[]);
|
||||
|
||||
EXPORT void compress_uyvx_to_nv12(
|
||||
const uint8_t *input, uint32_t in_linesize,
|
||||
uint32_t start_y, uint32_t end_y,
|
||||
uint8_t *output[], const uint32_t out_linesize[]);
|
||||
|
||||
EXPORT void convert_uyvx_to_i444(
|
||||
const uint8_t *input, uint32_t in_linesize,
|
||||
uint32_t start_y, uint32_t end_y,
|
||||
uint8_t *output[], const uint32_t out_linesize[]);
|
||||
|
||||
EXPORT void decompress_nv12(
|
||||
const uint8_t *const input[], const uint32_t in_linesize[],
|
||||
uint32_t start_y, uint32_t end_y,
|
||||
uint8_t *output, uint32_t out_linesize);
|
||||
|
||||
EXPORT void decompress_420(
|
||||
const uint8_t *const input[], const uint32_t in_linesize[],
|
||||
uint32_t start_y, uint32_t end_y,
|
||||
uint8_t *output, uint32_t out_linesize);
|
||||
|
||||
EXPORT void decompress_422(
|
||||
const uint8_t *input, uint32_t in_linesize,
|
||||
uint32_t start_y, uint32_t end_y,
|
||||
uint8_t *output, uint32_t out_linesize,
|
||||
bool leading_lum);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
32
libobs/media-io/frame-rate.h
Normal file
32
libobs/media-io/frame-rate.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct media_frames_per_second {
|
||||
uint32_t numerator;
|
||||
uint32_t denominator;
|
||||
};
|
||||
|
||||
static inline double media_frames_per_second_to_frame_interval(
|
||||
struct media_frames_per_second fps)
|
||||
{
|
||||
return (double)fps.denominator / fps.numerator;
|
||||
}
|
||||
|
||||
static inline double media_frames_per_second_to_fps(
|
||||
struct media_frames_per_second fps)
|
||||
{
|
||||
return (double)fps.numerator / fps.denominator;
|
||||
}
|
||||
|
||||
static inline bool media_frames_per_second_is_valid(
|
||||
struct media_frames_per_second fps)
|
||||
{
|
||||
return fps.numerator && fps.denominator;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
24
libobs/media-io/media-io-defs.h
Normal file
24
libobs/media-io/media-io-defs.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2014 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define MAX_AV_PLANES 8
|
||||
|
||||
/* time threshold in nanoseconds to ensure audio timing is as seamless as
|
||||
* possible */
|
||||
#define TS_SMOOTHING_THRESHOLD 70000000ULL
|
||||
251
libobs/media-io/media-remux.c
Normal file
251
libobs/media-io/media-remux.c
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2014 by Ruwen Hahn <palana@stunned.de>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "media-remux.h"
|
||||
|
||||
#include "../util/base.h"
|
||||
#include "../util/bmem.h"
|
||||
#include "../util/platform.h"
|
||||
|
||||
#include <libavformat/avformat.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
struct media_remux_job {
|
||||
int64_t in_size;
|
||||
AVFormatContext *ifmt_ctx, *ofmt_ctx;
|
||||
};
|
||||
|
||||
static inline void init_size(media_remux_job_t job, const char *in_filename)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
struct _stat64 st = {0};
|
||||
_stat64(in_filename, &st);
|
||||
#else
|
||||
struct stat st = {0};
|
||||
stat(in_filename, &st);
|
||||
#endif
|
||||
job->in_size = st.st_size;
|
||||
}
|
||||
|
||||
static inline bool init_input(media_remux_job_t job, const char *in_filename)
|
||||
{
|
||||
int ret = avformat_open_input(&job->ifmt_ctx, in_filename, NULL, NULL);
|
||||
if (ret < 0) {
|
||||
blog(LOG_ERROR, "media_remux: Could not open input file '%s'",
|
||||
in_filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = avformat_find_stream_info(job->ifmt_ctx, NULL);
|
||||
if (ret < 0) {
|
||||
blog(LOG_ERROR, "media_remux: Failed to retrieve input stream"
|
||||
" information");
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef _NDEBUG
|
||||
av_dump_format(job->ifmt_ctx, 0, in_filename, false);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool init_output(media_remux_job_t job, const char *out_filename)
|
||||
{
|
||||
int ret;
|
||||
|
||||
avformat_alloc_output_context2(&job->ofmt_ctx, NULL, NULL,
|
||||
out_filename);
|
||||
if (!job->ofmt_ctx) {
|
||||
blog(LOG_ERROR, "media_remux: Could not create output context");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < job->ifmt_ctx->nb_streams; i++) {
|
||||
AVStream *in_stream = job->ifmt_ctx->streams[i];
|
||||
AVStream *out_stream = avformat_new_stream(job->ofmt_ctx,
|
||||
in_stream->codec->codec);
|
||||
if (!out_stream) {
|
||||
blog(LOG_ERROR, "media_remux: Failed to allocate output"
|
||||
" stream");
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = avcodec_copy_context(out_stream->codec, in_stream->codec);
|
||||
if (ret < 0) {
|
||||
blog(LOG_ERROR, "media_remux: Failed to copy context");
|
||||
return false;
|
||||
}
|
||||
out_stream->time_base = out_stream->codec->time_base;
|
||||
|
||||
out_stream->codec->codec_tag = 0;
|
||||
if (job->ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
|
||||
out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
|
||||
}
|
||||
|
||||
#ifndef _NDEBUG
|
||||
av_dump_format(job->ofmt_ctx, 0, out_filename, true);
|
||||
#endif
|
||||
|
||||
if (!(job->ofmt_ctx->oformat->flags & AVFMT_NOFILE)) {
|
||||
ret = avio_open(&job->ofmt_ctx->pb, out_filename,
|
||||
AVIO_FLAG_WRITE);
|
||||
if (ret < 0) {
|
||||
blog(LOG_ERROR, "media_remux: Failed to open output"
|
||||
" file '%s'", out_filename);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool media_remux_job_create(media_remux_job_t *job, const char *in_filename,
|
||||
const char *out_filename)
|
||||
{
|
||||
if (!job)
|
||||
return false;
|
||||
|
||||
*job = NULL;
|
||||
if (!os_file_exists(in_filename))
|
||||
return false;
|
||||
|
||||
*job = (media_remux_job_t)bzalloc(sizeof(struct media_remux_job));
|
||||
if (!*job)
|
||||
return false;
|
||||
|
||||
init_size(*job, in_filename);
|
||||
|
||||
av_register_all();
|
||||
|
||||
if (!init_input(*job, in_filename))
|
||||
goto fail;
|
||||
|
||||
if (!init_output(*job, out_filename))
|
||||
goto fail;
|
||||
|
||||
return true;
|
||||
|
||||
fail:
|
||||
media_remux_job_destroy(*job);
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void process_packet(AVPacket *pkt,
|
||||
AVStream *in_stream, AVStream *out_stream)
|
||||
{
|
||||
pkt->pts = av_rescale_q_rnd(pkt->pts, in_stream->time_base,
|
||||
out_stream->time_base,
|
||||
AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
|
||||
pkt->dts = av_rescale_q_rnd(pkt->dts, in_stream->time_base,
|
||||
out_stream->time_base,
|
||||
AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
|
||||
pkt->duration = (int)av_rescale_q(pkt->duration,
|
||||
in_stream->time_base, out_stream->time_base);
|
||||
pkt->pos = -1;
|
||||
|
||||
}
|
||||
|
||||
static inline int process_packets(media_remux_job_t job,
|
||||
media_remux_progress_callback callback, void *data)
|
||||
{
|
||||
AVPacket pkt;
|
||||
|
||||
int ret, throttle = 0;
|
||||
for (;;) {
|
||||
ret = av_read_frame(job->ifmt_ctx, &pkt);
|
||||
if (ret < 0) {
|
||||
if (ret != AVERROR_EOF)
|
||||
blog(LOG_ERROR, "media_remux: Error reading"
|
||||
" packet: %s",
|
||||
av_err2str(ret));
|
||||
break;
|
||||
}
|
||||
|
||||
if (callback != NULL && throttle++ > 10) {
|
||||
float progress = pkt.pos / (float)job->in_size * 100.f;
|
||||
if (!callback(data, progress))
|
||||
break;
|
||||
throttle = 0;
|
||||
}
|
||||
|
||||
process_packet(&pkt, job->ifmt_ctx->streams[pkt.stream_index],
|
||||
job->ofmt_ctx->streams[pkt.stream_index]);
|
||||
|
||||
ret = av_interleaved_write_frame(job->ofmt_ctx, &pkt);
|
||||
av_free_packet(&pkt);
|
||||
|
||||
if (ret < 0) {
|
||||
blog(LOG_ERROR, "media_remux: Error muxing packet: %s",
|
||||
av_err2str(ret));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool media_remux_job_process(media_remux_job_t job,
|
||||
media_remux_progress_callback callback, void *data)
|
||||
{
|
||||
int ret;
|
||||
bool success = false;
|
||||
|
||||
if (!job)
|
||||
return success;
|
||||
|
||||
ret = avformat_write_header(job->ofmt_ctx, NULL);
|
||||
if (ret < 0) {
|
||||
blog(LOG_ERROR, "media_remux: Error opening output file: %s",
|
||||
av_err2str(ret));
|
||||
return success;
|
||||
}
|
||||
|
||||
if (callback != NULL)
|
||||
callback(data, 0.f);
|
||||
|
||||
ret = process_packets(job, callback, data);
|
||||
success = ret >= 0 || ret == AVERROR_EOF;
|
||||
|
||||
ret = av_write_trailer(job->ofmt_ctx);
|
||||
if (ret < 0) {
|
||||
blog(LOG_ERROR, "media_remux: av_write_trailer: %s",
|
||||
av_err2str(ret));
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (callback != NULL)
|
||||
callback(data, 100.f);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void media_remux_job_destroy(media_remux_job_t job)
|
||||
{
|
||||
if (!job)
|
||||
return;
|
||||
|
||||
avformat_close_input(&job->ifmt_ctx);
|
||||
|
||||
if (job->ofmt_ctx && !(job->ofmt_ctx->oformat->flags & AVFMT_NOFILE))
|
||||
avio_close(job->ofmt_ctx->pb);
|
||||
|
||||
avformat_free_context(job->ofmt_ctx);
|
||||
|
||||
bfree(job);
|
||||
}
|
||||
39
libobs/media-io/media-remux.h
Normal file
39
libobs/media-io/media-remux.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2014 by Ruwen Hahn <palana@stunned.de>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "../util/c99defs.h"
|
||||
|
||||
#pragma once
|
||||
|
||||
struct media_remux_job;
|
||||
typedef struct media_remux_job *media_remux_job_t;
|
||||
|
||||
typedef bool (media_remux_progress_callback)(void *data, float percent);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
EXPORT bool media_remux_job_create(media_remux_job_t *job,
|
||||
const char *in_filename, const char *out_filename);
|
||||
EXPORT bool media_remux_job_process(media_remux_job_t job,
|
||||
media_remux_progress_callback callback, void *data);
|
||||
EXPORT void media_remux_job_destroy(media_remux_job_t job);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
51
libobs/media-io/video-fourcc.c
Normal file
51
libobs/media-io/video-fourcc.c
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2014 by Ruwen Hahn <palana@stunned.de>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "../util/c99defs.h"
|
||||
#include "video-io.h"
|
||||
|
||||
#define MAKE_FOURCC(a, b, c, d) \
|
||||
((uint32_t)(((d) << 24) | ((c) << 16) | ((b) << 8) | (a)))
|
||||
|
||||
enum video_format video_format_from_fourcc(uint32_t fourcc)
|
||||
{
|
||||
switch (fourcc) {
|
||||
case MAKE_FOURCC('U','Y','V','Y'):
|
||||
case MAKE_FOURCC('H','D','Y','C'):
|
||||
case MAKE_FOURCC('U','Y','N','V'):
|
||||
case MAKE_FOURCC('U','Y','N','Y'):
|
||||
case MAKE_FOURCC('u','y','v','1'):
|
||||
case MAKE_FOURCC('2','v','u','y'):
|
||||
case MAKE_FOURCC('2','V','u','y'):
|
||||
return VIDEO_FORMAT_UYVY;
|
||||
|
||||
case MAKE_FOURCC('Y','U','Y','2'):
|
||||
case MAKE_FOURCC('Y','4','2','2'):
|
||||
case MAKE_FOURCC('V','4','2','2'):
|
||||
case MAKE_FOURCC('V','Y','U','Y'):
|
||||
case MAKE_FOURCC('Y','U','N','V'):
|
||||
case MAKE_FOURCC('y','u','v','2'):
|
||||
case MAKE_FOURCC('y','u','v','s'):
|
||||
return VIDEO_FORMAT_YUY2;
|
||||
|
||||
case MAKE_FOURCC('Y','V','Y','U'):
|
||||
return VIDEO_FORMAT_YVYU;
|
||||
|
||||
}
|
||||
return VIDEO_FORMAT_NONE;
|
||||
}
|
||||
|
||||
133
libobs/media-io/video-frame.c
Normal file
133
libobs/media-io/video-frame.c
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "video-frame.h"
|
||||
|
||||
#define ALIGN_SIZE(size, align) \
|
||||
size = (((size)+(align-1)) & (~(align-1)))
|
||||
|
||||
/* messy code alarm */
|
||||
void video_frame_init(struct video_frame *frame, enum video_format format,
|
||||
uint32_t width, uint32_t height)
|
||||
{
|
||||
size_t size;
|
||||
size_t offsets[MAX_AV_PLANES];
|
||||
int alignment = base_get_alignment();
|
||||
|
||||
if (!frame) return;
|
||||
|
||||
memset(frame, 0, sizeof(struct video_frame));
|
||||
memset(offsets, 0, sizeof(offsets));
|
||||
|
||||
switch (format) {
|
||||
case VIDEO_FORMAT_NONE:
|
||||
return;
|
||||
|
||||
case VIDEO_FORMAT_I420:
|
||||
size = width * height;
|
||||
ALIGN_SIZE(size, alignment);
|
||||
offsets[0] = size;
|
||||
size += (width/2) * (height/2);
|
||||
ALIGN_SIZE(size, alignment);
|
||||
offsets[1] = size;
|
||||
size += (width/2) * (height/2);
|
||||
ALIGN_SIZE(size, alignment);
|
||||
frame->data[0] = bmalloc(size);
|
||||
frame->data[1] = (uint8_t*)frame->data[0] + offsets[0];
|
||||
frame->data[2] = (uint8_t*)frame->data[0] + offsets[1];
|
||||
frame->linesize[0] = width;
|
||||
frame->linesize[1] = width/2;
|
||||
frame->linesize[2] = width/2;
|
||||
break;
|
||||
|
||||
case VIDEO_FORMAT_NV12:
|
||||
size = width * height;
|
||||
ALIGN_SIZE(size, alignment);
|
||||
offsets[0] = size;
|
||||
size += (width/2) * (height/2) * 2;
|
||||
ALIGN_SIZE(size, alignment);
|
||||
frame->data[0] = bmalloc(size);
|
||||
frame->data[1] = (uint8_t*)frame->data[0] + offsets[0];
|
||||
frame->linesize[0] = width;
|
||||
frame->linesize[1] = width;
|
||||
break;
|
||||
|
||||
case VIDEO_FORMAT_YVYU:
|
||||
case VIDEO_FORMAT_YUY2:
|
||||
case VIDEO_FORMAT_UYVY:
|
||||
size = width * height * 2;
|
||||
ALIGN_SIZE(size, alignment);
|
||||
frame->data[0] = bmalloc(size);
|
||||
frame->linesize[0] = width*2;
|
||||
break;
|
||||
|
||||
case VIDEO_FORMAT_RGBA:
|
||||
case VIDEO_FORMAT_BGRA:
|
||||
case VIDEO_FORMAT_BGRX:
|
||||
size = width * height * 4;
|
||||
ALIGN_SIZE(size, alignment);
|
||||
frame->data[0] = bmalloc(size);
|
||||
frame->linesize[0] = width*4;
|
||||
break;
|
||||
|
||||
case VIDEO_FORMAT_I444:
|
||||
size = width * height;
|
||||
ALIGN_SIZE(size, alignment);
|
||||
frame->data[0] = bmalloc(size * 3);
|
||||
frame->data[1] = (uint8_t*)frame->data[0] + size;
|
||||
frame->data[2] = (uint8_t*)frame->data[1] + size;
|
||||
frame->linesize[0] = width;
|
||||
frame->linesize[1] = width;
|
||||
frame->linesize[2] = width;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void video_frame_copy(struct video_frame *dst, const struct video_frame *src,
|
||||
enum video_format format, uint32_t cy)
|
||||
{
|
||||
switch (format) {
|
||||
case VIDEO_FORMAT_NONE:
|
||||
return;
|
||||
|
||||
case VIDEO_FORMAT_I420:
|
||||
memcpy(dst->data[0], src->data[0], src->linesize[0] * cy);
|
||||
memcpy(dst->data[1], src->data[1], src->linesize[1] * cy / 2);
|
||||
memcpy(dst->data[2], src->data[2], src->linesize[2] * cy / 2);
|
||||
break;
|
||||
|
||||
case VIDEO_FORMAT_NV12:
|
||||
memcpy(dst->data[0], src->data[0], src->linesize[0] * cy);
|
||||
memcpy(dst->data[1], src->data[1], src->linesize[1] * cy / 2);
|
||||
break;
|
||||
|
||||
case VIDEO_FORMAT_YVYU:
|
||||
case VIDEO_FORMAT_YUY2:
|
||||
case VIDEO_FORMAT_UYVY:
|
||||
case VIDEO_FORMAT_RGBA:
|
||||
case VIDEO_FORMAT_BGRA:
|
||||
case VIDEO_FORMAT_BGRX:
|
||||
memcpy(dst->data[0], src->data[0], src->linesize[0] * cy);
|
||||
break;
|
||||
|
||||
case VIDEO_FORMAT_I444:
|
||||
memcpy(dst->data[0], src->data[0], src->linesize[0] * cy);
|
||||
memcpy(dst->data[1], src->data[1], src->linesize[1] * cy);
|
||||
memcpy(dst->data[2], src->data[2], src->linesize[2] * cy);
|
||||
break;
|
||||
}
|
||||
}
|
||||
59
libobs/media-io/video-frame.h
Normal file
59
libobs/media-io/video-frame.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../util/bmem.h"
|
||||
#include "video-io.h"
|
||||
|
||||
struct video_frame {
|
||||
uint8_t *data[MAX_AV_PLANES];
|
||||
uint32_t linesize[MAX_AV_PLANES];
|
||||
};
|
||||
|
||||
EXPORT void video_frame_init(struct video_frame *frame,
|
||||
enum video_format format, uint32_t width, uint32_t height);
|
||||
|
||||
static inline void video_frame_free(struct video_frame *frame)
|
||||
{
|
||||
if (frame) {
|
||||
bfree(frame->data[0]);
|
||||
memset(frame, 0, sizeof(struct video_frame));
|
||||
}
|
||||
}
|
||||
|
||||
static inline struct video_frame *video_frame_create(
|
||||
enum video_format format, uint32_t width, uint32_t height)
|
||||
{
|
||||
struct video_frame *frame;
|
||||
|
||||
frame = (struct video_frame*)bzalloc(sizeof(struct video_frame));
|
||||
video_frame_init(frame, format, width, height);
|
||||
return frame;
|
||||
}
|
||||
|
||||
static inline void video_frame_destroy(struct video_frame *frame)
|
||||
{
|
||||
if (frame) {
|
||||
bfree(frame->data[0]);
|
||||
bfree(frame);
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT void video_frame_copy(struct video_frame *dst,
|
||||
const struct video_frame *src, enum video_format format,
|
||||
uint32_t height);
|
||||
501
libobs/media-io/video-io.c
Normal file
501
libobs/media-io/video-io.c
Normal file
|
|
@ -0,0 +1,501 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include <assert.h>
|
||||
#include "../util/bmem.h"
|
||||
#include "../util/platform.h"
|
||||
#include "../util/profiler.h"
|
||||
#include "../util/threading.h"
|
||||
#include "../util/darray.h"
|
||||
|
||||
#include "format-conversion.h"
|
||||
#include "video-io.h"
|
||||
#include "video-frame.h"
|
||||
#include "video-scaler.h"
|
||||
|
||||
extern profiler_name_store_t *obs_get_profiler_name_store(void);
|
||||
|
||||
#define MAX_CONVERT_BUFFERS 3
|
||||
#define MAX_CACHE_SIZE 16
|
||||
|
||||
struct cached_frame_info {
|
||||
struct video_data frame;
|
||||
int count;
|
||||
};
|
||||
|
||||
struct video_input {
|
||||
struct video_scale_info conversion;
|
||||
video_scaler_t *scaler;
|
||||
struct video_frame frame[MAX_CONVERT_BUFFERS];
|
||||
int cur_frame;
|
||||
|
||||
void (*callback)(void *param, struct video_data *frame);
|
||||
void *param;
|
||||
};
|
||||
|
||||
static inline void video_input_free(struct video_input *input)
|
||||
{
|
||||
for (size_t i = 0; i < MAX_CONVERT_BUFFERS; i++)
|
||||
video_frame_free(&input->frame[i]);
|
||||
video_scaler_destroy(input->scaler);
|
||||
}
|
||||
|
||||
struct video_output {
|
||||
struct video_output_info info;
|
||||
|
||||
pthread_t thread;
|
||||
pthread_mutex_t data_mutex;
|
||||
bool stop;
|
||||
|
||||
os_sem_t *update_semaphore;
|
||||
uint64_t frame_time;
|
||||
uint32_t skipped_frames;
|
||||
uint32_t total_frames;
|
||||
|
||||
bool initialized;
|
||||
|
||||
pthread_mutex_t input_mutex;
|
||||
DARRAY(struct video_input) inputs;
|
||||
|
||||
size_t available_frames;
|
||||
size_t first_added;
|
||||
size_t last_added;
|
||||
struct cached_frame_info cache[MAX_CACHE_SIZE];
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static inline bool scale_video_output(struct video_input *input,
|
||||
struct video_data *data)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
if (input->scaler) {
|
||||
struct video_frame *frame;
|
||||
|
||||
if (++input->cur_frame == MAX_CONVERT_BUFFERS)
|
||||
input->cur_frame = 0;
|
||||
|
||||
frame = &input->frame[input->cur_frame];
|
||||
|
||||
success = video_scaler_scale(input->scaler,
|
||||
frame->data, frame->linesize,
|
||||
(const uint8_t * const*)data->data,
|
||||
data->linesize);
|
||||
|
||||
if (success) {
|
||||
for (size_t i = 0; i < MAX_AV_PLANES; i++) {
|
||||
data->data[i] = frame->data[i];
|
||||
data->linesize[i] = frame->linesize[i];
|
||||
}
|
||||
} else {
|
||||
blog(LOG_WARNING, "video-io: Could not scale frame!");
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static inline bool video_output_cur_frame(struct video_output *video)
|
||||
{
|
||||
struct cached_frame_info *frame_info;
|
||||
bool complete;
|
||||
|
||||
/* -------------------------------- */
|
||||
|
||||
pthread_mutex_lock(&video->data_mutex);
|
||||
|
||||
frame_info = &video->cache[video->first_added];
|
||||
|
||||
pthread_mutex_unlock(&video->data_mutex);
|
||||
|
||||
/* -------------------------------- */
|
||||
|
||||
pthread_mutex_lock(&video->input_mutex);
|
||||
|
||||
for (size_t i = 0; i < video->inputs.num; i++) {
|
||||
struct video_input *input = video->inputs.array+i;
|
||||
struct video_data frame = frame_info->frame;
|
||||
|
||||
if (scale_video_output(input, &frame))
|
||||
input->callback(input->param, &frame);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&video->input_mutex);
|
||||
|
||||
/* -------------------------------- */
|
||||
|
||||
pthread_mutex_lock(&video->data_mutex);
|
||||
|
||||
frame_info->frame.timestamp += video->frame_time;
|
||||
complete = --frame_info->count == 0;
|
||||
|
||||
if (complete) {
|
||||
if (++video->first_added == video->info.cache_size)
|
||||
video->first_added = 0;
|
||||
|
||||
if (++video->available_frames == video->info.cache_size)
|
||||
video->last_added = video->first_added;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&video->data_mutex);
|
||||
|
||||
/* -------------------------------- */
|
||||
|
||||
return complete;
|
||||
}
|
||||
|
||||
static void *video_thread(void *param)
|
||||
{
|
||||
struct video_output *video = param;
|
||||
|
||||
os_set_thread_name("video-io: video thread");
|
||||
|
||||
const char *video_thread_name =
|
||||
profile_store_name(obs_get_profiler_name_store(),
|
||||
"video_thread(%s)", video->info.name);
|
||||
|
||||
while (os_sem_wait(video->update_semaphore) == 0) {
|
||||
if (video->stop)
|
||||
break;
|
||||
|
||||
profile_start(video_thread_name);
|
||||
while (!video->stop && !video_output_cur_frame(video)) {
|
||||
video->total_frames++;
|
||||
}
|
||||
|
||||
video->total_frames++;
|
||||
profile_end(video_thread_name);
|
||||
|
||||
profile_reenable_thread();
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static inline bool valid_video_params(const struct video_output_info *info)
|
||||
{
|
||||
return info->height != 0 && info->width != 0 && info->fps_den != 0 &&
|
||||
info->fps_num != 0;
|
||||
}
|
||||
|
||||
static inline void init_cache(struct video_output *video)
|
||||
{
|
||||
if (video->info.cache_size > MAX_CACHE_SIZE)
|
||||
video->info.cache_size = MAX_CACHE_SIZE;
|
||||
|
||||
for (size_t i = 0; i < video->info.cache_size; i++) {
|
||||
struct video_frame *frame;
|
||||
frame = (struct video_frame*)&video->cache[i];
|
||||
|
||||
video_frame_init(frame, video->info.format,
|
||||
video->info.width, video->info.height);
|
||||
}
|
||||
|
||||
video->available_frames = video->info.cache_size;
|
||||
}
|
||||
|
||||
int video_output_open(video_t **video, struct video_output_info *info)
|
||||
{
|
||||
struct video_output *out;
|
||||
pthread_mutexattr_t attr;
|
||||
|
||||
if (!valid_video_params(info))
|
||||
return VIDEO_OUTPUT_INVALIDPARAM;
|
||||
|
||||
out = bzalloc(sizeof(struct video_output));
|
||||
if (!out)
|
||||
goto fail;
|
||||
|
||||
memcpy(&out->info, info, sizeof(struct video_output_info));
|
||||
out->frame_time = (uint64_t)(1000000000.0 * (double)info->fps_den /
|
||||
(double)info->fps_num);
|
||||
out->initialized = false;
|
||||
|
||||
if (pthread_mutexattr_init(&attr) != 0)
|
||||
goto fail;
|
||||
if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0)
|
||||
goto fail;
|
||||
if (pthread_mutex_init(&out->data_mutex, &attr) != 0)
|
||||
goto fail;
|
||||
if (pthread_mutex_init(&out->input_mutex, &attr) != 0)
|
||||
goto fail;
|
||||
if (os_sem_init(&out->update_semaphore, 0) != 0)
|
||||
goto fail;
|
||||
if (pthread_create(&out->thread, NULL, video_thread, out) != 0)
|
||||
goto fail;
|
||||
|
||||
init_cache(out);
|
||||
|
||||
out->initialized = true;
|
||||
*video = out;
|
||||
return VIDEO_OUTPUT_SUCCESS;
|
||||
|
||||
fail:
|
||||
video_output_close(out);
|
||||
return VIDEO_OUTPUT_FAIL;
|
||||
}
|
||||
|
||||
void video_output_close(video_t *video)
|
||||
{
|
||||
if (!video)
|
||||
return;
|
||||
|
||||
video_output_stop(video);
|
||||
|
||||
for (size_t i = 0; i < video->inputs.num; i++)
|
||||
video_input_free(&video->inputs.array[i]);
|
||||
da_free(video->inputs);
|
||||
|
||||
for (size_t i = 0; i < video->info.cache_size; i++)
|
||||
video_frame_free((struct video_frame*)&video->cache[i]);
|
||||
|
||||
os_sem_destroy(video->update_semaphore);
|
||||
pthread_mutex_destroy(&video->data_mutex);
|
||||
pthread_mutex_destroy(&video->input_mutex);
|
||||
bfree(video);
|
||||
}
|
||||
|
||||
static size_t video_get_input_idx(const video_t *video,
|
||||
void (*callback)(void *param, struct video_data *frame),
|
||||
void *param)
|
||||
{
|
||||
for (size_t i = 0; i < video->inputs.num; i++) {
|
||||
struct video_input *input = video->inputs.array+i;
|
||||
if (input->callback == callback && input->param == param)
|
||||
return i;
|
||||
}
|
||||
|
||||
return DARRAY_INVALID;
|
||||
}
|
||||
|
||||
static inline bool video_input_init(struct video_input *input,
|
||||
struct video_output *video)
|
||||
{
|
||||
if (input->conversion.width != video->info.width ||
|
||||
input->conversion.height != video->info.height ||
|
||||
input->conversion.format != video->info.format) {
|
||||
struct video_scale_info from = {
|
||||
.format = video->info.format,
|
||||
.width = video->info.width,
|
||||
.height = video->info.height,
|
||||
};
|
||||
|
||||
int ret = video_scaler_create(&input->scaler,
|
||||
&input->conversion, &from,
|
||||
VIDEO_SCALE_FAST_BILINEAR);
|
||||
if (ret != VIDEO_SCALER_SUCCESS) {
|
||||
if (ret == VIDEO_SCALER_BAD_CONVERSION)
|
||||
blog(LOG_ERROR, "video_input_init: Bad "
|
||||
"scale conversion type");
|
||||
else
|
||||
blog(LOG_ERROR, "video_input_init: Failed to "
|
||||
"create scaler");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < MAX_CONVERT_BUFFERS; i++)
|
||||
video_frame_init(&input->frame[i],
|
||||
input->conversion.format,
|
||||
input->conversion.width,
|
||||
input->conversion.height);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool video_output_connect(video_t *video,
|
||||
const struct video_scale_info *conversion,
|
||||
void (*callback)(void *param, struct video_data *frame),
|
||||
void *param)
|
||||
{
|
||||
bool success = false;
|
||||
|
||||
if (!video || !callback)
|
||||
return false;
|
||||
|
||||
pthread_mutex_lock(&video->input_mutex);
|
||||
|
||||
if (video_get_input_idx(video, callback, param) == DARRAY_INVALID) {
|
||||
struct video_input input;
|
||||
memset(&input, 0, sizeof(input));
|
||||
|
||||
input.callback = callback;
|
||||
input.param = param;
|
||||
|
||||
if (conversion) {
|
||||
input.conversion = *conversion;
|
||||
} else {
|
||||
input.conversion.format = video->info.format;
|
||||
input.conversion.width = video->info.width;
|
||||
input.conversion.height = video->info.height;
|
||||
}
|
||||
|
||||
if (input.conversion.width == 0)
|
||||
input.conversion.width = video->info.width;
|
||||
if (input.conversion.height == 0)
|
||||
input.conversion.height = video->info.height;
|
||||
|
||||
success = video_input_init(&input, video);
|
||||
if (success)
|
||||
da_push_back(video->inputs, &input);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&video->input_mutex);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void video_output_disconnect(video_t *video,
|
||||
void (*callback)(void *param, struct video_data *frame),
|
||||
void *param)
|
||||
{
|
||||
if (!video || !callback)
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&video->input_mutex);
|
||||
|
||||
size_t idx = video_get_input_idx(video, callback, param);
|
||||
if (idx != DARRAY_INVALID) {
|
||||
video_input_free(video->inputs.array+idx);
|
||||
da_erase(video->inputs, idx);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&video->input_mutex);
|
||||
}
|
||||
|
||||
bool video_output_active(const video_t *video)
|
||||
{
|
||||
if (!video) return false;
|
||||
return video->inputs.num != 0;
|
||||
}
|
||||
|
||||
const struct video_output_info *video_output_get_info(const video_t *video)
|
||||
{
|
||||
return video ? &video->info : NULL;
|
||||
}
|
||||
|
||||
bool video_output_lock_frame(video_t *video, struct video_frame *frame,
|
||||
int count, uint64_t timestamp)
|
||||
{
|
||||
struct cached_frame_info *cfi;
|
||||
bool locked;
|
||||
|
||||
if (!video) return false;
|
||||
|
||||
pthread_mutex_lock(&video->data_mutex);
|
||||
|
||||
if (video->available_frames == 0) {
|
||||
video->skipped_frames += count;
|
||||
video->cache[video->last_added].count += count;
|
||||
locked = false;
|
||||
|
||||
} else {
|
||||
if (video->available_frames != video->info.cache_size) {
|
||||
if (++video->last_added == video->info.cache_size)
|
||||
video->last_added = 0;
|
||||
}
|
||||
|
||||
cfi = &video->cache[video->last_added];
|
||||
cfi->frame.timestamp = timestamp;
|
||||
cfi->count = count;
|
||||
|
||||
memcpy(frame, &cfi->frame, sizeof(*frame));
|
||||
|
||||
locked = true;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&video->data_mutex);
|
||||
|
||||
return locked;
|
||||
}
|
||||
|
||||
void video_output_unlock_frame(video_t *video)
|
||||
{
|
||||
if (!video) return;
|
||||
|
||||
pthread_mutex_lock(&video->data_mutex);
|
||||
|
||||
video->available_frames--;
|
||||
os_sem_post(video->update_semaphore);
|
||||
|
||||
pthread_mutex_unlock(&video->data_mutex);
|
||||
}
|
||||
|
||||
uint64_t video_output_get_frame_time(const video_t *video)
|
||||
{
|
||||
return video ? video->frame_time : 0;
|
||||
}
|
||||
|
||||
void video_output_stop(video_t *video)
|
||||
{
|
||||
void *thread_ret;
|
||||
|
||||
if (!video)
|
||||
return;
|
||||
|
||||
if (video->initialized) {
|
||||
video->initialized = false;
|
||||
video->stop = true;
|
||||
os_sem_post(video->update_semaphore);
|
||||
pthread_join(video->thread, &thread_ret);
|
||||
}
|
||||
}
|
||||
|
||||
bool video_output_stopped(video_t *video)
|
||||
{
|
||||
if (!video)
|
||||
return true;
|
||||
|
||||
return video->stop;
|
||||
}
|
||||
|
||||
enum video_format video_output_get_format(const video_t *video)
|
||||
{
|
||||
return video ? video->info.format : VIDEO_FORMAT_NONE;
|
||||
}
|
||||
|
||||
uint32_t video_output_get_width(const video_t *video)
|
||||
{
|
||||
return video ? video->info.width : 0;
|
||||
}
|
||||
|
||||
uint32_t video_output_get_height(const video_t *video)
|
||||
{
|
||||
return video ? video->info.height : 0;
|
||||
}
|
||||
|
||||
double video_output_get_frame_rate(const video_t *video)
|
||||
{
|
||||
if (!video)
|
||||
return 0.0;
|
||||
|
||||
return (double)video->info.fps_num / (double)video->info.fps_den;
|
||||
}
|
||||
|
||||
uint32_t video_output_get_skipped_frames(const video_t *video)
|
||||
{
|
||||
return video->skipped_frames;
|
||||
}
|
||||
|
||||
uint32_t video_output_get_total_frames(const video_t *video)
|
||||
{
|
||||
return video->total_frames;
|
||||
}
|
||||
183
libobs/media-io/video-io.h
Normal file
183
libobs/media-io/video-io.h
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "media-io-defs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct video_frame;
|
||||
|
||||
/* Base video output component. Use this to create a video output track. */
|
||||
|
||||
struct video_output;
|
||||
typedef struct video_output video_t;
|
||||
|
||||
enum video_format {
|
||||
VIDEO_FORMAT_NONE,
|
||||
|
||||
/* planar 420 format */
|
||||
VIDEO_FORMAT_I420, /* three-plane */
|
||||
VIDEO_FORMAT_NV12, /* two-plane, luma and packed chroma */
|
||||
|
||||
/* packed 422 formats */
|
||||
VIDEO_FORMAT_YVYU,
|
||||
VIDEO_FORMAT_YUY2, /* YUYV */
|
||||
VIDEO_FORMAT_UYVY,
|
||||
|
||||
/* packed uncompressed formats */
|
||||
VIDEO_FORMAT_RGBA,
|
||||
VIDEO_FORMAT_BGRA,
|
||||
VIDEO_FORMAT_BGRX,
|
||||
|
||||
/* planar 4:4:4 */
|
||||
VIDEO_FORMAT_I444,
|
||||
};
|
||||
|
||||
enum video_colorspace {
|
||||
VIDEO_CS_DEFAULT,
|
||||
VIDEO_CS_601,
|
||||
VIDEO_CS_709,
|
||||
};
|
||||
|
||||
enum video_range_type {
|
||||
VIDEO_RANGE_DEFAULT,
|
||||
VIDEO_RANGE_PARTIAL,
|
||||
VIDEO_RANGE_FULL
|
||||
};
|
||||
|
||||
struct video_data {
|
||||
uint8_t *data[MAX_AV_PLANES];
|
||||
uint32_t linesize[MAX_AV_PLANES];
|
||||
uint64_t timestamp;
|
||||
};
|
||||
|
||||
struct video_output_info {
|
||||
const char *name;
|
||||
|
||||
enum video_format format;
|
||||
uint32_t fps_num;
|
||||
uint32_t fps_den;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
size_t cache_size;
|
||||
|
||||
enum video_colorspace colorspace;
|
||||
enum video_range_type range;
|
||||
};
|
||||
|
||||
static inline bool format_is_yuv(enum video_format format)
|
||||
{
|
||||
switch (format) {
|
||||
case VIDEO_FORMAT_I420:
|
||||
case VIDEO_FORMAT_NV12:
|
||||
case VIDEO_FORMAT_YVYU:
|
||||
case VIDEO_FORMAT_YUY2:
|
||||
case VIDEO_FORMAT_UYVY:
|
||||
case VIDEO_FORMAT_I444:
|
||||
return true;
|
||||
case VIDEO_FORMAT_NONE:
|
||||
case VIDEO_FORMAT_RGBA:
|
||||
case VIDEO_FORMAT_BGRA:
|
||||
case VIDEO_FORMAT_BGRX:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline const char *get_video_format_name(enum video_format format)
|
||||
{
|
||||
switch (format) {
|
||||
case VIDEO_FORMAT_I420: return "I420";
|
||||
case VIDEO_FORMAT_NV12: return "NV12";
|
||||
case VIDEO_FORMAT_YVYU: return "YVYU";
|
||||
case VIDEO_FORMAT_YUY2: return "YUY2";
|
||||
case VIDEO_FORMAT_UYVY: return "UYVY";
|
||||
case VIDEO_FORMAT_RGBA: return "RGBA";
|
||||
case VIDEO_FORMAT_BGRA: return "BGRA";
|
||||
case VIDEO_FORMAT_BGRX: return "BGRX";
|
||||
case VIDEO_FORMAT_I444: return "I444";
|
||||
case VIDEO_FORMAT_NONE:;
|
||||
}
|
||||
|
||||
return "None";
|
||||
}
|
||||
|
||||
enum video_scale_type {
|
||||
VIDEO_SCALE_DEFAULT,
|
||||
VIDEO_SCALE_POINT,
|
||||
VIDEO_SCALE_FAST_BILINEAR,
|
||||
VIDEO_SCALE_BILINEAR,
|
||||
VIDEO_SCALE_BICUBIC,
|
||||
};
|
||||
|
||||
struct video_scale_info {
|
||||
enum video_format format;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
enum video_range_type range;
|
||||
enum video_colorspace colorspace;
|
||||
};
|
||||
|
||||
EXPORT enum video_format video_format_from_fourcc(uint32_t fourcc);
|
||||
|
||||
EXPORT bool video_format_get_parameters(enum video_colorspace color_space,
|
||||
enum video_range_type range, float matrix[16],
|
||||
float min_range[3], float max_range[3]);
|
||||
|
||||
#define VIDEO_OUTPUT_SUCCESS 0
|
||||
#define VIDEO_OUTPUT_INVALIDPARAM -1
|
||||
#define VIDEO_OUTPUT_FAIL -2
|
||||
|
||||
EXPORT int video_output_open(video_t **video, struct video_output_info *info);
|
||||
EXPORT void video_output_close(video_t *video);
|
||||
|
||||
EXPORT bool video_output_connect(video_t *video,
|
||||
const struct video_scale_info *conversion,
|
||||
void (*callback)(void *param, struct video_data *frame),
|
||||
void *param);
|
||||
EXPORT void video_output_disconnect(video_t *video,
|
||||
void (*callback)(void *param, struct video_data *frame),
|
||||
void *param);
|
||||
|
||||
EXPORT bool video_output_active(const video_t *video);
|
||||
|
||||
EXPORT const struct video_output_info *video_output_get_info(
|
||||
const video_t *video);
|
||||
EXPORT bool video_output_lock_frame(video_t *video, struct video_frame *frame,
|
||||
int count, uint64_t timestamp);
|
||||
EXPORT void video_output_unlock_frame(video_t *video);
|
||||
EXPORT uint64_t video_output_get_frame_time(const video_t *video);
|
||||
EXPORT void video_output_stop(video_t *video);
|
||||
EXPORT bool video_output_stopped(video_t *video);
|
||||
|
||||
EXPORT enum video_format video_output_get_format(const video_t *video);
|
||||
EXPORT uint32_t video_output_get_width(const video_t *video);
|
||||
EXPORT uint32_t video_output_get_height(const video_t *video);
|
||||
EXPORT double video_output_get_frame_rate(const video_t *video);
|
||||
|
||||
EXPORT uint32_t video_output_get_skipped_frames(const video_t *video);
|
||||
EXPORT uint32_t video_output_get_total_frames(const video_t *video);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
220
libobs/media-io/video-matrices.c
Normal file
220
libobs/media-io/video-matrices.c
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2014 by Ruwen Hahn <palana@stunned.de>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "../util/bmem.h"
|
||||
#include "video-io.h"
|
||||
|
||||
//#define COMPUTE_MATRICES
|
||||
|
||||
#ifdef COMPUTE_MATRICES
|
||||
#include "../graphics/matrix3.h"
|
||||
#endif
|
||||
|
||||
static struct {
|
||||
enum video_colorspace const color_space;
|
||||
float const Kb, Kr;
|
||||
int const range_min[3];
|
||||
int const range_max[3];
|
||||
int const black_levels[2][3];
|
||||
|
||||
float float_range_min[3];
|
||||
float float_range_max[3];
|
||||
float matrix[2][16];
|
||||
|
||||
} format_info[] = {
|
||||
{VIDEO_CS_601,
|
||||
0.114f, 0.299f, {16, 16, 16}, {235, 240, 240},
|
||||
{{16, 128, 128}, {0, 128, 128}},
|
||||
#ifndef COMPUTE_MATRICES
|
||||
{ 16.0f/255.0f, 16.0f/255.0f, 16.0f/255.0f},
|
||||
{235.0f/255.0f, 240.0f/255.0f, 240.0f/255.0f},
|
||||
{
|
||||
{
|
||||
1.164384f, 0.000000f, 1.596027f, -0.874202f,
|
||||
1.164384f, -0.391762f, -0.812968f, 0.531668f,
|
||||
1.164384f, 2.017232f, 0.000000f, -1.085631f,
|
||||
0.000000f, 0.000000f, 0.000000f, 1.000000f
|
||||
},
|
||||
{
|
||||
1.000000f, 0.000000f, 1.407520f, -0.706520f,
|
||||
1.000000f, -0.345491f, -0.716948f, 0.533303f,
|
||||
1.000000f, 1.778976f, 0.000000f, -0.892976f,
|
||||
0.000000f, 0.000000f, 0.000000f, 1.000000f
|
||||
}
|
||||
}
|
||||
#endif
|
||||
},
|
||||
{VIDEO_CS_709,
|
||||
0.0722f, 0.2126f, {16, 16, 16}, {235, 240, 240},
|
||||
{{16, 128, 128}, {0, 128, 128}},
|
||||
#ifndef COMPUTE_MATRICES
|
||||
{ 16.0f/255.0f, 16.0f/255.0f, 16.0f/255.0f},
|
||||
{235.0f/255.0f, 240.0f/255.0f, 240.0f/255.0f},
|
||||
{
|
||||
{
|
||||
1.164384f, 0.000000f, 1.792741f, -0.972945f,
|
||||
1.164384f, -0.213249f, -0.532909f, 0.301483f,
|
||||
1.164384f, 2.112402f, 0.000000f, -1.133402f,
|
||||
0.000000f, 0.000000f, 0.000000f, 1.000000f
|
||||
},
|
||||
{
|
||||
1.000000f, 0.000000f, 1.581000f, -0.793600f,
|
||||
1.000000f, -0.188062f, -0.469967f, 0.330305f,
|
||||
1.000000f, 1.862906f, 0.000000f, -0.935106f,
|
||||
0.000000f, 0.000000f, 0.000000f, 1.000000f
|
||||
}
|
||||
}
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
#define NUM_FORMATS (sizeof(format_info)/sizeof(format_info[0]))
|
||||
|
||||
#ifdef COMPUTE_MATRICES
|
||||
static void log_matrix(float const matrix[16])
|
||||
{
|
||||
blog(LOG_DEBUG, "\n% f, % f, % f, % f" \
|
||||
"\n% f, % f, % f, % f" \
|
||||
"\n% f, % f, % f, % f" \
|
||||
"\n% f, % f, % f, % f",
|
||||
matrix[ 0], matrix[ 1], matrix[ 2], matrix[ 3],
|
||||
matrix[ 4], matrix[ 5], matrix[ 6], matrix[ 7],
|
||||
matrix[ 8], matrix[ 9], matrix[10], matrix[11],
|
||||
matrix[12], matrix[13], matrix[14], matrix[15]);
|
||||
}
|
||||
|
||||
static void initialize_matrix(float const Kb, float const Kr,
|
||||
int const range_min[3], int const range_max[3],
|
||||
int const black_levels[3], float matrix[16])
|
||||
{
|
||||
struct matrix3 color_matrix;
|
||||
|
||||
int yvals = range_max[0] - range_min[0];
|
||||
int uvals = (range_max[1] - range_min[1]) / 2;
|
||||
int vvals = (range_max[2] - range_min[2]) / 2;
|
||||
|
||||
vec3_set(&color_matrix.x, 255./yvals,
|
||||
0.,
|
||||
255./vvals * (1. - Kr));
|
||||
vec3_set(&color_matrix.y, 255./yvals,
|
||||
255./uvals * (Kb - 1.) * Kb / (1. - Kb - Kr),
|
||||
255./vvals * (Kr - 1.) * Kr / (1. - Kb - Kr));
|
||||
vec3_set(&color_matrix.z, 255./yvals,
|
||||
255./uvals * (1. - Kb),
|
||||
0.);
|
||||
|
||||
struct vec3 offsets, multiplied;
|
||||
vec3_set(&offsets,
|
||||
-black_levels[0]/255.,
|
||||
-black_levels[1]/255.,
|
||||
-black_levels[2]/255.);
|
||||
vec3_rotate(&multiplied, &offsets, &color_matrix);
|
||||
|
||||
matrix[ 0] = color_matrix.x.x;
|
||||
matrix[ 1] = color_matrix.x.y;
|
||||
matrix[ 2] = color_matrix.x.z;
|
||||
matrix[ 3] = multiplied.x;
|
||||
|
||||
matrix[ 4] = color_matrix.y.x;
|
||||
matrix[ 5] = color_matrix.y.y;
|
||||
matrix[ 6] = color_matrix.y.z;
|
||||
matrix[ 7] = multiplied.y;
|
||||
|
||||
matrix[ 8] = color_matrix.z.x;
|
||||
matrix[ 9] = color_matrix.z.y;
|
||||
matrix[10] = color_matrix.z.z;
|
||||
matrix[11] = multiplied.z;
|
||||
|
||||
matrix[12] = matrix[13] = matrix[14] = 0.;
|
||||
matrix[15] = 1.;
|
||||
|
||||
log_matrix(matrix);
|
||||
}
|
||||
|
||||
static void initialize_matrices()
|
||||
{
|
||||
static int range_min[] = { 0, 0, 0};
|
||||
static int range_max[] = {255, 255, 255};
|
||||
|
||||
for (size_t i = 0; i < NUM_FORMATS; i++) {
|
||||
initialize_matrix(format_info[i].Kb, format_info[i].Kr,
|
||||
range_min, range_max,
|
||||
format_info[i].black_levels[1],
|
||||
format_info[i].matrix[1]);
|
||||
|
||||
initialize_matrix(format_info[i].Kb, format_info[i].Kr,
|
||||
format_info[i].range_min,
|
||||
format_info[i].range_max,
|
||||
format_info[i].black_levels[0],
|
||||
format_info[i].matrix[0]);
|
||||
|
||||
for (int j = 0; j < 3; j++) {
|
||||
format_info[i].float_range_min[j] =
|
||||
format_info[i].range_min[j]/255.;
|
||||
format_info[i].float_range_max[j] =
|
||||
format_info[i].range_max[j]/255.;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool matrices_initialized = false;
|
||||
#endif
|
||||
|
||||
static const float full_min[3] = {0.0f, 0.0f, 0.0f};
|
||||
static const float full_max[3] = {1.0f, 1.0f, 1.0f};
|
||||
|
||||
bool video_format_get_parameters(enum video_colorspace color_space,
|
||||
enum video_range_type range, float matrix[16],
|
||||
float range_min[3], float range_max[3])
|
||||
{
|
||||
#ifdef COMPUTE_MATRICES
|
||||
if (!matrices_initialized) {
|
||||
initialize_matrices();
|
||||
matrices_initialized = true;
|
||||
}
|
||||
#endif
|
||||
if (color_space == VIDEO_CS_DEFAULT)
|
||||
color_space = VIDEO_CS_601;
|
||||
|
||||
for (size_t i = 0; i < NUM_FORMATS; i++) {
|
||||
if (format_info[i].color_space != color_space)
|
||||
continue;
|
||||
|
||||
int full_range = range == VIDEO_RANGE_FULL ? 1 : 0;
|
||||
memcpy(matrix, format_info[i].matrix[full_range],
|
||||
sizeof(float) * 16);
|
||||
|
||||
if (range == VIDEO_RANGE_FULL) {
|
||||
if (range_min)
|
||||
memcpy(range_min, full_min, sizeof(float) * 3);
|
||||
if (range_max)
|
||||
memcpy(range_max, full_max, sizeof(float) * 3);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (range_min)
|
||||
memcpy(range_min, format_info[i].float_range_min,
|
||||
sizeof(float) * 3);
|
||||
|
||||
if (range_max)
|
||||
memcpy(range_max, format_info[i].float_range_max,
|
||||
sizeof(float) * 3);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
162
libobs/media-io/video-scaler-ffmpeg.c
Normal file
162
libobs/media-io/video-scaler-ffmpeg.c
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2014 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "../util/bmem.h"
|
||||
#include "video-scaler.h"
|
||||
|
||||
#include <libswscale/swscale.h>
|
||||
|
||||
struct video_scaler {
|
||||
struct SwsContext *swscale;
|
||||
int src_height;
|
||||
};
|
||||
|
||||
static inline enum AVPixelFormat get_ffmpeg_video_format(
|
||||
enum video_format format)
|
||||
{
|
||||
switch (format) {
|
||||
case VIDEO_FORMAT_NONE: return AV_PIX_FMT_NONE;
|
||||
case VIDEO_FORMAT_I420: return AV_PIX_FMT_YUV420P;
|
||||
case VIDEO_FORMAT_NV12: return AV_PIX_FMT_NV12;
|
||||
case VIDEO_FORMAT_YVYU: return AV_PIX_FMT_NONE;
|
||||
case VIDEO_FORMAT_YUY2: return AV_PIX_FMT_YUYV422;
|
||||
case VIDEO_FORMAT_UYVY: return AV_PIX_FMT_UYVY422;
|
||||
case VIDEO_FORMAT_RGBA: return AV_PIX_FMT_RGBA;
|
||||
case VIDEO_FORMAT_BGRA: return AV_PIX_FMT_BGRA;
|
||||
case VIDEO_FORMAT_BGRX: return AV_PIX_FMT_BGRA;
|
||||
case VIDEO_FORMAT_I444: return AV_PIX_FMT_YUV444P;
|
||||
}
|
||||
|
||||
return AV_PIX_FMT_NONE;
|
||||
}
|
||||
|
||||
static inline int get_ffmpeg_scale_type(enum video_scale_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case VIDEO_SCALE_DEFAULT: return SWS_FAST_BILINEAR;
|
||||
case VIDEO_SCALE_POINT: return SWS_POINT;
|
||||
case VIDEO_SCALE_FAST_BILINEAR: return SWS_FAST_BILINEAR;
|
||||
case VIDEO_SCALE_BILINEAR: return SWS_BILINEAR | SWS_AREA;
|
||||
case VIDEO_SCALE_BICUBIC: return SWS_BICUBIC;
|
||||
}
|
||||
|
||||
return SWS_POINT;
|
||||
}
|
||||
|
||||
static inline const int *get_ffmpeg_coeffs(enum video_colorspace cs)
|
||||
{
|
||||
switch (cs) {
|
||||
case VIDEO_CS_DEFAULT: return sws_getCoefficients(SWS_CS_ITU601);
|
||||
case VIDEO_CS_601: return sws_getCoefficients(SWS_CS_ITU601);
|
||||
case VIDEO_CS_709: return sws_getCoefficients(SWS_CS_ITU709);
|
||||
}
|
||||
|
||||
return sws_getCoefficients(SWS_CS_ITU601);
|
||||
}
|
||||
|
||||
static inline int get_ffmpeg_range_type(enum video_range_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case VIDEO_RANGE_DEFAULT: return 0;
|
||||
case VIDEO_RANGE_PARTIAL: return 0;
|
||||
case VIDEO_RANGE_FULL: return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define FIXED_1_0 (1<<16)
|
||||
|
||||
int video_scaler_create(video_scaler_t **scaler_out,
|
||||
const struct video_scale_info *dst,
|
||||
const struct video_scale_info *src,
|
||||
enum video_scale_type type)
|
||||
{
|
||||
enum AVPixelFormat format_src = get_ffmpeg_video_format(src->format);
|
||||
enum AVPixelFormat format_dst = get_ffmpeg_video_format(dst->format);
|
||||
int scale_type = get_ffmpeg_scale_type(type);
|
||||
const int *coeff_src = get_ffmpeg_coeffs(src->colorspace);
|
||||
const int *coeff_dst = get_ffmpeg_coeffs(dst->colorspace);
|
||||
int range_src = get_ffmpeg_range_type(src->range);
|
||||
int range_dst = get_ffmpeg_range_type(dst->range);
|
||||
struct video_scaler *scaler;
|
||||
int ret;
|
||||
|
||||
if (!scaler_out)
|
||||
return VIDEO_SCALER_FAILED;
|
||||
|
||||
if (format_src == AV_PIX_FMT_NONE ||
|
||||
format_dst == AV_PIX_FMT_NONE)
|
||||
return VIDEO_SCALER_BAD_CONVERSION;
|
||||
|
||||
scaler = bzalloc(sizeof(struct video_scaler));
|
||||
scaler->src_height = src->height;
|
||||
|
||||
scaler->swscale = sws_getCachedContext(NULL,
|
||||
src->width, src->height, format_src,
|
||||
dst->width, dst->height, format_dst,
|
||||
scale_type, NULL, NULL, NULL);
|
||||
if (!scaler->swscale) {
|
||||
blog(LOG_ERROR, "video_scaler_create: Could not create "
|
||||
"swscale");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = sws_setColorspaceDetails(scaler->swscale,
|
||||
coeff_src, range_src,
|
||||
coeff_dst, range_dst,
|
||||
0, FIXED_1_0, FIXED_1_0);
|
||||
if (ret < 0) {
|
||||
blog(LOG_DEBUG, "video_scaler_create: "
|
||||
"sws_setColorspaceDetails failed, ignoring");
|
||||
}
|
||||
|
||||
*scaler_out = scaler;
|
||||
return VIDEO_SCALER_SUCCESS;
|
||||
|
||||
fail:
|
||||
video_scaler_destroy(scaler);
|
||||
return VIDEO_SCALER_FAILED;
|
||||
}
|
||||
|
||||
void video_scaler_destroy(video_scaler_t *scaler)
|
||||
{
|
||||
if (scaler) {
|
||||
sws_freeContext(scaler->swscale);
|
||||
bfree(scaler);
|
||||
}
|
||||
}
|
||||
|
||||
bool video_scaler_scale(video_scaler_t *scaler,
|
||||
uint8_t *output[], const uint32_t out_linesize[],
|
||||
const uint8_t *const input[], const uint32_t in_linesize[])
|
||||
{
|
||||
if (!scaler)
|
||||
return false;
|
||||
|
||||
int ret = sws_scale(scaler->swscale,
|
||||
input, (const int *)in_linesize,
|
||||
0, scaler->src_height,
|
||||
output, (const int *)out_linesize);
|
||||
if (ret <= 0) {
|
||||
blog(LOG_ERROR, "video_scaler_scale: sws_scale failed: %d",
|
||||
ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
46
libobs/media-io/video-scaler.h
Normal file
46
libobs/media-io/video-scaler.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2014 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../util/c99defs.h"
|
||||
#include "video-io.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct video_scaler;
|
||||
typedef struct video_scaler video_scaler_t;
|
||||
|
||||
#define VIDEO_SCALER_SUCCESS 0
|
||||
#define VIDEO_SCALER_BAD_CONVERSION -1
|
||||
#define VIDEO_SCALER_FAILED -2
|
||||
|
||||
EXPORT int video_scaler_create(video_scaler_t **scaler,
|
||||
const struct video_scale_info *dst,
|
||||
const struct video_scale_info *src,
|
||||
enum video_scale_type type);
|
||||
EXPORT void video_scaler_destroy(video_scaler_t *scaler);
|
||||
|
||||
EXPORT bool video_scaler_scale(video_scaler_t *scaler,
|
||||
uint8_t *output[], const uint32_t out_linesize[],
|
||||
const uint8_t *const input[], const uint32_t in_linesize[]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
798
libobs/obs-audio-controls.c
Normal file
798
libobs/obs-audio-controls.c
Normal file
|
|
@ -0,0 +1,798 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Leonhard Oelke <leonhard@in-verted.de>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "util/threading.h"
|
||||
#include "util/bmem.h"
|
||||
#include "media-io/audio-math.h"
|
||||
#include "obs.h"
|
||||
#include "obs-internal.h"
|
||||
|
||||
#include "obs-audio-controls.h"
|
||||
|
||||
/* These are pointless warnings generated not by our code, but by a standard
|
||||
* library macro, INFINITY */
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4056)
|
||||
#pragma warning(disable : 4756)
|
||||
#endif
|
||||
|
||||
typedef float (*obs_fader_conversion_t)(const float val);
|
||||
|
||||
struct fader_cb {
|
||||
obs_fader_changed_t callback;
|
||||
void *param;
|
||||
};
|
||||
|
||||
struct obs_fader {
|
||||
pthread_mutex_t mutex;
|
||||
obs_fader_conversion_t def_to_db;
|
||||
obs_fader_conversion_t db_to_def;
|
||||
obs_source_t *source;
|
||||
enum obs_fader_type type;
|
||||
float max_db;
|
||||
float min_db;
|
||||
float cur_db;
|
||||
bool ignore_next_signal;
|
||||
|
||||
pthread_mutex_t callback_mutex;
|
||||
DARRAY(struct fader_cb)callbacks;
|
||||
};
|
||||
|
||||
struct meter_cb {
|
||||
obs_volmeter_updated_t callback;
|
||||
void *param;
|
||||
};
|
||||
|
||||
struct obs_volmeter {
|
||||
pthread_mutex_t mutex;
|
||||
obs_fader_conversion_t pos_to_db;
|
||||
obs_fader_conversion_t db_to_pos;
|
||||
obs_source_t *source;
|
||||
enum obs_fader_type type;
|
||||
float cur_db;
|
||||
|
||||
pthread_mutex_t callback_mutex;
|
||||
DARRAY(struct meter_cb)callbacks;
|
||||
|
||||
unsigned int channels;
|
||||
unsigned int update_ms;
|
||||
unsigned int update_frames;
|
||||
unsigned int peakhold_ms;
|
||||
unsigned int peakhold_frames;
|
||||
|
||||
unsigned int peakhold_count;
|
||||
unsigned int ival_frames;
|
||||
float ival_sum;
|
||||
float ival_max;
|
||||
|
||||
float vol_peak;
|
||||
float vol_mag;
|
||||
float vol_max;
|
||||
};
|
||||
|
||||
static float cubic_def_to_db(const float def)
|
||||
{
|
||||
if (def == 1.0f)
|
||||
return 0.0f;
|
||||
else if (def <= 0.0f)
|
||||
return -INFINITY;
|
||||
|
||||
return mul_to_db(def * def * def);
|
||||
}
|
||||
|
||||
static float cubic_db_to_def(const float db)
|
||||
{
|
||||
if (db == 0.0f)
|
||||
return 1.0f;
|
||||
else if (db == -INFINITY)
|
||||
return 0.0f;
|
||||
|
||||
return cbrtf(db_to_mul(db));
|
||||
}
|
||||
|
||||
static float iec_def_to_db(const float def)
|
||||
{
|
||||
if (def == 1.0f)
|
||||
return 0.0f;
|
||||
else if (def <= 0.0f)
|
||||
return -INFINITY;
|
||||
|
||||
float db;
|
||||
|
||||
if (def >= 0.75f)
|
||||
db = (def - 1.0f) / 0.25f * 9.0f;
|
||||
else if (def >= 0.5f)
|
||||
db = (def - 0.75f) / 0.25f * 11.0f - 9.0f;
|
||||
else if (def >= 0.3f)
|
||||
db = (def - 0.5f) / 0.2f * 10.0f - 20.0f;
|
||||
else if (def >= 0.15f)
|
||||
db = (def - 0.3f) / 0.15f * 10.0f - 30.0f;
|
||||
else if (def >= 0.075f)
|
||||
db = (def - 0.15f) / 0.075f * 10.0f - 40.0f;
|
||||
else if (def >= 0.025f)
|
||||
db = (def - 0.075f) / 0.05f * 10.0f - 50.0f;
|
||||
else if (def >= 0.001f)
|
||||
db = (def - 0.025f) / 0.025f * 90.0f - 60.0f;
|
||||
else
|
||||
db = -INFINITY;
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
static float iec_db_to_def(const float db)
|
||||
{
|
||||
if (db == 0.0f)
|
||||
return 1.0f;
|
||||
else if (db == -INFINITY)
|
||||
return 0.0f;
|
||||
|
||||
float def;
|
||||
|
||||
if (db >= -9.0f)
|
||||
def = (db + 9.0f) / 9.0f * 0.25f + 0.75f;
|
||||
else if (db >= -20.0f)
|
||||
def = (db + 20.0f) / 11.0f * 0.25f + 0.5f;
|
||||
else if (db >= -30.0f)
|
||||
def = (db + 30.0f) / 10.0f * 0.2f + 0.3f;
|
||||
else if (db >= -40.0f)
|
||||
def = (db + 40.0f) / 10.0f * 0.15f + 0.15f;
|
||||
else if (db >= -50.0f)
|
||||
def = (db + 50.0f) / 10.0f * 0.075f + 0.075f;
|
||||
else if (db >= -60.0f)
|
||||
def = (db + 60.0f) / 10.0f * 0.05f + 0.025f;
|
||||
else if (db >= -114.0f)
|
||||
def = (db + 150.0f) / 90.0f * 0.025f;
|
||||
else
|
||||
def = 0.0f;
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
#define LOG_OFFSET_DB 6.0f
|
||||
#define LOG_RANGE_DB 96.0f
|
||||
/* equals -log10f(LOG_OFFSET_DB) */
|
||||
#define LOG_OFFSET_VAL -0.77815125038364363f
|
||||
/* equals -log10f(-LOG_RANGE_DB + LOG_OFFSET_DB) */
|
||||
#define LOG_RANGE_VAL -2.00860017176191756f
|
||||
|
||||
static float log_def_to_db(const float def)
|
||||
{
|
||||
if (def >= 1.0f)
|
||||
return 0.0f;
|
||||
else if (def <= 0.0f)
|
||||
return -INFINITY;
|
||||
|
||||
return -(LOG_RANGE_DB + LOG_OFFSET_DB) * powf(
|
||||
(LOG_RANGE_DB + LOG_OFFSET_DB) / LOG_OFFSET_DB, -def)
|
||||
+ LOG_OFFSET_DB;
|
||||
}
|
||||
|
||||
static float log_db_to_def(const float db)
|
||||
{
|
||||
if (db >= 0.0f)
|
||||
return 1.0f;
|
||||
else if (db <= -96.0f)
|
||||
return 0.0f;
|
||||
|
||||
return (-log10f(-db + LOG_OFFSET_DB) - LOG_RANGE_VAL)
|
||||
/ (LOG_OFFSET_VAL - LOG_RANGE_VAL);
|
||||
}
|
||||
|
||||
static void signal_volume_changed(struct obs_fader *fader, const float db)
|
||||
{
|
||||
pthread_mutex_lock(&fader->callback_mutex);
|
||||
for (size_t i = fader->callbacks.num; i > 0; i--) {
|
||||
struct fader_cb cb = fader->callbacks.array[i - 1];
|
||||
cb.callback(cb.param, db);
|
||||
}
|
||||
pthread_mutex_unlock(&fader->callback_mutex);
|
||||
}
|
||||
|
||||
static void signal_levels_updated(struct obs_volmeter *volmeter,
|
||||
const float level, const float magnitude, const float peak,
|
||||
bool muted)
|
||||
{
|
||||
pthread_mutex_lock(&volmeter->callback_mutex);
|
||||
for (size_t i = volmeter->callbacks.num; i > 0; i--) {
|
||||
struct meter_cb cb = volmeter->callbacks.array[i - 1];
|
||||
cb.callback(cb.param, level, magnitude, peak, muted);
|
||||
}
|
||||
pthread_mutex_unlock(&volmeter->callback_mutex);
|
||||
}
|
||||
|
||||
static void fader_source_volume_changed(void *vptr, calldata_t *calldata)
|
||||
{
|
||||
struct obs_fader *fader = (struct obs_fader *) vptr;
|
||||
|
||||
pthread_mutex_lock(&fader->mutex);
|
||||
|
||||
if (fader->ignore_next_signal) {
|
||||
fader->ignore_next_signal = false;
|
||||
pthread_mutex_unlock(&fader->mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
const float mul = (float)calldata_float(calldata, "volume");
|
||||
const float db = mul_to_db(mul);
|
||||
fader->cur_db = db;
|
||||
|
||||
pthread_mutex_unlock(&fader->mutex);
|
||||
|
||||
signal_volume_changed(fader, db);
|
||||
}
|
||||
|
||||
static void volmeter_source_volume_changed(void *vptr, calldata_t *calldata)
|
||||
{
|
||||
struct obs_volmeter *volmeter = (struct obs_volmeter *) vptr;
|
||||
|
||||
pthread_mutex_lock(&volmeter->mutex);
|
||||
|
||||
float mul = (float) calldata_float(calldata, "volume");
|
||||
volmeter->cur_db = mul_to_db(mul);
|
||||
|
||||
pthread_mutex_unlock(&volmeter->mutex);
|
||||
}
|
||||
|
||||
static void fader_source_destroyed(void *vptr, calldata_t *calldata)
|
||||
{
|
||||
UNUSED_PARAMETER(calldata);
|
||||
struct obs_fader *fader = (struct obs_fader *) vptr;
|
||||
|
||||
obs_fader_detach_source(fader);
|
||||
}
|
||||
|
||||
static void volmeter_source_destroyed(void *vptr, calldata_t *calldata)
|
||||
{
|
||||
UNUSED_PARAMETER(calldata);
|
||||
struct obs_volmeter *volmeter = (struct obs_volmeter *) vptr;
|
||||
|
||||
obs_volmeter_detach_source(volmeter);
|
||||
}
|
||||
|
||||
/* TODO: Separate for individual channels */
|
||||
static void volmeter_sum_and_max(float *data[MAX_AV_PLANES], size_t frames,
|
||||
float *sum, float *max)
|
||||
{
|
||||
float s = *sum;
|
||||
float m = *max;
|
||||
|
||||
for (size_t plane = 0; plane < MAX_AV_PLANES; plane++) {
|
||||
if (!data[plane])
|
||||
break;
|
||||
|
||||
for (float *c = data[plane]; c < data[plane] + frames; ++c) {
|
||||
const float pow = *c * *c;
|
||||
s += pow;
|
||||
m = (m > pow) ? m : pow;
|
||||
}
|
||||
}
|
||||
|
||||
*sum = s;
|
||||
*max = m;
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo The IIR low pass filter has a different behavior depending on the
|
||||
* update interval and sample rate, it should be replaced with something
|
||||
* that is independent from both.
|
||||
*/
|
||||
static void volmeter_calc_ival_levels(obs_volmeter_t *volmeter)
|
||||
{
|
||||
const unsigned int samples = volmeter->ival_frames * volmeter->channels;
|
||||
const float alpha = 0.15f;
|
||||
const float ival_max = sqrtf(volmeter->ival_max);
|
||||
const float ival_rms = sqrtf(volmeter->ival_sum / (float)samples);
|
||||
|
||||
if (ival_max > volmeter->vol_max) {
|
||||
volmeter->vol_max = ival_max;
|
||||
} else {
|
||||
volmeter->vol_max = alpha * volmeter->vol_max +
|
||||
(1.0f - alpha) * ival_max;
|
||||
}
|
||||
|
||||
if (volmeter->vol_max > volmeter->vol_peak ||
|
||||
volmeter->peakhold_count > volmeter->peakhold_frames) {
|
||||
volmeter->vol_peak = volmeter->vol_max;
|
||||
volmeter->peakhold_count = 0;
|
||||
} else {
|
||||
volmeter->peakhold_count += volmeter->ival_frames;
|
||||
}
|
||||
|
||||
volmeter->vol_mag = alpha * ival_rms +
|
||||
volmeter->vol_mag * (1.0f - alpha);
|
||||
|
||||
/* reset interval data */
|
||||
volmeter->ival_frames = 0;
|
||||
volmeter->ival_sum = 0.0f;
|
||||
volmeter->ival_max = 0.0f;
|
||||
}
|
||||
|
||||
static bool volmeter_process_audio_data(obs_volmeter_t *volmeter,
|
||||
const struct audio_data *data)
|
||||
{
|
||||
bool updated = false;
|
||||
size_t frames = 0;
|
||||
size_t left = data->frames;
|
||||
float *adata[MAX_AV_PLANES];
|
||||
|
||||
for (size_t i = 0; i < MAX_AV_PLANES; i++)
|
||||
adata[i] = (float*)data->data[i];
|
||||
|
||||
while (left) {
|
||||
frames = (volmeter->ival_frames + left >
|
||||
volmeter->update_frames)
|
||||
? volmeter->update_frames - volmeter->ival_frames
|
||||
: left;
|
||||
|
||||
volmeter_sum_and_max(adata, frames, &volmeter->ival_sum,
|
||||
&volmeter->ival_max);
|
||||
|
||||
volmeter->ival_frames += (unsigned int)frames;
|
||||
left -= frames;
|
||||
|
||||
for (size_t i = 0; i < MAX_AV_PLANES; i++) {
|
||||
if (!adata[i])
|
||||
break;
|
||||
adata[i] += frames;
|
||||
}
|
||||
|
||||
/* break if we did not reach the end of the interval */
|
||||
if (volmeter->ival_frames != volmeter->update_frames)
|
||||
break;
|
||||
|
||||
volmeter_calc_ival_levels(volmeter);
|
||||
updated = true;
|
||||
}
|
||||
|
||||
return updated;
|
||||
}
|
||||
|
||||
static void volmeter_source_data_received(void *vptr, obs_source_t *source,
|
||||
const struct audio_data *data, bool muted)
|
||||
{
|
||||
struct obs_volmeter *volmeter = (struct obs_volmeter *) vptr;
|
||||
bool updated = false;
|
||||
float mul, level, mag, peak;
|
||||
|
||||
pthread_mutex_lock(&volmeter->mutex);
|
||||
|
||||
updated = volmeter_process_audio_data(volmeter, data);
|
||||
|
||||
if (updated) {
|
||||
mul = db_to_mul(volmeter->cur_db);
|
||||
|
||||
level = volmeter->db_to_pos(mul_to_db(volmeter->vol_max * mul));
|
||||
mag = volmeter->db_to_pos(mul_to_db(volmeter->vol_mag * mul));
|
||||
peak = volmeter->db_to_pos(
|
||||
mul_to_db(volmeter->vol_peak * mul));
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&volmeter->mutex);
|
||||
|
||||
if (updated)
|
||||
signal_levels_updated(volmeter, level, mag, peak, muted);
|
||||
|
||||
UNUSED_PARAMETER(source);
|
||||
}
|
||||
|
||||
static void volmeter_update_audio_settings(obs_volmeter_t *volmeter)
|
||||
{
|
||||
audio_t *audio = obs_get_audio();
|
||||
const unsigned int sr = audio_output_get_sample_rate(audio);
|
||||
|
||||
volmeter->channels = (uint32_t)audio_output_get_channels(audio);
|
||||
volmeter->update_frames = volmeter->update_ms * sr / 1000;
|
||||
volmeter->peakhold_frames = volmeter->peakhold_ms * sr / 1000;
|
||||
}
|
||||
|
||||
obs_fader_t *obs_fader_create(enum obs_fader_type type)
|
||||
{
|
||||
struct obs_fader *fader = bzalloc(sizeof(struct obs_fader));
|
||||
if (!fader)
|
||||
return NULL;
|
||||
|
||||
pthread_mutex_init_value(&fader->mutex);
|
||||
pthread_mutex_init_value(&fader->callback_mutex);
|
||||
if (pthread_mutex_init(&fader->mutex, NULL) != 0)
|
||||
goto fail;
|
||||
if (pthread_mutex_init(&fader->callback_mutex, NULL) != 0)
|
||||
goto fail;
|
||||
|
||||
switch(type) {
|
||||
case OBS_FADER_CUBIC:
|
||||
fader->def_to_db = cubic_def_to_db;
|
||||
fader->db_to_def = cubic_db_to_def;
|
||||
fader->max_db = 0.0f;
|
||||
fader->min_db = -INFINITY;
|
||||
break;
|
||||
case OBS_FADER_IEC:
|
||||
fader->def_to_db = iec_def_to_db;
|
||||
fader->db_to_def = iec_db_to_def;
|
||||
fader->max_db = 0.0f;
|
||||
fader->min_db = -INFINITY;
|
||||
break;
|
||||
case OBS_FADER_LOG:
|
||||
fader->def_to_db = log_def_to_db;
|
||||
fader->db_to_def = log_db_to_def;
|
||||
fader->max_db = 0.0f;
|
||||
fader->min_db = -96.0f;
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
break;
|
||||
}
|
||||
fader->type = type;
|
||||
|
||||
return fader;
|
||||
fail:
|
||||
obs_fader_destroy(fader);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void obs_fader_destroy(obs_fader_t *fader)
|
||||
{
|
||||
if (!fader)
|
||||
return;
|
||||
|
||||
obs_fader_detach_source(fader);
|
||||
da_free(fader->callbacks);
|
||||
pthread_mutex_destroy(&fader->callback_mutex);
|
||||
pthread_mutex_destroy(&fader->mutex);
|
||||
|
||||
bfree(fader);
|
||||
}
|
||||
|
||||
bool obs_fader_set_db(obs_fader_t *fader, const float db)
|
||||
{
|
||||
if (!fader)
|
||||
return false;
|
||||
|
||||
pthread_mutex_lock(&fader->mutex);
|
||||
|
||||
bool clamped = false;
|
||||
fader->cur_db = db;
|
||||
|
||||
if (fader->cur_db > fader->max_db) {
|
||||
fader->cur_db = fader->max_db;
|
||||
clamped = true;
|
||||
}
|
||||
if (fader->cur_db < fader->min_db) {
|
||||
fader->cur_db = -INFINITY;
|
||||
clamped = true;
|
||||
}
|
||||
|
||||
fader->ignore_next_signal = true;
|
||||
obs_source_t *src = fader->source;
|
||||
const float mul = db_to_mul(fader->cur_db);
|
||||
|
||||
pthread_mutex_unlock(&fader->mutex);
|
||||
|
||||
if (src)
|
||||
obs_source_set_volume(src, mul);
|
||||
|
||||
return !clamped;
|
||||
}
|
||||
|
||||
float obs_fader_get_db(obs_fader_t *fader)
|
||||
{
|
||||
if (!fader)
|
||||
return 0.0f;
|
||||
|
||||
pthread_mutex_lock(&fader->mutex);
|
||||
const float db = fader->cur_db;
|
||||
pthread_mutex_unlock(&fader->mutex);
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
bool obs_fader_set_deflection(obs_fader_t *fader, const float def)
|
||||
{
|
||||
if (!fader)
|
||||
return false;
|
||||
|
||||
return obs_fader_set_db(fader, fader->def_to_db(def));
|
||||
}
|
||||
|
||||
float obs_fader_get_deflection(obs_fader_t *fader)
|
||||
{
|
||||
if (!fader)
|
||||
return 0.0f;
|
||||
|
||||
pthread_mutex_lock(&fader->mutex);
|
||||
const float def = fader->db_to_def(fader->cur_db);
|
||||
pthread_mutex_unlock(&fader->mutex);
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
bool obs_fader_set_mul(obs_fader_t *fader, const float mul)
|
||||
{
|
||||
if (!fader)
|
||||
return false;
|
||||
|
||||
return obs_fader_set_db(fader, mul_to_db(mul));
|
||||
}
|
||||
|
||||
float obs_fader_get_mul(obs_fader_t *fader)
|
||||
{
|
||||
if (!fader)
|
||||
return 0.0f;
|
||||
|
||||
pthread_mutex_lock(&fader->mutex);
|
||||
const float mul = db_to_mul(fader->cur_db);
|
||||
pthread_mutex_unlock(&fader->mutex);
|
||||
|
||||
return mul;
|
||||
}
|
||||
|
||||
bool obs_fader_attach_source(obs_fader_t *fader, obs_source_t *source)
|
||||
{
|
||||
signal_handler_t *sh;
|
||||
|
||||
if (!fader || !source)
|
||||
return false;
|
||||
|
||||
obs_fader_detach_source(fader);
|
||||
|
||||
pthread_mutex_lock(&fader->mutex);
|
||||
|
||||
sh = obs_source_get_signal_handler(source);
|
||||
signal_handler_connect(sh, "volume",
|
||||
fader_source_volume_changed, fader);
|
||||
signal_handler_connect(sh, "destroy",
|
||||
fader_source_destroyed, fader);
|
||||
|
||||
fader->source = source;
|
||||
fader->cur_db = mul_to_db(obs_source_get_volume(source));
|
||||
|
||||
pthread_mutex_unlock(&fader->mutex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void obs_fader_detach_source(obs_fader_t *fader)
|
||||
{
|
||||
signal_handler_t *sh;
|
||||
|
||||
if (!fader)
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&fader->mutex);
|
||||
|
||||
if (!fader->source)
|
||||
goto exit;
|
||||
|
||||
sh = obs_source_get_signal_handler(fader->source);
|
||||
signal_handler_disconnect(sh, "volume",
|
||||
fader_source_volume_changed, fader);
|
||||
signal_handler_disconnect(sh, "destroy",
|
||||
fader_source_destroyed, fader);
|
||||
|
||||
fader->source = NULL;
|
||||
|
||||
exit:
|
||||
pthread_mutex_unlock(&fader->mutex);
|
||||
}
|
||||
|
||||
void obs_fader_add_callback(obs_fader_t *fader, obs_fader_changed_t callback,
|
||||
void *param)
|
||||
{
|
||||
struct fader_cb cb = {callback, param};
|
||||
|
||||
if (!obs_ptr_valid(fader, "obs_fader_add_callback"))
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&fader->callback_mutex);
|
||||
da_push_back(fader->callbacks, &cb);
|
||||
pthread_mutex_unlock(&fader->callback_mutex);
|
||||
}
|
||||
|
||||
void obs_fader_remove_callback(obs_fader_t *fader, obs_fader_changed_t callback,
|
||||
void *param)
|
||||
{
|
||||
struct fader_cb cb = {callback, param};
|
||||
|
||||
if (!obs_ptr_valid(fader, "obs_fader_remove_callback"))
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&fader->callback_mutex);
|
||||
da_erase_item(fader->callbacks, &cb);
|
||||
pthread_mutex_unlock(&fader->callback_mutex);
|
||||
}
|
||||
|
||||
obs_volmeter_t *obs_volmeter_create(enum obs_fader_type type)
|
||||
{
|
||||
struct obs_volmeter *volmeter = bzalloc(sizeof(struct obs_volmeter));
|
||||
if (!volmeter)
|
||||
return NULL;
|
||||
|
||||
pthread_mutex_init_value(&volmeter->mutex);
|
||||
pthread_mutex_init_value(&volmeter->callback_mutex);
|
||||
if (pthread_mutex_init(&volmeter->mutex, NULL) != 0)
|
||||
goto fail;
|
||||
if (pthread_mutex_init(&volmeter->callback_mutex, NULL) != 0)
|
||||
goto fail;
|
||||
|
||||
/* set conversion functions */
|
||||
switch(type) {
|
||||
case OBS_FADER_CUBIC:
|
||||
volmeter->pos_to_db = cubic_def_to_db;
|
||||
volmeter->db_to_pos = cubic_db_to_def;
|
||||
break;
|
||||
case OBS_FADER_IEC:
|
||||
volmeter->pos_to_db = iec_def_to_db;
|
||||
volmeter->db_to_pos = iec_db_to_def;
|
||||
break;
|
||||
case OBS_FADER_LOG:
|
||||
volmeter->pos_to_db = log_def_to_db;
|
||||
volmeter->db_to_pos = log_db_to_def;
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
break;
|
||||
}
|
||||
volmeter->type = type;
|
||||
|
||||
obs_volmeter_set_update_interval(volmeter, 50);
|
||||
obs_volmeter_set_peak_hold(volmeter, 1500);
|
||||
|
||||
return volmeter;
|
||||
fail:
|
||||
obs_volmeter_destroy(volmeter);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void obs_volmeter_destroy(obs_volmeter_t *volmeter)
|
||||
{
|
||||
if (!volmeter)
|
||||
return;
|
||||
|
||||
obs_volmeter_detach_source(volmeter);
|
||||
da_free(volmeter->callbacks);
|
||||
pthread_mutex_destroy(&volmeter->callback_mutex);
|
||||
pthread_mutex_destroy(&volmeter->mutex);
|
||||
|
||||
bfree(volmeter);
|
||||
}
|
||||
|
||||
bool obs_volmeter_attach_source(obs_volmeter_t *volmeter, obs_source_t *source)
|
||||
{
|
||||
signal_handler_t *sh;
|
||||
|
||||
if (!volmeter || !source)
|
||||
return false;
|
||||
|
||||
obs_volmeter_detach_source(volmeter);
|
||||
|
||||
pthread_mutex_lock(&volmeter->mutex);
|
||||
|
||||
sh = obs_source_get_signal_handler(source);
|
||||
signal_handler_connect(sh, "volume",
|
||||
volmeter_source_volume_changed, volmeter);
|
||||
signal_handler_connect(sh, "destroy",
|
||||
volmeter_source_destroyed, volmeter);
|
||||
obs_source_add_audio_capture_callback(source,
|
||||
volmeter_source_data_received, volmeter);
|
||||
|
||||
volmeter->source = source;
|
||||
volmeter->cur_db = mul_to_db(obs_source_get_volume(source));
|
||||
|
||||
pthread_mutex_unlock(&volmeter->mutex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void obs_volmeter_detach_source(obs_volmeter_t *volmeter)
|
||||
{
|
||||
signal_handler_t *sh;
|
||||
|
||||
if (!volmeter)
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&volmeter->mutex);
|
||||
|
||||
if (!volmeter->source)
|
||||
goto exit;
|
||||
|
||||
sh = obs_source_get_signal_handler(volmeter->source);
|
||||
signal_handler_disconnect(sh, "volume",
|
||||
volmeter_source_volume_changed, volmeter);
|
||||
signal_handler_disconnect(sh, "destroy",
|
||||
volmeter_source_destroyed, volmeter);
|
||||
obs_source_remove_audio_capture_callback(volmeter->source,
|
||||
volmeter_source_data_received, volmeter);
|
||||
|
||||
volmeter->source = NULL;
|
||||
|
||||
exit:
|
||||
pthread_mutex_unlock(&volmeter->mutex);
|
||||
}
|
||||
|
||||
void obs_volmeter_set_update_interval(obs_volmeter_t *volmeter,
|
||||
const unsigned int ms)
|
||||
{
|
||||
if (!volmeter || !ms)
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&volmeter->mutex);
|
||||
volmeter->update_ms = ms;
|
||||
volmeter_update_audio_settings(volmeter);
|
||||
pthread_mutex_unlock(&volmeter->mutex);
|
||||
}
|
||||
|
||||
unsigned int obs_volmeter_get_update_interval(obs_volmeter_t *volmeter)
|
||||
{
|
||||
if (!volmeter)
|
||||
return 0;
|
||||
|
||||
pthread_mutex_lock(&volmeter->mutex);
|
||||
const unsigned int interval = volmeter->update_ms;
|
||||
pthread_mutex_unlock(&volmeter->mutex);
|
||||
|
||||
return interval;
|
||||
}
|
||||
|
||||
void obs_volmeter_set_peak_hold(obs_volmeter_t *volmeter, const unsigned int ms)
|
||||
{
|
||||
if (!volmeter)
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&volmeter->mutex);
|
||||
volmeter->peakhold_ms = ms;
|
||||
volmeter_update_audio_settings(volmeter);
|
||||
pthread_mutex_unlock(&volmeter->mutex);
|
||||
}
|
||||
|
||||
unsigned int obs_volmeter_get_peak_hold(obs_volmeter_t *volmeter)
|
||||
{
|
||||
if (!volmeter)
|
||||
return 0;
|
||||
|
||||
pthread_mutex_lock(&volmeter->mutex);
|
||||
const unsigned int peakhold = volmeter->peakhold_ms;
|
||||
pthread_mutex_unlock(&volmeter->mutex);
|
||||
|
||||
return peakhold;
|
||||
}
|
||||
|
||||
void obs_volmeter_add_callback(obs_volmeter_t *volmeter,
|
||||
obs_volmeter_updated_t callback, void *param)
|
||||
{
|
||||
struct meter_cb cb = {callback, param};
|
||||
|
||||
if (!obs_ptr_valid(volmeter, "obs_volmeter_add_callback"))
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&volmeter->callback_mutex);
|
||||
da_push_back(volmeter->callbacks, &cb);
|
||||
pthread_mutex_unlock(&volmeter->callback_mutex);
|
||||
}
|
||||
|
||||
void obs_volmeter_remove_callback(obs_volmeter_t *volmeter,
|
||||
obs_volmeter_updated_t callback, void *param)
|
||||
{
|
||||
struct meter_cb cb = {callback, param};
|
||||
|
||||
if (!obs_ptr_valid(volmeter, "obs_volmeter_remove_callback"))
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&volmeter->callback_mutex);
|
||||
da_erase_item(volmeter->callbacks, &cb);
|
||||
pthread_mutex_unlock(&volmeter->callback_mutex);
|
||||
}
|
||||
264
libobs/obs-audio-controls.h
Normal file
264
libobs/obs-audio-controls.h
Normal file
|
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Leonhard Oelke <leonhard@in-verted.de>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "obs.h"
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief header for audio controls
|
||||
*
|
||||
* @brief Audio controls for use in GUIs
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Fader types
|
||||
*/
|
||||
enum obs_fader_type {
|
||||
/**
|
||||
* @brief A simple cubic fader for controlling audio levels
|
||||
*
|
||||
* This is a very common type of software fader since it yields good
|
||||
* results while being quite performant.
|
||||
* The input value is mapped to mul values with the simple formula x^3.
|
||||
*/
|
||||
OBS_FADER_CUBIC,
|
||||
/**
|
||||
* @brief A fader compliant to IEC 60-268-18
|
||||
*
|
||||
* This type of fader has several segments with different slopes that
|
||||
* map deflection linearly to dB values. The segments are defined as
|
||||
* in the following table:
|
||||
*
|
||||
@code
|
||||
Deflection | Volume
|
||||
------------------------------------------
|
||||
[ 100 %, 75 % ] | [ 0 dB, -9 dB ]
|
||||
[ 75 %, 50 % ] | [ -9 dB, -20 dB ]
|
||||
[ 50 %, 30 % ] | [ -20 dB, -30 dB ]
|
||||
[ 30 %, 15 % ] | [ -30 dB, -40 dB ]
|
||||
[ 15 %, 7.5 % ] | [ -40 dB, -50 dB ]
|
||||
[ 7.5 %, 2.5 % ] | [ -50 dB, -60 dB ]
|
||||
[ 2.5 %, 0 % ] | [ -60 dB, -inf dB ]
|
||||
@endcode
|
||||
*/
|
||||
OBS_FADER_IEC,
|
||||
/**
|
||||
* @brief Logarithmic fader
|
||||
*/
|
||||
OBS_FADER_LOG
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Create a fader
|
||||
* @param type the type of the fader
|
||||
* @return pointer to the fader object
|
||||
*
|
||||
* A fader object is used to map input values from a gui element to dB and
|
||||
* subsequently multiplier values used by libobs to mix audio.
|
||||
* The current "position" of the fader is internally stored as dB value.
|
||||
*/
|
||||
EXPORT obs_fader_t *obs_fader_create(enum obs_fader_type type);
|
||||
|
||||
/**
|
||||
* @brief Destroy a fader
|
||||
* @param fader pointer to the fader object
|
||||
*
|
||||
* Destroy the fader and free all related data
|
||||
*/
|
||||
EXPORT void obs_fader_destroy(obs_fader_t *fader);
|
||||
|
||||
/**
|
||||
* @brief Set the fader dB value
|
||||
* @param fader pointer to the fader object
|
||||
* @param db new dB value
|
||||
* @return true if value was set without clamping
|
||||
*/
|
||||
EXPORT bool obs_fader_set_db(obs_fader_t *fader, const float db);
|
||||
|
||||
/**
|
||||
* @brief Get the current fader dB value
|
||||
* @param fader pointer to the fader object
|
||||
* @return current fader dB value
|
||||
*/
|
||||
EXPORT float obs_fader_get_db(obs_fader_t *fader);
|
||||
|
||||
/**
|
||||
* @brief Set the fader value from deflection
|
||||
* @param fader pointer to the fader object
|
||||
* @param def new deflection
|
||||
* @return true if value was set without clamping
|
||||
*
|
||||
* This sets the new fader value from the supplied deflection, in case the
|
||||
* resulting value was clamped due to limits this function will return false.
|
||||
* The deflection is typically in the range [0.0, 1.0] but may be higher in
|
||||
* order to provide some amplification. In order for this to work the high dB
|
||||
* limit has to be set.
|
||||
*/
|
||||
EXPORT bool obs_fader_set_deflection(obs_fader_t *fader, const float def);
|
||||
|
||||
/**
|
||||
* @brief Get the current fader deflection
|
||||
* @param fader pointer to the fader object
|
||||
* @return current fader deflection
|
||||
*/
|
||||
EXPORT float obs_fader_get_deflection(obs_fader_t *fader);
|
||||
|
||||
/**
|
||||
* @brief Set the fader value from multiplier
|
||||
* @param fader pointer to the fader object
|
||||
* @return true if the value was set without clamping
|
||||
*/
|
||||
EXPORT bool obs_fader_set_mul(obs_fader_t *fader, const float mul);
|
||||
|
||||
/**
|
||||
* @brief Get the current fader multiplier value
|
||||
* @param fader pointer to the fader object
|
||||
* @return current fader multiplier
|
||||
*/
|
||||
EXPORT float obs_fader_get_mul(obs_fader_t *fader);
|
||||
|
||||
/**
|
||||
* @brief Attach the fader to a source
|
||||
* @param fader pointer to the fader object
|
||||
* @param source pointer to the source object
|
||||
* @return true on success
|
||||
*
|
||||
* When the fader is attached to a source it will automatically sync it's state
|
||||
* to the volume of the source.
|
||||
*/
|
||||
EXPORT bool obs_fader_attach_source(obs_fader_t *fader, obs_source_t *source);
|
||||
|
||||
/**
|
||||
* @brief Detach the fader from the currently attached source
|
||||
* @param fader pointer to the fader object
|
||||
*/
|
||||
EXPORT void obs_fader_detach_source(obs_fader_t *fader);
|
||||
|
||||
typedef void (*obs_fader_changed_t)(void *param, float db);
|
||||
|
||||
EXPORT void obs_fader_add_callback(obs_fader_t *fader,
|
||||
obs_fader_changed_t callback, void *param);
|
||||
EXPORT void obs_fader_remove_callback(obs_fader_t *fader,
|
||||
obs_fader_changed_t callback, void *param);
|
||||
|
||||
/**
|
||||
* @brief Create a volume meter
|
||||
* @param type the mapping type to use for the volume meter
|
||||
* @return pointer to the volume meter object
|
||||
*
|
||||
* A volume meter object is used to prepare the sound levels reported by audio
|
||||
* sources for display in a GUI.
|
||||
* It will automatically take source volume into account and map the levels
|
||||
* to a range [0.0f, 1.0f].
|
||||
*/
|
||||
EXPORT obs_volmeter_t *obs_volmeter_create(enum obs_fader_type type);
|
||||
|
||||
/**
|
||||
* @brief Destroy a volume meter
|
||||
* @param volmeter pointer to the volmeter object
|
||||
*
|
||||
* Destroy the volume meter and free all related data
|
||||
*/
|
||||
EXPORT void obs_volmeter_destroy(obs_volmeter_t *volmeter);
|
||||
|
||||
/**
|
||||
* @brief Attach the volume meter to a source
|
||||
* @param volmeter pointer to the volume meter object
|
||||
* @param source pointer to the source object
|
||||
* @return true on success
|
||||
*
|
||||
* When the volume meter is attached to a source it will start to listen to
|
||||
* volume updates on the source and after preparing the data emit its own
|
||||
* signal.
|
||||
*/
|
||||
EXPORT bool obs_volmeter_attach_source(obs_volmeter_t *volmeter,
|
||||
obs_source_t *source);
|
||||
|
||||
/**
|
||||
* @brief Detach the volume meter from the currently attached source
|
||||
* @param volmeter pointer to the volume meter object
|
||||
*/
|
||||
EXPORT void obs_volmeter_detach_source(obs_volmeter_t *volmeter);
|
||||
|
||||
/**
|
||||
* @brief Get signal handler for the volume meter object
|
||||
* @param volmeter pointer to the volume meter object
|
||||
* @return signal handler
|
||||
*/
|
||||
EXPORT signal_handler_t *obs_volmeter_get_signal_handler(
|
||||
obs_volmeter_t *volmeter);
|
||||
|
||||
/**
|
||||
* @brief Set the update interval for the volume meter
|
||||
* @param volmeter pointer to the volume meter object
|
||||
* @param ms update interval in ms
|
||||
*
|
||||
* This sets the update interval in milliseconds that should be processed before
|
||||
* the resulting values are emitted by the levels_updated signal. The resulting
|
||||
* number of audio samples is rounded to an integer.
|
||||
*
|
||||
* Please note that due to way obs does receive audio data from the sources
|
||||
* this is no hard guarantee for the timing of the signal itself. When the
|
||||
* volume meter receives a chunk of data that is multiple the size of the sample
|
||||
* interval, all data will be sampled and the values updated accordingly, but
|
||||
* only the signal for the last segment is actually emitted.
|
||||
* On the other hand data might be received in a way that will cause the signal
|
||||
* to be emitted in shorter intervals than specified here under some
|
||||
* circumstances.
|
||||
*/
|
||||
EXPORT void obs_volmeter_set_update_interval(obs_volmeter_t *volmeter,
|
||||
const unsigned int ms);
|
||||
|
||||
/**
|
||||
* @brief Get the update interval currently used for the volume meter
|
||||
* @param volmeter pointer to the volume meter object
|
||||
* @return update interval in ms
|
||||
*/
|
||||
EXPORT unsigned int obs_volmeter_get_update_interval(obs_volmeter_t *volmeter);
|
||||
|
||||
/**
|
||||
* @brief Set the peak hold time for the volume meter
|
||||
* @param volmeter pointer to the volume meter object
|
||||
* @param ms peak hold time in ms
|
||||
*/
|
||||
EXPORT void obs_volmeter_set_peak_hold(obs_volmeter_t *volmeter,
|
||||
const unsigned int ms);
|
||||
|
||||
/**
|
||||
* @brief Get the peak hold time for the volume meter
|
||||
* @param volmeter pointer to the volume meter object
|
||||
* @return the peak hold time in ms
|
||||
*/
|
||||
EXPORT unsigned int obs_volmeter_get_peak_hold(obs_volmeter_t *volmeter);
|
||||
|
||||
typedef void (*obs_volmeter_updated_t)(void *param, float level,
|
||||
float magnitude, float peak, float muted);
|
||||
|
||||
EXPORT void obs_volmeter_add_callback(obs_volmeter_t *volmeter,
|
||||
obs_volmeter_updated_t callback, void *param);
|
||||
EXPORT void obs_volmeter_remove_callback(obs_volmeter_t *volmeter,
|
||||
obs_volmeter_updated_t callback, void *param);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
474
libobs/obs-audio.c
Normal file
474
libobs/obs-audio.c
Normal file
|
|
@ -0,0 +1,474 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2015 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "obs-internal.h"
|
||||
|
||||
struct ts_info {
|
||||
uint64_t start;
|
||||
uint64_t end;
|
||||
};
|
||||
|
||||
#define DEBUG_AUDIO 0
|
||||
#define MAX_BUFFERING_TICKS 45
|
||||
|
||||
static void push_audio_tree(obs_source_t *parent, obs_source_t *source, void *p)
|
||||
{
|
||||
struct obs_core_audio *audio = p;
|
||||
|
||||
if (da_find(audio->render_order, &source, 0) == DARRAY_INVALID) {
|
||||
obs_source_addref(source);
|
||||
da_push_back(audio->render_order, &source);
|
||||
}
|
||||
|
||||
UNUSED_PARAMETER(parent);
|
||||
}
|
||||
|
||||
static inline size_t convert_time_to_frames(size_t sample_rate, uint64_t t)
|
||||
{
|
||||
return (size_t)(t * (uint64_t)sample_rate / 1000000000ULL);
|
||||
}
|
||||
|
||||
static inline void mix_audio(struct audio_output_data *mixes,
|
||||
obs_source_t *source, size_t channels, size_t sample_rate,
|
||||
struct ts_info *ts)
|
||||
{
|
||||
size_t total_floats = AUDIO_OUTPUT_FRAMES;
|
||||
size_t start_point = 0;
|
||||
|
||||
if (source->audio_ts < ts->start || ts->end <= source->audio_ts)
|
||||
return;
|
||||
|
||||
if (source->audio_ts != ts->start) {
|
||||
start_point = convert_time_to_frames(sample_rate,
|
||||
source->audio_ts - ts->start);
|
||||
if (start_point == AUDIO_OUTPUT_FRAMES)
|
||||
return;
|
||||
|
||||
total_floats -= start_point;
|
||||
}
|
||||
|
||||
for (size_t mix_idx = 0; mix_idx < MAX_AUDIO_MIXES; mix_idx++) {
|
||||
for (size_t ch = 0; ch < channels; ch++) {
|
||||
register float *mix = mixes[mix_idx].data[ch];
|
||||
register float *aud =
|
||||
source->audio_output_buf[mix_idx][ch];
|
||||
register float *end;
|
||||
|
||||
mix += start_point;
|
||||
end = aud + total_floats;
|
||||
|
||||
while (aud < end)
|
||||
*(mix++) += *(aud++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ignore_audio(obs_source_t *source, size_t channels,
|
||||
size_t sample_rate)
|
||||
{
|
||||
size_t num_floats = source->audio_input_buf[0].size / sizeof(float);
|
||||
|
||||
if (num_floats) {
|
||||
for (size_t ch = 0; ch < channels; ch++)
|
||||
circlebuf_pop_front(&source->audio_input_buf[ch], NULL,
|
||||
source->audio_input_buf[ch].size);
|
||||
|
||||
source->last_audio_input_buf_size = 0;
|
||||
source->audio_ts += (uint64_t)num_floats * 1000000000ULL /
|
||||
(uint64_t)sample_rate;
|
||||
}
|
||||
}
|
||||
|
||||
static bool discard_if_stopped(obs_source_t *source, size_t channels)
|
||||
{
|
||||
size_t last_size;
|
||||
size_t size;
|
||||
|
||||
last_size = source->last_audio_input_buf_size;
|
||||
size = source->audio_input_buf[0].size;
|
||||
|
||||
if (!size)
|
||||
return false;
|
||||
|
||||
/* if perpetually pending data, it means the audio has stopped,
|
||||
* so clear the audio data */
|
||||
if (last_size == size) {
|
||||
for (size_t ch = 0; ch < channels; ch++)
|
||||
circlebuf_pop_front(&source->audio_input_buf[ch], NULL,
|
||||
source->audio_input_buf[ch].size);
|
||||
|
||||
source->audio_ts = 0;
|
||||
source->last_audio_input_buf_size = 0;
|
||||
#if DEBUG_AUDIO == 1
|
||||
blog(LOG_DEBUG, "source audio data appears to have "
|
||||
"stopped, clearing");
|
||||
#endif
|
||||
return true;
|
||||
} else {
|
||||
source->last_audio_input_buf_size = size;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#define MAX_AUDIO_SIZE (AUDIO_OUTPUT_FRAMES * sizeof(float))
|
||||
|
||||
static inline void discard_audio(struct obs_core_audio *audio,
|
||||
obs_source_t *source, size_t channels, size_t sample_rate,
|
||||
struct ts_info *ts)
|
||||
{
|
||||
size_t total_floats = AUDIO_OUTPUT_FRAMES;
|
||||
size_t size;
|
||||
|
||||
#if DEBUG_AUDIO == 1
|
||||
bool is_audio_source = source->info.output_flags & OBS_SOURCE_AUDIO;
|
||||
#endif
|
||||
|
||||
if (source->info.audio_render) {
|
||||
source->audio_ts = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ts->end <= source->audio_ts) {
|
||||
#if DEBUG_AUDIO == 1
|
||||
blog(LOG_DEBUG, "can't discard, source "
|
||||
"timestamp (%"PRIu64") >= "
|
||||
"end timestamp (%"PRIu64")",
|
||||
source->audio_ts, ts->end);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (source->audio_ts < (ts->start - 1)) {
|
||||
if (source->audio_pending &&
|
||||
source->audio_input_buf[0].size < MAX_AUDIO_SIZE &&
|
||||
discard_if_stopped(source, channels))
|
||||
return;
|
||||
|
||||
#if DEBUG_AUDIO == 1
|
||||
if (is_audio_source) {
|
||||
blog(LOG_DEBUG, "can't discard, source "
|
||||
"timestamp (%"PRIu64") < "
|
||||
"start timestamp (%"PRIu64")",
|
||||
source->audio_ts, ts->start);
|
||||
}
|
||||
#endif
|
||||
if (audio->total_buffering_ticks == MAX_BUFFERING_TICKS)
|
||||
ignore_audio(source, channels, sample_rate);
|
||||
return;
|
||||
}
|
||||
|
||||
if (source->audio_ts != ts->start &&
|
||||
source->audio_ts != (ts->start - 1)) {
|
||||
size_t start_point = convert_time_to_frames(sample_rate,
|
||||
source->audio_ts - ts->start);
|
||||
if (start_point == AUDIO_OUTPUT_FRAMES) {
|
||||
#if DEBUG_AUDIO == 1
|
||||
if (is_audio_source)
|
||||
blog(LOG_DEBUG, "can't dicard, start point is "
|
||||
"at audio frame count");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
total_floats -= start_point;
|
||||
}
|
||||
|
||||
size = total_floats * sizeof(float);
|
||||
|
||||
if (source->audio_input_buf[0].size < size) {
|
||||
if (discard_if_stopped(source, channels))
|
||||
return;
|
||||
|
||||
#if DEBUG_AUDIO == 1
|
||||
if (is_audio_source)
|
||||
blog(LOG_DEBUG, "can't discard, data still pending");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t ch = 0; ch < channels; ch++)
|
||||
circlebuf_pop_front(&source->audio_input_buf[ch], NULL, size);
|
||||
|
||||
source->last_audio_input_buf_size = 0;
|
||||
|
||||
#if DEBUG_AUDIO == 1
|
||||
if (is_audio_source)
|
||||
blog(LOG_DEBUG, "audio discarded, new ts: %"PRIu64,
|
||||
ts->end);
|
||||
#endif
|
||||
|
||||
source->audio_ts = ts->end;
|
||||
}
|
||||
|
||||
static void add_audio_buffering(struct obs_core_audio *audio,
|
||||
size_t sample_rate, struct ts_info *ts, uint64_t min_ts)
|
||||
{
|
||||
struct ts_info new_ts;
|
||||
uint64_t offset;
|
||||
uint64_t frames;
|
||||
size_t total_ms;
|
||||
size_t ms;
|
||||
int ticks;
|
||||
|
||||
if (audio->total_buffering_ticks == MAX_BUFFERING_TICKS)
|
||||
return;
|
||||
|
||||
if (!audio->buffering_wait_ticks)
|
||||
audio->buffered_ts = ts->start;
|
||||
|
||||
offset = ts->start - min_ts;
|
||||
frames = ns_to_audio_frames(sample_rate, offset);
|
||||
ticks = (int)((frames + AUDIO_OUTPUT_FRAMES - 1) / AUDIO_OUTPUT_FRAMES);
|
||||
|
||||
audio->total_buffering_ticks += ticks;
|
||||
|
||||
if (audio->total_buffering_ticks >= MAX_BUFFERING_TICKS) {
|
||||
ticks -= audio->total_buffering_ticks - MAX_BUFFERING_TICKS;
|
||||
audio->total_buffering_ticks = MAX_BUFFERING_TICKS;
|
||||
blog(LOG_WARNING, "Max audio buffering reached!");
|
||||
}
|
||||
|
||||
ms = ticks * AUDIO_OUTPUT_FRAMES * 1000 / sample_rate;
|
||||
total_ms = audio->total_buffering_ticks * AUDIO_OUTPUT_FRAMES * 1000 /
|
||||
sample_rate;
|
||||
|
||||
blog(LOG_INFO, "adding %d milliseconds of audio buffering, total "
|
||||
"audio buffering is now %d milliseconds",
|
||||
(int)ms, (int)total_ms);
|
||||
#if DEBUG_AUDIO == 1
|
||||
blog(LOG_DEBUG, "min_ts (%"PRIu64") < start timestamp "
|
||||
"(%"PRIu64")", min_ts, ts->start);
|
||||
blog(LOG_DEBUG, "old buffered ts: %"PRIu64"-%"PRIu64,
|
||||
ts->start, ts->end);
|
||||
#endif
|
||||
|
||||
new_ts.start = audio->buffered_ts - audio_frames_to_ns(sample_rate,
|
||||
audio->buffering_wait_ticks * AUDIO_OUTPUT_FRAMES);
|
||||
|
||||
while (ticks--) {
|
||||
int cur_ticks = ++audio->buffering_wait_ticks;
|
||||
|
||||
new_ts.end = new_ts.start;
|
||||
new_ts.start = audio->buffered_ts - audio_frames_to_ns(
|
||||
sample_rate,
|
||||
cur_ticks * AUDIO_OUTPUT_FRAMES);
|
||||
|
||||
#if DEBUG_AUDIO == 1
|
||||
blog(LOG_DEBUG, "add buffered ts: %"PRIu64"-%"PRIu64,
|
||||
new_ts.start, new_ts.end);
|
||||
#endif
|
||||
|
||||
circlebuf_push_front(&audio->buffered_timestamps, &new_ts,
|
||||
sizeof(new_ts));
|
||||
}
|
||||
|
||||
*ts = new_ts;
|
||||
}
|
||||
|
||||
static bool audio_buffer_insuffient(struct obs_source *source,
|
||||
size_t sample_rate, uint64_t min_ts)
|
||||
{
|
||||
size_t total_floats = AUDIO_OUTPUT_FRAMES;
|
||||
size_t size;
|
||||
|
||||
if (source->info.audio_render || source->audio_pending ||
|
||||
!source->audio_ts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (source->audio_ts != min_ts &&
|
||||
source->audio_ts != (min_ts - 1)) {
|
||||
size_t start_point = convert_time_to_frames(sample_rate,
|
||||
source->audio_ts - min_ts);
|
||||
if (start_point >= AUDIO_OUTPUT_FRAMES)
|
||||
return false;
|
||||
|
||||
total_floats -= start_point;
|
||||
}
|
||||
|
||||
size = total_floats * sizeof(float);
|
||||
|
||||
if (source->audio_input_buf[0].size < size) {
|
||||
source->audio_pending = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void find_min_ts(struct obs_core_data *data,
|
||||
uint64_t *min_ts)
|
||||
{
|
||||
struct obs_source *source = data->first_audio_source;
|
||||
while (source) {
|
||||
if (!source->audio_pending && source->audio_ts &&
|
||||
source->audio_ts < *min_ts)
|
||||
*min_ts = source->audio_ts;
|
||||
|
||||
source = (struct obs_source*)source->next_audio_source;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool mark_invalid_sources(struct obs_core_data *data,
|
||||
size_t sample_rate, uint64_t min_ts)
|
||||
{
|
||||
bool recalculate = false;
|
||||
|
||||
struct obs_source *source = data->first_audio_source;
|
||||
while (source) {
|
||||
recalculate |= audio_buffer_insuffient(source, sample_rate,
|
||||
min_ts);
|
||||
source = (struct obs_source*)source->next_audio_source;
|
||||
}
|
||||
|
||||
return recalculate;
|
||||
}
|
||||
|
||||
static inline void calc_min_ts(struct obs_core_data *data,
|
||||
size_t sample_rate, uint64_t *min_ts)
|
||||
{
|
||||
find_min_ts(data, min_ts);
|
||||
if (mark_invalid_sources(data, sample_rate, *min_ts))
|
||||
find_min_ts(data, min_ts);
|
||||
}
|
||||
|
||||
static inline void release_audio_sources(struct obs_core_audio *audio)
|
||||
{
|
||||
for (size_t i = 0; i < audio->render_order.num; i++)
|
||||
obs_source_release(audio->render_order.array[i]);
|
||||
}
|
||||
|
||||
bool audio_callback(void *param,
|
||||
uint64_t start_ts_in, uint64_t end_ts_in, uint64_t *out_ts,
|
||||
uint32_t mixers, struct audio_output_data *mixes)
|
||||
{
|
||||
struct obs_core_data *data = &obs->data;
|
||||
struct obs_core_audio *audio = &obs->audio;
|
||||
struct obs_source *source;
|
||||
size_t sample_rate = audio_output_get_sample_rate(audio->audio);
|
||||
size_t channels = audio_output_get_channels(audio->audio);
|
||||
struct ts_info ts = {start_ts_in, end_ts_in};
|
||||
size_t audio_size;
|
||||
uint64_t min_ts;
|
||||
|
||||
da_resize(audio->render_order, 0);
|
||||
da_resize(audio->root_nodes, 0);
|
||||
|
||||
circlebuf_push_back(&audio->buffered_timestamps, &ts, sizeof(ts));
|
||||
circlebuf_peek_front(&audio->buffered_timestamps, &ts, sizeof(ts));
|
||||
min_ts = ts.start;
|
||||
|
||||
audio_size = AUDIO_OUTPUT_FRAMES * sizeof(float);
|
||||
|
||||
#if DEBUG_AUDIO == 1
|
||||
blog(LOG_DEBUG, "ts %llu-%llu", ts.start, ts.end);
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------ */
|
||||
/* build audio render order
|
||||
* NOTE: these are source channels, not audio channels */
|
||||
for (uint32_t i = 0; i < MAX_CHANNELS; i++) {
|
||||
obs_source_t *source = obs_get_output_source(i);
|
||||
if (source) {
|
||||
obs_source_enum_active_tree(source, push_audio_tree,
|
||||
audio);
|
||||
push_audio_tree(NULL, source, audio);
|
||||
da_push_back(audio->root_nodes, &source);
|
||||
obs_source_release(source);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&data->audio_sources_mutex);
|
||||
|
||||
source = data->first_audio_source;
|
||||
while (source) {
|
||||
push_audio_tree(NULL, source, audio);
|
||||
source = (struct obs_source*)source->next_audio_source;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&data->audio_sources_mutex);
|
||||
|
||||
/* ------------------------------------------------ */
|
||||
/* render audio data */
|
||||
for (size_t i = 0; i < audio->render_order.num; i++) {
|
||||
obs_source_t *source = audio->render_order.array[i];
|
||||
obs_source_audio_render(source, mixers, channels, sample_rate,
|
||||
audio_size);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------ */
|
||||
/* get minimum audio timestamp */
|
||||
pthread_mutex_lock(&data->audio_sources_mutex);
|
||||
calc_min_ts(data, sample_rate, &min_ts);
|
||||
pthread_mutex_unlock(&data->audio_sources_mutex);
|
||||
|
||||
/* ------------------------------------------------ */
|
||||
/* if a source has gone backward in time, buffer */
|
||||
if (min_ts < ts.start)
|
||||
add_audio_buffering(audio, sample_rate, &ts, min_ts);
|
||||
|
||||
/* ------------------------------------------------ */
|
||||
/* mix audio */
|
||||
if (!audio->buffering_wait_ticks) {
|
||||
for (size_t i = 0; i < audio->root_nodes.num; i++) {
|
||||
obs_source_t *source = audio->root_nodes.array[i];
|
||||
|
||||
if (source->audio_pending)
|
||||
continue;
|
||||
|
||||
pthread_mutex_lock(&source->audio_buf_mutex);
|
||||
|
||||
if (source->audio_output_buf[0][0] && source->audio_ts)
|
||||
mix_audio(mixes, source, channels, sample_rate,
|
||||
&ts);
|
||||
|
||||
pthread_mutex_unlock(&source->audio_buf_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------ */
|
||||
/* discard audio */
|
||||
pthread_mutex_lock(&data->audio_sources_mutex);
|
||||
|
||||
source = data->first_audio_source;
|
||||
while (source) {
|
||||
pthread_mutex_lock(&source->audio_buf_mutex);
|
||||
discard_audio(audio, source, channels, sample_rate, &ts);
|
||||
pthread_mutex_unlock(&source->audio_buf_mutex);
|
||||
|
||||
source = (struct obs_source*)source->next_audio_source;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&data->audio_sources_mutex);
|
||||
|
||||
/* ------------------------------------------------ */
|
||||
/* release audio sources */
|
||||
release_audio_sources(audio);
|
||||
|
||||
circlebuf_pop_front(&audio->buffered_timestamps, NULL, sizeof(ts));
|
||||
|
||||
*out_ts = ts.start;
|
||||
|
||||
if (audio->buffering_wait_ticks) {
|
||||
audio->buffering_wait_ticks--;
|
||||
return false;
|
||||
}
|
||||
|
||||
UNUSED_PARAMETER(param);
|
||||
return true;
|
||||
}
|
||||
223
libobs/obs-avc.c
Normal file
223
libobs/obs-avc.c
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2014 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "obs.h"
|
||||
#include "obs-avc.h"
|
||||
#include "util/array-serializer.h"
|
||||
|
||||
bool obs_avc_keyframe(const uint8_t *data, size_t size)
|
||||
{
|
||||
const uint8_t *nal_start, *nal_end;
|
||||
const uint8_t *end = data + size;
|
||||
int type;
|
||||
|
||||
nal_start = obs_avc_find_startcode(data, end);
|
||||
while (true) {
|
||||
while (nal_start < end && !*(nal_start++));
|
||||
|
||||
if (nal_start == end)
|
||||
break;
|
||||
|
||||
type = nal_start[0] & 0x1F;
|
||||
|
||||
if (type == OBS_NAL_SLICE_IDR || type == OBS_NAL_SLICE)
|
||||
return (type == OBS_NAL_SLICE_IDR);
|
||||
|
||||
nal_end = obs_avc_find_startcode(nal_start, end);
|
||||
nal_start = nal_end;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* NOTE: I noticed that FFmpeg does some unusual special handling of certain
|
||||
* scenarios that I was unaware of, so instead of just searching for {0, 0, 1}
|
||||
* we'll just use the code from FFmpeg - http://www.ffmpeg.org/ */
|
||||
static const uint8_t *ff_avc_find_startcode_internal(const uint8_t *p,
|
||||
const uint8_t *end)
|
||||
{
|
||||
const uint8_t *a = p + 4 - ((intptr_t)p & 3);
|
||||
|
||||
for (end -= 3; p < a && p < end; p++) {
|
||||
if (p[0] == 0 && p[1] == 0 && p[2] == 1)
|
||||
return p;
|
||||
}
|
||||
|
||||
for (end -= 3; p < end; p += 4) {
|
||||
uint32_t x = *(const uint32_t*)p;
|
||||
|
||||
if ((x - 0x01010101) & (~x) & 0x80808080) {
|
||||
if (p[1] == 0) {
|
||||
if (p[0] == 0 && p[2] == 1)
|
||||
return p;
|
||||
if (p[2] == 0 && p[3] == 1)
|
||||
return p+1;
|
||||
}
|
||||
|
||||
if (p[3] == 0) {
|
||||
if (p[2] == 0 && p[4] == 1)
|
||||
return p+2;
|
||||
if (p[4] == 0 && p[5] == 1)
|
||||
return p+3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (end += 3; p < end; p++) {
|
||||
if (p[0] == 0 && p[1] == 0 && p[2] == 1)
|
||||
return p;
|
||||
}
|
||||
|
||||
return end + 3;
|
||||
}
|
||||
|
||||
const uint8_t *obs_avc_find_startcode(const uint8_t *p, const uint8_t *end)
|
||||
{
|
||||
const uint8_t *out= ff_avc_find_startcode_internal(p, end);
|
||||
if (p < out && out < end && !out[-1]) out--;
|
||||
return out;
|
||||
}
|
||||
|
||||
static inline int get_drop_priority(int priority)
|
||||
{
|
||||
switch (priority) {
|
||||
case OBS_NAL_PRIORITY_DISPOSABLE: return OBS_NAL_PRIORITY_DISPOSABLE;
|
||||
case OBS_NAL_PRIORITY_LOW: return OBS_NAL_PRIORITY_LOW;
|
||||
}
|
||||
|
||||
return OBS_NAL_PRIORITY_HIGHEST;
|
||||
}
|
||||
|
||||
static void serialize_avc_data(struct serializer *s, const uint8_t *data,
|
||||
size_t size, bool *is_keyframe, int *priority)
|
||||
{
|
||||
const uint8_t *nal_start, *nal_end;
|
||||
const uint8_t *end = data+size;
|
||||
int type;
|
||||
|
||||
nal_start = obs_avc_find_startcode(data, end);
|
||||
while (true) {
|
||||
while (nal_start < end && !*(nal_start++));
|
||||
|
||||
if (nal_start == end)
|
||||
break;
|
||||
|
||||
type = nal_start[0] & 0x1F;
|
||||
|
||||
if (type == OBS_NAL_SLICE_IDR || type == OBS_NAL_SLICE) {
|
||||
if (is_keyframe)
|
||||
*is_keyframe = (type == OBS_NAL_SLICE_IDR);
|
||||
if (priority)
|
||||
*priority = nal_start[0] >> 5;
|
||||
}
|
||||
|
||||
nal_end = obs_avc_find_startcode(nal_start, end);
|
||||
s_wb32(s, (uint32_t)(nal_end - nal_start));
|
||||
s_write(s, nal_start, nal_end - nal_start);
|
||||
nal_start = nal_end;
|
||||
}
|
||||
}
|
||||
|
||||
void obs_parse_avc_packet(struct encoder_packet *avc_packet,
|
||||
const struct encoder_packet *src)
|
||||
{
|
||||
struct array_output_data output;
|
||||
struct serializer s;
|
||||
|
||||
array_output_serializer_init(&s, &output);
|
||||
*avc_packet = *src;
|
||||
|
||||
serialize_avc_data(&s, src->data, src->size, &avc_packet->keyframe,
|
||||
&avc_packet->priority);
|
||||
|
||||
avc_packet->data = output.bytes.array;
|
||||
avc_packet->size = output.bytes.num;
|
||||
avc_packet->drop_priority = get_drop_priority(avc_packet->priority);
|
||||
}
|
||||
|
||||
static inline bool has_start_code(const uint8_t *data)
|
||||
{
|
||||
if (data[0] != 0 || data[1] != 0)
|
||||
return false;
|
||||
|
||||
return data[2] == 1 || (data[2] == 0 && data[3] == 1);
|
||||
}
|
||||
|
||||
static void get_sps_pps(const uint8_t *data, size_t size,
|
||||
const uint8_t **sps, size_t *sps_size,
|
||||
const uint8_t **pps, size_t *pps_size)
|
||||
{
|
||||
const uint8_t *nal_start, *nal_end;
|
||||
const uint8_t *end = data+size;
|
||||
int type;
|
||||
|
||||
nal_start = obs_avc_find_startcode(data, end);
|
||||
while (true) {
|
||||
while (nal_start < end && !*(nal_start++));
|
||||
|
||||
if (nal_start == end)
|
||||
break;
|
||||
|
||||
nal_end = obs_avc_find_startcode(nal_start, end);
|
||||
|
||||
type = nal_start[0] & 0x1F;
|
||||
if (type == OBS_NAL_SPS) {
|
||||
*sps = nal_start;
|
||||
*sps_size = nal_end - nal_start;
|
||||
} else if (type == OBS_NAL_PPS) {
|
||||
*pps = nal_start;
|
||||
*pps_size = nal_end - nal_start;
|
||||
}
|
||||
|
||||
nal_start = nal_end;
|
||||
}
|
||||
}
|
||||
|
||||
size_t obs_parse_avc_header(uint8_t **header, const uint8_t *data, size_t size)
|
||||
{
|
||||
struct array_output_data output;
|
||||
struct serializer s;
|
||||
const uint8_t *sps = NULL, *pps = NULL;
|
||||
size_t sps_size = 0, pps_size = 0;
|
||||
|
||||
array_output_serializer_init(&s, &output);
|
||||
|
||||
if (size <= 6) return 0;
|
||||
|
||||
if (!has_start_code(data)) {
|
||||
*header = bmemdup(data, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
get_sps_pps(data, size, &sps, &sps_size, &pps, &pps_size);
|
||||
if (!sps || !pps || sps_size < 4)
|
||||
return 0;
|
||||
|
||||
s_w8(&s, 0x01);
|
||||
s_write(&s, sps+1, 3);
|
||||
s_w8(&s, 0xff);
|
||||
s_w8(&s, 0xe1);
|
||||
|
||||
s_wb16(&s, (uint16_t)sps_size);
|
||||
s_write(&s, sps, sps_size);
|
||||
s_w8(&s, 0x01);
|
||||
s_wb16(&s, (uint16_t)pps_size);
|
||||
s_write(&s, pps, pps_size);
|
||||
|
||||
*header = output.bytes.array;
|
||||
return output.bytes.num;
|
||||
}
|
||||
61
libobs/obs-avc.h
Normal file
61
libobs/obs-avc.h
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2014 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/c99defs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct encoder_packet;
|
||||
|
||||
enum {
|
||||
OBS_NAL_UNKNOWN = 0,
|
||||
OBS_NAL_SLICE = 1,
|
||||
OBS_NAL_SLICE_DPA = 2,
|
||||
OBS_NAL_SLICE_DPB = 3,
|
||||
OBS_NAL_SLICE_DPC = 4,
|
||||
OBS_NAL_SLICE_IDR = 5,
|
||||
OBS_NAL_SEI = 6,
|
||||
OBS_NAL_SPS = 7,
|
||||
OBS_NAL_PPS = 8,
|
||||
OBS_NAL_AUD = 9,
|
||||
OBS_NAL_FILLER = 12,
|
||||
};
|
||||
|
||||
enum {
|
||||
OBS_NAL_PRIORITY_DISPOSABLE = 0,
|
||||
OBS_NAL_PRIORITY_LOW = 1,
|
||||
OBS_NAL_PRIORITY_HIGH = 2,
|
||||
OBS_NAL_PRIORITY_HIGHEST = 3,
|
||||
};
|
||||
|
||||
/* Helpers for parsing AVC NAL units. */
|
||||
|
||||
EXPORT bool obs_avc_keyframe(const uint8_t *data, size_t size);
|
||||
EXPORT const uint8_t *obs_avc_find_startcode(const uint8_t *p,
|
||||
const uint8_t *end);
|
||||
EXPORT void obs_parse_avc_packet(struct encoder_packet *avc_packet,
|
||||
const struct encoder_packet *src);
|
||||
EXPORT size_t obs_parse_avc_header(uint8_t **header, const uint8_t *data,
|
||||
size_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
1342
libobs/obs-cocoa.c
Normal file
1342
libobs/obs-cocoa.c
Normal file
File diff suppressed because it is too large
Load diff
66
libobs/obs-config.h
Normal file
66
libobs/obs-config.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2014 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* LIBOBS_API_VER is returned by module_version in each module.
|
||||
*
|
||||
* Libobs uses semantic versioning. See http://semver.org/ for more
|
||||
* information.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Increment if major breaking API changes
|
||||
*/
|
||||
#define LIBOBS_API_MAJOR_VER 0 /* 0 means development, anything can break */
|
||||
|
||||
/*
|
||||
* Increment if backward-compatible additions
|
||||
*
|
||||
* Reset to zero each major version
|
||||
*/
|
||||
#define LIBOBS_API_MINOR_VER 13
|
||||
|
||||
/*
|
||||
* Increment if backward-compatible bug fix
|
||||
*
|
||||
* Reset to zero each major or minor version
|
||||
*/
|
||||
#define LIBOBS_API_PATCH_VER 2
|
||||
|
||||
#define MAKE_SEMANTIC_VERSION(major, minor, patch) \
|
||||
((major << 24) | \
|
||||
(minor << 16) | \
|
||||
patch )
|
||||
|
||||
#define LIBOBS_API_VER \
|
||||
MAKE_SEMANTIC_VERSION(LIBOBS_API_MAJOR_VER, \
|
||||
LIBOBS_API_MINOR_VER, \
|
||||
LIBOBS_API_PATCH_VER)
|
||||
|
||||
#ifdef HAVE_OBSCONFIG_H
|
||||
# include "obsconfig.h"
|
||||
#else
|
||||
# define OBS_VERSION "unknown"
|
||||
# define OBS_DATA_PATH "../../data"
|
||||
# define OBS_INSTALL_PREFIX ""
|
||||
# define OBS_PLUGIN_DESTINATION "obs-plugins"
|
||||
# define OBS_RELATIVE_PREFIX "../../"
|
||||
#endif
|
||||
|
||||
#define OBS_INSTALL_DATA_PATH OBS_INSTALL_PREFIX OBS_DATA_PATH
|
||||
2152
libobs/obs-data.c
Normal file
2152
libobs/obs-data.c
Normal file
File diff suppressed because it is too large
Load diff
366
libobs/obs-data.h
Normal file
366
libobs/obs-data.h
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2014 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/c99defs.h"
|
||||
#include "media-io/frame-rate.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct vec2;
|
||||
struct vec3;
|
||||
struct vec4;
|
||||
struct quat;
|
||||
|
||||
/*
|
||||
* OBS data settings storage
|
||||
*
|
||||
* This is used for retrieving or setting the data settings for things such
|
||||
* as sources, encoders, etc. This is designed for JSON serialization.
|
||||
*/
|
||||
|
||||
struct obs_data;
|
||||
struct obs_data_item;
|
||||
struct obs_data_array;
|
||||
typedef struct obs_data obs_data_t;
|
||||
typedef struct obs_data_item obs_data_item_t;
|
||||
typedef struct obs_data_array obs_data_array_t;
|
||||
|
||||
enum obs_data_type {
|
||||
OBS_DATA_NULL,
|
||||
OBS_DATA_STRING,
|
||||
OBS_DATA_NUMBER,
|
||||
OBS_DATA_BOOLEAN,
|
||||
OBS_DATA_OBJECT,
|
||||
OBS_DATA_ARRAY
|
||||
};
|
||||
|
||||
enum obs_data_number_type {
|
||||
OBS_DATA_NUM_INVALID,
|
||||
OBS_DATA_NUM_INT,
|
||||
OBS_DATA_NUM_DOUBLE
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Main usage functions */
|
||||
|
||||
EXPORT obs_data_t *obs_data_create();
|
||||
EXPORT obs_data_t *obs_data_create_from_json(const char *json_string);
|
||||
EXPORT obs_data_t *obs_data_create_from_json_file(const char *json_file);
|
||||
EXPORT obs_data_t *obs_data_create_from_json_file_safe(const char *json_file,
|
||||
const char *backup_ext);
|
||||
EXPORT void obs_data_addref(obs_data_t *data);
|
||||
EXPORT void obs_data_release(obs_data_t *data);
|
||||
|
||||
EXPORT const char *obs_data_get_json(obs_data_t *data);
|
||||
EXPORT bool obs_data_save_json(obs_data_t *data, const char *file);
|
||||
EXPORT bool obs_data_save_json_safe(obs_data_t *data, const char *file,
|
||||
const char *temp_ext, const char *backup_ext);
|
||||
|
||||
EXPORT void obs_data_apply(obs_data_t *target, obs_data_t *apply_data);
|
||||
|
||||
EXPORT void obs_data_erase(obs_data_t *data, const char *name);
|
||||
EXPORT void obs_data_clear(obs_data_t *data);
|
||||
|
||||
/* Set functions */
|
||||
EXPORT void obs_data_set_string(obs_data_t *data, const char *name,
|
||||
const char *val);
|
||||
EXPORT void obs_data_set_int(obs_data_t *data, const char *name,
|
||||
long long val);
|
||||
EXPORT void obs_data_set_double(obs_data_t *data, const char *name, double val);
|
||||
EXPORT void obs_data_set_bool(obs_data_t *data, const char *name, bool val);
|
||||
EXPORT void obs_data_set_obj(obs_data_t *data, const char *name, obs_data_t *obj);
|
||||
EXPORT void obs_data_set_array(obs_data_t *data, const char *name,
|
||||
obs_data_array_t *array);
|
||||
|
||||
/*
|
||||
* Default value functions.
|
||||
*/
|
||||
EXPORT void obs_data_set_default_string(obs_data_t *data, const char *name,
|
||||
const char *val);
|
||||
EXPORT void obs_data_set_default_int(obs_data_t *data, const char *name,
|
||||
long long val);
|
||||
EXPORT void obs_data_set_default_double(obs_data_t *data, const char *name,
|
||||
double val);
|
||||
EXPORT void obs_data_set_default_bool(obs_data_t *data, const char *name,
|
||||
bool val);
|
||||
EXPORT void obs_data_set_default_obj(obs_data_t *data, const char *name,
|
||||
obs_data_t *obj);
|
||||
|
||||
/*
|
||||
* Application overrides
|
||||
* Use these to communicate the actual values of settings in case the user
|
||||
* settings aren't appropriate
|
||||
*/
|
||||
EXPORT void obs_data_set_autoselect_string(obs_data_t *data, const char *name,
|
||||
const char *val);
|
||||
EXPORT void obs_data_set_autoselect_int(obs_data_t *data, const char *name,
|
||||
long long val);
|
||||
EXPORT void obs_data_set_autoselect_double(obs_data_t *data, const char *name,
|
||||
double val);
|
||||
EXPORT void obs_data_set_autoselect_bool(obs_data_t *data, const char *name,
|
||||
bool val);
|
||||
EXPORT void obs_data_set_autoselect_obj(obs_data_t *data, const char *name,
|
||||
obs_data_t *obj);
|
||||
|
||||
/*
|
||||
* Get functions
|
||||
*/
|
||||
EXPORT const char *obs_data_get_string(obs_data_t *data, const char *name);
|
||||
EXPORT long long obs_data_get_int(obs_data_t *data, const char *name);
|
||||
EXPORT double obs_data_get_double(obs_data_t *data, const char *name);
|
||||
EXPORT bool obs_data_get_bool(obs_data_t *data, const char *name);
|
||||
EXPORT obs_data_t *obs_data_get_obj(obs_data_t *data, const char *name);
|
||||
EXPORT obs_data_array_t *obs_data_get_array(obs_data_t *data, const char *name);
|
||||
|
||||
EXPORT const char *obs_data_get_default_string(obs_data_t *data,
|
||||
const char *name);
|
||||
EXPORT long long obs_data_get_default_int(obs_data_t *data, const char *name);
|
||||
EXPORT double obs_data_get_default_double(obs_data_t *data, const char *name);
|
||||
EXPORT bool obs_data_get_default_bool(obs_data_t *data, const char *name);
|
||||
EXPORT obs_data_t *obs_data_get_default_obj(obs_data_t *data, const char *name);
|
||||
EXPORT obs_data_array_t *obs_data_get_default_array(obs_data_t *data,
|
||||
const char *name);
|
||||
|
||||
EXPORT const char *obs_data_get_autoselect_string(obs_data_t *data,
|
||||
const char *name);
|
||||
EXPORT long long obs_data_get_autoselect_int(obs_data_t *data, const char *name);
|
||||
EXPORT double obs_data_get_autoselect_double(obs_data_t *data, const char *name);
|
||||
EXPORT bool obs_data_get_autoselect_bool(obs_data_t *data, const char *name);
|
||||
EXPORT obs_data_t *obs_data_get_autoselect_obj(obs_data_t *data,
|
||||
const char *name);
|
||||
EXPORT obs_data_array_t *obs_data_get_autoselect_array(obs_data_t *data,
|
||||
const char *name);
|
||||
|
||||
/* Array functions */
|
||||
EXPORT obs_data_array_t *obs_data_array_create();
|
||||
EXPORT void obs_data_array_addref(obs_data_array_t *array);
|
||||
EXPORT void obs_data_array_release(obs_data_array_t *array);
|
||||
|
||||
EXPORT size_t obs_data_array_count(obs_data_array_t *array);
|
||||
EXPORT obs_data_t *obs_data_array_item(obs_data_array_t *array, size_t idx);
|
||||
EXPORT size_t obs_data_array_push_back(obs_data_array_t *array, obs_data_t *obj);
|
||||
EXPORT void obs_data_array_insert(obs_data_array_t *array, size_t idx,
|
||||
obs_data_t *obj);
|
||||
EXPORT void obs_data_array_erase(obs_data_array_t *array, size_t idx);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Item status inspection */
|
||||
|
||||
EXPORT bool obs_data_has_user_value(obs_data_t *data, const char *name);
|
||||
EXPORT bool obs_data_has_default_value(obs_data_t *data, const char *name);
|
||||
EXPORT bool obs_data_has_autoselect_value(obs_data_t *data, const char *name);
|
||||
|
||||
EXPORT bool obs_data_item_has_user_value(obs_data_item_t *data);
|
||||
EXPORT bool obs_data_item_has_default_value(obs_data_item_t *data);
|
||||
EXPORT bool obs_data_item_has_autoselect_value(obs_data_item_t *data);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Clearing data values */
|
||||
|
||||
EXPORT void obs_data_unset_user_value(obs_data_t *data, const char *name);
|
||||
EXPORT void obs_data_unset_default_value(obs_data_t *data, const char *name);
|
||||
EXPORT void obs_data_unset_autoselect_value(obs_data_t *data, const char *name);
|
||||
|
||||
EXPORT void obs_data_item_unset_user_value(obs_data_item_t *data);
|
||||
EXPORT void obs_data_item_unset_default_value(obs_data_item_t *data);
|
||||
EXPORT void obs_data_item_unset_autoselect_value(obs_data_item_t *data);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Item iteration */
|
||||
|
||||
EXPORT obs_data_item_t *obs_data_first(obs_data_t *data);
|
||||
EXPORT obs_data_item_t *obs_data_item_byname(obs_data_t *data, const char *name);
|
||||
EXPORT bool obs_data_item_next(obs_data_item_t **item);
|
||||
EXPORT void obs_data_item_release(obs_data_item_t **item);
|
||||
EXPORT void obs_data_item_remove(obs_data_item_t **item);
|
||||
|
||||
/* Gets Item type */
|
||||
EXPORT enum obs_data_type obs_data_item_gettype(obs_data_item_t *item);
|
||||
EXPORT enum obs_data_number_type obs_data_item_numtype(obs_data_item_t *item);
|
||||
|
||||
/* Item set functions */
|
||||
EXPORT void obs_data_item_set_string(obs_data_item_t **item, const char *val);
|
||||
EXPORT void obs_data_item_set_int(obs_data_item_t **item, long long val);
|
||||
EXPORT void obs_data_item_set_double(obs_data_item_t **item, double val);
|
||||
EXPORT void obs_data_item_set_bool(obs_data_item_t **item, bool val);
|
||||
EXPORT void obs_data_item_set_obj(obs_data_item_t **item, obs_data_t *val);
|
||||
EXPORT void obs_data_item_set_array(obs_data_item_t **item,
|
||||
obs_data_array_t *val);
|
||||
|
||||
EXPORT void obs_data_item_set_default_string(obs_data_item_t **item,
|
||||
const char *val);
|
||||
EXPORT void obs_data_item_set_default_int(obs_data_item_t **item, long long val);
|
||||
EXPORT void obs_data_item_set_default_double(obs_data_item_t **item, double val);
|
||||
EXPORT void obs_data_item_set_default_bool(obs_data_item_t **item, bool val);
|
||||
EXPORT void obs_data_item_set_default_obj(obs_data_item_t **item,
|
||||
obs_data_t *val);
|
||||
EXPORT void obs_data_item_set_default_array(obs_data_item_t **item,
|
||||
obs_data_array_t *val);
|
||||
|
||||
EXPORT void obs_data_item_set_autoselect_string(obs_data_item_t **item,
|
||||
const char *val);
|
||||
EXPORT void obs_data_item_set_autoselect_int(obs_data_item_t **item,
|
||||
long long val);
|
||||
EXPORT void obs_data_item_set_autoselect_double(obs_data_item_t **item,
|
||||
double val);
|
||||
EXPORT void obs_data_item_set_autoselect_bool(obs_data_item_t **item, bool val);
|
||||
EXPORT void obs_data_item_set_autoselect_obj(obs_data_item_t **item,
|
||||
obs_data_t *val);
|
||||
EXPORT void obs_data_item_set_autoselect_array(obs_data_item_t **item,
|
||||
obs_data_array_t *val);
|
||||
|
||||
/* Item get functions */
|
||||
EXPORT const char *obs_data_item_get_string(obs_data_item_t *item);
|
||||
EXPORT long long obs_data_item_get_int(obs_data_item_t *item);
|
||||
EXPORT double obs_data_item_get_double(obs_data_item_t *item);
|
||||
EXPORT bool obs_data_item_get_bool(obs_data_item_t *item);
|
||||
EXPORT obs_data_t *obs_data_item_get_obj(obs_data_item_t *item);
|
||||
EXPORT obs_data_array_t *obs_data_item_get_array(obs_data_item_t *item);
|
||||
|
||||
EXPORT const char *obs_data_item_get_default_string(obs_data_item_t *item);
|
||||
EXPORT long long obs_data_item_get_default_int(obs_data_item_t *item);
|
||||
EXPORT double obs_data_item_get_default_double(obs_data_item_t *item);
|
||||
EXPORT bool obs_data_item_get_default_bool(obs_data_item_t *item);
|
||||
EXPORT obs_data_t *obs_data_item_get_default_obj(obs_data_item_t *item);
|
||||
EXPORT obs_data_array_t *obs_data_item_get_default_array(obs_data_item_t *item);
|
||||
|
||||
EXPORT const char *obs_data_item_get_autoselect_string(obs_data_item_t *item);
|
||||
EXPORT long long obs_data_item_get_autoselect_int(obs_data_item_t *item);
|
||||
EXPORT double obs_data_item_get_autoselect_double(obs_data_item_t *item);
|
||||
EXPORT bool obs_data_item_get_autoselect_bool(obs_data_item_t *item);
|
||||
EXPORT obs_data_t *obs_data_item_get_autoselect_obj(obs_data_item_t *item);
|
||||
EXPORT obs_data_array_t *obs_data_item_get_autoselect_array(
|
||||
obs_data_item_t *item);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Helper functions for certain structures */
|
||||
EXPORT void obs_data_set_vec2(obs_data_t *data, const char *name,
|
||||
const struct vec2 *val);
|
||||
EXPORT void obs_data_set_vec3(obs_data_t *data, const char *name,
|
||||
const struct vec3 *val);
|
||||
EXPORT void obs_data_set_vec4(obs_data_t *data, const char *name,
|
||||
const struct vec4 *val);
|
||||
EXPORT void obs_data_set_quat(obs_data_t *data, const char *name,
|
||||
const struct quat *val);
|
||||
|
||||
EXPORT void obs_data_set_default_vec2(obs_data_t *data, const char *name,
|
||||
const struct vec2 *val);
|
||||
EXPORT void obs_data_set_default_vec3(obs_data_t *data, const char *name,
|
||||
const struct vec3 *val);
|
||||
EXPORT void obs_data_set_default_vec4(obs_data_t *data, const char *name,
|
||||
const struct vec4 *val);
|
||||
EXPORT void obs_data_set_default_quat(obs_data_t *data, const char *name,
|
||||
const struct quat *val);
|
||||
|
||||
EXPORT void obs_data_set_autoselect_vec2(obs_data_t *data, const char *name,
|
||||
const struct vec2 *val);
|
||||
EXPORT void obs_data_set_autoselect_vec3(obs_data_t *data, const char *name,
|
||||
const struct vec3 *val);
|
||||
EXPORT void obs_data_set_autoselect_vec4(obs_data_t *data, const char *name,
|
||||
const struct vec4 *val);
|
||||
EXPORT void obs_data_set_autoselect_quat(obs_data_t *data, const char *name,
|
||||
const struct quat *val);
|
||||
|
||||
EXPORT void obs_data_get_vec2(obs_data_t *data, const char *name,
|
||||
struct vec2 *val);
|
||||
EXPORT void obs_data_get_vec3(obs_data_t *data, const char *name,
|
||||
struct vec3 *val);
|
||||
EXPORT void obs_data_get_vec4(obs_data_t *data, const char *name,
|
||||
struct vec4 *val);
|
||||
EXPORT void obs_data_get_quat(obs_data_t *data, const char *name,
|
||||
struct quat *val);
|
||||
|
||||
EXPORT void obs_data_get_default_vec2(obs_data_t *data, const char *name,
|
||||
struct vec2 *val);
|
||||
EXPORT void obs_data_get_default_vec3(obs_data_t *data, const char *name,
|
||||
struct vec3 *val);
|
||||
EXPORT void obs_data_get_default_vec4(obs_data_t *data, const char *name,
|
||||
struct vec4 *val);
|
||||
EXPORT void obs_data_get_default_quat(obs_data_t *data, const char *name,
|
||||
struct quat *val);
|
||||
|
||||
EXPORT void obs_data_get_autoselect_vec2(obs_data_t *data, const char *name,
|
||||
struct vec2 *val);
|
||||
EXPORT void obs_data_get_autoselect_vec3(obs_data_t *data, const char *name,
|
||||
struct vec3 *val);
|
||||
EXPORT void obs_data_get_autoselect_vec4(obs_data_t *data, const char *name,
|
||||
struct vec4 *val);
|
||||
EXPORT void obs_data_get_autoselect_quat(obs_data_t *data, const char *name,
|
||||
struct quat *val);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Helper functions for media_frames_per_second/OBS_PROPERTY_FRAME_RATE */
|
||||
EXPORT void obs_data_set_frames_per_second(obs_data_t *data,
|
||||
const char *name,
|
||||
struct media_frames_per_second fps, const char *option);
|
||||
EXPORT void obs_data_set_default_frames_per_second(obs_data_t *data,
|
||||
const char *name,
|
||||
struct media_frames_per_second fps, const char *option);
|
||||
EXPORT void obs_data_set_autoselect_frames_per_second(obs_data_t *data,
|
||||
const char *name,
|
||||
struct media_frames_per_second fps, const char *option);
|
||||
|
||||
EXPORT bool obs_data_get_frames_per_second(obs_data_t *data,
|
||||
const char *name,
|
||||
struct media_frames_per_second *fps, const char **option);
|
||||
EXPORT bool obs_data_get_default_frames_per_second(obs_data_t *data,
|
||||
const char *name,
|
||||
struct media_frames_per_second *fps, const char **option);
|
||||
EXPORT bool obs_data_get_autoselect_frames_per_second(obs_data_t *data,
|
||||
const char *name,
|
||||
struct media_frames_per_second *fps, const char **option);
|
||||
|
||||
EXPORT void obs_data_item_set_frames_per_second(
|
||||
obs_data_item_t **item,
|
||||
struct media_frames_per_second fps, const char *option);
|
||||
EXPORT void obs_data_item_set_default_frames_per_second(
|
||||
obs_data_item_t **item,
|
||||
struct media_frames_per_second fps, const char *option);
|
||||
EXPORT void obs_data_item_set_autoselect_frames_per_second(
|
||||
obs_data_item_t **item,
|
||||
struct media_frames_per_second fps, const char *option);
|
||||
|
||||
EXPORT bool obs_data_item_get_frames_per_second(
|
||||
obs_data_item_t *item,
|
||||
struct media_frames_per_second *fps, const char **option);
|
||||
EXPORT bool obs_data_item_get_default_frames_per_second(
|
||||
obs_data_item_t *item,
|
||||
struct media_frames_per_second *fps, const char **option);
|
||||
EXPORT bool obs_data_item_get_autoselect_frames_per_second(
|
||||
obs_data_item_t *item,
|
||||
struct media_frames_per_second *fps, const char **option);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* OBS-specific functions */
|
||||
|
||||
static inline obs_data_t *obs_data_newref(obs_data_t *data)
|
||||
{
|
||||
if (data)
|
||||
obs_data_addref(data);
|
||||
else
|
||||
data = obs_data_create();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
49
libobs/obs-defs.h
Normal file
49
libobs/obs-defs.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013-2014 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
/** Maximum number of source channels for output and per display */
|
||||
#define MAX_CHANNELS 64
|
||||
|
||||
#define OBS_ALIGN_CENTER (0)
|
||||
#define OBS_ALIGN_LEFT (1<<0)
|
||||
#define OBS_ALIGN_RIGHT (1<<1)
|
||||
#define OBS_ALIGN_TOP (1<<2)
|
||||
#define OBS_ALIGN_BOTTOM (1<<3)
|
||||
|
||||
#define MODULE_SUCCESS 0
|
||||
#define MODULE_ERROR -1
|
||||
#define MODULE_FILE_NOT_FOUND -2
|
||||
#define MODULE_MISSING_EXPORTS -3
|
||||
#define MODULE_INCOMPATIBLE_VER -4
|
||||
|
||||
#define OBS_OUTPUT_SUCCESS 0
|
||||
#define OBS_OUTPUT_BAD_PATH -1
|
||||
#define OBS_OUTPUT_CONNECT_FAILED -2
|
||||
#define OBS_OUTPUT_INVALID_STREAM -3
|
||||
#define OBS_OUTPUT_ERROR -4
|
||||
#define OBS_OUTPUT_DISCONNECTED -5
|
||||
#define OBS_OUTPUT_UNSUPPORTED -6
|
||||
#define OBS_OUTPUT_NO_SPACE -7
|
||||
|
||||
#define OBS_VIDEO_SUCCESS 0
|
||||
#define OBS_VIDEO_FAIL -1
|
||||
#define OBS_VIDEO_NOT_SUPPORTED -2
|
||||
#define OBS_VIDEO_INVALID_PARAM -3
|
||||
#define OBS_VIDEO_CURRENTLY_ACTIVE -4
|
||||
#define OBS_VIDEO_MODULE_NOT_FOUND -5
|
||||
210
libobs/obs-display.c
Normal file
210
libobs/obs-display.c
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "graphics/vec4.h"
|
||||
#include "obs.h"
|
||||
#include "obs-internal.h"
|
||||
|
||||
bool obs_display_init(struct obs_display *display,
|
||||
const struct gs_init_data *graphics_data)
|
||||
{
|
||||
pthread_mutex_init_value(&display->draw_callbacks_mutex);
|
||||
|
||||
if (graphics_data) {
|
||||
display->swap = gs_swapchain_create(graphics_data);
|
||||
if (!display->swap) {
|
||||
blog(LOG_ERROR, "obs_display_init: Failed to "
|
||||
"create swap chain");
|
||||
return false;
|
||||
}
|
||||
|
||||
display->cx = graphics_data->cx;
|
||||
display->cy = graphics_data->cy;
|
||||
}
|
||||
|
||||
if (pthread_mutex_init(&display->draw_callbacks_mutex, NULL) != 0) {
|
||||
blog(LOG_ERROR, "obs_display_init: Failed to create mutex");
|
||||
return false;
|
||||
}
|
||||
|
||||
display->background_color = 0x4C4C4C;
|
||||
display->enabled = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
obs_display_t *obs_display_create(const struct gs_init_data *graphics_data)
|
||||
{
|
||||
struct obs_display *display = bzalloc(sizeof(struct obs_display));
|
||||
|
||||
gs_enter_context(obs->video.graphics);
|
||||
|
||||
if (!obs_display_init(display, graphics_data)) {
|
||||
obs_display_destroy(display);
|
||||
display = NULL;
|
||||
} else {
|
||||
pthread_mutex_lock(&obs->data.displays_mutex);
|
||||
display->prev_next = &obs->data.first_display;
|
||||
display->next = obs->data.first_display;
|
||||
obs->data.first_display = display;
|
||||
if (display->next)
|
||||
display->next->prev_next = &display->next;
|
||||
pthread_mutex_unlock(&obs->data.displays_mutex);
|
||||
}
|
||||
|
||||
gs_leave_context();
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
void obs_display_free(obs_display_t *display)
|
||||
{
|
||||
pthread_mutex_destroy(&display->draw_callbacks_mutex);
|
||||
da_free(display->draw_callbacks);
|
||||
|
||||
if (display->swap) {
|
||||
gs_swapchain_destroy(display->swap);
|
||||
display->swap = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void obs_display_destroy(obs_display_t *display)
|
||||
{
|
||||
if (display) {
|
||||
pthread_mutex_lock(&obs->data.displays_mutex);
|
||||
if (display->prev_next)
|
||||
*display->prev_next = display->next;
|
||||
if (display->next)
|
||||
display->next->prev_next = display->prev_next;
|
||||
pthread_mutex_unlock(&obs->data.displays_mutex);
|
||||
|
||||
obs_enter_graphics();
|
||||
obs_display_free(display);
|
||||
obs_leave_graphics();
|
||||
|
||||
bfree(display);
|
||||
}
|
||||
}
|
||||
|
||||
void obs_display_resize(obs_display_t *display, uint32_t cx, uint32_t cy)
|
||||
{
|
||||
if (!display) return;
|
||||
|
||||
pthread_mutex_lock(&display->draw_callbacks_mutex);
|
||||
|
||||
display->cx = cx;
|
||||
display->cy = cy;
|
||||
display->size_changed = true;
|
||||
|
||||
pthread_mutex_unlock(&display->draw_callbacks_mutex);
|
||||
}
|
||||
|
||||
void obs_display_add_draw_callback(obs_display_t *display,
|
||||
void (*draw)(void *param, uint32_t cx, uint32_t cy),
|
||||
void *param)
|
||||
{
|
||||
if (!display) return;
|
||||
|
||||
struct draw_callback data = {draw, param};
|
||||
|
||||
pthread_mutex_lock(&display->draw_callbacks_mutex);
|
||||
da_push_back(display->draw_callbacks, &data);
|
||||
pthread_mutex_unlock(&display->draw_callbacks_mutex);
|
||||
}
|
||||
|
||||
void obs_display_remove_draw_callback(obs_display_t *display,
|
||||
void (*draw)(void *param, uint32_t cx, uint32_t cy),
|
||||
void *param)
|
||||
{
|
||||
if (!display) return;
|
||||
|
||||
struct draw_callback data = {draw, param};
|
||||
|
||||
pthread_mutex_lock(&display->draw_callbacks_mutex);
|
||||
da_erase_item(display->draw_callbacks, &data);
|
||||
pthread_mutex_unlock(&display->draw_callbacks_mutex);
|
||||
}
|
||||
|
||||
static inline void render_display_begin(struct obs_display *display)
|
||||
{
|
||||
struct vec4 clear_color;
|
||||
|
||||
gs_load_swapchain(display ? display->swap : NULL);
|
||||
|
||||
if (display->size_changed) {
|
||||
gs_resize(display->cx, display->cy);
|
||||
display->size_changed = false;
|
||||
}
|
||||
|
||||
gs_begin_scene();
|
||||
|
||||
vec4_from_rgba(&clear_color, display->background_color);
|
||||
clear_color.w = 1.0f;
|
||||
|
||||
gs_clear(GS_CLEAR_COLOR | GS_CLEAR_DEPTH | GS_CLEAR_STENCIL,
|
||||
&clear_color, 1.0f, 0);
|
||||
|
||||
gs_enable_depth_test(false);
|
||||
/* gs_enable_blending(false); */
|
||||
gs_set_cull_mode(GS_NEITHER);
|
||||
|
||||
gs_ortho(0.0f, (float)display->cx,
|
||||
0.0f, (float)display->cy, -100.0f, 100.0f);
|
||||
gs_set_viewport(0, 0, display->cx, display->cy);
|
||||
}
|
||||
|
||||
static inline void render_display_end()
|
||||
{
|
||||
gs_end_scene();
|
||||
gs_present();
|
||||
}
|
||||
|
||||
void render_display(struct obs_display *display)
|
||||
{
|
||||
if (!display || !display->enabled) return;
|
||||
|
||||
render_display_begin(display);
|
||||
|
||||
pthread_mutex_lock(&display->draw_callbacks_mutex);
|
||||
|
||||
for (size_t i = 0; i < display->draw_callbacks.num; i++) {
|
||||
struct draw_callback *callback;
|
||||
callback = display->draw_callbacks.array+i;
|
||||
|
||||
callback->draw(callback->param, display->cx, display->cy);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&display->draw_callbacks_mutex);
|
||||
|
||||
render_display_end();
|
||||
}
|
||||
|
||||
void obs_display_set_enabled(obs_display_t *display, bool enable)
|
||||
{
|
||||
if (display)
|
||||
display->enabled = enable;
|
||||
}
|
||||
|
||||
bool obs_display_enabled(obs_display_t *display)
|
||||
{
|
||||
return display ? display->enabled : false;
|
||||
}
|
||||
|
||||
void obs_display_set_background_color(obs_display_t *display, uint32_t color)
|
||||
{
|
||||
if (display)
|
||||
display->background_color = color;
|
||||
}
|
||||
1152
libobs/obs-encoder.c
Normal file
1152
libobs/obs-encoder.c
Normal file
File diff suppressed because it is too large
Load diff
246
libobs/obs-encoder.h
Normal file
246
libobs/obs-encoder.h
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013-2014 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief header for modules implementing encoders.
|
||||
*
|
||||
* Encoders are modules that implement some codec that can be used by libobs
|
||||
* to process output data.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Specifies the encoder type */
|
||||
enum obs_encoder_type {
|
||||
OBS_ENCODER_AUDIO, /**< The encoder provides an audio codec */
|
||||
OBS_ENCODER_VIDEO /**< The encoder provides a video codec */
|
||||
};
|
||||
|
||||
/** Encoder output packet */
|
||||
struct encoder_packet {
|
||||
uint8_t *data; /**< Packet data */
|
||||
size_t size; /**< Packet size */
|
||||
|
||||
int64_t pts; /**< Presentation timestamp */
|
||||
int64_t dts; /**< Decode timestamp */
|
||||
|
||||
int32_t timebase_num; /**< Timebase numerator */
|
||||
int32_t timebase_den; /**< Timebase denominator */
|
||||
|
||||
enum obs_encoder_type type; /**< Encoder type */
|
||||
|
||||
bool keyframe; /**< Is a keyframe */
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
/* Internal video variables (will be parsed automatically) */
|
||||
|
||||
/* DTS in microseconds */
|
||||
int64_t dts_usec;
|
||||
|
||||
/**
|
||||
* Packet priority
|
||||
*
|
||||
* This is generally use by video encoders to specify the priority
|
||||
* of the packet.
|
||||
*/
|
||||
int priority;
|
||||
|
||||
/**
|
||||
* Dropped packet priority
|
||||
*
|
||||
* If this packet needs to be dropped, the next packet must be of this
|
||||
* priority or higher to continue transmission.
|
||||
*/
|
||||
int drop_priority;
|
||||
|
||||
/** Audio track index (used with outputs) */
|
||||
size_t track_idx;
|
||||
|
||||
/** Encoder from which the track originated from */
|
||||
obs_encoder_t *encoder;
|
||||
};
|
||||
|
||||
/** Encoder input frame */
|
||||
struct encoder_frame {
|
||||
/** Data for the frame/audio */
|
||||
uint8_t *data[MAX_AV_PLANES];
|
||||
|
||||
/** size of each plane */
|
||||
uint32_t linesize[MAX_AV_PLANES];
|
||||
|
||||
/** Number of frames (audio only) */
|
||||
uint32_t frames;
|
||||
|
||||
/** Presentation timestamp */
|
||||
int64_t pts;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encoder interface
|
||||
*
|
||||
* Encoders have a limited usage with OBS. You are not generally supposed to
|
||||
* implement every encoder out there. Generally, these are limited or specific
|
||||
* encoders for h264/aac for streaming and recording. It doesn't have to be
|
||||
* *just* h264 or aac of course, but generally those are the expected encoders.
|
||||
*
|
||||
* That being said, other encoders will be kept in mind for future use.
|
||||
*/
|
||||
struct obs_encoder_info {
|
||||
/* ----------------------------------------------------------------- */
|
||||
/* Required implementation*/
|
||||
|
||||
/** Specifies the named identifier of this encoder */
|
||||
const char *id;
|
||||
|
||||
/** Specifies the encoder type (video or audio) */
|
||||
enum obs_encoder_type type;
|
||||
|
||||
/** Specifies the codec */
|
||||
const char *codec;
|
||||
|
||||
/**
|
||||
* Gets the full translated name of this encoder
|
||||
*
|
||||
* @param type_data The type_data variable of this structure
|
||||
* @return Translated name of the encoder
|
||||
*/
|
||||
const char *(*get_name)(void *type_data);
|
||||
|
||||
/**
|
||||
* Creates the encoder with the specified settings
|
||||
*
|
||||
* @param settings Settings for the encoder
|
||||
* @param encoder OBS encoder context
|
||||
* @return Data associated with this encoder context, or
|
||||
* NULL if initialization failed.
|
||||
*/
|
||||
void *(*create)(obs_data_t *settings, obs_encoder_t *encoder);
|
||||
|
||||
/**
|
||||
* Destroys the encoder data
|
||||
*
|
||||
* @param data Data associated with this encoder context
|
||||
*/
|
||||
void (*destroy)(void *data);
|
||||
|
||||
/**
|
||||
* Encodes frame(s), and outputs encoded packets as they become
|
||||
* available.
|
||||
*
|
||||
* @param data Data associated with this encoder
|
||||
* context
|
||||
* @param[in] frame Raw audio/video data to encode
|
||||
* @param[out] packet Encoder packet output, if any
|
||||
* @param[out] received_packet Set to true if a packet was received,
|
||||
* false otherwise
|
||||
* @return true if successful, false otherwise.
|
||||
*/
|
||||
bool (*encode)(void *data, struct encoder_frame *frame,
|
||||
struct encoder_packet *packet, bool *received_packet);
|
||||
|
||||
/** Audio encoder only: Returns the frame size for this encoder */
|
||||
size_t (*get_frame_size)(void *data);
|
||||
|
||||
/* ----------------------------------------------------------------- */
|
||||
/* Optional implementation */
|
||||
|
||||
/**
|
||||
* Gets the default settings for this encoder
|
||||
*
|
||||
* @param[out] settings Data to assign default settings to
|
||||
*/
|
||||
void (*get_defaults)(obs_data_t *settings);
|
||||
|
||||
/**
|
||||
* Gets the property information of this encoder
|
||||
*
|
||||
* @return The properties data
|
||||
*/
|
||||
obs_properties_t *(*get_properties)(void *data);
|
||||
|
||||
/**
|
||||
* Updates the settings for this encoder (usually used for things like
|
||||
* changeing birate while active)
|
||||
*
|
||||
* @param data Data associated with this encoder context
|
||||
* @param settings New settings for this encoder
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool (*update)(void *data, obs_data_t *settings);
|
||||
|
||||
/**
|
||||
* Returns extra data associated with this encoder (usually header)
|
||||
*
|
||||
* @param data Data associated with this encoder context
|
||||
* @param[out] extra_data Pointer to receive the extra data
|
||||
* @param[out] size Pointer to receive the size of the extra
|
||||
* data
|
||||
* @return true if extra data available, false
|
||||
* otherwise
|
||||
*/
|
||||
bool (*get_extra_data)(void *data, uint8_t **extra_data, size_t *size);
|
||||
|
||||
/**
|
||||
* Gets the SEI data, if any
|
||||
*
|
||||
* @param data Data associated with this encoder context
|
||||
* @param[out] sei_data Pointer to receive the SEI data
|
||||
* @param[out] size Pointer to receive the SEI data size
|
||||
* @return true if SEI data available, false otherwise
|
||||
*/
|
||||
bool (*get_sei_data)(void *data, uint8_t **sei_data, size_t *size);
|
||||
|
||||
/**
|
||||
* Returns desired audio format and sample information
|
||||
*
|
||||
* @param data Data associated with this encoder context
|
||||
* @param[in/out] info Audio format information
|
||||
*/
|
||||
void (*get_audio_info)(void *data, struct audio_convert_info *info);
|
||||
|
||||
/**
|
||||
* Returns desired video format information
|
||||
*
|
||||
* @param data Data associated with this encoder context
|
||||
* @param[in/out] info Video format information
|
||||
*/
|
||||
void (*get_video_info)(void *data, struct video_scale_info *info);
|
||||
|
||||
void *type_data;
|
||||
void (*free_type_data)(void *type_data);
|
||||
};
|
||||
|
||||
EXPORT void obs_register_encoder_s(const struct obs_encoder_info *info,
|
||||
size_t size);
|
||||
|
||||
/**
|
||||
* Register an encoder definition to the current obs context. This should be
|
||||
* used in obs_module_load.
|
||||
*
|
||||
* @param info Pointer to the source definition structure.
|
||||
*/
|
||||
#define obs_register_encoder(info) \
|
||||
obs_register_encoder_s(info, sizeof(struct obs_encoder_info))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
22
libobs/obs-ffmpeg-compat.h
Normal file
22
libobs/obs-ffmpeg-compat.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
|
||||
/* LIBAVCODEC_VERSION_CHECK checks for the right version of libav and FFmpeg
|
||||
* a is the major version
|
||||
* b and c the minor and micro versions of libav
|
||||
* d and e the minor and micro versions of FFmpeg */
|
||||
#define LIBAVCODEC_VERSION_CHECK( a, b, c, d, e ) \
|
||||
( (LIBAVCODEC_VERSION_MICRO < 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT( a, b, c ) ) || \
|
||||
(LIBAVCODEC_VERSION_MICRO >= 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT( a, d, e ) ) )
|
||||
|
||||
#if !LIBAVCODEC_VERSION_CHECK(54, 28, 0, 59, 100)
|
||||
# define avcodec_free_frame av_freep
|
||||
#endif
|
||||
|
||||
#if LIBAVCODEC_VERSION_INT < 0x371c01
|
||||
# define av_frame_alloc avcodec_alloc_frame
|
||||
# define av_frame_unref avcodec_get_frame_defaults
|
||||
# define av_frame_free avcodec_free_frame
|
||||
#endif
|
||||
|
||||
411
libobs/obs-hotkey-name-map.c
Normal file
411
libobs/obs-hotkey-name-map.c
Normal file
|
|
@ -0,0 +1,411 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2014 by Ruwen Hahn <palana@stunned.de>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <util/bmem.h>
|
||||
#include <util/c99defs.h>
|
||||
#include <util/darray.h>
|
||||
|
||||
#include "obs-internal.h"
|
||||
|
||||
struct obs_hotkey_name_map_edge;
|
||||
struct obs_hotkey_name_map_node;
|
||||
struct obs_hotkey_name_map;
|
||||
|
||||
typedef struct obs_hotkey_name_map_edge obs_hotkey_name_map_edge_t;
|
||||
typedef struct obs_hotkey_name_map_node obs_hotkey_name_map_node_t;
|
||||
typedef struct obs_hotkey_name_map obs_hotkey_name_map_t;
|
||||
|
||||
struct obs_hotkey_name_map_node {
|
||||
bool is_leaf;
|
||||
union {
|
||||
int val;
|
||||
DARRAY(obs_hotkey_name_map_edge_t) children;
|
||||
};
|
||||
};
|
||||
|
||||
struct obs_hotkey_name_map {
|
||||
obs_hotkey_name_map_node_t root;
|
||||
obs_hotkey_name_map_node_t *leaves;
|
||||
size_t num_leaves;
|
||||
size_t next_leaf;
|
||||
};
|
||||
|
||||
struct obs_hotkey_name_map_edge_prefix {
|
||||
uint8_t prefix_len;
|
||||
char *prefix;
|
||||
};
|
||||
|
||||
#define NAME_MAP_COMPRESS_LENGTH \
|
||||
(sizeof(struct obs_hotkey_name_map_edge_prefix) - sizeof(uint8_t))
|
||||
|
||||
struct obs_hotkey_name_map_edge {
|
||||
union {
|
||||
struct {
|
||||
uint8_t prefix_len;
|
||||
char *prefix;
|
||||
};
|
||||
struct {
|
||||
uint8_t compressed_len;
|
||||
char compressed_prefix[NAME_MAP_COMPRESS_LENGTH];
|
||||
};
|
||||
};
|
||||
struct obs_hotkey_name_map_node *node;
|
||||
};
|
||||
|
||||
|
||||
static inline obs_hotkey_name_map_node_t *new_node(void)
|
||||
{
|
||||
return bzalloc(sizeof(obs_hotkey_name_map_node_t));
|
||||
}
|
||||
|
||||
static inline obs_hotkey_name_map_node_t *new_leaf(void)
|
||||
{
|
||||
obs_hotkey_name_map_t *name_map = obs->hotkeys.name_map;
|
||||
obs_hotkey_name_map_node_t *node =
|
||||
&name_map->leaves[name_map->next_leaf];
|
||||
name_map->next_leaf += 1;
|
||||
|
||||
node->is_leaf = true;
|
||||
return node;
|
||||
}
|
||||
|
||||
static inline char *get_prefix(obs_hotkey_name_map_edge_t *e)
|
||||
{
|
||||
return e->prefix_len >= NAME_MAP_COMPRESS_LENGTH ?
|
||||
e->prefix : e->compressed_prefix;
|
||||
}
|
||||
|
||||
static void set_prefix(obs_hotkey_name_map_edge_t *e, const char *prefix,
|
||||
size_t l)
|
||||
{
|
||||
assert(e->prefix_len == 0);
|
||||
|
||||
e->compressed_len = (uint8_t)l;
|
||||
if (l < NAME_MAP_COMPRESS_LENGTH)
|
||||
strncpy(e->compressed_prefix, prefix, l);
|
||||
else
|
||||
e->prefix = bstrdup_n(prefix, l);
|
||||
}
|
||||
|
||||
static obs_hotkey_name_map_edge_t *add_leaf(obs_hotkey_name_map_node_t *node,
|
||||
const char *key, size_t l, int v)
|
||||
{
|
||||
obs_hotkey_name_map_edge_t *e = da_push_back_new(node->children);
|
||||
|
||||
set_prefix(e, key, l);
|
||||
|
||||
e->node = new_leaf();
|
||||
e->node->val = v;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static void shrink_prefix(obs_hotkey_name_map_edge_t *e, size_t l)
|
||||
{
|
||||
bool old_comp = e->prefix_len < NAME_MAP_COMPRESS_LENGTH;
|
||||
bool new_comp = l < NAME_MAP_COMPRESS_LENGTH;
|
||||
|
||||
char *str = get_prefix(e);
|
||||
|
||||
e->prefix_len = (uint8_t)l;
|
||||
if (get_prefix(e) != str)
|
||||
strncpy(get_prefix(e), str, l);
|
||||
else
|
||||
str[l] = 0;
|
||||
|
||||
if (!old_comp && new_comp)
|
||||
bfree(str);
|
||||
}
|
||||
|
||||
static void connect(obs_hotkey_name_map_edge_t *e,
|
||||
obs_hotkey_name_map_node_t *n)
|
||||
{
|
||||
e->node = n;
|
||||
}
|
||||
|
||||
static void reduce_edge(obs_hotkey_name_map_edge_t *e, const char *key,
|
||||
size_t l, int v)
|
||||
{
|
||||
const char *str = get_prefix(e), *str_ = key;
|
||||
size_t common_length = 0;
|
||||
while (*str == *str_) {
|
||||
common_length += 1;
|
||||
str += 1;
|
||||
str_ += 1;
|
||||
}
|
||||
|
||||
obs_hotkey_name_map_node_t *new_node_ = new_node();
|
||||
obs_hotkey_name_map_edge_t *tail =
|
||||
da_push_back_new(new_node_->children);
|
||||
|
||||
connect(tail, e->node);
|
||||
set_prefix(tail, str, e->prefix_len - common_length);
|
||||
|
||||
add_leaf(new_node_, str_, l - common_length, v);
|
||||
|
||||
connect(e, new_node_);
|
||||
shrink_prefix(e, common_length);
|
||||
}
|
||||
|
||||
enum obs_hotkey_name_map_edge_compare_result {
|
||||
RES_MATCHES,
|
||||
RES_NO_MATCH,
|
||||
RES_COMMON_PREFIX,
|
||||
RES_PREFIX_MATCHES,
|
||||
};
|
||||
|
||||
static enum obs_hotkey_name_map_edge_compare_result compare_prefix(
|
||||
obs_hotkey_name_map_edge_t *edge, const char *key, size_t l)
|
||||
{
|
||||
uint8_t pref_len = edge->prefix_len;
|
||||
const char *str = get_prefix(edge);
|
||||
size_t i = 0;
|
||||
|
||||
for (; i < l && i < pref_len; i++)
|
||||
if (str[i] != key[i])
|
||||
break;
|
||||
|
||||
|
||||
if (i != 0 && pref_len == i)
|
||||
return l == i ? RES_MATCHES : RES_PREFIX_MATCHES;
|
||||
if (i != 0)
|
||||
return pref_len == i ? RES_PREFIX_MATCHES : RES_COMMON_PREFIX;
|
||||
return RES_NO_MATCH;
|
||||
}
|
||||
|
||||
static void insert(obs_hotkey_name_map_edge_t *edge,
|
||||
obs_hotkey_name_map_node_t *node,
|
||||
const char *key, size_t l, int v)
|
||||
{
|
||||
if (node->is_leaf && l > 0) {
|
||||
obs_hotkey_name_map_node_t *new_node_ = new_node();
|
||||
connect(edge, new_node_);
|
||||
|
||||
obs_hotkey_name_map_edge_t *edge =
|
||||
da_push_back_new(new_node_->children);
|
||||
connect(edge, node);
|
||||
add_leaf(new_node_, key, l, v);
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->is_leaf && l == 0) {
|
||||
node->val = v;
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < node->children.num; i++) {
|
||||
obs_hotkey_name_map_edge_t *e = &node->children.array[i];
|
||||
|
||||
switch (compare_prefix(e, key, l)) {
|
||||
case RES_NO_MATCH:
|
||||
continue;
|
||||
|
||||
case RES_MATCHES:
|
||||
case RES_PREFIX_MATCHES:
|
||||
insert(e, e->node, key + e->prefix_len,
|
||||
l - e->prefix_len, v);
|
||||
return;
|
||||
|
||||
case RES_COMMON_PREFIX:
|
||||
reduce_edge(e, key, l, v);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
add_leaf(node, key, l, v);
|
||||
}
|
||||
|
||||
static void obs_hotkey_name_map_insert(obs_hotkey_name_map_t *trie,
|
||||
const char *key, int v)
|
||||
{
|
||||
if (!trie || !key)
|
||||
return;
|
||||
|
||||
insert(NULL, &trie->root, key, strlen(key), v);
|
||||
}
|
||||
|
||||
static bool obs_hotkey_name_map_lookup(obs_hotkey_name_map_t *trie,
|
||||
const char *key, int *v)
|
||||
{
|
||||
if (!trie || !key)
|
||||
return false;
|
||||
|
||||
size_t len = strlen(key);
|
||||
obs_hotkey_name_map_node_t *n = &trie->root;
|
||||
|
||||
size_t i = 0;
|
||||
for (; i < n->children.num;) {
|
||||
obs_hotkey_name_map_edge_t *e = &n->children.array[i];
|
||||
|
||||
switch (compare_prefix(e, key, len)) {
|
||||
case RES_NO_MATCH:
|
||||
i++;
|
||||
continue;
|
||||
|
||||
case RES_COMMON_PREFIX:
|
||||
return false;
|
||||
|
||||
case RES_PREFIX_MATCHES:
|
||||
key += e->prefix_len;
|
||||
len -= e->prefix_len;
|
||||
n = e->node;
|
||||
i = 0;
|
||||
continue;
|
||||
|
||||
case RES_MATCHES:
|
||||
n = e->node;
|
||||
if (!n->is_leaf) {
|
||||
for (size_t j = 0; j < n->children.num; j++) {
|
||||
if (n->children.array[j].prefix_len)
|
||||
continue;
|
||||
|
||||
if (v) *v =
|
||||
n->children.array[j].node->val;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (v) *v = n->val;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void show_node(obs_hotkey_name_map_node_t *node, int in)
|
||||
{
|
||||
if (node->is_leaf) {
|
||||
printf(": % 3d\n", node->val);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
for (int i = 0; i < in; i += 2)
|
||||
printf("| ");
|
||||
printf("%lu:\n", (unsigned long)node->children.num);
|
||||
|
||||
for (size_t i = 0; i < node->children.num; i++) {
|
||||
for (int i = 0; i < in; i += 2)
|
||||
printf("| ");
|
||||
printf("\\ ");
|
||||
|
||||
obs_hotkey_name_map_edge_t *e = &node->children.array[i];
|
||||
printf("%s", get_prefix(e));
|
||||
show_node(e->node, in+2);
|
||||
}
|
||||
}
|
||||
|
||||
void trie_print_size(obs_hotkey_name_map_t *trie)
|
||||
{
|
||||
show_node(&trie->root, 0);
|
||||
}
|
||||
|
||||
static const char* obs_key_names[] = {
|
||||
#define OBS_HOTKEY(x) #x,
|
||||
#include "obs-hotkeys.h"
|
||||
#undef OBS_HOTKEY
|
||||
};
|
||||
|
||||
const char* obs_key_to_name(obs_key_t key)
|
||||
{
|
||||
if (key >= OBS_KEY_LAST_VALUE) {
|
||||
blog(LOG_ERROR, "obs-hotkey.c: queried unknown key "
|
||||
"with code %d", (int)key);
|
||||
return "";
|
||||
}
|
||||
|
||||
return obs_key_names[key];
|
||||
}
|
||||
|
||||
static obs_key_t obs_key_from_name_fallback(const char *name)
|
||||
{
|
||||
#define OBS_HOTKEY(x) if (strcmp(#x, name) == 0) return x;
|
||||
#include "obs-hotkeys.h"
|
||||
#undef OBS_HOTKEY
|
||||
return OBS_KEY_NONE;
|
||||
}
|
||||
|
||||
static void init_name_map(void)
|
||||
{
|
||||
obs->hotkeys.name_map = bzalloc(sizeof(struct obs_hotkey_name_map));
|
||||
obs_hotkey_name_map_t *name_map = obs->hotkeys.name_map;
|
||||
|
||||
#define OBS_HOTKEY(x) name_map->num_leaves += 1;
|
||||
#include "obs-hotkeys.h"
|
||||
#undef OBS_HOTKEY
|
||||
|
||||
size_t size = sizeof(obs_hotkey_name_map_node_t) * name_map->num_leaves;
|
||||
name_map->leaves = bzalloc(size);
|
||||
|
||||
#define OBS_HOTKEY(x) obs_hotkey_name_map_insert(name_map, #x, x);
|
||||
#include "obs-hotkeys.h"
|
||||
#undef OBS_HOTKEY
|
||||
}
|
||||
|
||||
obs_key_t obs_key_from_name(const char *name)
|
||||
{
|
||||
if (!obs)
|
||||
return obs_key_from_name_fallback(name);
|
||||
|
||||
if (pthread_once(&obs->hotkeys.name_map_init_token, init_name_map))
|
||||
return obs_key_from_name_fallback(name);
|
||||
|
||||
int v = 0;
|
||||
if (obs_hotkey_name_map_lookup(obs->hotkeys.name_map, name, &v))
|
||||
return v;
|
||||
|
||||
return OBS_KEY_NONE;
|
||||
}
|
||||
|
||||
static void free_node(obs_hotkey_name_map_node_t *node, bool release);
|
||||
|
||||
static void free_edge(obs_hotkey_name_map_edge_t *edge)
|
||||
{
|
||||
free_node(edge->node, true);
|
||||
|
||||
if (edge->prefix_len < NAME_MAP_COMPRESS_LENGTH)
|
||||
return;
|
||||
|
||||
bfree(get_prefix(edge));
|
||||
}
|
||||
|
||||
static void free_node(obs_hotkey_name_map_node_t *node, bool release)
|
||||
{
|
||||
if (!node->is_leaf) {
|
||||
for (size_t i = 0; i < node->children.num; i++)
|
||||
free_edge(&node->children.array[i]);
|
||||
|
||||
da_free(node->children);
|
||||
}
|
||||
|
||||
if (release && !node->is_leaf) bfree(node);
|
||||
}
|
||||
|
||||
void obs_hotkey_name_map_free(void)
|
||||
{
|
||||
if (!obs || !obs->hotkeys.name_map)
|
||||
return;
|
||||
|
||||
free_node(&obs->hotkeys.name_map->root, false);
|
||||
bfree(obs->hotkeys.name_map->leaves);
|
||||
bfree(obs->hotkeys.name_map);
|
||||
}
|
||||
1532
libobs/obs-hotkey.c
Normal file
1532
libobs/obs-hotkey.c
Normal file
File diff suppressed because it is too large
Load diff
287
libobs/obs-hotkey.h
Normal file
287
libobs/obs-hotkey.h
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2014-2015 by Ruwen Hahn <palana@stunned.de>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef size_t obs_hotkey_id;
|
||||
#define OBS_INVALID_HOTKEY_ID (~(obs_hotkey_id)0)
|
||||
typedef size_t obs_hotkey_pair_id;
|
||||
#define OBS_INVALID_HOTKEY_PAIR_ID (~(obs_hotkey_pair_id)0)
|
||||
|
||||
enum obs_key {
|
||||
#define OBS_HOTKEY(x) x,
|
||||
#include "obs-hotkeys.h"
|
||||
#undef OBS_HOTKEY
|
||||
OBS_KEY_LAST_VALUE //not an actual key
|
||||
};
|
||||
typedef enum obs_key obs_key_t;
|
||||
|
||||
struct obs_key_combination {
|
||||
uint32_t modifiers;
|
||||
obs_key_t key;
|
||||
};
|
||||
typedef struct obs_key_combination obs_key_combination_t;
|
||||
|
||||
typedef struct obs_hotkey obs_hotkey_t;
|
||||
typedef struct obs_hotkey_binding obs_hotkey_binding_t;
|
||||
|
||||
enum obs_hotkey_registerer_type {
|
||||
OBS_HOTKEY_REGISTERER_FRONTEND,
|
||||
OBS_HOTKEY_REGISTERER_SOURCE,
|
||||
OBS_HOTKEY_REGISTERER_OUTPUT,
|
||||
OBS_HOTKEY_REGISTERER_ENCODER,
|
||||
OBS_HOTKEY_REGISTERER_SERVICE,
|
||||
};
|
||||
typedef enum obs_hotkey_registerer_type obs_hotkey_registerer_t;
|
||||
|
||||
EXPORT obs_hotkey_id obs_hotkey_get_id(const obs_hotkey_t *key);
|
||||
EXPORT const char *obs_hotkey_get_name(const obs_hotkey_t *key);
|
||||
EXPORT const char *obs_hotkey_get_description(const obs_hotkey_t *key);
|
||||
EXPORT obs_hotkey_registerer_t obs_hotkey_get_registerer_type(
|
||||
const obs_hotkey_t *key);
|
||||
EXPORT void *obs_hotkey_get_registerer(const obs_hotkey_t *key);
|
||||
EXPORT obs_hotkey_id obs_hotkey_get_pair_partner_id(const obs_hotkey_t *key);
|
||||
|
||||
|
||||
EXPORT obs_key_combination_t obs_hotkey_binding_get_key_combination(
|
||||
obs_hotkey_binding_t *binding);
|
||||
EXPORT obs_hotkey_id obs_hotkey_binding_get_hotkey_id(
|
||||
obs_hotkey_binding_t *binding);
|
||||
EXPORT obs_hotkey_t *obs_hotkey_binding_get_hotkey(
|
||||
obs_hotkey_binding_t *binding);
|
||||
|
||||
struct obs_hotkeys_translations {
|
||||
const char *insert;
|
||||
const char *del;
|
||||
const char *home;
|
||||
const char *end;
|
||||
const char *page_up;
|
||||
const char *page_down;
|
||||
const char *num_lock;
|
||||
const char *scroll_lock;
|
||||
const char *caps_lock;
|
||||
const char *backspace;
|
||||
const char *tab;
|
||||
const char *print;
|
||||
const char *pause;
|
||||
const char *left;
|
||||
const char *right;
|
||||
const char *up;
|
||||
const char *down;
|
||||
const char *shift;
|
||||
const char *alt;
|
||||
const char *control;
|
||||
const char *meta; /* windows/super key */
|
||||
const char *menu;
|
||||
const char *space;
|
||||
const char *numpad_num; /* For example, "Numpad %1" */
|
||||
const char *numpad_divide;
|
||||
const char *numpad_multiply;
|
||||
const char *numpad_minus;
|
||||
const char *numpad_plus;
|
||||
const char *numpad_decimal;
|
||||
const char *apple_keypad_num; /* For example, "%1 (Keypad)" */
|
||||
const char *apple_keypad_divide;
|
||||
const char *apple_keypad_multiply;
|
||||
const char *apple_keypad_minus;
|
||||
const char *apple_keypad_plus;
|
||||
const char *apple_keypad_decimal;
|
||||
const char *apple_keypad_equal;
|
||||
const char *mouse_num; /* For example, "Mouse %1" */
|
||||
};
|
||||
|
||||
/* This function is an optional way to provide translations for specific keys
|
||||
* that may not have translations. If the operating system can provide
|
||||
* translations for these keys, it will use the operating system's translation
|
||||
* over these translations. If no translations are specified, it will use
|
||||
* the default english translations for that specific operating system. */
|
||||
EXPORT void obs_hotkeys_set_translations_s(
|
||||
struct obs_hotkeys_translations *translations, size_t size);
|
||||
|
||||
#define obs_hotkeys_set_translations(translations) \
|
||||
obs_hotkeys_set_translations_s(translations, \
|
||||
sizeof(struct obs_hotkeys_translations))
|
||||
|
||||
EXPORT void obs_hotkeys_set_audio_hotkeys_translations(
|
||||
const char *mute, const char *unmute,
|
||||
const char *push_to_mute, const char *push_to_talk);
|
||||
|
||||
EXPORT void obs_hotkeys_set_sceneitem_hotkeys_translations(
|
||||
const char *show, const char *hide);
|
||||
|
||||
/* registering hotkeys (giving hotkeys a name and a function) */
|
||||
|
||||
typedef void (*obs_hotkey_func)(void *data,
|
||||
obs_hotkey_id id, obs_hotkey_t *hotkey, bool pressed);
|
||||
|
||||
EXPORT obs_hotkey_id obs_hotkey_register_frontend(const char *name,
|
||||
const char *description, obs_hotkey_func func, void *data);
|
||||
|
||||
EXPORT obs_hotkey_id obs_hotkey_register_encoder(obs_encoder_t *encoder,
|
||||
const char *name, const char *description,
|
||||
obs_hotkey_func func, void *data);
|
||||
|
||||
EXPORT obs_hotkey_id obs_hotkey_register_output(obs_output_t *output,
|
||||
const char *name, const char *description,
|
||||
obs_hotkey_func func, void *data);
|
||||
|
||||
EXPORT obs_hotkey_id obs_hotkey_register_service(obs_service_t *service,
|
||||
const char *name, const char *description,
|
||||
obs_hotkey_func func, void *data);
|
||||
|
||||
EXPORT obs_hotkey_id obs_hotkey_register_source(obs_source_t *source,
|
||||
const char *name, const char *description,
|
||||
obs_hotkey_func func, void *data);
|
||||
|
||||
typedef bool (*obs_hotkey_active_func)(void *data,
|
||||
obs_hotkey_pair_id id, obs_hotkey_t *hotkey, bool pressed);
|
||||
|
||||
EXPORT obs_hotkey_pair_id obs_hotkey_pair_register_frontend(
|
||||
const char *name0, const char *description0,
|
||||
const char *name1, const char *description1,
|
||||
obs_hotkey_active_func func0, obs_hotkey_active_func func1,
|
||||
void *data0, void *data1);
|
||||
|
||||
EXPORT obs_hotkey_pair_id obs_hotkey_pair_register_encoder(
|
||||
obs_encoder_t *encoder,
|
||||
const char *name0, const char *description0,
|
||||
const char *name1, const char *description1,
|
||||
obs_hotkey_active_func func0, obs_hotkey_active_func func1,
|
||||
void *data0, void *data1);
|
||||
|
||||
EXPORT obs_hotkey_pair_id obs_hotkey_pair_register_output(
|
||||
obs_output_t *output,
|
||||
const char *name0, const char *description0,
|
||||
const char *name1, const char *description1,
|
||||
obs_hotkey_active_func func0, obs_hotkey_active_func func1,
|
||||
void *data0, void *data1);
|
||||
|
||||
EXPORT obs_hotkey_pair_id obs_hotkey_pair_register_service(
|
||||
obs_service_t *service,
|
||||
const char *name0, const char *description0,
|
||||
const char *name1, const char *description1,
|
||||
obs_hotkey_active_func func0, obs_hotkey_active_func func1,
|
||||
void *data0, void *data1);
|
||||
|
||||
EXPORT obs_hotkey_pair_id obs_hotkey_pair_register_source(
|
||||
obs_source_t *source,
|
||||
const char *name0, const char *description0,
|
||||
const char *name1, const char *description1,
|
||||
obs_hotkey_active_func func0, obs_hotkey_active_func func1,
|
||||
void *data0, void *data1);
|
||||
|
||||
EXPORT void obs_hotkey_unregister(obs_hotkey_id id);
|
||||
|
||||
EXPORT void obs_hotkey_pair_unregister(obs_hotkey_pair_id id);
|
||||
|
||||
/* loading hotkeys (associating a hotkey with a physical key and modifiers) */
|
||||
|
||||
EXPORT void obs_hotkey_load_bindings(obs_hotkey_id id,
|
||||
obs_key_combination_t *combinations, size_t num);
|
||||
|
||||
EXPORT void obs_hotkey_load(obs_hotkey_id id, obs_data_array_t *data);
|
||||
|
||||
EXPORT void obs_hotkeys_load_encoder(obs_encoder_t *encoder,
|
||||
obs_data_t *hotkeys);
|
||||
|
||||
EXPORT void obs_hotkeys_load_output(obs_output_t *output, obs_data_t *hotkeys);
|
||||
|
||||
EXPORT void obs_hotkeys_load_service(obs_service_t *service,
|
||||
obs_data_t *hotkeys);
|
||||
|
||||
EXPORT void obs_hotkeys_load_source(obs_source_t *source, obs_data_t *hotkeys);
|
||||
|
||||
EXPORT void obs_hotkey_pair_load(obs_hotkey_pair_id id, obs_data_array_t *data0,
|
||||
obs_data_array_t *data1);
|
||||
|
||||
|
||||
EXPORT obs_data_array_t *obs_hotkey_save(obs_hotkey_id id);
|
||||
|
||||
EXPORT obs_data_t *obs_hotkeys_save_encoder(obs_encoder_t *encoder);
|
||||
|
||||
EXPORT obs_data_t *obs_hotkeys_save_output(obs_output_t *output);
|
||||
|
||||
EXPORT obs_data_t *obs_hotkeys_save_service(obs_service_t *service);
|
||||
|
||||
EXPORT obs_data_t *obs_hotkeys_save_source(obs_source_t *source);
|
||||
|
||||
/* enumerating hotkeys */
|
||||
|
||||
typedef bool (*obs_hotkey_enum_func)(void *data,
|
||||
obs_hotkey_id id, obs_hotkey_t *key);
|
||||
|
||||
EXPORT void obs_enum_hotkeys(obs_hotkey_enum_func func, void *data);
|
||||
|
||||
/* enumerating bindings */
|
||||
|
||||
typedef bool (*obs_hotkey_binding_enum_func)(void *data,
|
||||
size_t idx, obs_hotkey_binding_t* binding);
|
||||
|
||||
EXPORT void obs_enum_hotkey_bindings(obs_hotkey_binding_enum_func func,
|
||||
void *data);
|
||||
|
||||
/* hotkey event control */
|
||||
|
||||
EXPORT void obs_hotkey_inject_event(obs_key_combination_t hotkey, bool pressed);
|
||||
|
||||
EXPORT void obs_hotkey_enable_background_press(bool enable);
|
||||
|
||||
EXPORT void obs_hotkey_enable_strict_modifiers(bool enable);
|
||||
|
||||
/* hotkey callback routing (trigger callbacks through e.g. a UI thread) */
|
||||
|
||||
typedef void (*obs_hotkey_callback_router_func)(void *data,
|
||||
obs_hotkey_id id, bool pressed);
|
||||
|
||||
EXPORT void obs_hotkey_set_callback_routing_func(obs_hotkey_callback_router_func
|
||||
func, void *data);
|
||||
|
||||
EXPORT void obs_hotkey_trigger_routed_callback(obs_hotkey_id id, bool pressed);
|
||||
|
||||
/* hotkey callbacks won't be processed if callback rerouting is enabled and no
|
||||
* router func is set */
|
||||
EXPORT void obs_hotkey_enable_callback_rerouting(bool enable);
|
||||
|
||||
/* misc */
|
||||
|
||||
typedef void (*obs_hotkey_atomic_update_func)(void *);
|
||||
EXPORT void obs_hotkey_update_atomic(obs_hotkey_atomic_update_func func,
|
||||
void *data);
|
||||
|
||||
struct dstr;
|
||||
EXPORT void obs_key_to_str(obs_key_t key, struct dstr *str);
|
||||
EXPORT void obs_key_combination_to_str(obs_key_combination_t key,
|
||||
struct dstr *str);
|
||||
|
||||
EXPORT obs_key_t obs_key_from_virtual_key(int code);
|
||||
EXPORT int obs_key_to_virtual_key(obs_key_t key);
|
||||
|
||||
EXPORT const char *obs_key_to_name(obs_key_t key);
|
||||
EXPORT obs_key_t obs_key_from_name(const char *name);
|
||||
|
||||
inline bool obs_key_combination_is_empty(obs_key_combination_t combo)
|
||||
{
|
||||
return !combo.modifiers && combo.key == OBS_KEY_NONE;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
477
libobs/obs-hotkeys.h
Normal file
477
libobs/obs-hotkeys.h
Normal file
|
|
@ -0,0 +1,477 @@
|
|||
OBS_HOTKEY(OBS_KEY_NONE)
|
||||
|
||||
OBS_HOTKEY(OBS_KEY_RETURN)
|
||||
OBS_HOTKEY(OBS_KEY_ENTER)
|
||||
OBS_HOTKEY(OBS_KEY_ESCAPE)
|
||||
OBS_HOTKEY(OBS_KEY_TAB)
|
||||
OBS_HOTKEY(OBS_KEY_BACKTAB)
|
||||
OBS_HOTKEY(OBS_KEY_BACKSPACE)
|
||||
OBS_HOTKEY(OBS_KEY_INSERT)
|
||||
OBS_HOTKEY(OBS_KEY_DELETE)
|
||||
OBS_HOTKEY(OBS_KEY_PAUSE)
|
||||
OBS_HOTKEY(OBS_KEY_PRINT)
|
||||
OBS_HOTKEY(OBS_KEY_SYSREQ)
|
||||
OBS_HOTKEY(OBS_KEY_CLEAR)
|
||||
OBS_HOTKEY(OBS_KEY_HOME)
|
||||
OBS_HOTKEY(OBS_KEY_END)
|
||||
OBS_HOTKEY(OBS_KEY_LEFT)
|
||||
OBS_HOTKEY(OBS_KEY_UP)
|
||||
OBS_HOTKEY(OBS_KEY_RIGHT)
|
||||
OBS_HOTKEY(OBS_KEY_DOWN)
|
||||
OBS_HOTKEY(OBS_KEY_PAGEUP)
|
||||
OBS_HOTKEY(OBS_KEY_PAGEDOWN)
|
||||
|
||||
OBS_HOTKEY(OBS_KEY_SHIFT)
|
||||
OBS_HOTKEY(OBS_KEY_CONTROL)
|
||||
OBS_HOTKEY(OBS_KEY_META)
|
||||
OBS_HOTKEY(OBS_KEY_ALT)
|
||||
OBS_HOTKEY(OBS_KEY_ALTGR)
|
||||
OBS_HOTKEY(OBS_KEY_CAPSLOCK)
|
||||
OBS_HOTKEY(OBS_KEY_NUMLOCK)
|
||||
OBS_HOTKEY(OBS_KEY_SCROLLLOCK)
|
||||
|
||||
OBS_HOTKEY(OBS_KEY_F1)
|
||||
OBS_HOTKEY(OBS_KEY_F2)
|
||||
OBS_HOTKEY(OBS_KEY_F3)
|
||||
OBS_HOTKEY(OBS_KEY_F4)
|
||||
OBS_HOTKEY(OBS_KEY_F5)
|
||||
OBS_HOTKEY(OBS_KEY_F6)
|
||||
OBS_HOTKEY(OBS_KEY_F7)
|
||||
OBS_HOTKEY(OBS_KEY_F8)
|
||||
OBS_HOTKEY(OBS_KEY_F9)
|
||||
OBS_HOTKEY(OBS_KEY_F10)
|
||||
OBS_HOTKEY(OBS_KEY_F11)
|
||||
OBS_HOTKEY(OBS_KEY_F12)
|
||||
OBS_HOTKEY(OBS_KEY_F13)
|
||||
OBS_HOTKEY(OBS_KEY_F14)
|
||||
OBS_HOTKEY(OBS_KEY_F15)
|
||||
OBS_HOTKEY(OBS_KEY_F16)
|
||||
OBS_HOTKEY(OBS_KEY_F17)
|
||||
OBS_HOTKEY(OBS_KEY_F18)
|
||||
OBS_HOTKEY(OBS_KEY_F19)
|
||||
OBS_HOTKEY(OBS_KEY_F20)
|
||||
OBS_HOTKEY(OBS_KEY_F21)
|
||||
OBS_HOTKEY(OBS_KEY_F22)
|
||||
OBS_HOTKEY(OBS_KEY_F23)
|
||||
OBS_HOTKEY(OBS_KEY_F24)
|
||||
OBS_HOTKEY(OBS_KEY_F25)
|
||||
OBS_HOTKEY(OBS_KEY_F26)
|
||||
OBS_HOTKEY(OBS_KEY_F27)
|
||||
OBS_HOTKEY(OBS_KEY_F28)
|
||||
OBS_HOTKEY(OBS_KEY_F29)
|
||||
OBS_HOTKEY(OBS_KEY_F30)
|
||||
OBS_HOTKEY(OBS_KEY_F31)
|
||||
OBS_HOTKEY(OBS_KEY_F32)
|
||||
OBS_HOTKEY(OBS_KEY_F33)
|
||||
OBS_HOTKEY(OBS_KEY_F34)
|
||||
OBS_HOTKEY(OBS_KEY_F35)
|
||||
|
||||
OBS_HOTKEY(OBS_KEY_MENU)
|
||||
OBS_HOTKEY(OBS_KEY_HYPER_L)
|
||||
OBS_HOTKEY(OBS_KEY_HYPER_R)
|
||||
OBS_HOTKEY(OBS_KEY_HELP)
|
||||
OBS_HOTKEY(OBS_KEY_DIRECTION_L)
|
||||
OBS_HOTKEY(OBS_KEY_DIRECTION_R)
|
||||
|
||||
OBS_HOTKEY(OBS_KEY_SPACE)
|
||||
OBS_HOTKEY(OBS_KEY_EXCLAM)
|
||||
OBS_HOTKEY(OBS_KEY_QUOTEDBL)
|
||||
OBS_HOTKEY(OBS_KEY_NUMBERSIGN)
|
||||
OBS_HOTKEY(OBS_KEY_DOLLAR)
|
||||
OBS_HOTKEY(OBS_KEY_PERCENT)
|
||||
OBS_HOTKEY(OBS_KEY_AMPERSAND)
|
||||
OBS_HOTKEY(OBS_KEY_APOSTROPHE)
|
||||
OBS_HOTKEY(OBS_KEY_PARENLEFT)
|
||||
OBS_HOTKEY(OBS_KEY_PARENRIGHT)
|
||||
OBS_HOTKEY(OBS_KEY_ASTERISK)
|
||||
OBS_HOTKEY(OBS_KEY_PLUS)
|
||||
OBS_HOTKEY(OBS_KEY_COMMA)
|
||||
OBS_HOTKEY(OBS_KEY_MINUS)
|
||||
OBS_HOTKEY(OBS_KEY_PERIOD)
|
||||
OBS_HOTKEY(OBS_KEY_SLASH)
|
||||
OBS_HOTKEY(OBS_KEY_0)
|
||||
OBS_HOTKEY(OBS_KEY_1)
|
||||
OBS_HOTKEY(OBS_KEY_2)
|
||||
OBS_HOTKEY(OBS_KEY_3)
|
||||
OBS_HOTKEY(OBS_KEY_4)
|
||||
OBS_HOTKEY(OBS_KEY_5)
|
||||
OBS_HOTKEY(OBS_KEY_6)
|
||||
OBS_HOTKEY(OBS_KEY_7)
|
||||
OBS_HOTKEY(OBS_KEY_8)
|
||||
OBS_HOTKEY(OBS_KEY_9)
|
||||
OBS_HOTKEY(OBS_KEY_NUMEQUAL)
|
||||
OBS_HOTKEY(OBS_KEY_NUMASTERISK)
|
||||
OBS_HOTKEY(OBS_KEY_NUMPLUS)
|
||||
OBS_HOTKEY(OBS_KEY_NUMCOMMA)
|
||||
OBS_HOTKEY(OBS_KEY_NUMMINUS)
|
||||
OBS_HOTKEY(OBS_KEY_NUMPERIOD)
|
||||
OBS_HOTKEY(OBS_KEY_NUMSLASH)
|
||||
OBS_HOTKEY(OBS_KEY_NUM0)
|
||||
OBS_HOTKEY(OBS_KEY_NUM1)
|
||||
OBS_HOTKEY(OBS_KEY_NUM2)
|
||||
OBS_HOTKEY(OBS_KEY_NUM3)
|
||||
OBS_HOTKEY(OBS_KEY_NUM4)
|
||||
OBS_HOTKEY(OBS_KEY_NUM5)
|
||||
OBS_HOTKEY(OBS_KEY_NUM6)
|
||||
OBS_HOTKEY(OBS_KEY_NUM7)
|
||||
OBS_HOTKEY(OBS_KEY_NUM8)
|
||||
OBS_HOTKEY(OBS_KEY_NUM9)
|
||||
OBS_HOTKEY(OBS_KEY_COLON)
|
||||
OBS_HOTKEY(OBS_KEY_SEMICOLON)
|
||||
OBS_HOTKEY(OBS_KEY_QUOTE)
|
||||
OBS_HOTKEY(OBS_KEY_LESS)
|
||||
OBS_HOTKEY(OBS_KEY_EQUAL)
|
||||
OBS_HOTKEY(OBS_KEY_GREATER)
|
||||
OBS_HOTKEY(OBS_KEY_QUESTION)
|
||||
OBS_HOTKEY(OBS_KEY_AT)
|
||||
OBS_HOTKEY(OBS_KEY_A)
|
||||
OBS_HOTKEY(OBS_KEY_B)
|
||||
OBS_HOTKEY(OBS_KEY_C)
|
||||
OBS_HOTKEY(OBS_KEY_D)
|
||||
OBS_HOTKEY(OBS_KEY_E)
|
||||
OBS_HOTKEY(OBS_KEY_F)
|
||||
OBS_HOTKEY(OBS_KEY_G)
|
||||
OBS_HOTKEY(OBS_KEY_H)
|
||||
OBS_HOTKEY(OBS_KEY_I)
|
||||
OBS_HOTKEY(OBS_KEY_J)
|
||||
OBS_HOTKEY(OBS_KEY_K)
|
||||
OBS_HOTKEY(OBS_KEY_L)
|
||||
OBS_HOTKEY(OBS_KEY_M)
|
||||
OBS_HOTKEY(OBS_KEY_N)
|
||||
OBS_HOTKEY(OBS_KEY_O)
|
||||
OBS_HOTKEY(OBS_KEY_P)
|
||||
OBS_HOTKEY(OBS_KEY_Q)
|
||||
OBS_HOTKEY(OBS_KEY_R)
|
||||
OBS_HOTKEY(OBS_KEY_S)
|
||||
OBS_HOTKEY(OBS_KEY_T)
|
||||
OBS_HOTKEY(OBS_KEY_U)
|
||||
OBS_HOTKEY(OBS_KEY_V)
|
||||
OBS_HOTKEY(OBS_KEY_W)
|
||||
OBS_HOTKEY(OBS_KEY_X)
|
||||
OBS_HOTKEY(OBS_KEY_Y)
|
||||
OBS_HOTKEY(OBS_KEY_Z)
|
||||
OBS_HOTKEY(OBS_KEY_BRACKETLEFT)
|
||||
OBS_HOTKEY(OBS_KEY_BACKSLASH)
|
||||
OBS_HOTKEY(OBS_KEY_BRACKETRIGHT)
|
||||
OBS_HOTKEY(OBS_KEY_ASCIICIRCUM)
|
||||
OBS_HOTKEY(OBS_KEY_UNDERSCORE)
|
||||
OBS_HOTKEY(OBS_KEY_QUOTELEFT)
|
||||
OBS_HOTKEY(OBS_KEY_BRACELEFT)
|
||||
OBS_HOTKEY(OBS_KEY_BAR)
|
||||
OBS_HOTKEY(OBS_KEY_BRACERIGHT)
|
||||
OBS_HOTKEY(OBS_KEY_ASCIITILDE)
|
||||
OBS_HOTKEY(OBS_KEY_NOBREAKSPACE)
|
||||
OBS_HOTKEY(OBS_KEY_EXCLAMDOWN)
|
||||
OBS_HOTKEY(OBS_KEY_CENT)
|
||||
OBS_HOTKEY(OBS_KEY_STERLING)
|
||||
OBS_HOTKEY(OBS_KEY_CURRENCY)
|
||||
OBS_HOTKEY(OBS_KEY_YEN)
|
||||
OBS_HOTKEY(OBS_KEY_BROKENBAR)
|
||||
OBS_HOTKEY(OBS_KEY_SECTION)
|
||||
OBS_HOTKEY(OBS_KEY_DIAERESIS)
|
||||
OBS_HOTKEY(OBS_KEY_COPYRIGHT)
|
||||
OBS_HOTKEY(OBS_KEY_ORDFEMININE)
|
||||
OBS_HOTKEY(OBS_KEY_GUILLEMOTLEFT)
|
||||
OBS_HOTKEY(OBS_KEY_NOTSIGN)
|
||||
OBS_HOTKEY(OBS_KEY_HYPHEN)
|
||||
OBS_HOTKEY(OBS_KEY_REGISTERED)
|
||||
OBS_HOTKEY(OBS_KEY_MACRON)
|
||||
OBS_HOTKEY(OBS_KEY_DEGREE)
|
||||
OBS_HOTKEY(OBS_KEY_PLUSMINUS)
|
||||
OBS_HOTKEY(OBS_KEY_TWOSUPERIOR)
|
||||
OBS_HOTKEY(OBS_KEY_THREESUPERIOR)
|
||||
OBS_HOTKEY(OBS_KEY_ACUTE)
|
||||
OBS_HOTKEY(OBS_KEY_MU)
|
||||
OBS_HOTKEY(OBS_KEY_PARAGRAPH)
|
||||
OBS_HOTKEY(OBS_KEY_PERIODCENTERED)
|
||||
OBS_HOTKEY(OBS_KEY_CEDILLA)
|
||||
OBS_HOTKEY(OBS_KEY_ONESUPERIOR)
|
||||
OBS_HOTKEY(OBS_KEY_MASCULINE)
|
||||
OBS_HOTKEY(OBS_KEY_GUILLEMOTRIGHT)
|
||||
OBS_HOTKEY(OBS_KEY_ONEQUARTER)
|
||||
OBS_HOTKEY(OBS_KEY_ONEHALF)
|
||||
OBS_HOTKEY(OBS_KEY_THREEQUARTERS)
|
||||
OBS_HOTKEY(OBS_KEY_QUESTIONDOWN)
|
||||
OBS_HOTKEY(OBS_KEY_AGRAVE)
|
||||
OBS_HOTKEY(OBS_KEY_AACUTE)
|
||||
OBS_HOTKEY(OBS_KEY_ACIRCUMFLEX)
|
||||
OBS_HOTKEY(OBS_KEY_ATILDE)
|
||||
OBS_HOTKEY(OBS_KEY_ADIAERESIS)
|
||||
OBS_HOTKEY(OBS_KEY_ARING)
|
||||
OBS_HOTKEY(OBS_KEY_AE)
|
||||
OBS_HOTKEY(OBS_KEY_CCEDILLA)
|
||||
OBS_HOTKEY(OBS_KEY_EGRAVE)
|
||||
OBS_HOTKEY(OBS_KEY_EACUTE)
|
||||
OBS_HOTKEY(OBS_KEY_ECIRCUMFLEX)
|
||||
OBS_HOTKEY(OBS_KEY_EDIAERESIS)
|
||||
OBS_HOTKEY(OBS_KEY_IGRAVE)
|
||||
OBS_HOTKEY(OBS_KEY_IACUTE)
|
||||
OBS_HOTKEY(OBS_KEY_ICIRCUMFLEX)
|
||||
OBS_HOTKEY(OBS_KEY_IDIAERESIS)
|
||||
OBS_HOTKEY(OBS_KEY_ETH)
|
||||
OBS_HOTKEY(OBS_KEY_NTILDE)
|
||||
OBS_HOTKEY(OBS_KEY_OGRAVE)
|
||||
OBS_HOTKEY(OBS_KEY_OACUTE)
|
||||
OBS_HOTKEY(OBS_KEY_OCIRCUMFLEX)
|
||||
OBS_HOTKEY(OBS_KEY_OTILDE)
|
||||
OBS_HOTKEY(OBS_KEY_ODIAERESIS)
|
||||
OBS_HOTKEY(OBS_KEY_MULTIPLY)
|
||||
OBS_HOTKEY(OBS_KEY_OOBLIQUE)
|
||||
OBS_HOTKEY(OBS_KEY_UGRAVE)
|
||||
OBS_HOTKEY(OBS_KEY_UACUTE)
|
||||
OBS_HOTKEY(OBS_KEY_UCIRCUMFLEX)
|
||||
OBS_HOTKEY(OBS_KEY_UDIAERESIS)
|
||||
OBS_HOTKEY(OBS_KEY_YACUTE)
|
||||
OBS_HOTKEY(OBS_KEY_THORN)
|
||||
OBS_HOTKEY(OBS_KEY_SSHARP)
|
||||
OBS_HOTKEY(OBS_KEY_DIVISION)
|
||||
OBS_HOTKEY(OBS_KEY_YDIAERESIS)
|
||||
OBS_HOTKEY(OBS_KEY_MULTI_KEY)
|
||||
OBS_HOTKEY(OBS_KEY_CODEINPUT)
|
||||
OBS_HOTKEY(OBS_KEY_SINGLECANDIDATE)
|
||||
OBS_HOTKEY(OBS_KEY_MULTIPLECANDIDATE)
|
||||
OBS_HOTKEY(OBS_KEY_PREVIOUSCANDIDATE)
|
||||
OBS_HOTKEY(OBS_KEY_MODE_SWITCH)
|
||||
OBS_HOTKEY(OBS_KEY_KANJI)
|
||||
OBS_HOTKEY(OBS_KEY_MUHENKAN)
|
||||
OBS_HOTKEY(OBS_KEY_HENKAN)
|
||||
OBS_HOTKEY(OBS_KEY_ROMAJI)
|
||||
OBS_HOTKEY(OBS_KEY_HIRAGANA)
|
||||
OBS_HOTKEY(OBS_KEY_KATAKANA)
|
||||
OBS_HOTKEY(OBS_KEY_HIRAGANA_KATAKANA)
|
||||
OBS_HOTKEY(OBS_KEY_ZENKAKU)
|
||||
OBS_HOTKEY(OBS_KEY_HANKAKU)
|
||||
OBS_HOTKEY(OBS_KEY_ZENKAKU_HANKAKU)
|
||||
OBS_HOTKEY(OBS_KEY_TOUROKU)
|
||||
OBS_HOTKEY(OBS_KEY_MASSYO)
|
||||
OBS_HOTKEY(OBS_KEY_KANA_LOCK)
|
||||
OBS_HOTKEY(OBS_KEY_KANA_SHIFT)
|
||||
OBS_HOTKEY(OBS_KEY_EISU_SHIFT)
|
||||
OBS_HOTKEY(OBS_KEY_EISU_TOGGLE)
|
||||
OBS_HOTKEY(OBS_KEY_HANGUL)
|
||||
OBS_HOTKEY(OBS_KEY_HANGUL_START)
|
||||
OBS_HOTKEY(OBS_KEY_HANGUL_END)
|
||||
OBS_HOTKEY(OBS_KEY_HANGUL_HANJA)
|
||||
OBS_HOTKEY(OBS_KEY_HANGUL_JAMO)
|
||||
OBS_HOTKEY(OBS_KEY_HANGUL_ROMAJA)
|
||||
OBS_HOTKEY(OBS_KEY_HANGUL_JEONJA)
|
||||
OBS_HOTKEY(OBS_KEY_HANGUL_BANJA)
|
||||
OBS_HOTKEY(OBS_KEY_HANGUL_PREHANJA)
|
||||
OBS_HOTKEY(OBS_KEY_HANGUL_POSTHANJA)
|
||||
OBS_HOTKEY(OBS_KEY_HANGUL_SPECIAL)
|
||||
OBS_HOTKEY(OBS_KEY_DEAD_GRAVE)
|
||||
OBS_HOTKEY(OBS_KEY_DEAD_ACUTE)
|
||||
OBS_HOTKEY(OBS_KEY_DEAD_CIRCUMFLEX)
|
||||
OBS_HOTKEY(OBS_KEY_DEAD_TILDE)
|
||||
OBS_HOTKEY(OBS_KEY_DEAD_MACRON)
|
||||
OBS_HOTKEY(OBS_KEY_DEAD_BREVE)
|
||||
OBS_HOTKEY(OBS_KEY_DEAD_ABOVEDOT)
|
||||
OBS_HOTKEY(OBS_KEY_DEAD_DIAERESIS)
|
||||
OBS_HOTKEY(OBS_KEY_DEAD_ABOVERING)
|
||||
OBS_HOTKEY(OBS_KEY_DEAD_DOUBLEACUTE)
|
||||
OBS_HOTKEY(OBS_KEY_DEAD_CARON)
|
||||
OBS_HOTKEY(OBS_KEY_DEAD_CEDILLA)
|
||||
OBS_HOTKEY(OBS_KEY_DEAD_OGONEK)
|
||||
OBS_HOTKEY(OBS_KEY_DEAD_IOTA)
|
||||
OBS_HOTKEY(OBS_KEY_DEAD_VOICED_SOUND)
|
||||
OBS_HOTKEY(OBS_KEY_DEAD_SEMIVOICED_SOUND)
|
||||
OBS_HOTKEY(OBS_KEY_DEAD_BELOWDOT)
|
||||
OBS_HOTKEY(OBS_KEY_DEAD_HOOK)
|
||||
OBS_HOTKEY(OBS_KEY_DEAD_HORN)
|
||||
OBS_HOTKEY(OBS_KEY_BACK)
|
||||
OBS_HOTKEY(OBS_KEY_FORWARD)
|
||||
OBS_HOTKEY(OBS_KEY_STOP)
|
||||
OBS_HOTKEY(OBS_KEY_REFRESH)
|
||||
OBS_HOTKEY(OBS_KEY_VOLUMEDOWN)
|
||||
OBS_HOTKEY(OBS_KEY_VOLUMEMUTE)
|
||||
OBS_HOTKEY(OBS_KEY_VOLUMEUP)
|
||||
OBS_HOTKEY(OBS_KEY_BASSBOOST)
|
||||
OBS_HOTKEY(OBS_KEY_BASSUP)
|
||||
OBS_HOTKEY(OBS_KEY_BASSDOWN)
|
||||
OBS_HOTKEY(OBS_KEY_TREBLEUP)
|
||||
OBS_HOTKEY(OBS_KEY_TREBLEDOWN)
|
||||
OBS_HOTKEY(OBS_KEY_MEDIAPLAY)
|
||||
OBS_HOTKEY(OBS_KEY_MEDIASTOP)
|
||||
OBS_HOTKEY(OBS_KEY_MEDIAPREVIOUS)
|
||||
OBS_HOTKEY(OBS_KEY_MEDIANEXT)
|
||||
OBS_HOTKEY(OBS_KEY_MEDIARECORD)
|
||||
OBS_HOTKEY(OBS_KEY_MEDIAPAUSE)
|
||||
OBS_HOTKEY(OBS_KEY_MEDIATOGGLEPLAYPAUSE)
|
||||
OBS_HOTKEY(OBS_KEY_HOMEPAGE)
|
||||
OBS_HOTKEY(OBS_KEY_FAVORITES)
|
||||
OBS_HOTKEY(OBS_KEY_SEARCH)
|
||||
OBS_HOTKEY(OBS_KEY_STANDBY)
|
||||
OBS_HOTKEY(OBS_KEY_OPENURL)
|
||||
OBS_HOTKEY(OBS_KEY_LAUNCHMAIL)
|
||||
OBS_HOTKEY(OBS_KEY_LAUNCHMEDIA)
|
||||
OBS_HOTKEY(OBS_KEY_LAUNCH0)
|
||||
OBS_HOTKEY(OBS_KEY_LAUNCH1)
|
||||
OBS_HOTKEY(OBS_KEY_LAUNCH2)
|
||||
OBS_HOTKEY(OBS_KEY_LAUNCH3)
|
||||
OBS_HOTKEY(OBS_KEY_LAUNCH4)
|
||||
OBS_HOTKEY(OBS_KEY_LAUNCH5)
|
||||
OBS_HOTKEY(OBS_KEY_LAUNCH6)
|
||||
OBS_HOTKEY(OBS_KEY_LAUNCH7)
|
||||
OBS_HOTKEY(OBS_KEY_LAUNCH8)
|
||||
OBS_HOTKEY(OBS_KEY_LAUNCH9)
|
||||
OBS_HOTKEY(OBS_KEY_LAUNCHA)
|
||||
OBS_HOTKEY(OBS_KEY_LAUNCHB)
|
||||
OBS_HOTKEY(OBS_KEY_LAUNCHC)
|
||||
OBS_HOTKEY(OBS_KEY_LAUNCHD)
|
||||
OBS_HOTKEY(OBS_KEY_LAUNCHE)
|
||||
OBS_HOTKEY(OBS_KEY_LAUNCHF)
|
||||
OBS_HOTKEY(OBS_KEY_LAUNCHG)
|
||||
OBS_HOTKEY(OBS_KEY_LAUNCHH)
|
||||
OBS_HOTKEY(OBS_KEY_MONBRIGHTNESSUP)
|
||||
OBS_HOTKEY(OBS_KEY_MONBRIGHTNESSDOWN)
|
||||
OBS_HOTKEY(OBS_KEY_KEYBOARDLIGHTONOFF)
|
||||
OBS_HOTKEY(OBS_KEY_KEYBOARDBRIGHTNESSUP)
|
||||
OBS_HOTKEY(OBS_KEY_KEYBOARDBRIGHTNESSDOWN)
|
||||
OBS_HOTKEY(OBS_KEY_POWEROFF)
|
||||
OBS_HOTKEY(OBS_KEY_WAKEUP)
|
||||
OBS_HOTKEY(OBS_KEY_EJECT)
|
||||
OBS_HOTKEY(OBS_KEY_SCREENSAVER)
|
||||
OBS_HOTKEY(OBS_KEY_WWW)
|
||||
OBS_HOTKEY(OBS_KEY_MEMO)
|
||||
OBS_HOTKEY(OBS_KEY_LIGHTBULB)
|
||||
OBS_HOTKEY(OBS_KEY_SHOP)
|
||||
OBS_HOTKEY(OBS_KEY_HISTORY)
|
||||
OBS_HOTKEY(OBS_KEY_ADDFAVORITE)
|
||||
OBS_HOTKEY(OBS_KEY_HOTLINKS)
|
||||
OBS_HOTKEY(OBS_KEY_BRIGHTNESSADJUST)
|
||||
OBS_HOTKEY(OBS_KEY_FINANCE)
|
||||
OBS_HOTKEY(OBS_KEY_COMMUNITY)
|
||||
OBS_HOTKEY(OBS_KEY_AUDIOREWIND)
|
||||
OBS_HOTKEY(OBS_KEY_BACKFORWARD)
|
||||
OBS_HOTKEY(OBS_KEY_APPLICATIONLEFT)
|
||||
OBS_HOTKEY(OBS_KEY_APPLICATIONRIGHT)
|
||||
OBS_HOTKEY(OBS_KEY_BOOK)
|
||||
OBS_HOTKEY(OBS_KEY_CD)
|
||||
OBS_HOTKEY(OBS_KEY_CALCULATOR)
|
||||
OBS_HOTKEY(OBS_KEY_TODOLIST)
|
||||
OBS_HOTKEY(OBS_KEY_CLEARGRAB)
|
||||
OBS_HOTKEY(OBS_KEY_CLOSE)
|
||||
OBS_HOTKEY(OBS_KEY_COPY)
|
||||
OBS_HOTKEY(OBS_KEY_CUT)
|
||||
OBS_HOTKEY(OBS_KEY_DISPLAY)
|
||||
OBS_HOTKEY(OBS_KEY_DOS)
|
||||
OBS_HOTKEY(OBS_KEY_DOCUMENTS)
|
||||
OBS_HOTKEY(OBS_KEY_EXCEL)
|
||||
OBS_HOTKEY(OBS_KEY_EXPLORER)
|
||||
OBS_HOTKEY(OBS_KEY_GAME)
|
||||
OBS_HOTKEY(OBS_KEY_GO)
|
||||
OBS_HOTKEY(OBS_KEY_ITOUCH)
|
||||
OBS_HOTKEY(OBS_KEY_LOGOFF)
|
||||
OBS_HOTKEY(OBS_KEY_MARKET)
|
||||
OBS_HOTKEY(OBS_KEY_MEETING)
|
||||
OBS_HOTKEY(OBS_KEY_MENUKB)
|
||||
OBS_HOTKEY(OBS_KEY_MENUPB)
|
||||
OBS_HOTKEY(OBS_KEY_MYSITES)
|
||||
OBS_HOTKEY(OBS_KEY_NEWS)
|
||||
OBS_HOTKEY(OBS_KEY_OFFICEHOME)
|
||||
OBS_HOTKEY(OBS_KEY_OPTION)
|
||||
OBS_HOTKEY(OBS_KEY_PASTE)
|
||||
OBS_HOTKEY(OBS_KEY_PHONE)
|
||||
OBS_HOTKEY(OBS_KEY_CALENDAR)
|
||||
OBS_HOTKEY(OBS_KEY_REPLY)
|
||||
OBS_HOTKEY(OBS_KEY_RELOAD)
|
||||
OBS_HOTKEY(OBS_KEY_ROTATEWINDOWS)
|
||||
OBS_HOTKEY(OBS_KEY_ROTATIONPB)
|
||||
OBS_HOTKEY(OBS_KEY_ROTATIONKB)
|
||||
OBS_HOTKEY(OBS_KEY_SAVE)
|
||||
OBS_HOTKEY(OBS_KEY_SEND)
|
||||
OBS_HOTKEY(OBS_KEY_SPELL)
|
||||
OBS_HOTKEY(OBS_KEY_SPLITSCREEN)
|
||||
OBS_HOTKEY(OBS_KEY_SUPPORT)
|
||||
OBS_HOTKEY(OBS_KEY_TASKPANE)
|
||||
OBS_HOTKEY(OBS_KEY_TERMINAL)
|
||||
OBS_HOTKEY(OBS_KEY_TOOLS)
|
||||
OBS_HOTKEY(OBS_KEY_TRAVEL)
|
||||
OBS_HOTKEY(OBS_KEY_VIDEO)
|
||||
OBS_HOTKEY(OBS_KEY_WORD)
|
||||
OBS_HOTKEY(OBS_KEY_XFER)
|
||||
OBS_HOTKEY(OBS_KEY_ZOOMIN)
|
||||
OBS_HOTKEY(OBS_KEY_ZOOMOUT)
|
||||
OBS_HOTKEY(OBS_KEY_AWAY)
|
||||
OBS_HOTKEY(OBS_KEY_MESSENGER)
|
||||
OBS_HOTKEY(OBS_KEY_WEBCAM)
|
||||
OBS_HOTKEY(OBS_KEY_MAILFORWARD)
|
||||
OBS_HOTKEY(OBS_KEY_PICTURES)
|
||||
OBS_HOTKEY(OBS_KEY_MUSIC)
|
||||
OBS_HOTKEY(OBS_KEY_BATTERY)
|
||||
OBS_HOTKEY(OBS_KEY_BLUETOOTH)
|
||||
OBS_HOTKEY(OBS_KEY_WLAN)
|
||||
OBS_HOTKEY(OBS_KEY_UWB)
|
||||
OBS_HOTKEY(OBS_KEY_AUDIOFORWARD)
|
||||
OBS_HOTKEY(OBS_KEY_AUDIOREPEAT)
|
||||
OBS_HOTKEY(OBS_KEY_AUDIORANDOMPLAY)
|
||||
OBS_HOTKEY(OBS_KEY_SUBTITLE)
|
||||
OBS_HOTKEY(OBS_KEY_AUDIOCYCLETRACK)
|
||||
OBS_HOTKEY(OBS_KEY_TIME)
|
||||
OBS_HOTKEY(OBS_KEY_HIBERNATE)
|
||||
OBS_HOTKEY(OBS_KEY_VIEW)
|
||||
OBS_HOTKEY(OBS_KEY_TOPMENU)
|
||||
OBS_HOTKEY(OBS_KEY_POWERDOWN)
|
||||
OBS_HOTKEY(OBS_KEY_SUSPEND)
|
||||
OBS_HOTKEY(OBS_KEY_CONTRASTADJUST)
|
||||
OBS_HOTKEY(OBS_KEY_MEDIALAST)
|
||||
OBS_HOTKEY(OBS_KEY_CALL)
|
||||
OBS_HOTKEY(OBS_KEY_CAMERA)
|
||||
OBS_HOTKEY(OBS_KEY_CAMERAFOCUS)
|
||||
OBS_HOTKEY(OBS_KEY_CONTEXT1)
|
||||
OBS_HOTKEY(OBS_KEY_CONTEXT2)
|
||||
OBS_HOTKEY(OBS_KEY_CONTEXT3)
|
||||
OBS_HOTKEY(OBS_KEY_CONTEXT4)
|
||||
OBS_HOTKEY(OBS_KEY_FLIP)
|
||||
OBS_HOTKEY(OBS_KEY_HANGUP)
|
||||
OBS_HOTKEY(OBS_KEY_NO)
|
||||
OBS_HOTKEY(OBS_KEY_SELECT)
|
||||
OBS_HOTKEY(OBS_KEY_YES)
|
||||
OBS_HOTKEY(OBS_KEY_TOGGLECALLHANGUP)
|
||||
OBS_HOTKEY(OBS_KEY_VOICEDIAL)
|
||||
OBS_HOTKEY(OBS_KEY_LASTNUMBERREDIAL)
|
||||
OBS_HOTKEY(OBS_KEY_EXECUTE)
|
||||
OBS_HOTKEY(OBS_KEY_PRINTER)
|
||||
OBS_HOTKEY(OBS_KEY_PLAY)
|
||||
OBS_HOTKEY(OBS_KEY_SLEEP)
|
||||
OBS_HOTKEY(OBS_KEY_ZOOM)
|
||||
OBS_HOTKEY(OBS_KEY_CANCEL)
|
||||
|
||||
|
||||
#ifndef OBS_MOUSE_BUTTON
|
||||
#define OBS_MOUSE_BUTTON(x) OBS_HOTKEY(x)
|
||||
#define OBS_MOUSE_BUTTON_DEFAULT 1
|
||||
#endif
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE1)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE2)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE3)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE4)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE5)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE6)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE7)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE8)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE9)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE10)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE11)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE12)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE13)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE14)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE15)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE16)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE17)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE18)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE19)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE20)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE21)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE22)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE23)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE24)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE25)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE26)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE27)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE28)
|
||||
OBS_MOUSE_BUTTON(OBS_KEY_MOUSE29)
|
||||
#ifdef OBS_MOUSE_BUTTON_DEFAULT
|
||||
#undef OBS_MOUSE_BUTTON
|
||||
#undef OBS_MOUSE_BUTTON_DEFAULT
|
||||
#endif
|
||||
56
libobs/obs-interaction.h
Normal file
56
libobs/obs-interaction.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2014 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/c99defs.h"
|
||||
|
||||
enum obs_interaction_flags {
|
||||
INTERACT_NONE = 0,
|
||||
INTERACT_CAPS_KEY = 1,
|
||||
INTERACT_SHIFT_KEY = 1 << 1,
|
||||
INTERACT_CONTROL_KEY = 1 << 2,
|
||||
INTERACT_ALT_KEY = 1 << 3,
|
||||
INTERACT_MOUSE_LEFT = 1 << 4,
|
||||
INTERACT_MOUSE_MIDDLE = 1 << 5,
|
||||
INTERACT_MOUSE_RIGHT = 1 << 6,
|
||||
INTERACT_COMMAND_KEY = 1 << 7,
|
||||
INTERACT_NUMLOCK_KEY = 1 << 8,
|
||||
INTERACT_IS_KEY_PAD = 1 << 9,
|
||||
INTERACT_IS_LEFT = 1 << 10,
|
||||
INTERACT_IS_RIGHT = 1 << 11
|
||||
};
|
||||
|
||||
enum obs_mouse_button_type {
|
||||
MOUSE_LEFT,
|
||||
MOUSE_MIDDLE,
|
||||
MOUSE_RIGHT
|
||||
};
|
||||
|
||||
struct obs_mouse_event {
|
||||
uint32_t modifiers;
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
};
|
||||
|
||||
struct obs_key_event {
|
||||
uint32_t modifiers;
|
||||
char *text;
|
||||
uint32_t native_modifiers;
|
||||
uint32_t native_scancode;
|
||||
uint32_t native_vkey;
|
||||
};
|
||||
916
libobs/obs-internal.h
Normal file
916
libobs/obs-internal.h
Normal file
|
|
@ -0,0 +1,916 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013-2014 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/c99defs.h"
|
||||
#include "util/darray.h"
|
||||
#include "util/circlebuf.h"
|
||||
#include "util/dstr.h"
|
||||
#include "util/threading.h"
|
||||
#include "util/platform.h"
|
||||
#include "util/profiler.h"
|
||||
#include "callback/signal.h"
|
||||
#include "callback/proc.h"
|
||||
|
||||
#include "graphics/graphics.h"
|
||||
#include "graphics/matrix4.h"
|
||||
|
||||
#include "media-io/audio-resampler.h"
|
||||
#include "media-io/video-io.h"
|
||||
#include "media-io/audio-io.h"
|
||||
|
||||
#include "obs.h"
|
||||
|
||||
#define NUM_TEXTURES 2
|
||||
#define MICROSECOND_DEN 1000000
|
||||
|
||||
static inline int64_t packet_dts_usec(struct encoder_packet *packet)
|
||||
{
|
||||
return packet->dts * MICROSECOND_DEN / packet->timebase_den;
|
||||
}
|
||||
|
||||
struct draw_callback {
|
||||
void (*draw)(void *param, uint32_t cx, uint32_t cy);
|
||||
void *param;
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* validity checks */
|
||||
|
||||
static inline bool obs_object_valid(const void *obj, const char *f,
|
||||
const char *t)
|
||||
{
|
||||
if (!obj) {
|
||||
blog(LOG_DEBUG, "%s: Null '%s' parameter", f, t);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define obs_ptr_valid(ptr, func) obs_object_valid(ptr, func, #ptr)
|
||||
#define obs_source_valid obs_ptr_valid
|
||||
#define obs_output_valid obs_ptr_valid
|
||||
#define obs_encoder_valid obs_ptr_valid
|
||||
#define obs_service_valid obs_ptr_valid
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* modules */
|
||||
|
||||
struct obs_module {
|
||||
char *mod_name;
|
||||
const char *file;
|
||||
char *bin_path;
|
||||
char *data_path;
|
||||
void *module;
|
||||
bool loaded;
|
||||
|
||||
bool (*load)(void);
|
||||
void (*unload)(void);
|
||||
void (*set_locale)(const char *locale);
|
||||
void (*free_locale)(void);
|
||||
uint32_t (*ver)(void);
|
||||
void (*set_pointer)(obs_module_t *module);
|
||||
const char *(*name)(void);
|
||||
const char *(*description)(void);
|
||||
const char *(*author)(void);
|
||||
|
||||
struct obs_module *next;
|
||||
};
|
||||
|
||||
extern void free_module(struct obs_module *mod);
|
||||
|
||||
struct obs_module_path {
|
||||
char *bin;
|
||||
char *data;
|
||||
};
|
||||
|
||||
static inline void free_module_path(struct obs_module_path *omp)
|
||||
{
|
||||
if (omp) {
|
||||
bfree(omp->bin);
|
||||
bfree(omp->data);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool check_path(const char *data, const char *path,
|
||||
struct dstr *output)
|
||||
{
|
||||
dstr_copy(output, path);
|
||||
dstr_cat(output, data);
|
||||
|
||||
return os_file_exists(output->array);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* hotkeys */
|
||||
|
||||
struct obs_hotkey {
|
||||
obs_hotkey_id id;
|
||||
char *name;
|
||||
char *description;
|
||||
|
||||
obs_hotkey_func func;
|
||||
void *data;
|
||||
int pressed;
|
||||
|
||||
obs_hotkey_registerer_t registerer_type;
|
||||
void *registerer;
|
||||
|
||||
obs_hotkey_id pair_partner_id;
|
||||
};
|
||||
|
||||
struct obs_hotkey_pair {
|
||||
obs_hotkey_pair_id pair_id;
|
||||
obs_hotkey_id id[2];
|
||||
obs_hotkey_active_func func[2];
|
||||
bool pressed0 : 1;
|
||||
bool pressed1 : 1;
|
||||
void *data[2];
|
||||
};
|
||||
|
||||
typedef struct obs_hotkey_pair obs_hotkey_pair_t;
|
||||
|
||||
typedef struct obs_hotkeys_platform obs_hotkeys_platform_t;
|
||||
|
||||
void *obs_hotkey_thread(void *param);
|
||||
|
||||
struct obs_core_hotkeys;
|
||||
bool obs_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys);
|
||||
void obs_hotkeys_platform_free(struct obs_core_hotkeys *hotkeys);
|
||||
bool obs_hotkeys_platform_is_pressed(obs_hotkeys_platform_t *context,
|
||||
obs_key_t key);
|
||||
|
||||
const char *obs_get_hotkey_translation(obs_key_t key, const char *def);
|
||||
|
||||
struct obs_context_data;
|
||||
void obs_hotkeys_context_release(struct obs_context_data *context);
|
||||
|
||||
void obs_hotkeys_free(void);
|
||||
|
||||
struct obs_hotkey_binding {
|
||||
obs_key_combination_t key;
|
||||
bool pressed : 1;
|
||||
bool modifiers_match : 1;
|
||||
|
||||
obs_hotkey_id hotkey_id;
|
||||
obs_hotkey_t *hotkey;
|
||||
};
|
||||
|
||||
struct obs_hotkey_name_map;
|
||||
void obs_hotkey_name_map_free(void);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* views */
|
||||
|
||||
struct obs_view {
|
||||
pthread_mutex_t channels_mutex;
|
||||
obs_source_t *channels[MAX_CHANNELS];
|
||||
};
|
||||
|
||||
extern bool obs_view_init(struct obs_view *view);
|
||||
extern void obs_view_free(struct obs_view *view);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* displays */
|
||||
|
||||
struct obs_display {
|
||||
bool size_changed;
|
||||
bool enabled;
|
||||
uint32_t cx, cy;
|
||||
uint32_t background_color;
|
||||
gs_swapchain_t *swap;
|
||||
pthread_mutex_t draw_callbacks_mutex;
|
||||
DARRAY(struct draw_callback) draw_callbacks;
|
||||
|
||||
struct obs_display *next;
|
||||
struct obs_display **prev_next;
|
||||
};
|
||||
|
||||
extern bool obs_display_init(struct obs_display *display,
|
||||
const struct gs_init_data *graphics_data);
|
||||
extern void obs_display_free(struct obs_display *display);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* core */
|
||||
|
||||
struct obs_vframe_info {
|
||||
uint64_t timestamp;
|
||||
int count;
|
||||
};
|
||||
|
||||
struct obs_core_video {
|
||||
graphics_t *graphics;
|
||||
gs_stagesurf_t *copy_surfaces[NUM_TEXTURES];
|
||||
gs_texture_t *render_textures[NUM_TEXTURES];
|
||||
gs_texture_t *output_textures[NUM_TEXTURES];
|
||||
gs_texture_t *convert_textures[NUM_TEXTURES];
|
||||
bool textures_rendered[NUM_TEXTURES];
|
||||
bool textures_output[NUM_TEXTURES];
|
||||
bool textures_copied[NUM_TEXTURES];
|
||||
bool textures_converted[NUM_TEXTURES];
|
||||
struct circlebuf vframe_info_buffer;
|
||||
gs_effect_t *default_effect;
|
||||
gs_effect_t *default_rect_effect;
|
||||
gs_effect_t *opaque_effect;
|
||||
gs_effect_t *solid_effect;
|
||||
gs_effect_t *conversion_effect;
|
||||
gs_effect_t *bicubic_effect;
|
||||
gs_effect_t *lanczos_effect;
|
||||
gs_effect_t *bilinear_lowres_effect;
|
||||
gs_stagesurf_t *mapped_surface;
|
||||
int cur_texture;
|
||||
|
||||
uint64_t video_time;
|
||||
video_t *video;
|
||||
pthread_t video_thread;
|
||||
uint32_t total_frames;
|
||||
uint32_t lagged_frames;
|
||||
bool thread_initialized;
|
||||
|
||||
bool gpu_conversion;
|
||||
const char *conversion_tech;
|
||||
uint32_t conversion_height;
|
||||
uint32_t plane_offsets[3];
|
||||
uint32_t plane_sizes[3];
|
||||
uint32_t plane_linewidth[3];
|
||||
|
||||
uint32_t output_width;
|
||||
uint32_t output_height;
|
||||
uint32_t base_width;
|
||||
uint32_t base_height;
|
||||
float color_matrix[16];
|
||||
enum obs_scale_type scale_type;
|
||||
|
||||
gs_texture_t *transparent_texture;
|
||||
};
|
||||
|
||||
struct obs_core_audio {
|
||||
/* TODO: sound output subsystem */
|
||||
audio_t *audio;
|
||||
|
||||
DARRAY(struct obs_source*) render_order;
|
||||
DARRAY(struct obs_source*) root_nodes;
|
||||
|
||||
uint64_t buffered_ts;
|
||||
struct circlebuf buffered_timestamps;
|
||||
int buffering_wait_ticks;
|
||||
int total_buffering_ticks;
|
||||
|
||||
float user_volume;
|
||||
};
|
||||
|
||||
/* user sources, output channels, and displays */
|
||||
struct obs_core_data {
|
||||
struct obs_source *first_source;
|
||||
struct obs_source *first_audio_source;
|
||||
struct obs_display *first_display;
|
||||
struct obs_output *first_output;
|
||||
struct obs_encoder *first_encoder;
|
||||
struct obs_service *first_service;
|
||||
|
||||
pthread_mutex_t sources_mutex;
|
||||
pthread_mutex_t displays_mutex;
|
||||
pthread_mutex_t outputs_mutex;
|
||||
pthread_mutex_t encoders_mutex;
|
||||
pthread_mutex_t services_mutex;
|
||||
pthread_mutex_t audio_sources_mutex;
|
||||
|
||||
struct obs_view main_view;
|
||||
|
||||
long long unnamed_index;
|
||||
|
||||
volatile bool valid;
|
||||
};
|
||||
|
||||
/* user hotkeys */
|
||||
struct obs_core_hotkeys {
|
||||
pthread_mutex_t mutex;
|
||||
DARRAY(obs_hotkey_t) hotkeys;
|
||||
obs_hotkey_id next_id;
|
||||
DARRAY(obs_hotkey_pair_t) hotkey_pairs;
|
||||
obs_hotkey_pair_id next_pair_id;
|
||||
|
||||
pthread_t hotkey_thread;
|
||||
bool hotkey_thread_initialized;
|
||||
os_event_t *stop_event;
|
||||
bool thread_disable_press : 1;
|
||||
bool strict_modifiers : 1;
|
||||
bool reroute_hotkeys : 1;
|
||||
DARRAY(obs_hotkey_binding_t) bindings;
|
||||
|
||||
obs_hotkey_callback_router_func router_func;
|
||||
void *router_func_data;
|
||||
|
||||
obs_hotkeys_platform_t *platform_context;
|
||||
|
||||
pthread_once_t name_map_init_token;
|
||||
struct obs_hotkey_name_map *name_map;
|
||||
|
||||
signal_handler_t *signals;
|
||||
|
||||
char *translations[OBS_KEY_LAST_VALUE];
|
||||
char *mute;
|
||||
char *unmute;
|
||||
char *push_to_mute;
|
||||
char *push_to_talk;
|
||||
char *sceneitem_show;
|
||||
char *sceneitem_hide;
|
||||
};
|
||||
|
||||
struct obs_core {
|
||||
struct obs_module *first_module;
|
||||
DARRAY(struct obs_module_path) module_paths;
|
||||
|
||||
DARRAY(struct obs_source_info) source_types;
|
||||
DARRAY(struct obs_source_info) input_types;
|
||||
DARRAY(struct obs_source_info) filter_types;
|
||||
DARRAY(struct obs_source_info) transition_types;
|
||||
DARRAY(struct obs_output_info) output_types;
|
||||
DARRAY(struct obs_encoder_info) encoder_types;
|
||||
DARRAY(struct obs_service_info) service_types;
|
||||
DARRAY(struct obs_modal_ui) modal_ui_callbacks;
|
||||
DARRAY(struct obs_modeless_ui) modeless_ui_callbacks;
|
||||
|
||||
signal_handler_t *signals;
|
||||
proc_handler_t *procs;
|
||||
|
||||
char *locale;
|
||||
char *module_config_path;
|
||||
bool name_store_owned;
|
||||
profiler_name_store_t *name_store;
|
||||
|
||||
/* segmented into multiple sub-structures to keep things a bit more
|
||||
* clean and organized */
|
||||
struct obs_core_video video;
|
||||
struct obs_core_audio audio;
|
||||
struct obs_core_data data;
|
||||
struct obs_core_hotkeys hotkeys;
|
||||
};
|
||||
|
||||
extern struct obs_core *obs;
|
||||
|
||||
extern void *obs_video_thread(void *param);
|
||||
|
||||
extern bool audio_callback(void *param,
|
||||
uint64_t start_ts_in, uint64_t end_ts_in, uint64_t *out_ts,
|
||||
uint32_t mixers, struct audio_output_data *mixes);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* obs shared context data */
|
||||
|
||||
struct obs_context_data {
|
||||
char *name;
|
||||
void *data;
|
||||
obs_data_t *settings;
|
||||
signal_handler_t *signals;
|
||||
proc_handler_t *procs;
|
||||
|
||||
DARRAY(obs_hotkey_id) hotkeys;
|
||||
DARRAY(obs_hotkey_pair_id) hotkey_pairs;
|
||||
obs_data_t *hotkey_data;
|
||||
|
||||
DARRAY(char*) rename_cache;
|
||||
pthread_mutex_t rename_cache_mutex;
|
||||
|
||||
pthread_mutex_t *mutex;
|
||||
struct obs_context_data *next;
|
||||
struct obs_context_data **prev_next;
|
||||
|
||||
bool private;
|
||||
};
|
||||
|
||||
extern bool obs_context_data_init(
|
||||
struct obs_context_data *context,
|
||||
obs_data_t *settings,
|
||||
const char *name,
|
||||
obs_data_t *hotkey_data,
|
||||
bool private);
|
||||
extern void obs_context_data_free(struct obs_context_data *context);
|
||||
|
||||
extern void obs_context_data_insert(struct obs_context_data *context,
|
||||
pthread_mutex_t *mutex, void *first);
|
||||
extern void obs_context_data_remove(struct obs_context_data *context);
|
||||
|
||||
extern void obs_context_data_setname(struct obs_context_data *context,
|
||||
const char *name);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* ref-counting */
|
||||
|
||||
struct obs_weak_ref {
|
||||
volatile long refs;
|
||||
volatile long weak_refs;
|
||||
};
|
||||
|
||||
static inline void obs_ref_addref(struct obs_weak_ref *ref)
|
||||
{
|
||||
os_atomic_inc_long(&ref->refs);
|
||||
}
|
||||
|
||||
static inline bool obs_ref_release(struct obs_weak_ref *ref)
|
||||
{
|
||||
return os_atomic_dec_long(&ref->refs) == -1;
|
||||
}
|
||||
|
||||
static inline void obs_weak_ref_addref(struct obs_weak_ref *ref)
|
||||
{
|
||||
os_atomic_inc_long(&ref->weak_refs);
|
||||
}
|
||||
|
||||
static inline bool obs_weak_ref_release(struct obs_weak_ref *ref)
|
||||
{
|
||||
return os_atomic_dec_long(&ref->weak_refs) == -1;
|
||||
}
|
||||
|
||||
static inline bool obs_weak_ref_get_ref(struct obs_weak_ref *ref)
|
||||
{
|
||||
long owners = ref->refs;
|
||||
while (owners > -1) {
|
||||
if (os_atomic_compare_swap_long(&ref->refs, owners, owners + 1))
|
||||
return true;
|
||||
|
||||
owners = ref->refs;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* sources */
|
||||
|
||||
struct async_frame {
|
||||
struct obs_source_frame *frame;
|
||||
long unused_count;
|
||||
bool used;
|
||||
};
|
||||
|
||||
enum audio_action_type {
|
||||
AUDIO_ACTION_VOL,
|
||||
AUDIO_ACTION_MUTE,
|
||||
AUDIO_ACTION_PTT,
|
||||
AUDIO_ACTION_PTM,
|
||||
};
|
||||
|
||||
struct audio_action {
|
||||
uint64_t timestamp;
|
||||
enum audio_action_type type;
|
||||
union {
|
||||
float vol;
|
||||
bool set;
|
||||
};
|
||||
};
|
||||
|
||||
struct obs_weak_source {
|
||||
struct obs_weak_ref ref;
|
||||
struct obs_source *source;
|
||||
};
|
||||
|
||||
struct audio_cb_info {
|
||||
obs_source_audio_capture_t callback;
|
||||
void *param;
|
||||
};
|
||||
|
||||
struct obs_source {
|
||||
struct obs_context_data context;
|
||||
struct obs_source_info info;
|
||||
struct obs_weak_source *control;
|
||||
|
||||
/* general exposed flags that can be set for the source */
|
||||
uint32_t flags;
|
||||
uint32_t default_flags;
|
||||
|
||||
/* indicates ownership of the info.id buffer */
|
||||
bool owns_info_id;
|
||||
|
||||
/* signals to call the source update in the video thread */
|
||||
bool defer_update;
|
||||
|
||||
/* ensures show/hide are only called once */
|
||||
volatile long show_refs;
|
||||
|
||||
/* ensures activate/deactivate are only called once */
|
||||
volatile long activate_refs;
|
||||
|
||||
/* used to indicate that the source has been removed and all
|
||||
* references to it should be released (not exactly how I would prefer
|
||||
* to handle things but it's the best option) */
|
||||
bool removed;
|
||||
|
||||
bool active;
|
||||
bool showing;
|
||||
|
||||
/* used to temporarily disable sources if needed */
|
||||
bool enabled;
|
||||
|
||||
/* timing (if video is present, is based upon video) */
|
||||
volatile bool timing_set;
|
||||
volatile uint64_t timing_adjust;
|
||||
uint64_t resample_offset;
|
||||
uint64_t next_audio_ts_min;
|
||||
uint64_t next_audio_sys_ts_min;
|
||||
uint64_t last_frame_ts;
|
||||
uint64_t last_sys_timestamp;
|
||||
bool async_rendered;
|
||||
|
||||
/* audio */
|
||||
bool audio_failed;
|
||||
bool audio_pending;
|
||||
bool user_muted;
|
||||
bool muted;
|
||||
struct obs_source *next_audio_source;
|
||||
struct obs_source **prev_next_audio_source;
|
||||
uint64_t audio_ts;
|
||||
struct circlebuf audio_input_buf[MAX_AUDIO_CHANNELS];
|
||||
size_t last_audio_input_buf_size;
|
||||
DARRAY(struct audio_action) audio_actions;
|
||||
float *audio_output_buf[MAX_AUDIO_MIXES][MAX_AUDIO_CHANNELS];
|
||||
struct resample_info sample_info;
|
||||
audio_resampler_t *resampler;
|
||||
pthread_mutex_t audio_actions_mutex;
|
||||
pthread_mutex_t audio_buf_mutex;
|
||||
pthread_mutex_t audio_mutex;
|
||||
pthread_mutex_t audio_cb_mutex;
|
||||
DARRAY(struct audio_cb_info) audio_cb_list;
|
||||
struct obs_audio_data audio_data;
|
||||
size_t audio_storage_size;
|
||||
uint32_t audio_mixers;
|
||||
float user_volume;
|
||||
float volume;
|
||||
int64_t sync_offset;
|
||||
int64_t last_sync_offset;
|
||||
|
||||
/* async video data */
|
||||
gs_texture_t *async_texture;
|
||||
gs_texrender_t *async_convert_texrender;
|
||||
struct obs_source_frame *cur_async_frame;
|
||||
bool async_gpu_conversion;
|
||||
enum video_format async_format;
|
||||
enum video_format async_cache_format;
|
||||
enum gs_color_format async_texture_format;
|
||||
float async_color_matrix[16];
|
||||
bool async_full_range;
|
||||
float async_color_range_min[3];
|
||||
float async_color_range_max[3];
|
||||
int async_plane_offset[2];
|
||||
bool async_flip;
|
||||
bool async_active;
|
||||
DARRAY(struct async_frame) async_cache;
|
||||
DARRAY(struct obs_source_frame*)async_frames;
|
||||
pthread_mutex_t async_mutex;
|
||||
uint32_t async_width;
|
||||
uint32_t async_height;
|
||||
uint32_t async_cache_width;
|
||||
uint32_t async_cache_height;
|
||||
uint32_t async_convert_width;
|
||||
uint32_t async_convert_height;
|
||||
|
||||
/* filters */
|
||||
struct obs_source *filter_parent;
|
||||
struct obs_source *filter_target;
|
||||
DARRAY(struct obs_source*) filters;
|
||||
pthread_mutex_t filter_mutex;
|
||||
gs_texrender_t *filter_texrender;
|
||||
enum obs_allow_direct_render allow_direct;
|
||||
bool rendering_filter;
|
||||
|
||||
/* sources specific hotkeys */
|
||||
obs_hotkey_pair_id mute_unmute_key;
|
||||
obs_hotkey_id push_to_mute_key;
|
||||
obs_hotkey_id push_to_talk_key;
|
||||
bool push_to_mute_enabled : 1;
|
||||
bool push_to_mute_pressed : 1;
|
||||
bool user_push_to_mute_pressed : 1;
|
||||
bool push_to_talk_enabled : 1;
|
||||
bool push_to_talk_pressed : 1;
|
||||
bool user_push_to_talk_pressed : 1;
|
||||
uint64_t push_to_mute_delay;
|
||||
uint64_t push_to_mute_stop_time;
|
||||
uint64_t push_to_talk_delay;
|
||||
uint64_t push_to_talk_stop_time;
|
||||
|
||||
/* transitions */
|
||||
uint64_t transition_start_time;
|
||||
uint64_t transition_duration;
|
||||
pthread_mutex_t transition_tex_mutex;
|
||||
gs_texrender_t *transition_texrender[2];
|
||||
pthread_mutex_t transition_mutex;
|
||||
obs_source_t *transition_sources[2];
|
||||
bool transitioning_video;
|
||||
bool transitioning_audio;
|
||||
bool transition_source_active[2];
|
||||
uint32_t transition_alignment;
|
||||
uint32_t transition_actual_cx;
|
||||
uint32_t transition_actual_cy;
|
||||
uint32_t transition_cx;
|
||||
uint32_t transition_cy;
|
||||
uint32_t transition_fixed_duration;
|
||||
bool transition_use_fixed_duration : 1;
|
||||
enum obs_transition_mode transition_mode;
|
||||
enum obs_transition_scale_type transition_scale_type;
|
||||
struct matrix4 transition_matrices[2];
|
||||
};
|
||||
|
||||
extern const struct obs_source_info *get_source_info(const char *id);
|
||||
extern bool obs_source_init_context(struct obs_source *source,
|
||||
obs_data_t *settings, const char *name,
|
||||
obs_data_t *hotkey_data, bool private);
|
||||
|
||||
extern void obs_source_save(obs_source_t *source);
|
||||
extern void obs_source_load(obs_source_t *source);
|
||||
|
||||
extern bool obs_transition_init(obs_source_t *transition);
|
||||
extern void obs_transition_free(obs_source_t *transition);
|
||||
extern void obs_transition_tick(obs_source_t *transition);
|
||||
extern void obs_transition_enum_sources(obs_source_t *transition,
|
||||
obs_source_enum_proc_t enum_callback, void *param);
|
||||
extern void obs_transition_save(obs_source_t *source, obs_data_t *data);
|
||||
extern void obs_transition_load(obs_source_t *source, obs_data_t *data);
|
||||
|
||||
extern void obs_source_destroy(struct obs_source *source);
|
||||
|
||||
enum view_type {
|
||||
MAIN_VIEW,
|
||||
AUX_VIEW
|
||||
};
|
||||
|
||||
static inline void obs_source_dosignal(struct obs_source *source,
|
||||
const char *signal_obs, const char *signal_source)
|
||||
{
|
||||
struct calldata data;
|
||||
uint8_t stack[128];
|
||||
|
||||
calldata_init_fixed(&data, stack, sizeof(stack));
|
||||
calldata_set_ptr(&data, "source", source);
|
||||
if (signal_obs && !source->context.private)
|
||||
signal_handler_signal(obs->signals, signal_obs, &data);
|
||||
if (signal_source)
|
||||
signal_handler_signal(source->context.signals, signal_source,
|
||||
&data);
|
||||
}
|
||||
|
||||
extern void obs_source_activate(obs_source_t *source, enum view_type type);
|
||||
extern void obs_source_deactivate(obs_source_t *source, enum view_type type);
|
||||
extern void obs_source_video_tick(obs_source_t *source, float seconds);
|
||||
extern float obs_source_get_target_volume(obs_source_t *source,
|
||||
obs_source_t *target);
|
||||
|
||||
extern void obs_source_audio_render(obs_source_t *source, uint32_t mixers,
|
||||
size_t channels, size_t sample_rate, size_t size);
|
||||
|
||||
extern void add_alignment(struct vec2 *v, uint32_t align, int cx, int cy);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* outputs */
|
||||
|
||||
enum delay_msg {
|
||||
DELAY_MSG_PACKET,
|
||||
DELAY_MSG_START,
|
||||
DELAY_MSG_STOP,
|
||||
};
|
||||
|
||||
struct delay_data {
|
||||
enum delay_msg msg;
|
||||
uint64_t ts;
|
||||
struct encoder_packet packet;
|
||||
};
|
||||
|
||||
typedef void (*encoded_callback_t)(void *data, struct encoder_packet *packet);
|
||||
|
||||
struct obs_weak_output {
|
||||
struct obs_weak_ref ref;
|
||||
struct obs_output *output;
|
||||
};
|
||||
|
||||
struct obs_output {
|
||||
struct obs_context_data context;
|
||||
struct obs_output_info info;
|
||||
struct obs_weak_output *control;
|
||||
|
||||
/* indicates ownership of the info.id buffer */
|
||||
bool owns_info_id;
|
||||
|
||||
bool received_video;
|
||||
bool received_audio;
|
||||
int64_t video_offset;
|
||||
int64_t audio_offsets[MAX_AUDIO_MIXES];
|
||||
int64_t highest_audio_ts;
|
||||
int64_t highest_video_ts;
|
||||
pthread_mutex_t interleaved_mutex;
|
||||
DARRAY(struct encoder_packet) interleaved_packets;
|
||||
|
||||
int reconnect_retry_sec;
|
||||
int reconnect_retry_max;
|
||||
int reconnect_retries;
|
||||
int reconnect_retry_cur_sec;
|
||||
bool reconnecting;
|
||||
pthread_t reconnect_thread;
|
||||
os_event_t *reconnect_stop_event;
|
||||
volatile bool reconnect_thread_active;
|
||||
|
||||
uint32_t starting_drawn_count;
|
||||
uint32_t starting_lagged_count;
|
||||
uint32_t starting_frame_count;
|
||||
uint32_t starting_skipped_frame_count;
|
||||
|
||||
int total_frames;
|
||||
|
||||
bool active;
|
||||
volatile bool stopped;
|
||||
video_t *video;
|
||||
audio_t *audio;
|
||||
obs_encoder_t *video_encoder;
|
||||
obs_encoder_t *audio_encoders[MAX_AUDIO_MIXES];
|
||||
obs_service_t *service;
|
||||
size_t mixer_idx;
|
||||
|
||||
uint32_t scaled_width;
|
||||
uint32_t scaled_height;
|
||||
|
||||
bool video_conversion_set;
|
||||
bool audio_conversion_set;
|
||||
struct video_scale_info video_conversion;
|
||||
struct audio_convert_info audio_conversion;
|
||||
|
||||
bool valid;
|
||||
|
||||
uint64_t active_delay_ns;
|
||||
encoded_callback_t delay_callback;
|
||||
struct circlebuf delay_data; /* struct delay_data */
|
||||
pthread_mutex_t delay_mutex;
|
||||
uint32_t delay_sec;
|
||||
uint32_t delay_flags;
|
||||
uint32_t delay_cur_flags;
|
||||
volatile long delay_restart_refs;
|
||||
bool delay_active;
|
||||
bool delay_capturing;
|
||||
};
|
||||
|
||||
static inline void do_output_signal(struct obs_output *output,
|
||||
const char *signal)
|
||||
{
|
||||
struct calldata params = {0};
|
||||
calldata_set_ptr(¶ms, "output", output);
|
||||
signal_handler_signal(output->context.signals, signal, ¶ms);
|
||||
calldata_free(¶ms);
|
||||
}
|
||||
|
||||
extern void process_delay(void *data, struct encoder_packet *packet);
|
||||
extern void obs_output_cleanup_delay(obs_output_t *output);
|
||||
extern bool obs_output_delay_start(obs_output_t *output);
|
||||
extern void obs_output_delay_stop(obs_output_t *output);
|
||||
extern bool obs_output_actual_start(obs_output_t *output);
|
||||
extern void obs_output_actual_stop(obs_output_t *output, bool force);
|
||||
|
||||
extern const struct obs_output_info *find_output(const char *id);
|
||||
|
||||
extern void obs_output_remove_encoder(struct obs_output *output,
|
||||
struct obs_encoder *encoder);
|
||||
|
||||
void obs_output_destroy(obs_output_t *output);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* encoders */
|
||||
|
||||
struct obs_weak_encoder {
|
||||
struct obs_weak_ref ref;
|
||||
struct obs_encoder *encoder;
|
||||
};
|
||||
|
||||
struct encoder_callback {
|
||||
bool sent_first_packet;
|
||||
void (*new_packet)(void *param, struct encoder_packet *packet);
|
||||
void *param;
|
||||
};
|
||||
|
||||
struct obs_encoder {
|
||||
struct obs_context_data context;
|
||||
struct obs_encoder_info info;
|
||||
struct obs_weak_encoder *control;
|
||||
|
||||
pthread_mutex_t init_mutex;
|
||||
|
||||
uint32_t samplerate;
|
||||
size_t planes;
|
||||
size_t blocksize;
|
||||
size_t framesize;
|
||||
size_t framesize_bytes;
|
||||
|
||||
size_t mixer_idx;
|
||||
|
||||
uint32_t scaled_width;
|
||||
uint32_t scaled_height;
|
||||
enum video_format preferred_format;
|
||||
|
||||
volatile bool active;
|
||||
bool initialized;
|
||||
|
||||
/* indicates ownership of the info.id buffer */
|
||||
bool owns_info_id;
|
||||
|
||||
uint32_t timebase_num;
|
||||
uint32_t timebase_den;
|
||||
|
||||
int64_t cur_pts;
|
||||
|
||||
struct circlebuf audio_input_buffer[MAX_AV_PLANES];
|
||||
uint8_t *audio_output_buffer[MAX_AV_PLANES];
|
||||
|
||||
/* if a video encoder is paired with an audio encoder, make it start
|
||||
* up at the specific timestamp. if this is the audio encoder,
|
||||
* wait_for_video makes it wait until it's ready to sync up with
|
||||
* video */
|
||||
bool wait_for_video;
|
||||
bool first_received;
|
||||
struct obs_encoder *paired_encoder;
|
||||
int64_t offset_usec;
|
||||
uint64_t first_raw_ts;
|
||||
uint64_t start_ts;
|
||||
|
||||
pthread_mutex_t outputs_mutex;
|
||||
DARRAY(obs_output_t*) outputs;
|
||||
|
||||
bool destroy_on_stop;
|
||||
|
||||
/* stores the video/audio media output pointer. video_t *or audio_t **/
|
||||
void *media;
|
||||
|
||||
pthread_mutex_t callbacks_mutex;
|
||||
DARRAY(struct encoder_callback) callbacks;
|
||||
|
||||
const char *profile_encoder_encode_name;
|
||||
};
|
||||
|
||||
extern struct obs_encoder_info *find_encoder(const char *id);
|
||||
|
||||
extern bool obs_encoder_initialize(obs_encoder_t *encoder);
|
||||
extern void obs_encoder_shutdown(obs_encoder_t *encoder);
|
||||
|
||||
extern void obs_encoder_start(obs_encoder_t *encoder,
|
||||
void (*new_packet)(void *param, struct encoder_packet *packet),
|
||||
void *param);
|
||||
extern void obs_encoder_stop(obs_encoder_t *encoder,
|
||||
void (*new_packet)(void *param, struct encoder_packet *packet),
|
||||
void *param);
|
||||
|
||||
extern void obs_encoder_add_output(struct obs_encoder *encoder,
|
||||
struct obs_output *output);
|
||||
extern void obs_encoder_remove_output(struct obs_encoder *encoder,
|
||||
struct obs_output *output);
|
||||
|
||||
void obs_encoder_destroy(obs_encoder_t *encoder);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* services */
|
||||
|
||||
struct obs_weak_service {
|
||||
struct obs_weak_ref ref;
|
||||
struct obs_service *service;
|
||||
};
|
||||
|
||||
struct obs_service {
|
||||
struct obs_context_data context;
|
||||
struct obs_service_info info;
|
||||
struct obs_weak_service *control;
|
||||
|
||||
/* indicates ownership of the info.id buffer */
|
||||
bool owns_info_id;
|
||||
|
||||
bool active;
|
||||
bool destroy;
|
||||
struct obs_output *output;
|
||||
};
|
||||
|
||||
extern const struct obs_service_info *find_service(const char *id);
|
||||
|
||||
extern void obs_service_activate(struct obs_service *service);
|
||||
extern void obs_service_deactivate(struct obs_service *service, bool remove);
|
||||
extern bool obs_service_initialize(struct obs_service *service,
|
||||
struct obs_output *output);
|
||||
|
||||
void obs_service_destroy(obs_service_t *service);
|
||||
|
||||
743
libobs/obs-module.c
Normal file
743
libobs/obs-module.c
Normal file
|
|
@ -0,0 +1,743 @@
|
|||
/******************************************************************************
|
||||
Copyright (C) 2013-2014 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "util/platform.h"
|
||||
#include "util/dstr.h"
|
||||
|
||||
#include "obs-defs.h"
|
||||
#include "obs-internal.h"
|
||||
#include "obs-module.h"
|
||||
|
||||
extern const char *get_module_extension(void);
|
||||
|
||||
static inline int req_func_not_found(const char *name, const char *path)
|
||||
{
|
||||
blog(LOG_ERROR, "Required module function '%s' in module '%s' not "
|
||||
"found, loading of module failed",
|
||||
name, path);
|
||||
return MODULE_MISSING_EXPORTS;
|
||||
}
|
||||
|
||||
static int load_module_exports(struct obs_module *mod, const char *path)
|
||||
{
|
||||
mod->load = os_dlsym(mod->module, "obs_module_load");
|
||||
if (!mod->load)
|
||||
return req_func_not_found("obs_module_load", path);
|
||||
|
||||
mod->set_pointer = os_dlsym(mod->module, "obs_module_set_pointer");
|
||||
if (!mod->set_pointer)
|
||||
return req_func_not_found("obs_module_set_pointer", path);
|
||||
|
||||
mod->ver = os_dlsym(mod->module, "obs_module_ver");
|
||||
if (!mod->ver)
|
||||
return req_func_not_found("obs_module_ver", path);
|
||||
|
||||
/* optional exports */
|
||||
mod->unload = os_dlsym(mod->module, "obs_module_unload");
|
||||
mod->set_locale = os_dlsym(mod->module, "obs_module_set_locale");
|
||||
mod->free_locale = os_dlsym(mod->module, "obs_module_free_locale");
|
||||
mod->name = os_dlsym(mod->module, "obs_module_name");
|
||||
mod->description = os_dlsym(mod->module, "obs_module_description");
|
||||
mod->author = os_dlsym(mod->module, "obs_module_author");
|
||||
return MODULE_SUCCESS;
|
||||
}
|
||||
|
||||
static inline char *get_module_name(const char *file)
|
||||
{
|
||||
static size_t ext_len = 0;
|
||||
struct dstr name = {0};
|
||||
|
||||
if (ext_len == 0) {
|
||||
const char *ext = get_module_extension();
|
||||
ext_len = strlen(ext);
|
||||
}
|
||||
|
||||
dstr_copy(&name, file);
|
||||
dstr_resize(&name, name.len - ext_len);
|
||||
return name.array;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
extern void reset_win32_symbol_paths(void);
|
||||
#endif
|
||||
|
||||
int obs_open_module(obs_module_t **module, const char *path,
|
||||
const char *data_path)
|
||||
{
|
||||
struct obs_module mod = {0};
|
||||
int errorcode;
|
||||
|
||||
if (!module || !path || !obs)
|
||||
return MODULE_ERROR;
|
||||
|
||||
blog(LOG_INFO, "---------------------------------");
|
||||
|
||||
mod.module = os_dlopen(path);
|
||||
if (!mod.module) {
|
||||
blog(LOG_WARNING, "Module '%s' not found", path);
|
||||
return MODULE_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
errorcode = load_module_exports(&mod, path);
|
||||
if (errorcode != MODULE_SUCCESS)
|
||||
return errorcode;
|
||||
|
||||
mod.bin_path = bstrdup(path);
|
||||
mod.file = strrchr(mod.bin_path, '/');
|
||||
mod.file = (!mod.file) ? mod.bin_path : (mod.file + 1);
|
||||
mod.mod_name = get_module_name(mod.file);
|
||||
mod.data_path = bstrdup(data_path);
|
||||
mod.next = obs->first_module;
|
||||
|
||||
if (mod.file) {
|
||||
blog(LOG_INFO, "Loading module: %s", mod.file);
|
||||
}
|
||||
|
||||
*module = bmemdup(&mod, sizeof(mod));
|
||||
obs->first_module = (*module);
|
||||
mod.set_pointer(*module);
|
||||
|
||||
if (mod.set_locale)
|
||||
mod.set_locale(obs->locale);
|
||||
|
||||
return MODULE_SUCCESS;
|
||||
}
|
||||
|
||||
bool obs_init_module(obs_module_t *module)
|
||||
{
|
||||
if (!module || !obs)
|
||||
return false;
|
||||
if (module->loaded)
|
||||
return true;
|
||||
|
||||
const char *profile_name =
|
||||
profile_store_name(obs_get_profiler_name_store(),
|
||||
"obs_init_module(%s)", module->file);
|
||||
profile_start(profile_name);
|
||||
|
||||
module->loaded = module->load();
|
||||
if (!module->loaded)
|
||||
blog(LOG_WARNING, "Failed to initialize module '%s'",
|
||||
module->file);
|
||||
|
||||
profile_end(profile_name);
|
||||
return module->loaded;
|
||||
}
|
||||
|
||||
const char *obs_get_module_file_name(obs_module_t *module)
|
||||
{
|
||||
return module ? module->file : NULL;
|
||||
}
|
||||
|
||||
const char *obs_get_module_name(obs_module_t *module)
|
||||
{
|
||||
return (module && module->name) ? module->name() : NULL;
|
||||
}
|
||||
|
||||
const char *obs_get_module_author(obs_module_t *module)
|
||||
{
|
||||
return (module && module->author) ? module->author() : NULL;
|
||||
}
|
||||
|
||||
const char *obs_get_module_description(obs_module_t *module)
|
||||
{
|
||||
return (module && module->description) ? module->description() : NULL;
|
||||
}
|
||||
|
||||
const char *obs_get_module_binary_path(obs_module_t *module)
|
||||
{
|
||||
return module ? module->bin_path : NULL;
|
||||
}
|
||||
|
||||
const char *obs_get_module_data_path(obs_module_t *module)
|
||||
{
|
||||
return module ? module->data_path : NULL;
|
||||
}
|
||||
|
||||
char *obs_find_module_file(obs_module_t *module, const char *file)
|
||||
{
|
||||
struct dstr output = {0};
|
||||
|
||||
if (!file)
|
||||
file = "";
|
||||
|
||||
if (!module)
|
||||
return NULL;
|
||||
|
||||
dstr_copy(&output, module->data_path);
|
||||
if (!dstr_is_empty(&output) && dstr_end(&output) != '/' && *file)
|
||||
dstr_cat_ch(&output, '/');
|
||||
dstr_cat(&output, file);
|
||||
|
||||
if (!os_file_exists(output.array))
|
||||
dstr_free(&output);
|
||||
return output.array;
|
||||
}
|
||||
|
||||
char *obs_module_get_config_path(obs_module_t *module, const char *file)
|
||||
{
|
||||
struct dstr output = {0};
|
||||
|
||||
dstr_copy(&output, obs->module_config_path);
|
||||
if (!dstr_is_empty(&output) && dstr_end(&output) != '/')
|
||||
dstr_cat_ch(&output, '/');
|
||||
dstr_cat(&output, module->mod_name);
|
||||
dstr_cat_ch(&output, '/');
|
||||
dstr_cat(&output, file);
|
||||
|
||||
return output.array;
|
||||
}
|
||||
|
||||
void obs_add_module_path(const char *bin, const char *data)
|
||||
{
|
||||
struct obs_module_path omp;
|
||||
|
||||
if (!obs || !bin || !data) return;
|
||||
|
||||
omp.bin = bstrdup(bin);
|
||||
omp.data = bstrdup(data);
|
||||
da_push_back(obs->module_paths, &omp);
|
||||
}
|
||||
|
||||
static void load_all_callback(void *param, const struct obs_module_info *info)
|
||||
{
|
||||
obs_module_t *module;
|
||||
|
||||
int code = obs_open_module(&module, info->bin_path, info->data_path);
|
||||
if (code != MODULE_SUCCESS) {
|
||||
blog(LOG_DEBUG, "Failed to load module file '%s': %d",
|
||||
info->bin_path, code);
|
||||
return;
|
||||
}
|
||||
|
||||
obs_init_module(module);
|
||||
|
||||
UNUSED_PARAMETER(param);
|
||||
}
|
||||
|
||||
static const char *obs_load_all_modules_name = "obs_load_all_modules";
|
||||
#ifdef _WIN32
|
||||
static const char *reset_win32_symbol_paths_name = "reset_win32_symbol_paths";
|
||||
#endif
|
||||
|
||||
void obs_load_all_modules(void)
|
||||
{
|
||||
profile_start(obs_load_all_modules_name);
|
||||
obs_find_modules(load_all_callback, NULL);
|
||||
#ifdef _WIN32
|
||||
profile_start(reset_win32_symbol_paths_name);
|
||||
reset_win32_symbol_paths();
|
||||
profile_end(reset_win32_symbol_paths_name);
|
||||
#endif
|
||||
profile_end(obs_load_all_modules_name);
|
||||
}
|
||||
|
||||
static inline void make_data_dir(struct dstr *parsed_data_dir,
|
||||
const char *data_dir, const char *name)
|
||||
{
|
||||
dstr_copy(parsed_data_dir, data_dir);
|
||||
dstr_replace(parsed_data_dir, "%module%", name);
|
||||
if (dstr_end(parsed_data_dir) == '/')
|
||||
dstr_resize(parsed_data_dir, parsed_data_dir->len - 1);
|
||||
}
|
||||
|
||||
static char *make_data_directory(const char *module_name, const char *data_dir)
|
||||
{
|
||||
struct dstr parsed_data_dir = {0};
|
||||
bool found = false;
|
||||
|
||||
make_data_dir(&parsed_data_dir, data_dir, module_name);
|
||||
|
||||
found = os_file_exists(parsed_data_dir.array);
|
||||
|
||||
if (!found && astrcmpi_n(module_name, "lib", 3) == 0)
|
||||
make_data_dir(&parsed_data_dir, data_dir, module_name + 3);
|
||||
|
||||
return parsed_data_dir.array;
|
||||
}
|
||||
|
||||
static bool parse_binary_from_directory(struct dstr *parsed_bin_path,
|
||||
const char *bin_path, const char *file)
|
||||
{
|
||||
struct dstr directory = {0};
|
||||
bool found = true;
|
||||
|
||||
dstr_copy(&directory, bin_path);
|
||||
dstr_replace(&directory, "%module%", file);
|
||||
if (dstr_end(&directory) != '/')
|
||||
dstr_cat_ch(&directory, '/');
|
||||
|
||||
dstr_copy_dstr(parsed_bin_path, &directory);
|
||||
dstr_cat(parsed_bin_path, file);
|
||||
dstr_cat(parsed_bin_path, get_module_extension());
|
||||
|
||||
if (!os_file_exists(parsed_bin_path->array)) {
|
||||
/* if the file doesn't exist, check with 'lib' prefix */
|
||||
dstr_copy_dstr(parsed_bin_path, &directory);
|
||||
dstr_cat(parsed_bin_path, "lib");
|
||||
dstr_cat(parsed_bin_path, file);
|
||||
dstr_cat(parsed_bin_path, get_module_extension());
|
||||
|
||||
/* if neither exist, don't include this as a library */
|
||||
if (!os_file_exists(parsed_bin_path->array)) {
|
||||
dstr_free(parsed_bin_path);
|
||||
found = false;
|
||||
}
|
||||
}
|
||||
|
||||
dstr_free(&directory);
|
||||
return found;
|
||||
}
|
||||
|
||||
static void process_found_module(struct obs_module_path *omp,
|
||||
const char *path, bool directory,
|
||||
obs_find_module_callback_t callback, void *param)
|
||||
{
|
||||
struct obs_module_info info;
|
||||
struct dstr name = {0};
|
||||
struct dstr parsed_bin_path = {0};
|
||||
const char *file;
|
||||
char *parsed_data_dir;
|
||||
bool bin_found = true;
|
||||
|
||||
file = strrchr(path, '/');
|
||||
file = file ? (file + 1) : path;
|
||||
|
||||
if (strcmp(file, ".") == 0 || strcmp(file, "..") == 0)
|
||||
return;
|
||||
|
||||
dstr_copy(&name, file);
|
||||
if (!directory) {
|
||||
char *ext = strrchr(name.array, '.');
|
||||
if (ext)
|
||||
dstr_resize(&name, ext - name.array);
|
||||
|
||||
dstr_copy(&parsed_bin_path, path);
|
||||
} else {
|
||||
bin_found = parse_binary_from_directory(&parsed_bin_path,
|
||||
omp->bin, file);
|
||||
}
|
||||
|
||||
parsed_data_dir = make_data_directory(name.array, omp->data);
|
||||
|
||||
if (parsed_data_dir && bin_found) {
|
||||
info.bin_path = parsed_bin_path.array;
|
||||
info.data_path = parsed_data_dir;
|
||||
callback(param, &info);
|
||||
}
|
||||
|
||||
bfree(parsed_data_dir);
|
||||
dstr_free(&name);
|
||||
dstr_free(&parsed_bin_path);
|
||||
}
|
||||
|
||||
static void find_modules_in_path(struct obs_module_path *omp,
|
||||
obs_find_module_callback_t callback, void *param)
|
||||
{
|
||||
struct dstr search_path = {0};
|
||||
char *module_start;
|
||||
bool search_directories = false;
|
||||
os_glob_t *gi;
|
||||
|
||||
dstr_copy(&search_path, omp->bin);
|
||||
|
||||
module_start = strstr(search_path.array, "%module%");
|
||||
if (module_start) {
|
||||
dstr_resize(&search_path, module_start - search_path.array);
|
||||
search_directories = true;
|
||||
}
|
||||
|
||||
if (!dstr_is_empty(&search_path) && dstr_end(&search_path) != '/')
|
||||
dstr_cat_ch(&search_path, '/');
|
||||
|
||||
dstr_cat_ch(&search_path, '*');
|
||||
if (!search_directories)
|
||||
dstr_cat(&search_path, get_module_extension());
|
||||
|
||||
if (os_glob(search_path.array, 0, &gi) == 0) {
|
||||
for (size_t i = 0; i < gi->gl_pathc; i++) {
|
||||
if (search_directories == gi->gl_pathv[i].directory)
|
||||
process_found_module(omp,
|
||||
gi->gl_pathv[i].path,
|
||||
search_directories,
|
||||
callback, param);
|
||||
}
|
||||
|
||||
os_globfree(gi);
|
||||
}
|
||||
|
||||
dstr_free(&search_path);
|
||||
}
|
||||
|
||||
void obs_find_modules(obs_find_module_callback_t callback, void *param)
|
||||
{
|
||||
if (!obs)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < obs->module_paths.num; i++) {
|
||||
struct obs_module_path *omp = obs->module_paths.array + i;
|
||||
find_modules_in_path(omp, callback, param);
|
||||
}
|
||||
}
|
||||
|
||||
void obs_enum_modules(obs_enum_module_callback_t callback, void *param)
|
||||
{
|
||||
struct obs_module *module;
|
||||
if (!obs)
|
||||
return;
|
||||
|
||||
module = obs->first_module;
|
||||
while (module) {
|
||||
callback(param, module);
|
||||
module = module->next;
|
||||
}
|
||||
}
|
||||
|
||||
void free_module(struct obs_module *mod)
|
||||
{
|
||||
if (!mod)
|
||||
return;
|
||||
|
||||
if (mod->module) {
|
||||
if (mod->free_locale)
|
||||
mod->free_locale();
|
||||
|
||||
if (mod->loaded && mod->unload)
|
||||
mod->unload();
|
||||
|
||||
/* there is no real reason to close the dynamic libraries,
|
||||
* and sometimes this can cause issues. */
|
||||
/* os_dlclose(mod->module); */
|
||||
}
|
||||
|
||||
bfree(mod->mod_name);
|
||||
bfree(mod->bin_path);
|
||||
bfree(mod->data_path);
|
||||
bfree(mod);
|
||||
}
|
||||
|
||||
lookup_t *obs_module_load_locale(obs_module_t *module,
|
||||
const char *default_locale, const char *locale)
|
||||
{
|
||||
struct dstr str = {0};
|
||||
lookup_t *lookup = NULL;
|
||||
|
||||
if (!module || !default_locale || !locale) {
|
||||
blog(LOG_WARNING, "obs_module_load_locale: Invalid parameters");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dstr_copy(&str, "locale/");
|
||||
dstr_cat(&str, default_locale);
|
||||
dstr_cat(&str, ".ini");
|
||||
|
||||
char *file = obs_find_module_file(module, str.array);
|
||||
if (file)
|
||||
lookup = text_lookup_create(file);
|
||||
|
||||
bfree(file);
|
||||
|
||||
if (!lookup) {
|
||||
blog(LOG_WARNING, "Failed to load '%s' text for module: '%s'",
|
||||
default_locale, module->file);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (astrcmpi(locale, default_locale) == 0)
|
||||
goto cleanup;
|
||||
|
||||
dstr_copy(&str, "/locale/");
|
||||
dstr_cat(&str, locale);
|
||||
dstr_cat(&str, ".ini");
|
||||
|
||||
file = obs_find_module_file(module, str.array);
|
||||
|
||||
if (!text_lookup_add(lookup, file))
|
||||
blog(LOG_WARNING, "Failed to load '%s' text for module: '%s'",
|
||||
locale, module->file);
|
||||
|
||||
bfree(file);
|
||||
cleanup:
|
||||
dstr_free(&str);
|
||||
return lookup;
|
||||
}
|
||||
|
||||
#define REGISTER_OBS_DEF(size_var, structure, dest, info) \
|
||||
do { \
|
||||
struct structure data = {0}; \
|
||||
if (!size_var) { \
|
||||
blog(LOG_ERROR, "Tried to register " #structure \
|
||||
" outside of obs_module_load"); \
|
||||
return; \
|
||||
} \
|
||||
\
|
||||
if (size_var > sizeof(data)) { \
|
||||
blog(LOG_ERROR, "Tried to register " #structure \
|
||||
" with size %llu which is more " \
|
||||
"than libobs currently supports " \
|
||||
"(%llu)", \
|
||||
(long long unsigned)size_var, \
|
||||
(long long unsigned)sizeof(data));\
|
||||
goto error; \
|
||||
} \
|
||||
\
|
||||
memcpy(&data, info, size_var); \
|
||||
da_push_back(dest, &data); \
|
||||
} while (false)
|
||||
|
||||
#define CHECK_REQUIRED_VAL(type, info, val, func) \
|
||||
do { \
|
||||
if ((offsetof(type, val) + sizeof(info->val) > size) || \
|
||||
!info->val) { \
|
||||
blog(LOG_ERROR, "Required value '" #val "' for " \
|
||||
"'%s' not found. " #func \
|
||||
" failed.", info->id); \
|
||||
goto error; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define HANDLE_ERROR(size_var, structure, info) \
|
||||
do { \
|
||||
struct structure data = {0}; \
|
||||
if (!size_var) \
|
||||
return; \
|
||||
\
|
||||
memcpy(&data, info, sizeof(data) < size_var ? \
|
||||
sizeof(data) : size_var); \
|
||||
\
|
||||
if (info->type_data && info->free_type_data) \
|
||||
info->free_type_data(info->type_data); \
|
||||
} while (false)
|
||||
|
||||
#define source_warn(format, ...) \
|
||||
blog(LOG_WARNING, "obs_register_source: " format, ##__VA_ARGS__)
|
||||
#define output_warn(format, ...) \
|
||||
blog(LOG_WARNING, "obs_register_output: " format, ##__VA_ARGS__)
|
||||
#define encoder_warn(format, ...) \
|
||||
blog(LOG_WARNING, "obs_register_encoder: " format, ##__VA_ARGS__)
|
||||
#define service_warn(format, ...) \
|
||||
blog(LOG_WARNING, "obs_register_service: " format, ##__VA_ARGS__)
|
||||
|
||||
void obs_register_source_s(const struct obs_source_info *info, size_t size)
|
||||
{
|
||||
struct obs_source_info data = {0};
|
||||
struct darray *array = NULL;
|
||||
|
||||
if (info->type == OBS_SOURCE_TYPE_INPUT) {
|
||||
array = &obs->input_types.da;
|
||||
} else if (info->type == OBS_SOURCE_TYPE_FILTER) {
|
||||
array = &obs->filter_types.da;
|
||||
} else if (info->type == OBS_SOURCE_TYPE_TRANSITION) {
|
||||
array = &obs->transition_types.da;
|
||||
} else if (info->type != OBS_SOURCE_TYPE_SCENE) {
|
||||
source_warn("Tried to register unknown source type: %u",
|
||||
info->type);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (get_source_info(info->id)) {
|
||||
source_warn("Source '%s' already exists! "
|
||||
"Duplicate library?", info->id);
|
||||
goto error;
|
||||
}
|
||||
|
||||
memcpy(&data, info, size);
|
||||
|
||||
/* mark audio-only filters as an async filter categorically */
|
||||
if (data.type == OBS_SOURCE_TYPE_FILTER) {
|
||||
if ((data.output_flags & OBS_SOURCE_VIDEO) == 0)
|
||||
data.output_flags |= OBS_SOURCE_ASYNC;
|
||||
}
|
||||
|
||||
if (data.type == OBS_SOURCE_TYPE_TRANSITION) {
|
||||
if (data.get_width)
|
||||
source_warn("get_width ignored registering "
|
||||
"transition '%s'",
|
||||
data.id);
|
||||
if (data.get_height)
|
||||
source_warn("get_height ignored registering "
|
||||
"transition '%s'",
|
||||
data.id);
|
||||
data.output_flags |= OBS_SOURCE_COMPOSITE | OBS_SOURCE_VIDEO |
|
||||
OBS_SOURCE_CUSTOM_DRAW;
|
||||
}
|
||||
|
||||
if ((data.output_flags & OBS_SOURCE_COMPOSITE) != 0) {
|
||||
if ((data.output_flags & OBS_SOURCE_AUDIO) != 0) {
|
||||
source_warn("Source '%s': Composite sources "
|
||||
"cannot be audio sources", info->id);
|
||||
goto error;
|
||||
}
|
||||
if ((data.output_flags & OBS_SOURCE_ASYNC) != 0) {
|
||||
source_warn("Source '%s': Composite sources "
|
||||
"cannot be async sources", info->id);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
#define CHECK_REQUIRED_VAL_(info, val, func) \
|
||||
CHECK_REQUIRED_VAL(struct obs_source_info, info, val, func)
|
||||
CHECK_REQUIRED_VAL_(info, get_name, obs_register_source);
|
||||
CHECK_REQUIRED_VAL_(info, create, obs_register_source);
|
||||
CHECK_REQUIRED_VAL_(info, destroy, obs_register_source);
|
||||
|
||||
if (info->type != OBS_SOURCE_TYPE_FILTER &&
|
||||
info->type != OBS_SOURCE_TYPE_TRANSITION &&
|
||||
(info->output_flags & OBS_SOURCE_VIDEO) != 0 &&
|
||||
(info->output_flags & OBS_SOURCE_ASYNC) == 0) {
|
||||
CHECK_REQUIRED_VAL_(info, get_width, obs_register_source);
|
||||
CHECK_REQUIRED_VAL_(info, get_height, obs_register_source);
|
||||
}
|
||||
|
||||
if ((data.output_flags & OBS_SOURCE_COMPOSITE) != 0) {
|
||||
CHECK_REQUIRED_VAL_(info, audio_render, obs_register_source);
|
||||
}
|
||||
#undef CHECK_REQUIRED_VAL_
|
||||
|
||||
if (size > sizeof(data)) {
|
||||
source_warn("Tried to register obs_source_info with size "
|
||||
"%llu which is more than libobs currently "
|
||||
"supports (%llu)", (long long unsigned)size,
|
||||
(long long unsigned)sizeof(data));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (array)
|
||||
darray_push_back(sizeof(struct obs_source_info), array, &data);
|
||||
da_push_back(obs->source_types, &data);
|
||||
return;
|
||||
|
||||
error:
|
||||
HANDLE_ERROR(size, obs_source_info, info);
|
||||
}
|
||||
|
||||
void obs_register_output_s(const struct obs_output_info *info, size_t size)
|
||||
{
|
||||
if (find_output(info->id)) {
|
||||
output_warn("Output id '%s' already exists! "
|
||||
"Duplicate library?", info->id);
|
||||
goto error;
|
||||
}
|
||||
|
||||
#define CHECK_REQUIRED_VAL_(info, val, func) \
|
||||
CHECK_REQUIRED_VAL(struct obs_output_info, info, val, func)
|
||||
CHECK_REQUIRED_VAL_(info, get_name, obs_register_output);
|
||||
CHECK_REQUIRED_VAL_(info, create, obs_register_output);
|
||||
CHECK_REQUIRED_VAL_(info, destroy, obs_register_output);
|
||||
CHECK_REQUIRED_VAL_(info, start, obs_register_output);
|
||||
CHECK_REQUIRED_VAL_(info, stop, obs_register_output);
|
||||
|
||||
if (info->flags & OBS_OUTPUT_ENCODED) {
|
||||
CHECK_REQUIRED_VAL_(info, encoded_packet, obs_register_output);
|
||||
} else {
|
||||
if (info->flags & OBS_OUTPUT_VIDEO)
|
||||
CHECK_REQUIRED_VAL_(info, raw_video,
|
||||
obs_register_output);
|
||||
|
||||
if (info->flags & OBS_OUTPUT_AUDIO)
|
||||
CHECK_REQUIRED_VAL_(info, raw_audio,
|
||||
obs_register_output);
|
||||
}
|
||||
#undef CHECK_REQUIRED_VAL_
|
||||
|
||||
REGISTER_OBS_DEF(size, obs_output_info, obs->output_types, info);
|
||||
return;
|
||||
|
||||
error:
|
||||
HANDLE_ERROR(size, obs_output_info, info);
|
||||
}
|
||||
|
||||
void obs_register_encoder_s(const struct obs_encoder_info *info, size_t size)
|
||||
{
|
||||
if (find_encoder(info->id)) {
|
||||
encoder_warn("Encoder id '%s' already exists! "
|
||||
"Duplicate library?", info->id);
|
||||
goto error;
|
||||
}
|
||||
|
||||
#define CHECK_REQUIRED_VAL_(info, val, func) \
|
||||
CHECK_REQUIRED_VAL(struct obs_encoder_info, info, val, func)
|
||||
CHECK_REQUIRED_VAL_(info, get_name, obs_register_encoder);
|
||||
CHECK_REQUIRED_VAL_(info, create, obs_register_encoder);
|
||||
CHECK_REQUIRED_VAL_(info, destroy, obs_register_encoder);
|
||||
CHECK_REQUIRED_VAL_(info, encode, obs_register_encoder);
|
||||
|
||||
if (info->type == OBS_ENCODER_AUDIO)
|
||||
CHECK_REQUIRED_VAL_(info, get_frame_size, obs_register_encoder);
|
||||
#undef CHECK_REQUIRED_VAL_
|
||||
|
||||
REGISTER_OBS_DEF(size, obs_encoder_info, obs->encoder_types, info);
|
||||
return;
|
||||
|
||||
error:
|
||||
HANDLE_ERROR(size, obs_encoder_info, info);
|
||||
}
|
||||
|
||||
void obs_register_service_s(const struct obs_service_info *info, size_t size)
|
||||
{
|
||||
if (find_service(info->id)) {
|
||||
service_warn("Service id '%s' already exists! "
|
||||
"Duplicate library?", info->id);
|
||||
goto error;
|
||||
}
|
||||
|
||||
#define CHECK_REQUIRED_VAL_(info, val, func) \
|
||||
CHECK_REQUIRED_VAL(struct obs_service_info, info, val, func)
|
||||
CHECK_REQUIRED_VAL_(info, get_name, obs_register_service);
|
||||
CHECK_REQUIRED_VAL_(info, create, obs_register_service);
|
||||
CHECK_REQUIRED_VAL_(info, destroy, obs_register_service);
|
||||
#undef CHECK_REQUIRED_VAL_
|
||||
|
||||
REGISTER_OBS_DEF(size, obs_service_info, obs->service_types, info);
|
||||
return;
|
||||
|
||||
error:
|
||||
HANDLE_ERROR(size, obs_service_info, info);
|
||||
}
|
||||
|
||||
void obs_regsiter_modal_ui_s(const struct obs_modal_ui *info, size_t size)
|
||||
{
|
||||
#define CHECK_REQUIRED_VAL_(info, val, func) \
|
||||
CHECK_REQUIRED_VAL(struct obs_modal_ui, info, val, func)
|
||||
CHECK_REQUIRED_VAL_(info, task, obs_regsiter_modal_ui);
|
||||
CHECK_REQUIRED_VAL_(info, target, obs_regsiter_modal_ui);
|
||||
CHECK_REQUIRED_VAL_(info, exec, obs_regsiter_modal_ui);
|
||||
#undef CHECK_REQUIRED_VAL_
|
||||
|
||||
REGISTER_OBS_DEF(size, obs_modal_ui, obs->modal_ui_callbacks, info);
|
||||
return;
|
||||
|
||||
error:
|
||||
HANDLE_ERROR(size, obs_modal_ui, info);
|
||||
}
|
||||
|
||||
void obs_regsiter_modeless_ui_s(const struct obs_modeless_ui *info, size_t size)
|
||||
{
|
||||
#define CHECK_REQUIRED_VAL_(info, val, func) \
|
||||
CHECK_REQUIRED_VAL(struct obs_modeless_ui, info, val, func)
|
||||
CHECK_REQUIRED_VAL_(info, task, obs_regsiter_modeless_ui);
|
||||
CHECK_REQUIRED_VAL_(info, target, obs_regsiter_modeless_ui);
|
||||
CHECK_REQUIRED_VAL_(info, create, obs_regsiter_modeless_ui);
|
||||
#undef CHECK_REQUIRED_VAL_
|
||||
|
||||
REGISTER_OBS_DEF(size, obs_modeless_ui, obs->modeless_ui_callbacks,
|
||||
info);
|
||||
return;
|
||||
|
||||
error:
|
||||
HANDLE_ERROR(size, obs_modeless_ui, info);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue