├── .clang-format ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── feature.md │ ├── feedback.md │ └── issue.md ├── pull_request_template.md └── workflows │ └── main.yml ├── .gitignore ├── CMakeLists.txt ├── CONTRIBUTING.md ├── LICENSE ├── README.MD ├── ci ├── builder.js ├── packager.js └── runner.js ├── cmake ├── DownloadProject.CMakeLists.cmake.in ├── DownloadProject.cmake ├── cppcheck.cmake ├── installer.iss.in ├── module.cpp.in ├── modules │ └── FindFFmpeg.cmake ├── util.cmake ├── version.hpp.in └── version.rc.in ├── data └── locale │ └── en-US.ini └── source ├── codecs ├── h264.cpp ├── h264.hpp ├── hevc.cpp ├── hevc.hpp ├── prores.cpp └── prores.hpp ├── encoder.cpp ├── encoder.hpp ├── ffmpeg ├── avframe-queue.cpp ├── avframe-queue.hpp ├── swscale.cpp ├── swscale.hpp ├── tools.cpp └── tools.hpp ├── hwapi ├── base.cpp ├── base.hpp ├── d3d11.cpp └── d3d11.hpp ├── plugin.cpp ├── plugin.hpp ├── strings.hpp ├── ui ├── debug_handler.cpp ├── debug_handler.hpp ├── handler.cpp ├── handler.hpp ├── nvenc_h264_handler.cpp ├── nvenc_h264_handler.hpp ├── nvenc_hevc_handler.cpp ├── nvenc_hevc_handler.hpp ├── nvenc_shared.cpp ├── nvenc_shared.hpp ├── prores_aw_handler.cpp └── prores_aw_handler.hpp ├── utility.cpp └── utility.hpp /.clang-format: -------------------------------------------------------------------------------- 1 | # Basic Formatting 2 | TabWidth: 8 3 | UseTab: ForIndentation 4 | ColumnLimit: 120 5 | 6 | # Language 7 | Language: Cpp 8 | Standard: Cpp11 9 | 10 | # Indentation 11 | AccessModifierOffset: 0 12 | ConstructorInitializerIndentWidth: 4 13 | ContinuationIndentWidth: 4 14 | IndentCaseLabels: false 15 | #IndentPPDirectives: true 16 | IndentWidth: 8 17 | IndentWrappedFunctionNames: true 18 | NamespaceIndentation: All 19 | 20 | # Includes 21 | #IncludeBlocks: Regroup 22 | IncludeCategories: 23 | - Regex: '^<' 24 | Priority: 1 25 | - Regex: '^"' 26 | Priority: 2 27 | SortIncludes: true 28 | 29 | # Alignment 30 | AlignAfterOpenBracket: true 31 | AlignConsecutiveAssignments: true 32 | AlignConsecutiveDeclarations: true 33 | AlignEscapedNewlines: DontAlign 34 | AlignOperands: true 35 | AlignTrailingComments: true 36 | DerivePointerAlignment: false 37 | PointerAlignment: Left 38 | 39 | # Wrapping and Breaking 40 | AllowAllParametersOfDeclarationOnNextLine: true 41 | AllowShortBlocksOnASingleLine: false 42 | AllowShortCaseLabelsOnASingleLine: false 43 | AllowShortFunctionsOnASingleLine: Empty 44 | AllowShortIfStatementsOnASingleLine: false 45 | AllowShortLoopsOnASingleLine: false 46 | AlwaysBreakAfterReturnType: None 47 | AlwaysBreakBeforeMultilineStrings: true 48 | AlwaysBreakTemplateDeclarations: true 49 | BraceWrapping: 50 | AfterClass: false 51 | AfterControlStatement: false 52 | AfterEnum: false 53 | # AfterExternBlock: false 54 | AfterFunction: true 55 | AfterNamespace: false 56 | AfterStruct: false 57 | AfterUnion: false 58 | BeforeCatch: false 59 | BeforeElse: false 60 | SplitEmptyFunction: false 61 | SplitEmptyRecord: false 62 | SplitEmptyNamespace: false 63 | BinPackArguments: true 64 | BinPackParameters: true 65 | BreakBeforeBinaryOperators: NonAssignment 66 | BreakBeforeBraces: Custom 67 | BreakBeforeTernaryOperators: true 68 | BreakConstructorInitializers: BeforeColon 69 | #BreakInheritanceList: BeforeColon 70 | BreakStringLiterals: false 71 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 72 | Cpp11BracedListStyle: true 73 | 74 | # Spaces 75 | SpaceAfterCStyleCast: false 76 | SpaceAfterTemplateKeyword: false 77 | SpaceBeforeAssignmentOperators: true 78 | #SpaceBeforeCpp11BracedList: false 79 | #SpaceBeforeCtorInitializerColon: true 80 | #SpaceBeforeInheritanceColon: true 81 | SpaceBeforeParens: ControlStatements 82 | #SpaceBeforeRangeBasedForLoopColon: true 83 | SpaceInEmptyParentheses: false 84 | SpacesBeforeTrailingComments: 1 85 | SpacesInAngles: false 86 | SpacesInCStyleCastParentheses: false 87 | SpacesInContainerLiterals: false 88 | SpacesInParentheses: false 89 | SpacesInSquareBrackets: false 90 | 91 | # Other 92 | CommentPragmas: '^(!FIXME!|!TODO!|ToDo:)' 93 | CompactNamespaces: false 94 | DisableFormat: false 95 | FixNamespaceComments: true 96 | #ForEachMacros: '' 97 | KeepEmptyLinesAtTheStartOfBlocks: false 98 | ReflowComments: false 99 | SortUsingDeclarations: true 100 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | issuehunt: xaymar 2 | patreon: xaymar 3 | custom: https://www.paypal.me/xaymar -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Want a new feature implemented? 4 | 5 | --- 6 | 7 | 8 | 9 | 10 | ### Description 11 | 12 | 13 | 14 | ### Checklist: 15 | 16 | 17 | - [ ] I have read the contribution guidelines. 18 | - [ ] This feature is necessary and can't be done through other means. 19 | - [ ] This feature does not break existing functionality. 20 | - [ ] I am willing to hire/pay someone to implement this. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feedback.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question/Feedback 3 | about: Submit a question or some feedback! (Not for bugs, issues or crashes!) 4 | 5 | --- 6 | 7 | 8 | 9 | 10 | ### Description 11 | 12 | 13 | 14 | ### System Information 15 | 16 | - Software Version: [e. g. 1.0.0, 1.2.1, ... - NEVER LATEST] 17 | - Operating System: [e. g. Windows, Debian, Ubuntu, RedHat, FreeBSD, ...] 18 | - Kernel Version: [e. g. 1903/1809 (Windows), 4.12 (Linux), ...] 19 | - CPU: [e. g. Intel i7, AMD Zen2, Qualcomm, ...] 20 | - GPU: [e. g. Nvidia RTX 2xxx Series, AMD Radeone 5xxx Series, ...] 21 | - RAM: [e. g. 16GB, 32GB, 64GB, ...] 22 | 23 | ### Checklist: 24 | 25 | 26 | - [ ] I have read the contribution guidelines. 27 | - [ ] This is not a bug, crash or generic issue. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Issue/Bug report 3 | about: Encountered a problem, a bug or a crash? 4 | 5 | --- 6 | 7 | 8 | 9 | 10 | ### Description 11 | 12 | 13 | 14 | ### Expected Behavior 15 | 16 | 17 | ### Reproduction Steps 18 | 19 | 20 | 1. Open a portal to the Infinite Void 21 | 2. Summon an ancient God 22 | 3. Realize your mistake 23 | 24 | ### System Information 25 | 26 | - Software Version: [e. g. 1.0.0, 1.2.1, ... - NEVER LATEST] 27 | - Operating System: [e. g. Windows, Debian, Ubuntu, RedHat, FreeBSD, ...] 28 | - Kernel Version: [e. g. 1903/1809 (Windows), 4.12 (Linux), ...] 29 | - CPU: [e. g. Intel i7, AMD Zen2, Qualcomm, ...] 30 | - GPU: [e. g. Nvidia RTX 2xxx Series, AMD Radeone 5xxx Series, ...] 31 | - RAM: [e. g. 16GB, 32GB, 64GB, ...] 32 | 33 | ### Checklist: 34 | 35 | 36 | - [ ] I have read the contribution guidelines. 37 | - [ ] I can reproduce the issue with the exact reproduction steps I have provided. 38 | - [ ] The issue appears on all of my systems that can run this software. 39 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ### Description 6 | 7 | 8 | 9 | ### Motivation and Context 10 | 11 | 12 | 13 | 14 | ### How Has This Been Tested? 15 | 16 | 17 | 18 | 19 | ### Types of changes 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | ### Checklist: 29 | 30 | 31 | - [ ] My code has been run through [clang-format](https://github.com/obsproject/obs-studio/blob/master/.clang-format). 32 | - [ ] I have read the [**contributing** document](https://github.com/obsproject/obs-studio/blob/master/CONTRIBUTING.rst). 33 | - [ ] My code is not on the master branch. 34 | - [ ] The code has been tested. 35 | - [ ] All commit messages are properly formatted and commits squashed where appropriate. 36 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | windows: 7 | strategy: 8 | matrix: 9 | os: [windows-2016, windows-2019] 10 | include: 11 | - os: windows-2016 12 | generator_32: "Visual Studio 15 2017" 13 | generator_64: "Visual Studio 15 2017" 14 | sysversion: "10.0.17763.0" 15 | - os: windows-2019 16 | generator_32: "Visual Studio 16 2019" 17 | generator_64: "Visual Studio 16 2019" 18 | sysversion: "10.0.18362.0" 19 | runs-on: ${{ matrix.os }} 20 | steps: 21 | - name: "Clone Repository" 22 | uses: actions/checkout@v1 23 | - name: Install Node.JS 10.x 24 | uses: actions/setup-node@v1 25 | with: 26 | node-version: 10 27 | - name: Configure & Compile 28 | env: 29 | CMAKE_GENERATOR_32: ${{ matrix.generator_32 }} 30 | CMAKE_GENERATOR_64: ${{ matrix.generator_64 }} 31 | CMAKE_SYSTEM_VERSION: ${{ matrix.sysversion }} 32 | run: node ./ci/builder.js 33 | - name: Package 34 | env: 35 | CMAKE_GENERATOR_32: ${{ matrix.generator_32 }} 36 | CMAKE_GENERATOR_64: ${{ matrix.generator_64 }} 37 | run: | 38 | mkdir build/package 39 | node ./ci/packager.js 40 | - name: "Package Installer (Prereqs)" 41 | run: | 42 | curl "-kL" "https://cdn.xaymar.com/ci/innosetup-6.0.3.exe" "-f" "--retry" "5" "-o" "inno.exe" 43 | .\inno.exe /VERYSILENT /SP- /SUPPRESSMSGBOXES /NORESTART 44 | - name: "Package Installer (Compile)" 45 | run: | 46 | & 'C:\Program Files (x86)\Inno Setup 6\ISCC.exe' /Qp ".\build\64\installer.iss" 47 | - name: "Upload Artifacts" 48 | uses: actions/upload-artifact@v1 49 | with: 50 | name: ${{ matrix.os }} 51 | path: build/package -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /build32 3 | /build64 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # FFMPEG Video Encoder Integration for OBS Studio 2 | # Copyright (c) 2019 Michael Fabian Dirks 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in all 12 | # copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | # SOFTWARE. 21 | 22 | # CMake Setup 23 | CMake_Minimum_Required(VERSION 3.8.0) 24 | Include("cmake/util.cmake") 25 | 26 | # Automatic Versioning 27 | set(VERSION_MAJOR 0) 28 | set(VERSION_MINOR 4) 29 | set(VERSION_PATCH 0) 30 | set(VERSION_TWEAK 0) 31 | set(PROJECT_COMMIT "N/A") 32 | if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/.git") 33 | set(GIT_RESULT "") 34 | set(GIT_OUTPUT "") 35 | execute_process( 36 | COMMAND git rev-list --count --topo-order ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}..HEAD 37 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} 38 | RESULT_VARIABLE GIT_RESULT 39 | OUTPUT_VARIABLE GIT_OUTPUT 40 | OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE ERROR_QUIET 41 | ) 42 | if(GIT_RESULT EQUAL 0) 43 | set(VERSION_TWEAK ${GIT_OUTPUT}) 44 | endif() 45 | execute_process( 46 | COMMAND git rev-parse HEAD 47 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} 48 | RESULT_VARIABLE GIT_RESULT 49 | OUTPUT_VARIABLE GIT_OUTPUT 50 | OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE ERROR_QUIET 51 | ) 52 | if(GIT_RESULT EQUAL 0) 53 | set(PROJECT_COMMIT ${GIT_OUTPUT}) 54 | endif() 55 | endif() 56 | 57 | # All Warnings, Extra Warnings, Pedantic 58 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 59 | # using Clang 60 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-missing-braces -Wmissing-field-initializers -Wno-c++98-compat-pedantic -Wold-style-cast -Wno-documentation -Wno-documentation-unknown-command -Wno-covered-switch-default -Wno-switch-enum") 61 | elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") 62 | # GCC: -fpermissive is required as GCC does not allow the same template to be in different namespaces. 63 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wpedantic -fpermissive -Wno-long-long -Wno-missing-braces -Wmissing-field-initializers") 64 | elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel") 65 | # using Intel C++ 66 | elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") 67 | # Force to always compile with W4 68 | if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]") 69 | string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 70 | else() 71 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") 72 | endif() 73 | endif() 74 | # C++ Standard and Extensions 75 | ## Use C++17 and no non-standard extensions. 76 | set(_CXX_STANDARD 17) 77 | set(_CXX_EXTENSIONS OFF) 78 | 79 | # Define Project 80 | project( 81 | obs-ffmpeg-encoder 82 | VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}.${VERSION_TWEAK} 83 | ) 84 | set(PROJECT_FULL_NAME "FFMPEG Encoder for OBS Studio") 85 | set(PROJECT_DESCRIPTION "FFmpeg Encoders for OBS Studio") 86 | set(PROJECT_AUTHORS "Michael Fabian 'Xaymar' Dirks ") 87 | set(PROJECT_COPYRIGHT_YEARS "2018 - 2019") 88 | 89 | ################################################################################ 90 | # Setup / Bootstrap 91 | ################################################################################ 92 | 93 | # Detect Build Type 94 | if("${CMAKE_SOURCE_DIR}" STREQUAL "${PROJECT_SOURCE_DIR}") 95 | set(PropertyPrefix "") 96 | else() 97 | set(PropertyPrefix "${PROJECT_NAME}_") 98 | endif() 99 | 100 | # Detect Architecture 101 | math(EXPR BITS "8*${CMAKE_SIZEOF_VOID_P}") 102 | if("${BITS}" STREQUAL "32") 103 | set(ARCH "x86") 104 | else() 105 | set(ARCH "x64") 106 | endif() 107 | 108 | # Search Path 109 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules/") 110 | 111 | ################################################################################ 112 | # CMake / Compiler 113 | ################################################################################ 114 | 115 | # Configure Version Header 116 | configure_file( 117 | "${PROJECT_SOURCE_DIR}/cmake/version.hpp.in" 118 | "${PROJECT_BINARY_DIR}/source/version.hpp" 119 | @ONLY 120 | ) 121 | configure_file( 122 | "${PROJECT_SOURCE_DIR}/cmake/module.cpp.in" 123 | "${PROJECT_BINARY_DIR}/source/module.cpp" 124 | @ONLY 125 | ) 126 | 127 | # Windows 128 | if (WIN32) 129 | ## Installer (InnoSetup) 130 | get_filename_component(ISS_FILES_DIR "${CMAKE_INSTALL_PREFIX}" ABSOLUTE) 131 | file(TO_NATIVE_PATH "${ISS_FILES_DIR}" ISS_FILES_DIR) 132 | get_filename_component(ISS_PACKAGE_DIR "${CMAKE_PACKAGE_PREFIX}" ABSOLUTE) 133 | file(TO_NATIVE_PATH "${ISS_PACKAGE_DIR}" ISS_PACKAGE_DIR) 134 | get_filename_component(ISS_SOURCE_DIR "${PROJECT_SOURCE_DIR}" ABSOLUTE) 135 | file(TO_NATIVE_PATH "${ISS_SOURCE_DIR}" ISS_SOURCE_DIR) 136 | configure_file( 137 | "${PROJECT_SOURCE_DIR}/cmake/installer.iss.in" 138 | "${PROJECT_BINARY_DIR}/installer.iss" 139 | @ONLY 140 | ) 141 | 142 | # Windows Specific Resource Definition 143 | set(PROJECT_PRODUCT_NAME "${PROJECT_FULL_NAME}") 144 | set(PROJECT_COMPANY_NAME "${PROJECT_AUTHORS}") 145 | set(PROJECT_COPYRIGHT "${PROJECT_AUTHORS} © ${PROJECT_COPYRIGHT_YEARS}") 146 | set(PROJECT_LEGAL_TRADEMARKS_1 "") 147 | set(PROJECT_LEGAL_TRADEMARKS_2 "") 148 | 149 | configure_file( 150 | "${PROJECT_SOURCE_DIR}/cmake/version.rc.in" 151 | "${PROJECT_BINARY_DIR}/cmake/version.rc" 152 | @ONLY 153 | ) 154 | endif() 155 | 156 | ################################################################################ 157 | # Options 158 | ################################################################################ 159 | set(${PropertyPrefix}OBS_NATIVE FALSE CACHE BOOL "Use native obs-studio build" FORCE) 160 | set(${PropertyPrefix}OBS_REFERENCE FALSE CACHE BOOL "Use referenced obs-studio build" FORCE) 161 | set(${PropertyPrefix}OBS_PACKAGE FALSE CACHE BOOL "Use packaged obs-studio build" FORCE) 162 | set(${PropertyPrefix}OBS_DOWNLOAD FALSE CACHE BOOL "Use downloaded obs-studio build" FORCE) 163 | mark_as_advanced(FORCE OBS_NATIVE OBS_PACKAGE OBS_REFERENCE OBS_DOWNLOAD) 164 | 165 | if(NOT TARGET libobs) 166 | set(${PropertyPrefix}OBS_STUDIO_DIR "" CACHE PATH "OBS Studio Source/Package Directory") 167 | set(${PropertyPrefix}OBS_DOWNLOAD_VERSION "24.0.3-ci" CACHE STRING "OBS Studio Version to download") 168 | endif() 169 | 170 | if(NOT ${PropertyPrefix}OBS_NATIVE) 171 | set(${PropertyPrefix}OBS_DEPENDENCIES_DIR "" CACHE PATH "Path to OBS Dependencies") 172 | set(CMAKE_PACKAGE_PREFIX "${CMAKE_BINARY_DIR}" CACHE PATH "Path for generated archives.") 173 | set(CMAKE_PACKAGE_NAME "${PROJECT_NAME}" CACHE STRING "Name for the generated archives.") 174 | set(CMAKE_PACKAGE_SUFFIX_OVERRIDE "" CACHE STRING "Override for the suffix.") 175 | endif() 176 | 177 | ################################################################################ 178 | # Dependencies 179 | ################################################################################ 180 | 181 | # Detect OBS Studio Type 182 | if(TARGET libobs) 183 | message(STATUS "${PROJECT_NAME}: Using native obs-studio.") 184 | CacheSet(${PropertyPrefix}OBS_NATIVE TRUE) 185 | else() 186 | CacheSet(${PropertyPrefix}OBS_NATIVE FALSE) 187 | if(EXISTS "${OBS_STUDIO_DIR}/cmake/LibObs/LibObsConfig.cmake") 188 | message(STATUS "${PROJECT_NAME}: Using packaged obs-studio.") 189 | CacheSet(${PropertyPrefix}OBS_PACKAGE TRUE) 190 | elseif(EXISTS "${OBS_STUDIO_DIR}/libobs/obs-module.h") 191 | message(STATUS "${PROJECT_NAME}: Using referenced obs-studio.") 192 | CacheSet(${PropertyPrefix}OBS_REFERENCE TRUE) 193 | else() 194 | message(STATUS "${PROJECT_NAME}: No OBS Studio detected, using downloadable prebuilt binaries.") 195 | CacheSet(${PropertyPrefix}OBS_DOWNLOAD TRUE) 196 | 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") 197 | endif() 198 | endif() 199 | 200 | # CMake Modules 201 | if(${PropertyPrefix}OBS_DOWNLOAD) 202 | include("cmake/DownloadProject.cmake") 203 | endif() 204 | 205 | # Load OBS Studio 206 | if(${PropertyPrefix}OBS_NATIVE) 207 | elseif(${PropertyPrefix}OBS_PACKAGE) 208 | include("${OBS_STUDIO_DIR}/cmake/LibObs/LibObsConfig.cmake") 209 | elseif(${PropertyPrefix}OBS_REFERENCE) 210 | set(obsPath "${OBS_STUDIO_DIR}") 211 | include("${OBS_STUDIO_DIR}/cmake/external/Findlibobs.cmake") 212 | elseif(${PropertyPrefix}OBS_DOWNLOAD) 213 | download_project( 214 | PROJ libobs 215 | URL ${OBS_DOWNLOAD_URL} 216 | UPDATE_DISCONNECTED 1 217 | ) 218 | include("${libobs_SOURCE_DIR}/cmake/LibObs/LibObsConfig.cmake") 219 | else() 220 | message(CRITICAL "Impossible case reached, verify system stability.") 221 | return() 222 | endif() 223 | 224 | # FFmpeg (compatible with OBS Studio) 225 | if(NOT ${PropertyPrefix}OBS_NATIVE) 226 | if(WIN32) 227 | if(NOT IS_DIRECTORY FFmpegPath) 228 | if(IS_DIRECTORY "${${PropertyPrefix}OBS_DEPENDENCIES_DIR}") 229 | set(FFmpegPath "${${PropertyPrefix}OBS_DEPENDENCIES_DIR}/win${BITS}/") 230 | else() 231 | download_project( 232 | PROJ obsdeps 233 | URL "https://obsproject.com/downloads/dependencies2017.zip" 234 | UPDATE_DISCONNECTED 1 235 | ) 236 | set(FFmpegPath "${obsdeps_SOURCE_DIR}/win${BITS}/") 237 | endif() 238 | endif() 239 | 240 | set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /SAFESEH:NO") 241 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /SAFESEH:NO") 242 | endif() 243 | endif() 244 | find_package(FFmpeg REQUIRED COMPONENTS avutil avcodec swscale) 245 | 246 | ################################################################################ 247 | # Code 248 | ################################################################################ 249 | set(PROJECT_DATA 250 | "${PROJECT_SOURCE_DIR}/data/locale/en-US.ini" 251 | "${PROJECT_SOURCE_DIR}/LICENSE" 252 | ) 253 | 254 | set(PROJECT_LIBRARIES 255 | ) 256 | 257 | set(PROJECT_TEMPLATES 258 | "${PROJECT_SOURCE_DIR}/cmake/version.hpp.in" 259 | "${PROJECT_SOURCE_DIR}/cmake/module.cpp.in" 260 | ) 261 | if(WIN32) 262 | list(APPEND PROJECT_TEMPLATES 263 | "${PROJECT_SOURCE_DIR}/cmake/installer.iss.in" 264 | "${PROJECT_SOURCE_DIR}/cmake/version.rc.in" 265 | ) 266 | endif() 267 | 268 | set(PROJECT_GENERATED 269 | "${PROJECT_BINARY_DIR}/source/module.cpp" 270 | "${PROJECT_BINARY_DIR}/source/version.hpp" 271 | ) 272 | set(PROJECT_PRIVATE 273 | "${PROJECT_SOURCE_DIR}/source/encoder.hpp" 274 | "${PROJECT_SOURCE_DIR}/source/encoder.cpp" 275 | "${PROJECT_SOURCE_DIR}/source/plugin.cpp" 276 | "${PROJECT_SOURCE_DIR}/source/plugin.hpp" 277 | "${PROJECT_SOURCE_DIR}/source/utility.cpp" 278 | "${PROJECT_SOURCE_DIR}/source/utility.hpp" 279 | "${PROJECT_SOURCE_DIR}/source/strings.hpp" 280 | "${PROJECT_SOURCE_DIR}/source/codecs/hevc.hpp" 281 | "${PROJECT_SOURCE_DIR}/source/codecs/hevc.cpp" 282 | "${PROJECT_SOURCE_DIR}/source/codecs/h264.hpp" 283 | "${PROJECT_SOURCE_DIR}/source/codecs/h264.cpp" 284 | "${PROJECT_SOURCE_DIR}/source/codecs/prores.hpp" 285 | "${PROJECT_SOURCE_DIR}/source/codecs/prores.cpp" 286 | "${PROJECT_SOURCE_DIR}/source/ffmpeg/avframe-queue.cpp" 287 | "${PROJECT_SOURCE_DIR}/source/ffmpeg/avframe-queue.hpp" 288 | "${PROJECT_SOURCE_DIR}/source/ffmpeg/swscale.hpp" 289 | "${PROJECT_SOURCE_DIR}/source/ffmpeg/swscale.cpp" 290 | "${PROJECT_SOURCE_DIR}/source/ffmpeg/tools.hpp" 291 | "${PROJECT_SOURCE_DIR}/source/ffmpeg/tools.cpp" 292 | "${PROJECT_SOURCE_DIR}/source/hwapi/base.hpp" 293 | "${PROJECT_SOURCE_DIR}/source/hwapi/base.cpp" 294 | "${PROJECT_SOURCE_DIR}/source/ui/handler.hpp" 295 | "${PROJECT_SOURCE_DIR}/source/ui/handler.cpp" 296 | "${PROJECT_SOURCE_DIR}/source/ui/debug_handler.hpp" 297 | "${PROJECT_SOURCE_DIR}/source/ui/debug_handler.cpp" 298 | "${PROJECT_SOURCE_DIR}/source/ui/prores_aw_handler.hpp" 299 | "${PROJECT_SOURCE_DIR}/source/ui/prores_aw_handler.cpp" 300 | "${PROJECT_SOURCE_DIR}/source/ui/nvenc_shared.hpp" 301 | "${PROJECT_SOURCE_DIR}/source/ui/nvenc_shared.cpp" 302 | "${PROJECT_SOURCE_DIR}/source/ui/nvenc_h264_handler.hpp" 303 | "${PROJECT_SOURCE_DIR}/source/ui/nvenc_h264_handler.cpp" 304 | "${PROJECT_SOURCE_DIR}/source/ui/nvenc_hevc_handler.hpp" 305 | "${PROJECT_SOURCE_DIR}/source/ui/nvenc_hevc_handler.cpp" 306 | ) 307 | if(WIN32) 308 | list(APPEND PROJECT_PRIVATE 309 | "${PROJECT_SOURCE_DIR}/source/hwapi/d3d11.hpp" 310 | "${PROJECT_SOURCE_DIR}/source/hwapi/d3d11.cpp" 311 | ) 312 | endif() 313 | 314 | # Source Grouping 315 | source_group(TREE "${PROJECT_SOURCE_DIR}" PREFIX "Data Files" FILES ${PROJECT_DATA}) 316 | source_group(TREE "${PROJECT_BINARY_DIR}/source" PREFIX "Generated Files" FILES ${PROJECT_GENERATED}) 317 | source_group(TREE "${PROJECT_SOURCE_DIR}/cmake" PREFIX "Template Files" FILES ${PROJECT_TEMPLATES}) 318 | 319 | # Filter Sources 320 | set(_TMP_SOURCE ${PROJECT_PRIVATE}) 321 | list(FILTER _TMP_SOURCE INCLUDE REGEX "\.(c|cpp)$") 322 | source_group(TREE "${PROJECT_SOURCE_DIR}/source" PREFIX "Source Files" FILES ${_TMP_SOURCE}) 323 | 324 | # Filter Headers 325 | set(_TMP_HEADER ${PROJECT_PRIVATE}) 326 | list(FILTER _TMP_HEADER INCLUDE REGEX "\.(h|hpp)$") 327 | source_group(TREE "${PROJECT_SOURCE_DIR}/source" PREFIX "Header Files" FILES ${_TMP_HEADER}) 328 | 329 | ################################################################################ 330 | # Target 331 | ################################################################################ 332 | 333 | add_library(${PROJECT_NAME} MODULE 334 | ${PROJECT_GENERATED} 335 | ${PROJECT_PRIVATE} 336 | ${PROJECT_DATA} 337 | ${PROJECT_TEMPLATES} 338 | ) 339 | 340 | # Include Directories 341 | target_include_directories(${PROJECT_NAME} 342 | PUBLIC 343 | PRIVATE 344 | "${PROJECT_BINARY_DIR}/source" 345 | "${PROJECT_SOURCE_DIR}/source" 346 | ${FFMPEG_INCLUDE_DIRS} 347 | ) 348 | 349 | # OBS Studio 350 | if(${PropertyPrefix}OBS_NATIVE) 351 | target_link_libraries(${PROJECT_NAME} 352 | libobs 353 | ) 354 | elseif(${PropertyPrefix}OBS_REFERENCE) 355 | target_include_directories(${PROJECT_NAME} 356 | PRIVATE 357 | "${OBS_STUDIO_DIR}/libobs" 358 | ) 359 | target_link_libraries(${PROJECT_NAME} 360 | "${LIBOBS_LIB}" 361 | ) 362 | elseif(${PropertyPrefix}OBS_PACKAGE) 363 | target_include_directories(${PROJECT_NAME} 364 | PRIVATE 365 | "${OBS_STUDIO_DIR}/include" 366 | ) 367 | target_link_libraries(${PROJECT_NAME} 368 | libobs 369 | ) 370 | elseif(${PropertyPrefix}OBS_DOWNLOAD) 371 | target_link_libraries(${PROJECT_NAME} 372 | libobs 373 | ) 374 | endif() 375 | 376 | # Link Libraries 377 | target_link_libraries(${PROJECT_NAME} 378 | ${PROJECT_LIBRARIES} 379 | ${FFMPEG_LIBRARIES} 380 | ) 381 | 382 | # Definitions 383 | if (WIN32) 384 | target_compile_definitions(${PROJECT_NAME} 385 | PRIVATE 386 | _CRT_SECURE_NO_WARNINGS 387 | # windows.h 388 | WIN32_LEAN_AND_MEAN 389 | NOGPICAPMASKS 390 | NOVIRTUALKEYCODES 391 | #NOWINMESSAGES 392 | NOWINSTYLES 393 | NOSYSMETRICS 394 | NOMENUS 395 | NOICONS 396 | NOKEYSTATES 397 | NOSYSCOMMANDS 398 | NORASTEROPS 399 | NOSHOWWINDOW 400 | NOATOM 401 | NOCLIPBOARD 402 | NOCOLOR 403 | NOCTLMGR 404 | NODRAWTEXT 405 | #NOGDI 406 | NOKERNEL 407 | #NOUSER 408 | #NONLS 409 | NOMB 410 | NOMEMMGR 411 | NOMETAFILE 412 | NOMINMAX 413 | #NOMSG 414 | NOOPENFILE 415 | NOSCROLL 416 | NOSERVICE 417 | NOSOUND 418 | #NOTEXTMETRIC 419 | NOWH 420 | NOWINOFFSETS 421 | NOCOMM 422 | NOKANJI 423 | NOHELP 424 | NOPROFILER 425 | NODEFERWINDOWPOS 426 | NOMCX 427 | NOIME 428 | NOMDI 429 | NOINOUT 430 | ) 431 | endif() 432 | 433 | # C++ Standard and Extensions 434 | set_target_properties( 435 | ${PROJECT_NAME} 436 | PROPERTIES 437 | CXX_STANDARD ${_CXX_STANDARD} 438 | CXX_EXTENSIONS ${_CXX_EXTENSIONS} 439 | ) 440 | 441 | # File Version 442 | if(WIN32) 443 | set_target_properties( 444 | ${PROJECT_NAME} 445 | PROPERTIES 446 | VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}.${PROJECT_VERSION_TWEAK} 447 | SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}.${PROJECT_VERSION_TWEAK} 448 | ) 449 | else() 450 | set_target_properties( 451 | ${PROJECT_NAME} 452 | PROPERTIES 453 | VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}.${PROJECT_VERSION_TWEAK} 454 | SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}.${PROJECT_VERSION_TWEAK} 455 | ) 456 | endif() 457 | 458 | ################################################################################ 459 | # Installation 460 | ################################################################################ 461 | 462 | if(${PropertyPrefix}OBS_NATIVE) 463 | install_obs_plugin_with_data(${PROJECT_NAME} data) 464 | else() 465 | install( 466 | TARGETS ${PROJECT_NAME} 467 | RUNTIME DESTINATION "./obs-plugins/${BITS}bit/" COMPONENT Runtime 468 | LIBRARY DESTINATION "./obs-plugins/${BITS}bit/" COMPONENT Runtime 469 | ) 470 | if(MSVC) 471 | install( 472 | FILES $ 473 | DESTINATION "./obs-plugins/${BITS}bit/" 474 | OPTIONAL 475 | ) 476 | endif() 477 | 478 | install( 479 | DIRECTORY "${PROJECT_SOURCE_DIR}/data/" 480 | DESTINATION "./data/obs-plugins/${PROJECT_NAME}/" 481 | ) 482 | 483 | if("${CMAKE_PACKAGE_SUFFIX_OVERRIDE}" STREQUAL "") 484 | set(PackageFullName "${CMAKE_PACKAGE_PREFIX}/${CMAKE_PACKAGE_NAME}-${PROJECT_VERSION}") 485 | else() 486 | set(PackageFullName "${CMAKE_PACKAGE_PREFIX}/${CMAKE_PACKAGE_NAME}-${CMAKE_PACKAGE_SUFFIX_OVERRIDE}") 487 | endif() 488 | 489 | add_custom_target( 490 | PACKAGE_7Z 491 | ${CMAKE_COMMAND} -E tar cfv "${PackageFullName}.7z" --format=7zip -- 492 | "${CMAKE_INSTALL_PREFIX}/obs-plugins" 493 | "${CMAKE_INSTALL_PREFIX}/data" 494 | WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}" 495 | ) 496 | add_custom_target( 497 | PACKAGE_ZIP 498 | ${CMAKE_COMMAND} -E tar cfv "${PackageFullName}.zip" --format=zip -- 499 | "${CMAKE_INSTALL_PREFIX}/obs-plugins" 500 | "${CMAKE_INSTALL_PREFIX}/data" 501 | WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}" 502 | ) 503 | endif() 504 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Build Instructions 2 | The project is compatible with being included directly in an obs-studio build, but can be built standalone. Depending on which you chose, the instructions differ greatly. 3 | 4 | ## Building with OBS Studio 5 | 1. Prepare OBS Studio for building on your platform. 6 | 2. Navigate to /plugins in a git-enabled shell. 7 | 3. Run `git submodule add https://github.com/Xaymar/obs-ffmpeg-encoder.git obs-ffmpeg-encoder` and wait for it to complete. 8 | 4. Edit the CMakeLists.txt in the same directory and append `add_subdirectory(obs-ffmpeg-encoder)`. 9 | 5. Use CMake to reconfigure OBS Studio, at which point it should detect the new plugin and allow you to build it. 10 | 6. Done. 11 | 12 | ## Building Standalone 13 | This mode of building is used by CI providers and offers the fastest compile time, but does not allow quick testing as it requires a working OBS Studio installation to test. 14 | 15 | 1. Install CMake (3.1 or newer) and if you aren't using Windows, build the PACKAGE target in OBS Studio, which will generate a CPack archive in the build folder. 16 | 2. Clone the obs-ffmpeg-encoder repository using git or a comparable tool. 17 | 3. Configure obs-ffmpeg-encoder using CMake to build a subdirectory or any other location that is not the same as the source directory, and adjust the following options to suit your needs: 18 | - OBS_STUDIO_DIR: Path to a normal or CPack OBS Studio build. 19 | - OBS_DOWNLOAD_VERSION: The version to attempt to automatically download and use if no OBS Studio is detected (may not work on all platforms). 20 | - CMAKE_INSTALL_PREFIX: Path that the INSTALL target copies files to. 21 | - CMAKE_PACKAGE_PREFIX: Path for the archives generated by PACKAGE_ZIP and PACKAGE_7Z. 22 | - CMAKE_PACKAGE_NAME: Name for the archives generated by PACKAGE_ZIP and PACKAGE_7Z. 23 | - CMAKE_PACKAGE_SUFFIX_OVERRIDE: 24 | 4. Build obs-ffmpeg-encoder. 25 | 26 | ### Installing into a local OBS Studio Installation 27 | Modify CMAKE_INSTALL_PREFIX to point at your OBS Studio installation directory (not the data directory) and build the INSTALL target. 28 | 29 | ### Packaging Release Archives 30 | Generate release archives by building either PACKAGE_ZIP or PACKAGE_7Z. To generate a full release archive, CMAKE_INSTALL_PREFIX has to be set to a directory that only contains release files for this plugin. See the CI scripts for an example on this. 31 | 32 | # Commits 33 | Commits should always only focus on a single change that is necessary for that commit to work. For example, a commit that changes how something logs messages should not also include a new blur effect. In those cases, the commit should be split up into two, so that they can be reverted independently from another. 34 | 35 | ## Commit Subject 36 | The subject line of a commit should begin with the prefix followed by a `: `, and then followed by a summary of what the change does, which should be no longer than 52 alphanumerical characters including whitespace. The prefix is determined by the file being modified, simply remove the extension or find the group that a file belongs to. For example, a modifiation to blur.effect would have the category effects, due to it being re-usable. 37 | 38 | ### Prefixes 39 | - effects: Anything modifying generic effects like blur.effect, color-conversion.effect, mask.effect, etc. 40 | - locale: Changes in `/data/locale`. 41 | - shaders: Changes in `/data/shaders` that are not directly influenced by a change to custom shaders. 42 | - project: Changes to files like README, CONTRIBUTING, AUTHORS, etc. 43 | - cmake: Changes to CMake build scripts. 44 | - ci: Changes to Continuous Integration. 45 | 46 | All other files should be prefixed with the main file changed, so a change to the translations for Source Mirror would be `source-mirror: commit`. 47 | 48 | ## Commit Messages 49 | The commit message should always convey why this change was necessary, what is being changed and how it affects the code when being run. There are rare cases where this can be left out (formatting, refactoring, ...) but it should always be descriptive of what is actually being done. 50 | 51 | # Coding Guidelines 52 | 53 | ## Naming 54 | The project uses the generally known snake_case for code and the uppercase variant for enumerations and macros: 55 | 56 | ### Macros (ELEPHANT_CASE) 57 | - Casing: Uppercase 58 | - Separator: `_` 59 | - Prefixes: `S_` for global values, `ST_` for local (this file) values, `D_` for simple functions, `P_` for complex functions 60 | - Suffixes: No 61 | 62 | Example: 63 | ``` 64 | #define S_PI 3.14141 65 | #define ST_PI2 S_PI / 2.0 66 | #define D_(x) S_PI * x 67 | #define P_(x, y) double_t x(double_t a, double_t b) { return a * b * y; } 68 | ``` 69 | 70 | ### Enumerations (snake_case) 71 | - Casing: Lowercase 72 | - Separator: `_` 73 | 74 | Example: 75 | ``` 76 | enum my_enum {}; 77 | enum class my_enum_class {}; 78 | enum class my_enum_class_int : int {}; 79 | ``` 80 | 81 | #### Enumeration Entries (ELEPHANT_CASE) 82 | - Casing: Uppercase 83 | - Separator: `_` 84 | 85 | Example: 86 | ``` 87 | enum my_enum { 88 | ENTRY_1, 89 | ENTRY_2 90 | }; 91 | ``` 92 | 93 | ### Variables (snake_case) 94 | - Casing: Lowercase 95 | - Separator: `_` 96 | - Prefixes: No 97 | - Suffixes: No 98 | 99 | Example: 100 | ``` 101 | int my_var = 0; 102 | ``` 103 | 104 | ### Functions (snake_case) 105 | - Casing: Lowercase 106 | - Separator: `_` 107 | - Prefixes: No 108 | - Suffixes: No (differentiate by parameters only) 109 | 110 | Example: 111 | ``` 112 | // This is forbidden. 113 | void func(); 114 | int func_int(); 115 | 116 | // This is okay. 117 | void func(); 118 | void func(int& result); 119 | ``` 120 | 121 | ### Namespaces (snake_case) 122 | - Casing: Lowercase 123 | - Separator: `_` 124 | 125 | Example: 126 | ``` 127 | namespace a_space {}; 128 | ``` 129 | 130 | ### Classes, Structs, Unions (snake_case) 131 | - Casing: Lowercase 132 | - Separator: `_` 133 | - Prefixes: No 134 | - Suffixes: No 135 | 136 | Example: 137 | ``` 138 | class a_class {}; 139 | class interface_class {}; 140 | ``` 141 | 142 | #### Interface Classes 143 | Interface Classes are handled like normal classes. There are no prefixes or suffixes to attach. 144 | 145 | #### Methods (snake_case) 146 | - Casing: Lowercase 147 | - Separator: `_` 148 | - Prefixes: No 149 | - Suffixes: No (differentiate by parameters only) 150 | 151 | Example: 152 | ``` 153 | class a_class { 154 | // This is forbidden. 155 | void func(); 156 | int func_int(); 157 | 158 | // This is okay. 159 | void func(); 160 | void func(int& result); 161 | }; 162 | ``` 163 | 164 | #### Member Variables (snake_case) 165 | - Casing: Lowercase 166 | - Separator: `_` 167 | - Prefixes: `_` if private, otherwise none 168 | - Suffixes: No 169 | 170 | Example: 171 | ``` 172 | class a_class { 173 | int64_t _local_var; 174 | void* _pointer; 175 | int32_t _id; 176 | }; 177 | ``` 178 | 179 | ### Type Definitions (snake_case) 180 | - Casing: Lowercase 181 | - Separator: `_` 182 | - Prefixes: No 183 | - Suffixes: `_t` 184 | 185 | Example: 186 | ``` 187 | typedef int32_t my_type_t; 188 | ``` 189 | 190 | ## Preprocessor Macros 191 | Preprocessor `#define` Macros should be used sparingly, due to their nature of changing code before the compiler gets a chance to work with it. Unless necessary, they should never be in a header file. 192 | 193 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Michael Fabian Dirks 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # FFMPEG Encoders for OBS studio 2 | Adds ffmpeg encoders for normal use instead of requiring ffmpeg output. Not much else to say. 3 | 4 | # Encoders 5 | ## Video Production / Edition 6 | ### Apple ProRes (prores_aw) 7 | * Intra-Frame only 8 | * Fixed Bitrate (defined by Profile) 9 | * Profiles: PXY, LT, Standard, HQ, 4444 10 | * Threading: Frame 11 | 12 | # Threading Modes 13 | All threading modes can be combined to create a more complex one. 14 | ## Frame 15 | Frame Threading means that new frames can be submitted before the last one is complete. This is the most basic threading and has huge latency issues, and is likely also very unstable with frame times. 16 | 17 | ## Slice 18 | Slice Threading allows individual frames to be done quicker by splitting up the entire submitted frame into individual slices, each of which are handed off to workers. More complex than frame threading, but can result in faster encoding and will result in less latency. 19 | 20 | ## Macroblock 21 | This kind of threading is rarely seen and splits the frame into macroblocks, allowing for massive parallelization. Similar latency to Slice threading and can occasionally use SMT/HT better, resulting in better performance. 22 | -------------------------------------------------------------------------------- /ci/builder.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const process = require('process'); 4 | const runner = require('./runner.js'); 5 | let env = process.env; 6 | 7 | let x32_steps = []; 8 | let x64_steps = []; 9 | 10 | if ((process.platform == "win32") || (process.platform == "win64")) { 11 | // Windows 12 | let extra_conf = [ 13 | `-DCMAKE_SYSTEM_VERSION=${process.env.CMAKE_SYSTEM_VERSION}`, 14 | `-DCMAKE_PACKAGE_NAME=obs-ffmpeg-encoder`, 15 | '-DCMAKE_INSTALL_PREFIX="build/distrib/"', 16 | '-DCMAKE_PACKAGE_PREFIX="build/package/"', 17 | ]; 18 | let extra_build = [ 19 | 20 | ]; 21 | 22 | if(process.env.APPVEYOR) { 23 | extra_build.concat(['--', '/logger:"C:\\Program Files\\AppVeyor\\BuildAgent\\Appveyor.MSBuildLogger.dll"']); 24 | } 25 | 26 | if ((process.env.CMAKE_GENERATOR_32 !== undefined) && (process.env.CMAKE_GENERATOR_32 !== "")) { 27 | x32_steps.push( 28 | [ 'cmake', [ 29 | '-H.', '-Bbuild/32', 30 | `-G"${process.env.CMAKE_GENERATOR_32}"`, '-AWin32', '-T"host=x64"', 31 | ].concat(extra_conf), env ] 32 | ); 33 | x32_steps.push( 34 | [ 'cmake', [ 35 | '--build', 'build/32', 36 | '--config', 'RelWithDebInfo', 37 | '--target', 'INSTALL' 38 | ].concat(extra_build), env ] 39 | ); 40 | } 41 | if ((process.env.CMAKE_GENERATOR_64 !== undefined) && (process.env.CMAKE_GENERATOR_64 !== "")) { 42 | x64_steps.push( 43 | [ 'cmake', [ 44 | '-H.', '-Bbuild/64', 45 | `-G"${process.env.CMAKE_GENERATOR_64}"`, '-Ax64', '-T"host=x64"', 46 | ].concat(extra_conf), env ] 47 | ); 48 | x64_steps.push( 49 | [ 'cmake', [ 50 | '--build', 'build/64', 51 | '--config', 'RelWithDebInfo', 52 | '--target', 'INSTALL' 53 | ].concat(extra_build), env ] 54 | ); 55 | } 56 | } else { 57 | // Unix 58 | 59 | } 60 | 61 | function runRunners(runnerArray, name) { 62 | return new Promise(async (resolve, reject) => { 63 | let local = runnerArray.reverse(); 64 | while (local.length > 0) { 65 | try { 66 | let task = local.pop(); 67 | let work = new runner(name, task[0], task[1], task[2]); 68 | await work.run(); 69 | } catch (e) { 70 | reject(e); 71 | return; 72 | } 73 | } 74 | resolve(0); 75 | }); 76 | } 77 | 78 | let promises = []; 79 | promises.push(runRunners(x32_steps, "32-Bit")); 80 | promises.push(runRunners(x64_steps, "64-Bit")); 81 | Promise.all(promises).then( 82 | res => { 83 | process.exit(0); 84 | }, 85 | err => { 86 | console.log(err); 87 | process.exit(1); 88 | } 89 | ).catch(err => { 90 | console.log(err); 91 | process.exit(1); 92 | }) 93 | -------------------------------------------------------------------------------- /ci/packager.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const process = require('process'); 4 | const runner = require('./runner.js'); 5 | let env = process.env; 6 | 7 | let steps = []; 8 | 9 | if ((process.env.CMAKE_GENERATOR_64 !== undefined) && (process.env.CMAKE_GENERATOR_64 !== "")) { 10 | steps.push( 11 | [ 'cmake', [ 12 | '--build', 'build/64', 13 | '--config', 'RelWithDebInfo', 14 | '--target', 'PACKAGE_7Z' 15 | ], env ] 16 | ); 17 | steps.push( 18 | [ 'cmake', [ 19 | '--build', 'build/64', 20 | '--config', 'RelWithDebInfo', 21 | '--target', 'PACKAGE_ZIP' 22 | ], env ] 23 | ); 24 | } else if ((process.env.CMAKE_GENERATOR_32 !== undefined) && (process.env.CMAKE_GENERATOR_32 !== "")) { 25 | steps.push( 26 | [ 'cmake', [ 27 | '--build', 'build/32', 28 | '--config', 'RelWithDebInfo', 29 | '--target', 'PACKAGE_7Z' 30 | ], env ] 31 | ); 32 | steps.push( 33 | [ 'cmake', [ 34 | '--build', 'build/32', 35 | '--config', 'RelWithDebInfo', 36 | '--target', 'PACKAGE_ZIP' 37 | ], env ] 38 | ); 39 | } 40 | 41 | function runRunners(runnerArray, name) { 42 | return new Promise(async (resolve, reject) => { 43 | let local = runnerArray.reverse(); 44 | while (local.length > 0) { 45 | try { 46 | let task = local.pop(); 47 | let work = new runner(name, task[0], task[1], task[2]); 48 | await work.run(); 49 | } catch (e) { 50 | reject(e); 51 | return; 52 | } 53 | } 54 | resolve(0); 55 | }); 56 | } 57 | 58 | 59 | let promises = []; 60 | promises.push(runRunners(steps, "32-Bit")); 61 | Promise.all(promises).then( 62 | res => { 63 | process.exit(0); 64 | }, 65 | err => { 66 | console.log(err); 67 | process.exit(1); 68 | } 69 | ) 70 | -------------------------------------------------------------------------------- /ci/runner.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const process = require('process'); 4 | const cp = require('child_process'); 5 | 6 | class Runner { 7 | constructor(name, cmd, args, env) { 8 | this.name = name; 9 | this.cmd = cmd; 10 | if (args == undefined) 11 | args = []; 12 | this.args = args; 13 | if (env == undefined) 14 | env = process.env; 15 | this.env = env; 16 | } 17 | 18 | run() { 19 | let self = this; 20 | return new Promise(function(resolve, reject) { 21 | self.proc = cp.spawn( 22 | self.cmd, self.args, { 23 | windowsVerbatimArguments: true, 24 | windowsHide: true, 25 | env: self.env, 26 | } 27 | ); 28 | self.proc.stdout.on('data', (data) => { 29 | process.stdout.write(`[${self.name}:Out] ${data}`); 30 | }); 31 | self.proc.stderr.on('data', (data) => { 32 | process.stderr.write(`[${self.name}:Err] ${data}`); 33 | }); 34 | self.proc.on('exit', (code, signal) => { 35 | if (code != 0) { 36 | reject(code); 37 | return; 38 | } 39 | resolve(code); 40 | return; 41 | }); 42 | }); 43 | } 44 | 45 | execute() { 46 | return this.run(); 47 | } 48 | } 49 | 50 | module.exports = Runner; -------------------------------------------------------------------------------- /cmake/DownloadProject.CMakeLists.cmake.in: -------------------------------------------------------------------------------- 1 | # Distributed under the OSI-approved MIT License. See accompanying 2 | # file LICENSE or https://github.com/Crascit/DownloadProject for details. 3 | 4 | cmake_minimum_required(VERSION 2.8.2) 5 | 6 | project(${DL_ARGS_PROJ}-download NONE) 7 | 8 | include(ExternalProject) 9 | ExternalProject_Add(${DL_ARGS_PROJ}-download 10 | ${DL_ARGS_UNPARSED_ARGUMENTS} 11 | SOURCE_DIR "${DL_ARGS_SOURCE_DIR}" 12 | BINARY_DIR "${DL_ARGS_BINARY_DIR}" 13 | CONFIGURE_COMMAND "" 14 | BUILD_COMMAND "" 15 | INSTALL_COMMAND "" 16 | TEST_COMMAND "" 17 | ) 18 | -------------------------------------------------------------------------------- /cmake/DownloadProject.cmake: -------------------------------------------------------------------------------- 1 | # Distributed under the OSI-approved MIT License. See accompanying 2 | # file LICENSE or https://github.com/Crascit/DownloadProject for details. 3 | # 4 | # MODULE: DownloadProject 5 | # 6 | # PROVIDES: 7 | # download_project( PROJ projectName 8 | # [PREFIX prefixDir] 9 | # [DOWNLOAD_DIR downloadDir] 10 | # [SOURCE_DIR srcDir] 11 | # [BINARY_DIR binDir] 12 | # [QUIET] 13 | # ... 14 | # ) 15 | # 16 | # Provides the ability to download and unpack a tarball, zip file, git repository, 17 | # etc. at configure time (i.e. when the cmake command is run). How the downloaded 18 | # and unpacked contents are used is up to the caller, but the motivating case is 19 | # to download source code which can then be included directly in the build with 20 | # add_subdirectory() after the call to download_project(). Source and build 21 | # directories are set up with this in mind. 22 | # 23 | # The PROJ argument is required. The projectName value will be used to construct 24 | # the following variables upon exit (obviously replace projectName with its actual 25 | # value): 26 | # 27 | # projectName_SOURCE_DIR 28 | # projectName_BINARY_DIR 29 | # 30 | # The SOURCE_DIR and BINARY_DIR arguments are optional and would not typically 31 | # need to be provided. They can be specified if you want the downloaded source 32 | # and build directories to be located in a specific place. The contents of 33 | # projectName_SOURCE_DIR and projectName_BINARY_DIR will be populated with the 34 | # locations used whether you provide SOURCE_DIR/BINARY_DIR or not. 35 | # 36 | # The DOWNLOAD_DIR argument does not normally need to be set. It controls the 37 | # location of the temporary CMake build used to perform the download. 38 | # 39 | # The PREFIX argument can be provided to change the base location of the default 40 | # values of DOWNLOAD_DIR, SOURCE_DIR and BINARY_DIR. If all of those three arguments 41 | # are provided, then PREFIX will have no effect. The default value for PREFIX is 42 | # CMAKE_BINARY_DIR. 43 | # 44 | # The QUIET option can be given if you do not want to show the output associated 45 | # with downloading the specified project. 46 | # 47 | # In addition to the above, any other options are passed through unmodified to 48 | # ExternalProject_Add() to perform the actual download, patch and update steps. 49 | # The following ExternalProject_Add() options are explicitly prohibited (they 50 | # are reserved for use by the download_project() command): 51 | # 52 | # CONFIGURE_COMMAND 53 | # BUILD_COMMAND 54 | # INSTALL_COMMAND 55 | # TEST_COMMAND 56 | # 57 | # Only those ExternalProject_Add() arguments which relate to downloading, patching 58 | # and updating of the project sources are intended to be used. Also note that at 59 | # least one set of download-related arguments are required. 60 | # 61 | # If using CMake 3.2 or later, the UPDATE_DISCONNECTED option can be used to 62 | # prevent a check at the remote end for changes every time CMake is run 63 | # after the first successful download. See the documentation of the ExternalProject 64 | # module for more information. It is likely you will want to use this option if it 65 | # is available to you. Note, however, that the ExternalProject implementation contains 66 | # bugs which result in incorrect handling of the UPDATE_DISCONNECTED option when 67 | # using the URL download method or when specifying a SOURCE_DIR with no download 68 | # method. Fixes for these have been created, the last of which is scheduled for 69 | # inclusion in CMake 3.8.0. Details can be found here: 70 | # 71 | # https://gitlab.kitware.com/cmake/cmake/commit/bdca68388bd57f8302d3c1d83d691034b7ffa70c 72 | # https://gitlab.kitware.com/cmake/cmake/issues/16428 73 | # 74 | # If you experience build errors related to the update step, consider avoiding 75 | # the use of UPDATE_DISCONNECTED. 76 | # 77 | # EXAMPLE USAGE: 78 | # 79 | # include(DownloadProject) 80 | # download_project(PROJ googletest 81 | # GIT_REPOSITORY https://github.com/google/googletest.git 82 | # GIT_TAG master 83 | # UPDATE_DISCONNECTED 1 84 | # QUIET 85 | # ) 86 | # 87 | # add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR}) 88 | # 89 | #======================================================================================== 90 | 91 | 92 | set(_DownloadProjectDir "${CMAKE_CURRENT_LIST_DIR}") 93 | 94 | include(CMakeParseArguments) 95 | 96 | function(download_project) 97 | 98 | set(options QUIET) 99 | set(oneValueArgs 100 | PROJ 101 | PREFIX 102 | DOWNLOAD_DIR 103 | SOURCE_DIR 104 | BINARY_DIR 105 | # Prevent the following from being passed through 106 | CONFIGURE_COMMAND 107 | BUILD_COMMAND 108 | INSTALL_COMMAND 109 | TEST_COMMAND 110 | ) 111 | set(multiValueArgs "") 112 | 113 | cmake_parse_arguments(DL_ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 114 | 115 | # Hide output if requested 116 | if (DL_ARGS_QUIET) 117 | set(OUTPUT_QUIET "OUTPUT_QUIET") 118 | else() 119 | unset(OUTPUT_QUIET) 120 | message(STATUS "Downloading/updating ${DL_ARGS_PROJ}") 121 | endif() 122 | 123 | # Set up where we will put our temporary CMakeLists.txt file and also 124 | # the base point below which the default source and binary dirs will be. 125 | # The prefix must always be an absolute path. 126 | if (NOT DL_ARGS_PREFIX) 127 | set(DL_ARGS_PREFIX "${CMAKE_BINARY_DIR}") 128 | else() 129 | get_filename_component(DL_ARGS_PREFIX "${DL_ARGS_PREFIX}" ABSOLUTE 130 | BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}") 131 | endif() 132 | if (NOT DL_ARGS_DOWNLOAD_DIR) 133 | set(DL_ARGS_DOWNLOAD_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-download") 134 | endif() 135 | 136 | # Ensure the caller can know where to find the source and build directories 137 | if (NOT DL_ARGS_SOURCE_DIR) 138 | set(DL_ARGS_SOURCE_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-src") 139 | endif() 140 | if (NOT DL_ARGS_BINARY_DIR) 141 | set(DL_ARGS_BINARY_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-build") 142 | endif() 143 | set(${DL_ARGS_PROJ}_SOURCE_DIR "${DL_ARGS_SOURCE_DIR}" PARENT_SCOPE) 144 | set(${DL_ARGS_PROJ}_BINARY_DIR "${DL_ARGS_BINARY_DIR}" PARENT_SCOPE) 145 | 146 | # The way that CLion manages multiple configurations, it causes a copy of 147 | # the CMakeCache.txt to be copied across due to it not expecting there to 148 | # be a project within a project. This causes the hard-coded paths in the 149 | # cache to be copied and builds to fail. To mitigate this, we simply 150 | # remove the cache if it exists before we configure the new project. It 151 | # is safe to do so because it will be re-generated. Since this is only 152 | # executed at the configure step, it should not cause additional builds or 153 | # downloads. 154 | file(REMOVE "${DL_ARGS_DOWNLOAD_DIR}/CMakeCache.txt") 155 | 156 | # Create and build a separate CMake project to carry out the download. 157 | # If we've already previously done these steps, they will not cause 158 | # anything to be updated, so extra rebuilds of the project won't occur. 159 | # Make sure to pass through CMAKE_MAKE_PROGRAM in case the main project 160 | # has this set to something not findable on the PATH. 161 | configure_file("${_DownloadProjectDir}/DownloadProject.CMakeLists.cmake.in" 162 | "${DL_ARGS_DOWNLOAD_DIR}/CMakeLists.txt") 163 | execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" 164 | -D "CMAKE_MAKE_PROGRAM:FILE=${CMAKE_MAKE_PROGRAM}" 165 | . 166 | RESULT_VARIABLE result 167 | ${OUTPUT_QUIET} 168 | WORKING_DIRECTORY "${DL_ARGS_DOWNLOAD_DIR}" 169 | ) 170 | if(result) 171 | message(FATAL_ERROR "CMake step for ${DL_ARGS_PROJ} failed: ${result}") 172 | endif() 173 | execute_process(COMMAND ${CMAKE_COMMAND} --build . 174 | RESULT_VARIABLE result 175 | ${OUTPUT_QUIET} 176 | WORKING_DIRECTORY "${DL_ARGS_DOWNLOAD_DIR}" 177 | ) 178 | if(result) 179 | message(FATAL_ERROR "Build step for ${DL_ARGS_PROJ} failed: ${result}") 180 | endif() 181 | 182 | endfunction() 183 | -------------------------------------------------------------------------------- /cmake/cppcheck.cmake: -------------------------------------------------------------------------------- 1 | set(CPPCHECK_PROJECTS "") 2 | set(CPPCHECK_ARGUMENTS "") 3 | set(CPPCHECK_PLATFORM "") 4 | set(CPPCHECK_LIBRARIES "") 5 | set(CPPCHECK_ENABLED FALSE) 6 | 7 | include(CMakeParseArguments) 8 | 9 | function(cppcheck) 10 | set(CPPCHECK_PATH "" CACHE PATH "Path to cppcheck binary") 11 | set(CPPCHECK_BIN "cppcheck.exe" CACHE STRING "CPPCheck Binary File") 12 | set(CPPCHECK_ENABLE_INCONCLUSIVE OFF CACHE BOOL "Enable inconclusive checks?") 13 | set(CPPCHECK_ENABLE_MISSING_INCLUDE ON CACHE BOOL "Check for missing includes?") 14 | set(CPPCHECK_ENABLE_UNUSED_FUNCTION OFF CACHE BOOL "Check for unused functions?") 15 | set(CPPCHECK_ENABLE_INFORMATION ON CACHE BOOL "Enable information messages?") 16 | set(CPPCHECK_ENABLE_PORTABILITY ON CACHE BOOL "Enable portability messages?") 17 | set(CPPCHECK_ENABLE_PERFORMANCE ON CACHE BOOL "Enable performance messages?") 18 | set(CPPCHECK_ENABLE_WARNING ON CACHE BOOL "Enable warning messages?") 19 | set(CPPCHECK_STD_POSIX OFF CACHE BOOL "POSIX Standard Compatibility Checks") 20 | set(CPPCHECK_STD_C89 OFF CACHE BOOL "C89 Standard Compatibility Checks") 21 | set(CPPCHECK_STD_C99 OFF CACHE BOOL "C99 Standard Compatibility Checks") 22 | set(CPPCHECK_STD_C11 ON CACHE BOOL "C11 Standard Compatibility Checks") 23 | set(CPPCHECK_STD_CPP03 OFF CACHE BOOL "C++03 Standard Compatibility Checks") 24 | set(CPPCHECK_STD_CPP11 OFF CACHE BOOL "C++11 Standard Compatibility Checks") 25 | set(CPPCHECK_STD_CPP14 ON CACHE BOOL "C++14 Standard Compatibility Checks") 26 | set(CPPCHECK_FORCE_C OFF CACHE BOOL "Force checking with C language") 27 | set(CPPCHECK_FORCE_CPP OFF CACHE BOOL "Force checking with C++ language (overrides CPPCHECK_FORCE_C)") 28 | set(CPPCHECK_VERBOSE ON CACHE BOOL "Show more detailed error reports") 29 | set(CPPCHECK_QUIET ON CACHE BOOL "Hide progress reports") 30 | set(CPPCHECK_LIBRARIES "" CACHE STRING "List of Libraries to load separated by semicolon") 31 | set(CPPCHECK_EXCLUDE_DIRECTORIES "" CACHE STRING "List of directories to exclude separated by semicolon") 32 | set(CPPCHECK_PARALLEL_TASKS "4" CACHE STRING "Number of threads to use for cppcheck") 33 | if(WIN32) 34 | set(CPPCHECK_WIN32_UNICODE ON CACHE BOOL "Use Unicode character encoding for Win32") 35 | endif() 36 | 37 | mark_as_advanced(CPPCHECK_BIN CPPCHECK_QUIET CPPCHECK_VERBOSE CPPCHECK_LIBRARIES CPPCHECK_ENABLE_INCONCLUSIVE CPPCHECK_PARALLEL_TASKS) 38 | 39 | # Parse arguments 40 | set(cppcheck_options ) 41 | set(cppcheck_oneval ) 42 | set(cppcheck_mulval EXCLUDE) 43 | cmake_parse_arguments( 44 | CPPCHECKP 45 | "${cppcheck_options}" 46 | "${cppcheck_oneval}" 47 | "${cppcheck_mulval}" 48 | ${ARGN} 49 | ) 50 | 51 | # Do nothing if we can't find CppCheck 52 | if(NOT EXISTS ${CPPCHECK_PATH}) 53 | return() 54 | endif() 55 | if(NOT IS_DIRECTORY ${CPPCHECK_PATH}) 56 | return() 57 | endif() 58 | if(NOT EXISTS ${CPPCHECK_PATH}/${CPPCHECK_BIN}) 59 | return() 60 | endif() 61 | set(CPPCHECK_ENABLED TRUE) 62 | 63 | # Detect Architecture (Bitness) 64 | math(EXPR BITS "8*${CMAKE_SIZEOF_VOID_P}") 65 | 66 | # Detect Platform 67 | if(WIN32) 68 | if(BITS EQUAL "32") 69 | if(CPPCHECK_WIN32_UNICODE) 70 | set(CPPCHECK_PLATFORM "win32W") 71 | else() 72 | set(CPPCHECK_PLATFORM "win32A") 73 | endif() 74 | else() 75 | set(CPPCHECK_PLATFORM "win64") 76 | endif() 77 | elseif(("${CMAKE_SYSTEM_NAME}" MATCHES "Linux") OR ("${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD") OR APPLE) 78 | if(BITS EQUAL "32") 79 | set(CPPCHECK_PLATFORM "unix32") 80 | else() 81 | set(CPPCHECK_PLATFORM "unix64") 82 | endif() 83 | else() 84 | set(CPPCHECK_PLATFORM "unspecified") 85 | endif() 86 | if(WIN32) 87 | list(APPEND CPPCHECK_LIBRARIES "windows") 88 | elseif("${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD") 89 | list(APPEND CPPCHECK_LIBRARIES "bsd") 90 | elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Linux") 91 | list(APPEND CPPCHECK_LIBRARIES "gnu") 92 | endif() 93 | 94 | # Arguments 95 | set(CPPCHECK_ARGUMENTS "") 96 | 97 | # Compiler 98 | if(MSVC) 99 | #list(APPEND CPPCHECK_ARGUMENTS --template="{file}|{line}|{severity}|{id}|{message}") 100 | endif() 101 | 102 | # Flags 103 | if(CPPCHECK_ENABLE_INCONCLUSIVE) 104 | list(APPEND CPPCHECK_ARGUMENTS --inconclusive) 105 | endif() 106 | if(CPPCHECK_VERBOSE) 107 | list(APPEND CPPCHECK_ARGUMENTS -v) 108 | endif() 109 | if(CPPCHECK_QUIET) 110 | list(APPEND CPPCHECK_ARGUMENTS -q) 111 | endif() 112 | if(CPPCHECK_PLATFORM) 113 | list(APPEND CPPCHECK_ARGUMENTS --platform=${CPPCHECK_PLATFORM}) 114 | endif() 115 | if(CPPCHECK_PARALLEL_TASKS) 116 | list(APPEND CPPCHECK_ARGUMENTS -j ${CPPCHECK_PARALLEL_TASKS}) 117 | endif() 118 | 119 | # Libraries 120 | foreach(_library ${CPPCHECK_LIBRARIES}) 121 | list(APPEND CPPCHECK_ARGUMENTS --library=${_library}) 122 | endforeach() 123 | 124 | # Exclusion 125 | foreach(_path ${CPPCHECK_EXCLUDE_DIRECTORIES}) 126 | file(TO_NATIVE_PATH "${_path}" _npath) 127 | if(MSVC) 128 | list(APPEND CPPCHECK_ARGUMENTS --suppress=*:${_npath}\\*) 129 | else() 130 | list(APPEND CPPCHECK_ARGUMENTS -i${_npath}) 131 | endif() 132 | endforeach() 133 | if(CPPCHECKP_EXCLUDE) 134 | foreach(_path ${CPPCHECKP_EXCLUDE}) 135 | file(TO_NATIVE_PATH "${_path}" _npath) 136 | if(MSVC) 137 | list(APPEND CPPCHECK_ARGUMENTS --suppress=*:${_npath}\\*) 138 | else() 139 | list(APPEND CPPCHECK_ARGUMENTS -i${_npath}) 140 | endif() 141 | endforeach() 142 | endif() 143 | 144 | # Checks 145 | if(CPPCHECK_ENABLE_MISSING_INCLUDE) 146 | list(APPEND CPPCHECK_ARGUMENTS --enable=missingInclude) 147 | endif() 148 | if(CPPCHECK_ENABLE_UNUSED_FUNCTION) 149 | list(APPEND CPPCHECK_ARGUMENTS --enable=unusedFunction) 150 | endif() 151 | if(CPPCHECK_ENABLE_INFORMATION) 152 | list(APPEND CPPCHECK_ARGUMENTS --enable=information) 153 | endif() 154 | if(CPPCHECK_ENABLE_PORTABILITY) 155 | list(APPEND CPPCHECK_ARGUMENTS --enable=portability) 156 | endif() 157 | if(CPPCHECK_ENABLE_PERFORMANCE) 158 | list(APPEND CPPCHECK_ARGUMENTS --enable=performance) 159 | endif() 160 | if(CPPCHECK_ENABLE_WARNING) 161 | list(APPEND CPPCHECK_ARGUMENTS --enable=warning) 162 | endif() 163 | 164 | # Std 165 | if(CPPCHECK_STD_POSIX) 166 | list(APPEND CPPCHECK_ARGUMENTS --std=posix) 167 | endif() 168 | if(CPPCHECK_STD_C89) 169 | list(APPEND CPPCHECK_ARGUMENTS --std=c89) 170 | endif() 171 | if(CPPCHECK_STD_C99) 172 | list(APPEND CPPCHECK_ARGUMENTS --std=c99) 173 | endif() 174 | if(CPPCHECK_STD_C11) 175 | list(APPEND CPPCHECK_ARGUMENTS --std=c11) 176 | endif() 177 | if(CPPCHECK_STD_CPP03) 178 | list(APPEND CPPCHECK_ARGUMENTS --std=c++03) 179 | endif() 180 | if(CPPCHECK_STD_CPP11) 181 | list(APPEND CPPCHECK_ARGUMENTS --std=c++11) 182 | endif() 183 | if(CPPCHECK_STD_CPP14) 184 | list(APPEND CPPCHECK_ARGUMENTS --std=c++14) 185 | endif() 186 | 187 | # Force Language 188 | if(CPPCHECK_FORCE_CPP) 189 | list(APPEND CPPCHECK_ARGUMENTS --language=c++) 190 | elseif(CPPCHECK_FORCE_C) 191 | list(APPEND CPPCHECK_ARGUMENTS --language=c) 192 | endif() 193 | 194 | add_custom_target( 195 | CPPCHECK 196 | ) 197 | 198 | # Propagate to parent scope 199 | set(CPPCHECK_PROJECTS "${CPPCHECK_PROJECTS}" PARENT_SCOPE) 200 | set(CPPCHECK_ARGUMENTS "${CPPCHECK_ARGUMENTS}" PARENT_SCOPE) 201 | set(CPPCHECK_PLATFORM "${CPPCHECK_PLATFORM}" PARENT_SCOPE) 202 | set(CPPCHECK_LIBRARIES "${CPPCHECK_LIBRARIES}" PARENT_SCOPE) 203 | endfunction() 204 | 205 | function(cppcheck_add_project u_project) 206 | list(APPEND CPPCHECK_PROJECTS ${u_project}) 207 | 208 | if(NOT CPPCHECK_ENABLED) 209 | return() 210 | endif() 211 | 212 | # Include Directories 213 | get_target_property(_INCLUDE_DIRECTORIES ${u_project} INCLUDE_DIRECTORIES) 214 | foreach(_path ${_INCLUDE_DIRECTORIES}) 215 | file(TO_NATIVE_PATH "${_path}" _npath) 216 | list(APPEND CPPCHECK_ARGUMENTS -I${_npath}) 217 | endforeach() 218 | 219 | if(MSVC) 220 | add_custom_target( 221 | CPPCHECK_${u_project} 222 | COMMAND "${CPPCHECK_PATH}/${CPPCHECK_BIN}" ${CPPCHECK_ARGUMENTS} --project=${${u_project}_BINARY_DIR}/${u_project}.sln 223 | COMMAND_EXPAND_LISTS 224 | VERBATIM 225 | ) 226 | else() 227 | # Non-MSVC and Unix (Linux, FreeBSD, APPLE) need to have -I, -i, -D and -U specified manually. 228 | # Each file can be added to --file-list= as a comma separated list. 229 | 230 | # Defines 231 | get_target_property(_COMPILE_DEFINITIONS ${u_project} COMPILE_DEFINITIONS) 232 | foreach(_def ${_COMPILE_DEFINITIONS}) 233 | list(APPEND CPPCHECK_ARGUMENTS -D${_def}) 234 | endforeach() 235 | 236 | # Source Files 237 | get_target_property(_SOURCES ${u_project} SOURCES) 238 | foreach(_path ${_SOURCES}) 239 | file(TO_NATIVE_PATH "${_path}" _npath) 240 | list(APPEND CPPCHECK_ARGUMENTS ${_npath}) 241 | endforeach() 242 | 243 | add_custom_target( 244 | CPPCHECK_${u_project} 245 | COMMAND "${CPPCHECK_PATH}/${CPPCHECK_BIN}" ${CPPCHECK_ARGUMENTS} 246 | COMMAND_EXPAND_LISTS 247 | VERBATIM 248 | ) 249 | endif() 250 | add_dependencies(CPPCHECK CPPCHECK_${u_project}) 251 | endfunction() 252 | -------------------------------------------------------------------------------- /cmake/installer.iss.in: -------------------------------------------------------------------------------- 1 | ; Script generated by the Inno Setup Script Wizard. 2 | ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! 3 | 4 | #define MyAppName "@PROJECT_FULL_NAME@" 5 | #define MyAppVersion "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@.@PROJECT_VERSION_TWEAK@" 6 | #define MyAppPublisher "Xaymars Technology Workshop" 7 | #define MyAppURL "https://www.xaymar.com/" 8 | 9 | [Setup] 10 | ; NOTE: The value of AppId uniquely identifies this application. 11 | ; Do not use the same AppId value in installers for other applications. 12 | ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) 13 | AppId={{6559B77C-0F41-4F97-AC9D-2C13DE22D236}} 14 | AppName={#MyAppName} 15 | AppVersion={#MyAppVersion} 16 | ;AppVerName={#MyAppName} {#MyAppVersion} 17 | AppPublisher={#MyAppPublisher} 18 | AppPublisherURL={#MyAppURL} 19 | AppSupportURL={#MyAppURL} 20 | AppUpdatesURL={#MyAppURL} 21 | DefaultDirName={code:GetDirName} 22 | DefaultGroupName={#MyAppName} 23 | AllowNoIcons=yes 24 | LicenseFile="@ISS_SOURCE_DIR@/LICENSE" 25 | OutputDir="@ISS_PACKAGE_DIR@" 26 | OutputBaseFilename=obs-ffmpeg-encoder-{#MyAppVersion} 27 | Compression=lzma 28 | SolidCompression=yes 29 | VersionInfoVersion={#MyAppVersion} 30 | VersionInfoCompany={#MyAppPublisher} 31 | VersionInfoDescription={#MyAppName} Setup 32 | 33 | [Languages] 34 | Name: "english"; MessagesFile: "compiler:Default.isl" 35 | 36 | [Files] 37 | Source: "@ISS_FILES_DIR@/*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs 38 | ; NOTE: Don't use "Flags: ignoreversion" on any shared system files 39 | 40 | [Icons] 41 | Name: "{group}\{cm:ProgramOnTheWeb,{#MyAppName}}"; Filename: "{#MyAppURL}" 42 | Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}" 43 | 44 | [Code] 45 | function GetDirName(Value: string): string; 46 | var 47 | InstallPath: string; 48 | begin 49 | // initialize default path, which will be returned when the following registry 50 | // key queries fail due to missing keys or for some different reason 51 | Result := '{pf}\obs-studio'; 52 | // query the first registry value; if this succeeds, return the obtained value 53 | if RegQueryStringValue(HKLM32, 'SOFTWARE\OBS Studio', '', InstallPath) then 54 | Result := InstallPath 55 | end; 56 | 57 | ///////////////////////////////////////////////////////////////////// 58 | function GetUninstallString(): String; 59 | var 60 | sUnInstPath: String; 61 | sUnInstallString: String; 62 | begin 63 | sUnInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{#emit SetupSetting("AppId")}_is1'); 64 | sUnInstallString := ''; 65 | if not RegQueryStringValue(HKLM, sUnInstPath, 'UninstallString', sUnInstallString) then 66 | RegQueryStringValue(HKCU, sUnInstPath, 'UninstallString', sUnInstallString); 67 | Result := sUnInstallString; 68 | end; 69 | 70 | 71 | ///////////////////////////////////////////////////////////////////// 72 | function IsUpgrade(): Boolean; 73 | begin 74 | Result := (GetUninstallString() <> ''); 75 | end; 76 | 77 | 78 | ///////////////////////////////////////////////////////////////////// 79 | function UnInstallOldVersion(): Integer; 80 | var 81 | sUnInstallString: String; 82 | iResultCode: Integer; 83 | begin 84 | // Return Values: 85 | // 1 - uninstall string is empty 86 | // 2 - error executing the UnInstallString 87 | // 3 - successfully executed the UnInstallString 88 | 89 | // default return value 90 | Result := 0; 91 | 92 | // get the uninstall string of the old app 93 | sUnInstallString := GetUninstallString(); 94 | if sUnInstallString <> '' then begin 95 | sUnInstallString := RemoveQuotes(sUnInstallString); 96 | if Exec(sUnInstallString, '/SILENT /NORESTART /SUPPRESSMSGBOXES','', SW_HIDE, ewWaitUntilTerminated, iResultCode) then 97 | Result := 3 98 | else 99 | Result := 2; 100 | end else 101 | Result := 1; 102 | end; 103 | 104 | ///////////////////////////////////////////////////////////////////// 105 | procedure CurStepChanged(CurStep: TSetupStep); 106 | begin 107 | if (CurStep=ssInstall) then 108 | begin 109 | if (IsUpgrade()) then 110 | begin 111 | UnInstallOldVersion(); 112 | end; 113 | end; 114 | end; -------------------------------------------------------------------------------- /cmake/module.cpp.in: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | OBS_DECLARE_MODULE(); 5 | OBS_MODULE_AUTHOR("@PROJECT_AUTHORS@"); 6 | OBS_MODULE_USE_DEFAULT_LOCALE("@PROJECT_NAME@", "en-US"); 7 | 8 | MODULE_EXPORT const char* obs_module_name() 9 | { 10 | return "@PROJECT_FULL_NAME@"; 11 | } 12 | 13 | 14 | MODULE_EXPORT const char* obs_module_description() 15 | { 16 | return "@PROJECT_DESCRIPTION@"; 17 | } 18 | -------------------------------------------------------------------------------- /cmake/modules/FindFFmpeg.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # This module defines the following variables: 3 | # 4 | # FFMPEG_FOUND - All required components and the core library were found 5 | # FFMPEG_INCLUDE_DIRS - Combined list of all components include dirs 6 | # FFMPEG_LIBRARIES - Combined list of all componenets libraries 7 | # FFMPEG_VERSION_STRING - Version of the first component requested 8 | # 9 | # For each requested component the following variables are defined: 10 | # 11 | # FFMPEG__FOUND - The component was found 12 | # FFMPEG__INCLUDE_DIRS - The components include dirs 13 | # FFMPEG__LIBRARIES - The components libraries 14 | # FFMPEG__VERSION_STRING - The components version string 15 | # FFMPEG__VERSION_MAJOR - The components major version 16 | # FFMPEG__VERSION_MINOR - The components minor version 17 | # FFMPEG__VERSION_MICRO - The components micro version 18 | # 19 | # is the uppercase name of the component 20 | 21 | 22 | find_package(PkgConfig QUIET) 23 | 24 | if(CMAKE_SIZEOF_VOID_P EQUAL 8) 25 | set(_lib_suffix 64) 26 | else() 27 | set(_lib_suffix 32) 28 | endif() 29 | 30 | function(find_ffmpeg_library component header) 31 | string(TOUPPER "${component}" component_u) 32 | set(FFMPEG_${component_u}_FOUND FALSE PARENT_SCOPE) 33 | set(FFmpeg_${component}_FOUND FALSE PARENT_SCOPE) 34 | 35 | if(PKG_CONFIG_FOUND) 36 | pkg_check_modules(PC_FFMPEG_${component} QUIET lib${component}) 37 | endif() 38 | 39 | find_path(FFMPEG_${component}_INCLUDE_DIR 40 | NAMES 41 | "lib${component}/${header}" "lib${component}/version.h" 42 | HINTS 43 | ENV FFmpegPath${_lib_suffix} 44 | ENV FFmpegPath 45 | ENV DepsPath${_lib_suffix} 46 | ENV DepsPath 47 | ${FFmpegPath${_lib_suffix}} 48 | ${FFmpegPath} 49 | ${DepsPath${_lib_suffix}} 50 | ${DepsPath} 51 | ${PC_FFMPEG_${component}_INCLUDE_DIRS} 52 | PATHS 53 | /usr/include /usr/local/include /opt/local/include /sw/include 54 | PATH_SUFFIXES ffmpeg libav include) 55 | 56 | find_library(FFMPEG_${component}_LIBRARY 57 | NAMES 58 | "${component}" "lib${component}" 59 | HINTS 60 | ENV FFmpegPath${_lib_suffix} 61 | ENV FFmpegPath 62 | ENV DepsPath${_lib_suffix} 63 | ENV DepsPath 64 | ${FFmpegPath${_lib_suffix}} 65 | ${FFmpegPath} 66 | ${DepsPath${_lib_suffix}} 67 | ${DepsPath} 68 | ${PC_FFMPEG_${component}_LIBRARY_DIRS} 69 | PATHS 70 | /usr/lib /usr/local/lib /opt/local/lib /sw/lib 71 | PATH_SUFFIXES 72 | lib${_lib_suffix} lib 73 | libs${_lib_suffix} libs 74 | bin${_lib_suffix} bin 75 | ../lib${_lib_suffix} ../lib 76 | ../libs${_lib_suffix} ../libs 77 | ../bin${_lib_suffix} ../bin) 78 | 79 | set(FFMPEG_${component_u}_INCLUDE_DIRS ${FFMPEG_${component}_INCLUDE_DIR} PARENT_SCOPE) 80 | set(FFMPEG_${component_u}_LIBRARIES ${FFMPEG_${component}_LIBRARY} PARENT_SCOPE) 81 | 82 | mark_as_advanced(FFMPEG_${component}_INCLUDE_DIR FFMPEG_${component}_LIBRARY) 83 | 84 | if(FFMPEG_${component}_INCLUDE_DIR AND FFMPEG_${component}_LIBRARY) 85 | set(FFMPEG_${component_u}_FOUND TRUE PARENT_SCOPE) 86 | set(FFmpeg_${component}_FOUND TRUE PARENT_SCOPE) 87 | 88 | list(APPEND FFMPEG_INCLUDE_DIRS ${FFMPEG_${component}_INCLUDE_DIR}) 89 | list(REMOVE_DUPLICATES FFMPEG_INCLUDE_DIRS) 90 | set(FFMPEG_INCLUDE_DIRS "${FFMPEG_INCLUDE_DIRS}" PARENT_SCOPE) 91 | 92 | list(APPEND FFMPEG_LIBRARIES ${FFMPEG_${component}_LIBRARY}) 93 | list(REMOVE_DUPLICATES FFMPEG_LIBRARIES) 94 | set(FFMPEG_LIBRARIES "${FFMPEG_LIBRARIES}" PARENT_SCOPE) 95 | 96 | set(FFMPEG_${component_u}_VERSION_STRING "unknown" PARENT_SCOPE) 97 | set(_vfile "${FFMPEG_${component}_INCLUDE_DIR}/lib${component}/version.h") 98 | 99 | if(EXISTS "${_vfile}") 100 | file(STRINGS "${_vfile}" _version_parse REGEX "^.*VERSION_(MAJOR|MINOR|MICRO)[ \t]+[0-9]+[ \t]*$") 101 | string(REGEX REPLACE ".*VERSION_MAJOR[ \t]+([0-9]+).*" "\\1" _major "${_version_parse}") 102 | string(REGEX REPLACE ".*VERSION_MINOR[ \t]+([0-9]+).*" "\\1" _minor "${_version_parse}") 103 | string(REGEX REPLACE ".*VERSION_MICRO[ \t]+([0-9]+).*" "\\1" _micro "${_version_parse}") 104 | 105 | set(FFMPEG_${component_u}_VERSION_MAJOR "${_major}" PARENT_SCOPE) 106 | set(FFMPEG_${component_u}_VERSION_MINOR "${_minor}" PARENT_SCOPE) 107 | set(FFMPEG_${component_u}_VERSION_MICRO "${_micro}" PARENT_SCOPE) 108 | 109 | set(FFMPEG_${component_u}_VERSION_STRING "${_major}.${_minor}.${_micro}" PARENT_SCOPE) 110 | else() 111 | message(STATUS "Failed parsing FFmpeg ${component} version") 112 | endif() 113 | endif() 114 | endfunction() 115 | 116 | set(FFMPEG_INCLUDE_DIRS) 117 | set(FFMPEG_LIBRARIES) 118 | 119 | if(NOT FFmpeg_FIND_COMPONENTS) 120 | message(FATAL_ERROR "No FFmpeg components requested") 121 | endif() 122 | 123 | list(GET FFmpeg_FIND_COMPONENTS 0 _first_comp) 124 | string(TOUPPER "${_first_comp}" _first_comp) 125 | 126 | foreach(component ${FFmpeg_FIND_COMPONENTS}) 127 | if(component STREQUAL "avcodec") 128 | find_ffmpeg_library("${component}" "avcodec.h") 129 | elseif(component STREQUAL "avdevice") 130 | find_ffmpeg_library("${component}" "avdevice.h") 131 | elseif(component STREQUAL "avfilter") 132 | find_ffmpeg_library("${component}" "avfilter.h") 133 | elseif(component STREQUAL "avformat") 134 | find_ffmpeg_library("${component}" "avformat.h") 135 | elseif(component STREQUAL "avresample") 136 | find_ffmpeg_library("${component}" "avresample.h") 137 | elseif(component STREQUAL "avutil") 138 | find_ffmpeg_library("${component}" "avutil.h") 139 | elseif(component STREQUAL "postproc") 140 | find_ffmpeg_library("${component}" "postprocess.h") 141 | elseif(component STREQUAL "swresample") 142 | find_ffmpeg_library("${component}" "swresample.h") 143 | elseif(component STREQUAL "swscale") 144 | find_ffmpeg_library("${component}" "swscale.h") 145 | else() 146 | message(FATAL_ERROR "Unknown FFmpeg component requested: ${component}") 147 | endif() 148 | endforeach() 149 | 150 | include(FindPackageHandleStandardArgs) 151 | find_package_handle_standard_args(FFmpeg 152 | FOUND_VAR FFMPEG_FOUND 153 | REQUIRED_VARS FFMPEG_${_first_comp}_LIBRARIES FFMPEG_${_first_comp}_INCLUDE_DIRS 154 | VERSION_VAR FFMPEG_${_first_comp}_VERSION_STRING 155 | HANDLE_COMPONENTS) 156 | -------------------------------------------------------------------------------- /cmake/util.cmake: -------------------------------------------------------------------------------- 1 | function(cacheset Name Value) 2 | get_property(V_ADVANCED CACHE "${Name}" PROPERTY ADVANCED) 3 | get_property(V_TYPE CACHE "${Name}" PROPERTY TYPE) 4 | get_property(V_HELPSTRING CACHE "${Name}" PROPERTY HELPSTRING) 5 | set(${Name} ${Value} CACHE ${V_TYPE} ${V_HELPSTRING} FORCE) 6 | if(${V_ADVANCED}) 7 | mark_as_advanced(FORCE ${Name}) 8 | endif() 9 | endfunction() 10 | 11 | function(cacheclear Name) 12 | get_property(V_ADVANCED CACHE "${Name}" PROPERTY ADVANCED) 13 | get_property(V_TYPE CACHE "${Name}" PROPERTY TYPE) 14 | get_property(V_HELPSTRING CACHE "${Name}" PROPERTY HELPSTRING) 15 | set(${Name} 0 CACHE ${V_TYPE} ${V_HELPSTRING} FORCE) 16 | if(${V_ADVANCED}) 17 | mark_as_advanced(FORCE ${Name}) 18 | endif() 19 | endfunction() -------------------------------------------------------------------------------- /cmake/version.hpp.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #define MAKE_VERSION(major,minor,patch,build) ( \ 6 | ((uint64_t(major) << 48) & 0xFFFF) \ 7 | ((uint64_t(minor) << 32) & 0xFFFF) \ 8 | ((uint64_t(patch) << 16) & 0xFFFF) \ 9 | ((uint64_t(build)) & 0xFFFF)) 10 | 11 | #define PROJECT_VERSION_MAJOR @PROJECT_VERSION_MAJOR@ 12 | #define PROJECT_VERSION_MINOR @PROJECT_VERSION_MINOR@ 13 | #define PROJECT_VERSION_PATCH @PROJECT_VERSION_PATCH@ 14 | #define PROJECT_VERSION_BUILD @PROJECT_VERSION_TWEAK@ 15 | 16 | #define PROJECT_NAME "@PROJECT_NAME@" 17 | #define PROJECT_FULL_NAME "@PROJECT_FULL_NAME@" 18 | #define PROJECT_DESCRIPTION "@PROJECT_DESCRIPTION@" 19 | 20 | struct version { 21 | union { 22 | uint64_t full; 23 | struct { 24 | uint16_t major; 25 | uint16_t minor; 26 | uint16_t patch; 27 | uint16_t build; 28 | }; 29 | }; 30 | 31 | inline bool operator==(version const& rhl) 32 | { 33 | return (rhl.full == this->full); 34 | } 35 | 36 | inline bool operator!=(version const& rhl) 37 | { 38 | return (rhl.full != this->full); 39 | } 40 | 41 | inline bool operator<(version const& rhl) 42 | { 43 | return (rhl.full < this->full); 44 | } 45 | 46 | inline bool operator<=(version const& rhl) 47 | { 48 | return (rhl.full <= this->full); 49 | } 50 | 51 | inline bool operator>(version const& rhl) 52 | { 53 | return (rhl.full > this->full); 54 | } 55 | 56 | inline bool operator>=(version const& rhl) 57 | { 58 | return (rhl.full >= this->full); 59 | } 60 | 61 | inline int32_t compare(version const& rhl, bool& major, bool& minor, bool& patch, bool& build) 62 | { 63 | if (major) { 64 | int32_t diff = (this->major - rhl.major); 65 | if (diff != 0) { 66 | major = true; 67 | minor = patch = build = false; 68 | return diff; 69 | } 70 | } 71 | if (minor) { 72 | int32_t diff = (this->minor - rhl.minor); 73 | if (diff != 0) { 74 | minor = true; 75 | major = patch = build = false; 76 | return diff; 77 | } 78 | } 79 | if (patch) { 80 | int32_t diff = (this->patch - rhl.patch); 81 | if (diff != 0) { 82 | patch = true; 83 | major = minor = build = false; 84 | return diff; 85 | } 86 | } 87 | if (build) { 88 | int32_t diff = (this->build - rhl.build); 89 | if (diff != 0) { 90 | build = true; 91 | major = minor = patch = false; 92 | return diff; 93 | } 94 | } 95 | major = minor = patch = build = false; 96 | return 0; 97 | } 98 | }; 99 | -------------------------------------------------------------------------------- /cmake/version.rc.in: -------------------------------------------------------------------------------- 1 | #pragma code_page(65001) 2 | #include 3 | #include 4 | 5 | #define VER_FILEVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,@PROJECT_VERSION_TWEAK@ 6 | #define VER_FILEVERSION_STR "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@.@PROJECT_VERSION_TWEAK@\0" 7 | 8 | #define VER_PRODUCTVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,@PROJECT_VERSION_TWEAK@ 9 | #define VER_PRODUCTVERSION_STR "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@.@PROJECT_VERSION_TWEAK@\0" 10 | 11 | #ifndef DEBUG 12 | #define VER_DEBUG 0 13 | #else 14 | #define VER_DEBUG VS_FF_DEBUG 15 | #endif 16 | 17 | VS_VERSION_INFO VERSIONINFO 18 | FILEVERSION VER_FILEVERSION 19 | PRODUCTVERSION VER_PRODUCTVERSION 20 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 21 | FILEFLAGS (VER_DEBUG) 22 | FILEOS VOS__WINDOWS32 23 | FILETYPE VFT_DLL 24 | FILESUBTYPE VFT2_UNKNOWN 25 | BEGIN 26 | BLOCK "StringFileInfo" 27 | BEGIN 28 | BLOCK "040904E4" 29 | BEGIN 30 | VALUE "CompanyName", "@PROJECT_COMPANY_NAME@\0" 31 | VALUE "FileDescription", "@PROJECT_DESCRIPTION@\0" 32 | VALUE "FileVersion", VER_FILEVERSION_STR 33 | VALUE "InternalName", "@PROJECT_NAME@\0" 34 | VALUE "LegalCopyright", "@PROJECT_COPYRIGHT@\0" 35 | VALUE "LegalTrademarks1", "@PROJECT_LEGAL_TRADEMARKS_1@\0" 36 | VALUE "LegalTrademarks2", "@PROJECT_LEGAL_TRADEMARKS_1@\0" 37 | VALUE "OriginalFilename", "@PROJECT_NAME@\0" 38 | VALUE "ProductName", "@PROJECT_PRODUCT_NAME@\0" 39 | VALUE "ProductVersion", VER_PRODUCTVERSION_STR 40 | END 41 | END 42 | 43 | BLOCK "VarFileInfo" 44 | BEGIN 45 | /* The following line should only be modified for localized versions. */ 46 | /* It consists of any number of WORD,WORD pairs, with each pair */ 47 | /* describing a language,codepage combination supported by the file. */ 48 | /* */ 49 | /* For example, a file might have values "0x409,1252" indicating that it */ 50 | /* supports English language (0x409) in the Windows ANSI codepage (1252). */ 51 | 52 | VALUE "Translation", 0x409, 1252 53 | 54 | END 55 | END -------------------------------------------------------------------------------- /data/locale/en-US.ini: -------------------------------------------------------------------------------- 1 | # Generic 2 | State.Automatic="Automatic" 3 | State.Default="Default" 4 | State.Disabled="Disabled" 5 | State.Enabled="Enabled" 6 | State.Manual="Manual" 7 | 8 | # FFmpeg 9 | FFmpeg="FFmpeg Options" 10 | FFmpeg.CustomSettings="Custom Settings" 11 | FFmpeg.CustomSettings.Description="Override any options shown (or not shown) above with your own.\nThe format is similar to that of the FFmpeg command line:\n -key=value -key2=value2 -key3='quoted value'" 12 | FFmpeg.Threads="Number of Threads" 13 | FFmpeg.Threads.Description="The number of threads to use for encoding, if supported by the encoder.\nA value of 0 is equal to 'auto-detect' and may result in excessive CPU usage." 14 | FFmpeg.ColorFormat="Override Color Format" 15 | FFmpeg.ColorFormat.Description="Overriding the color format can unlock higher quality, but might cause additional stress.\nNot all encoders support all color formats, and you might end up causing errors or corrupted video due to this." 16 | FFmpeg.StandardCompliance="Standard Compliance" 17 | FFmpeg.StandardCompliance.Description="How strict should the encoder keep to the standard? A strictness below 'Normal' may cause issues with playback." 18 | FFmpeg.StandardCompliance.VeryStrict="Very Strict" 19 | FFmpeg.StandardCompliance.Strict="Strict" 20 | FFmpeg.StandardCompliance.Normal="Normal" 21 | FFmpeg.StandardCompliance.Unofficial="Unofficial" 22 | FFmpeg.StandardCompliance.Experimental="Experimental" 23 | FFmpeg.GPU="GPU" 24 | FFmpeg.GPU.Description="For multiple GPU systems, selects which GPU to use as the main encoder" 25 | 26 | 27 | # Rate Control 28 | RateControl="Rate Control" 29 | RateControl.Mode="Method" 30 | RateControl.Mode.ConstantBitrate="Constant Bitrate" 31 | RateControl.Mode.AverageBitrate="Average Bitrate" 32 | RateControl.Mode.VariableBitrate="Variable Bitrate" 33 | RateControl.Mode.ConstantQuantizationParameter="Constant Quantization Parameter" 34 | RateControl.Mode.ConstantRateFactor="Constant Rate Factor" 35 | RateControl.Mode.ConstantQuality="Constant Quality" 36 | RateControl.Mode.VariableQuality="Variable Quality" 37 | RateControl.Mode.Lossless="Lossless" 38 | RateControl.Bitrate.Target="Target Bitrate" 39 | RateControl.Bitrate.Minimum="Minimum Bitrate" 40 | RateControl.Bitrate.Maximum="Maximum Bitrate" 41 | RateControl.Quality.Target="Target Quality" 42 | RateControl.Quality.Minimum="Minimum Quality" 43 | RateControl.Quality.Maximum="Maximum Quality" 44 | RateControl.BufferSize="Buffer Size" 45 | RateControl.BufferSize.Description="The size of the buffer to use when determining the remaining available bitrate.\nIdeally should be equal to your bitrate times the key frame interval in seconds." 46 | 47 | # Key Frames 48 | KeyFrames="Key Frames" 49 | KeyFrames.IntervalType="Interval Type" 50 | KeyFrames.IntervalType.Frames="Frames" 51 | KeyFrames.IntervalType.Seconds="Seconds" 52 | KeyFrames.IntervalType.Description="Keyframe interval type" 53 | KeyFrames.Interval.Description="Distance between key frames, in frames or seconds." 54 | KeyFrames.Interval="Interval" 55 | 56 | # Codec: H264 57 | Codec.H264="H264" 58 | Codec.H264.Profile="Profile" 59 | Codec.H264.Profile.baseline="Baseline" 60 | Codec.H264.Profile.main="Main" 61 | Codec.H264.Profile.high="High" 62 | Codec.H264.Profile.high444p="High 4:4:4 Predictive" 63 | Codec.H264.Profile.Description="H.264 profile determines which features of the codec can be used.\nHigh 4:4:4 Predictive is required for YUV 4:4:4 color space." 64 | Codec.H264.Level="Level" 65 | Codec.H264.Level.Description="Level determines the upper limits of resolution, frame rate and bitrate for the video." 66 | 67 | # Codec: HEVC 68 | Codec.HEVC="HEVC" 69 | Codec.HEVC.Profile="Profile" 70 | Codec.HEVC.Profile.main="Main" 71 | Codec.HEVC.Profile.main10="Main 10-bit" 72 | Codec.HEVC.Profile.rext="Range Extended" 73 | Codec.HEVC.Tier="Tier" 74 | Codec.HEVC.Tier.main="Main" 75 | Codec.HEVC.Tier.high="High" 76 | Codec.HEVC.Level="Level" 77 | Codec.HEVC.Level.Description="Level determines the upper limits of resolution, frame rate and bitrate for the video." 78 | 79 | # Codec: Apple ProRes 80 | Codec.ProRes.Profile="Profile" 81 | Codec.ProRes.Profile.APCO="422 Proxy/PXY (APCO)" 82 | Codec.ProRes.Profile.APCS="422 Light/LT (APCS)" 83 | Codec.ProRes.Profile.APCN="422 Standard (APCN)" 84 | Codec.ProRes.Profile.APCH="422 High Quality/HQ (APCH)" 85 | Codec.ProRes.Profile.AP4H="4444 Standard (AP4H)" 86 | Codec.ProRes.Profile.AP4X="4444 Extra Quality/XQ (AP4X)" 87 | 88 | # NVENC 89 | NVENC.Preset="Preset" 90 | NVENC.Preset.Description="Presets are NVIDIA's preconfigured default settings." 91 | NVENC.Preset.Default="Default" 92 | NVENC.Preset.Slow="Slow" 93 | NVENC.Preset.Medium="Medium" 94 | NVENC.Preset.Fast="Fast" 95 | NVENC.Preset.HighPerformance="High Performance" 96 | NVENC.Preset.HighQuality="High Quality" 97 | NVENC.Preset.BluRayDisc="BluRay Disc" 98 | NVENC.Preset.LowLatency="Low Latency" 99 | NVENC.Preset.LowLatencyHighPerformance="Low Latency High Performance" 100 | NVENC.Preset.LowLatencyHighQuality="Low Latency High Quality" 101 | NVENC.Preset.Lossless="Lossless" 102 | NVENC.Preset.LosslessHighPerformance="Lossless High Performance" 103 | NVENC.RateControl="Rate Control Options" 104 | NVENC.RateControl.Mode="Mode" 105 | NVENC.RateControl.Mode.Description="Rate control mode selection" 106 | NVENC.RateControl.Mode.CQP="Constant Quantization Parameter" 107 | NVENC.RateControl.Mode.CQP.Description="A flat compression ratio with no regard for bit rates." 108 | NVENC.RateControl.Mode.VBR="Variable Bitrate" 109 | NVENC.RateControl.Mode.VBR.Description="Sacrifices quality to stay below the upper bitrate limit,\nor saves bitrate where possible." 110 | NVENC.RateControl.Mode.VBR_HQ="High Quality Variable Bitrate" 111 | NVENC.RateControl.Mode.VBR_HQ.Description="Variable Bitrate with two-pass encoding enabled by default." 112 | NVENC.RateControl.Mode.CBR="Constant Bitrate" 113 | NVENC.RateControl.Mode.CBR.Description="Compresses footage so that it matches the target bitrate over the duration of\none second. This comes at a cost in quality during high motion scenes or\nscenes with flickering brightness like often seen in RPGs." 114 | NVENC.RateControl.Mode.CBR_HQ="High Quality Constant Bitrate" 115 | NVENC.RateControl.Mode.CBR_HQ.Description="Constant Bitrate with two-pass encoding enabled by default." 116 | NVENC.RateControl.Mode.CBR_LD_HQ="Low Delay High Quality Constant Bitrate" 117 | NVENC.RateControl.Mode.CBR_LD_HQ.Description="Constant Bitrate optimized for lowest encoding latency." 118 | NVENC.RateControl.LookAhead="Look Ahead" 119 | NVENC.RateControl.LookAhead.Description="Look ahead this many frames while encoding to better distribute bitrate.\nImproves quality slightly at the cost of some GPU time.\nSet to 0 to disable." 120 | NVENC.RateControl.AdaptiveI="Adaptive I-Frames" 121 | NVENC.RateControl.AdaptiveI.Description="Enables adaptive I-Frame insertion.\nOnly has an effect when look ahead is set to a value other than 0." 122 | NVENC.RateControl.AdaptiveB="Adaptive B-Frames" 123 | NVENC.RateControl.AdaptiveB.Description="Enables adaptive B-Frame insertion.\nOnly has an effect when look ahead is set to a value other than 0." 124 | NVENC.RateControl.TwoPass="Two Pass" 125 | NVENC.RateControl.TwoPass.Description="Enable a secondary pass for encoding, which can help with quality and bitrate stability.\nImproves quality slightly at the cost of some GPU time.\nNvidia Turing hardware might actually see a quality degrade from this." 126 | NVENC.RateControl.Bitrate="Bitrate Limits" 127 | NVENC.RateControl.Bitrate.Target="Target Bitrate" 128 | NVENC.RateControl.Bitrate.Maximum="Maximum Bitrate" 129 | NVENC.RateControl.Quality="Enable Quality Limits" 130 | NVENC.RateControl.Quality.Minimum="Minimum Quality" 131 | NVENC.RateControl.Quality.Minimum.Description="Minimum quality to achieve, with values closer to 0 being better quality." 132 | NVENC.RateControl.Quality.Maximum="Maximum Quality" 133 | NVENC.RateControl.Quality.Maximum.Description="Maximum quality to achieve, with values closer to 0 being better quality.\nSet to -1 to disable the maximum restriction." 134 | NVENC.RateControl.Quality.Target="Target Quality" 135 | NVENC.RateControl.Quality.Target.Description="Target quality to achieve, with values closer to 0 being better quality.\nSet to 0 to disable the maximum restriction." 136 | NVENC.RateControl.QP="Quantization Parameters" 137 | NVENC.RateControl.QP.I="I-Frame QP" 138 | NVENC.RateControl.QP.I.Description="Quantization parameter for I-Frames.\nSmaller values mean better quality in exchange for higher bitrate, while higher values mean less bitrate in exchange for less quality." 139 | NVENC.RateControl.QP.I.Initial="Initial I-Frame QP" 140 | NVENC.RateControl.QP.I.Initial.Description="Initial B-Frame quantization parameter.\nSet to -1 to use the automatically detected value instead." 141 | NVENC.RateControl.QP.P="P-Frame QP" 142 | NVENC.RateControl.QP.P.Description="Quantization parameter for P-Frames.\nSmaller values mean better quality in exchange for higher bitrate, while higher values mean less bitrate in exchange for less quality." 143 | NVENC.RateControl.QP.P.Initial="Initial P-Frame QP" 144 | NVENC.RateControl.QP.P.Initial.Description="Initial P-Frame quantization parameter.\nSet to -1 to use the automatically detected value instead." 145 | NVENC.RateControl.QP.B="B-Frame QP" 146 | NVENC.RateControl.QP.B.Description="Quantization parameter for B-Frames.\nSmaller values mean better quality in exchange for higher bitrate, while higher values mean less bitrate in exchange for less quality." 147 | NVENC.RateControl.QP.B.Initial="Initial B-Frame QP" 148 | NVENC.RateControl.QP.B.Initial.Description="Initial B-Frame quantization parameter.\nSet to -1 to use the automatically detected value instead." 149 | NVENC.AQ="Adaptive Quantization" 150 | NVENC.AQ.Spatial="Spatial Adaptive Quantization" 151 | NVENC.AQ.Spatial.Description="Enable spatial adaptive quantization, also sometimes referred to as Psychovisual Adaptive Quantization." 152 | NVENC.AQ.Strength="Spatial AQ Strength" 153 | NVENC.AQ.Strength.Description="Strength of the spatial adaptive quantization.\nValues closer to 15 mean more aggressive, while values closer to 1 mean more relaxed." 154 | NVENC.AQ.Temporal="Temporal Adaptive Quantization" 155 | NVENC.AQ.Temporal.Description="Enable temporal adaptive quantization." 156 | NVENC.Other="Other Options" 157 | NVENC.Other.BFrames="Maximum B-Frames" 158 | NVENC.Other.BFrames.Description="Maximum number of B-Frames to insert into the encoded bitstream.\nActual number of B-Frames may be lower depending on content and lookahead settings.\nOnly Turing NVENC supports B-Frames for HEVC." 159 | NVENC.Other.BFrameReferenceMode="B-Frame Reference Mode" 160 | NVENC.Other.BFrameReferenceMode.Each="Each B-Frame will be used for references" 161 | NVENC.Other.BFrameReferenceMode.Middle="Only (# of B-Frames)/2 will be used for references" 162 | NVENC.Other.ZeroLatency="Zero Latency" 163 | NVENC.Other.ZeroLatency.Description="Enable zero latency operation, which ensures that there is no reordering delay." 164 | NVENC.Other.WeightedPrediction="Weighted Prediction" 165 | NVENC.Other.WeightedPrediction.Description="Enable weighted prediction for encoding.\nCan't be used with B-Frames." 166 | NVENC.Other.NonReferencePFrames="Non-reference P-Frames" 167 | NVENC.Other.NonReferencePFrames.Description="Enable the automatic insertion of non-reference P-Frames." 168 | -------------------------------------------------------------------------------- /source/codecs/h264.cpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #include "h264.hpp" 23 | -------------------------------------------------------------------------------- /source/codecs/h264.hpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #pragma once 23 | #include 24 | 25 | // Codec: H264 26 | #define P_H264 "Codec.H264" 27 | #define P_H264_PROFILE "Codec.H264.Profile" 28 | #define P_H264_LEVEL "Codec.H264.Level" 29 | 30 | namespace obsffmpeg { 31 | namespace codecs { 32 | namespace h264 { 33 | enum class profile { 34 | CONSTRAINED_BASELINE, 35 | BASELINE, 36 | MAIN, 37 | HIGH, 38 | HIGH444_PREDICTIVE, 39 | UNKNOWN = -1, 40 | }; 41 | 42 | enum class level { 43 | L1_0 = 10, 44 | L1_0b, 45 | L1_1, 46 | L1_2, 47 | L1_3, 48 | L2_0 = 20, 49 | L2_1, 50 | L2_2, 51 | L3_0 = 30, 52 | L3_1, 53 | L3_2, 54 | L4_0 = 40, 55 | L4_1, 56 | L4_2, 57 | L5_0 = 50, 58 | L5_1, 59 | L5_2, 60 | L6_0 = 60, 61 | L6_1, 62 | L6_2, 63 | UNKNOWN = -1, 64 | }; 65 | } // namespace h264 66 | } // namespace codecs 67 | } // namespace obsffmpeg 68 | -------------------------------------------------------------------------------- /source/codecs/hevc.cpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #include "hevc.hpp" 23 | #include "utility.hpp" 24 | 25 | enum class nal_unit_type : uint8_t { // 6 bits 26 | TRAIL_N = 0, 27 | TRAIL_R = 1, 28 | TSA_N = 2, 29 | TSA_R = 3, 30 | STSA_N = 4, 31 | STSA_R = 5, 32 | RADL_N = 6, 33 | RADL_R = 7, 34 | RASL_N = 8, 35 | RASL_R = 9, 36 | RSV_VCL_N10 = 10, 37 | RSV_VCL_R11 = 11, 38 | RSV_VCL_N12 = 12, 39 | RSV_VCL_R13 = 13, 40 | RSV_VCL_N14 = 14, 41 | RSV_VCL_R15 = 15, 42 | BLA_W_LP = 16, 43 | BLA_W_RADL = 17, 44 | BLA_N_LP = 18, 45 | IDR_W_RADL = 19, 46 | IDR_N_LP = 20, 47 | CRA = 21, 48 | RSV_IRAP_VCL22 = 22, 49 | RSV_IRAP_VCL23 = 23, 50 | RSV_VCL24 = 24, 51 | RSV_VCL25 = 25, 52 | RSV_VCL26 = 26, 53 | RSV_VCL27 = 27, 54 | RSV_VCL28 = 28, 55 | RSV_VCL29 = 29, 56 | RSV_VCL30 = 30, 57 | RSV_VCL31 = 31, 58 | VPS = 32, 59 | SPS = 33, 60 | PPS = 34, 61 | AUD = 35, 62 | EOS = 36, 63 | EOB = 37, 64 | FD = 38, 65 | PREFIX_SEI = 39, 66 | SUFFIX_SEI = 40, 67 | RSV_NVCL41 = 41, 68 | RSV_NVCL42 = 42, 69 | RSV_NVCL43 = 43, 70 | RSV_NVCL44 = 44, 71 | RSV_NVCL45 = 45, 72 | RSV_NVCL46 = 46, 73 | RSV_NVCL47 = 47, 74 | UNSPEC48 = 48, 75 | UNSPEC49 = 49, 76 | UNSPEC50 = 50, 77 | UNSPEC51 = 51, 78 | UNSPEC52 = 52, 79 | UNSPEC53 = 53, 80 | UNSPEC54 = 54, 81 | UNSPEC55 = 55, 82 | UNSPEC56 = 56, 83 | UNSPEC57 = 57, 84 | UNSPEC58 = 58, 85 | UNSPEC59 = 59, 86 | UNSPEC60 = 60, 87 | UNSPEC61 = 61, 88 | UNSPEC62 = 62, 89 | UNSPEC63 = 63, 90 | }; 91 | 92 | struct hevc_nal_unit_header { 93 | bool zero_bit : 1; 94 | nal_unit_type nut : 6; 95 | uint8_t layer_id : 6; 96 | uint8_t temporal_id_plus1 : 3; 97 | }; 98 | 99 | struct hevc_nal { 100 | hevc_nal_unit_header* header; 101 | size_t size = 0; 102 | uint8_t* data = nullptr; 103 | }; 104 | 105 | bool is_nal(uint8_t* data, uint8_t* end) 106 | { 107 | size_t s = end - data; 108 | if (s < 4) 109 | return false; 110 | 111 | if (*data != 0x0) 112 | return false; 113 | if (*(data + 1) != 0x0) 114 | return false; 115 | if (*(data + 2) != 0x0) 116 | return false; 117 | if (*(data + 3) != 0x1) 118 | return false; 119 | 120 | return true; 121 | } 122 | 123 | bool seek_to_nal(uint8_t*& data, uint8_t* end) 124 | { 125 | if (data > end) 126 | return false; 127 | 128 | for (; data <= end; data++) { 129 | if (is_nal(data, end)) { 130 | return true; 131 | } 132 | } 133 | 134 | return false; 135 | } 136 | 137 | size_t get_nal_size(uint8_t* data, uint8_t* end) 138 | { 139 | uint8_t* ptr = data + 4; 140 | if (!seek_to_nal(ptr, end)) { 141 | return end - data; 142 | } 143 | return ptr - data; 144 | } 145 | 146 | bool is_discard_marker(uint8_t* data, uint8_t* end) 147 | { 148 | size_t s = end - data; 149 | if (s < 4) 150 | return false; 151 | 152 | if (*data != 0x0) 153 | return false; 154 | if (*(data + 1) != 0x0) 155 | return false; 156 | 157 | if (*(data + 2) == 0x3) { 158 | // Discard marker only if the next byte is not 0x0, 0x1, 0x2 or 0x3. 159 | if (*(data + 3) != 0x0) 160 | return false; 161 | if (*(data + 3) != 0x1) 162 | return false; 163 | if (*(data + 3) != 0x2) 164 | return false; 165 | if (*(data + 3) != 0x3) 166 | return false; 167 | 168 | return true; 169 | } else { 170 | if (*(data + 2) == 0x0) 171 | return true; 172 | if (*(data + 2) == 0x1) 173 | return true; 174 | if (*(data + 2) == 0x2) 175 | return true; 176 | 177 | return false; 178 | } 179 | } 180 | 181 | bool should_discard_nal(uint8_t* data, uint8_t* end) 182 | { 183 | if (data > end) 184 | return true; 185 | 186 | for (; data <= end; data++) { 187 | if (is_discard_marker(data, end)) 188 | return true; 189 | } 190 | 191 | return false; 192 | } 193 | 194 | void progress_parse(uint8_t*& ptr, uint8_t* end, size_t& sz) 195 | { 196 | ptr += sz; 197 | sz = get_nal_size(ptr, end); 198 | } 199 | 200 | void obsffmpeg::codecs::hevc::extract_header_sei(uint8_t* data, size_t sz_data, std::vector& header, 201 | std::vector& sei) 202 | { 203 | uint8_t* ptr = data; 204 | uint8_t* end = data + sz_data; 205 | 206 | // Reserve enough memory to store the entire packet data if necessary. 207 | header.reserve(sz_data); 208 | sei.reserve(sz_data); 209 | 210 | if (!seek_to_nal(ptr, end)) { 211 | return; 212 | } 213 | 214 | for (size_t nal_sz = get_nal_size(ptr, end); nal_sz > 0; progress_parse(ptr, end, nal_sz)) { 215 | if (should_discard_nal(ptr + 4, ptr + nal_sz)) { 216 | continue; 217 | } 218 | 219 | hevc_nal nal; 220 | nal.header = reinterpret_cast(ptr + 4); 221 | nal.size = nal_sz - 4 - 2; 222 | nal.data = ptr + 4 + 2; 223 | 224 | switch (nal.header->nut) { 225 | case nal_unit_type::VPS: 226 | case nal_unit_type::SPS: 227 | case nal_unit_type::PPS: 228 | header.insert(header.end(), ptr, ptr + nal_sz); 229 | break; 230 | case nal_unit_type::PREFIX_SEI: 231 | case nal_unit_type::SUFFIX_SEI: 232 | sei.insert(sei.end(), ptr, ptr + nal_sz); 233 | break; 234 | } 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /source/codecs/hevc.hpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #pragma once 23 | #include 24 | 25 | // Codec: HEVC 26 | #define P_HEVC "Codec.HEVC" 27 | #define P_HEVC_PROFILE "Codec.HEVC.Profile" 28 | #define P_HEVC_TIER "Codec.HEVC.Tier" 29 | #define P_HEVC_LEVEL "Codec.HEVC.Level" 30 | 31 | namespace obsffmpeg { 32 | namespace codecs { 33 | namespace hevc { 34 | enum class profile { 35 | MAIN, 36 | MAIN10, 37 | RANGE_EXTENDED, 38 | UNKNOWN = -1, 39 | }; 40 | 41 | enum class tier { 42 | MAIN, 43 | HIGH, 44 | UNKNOWN = -1, 45 | }; 46 | 47 | enum class level { 48 | L1_0 = 30, 49 | L2_0 = 60, 50 | L2_1 = 63, 51 | L3_0 = 90, 52 | L3_1 = 93, 53 | L4_0 = 120, 54 | L4_1 = 123, 55 | L5_0 = 150, 56 | L5_1 = 153, 57 | L5_2 = 156, 58 | L6_0 = 180, 59 | L6_1 = 183, 60 | L6_2 = 186, 61 | UNKNOWN = -1, 62 | }; 63 | 64 | void extract_header_sei(uint8_t* data, size_t sz_data, std::vector& header, 65 | std::vector& sei); 66 | 67 | } // namespace hevc 68 | } // namespace codecs 69 | } // namespace obsffmpeg 70 | -------------------------------------------------------------------------------- /source/codecs/prores.cpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #include "prores.hpp" 23 | -------------------------------------------------------------------------------- /source/codecs/prores.hpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #pragma once 23 | 24 | // Codec: ProRes 25 | #define P_PRORES "Codec.ProRes" 26 | #define P_PRORES_PROFILE "Codec.ProRes.Profile" 27 | #define P_PRORES_PROFILE_APCS "Codec.ProRes.Profile.APCS" 28 | #define P_PRORES_PROFILE_APCO "Codec.ProRes.Profile.APCO" 29 | #define P_PRORES_PROFILE_APCN "Codec.ProRes.Profile.APCN" 30 | #define P_PRORES_PROFILE_APCH "Codec.ProRes.Profile.APCH" 31 | #define P_PRORES_PROFILE_AP4H "Codec.ProRes.Profile.AP4H" 32 | #define P_PRORES_PROFILE_AP4X "Codec.ProRes.Profile.AP4X" 33 | -------------------------------------------------------------------------------- /source/encoder.hpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #pragma once 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "ffmpeg/avframe-queue.hpp" 31 | #include "ffmpeg/swscale.hpp" 32 | #include "hwapi/base.hpp" 33 | #include "ui/handler.hpp" 34 | 35 | extern "C" { 36 | #include 37 | #include 38 | #pragma warning(push) 39 | #pragma warning(disable : 4244) 40 | #include 41 | #include 42 | #pragma warning(pop) 43 | } 44 | 45 | namespace obsffmpeg { 46 | class unsupported_gpu_exception : public std::runtime_error { 47 | public: 48 | unsupported_gpu_exception(const std::string& reason) : runtime_error(reason) {} 49 | }; 50 | 51 | struct encoder_info { 52 | std::string uid; 53 | std::string codec; 54 | std::string readable_name; 55 | obs_encoder_info oei = {0}; 56 | }; 57 | 58 | class encoder_factory { 59 | encoder_info info; 60 | encoder_info info_fallback; 61 | const AVCodec* avcodec_ptr; 62 | 63 | std::shared_ptr _handler; 64 | 65 | public: 66 | encoder_factory(const AVCodec* codec); 67 | virtual ~encoder_factory(); 68 | 69 | void register_encoder(); 70 | 71 | void get_defaults(obs_data_t* settings, bool hw_encoder = false); 72 | 73 | void get_properties(obs_properties_t* props, bool hw_encoder = false); 74 | 75 | const AVCodec* get_avcodec(); 76 | 77 | const encoder_info& get_info(); 78 | 79 | const encoder_info& get_fallback(); 80 | }; 81 | 82 | class encoder { 83 | obs_encoder_t* _self; 84 | encoder_factory* _factory; 85 | 86 | const AVCodec* _codec; 87 | AVCodecContext* _context; 88 | 89 | std::shared_ptr _handler; 90 | 91 | std::shared_ptr _hwapi; 92 | std::shared_ptr _hwinst; 93 | 94 | ffmpeg::swscale _swscale; 95 | AVPacket _current_packet; 96 | 97 | size_t _lag_in_frames; 98 | size_t _count_send_frames; 99 | 100 | // Extra Data 101 | bool _have_first_frame; 102 | std::vector _extra_data; 103 | std::vector _sei_data; 104 | 105 | // Frame Stack and Queue 106 | std::stack> _free_frames; 107 | std::queue> _used_frames; 108 | std::chrono::high_resolution_clock::time_point _free_frames_last_used; 109 | 110 | void initialize_sw(obs_data_t* settings); 111 | void initialize_hw(obs_data_t* settings); 112 | 113 | void push_free_frame(std::shared_ptr frame); 114 | std::shared_ptr pop_free_frame(); 115 | 116 | void push_used_frame(std::shared_ptr frame); 117 | std::shared_ptr pop_used_frame(); 118 | 119 | public: 120 | encoder(obs_data_t* settings, obs_encoder_t* encoder, bool is_texture_encode = false); 121 | virtual ~encoder(); 122 | 123 | public: // OBS API 124 | // Shared 125 | void get_properties(obs_properties_t* props, bool hw_encode = false); 126 | 127 | bool update(obs_data_t* settings); 128 | 129 | // Audio only 130 | void get_audio_info(struct audio_convert_info* info); 131 | 132 | size_t get_frame_size(); 133 | 134 | bool audio_encode(struct encoder_frame* frame, struct encoder_packet* packet, bool* received_packet); 135 | 136 | // Video only 137 | void get_video_info(struct video_scale_info* info); 138 | 139 | bool get_sei_data(uint8_t** sei_data, size_t* size); 140 | 141 | bool get_extra_data(uint8_t** extra_data, size_t* size); 142 | 143 | bool video_encode(struct encoder_frame* frame, struct encoder_packet* packet, bool* received_packet); 144 | 145 | bool video_encode_texture(uint32_t handle, int64_t pts, uint64_t lock_key, uint64_t* next_key, 146 | struct encoder_packet* packet, bool* received_packet); 147 | 148 | int receive_packet(bool* received_packet, struct encoder_packet* packet); 149 | 150 | int send_frame(std::shared_ptr frame); 151 | 152 | bool encode_avframe(std::shared_ptr frame, struct encoder_packet* packet, 153 | bool* received_packet); 154 | 155 | public: // Handler API 156 | bool is_hardware_encode(); 157 | 158 | const AVCodec* get_avcodec(); 159 | 160 | const AVCodecContext* get_avcodeccontext(); 161 | 162 | void parse_ffmpeg_commandline(std::string text); 163 | }; 164 | } // namespace obsffmpeg 165 | -------------------------------------------------------------------------------- /source/ffmpeg/avframe-queue.cpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #include "avframe-queue.hpp" 23 | #include "tools.hpp" 24 | 25 | std::shared_ptr ffmpeg::avframe_queue::create_frame() 26 | { 27 | std::shared_ptr frame = std::shared_ptr(av_frame_alloc(), [](AVFrame* frame) { 28 | av_frame_unref(frame); 29 | av_frame_free(&frame); 30 | }); 31 | frame->width = this->resolution.first; 32 | frame->height = this->resolution.second; 33 | frame->format = this->format; 34 | 35 | int res = av_frame_get_buffer(frame.get(), 32); 36 | if (res < 0) { 37 | throw std::exception(ffmpeg::tools::get_error_description(res)); 38 | } 39 | 40 | return frame; 41 | } 42 | 43 | ffmpeg::avframe_queue::avframe_queue() {} 44 | 45 | ffmpeg::avframe_queue::~avframe_queue() 46 | { 47 | clear(); 48 | } 49 | 50 | void ffmpeg::avframe_queue::set_resolution(uint32_t const width, uint32_t const height) 51 | { 52 | this->resolution.first = width; 53 | this->resolution.second = height; 54 | } 55 | 56 | void ffmpeg::avframe_queue::get_resolution(uint32_t& width, uint32_t& height) 57 | { 58 | width = this->resolution.first; 59 | height = this->resolution.second; 60 | } 61 | 62 | uint32_t ffmpeg::avframe_queue::get_width() 63 | { 64 | return this->resolution.first; 65 | } 66 | 67 | uint32_t ffmpeg::avframe_queue::get_height() 68 | { 69 | return this->resolution.second; 70 | } 71 | 72 | void ffmpeg::avframe_queue::set_pixel_format(AVPixelFormat const format) 73 | { 74 | this->format = format; 75 | } 76 | 77 | AVPixelFormat ffmpeg::avframe_queue::get_pixel_format() 78 | { 79 | return this->format; 80 | } 81 | 82 | void ffmpeg::avframe_queue::precache(size_t count) 83 | { 84 | for (size_t n = 0; n < count; n++) { 85 | push(create_frame()); 86 | } 87 | } 88 | 89 | void ffmpeg::avframe_queue::clear() 90 | { 91 | std::unique_lock ulock(this->lock); 92 | frames.clear(); 93 | } 94 | 95 | void ffmpeg::avframe_queue::push(std::shared_ptr const frame) 96 | { 97 | std::unique_lock ulock(this->lock); 98 | frames.push_back(frame); 99 | } 100 | 101 | std::shared_ptr ffmpeg::avframe_queue::pop() 102 | { 103 | std::unique_lock ulock(this->lock); 104 | std::shared_ptr ret; 105 | while (ret == nullptr) { 106 | if (frames.size() == 0) { 107 | ret = create_frame(); 108 | } else { 109 | ret = frames.front(); 110 | if (ret == nullptr) { 111 | ret = create_frame(); 112 | } else { 113 | frames.pop_front(); 114 | if ((static_cast(ret->width) != this->resolution.first) 115 | || (static_cast(ret->height) != this->resolution.second) 116 | || (ret->format != this->format)) { 117 | ret = nullptr; 118 | } 119 | } 120 | } 121 | } 122 | return ret; 123 | } 124 | 125 | std::shared_ptr ffmpeg::avframe_queue::pop_only() 126 | { 127 | std::unique_lock ulock(this->lock); 128 | if (frames.size() == 0) { 129 | return nullptr; 130 | } 131 | std::shared_ptr ret = frames.front(); 132 | if (ret == nullptr) { 133 | return nullptr; 134 | } 135 | frames.pop_front(); 136 | return ret; 137 | } 138 | 139 | bool ffmpeg::avframe_queue::empty() 140 | { 141 | return frames.empty(); 142 | } 143 | 144 | size_t ffmpeg::avframe_queue::size() 145 | { 146 | return frames.size(); 147 | } 148 | -------------------------------------------------------------------------------- /source/ffmpeg/avframe-queue.hpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #pragma once 23 | #include 24 | #include 25 | 26 | extern "C" { 27 | #pragma warning(push) 28 | #pragma warning(disable : 4244) 29 | #include 30 | #pragma warning(pop) 31 | } 32 | 33 | namespace ffmpeg { 34 | class avframe_queue { 35 | std::deque> frames; 36 | std::mutex lock; 37 | 38 | std::pair resolution; 39 | AVPixelFormat format = AV_PIX_FMT_NONE; 40 | 41 | std::shared_ptr create_frame(); 42 | 43 | public: 44 | avframe_queue(); 45 | ~avframe_queue(); 46 | 47 | void set_resolution(uint32_t width, uint32_t height); 48 | void get_resolution(uint32_t& width, uint32_t& height); 49 | uint32_t get_width(); 50 | uint32_t get_height(); 51 | 52 | void set_pixel_format(AVPixelFormat format); 53 | AVPixelFormat get_pixel_format(); 54 | 55 | void precache(size_t count); 56 | 57 | void clear(); 58 | 59 | void push(std::shared_ptr frame); 60 | 61 | std::shared_ptr pop(); 62 | 63 | std::shared_ptr pop_only(); 64 | 65 | bool empty(); 66 | 67 | size_t size(); 68 | }; 69 | } // namespace ffmpeg 70 | -------------------------------------------------------------------------------- /source/ffmpeg/swscale.cpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #include "swscale.hpp" 23 | #include 24 | 25 | ffmpeg::swscale::swscale() {} 26 | 27 | ffmpeg::swscale::~swscale() 28 | { 29 | finalize(); 30 | } 31 | 32 | void ffmpeg::swscale::set_source_size(uint32_t width, uint32_t height) 33 | { 34 | source_size.first = width; 35 | source_size.second = height; 36 | } 37 | 38 | void ffmpeg::swscale::get_source_size(uint32_t& width, uint32_t& height) 39 | { 40 | width = this->source_size.first; 41 | height = this->source_size.second; 42 | } 43 | 44 | std::pair ffmpeg::swscale::get_source_size() 45 | { 46 | return this->source_size; 47 | } 48 | 49 | uint32_t ffmpeg::swscale::get_source_width() 50 | { 51 | return this->source_size.first; 52 | } 53 | 54 | uint32_t ffmpeg::swscale::get_source_height() 55 | { 56 | return this->source_size.second; 57 | } 58 | 59 | void ffmpeg::swscale::set_source_format(AVPixelFormat format) 60 | { 61 | source_format = format; 62 | } 63 | 64 | AVPixelFormat ffmpeg::swscale::get_source_format() 65 | { 66 | return this->source_format; 67 | } 68 | 69 | void ffmpeg::swscale::set_source_color(bool full_range, AVColorSpace space) 70 | { 71 | source_full_range = full_range; 72 | source_colorspace = space; 73 | } 74 | 75 | void ffmpeg::swscale::set_source_colorspace(AVColorSpace space) 76 | { 77 | this->source_colorspace = space; 78 | } 79 | 80 | AVColorSpace ffmpeg::swscale::get_source_colorspace() 81 | { 82 | return this->source_colorspace; 83 | } 84 | 85 | void ffmpeg::swscale::set_source_full_range(bool full_range) 86 | { 87 | this->source_full_range = full_range; 88 | } 89 | 90 | bool ffmpeg::swscale::is_source_full_range() 91 | { 92 | return this->source_full_range; 93 | } 94 | 95 | void ffmpeg::swscale::set_target_size(uint32_t width, uint32_t height) 96 | { 97 | target_size.first = width; 98 | target_size.second = height; 99 | } 100 | 101 | void ffmpeg::swscale::get_target_size(uint32_t& width, uint32_t& height) 102 | { 103 | width = target_size.first; 104 | height = target_size.second; 105 | } 106 | 107 | std::pair ffmpeg::swscale::get_target_size() 108 | { 109 | return this->target_size; 110 | } 111 | 112 | uint32_t ffmpeg::swscale::get_target_width() 113 | { 114 | return this->target_size.first; 115 | } 116 | 117 | uint32_t ffmpeg::swscale::get_target_height() 118 | { 119 | return this->target_size.second; 120 | } 121 | 122 | void ffmpeg::swscale::set_target_format(AVPixelFormat format) 123 | { 124 | target_format = format; 125 | } 126 | 127 | AVPixelFormat ffmpeg::swscale::get_target_format() 128 | { 129 | return this->target_format; 130 | } 131 | 132 | void ffmpeg::swscale::set_target_color(bool full_range, AVColorSpace space) 133 | { 134 | target_full_range = full_range; 135 | target_colorspace = space; 136 | } 137 | 138 | void ffmpeg::swscale::set_target_colorspace(AVColorSpace space) 139 | { 140 | this->target_colorspace = space; 141 | } 142 | 143 | AVColorSpace ffmpeg::swscale::get_target_colorspace() 144 | { 145 | return this->target_colorspace; 146 | } 147 | 148 | void ffmpeg::swscale::set_target_full_range(bool full_range) 149 | { 150 | this->target_full_range = full_range; 151 | } 152 | 153 | bool ffmpeg::swscale::is_target_full_range() 154 | { 155 | return this->target_full_range; 156 | } 157 | 158 | bool ffmpeg::swscale::initialize(int flags) 159 | { 160 | if (this->context) { 161 | return false; 162 | } 163 | if (source_size.first == 0 || source_size.second == 0 || source_format == AV_PIX_FMT_NONE 164 | || source_colorspace == AVCOL_SPC_UNSPECIFIED) { 165 | throw std::invalid_argument("not all source parameters were set"); 166 | } 167 | if (target_size.first == 0 || target_size.second == 0 || target_format == AV_PIX_FMT_NONE 168 | || target_colorspace == AVCOL_SPC_UNSPECIFIED) { 169 | throw std::invalid_argument("not all target parameters were set"); 170 | } 171 | 172 | this->context = sws_getContext(source_size.first, source_size.second, source_format, target_size.first, 173 | target_size.second, target_format, flags, nullptr, nullptr, nullptr); 174 | if (!this->context) { 175 | return false; 176 | } 177 | 178 | sws_setColorspaceDetails(this->context, sws_getCoefficients(source_colorspace), source_full_range ? 1 : 0, 179 | sws_getCoefficients(target_colorspace), target_full_range ? 1 : 0, 1L << 16 | 0L, 180 | 1L << 16 | 0L, 1L << 16 | 0L); 181 | 182 | return true; 183 | } 184 | 185 | bool ffmpeg::swscale::finalize() 186 | { 187 | if (this->context) { 188 | sws_freeContext(this->context); 189 | this->context = nullptr; 190 | return true; 191 | } 192 | return false; 193 | } 194 | 195 | int32_t ffmpeg::swscale::convert(const uint8_t* const source_data[], const int source_stride[], int32_t source_row, 196 | int32_t source_rows, uint8_t* const target_data[], const int target_stride[]) 197 | { 198 | if (!this->context) { 199 | return 0; 200 | } 201 | int height = 202 | sws_scale(this->context, source_data, source_stride, source_row, source_rows, target_data, target_stride); 203 | return height; 204 | } 205 | -------------------------------------------------------------------------------- /source/ffmpeg/swscale.hpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #ifndef OBS_FFMPEG_FFMPEG_SWSCALE 23 | #define OBS_FFMPEG_FFMPEG_SWSCALE 24 | #pragma once 25 | 26 | #include 27 | #include 28 | 29 | extern "C" { 30 | #pragma warning(push) 31 | #pragma warning(disable : 4244) 32 | #include 33 | #include 34 | #pragma warning(pop) 35 | } 36 | 37 | namespace ffmpeg { 38 | class swscale { 39 | std::pair source_size; 40 | AVPixelFormat source_format = AV_PIX_FMT_NONE; 41 | bool source_full_range = false; 42 | AVColorSpace source_colorspace = AVCOL_SPC_UNSPECIFIED; 43 | 44 | std::pair target_size; 45 | AVPixelFormat target_format = AV_PIX_FMT_NONE; 46 | bool target_full_range = false; 47 | AVColorSpace target_colorspace = AVCOL_SPC_UNSPECIFIED; 48 | 49 | SwsContext* context = nullptr; 50 | 51 | public: 52 | swscale(); 53 | ~swscale(); 54 | 55 | void set_source_size(uint32_t width, uint32_t height); 56 | void get_source_size(uint32_t& width, uint32_t& height); 57 | std::pair get_source_size(); 58 | uint32_t get_source_width(); 59 | uint32_t get_source_height(); 60 | void set_source_format(AVPixelFormat format); 61 | AVPixelFormat get_source_format(); 62 | void set_source_color(bool full_range, AVColorSpace space); 63 | void set_source_colorspace(AVColorSpace space); 64 | AVColorSpace get_source_colorspace(); 65 | void set_source_full_range(bool full_range); 66 | bool is_source_full_range(); 67 | 68 | void set_target_size(uint32_t width, uint32_t height); 69 | void get_target_size(uint32_t& width, uint32_t& height); 70 | std::pair get_target_size(); 71 | uint32_t get_target_width(); 72 | uint32_t get_target_height(); 73 | void set_target_format(AVPixelFormat format); 74 | AVPixelFormat get_target_format(); 75 | void set_target_color(bool full_range, AVColorSpace space); 76 | void set_target_colorspace(AVColorSpace space); 77 | AVColorSpace get_target_colorspace(); 78 | void set_target_full_range(bool full_range); 79 | bool is_target_full_range(); 80 | 81 | bool initialize(int flags); 82 | bool finalize(); 83 | 84 | int32_t convert(const uint8_t* const source_data[], const int source_stride[], int32_t source_row, 85 | int32_t source_rows, uint8_t* const target_data[], const int target_stride[]); 86 | }; 87 | } // namespace ffmpeg 88 | 89 | #endif OBS_FFMPEG_FFMPEG_SWSCALE 90 | -------------------------------------------------------------------------------- /source/ffmpeg/tools.cpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #include "tools.hpp" 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "plugin.hpp" 28 | #include "utility.hpp" 29 | 30 | extern "C" { 31 | #pragma warning(push) 32 | #pragma warning(disable : 4244) 33 | #include 34 | #include 35 | #include 36 | #include 37 | #pragma warning(pop) 38 | } 39 | 40 | std::string ffmpeg::tools::translate_encoder_capabilities(int capabilities) 41 | { 42 | // Sorted by relative importance. 43 | std::pair caps[] = { 44 | {AV_CODEC_CAP_EXPERIMENTAL, "Experimental"}, 45 | 46 | // Quality 47 | {AV_CODEC_CAP_LOSSLESS, "Lossless"}, 48 | 49 | // Features 50 | {AV_CODEC_CAP_PARAM_CHANGE, "Dynamic Parameter Change"}, 51 | {AV_CODEC_CAP_SUBFRAMES, "Sub-Frames"}, 52 | {AV_CODEC_CAP_VARIABLE_FRAME_SIZE, "Variable Frame Size"}, 53 | {AV_CODEC_CAP_SMALL_LAST_FRAME, "Small Final Frame"}, 54 | 55 | // Other 56 | {AV_CODEC_CAP_TRUNCATED, "Truncated"}, 57 | {AV_CODEC_CAP_CHANNEL_CONF, "AV_CODEC_CAP_CHANNEL_CONF"}, 58 | {AV_CODEC_CAP_DRAW_HORIZ_BAND, "AV_CODEC_CAP_DRAW_HORIZ_BAND"}, 59 | {AV_CODEC_CAP_AVOID_PROBING, "AV_CODEC_CAP_AVOID_PROBING"}, 60 | }; 61 | 62 | std::stringstream sstr; 63 | for (auto const kv : caps) { 64 | if (capabilities & kv.first) { 65 | capabilities &= ~kv.first; 66 | sstr << kv.second; 67 | if (capabilities != 0) { 68 | sstr << ", "; 69 | } else { 70 | break; 71 | } 72 | } 73 | } 74 | 75 | return sstr.str(); 76 | } 77 | 78 | const char* ffmpeg::tools::get_pixel_format_name(AVPixelFormat v) 79 | { 80 | return av_get_pix_fmt_name(v); 81 | } 82 | 83 | const char* ffmpeg::tools::get_color_space_name(AVColorSpace v) 84 | { 85 | switch (v) { 86 | case AVCOL_SPC_RGB: 87 | return "RGB"; 88 | case AVCOL_SPC_BT709: 89 | return "BT.709"; 90 | case AVCOL_SPC_FCC: 91 | return "FCC Title 47 CoFR 73.682 (a)(20)"; 92 | case AVCOL_SPC_BT470BG: 93 | return "BT.601 625"; 94 | case AVCOL_SPC_SMPTE170M: 95 | case AVCOL_SPC_SMPTE240M: 96 | return "BT.601 525"; 97 | case AVCOL_SPC_YCGCO: 98 | return "ITU-T SG16"; 99 | case AVCOL_SPC_BT2020_NCL: 100 | return "BT.2020 NCL"; 101 | case AVCOL_SPC_BT2020_CL: 102 | return "BT.2020 CL"; 103 | case AVCOL_SPC_SMPTE2085: 104 | return "SMPTE 2085"; 105 | case AVCOL_SPC_CHROMA_DERIVED_NCL: 106 | return "Chroma NCL"; 107 | case AVCOL_SPC_CHROMA_DERIVED_CL: 108 | return "Chroma CL"; 109 | case AVCOL_SPC_ICTCP: 110 | return "BT.2100"; 111 | case AVCOL_SPC_NB: 112 | return "Not Part of ABI"; 113 | } 114 | return "Unknown"; 115 | } 116 | 117 | const char* ffmpeg::tools::get_error_description(int error) 118 | { 119 | thread_local char error_buf[AV_ERROR_MAX_STRING_SIZE + 1]; 120 | if (av_strerror(error, error_buf, AV_ERROR_MAX_STRING_SIZE) < 0) { 121 | snprintf(error_buf, AV_ERROR_MAX_STRING_SIZE, "Unknown Error (%i)", error); 122 | } 123 | return error_buf; 124 | } 125 | 126 | static std::map obs_to_av_format_map = { 127 | {VIDEO_FORMAT_I420, AV_PIX_FMT_YUV420P}, // YUV 4:2:0 128 | {VIDEO_FORMAT_NV12, AV_PIX_FMT_NV12}, // NV12 Packed YUV 129 | {VIDEO_FORMAT_YVYU, AV_PIX_FMT_YVYU422}, // YVYU Packed YUV 130 | {VIDEO_FORMAT_YUY2, AV_PIX_FMT_YUYV422}, // YUYV Packed YUV 131 | {VIDEO_FORMAT_UYVY, AV_PIX_FMT_UYVY422}, // UYVY Packed YUV 132 | {VIDEO_FORMAT_RGBA, AV_PIX_FMT_RGBA}, // 133 | {VIDEO_FORMAT_BGRA, AV_PIX_FMT_BGRA}, // 134 | {VIDEO_FORMAT_BGRX, AV_PIX_FMT_BGR0}, // 135 | {VIDEO_FORMAT_Y800, AV_PIX_FMT_GRAY8}, // 136 | {VIDEO_FORMAT_I444, AV_PIX_FMT_YUV444P}, // 137 | {VIDEO_FORMAT_BGR3, AV_PIX_FMT_BGR24}, // 138 | {VIDEO_FORMAT_I422, AV_PIX_FMT_YUV422P}, // 139 | {VIDEO_FORMAT_I40A, AV_PIX_FMT_YUVA420P}, // 140 | {VIDEO_FORMAT_I42A, AV_PIX_FMT_YUVA422P}, // 141 | {VIDEO_FORMAT_YUVA, AV_PIX_FMT_YUVA444P}, // 142 | //{VIDEO_FORMAT_AYUV, AV_PIX_FMT_AYUV444P}, // 143 | }; 144 | 145 | AVPixelFormat ffmpeg::tools::obs_videoformat_to_avpixelformat(video_format v) 146 | { 147 | auto found = obs_to_av_format_map.find(v); 148 | if (found != obs_to_av_format_map.end()) { 149 | return found->second; 150 | } 151 | return AV_PIX_FMT_NONE; 152 | } 153 | 154 | video_format ffmpeg::tools::avpixelformat_to_obs_videoformat(AVPixelFormat v) 155 | { 156 | for (const auto& kv : obs_to_av_format_map) { 157 | if (kv.second == v) 158 | return kv.first; 159 | } 160 | return VIDEO_FORMAT_NONE; 161 | } 162 | 163 | AVPixelFormat ffmpeg::tools::get_least_lossy_format(const AVPixelFormat* haystack, AVPixelFormat needle) 164 | { 165 | int data_loss = 0; 166 | return avcodec_find_best_pix_fmt_of_list(haystack, needle, 0, &data_loss); 167 | } 168 | 169 | AVColorSpace ffmpeg::tools::obs_videocolorspace_to_avcolorspace(video_colorspace v) 170 | { 171 | switch (v) { 172 | case VIDEO_CS_DEFAULT: 173 | case VIDEO_CS_709: 174 | return AVCOL_SPC_BT709; 175 | case VIDEO_CS_601: 176 | return AVCOL_SPC_BT470BG; 177 | } 178 | throw std::invalid_argument("unknown color space"); 179 | } 180 | 181 | AVColorRange ffmpeg::tools::obs_videorangetype_to_avcolorrange(video_range_type v) 182 | { 183 | switch (v) { 184 | case VIDEO_RANGE_DEFAULT: 185 | case VIDEO_RANGE_PARTIAL: 186 | return AVCOL_RANGE_MPEG; 187 | case VIDEO_RANGE_FULL: 188 | return AVCOL_RANGE_JPEG; 189 | } 190 | throw std::invalid_argument("unknown range"); 191 | } 192 | 193 | bool ffmpeg::tools::can_hardware_encode(const AVCodec* codec) 194 | { 195 | AVPixelFormat hardware_formats[] = {AV_PIX_FMT_D3D11}; 196 | 197 | for (const AVPixelFormat* fmt = codec->pix_fmts; (fmt != nullptr) && (*fmt != AV_PIX_FMT_NONE); fmt++) { 198 | for (auto cmp : hardware_formats) { 199 | if (*fmt == cmp) { 200 | return true; 201 | } 202 | } 203 | } 204 | return false; 205 | } 206 | 207 | std::vector ffmpeg::tools::get_software_formats(const AVPixelFormat* list) 208 | { 209 | AVPixelFormat hardware_formats[] = { 210 | #if FF_API_VAAPI 211 | AV_PIX_FMT_VAAPI_MOCO, 212 | AV_PIX_FMT_VAAPI_IDCT, 213 | #endif 214 | AV_PIX_FMT_VAAPI, 215 | AV_PIX_FMT_DXVA2_VLD, 216 | AV_PIX_FMT_VDPAU, 217 | AV_PIX_FMT_QSV, 218 | AV_PIX_FMT_MMAL, 219 | AV_PIX_FMT_D3D11VA_VLD, 220 | AV_PIX_FMT_CUDA, 221 | AV_PIX_FMT_XVMC, 222 | AV_PIX_FMT_VIDEOTOOLBOX, 223 | AV_PIX_FMT_MEDIACODEC, 224 | AV_PIX_FMT_D3D11, 225 | AV_PIX_FMT_OPENCL, 226 | }; 227 | 228 | std::vector fmts; 229 | for (auto fmt = list; fmt && (*fmt != AV_PIX_FMT_NONE); fmt++) { 230 | bool is_blacklisted = false; 231 | for (auto blacklisted : hardware_formats) { 232 | if (*fmt == blacklisted) 233 | is_blacklisted = true; 234 | } 235 | if (!is_blacklisted) 236 | fmts.push_back(*fmt); 237 | } 238 | 239 | fmts.push_back(AV_PIX_FMT_NONE); 240 | 241 | return std::move(fmts); 242 | } 243 | 244 | void ffmpeg::tools::setup_obs_color(video_colorspace colorspace, video_range_type range, AVCodecContext* context) 245 | { 246 | std::map> 247 | colorspaces = { 248 | {VIDEO_CS_DEFAULT, {AVCOL_SPC_BT470BG, AVCOL_PRI_BT470BG, AVCOL_TRC_SMPTE170M}}, 249 | {VIDEO_CS_601, {AVCOL_SPC_BT470BG, AVCOL_PRI_BT470BG, AVCOL_TRC_SMPTE170M}}, 250 | {VIDEO_CS_709, {AVCOL_SPC_BT709, AVCOL_PRI_BT709, AVCOL_TRC_BT709}}, 251 | }; 252 | std::map colorranges = { 253 | {VIDEO_RANGE_DEFAULT, AVCOL_RANGE_MPEG}, 254 | {VIDEO_RANGE_PARTIAL, AVCOL_RANGE_MPEG}, 255 | {VIDEO_RANGE_FULL, AVCOL_RANGE_JPEG}, 256 | }; 257 | 258 | { 259 | auto found = colorspaces.find(colorspace); 260 | if (found != colorspaces.end()) { 261 | context->colorspace = std::get(found->second); 262 | context->color_primaries = std::get(found->second); 263 | context->color_trc = std::get(found->second); 264 | } 265 | } 266 | { 267 | auto found = colorranges.find(range); 268 | if (found != colorranges.end()) { 269 | context->color_range = found->second; 270 | } 271 | } 272 | 273 | // Downscaling should result in downscaling, not pixelation 274 | context->chroma_sample_location = AVCHROMA_LOC_CENTER; 275 | } 276 | 277 | const char* ffmpeg::tools::get_std_compliance_name(int compliance) 278 | { 279 | switch (compliance) { 280 | case FF_COMPLIANCE_VERY_STRICT: 281 | return "Very Strict"; 282 | case FF_COMPLIANCE_STRICT: 283 | return "Strict"; 284 | case FF_COMPLIANCE_NORMAL: 285 | return "Normal"; 286 | case FF_COMPLIANCE_UNOFFICIAL: 287 | return "Unofficial"; 288 | case FF_COMPLIANCE_EXPERIMENTAL: 289 | return "Experimental"; 290 | } 291 | return "Invalid"; 292 | } 293 | 294 | const char* ffmpeg::tools::get_thread_type_name(int thread_type) 295 | { 296 | switch (thread_type) { 297 | case FF_THREAD_FRAME | FF_THREAD_SLICE: 298 | return "Slice & Frame"; 299 | case FF_THREAD_FRAME: 300 | return "Frame"; 301 | case FF_THREAD_SLICE: 302 | return "Slice"; 303 | default: 304 | return "None"; 305 | } 306 | } 307 | 308 | void ffmpeg::tools::print_av_option_bool(AVCodecContext* context, const char* option, std::string text) 309 | { 310 | if (av_opt_is_set_to_default_by_name(context, option, AV_OPT_SEARCH_CHILDREN) == 0) { 311 | int64_t v = 0; 312 | if (av_opt_get_int(context, option, AV_OPT_SEARCH_CHILDREN, &v) == 0) { 313 | PLOG_INFO("[%s] %s: %s", context->codec->name, text.c_str(), v == 0 ? "Disabled" : "Enabled"); 314 | } else { 315 | PLOG_INFO("[%s] %s: ", context->codec->name, text.c_str()); 316 | } 317 | } else { 318 | PLOG_INFO("[%s] %s: ", context->codec->name, text.c_str()); 319 | } 320 | } 321 | 322 | void ffmpeg::tools::print_av_option_int(AVCodecContext* context, const char* option, std::string text, 323 | std::string suffix) 324 | { 325 | if (av_opt_is_set_to_default_by_name(context, option, AV_OPT_SEARCH_CHILDREN) == 0) { 326 | int64_t v = 0; 327 | if (av_opt_get_int(context, option, AV_OPT_SEARCH_CHILDREN, &v) == 0) { 328 | PLOG_INFO("[%s] %s: %lld %s", context->codec->name, text.c_str(), v, suffix.c_str()); 329 | } else { 330 | PLOG_INFO("[%s] %s: ", context->codec->name, text.c_str()); 331 | } 332 | } else { 333 | PLOG_INFO("[%s] %s: ", context->codec->name, text.c_str()); 334 | } 335 | } 336 | 337 | void ffmpeg::tools::print_av_option_string(AVCodecContext* context, const char* option, std::string text, 338 | std::function decoder) 339 | { 340 | if (av_opt_is_set_to_default_by_name(context, option, AV_OPT_SEARCH_CHILDREN) == 0) { 341 | int64_t v = 0; 342 | if (av_opt_get_int(context, option, AV_OPT_SEARCH_CHILDREN, &v) == 0) { 343 | std::string name = ""; 344 | if (decoder) 345 | name = decoder(v); 346 | PLOG_INFO("[%s] %s: %s", context->codec->name, text.c_str(), name.c_str()); 347 | } else { 348 | PLOG_INFO("[%s] %s: ", context->codec->name, text.c_str()); 349 | } 350 | } else { 351 | PLOG_INFO("[%s] %s: ", context->codec->name, text.c_str()); 352 | } 353 | } 354 | -------------------------------------------------------------------------------- /source/ffmpeg/tools.hpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #ifndef OBS_FFMPEG_FFMPEG_UTILITY 23 | #define OBS_FFMPEG_FFMPEG_UTILITY 24 | #pragma once 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | extern "C" { 32 | #include 33 | #include 34 | } 35 | 36 | namespace ffmpeg { 37 | namespace tools { 38 | std::string translate_encoder_capabilities(int capabilities); 39 | 40 | const char* get_pixel_format_name(AVPixelFormat v); 41 | 42 | const char* get_color_space_name(AVColorSpace v); 43 | 44 | const char* get_error_description(int error); 45 | 46 | AVPixelFormat obs_videoformat_to_avpixelformat(video_format v); 47 | 48 | video_format avpixelformat_to_obs_videoformat(AVPixelFormat v); 49 | 50 | AVPixelFormat get_least_lossy_format(const AVPixelFormat* haystack, AVPixelFormat needle); 51 | 52 | AVColorSpace obs_videocolorspace_to_avcolorspace(video_colorspace v); 53 | 54 | AVColorRange obs_videorangetype_to_avcolorrange(video_range_type v); 55 | 56 | bool can_hardware_encode(const AVCodec* codec); 57 | 58 | std::vector get_software_formats(const AVPixelFormat* list); 59 | 60 | void setup_obs_color(video_colorspace colorspace, video_range_type range, AVCodecContext* context); 61 | 62 | const char* get_std_compliance_name(int compliance); 63 | 64 | const char* get_thread_type_name(int thread_type); 65 | 66 | void print_av_option_bool(AVCodecContext* context, const char* option, std::string text); 67 | 68 | void print_av_option_int(AVCodecContext* context, const char* option, std::string text, 69 | std::string suffix); 70 | 71 | void print_av_option_string(AVCodecContext* context, const char* option, std::string text, 72 | std::function decoder); 73 | 74 | } // namespace tools 75 | } // namespace ffmpeg 76 | 77 | #endif OBS_FFMPEG_FFMPEG_UTILITY 78 | -------------------------------------------------------------------------------- /source/hwapi/base.cpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #include "base.hpp" 23 | -------------------------------------------------------------------------------- /source/hwapi/base.hpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #pragma once 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | extern "C" { 31 | #pragma warning(push) 32 | #pragma warning(disable : 4244) 33 | #include 34 | #include 35 | #pragma warning(pop) 36 | } 37 | 38 | namespace obsffmpeg { 39 | namespace hwapi { 40 | struct device { 41 | std::pair id; 42 | std::string name; 43 | }; 44 | 45 | class instance; 46 | 47 | class base { 48 | public: 49 | virtual std::list enumerate_adapters() = 0; 50 | 51 | virtual std::shared_ptr create(obsffmpeg::hwapi::device target) = 0; 52 | 53 | virtual std::shared_ptr create_from_obs() = 0; 54 | }; 55 | 56 | class instance { 57 | public: 58 | virtual AVBufferRef* create_device_context() = 0; 59 | 60 | virtual std::shared_ptr allocate_frame(AVBufferRef* frames) = 0; 61 | 62 | virtual void copy_from_obs(AVBufferRef* frames, uint32_t handle, uint64_t lock_key, 63 | uint64_t* next_lock_key, std::shared_ptr frame) = 0; 64 | 65 | virtual std::shared_ptr avframe_from_obs(AVBufferRef* frames, uint32_t handle, 66 | uint64_t lock_key, 67 | uint64_t* next_lock_key) = 0; 68 | }; 69 | } // namespace hwapi 70 | } // namespace obsffmpeg 71 | -------------------------------------------------------------------------------- /source/hwapi/d3d11.cpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #include "d3d11.hpp" 23 | #include 24 | #include 25 | #include "utility.hpp" 26 | 27 | extern "C" { 28 | #pragma warning(push) 29 | #pragma warning(disable : 4244) 30 | #include 31 | #include 32 | #include 33 | #pragma warning(pop) 34 | } 35 | 36 | obsffmpeg::hwapi::d3d11::d3d11() : _dxgi_module(0), _d3d11_module(0) 37 | { 38 | _dxgi_module = LoadLibraryW(L"dxgi.dll"); 39 | if (!_dxgi_module) 40 | throw std::runtime_error("Unable to load DXGI"); 41 | 42 | _d3d11_module = LoadLibraryW(L"d3d11.dll"); 43 | if (!_d3d11_module) 44 | throw std::runtime_error("Unable to load D3D11"); 45 | 46 | _CreateDXGIFactory = reinterpret_cast(GetProcAddress(_dxgi_module, "CreateDXGIFactory")); 47 | _CreateDXGIFactory1 = 48 | reinterpret_cast(GetProcAddress(_dxgi_module, "CreateDXGIFactory1")); 49 | _D3D11CreateDevice = reinterpret_cast(GetProcAddress(_d3d11_module, "D3D11CreateDevice")); 50 | 51 | if (!_CreateDXGIFactory && !_CreateDXGIFactory1) 52 | throw std::runtime_error("DXGI not supported"); 53 | 54 | if (!_D3D11CreateDevice) 55 | throw std::runtime_error("D3D11 not supported"); 56 | 57 | HRESULT hr = _CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&_dxgifactory); 58 | if (FAILED(hr)) { 59 | std::stringstream sstr; 60 | sstr << "Failed to create DXGI Factory (" << hr << ")"; 61 | throw std::runtime_error(sstr.str()); 62 | } 63 | } 64 | 65 | obsffmpeg::hwapi::d3d11::~d3d11() 66 | { 67 | FreeLibrary(_dxgi_module); 68 | FreeLibrary(_d3d11_module); 69 | } 70 | 71 | std::list obsffmpeg::hwapi::d3d11::enumerate_adapters() 72 | { 73 | std::list adapters; 74 | 75 | // Enumerate Adapters 76 | IDXGIAdapter1* dxgi_adapter = nullptr; 77 | for (UINT idx = 0; !FAILED(_dxgifactory->EnumAdapters1(idx, &dxgi_adapter)); idx++) { 78 | DXGI_ADAPTER_DESC1 desc = DXGI_ADAPTER_DESC1(); 79 | dxgi_adapter->GetDesc1(&desc); 80 | 81 | std::vector buf(1024); 82 | size_t len = snprintf(buf.data(), buf.size(), "%ls (VEN_%04x/DEV_%04x/SUB_%04x/REV_%04x)", 83 | desc.Description, desc.VendorId, desc.DeviceId, desc.SubSysId, desc.Revision); 84 | 85 | device dev; 86 | dev.name = std::string(buf.data(), buf.data() + len); 87 | dev.id.first = desc.AdapterLuid.HighPart; 88 | dev.id.second = desc.AdapterLuid.LowPart; 89 | 90 | adapters.push_back(dev); 91 | } 92 | 93 | return std::move(adapters); 94 | } 95 | 96 | std::shared_ptr obsffmpeg::hwapi::d3d11::create(obsffmpeg::hwapi::device target) 97 | { 98 | std::shared_ptr inst; 99 | ATL::CComPtr device; 100 | ATL::CComPtr context; 101 | IDXGIAdapter1* adapter = nullptr; 102 | 103 | // Find the correct "Adapter" (device). 104 | IDXGIAdapter1* dxgi_adapter = nullptr; 105 | for (UINT idx = 0; !FAILED(_dxgifactory->EnumAdapters1(idx, &dxgi_adapter)); idx++) { 106 | DXGI_ADAPTER_DESC1 desc = DXGI_ADAPTER_DESC1(); 107 | dxgi_adapter->GetDesc1(&desc); 108 | 109 | if ((desc.AdapterLuid.LowPart == target.id.second) && (desc.AdapterLuid.HighPart == target.id.first)) { 110 | adapter = dxgi_adapter; 111 | break; 112 | } 113 | } 114 | 115 | // Create a D3D11 Device 116 | UINT device_flags = D3D11_CREATE_DEVICE_VIDEO_SUPPORT; 117 | std::vector feature_levels = {D3D_FEATURE_LEVEL_12_1, D3D_FEATURE_LEVEL_12_0, 118 | D3D_FEATURE_LEVEL_11_1}; 119 | 120 | if (FAILED(_D3D11CreateDevice(adapter, D3D_DRIVER_TYPE_HARDWARE, NULL, device_flags, feature_levels.data(), 121 | static_cast(feature_levels.size()), D3D11_SDK_VERSION, &device, NULL, 122 | &context))) { 123 | throw std::runtime_error("Failed to create D3D11 device for target."); 124 | } 125 | 126 | return std::make_shared(device, context); 127 | } 128 | 129 | std::shared_ptr obsffmpeg::hwapi::d3d11::create_from_obs() 130 | { 131 | auto gctx = obsffmpeg::obs_graphics(); 132 | 133 | if (GS_DEVICE_DIRECT3D_11 != gs_get_device_type()) { 134 | throw std::runtime_error("OBS Device is not a D3D11 Device."); 135 | } 136 | 137 | ATL::CComPtr device = 138 | ATL::CComPtr(reinterpret_cast(gs_get_device_obj())); 139 | ATL::CComPtr context; 140 | device->GetImmediateContext(&context); 141 | 142 | return std::make_shared(device, context); 143 | } 144 | 145 | struct D3D11AVFrame { 146 | ATL::CComPtr handle; 147 | }; 148 | 149 | obsffmpeg::hwapi::d3d11_instance::d3d11_instance(ATL::CComPtr device, 150 | ATL::CComPtr context) 151 | { 152 | _device = device; 153 | _context = context; 154 | } 155 | 156 | obsffmpeg::hwapi::d3d11_instance::~d3d11_instance() {} 157 | 158 | AVBufferRef* obsffmpeg::hwapi::d3d11_instance::create_device_context() 159 | { 160 | AVBufferRef* dctx_ref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_D3D11VA); 161 | if (!dctx_ref) 162 | throw std::runtime_error("Failed to allocate AVHWDeviceContext."); 163 | 164 | AVHWDeviceContext* dctx = reinterpret_cast(dctx_ref->data); 165 | AVD3D11VADeviceContext* d3d11va = reinterpret_cast(dctx->hwctx); 166 | 167 | // TODO: Determine if these need an additional reference. 168 | d3d11va->device = _device; 169 | d3d11va->device->AddRef(); 170 | d3d11va->device_context = _context; 171 | d3d11va->device_context->AddRef(); 172 | d3d11va->lock = [](void*) { obs_enter_graphics(); }; 173 | d3d11va->unlock = [](void*) { obs_leave_graphics(); }; 174 | 175 | int ret = av_hwdevice_ctx_init(dctx_ref); 176 | if (ret < 0) 177 | throw std::runtime_error("Failed to initialize AVHWDeviceContext."); 178 | 179 | return dctx_ref; 180 | } 181 | 182 | std::shared_ptr obsffmpeg::hwapi::d3d11_instance::allocate_frame(AVBufferRef* frames) 183 | { 184 | auto gctx = obsffmpeg::obs_graphics(); 185 | 186 | auto frame = std::shared_ptr(av_frame_alloc(), [](AVFrame* frame) { 187 | av_frame_unref(frame); 188 | av_frame_free(&frame); 189 | }); 190 | 191 | if (av_hwframe_get_buffer(frames, frame.get(), 0) < 0) { 192 | throw std::runtime_error("Failed to create AVFrame."); 193 | } 194 | 195 | return frame; 196 | } 197 | 198 | void obsffmpeg::hwapi::d3d11_instance::copy_from_obs(AVBufferRef*, uint32_t handle, uint64_t lock_key, 199 | uint64_t* next_lock_key, std::shared_ptr frame) 200 | { 201 | auto gctx = obsffmpeg::obs_graphics(); 202 | 203 | ATL::CComPtr mutex; 204 | ATL::CComPtr input; 205 | 206 | if (FAILED(_device->OpenSharedResource(reinterpret_cast(static_cast(handle)), 207 | __uuidof(ID3D11Texture2D), reinterpret_cast(&input)))) { 208 | throw std::runtime_error("Failed to open shared texture resource."); 209 | } 210 | 211 | if (FAILED(input->QueryInterface(__uuidof(IDXGIKeyedMutex), reinterpret_cast(&mutex)))) { 212 | throw std::runtime_error("Failed to retrieve mutex for texture resource."); 213 | } 214 | 215 | if (FAILED(mutex->AcquireSync(lock_key, 1000))) { 216 | throw std::runtime_error("Failed to acquire lock on input texture."); 217 | } 218 | 219 | // Set some parameters on the input texture, and get its description. 220 | UINT evict = input->GetEvictionPriority(); 221 | input->SetEvictionPriority(DXGI_RESOURCE_PRIORITY_MAXIMUM); 222 | 223 | // Clone the content of the input texture. 224 | _context->CopyResource(reinterpret_cast(frame->data[0]), input); 225 | 226 | // Restore original parameters on input. 227 | input->SetEvictionPriority(evict); 228 | 229 | if (FAILED(mutex->ReleaseSync(lock_key))) { 230 | throw std::runtime_error("Failed to release lock on input texture."); 231 | } 232 | 233 | // TODO: Determine if this is necessary. 234 | mutex->ReleaseSync(*next_lock_key); 235 | } 236 | 237 | std::shared_ptr obsffmpeg::hwapi::d3d11_instance::avframe_from_obs(AVBufferRef* frames, uint32_t handle, 238 | uint64_t lock_key, uint64_t* next_lock_key) 239 | { 240 | auto gctx = obsffmpeg::obs_graphics(); 241 | 242 | auto frame = this->allocate_frame(frames); 243 | this->copy_from_obs(frames, handle, lock_key, next_lock_key, frame); 244 | return frame; 245 | } 246 | -------------------------------------------------------------------------------- /source/hwapi/d3d11.hpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "base.hpp" 27 | 28 | namespace obsffmpeg { 29 | namespace hwapi { 30 | class d3d11 : public ::obsffmpeg::hwapi::base { 31 | typedef HRESULT(__stdcall* CreateDXGIFactory_t)(REFIID, void**); 32 | typedef HRESULT(__stdcall* CreateDXGIFactory1_t)(REFIID, void**); 33 | typedef HRESULT(__stdcall* D3D11CreateDevice_t)(_In_opt_ IDXGIAdapter*, D3D_DRIVER_TYPE, 34 | HMODULE, UINT, CONST D3D_FEATURE_LEVEL*, UINT, 35 | UINT, _Out_opt_ ID3D11Device**, 36 | _Out_opt_ D3D_FEATURE_LEVEL*, 37 | _Out_opt_ ID3D11DeviceContext**); 38 | 39 | HMODULE _dxgi_module; 40 | CreateDXGIFactory_t _CreateDXGIFactory; 41 | CreateDXGIFactory1_t _CreateDXGIFactory1; 42 | 43 | HMODULE _d3d11_module; 44 | D3D11CreateDevice_t _D3D11CreateDevice; 45 | 46 | ATL::CComPtr _dxgifactory; 47 | 48 | public: 49 | d3d11(); 50 | virtual ~d3d11(); 51 | 52 | virtual std::list enumerate_adapters() override; 53 | 54 | virtual std::shared_ptr 55 | create(obsffmpeg::hwapi::device target) override; 56 | 57 | virtual std::shared_ptr create_from_obs() override; 58 | }; 59 | 60 | class d3d11_instance : public ::obsffmpeg::hwapi::instance { 61 | ATL::CComPtr _device; 62 | ATL::CComPtr _context; 63 | 64 | public: 65 | d3d11_instance(ATL::CComPtr device, ATL::CComPtr context); 66 | virtual ~d3d11_instance(); 67 | 68 | virtual AVBufferRef* create_device_context() override; 69 | 70 | virtual std::shared_ptr allocate_frame(AVBufferRef* frames) override; 71 | 72 | virtual void copy_from_obs(AVBufferRef* frames, uint32_t handle, uint64_t lock_key, 73 | uint64_t* next_lock_key, std::shared_ptr frame) override; 74 | 75 | virtual std::shared_ptr avframe_from_obs(AVBufferRef* frames, uint32_t handle, 76 | uint64_t lock_key, 77 | uint64_t* next_lock_key) override; 78 | }; 79 | } // namespace hwapi 80 | } // namespace obsffmpeg 81 | -------------------------------------------------------------------------------- /source/plugin.cpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #include "plugin.hpp" 23 | #include 24 | #include 25 | #include "encoder.hpp" 26 | #include "ui/debug_handler.hpp" 27 | #include "ui/handler.hpp" 28 | #include "utility.hpp" 29 | 30 | extern "C" { 31 | #include 32 | #include 33 | #pragma warning(push) 34 | #pragma warning(disable : 4244) 35 | #include 36 | #pragma warning(pop) 37 | } 38 | 39 | // Initializers and finalizers. 40 | std::list> obsffmpeg::initializers; 41 | 42 | std::list> obsffmpeg::finalizers; 43 | 44 | // Codec to Handler mapping. 45 | static std::map> codec_to_handler_map; 46 | static std::shared_ptr debug_handler = std::make_shared(); 47 | 48 | void obsffmpeg::register_codec_handler(std::string const codec, std::shared_ptr const handler) 49 | { 50 | codec_to_handler_map.emplace(codec, handler); 51 | } 52 | 53 | std::shared_ptr obsffmpeg::find_codec_handler(std::string const codec) 54 | { 55 | auto found = codec_to_handler_map.find(codec); 56 | if (found == codec_to_handler_map.end()) 57 | return debug_handler; 58 | return found->second; 59 | } 60 | 61 | bool obsffmpeg::has_codec_handler(std::string const codec) 62 | { 63 | auto found = codec_to_handler_map.find(codec); 64 | return (found != codec_to_handler_map.end()); 65 | } 66 | 67 | static std::map> generic_factories; 68 | 69 | #pragma warning(push) 70 | #pragma warning(disable : 4996) // Handled by software and precompiler branch 71 | MODULE_EXPORT bool obs_module_load(void) 72 | try { 73 | #if FF_API_NEXT 74 | if (avcodec_version() < AV_VERSION_INT(58, 0, 0)) { 75 | // Initialize avcodec. 76 | avcodec_register_all(); 77 | } 78 | #endif 79 | 80 | // Run all initializers. 81 | for (auto const func : obsffmpeg::initializers) { 82 | func(); 83 | } 84 | 85 | // Register all codecs. 86 | #if FF_API_NEXT 87 | if (avcodec_version() < AV_VERSION_INT(58, 0, 0)) { 88 | AVCodec* cdc = nullptr; 89 | while ((cdc = av_codec_next(cdc)) != nullptr) { 90 | if (!av_codec_is_encoder(cdc)) 91 | continue; 92 | 93 | if ((cdc->type == AVMediaType::AVMEDIA_TYPE_AUDIO) 94 | || (cdc->type == AVMediaType::AVMEDIA_TYPE_VIDEO)) { 95 | auto ptr = std::make_shared(cdc); 96 | ptr->register_encoder(); 97 | generic_factories.emplace(cdc, ptr); 98 | } 99 | } 100 | } else { 101 | #endif 102 | #if LIBAVCODEC_VERSION_MAJOR >= 58 103 | void* storage = nullptr; 104 | const AVCodec* cdc = nullptr; 105 | for (cdc = av_codec_iterate(&storage); cdc != nullptr; cdc = av_codec_iterate(&storage)) { 106 | if (!av_codec_is_encoder(cdc)) 107 | continue; 108 | 109 | if (/*(cdc->type == AVMediaType::AVMEDIA_TYPE_AUDIO) 110 | || */(cdc->type == AVMediaType::AVMEDIA_TYPE_VIDEO)) { 111 | auto ptr = std::make_shared(cdc); 112 | ptr->register_encoder(); 113 | generic_factories.emplace(cdc, ptr); 114 | } 115 | } 116 | #endif 117 | #if FF_API_NEXT 118 | } 119 | #endif 120 | 121 | return true; 122 | } catch (std::exception& ex) { 123 | PLOG_ERROR("Exception during initalization: %s.", ex.what()); 124 | return false; 125 | } catch (...) { 126 | PLOG_ERROR("Unrecognized exception during initalization."); 127 | return false; 128 | } 129 | #pragma warning(pop) 130 | 131 | MODULE_EXPORT void obs_module_unload(void) 132 | try { 133 | // Run all finalizers. 134 | for (auto const func : obsffmpeg::finalizers) { 135 | func(); 136 | } 137 | } catch (std::exception& ex) { 138 | PLOG_ERROR("Exception during finalizing: %s.", ex.what()); 139 | } catch (...) { 140 | PLOG_ERROR("Unrecognized exception during finalizing."); 141 | } 142 | -------------------------------------------------------------------------------- /source/plugin.hpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #pragma once 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "ui/handler.hpp" 28 | 29 | namespace obsffmpeg { 30 | extern std::list> initializers; 31 | 32 | extern std::list> finalizers; 33 | 34 | void register_codec_handler(std::string codec, std::shared_ptr handler); 35 | 36 | std::shared_ptr find_codec_handler(std::string codec); 37 | 38 | bool has_codec_handler(std::string codec); 39 | 40 | } // namespace obsffmpeg 41 | 42 | MODULE_EXPORT bool obs_module_load(void); 43 | MODULE_EXPORT void obs_module_unload(void); 44 | -------------------------------------------------------------------------------- /source/strings.hpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #pragma once 23 | #include "utility.hpp" 24 | 25 | #define S_STATE_DEFAULT "State.Default" 26 | #define S_STATE_DISABLED "State.Disabled" 27 | #define S_STATE_ENABLED "State.Enabled" 28 | #define S_STATE_AUTOMATIC "State.Automatic" 29 | #define S_STATE_MANUAL "State.Manual" 30 | 31 | #define S_RATECONTROL "RateControl" 32 | #define S_RATECONTROL_MODE "RateControl.Mode" 33 | #define S_RATECONTROL_MODE_(x) "RateControl.Mode." D_VSTR(x) 34 | #define S_RATECONTROL_BITRATE_TARGET "RateControl.Bitrate.Target" 35 | #define S_RATECONTROL_BITRATE_MINIMUM "RateControl.Bitrate.Minimum" 36 | #define S_RATECONTROL_BITRATE_MAXIMUM "RateControl.Bitrate.Maximum" 37 | #define S_RATECONTROL_BUFFERSIZE "RateControl.BufferSize" 38 | #define S_RATECONTROL_QUALITY_TARGET "RateControl.Quality.Target" 39 | #define S_RATECONTROL_QUALITY_MINIMUM "RateControl.Quality.Minimum" 40 | #define S_RATECONTROL_QUALITY_MAXIMUM "RateControl.Quality.Maximum" 41 | #define S_RATECONTROL_QP_I "RateControl.QP.I" 42 | #define S_RATECONTROL_QP_P "RateControl.QP.P" 43 | #define S_RATECONTROL_QP_B "RateControl.QP.B" 44 | #define S_RATECONTROL_QP_I_INITIAL "RateControl.QP.I.Initial" 45 | #define S_RATECONTROL_QP_P_INITIAL "RateControl.QP.P.Initial" 46 | #define S_RATECONTROL_QP_B_INITIAL "RateControl.QP.B.Initial" 47 | 48 | #define S_KEYFRAMES "KeyFrames" 49 | #define S_KEYFRAMES_INTERVALTYPE "KeyFrames.IntervalType" 50 | #define S_KEYFRAMES_INTERVALTYPE_(x) "KeyFrames.IntervalType." D_VSTR(x) 51 | #define S_KEYFRAMES_INTERVAL "KeyFrames.Interval" 52 | #define S_KEYFRAMES_INTERVAL_SECONDS "KeyFrames.Interval.Seconds" 53 | #define S_KEYFRAMES_INTERVAL_FRAMES "KeyFrames.Interval.Frames" 54 | -------------------------------------------------------------------------------- /source/ui/debug_handler.cpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #include "debug_handler.hpp" 23 | #include 24 | #include 25 | #include 26 | #include "handler.hpp" 27 | #include "plugin.hpp" 28 | #include "utility.hpp" 29 | 30 | extern "C" { 31 | #include 32 | #pragma warning(push) 33 | #pragma warning(disable : 4244) 34 | #include 35 | #pragma warning(pop) 36 | } 37 | 38 | void obsffmpeg::ui::debug_handler::get_defaults(obs_data_t*, const AVCodec*, AVCodecContext*, bool) {} 39 | 40 | template 41 | std::string to_string(T value){}; 42 | 43 | template<> 44 | std::string to_string(int64_t value) 45 | { 46 | std::vector buf(32); 47 | snprintf(buf.data(), buf.size(), "%lld", value); 48 | return std::string(buf.data(), buf.data() + buf.size()); 49 | } 50 | 51 | template<> 52 | std::string to_string(uint64_t value) 53 | { 54 | std::vector buf(32); 55 | snprintf(buf.data(), buf.size(), "%llu", value); 56 | return std::string(buf.data(), buf.data() + buf.size()); 57 | } 58 | 59 | template<> 60 | std::string to_string(double_t value) 61 | { 62 | std::vector buf(32); 63 | snprintf(buf.data(), buf.size(), "%f", value); 64 | return std::string(buf.data(), buf.data() + buf.size()); 65 | } 66 | 67 | void obsffmpeg::ui::debug_handler::get_properties(obs_properties_t*, const AVCodec* codec, AVCodecContext* context, 68 | bool) 69 | { 70 | if (context) 71 | return; 72 | 73 | AVCodecContext* ctx = avcodec_alloc_context3(codec); 74 | if (!ctx->priv_data) { 75 | avcodec_free_context(&ctx); 76 | return; 77 | } 78 | 79 | PLOG_INFO("Options for '%s':", codec->name); 80 | 81 | std::pair opt_type_name[] = { 82 | {AV_OPT_TYPE_FLAGS, "Flags"}, 83 | {AV_OPT_TYPE_INT, "Int"}, 84 | {AV_OPT_TYPE_INT64, "Int64"}, 85 | {AV_OPT_TYPE_DOUBLE, "Double"}, 86 | {AV_OPT_TYPE_FLOAT, "Float"}, 87 | {AV_OPT_TYPE_STRING, "String"}, 88 | {AV_OPT_TYPE_RATIONAL, "Rational"}, 89 | {AV_OPT_TYPE_BINARY, "Binary"}, 90 | {AV_OPT_TYPE_DICT, "Dictionary"}, 91 | {AV_OPT_TYPE_UINT64, "Unsigned Int64"}, 92 | {AV_OPT_TYPE_CONST, "Constant"}, 93 | {AV_OPT_TYPE_IMAGE_SIZE, "Image Size"}, 94 | {AV_OPT_TYPE_PIXEL_FMT, "Pixel Format"}, 95 | {AV_OPT_TYPE_SAMPLE_FMT, "Sample Format"}, 96 | {AV_OPT_TYPE_VIDEO_RATE, "Video Rate"}, 97 | {AV_OPT_TYPE_DURATION, "Duration"}, 98 | {AV_OPT_TYPE_COLOR, "Color"}, 99 | {AV_OPT_TYPE_CHANNEL_LAYOUT, "Layout"}, 100 | {AV_OPT_TYPE_BOOL, "Bool"}, 101 | }; 102 | std::map unit_types; 103 | 104 | const AVOption* opt = nullptr; 105 | while ((opt = av_opt_next(ctx->priv_data, opt)) != nullptr) { 106 | std::string type_name = ""; 107 | for (auto kv : opt_type_name) { 108 | if (opt->type == kv.first) { 109 | type_name = kv.second; 110 | break; 111 | } 112 | } 113 | 114 | if (opt->type == AV_OPT_TYPE_CONST) { 115 | if (opt->unit == nullptr) { 116 | PLOG_INFO(" Constant '%s' and help text '%s' with unknown settings.", opt->name, 117 | opt->help); 118 | } else { 119 | auto unit_type = unit_types.find(opt->unit); 120 | if (unit_type == unit_types.end()) { 121 | PLOG_INFO(" [%s] Flag '%s' and help text '%s' with value '%lld'.", opt->unit, 122 | opt->name, opt->help, opt->default_val.i64); 123 | } else { 124 | std::string out; 125 | switch (unit_type->second) { 126 | case AV_OPT_TYPE_BOOL: 127 | out = opt->default_val.i64 ? "true" : "false"; 128 | break; 129 | case AV_OPT_TYPE_INT: 130 | out = to_string(opt->default_val.i64); 131 | break; 132 | case AV_OPT_TYPE_UINT64: 133 | out = to_string(static_cast(opt->default_val.i64)); 134 | break; 135 | case AV_OPT_TYPE_FLAGS: 136 | out = to_string(static_cast(opt->default_val.i64)); 137 | break; 138 | case AV_OPT_TYPE_FLOAT: 139 | case AV_OPT_TYPE_DOUBLE: 140 | out = to_string(opt->default_val.dbl); 141 | break; 142 | case AV_OPT_TYPE_STRING: 143 | out = opt->default_val.str; 144 | break; 145 | case AV_OPT_TYPE_BINARY: 146 | case AV_OPT_TYPE_IMAGE_SIZE: 147 | case AV_OPT_TYPE_PIXEL_FMT: 148 | case AV_OPT_TYPE_SAMPLE_FMT: 149 | case AV_OPT_TYPE_VIDEO_RATE: 150 | case AV_OPT_TYPE_DURATION: 151 | case AV_OPT_TYPE_COLOR: 152 | case AV_OPT_TYPE_CHANNEL_LAYOUT: 153 | break; 154 | } 155 | 156 | PLOG_INFO(" [%s] Constant '%s' and help text '%s' with value '%s'.", opt->unit, 157 | opt->name, opt->help, out.c_str()); 158 | } 159 | } 160 | } else { 161 | if (opt->unit != nullptr) { 162 | unit_types.emplace(opt->name, opt->type); 163 | } 164 | 165 | std::string minimum = "", maximum = "", out; 166 | minimum = to_string(opt->min); 167 | maximum = to_string(opt->max); 168 | { 169 | switch (opt->type) { 170 | case AV_OPT_TYPE_BOOL: 171 | out = opt->default_val.i64 ? "true" : "false"; 172 | break; 173 | case AV_OPT_TYPE_INT: 174 | out = to_string(opt->default_val.i64); 175 | break; 176 | case AV_OPT_TYPE_UINT64: 177 | out = to_string(static_cast(opt->default_val.i64)); 178 | break; 179 | case AV_OPT_TYPE_FLAGS: 180 | out = to_string(static_cast(opt->default_val.i64)); 181 | break; 182 | case AV_OPT_TYPE_FLOAT: 183 | case AV_OPT_TYPE_DOUBLE: 184 | out = to_string(opt->default_val.dbl); 185 | break; 186 | case AV_OPT_TYPE_STRING: 187 | out = opt->default_val.str ? opt->default_val.str : ""; 188 | break; 189 | case AV_OPT_TYPE_BINARY: 190 | case AV_OPT_TYPE_IMAGE_SIZE: 191 | case AV_OPT_TYPE_PIXEL_FMT: 192 | case AV_OPT_TYPE_SAMPLE_FMT: 193 | case AV_OPT_TYPE_VIDEO_RATE: 194 | case AV_OPT_TYPE_DURATION: 195 | case AV_OPT_TYPE_COLOR: 196 | case AV_OPT_TYPE_CHANNEL_LAYOUT: 197 | break; 198 | } 199 | } 200 | 201 | PLOG_INFO( 202 | " Option '%s'%s%s%s with help '%s' of type '%s' with default value '%s', minimum '%s' and maximum '%s'.", 203 | opt->name, opt->unit ? " with unit (" : "", opt->unit ? opt->unit : "", 204 | opt->unit ? ")" : "", opt->help, type_name.c_str(), out.c_str(), minimum.c_str(), 205 | maximum.c_str()); 206 | } 207 | } 208 | } 209 | 210 | void obsffmpeg::ui::debug_handler::update(obs_data_t*, const AVCodec*, AVCodecContext*) {} 211 | -------------------------------------------------------------------------------- /source/ui/debug_handler.hpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #pragma once 23 | #include "handler.hpp" 24 | 25 | namespace obsffmpeg { 26 | namespace ui { 27 | class debug_handler : public handler { 28 | public: 29 | virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context, 30 | bool hw_encode) override; 31 | 32 | virtual void get_properties(obs_properties_t* props, const AVCodec* codec, 33 | AVCodecContext* context, bool hw_encode) override; 34 | 35 | virtual void update(obs_data_t* settings, const AVCodec* codec, 36 | AVCodecContext* context) override; 37 | }; 38 | } // namespace ui 39 | } // namespace obsffmpeg 40 | -------------------------------------------------------------------------------- /source/ui/handler.cpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #include "handler.hpp" 23 | #include "encoder.hpp" 24 | 25 | void obsffmpeg::ui::handler::adjust_encoder_info(obsffmpeg::encoder_factory*, obsffmpeg::encoder_info*, 26 | obsffmpeg::encoder_info*) 27 | {} 28 | 29 | void obsffmpeg::ui::handler::get_defaults(obs_data_t*, const AVCodec*, AVCodecContext*, bool) {} 30 | 31 | bool obsffmpeg::ui::handler::has_keyframe_support(obsffmpeg::encoder* instance) 32 | { 33 | return (instance->get_avcodec()->capabilities & AV_CODEC_CAP_INTRA_ONLY) == 0; 34 | } 35 | 36 | void obsffmpeg::ui::handler::get_properties(obs_properties_t*, const AVCodec*, AVCodecContext*, bool) {} 37 | 38 | void obsffmpeg::ui::handler::update(obs_data_t*, const AVCodec*, AVCodecContext*) {} 39 | 40 | void obsffmpeg::ui::handler::override_update(obsffmpeg::encoder*, obs_data_t*) {} 41 | 42 | void obsffmpeg::ui::handler::log_options(obs_data_t*, const AVCodec*, AVCodecContext*) {} 43 | 44 | void obsffmpeg::ui::handler::override_colorformat(AVPixelFormat&, obs_data_t*, const AVCodec*, AVCodecContext*) {} 45 | 46 | void obsffmpeg::ui::handler::process_avpacket(AVPacket&, const AVCodec*, AVCodecContext*) {} 47 | -------------------------------------------------------------------------------- /source/ui/handler.hpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #pragma once 23 | 24 | #include 25 | #include "hwapi/base.hpp" 26 | 27 | extern "C" { 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | #pragma warning(push) 34 | #pragma warning(disable : 4244) 35 | #include 36 | #pragma warning(pop) 37 | } 38 | 39 | namespace obsffmpeg { 40 | struct encoder_info; 41 | class encoder_factory; 42 | class encoder; 43 | 44 | namespace ui { 45 | class handler { 46 | public /*factory*/: 47 | virtual void adjust_encoder_info(obsffmpeg::encoder_factory* factory, 48 | obsffmpeg::encoder_info* main, 49 | obsffmpeg::encoder_info* fallback); 50 | 51 | virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context, 52 | bool hw_encode); 53 | 54 | public /*settings*/: 55 | virtual bool has_keyframe_support(obsffmpeg::encoder* instance); 56 | 57 | virtual void get_properties(obs_properties_t* props, const AVCodec* codec, 58 | AVCodecContext* context, bool hw_encode); 59 | 60 | virtual void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context); 61 | 62 | virtual void override_update(obsffmpeg::encoder* instance, obs_data_t* settings); 63 | 64 | virtual void log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context); 65 | 66 | public /*instance*/: 67 | 68 | virtual void override_colorformat(AVPixelFormat& target_format, obs_data_t* settings, 69 | const AVCodec* codec, AVCodecContext* context); 70 | 71 | virtual void process_avpacket(AVPacket& packet, const AVCodec* codec, AVCodecContext* context); 72 | }; 73 | } // namespace ui 74 | } // namespace obsffmpeg 75 | -------------------------------------------------------------------------------- /source/ui/nvenc_h264_handler.cpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #include "nvenc_h264_handler.hpp" 23 | #include "codecs/h264.hpp" 24 | #include "encoder.hpp" 25 | #include "ffmpeg/tools.hpp" 26 | #include "nvenc_shared.hpp" 27 | #include "plugin.hpp" 28 | #include "strings.hpp" 29 | #include "utility.hpp" 30 | 31 | extern "C" { 32 | #include 33 | #pragma warning(push) 34 | #pragma warning(disable : 4244) 35 | #include 36 | #pragma warning(pop) 37 | } 38 | 39 | using namespace obsffmpeg::codecs::h264; 40 | 41 | std::map profiles{ 42 | {profile::BASELINE, "baseline"}, 43 | {profile::MAIN, "main"}, 44 | {profile::HIGH, "high"}, 45 | {profile::HIGH444_PREDICTIVE, "high444p"}, 46 | }; 47 | 48 | std::map levels{ 49 | {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"}, 50 | {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"}, 51 | {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"}, 52 | {level::L5_1, "5.1"}, {level::L5_2, "5.2"}, 53 | }; 54 | 55 | INITIALIZER(nvenc_h264_handler_init) 56 | { 57 | obsffmpeg::initializers.push_back([]() { 58 | obsffmpeg::register_codec_handler("h264_nvenc", std::make_shared()); 59 | }); 60 | }; 61 | 62 | void obsffmpeg::ui::nvenc_h264_handler::adjust_encoder_info(obsffmpeg::encoder_factory*, obsffmpeg::encoder_info* main, 63 | obsffmpeg::encoder_info* fallback) 64 | { 65 | main->readable_name = "H.264/AVC NVidia NVENC (Hardware)"; 66 | fallback->readable_name = "H.264/AVC NVidia NVENC (Software)"; 67 | } 68 | 69 | void obsffmpeg::ui::nvenc_h264_handler::get_defaults(obs_data_t* settings, const AVCodec* codec, 70 | AVCodecContext* context, bool) 71 | { 72 | nvenc::get_defaults(settings, codec, context); 73 | 74 | obs_data_set_default_int(settings, P_H264_PROFILE, static_cast(codecs::h264::profile::HIGH)); 75 | obs_data_set_default_int(settings, P_H264_LEVEL, static_cast(codecs::h264::level::UNKNOWN)); 76 | } 77 | 78 | bool obsffmpeg::ui::nvenc_h264_handler::has_keyframe_support(obsffmpeg::encoder*) 79 | { 80 | return true; 81 | } 82 | 83 | void obsffmpeg::ui::nvenc_h264_handler::get_properties(obs_properties_t* props, const AVCodec* codec, 84 | AVCodecContext* context, bool) 85 | { 86 | if (!context) { 87 | this->get_encoder_properties(props, codec); 88 | } else { 89 | this->get_runtime_properties(props, codec, context); 90 | } 91 | } 92 | 93 | void obsffmpeg::ui::nvenc_h264_handler::update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context) 94 | { 95 | nvenc::update(settings, codec, context); 96 | 97 | { 98 | auto found = 99 | profiles.find(static_cast(obs_data_get_int(settings, P_H264_PROFILE))); 100 | if (found != profiles.end()) { 101 | av_opt_set(context->priv_data, "profile", found->second.c_str(), 0); 102 | } 103 | } 104 | 105 | { 106 | auto found = levels.find(static_cast(obs_data_get_int(settings, P_H264_LEVEL))); 107 | if (found != levels.end()) { 108 | av_opt_set(context->priv_data, "level", found->second.c_str(), 0); 109 | } else { 110 | av_opt_set(context->priv_data, "level", "auto", 0); 111 | } 112 | } 113 | } 114 | 115 | void obsffmpeg::ui::nvenc_h264_handler::override_update(obsffmpeg::encoder* instance, obs_data_t* settings) 116 | { 117 | nvenc::override_update(instance, settings); 118 | } 119 | 120 | void obsffmpeg::ui::nvenc_h264_handler::log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context) 121 | { 122 | nvenc::log_options(settings, codec, context); 123 | 124 | PLOG_INFO("[%s] H.265/HEVC:", codec->name); 125 | ffmpeg::tools::print_av_option_string(context, "profile", " Profile", [](int64_t v) { 126 | profile val = static_cast(v); 127 | auto index = profiles.find(val); 128 | if (index != profiles.end()) 129 | return index->second; 130 | return std::string(""); 131 | }); 132 | ffmpeg::tools::print_av_option_string(context, "level", " Level", [](int64_t v) { 133 | level val = static_cast(v); 134 | auto index = levels.find(val); 135 | if (index != levels.end()) 136 | return index->second; 137 | return std::string(""); 138 | }); 139 | } 140 | 141 | void obsffmpeg::ui::nvenc_h264_handler::get_encoder_properties(obs_properties_t* props, const AVCodec* codec) 142 | { 143 | nvenc::get_properties_pre(props, codec); 144 | 145 | { 146 | obs_properties_t* grp = props; 147 | if (!obsffmpeg::are_property_groups_broken()) { 148 | grp = obs_properties_create(); 149 | obs_properties_add_group(props, P_H264, TRANSLATE(P_H264), OBS_GROUP_NORMAL, grp); 150 | } 151 | 152 | { 153 | auto p = obs_properties_add_list(grp, P_H264_PROFILE, TRANSLATE(P_H264_PROFILE), 154 | OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); 155 | obs_property_set_long_description(p, TRANSLATE(DESC(P_H264_PROFILE))); 156 | obs_property_list_add_int(p, TRANSLATE(S_STATE_DEFAULT), 157 | static_cast(codecs::h264::profile::UNKNOWN)); 158 | for (auto const kv : profiles) { 159 | std::string trans = std::string(P_H264_PROFILE) + "." + kv.second; 160 | obs_property_list_add_int(p, TRANSLATE(trans.c_str()), static_cast(kv.first)); 161 | } 162 | } 163 | { 164 | auto p = obs_properties_add_list(grp, P_H264_LEVEL, TRANSLATE(P_H264_LEVEL), 165 | OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); 166 | obs_property_set_long_description(p, TRANSLATE(DESC(P_H264_LEVEL))); 167 | obs_property_list_add_int(p, TRANSLATE(S_STATE_AUTOMATIC), 168 | static_cast(codecs::h264::level::UNKNOWN)); 169 | for (auto const kv : levels) { 170 | obs_property_list_add_int(p, kv.second.c_str(), static_cast(kv.first)); 171 | } 172 | } 173 | } 174 | 175 | nvenc::get_properties_post(props, codec); 176 | } 177 | 178 | void obsffmpeg::ui::nvenc_h264_handler::get_runtime_properties(obs_properties_t* props, const AVCodec* codec, 179 | AVCodecContext* context) 180 | { 181 | nvenc::get_runtime_properties(props, codec, context); 182 | } 183 | -------------------------------------------------------------------------------- /source/ui/nvenc_h264_handler.hpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #pragma once 23 | #include "handler.hpp" 24 | 25 | extern "C" { 26 | #include 27 | #pragma warning(push) 28 | #pragma warning(disable : 4244) 29 | #include 30 | #pragma warning(pop) 31 | } 32 | 33 | namespace obsffmpeg { 34 | namespace ui { 35 | class nvenc_h264_handler : public handler { 36 | public /*factory*/: 37 | virtual void adjust_encoder_info(obsffmpeg::encoder_factory* factory, 38 | obsffmpeg::encoder_info* main, 39 | obsffmpeg::encoder_info* fallback); 40 | 41 | virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context, 42 | bool hw_encode); 43 | 44 | public /*settings*/: 45 | virtual bool has_keyframe_support(obsffmpeg::encoder* instance); 46 | 47 | virtual void get_properties(obs_properties_t* props, const AVCodec* codec, 48 | AVCodecContext* context, bool hw_encode); 49 | 50 | virtual void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context); 51 | 52 | virtual void override_update(obsffmpeg::encoder* instance, obs_data_t* settings); 53 | 54 | virtual void log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context); 55 | 56 | public /*instance*/: 57 | //virtual void override_colorformat(AVPixelFormat& target_format, obs_data_t* settings, const AVCodec* codec, AVCodecContext* context); 58 | 59 | private: 60 | void get_encoder_properties(obs_properties_t* props, const AVCodec* codec); 61 | 62 | void get_runtime_properties(obs_properties_t* props, const AVCodec* codec, 63 | AVCodecContext* context); 64 | }; 65 | } // namespace ui 66 | } // namespace obsffmpeg 67 | -------------------------------------------------------------------------------- /source/ui/nvenc_hevc_handler.cpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #include "nvenc_hevc_handler.hpp" 23 | #include "codecs/hevc.hpp" 24 | #include "encoder.hpp" 25 | #include "ffmpeg/tools.hpp" 26 | #include "nvenc_shared.hpp" 27 | #include "plugin.hpp" 28 | #include "strings.hpp" 29 | #include "utility.hpp" 30 | 31 | extern "C" { 32 | #include 33 | #pragma warning(push) 34 | #pragma warning(disable : 4244) 35 | #include 36 | #pragma warning(pop) 37 | } 38 | 39 | using namespace obsffmpeg::codecs::hevc; 40 | 41 | std::map profiles{ 42 | {profile::MAIN, "main"}, 43 | {profile::MAIN10, "main10"}, 44 | {profile::RANGE_EXTENDED, "rext"}, 45 | }; 46 | 47 | std::map tiers{ 48 | {tier::MAIN, "main"}, 49 | {tier::HIGH, "high"}, 50 | }; 51 | 52 | std::map levels{ 53 | {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"}, 54 | {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"}, 55 | {level::L6_0, "6.0"}, {level::L6_1, "6.1"}, {level::L6_2, "6.2"}, 56 | }; 57 | 58 | INITIALIZER(nvenc_hevc_handler_init) 59 | { 60 | obsffmpeg::initializers.push_back([]() { 61 | obsffmpeg::register_codec_handler("hevc_nvenc", std::make_shared()); 62 | }); 63 | }; 64 | 65 | void obsffmpeg::ui::nvenc_hevc_handler::adjust_encoder_info(obsffmpeg::encoder_factory*, obsffmpeg::encoder_info* main, 66 | obsffmpeg::encoder_info* fallback) 67 | { 68 | main->readable_name = "H.265/HEVC Nvidia NVENC (Hardware)"; 69 | fallback->readable_name = "H.265/HEVC Nvidia NVENC (Software)"; 70 | } 71 | 72 | void obsffmpeg::ui::nvenc_hevc_handler::get_defaults(obs_data_t* settings, const AVCodec* codec, 73 | AVCodecContext* context, bool) 74 | { 75 | nvenc::get_defaults(settings, codec, context); 76 | 77 | obs_data_set_default_int(settings, P_HEVC_PROFILE, static_cast(codecs::hevc::profile::MAIN)); 78 | obs_data_set_default_int(settings, P_HEVC_TIER, static_cast(codecs::hevc::profile::MAIN)); 79 | obs_data_set_default_int(settings, P_HEVC_LEVEL, static_cast(codecs::hevc::level::UNKNOWN)); 80 | } 81 | 82 | bool obsffmpeg::ui::nvenc_hevc_handler::has_keyframe_support(obsffmpeg::encoder*) 83 | { 84 | return true; 85 | } 86 | 87 | void obsffmpeg::ui::nvenc_hevc_handler::get_properties(obs_properties_t* props, const AVCodec* codec, 88 | AVCodecContext* context, bool) 89 | { 90 | if (!context) { 91 | this->get_encoder_properties(props, codec); 92 | } else { 93 | this->get_runtime_properties(props, codec, context); 94 | } 95 | } 96 | 97 | void obsffmpeg::ui::nvenc_hevc_handler::update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context) 98 | { 99 | nvenc::update(settings, codec, context); 100 | 101 | { // HEVC Options 102 | auto found = profiles.find(static_cast(obs_data_get_int(settings, P_HEVC_PROFILE))); 103 | if (found != profiles.end()) { 104 | av_opt_set(context->priv_data, "profile", found->second.c_str(), 0); 105 | } 106 | } 107 | { 108 | auto found = tiers.find(static_cast(obs_data_get_int(settings, P_HEVC_TIER))); 109 | if (found != tiers.end()) { 110 | av_opt_set(context->priv_data, "tier", found->second.c_str(), 0); 111 | } 112 | } 113 | { 114 | auto found = levels.find(static_cast(obs_data_get_int(settings, P_HEVC_LEVEL))); 115 | if (found != levels.end()) { 116 | av_opt_set(context->priv_data, "level", found->second.c_str(), 0); 117 | } else { 118 | av_opt_set(context->priv_data, "level", "auto", 0); 119 | } 120 | } 121 | } 122 | 123 | void obsffmpeg::ui::nvenc_hevc_handler::override_update(obsffmpeg::encoder* instance, obs_data_t* settings) 124 | { 125 | nvenc::override_update(instance, settings); 126 | } 127 | 128 | void obsffmpeg::ui::nvenc_hevc_handler::log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context) 129 | { 130 | nvenc::log_options(settings, codec, context); 131 | 132 | PLOG_INFO("[%s] H.265/HEVC:", codec->name); 133 | ffmpeg::tools::print_av_option_string(context, "profile", " Profile", [](int64_t v) { 134 | profile val = static_cast(v); 135 | auto index = profiles.find(val); 136 | if (index != profiles.end()) 137 | return index->second; 138 | return std::string(""); 139 | }); 140 | ffmpeg::tools::print_av_option_string(context, "level", " Level", [](int64_t v) { 141 | level val = static_cast(v); 142 | auto index = levels.find(val); 143 | if (index != levels.end()) 144 | return index->second; 145 | return std::string(""); 146 | }); 147 | ffmpeg::tools::print_av_option_string(context, "tier", " Tier", [](int64_t v) { 148 | tier val = static_cast(v); 149 | auto index = tiers.find(val); 150 | if (index != tiers.end()) 151 | return index->second; 152 | return std::string(""); 153 | }); 154 | } 155 | 156 | void obsffmpeg::ui::nvenc_hevc_handler::get_encoder_properties(obs_properties_t* props, const AVCodec* codec) 157 | { 158 | nvenc::get_properties_pre(props, codec); 159 | 160 | { 161 | obs_properties_t* grp = props; 162 | if (!obsffmpeg::are_property_groups_broken()) { 163 | grp = obs_properties_create(); 164 | obs_properties_add_group(props, P_HEVC, TRANSLATE(P_HEVC), OBS_GROUP_NORMAL, grp); 165 | } 166 | 167 | { 168 | auto p = obs_properties_add_list(grp, P_HEVC_PROFILE, TRANSLATE(P_HEVC_PROFILE), 169 | OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); 170 | obs_property_set_long_description(p, TRANSLATE(DESC(P_HEVC_PROFILE))); 171 | obs_property_list_add_int(p, TRANSLATE(S_STATE_DEFAULT), 172 | static_cast(codecs::hevc::profile::UNKNOWN)); 173 | for (auto const kv : profiles) { 174 | std::string trans = std::string(P_HEVC_PROFILE) + "." + kv.second; 175 | obs_property_list_add_int(p, TRANSLATE(trans.c_str()), static_cast(kv.first)); 176 | } 177 | } 178 | { 179 | auto p = obs_properties_add_list(grp, P_HEVC_TIER, TRANSLATE(P_HEVC_TIER), OBS_COMBO_TYPE_LIST, 180 | OBS_COMBO_FORMAT_INT); 181 | obs_property_set_long_description(p, TRANSLATE(DESC(P_HEVC_TIER))); 182 | obs_property_list_add_int(p, TRANSLATE(S_STATE_DEFAULT), 183 | static_cast(codecs::hevc::tier::UNKNOWN)); 184 | for (auto const kv : tiers) { 185 | std::string trans = std::string(P_HEVC_TIER) + "." + kv.second; 186 | obs_property_list_add_int(p, TRANSLATE(trans.c_str()), static_cast(kv.first)); 187 | } 188 | } 189 | { 190 | auto p = obs_properties_add_list(grp, P_HEVC_LEVEL, TRANSLATE(P_HEVC_LEVEL), 191 | OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); 192 | obs_property_set_long_description(p, TRANSLATE(DESC(P_HEVC_LEVEL))); 193 | obs_property_list_add_int(p, TRANSLATE(S_STATE_AUTOMATIC), 194 | static_cast(codecs::hevc::level::UNKNOWN)); 195 | for (auto const kv : levels) { 196 | obs_property_list_add_int(p, kv.second.c_str(), static_cast(kv.first)); 197 | } 198 | } 199 | } 200 | 201 | nvenc::get_properties_post(props, codec); 202 | } 203 | 204 | void obsffmpeg::ui::nvenc_hevc_handler::get_runtime_properties(obs_properties_t* props, const AVCodec* codec, 205 | AVCodecContext* context) 206 | { 207 | nvenc::get_runtime_properties(props, codec, context); 208 | } 209 | -------------------------------------------------------------------------------- /source/ui/nvenc_hevc_handler.hpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #pragma once 23 | #include "handler.hpp" 24 | 25 | extern "C" { 26 | #include 27 | #pragma warning(push) 28 | #pragma warning(disable : 4244) 29 | #include 30 | #pragma warning(pop) 31 | } 32 | 33 | namespace obsffmpeg { 34 | namespace ui { 35 | class nvenc_hevc_handler : public handler { 36 | public /*factory*/: 37 | virtual void adjust_encoder_info(obsffmpeg::encoder_factory* factory, 38 | obsffmpeg::encoder_info* main, 39 | obsffmpeg::encoder_info* fallback); 40 | 41 | virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context, 42 | bool hw_encode); 43 | 44 | public /*settings*/: 45 | virtual bool has_keyframe_support(obsffmpeg::encoder* instance); 46 | 47 | virtual void get_properties(obs_properties_t* props, const AVCodec* codec, 48 | AVCodecContext* context, bool hw_encode); 49 | 50 | virtual void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context); 51 | 52 | virtual void override_update(obsffmpeg::encoder* instance, obs_data_t* settings); 53 | 54 | virtual void log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context); 55 | 56 | public /*instance*/: 57 | //virtual void override_colorformat(AVPixelFormat& target_format, obs_data_t* settings, const AVCodec* codec, AVCodecContext* context); 58 | 59 | private: 60 | void get_encoder_properties(obs_properties_t* props, const AVCodec* codec); 61 | 62 | void get_runtime_properties(obs_properties_t* props, const AVCodec* codec, 63 | AVCodecContext* context); 64 | }; 65 | } // namespace ui 66 | } // namespace obsffmpeg 67 | -------------------------------------------------------------------------------- /source/ui/nvenc_shared.hpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #pragma once 23 | #include 24 | #include "utility.hpp" 25 | 26 | extern "C" { 27 | #include 28 | #pragma warning(push) 29 | #pragma warning(disable : 4244) 30 | #include 31 | #pragma warning(pop) 32 | } 33 | 34 | namespace obsffmpeg { 35 | class encoder; 36 | 37 | namespace nvenc { 38 | enum class preset : int64_t { 39 | DEFAULT, 40 | SLOW, 41 | MEDIUM, 42 | FAST, 43 | HIGH_PERFORMANCE, 44 | HIGH_QUALITY, 45 | BLURAYDISC, 46 | LOW_LATENCY, 47 | LOW_LATENCY_HIGH_PERFORMANCE, 48 | LOW_LATENCY_HIGH_QUALITY, 49 | LOSSLESS, 50 | LOSSLESS_HIGH_PERFORMANCE, 51 | // Append things before this. 52 | INVALID = -1, 53 | }; 54 | 55 | enum class ratecontrolmode : int64_t { 56 | CQP, 57 | VBR, 58 | VBR_HQ, 59 | CBR, 60 | CBR_HQ, 61 | CBR_LD_HQ, 62 | // Append things before this. 63 | INVALID = -1, 64 | }; 65 | 66 | enum class b_ref_mode : int64_t { 67 | DISABLED, 68 | EACH, 69 | MIDDLE, 70 | // Append things before this. 71 | INVALID = -1, 72 | }; 73 | 74 | extern std::map presets; 75 | 76 | extern std::map preset_to_opt; 77 | 78 | extern std::map ratecontrolmodes; 79 | 80 | extern std::map ratecontrolmode_to_opt; 81 | 82 | extern std::map b_ref_modes; 83 | 84 | extern std::map b_ref_mode_to_opt; 85 | 86 | void override_update(obsffmpeg::encoder* instance, obs_data_t* settings); 87 | 88 | void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context); 89 | 90 | void get_properties_pre(obs_properties_t* props, const AVCodec* codec); 91 | 92 | void get_properties_post(obs_properties_t* props, const AVCodec* codec); 93 | 94 | void get_runtime_properties(obs_properties_t* props, const AVCodec* codec, AVCodecContext* context); 95 | 96 | void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context); 97 | 98 | void log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context); 99 | } // namespace nvenc 100 | } // namespace obsffmpeg 101 | -------------------------------------------------------------------------------- /source/ui/prores_aw_handler.cpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #include "prores_aw_handler.hpp" 23 | #include "codecs/prores.hpp" 24 | #include "ffmpeg/tools.hpp" 25 | #include "plugin.hpp" 26 | #include "utility.hpp" 27 | 28 | extern "C" { 29 | #include 30 | } 31 | 32 | INITIALIZER(prores_aw_handler_init) 33 | { 34 | obsffmpeg::initializers.push_back([]() { 35 | obsffmpeg::register_codec_handler("prores_aw", std::make_shared()); 36 | }); 37 | }; 38 | 39 | void obsffmpeg::ui::prores_aw_handler::override_colorformat(AVPixelFormat& target_format, obs_data_t* settings, 40 | const AVCodec* codec, AVCodecContext*) 41 | { 42 | std::string profile = ""; 43 | 44 | int profile_id = static_cast(obs_data_get_int(settings, P_PRORES_PROFILE)); 45 | for (auto ptr = codec->profiles; ptr->profile != FF_PROFILE_UNKNOWN; ptr++) { 46 | if (ptr->profile == profile_id) { 47 | profile = ptr->name; 48 | break; 49 | } 50 | } 51 | 52 | std::unordered_map> valid_formats = { 53 | {AV_PIX_FMT_YUV422P10, {"apco", "apcs", "apcn", "apch"}}, {AV_PIX_FMT_YUV444P10, {"ap4h", "ap4x"}}}; 54 | 55 | for (auto kv : valid_formats) { 56 | for (auto name : kv.second) { 57 | if (profile == name) { 58 | target_format = kv.first; 59 | } 60 | } 61 | } 62 | } 63 | 64 | void obsffmpeg::ui::prores_aw_handler::get_defaults(obs_data_t* settings, const AVCodec*, AVCodecContext*, bool) 65 | { 66 | obs_data_set_default_int(settings, P_PRORES_PROFILE, 0); 67 | } 68 | 69 | inline const char* profile_to_name(const AVProfile* ptr) 70 | { 71 | switch (ptr->profile) { 72 | case 0: 73 | return TRANSLATE(P_PRORES_PROFILE_APCO); 74 | case 1: 75 | return TRANSLATE(P_PRORES_PROFILE_APCS); 76 | case 2: 77 | return TRANSLATE(P_PRORES_PROFILE_APCN); 78 | case 3: 79 | return TRANSLATE(P_PRORES_PROFILE_APCH); 80 | case 4: 81 | return TRANSLATE(P_PRORES_PROFILE_AP4H); 82 | case 5: 83 | return TRANSLATE(P_PRORES_PROFILE_AP4X); 84 | default: 85 | return ptr->name; 86 | } 87 | } 88 | 89 | void obsffmpeg::ui::prores_aw_handler::get_properties(obs_properties_t* props, const AVCodec* codec, 90 | AVCodecContext* context, bool) 91 | { 92 | if (!context) { 93 | auto p = obs_properties_add_list(props, P_PRORES_PROFILE, TRANSLATE(P_PRORES_PROFILE), 94 | OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); 95 | obs_property_set_long_description(p, TRANSLATE(DESC(P_PRORES_PROFILE))); 96 | for (auto ptr = codec->profiles; ptr->profile != FF_PROFILE_UNKNOWN; ptr++) { 97 | obs_property_list_add_int(p, profile_to_name(ptr), static_cast(ptr->profile)); 98 | } 99 | } else { 100 | obs_property_set_enabled(obs_properties_get(props, P_PRORES_PROFILE), false); 101 | } 102 | } 103 | 104 | void obsffmpeg::ui::prores_aw_handler::update(obs_data_t* settings, const AVCodec*, AVCodecContext* context) 105 | { 106 | context->profile = static_cast(obs_data_get_int(settings, P_PRORES_PROFILE)); 107 | } 108 | 109 | void obsffmpeg::ui::prores_aw_handler::log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context) 110 | { 111 | PLOG_INFO("[%s] Apple ProRes:", codec->name); 112 | ffmpeg::tools::print_av_option_string(context, "profile", " Profile", [&codec](int64_t v) { 113 | int val = static_cast(v); 114 | for (auto ptr = codec->profiles; (ptr->profile != FF_PROFILE_UNKNOWN) && (ptr != nullptr); ptr++) { 115 | if (ptr->profile == val) { 116 | return std::string(profile_to_name(ptr)); 117 | } 118 | } 119 | return std::string(""); 120 | }); 121 | } 122 | 123 | void obsffmpeg::ui::prores_aw_handler::process_avpacket(AVPacket& packet, const AVCodec*, AVCodecContext*) 124 | { 125 | //FFmpeg Bug: 126 | // When ProRes content is stored in Matroska, FFmpeg strips the size 127 | // from the atom. Later when the ProRes content is demuxed from Matroska, 128 | // FFmpeg creates an atom with the incorrect size, as the ATOM size 129 | // should be content + atom, but FFmpeg set it to only be content. This 130 | // difference leads to decoders to be off by 8 bytes. 131 | //Fix (until FFmpeg stops being broken): 132 | // Pad the packet with 8 bytes of 0x00. 133 | 134 | av_grow_packet(&packet, 8); 135 | } 136 | -------------------------------------------------------------------------------- /source/ui/prores_aw_handler.hpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #pragma once 23 | #include "handler.hpp" 24 | 25 | extern "C" { 26 | #include 27 | #pragma warning(push) 28 | #pragma warning(disable : 4244) 29 | #include 30 | #pragma warning(pop) 31 | } 32 | 33 | namespace obsffmpeg { 34 | namespace ui { 35 | class prores_aw_handler : public handler { 36 | public: 37 | virtual void override_colorformat(AVPixelFormat& target_format, obs_data_t* settings, 38 | const AVCodec* codec, AVCodecContext* context) override; 39 | 40 | virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context, 41 | bool hw_encode) override; 42 | 43 | virtual void get_properties(obs_properties_t* props, const AVCodec* codec, 44 | AVCodecContext* context, bool hw_encode) override; 45 | 46 | virtual void update(obs_data_t* settings, const AVCodec* codec, 47 | AVCodecContext* context) override; 48 | 49 | virtual void log_options(obs_data_t* settings, const AVCodec* codec, 50 | AVCodecContext* context) override; 51 | 52 | virtual void process_avpacket(AVPacket& packet, const AVCodec* codec, 53 | AVCodecContext* context) override; 54 | }; 55 | } // namespace ui 56 | } // namespace obsffmpeg 57 | -------------------------------------------------------------------------------- /source/utility.cpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #include "utility.hpp" 23 | #include "strings.hpp" 24 | #include "plugin.hpp" 25 | 26 | obs_property_t* obsffmpeg::obs_properties_add_tristate(obs_properties_t* props, const char* name, const char* desc) 27 | { 28 | obs_property_t* p = obs_properties_add_list(props, name, desc, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); 29 | obs_property_list_add_int(p, TRANSLATE(S_STATE_DEFAULT), -1); 30 | obs_property_list_add_int(p, TRANSLATE(S_STATE_DISABLED), 0); 31 | obs_property_list_add_int(p, TRANSLATE(S_STATE_ENABLED), 1); 32 | return p; 33 | } 34 | -------------------------------------------------------------------------------- /source/utility.hpp: -------------------------------------------------------------------------------- 1 | // FFMPEG Video Encoder Integration for OBS Studio 2 | // Copyright (c) 2019 Michael Fabian Dirks 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | #pragma once 23 | 24 | #include "version.hpp" 25 | 26 | extern "C" { 27 | #include 28 | #include 29 | } 30 | 31 | // Logging 32 | #define PLOG(level, ...) blog(level, "[obs-ffmpeg-encoder] " __VA_ARGS__); 33 | #define PLOG_ERROR(...) PLOG(LOG_ERROR, __VA_ARGS__) 34 | #define PLOG_WARNING(...) PLOG(LOG_WARNING, __VA_ARGS__) 35 | #define PLOG_INFO(...) PLOG(LOG_INFO, __VA_ARGS__) 36 | #define PLOG_DEBUG(...) PLOG(LOG_DEBUG, __VA_ARGS__) 37 | 38 | // Function Name 39 | #ifndef __FUNCTION_NAME__ 40 | #if defined(_WIN32) || defined(_WIN64) //WINDOWS 41 | #define __FUNCTION_NAME__ __FUNCTION__ 42 | #else //*NIX 43 | #define __FUNCTION_NAME__ __func__ 44 | #endif 45 | #endif 46 | 47 | // I18n 48 | #define TRANSLATE(x) obs_module_text(x) 49 | #define DESC(x) x ".Description" 50 | 51 | // Other 52 | #define D_STR(s) #s 53 | #define D_VSTR(s) D_STR(s) 54 | 55 | // Initializer 56 | #ifdef __cplusplus 57 | #define INITIALIZER(f) \ 58 | static void f(void); \ 59 | struct f##_t_ { \ 60 | f##_t_(void) \ 61 | { \ 62 | f(); \ 63 | } \ 64 | }; \ 65 | static f##_t_ f##_; \ 66 | static void f(void) 67 | #elif defined(_MSC_VER) 68 | #pragma section(".CRT$XCU", read) 69 | #define INITIALIZER2_(f, p) \ 70 | static void f(void); \ 71 | __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \ 72 | __pragma(comment(linker, "/include:" p #f "_")) static void f(void) 73 | #ifdef _WIN64 74 | #define INITIALIZER(f) INITIALIZER2_(f, "") 75 | #else 76 | #define INITIALIZER(f) INITIALIZER2_(f, "_") 77 | #endif 78 | #else 79 | #define INITIALIZER(f) \ 80 | static void f(void) __attribute__((constructor)); \ 81 | static void f(void) 82 | #endif 83 | 84 | // Helpers 85 | namespace obsffmpeg { 86 | bool inline are_property_groups_broken() 87 | { 88 | return obs_get_version() < MAKE_SEMANTIC_VERSION(24, 0, 0); 89 | } 90 | 91 | struct obs_graphics { 92 | obs_graphics() 93 | { 94 | obs_enter_graphics(); 95 | } 96 | ~obs_graphics() 97 | { 98 | obs_leave_graphics(); 99 | } 100 | }; 101 | 102 | obs_property_t* obs_properties_add_tristate(obs_properties_t* props, const char* name, const char* desc); 103 | 104 | inline bool is_tristate_enabled(int64_t tristate) { 105 | return tristate == 1; 106 | } 107 | 108 | inline bool is_tristate_disabled(int64_t tristate) { 109 | return tristate == 0; 110 | } 111 | 112 | inline bool is_tristate_default(int64_t tristate) { 113 | return tristate == -1; 114 | } 115 | } // namespace obsffmpeg 116 | --------------------------------------------------------------------------------