mirror of
https://github.com/Xaymar/obs-StreamFX
synced 2024-12-28 02:21:25 +00:00
project: Merged FFmpeg Encoders Step 1
This commit is contained in:
parent
3432ab4c1d
commit
3eea289679
34 changed files with 5629 additions and 82 deletions
152
CMakeLists.txt
152
CMakeLists.txt
|
@ -56,16 +56,16 @@ project(
|
|||
VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}.${VERSION_TWEAK}
|
||||
)
|
||||
set(PROJECT_FULL_NAME "StreamFX for OBS Studio")
|
||||
set(PROJECT_DESCRIPTION "Adds new Effects, like Sources, Transitions and Filters to OBS Studio.")
|
||||
set(PROJECT_DESCRIPTION "Better Production Quality, for free.")
|
||||
set(PROJECT_AUTHORS "Michael Fabian 'Xaymar' Dirks <info@xaymar.com>")
|
||||
set(PROJECT_COPYRIGHT_YEARS "2018 - 2019")
|
||||
set(PROJECT_COPYRIGHT_YEARS "2018 - 2020")
|
||||
|
||||
################################################################################
|
||||
# Setup / Bootstrap
|
||||
################################################################################
|
||||
|
||||
# Search Path
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/;${CMAKE_MODULE_PATH}")
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
# CMake Modules
|
||||
include("util")
|
||||
|
@ -127,19 +127,40 @@ set(_CXX_EXTENSIONS OFF)
|
|||
set(${PropertyPrefix}OBS_NATIVE FALSE CACHE BOOL "Use native obs-studio build" FORCE)
|
||||
set(${PropertyPrefix}OBS_REFERENCE FALSE CACHE BOOL "Use referenced obs-studio build" FORCE)
|
||||
set(${PropertyPrefix}OBS_PACKAGE FALSE CACHE BOOL "Use packaged obs-studio build" FORCE)
|
||||
set(${PropertyPrefix}OBS_DOWNLOAD FALSE CACHE BOOL "Use downloaded obs-studio build" FORCE)
|
||||
set(${PropertyPrefix}OBS_DOWNLOAD FALSE CACHE BOOL "Use downloaded obs-studio build")
|
||||
mark_as_advanced(FORCE OBS_NATIVE OBS_PACKAGE OBS_REFERENCE OBS_DOWNLOAD)
|
||||
|
||||
if(NOT TARGET libobs)
|
||||
set(${PropertyPrefix}OBS_STUDIO_DIR "" CACHE PATH "OBS Studio Source/Package Directory")
|
||||
set(${PropertyPrefix}OBS_DOWNLOAD_VERSION "24.0.3-ci" CACHE STRING "OBS Studio Version to download")
|
||||
endif()
|
||||
# Solve OBS_NATIVE, OBS_REFERENCE, OBS_PACKAGE, OBS_DOWNLOAD
|
||||
if(TARGET libobs)
|
||||
message(STATUS "${PROJECT_NAME}: Using native obs-studio.")
|
||||
CacheSet(${PropertyPrefix}OBS_NATIVE TRUE)
|
||||
else()
|
||||
message(STATUS "${PROJECT_NAME}: Using packaged or remote obs-studio.")
|
||||
CacheSet(${PropertyPrefix}OBS_NATIVE FALSE)
|
||||
|
||||
if(NOT ${PropertyPrefix}OBS_NATIVE)
|
||||
set(${PropertyPrefix}OBS_DEPENDENCIES_DIR "" CACHE PATH "Path to OBS Dependencies")
|
||||
set(CMAKE_PACKAGE_PREFIX "${CMAKE_BINARY_DIR}" CACHE PATH "Path for generated archives.")
|
||||
set(CMAKE_PACKAGE_NAME "${PROJECT_NAME}" CACHE STRING "Name for the generated archives.")
|
||||
set(CMAKE_PACKAGE_SUFFIX_OVERRIDE "" CACHE STRING "Override for the suffix.")
|
||||
|
||||
if(${PropertyPrefix}OBS_DOWNLOAD)
|
||||
set(${PropertyPrefix}OBS_DOWNLOAD_VERSION "24.0.3" CACHE STRING "OBS Studio Version to download")
|
||||
set(${PropertyPrefix}OBS_DEPENDENCIES_VERSION "24.0.0" CACHE STRING "OBS Studio Version to download")
|
||||
set(${PropertyPrefix}OBS_DOWNLOAD_URL "https://github.com/Xaymar/obs-studio/releases/download/${OBS_DOWNLOAD_VERSION}-ci/obs-studio-${ARCH}-0.0.0.0-vs2017.7z")
|
||||
set(${PropertyPrefix}OBS_DEPENDENCIES_URL "https://cdn.xaymar.com/obs/dependencies_${OBS_DEPENDENCIES_VERSION}.zip")
|
||||
else()
|
||||
set(${PropertyPrefix}OBS_STUDIO_DIR "" CACHE PATH "OBS Studio Source/Package Directory")
|
||||
set(${PropertyPrefix}OBS_DEPENDENCIES_DIR "" CACHE PATH "OBS Studio Dependencies Directory")
|
||||
if(EXISTS "${OBS_STUDIO_DIR}/cmake/LibObs/LibObsConfig.cmake")
|
||||
message(STATUS "${PROJECT_NAME}: Using packaged obs-studio.")
|
||||
CacheSet(${PropertyPrefix}OBS_PACKAGE TRUE)
|
||||
elseif(EXISTS "${OBS_STUDIO_DIR}/libobs/obs-module.h")
|
||||
message(STATUS "${PROJECT_NAME}: Using referenced obs-studio.")
|
||||
CacheSet(${PropertyPrefix}OBS_REFERENCE TRUE)
|
||||
else()
|
||||
message(FATAL_ERROR "${PROJECT_NAME}: No OBS Studio detected. If you wish to continue, either check ${PropertyPrefix}OBS_DOWNLOAD or fix your configuration")
|
||||
return()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
################################################################################
|
||||
|
@ -192,49 +213,59 @@ if(WIN32)
|
|||
)
|
||||
endif()
|
||||
|
||||
# Detect OBS Studio Type
|
||||
if(TARGET libobs)
|
||||
message(STATUS "${PROJECT_NAME}: Using native obs-studio.")
|
||||
CacheSet(${PropertyPrefix}OBS_NATIVE TRUE)
|
||||
else()
|
||||
CacheSet(${PropertyPrefix}OBS_NATIVE FALSE)
|
||||
if(EXISTS "${OBS_STUDIO_DIR}/cmake/LibObs/LibObsConfig.cmake")
|
||||
message(STATUS "${PROJECT_NAME}: Using packaged obs-studio.")
|
||||
CacheSet(${PropertyPrefix}OBS_PACKAGE TRUE)
|
||||
elseif(EXISTS "${OBS_STUDIO_DIR}/libobs/obs-module.h")
|
||||
message(STATUS "${PROJECT_NAME}: Using referenced obs-studio.")
|
||||
CacheSet(${PropertyPrefix}OBS_REFERENCE TRUE)
|
||||
else()
|
||||
message(STATUS "${PROJECT_NAME}: No OBS Studio detected, using downloadable prebuilt binaries.")
|
||||
CacheSet(${PropertyPrefix}OBS_DOWNLOAD TRUE)
|
||||
set(${PropertyPrefix}OBS_DOWNLOAD_URL "https://github.com/Xaymar/obs-studio/releases/download/${OBS_DOWNLOAD_VERSION}/obs-studio-${ARCH}-0.0.0.0-vs2017.7z")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# CMake Modules
|
||||
# Download OBS Studio and OBS Dependencies
|
||||
if(${PropertyPrefix}OBS_DOWNLOAD)
|
||||
include("DownloadProject")
|
||||
endif()
|
||||
|
||||
# Load OBS Studio
|
||||
if(${PropertyPrefix}OBS_NATIVE)
|
||||
elseif(${PropertyPrefix}OBS_PACKAGE)
|
||||
include("${OBS_STUDIO_DIR}/cmake/LibObs/LibObsConfig.cmake")
|
||||
elseif(${PropertyPrefix}OBS_REFERENCE)
|
||||
set(obsPath "${OBS_STUDIO_DIR}")
|
||||
include("${OBS_STUDIO_DIR}/cmake/external/FindLibobs.cmake")
|
||||
elseif(${PropertyPrefix}OBS_DOWNLOAD)
|
||||
|
||||
download_project(
|
||||
PROJ libobs
|
||||
URL ${OBS_DOWNLOAD_URL}
|
||||
UPDATE_DISCONNECTED 1
|
||||
)
|
||||
|
||||
download_project(
|
||||
PROJ obsdeps
|
||||
URL ${OBS_DEPENDENCIES_URL}
|
||||
UPDATE_DISCONNECTED 1
|
||||
)
|
||||
endif()
|
||||
|
||||
# Load OBS Studio & Dependencies
|
||||
if(${PropertyPrefix}OBS_PACKAGE)
|
||||
include("${OBS_STUDIO_DIR}/cmake/LibObs/LibObsConfig.cmake")
|
||||
elseif(${PropertyPrefix}OBS_REFERENCE)
|
||||
set(obsPath "${OBS_STUDIO_DIR}")
|
||||
include("${OBS_STUDIO_DIR}/cmake/external/FindLibobs.cmake")
|
||||
elseif(${PropertyPrefix}OBS_DOWNLOAD)
|
||||
include("${libobs_SOURCE_DIR}/cmake/LibObs/LibObsConfig.cmake")
|
||||
else()
|
||||
message(CRITICAL "Impossible case reached, verify system stability.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Load FFmpeg
|
||||
find_path(
|
||||
FFmpegPath "libavcodec/avcodec.h"
|
||||
HINTS
|
||||
${OBS_DEPENDENCIES_DIR}
|
||||
${obsdeps_SOURCE_DIR}
|
||||
PATHS
|
||||
/usr/include
|
||||
/usr/local/include
|
||||
/opt/local/include
|
||||
/sw/include
|
||||
PATH_SUFFIXES
|
||||
win${BITS}
|
||||
win${BITS}/bin
|
||||
win${BITS}/include
|
||||
win${ARCH}
|
||||
win${ARCH}/bin
|
||||
win${ARCH}/include
|
||||
bin
|
||||
include
|
||||
)
|
||||
find_package(FFmpeg REQUIRED COMPONENTS avutil avcodec swscale)
|
||||
|
||||
################################################################################
|
||||
# Code
|
||||
################################################################################
|
||||
|
@ -297,7 +328,6 @@ set(PROJECT_PRIVATE_GENERATED
|
|||
"${PROJECT_BINARY_DIR}/source/version.hpp"
|
||||
)
|
||||
set(PROJECT_PRIVATE_SOURCE
|
||||
|
||||
# Plugin
|
||||
"${PROJECT_SOURCE_DIR}/source/plugin.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/plugin.cpp"
|
||||
|
@ -399,7 +429,44 @@ set(PROJECT_PRIVATE_SOURCE
|
|||
"${PROJECT_SOURCE_DIR}/source/filters/filter-transform.cpp"
|
||||
|
||||
# Transitions
|
||||
|
||||
# ffmpeg
|
||||
"${PROJECT_SOURCE_DIR}/source/ffmpeg/avframe-queue.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/ffmpeg/avframe-queue.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/ffmpeg/swscale.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/ffmpeg/swscale.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/ffmpeg/tools.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/ffmpeg/tools.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/ffmpeg/hwapi/base.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/ffmpeg/hwapi/base.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/ffmpeg/hwapi/d3d11.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/ffmpeg/hwapi/d3d11.cpp"
|
||||
|
||||
# Encoders
|
||||
"${PROJECT_SOURCE_DIR}/source/encoders/encoder.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/encoders/encoder.cpp"
|
||||
# Encoders/Codecs
|
||||
"${PROJECT_SOURCE_DIR}/source/encoders/codecs/hevc.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/encoders/codecs/hevc.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/encoders/codecs/h264.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/encoders/codecs/h264.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/encoders/codecs/prores.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/encoders/codecs/prores.cpp"
|
||||
# Encoders/Handlers
|
||||
"${PROJECT_SOURCE_DIR}/source/encoders/handlers/handler.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/encoders/handlers/handler.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/encoders/handlers/debug_handler.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/encoders/handlers/debug_handler.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/encoders/handlers/prores_aw_handler.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/encoders/handlers/prores_aw_handler.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/encoders/handlers/nvenc_shared.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/encoders/handlers/nvenc_shared.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/encoders/handlers/nvenc_h264_handler.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/encoders/handlers/nvenc_h264_handler.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/encoders/handlers/nvenc_hevc_handler.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/encoders/handlers/nvenc_hevc_handler.cpp"
|
||||
)
|
||||
|
||||
set(PROJECT_PRIVATE
|
||||
${PROJECT_DATA}
|
||||
${PROJECT_PRIVATE_GENERATED}
|
||||
|
@ -474,7 +541,8 @@ endif()
|
|||
|
||||
# Link Libraries
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
"${PROJECT_LIBRARIES}"
|
||||
${PROJECT_LIBRARIES}
|
||||
${FFMPEG_LIBRARIES}
|
||||
)
|
||||
|
||||
# Definitions
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
set(CLANG_FORMAT_BIN "clang-format" CACHE PATH "Path (or name) of the clang-format binary")
|
||||
set(CLANG_PATH "" CACHE PATH "Path to Clang Toolset (if not in environment)")
|
||||
|
||||
function(clang_format)
|
||||
cmake_parse_arguments(
|
||||
|
@ -9,15 +9,23 @@ function(clang_format)
|
|||
"TARGETS"
|
||||
)
|
||||
|
||||
if(NOT EXISTS ${CLANG_FORMAT_BIN})
|
||||
find_program(clang_format_bin_tmp ${CLANG_FORMAT_BIN})
|
||||
if(clang_format_bin_tmp)
|
||||
set(CLANG_FORMAT_BIN "${clang_format_bin_tmp}" CACHE PATH "Path (or name) of the clang-format binary")
|
||||
unset(clang_format_bin_tmp)
|
||||
else()
|
||||
message(WARNING "Clang: Could not find clang-format at path '${CLANG_FORMAT_BIN}'. Disabling clang-format...")
|
||||
return()
|
||||
endif()
|
||||
find_program(CLANG_FORMAT_BIN
|
||||
"clang-format"
|
||||
HINTS
|
||||
${CLANG_PATH}
|
||||
PATHS
|
||||
/bin
|
||||
/sbin
|
||||
/usr/bin
|
||||
/usr/local/bin
|
||||
PATH_SUFFIXES
|
||||
bin
|
||||
bin64
|
||||
bin32
|
||||
)
|
||||
if(NOT CLANG_FORMAT_BIN)
|
||||
message(WARNING "Clang: Could not find clang-format at path '${CLANG_FORMAT_BIN}'. Disabling clang-format...")
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(NOT _CLANG_FORMAT_FILTER)
|
||||
|
|
155
cmake/modules/FindFFmpeg.cmake
Normal file
155
cmake/modules/FindFFmpeg.cmake
Normal file
|
@ -0,0 +1,155 @@
|
|||
#
|
||||
# This module defines the following variables:
|
||||
#
|
||||
# FFMPEG_FOUND - All required components and the core library were found
|
||||
# FFMPEG_INCLUDE_DIRS - Combined list of all components include dirs
|
||||
# FFMPEG_LIBRARIES - Combined list of all componenets libraries
|
||||
# FFMPEG_VERSION_STRING - Version of the first component requested
|
||||
#
|
||||
# For each requested component the following variables are defined:
|
||||
#
|
||||
# FFMPEG_<component>_FOUND - The component was found
|
||||
# FFMPEG_<component>_INCLUDE_DIRS - The components include dirs
|
||||
# FFMPEG_<component>_LIBRARIES - The components libraries
|
||||
# FFMPEG_<component>_VERSION_STRING - The components version string
|
||||
# FFMPEG_<component>_VERSION_MAJOR - The components major version
|
||||
# FFMPEG_<component>_VERSION_MINOR - The components minor version
|
||||
# FFMPEG_<component>_VERSION_MICRO - The components micro version
|
||||
#
|
||||
# <component> is the uppercase name of the component
|
||||
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(_lib_suffix 64)
|
||||
else()
|
||||
set(_lib_suffix 32)
|
||||
endif()
|
||||
|
||||
function(find_ffmpeg_library component header)
|
||||
string(TOUPPER "${component}" component_u)
|
||||
set(FFMPEG_${component_u}_FOUND FALSE PARENT_SCOPE)
|
||||
set(FFmpeg_${component}_FOUND FALSE PARENT_SCOPE)
|
||||
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(PC_FFMPEG_${component} QUIET lib${component})
|
||||
endif()
|
||||
|
||||
find_path(FFMPEG_${component}_INCLUDE_DIR
|
||||
NAMES
|
||||
"lib${component}/${header}" "lib${component}/version.h"
|
||||
HINTS
|
||||
ENV FFmpegPath${_lib_suffix}
|
||||
ENV FFmpegPath
|
||||
ENV DepsPath${_lib_suffix}
|
||||
ENV DepsPath
|
||||
${FFmpegPath${_lib_suffix}}
|
||||
${FFmpegPath}
|
||||
${DepsPath${_lib_suffix}}
|
||||
${DepsPath}
|
||||
${PC_FFMPEG_${component}_INCLUDE_DIRS}
|
||||
PATHS
|
||||
/usr/include /usr/local/include /opt/local/include /sw/include
|
||||
PATH_SUFFIXES ffmpeg libav include)
|
||||
|
||||
find_library(FFMPEG_${component}_LIBRARY
|
||||
NAMES
|
||||
"${component}" "lib${component}"
|
||||
HINTS
|
||||
ENV FFmpegPath${_lib_suffix}
|
||||
ENV FFmpegPath
|
||||
ENV DepsPath${_lib_suffix}
|
||||
ENV DepsPath
|
||||
${FFmpegPath${_lib_suffix}}
|
||||
${FFmpegPath}
|
||||
${DepsPath${_lib_suffix}}
|
||||
${DepsPath}
|
||||
${PC_FFMPEG_${component}_LIBRARY_DIRS}
|
||||
PATHS
|
||||
/usr/lib /usr/local/lib /opt/local/lib /sw/lib
|
||||
PATH_SUFFIXES
|
||||
lib${_lib_suffix} lib
|
||||
libs${_lib_suffix} libs
|
||||
bin${_lib_suffix} bin
|
||||
../lib${_lib_suffix} ../lib
|
||||
../libs${_lib_suffix} ../libs
|
||||
../bin${_lib_suffix} ../bin)
|
||||
|
||||
set(FFMPEG_${component_u}_INCLUDE_DIRS ${FFMPEG_${component}_INCLUDE_DIR} PARENT_SCOPE)
|
||||
set(FFMPEG_${component_u}_LIBRARIES ${FFMPEG_${component}_LIBRARY} PARENT_SCOPE)
|
||||
|
||||
mark_as_advanced(FFMPEG_${component}_INCLUDE_DIR FFMPEG_${component}_LIBRARY)
|
||||
|
||||
if(FFMPEG_${component}_INCLUDE_DIR AND FFMPEG_${component}_LIBRARY)
|
||||
set(FFMPEG_${component_u}_FOUND TRUE PARENT_SCOPE)
|
||||
set(FFmpeg_${component}_FOUND TRUE PARENT_SCOPE)
|
||||
|
||||
list(APPEND FFMPEG_INCLUDE_DIRS ${FFMPEG_${component}_INCLUDE_DIR})
|
||||
list(REMOVE_DUPLICATES FFMPEG_INCLUDE_DIRS)
|
||||
set(FFMPEG_INCLUDE_DIRS "${FFMPEG_INCLUDE_DIRS}" PARENT_SCOPE)
|
||||
|
||||
list(APPEND FFMPEG_LIBRARIES ${FFMPEG_${component}_LIBRARY})
|
||||
list(REMOVE_DUPLICATES FFMPEG_LIBRARIES)
|
||||
set(FFMPEG_LIBRARIES "${FFMPEG_LIBRARIES}" PARENT_SCOPE)
|
||||
|
||||
set(FFMPEG_${component_u}_VERSION_STRING "unknown" PARENT_SCOPE)
|
||||
set(_vfile "${FFMPEG_${component}_INCLUDE_DIR}/lib${component}/version.h")
|
||||
|
||||
if(EXISTS "${_vfile}")
|
||||
file(STRINGS "${_vfile}" _version_parse REGEX "^.*VERSION_(MAJOR|MINOR|MICRO)[ \t]+[0-9]+[ \t]*$")
|
||||
string(REGEX REPLACE ".*VERSION_MAJOR[ \t]+([0-9]+).*" "\\1" _major "${_version_parse}")
|
||||
string(REGEX REPLACE ".*VERSION_MINOR[ \t]+([0-9]+).*" "\\1" _minor "${_version_parse}")
|
||||
string(REGEX REPLACE ".*VERSION_MICRO[ \t]+([0-9]+).*" "\\1" _micro "${_version_parse}")
|
||||
|
||||
set(FFMPEG_${component_u}_VERSION_MAJOR "${_major}" PARENT_SCOPE)
|
||||
set(FFMPEG_${component_u}_VERSION_MINOR "${_minor}" PARENT_SCOPE)
|
||||
set(FFMPEG_${component_u}_VERSION_MICRO "${_micro}" PARENT_SCOPE)
|
||||
|
||||
set(FFMPEG_${component_u}_VERSION_STRING "${_major}.${_minor}.${_micro}" PARENT_SCOPE)
|
||||
else()
|
||||
message(STATUS "Failed parsing FFmpeg ${component} version")
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
set(FFMPEG_INCLUDE_DIRS)
|
||||
set(FFMPEG_LIBRARIES)
|
||||
|
||||
if(NOT FFmpeg_FIND_COMPONENTS)
|
||||
message(FATAL_ERROR "No FFmpeg components requested")
|
||||
endif()
|
||||
|
||||
list(GET FFmpeg_FIND_COMPONENTS 0 _first_comp)
|
||||
string(TOUPPER "${_first_comp}" _first_comp)
|
||||
|
||||
foreach(component ${FFmpeg_FIND_COMPONENTS})
|
||||
if(component STREQUAL "avcodec")
|
||||
find_ffmpeg_library("${component}" "avcodec.h")
|
||||
elseif(component STREQUAL "avdevice")
|
||||
find_ffmpeg_library("${component}" "avdevice.h")
|
||||
elseif(component STREQUAL "avfilter")
|
||||
find_ffmpeg_library("${component}" "avfilter.h")
|
||||
elseif(component STREQUAL "avformat")
|
||||
find_ffmpeg_library("${component}" "avformat.h")
|
||||
elseif(component STREQUAL "avresample")
|
||||
find_ffmpeg_library("${component}" "avresample.h")
|
||||
elseif(component STREQUAL "avutil")
|
||||
find_ffmpeg_library("${component}" "avutil.h")
|
||||
elseif(component STREQUAL "postproc")
|
||||
find_ffmpeg_library("${component}" "postprocess.h")
|
||||
elseif(component STREQUAL "swresample")
|
||||
find_ffmpeg_library("${component}" "swresample.h")
|
||||
elseif(component STREQUAL "swscale")
|
||||
find_ffmpeg_library("${component}" "swscale.h")
|
||||
else()
|
||||
message(FATAL_ERROR "Unknown FFmpeg component requested: ${component}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(FFmpeg
|
||||
FOUND_VAR FFMPEG_FOUND
|
||||
REQUIRED_VARS FFMPEG_${_first_comp}_LIBRARIES FFMPEG_${_first_comp}_INCLUDE_DIRS
|
||||
VERSION_VAR FFMPEG_${_first_comp}_VERSION_STRING
|
||||
HANDLE_COMPONENTS)
|
22
source/encoders/codecs/h264.cpp
Normal file
22
source/encoders/codecs/h264.cpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "h264.hpp"
|
67
source/encoders/codecs/h264.hpp
Normal file
67
source/encoders/codecs/h264.hpp
Normal file
|
@ -0,0 +1,67 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
#include <map>
|
||||
|
||||
// Codec: H264
|
||||
#define P_H264 "Codec.H264"
|
||||
#define P_H264_PROFILE "Codec.H264.Profile"
|
||||
#define P_H264_LEVEL "Codec.H264.Level"
|
||||
|
||||
namespace obsffmpeg {
|
||||
namespace codecs {
|
||||
namespace h264 {
|
||||
enum class profile {
|
||||
CONSTRAINED_BASELINE,
|
||||
BASELINE,
|
||||
MAIN,
|
||||
HIGH,
|
||||
HIGH444_PREDICTIVE,
|
||||
UNKNOWN = -1,
|
||||
};
|
||||
|
||||
enum class level {
|
||||
L1_0 = 10,
|
||||
L1_0b,
|
||||
L1_1,
|
||||
L1_2,
|
||||
L1_3,
|
||||
L2_0 = 20,
|
||||
L2_1,
|
||||
L2_2,
|
||||
L3_0 = 30,
|
||||
L3_1,
|
||||
L3_2,
|
||||
L4_0 = 40,
|
||||
L4_1,
|
||||
L4_2,
|
||||
L5_0 = 50,
|
||||
L5_1,
|
||||
L5_2,
|
||||
L6_0 = 60,
|
||||
L6_1,
|
||||
L6_2,
|
||||
UNKNOWN = -1,
|
||||
};
|
||||
} // namespace h264
|
||||
} // namespace codecs
|
||||
} // namespace obsffmpeg
|
236
source/encoders/codecs/hevc.cpp
Normal file
236
source/encoders/codecs/hevc.cpp
Normal file
|
@ -0,0 +1,236 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "hevc.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
enum class nal_unit_type : uint8_t { // 6 bits
|
||||
TRAIL_N = 0,
|
||||
TRAIL_R = 1,
|
||||
TSA_N = 2,
|
||||
TSA_R = 3,
|
||||
STSA_N = 4,
|
||||
STSA_R = 5,
|
||||
RADL_N = 6,
|
||||
RADL_R = 7,
|
||||
RASL_N = 8,
|
||||
RASL_R = 9,
|
||||
RSV_VCL_N10 = 10,
|
||||
RSV_VCL_R11 = 11,
|
||||
RSV_VCL_N12 = 12,
|
||||
RSV_VCL_R13 = 13,
|
||||
RSV_VCL_N14 = 14,
|
||||
RSV_VCL_R15 = 15,
|
||||
BLA_W_LP = 16,
|
||||
BLA_W_RADL = 17,
|
||||
BLA_N_LP = 18,
|
||||
IDR_W_RADL = 19,
|
||||
IDR_N_LP = 20,
|
||||
CRA = 21,
|
||||
RSV_IRAP_VCL22 = 22,
|
||||
RSV_IRAP_VCL23 = 23,
|
||||
RSV_VCL24 = 24,
|
||||
RSV_VCL25 = 25,
|
||||
RSV_VCL26 = 26,
|
||||
RSV_VCL27 = 27,
|
||||
RSV_VCL28 = 28,
|
||||
RSV_VCL29 = 29,
|
||||
RSV_VCL30 = 30,
|
||||
RSV_VCL31 = 31,
|
||||
VPS = 32,
|
||||
SPS = 33,
|
||||
PPS = 34,
|
||||
AUD = 35,
|
||||
EOS = 36,
|
||||
EOB = 37,
|
||||
FD = 38,
|
||||
PREFIX_SEI = 39,
|
||||
SUFFIX_SEI = 40,
|
||||
RSV_NVCL41 = 41,
|
||||
RSV_NVCL42 = 42,
|
||||
RSV_NVCL43 = 43,
|
||||
RSV_NVCL44 = 44,
|
||||
RSV_NVCL45 = 45,
|
||||
RSV_NVCL46 = 46,
|
||||
RSV_NVCL47 = 47,
|
||||
UNSPEC48 = 48,
|
||||
UNSPEC49 = 49,
|
||||
UNSPEC50 = 50,
|
||||
UNSPEC51 = 51,
|
||||
UNSPEC52 = 52,
|
||||
UNSPEC53 = 53,
|
||||
UNSPEC54 = 54,
|
||||
UNSPEC55 = 55,
|
||||
UNSPEC56 = 56,
|
||||
UNSPEC57 = 57,
|
||||
UNSPEC58 = 58,
|
||||
UNSPEC59 = 59,
|
||||
UNSPEC60 = 60,
|
||||
UNSPEC61 = 61,
|
||||
UNSPEC62 = 62,
|
||||
UNSPEC63 = 63,
|
||||
};
|
||||
|
||||
struct hevc_nal_unit_header {
|
||||
bool zero_bit : 1;
|
||||
nal_unit_type nut : 6;
|
||||
uint8_t layer_id : 6;
|
||||
uint8_t temporal_id_plus1 : 3;
|
||||
};
|
||||
|
||||
struct hevc_nal {
|
||||
hevc_nal_unit_header* header;
|
||||
size_t size = 0;
|
||||
uint8_t* data = nullptr;
|
||||
};
|
||||
|
||||
bool is_nal(uint8_t* data, uint8_t* end)
|
||||
{
|
||||
size_t s = end - data;
|
||||
if (s < 4)
|
||||
return false;
|
||||
|
||||
if (*data != 0x0)
|
||||
return false;
|
||||
if (*(data + 1) != 0x0)
|
||||
return false;
|
||||
if (*(data + 2) != 0x0)
|
||||
return false;
|
||||
if (*(data + 3) != 0x1)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool seek_to_nal(uint8_t*& data, uint8_t* end)
|
||||
{
|
||||
if (data > end)
|
||||
return false;
|
||||
|
||||
for (; data <= end; data++) {
|
||||
if (is_nal(data, end)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t get_nal_size(uint8_t* data, uint8_t* end)
|
||||
{
|
||||
uint8_t* ptr = data + 4;
|
||||
if (!seek_to_nal(ptr, end)) {
|
||||
return end - data;
|
||||
}
|
||||
return ptr - data;
|
||||
}
|
||||
|
||||
bool is_discard_marker(uint8_t* data, uint8_t* end)
|
||||
{
|
||||
size_t s = end - data;
|
||||
if (s < 4)
|
||||
return false;
|
||||
|
||||
if (*data != 0x0)
|
||||
return false;
|
||||
if (*(data + 1) != 0x0)
|
||||
return false;
|
||||
|
||||
if (*(data + 2) == 0x3) {
|
||||
// Discard marker only if the next byte is not 0x0, 0x1, 0x2 or 0x3.
|
||||
if (*(data + 3) != 0x0)
|
||||
return false;
|
||||
if (*(data + 3) != 0x1)
|
||||
return false;
|
||||
if (*(data + 3) != 0x2)
|
||||
return false;
|
||||
if (*(data + 3) != 0x3)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
} else {
|
||||
if (*(data + 2) == 0x0)
|
||||
return true;
|
||||
if (*(data + 2) == 0x1)
|
||||
return true;
|
||||
if (*(data + 2) == 0x2)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool should_discard_nal(uint8_t* data, uint8_t* end)
|
||||
{
|
||||
if (data > end)
|
||||
return true;
|
||||
|
||||
for (; data <= end; data++) {
|
||||
if (is_discard_marker(data, end))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void progress_parse(uint8_t*& ptr, uint8_t* end, size_t& sz)
|
||||
{
|
||||
ptr += sz;
|
||||
sz = get_nal_size(ptr, end);
|
||||
}
|
||||
|
||||
void obsffmpeg::codecs::hevc::extract_header_sei(uint8_t* data, size_t sz_data, std::vector<uint8_t>& header,
|
||||
std::vector<uint8_t>& sei)
|
||||
{
|
||||
uint8_t* ptr = data;
|
||||
uint8_t* end = data + sz_data;
|
||||
|
||||
// Reserve enough memory to store the entire packet data if necessary.
|
||||
header.reserve(sz_data);
|
||||
sei.reserve(sz_data);
|
||||
|
||||
if (!seek_to_nal(ptr, end)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t nal_sz = get_nal_size(ptr, end); nal_sz > 0; progress_parse(ptr, end, nal_sz)) {
|
||||
if (should_discard_nal(ptr + 4, ptr + nal_sz)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
hevc_nal nal;
|
||||
nal.header = reinterpret_cast<hevc_nal_unit_header*>(ptr + 4);
|
||||
nal.size = nal_sz - 4 - 2;
|
||||
nal.data = ptr + 4 + 2;
|
||||
|
||||
switch (nal.header->nut) {
|
||||
case nal_unit_type::VPS:
|
||||
case nal_unit_type::SPS:
|
||||
case nal_unit_type::PPS:
|
||||
header.insert(header.end(), ptr, ptr + nal_sz);
|
||||
break;
|
||||
case nal_unit_type::PREFIX_SEI:
|
||||
case nal_unit_type::SUFFIX_SEI:
|
||||
sei.insert(sei.end(), ptr, ptr + nal_sz);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
69
source/encoders/codecs/hevc.hpp
Normal file
69
source/encoders/codecs/hevc.hpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
#include <vector>
|
||||
|
||||
// Codec: HEVC
|
||||
#define P_HEVC "Codec.HEVC"
|
||||
#define P_HEVC_PROFILE "Codec.HEVC.Profile"
|
||||
#define P_HEVC_TIER "Codec.HEVC.Tier"
|
||||
#define P_HEVC_LEVEL "Codec.HEVC.Level"
|
||||
|
||||
namespace obsffmpeg {
|
||||
namespace codecs {
|
||||
namespace hevc {
|
||||
enum class profile {
|
||||
MAIN,
|
||||
MAIN10,
|
||||
RANGE_EXTENDED,
|
||||
UNKNOWN = -1,
|
||||
};
|
||||
|
||||
enum class tier {
|
||||
MAIN,
|
||||
HIGH,
|
||||
UNKNOWN = -1,
|
||||
};
|
||||
|
||||
enum class level {
|
||||
L1_0 = 30,
|
||||
L2_0 = 60,
|
||||
L2_1 = 63,
|
||||
L3_0 = 90,
|
||||
L3_1 = 93,
|
||||
L4_0 = 120,
|
||||
L4_1 = 123,
|
||||
L5_0 = 150,
|
||||
L5_1 = 153,
|
||||
L5_2 = 156,
|
||||
L6_0 = 180,
|
||||
L6_1 = 183,
|
||||
L6_2 = 186,
|
||||
UNKNOWN = -1,
|
||||
};
|
||||
|
||||
void extract_header_sei(uint8_t* data, size_t sz_data, std::vector<uint8_t>& header,
|
||||
std::vector<uint8_t>& sei);
|
||||
|
||||
} // namespace hevc
|
||||
} // namespace codecs
|
||||
} // namespace obsffmpeg
|
22
source/encoders/codecs/prores.cpp
Normal file
22
source/encoders/codecs/prores.cpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "prores.hpp"
|
32
source/encoders/codecs/prores.hpp
Normal file
32
source/encoders/codecs/prores.hpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
// Codec: ProRes
|
||||
#define P_PRORES "Codec.ProRes"
|
||||
#define P_PRORES_PROFILE "Codec.ProRes.Profile"
|
||||
#define P_PRORES_PROFILE_APCS "Codec.ProRes.Profile.APCS"
|
||||
#define P_PRORES_PROFILE_APCO "Codec.ProRes.Profile.APCO"
|
||||
#define P_PRORES_PROFILE_APCN "Codec.ProRes.Profile.APCN"
|
||||
#define P_PRORES_PROFILE_APCH "Codec.ProRes.Profile.APCH"
|
||||
#define P_PRORES_PROFILE_AP4H "Codec.ProRes.Profile.AP4H"
|
||||
#define P_PRORES_PROFILE_AP4X "Codec.ProRes.Profile.AP4X"
|
1429
source/encoders/encoder.cpp
Normal file
1429
source/encoders/encoder.cpp
Normal file
File diff suppressed because it is too large
Load diff
164
source/encoders/encoder.hpp
Normal file
164
source/encoders/encoder.hpp
Normal file
|
@ -0,0 +1,164 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <stack>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include "ffmpeg/avframe-queue.hpp"
|
||||
#include "ffmpeg/swscale.hpp"
|
||||
#include "hwapi/base.hpp"
|
||||
#include "ui/handler.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <obs-properties.h>
|
||||
#include <obs.h>
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/frame.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
namespace obsffmpeg {
|
||||
class unsupported_gpu_exception : public std::runtime_error {
|
||||
public:
|
||||
unsupported_gpu_exception(const std::string& reason) : runtime_error(reason) {}
|
||||
};
|
||||
|
||||
struct encoder_info {
|
||||
std::string uid;
|
||||
std::string codec;
|
||||
std::string readable_name;
|
||||
obs_encoder_info oei = {0};
|
||||
};
|
||||
|
||||
class encoder_factory {
|
||||
encoder_info info;
|
||||
encoder_info info_fallback;
|
||||
const AVCodec* avcodec_ptr;
|
||||
|
||||
std::shared_ptr<obsffmpeg::ui::handler> _handler;
|
||||
|
||||
public:
|
||||
encoder_factory(const AVCodec* codec);
|
||||
virtual ~encoder_factory();
|
||||
|
||||
void register_encoder();
|
||||
|
||||
void get_defaults(obs_data_t* settings, bool hw_encoder = false);
|
||||
|
||||
void get_properties(obs_properties_t* props, bool hw_encoder = false);
|
||||
|
||||
const AVCodec* get_avcodec();
|
||||
|
||||
const encoder_info& get_info();
|
||||
|
||||
const encoder_info& get_fallback();
|
||||
};
|
||||
|
||||
class encoder {
|
||||
obs_encoder_t* _self;
|
||||
encoder_factory* _factory;
|
||||
|
||||
const AVCodec* _codec;
|
||||
AVCodecContext* _context;
|
||||
|
||||
std::shared_ptr<obsffmpeg::ui::handler> _handler;
|
||||
|
||||
std::shared_ptr<obsffmpeg::hwapi::base> _hwapi;
|
||||
std::shared_ptr<obsffmpeg::hwapi::instance> _hwinst;
|
||||
|
||||
ffmpeg::swscale _swscale;
|
||||
AVPacket _current_packet;
|
||||
|
||||
size_t _lag_in_frames;
|
||||
size_t _count_send_frames;
|
||||
|
||||
// Extra Data
|
||||
bool _have_first_frame;
|
||||
std::vector<uint8_t> _extra_data;
|
||||
std::vector<uint8_t> _sei_data;
|
||||
|
||||
// Frame Stack and Queue
|
||||
std::stack<std::shared_ptr<AVFrame>> _free_frames;
|
||||
std::queue<std::shared_ptr<AVFrame>> _used_frames;
|
||||
std::chrono::high_resolution_clock::time_point _free_frames_last_used;
|
||||
|
||||
void initialize_sw(obs_data_t* settings);
|
||||
void initialize_hw(obs_data_t* settings);
|
||||
|
||||
void push_free_frame(std::shared_ptr<AVFrame> frame);
|
||||
std::shared_ptr<AVFrame> pop_free_frame();
|
||||
|
||||
void push_used_frame(std::shared_ptr<AVFrame> frame);
|
||||
std::shared_ptr<AVFrame> pop_used_frame();
|
||||
|
||||
public:
|
||||
encoder(obs_data_t* settings, obs_encoder_t* encoder, bool is_texture_encode = false);
|
||||
virtual ~encoder();
|
||||
|
||||
public: // OBS API
|
||||
// Shared
|
||||
void get_properties(obs_properties_t* props, bool hw_encode = false);
|
||||
|
||||
bool update(obs_data_t* settings);
|
||||
|
||||
// Audio only
|
||||
void get_audio_info(struct audio_convert_info* info);
|
||||
|
||||
size_t get_frame_size();
|
||||
|
||||
bool audio_encode(struct encoder_frame* frame, struct encoder_packet* packet, bool* received_packet);
|
||||
|
||||
// Video only
|
||||
void get_video_info(struct video_scale_info* info);
|
||||
|
||||
bool get_sei_data(uint8_t** sei_data, size_t* size);
|
||||
|
||||
bool get_extra_data(uint8_t** extra_data, size_t* size);
|
||||
|
||||
bool video_encode(struct encoder_frame* frame, struct encoder_packet* packet, bool* received_packet);
|
||||
|
||||
bool video_encode_texture(uint32_t handle, int64_t pts, uint64_t lock_key, uint64_t* next_key,
|
||||
struct encoder_packet* packet, bool* received_packet);
|
||||
|
||||
int receive_packet(bool* received_packet, struct encoder_packet* packet);
|
||||
|
||||
int send_frame(std::shared_ptr<AVFrame> frame);
|
||||
|
||||
bool encode_avframe(std::shared_ptr<AVFrame> frame, struct encoder_packet* packet,
|
||||
bool* received_packet);
|
||||
|
||||
public: // Handler API
|
||||
bool is_hardware_encode();
|
||||
|
||||
const AVCodec* get_avcodec();
|
||||
|
||||
const AVCodecContext* get_avcodeccontext();
|
||||
|
||||
void parse_ffmpeg_commandline(std::string text);
|
||||
};
|
||||
} // namespace obsffmpeg
|
210
source/encoders/handlers/debug_handler.cpp
Normal file
210
source/encoders/handlers/debug_handler.cpp
Normal file
|
@ -0,0 +1,210 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "debug_handler.hpp"
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include "handler.hpp"
|
||||
#include "plugin.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <obs-properties.h>
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavutil/opt.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::debug_handler::get_defaults(obs_data_t*, const AVCodec*, AVCodecContext*, bool) {}
|
||||
|
||||
template<typename T>
|
||||
std::string to_string(T value){};
|
||||
|
||||
template<>
|
||||
std::string to_string(int64_t value)
|
||||
{
|
||||
std::vector<char> buf(32);
|
||||
snprintf(buf.data(), buf.size(), "%lld", value);
|
||||
return std::string(buf.data(), buf.data() + buf.size());
|
||||
}
|
||||
|
||||
template<>
|
||||
std::string to_string(uint64_t value)
|
||||
{
|
||||
std::vector<char> buf(32);
|
||||
snprintf(buf.data(), buf.size(), "%llu", value);
|
||||
return std::string(buf.data(), buf.data() + buf.size());
|
||||
}
|
||||
|
||||
template<>
|
||||
std::string to_string(double_t value)
|
||||
{
|
||||
std::vector<char> buf(32);
|
||||
snprintf(buf.data(), buf.size(), "%f", value);
|
||||
return std::string(buf.data(), buf.data() + buf.size());
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::debug_handler::get_properties(obs_properties_t*, const AVCodec* codec, AVCodecContext* context,
|
||||
bool)
|
||||
{
|
||||
if (context)
|
||||
return;
|
||||
|
||||
AVCodecContext* ctx = avcodec_alloc_context3(codec);
|
||||
if (!ctx->priv_data) {
|
||||
avcodec_free_context(&ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
PLOG_INFO("Options for '%s':", codec->name);
|
||||
|
||||
std::pair<AVOptionType, std::string> opt_type_name[] = {
|
||||
{AV_OPT_TYPE_FLAGS, "Flags"},
|
||||
{AV_OPT_TYPE_INT, "Int"},
|
||||
{AV_OPT_TYPE_INT64, "Int64"},
|
||||
{AV_OPT_TYPE_DOUBLE, "Double"},
|
||||
{AV_OPT_TYPE_FLOAT, "Float"},
|
||||
{AV_OPT_TYPE_STRING, "String"},
|
||||
{AV_OPT_TYPE_RATIONAL, "Rational"},
|
||||
{AV_OPT_TYPE_BINARY, "Binary"},
|
||||
{AV_OPT_TYPE_DICT, "Dictionary"},
|
||||
{AV_OPT_TYPE_UINT64, "Unsigned Int64"},
|
||||
{AV_OPT_TYPE_CONST, "Constant"},
|
||||
{AV_OPT_TYPE_IMAGE_SIZE, "Image Size"},
|
||||
{AV_OPT_TYPE_PIXEL_FMT, "Pixel Format"},
|
||||
{AV_OPT_TYPE_SAMPLE_FMT, "Sample Format"},
|
||||
{AV_OPT_TYPE_VIDEO_RATE, "Video Rate"},
|
||||
{AV_OPT_TYPE_DURATION, "Duration"},
|
||||
{AV_OPT_TYPE_COLOR, "Color"},
|
||||
{AV_OPT_TYPE_CHANNEL_LAYOUT, "Layout"},
|
||||
{AV_OPT_TYPE_BOOL, "Bool"},
|
||||
};
|
||||
std::map<std::string, AVOptionType> unit_types;
|
||||
|
||||
const AVOption* opt = nullptr;
|
||||
while ((opt = av_opt_next(ctx->priv_data, opt)) != nullptr) {
|
||||
std::string type_name = "";
|
||||
for (auto kv : opt_type_name) {
|
||||
if (opt->type == kv.first) {
|
||||
type_name = kv.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (opt->type == AV_OPT_TYPE_CONST) {
|
||||
if (opt->unit == nullptr) {
|
||||
PLOG_INFO(" Constant '%s' and help text '%s' with unknown settings.", opt->name,
|
||||
opt->help);
|
||||
} else {
|
||||
auto unit_type = unit_types.find(opt->unit);
|
||||
if (unit_type == unit_types.end()) {
|
||||
PLOG_INFO(" [%s] Flag '%s' and help text '%s' with value '%lld'.", opt->unit,
|
||||
opt->name, opt->help, opt->default_val.i64);
|
||||
} else {
|
||||
std::string out;
|
||||
switch (unit_type->second) {
|
||||
case AV_OPT_TYPE_BOOL:
|
||||
out = opt->default_val.i64 ? "true" : "false";
|
||||
break;
|
||||
case AV_OPT_TYPE_INT:
|
||||
out = to_string(opt->default_val.i64);
|
||||
break;
|
||||
case AV_OPT_TYPE_UINT64:
|
||||
out = to_string(static_cast<uint64_t>(opt->default_val.i64));
|
||||
break;
|
||||
case AV_OPT_TYPE_FLAGS:
|
||||
out = to_string(static_cast<uint64_t>(opt->default_val.i64));
|
||||
break;
|
||||
case AV_OPT_TYPE_FLOAT:
|
||||
case AV_OPT_TYPE_DOUBLE:
|
||||
out = to_string(opt->default_val.dbl);
|
||||
break;
|
||||
case AV_OPT_TYPE_STRING:
|
||||
out = opt->default_val.str;
|
||||
break;
|
||||
case AV_OPT_TYPE_BINARY:
|
||||
case AV_OPT_TYPE_IMAGE_SIZE:
|
||||
case AV_OPT_TYPE_PIXEL_FMT:
|
||||
case AV_OPT_TYPE_SAMPLE_FMT:
|
||||
case AV_OPT_TYPE_VIDEO_RATE:
|
||||
case AV_OPT_TYPE_DURATION:
|
||||
case AV_OPT_TYPE_COLOR:
|
||||
case AV_OPT_TYPE_CHANNEL_LAYOUT:
|
||||
break;
|
||||
}
|
||||
|
||||
PLOG_INFO(" [%s] Constant '%s' and help text '%s' with value '%s'.", opt->unit,
|
||||
opt->name, opt->help, out.c_str());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (opt->unit != nullptr) {
|
||||
unit_types.emplace(opt->name, opt->type);
|
||||
}
|
||||
|
||||
std::string minimum = "", maximum = "", out;
|
||||
minimum = to_string(opt->min);
|
||||
maximum = to_string(opt->max);
|
||||
{
|
||||
switch (opt->type) {
|
||||
case AV_OPT_TYPE_BOOL:
|
||||
out = opt->default_val.i64 ? "true" : "false";
|
||||
break;
|
||||
case AV_OPT_TYPE_INT:
|
||||
out = to_string(opt->default_val.i64);
|
||||
break;
|
||||
case AV_OPT_TYPE_UINT64:
|
||||
out = to_string(static_cast<uint64_t>(opt->default_val.i64));
|
||||
break;
|
||||
case AV_OPT_TYPE_FLAGS:
|
||||
out = to_string(static_cast<uint64_t>(opt->default_val.i64));
|
||||
break;
|
||||
case AV_OPT_TYPE_FLOAT:
|
||||
case AV_OPT_TYPE_DOUBLE:
|
||||
out = to_string(opt->default_val.dbl);
|
||||
break;
|
||||
case AV_OPT_TYPE_STRING:
|
||||
out = opt->default_val.str ? opt->default_val.str : "<invalid>";
|
||||
break;
|
||||
case AV_OPT_TYPE_BINARY:
|
||||
case AV_OPT_TYPE_IMAGE_SIZE:
|
||||
case AV_OPT_TYPE_PIXEL_FMT:
|
||||
case AV_OPT_TYPE_SAMPLE_FMT:
|
||||
case AV_OPT_TYPE_VIDEO_RATE:
|
||||
case AV_OPT_TYPE_DURATION:
|
||||
case AV_OPT_TYPE_COLOR:
|
||||
case AV_OPT_TYPE_CHANNEL_LAYOUT:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PLOG_INFO(
|
||||
" Option '%s'%s%s%s with help '%s' of type '%s' with default value '%s', minimum '%s' and maximum '%s'.",
|
||||
opt->name, opt->unit ? " with unit (" : "", opt->unit ? opt->unit : "",
|
||||
opt->unit ? ")" : "", opt->help, type_name.c_str(), out.c_str(), minimum.c_str(),
|
||||
maximum.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::debug_handler::update(obs_data_t*, const AVCodec*, AVCodecContext*) {}
|
39
source/encoders/handlers/debug_handler.hpp
Normal file
39
source/encoders/handlers/debug_handler.hpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
#include "handler.hpp"
|
||||
|
||||
namespace obsffmpeg {
|
||||
namespace ui {
|
||||
class debug_handler : public handler {
|
||||
public:
|
||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context,
|
||||
bool hw_encode) override;
|
||||
|
||||
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||
AVCodecContext* context, bool hw_encode) override;
|
||||
|
||||
virtual void update(obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
};
|
||||
} // namespace ui
|
||||
} // namespace obsffmpeg
|
46
source/encoders/handlers/handler.cpp
Normal file
46
source/encoders/handlers/handler.cpp
Normal file
|
@ -0,0 +1,46 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "handler.hpp"
|
||||
#include "encoder.hpp"
|
||||
|
||||
void obsffmpeg::ui::handler::adjust_encoder_info(obsffmpeg::encoder_factory*, obsffmpeg::encoder_info*,
|
||||
obsffmpeg::encoder_info*)
|
||||
{}
|
||||
|
||||
void obsffmpeg::ui::handler::get_defaults(obs_data_t*, const AVCodec*, AVCodecContext*, bool) {}
|
||||
|
||||
bool obsffmpeg::ui::handler::has_keyframe_support(obsffmpeg::encoder* instance)
|
||||
{
|
||||
return (instance->get_avcodec()->capabilities & AV_CODEC_CAP_INTRA_ONLY) == 0;
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::handler::get_properties(obs_properties_t*, const AVCodec*, AVCodecContext*, bool) {}
|
||||
|
||||
void obsffmpeg::ui::handler::update(obs_data_t*, const AVCodec*, AVCodecContext*) {}
|
||||
|
||||
void obsffmpeg::ui::handler::override_update(obsffmpeg::encoder*, obs_data_t*) {}
|
||||
|
||||
void obsffmpeg::ui::handler::log_options(obs_data_t*, const AVCodec*, AVCodecContext*) {}
|
||||
|
||||
void obsffmpeg::ui::handler::override_colorformat(AVPixelFormat&, obs_data_t*, const AVCodec*, AVCodecContext*) {}
|
||||
|
||||
void obsffmpeg::ui::handler::process_avpacket(AVPacket&, const AVCodec*, AVCodecContext*) {}
|
74
source/encoders/handlers/handler.hpp
Normal file
74
source/encoders/handlers/handler.hpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "hwapi/base.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <obs.h>
|
||||
|
||||
#include <obs-data.h>
|
||||
#include <obs-encoder.h>
|
||||
#include <obs-properties.h>
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavcodec/avcodec.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
namespace obsffmpeg {
|
||||
struct encoder_info;
|
||||
class encoder_factory;
|
||||
class encoder;
|
||||
|
||||
namespace ui {
|
||||
class handler {
|
||||
public /*factory*/:
|
||||
virtual void adjust_encoder_info(obsffmpeg::encoder_factory* factory,
|
||||
obsffmpeg::encoder_info* main,
|
||||
obsffmpeg::encoder_info* fallback);
|
||||
|
||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context,
|
||||
bool hw_encode);
|
||||
|
||||
public /*settings*/:
|
||||
virtual bool has_keyframe_support(obsffmpeg::encoder* instance);
|
||||
|
||||
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||
AVCodecContext* context, bool hw_encode);
|
||||
|
||||
virtual void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||
|
||||
virtual void override_update(obsffmpeg::encoder* instance, obs_data_t* settings);
|
||||
|
||||
virtual void log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||
|
||||
public /*instance*/:
|
||||
|
||||
virtual void override_colorformat(AVPixelFormat& target_format, obs_data_t* settings,
|
||||
const AVCodec* codec, AVCodecContext* context);
|
||||
|
||||
virtual void process_avpacket(AVPacket& packet, const AVCodec* codec, AVCodecContext* context);
|
||||
};
|
||||
} // namespace ui
|
||||
} // namespace obsffmpeg
|
182
source/encoders/handlers/nvenc_h264_handler.cpp
Normal file
182
source/encoders/handlers/nvenc_h264_handler.cpp
Normal file
|
@ -0,0 +1,182 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "nvenc_h264_handler.hpp"
|
||||
#include "codecs/h264.hpp"
|
||||
#include "encoder.hpp"
|
||||
#include "ffmpeg/tools.hpp"
|
||||
#include "nvenc_shared.hpp"
|
||||
#include "plugin.hpp"
|
||||
#include "strings.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <obs-module.h>
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavutil/opt.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
using namespace obsffmpeg::codecs::h264;
|
||||
|
||||
std::map<profile, std::string> profiles{
|
||||
{profile::BASELINE, "baseline"},
|
||||
{profile::MAIN, "main"},
|
||||
{profile::HIGH, "high"},
|
||||
{profile::HIGH444_PREDICTIVE, "high444p"},
|
||||
};
|
||||
|
||||
std::map<level, std::string> levels{
|
||||
{level::L1_0, "1.0"}, {level::L1_0b, "1.0b"}, {level::L1_1, "1.1"}, {level::L1_2, "1.2"}, {level::L1_3, "1.3"},
|
||||
{level::L2_0, "2.0"}, {level::L2_1, "2.1"}, {level::L2_2, "2.2"}, {level::L3_0, "3.0"}, {level::L3_1, "3.1"},
|
||||
{level::L3_2, "3.2"}, {level::L4_0, "4.0"}, {level::L4_1, "4.1"}, {level::L4_2, "4.2"}, {level::L5_0, "5.0"},
|
||||
{level::L5_1, "5.1"}, {level::L5_2, "5.2"},
|
||||
};
|
||||
|
||||
INITIALIZER(nvenc_h264_handler_init)
|
||||
{
|
||||
obsffmpeg::initializers.push_back([]() {
|
||||
obsffmpeg::register_codec_handler("h264_nvenc", std::make_shared<obsffmpeg::ui::nvenc_h264_handler>());
|
||||
});
|
||||
};
|
||||
|
||||
void obsffmpeg::ui::nvenc_h264_handler::adjust_encoder_info(obsffmpeg::encoder_factory*, obsffmpeg::encoder_info* main,
|
||||
obsffmpeg::encoder_info* fallback)
|
||||
{
|
||||
main->readable_name = "H.264/AVC NVidia NVENC (Hardware)";
|
||||
fallback->readable_name = "H.264/AVC NVidia NVENC (Software)";
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_h264_handler::get_defaults(obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context, bool)
|
||||
{
|
||||
nvenc::get_defaults(settings, codec, context);
|
||||
|
||||
obs_data_set_default_int(settings, P_H264_PROFILE, static_cast<int64_t>(codecs::h264::profile::HIGH));
|
||||
obs_data_set_default_int(settings, P_H264_LEVEL, static_cast<int64_t>(codecs::h264::level::UNKNOWN));
|
||||
}
|
||||
|
||||
bool obsffmpeg::ui::nvenc_h264_handler::has_keyframe_support(obsffmpeg::encoder*)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_h264_handler::get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||
AVCodecContext* context, bool)
|
||||
{
|
||||
if (!context) {
|
||||
this->get_encoder_properties(props, codec);
|
||||
} else {
|
||||
this->get_runtime_properties(props, codec, context);
|
||||
}
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_h264_handler::update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context)
|
||||
{
|
||||
nvenc::update(settings, codec, context);
|
||||
|
||||
{
|
||||
auto found =
|
||||
profiles.find(static_cast<codecs::h264::profile>(obs_data_get_int(settings, P_H264_PROFILE)));
|
||||
if (found != profiles.end()) {
|
||||
av_opt_set(context->priv_data, "profile", found->second.c_str(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto found = levels.find(static_cast<codecs::h264::level>(obs_data_get_int(settings, P_H264_LEVEL)));
|
||||
if (found != levels.end()) {
|
||||
av_opt_set(context->priv_data, "level", found->second.c_str(), 0);
|
||||
} else {
|
||||
av_opt_set(context->priv_data, "level", "auto", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_h264_handler::override_update(obsffmpeg::encoder* instance, obs_data_t* settings)
|
||||
{
|
||||
nvenc::override_update(instance, settings);
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_h264_handler::log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context)
|
||||
{
|
||||
nvenc::log_options(settings, codec, context);
|
||||
|
||||
PLOG_INFO("[%s] H.265/HEVC:", codec->name);
|
||||
ffmpeg::tools::print_av_option_string(context, "profile", " Profile", [](int64_t v) {
|
||||
profile val = static_cast<profile>(v);
|
||||
auto index = profiles.find(val);
|
||||
if (index != profiles.end())
|
||||
return index->second;
|
||||
return std::string("<Unknown>");
|
||||
});
|
||||
ffmpeg::tools::print_av_option_string(context, "level", " Level", [](int64_t v) {
|
||||
level val = static_cast<level>(v);
|
||||
auto index = levels.find(val);
|
||||
if (index != levels.end())
|
||||
return index->second;
|
||||
return std::string("<Unknown>");
|
||||
});
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_h264_handler::get_encoder_properties(obs_properties_t* props, const AVCodec* codec)
|
||||
{
|
||||
nvenc::get_properties_pre(props, codec);
|
||||
|
||||
{
|
||||
obs_properties_t* grp = props;
|
||||
if (!obsffmpeg::are_property_groups_broken()) {
|
||||
grp = obs_properties_create();
|
||||
obs_properties_add_group(props, P_H264, TRANSLATE(P_H264), OBS_GROUP_NORMAL, grp);
|
||||
}
|
||||
|
||||
{
|
||||
auto p = obs_properties_add_list(grp, P_H264_PROFILE, TRANSLATE(P_H264_PROFILE),
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_H264_PROFILE)));
|
||||
obs_property_list_add_int(p, TRANSLATE(S_STATE_DEFAULT),
|
||||
static_cast<int64_t>(codecs::h264::profile::UNKNOWN));
|
||||
for (auto const kv : profiles) {
|
||||
std::string trans = std::string(P_H264_PROFILE) + "." + kv.second;
|
||||
obs_property_list_add_int(p, TRANSLATE(trans.c_str()), static_cast<int64_t>(kv.first));
|
||||
}
|
||||
}
|
||||
{
|
||||
auto p = obs_properties_add_list(grp, P_H264_LEVEL, TRANSLATE(P_H264_LEVEL),
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_H264_LEVEL)));
|
||||
obs_property_list_add_int(p, TRANSLATE(S_STATE_AUTOMATIC),
|
||||
static_cast<int64_t>(codecs::h264::level::UNKNOWN));
|
||||
for (auto const kv : levels) {
|
||||
obs_property_list_add_int(p, kv.second.c_str(), static_cast<int64_t>(kv.first));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nvenc::get_properties_post(props, codec);
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_h264_handler::get_runtime_properties(obs_properties_t* props, const AVCodec* codec,
|
||||
AVCodecContext* context)
|
||||
{
|
||||
nvenc::get_runtime_properties(props, codec, context);
|
||||
}
|
66
source/encoders/handlers/nvenc_h264_handler.hpp
Normal file
66
source/encoders/handlers/nvenc_h264_handler.hpp
Normal file
|
@ -0,0 +1,66 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
#include "handler.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <obs-properties.h>
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavcodec/avcodec.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
namespace obsffmpeg {
|
||||
namespace ui {
|
||||
class nvenc_h264_handler : public handler {
|
||||
public /*factory*/:
|
||||
virtual void adjust_encoder_info(obsffmpeg::encoder_factory* factory,
|
||||
obsffmpeg::encoder_info* main,
|
||||
obsffmpeg::encoder_info* fallback);
|
||||
|
||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context,
|
||||
bool hw_encode);
|
||||
|
||||
public /*settings*/:
|
||||
virtual bool has_keyframe_support(obsffmpeg::encoder* instance);
|
||||
|
||||
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||
AVCodecContext* context, bool hw_encode);
|
||||
|
||||
virtual void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||
|
||||
virtual void override_update(obsffmpeg::encoder* instance, obs_data_t* settings);
|
||||
|
||||
virtual void log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||
|
||||
public /*instance*/:
|
||||
//virtual void override_colorformat(AVPixelFormat& target_format, obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||
|
||||
private:
|
||||
void get_encoder_properties(obs_properties_t* props, const AVCodec* codec);
|
||||
|
||||
void get_runtime_properties(obs_properties_t* props, const AVCodec* codec,
|
||||
AVCodecContext* context);
|
||||
};
|
||||
} // namespace ui
|
||||
} // namespace obsffmpeg
|
208
source/encoders/handlers/nvenc_hevc_handler.cpp
Normal file
208
source/encoders/handlers/nvenc_hevc_handler.cpp
Normal file
|
@ -0,0 +1,208 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "nvenc_hevc_handler.hpp"
|
||||
#include "codecs/hevc.hpp"
|
||||
#include "encoder.hpp"
|
||||
#include "ffmpeg/tools.hpp"
|
||||
#include "nvenc_shared.hpp"
|
||||
#include "plugin.hpp"
|
||||
#include "strings.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <obs-module.h>
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavutil/opt.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
using namespace obsffmpeg::codecs::hevc;
|
||||
|
||||
std::map<profile, std::string> profiles{
|
||||
{profile::MAIN, "main"},
|
||||
{profile::MAIN10, "main10"},
|
||||
{profile::RANGE_EXTENDED, "rext"},
|
||||
};
|
||||
|
||||
std::map<tier, std::string> tiers{
|
||||
{tier::MAIN, "main"},
|
||||
{tier::HIGH, "high"},
|
||||
};
|
||||
|
||||
std::map<level, std::string> levels{
|
||||
{level::L1_0, "1.0"}, {level::L2_0, "2.0"}, {level::L2_1, "2.1"}, {level::L3_0, "3.0"}, {level::L3_1, "3.1"},
|
||||
{level::L4_0, "4.0"}, {level::L4_1, "4.1"}, {level::L5_0, "5.0"}, {level::L5_1, "5.1"}, {level::L5_2, "5.2"},
|
||||
{level::L6_0, "6.0"}, {level::L6_1, "6.1"}, {level::L6_2, "6.2"},
|
||||
};
|
||||
|
||||
INITIALIZER(nvenc_hevc_handler_init)
|
||||
{
|
||||
obsffmpeg::initializers.push_back([]() {
|
||||
obsffmpeg::register_codec_handler("hevc_nvenc", std::make_shared<obsffmpeg::ui::nvenc_hevc_handler>());
|
||||
});
|
||||
};
|
||||
|
||||
void obsffmpeg::ui::nvenc_hevc_handler::adjust_encoder_info(obsffmpeg::encoder_factory*, obsffmpeg::encoder_info* main,
|
||||
obsffmpeg::encoder_info* fallback)
|
||||
{
|
||||
main->readable_name = "H.265/HEVC Nvidia NVENC (Hardware)";
|
||||
fallback->readable_name = "H.265/HEVC Nvidia NVENC (Software)";
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_hevc_handler::get_defaults(obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context, bool)
|
||||
{
|
||||
nvenc::get_defaults(settings, codec, context);
|
||||
|
||||
obs_data_set_default_int(settings, P_HEVC_PROFILE, static_cast<int64_t>(codecs::hevc::profile::MAIN));
|
||||
obs_data_set_default_int(settings, P_HEVC_TIER, static_cast<int64_t>(codecs::hevc::profile::MAIN));
|
||||
obs_data_set_default_int(settings, P_HEVC_LEVEL, static_cast<int64_t>(codecs::hevc::level::UNKNOWN));
|
||||
}
|
||||
|
||||
bool obsffmpeg::ui::nvenc_hevc_handler::has_keyframe_support(obsffmpeg::encoder*)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_hevc_handler::get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||
AVCodecContext* context, bool)
|
||||
{
|
||||
if (!context) {
|
||||
this->get_encoder_properties(props, codec);
|
||||
} else {
|
||||
this->get_runtime_properties(props, codec, context);
|
||||
}
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_hevc_handler::update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context)
|
||||
{
|
||||
nvenc::update(settings, codec, context);
|
||||
|
||||
{ // HEVC Options
|
||||
auto found = profiles.find(static_cast<profile>(obs_data_get_int(settings, P_HEVC_PROFILE)));
|
||||
if (found != profiles.end()) {
|
||||
av_opt_set(context->priv_data, "profile", found->second.c_str(), 0);
|
||||
}
|
||||
}
|
||||
{
|
||||
auto found = tiers.find(static_cast<tier>(obs_data_get_int(settings, P_HEVC_TIER)));
|
||||
if (found != tiers.end()) {
|
||||
av_opt_set(context->priv_data, "tier", found->second.c_str(), 0);
|
||||
}
|
||||
}
|
||||
{
|
||||
auto found = levels.find(static_cast<level>(obs_data_get_int(settings, P_HEVC_LEVEL)));
|
||||
if (found != levels.end()) {
|
||||
av_opt_set(context->priv_data, "level", found->second.c_str(), 0);
|
||||
} else {
|
||||
av_opt_set(context->priv_data, "level", "auto", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_hevc_handler::override_update(obsffmpeg::encoder* instance, obs_data_t* settings)
|
||||
{
|
||||
nvenc::override_update(instance, settings);
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_hevc_handler::log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context)
|
||||
{
|
||||
nvenc::log_options(settings, codec, context);
|
||||
|
||||
PLOG_INFO("[%s] H.265/HEVC:", codec->name);
|
||||
ffmpeg::tools::print_av_option_string(context, "profile", " Profile", [](int64_t v) {
|
||||
profile val = static_cast<profile>(v);
|
||||
auto index = profiles.find(val);
|
||||
if (index != profiles.end())
|
||||
return index->second;
|
||||
return std::string("<Unknown>");
|
||||
});
|
||||
ffmpeg::tools::print_av_option_string(context, "level", " Level", [](int64_t v) {
|
||||
level val = static_cast<level>(v);
|
||||
auto index = levels.find(val);
|
||||
if (index != levels.end())
|
||||
return index->second;
|
||||
return std::string("<Unknown>");
|
||||
});
|
||||
ffmpeg::tools::print_av_option_string(context, "tier", " Tier", [](int64_t v) {
|
||||
tier val = static_cast<tier>(v);
|
||||
auto index = tiers.find(val);
|
||||
if (index != tiers.end())
|
||||
return index->second;
|
||||
return std::string("<Unknown>");
|
||||
});
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_hevc_handler::get_encoder_properties(obs_properties_t* props, const AVCodec* codec)
|
||||
{
|
||||
nvenc::get_properties_pre(props, codec);
|
||||
|
||||
{
|
||||
obs_properties_t* grp = props;
|
||||
if (!obsffmpeg::are_property_groups_broken()) {
|
||||
grp = obs_properties_create();
|
||||
obs_properties_add_group(props, P_HEVC, TRANSLATE(P_HEVC), OBS_GROUP_NORMAL, grp);
|
||||
}
|
||||
|
||||
{
|
||||
auto p = obs_properties_add_list(grp, P_HEVC_PROFILE, TRANSLATE(P_HEVC_PROFILE),
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_HEVC_PROFILE)));
|
||||
obs_property_list_add_int(p, TRANSLATE(S_STATE_DEFAULT),
|
||||
static_cast<int64_t>(codecs::hevc::profile::UNKNOWN));
|
||||
for (auto const kv : profiles) {
|
||||
std::string trans = std::string(P_HEVC_PROFILE) + "." + kv.second;
|
||||
obs_property_list_add_int(p, TRANSLATE(trans.c_str()), static_cast<int64_t>(kv.first));
|
||||
}
|
||||
}
|
||||
{
|
||||
auto p = obs_properties_add_list(grp, P_HEVC_TIER, TRANSLATE(P_HEVC_TIER), OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_INT);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_HEVC_TIER)));
|
||||
obs_property_list_add_int(p, TRANSLATE(S_STATE_DEFAULT),
|
||||
static_cast<int64_t>(codecs::hevc::tier::UNKNOWN));
|
||||
for (auto const kv : tiers) {
|
||||
std::string trans = std::string(P_HEVC_TIER) + "." + kv.second;
|
||||
obs_property_list_add_int(p, TRANSLATE(trans.c_str()), static_cast<int64_t>(kv.first));
|
||||
}
|
||||
}
|
||||
{
|
||||
auto p = obs_properties_add_list(grp, P_HEVC_LEVEL, TRANSLATE(P_HEVC_LEVEL),
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_HEVC_LEVEL)));
|
||||
obs_property_list_add_int(p, TRANSLATE(S_STATE_AUTOMATIC),
|
||||
static_cast<int64_t>(codecs::hevc::level::UNKNOWN));
|
||||
for (auto const kv : levels) {
|
||||
obs_property_list_add_int(p, kv.second.c_str(), static_cast<int64_t>(kv.first));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nvenc::get_properties_post(props, codec);
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_hevc_handler::get_runtime_properties(obs_properties_t* props, const AVCodec* codec,
|
||||
AVCodecContext* context)
|
||||
{
|
||||
nvenc::get_runtime_properties(props, codec, context);
|
||||
}
|
66
source/encoders/handlers/nvenc_hevc_handler.hpp
Normal file
66
source/encoders/handlers/nvenc_hevc_handler.hpp
Normal file
|
@ -0,0 +1,66 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
#include "handler.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <obs-properties.h>
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavcodec/avcodec.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
namespace obsffmpeg {
|
||||
namespace ui {
|
||||
class nvenc_hevc_handler : public handler {
|
||||
public /*factory*/:
|
||||
virtual void adjust_encoder_info(obsffmpeg::encoder_factory* factory,
|
||||
obsffmpeg::encoder_info* main,
|
||||
obsffmpeg::encoder_info* fallback);
|
||||
|
||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context,
|
||||
bool hw_encode);
|
||||
|
||||
public /*settings*/:
|
||||
virtual bool has_keyframe_support(obsffmpeg::encoder* instance);
|
||||
|
||||
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||
AVCodecContext* context, bool hw_encode);
|
||||
|
||||
virtual void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||
|
||||
virtual void override_update(obsffmpeg::encoder* instance, obs_data_t* settings);
|
||||
|
||||
virtual void log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||
|
||||
public /*instance*/:
|
||||
//virtual void override_colorformat(AVPixelFormat& target_format, obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||
|
||||
private:
|
||||
void get_encoder_properties(obs_properties_t* props, const AVCodec* codec);
|
||||
|
||||
void get_runtime_properties(obs_properties_t* props, const AVCodec* codec,
|
||||
AVCodecContext* context);
|
||||
};
|
||||
} // namespace ui
|
||||
} // namespace obsffmpeg
|
767
source/encoders/handlers/nvenc_shared.cpp
Normal file
767
source/encoders/handlers/nvenc_shared.cpp
Normal file
|
@ -0,0 +1,767 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "nvenc_shared.hpp"
|
||||
#include <algorithm>
|
||||
#include "codecs/hevc.hpp"
|
||||
#include "encoder.hpp"
|
||||
#include "ffmpeg/tools.hpp"
|
||||
#include "plugin.hpp"
|
||||
#include "strings.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <obs-module.h>
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavutil/opt.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
#define ST_PRESET "NVENC.Preset"
|
||||
#define ST_PRESET_(x) ST_PRESET "." D_VSTR(x)
|
||||
|
||||
#define ST_RATECONTROL "NVENC.RateControl"
|
||||
#define ST_RATECONTROL_MODE ST_RATECONTROL ".Mode"
|
||||
#define ST_RATECONTROL_MODE_(x) ST_RATECONTROL_MODE "." D_VSTR(x)
|
||||
#define ST_RATECONTROL_TWOPASS ST_RATECONTROL ".TwoPass"
|
||||
#define ST_RATECONTROL_LOOKAHEAD ST_RATECONTROL ".LookAhead"
|
||||
#define ST_RATECONTROL_ADAPTIVEI ST_RATECONTROL ".AdaptiveI"
|
||||
#define ST_RATECONTROL_ADAPTIVEB ST_RATECONTROL ".AdaptiveB"
|
||||
|
||||
#define ST_RATECONTROL_BITRATE ST_RATECONTROL ".Bitrate"
|
||||
#define ST_RATECONTROL_BITRATE_TARGET ST_RATECONTROL_BITRATE ".Target"
|
||||
#define ST_RATECONTROL_BITRATE_MAXIMUM ST_RATECONTROL_BITRATE ".Maximum"
|
||||
|
||||
#define ST_RATECONTROL_QUALITY ST_RATECONTROL ".Quality"
|
||||
#define ST_RATECONTROL_QUALITY_MINIMUM ST_RATECONTROL_QUALITY ".Minimum"
|
||||
#define ST_RATECONTROL_QUALITY_MAXIMUM ST_RATECONTROL_QUALITY ".Maximum"
|
||||
#define ST_RATECONTROL_QUALITY_TARGET ST_RATECONTROL_QUALITY ".Target"
|
||||
|
||||
#define ST_RATECONTROL_QP ST_RATECONTROL ".QP"
|
||||
#define ST_RATECONTROL_QP_I ST_RATECONTROL_QP ".I"
|
||||
#define ST_RATECONTROL_QP_I_INITIAL ST_RATECONTROL_QP_I ".Initial"
|
||||
#define ST_RATECONTROL_QP_P ST_RATECONTROL_QP ".P"
|
||||
#define ST_RATECONTROL_QP_P_INITIAL ST_RATECONTROL_QP_P ".Initial"
|
||||
#define ST_RATECONTROL_QP_B ST_RATECONTROL_QP ".B"
|
||||
#define ST_RATECONTROL_QP_B_INITIAL ST_RATECONTROL_QP_B ".Initial"
|
||||
|
||||
#define ST_AQ "NVENC.AQ"
|
||||
#define ST_AQ_SPATIAL ST_AQ ".Spatial"
|
||||
#define ST_AQ_TEMPORAL ST_AQ ".Temporal"
|
||||
#define ST_AQ_STRENGTH ST_AQ ".Strength"
|
||||
|
||||
#define ST_OTHER "NVENC.Other"
|
||||
#define ST_OTHER_BFRAMES ST_OTHER ".BFrames"
|
||||
#define ST_OTHER_BFRAME_REFERENCEMODE ST_OTHER ".BFrameReferenceMode"
|
||||
#define ST_OTHER_ZEROLATENCY ST_OTHER ".ZeroLatency"
|
||||
#define ST_OTHER_WEIGHTED_PREDICTION ST_OTHER ".WeightedPrediction"
|
||||
#define ST_OTHER_NONREFERENCE_PFRAMES ST_OTHER ".NonReferencePFrames"
|
||||
|
||||
using namespace obsffmpeg::nvenc;
|
||||
|
||||
std::map<preset, std::string> obsffmpeg::nvenc::presets{
|
||||
{preset::DEFAULT, ST_PRESET_(Default)},
|
||||
{preset::SLOW, ST_PRESET_(Slow)},
|
||||
{preset::MEDIUM, ST_PRESET_(Medium)},
|
||||
{preset::FAST, ST_PRESET_(Fast)},
|
||||
{preset::HIGH_PERFORMANCE, ST_PRESET_(HighPerformance)},
|
||||
{preset::HIGH_QUALITY, ST_PRESET_(HighQuality)},
|
||||
{preset::BLURAYDISC, ST_PRESET_(BluRayDisc)},
|
||||
{preset::LOW_LATENCY, ST_PRESET_(LowLatency)},
|
||||
{preset::LOW_LATENCY_HIGH_PERFORMANCE, ST_PRESET_(LowLatencyHighPerformance)},
|
||||
{preset::LOW_LATENCY_HIGH_QUALITY, ST_PRESET_(LowLatencyHighQuality)},
|
||||
{preset::LOSSLESS, ST_PRESET_(Lossless)},
|
||||
{preset::LOSSLESS_HIGH_PERFORMANCE, ST_PRESET_(LosslessHighPerformance)},
|
||||
};
|
||||
|
||||
std::map<preset, std::string> obsffmpeg::nvenc::preset_to_opt{
|
||||
{preset::DEFAULT, "default"},
|
||||
{preset::SLOW, "slow"},
|
||||
{preset::MEDIUM, "medium"},
|
||||
{preset::FAST, "fast"},
|
||||
{preset::HIGH_PERFORMANCE, "hp"},
|
||||
{preset::HIGH_QUALITY, "hq"},
|
||||
{preset::BLURAYDISC, "bd"},
|
||||
{preset::LOW_LATENCY, "ll"},
|
||||
{preset::LOW_LATENCY_HIGH_PERFORMANCE, "llhp"},
|
||||
{preset::LOW_LATENCY_HIGH_QUALITY, "llhq"},
|
||||
{preset::LOSSLESS, "lossless"},
|
||||
{preset::LOSSLESS_HIGH_PERFORMANCE, "losslesshp"},
|
||||
};
|
||||
|
||||
std::map<ratecontrolmode, std::string> obsffmpeg::nvenc::ratecontrolmodes{
|
||||
{ratecontrolmode::CQP, ST_RATECONTROL_MODE_(CQP)},
|
||||
{ratecontrolmode::VBR, ST_RATECONTROL_MODE_(VBR)},
|
||||
{ratecontrolmode::VBR_HQ, ST_RATECONTROL_MODE_(VBR_HQ)},
|
||||
{ratecontrolmode::CBR, ST_RATECONTROL_MODE_(CBR)},
|
||||
{ratecontrolmode::CBR_HQ, ST_RATECONTROL_MODE_(CBR_HQ)},
|
||||
{ratecontrolmode::CBR_LD_HQ, ST_RATECONTROL_MODE_(CBR_LD_HQ)},
|
||||
};
|
||||
|
||||
std::map<ratecontrolmode, std::string> obsffmpeg::nvenc::ratecontrolmode_to_opt{
|
||||
{ratecontrolmode::CQP, "constqp"}, {ratecontrolmode::VBR, "vbr"}, {ratecontrolmode::VBR_HQ, "vbr_hq"},
|
||||
{ratecontrolmode::CBR, "cbr"}, {ratecontrolmode::CBR_HQ, "cbr_hq"}, {ratecontrolmode::CBR_LD_HQ, "cbr_ld_hq"},
|
||||
};
|
||||
|
||||
std::map<b_ref_mode, std::string> obsffmpeg::nvenc::b_ref_modes{
|
||||
{b_ref_mode::DISABLED, S_STATE_DISABLED},
|
||||
{b_ref_mode::EACH, ST_OTHER_BFRAME_REFERENCEMODE ".Each"},
|
||||
{b_ref_mode::MIDDLE, ST_OTHER_BFRAME_REFERENCEMODE ".Middle"},
|
||||
};
|
||||
|
||||
std::map<b_ref_mode, std::string> obsffmpeg::nvenc::b_ref_mode_to_opt{
|
||||
{b_ref_mode::DISABLED, "disabled"},
|
||||
{b_ref_mode::EACH, "each"},
|
||||
{b_ref_mode::MIDDLE, "middle"},
|
||||
};
|
||||
|
||||
void obsffmpeg::nvenc::override_update(obsffmpeg::encoder* instance, obs_data_t*)
|
||||
{
|
||||
AVCodecContext* context = const_cast<AVCodecContext*>(instance->get_avcodeccontext());
|
||||
|
||||
int64_t rclookahead = 0;
|
||||
int64_t surfaces = 0;
|
||||
int64_t async_depth = 0;
|
||||
|
||||
av_opt_get_int(context, "rc-lookahead", AV_OPT_SEARCH_CHILDREN, &rclookahead);
|
||||
av_opt_get_int(context, "surfaces", AV_OPT_SEARCH_CHILDREN, &surfaces);
|
||||
av_opt_get_int(context, "async_depth", AV_OPT_SEARCH_CHILDREN, &async_depth);
|
||||
|
||||
// Calculate and set the number of surfaces to allocate (if not user overridden).
|
||||
if (surfaces == 0) {
|
||||
surfaces = std::max(4ll, (context->max_b_frames + 1ll) * 4ll);
|
||||
if (rclookahead > 0) {
|
||||
surfaces = std::max(1ll, std::max(surfaces, rclookahead + (context->max_b_frames + 5ll)));
|
||||
} else if (context->max_b_frames > 0) {
|
||||
surfaces = std::max(4ll, (context->max_b_frames + 1ll) * 4ll);
|
||||
} else {
|
||||
surfaces = 4;
|
||||
}
|
||||
|
||||
av_opt_set_int(context, "surfaces", surfaces, AV_OPT_SEARCH_CHILDREN);
|
||||
}
|
||||
|
||||
// Set delay
|
||||
context->delay = static_cast<int>(std::min(std::max(async_depth, 3ll), surfaces - 1));
|
||||
}
|
||||
|
||||
void obsffmpeg::nvenc::get_defaults(obs_data_t* settings, const AVCodec*, AVCodecContext*)
|
||||
{
|
||||
obs_data_set_default_int(settings, ST_PRESET, static_cast<int64_t>(preset::DEFAULT));
|
||||
|
||||
obs_data_set_default_int(settings, ST_RATECONTROL_MODE, static_cast<int64_t>(ratecontrolmode::CBR_HQ));
|
||||
obs_data_set_default_int(settings, ST_RATECONTROL_TWOPASS, -1);
|
||||
obs_data_set_default_int(settings, ST_RATECONTROL_LOOKAHEAD, 0);
|
||||
obs_data_set_default_int(settings, ST_RATECONTROL_ADAPTIVEI, -1);
|
||||
obs_data_set_default_int(settings, ST_RATECONTROL_ADAPTIVEB, -1);
|
||||
|
||||
obs_data_set_default_int(settings, ST_RATECONTROL_BITRATE_TARGET, 6000);
|
||||
obs_data_set_default_int(settings, ST_RATECONTROL_BITRATE_MAXIMUM, 6000);
|
||||
obs_data_set_default_int(settings, S_RATECONTROL_BUFFERSIZE, 12000);
|
||||
|
||||
obs_data_set_default_int(settings, ST_RATECONTROL_QUALITY_MINIMUM, 51);
|
||||
obs_data_set_default_int(settings, ST_RATECONTROL_QUALITY_MAXIMUM, -1);
|
||||
obs_data_set_default_int(settings, ST_RATECONTROL_QUALITY_TARGET, 0);
|
||||
|
||||
obs_data_set_default_int(settings, ST_RATECONTROL_QP_I, 21);
|
||||
obs_data_set_default_int(settings, ST_RATECONTROL_QP_I_INITIAL, -1);
|
||||
obs_data_set_default_int(settings, ST_RATECONTROL_QP_P, 21);
|
||||
obs_data_set_default_int(settings, ST_RATECONTROL_QP_P_INITIAL, -1);
|
||||
obs_data_set_default_int(settings, ST_RATECONTROL_QP_B, 21);
|
||||
obs_data_set_default_int(settings, ST_RATECONTROL_QP_B_INITIAL, -1);
|
||||
|
||||
obs_data_set_default_int(settings, ST_AQ_SPATIAL, -1);
|
||||
obs_data_set_default_int(settings, ST_AQ_STRENGTH, 8);
|
||||
obs_data_set_default_int(settings, ST_AQ_TEMPORAL, -1);
|
||||
|
||||
obs_data_set_default_int(settings, ST_OTHER_BFRAMES, 2);
|
||||
obs_data_set_default_int(settings, ST_OTHER_BFRAME_REFERENCEMODE, static_cast<int64_t>(b_ref_mode::DISABLED));
|
||||
obs_data_set_default_int(settings, ST_OTHER_ZEROLATENCY, -1);
|
||||
obs_data_set_default_int(settings, ST_OTHER_WEIGHTED_PREDICTION, -1);
|
||||
obs_data_set_default_int(settings, ST_OTHER_NONREFERENCE_PFRAMES, -1);
|
||||
|
||||
// Replay Buffer
|
||||
obs_data_set_default_int(settings, "bitrate", 0);
|
||||
}
|
||||
|
||||
static bool modified_ratecontrol(obs_properties_t* props, obs_property_t*, obs_data_t* settings)
|
||||
{
|
||||
using namespace obsffmpeg::nvenc;
|
||||
|
||||
bool have_bitrate = false;
|
||||
bool have_bitrate_max = false;
|
||||
bool have_quality = false;
|
||||
bool have_qp = false;
|
||||
bool have_qp_init = false;
|
||||
|
||||
ratecontrolmode rc = static_cast<ratecontrolmode>(obs_data_get_int(settings, ST_RATECONTROL_MODE));
|
||||
switch (rc) {
|
||||
case ratecontrolmode::CQP:
|
||||
have_qp = true;
|
||||
break;
|
||||
case ratecontrolmode::CBR:
|
||||
case ratecontrolmode::CBR_HQ:
|
||||
case ratecontrolmode::CBR_LD_HQ:
|
||||
have_bitrate = true;
|
||||
break;
|
||||
case ratecontrolmode::VBR:
|
||||
case ratecontrolmode::VBR_HQ:
|
||||
have_bitrate = true;
|
||||
have_bitrate_max = true;
|
||||
have_quality = true;
|
||||
have_qp_init = true;
|
||||
break;
|
||||
}
|
||||
|
||||
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_BITRATE), have_bitrate || have_bitrate_max);
|
||||
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_BITRATE_TARGET), have_bitrate);
|
||||
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_BITRATE_MAXIMUM), have_bitrate_max);
|
||||
obs_property_set_visible(obs_properties_get(props, S_RATECONTROL_BUFFERSIZE), have_bitrate || have_bitrate_max);
|
||||
|
||||
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QUALITY), have_quality);
|
||||
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QUALITY_MINIMUM), have_quality);
|
||||
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QUALITY_MAXIMUM), have_quality);
|
||||
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QUALITY_TARGET), have_quality);
|
||||
|
||||
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP), have_qp || have_qp_init);
|
||||
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_I), have_qp);
|
||||
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_P), have_qp);
|
||||
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_B), have_qp);
|
||||
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_I_INITIAL), have_qp_init);
|
||||
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_P_INITIAL), have_qp_init);
|
||||
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_B_INITIAL), have_qp_init);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool modified_quality(obs_properties_t* props, obs_property_t*, obs_data_t* settings)
|
||||
{
|
||||
bool enabled = obs_data_get_bool(settings, ST_RATECONTROL_QUALITY);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QUALITY_MINIMUM), enabled);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QUALITY_MAXIMUM), enabled);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool modified_aq(obs_properties_t* props, obs_property_t*, obs_data_t* settings)
|
||||
{
|
||||
bool spatial_aq = obs_data_get_int(settings, ST_AQ_SPATIAL) == 1;
|
||||
obs_property_set_visible(obs_properties_get(props, ST_AQ_STRENGTH), spatial_aq);
|
||||
return true;
|
||||
}
|
||||
|
||||
void obsffmpeg::nvenc::get_properties_pre(obs_properties_t* props, const AVCodec*)
|
||||
{
|
||||
{
|
||||
auto p = obs_properties_add_list(props, ST_PRESET, TRANSLATE(ST_PRESET), OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_INT);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_PRESET)));
|
||||
for (auto kv : presets) {
|
||||
obs_property_list_add_int(p, TRANSLATE(kv.second.c_str()), static_cast<int64_t>(kv.first));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void obsffmpeg::nvenc::get_properties_post(obs_properties_t* props, const AVCodec* codec)
|
||||
{
|
||||
{ // Rate Control
|
||||
obs_properties_t* grp = props;
|
||||
if (!obsffmpeg::are_property_groups_broken()) {
|
||||
grp = obs_properties_create();
|
||||
obs_properties_add_group(props, ST_RATECONTROL, TRANSLATE(ST_RATECONTROL), OBS_GROUP_NORMAL,
|
||||
grp);
|
||||
}
|
||||
|
||||
{
|
||||
auto p = obs_properties_add_list(grp, ST_RATECONTROL_MODE, TRANSLATE(ST_RATECONTROL_MODE),
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_MODE)));
|
||||
obs_property_set_modified_callback(p, modified_ratecontrol);
|
||||
for (auto kv : ratecontrolmodes) {
|
||||
obs_property_list_add_int(p, TRANSLATE(kv.second.c_str()),
|
||||
static_cast<int64_t>(kv.first));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_RATECONTROL_TWOPASS,
|
||||
TRANSLATE(ST_RATECONTROL_TWOPASS));
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_TWOPASS)));
|
||||
}
|
||||
|
||||
{
|
||||
auto p = obs_properties_add_int_slider(grp, ST_RATECONTROL_LOOKAHEAD,
|
||||
TRANSLATE(ST_RATECONTROL_LOOKAHEAD), 0, 32, 1);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_LOOKAHEAD)));
|
||||
obs_property_int_set_suffix(p, " frames");
|
||||
}
|
||||
{
|
||||
auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_RATECONTROL_ADAPTIVEI,
|
||||
TRANSLATE(ST_RATECONTROL_ADAPTIVEI));
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_ADAPTIVEI)));
|
||||
}
|
||||
if (strcmp(codec->name, "h264_nvenc") == 0) {
|
||||
auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_RATECONTROL_ADAPTIVEB,
|
||||
TRANSLATE(ST_RATECONTROL_ADAPTIVEB));
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_ADAPTIVEB)));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
obs_properties_t* grp = props;
|
||||
if (!obsffmpeg::are_property_groups_broken()) {
|
||||
grp = obs_properties_create();
|
||||
obs_properties_add_group(props, ST_RATECONTROL_BITRATE, TRANSLATE(ST_RATECONTROL_BITRATE),
|
||||
OBS_GROUP_NORMAL, grp);
|
||||
}
|
||||
|
||||
{
|
||||
auto p = obs_properties_add_int(grp, ST_RATECONTROL_BITRATE_TARGET,
|
||||
TRANSLATE(ST_RATECONTROL_BITRATE_TARGET), 1,
|
||||
std::numeric_limits<int32_t>::max(), 1);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_BITRATE_TARGET)));
|
||||
obs_property_int_set_suffix(p, " kbit/s");
|
||||
}
|
||||
{
|
||||
auto p = obs_properties_add_int(grp, ST_RATECONTROL_BITRATE_MAXIMUM,
|
||||
TRANSLATE(ST_RATECONTROL_BITRATE_MAXIMUM), 0,
|
||||
std::numeric_limits<int32_t>::max(), 1);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_BITRATE_MAXIMUM)));
|
||||
obs_property_int_set_suffix(p, " kbit/s");
|
||||
}
|
||||
{
|
||||
auto p =
|
||||
obs_properties_add_int(grp, S_RATECONTROL_BUFFERSIZE, TRANSLATE(S_RATECONTROL_BUFFERSIZE),
|
||||
0, std::numeric_limits<int32_t>::max(), 1);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(S_RATECONTROL_BUFFERSIZE)));
|
||||
obs_property_int_set_suffix(p, " kbit");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
obs_properties_t* grp = props;
|
||||
if (!obsffmpeg::are_property_groups_broken()) {
|
||||
grp = obs_properties_create();
|
||||
auto p = obs_properties_add_group(props, ST_RATECONTROL_QUALITY,
|
||||
TRANSLATE(ST_RATECONTROL_QUALITY), OBS_GROUP_CHECKABLE, grp);
|
||||
obs_property_set_modified_callback(p, modified_quality);
|
||||
} else {
|
||||
auto p =
|
||||
obs_properties_add_bool(props, ST_RATECONTROL_QUALITY, TRANSLATE(ST_RATECONTROL_QUALITY));
|
||||
obs_property_set_modified_callback(p, modified_quality);
|
||||
}
|
||||
|
||||
{
|
||||
auto p = obs_properties_add_int_slider(grp, ST_RATECONTROL_QUALITY_MINIMUM,
|
||||
TRANSLATE(ST_RATECONTROL_QUALITY_MINIMUM), 0, 51, 1);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_QUALITY_MINIMUM)));
|
||||
}
|
||||
{
|
||||
auto p = obs_properties_add_int_slider(grp, ST_RATECONTROL_QUALITY_MAXIMUM,
|
||||
TRANSLATE(ST_RATECONTROL_QUALITY_MAXIMUM), -1, 51, 1);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_QUALITY_MAXIMUM)));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto p = obs_properties_add_float_slider(props, ST_RATECONTROL_QUALITY_TARGET,
|
||||
TRANSLATE(ST_RATECONTROL_QUALITY_TARGET), 0, 100, 0.01);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_QUALITY_TARGET)));
|
||||
}
|
||||
|
||||
{
|
||||
obs_properties_t* grp = props;
|
||||
if (!obsffmpeg::are_property_groups_broken()) {
|
||||
grp = obs_properties_create();
|
||||
auto p = obs_properties_add_group(props, ST_RATECONTROL_QP, TRANSLATE(ST_RATECONTROL_QP),
|
||||
OBS_GROUP_CHECKABLE, grp);
|
||||
obs_property_set_modified_callback(p, modified_quality);
|
||||
}
|
||||
|
||||
{
|
||||
auto p = obs_properties_add_int_slider(grp, ST_RATECONTROL_QP_I, TRANSLATE(ST_RATECONTROL_QP_I),
|
||||
0, 51, 1);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_QP_I)));
|
||||
}
|
||||
{
|
||||
auto p = obs_properties_add_int_slider(grp, ST_RATECONTROL_QP_I_INITIAL,
|
||||
TRANSLATE(ST_RATECONTROL_QP_I_INITIAL), -1, 51, 1);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_QP_I_INITIAL)));
|
||||
}
|
||||
{
|
||||
auto p = obs_properties_add_int_slider(grp, ST_RATECONTROL_QP_P, TRANSLATE(ST_RATECONTROL_QP_P),
|
||||
0, 51, 1);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_QP_P)));
|
||||
}
|
||||
{
|
||||
auto p = obs_properties_add_int_slider(grp, ST_RATECONTROL_QP_P_INITIAL,
|
||||
TRANSLATE(ST_RATECONTROL_QP_P_INITIAL), -1, 51, 1);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_QP_P_INITIAL)));
|
||||
}
|
||||
{
|
||||
auto p = obs_properties_add_int_slider(grp, ST_RATECONTROL_QP_B, TRANSLATE(ST_RATECONTROL_QP_B),
|
||||
0, 51, 1);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_QP_B)));
|
||||
}
|
||||
{
|
||||
auto p = obs_properties_add_int_slider(grp, ST_RATECONTROL_QP_B_INITIAL,
|
||||
TRANSLATE(ST_RATECONTROL_QP_B_INITIAL), -1, 51, 1);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_QP_B_INITIAL)));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
obs_properties_t* grp = props;
|
||||
if (!obsffmpeg::are_property_groups_broken()) {
|
||||
grp = obs_properties_create();
|
||||
obs_properties_add_group(props, ST_AQ, TRANSLATE(ST_AQ), OBS_GROUP_NORMAL, grp);
|
||||
}
|
||||
|
||||
{
|
||||
auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_AQ_SPATIAL, TRANSLATE(ST_AQ_SPATIAL));
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_AQ_SPATIAL)));
|
||||
obs_property_set_modified_callback(p, modified_aq);
|
||||
}
|
||||
{
|
||||
auto p =
|
||||
obs_properties_add_int_slider(grp, ST_AQ_STRENGTH, TRANSLATE(ST_AQ_STRENGTH), 1, 15, 1);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_AQ_STRENGTH)));
|
||||
}
|
||||
{
|
||||
auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_AQ_TEMPORAL, TRANSLATE(ST_AQ_TEMPORAL));
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_AQ_TEMPORAL)));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
obs_properties_t* grp = props;
|
||||
if (!obsffmpeg::are_property_groups_broken()) {
|
||||
grp = obs_properties_create();
|
||||
obs_properties_add_group(props, ST_OTHER, TRANSLATE(ST_OTHER), OBS_GROUP_NORMAL, grp);
|
||||
}
|
||||
|
||||
{
|
||||
auto p =
|
||||
obs_properties_add_int_slider(grp, ST_OTHER_BFRAMES, TRANSLATE(ST_OTHER_BFRAMES), 0, 4, 1);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_OTHER_BFRAMES)));
|
||||
obs_property_int_set_suffix(p, " frames");
|
||||
}
|
||||
|
||||
{
|
||||
auto p = obs_properties_add_list(grp, ST_OTHER_BFRAME_REFERENCEMODE,
|
||||
TRANSLATE(ST_OTHER_BFRAME_REFERENCEMODE), OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_INT);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_OTHER_BFRAME_REFERENCEMODE)));
|
||||
for (auto kv : b_ref_modes) {
|
||||
obs_property_list_add_int(p, TRANSLATE(kv.second.c_str()),
|
||||
static_cast<int64_t>(kv.first));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_OTHER_ZEROLATENCY,
|
||||
TRANSLATE(ST_OTHER_ZEROLATENCY));
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_OTHER_ZEROLATENCY)));
|
||||
}
|
||||
|
||||
{
|
||||
auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_OTHER_WEIGHTED_PREDICTION,
|
||||
TRANSLATE(ST_OTHER_WEIGHTED_PREDICTION));
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_OTHER_WEIGHTED_PREDICTION)));
|
||||
}
|
||||
|
||||
{
|
||||
auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_OTHER_NONREFERENCE_PFRAMES,
|
||||
TRANSLATE(ST_OTHER_NONREFERENCE_PFRAMES));
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_OTHER_NONREFERENCE_PFRAMES)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void obsffmpeg::nvenc::get_runtime_properties(obs_properties_t* props, const AVCodec*, AVCodecContext*)
|
||||
{
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_PRESET), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_MODE), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_TWOPASS), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_LOOKAHEAD), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_ADAPTIVEI), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_ADAPTIVEB), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_BITRATE), true);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_BITRATE_TARGET), true);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_BITRATE_MAXIMUM), true);
|
||||
obs_property_set_enabled(obs_properties_get(props, S_RATECONTROL_BUFFERSIZE), true);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QUALITY), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QUALITY_MINIMUM), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QUALITY_MAXIMUM), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QUALITY_TARGET), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_I), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_I_INITIAL), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_P), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_P_INITIAL), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_B), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_B_INITIAL), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_AQ), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_AQ_SPATIAL), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_AQ_STRENGTH), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_AQ_TEMPORAL), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_OTHER), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_OTHER_BFRAMES), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_OTHER_BFRAME_REFERENCEMODE), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_OTHER_ZEROLATENCY), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_OTHER_WEIGHTED_PREDICTION), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_OTHER_NONREFERENCE_PFRAMES), false);
|
||||
}
|
||||
|
||||
void obsffmpeg::nvenc::update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context)
|
||||
{
|
||||
{
|
||||
preset c_preset = static_cast<preset>(obs_data_get_int(settings, ST_PRESET));
|
||||
auto found = preset_to_opt.find(c_preset);
|
||||
if (found != preset_to_opt.end()) {
|
||||
av_opt_set(context->priv_data, "preset", found->second.c_str(), 0);
|
||||
} else {
|
||||
av_opt_set(context->priv_data, "preset", nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
{ // Rate Control
|
||||
bool have_bitrate = false;
|
||||
bool have_bitrate_max = false;
|
||||
bool have_quality = false;
|
||||
bool have_qp = false;
|
||||
bool have_qp_init = false;
|
||||
|
||||
ratecontrolmode rc = static_cast<ratecontrolmode>(obs_data_get_int(settings, ST_RATECONTROL_MODE));
|
||||
auto rcopt = nvenc::ratecontrolmode_to_opt.find(rc);
|
||||
if (rcopt != nvenc::ratecontrolmode_to_opt.end()) {
|
||||
av_opt_set(context->priv_data, "rc", rcopt->second.c_str(), 0);
|
||||
}
|
||||
|
||||
av_opt_set_int(context->priv_data, "cbr", 0, 0);
|
||||
switch (rc) {
|
||||
case ratecontrolmode::CQP:
|
||||
have_qp = true;
|
||||
break;
|
||||
case ratecontrolmode::CBR:
|
||||
case ratecontrolmode::CBR_HQ:
|
||||
case ratecontrolmode::CBR_LD_HQ:
|
||||
have_bitrate = true;
|
||||
av_opt_set_int(context->priv_data, "cbr", 1, 0);
|
||||
break;
|
||||
case ratecontrolmode::VBR:
|
||||
case ratecontrolmode::VBR_HQ:
|
||||
have_bitrate_max = true;
|
||||
have_bitrate = true;
|
||||
have_quality = true;
|
||||
have_qp_init = true;
|
||||
break;
|
||||
}
|
||||
|
||||
int tp = static_cast<int>(obs_data_get_int(settings, ST_RATECONTROL_TWOPASS));
|
||||
if (tp >= 0) {
|
||||
av_opt_set_int(context->priv_data, "2pass", tp ? 1 : 0, 0);
|
||||
}
|
||||
|
||||
int la = static_cast<int>(obs_data_get_int(settings, ST_RATECONTROL_LOOKAHEAD));
|
||||
av_opt_set_int(context->priv_data, "rc-lookahead", la, 0);
|
||||
if (la > 0) {
|
||||
int64_t adapt_i = obs_data_get_int(settings, ST_RATECONTROL_ADAPTIVEI);
|
||||
if (!is_tristate_default(adapt_i)) {
|
||||
av_opt_set_int(context->priv_data, "no-scenecut", adapt_i, AV_OPT_SEARCH_CHILDREN);
|
||||
}
|
||||
|
||||
if (strcmp(codec->name, "h264_nvenc")) {
|
||||
int64_t adapt_b = obs_data_get_int(settings, ST_RATECONTROL_ADAPTIVEB);
|
||||
if (!is_tristate_default(adapt_b)) {
|
||||
av_opt_set_int(context->priv_data, "b_adapt", adapt_b, AV_OPT_SEARCH_CHILDREN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (have_bitrate) {
|
||||
context->bit_rate =
|
||||
static_cast<int>(obs_data_get_int(settings, ST_RATECONTROL_BITRATE_TARGET) * 1000);
|
||||
// Support for Replay Buffer
|
||||
obs_data_set_int(settings, "bitrate",
|
||||
obs_data_get_int(settings, ST_RATECONTROL_BITRATE_TARGET));
|
||||
}
|
||||
if (have_bitrate_max)
|
||||
context->rc_max_rate =
|
||||
static_cast<int>(obs_data_get_int(settings, ST_RATECONTROL_BITRATE_MAXIMUM) * 1000);
|
||||
if (have_bitrate || have_bitrate_max)
|
||||
context->rc_buffer_size =
|
||||
static_cast<int>(obs_data_get_int(settings, S_RATECONTROL_BUFFERSIZE) * 1000);
|
||||
|
||||
if (have_quality && obs_data_get_bool(settings, ST_RATECONTROL_QUALITY)) {
|
||||
int qmin = static_cast<int>(obs_data_get_int(settings, ST_RATECONTROL_QUALITY_MINIMUM));
|
||||
context->qmin = qmin;
|
||||
if (qmin >= 0) {
|
||||
context->qmax =
|
||||
static_cast<int>(obs_data_get_int(settings, ST_RATECONTROL_QUALITY_MAXIMUM));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
double_t v = obs_data_get_double(settings, ST_RATECONTROL_QUALITY_TARGET) / 100.0 * 51.0;
|
||||
if (v > 0) {
|
||||
av_opt_set_double(context->priv_data, "cq", v, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (have_qp) {
|
||||
av_opt_set_int(context->priv_data, "init_qpI",
|
||||
static_cast<int>(obs_data_get_int(settings, ST_RATECONTROL_QP_I)), 0);
|
||||
av_opt_set_int(context->priv_data, "init_qpP",
|
||||
static_cast<int>(obs_data_get_int(settings, ST_RATECONTROL_QP_P)), 0);
|
||||
av_opt_set_int(context->priv_data, "init_qpB",
|
||||
static_cast<int>(obs_data_get_int(settings, ST_RATECONTROL_QP_B)), 0);
|
||||
}
|
||||
if (have_qp_init) {
|
||||
av_opt_set_int(context->priv_data, "init_qpI",
|
||||
obs_data_get_int(settings, ST_RATECONTROL_QP_I_INITIAL), 0);
|
||||
av_opt_set_int(context->priv_data, "init_qpP",
|
||||
obs_data_get_int(settings, ST_RATECONTROL_QP_P_INITIAL), 0);
|
||||
av_opt_set_int(context->priv_data, "init_qpB",
|
||||
obs_data_get_int(settings, ST_RATECONTROL_QP_B_INITIAL), 0);
|
||||
}
|
||||
}
|
||||
|
||||
{ // AQ
|
||||
int64_t saq = obs_data_get_int(settings, ST_AQ_SPATIAL);
|
||||
int64_t taq = obs_data_get_int(settings, ST_AQ_TEMPORAL);
|
||||
|
||||
if (strcmp(codec->name, "h264_nvenc") == 0) {
|
||||
if (!is_tristate_default(saq))
|
||||
av_opt_set_int(context->priv_data, "spatial-aq", saq, 0);
|
||||
if (!is_tristate_default(taq))
|
||||
av_opt_set_int(context->priv_data, "temporal-aq", taq, 0);
|
||||
} else {
|
||||
if (!is_tristate_default(saq))
|
||||
av_opt_set_int(context->priv_data, "spatial_aq", saq, 0);
|
||||
if (!is_tristate_default(taq))
|
||||
av_opt_set_int(context->priv_data, "temporal_aq", taq, 0);
|
||||
}
|
||||
if (is_tristate_enabled(saq))
|
||||
av_opt_set_int(context->priv_data, "aq-strength",
|
||||
static_cast<int>(obs_data_get_int(settings, ST_AQ_STRENGTH)), 0);
|
||||
}
|
||||
|
||||
{ // Other
|
||||
int64_t zl = obs_data_get_int(settings, ST_OTHER_ZEROLATENCY);
|
||||
int64_t wp = obs_data_get_int(settings, ST_OTHER_WEIGHTED_PREDICTION);
|
||||
int64_t nrp = obs_data_get_int(settings, ST_OTHER_NONREFERENCE_PFRAMES);
|
||||
|
||||
context->max_b_frames = static_cast<int>(obs_data_get_int(settings, ST_OTHER_BFRAMES));
|
||||
|
||||
if (!is_tristate_default(zl))
|
||||
av_opt_set_int(context->priv_data, "zerolatency", zl, 0);
|
||||
if (!is_tristate_default(nrp))
|
||||
av_opt_set_int(context->priv_data, "nonref_p", nrp, 0);
|
||||
|
||||
if ((context->max_b_frames != 0) && is_tristate_enabled(wp)) {
|
||||
PLOG_WARNING("[%s] Weighted Prediction disabled because of B-Frames being used.", codec->name);
|
||||
av_opt_set_int(context->priv_data, "weighted_pred", 0, 0);
|
||||
} else if (!is_tristate_default(wp)) {
|
||||
av_opt_set_int(context->priv_data, "weighted_pred", wp, 0);
|
||||
}
|
||||
|
||||
{
|
||||
auto found = b_ref_mode_to_opt.find(
|
||||
static_cast<b_ref_mode>(obs_data_get_int(settings, ST_OTHER_BFRAME_REFERENCEMODE)));
|
||||
if (found != b_ref_mode_to_opt.end()) {
|
||||
av_opt_set(context->priv_data, "b_ref_mode", found->second.c_str(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void obsffmpeg::nvenc::log_options(obs_data_t*, const AVCodec* codec, AVCodecContext* context)
|
||||
{
|
||||
PLOG_INFO("[%s] Nvidia NVENC:", codec->name);
|
||||
ffmpeg::tools::print_av_option_string(context, "preset", " Preset", [](int64_t v) {
|
||||
preset val = static_cast<preset>(v);
|
||||
std::string name = "<Unknown>";
|
||||
auto index = preset_to_opt.find(val);
|
||||
if (index != preset_to_opt.end())
|
||||
name = index->second;
|
||||
return name;
|
||||
});
|
||||
ffmpeg::tools::print_av_option_string(context, "rc", " Rate Control", [](int64_t v) {
|
||||
ratecontrolmode val = static_cast<ratecontrolmode>(v);
|
||||
std::string name = "<Unknown>";
|
||||
auto index = ratecontrolmode_to_opt.find(val);
|
||||
if (index != ratecontrolmode_to_opt.end())
|
||||
name = index->second;
|
||||
return name;
|
||||
});
|
||||
ffmpeg::tools::print_av_option_bool(context, "2pass", " Two Pass");
|
||||
ffmpeg::tools::print_av_option_int(context, "rc-lookahead", " Look-Ahead", "Frames");
|
||||
ffmpeg::tools::print_av_option_bool(context, "no-scenecut", " Adaptive I-Frames");
|
||||
if (strcmp(codec->name, "h264_nvenc") == 0)
|
||||
ffmpeg::tools::print_av_option_bool(context, "b_adapt", " Adaptive B-Frames");
|
||||
|
||||
PLOG_INFO("[%s] Bitrate:", codec->name);
|
||||
ffmpeg::tools::print_av_option_int(context, "bitrate", " Target", "bits/sec");
|
||||
ffmpeg::tools::print_av_option_int(context, "rc_max_rate", " Maximum", "bits/sec");
|
||||
ffmpeg::tools::print_av_option_int(context, "rc_buffer_size", " Buffer", "bits");
|
||||
PLOG_INFO("[%s] Quality:", codec->name);
|
||||
ffmpeg::tools::print_av_option_int(context, "qmin", " Minimum", "");
|
||||
ffmpeg::tools::print_av_option_int(context, "cq", " Target", "");
|
||||
ffmpeg::tools::print_av_option_int(context, "qmax", " Maximum", "");
|
||||
PLOG_INFO("[%s] Quantization Parameters:", codec->name);
|
||||
ffmpeg::tools::print_av_option_int(context, "init_qpI", " I-Frame", "");
|
||||
ffmpeg::tools::print_av_option_int(context, "init_qpP", " P-Frame", "");
|
||||
ffmpeg::tools::print_av_option_int(context, "init_qpB", " B-Frame", "");
|
||||
|
||||
ffmpeg::tools::print_av_option_int(context, "max_b_frames", " B-Frames", "Frames");
|
||||
ffmpeg::tools::print_av_option_string(context, "b_ref_mode", " Reference Mode", [](int64_t v) {
|
||||
b_ref_mode val = static_cast<b_ref_mode>(v);
|
||||
std::string name = "<Unknown>";
|
||||
auto index = b_ref_mode_to_opt.find(val);
|
||||
if (index != b_ref_mode_to_opt.end())
|
||||
name = index->second;
|
||||
return name;
|
||||
});
|
||||
|
||||
PLOG_INFO("[%s] Adaptive Quantization:", codec->name);
|
||||
if (strcmp(codec->name, "h264_nvenc") == 0) {
|
||||
ffmpeg::tools::print_av_option_bool(context, "spatial-aq", " Spatial AQ");
|
||||
ffmpeg::tools::print_av_option_int(context, "aq-strength", " Strength", "");
|
||||
ffmpeg::tools::print_av_option_bool(context, "temporal-aq", " Temporal AQ");
|
||||
} else {
|
||||
ffmpeg::tools::print_av_option_bool(context, "spatial_aq", " Spatial AQ");
|
||||
ffmpeg::tools::print_av_option_int(context, "aq-strength", " Strength", "");
|
||||
ffmpeg::tools::print_av_option_bool(context, "temporal_aq", " Temporal AQ");
|
||||
}
|
||||
|
||||
PLOG_INFO("[%s] Other:", codec->name);
|
||||
ffmpeg::tools::print_av_option_bool(context, "zerolatency", " Zero Latency");
|
||||
ffmpeg::tools::print_av_option_bool(context, "weighted_pred", " Weighted Prediction");
|
||||
ffmpeg::tools::print_av_option_bool(context, "nonref_p", " Non-reference P-Frames");
|
||||
ffmpeg::tools::print_av_option_bool(context, "strict_gop", " Strict GOP");
|
||||
ffmpeg::tools::print_av_option_bool(context, "aud", " Access Unit Delimiters");
|
||||
ffmpeg::tools::print_av_option_bool(context, "bluray-compat", " Bluray Compatibility");
|
||||
if (strcmp(codec->name, "h264_nvenc") == 0)
|
||||
ffmpeg::tools::print_av_option_bool(context, "a53cc", " A53 Closed Captions");
|
||||
ffmpeg::tools::print_av_option_int(context, "dpb_size", " DPB Size", "");
|
||||
}
|
100
source/encoders/handlers/nvenc_shared.hpp
Normal file
100
source/encoders/handlers/nvenc_shared.hpp
Normal file
|
@ -0,0 +1,100 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include "utility.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <obs-properties.h>
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavcodec/avcodec.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
namespace obsffmpeg {
|
||||
class encoder;
|
||||
|
||||
namespace nvenc {
|
||||
enum class preset : int64_t {
|
||||
DEFAULT,
|
||||
SLOW,
|
||||
MEDIUM,
|
||||
FAST,
|
||||
HIGH_PERFORMANCE,
|
||||
HIGH_QUALITY,
|
||||
BLURAYDISC,
|
||||
LOW_LATENCY,
|
||||
LOW_LATENCY_HIGH_PERFORMANCE,
|
||||
LOW_LATENCY_HIGH_QUALITY,
|
||||
LOSSLESS,
|
||||
LOSSLESS_HIGH_PERFORMANCE,
|
||||
// Append things before this.
|
||||
INVALID = -1,
|
||||
};
|
||||
|
||||
enum class ratecontrolmode : int64_t {
|
||||
CQP,
|
||||
VBR,
|
||||
VBR_HQ,
|
||||
CBR,
|
||||
CBR_HQ,
|
||||
CBR_LD_HQ,
|
||||
// Append things before this.
|
||||
INVALID = -1,
|
||||
};
|
||||
|
||||
enum class b_ref_mode : int64_t {
|
||||
DISABLED,
|
||||
EACH,
|
||||
MIDDLE,
|
||||
// Append things before this.
|
||||
INVALID = -1,
|
||||
};
|
||||
|
||||
extern std::map<preset, std::string> presets;
|
||||
|
||||
extern std::map<preset, std::string> preset_to_opt;
|
||||
|
||||
extern std::map<ratecontrolmode, std::string> ratecontrolmodes;
|
||||
|
||||
extern std::map<ratecontrolmode, std::string> ratecontrolmode_to_opt;
|
||||
|
||||
extern std::map<b_ref_mode, std::string> b_ref_modes;
|
||||
|
||||
extern std::map<b_ref_mode, std::string> b_ref_mode_to_opt;
|
||||
|
||||
void override_update(obsffmpeg::encoder* instance, obs_data_t* settings);
|
||||
|
||||
void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||
|
||||
void get_properties_pre(obs_properties_t* props, const AVCodec* codec);
|
||||
|
||||
void get_properties_post(obs_properties_t* props, const AVCodec* codec);
|
||||
|
||||
void get_runtime_properties(obs_properties_t* props, const AVCodec* codec, AVCodecContext* context);
|
||||
|
||||
void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||
|
||||
void log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||
} // namespace nvenc
|
||||
} // namespace obsffmpeg
|
135
source/encoders/handlers/prores_aw_handler.cpp
Normal file
135
source/encoders/handlers/prores_aw_handler.cpp
Normal file
|
@ -0,0 +1,135 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "prores_aw_handler.hpp"
|
||||
#include "codecs/prores.hpp"
|
||||
#include "ffmpeg/tools.hpp"
|
||||
#include "plugin.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <obs-module.h>
|
||||
}
|
||||
|
||||
INITIALIZER(prores_aw_handler_init)
|
||||
{
|
||||
obsffmpeg::initializers.push_back([]() {
|
||||
obsffmpeg::register_codec_handler("prores_aw", std::make_shared<obsffmpeg::ui::prores_aw_handler>());
|
||||
});
|
||||
};
|
||||
|
||||
void obsffmpeg::ui::prores_aw_handler::override_colorformat(AVPixelFormat& target_format, obs_data_t* settings,
|
||||
const AVCodec* codec, AVCodecContext*)
|
||||
{
|
||||
std::string profile = "";
|
||||
|
||||
int profile_id = static_cast<int>(obs_data_get_int(settings, P_PRORES_PROFILE));
|
||||
for (auto ptr = codec->profiles; ptr->profile != FF_PROFILE_UNKNOWN; ptr++) {
|
||||
if (ptr->profile == profile_id) {
|
||||
profile = ptr->name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_map<AVPixelFormat, std::list<std::string>> valid_formats = {
|
||||
{AV_PIX_FMT_YUV422P10, {"apco", "apcs", "apcn", "apch"}}, {AV_PIX_FMT_YUV444P10, {"ap4h", "ap4x"}}};
|
||||
|
||||
for (auto kv : valid_formats) {
|
||||
for (auto name : kv.second) {
|
||||
if (profile == name) {
|
||||
target_format = kv.first;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::prores_aw_handler::get_defaults(obs_data_t* settings, const AVCodec*, AVCodecContext*, bool)
|
||||
{
|
||||
obs_data_set_default_int(settings, P_PRORES_PROFILE, 0);
|
||||
}
|
||||
|
||||
inline const char* profile_to_name(const AVProfile* ptr)
|
||||
{
|
||||
switch (ptr->profile) {
|
||||
case 0:
|
||||
return TRANSLATE(P_PRORES_PROFILE_APCO);
|
||||
case 1:
|
||||
return TRANSLATE(P_PRORES_PROFILE_APCS);
|
||||
case 2:
|
||||
return TRANSLATE(P_PRORES_PROFILE_APCN);
|
||||
case 3:
|
||||
return TRANSLATE(P_PRORES_PROFILE_APCH);
|
||||
case 4:
|
||||
return TRANSLATE(P_PRORES_PROFILE_AP4H);
|
||||
case 5:
|
||||
return TRANSLATE(P_PRORES_PROFILE_AP4X);
|
||||
default:
|
||||
return ptr->name;
|
||||
}
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::prores_aw_handler::get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||
AVCodecContext* context, bool)
|
||||
{
|
||||
if (!context) {
|
||||
auto p = obs_properties_add_list(props, P_PRORES_PROFILE, TRANSLATE(P_PRORES_PROFILE),
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_PRORES_PROFILE)));
|
||||
for (auto ptr = codec->profiles; ptr->profile != FF_PROFILE_UNKNOWN; ptr++) {
|
||||
obs_property_list_add_int(p, profile_to_name(ptr), static_cast<int64_t>(ptr->profile));
|
||||
}
|
||||
} else {
|
||||
obs_property_set_enabled(obs_properties_get(props, P_PRORES_PROFILE), false);
|
||||
}
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::prores_aw_handler::update(obs_data_t* settings, const AVCodec*, AVCodecContext* context)
|
||||
{
|
||||
context->profile = static_cast<int>(obs_data_get_int(settings, P_PRORES_PROFILE));
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::prores_aw_handler::log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context)
|
||||
{
|
||||
PLOG_INFO("[%s] Apple ProRes:", codec->name);
|
||||
ffmpeg::tools::print_av_option_string(context, "profile", " Profile", [&codec](int64_t v) {
|
||||
int val = static_cast<int>(v);
|
||||
for (auto ptr = codec->profiles; (ptr->profile != FF_PROFILE_UNKNOWN) && (ptr != nullptr); ptr++) {
|
||||
if (ptr->profile == val) {
|
||||
return std::string(profile_to_name(ptr));
|
||||
}
|
||||
}
|
||||
return std::string("<Unknown>");
|
||||
});
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::prores_aw_handler::process_avpacket(AVPacket& packet, const AVCodec*, AVCodecContext*)
|
||||
{
|
||||
//FFmpeg Bug:
|
||||
// When ProRes content is stored in Matroska, FFmpeg strips the size
|
||||
// from the atom. Later when the ProRes content is demuxed from Matroska,
|
||||
// FFmpeg creates an atom with the incorrect size, as the ATOM size
|
||||
// should be content + atom, but FFmpeg set it to only be content. This
|
||||
// difference leads to decoders to be off by 8 bytes.
|
||||
//Fix (until FFmpeg stops being broken):
|
||||
// Pad the packet with 8 bytes of 0x00.
|
||||
|
||||
av_grow_packet(&packet, 8);
|
||||
}
|
56
source/encoders/handlers/prores_aw_handler.hpp
Normal file
56
source/encoders/handlers/prores_aw_handler.hpp
Normal file
|
@ -0,0 +1,56 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
#include "handler.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <obs-properties.h>
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavcodec/avcodec.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
namespace obsffmpeg {
|
||||
namespace ui {
|
||||
class prores_aw_handler : public handler {
|
||||
public:
|
||||
virtual void override_colorformat(AVPixelFormat& target_format, obs_data_t* settings,
|
||||
const AVCodec* codec, AVCodecContext* context) override;
|
||||
|
||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context,
|
||||
bool hw_encode) override;
|
||||
|
||||
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||
AVCodecContext* context, bool hw_encode) override;
|
||||
|
||||
virtual void update(obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
|
||||
virtual void log_options(obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
|
||||
virtual void process_avpacket(AVPacket& packet, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
};
|
||||
} // namespace ui
|
||||
} // namespace obsffmpeg
|
147
source/ffmpeg/avframe-queue.cpp
Normal file
147
source/ffmpeg/avframe-queue.cpp
Normal file
|
@ -0,0 +1,147 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "avframe-queue.hpp"
|
||||
#include "tools.hpp"
|
||||
|
||||
std::shared_ptr<AVFrame> ffmpeg::avframe_queue::create_frame()
|
||||
{
|
||||
std::shared_ptr<AVFrame> frame = std::shared_ptr<AVFrame>(av_frame_alloc(), [](AVFrame* frame) {
|
||||
av_frame_unref(frame);
|
||||
av_frame_free(&frame);
|
||||
});
|
||||
frame->width = this->resolution.first;
|
||||
frame->height = this->resolution.second;
|
||||
frame->format = this->format;
|
||||
|
||||
int res = av_frame_get_buffer(frame.get(), 32);
|
||||
if (res < 0) {
|
||||
throw std::exception(ffmpeg::tools::get_error_description(res));
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
ffmpeg::avframe_queue::avframe_queue() {}
|
||||
|
||||
ffmpeg::avframe_queue::~avframe_queue()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void ffmpeg::avframe_queue::set_resolution(uint32_t const width, uint32_t const height)
|
||||
{
|
||||
this->resolution.first = width;
|
||||
this->resolution.second = height;
|
||||
}
|
||||
|
||||
void ffmpeg::avframe_queue::get_resolution(uint32_t& width, uint32_t& height)
|
||||
{
|
||||
width = this->resolution.first;
|
||||
height = this->resolution.second;
|
||||
}
|
||||
|
||||
uint32_t ffmpeg::avframe_queue::get_width()
|
||||
{
|
||||
return this->resolution.first;
|
||||
}
|
||||
|
||||
uint32_t ffmpeg::avframe_queue::get_height()
|
||||
{
|
||||
return this->resolution.second;
|
||||
}
|
||||
|
||||
void ffmpeg::avframe_queue::set_pixel_format(AVPixelFormat const format)
|
||||
{
|
||||
this->format = format;
|
||||
}
|
||||
|
||||
AVPixelFormat ffmpeg::avframe_queue::get_pixel_format()
|
||||
{
|
||||
return this->format;
|
||||
}
|
||||
|
||||
void ffmpeg::avframe_queue::precache(size_t count)
|
||||
{
|
||||
for (size_t n = 0; n < count; n++) {
|
||||
push(create_frame());
|
||||
}
|
||||
}
|
||||
|
||||
void ffmpeg::avframe_queue::clear()
|
||||
{
|
||||
std::unique_lock<std::mutex> ulock(this->lock);
|
||||
frames.clear();
|
||||
}
|
||||
|
||||
void ffmpeg::avframe_queue::push(std::shared_ptr<AVFrame> const frame)
|
||||
{
|
||||
std::unique_lock<std::mutex> ulock(this->lock);
|
||||
frames.push_back(frame);
|
||||
}
|
||||
|
||||
std::shared_ptr<AVFrame> ffmpeg::avframe_queue::pop()
|
||||
{
|
||||
std::unique_lock<std::mutex> ulock(this->lock);
|
||||
std::shared_ptr<AVFrame> ret;
|
||||
while (ret == nullptr) {
|
||||
if (frames.size() == 0) {
|
||||
ret = create_frame();
|
||||
} else {
|
||||
ret = frames.front();
|
||||
if (ret == nullptr) {
|
||||
ret = create_frame();
|
||||
} else {
|
||||
frames.pop_front();
|
||||
if ((static_cast<uint32_t>(ret->width) != this->resolution.first)
|
||||
|| (static_cast<uint32_t>(ret->height) != this->resolution.second)
|
||||
|| (ret->format != this->format)) {
|
||||
ret = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::shared_ptr<AVFrame> ffmpeg::avframe_queue::pop_only()
|
||||
{
|
||||
std::unique_lock<std::mutex> ulock(this->lock);
|
||||
if (frames.size() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
std::shared_ptr<AVFrame> ret = frames.front();
|
||||
if (ret == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
frames.pop_front();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ffmpeg::avframe_queue::empty()
|
||||
{
|
||||
return frames.empty();
|
||||
}
|
||||
|
||||
size_t ffmpeg::avframe_queue::size()
|
||||
{
|
||||
return frames.size();
|
||||
}
|
69
source/ffmpeg/avframe-queue.hpp
Normal file
69
source/ffmpeg/avframe-queue.hpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
|
||||
extern "C" {
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavutil/frame.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
namespace ffmpeg {
|
||||
class avframe_queue {
|
||||
std::deque<std::shared_ptr<AVFrame>> frames;
|
||||
std::mutex lock;
|
||||
|
||||
std::pair<uint32_t, uint32_t> resolution;
|
||||
AVPixelFormat format = AV_PIX_FMT_NONE;
|
||||
|
||||
std::shared_ptr<AVFrame> create_frame();
|
||||
|
||||
public:
|
||||
avframe_queue();
|
||||
~avframe_queue();
|
||||
|
||||
void set_resolution(uint32_t width, uint32_t height);
|
||||
void get_resolution(uint32_t& width, uint32_t& height);
|
||||
uint32_t get_width();
|
||||
uint32_t get_height();
|
||||
|
||||
void set_pixel_format(AVPixelFormat format);
|
||||
AVPixelFormat get_pixel_format();
|
||||
|
||||
void precache(size_t count);
|
||||
|
||||
void clear();
|
||||
|
||||
void push(std::shared_ptr<AVFrame> frame);
|
||||
|
||||
std::shared_ptr<AVFrame> pop();
|
||||
|
||||
std::shared_ptr<AVFrame> pop_only();
|
||||
|
||||
bool empty();
|
||||
|
||||
size_t size();
|
||||
};
|
||||
} // namespace ffmpeg
|
22
source/ffmpeg/hwapi/base.cpp
Normal file
22
source/ffmpeg/hwapi/base.cpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "base.hpp"
|
70
source/ffmpeg/hwapi/base.hpp
Normal file
70
source/ffmpeg/hwapi/base.hpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cinttypes>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
extern "C" {
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavutil/frame.h>
|
||||
#include <libavutil/hwcontext.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
namespace obsffmpeg {
|
||||
namespace hwapi {
|
||||
struct device {
|
||||
std::pair<int64_t, int64_t> id;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
class instance;
|
||||
|
||||
class base {
|
||||
public:
|
||||
virtual std::list<obsffmpeg::hwapi::device> enumerate_adapters() = 0;
|
||||
|
||||
virtual std::shared_ptr<obsffmpeg::hwapi::instance> create(obsffmpeg::hwapi::device target) = 0;
|
||||
|
||||
virtual std::shared_ptr<obsffmpeg::hwapi::instance> create_from_obs() = 0;
|
||||
};
|
||||
|
||||
class instance {
|
||||
public:
|
||||
virtual AVBufferRef* create_device_context() = 0;
|
||||
|
||||
virtual std::shared_ptr<AVFrame> allocate_frame(AVBufferRef* frames) = 0;
|
||||
|
||||
virtual void copy_from_obs(AVBufferRef* frames, uint32_t handle, uint64_t lock_key,
|
||||
uint64_t* next_lock_key, std::shared_ptr<AVFrame> frame) = 0;
|
||||
|
||||
virtual std::shared_ptr<AVFrame> avframe_from_obs(AVBufferRef* frames, uint32_t handle,
|
||||
uint64_t lock_key,
|
||||
uint64_t* next_lock_key) = 0;
|
||||
};
|
||||
} // namespace hwapi
|
||||
} // namespace obsffmpeg
|
245
source/ffmpeg/hwapi/d3d11.cpp
Normal file
245
source/ffmpeg/hwapi/d3d11.cpp
Normal file
|
@ -0,0 +1,245 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "d3d11.hpp"
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include "utility.hpp"
|
||||
|
||||
extern "C" {
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <graphics/graphics.h>
|
||||
#include <libavutil/hwcontext_d3d11va.h>
|
||||
#include <obs.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
obsffmpeg::hwapi::d3d11::d3d11() : _dxgi_module(0), _d3d11_module(0)
|
||||
{
|
||||
_dxgi_module = LoadLibraryW(L"dxgi.dll");
|
||||
if (!_dxgi_module)
|
||||
throw std::runtime_error("Unable to load DXGI");
|
||||
|
||||
_d3d11_module = LoadLibraryW(L"d3d11.dll");
|
||||
if (!_d3d11_module)
|
||||
throw std::runtime_error("Unable to load D3D11");
|
||||
|
||||
_CreateDXGIFactory = reinterpret_cast<CreateDXGIFactory_t>(GetProcAddress(_dxgi_module, "CreateDXGIFactory"));
|
||||
_CreateDXGIFactory1 =
|
||||
reinterpret_cast<CreateDXGIFactory1_t>(GetProcAddress(_dxgi_module, "CreateDXGIFactory1"));
|
||||
_D3D11CreateDevice = reinterpret_cast<D3D11CreateDevice_t>(GetProcAddress(_d3d11_module, "D3D11CreateDevice"));
|
||||
|
||||
if (!_CreateDXGIFactory && !_CreateDXGIFactory1)
|
||||
throw std::runtime_error("DXGI not supported");
|
||||
|
||||
if (!_D3D11CreateDevice)
|
||||
throw std::runtime_error("D3D11 not supported");
|
||||
|
||||
HRESULT hr = _CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&_dxgifactory);
|
||||
if (FAILED(hr)) {
|
||||
std::stringstream sstr;
|
||||
sstr << "Failed to create DXGI Factory (" << hr << ")";
|
||||
throw std::runtime_error(sstr.str());
|
||||
}
|
||||
}
|
||||
|
||||
obsffmpeg::hwapi::d3d11::~d3d11()
|
||||
{
|
||||
FreeLibrary(_dxgi_module);
|
||||
FreeLibrary(_d3d11_module);
|
||||
}
|
||||
|
||||
std::list<obsffmpeg::hwapi::device> obsffmpeg::hwapi::d3d11::enumerate_adapters()
|
||||
{
|
||||
std::list<device> adapters;
|
||||
|
||||
// Enumerate Adapters
|
||||
IDXGIAdapter1* dxgi_adapter = nullptr;
|
||||
for (UINT idx = 0; !FAILED(_dxgifactory->EnumAdapters1(idx, &dxgi_adapter)); idx++) {
|
||||
DXGI_ADAPTER_DESC1 desc = DXGI_ADAPTER_DESC1();
|
||||
dxgi_adapter->GetDesc1(&desc);
|
||||
|
||||
std::vector<char> buf(1024);
|
||||
size_t len = snprintf(buf.data(), buf.size(), "%ls (VEN_%04x/DEV_%04x/SUB_%04x/REV_%04x)",
|
||||
desc.Description, desc.VendorId, desc.DeviceId, desc.SubSysId, desc.Revision);
|
||||
|
||||
device dev;
|
||||
dev.name = std::string(buf.data(), buf.data() + len);
|
||||
dev.id.first = desc.AdapterLuid.HighPart;
|
||||
dev.id.second = desc.AdapterLuid.LowPart;
|
||||
|
||||
adapters.push_back(dev);
|
||||
}
|
||||
|
||||
return std::move(adapters);
|
||||
}
|
||||
|
||||
std::shared_ptr<obsffmpeg::hwapi::instance> obsffmpeg::hwapi::d3d11::create(obsffmpeg::hwapi::device target)
|
||||
{
|
||||
std::shared_ptr<d3d11_instance> inst;
|
||||
ATL::CComPtr<ID3D11Device> device;
|
||||
ATL::CComPtr<ID3D11DeviceContext> context;
|
||||
IDXGIAdapter1* adapter = nullptr;
|
||||
|
||||
// Find the correct "Adapter" (device).
|
||||
IDXGIAdapter1* dxgi_adapter = nullptr;
|
||||
for (UINT idx = 0; !FAILED(_dxgifactory->EnumAdapters1(idx, &dxgi_adapter)); idx++) {
|
||||
DXGI_ADAPTER_DESC1 desc = DXGI_ADAPTER_DESC1();
|
||||
dxgi_adapter->GetDesc1(&desc);
|
||||
|
||||
if ((desc.AdapterLuid.LowPart == target.id.second) && (desc.AdapterLuid.HighPart == target.id.first)) {
|
||||
adapter = dxgi_adapter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a D3D11 Device
|
||||
UINT device_flags = D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
|
||||
std::vector<D3D_FEATURE_LEVEL> feature_levels = {D3D_FEATURE_LEVEL_12_1, D3D_FEATURE_LEVEL_12_0,
|
||||
D3D_FEATURE_LEVEL_11_1};
|
||||
|
||||
if (FAILED(_D3D11CreateDevice(adapter, D3D_DRIVER_TYPE_HARDWARE, NULL, device_flags, feature_levels.data(),
|
||||
static_cast<UINT>(feature_levels.size()), D3D11_SDK_VERSION, &device, NULL,
|
||||
&context))) {
|
||||
throw std::runtime_error("Failed to create D3D11 device for target.");
|
||||
}
|
||||
|
||||
return std::make_shared<d3d11_instance>(device, context);
|
||||
}
|
||||
|
||||
std::shared_ptr<obsffmpeg::hwapi::instance> obsffmpeg::hwapi::d3d11::create_from_obs()
|
||||
{
|
||||
auto gctx = obsffmpeg::obs_graphics();
|
||||
|
||||
if (GS_DEVICE_DIRECT3D_11 != gs_get_device_type()) {
|
||||
throw std::runtime_error("OBS Device is not a D3D11 Device.");
|
||||
}
|
||||
|
||||
ATL::CComPtr<ID3D11Device> device =
|
||||
ATL::CComPtr<ID3D11Device>(reinterpret_cast<ID3D11Device*>(gs_get_device_obj()));
|
||||
ATL::CComPtr<ID3D11DeviceContext> context;
|
||||
device->GetImmediateContext(&context);
|
||||
|
||||
return std::make_shared<d3d11_instance>(device, context);
|
||||
}
|
||||
|
||||
struct D3D11AVFrame {
|
||||
ATL::CComPtr<ID3D11Texture2D> handle;
|
||||
};
|
||||
|
||||
obsffmpeg::hwapi::d3d11_instance::d3d11_instance(ATL::CComPtr<ID3D11Device> device,
|
||||
ATL::CComPtr<ID3D11DeviceContext> context)
|
||||
{
|
||||
_device = device;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
obsffmpeg::hwapi::d3d11_instance::~d3d11_instance() {}
|
||||
|
||||
AVBufferRef* obsffmpeg::hwapi::d3d11_instance::create_device_context()
|
||||
{
|
||||
AVBufferRef* dctx_ref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_D3D11VA);
|
||||
if (!dctx_ref)
|
||||
throw std::runtime_error("Failed to allocate AVHWDeviceContext.");
|
||||
|
||||
AVHWDeviceContext* dctx = reinterpret_cast<AVHWDeviceContext*>(dctx_ref->data);
|
||||
AVD3D11VADeviceContext* d3d11va = reinterpret_cast<AVD3D11VADeviceContext*>(dctx->hwctx);
|
||||
|
||||
// TODO: Determine if these need an additional reference.
|
||||
d3d11va->device = _device;
|
||||
d3d11va->device->AddRef();
|
||||
d3d11va->device_context = _context;
|
||||
d3d11va->device_context->AddRef();
|
||||
d3d11va->lock = [](void*) { obs_enter_graphics(); };
|
||||
d3d11va->unlock = [](void*) { obs_leave_graphics(); };
|
||||
|
||||
int ret = av_hwdevice_ctx_init(dctx_ref);
|
||||
if (ret < 0)
|
||||
throw std::runtime_error("Failed to initialize AVHWDeviceContext.");
|
||||
|
||||
return dctx_ref;
|
||||
}
|
||||
|
||||
std::shared_ptr<AVFrame> obsffmpeg::hwapi::d3d11_instance::allocate_frame(AVBufferRef* frames)
|
||||
{
|
||||
auto gctx = obsffmpeg::obs_graphics();
|
||||
|
||||
auto frame = std::shared_ptr<AVFrame>(av_frame_alloc(), [](AVFrame* frame) {
|
||||
av_frame_unref(frame);
|
||||
av_frame_free(&frame);
|
||||
});
|
||||
|
||||
if (av_hwframe_get_buffer(frames, frame.get(), 0) < 0) {
|
||||
throw std::runtime_error("Failed to create AVFrame.");
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
void obsffmpeg::hwapi::d3d11_instance::copy_from_obs(AVBufferRef*, uint32_t handle, uint64_t lock_key,
|
||||
uint64_t* next_lock_key, std::shared_ptr<AVFrame> frame)
|
||||
{
|
||||
auto gctx = obsffmpeg::obs_graphics();
|
||||
|
||||
ATL::CComPtr<IDXGIKeyedMutex> mutex;
|
||||
ATL::CComPtr<ID3D11Texture2D> input;
|
||||
|
||||
if (FAILED(_device->OpenSharedResource(reinterpret_cast<HANDLE>(static_cast<uintptr_t>(handle)),
|
||||
__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&input)))) {
|
||||
throw std::runtime_error("Failed to open shared texture resource.");
|
||||
}
|
||||
|
||||
if (FAILED(input->QueryInterface(__uuidof(IDXGIKeyedMutex), reinterpret_cast<void**>(&mutex)))) {
|
||||
throw std::runtime_error("Failed to retrieve mutex for texture resource.");
|
||||
}
|
||||
|
||||
if (FAILED(mutex->AcquireSync(lock_key, 1000))) {
|
||||
throw std::runtime_error("Failed to acquire lock on input texture.");
|
||||
}
|
||||
|
||||
// Set some parameters on the input texture, and get its description.
|
||||
UINT evict = input->GetEvictionPriority();
|
||||
input->SetEvictionPriority(DXGI_RESOURCE_PRIORITY_MAXIMUM);
|
||||
|
||||
// Clone the content of the input texture.
|
||||
_context->CopyResource(reinterpret_cast<ID3D11Texture2D*>(frame->data[0]), input);
|
||||
|
||||
// Restore original parameters on input.
|
||||
input->SetEvictionPriority(evict);
|
||||
|
||||
if (FAILED(mutex->ReleaseSync(lock_key))) {
|
||||
throw std::runtime_error("Failed to release lock on input texture.");
|
||||
}
|
||||
|
||||
// TODO: Determine if this is necessary.
|
||||
mutex->ReleaseSync(*next_lock_key);
|
||||
}
|
||||
|
||||
std::shared_ptr<AVFrame> obsffmpeg::hwapi::d3d11_instance::avframe_from_obs(AVBufferRef* frames, uint32_t handle,
|
||||
uint64_t lock_key, uint64_t* next_lock_key)
|
||||
{
|
||||
auto gctx = obsffmpeg::obs_graphics();
|
||||
|
||||
auto frame = this->allocate_frame(frames);
|
||||
this->copy_from_obs(frames, handle, lock_key, next_lock_key, frame);
|
||||
return frame;
|
||||
}
|
80
source/ffmpeg/hwapi/d3d11.hpp
Normal file
80
source/ffmpeg/hwapi/d3d11.hpp
Normal file
|
@ -0,0 +1,80 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include <atlutil.h>
|
||||
#include <d3d11.h>
|
||||
#include <d3d11_1.h>
|
||||
#include <dxgi.h>
|
||||
#include "base.hpp"
|
||||
|
||||
namespace obsffmpeg {
|
||||
namespace hwapi {
|
||||
class d3d11 : public ::obsffmpeg::hwapi::base {
|
||||
typedef HRESULT(__stdcall* CreateDXGIFactory_t)(REFIID, void**);
|
||||
typedef HRESULT(__stdcall* CreateDXGIFactory1_t)(REFIID, void**);
|
||||
typedef HRESULT(__stdcall* D3D11CreateDevice_t)(_In_opt_ IDXGIAdapter*, D3D_DRIVER_TYPE,
|
||||
HMODULE, UINT, CONST D3D_FEATURE_LEVEL*, UINT,
|
||||
UINT, _Out_opt_ ID3D11Device**,
|
||||
_Out_opt_ D3D_FEATURE_LEVEL*,
|
||||
_Out_opt_ ID3D11DeviceContext**);
|
||||
|
||||
HMODULE _dxgi_module;
|
||||
CreateDXGIFactory_t _CreateDXGIFactory;
|
||||
CreateDXGIFactory1_t _CreateDXGIFactory1;
|
||||
|
||||
HMODULE _d3d11_module;
|
||||
D3D11CreateDevice_t _D3D11CreateDevice;
|
||||
|
||||
ATL::CComPtr<IDXGIFactory1> _dxgifactory;
|
||||
|
||||
public:
|
||||
d3d11();
|
||||
virtual ~d3d11();
|
||||
|
||||
virtual std::list<obsffmpeg::hwapi::device> enumerate_adapters() override;
|
||||
|
||||
virtual std::shared_ptr<obsffmpeg::hwapi::instance>
|
||||
create(obsffmpeg::hwapi::device target) override;
|
||||
|
||||
virtual std::shared_ptr<obsffmpeg::hwapi::instance> create_from_obs() override;
|
||||
};
|
||||
|
||||
class d3d11_instance : public ::obsffmpeg::hwapi::instance {
|
||||
ATL::CComPtr<ID3D11Device> _device;
|
||||
ATL::CComPtr<ID3D11DeviceContext> _context;
|
||||
|
||||
public:
|
||||
d3d11_instance(ATL::CComPtr<ID3D11Device> device, ATL::CComPtr<ID3D11DeviceContext> context);
|
||||
virtual ~d3d11_instance();
|
||||
|
||||
virtual AVBufferRef* create_device_context() override;
|
||||
|
||||
virtual std::shared_ptr<AVFrame> allocate_frame(AVBufferRef* frames) override;
|
||||
|
||||
virtual void copy_from_obs(AVBufferRef* frames, uint32_t handle, uint64_t lock_key,
|
||||
uint64_t* next_lock_key, std::shared_ptr<AVFrame> frame) override;
|
||||
|
||||
virtual std::shared_ptr<AVFrame> avframe_from_obs(AVBufferRef* frames, uint32_t handle,
|
||||
uint64_t lock_key,
|
||||
uint64_t* next_lock_key) override;
|
||||
};
|
||||
} // namespace hwapi
|
||||
} // namespace obsffmpeg
|
204
source/ffmpeg/swscale.cpp
Normal file
204
source/ffmpeg/swscale.cpp
Normal file
|
@ -0,0 +1,204 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "swscale.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
ffmpeg::swscale::swscale() {}
|
||||
|
||||
ffmpeg::swscale::~swscale()
|
||||
{
|
||||
finalize();
|
||||
}
|
||||
|
||||
void ffmpeg::swscale::set_source_size(uint32_t width, uint32_t height)
|
||||
{
|
||||
source_size.first = width;
|
||||
source_size.second = height;
|
||||
}
|
||||
|
||||
void ffmpeg::swscale::get_source_size(uint32_t& width, uint32_t& height)
|
||||
{
|
||||
width = this->source_size.first;
|
||||
height = this->source_size.second;
|
||||
}
|
||||
|
||||
std::pair<uint32_t, uint32_t> ffmpeg::swscale::get_source_size()
|
||||
{
|
||||
return this->source_size;
|
||||
}
|
||||
|
||||
uint32_t ffmpeg::swscale::get_source_width()
|
||||
{
|
||||
return this->source_size.first;
|
||||
}
|
||||
|
||||
uint32_t ffmpeg::swscale::get_source_height()
|
||||
{
|
||||
return this->source_size.second;
|
||||
}
|
||||
|
||||
void ffmpeg::swscale::set_source_format(AVPixelFormat format)
|
||||
{
|
||||
source_format = format;
|
||||
}
|
||||
|
||||
AVPixelFormat ffmpeg::swscale::get_source_format()
|
||||
{
|
||||
return this->source_format;
|
||||
}
|
||||
|
||||
void ffmpeg::swscale::set_source_color(bool full_range, AVColorSpace space)
|
||||
{
|
||||
source_full_range = full_range;
|
||||
source_colorspace = space;
|
||||
}
|
||||
|
||||
void ffmpeg::swscale::set_source_colorspace(AVColorSpace space)
|
||||
{
|
||||
this->source_colorspace = space;
|
||||
}
|
||||
|
||||
AVColorSpace ffmpeg::swscale::get_source_colorspace()
|
||||
{
|
||||
return this->source_colorspace;
|
||||
}
|
||||
|
||||
void ffmpeg::swscale::set_source_full_range(bool full_range)
|
||||
{
|
||||
this->source_full_range = full_range;
|
||||
}
|
||||
|
||||
bool ffmpeg::swscale::is_source_full_range()
|
||||
{
|
||||
return this->source_full_range;
|
||||
}
|
||||
|
||||
void ffmpeg::swscale::set_target_size(uint32_t width, uint32_t height)
|
||||
{
|
||||
target_size.first = width;
|
||||
target_size.second = height;
|
||||
}
|
||||
|
||||
void ffmpeg::swscale::get_target_size(uint32_t& width, uint32_t& height)
|
||||
{
|
||||
width = target_size.first;
|
||||
height = target_size.second;
|
||||
}
|
||||
|
||||
std::pair<uint32_t, uint32_t> ffmpeg::swscale::get_target_size()
|
||||
{
|
||||
return this->target_size;
|
||||
}
|
||||
|
||||
uint32_t ffmpeg::swscale::get_target_width()
|
||||
{
|
||||
return this->target_size.first;
|
||||
}
|
||||
|
||||
uint32_t ffmpeg::swscale::get_target_height()
|
||||
{
|
||||
return this->target_size.second;
|
||||
}
|
||||
|
||||
void ffmpeg::swscale::set_target_format(AVPixelFormat format)
|
||||
{
|
||||
target_format = format;
|
||||
}
|
||||
|
||||
AVPixelFormat ffmpeg::swscale::get_target_format()
|
||||
{
|
||||
return this->target_format;
|
||||
}
|
||||
|
||||
void ffmpeg::swscale::set_target_color(bool full_range, AVColorSpace space)
|
||||
{
|
||||
target_full_range = full_range;
|
||||
target_colorspace = space;
|
||||
}
|
||||
|
||||
void ffmpeg::swscale::set_target_colorspace(AVColorSpace space)
|
||||
{
|
||||
this->target_colorspace = space;
|
||||
}
|
||||
|
||||
AVColorSpace ffmpeg::swscale::get_target_colorspace()
|
||||
{
|
||||
return this->target_colorspace;
|
||||
}
|
||||
|
||||
void ffmpeg::swscale::set_target_full_range(bool full_range)
|
||||
{
|
||||
this->target_full_range = full_range;
|
||||
}
|
||||
|
||||
bool ffmpeg::swscale::is_target_full_range()
|
||||
{
|
||||
return this->target_full_range;
|
||||
}
|
||||
|
||||
bool ffmpeg::swscale::initialize(int flags)
|
||||
{
|
||||
if (this->context) {
|
||||
return false;
|
||||
}
|
||||
if (source_size.first == 0 || source_size.second == 0 || source_format == AV_PIX_FMT_NONE
|
||||
|| source_colorspace == AVCOL_SPC_UNSPECIFIED) {
|
||||
throw std::invalid_argument("not all source parameters were set");
|
||||
}
|
||||
if (target_size.first == 0 || target_size.second == 0 || target_format == AV_PIX_FMT_NONE
|
||||
|| target_colorspace == AVCOL_SPC_UNSPECIFIED) {
|
||||
throw std::invalid_argument("not all target parameters were set");
|
||||
}
|
||||
|
||||
this->context = sws_getContext(source_size.first, source_size.second, source_format, target_size.first,
|
||||
target_size.second, target_format, flags, nullptr, nullptr, nullptr);
|
||||
if (!this->context) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sws_setColorspaceDetails(this->context, sws_getCoefficients(source_colorspace), source_full_range ? 1 : 0,
|
||||
sws_getCoefficients(target_colorspace), target_full_range ? 1 : 0, 1L << 16 | 0L,
|
||||
1L << 16 | 0L, 1L << 16 | 0L);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ffmpeg::swscale::finalize()
|
||||
{
|
||||
if (this->context) {
|
||||
sws_freeContext(this->context);
|
||||
this->context = nullptr;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t ffmpeg::swscale::convert(const uint8_t* const source_data[], const int source_stride[], int32_t source_row,
|
||||
int32_t source_rows, uint8_t* const target_data[], const int target_stride[])
|
||||
{
|
||||
if (!this->context) {
|
||||
return 0;
|
||||
}
|
||||
int height =
|
||||
sws_scale(this->context, source_data, source_stride, source_row, source_rows, target_data, target_stride);
|
||||
return height;
|
||||
}
|
89
source/ffmpeg/swscale.hpp
Normal file
89
source/ffmpeg/swscale.hpp
Normal file
|
@ -0,0 +1,89 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef OBS_FFMPEG_FFMPEG_SWSCALE
|
||||
#define OBS_FFMPEG_FFMPEG_SWSCALE
|
||||
#pragma once
|
||||
|
||||
#include <cinttypes>
|
||||
#include <utility>
|
||||
|
||||
extern "C" {
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavutil/pixfmt.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
namespace ffmpeg {
|
||||
class swscale {
|
||||
std::pair<uint32_t, uint32_t> source_size;
|
||||
AVPixelFormat source_format = AV_PIX_FMT_NONE;
|
||||
bool source_full_range = false;
|
||||
AVColorSpace source_colorspace = AVCOL_SPC_UNSPECIFIED;
|
||||
|
||||
std::pair<uint32_t, uint32_t> target_size;
|
||||
AVPixelFormat target_format = AV_PIX_FMT_NONE;
|
||||
bool target_full_range = false;
|
||||
AVColorSpace target_colorspace = AVCOL_SPC_UNSPECIFIED;
|
||||
|
||||
SwsContext* context = nullptr;
|
||||
|
||||
public:
|
||||
swscale();
|
||||
~swscale();
|
||||
|
||||
void set_source_size(uint32_t width, uint32_t height);
|
||||
void get_source_size(uint32_t& width, uint32_t& height);
|
||||
std::pair<uint32_t, uint32_t> get_source_size();
|
||||
uint32_t get_source_width();
|
||||
uint32_t get_source_height();
|
||||
void set_source_format(AVPixelFormat format);
|
||||
AVPixelFormat get_source_format();
|
||||
void set_source_color(bool full_range, AVColorSpace space);
|
||||
void set_source_colorspace(AVColorSpace space);
|
||||
AVColorSpace get_source_colorspace();
|
||||
void set_source_full_range(bool full_range);
|
||||
bool is_source_full_range();
|
||||
|
||||
void set_target_size(uint32_t width, uint32_t height);
|
||||
void get_target_size(uint32_t& width, uint32_t& height);
|
||||
std::pair<uint32_t, uint32_t> get_target_size();
|
||||
uint32_t get_target_width();
|
||||
uint32_t get_target_height();
|
||||
void set_target_format(AVPixelFormat format);
|
||||
AVPixelFormat get_target_format();
|
||||
void set_target_color(bool full_range, AVColorSpace space);
|
||||
void set_target_colorspace(AVColorSpace space);
|
||||
AVColorSpace get_target_colorspace();
|
||||
void set_target_full_range(bool full_range);
|
||||
bool is_target_full_range();
|
||||
|
||||
bool initialize(int flags);
|
||||
bool finalize();
|
||||
|
||||
int32_t convert(const uint8_t* const source_data[], const int source_stride[], int32_t source_row,
|
||||
int32_t source_rows, uint8_t* const target_data[], const int target_stride[]);
|
||||
};
|
||||
} // namespace ffmpeg
|
||||
|
||||
#endif OBS_FFMPEG_FFMPEG_SWSCALE
|
353
source/ffmpeg/tools.cpp
Normal file
353
source/ffmpeg/tools.cpp
Normal file
|
@ -0,0 +1,353 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "tools.hpp"
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include "plugin.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
extern "C" {
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/error.h>
|
||||
#include <libavutil/opt.h>
|
||||
#include <libavutil/pixdesc.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
std::string ffmpeg::tools::translate_encoder_capabilities(int capabilities)
|
||||
{
|
||||
// Sorted by relative importance.
|
||||
std::pair<int, std::string> caps[] = {
|
||||
{AV_CODEC_CAP_EXPERIMENTAL, "Experimental"},
|
||||
|
||||
// Quality
|
||||
{AV_CODEC_CAP_LOSSLESS, "Lossless"},
|
||||
|
||||
// Features
|
||||
{AV_CODEC_CAP_PARAM_CHANGE, "Dynamic Parameter Change"},
|
||||
{AV_CODEC_CAP_SUBFRAMES, "Sub-Frames"},
|
||||
{AV_CODEC_CAP_VARIABLE_FRAME_SIZE, "Variable Frame Size"},
|
||||
{AV_CODEC_CAP_SMALL_LAST_FRAME, "Small Final Frame"},
|
||||
|
||||
// Other
|
||||
{AV_CODEC_CAP_TRUNCATED, "Truncated"},
|
||||
{AV_CODEC_CAP_CHANNEL_CONF, "AV_CODEC_CAP_CHANNEL_CONF"},
|
||||
{AV_CODEC_CAP_DRAW_HORIZ_BAND, "AV_CODEC_CAP_DRAW_HORIZ_BAND"},
|
||||
{AV_CODEC_CAP_AVOID_PROBING, "AV_CODEC_CAP_AVOID_PROBING"},
|
||||
};
|
||||
|
||||
std::stringstream sstr;
|
||||
for (auto const kv : caps) {
|
||||
if (capabilities & kv.first) {
|
||||
capabilities &= ~kv.first;
|
||||
sstr << kv.second;
|
||||
if (capabilities != 0) {
|
||||
sstr << ", ";
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
const char* ffmpeg::tools::get_pixel_format_name(AVPixelFormat v)
|
||||
{
|
||||
return av_get_pix_fmt_name(v);
|
||||
}
|
||||
|
||||
const char* ffmpeg::tools::get_color_space_name(AVColorSpace v)
|
||||
{
|
||||
switch (v) {
|
||||
case AVCOL_SPC_RGB:
|
||||
return "RGB";
|
||||
case AVCOL_SPC_BT709:
|
||||
return "BT.709";
|
||||
case AVCOL_SPC_FCC:
|
||||
return "FCC Title 47 CoFR 73.682 (a)(20)";
|
||||
case AVCOL_SPC_BT470BG:
|
||||
return "BT.601 625";
|
||||
case AVCOL_SPC_SMPTE170M:
|
||||
case AVCOL_SPC_SMPTE240M:
|
||||
return "BT.601 525";
|
||||
case AVCOL_SPC_YCGCO:
|
||||
return "ITU-T SG16";
|
||||
case AVCOL_SPC_BT2020_NCL:
|
||||
return "BT.2020 NCL";
|
||||
case AVCOL_SPC_BT2020_CL:
|
||||
return "BT.2020 CL";
|
||||
case AVCOL_SPC_SMPTE2085:
|
||||
return "SMPTE 2085";
|
||||
case AVCOL_SPC_CHROMA_DERIVED_NCL:
|
||||
return "Chroma NCL";
|
||||
case AVCOL_SPC_CHROMA_DERIVED_CL:
|
||||
return "Chroma CL";
|
||||
case AVCOL_SPC_ICTCP:
|
||||
return "BT.2100";
|
||||
case AVCOL_SPC_NB:
|
||||
return "Not Part of ABI";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
const char* ffmpeg::tools::get_error_description(int error)
|
||||
{
|
||||
thread_local char error_buf[AV_ERROR_MAX_STRING_SIZE + 1];
|
||||
if (av_strerror(error, error_buf, AV_ERROR_MAX_STRING_SIZE) < 0) {
|
||||
snprintf(error_buf, AV_ERROR_MAX_STRING_SIZE, "Unknown Error (%i)", error);
|
||||
}
|
||||
return error_buf;
|
||||
}
|
||||
|
||||
static std::map<video_format, AVPixelFormat> obs_to_av_format_map = {
|
||||
{VIDEO_FORMAT_I420, AV_PIX_FMT_YUV420P}, // YUV 4:2:0
|
||||
{VIDEO_FORMAT_NV12, AV_PIX_FMT_NV12}, // NV12 Packed YUV
|
||||
{VIDEO_FORMAT_YVYU, AV_PIX_FMT_YVYU422}, // YVYU Packed YUV
|
||||
{VIDEO_FORMAT_YUY2, AV_PIX_FMT_YUYV422}, // YUYV Packed YUV
|
||||
{VIDEO_FORMAT_UYVY, AV_PIX_FMT_UYVY422}, // UYVY Packed YUV
|
||||
{VIDEO_FORMAT_RGBA, AV_PIX_FMT_RGBA}, //
|
||||
{VIDEO_FORMAT_BGRA, AV_PIX_FMT_BGRA}, //
|
||||
{VIDEO_FORMAT_BGRX, AV_PIX_FMT_BGR0}, //
|
||||
{VIDEO_FORMAT_Y800, AV_PIX_FMT_GRAY8}, //
|
||||
{VIDEO_FORMAT_I444, AV_PIX_FMT_YUV444P}, //
|
||||
{VIDEO_FORMAT_BGR3, AV_PIX_FMT_BGR24}, //
|
||||
{VIDEO_FORMAT_I422, AV_PIX_FMT_YUV422P}, //
|
||||
{VIDEO_FORMAT_I40A, AV_PIX_FMT_YUVA420P}, //
|
||||
{VIDEO_FORMAT_I42A, AV_PIX_FMT_YUVA422P}, //
|
||||
{VIDEO_FORMAT_YUVA, AV_PIX_FMT_YUVA444P}, //
|
||||
//{VIDEO_FORMAT_AYUV, AV_PIX_FMT_AYUV444P}, //
|
||||
};
|
||||
|
||||
AVPixelFormat ffmpeg::tools::obs_videoformat_to_avpixelformat(video_format v)
|
||||
{
|
||||
auto found = obs_to_av_format_map.find(v);
|
||||
if (found != obs_to_av_format_map.end()) {
|
||||
return found->second;
|
||||
}
|
||||
return AV_PIX_FMT_NONE;
|
||||
}
|
||||
|
||||
video_format ffmpeg::tools::avpixelformat_to_obs_videoformat(AVPixelFormat v)
|
||||
{
|
||||
for (const auto& kv : obs_to_av_format_map) {
|
||||
if (kv.second == v)
|
||||
return kv.first;
|
||||
}
|
||||
return VIDEO_FORMAT_NONE;
|
||||
}
|
||||
|
||||
AVPixelFormat ffmpeg::tools::get_least_lossy_format(const AVPixelFormat* haystack, AVPixelFormat needle)
|
||||
{
|
||||
int data_loss = 0;
|
||||
return avcodec_find_best_pix_fmt_of_list(haystack, needle, 0, &data_loss);
|
||||
}
|
||||
|
||||
AVColorSpace ffmpeg::tools::obs_videocolorspace_to_avcolorspace(video_colorspace v)
|
||||
{
|
||||
switch (v) {
|
||||
case VIDEO_CS_DEFAULT:
|
||||
case VIDEO_CS_709:
|
||||
return AVCOL_SPC_BT709;
|
||||
case VIDEO_CS_601:
|
||||
return AVCOL_SPC_BT470BG;
|
||||
}
|
||||
throw std::invalid_argument("unknown color space");
|
||||
}
|
||||
|
||||
AVColorRange ffmpeg::tools::obs_videorangetype_to_avcolorrange(video_range_type v)
|
||||
{
|
||||
switch (v) {
|
||||
case VIDEO_RANGE_DEFAULT:
|
||||
case VIDEO_RANGE_PARTIAL:
|
||||
return AVCOL_RANGE_MPEG;
|
||||
case VIDEO_RANGE_FULL:
|
||||
return AVCOL_RANGE_JPEG;
|
||||
}
|
||||
throw std::invalid_argument("unknown range");
|
||||
}
|
||||
|
||||
bool ffmpeg::tools::can_hardware_encode(const AVCodec* codec)
|
||||
{
|
||||
AVPixelFormat hardware_formats[] = {AV_PIX_FMT_D3D11};
|
||||
|
||||
for (const AVPixelFormat* fmt = codec->pix_fmts; (fmt != nullptr) && (*fmt != AV_PIX_FMT_NONE); fmt++) {
|
||||
for (auto cmp : hardware_formats) {
|
||||
if (*fmt == cmp) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<AVPixelFormat> ffmpeg::tools::get_software_formats(const AVPixelFormat* list)
|
||||
{
|
||||
AVPixelFormat hardware_formats[] = {
|
||||
#if FF_API_VAAPI
|
||||
AV_PIX_FMT_VAAPI_MOCO,
|
||||
AV_PIX_FMT_VAAPI_IDCT,
|
||||
#endif
|
||||
AV_PIX_FMT_VAAPI,
|
||||
AV_PIX_FMT_DXVA2_VLD,
|
||||
AV_PIX_FMT_VDPAU,
|
||||
AV_PIX_FMT_QSV,
|
||||
AV_PIX_FMT_MMAL,
|
||||
AV_PIX_FMT_D3D11VA_VLD,
|
||||
AV_PIX_FMT_CUDA,
|
||||
AV_PIX_FMT_XVMC,
|
||||
AV_PIX_FMT_VIDEOTOOLBOX,
|
||||
AV_PIX_FMT_MEDIACODEC,
|
||||
AV_PIX_FMT_D3D11,
|
||||
AV_PIX_FMT_OPENCL,
|
||||
};
|
||||
|
||||
std::vector<AVPixelFormat> fmts;
|
||||
for (auto fmt = list; fmt && (*fmt != AV_PIX_FMT_NONE); fmt++) {
|
||||
bool is_blacklisted = false;
|
||||
for (auto blacklisted : hardware_formats) {
|
||||
if (*fmt == blacklisted)
|
||||
is_blacklisted = true;
|
||||
}
|
||||
if (!is_blacklisted)
|
||||
fmts.push_back(*fmt);
|
||||
}
|
||||
|
||||
fmts.push_back(AV_PIX_FMT_NONE);
|
||||
|
||||
return std::move(fmts);
|
||||
}
|
||||
|
||||
void ffmpeg::tools::setup_obs_color(video_colorspace colorspace, video_range_type range, AVCodecContext* context)
|
||||
{
|
||||
std::map<video_colorspace, std::tuple<AVColorSpace, AVColorPrimaries, AVColorTransferCharacteristic>>
|
||||
colorspaces = {
|
||||
{VIDEO_CS_DEFAULT, {AVCOL_SPC_BT470BG, AVCOL_PRI_BT470BG, AVCOL_TRC_SMPTE170M}},
|
||||
{VIDEO_CS_601, {AVCOL_SPC_BT470BG, AVCOL_PRI_BT470BG, AVCOL_TRC_SMPTE170M}},
|
||||
{VIDEO_CS_709, {AVCOL_SPC_BT709, AVCOL_PRI_BT709, AVCOL_TRC_BT709}},
|
||||
};
|
||||
std::map<video_range_type, AVColorRange> colorranges = {
|
||||
{VIDEO_RANGE_DEFAULT, AVCOL_RANGE_MPEG},
|
||||
{VIDEO_RANGE_PARTIAL, AVCOL_RANGE_MPEG},
|
||||
{VIDEO_RANGE_FULL, AVCOL_RANGE_JPEG},
|
||||
};
|
||||
|
||||
{
|
||||
auto found = colorspaces.find(colorspace);
|
||||
if (found != colorspaces.end()) {
|
||||
context->colorspace = std::get<AVColorSpace>(found->second);
|
||||
context->color_primaries = std::get<AVColorPrimaries>(found->second);
|
||||
context->color_trc = std::get<AVColorTransferCharacteristic>(found->second);
|
||||
}
|
||||
}
|
||||
{
|
||||
auto found = colorranges.find(range);
|
||||
if (found != colorranges.end()) {
|
||||
context->color_range = found->second;
|
||||
}
|
||||
}
|
||||
|
||||
// Downscaling should result in downscaling, not pixelation
|
||||
context->chroma_sample_location = AVCHROMA_LOC_CENTER;
|
||||
}
|
||||
|
||||
const char* ffmpeg::tools::get_std_compliance_name(int compliance)
|
||||
{
|
||||
switch (compliance) {
|
||||
case FF_COMPLIANCE_VERY_STRICT:
|
||||
return "Very Strict";
|
||||
case FF_COMPLIANCE_STRICT:
|
||||
return "Strict";
|
||||
case FF_COMPLIANCE_NORMAL:
|
||||
return "Normal";
|
||||
case FF_COMPLIANCE_UNOFFICIAL:
|
||||
return "Unofficial";
|
||||
case FF_COMPLIANCE_EXPERIMENTAL:
|
||||
return "Experimental";
|
||||
}
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
const char* ffmpeg::tools::get_thread_type_name(int thread_type)
|
||||
{
|
||||
switch (thread_type) {
|
||||
case FF_THREAD_FRAME | FF_THREAD_SLICE:
|
||||
return "Slice & Frame";
|
||||
case FF_THREAD_FRAME:
|
||||
return "Frame";
|
||||
case FF_THREAD_SLICE:
|
||||
return "Slice";
|
||||
default:
|
||||
return "None";
|
||||
}
|
||||
}
|
||||
|
||||
void ffmpeg::tools::print_av_option_bool(AVCodecContext* context, const char* option, std::string text)
|
||||
{
|
||||
if (av_opt_is_set_to_default_by_name(context, option, AV_OPT_SEARCH_CHILDREN) == 0) {
|
||||
int64_t v = 0;
|
||||
if (av_opt_get_int(context, option, AV_OPT_SEARCH_CHILDREN, &v) == 0) {
|
||||
PLOG_INFO("[%s] %s: %s", context->codec->name, text.c_str(), v == 0 ? "Disabled" : "Enabled");
|
||||
} else {
|
||||
PLOG_INFO("[%s] %s: <Error>", context->codec->name, text.c_str());
|
||||
}
|
||||
} else {
|
||||
PLOG_INFO("[%s] %s: <Default>", context->codec->name, text.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void ffmpeg::tools::print_av_option_int(AVCodecContext* context, const char* option, std::string text,
|
||||
std::string suffix)
|
||||
{
|
||||
if (av_opt_is_set_to_default_by_name(context, option, AV_OPT_SEARCH_CHILDREN) == 0) {
|
||||
int64_t v = 0;
|
||||
if (av_opt_get_int(context, option, AV_OPT_SEARCH_CHILDREN, &v) == 0) {
|
||||
PLOG_INFO("[%s] %s: %lld %s", context->codec->name, text.c_str(), v, suffix.c_str());
|
||||
} else {
|
||||
PLOG_INFO("[%s] %s: <Error>", context->codec->name, text.c_str());
|
||||
}
|
||||
} else {
|
||||
PLOG_INFO("[%s] %s: <Default>", context->codec->name, text.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void ffmpeg::tools::print_av_option_string(AVCodecContext* context, const char* option, std::string text,
|
||||
std::function<std::string(int64_t)> decoder)
|
||||
{
|
||||
if (av_opt_is_set_to_default_by_name(context, option, AV_OPT_SEARCH_CHILDREN) == 0) {
|
||||
int64_t v = 0;
|
||||
if (av_opt_get_int(context, option, AV_OPT_SEARCH_CHILDREN, &v) == 0) {
|
||||
std::string name = "<Unknown>";
|
||||
if (decoder)
|
||||
name = decoder(v);
|
||||
PLOG_INFO("[%s] %s: %s", context->codec->name, text.c_str(), name.c_str());
|
||||
} else {
|
||||
PLOG_INFO("[%s] %s: <Error>", context->codec->name, text.c_str());
|
||||
}
|
||||
} else {
|
||||
PLOG_INFO("[%s] %s: <Default>", context->codec->name, text.c_str());
|
||||
}
|
||||
}
|
77
source/ffmpeg/tools.hpp
Normal file
77
source/ffmpeg/tools.hpp
Normal file
|
@ -0,0 +1,77 @@
|
|||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef OBS_FFMPEG_FFMPEG_UTILITY
|
||||
#define OBS_FFMPEG_FFMPEG_UTILITY
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <obs.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/pixfmt.h>
|
||||
}
|
||||
|
||||
namespace ffmpeg {
|
||||
namespace tools {
|
||||
std::string translate_encoder_capabilities(int capabilities);
|
||||
|
||||
const char* get_pixel_format_name(AVPixelFormat v);
|
||||
|
||||
const char* get_color_space_name(AVColorSpace v);
|
||||
|
||||
const char* get_error_description(int error);
|
||||
|
||||
AVPixelFormat obs_videoformat_to_avpixelformat(video_format v);
|
||||
|
||||
video_format avpixelformat_to_obs_videoformat(AVPixelFormat v);
|
||||
|
||||
AVPixelFormat get_least_lossy_format(const AVPixelFormat* haystack, AVPixelFormat needle);
|
||||
|
||||
AVColorSpace obs_videocolorspace_to_avcolorspace(video_colorspace v);
|
||||
|
||||
AVColorRange obs_videorangetype_to_avcolorrange(video_range_type v);
|
||||
|
||||
bool can_hardware_encode(const AVCodec* codec);
|
||||
|
||||
std::vector<AVPixelFormat> get_software_formats(const AVPixelFormat* list);
|
||||
|
||||
void setup_obs_color(video_colorspace colorspace, video_range_type range, AVCodecContext* context);
|
||||
|
||||
const char* get_std_compliance_name(int compliance);
|
||||
|
||||
const char* get_thread_type_name(int thread_type);
|
||||
|
||||
void print_av_option_bool(AVCodecContext* context, const char* option, std::string text);
|
||||
|
||||
void print_av_option_int(AVCodecContext* context, const char* option, std::string text,
|
||||
std::string suffix);
|
||||
|
||||
void print_av_option_string(AVCodecContext* context, const char* option, std::string text,
|
||||
std::function<std::string(int64_t)> decoder);
|
||||
|
||||
} // namespace tools
|
||||
} // namespace ffmpeg
|
||||
|
||||
#endif OBS_FFMPEG_FFMPEG_UTILITY
|
|
@ -1,30 +0,0 @@
|
|||
@ECHO OFF
|
||||
SETLOCAL EnableDelayedExpansion
|
||||
mkdir %~dp0\..\build
|
||||
CD /D %~dp0\..\build
|
||||
|
||||
SET "DEPSPATH=%CD%/deps/obs"
|
||||
|
||||
:: Compilers
|
||||
SET COMPILER#=0
|
||||
SET /A COMPILER#=COMPILER#+1
|
||||
SET "COMPILER[%COMPILER#%]=Visual Studio 15 2017"
|
||||
SET "TOOLSET[%COMPILER#%]="
|
||||
SET "PATH[%COMPILER#%]=%CD%/vs2017-32"
|
||||
SET "DISTRIB[%COMPILER#%]=%CD%/vs2017-distrib"
|
||||
SET "SYSTEM[%COMPILER#%]=win32"
|
||||
SET /A COMPILER#=COMPILER#+1
|
||||
SET "COMPILER[%COMPILER#%]=Visual Studio 15 2017 Win64"
|
||||
SET "TOOLSET[%COMPILER#%]=host=x64"
|
||||
SET "PATH[%COMPILER#%]=%CD%/vs2017-64"
|
||||
SET "DISTRIB[%COMPILER#%]=%CD%/vs2017-distrib"
|
||||
SET "SYSTEM[%COMPILER#%]=win64"
|
||||
|
||||
FOR /L %%i IN (1,1,%COMPILER#%) DO (
|
||||
ECHO -- BUILD FOR "!COMPILER[%%i]!" --
|
||||
mkdir "!PATH[%%i]!"
|
||||
pushd "!PATH[%%i]!"
|
||||
cmake -G "!COMPILER[%%i]!" -T "!TOOLSET[%%i]!" -DCMAKE_INSTALL_PREFIX="!DISTRIB[%%i]!" -DCPACK_SYSTEM_NAME="!SYSTEM[%%i]!" ../../
|
||||
popd
|
||||
)
|
||||
PAUSE
|
Loading…
Reference in a new issue