Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3a75b9f963 | |||
| 815152d024 | |||
| e4474c29c4 | |||
| 2a75d5fabd | |||
| a0bfac9319 | |||
| 65dddb5aa8 | |||
| 59e33a132b | |||
| be7b2737b4 | |||
| 8f20a45ee8 | |||
| 300531ad8a | |||
| c775ae93b7 | |||
| 18c50b3cbb | |||
| 04a22a13dc | |||
| d8eb38037b | |||
| 81164d0f43 | |||
| 05d8e74088 | |||
| d233b52e1f | |||
| e89ef57bda | |||
| 2759153b0e | |||
| 629afe3661 | |||
| 21629f1e13 | |||
| 80d104bdeb | |||
| ffb348b6c8 | |||
| b67f51df76 | |||
| 24d92e4b92 | |||
| 412c9fc18b | |||
| 2184e5bc71 | |||
| ad761834ee | |||
| a0b667c26f | |||
| 18024aaf12 | |||
| b2b5cd8fad | |||
| b6e881b90f | |||
| bd60958b2f | |||
| f9ad87a56d | |||
| b07365cdc4 | |||
| f21cbe9aba | |||
| 403b43e77b | |||
| 58d8713369 | |||
| 2f8acc58cf | |||
| e4e76dae8f | |||
| 4cac28a8a3 | |||
| d0dc4be985 | |||
| 4836f9dda6 | |||
| 650b397ced | |||
| d3f7b15633 | |||
| 545dcd6d50 | |||
| 0461b20e1b | |||
| b3a6dbb1b4 | |||
| fe71944199 | |||
| cbd39a8c2a | |||
| 62eae3827b | |||
| 38e7639862 | |||
| 6bc1cb9c88 | |||
| c63900d575 | |||
| 9efda8af8d | |||
| 0c9764a15c | |||
| 5c5a235502 | |||
| 2ebf90ffd7 | |||
| a32f8dd28b | |||
| d8a692de93 | |||
| a9f39527f6 | |||
| 71440ed3c5 | |||
| 3bd147e6e7 | |||
| 8b6af720bf | |||
| 993a4f8110 | |||
| 36222cc186 | |||
| 9fcec9b9a9 | |||
| 11f72c9bc7 | |||
| 5c8939b4a8 |
@@ -0,0 +1,51 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
windows:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [windows-2016, windows-2019]
|
||||||
|
include:
|
||||||
|
- os: windows-2016
|
||||||
|
generator_32: "Visual Studio 15 2017"
|
||||||
|
generator_64: "Visual Studio 15 2017"
|
||||||
|
sysversion: "10.0.17763.0"
|
||||||
|
- os: windows-2019
|
||||||
|
generator_32: "Visual Studio 16 2019"
|
||||||
|
generator_64: "Visual Studio 16 2019"
|
||||||
|
sysversion: "10.0.18362.0"
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: "Clone Repository"
|
||||||
|
uses: actions/checkout@v1
|
||||||
|
- name: Install Node.JS 10.x
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 10
|
||||||
|
- name: Configure & Compile
|
||||||
|
env:
|
||||||
|
CMAKE_GENERATOR_32: ${{ matrix.generator_32 }}
|
||||||
|
CMAKE_GENERATOR_64: ${{ matrix.generator_64 }}
|
||||||
|
CMAKE_SYSTEM_VERSION: ${{ matrix.sysversion }}
|
||||||
|
run: node ./ci/builder.js
|
||||||
|
- name: Package
|
||||||
|
env:
|
||||||
|
CMAKE_GENERATOR_32: ${{ matrix.generator_32 }}
|
||||||
|
CMAKE_GENERATOR_64: ${{ matrix.generator_64 }}
|
||||||
|
run: |
|
||||||
|
mkdir build/package
|
||||||
|
node ./ci/packager.js
|
||||||
|
- name: "Package Installer (Prereqs)"
|
||||||
|
run: |
|
||||||
|
curl "-kL" "https://cdn.xaymar.com/ci/innosetup-6.0.3.exe" "-f" "--retry" "5" "-o" "inno.exe"
|
||||||
|
.\inno.exe /VERYSILENT /SP- /SUPPRESSMSGBOXES /NORESTART
|
||||||
|
- name: "Package Installer (Compile)"
|
||||||
|
run: |
|
||||||
|
& 'C:\Program Files (x86)\Inno Setup 6\ISCC.exe' /Qp ".\build\64\installer.iss"
|
||||||
|
- name: "Upload Artifacts"
|
||||||
|
uses: actions/upload-artifact@v1
|
||||||
|
with:
|
||||||
|
name: ${{ matrix.os }}
|
||||||
|
path: build/package
|
||||||
+23
-3
@@ -25,7 +25,7 @@ Include("cmake/util.cmake")
|
|||||||
|
|
||||||
# Automatic Versioning
|
# Automatic Versioning
|
||||||
set(VERSION_MAJOR 0)
|
set(VERSION_MAJOR 0)
|
||||||
set(VERSION_MINOR 2)
|
set(VERSION_MINOR 4)
|
||||||
set(VERSION_PATCH 0)
|
set(VERSION_PATCH 0)
|
||||||
set(VERSION_TWEAK 0)
|
set(VERSION_TWEAK 0)
|
||||||
set(PROJECT_COMMIT "N/A")
|
set(PROJECT_COMMIT "N/A")
|
||||||
@@ -116,18 +116,27 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules/")
|
|||||||
configure_file(
|
configure_file(
|
||||||
"${PROJECT_SOURCE_DIR}/cmake/version.hpp.in"
|
"${PROJECT_SOURCE_DIR}/cmake/version.hpp.in"
|
||||||
"${PROJECT_BINARY_DIR}/source/version.hpp"
|
"${PROJECT_BINARY_DIR}/source/version.hpp"
|
||||||
|
@ONLY
|
||||||
)
|
)
|
||||||
configure_file(
|
configure_file(
|
||||||
"${PROJECT_SOURCE_DIR}/cmake/module.cpp.in"
|
"${PROJECT_SOURCE_DIR}/cmake/module.cpp.in"
|
||||||
"${PROJECT_BINARY_DIR}/source/module.cpp"
|
"${PROJECT_BINARY_DIR}/source/module.cpp"
|
||||||
|
@ONLY
|
||||||
)
|
)
|
||||||
|
|
||||||
# Windows
|
# Windows
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
## Installer (InnoSetup)
|
## Installer (InnoSetup)
|
||||||
|
get_filename_component(ISS_FILES_DIR "${CMAKE_INSTALL_PREFIX}" ABSOLUTE)
|
||||||
|
file(TO_NATIVE_PATH "${ISS_FILES_DIR}" ISS_FILES_DIR)
|
||||||
|
get_filename_component(ISS_PACKAGE_DIR "${CMAKE_PACKAGE_PREFIX}" ABSOLUTE)
|
||||||
|
file(TO_NATIVE_PATH "${ISS_PACKAGE_DIR}" ISS_PACKAGE_DIR)
|
||||||
|
get_filename_component(ISS_SOURCE_DIR "${PROJECT_SOURCE_DIR}" ABSOLUTE)
|
||||||
|
file(TO_NATIVE_PATH "${ISS_SOURCE_DIR}" ISS_SOURCE_DIR)
|
||||||
configure_file(
|
configure_file(
|
||||||
"${PROJECT_SOURCE_DIR}/cmake/installer.iss.in"
|
"${PROJECT_SOURCE_DIR}/cmake/installer.iss.in"
|
||||||
"${PROJECT_BINARY_DIR}/installer.iss"
|
"${PROJECT_BINARY_DIR}/installer.iss"
|
||||||
|
@ONLY
|
||||||
)
|
)
|
||||||
|
|
||||||
# Windows Specific Resource Definition
|
# Windows Specific Resource Definition
|
||||||
@@ -155,7 +164,7 @@ mark_as_advanced(FORCE OBS_NATIVE OBS_PACKAGE OBS_REFERENCE OBS_DOWNLOAD)
|
|||||||
|
|
||||||
if(NOT TARGET libobs)
|
if(NOT TARGET libobs)
|
||||||
set(${PropertyPrefix}OBS_STUDIO_DIR "" CACHE PATH "OBS Studio Source/Package Directory")
|
set(${PropertyPrefix}OBS_STUDIO_DIR "" CACHE PATH "OBS Studio Source/Package Directory")
|
||||||
set(${PropertyPrefix}OBS_DOWNLOAD_VERSION "23.2.1-ci" CACHE STRING "OBS Studio Version to download")
|
set(${PropertyPrefix}OBS_DOWNLOAD_VERSION "24.0.3-ci" CACHE STRING "OBS Studio Version to download")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT ${PropertyPrefix}OBS_NATIVE)
|
if(NOT ${PropertyPrefix}OBS_NATIVE)
|
||||||
@@ -272,12 +281,16 @@ set(PROJECT_PRIVATE
|
|||||||
"${PROJECT_SOURCE_DIR}/source/codecs/hevc.cpp"
|
"${PROJECT_SOURCE_DIR}/source/codecs/hevc.cpp"
|
||||||
"${PROJECT_SOURCE_DIR}/source/codecs/h264.hpp"
|
"${PROJECT_SOURCE_DIR}/source/codecs/h264.hpp"
|
||||||
"${PROJECT_SOURCE_DIR}/source/codecs/h264.cpp"
|
"${PROJECT_SOURCE_DIR}/source/codecs/h264.cpp"
|
||||||
|
"${PROJECT_SOURCE_DIR}/source/codecs/prores.hpp"
|
||||||
|
"${PROJECT_SOURCE_DIR}/source/codecs/prores.cpp"
|
||||||
"${PROJECT_SOURCE_DIR}/source/ffmpeg/avframe-queue.cpp"
|
"${PROJECT_SOURCE_DIR}/source/ffmpeg/avframe-queue.cpp"
|
||||||
"${PROJECT_SOURCE_DIR}/source/ffmpeg/avframe-queue.hpp"
|
"${PROJECT_SOURCE_DIR}/source/ffmpeg/avframe-queue.hpp"
|
||||||
"${PROJECT_SOURCE_DIR}/source/ffmpeg/swscale.hpp"
|
"${PROJECT_SOURCE_DIR}/source/ffmpeg/swscale.hpp"
|
||||||
"${PROJECT_SOURCE_DIR}/source/ffmpeg/swscale.cpp"
|
"${PROJECT_SOURCE_DIR}/source/ffmpeg/swscale.cpp"
|
||||||
"${PROJECT_SOURCE_DIR}/source/ffmpeg/tools.hpp"
|
"${PROJECT_SOURCE_DIR}/source/ffmpeg/tools.hpp"
|
||||||
"${PROJECT_SOURCE_DIR}/source/ffmpeg/tools.cpp"
|
"${PROJECT_SOURCE_DIR}/source/ffmpeg/tools.cpp"
|
||||||
|
"${PROJECT_SOURCE_DIR}/source/hwapi/base.hpp"
|
||||||
|
"${PROJECT_SOURCE_DIR}/source/hwapi/base.cpp"
|
||||||
"${PROJECT_SOURCE_DIR}/source/ui/handler.hpp"
|
"${PROJECT_SOURCE_DIR}/source/ui/handler.hpp"
|
||||||
"${PROJECT_SOURCE_DIR}/source/ui/handler.cpp"
|
"${PROJECT_SOURCE_DIR}/source/ui/handler.cpp"
|
||||||
"${PROJECT_SOURCE_DIR}/source/ui/debug_handler.hpp"
|
"${PROJECT_SOURCE_DIR}/source/ui/debug_handler.hpp"
|
||||||
@@ -291,6 +304,12 @@ set(PROJECT_PRIVATE
|
|||||||
"${PROJECT_SOURCE_DIR}/source/ui/nvenc_hevc_handler.hpp"
|
"${PROJECT_SOURCE_DIR}/source/ui/nvenc_hevc_handler.hpp"
|
||||||
"${PROJECT_SOURCE_DIR}/source/ui/nvenc_hevc_handler.cpp"
|
"${PROJECT_SOURCE_DIR}/source/ui/nvenc_hevc_handler.cpp"
|
||||||
)
|
)
|
||||||
|
if(WIN32)
|
||||||
|
list(APPEND PROJECT_PRIVATE
|
||||||
|
"${PROJECT_SOURCE_DIR}/source/hwapi/d3d11.hpp"
|
||||||
|
"${PROJECT_SOURCE_DIR}/source/hwapi/d3d11.cpp"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Source Grouping
|
# Source Grouping
|
||||||
source_group(TREE "${PROJECT_SOURCE_DIR}" PREFIX "Data Files" FILES ${PROJECT_DATA})
|
source_group(TREE "${PROJECT_SOURCE_DIR}" PREFIX "Data Files" FILES ${PROJECT_DATA})
|
||||||
@@ -315,6 +334,7 @@ add_library(${PROJECT_NAME} MODULE
|
|||||||
${PROJECT_GENERATED}
|
${PROJECT_GENERATED}
|
||||||
${PROJECT_PRIVATE}
|
${PROJECT_PRIVATE}
|
||||||
${PROJECT_DATA}
|
${PROJECT_DATA}
|
||||||
|
${PROJECT_TEMPLATES}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Include Directories
|
# Include Directories
|
||||||
@@ -355,7 +375,7 @@ endif()
|
|||||||
|
|
||||||
# Link Libraries
|
# Link Libraries
|
||||||
target_link_libraries(${PROJECT_NAME}
|
target_link_libraries(${PROJECT_NAME}
|
||||||
"${PROJECT_LIBRARIES}"
|
${PROJECT_LIBRARIES}
|
||||||
${FFMPEG_LIBRARIES}
|
${FFMPEG_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,68 +0,0 @@
|
|||||||
# Generic Settings
|
|
||||||
version: '{build}-{branch}'
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
fast_finish: true
|
|
||||||
|
|
||||||
# Build Image & Environment
|
|
||||||
platform: x64
|
|
||||||
|
|
||||||
image:
|
|
||||||
- Visual Studio 2017
|
|
||||||
|
|
||||||
environment:
|
|
||||||
CMAKE_SYSTEM_VERSION: 10.0.17134.0
|
|
||||||
PACKAGE_PREFIX: obs-ffmpeg-encoder
|
|
||||||
INNOSETUP_URL: http://www.jrsoftware.org/download.php/is.exe
|
|
||||||
CURL_VERSION: 7.39.0
|
|
||||||
|
|
||||||
# Resource Cache
|
|
||||||
cache:
|
|
||||||
- inno.exe
|
|
||||||
- build/32/libobs-download
|
|
||||||
- build/32/libobs-src
|
|
||||||
- build/64/libobs-download
|
|
||||||
- build/64/libobs-src
|
|
||||||
|
|
||||||
# Building
|
|
||||||
install:
|
|
||||||
- cmd: ci/appveyor-install.bat
|
|
||||||
|
|
||||||
build_script:
|
|
||||||
- cmd: node ci/builder.js
|
|
||||||
|
|
||||||
after_build:
|
|
||||||
- cmd: ci/appveyor-package.bat
|
|
||||||
|
|
||||||
# Testing
|
|
||||||
test: off
|
|
||||||
|
|
||||||
# Artifacts
|
|
||||||
artifacts:
|
|
||||||
- path: build/obs-ffmpeg-encoder-*.zip
|
|
||||||
- path: build/obs-ffmpeg-encoder-*.7z
|
|
||||||
- path: build/obs-ffmpeg-encoder-*.exe
|
|
||||||
|
|
||||||
# Deploying
|
|
||||||
deploy:
|
|
||||||
- provider: GitHub
|
|
||||||
auth_token:
|
|
||||||
secure: diGN1FzupARljI1iJsiAdZHut8aXODkUC6YDDi2oDRikEp5Ic8kQd8SSRDyA4pAJ
|
|
||||||
draft: true
|
|
||||||
prerelease: false
|
|
||||||
force_update: true
|
|
||||||
on:
|
|
||||||
appveyor_repo_tag: true
|
|
||||||
|
|
||||||
# Notifications
|
|
||||||
notifications:
|
|
||||||
- provider: Webhook
|
|
||||||
url:
|
|
||||||
secure: PTtt5ALhmK0q42jYyx4/Qa1Uf18+gLMXKGdzJjDISJt8IE/K0Zyp58UYmDDbbyLp4pBRf/Ylj8rn/zYL/mqBoDVRIH5zasPqIvBD0ZhtvNjTOxQ3QoRkAmxgpWeMowm3A3I1rLizA2H4EctPpoAJGrvQ1G2HEYn9tVsGYeetFTo=
|
|
||||||
on_build_success: false
|
|
||||||
on_build_failure: false
|
|
||||||
on_build_status_changed: true
|
|
||||||
body: >-
|
|
||||||
{
|
|
||||||
"content": "**Build {{status}}**: [{{commitId}}] {{commitMessage}}\nBy {{commitAuthor}} on {{commitDate}}\n{{buildUrl}}"
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
@ECHO OFF
|
|
||||||
git submodule update --init --force --recursive
|
|
||||||
|
|
||||||
IF EXIST inno.exe (
|
|
||||||
curl -kL "%INNOSETUP_URL%" -f --retry 5 -o inno.exe -z inno.exe
|
|
||||||
) else (
|
|
||||||
curl -kL "%INNOSETUP_URL%" -f --retry 5 -o inno.exe
|
|
||||||
)
|
|
||||||
inno.exe /VERYSILENT /NORETART /SP- /SUPPRESSMSGBOXES
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
@ECHO OFF
|
|
||||||
ECHO -- Building Installer --
|
|
||||||
"C:\Program Files (x86)\Inno Setup 5\ISCC.exe" /Qp ".\build\64\installer.iss" > nul
|
|
||||||
+68
-85
@@ -2,108 +2,91 @@
|
|||||||
|
|
||||||
const process = require('process');
|
const process = require('process');
|
||||||
const runner = require('./runner.js');
|
const runner = require('./runner.js');
|
||||||
|
let env = process.env;
|
||||||
|
|
||||||
// Steps
|
let x32_steps = [];
|
||||||
let configure_runners = [];
|
let x64_steps = [];
|
||||||
let build_runners = [];
|
|
||||||
let package_runners = [];
|
|
||||||
|
|
||||||
{
|
if ((process.platform == "win32") || (process.platform == "win64")) {
|
||||||
let cmake_configure_extra = [
|
// Windows
|
||||||
|
let extra_conf = [
|
||||||
`-DCMAKE_SYSTEM_VERSION=${process.env.CMAKE_SYSTEM_VERSION}`,
|
`-DCMAKE_SYSTEM_VERSION=${process.env.CMAKE_SYSTEM_VERSION}`,
|
||||||
|
`-DCMAKE_PACKAGE_NAME=obs-ffmpeg-encoder`,
|
||||||
'-DCMAKE_INSTALL_PREFIX="build/distrib/"',
|
'-DCMAKE_INSTALL_PREFIX="build/distrib/"',
|
||||||
'-DCMAKE_PACKAGE_PREFIX="build/"',
|
'-DCMAKE_PACKAGE_PREFIX="build/package/"',
|
||||||
`-DCMAKE_PACKAGE_NAME="${process.env.PACKAGE_PREFIX}"`,
|
|
||||||
];
|
];
|
||||||
let cmake_build_extra = [
|
let extra_build = [
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// Configuration depends on platform
|
|
||||||
if (process.platform == "win32" || process.platform == "win64") {
|
|
||||||
configure_runners.push(new runner('32-bit', 'cmake', [
|
|
||||||
'-H.',
|
|
||||||
'-Bbuild/32',
|
|
||||||
`-G"Visual Studio 15 2017"`,
|
|
||||||
].concat(cmake_configure_extra)));
|
|
||||||
configure_runners.push(new runner('64-bit', 'cmake', [
|
|
||||||
'-H.',
|
|
||||||
'-Bbuild/64',
|
|
||||||
`-G"Visual Studio 15 2017 Win64"`,
|
|
||||||
'-T"host=x64"',
|
|
||||||
].concat(cmake_configure_extra)));
|
|
||||||
|
|
||||||
// Extra build steps for AppVeyor on Windows for Logging purposes.
|
|
||||||
if(process.env.APPVEYOR) {
|
if(process.env.APPVEYOR) {
|
||||||
cmake_build_extra.concat(['--', '/logger:"C:\\Program Files\\AppVeyor\\BuildAgent\\Appveyor.MSBuildLogger.dll"']);
|
extra_build.concat(['--', '/logger:"C:\\Program Files\\AppVeyor\\BuildAgent\\Appveyor.MSBuildLogger.dll"']);
|
||||||
}
|
|
||||||
} else if (process.platform == "linux") {
|
|
||||||
configure_runners.push(new runner('32-bit', 'cmake', [
|
|
||||||
'-H.',
|
|
||||||
'-Bbuild32',
|
|
||||||
`-G"Unix Makefiles"`,
|
|
||||||
`-DCOPIED_DEPENDENCIES=false`,
|
|
||||||
].concat(cmake_configure_extra),
|
|
||||||
{ ...process.env, ...{
|
|
||||||
CFLAGS: `${process.env.COMPILER_FLAGS_32}`,
|
|
||||||
CXXFLAGS: `${process.env.COMPILER_FLAGS_32}`,
|
|
||||||
}}));
|
|
||||||
configure_runners.push(new runner('64-bit', 'cmake', [
|
|
||||||
'-H.',
|
|
||||||
'-Bbuild64',
|
|
||||||
`-G"Unix Makefiles"`,
|
|
||||||
`-DCOPIED_DEPENDENCIES=false`,
|
|
||||||
].concat(cmake_configure_extra),
|
|
||||||
{ ...process.env, ...{
|
|
||||||
CFLAGS: `${process.env.COMPILER_FLAGS_64}`,
|
|
||||||
CXXFLAGS: `${process.env.COMPILER_FLAGS_64}`,
|
|
||||||
}}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
build_runners.push(new runner('32-bit', 'cmake', [
|
if ((process.env.CMAKE_GENERATOR_32 !== undefined) && (process.env.CMAKE_GENERATOR_32 !== "")) {
|
||||||
|
x32_steps.push(
|
||||||
|
[ 'cmake', [
|
||||||
|
'-H.', '-Bbuild/32',
|
||||||
|
`-G"${process.env.CMAKE_GENERATOR_32}"`, '-AWin32', '-T"host=x64"',
|
||||||
|
].concat(extra_conf), env ]
|
||||||
|
);
|
||||||
|
x32_steps.push(
|
||||||
|
[ 'cmake', [
|
||||||
'--build', 'build/32',
|
'--build', 'build/32',
|
||||||
'--config', 'RelWithDebInfo',
|
'--config', 'RelWithDebInfo',
|
||||||
'--target', 'INSTALL'
|
'--target', 'INSTALL'
|
||||||
].concat(cmake_build_extra)));
|
].concat(extra_build), env ]
|
||||||
build_runners.push(new runner('64-bit', 'cmake', [
|
);
|
||||||
|
}
|
||||||
|
if ((process.env.CMAKE_GENERATOR_64 !== undefined) && (process.env.CMAKE_GENERATOR_64 !== "")) {
|
||||||
|
x64_steps.push(
|
||||||
|
[ 'cmake', [
|
||||||
|
'-H.', '-Bbuild/64',
|
||||||
|
`-G"${process.env.CMAKE_GENERATOR_64}"`, '-Ax64', '-T"host=x64"',
|
||||||
|
].concat(extra_conf), env ]
|
||||||
|
);
|
||||||
|
x64_steps.push(
|
||||||
|
[ 'cmake', [
|
||||||
'--build', 'build/64',
|
'--build', 'build/64',
|
||||||
'--config', 'RelWithDebInfo',
|
'--config', 'RelWithDebInfo',
|
||||||
'--target', 'INSTALL'
|
'--target', 'INSTALL'
|
||||||
].concat(cmake_build_extra)));
|
].concat(extra_build), env ]
|
||||||
package_runners.push(new runner('32-bit', 'cmake', [
|
);
|
||||||
'--build', 'build/32',
|
}
|
||||||
'--target', 'PACKAGE_7Z',
|
} else {
|
||||||
'--config', 'RelWithDebInfo'
|
// Unix
|
||||||
].concat(cmake_build_extra)));
|
|
||||||
package_runners.push(new runner('64-bit', 'cmake', [
|
|
||||||
'--build', 'build/64',
|
|
||||||
'--target', 'PACKAGE_ZIP',
|
|
||||||
'--config', 'RelWithDebInfo'
|
|
||||||
].concat(cmake_build_extra)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run Configure steps.
|
function runRunners(runnerArray, name) {
|
||||||
let configure_promises = [];
|
return new Promise(async (resolve, reject) => {
|
||||||
for (let config of configure_runners) {
|
let local = runnerArray.reverse();
|
||||||
configure_promises.push(config.run());
|
while (local.length > 0) {
|
||||||
|
try {
|
||||||
|
let task = local.pop();
|
||||||
|
let work = new runner(name, task[0], task[1], task[2]);
|
||||||
|
await work.run();
|
||||||
|
} catch (e) {
|
||||||
|
reject(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolve(0);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
Promise.all(configure_promises).then(function(result) {
|
|
||||||
let build_promises = [];
|
let promises = [];
|
||||||
for (let build of build_runners) {
|
promises.push(runRunners(x32_steps, "32-Bit"));
|
||||||
build_promises.push(build.run());
|
promises.push(runRunners(x64_steps, "64-Bit"));
|
||||||
|
Promise.all(promises).then(
|
||||||
|
res => {
|
||||||
|
process.exit(0);
|
||||||
|
},
|
||||||
|
err => {
|
||||||
|
console.log(err);
|
||||||
|
process.exit(1);
|
||||||
}
|
}
|
||||||
Promise.all(build_promises).then(function(result) {
|
).catch(err => {
|
||||||
let package_promises = [];
|
console.log(err);
|
||||||
for (let pack of package_runners) {
|
process.exit(1);
|
||||||
package_promises.push(pack.run());
|
})
|
||||||
}
|
|
||||||
Promise.all(package_promises).then(function(result) {
|
|
||||||
process.exit(result);
|
|
||||||
}).catch(function(result) {
|
|
||||||
process.exit(result);
|
|
||||||
});
|
|
||||||
}).catch(function(result) {
|
|
||||||
process.exit(result);
|
|
||||||
});
|
|
||||||
}).catch(function(result) {
|
|
||||||
process.exit(result);
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const process = require('process');
|
||||||
|
const runner = require('./runner.js');
|
||||||
|
let env = process.env;
|
||||||
|
|
||||||
|
let steps = [];
|
||||||
|
|
||||||
|
if ((process.env.CMAKE_GENERATOR_64 !== undefined) && (process.env.CMAKE_GENERATOR_64 !== "")) {
|
||||||
|
steps.push(
|
||||||
|
[ 'cmake', [
|
||||||
|
'--build', 'build/64',
|
||||||
|
'--config', 'RelWithDebInfo',
|
||||||
|
'--target', 'PACKAGE_7Z'
|
||||||
|
], env ]
|
||||||
|
);
|
||||||
|
steps.push(
|
||||||
|
[ 'cmake', [
|
||||||
|
'--build', 'build/64',
|
||||||
|
'--config', 'RelWithDebInfo',
|
||||||
|
'--target', 'PACKAGE_ZIP'
|
||||||
|
], env ]
|
||||||
|
);
|
||||||
|
} else if ((process.env.CMAKE_GENERATOR_32 !== undefined) && (process.env.CMAKE_GENERATOR_32 !== "")) {
|
||||||
|
steps.push(
|
||||||
|
[ 'cmake', [
|
||||||
|
'--build', 'build/32',
|
||||||
|
'--config', 'RelWithDebInfo',
|
||||||
|
'--target', 'PACKAGE_7Z'
|
||||||
|
], env ]
|
||||||
|
);
|
||||||
|
steps.push(
|
||||||
|
[ 'cmake', [
|
||||||
|
'--build', 'build/32',
|
||||||
|
'--config', 'RelWithDebInfo',
|
||||||
|
'--target', 'PACKAGE_ZIP'
|
||||||
|
], env ]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function runRunners(runnerArray, name) {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
let local = runnerArray.reverse();
|
||||||
|
while (local.length > 0) {
|
||||||
|
try {
|
||||||
|
let task = local.pop();
|
||||||
|
let work = new runner(name, task[0], task[1], task[2]);
|
||||||
|
await work.run();
|
||||||
|
} catch (e) {
|
||||||
|
reject(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolve(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let promises = [];
|
||||||
|
promises.push(runRunners(steps, "32-Bit"));
|
||||||
|
Promise.all(promises).then(
|
||||||
|
res => {
|
||||||
|
process.exit(0);
|
||||||
|
},
|
||||||
|
err => {
|
||||||
|
console.log(err);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -18,7 +18,6 @@ class Runner {
|
|||||||
run() {
|
run() {
|
||||||
let self = this;
|
let self = this;
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
console.log(self.cmd, self.args);
|
|
||||||
self.proc = cp.spawn(
|
self.proc = cp.spawn(
|
||||||
self.cmd, self.args, {
|
self.cmd, self.args, {
|
||||||
windowsVerbatimArguments: true,
|
windowsVerbatimArguments: true,
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ AppUpdatesURL={#MyAppURL}
|
|||||||
DefaultDirName={code:GetDirName}
|
DefaultDirName={code:GetDirName}
|
||||||
DefaultGroupName={#MyAppName}
|
DefaultGroupName={#MyAppName}
|
||||||
AllowNoIcons=yes
|
AllowNoIcons=yes
|
||||||
LicenseFile="@PROJECT_SOURCE_DIR@/LICENSE"
|
LicenseFile="@ISS_SOURCE_DIR@/LICENSE"
|
||||||
OutputDir="@CMAKE_INSTALL_PREFIX@/../"
|
OutputDir="@ISS_PACKAGE_DIR@"
|
||||||
OutputBaseFilename=obs-ffmpeg-encoder-{#MyAppVersion}
|
OutputBaseFilename=obs-ffmpeg-encoder-{#MyAppVersion}
|
||||||
Compression=lzma
|
Compression=lzma
|
||||||
SolidCompression=yes
|
SolidCompression=yes
|
||||||
@@ -34,7 +34,7 @@ VersionInfoDescription={#MyAppName} Setup
|
|||||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||||
|
|
||||||
[Files]
|
[Files]
|
||||||
Source: "@CMAKE_INSTALL_PREFIX@/*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
Source: "@ISS_FILES_DIR@/*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||||
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
||||||
|
|
||||||
[Icons]
|
[Icons]
|
||||||
|
|||||||
+35
-24
@@ -8,7 +8,7 @@ State.Manual="Manual"
|
|||||||
# FFmpeg
|
# FFmpeg
|
||||||
FFmpeg="FFmpeg Options"
|
FFmpeg="FFmpeg Options"
|
||||||
FFmpeg.CustomSettings="Custom Settings"
|
FFmpeg.CustomSettings="Custom Settings"
|
||||||
FFmpeg.CustomSettings.Description="Custom settings that override any detected options above, use with caution.\nThe input should be in the format 'key=value;key=value;...'."
|
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'"
|
||||||
FFmpeg.Threads="Number of Threads"
|
FFmpeg.Threads="Number of Threads"
|
||||||
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."
|
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."
|
||||||
FFmpeg.ColorFormat="Override Color Format"
|
FFmpeg.ColorFormat="Override Color Format"
|
||||||
@@ -20,6 +20,9 @@ FFmpeg.StandardCompliance.Strict="Strict"
|
|||||||
FFmpeg.StandardCompliance.Normal="Normal"
|
FFmpeg.StandardCompliance.Normal="Normal"
|
||||||
FFmpeg.StandardCompliance.Unofficial="Unofficial"
|
FFmpeg.StandardCompliance.Unofficial="Unofficial"
|
||||||
FFmpeg.StandardCompliance.Experimental="Experimental"
|
FFmpeg.StandardCompliance.Experimental="Experimental"
|
||||||
|
FFmpeg.GPU="GPU"
|
||||||
|
FFmpeg.GPU.Description="For multiple GPU systems, selects which GPU to use as the main encoder"
|
||||||
|
|
||||||
|
|
||||||
# Rate Control
|
# Rate Control
|
||||||
RateControl="Rate Control"
|
RateControl="Rate Control"
|
||||||
@@ -46,22 +49,10 @@ KeyFrames="Key Frames"
|
|||||||
KeyFrames.IntervalType="Interval Type"
|
KeyFrames.IntervalType="Interval Type"
|
||||||
KeyFrames.IntervalType.Frames="Frames"
|
KeyFrames.IntervalType.Frames="Frames"
|
||||||
KeyFrames.IntervalType.Seconds="Seconds"
|
KeyFrames.IntervalType.Seconds="Seconds"
|
||||||
|
KeyFrames.IntervalType.Description="Keyframe interval type"
|
||||||
|
KeyFrames.Interval.Description="Distance between key frames, in frames or seconds."
|
||||||
KeyFrames.Interval="Interval"
|
KeyFrames.Interval="Interval"
|
||||||
|
|
||||||
# Apple ProRes
|
|
||||||
AppleProRes.Profile="Profile"
|
|
||||||
AppleProRes.Profile.APCO="Proxy"
|
|
||||||
AppleProRes.Profile.APCS="LT"
|
|
||||||
AppleProRes.Profile.APCN="Standard Definition"
|
|
||||||
AppleProRes.Profile.APCH="High Quality"
|
|
||||||
AppleProRes.Profile.AP4H="4444"
|
|
||||||
|
|
||||||
# ProRes
|
|
||||||
ProRes.Profile.Proxy="Proxy (PXY)"
|
|
||||||
ProRes.Profile.Light="Light (LT)"
|
|
||||||
ProRes.Profile.Standard="Standard"
|
|
||||||
ProRes.Profile.HighQuality="High Quality (HQ)"
|
|
||||||
|
|
||||||
# Codec: H264
|
# Codec: H264
|
||||||
Codec.H264="H264"
|
Codec.H264="H264"
|
||||||
Codec.H264.Profile="Profile"
|
Codec.H264.Profile="Profile"
|
||||||
@@ -69,7 +60,9 @@ Codec.H264.Profile.baseline="Baseline"
|
|||||||
Codec.H264.Profile.main="Main"
|
Codec.H264.Profile.main="Main"
|
||||||
Codec.H264.Profile.high="High"
|
Codec.H264.Profile.high="High"
|
||||||
Codec.H264.Profile.high444p="High 4:4:4 Predictive"
|
Codec.H264.Profile.high444p="High 4:4:4 Predictive"
|
||||||
|
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."
|
||||||
Codec.H264.Level="Level"
|
Codec.H264.Level="Level"
|
||||||
|
Codec.H264.Level.Description="Level determines the upper limits of resolution, frame rate and bitrate for the video."
|
||||||
|
|
||||||
# Codec: HEVC
|
# Codec: HEVC
|
||||||
Codec.HEVC="HEVC"
|
Codec.HEVC="HEVC"
|
||||||
@@ -81,9 +74,20 @@ Codec.HEVC.Tier="Tier"
|
|||||||
Codec.HEVC.Tier.main="Main"
|
Codec.HEVC.Tier.main="Main"
|
||||||
Codec.HEVC.Tier.high="High"
|
Codec.HEVC.Tier.high="High"
|
||||||
Codec.HEVC.Level="Level"
|
Codec.HEVC.Level="Level"
|
||||||
|
Codec.HEVC.Level.Description="Level determines the upper limits of resolution, frame rate and bitrate for the video."
|
||||||
|
|
||||||
|
# Codec: Apple ProRes
|
||||||
|
Codec.ProRes.Profile="Profile"
|
||||||
|
Codec.ProRes.Profile.APCO="422 Proxy/PXY (APCO)"
|
||||||
|
Codec.ProRes.Profile.APCS="422 Light/LT (APCS)"
|
||||||
|
Codec.ProRes.Profile.APCN="422 Standard (APCN)"
|
||||||
|
Codec.ProRes.Profile.APCH="422 High Quality/HQ (APCH)"
|
||||||
|
Codec.ProRes.Profile.AP4H="4444 Standard (AP4H)"
|
||||||
|
Codec.ProRes.Profile.AP4X="4444 Extra Quality/XQ (AP4X)"
|
||||||
|
|
||||||
# NVENC
|
# NVENC
|
||||||
NVENC.Preset="Preset"
|
NVENC.Preset="Preset"
|
||||||
|
NVENC.Preset.Description="Presets are NVIDIA's preconfigured default settings."
|
||||||
NVENC.Preset.Default="Default"
|
NVENC.Preset.Default="Default"
|
||||||
NVENC.Preset.Slow="Slow"
|
NVENC.Preset.Slow="Slow"
|
||||||
NVENC.Preset.Medium="Medium"
|
NVENC.Preset.Medium="Medium"
|
||||||
@@ -98,19 +102,26 @@ NVENC.Preset.Lossless="Lossless"
|
|||||||
NVENC.Preset.LosslessHighPerformance="Lossless High Performance"
|
NVENC.Preset.LosslessHighPerformance="Lossless High Performance"
|
||||||
NVENC.RateControl="Rate Control Options"
|
NVENC.RateControl="Rate Control Options"
|
||||||
NVENC.RateControl.Mode="Mode"
|
NVENC.RateControl.Mode="Mode"
|
||||||
|
NVENC.RateControl.Mode.Description="Rate control mode selection"
|
||||||
NVENC.RateControl.Mode.CQP="Constant Quantization Parameter"
|
NVENC.RateControl.Mode.CQP="Constant Quantization Parameter"
|
||||||
|
NVENC.RateControl.Mode.CQP.Description="A flat compression ratio with no regard for bit rates."
|
||||||
NVENC.RateControl.Mode.VBR="Variable Bitrate"
|
NVENC.RateControl.Mode.VBR="Variable Bitrate"
|
||||||
|
NVENC.RateControl.Mode.VBR.Description="Sacrifices quality to stay below the upper bitrate limit,\nor saves bitrate where possible."
|
||||||
NVENC.RateControl.Mode.VBR_HQ="High Quality Variable Bitrate"
|
NVENC.RateControl.Mode.VBR_HQ="High Quality Variable Bitrate"
|
||||||
|
NVENC.RateControl.Mode.VBR_HQ.Description="Variable Bitrate with two-pass encoding enabled by default."
|
||||||
NVENC.RateControl.Mode.CBR="Constant Bitrate"
|
NVENC.RateControl.Mode.CBR="Constant Bitrate"
|
||||||
|
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."
|
||||||
NVENC.RateControl.Mode.CBR_HQ="High Quality Constant Bitrate"
|
NVENC.RateControl.Mode.CBR_HQ="High Quality Constant Bitrate"
|
||||||
|
NVENC.RateControl.Mode.CBR_HQ.Description="Constant Bitrate with two-pass encoding enabled by default."
|
||||||
NVENC.RateControl.Mode.CBR_LD_HQ="Low Delay High Quality Constant Bitrate"
|
NVENC.RateControl.Mode.CBR_LD_HQ="Low Delay High Quality Constant Bitrate"
|
||||||
|
NVENC.RateControl.Mode.CBR_LD_HQ.Description="Constant Bitrate optimized for lowest encoding latency."
|
||||||
NVENC.RateControl.LookAhead="Look Ahead"
|
NVENC.RateControl.LookAhead="Look Ahead"
|
||||||
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."
|
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."
|
||||||
NVENC.RateControl.AdaptiveI="Enable adaptive I-Frame insertion"
|
NVENC.RateControl.AdaptiveI="Adaptive I-Frames"
|
||||||
NVENC.RateControl.AdaptiveI.Description="Enables adaptive I-Frame insertion.\nOnly has an effect when look ahead is set to a value other than 0."
|
NVENC.RateControl.AdaptiveI.Description="Enables adaptive I-Frame insertion.\nOnly has an effect when look ahead is set to a value other than 0."
|
||||||
NVENC.RateControl.AdaptiveB="Enable adaptive B-Frame insertion"
|
NVENC.RateControl.AdaptiveB="Adaptive B-Frames"
|
||||||
NVENC.RateControl.AdaptiveB.Description="Enables adaptive B-Frame insertion.\nOnly has an effect when look ahead is set to a value other than 0."
|
NVENC.RateControl.AdaptiveB.Description="Enables adaptive B-Frame insertion.\nOnly has an effect when look ahead is set to a value other than 0."
|
||||||
NVENC.RateControl.TwoPass="Enable Two Pass"
|
NVENC.RateControl.TwoPass="Two Pass"
|
||||||
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."
|
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."
|
||||||
NVENC.RateControl.Bitrate="Bitrate Limits"
|
NVENC.RateControl.Bitrate="Bitrate Limits"
|
||||||
NVENC.RateControl.Bitrate.Target="Target Bitrate"
|
NVENC.RateControl.Bitrate.Target="Target Bitrate"
|
||||||
@@ -136,21 +147,21 @@ NVENC.RateControl.QP.B.Description="Quantization parameter for B-Frames.\nSmalle
|
|||||||
NVENC.RateControl.QP.B.Initial="Initial B-Frame QP"
|
NVENC.RateControl.QP.B.Initial="Initial B-Frame QP"
|
||||||
NVENC.RateControl.QP.B.Initial.Description="Initial B-Frame quantization parameter.\nSet to -1 to use the automatically detected value instead."
|
NVENC.RateControl.QP.B.Initial.Description="Initial B-Frame quantization parameter.\nSet to -1 to use the automatically detected value instead."
|
||||||
NVENC.AQ="Adaptive Quantization"
|
NVENC.AQ="Adaptive Quantization"
|
||||||
NVENC.AQ.Spatial="Enable Spatial Adaptive Quantization"
|
NVENC.AQ.Spatial="Spatial Adaptive Quantization"
|
||||||
NVENC.AQ.Spatial.Description="Enable spatial adaptive quantization, also sometimes referred to as Psychovisual Adaptive Quantization."
|
NVENC.AQ.Spatial.Description="Enable spatial adaptive quantization, also sometimes referred to as Psychovisual Adaptive Quantization."
|
||||||
NVENC.AQ.Strength="Spatial AQ Strength"
|
NVENC.AQ.Strength="Spatial AQ Strength"
|
||||||
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."
|
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."
|
||||||
NVENC.AQ.Temporal="Enable Temporal Adaptive Quantization"
|
NVENC.AQ.Temporal="Temporal Adaptive Quantization"
|
||||||
NVENC.AQ.Temporal.Description="Enable temporal adaptive quantization."
|
NVENC.AQ.Temporal.Description="Enable temporal adaptive quantization."
|
||||||
NVENC.Other="Other Options"
|
NVENC.Other="Other Options"
|
||||||
NVENC.Other.BFrames="Maximum B-Frames"
|
NVENC.Other.BFrames="Maximum B-Frames"
|
||||||
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."
|
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."
|
||||||
NVENC.Other.BFrameReferenceMode="B-Frame Reference Mode"
|
NVENC.Other.BFrameReferenceMode="B-Frame Reference Mode"
|
||||||
NVENC.Other.BFrameReferenceMode.Each="Each B-Frame will be used for references"
|
NVENC.Other.BFrameReferenceMode.Each="Each B-Frame will be used for references"
|
||||||
NVENC.Other.BFrameReferenceMode.Middle="Only (# of B-Frames)/2 will be used for references"
|
NVENC.Other.BFrameReferenceMode.Middle="Only (# of B-Frames)/2 will be used for references"
|
||||||
NVENC.Other.ZeroLatency="Enable Zero Latency"
|
NVENC.Other.ZeroLatency="Zero Latency"
|
||||||
NVENC.Other.ZeroLatency.Description="Enable zero latency operation, which ensures that there is no reordering delay."
|
NVENC.Other.ZeroLatency.Description="Enable zero latency operation, which ensures that there is no reordering delay."
|
||||||
NVENC.Other.WeightedPrediction="Enable Weighted Prediction"
|
NVENC.Other.WeightedPrediction="Weighted Prediction"
|
||||||
NVENC.Other.WeightedPrediction.Description="Enable weighted prediction for encoding.\nCan't be used with B-Frames."
|
NVENC.Other.WeightedPrediction.Description="Enable weighted prediction for encoding.\nCan't be used with B-Frames."
|
||||||
NVENC.Other.NonReferencePFrames="Enable non-reference P-Frames"
|
NVENC.Other.NonReferencePFrames="Non-reference P-Frames"
|
||||||
NVENC.Other.NonReferencePFrames.Description="Enable the automatic insertion of non-reference P-Frames."
|
NVENC.Other.NonReferencePFrames.Description="Enable the automatic insertion of non-reference P-Frames."
|
||||||
|
|||||||
@@ -197,8 +197,8 @@ void progress_parse(uint8_t*& ptr, uint8_t* end, size_t& sz)
|
|||||||
sz = get_nal_size(ptr, end);
|
sz = get_nal_size(ptr, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
void obsffmpeg::codecs::hevc::extract_header_sei(uint8_t* data, size_t sz_data,
|
void obsffmpeg::codecs::hevc::extract_header_sei(uint8_t* data, size_t sz_data, std::vector<uint8_t>& header,
|
||||||
std::vector<uint8_t>& header, std::vector<uint8_t>& sei)
|
std::vector<uint8_t>& sei)
|
||||||
{
|
{
|
||||||
uint8_t* ptr = data;
|
uint8_t* ptr = data;
|
||||||
uint8_t* end = data + sz_data;
|
uint8_t* end = data + sz_data;
|
||||||
|
|||||||
@@ -61,8 +61,8 @@ namespace obsffmpeg {
|
|||||||
UNKNOWN = -1,
|
UNKNOWN = -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
void extract_header_sei(uint8_t* data, size_t sz_data,
|
void extract_header_sei(uint8_t* data, size_t sz_data, std::vector<uint8_t>& header,
|
||||||
std::vector<uint8_t>& header, std::vector<uint8_t>& sei);
|
std::vector<uint8_t>& sei);
|
||||||
|
|
||||||
} // namespace hevc
|
} // namespace hevc
|
||||||
} // namespace codecs
|
} // namespace codecs
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// FFMPEG Video Encoder Integration for OBS Studio
|
||||||
|
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#include "prores.hpp"
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
// FFMPEG Video Encoder Integration for OBS Studio
|
||||||
|
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Codec: ProRes
|
||||||
|
#define P_PRORES "Codec.ProRes"
|
||||||
|
#define P_PRORES_PROFILE "Codec.ProRes.Profile"
|
||||||
|
#define P_PRORES_PROFILE_APCS "Codec.ProRes.Profile.APCS"
|
||||||
|
#define P_PRORES_PROFILE_APCO "Codec.ProRes.Profile.APCO"
|
||||||
|
#define P_PRORES_PROFILE_APCN "Codec.ProRes.Profile.APCN"
|
||||||
|
#define P_PRORES_PROFILE_APCH "Codec.ProRes.Profile.APCH"
|
||||||
|
#define P_PRORES_PROFILE_AP4H "Codec.ProRes.Profile.AP4H"
|
||||||
|
#define P_PRORES_PROFILE_AP4X "Codec.ProRes.Profile.AP4X"
|
||||||
+975
-616
File diff suppressed because it is too large
Load Diff
+60
-14
@@ -23,9 +23,14 @@
|
|||||||
|
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
|
#include <stack>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
#include "ffmpeg/avframe-queue.hpp"
|
#include "ffmpeg/avframe-queue.hpp"
|
||||||
#include "ffmpeg/swscale.hpp"
|
#include "ffmpeg/swscale.hpp"
|
||||||
|
#include "hwapi/base.hpp"
|
||||||
|
#include "ui/handler.hpp"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <obs-properties.h>
|
#include <obs-properties.h>
|
||||||
@@ -38,28 +43,40 @@ extern "C" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace obsffmpeg {
|
namespace obsffmpeg {
|
||||||
class encoder_factory {
|
class unsupported_gpu_exception : public std::runtime_error {
|
||||||
struct info {
|
public:
|
||||||
|
unsupported_gpu_exception(const std::string& reason) : runtime_error(reason) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct encoder_info {
|
||||||
std::string uid;
|
std::string uid;
|
||||||
std::string codec;
|
std::string codec;
|
||||||
std::string readable_name;
|
std::string readable_name;
|
||||||
obs_encoder_info oei;
|
obs_encoder_info oei = {0};
|
||||||
} info;
|
};
|
||||||
|
|
||||||
|
class encoder_factory {
|
||||||
|
encoder_info info;
|
||||||
|
encoder_info info_fallback;
|
||||||
const AVCodec* avcodec_ptr;
|
const AVCodec* avcodec_ptr;
|
||||||
|
|
||||||
|
std::shared_ptr<obsffmpeg::ui::handler> _handler;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
encoder_factory(const AVCodec* codec);
|
encoder_factory(const AVCodec* codec);
|
||||||
virtual ~encoder_factory();
|
virtual ~encoder_factory();
|
||||||
|
|
||||||
void register_encoder();
|
void register_encoder();
|
||||||
|
|
||||||
const char* get_name();
|
void get_defaults(obs_data_t* settings, bool hw_encoder = false);
|
||||||
|
|
||||||
void get_defaults(obs_data_t* settings);
|
void get_properties(obs_properties_t* props, bool hw_encoder = false);
|
||||||
|
|
||||||
void get_properties(obs_properties_t* props);
|
|
||||||
|
|
||||||
const AVCodec* get_avcodec();
|
const AVCodec* get_avcodec();
|
||||||
|
|
||||||
|
const encoder_info& get_info();
|
||||||
|
|
||||||
|
const encoder_info& get_fallback();
|
||||||
};
|
};
|
||||||
|
|
||||||
class encoder {
|
class encoder {
|
||||||
@@ -69,26 +86,43 @@ namespace obsffmpeg {
|
|||||||
const AVCodec* _codec;
|
const AVCodec* _codec;
|
||||||
AVCodecContext* _context;
|
AVCodecContext* _context;
|
||||||
|
|
||||||
ffmpeg::avframe_queue _frame_queue;
|
std::shared_ptr<obsffmpeg::ui::handler> _handler;
|
||||||
ffmpeg::avframe_queue _frame_queue_used;
|
|
||||||
|
std::shared_ptr<obsffmpeg::hwapi::base> _hwapi;
|
||||||
|
std::shared_ptr<obsffmpeg::hwapi::instance> _hwinst;
|
||||||
|
|
||||||
ffmpeg::swscale _swscale;
|
ffmpeg::swscale _swscale;
|
||||||
AVPacket _current_packet;
|
AVPacket _current_packet;
|
||||||
|
|
||||||
int64_t _lag_in_frames;
|
size_t _lag_in_frames;
|
||||||
int64_t _count_send_frames;
|
size_t _count_send_frames;
|
||||||
|
|
||||||
// Extra Data
|
// Extra Data
|
||||||
bool _have_first_frame;
|
bool _have_first_frame;
|
||||||
std::vector<uint8_t> _extra_data;
|
std::vector<uint8_t> _extra_data;
|
||||||
std::vector<uint8_t> _sei_data;
|
std::vector<uint8_t> _sei_data;
|
||||||
|
|
||||||
|
// Frame Stack and Queue
|
||||||
|
std::stack<std::shared_ptr<AVFrame>> _free_frames;
|
||||||
|
std::queue<std::shared_ptr<AVFrame>> _used_frames;
|
||||||
|
std::chrono::high_resolution_clock::time_point _free_frames_last_used;
|
||||||
|
|
||||||
|
void initialize_sw(obs_data_t* settings);
|
||||||
|
void initialize_hw(obs_data_t* settings);
|
||||||
|
|
||||||
|
void push_free_frame(std::shared_ptr<AVFrame> frame);
|
||||||
|
std::shared_ptr<AVFrame> pop_free_frame();
|
||||||
|
|
||||||
|
void push_used_frame(std::shared_ptr<AVFrame> frame);
|
||||||
|
std::shared_ptr<AVFrame> pop_used_frame();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
encoder(obs_data_t* settings, obs_encoder_t* encoder);
|
encoder(obs_data_t* settings, obs_encoder_t* encoder, bool is_texture_encode = false);
|
||||||
virtual ~encoder();
|
virtual ~encoder();
|
||||||
|
|
||||||
public: // OBS API
|
public: // OBS API
|
||||||
// Shared
|
// Shared
|
||||||
void get_properties(obs_properties_t* props);
|
void get_properties(obs_properties_t* props, bool hw_encode = false);
|
||||||
|
|
||||||
bool update(obs_data_t* settings);
|
bool update(obs_data_t* settings);
|
||||||
|
|
||||||
@@ -114,5 +148,17 @@ namespace obsffmpeg {
|
|||||||
int receive_packet(bool* received_packet, struct encoder_packet* packet);
|
int receive_packet(bool* received_packet, struct encoder_packet* packet);
|
||||||
|
|
||||||
int send_frame(std::shared_ptr<AVFrame> frame);
|
int send_frame(std::shared_ptr<AVFrame> frame);
|
||||||
|
|
||||||
|
bool encode_avframe(std::shared_ptr<AVFrame> frame, struct encoder_packet* packet,
|
||||||
|
bool* received_packet);
|
||||||
|
|
||||||
|
public: // Handler API
|
||||||
|
bool is_hardware_encode();
|
||||||
|
|
||||||
|
const AVCodec* get_avcodec();
|
||||||
|
|
||||||
|
const AVCodecContext* get_avcodeccontext();
|
||||||
|
|
||||||
|
void parse_ffmpeg_commandline(std::string text);
|
||||||
};
|
};
|
||||||
} // namespace obsffmpeg
|
} // namespace obsffmpeg
|
||||||
|
|||||||
+205
-89
@@ -20,14 +20,19 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
|
|
||||||
#include "tools.hpp"
|
#include "tools.hpp"
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include "plugin.hpp"
|
||||||
|
#include "utility.hpp"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
#pragma warning(disable : 4244)
|
#pragma warning(disable : 4244)
|
||||||
#include <libavcodec/avcodec.h>
|
#include <libavcodec/avcodec.h>
|
||||||
#include <libavutil/error.h>
|
#include <libavutil/error.h>
|
||||||
|
#include <libavutil/opt.h>
|
||||||
#include <libavutil/pixdesc.h>
|
#include <libavutil/pixdesc.h>
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
}
|
}
|
||||||
@@ -111,107 +116,56 @@ const char* ffmpeg::tools::get_color_space_name(AVColorSpace v)
|
|||||||
|
|
||||||
const char* ffmpeg::tools::get_error_description(int error)
|
const char* ffmpeg::tools::get_error_description(int error)
|
||||||
{
|
{
|
||||||
switch (error) {
|
thread_local char error_buf[AV_ERROR_MAX_STRING_SIZE + 1];
|
||||||
case AVERROR(EPERM):
|
if (av_strerror(error, error_buf, AV_ERROR_MAX_STRING_SIZE) < 0) {
|
||||||
return "Permission Denied";
|
snprintf(error_buf, AV_ERROR_MAX_STRING_SIZE, "Unknown Error (%i)", error);
|
||||||
// case AVERROR(ENOENT):
|
|
||||||
// case AVERROR(ESRCH):
|
|
||||||
// case AVERROR(EINTR):
|
|
||||||
// case AVERROR(EIO):
|
|
||||||
// case AVERROR(ENXIO):
|
|
||||||
// case AVERROR(E2BIG):
|
|
||||||
// case AVERROR(ENOEXEC):
|
|
||||||
// case AVERROR(EBADF):
|
|
||||||
// case AVERROR(ECHILD):
|
|
||||||
// case AVERROR(EAGAIN):
|
|
||||||
case AVERROR(ENOMEM):
|
|
||||||
return "Out Of Memory";
|
|
||||||
// case AVERROR(EACCES):
|
|
||||||
// case AVERROR(EFAULT):
|
|
||||||
// case AVERROR(EBUSY):
|
|
||||||
// case AVERROR(EEXIST):
|
|
||||||
// case AVERROR(EXDEV):
|
|
||||||
// case AVERROR(ENODEV):
|
|
||||||
// case AVERROR(ENOTDIR):
|
|
||||||
// case AVERROR(EISDIR):
|
|
||||||
// case AVERROR(ENFILE):
|
|
||||||
// case AVERROR(EMFILE):
|
|
||||||
// case AVERROR(ENOTTY):
|
|
||||||
// case AVERROR(EFBIG):
|
|
||||||
// case AVERROR(ENOSPC):
|
|
||||||
// case AVERROR(ESPIPE):
|
|
||||||
// case AVERROR(EROFS):
|
|
||||||
// case AVERROR(EMLINK):
|
|
||||||
// case AVERROR(EPIPE):
|
|
||||||
// case AVERROR(EDOM):
|
|
||||||
// case AVERROR(EDEADLK):
|
|
||||||
// case AVERROR(ENAMETOOLONG):
|
|
||||||
// case AVERROR(ENOLCK):
|
|
||||||
// case AVERROR(ENOSYS):
|
|
||||||
// case AVERROR(ENOTEMPTY):
|
|
||||||
case AVERROR(EINVAL):
|
|
||||||
return "Invalid Value(s)";
|
|
||||||
case AVERROR(ERANGE):
|
|
||||||
return "Out of Range";
|
|
||||||
// case AVERROR(EILSEQ):
|
|
||||||
// case AVERROR(STRUNCATE):
|
|
||||||
}
|
}
|
||||||
return "Not Translated Yet";
|
return error_buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::map<video_format, AVPixelFormat> obs_to_av_format_map = {
|
||||||
|
{VIDEO_FORMAT_I420, AV_PIX_FMT_YUV420P}, // YUV 4:2:0
|
||||||
|
{VIDEO_FORMAT_NV12, AV_PIX_FMT_NV12}, // NV12 Packed YUV
|
||||||
|
{VIDEO_FORMAT_YVYU, AV_PIX_FMT_YVYU422}, // YVYU Packed YUV
|
||||||
|
{VIDEO_FORMAT_YUY2, AV_PIX_FMT_YUYV422}, // YUYV Packed YUV
|
||||||
|
{VIDEO_FORMAT_UYVY, AV_PIX_FMT_UYVY422}, // UYVY Packed YUV
|
||||||
|
{VIDEO_FORMAT_RGBA, AV_PIX_FMT_RGBA}, //
|
||||||
|
{VIDEO_FORMAT_BGRA, AV_PIX_FMT_BGRA}, //
|
||||||
|
{VIDEO_FORMAT_BGRX, AV_PIX_FMT_BGR0}, //
|
||||||
|
{VIDEO_FORMAT_Y800, AV_PIX_FMT_GRAY8}, //
|
||||||
|
{VIDEO_FORMAT_I444, AV_PIX_FMT_YUV444P}, //
|
||||||
|
{VIDEO_FORMAT_BGR3, AV_PIX_FMT_BGR24}, //
|
||||||
|
{VIDEO_FORMAT_I422, AV_PIX_FMT_YUV422P}, //
|
||||||
|
{VIDEO_FORMAT_I40A, AV_PIX_FMT_YUVA420P}, //
|
||||||
|
{VIDEO_FORMAT_I42A, AV_PIX_FMT_YUVA422P}, //
|
||||||
|
{VIDEO_FORMAT_YUVA, AV_PIX_FMT_YUVA444P}, //
|
||||||
|
//{VIDEO_FORMAT_AYUV, AV_PIX_FMT_AYUV444P}, //
|
||||||
|
};
|
||||||
|
|
||||||
AVPixelFormat ffmpeg::tools::obs_videoformat_to_avpixelformat(video_format v)
|
AVPixelFormat ffmpeg::tools::obs_videoformat_to_avpixelformat(video_format v)
|
||||||
{
|
{
|
||||||
switch (v) {
|
auto found = obs_to_av_format_map.find(v);
|
||||||
// 32-Bits
|
if (found != obs_to_av_format_map.end()) {
|
||||||
case VIDEO_FORMAT_BGRX:
|
return found->second;
|
||||||
return AV_PIX_FMT_BGR0;
|
|
||||||
case VIDEO_FORMAT_BGRA:
|
|
||||||
return AV_PIX_FMT_BGRA;
|
|
||||||
case VIDEO_FORMAT_RGBA:
|
|
||||||
return AV_PIX_FMT_RGBA;
|
|
||||||
case VIDEO_FORMAT_I444:
|
|
||||||
return AV_PIX_FMT_YUV444P;
|
|
||||||
case VIDEO_FORMAT_YUY2:
|
|
||||||
return AV_PIX_FMT_YUYV422;
|
|
||||||
case VIDEO_FORMAT_YVYU:
|
|
||||||
return AV_PIX_FMT_YVYU422;
|
|
||||||
case VIDEO_FORMAT_UYVY:
|
|
||||||
return AV_PIX_FMT_UYVY422;
|
|
||||||
case VIDEO_FORMAT_I420:
|
|
||||||
return AV_PIX_FMT_YUV420P;
|
|
||||||
case VIDEO_FORMAT_NV12:
|
|
||||||
return AV_PIX_FMT_NV12;
|
|
||||||
}
|
}
|
||||||
throw std::invalid_argument("unknown format");
|
return AV_PIX_FMT_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
video_format ffmpeg::tools::avpixelformat_to_obs_videoformat(AVPixelFormat v)
|
video_format ffmpeg::tools::avpixelformat_to_obs_videoformat(AVPixelFormat v)
|
||||||
{
|
{
|
||||||
switch (v) {
|
for (const auto& kv : obs_to_av_format_map) {
|
||||||
case AV_PIX_FMT_YUV420P:
|
if (kv.second == v)
|
||||||
return VIDEO_FORMAT_I420;
|
return kv.first;
|
||||||
case AV_PIX_FMT_NV12:
|
|
||||||
return VIDEO_FORMAT_NV12;
|
|
||||||
case AV_PIX_FMT_YVYU422:
|
|
||||||
return VIDEO_FORMAT_YVYU;
|
|
||||||
case AV_PIX_FMT_YUYV422:
|
|
||||||
return VIDEO_FORMAT_YUY2;
|
|
||||||
case AV_PIX_FMT_UYVY422:
|
|
||||||
return VIDEO_FORMAT_UYVY;
|
|
||||||
case AV_PIX_FMT_RGBA:
|
|
||||||
return VIDEO_FORMAT_RGBA;
|
|
||||||
case AV_PIX_FMT_BGRA:
|
|
||||||
return VIDEO_FORMAT_BGRA;
|
|
||||||
case AV_PIX_FMT_BGR0:
|
|
||||||
return VIDEO_FORMAT_BGRX;
|
|
||||||
case AV_PIX_FMT_GRAY8:
|
|
||||||
return VIDEO_FORMAT_Y800;
|
|
||||||
case AV_PIX_FMT_YUV444P:
|
|
||||||
return VIDEO_FORMAT_I444;
|
|
||||||
}
|
}
|
||||||
return VIDEO_FORMAT_NONE;
|
return VIDEO_FORMAT_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AVPixelFormat ffmpeg::tools::get_least_lossy_format(const AVPixelFormat* haystack, AVPixelFormat needle)
|
||||||
|
{
|
||||||
|
int data_loss = 0;
|
||||||
|
return avcodec_find_best_pix_fmt_of_list(haystack, needle, 0, &data_loss);
|
||||||
|
}
|
||||||
|
|
||||||
AVColorSpace ffmpeg::tools::obs_videocolorspace_to_avcolorspace(video_colorspace v)
|
AVColorSpace ffmpeg::tools::obs_videocolorspace_to_avcolorspace(video_colorspace v)
|
||||||
{
|
{
|
||||||
switch (v) {
|
switch (v) {
|
||||||
@@ -228,10 +182,172 @@ AVColorRange ffmpeg::tools::obs_videorangetype_to_avcolorrange(video_range_type
|
|||||||
{
|
{
|
||||||
switch (v) {
|
switch (v) {
|
||||||
case VIDEO_RANGE_DEFAULT:
|
case VIDEO_RANGE_DEFAULT:
|
||||||
case VIDEO_RANGE_FULL:
|
|
||||||
return AVCOL_RANGE_JPEG;
|
|
||||||
case VIDEO_RANGE_PARTIAL:
|
case VIDEO_RANGE_PARTIAL:
|
||||||
return AVCOL_RANGE_MPEG;
|
return AVCOL_RANGE_MPEG;
|
||||||
|
case VIDEO_RANGE_FULL:
|
||||||
|
return AVCOL_RANGE_JPEG;
|
||||||
}
|
}
|
||||||
throw std::invalid_argument("unknown range");
|
throw std::invalid_argument("unknown range");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ffmpeg::tools::can_hardware_encode(const AVCodec* codec)
|
||||||
|
{
|
||||||
|
AVPixelFormat hardware_formats[] = {AV_PIX_FMT_D3D11};
|
||||||
|
|
||||||
|
for (const AVPixelFormat* fmt = codec->pix_fmts; (fmt != nullptr) && (*fmt != AV_PIX_FMT_NONE); fmt++) {
|
||||||
|
for (auto cmp : hardware_formats) {
|
||||||
|
if (*fmt == cmp) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<AVPixelFormat> ffmpeg::tools::get_software_formats(const AVPixelFormat* list)
|
||||||
|
{
|
||||||
|
AVPixelFormat hardware_formats[] = {
|
||||||
|
#if FF_API_VAAPI
|
||||||
|
AV_PIX_FMT_VAAPI_MOCO,
|
||||||
|
AV_PIX_FMT_VAAPI_IDCT,
|
||||||
|
#endif
|
||||||
|
AV_PIX_FMT_VAAPI,
|
||||||
|
AV_PIX_FMT_DXVA2_VLD,
|
||||||
|
AV_PIX_FMT_VDPAU,
|
||||||
|
AV_PIX_FMT_QSV,
|
||||||
|
AV_PIX_FMT_MMAL,
|
||||||
|
AV_PIX_FMT_D3D11VA_VLD,
|
||||||
|
AV_PIX_FMT_CUDA,
|
||||||
|
AV_PIX_FMT_XVMC,
|
||||||
|
AV_PIX_FMT_VIDEOTOOLBOX,
|
||||||
|
AV_PIX_FMT_MEDIACODEC,
|
||||||
|
AV_PIX_FMT_D3D11,
|
||||||
|
AV_PIX_FMT_OPENCL,
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<AVPixelFormat> fmts;
|
||||||
|
for (auto fmt = list; fmt && (*fmt != AV_PIX_FMT_NONE); fmt++) {
|
||||||
|
bool is_blacklisted = false;
|
||||||
|
for (auto blacklisted : hardware_formats) {
|
||||||
|
if (*fmt == blacklisted)
|
||||||
|
is_blacklisted = true;
|
||||||
|
}
|
||||||
|
if (!is_blacklisted)
|
||||||
|
fmts.push_back(*fmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
fmts.push_back(AV_PIX_FMT_NONE);
|
||||||
|
|
||||||
|
return std::move(fmts);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ffmpeg::tools::setup_obs_color(video_colorspace colorspace, video_range_type range, AVCodecContext* context)
|
||||||
|
{
|
||||||
|
std::map<video_colorspace, std::tuple<AVColorSpace, AVColorPrimaries, AVColorTransferCharacteristic>>
|
||||||
|
colorspaces = {
|
||||||
|
{VIDEO_CS_DEFAULT, {AVCOL_SPC_BT470BG, AVCOL_PRI_BT470BG, AVCOL_TRC_SMPTE170M}},
|
||||||
|
{VIDEO_CS_601, {AVCOL_SPC_BT470BG, AVCOL_PRI_BT470BG, AVCOL_TRC_SMPTE170M}},
|
||||||
|
{VIDEO_CS_709, {AVCOL_SPC_BT709, AVCOL_PRI_BT709, AVCOL_TRC_BT709}},
|
||||||
|
};
|
||||||
|
std::map<video_range_type, AVColorRange> colorranges = {
|
||||||
|
{VIDEO_RANGE_DEFAULT, AVCOL_RANGE_MPEG},
|
||||||
|
{VIDEO_RANGE_PARTIAL, AVCOL_RANGE_MPEG},
|
||||||
|
{VIDEO_RANGE_FULL, AVCOL_RANGE_JPEG},
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
auto found = colorspaces.find(colorspace);
|
||||||
|
if (found != colorspaces.end()) {
|
||||||
|
context->colorspace = std::get<AVColorSpace>(found->second);
|
||||||
|
context->color_primaries = std::get<AVColorPrimaries>(found->second);
|
||||||
|
context->color_trc = std::get<AVColorTransferCharacteristic>(found->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto found = colorranges.find(range);
|
||||||
|
if (found != colorranges.end()) {
|
||||||
|
context->color_range = found->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Downscaling should result in downscaling, not pixelation
|
||||||
|
context->chroma_sample_location = AVCHROMA_LOC_CENTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* ffmpeg::tools::get_std_compliance_name(int compliance)
|
||||||
|
{
|
||||||
|
switch (compliance) {
|
||||||
|
case FF_COMPLIANCE_VERY_STRICT:
|
||||||
|
return "Very Strict";
|
||||||
|
case FF_COMPLIANCE_STRICT:
|
||||||
|
return "Strict";
|
||||||
|
case FF_COMPLIANCE_NORMAL:
|
||||||
|
return "Normal";
|
||||||
|
case FF_COMPLIANCE_UNOFFICIAL:
|
||||||
|
return "Unofficial";
|
||||||
|
case FF_COMPLIANCE_EXPERIMENTAL:
|
||||||
|
return "Experimental";
|
||||||
|
}
|
||||||
|
return "Invalid";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* ffmpeg::tools::get_thread_type_name(int thread_type)
|
||||||
|
{
|
||||||
|
switch (thread_type) {
|
||||||
|
case FF_THREAD_FRAME | FF_THREAD_SLICE:
|
||||||
|
return "Slice & Frame";
|
||||||
|
case FF_THREAD_FRAME:
|
||||||
|
return "Frame";
|
||||||
|
case FF_THREAD_SLICE:
|
||||||
|
return "Slice";
|
||||||
|
default:
|
||||||
|
return "None";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ffmpeg::tools::print_av_option_bool(AVCodecContext* context, const char* option, std::string text)
|
||||||
|
{
|
||||||
|
if (av_opt_is_set_to_default_by_name(context, option, AV_OPT_SEARCH_CHILDREN) == 0) {
|
||||||
|
int64_t v = 0;
|
||||||
|
if (av_opt_get_int(context, option, AV_OPT_SEARCH_CHILDREN, &v) == 0) {
|
||||||
|
PLOG_INFO("[%s] %s: %s", context->codec->name, text.c_str(), v == 0 ? "Disabled" : "Enabled");
|
||||||
|
} else {
|
||||||
|
PLOG_INFO("[%s] %s: <Error>", context->codec->name, text.c_str());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PLOG_INFO("[%s] %s: <Default>", context->codec->name, text.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ffmpeg::tools::print_av_option_int(AVCodecContext* context, const char* option, std::string text,
|
||||||
|
std::string suffix)
|
||||||
|
{
|
||||||
|
if (av_opt_is_set_to_default_by_name(context, option, AV_OPT_SEARCH_CHILDREN) == 0) {
|
||||||
|
int64_t v = 0;
|
||||||
|
if (av_opt_get_int(context, option, AV_OPT_SEARCH_CHILDREN, &v) == 0) {
|
||||||
|
PLOG_INFO("[%s] %s: %lld %s", context->codec->name, text.c_str(), v, suffix.c_str());
|
||||||
|
} else {
|
||||||
|
PLOG_INFO("[%s] %s: <Error>", context->codec->name, text.c_str());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PLOG_INFO("[%s] %s: <Default>", context->codec->name, text.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ffmpeg::tools::print_av_option_string(AVCodecContext* context, const char* option, std::string text,
|
||||||
|
std::function<std::string(int64_t)> decoder)
|
||||||
|
{
|
||||||
|
if (av_opt_is_set_to_default_by_name(context, option, AV_OPT_SEARCH_CHILDREN) == 0) {
|
||||||
|
int64_t v = 0;
|
||||||
|
if (av_opt_get_int(context, option, AV_OPT_SEARCH_CHILDREN, &v) == 0) {
|
||||||
|
std::string name = "<Unknown>";
|
||||||
|
if (decoder)
|
||||||
|
name = decoder(v);
|
||||||
|
PLOG_INFO("[%s] %s: %s", context->codec->name, text.c_str(), name.c_str());
|
||||||
|
} else {
|
||||||
|
PLOG_INFO("[%s] %s: <Error>", context->codec->name, text.c_str());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PLOG_INFO("[%s] %s: <Default>", context->codec->name, text.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,10 +23,13 @@
|
|||||||
#define OBS_FFMPEG_FFMPEG_UTILITY
|
#define OBS_FFMPEG_FFMPEG_UTILITY
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <obs.h>
|
#include <obs.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
#include <libavcodec/avcodec.h>
|
||||||
#include <libavutil/pixfmt.h>
|
#include <libavutil/pixfmt.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,9 +47,30 @@ namespace ffmpeg {
|
|||||||
|
|
||||||
video_format avpixelformat_to_obs_videoformat(AVPixelFormat v);
|
video_format avpixelformat_to_obs_videoformat(AVPixelFormat v);
|
||||||
|
|
||||||
|
AVPixelFormat get_least_lossy_format(const AVPixelFormat* haystack, AVPixelFormat needle);
|
||||||
|
|
||||||
AVColorSpace obs_videocolorspace_to_avcolorspace(video_colorspace v);
|
AVColorSpace obs_videocolorspace_to_avcolorspace(video_colorspace v);
|
||||||
|
|
||||||
AVColorRange obs_videorangetype_to_avcolorrange(video_range_type v);
|
AVColorRange obs_videorangetype_to_avcolorrange(video_range_type v);
|
||||||
|
|
||||||
|
bool can_hardware_encode(const AVCodec* codec);
|
||||||
|
|
||||||
|
std::vector<AVPixelFormat> get_software_formats(const AVPixelFormat* list);
|
||||||
|
|
||||||
|
void setup_obs_color(video_colorspace colorspace, video_range_type range, AVCodecContext* context);
|
||||||
|
|
||||||
|
const char* get_std_compliance_name(int compliance);
|
||||||
|
|
||||||
|
const char* get_thread_type_name(int thread_type);
|
||||||
|
|
||||||
|
void print_av_option_bool(AVCodecContext* context, const char* option, std::string text);
|
||||||
|
|
||||||
|
void print_av_option_int(AVCodecContext* context, const char* option, std::string text,
|
||||||
|
std::string suffix);
|
||||||
|
|
||||||
|
void print_av_option_string(AVCodecContext* context, const char* option, std::string text,
|
||||||
|
std::function<std::string(int64_t)> decoder);
|
||||||
|
|
||||||
} // namespace tools
|
} // namespace tools
|
||||||
} // namespace ffmpeg
|
} // namespace ffmpeg
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// FFMPEG Video Encoder Integration for OBS Studio
|
||||||
|
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#include "base.hpp"
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
// FFMPEG Video Encoder Integration for OBS Studio
|
||||||
|
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
#include <list>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable : 4244)
|
||||||
|
#include <libavutil/frame.h>
|
||||||
|
#include <libavutil/hwcontext.h>
|
||||||
|
#pragma warning(pop)
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace obsffmpeg {
|
||||||
|
namespace hwapi {
|
||||||
|
struct device {
|
||||||
|
std::pair<int64_t, int64_t> id;
|
||||||
|
std::string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
class instance;
|
||||||
|
|
||||||
|
class base {
|
||||||
|
public:
|
||||||
|
virtual std::list<obsffmpeg::hwapi::device> enumerate_adapters() = 0;
|
||||||
|
|
||||||
|
virtual std::shared_ptr<obsffmpeg::hwapi::instance> create(obsffmpeg::hwapi::device target) = 0;
|
||||||
|
|
||||||
|
virtual std::shared_ptr<obsffmpeg::hwapi::instance> create_from_obs() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class instance {
|
||||||
|
public:
|
||||||
|
virtual AVBufferRef* create_device_context() = 0;
|
||||||
|
|
||||||
|
virtual std::shared_ptr<AVFrame> allocate_frame(AVBufferRef* frames) = 0;
|
||||||
|
|
||||||
|
virtual void copy_from_obs(AVBufferRef* frames, uint32_t handle, uint64_t lock_key,
|
||||||
|
uint64_t* next_lock_key, std::shared_ptr<AVFrame> frame) = 0;
|
||||||
|
|
||||||
|
virtual std::shared_ptr<AVFrame> avframe_from_obs(AVBufferRef* frames, uint32_t handle,
|
||||||
|
uint64_t lock_key,
|
||||||
|
uint64_t* next_lock_key) = 0;
|
||||||
|
};
|
||||||
|
} // namespace hwapi
|
||||||
|
} // namespace obsffmpeg
|
||||||
@@ -0,0 +1,245 @@
|
|||||||
|
// FFMPEG Video Encoder Integration for OBS Studio
|
||||||
|
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#include "d3d11.hpp"
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
#include "utility.hpp"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable : 4244)
|
||||||
|
#include <graphics/graphics.h>
|
||||||
|
#include <libavutil/hwcontext_d3d11va.h>
|
||||||
|
#include <obs.h>
|
||||||
|
#pragma warning(pop)
|
||||||
|
}
|
||||||
|
|
||||||
|
obsffmpeg::hwapi::d3d11::d3d11() : _dxgi_module(0), _d3d11_module(0)
|
||||||
|
{
|
||||||
|
_dxgi_module = LoadLibraryW(L"dxgi.dll");
|
||||||
|
if (!_dxgi_module)
|
||||||
|
throw std::runtime_error("Unable to load DXGI");
|
||||||
|
|
||||||
|
_d3d11_module = LoadLibraryW(L"d3d11.dll");
|
||||||
|
if (!_d3d11_module)
|
||||||
|
throw std::runtime_error("Unable to load D3D11");
|
||||||
|
|
||||||
|
_CreateDXGIFactory = reinterpret_cast<CreateDXGIFactory_t>(GetProcAddress(_dxgi_module, "CreateDXGIFactory"));
|
||||||
|
_CreateDXGIFactory1 =
|
||||||
|
reinterpret_cast<CreateDXGIFactory1_t>(GetProcAddress(_dxgi_module, "CreateDXGIFactory1"));
|
||||||
|
_D3D11CreateDevice = reinterpret_cast<D3D11CreateDevice_t>(GetProcAddress(_d3d11_module, "D3D11CreateDevice"));
|
||||||
|
|
||||||
|
if (!_CreateDXGIFactory && !_CreateDXGIFactory1)
|
||||||
|
throw std::runtime_error("DXGI not supported");
|
||||||
|
|
||||||
|
if (!_D3D11CreateDevice)
|
||||||
|
throw std::runtime_error("D3D11 not supported");
|
||||||
|
|
||||||
|
HRESULT hr = _CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&_dxgifactory);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
std::stringstream sstr;
|
||||||
|
sstr << "Failed to create DXGI Factory (" << hr << ")";
|
||||||
|
throw std::runtime_error(sstr.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
obsffmpeg::hwapi::d3d11::~d3d11()
|
||||||
|
{
|
||||||
|
FreeLibrary(_dxgi_module);
|
||||||
|
FreeLibrary(_d3d11_module);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<obsffmpeg::hwapi::device> obsffmpeg::hwapi::d3d11::enumerate_adapters()
|
||||||
|
{
|
||||||
|
std::list<device> adapters;
|
||||||
|
|
||||||
|
// Enumerate Adapters
|
||||||
|
IDXGIAdapter1* dxgi_adapter = nullptr;
|
||||||
|
for (UINT idx = 0; !FAILED(_dxgifactory->EnumAdapters1(idx, &dxgi_adapter)); idx++) {
|
||||||
|
DXGI_ADAPTER_DESC1 desc = DXGI_ADAPTER_DESC1();
|
||||||
|
dxgi_adapter->GetDesc1(&desc);
|
||||||
|
|
||||||
|
std::vector<char> buf(1024);
|
||||||
|
size_t len = snprintf(buf.data(), buf.size(), "%ls (VEN_%04x/DEV_%04x/SUB_%04x/REV_%04x)",
|
||||||
|
desc.Description, desc.VendorId, desc.DeviceId, desc.SubSysId, desc.Revision);
|
||||||
|
|
||||||
|
device dev;
|
||||||
|
dev.name = std::string(buf.data(), buf.data() + len);
|
||||||
|
dev.id.first = desc.AdapterLuid.HighPart;
|
||||||
|
dev.id.second = desc.AdapterLuid.LowPart;
|
||||||
|
|
||||||
|
adapters.push_back(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::move(adapters);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<obsffmpeg::hwapi::instance> obsffmpeg::hwapi::d3d11::create(obsffmpeg::hwapi::device target)
|
||||||
|
{
|
||||||
|
std::shared_ptr<d3d11_instance> inst;
|
||||||
|
ATL::CComPtr<ID3D11Device> device;
|
||||||
|
ATL::CComPtr<ID3D11DeviceContext> context;
|
||||||
|
IDXGIAdapter1* adapter = nullptr;
|
||||||
|
|
||||||
|
// Find the correct "Adapter" (device).
|
||||||
|
IDXGIAdapter1* dxgi_adapter = nullptr;
|
||||||
|
for (UINT idx = 0; !FAILED(_dxgifactory->EnumAdapters1(idx, &dxgi_adapter)); idx++) {
|
||||||
|
DXGI_ADAPTER_DESC1 desc = DXGI_ADAPTER_DESC1();
|
||||||
|
dxgi_adapter->GetDesc1(&desc);
|
||||||
|
|
||||||
|
if ((desc.AdapterLuid.LowPart == target.id.second) && (desc.AdapterLuid.HighPart == target.id.first)) {
|
||||||
|
adapter = dxgi_adapter;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a D3D11 Device
|
||||||
|
UINT device_flags = D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
|
||||||
|
std::vector<D3D_FEATURE_LEVEL> feature_levels = {D3D_FEATURE_LEVEL_12_1, D3D_FEATURE_LEVEL_12_0,
|
||||||
|
D3D_FEATURE_LEVEL_11_1};
|
||||||
|
|
||||||
|
if (FAILED(_D3D11CreateDevice(adapter, D3D_DRIVER_TYPE_HARDWARE, NULL, device_flags, feature_levels.data(),
|
||||||
|
static_cast<UINT>(feature_levels.size()), D3D11_SDK_VERSION, &device, NULL,
|
||||||
|
&context))) {
|
||||||
|
throw std::runtime_error("Failed to create D3D11 device for target.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_shared<d3d11_instance>(device, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<obsffmpeg::hwapi::instance> obsffmpeg::hwapi::d3d11::create_from_obs()
|
||||||
|
{
|
||||||
|
auto gctx = obsffmpeg::obs_graphics();
|
||||||
|
|
||||||
|
if (GS_DEVICE_DIRECT3D_11 != gs_get_device_type()) {
|
||||||
|
throw std::runtime_error("OBS Device is not a D3D11 Device.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ATL::CComPtr<ID3D11Device> device =
|
||||||
|
ATL::CComPtr<ID3D11Device>(reinterpret_cast<ID3D11Device*>(gs_get_device_obj()));
|
||||||
|
ATL::CComPtr<ID3D11DeviceContext> context;
|
||||||
|
device->GetImmediateContext(&context);
|
||||||
|
|
||||||
|
return std::make_shared<d3d11_instance>(device, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct D3D11AVFrame {
|
||||||
|
ATL::CComPtr<ID3D11Texture2D> handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
obsffmpeg::hwapi::d3d11_instance::d3d11_instance(ATL::CComPtr<ID3D11Device> device,
|
||||||
|
ATL::CComPtr<ID3D11DeviceContext> context)
|
||||||
|
{
|
||||||
|
_device = device;
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
obsffmpeg::hwapi::d3d11_instance::~d3d11_instance() {}
|
||||||
|
|
||||||
|
AVBufferRef* obsffmpeg::hwapi::d3d11_instance::create_device_context()
|
||||||
|
{
|
||||||
|
AVBufferRef* dctx_ref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_D3D11VA);
|
||||||
|
if (!dctx_ref)
|
||||||
|
throw std::runtime_error("Failed to allocate AVHWDeviceContext.");
|
||||||
|
|
||||||
|
AVHWDeviceContext* dctx = reinterpret_cast<AVHWDeviceContext*>(dctx_ref->data);
|
||||||
|
AVD3D11VADeviceContext* d3d11va = reinterpret_cast<AVD3D11VADeviceContext*>(dctx->hwctx);
|
||||||
|
|
||||||
|
// TODO: Determine if these need an additional reference.
|
||||||
|
d3d11va->device = _device;
|
||||||
|
d3d11va->device->AddRef();
|
||||||
|
d3d11va->device_context = _context;
|
||||||
|
d3d11va->device_context->AddRef();
|
||||||
|
d3d11va->lock = [](void*) { obs_enter_graphics(); };
|
||||||
|
d3d11va->unlock = [](void*) { obs_leave_graphics(); };
|
||||||
|
|
||||||
|
int ret = av_hwdevice_ctx_init(dctx_ref);
|
||||||
|
if (ret < 0)
|
||||||
|
throw std::runtime_error("Failed to initialize AVHWDeviceContext.");
|
||||||
|
|
||||||
|
return dctx_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<AVFrame> obsffmpeg::hwapi::d3d11_instance::allocate_frame(AVBufferRef* frames)
|
||||||
|
{
|
||||||
|
auto gctx = obsffmpeg::obs_graphics();
|
||||||
|
|
||||||
|
auto frame = std::shared_ptr<AVFrame>(av_frame_alloc(), [](AVFrame* frame) {
|
||||||
|
av_frame_unref(frame);
|
||||||
|
av_frame_free(&frame);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (av_hwframe_get_buffer(frames, frame.get(), 0) < 0) {
|
||||||
|
throw std::runtime_error("Failed to create AVFrame.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
void obsffmpeg::hwapi::d3d11_instance::copy_from_obs(AVBufferRef*, uint32_t handle, uint64_t lock_key,
|
||||||
|
uint64_t* next_lock_key, std::shared_ptr<AVFrame> frame)
|
||||||
|
{
|
||||||
|
auto gctx = obsffmpeg::obs_graphics();
|
||||||
|
|
||||||
|
ATL::CComPtr<IDXGIKeyedMutex> mutex;
|
||||||
|
ATL::CComPtr<ID3D11Texture2D> input;
|
||||||
|
|
||||||
|
if (FAILED(_device->OpenSharedResource(reinterpret_cast<HANDLE>(static_cast<uintptr_t>(handle)),
|
||||||
|
__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&input)))) {
|
||||||
|
throw std::runtime_error("Failed to open shared texture resource.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FAILED(input->QueryInterface(__uuidof(IDXGIKeyedMutex), reinterpret_cast<void**>(&mutex)))) {
|
||||||
|
throw std::runtime_error("Failed to retrieve mutex for texture resource.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FAILED(mutex->AcquireSync(lock_key, 1000))) {
|
||||||
|
throw std::runtime_error("Failed to acquire lock on input texture.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set some parameters on the input texture, and get its description.
|
||||||
|
UINT evict = input->GetEvictionPriority();
|
||||||
|
input->SetEvictionPriority(DXGI_RESOURCE_PRIORITY_MAXIMUM);
|
||||||
|
|
||||||
|
// Clone the content of the input texture.
|
||||||
|
_context->CopyResource(reinterpret_cast<ID3D11Texture2D*>(frame->data[0]), input);
|
||||||
|
|
||||||
|
// Restore original parameters on input.
|
||||||
|
input->SetEvictionPriority(evict);
|
||||||
|
|
||||||
|
if (FAILED(mutex->ReleaseSync(lock_key))) {
|
||||||
|
throw std::runtime_error("Failed to release lock on input texture.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Determine if this is necessary.
|
||||||
|
mutex->ReleaseSync(*next_lock_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<AVFrame> obsffmpeg::hwapi::d3d11_instance::avframe_from_obs(AVBufferRef* frames, uint32_t handle,
|
||||||
|
uint64_t lock_key, uint64_t* next_lock_key)
|
||||||
|
{
|
||||||
|
auto gctx = obsffmpeg::obs_graphics();
|
||||||
|
|
||||||
|
auto frame = this->allocate_frame(frames);
|
||||||
|
this->copy_from_obs(frames, handle, lock_key, next_lock_key, frame);
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
// FFMPEG Video Encoder Integration for OBS Studio
|
||||||
|
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#include <atlutil.h>
|
||||||
|
#include <d3d11.h>
|
||||||
|
#include <d3d11_1.h>
|
||||||
|
#include <dxgi.h>
|
||||||
|
#include "base.hpp"
|
||||||
|
|
||||||
|
namespace obsffmpeg {
|
||||||
|
namespace hwapi {
|
||||||
|
class d3d11 : public ::obsffmpeg::hwapi::base {
|
||||||
|
typedef HRESULT(__stdcall* CreateDXGIFactory_t)(REFIID, void**);
|
||||||
|
typedef HRESULT(__stdcall* CreateDXGIFactory1_t)(REFIID, void**);
|
||||||
|
typedef HRESULT(__stdcall* D3D11CreateDevice_t)(_In_opt_ IDXGIAdapter*, D3D_DRIVER_TYPE,
|
||||||
|
HMODULE, UINT, CONST D3D_FEATURE_LEVEL*, UINT,
|
||||||
|
UINT, _Out_opt_ ID3D11Device**,
|
||||||
|
_Out_opt_ D3D_FEATURE_LEVEL*,
|
||||||
|
_Out_opt_ ID3D11DeviceContext**);
|
||||||
|
|
||||||
|
HMODULE _dxgi_module;
|
||||||
|
CreateDXGIFactory_t _CreateDXGIFactory;
|
||||||
|
CreateDXGIFactory1_t _CreateDXGIFactory1;
|
||||||
|
|
||||||
|
HMODULE _d3d11_module;
|
||||||
|
D3D11CreateDevice_t _D3D11CreateDevice;
|
||||||
|
|
||||||
|
ATL::CComPtr<IDXGIFactory1> _dxgifactory;
|
||||||
|
|
||||||
|
public:
|
||||||
|
d3d11();
|
||||||
|
virtual ~d3d11();
|
||||||
|
|
||||||
|
virtual std::list<obsffmpeg::hwapi::device> enumerate_adapters() override;
|
||||||
|
|
||||||
|
virtual std::shared_ptr<obsffmpeg::hwapi::instance>
|
||||||
|
create(obsffmpeg::hwapi::device target) override;
|
||||||
|
|
||||||
|
virtual std::shared_ptr<obsffmpeg::hwapi::instance> create_from_obs() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class d3d11_instance : public ::obsffmpeg::hwapi::instance {
|
||||||
|
ATL::CComPtr<ID3D11Device> _device;
|
||||||
|
ATL::CComPtr<ID3D11DeviceContext> _context;
|
||||||
|
|
||||||
|
public:
|
||||||
|
d3d11_instance(ATL::CComPtr<ID3D11Device> device, ATL::CComPtr<ID3D11DeviceContext> context);
|
||||||
|
virtual ~d3d11_instance();
|
||||||
|
|
||||||
|
virtual AVBufferRef* create_device_context() override;
|
||||||
|
|
||||||
|
virtual std::shared_ptr<AVFrame> allocate_frame(AVBufferRef* frames) override;
|
||||||
|
|
||||||
|
virtual void copy_from_obs(AVBufferRef* frames, uint32_t handle, uint64_t lock_key,
|
||||||
|
uint64_t* next_lock_key, std::shared_ptr<AVFrame> frame) override;
|
||||||
|
|
||||||
|
virtual std::shared_ptr<AVFrame> avframe_from_obs(AVBufferRef* frames, uint32_t handle,
|
||||||
|
uint64_t lock_key,
|
||||||
|
uint64_t* next_lock_key) override;
|
||||||
|
};
|
||||||
|
} // namespace hwapi
|
||||||
|
} // namespace obsffmpeg
|
||||||
@@ -35,7 +35,7 @@ extern "C" {
|
|||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
}
|
}
|
||||||
|
|
||||||
void obsffmpeg::ui::debug_handler::get_defaults(obs_data_t*, const AVCodec*, AVCodecContext*) {}
|
void obsffmpeg::ui::debug_handler::get_defaults(obs_data_t*, const AVCodec*, AVCodecContext*, bool) {}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
std::string to_string(T value){};
|
std::string to_string(T value){};
|
||||||
@@ -64,7 +64,8 @@ std::string to_string(double_t value)
|
|||||||
return std::string(buf.data(), buf.data() + buf.size());
|
return std::string(buf.data(), buf.data() + buf.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void obsffmpeg::ui::debug_handler::get_properties(obs_properties_t*, const AVCodec* codec, AVCodecContext* context)
|
void obsffmpeg::ui::debug_handler::get_properties(obs_properties_t*, const AVCodec* codec, AVCodecContext* context,
|
||||||
|
bool)
|
||||||
{
|
{
|
||||||
if (context)
|
if (context)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -26,11 +26,11 @@ namespace obsffmpeg {
|
|||||||
namespace ui {
|
namespace ui {
|
||||||
class debug_handler : public handler {
|
class debug_handler : public handler {
|
||||||
public:
|
public:
|
||||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec,
|
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context,
|
||||||
AVCodecContext* context) override;
|
bool hw_encode) override;
|
||||||
|
|
||||||
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||||
AVCodecContext* context) override;
|
AVCodecContext* context, bool hw_encode) override;
|
||||||
|
|
||||||
virtual void update(obs_data_t* settings, const AVCodec* codec,
|
virtual void update(obs_data_t* settings, const AVCodec* codec,
|
||||||
AVCodecContext* context) override;
|
AVCodecContext* context) override;
|
||||||
|
|||||||
+23
-1
@@ -20,5 +20,27 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
|
|
||||||
#include "handler.hpp"
|
#include "handler.hpp"
|
||||||
|
#include "encoder.hpp"
|
||||||
|
|
||||||
void obsffmpeg::ui::handler::override_visible_name(const AVCodec*, std::string&) {}
|
void obsffmpeg::ui::handler::adjust_encoder_info(obsffmpeg::encoder_factory*, obsffmpeg::encoder_info*,
|
||||||
|
obsffmpeg::encoder_info*)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::handler::get_defaults(obs_data_t*, const AVCodec*, AVCodecContext*, bool) {}
|
||||||
|
|
||||||
|
bool obsffmpeg::ui::handler::has_keyframe_support(obsffmpeg::encoder* instance)
|
||||||
|
{
|
||||||
|
return (instance->get_avcodec()->capabilities & AV_CODEC_CAP_INTRA_ONLY) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::handler::get_properties(obs_properties_t*, const AVCodec*, AVCodecContext*, bool) {}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::handler::update(obs_data_t*, const AVCodec*, AVCodecContext*) {}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::handler::override_update(obsffmpeg::encoder*, obs_data_t*) {}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::handler::log_options(obs_data_t*, const AVCodec*, AVCodecContext*) {}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::handler::override_colorformat(AVPixelFormat&, obs_data_t*, const AVCodec*, AVCodecContext*) {}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::handler::process_avpacket(AVPacket&, const AVCodec*, AVCodecContext*) {}
|
||||||
|
|||||||
+30
-6
@@ -22,9 +22,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include "hwapi/base.hpp"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
#include <obs.h>
|
||||||
|
|
||||||
#include <obs-data.h>
|
#include <obs-data.h>
|
||||||
|
#include <obs-encoder.h>
|
||||||
#include <obs-properties.h>
|
#include <obs-properties.h>
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
#pragma warning(disable : 4244)
|
#pragma warning(disable : 4244)
|
||||||
@@ -33,18 +37,38 @@ extern "C" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace obsffmpeg {
|
namespace obsffmpeg {
|
||||||
|
struct encoder_info;
|
||||||
|
class encoder_factory;
|
||||||
|
class encoder;
|
||||||
|
|
||||||
namespace ui {
|
namespace ui {
|
||||||
class handler {
|
class handler {
|
||||||
public:
|
public /*factory*/:
|
||||||
virtual void override_visible_name(const AVCodec* codec, std::string& name);
|
virtual void adjust_encoder_info(obsffmpeg::encoder_factory* factory,
|
||||||
|
obsffmpeg::encoder_info* main,
|
||||||
|
obsffmpeg::encoder_info* fallback);
|
||||||
|
|
||||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec,
|
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context,
|
||||||
AVCodecContext* context) = 0;
|
bool hw_encode);
|
||||||
|
|
||||||
|
public /*settings*/:
|
||||||
|
virtual bool has_keyframe_support(obsffmpeg::encoder* instance);
|
||||||
|
|
||||||
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||||
AVCodecContext* context) = 0;
|
AVCodecContext* context, bool hw_encode);
|
||||||
|
|
||||||
virtual void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context) = 0;
|
virtual void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||||
|
|
||||||
|
virtual void override_update(obsffmpeg::encoder* instance, obs_data_t* settings);
|
||||||
|
|
||||||
|
virtual void log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||||
|
|
||||||
|
public /*instance*/:
|
||||||
|
|
||||||
|
virtual void override_colorformat(AVPixelFormat& target_format, obs_data_t* settings,
|
||||||
|
const AVCodec* codec, AVCodecContext* context);
|
||||||
|
|
||||||
|
virtual void process_avpacket(AVPacket& packet, const AVCodec* codec, AVCodecContext* context);
|
||||||
};
|
};
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
} // namespace obsffmpeg
|
} // namespace obsffmpeg
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
|
|
||||||
#include "nvenc_h264_handler.hpp"
|
#include "nvenc_h264_handler.hpp"
|
||||||
#include "codecs/h264.hpp"
|
#include "codecs/h264.hpp"
|
||||||
|
#include "encoder.hpp"
|
||||||
|
#include "ffmpeg/tools.hpp"
|
||||||
#include "nvenc_shared.hpp"
|
#include "nvenc_shared.hpp"
|
||||||
#include "plugin.hpp"
|
#include "plugin.hpp"
|
||||||
#include "strings.hpp"
|
#include "strings.hpp"
|
||||||
@@ -34,28 +36,6 @@ extern "C" {
|
|||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Missing Options:
|
|
||||||
|
|
||||||
Seem to be covered by initQP_* instead.
|
|
||||||
- [obs-ffmpeg-encoder] Option 'cq' with help 'Set target quality level (0 to 51, 0 means automatic) for constant quality mode in VBR rate control' of type 'Float' with default value '0.000000', minimum '0.000000' and maximum '51.000000'.
|
|
||||||
- [obs-ffmpeg-encoder] Option 'qp' with help 'Constant quantization parameter rate control method' of type 'Int' with default value '-1', minimum '-1.000000' and maximum '51.000000'.
|
|
||||||
|
|
||||||
Not sure what there are useful for.
|
|
||||||
[obs-ffmpeg-encoder] Option 'aud' with help 'Use access unit delimiters' of type 'Bool' with default value 'false', minimum '0.000000' and maximum '1.000000'.
|
|
||||||
[obs-ffmpeg-encoder] Option 'surfaces' with help 'Number of concurrent surfaces' of type 'Int' with default value '0', minimum '0.000000' and maximum '64.000000'.
|
|
||||||
[obs-ffmpeg-encoder] Option 'delay' with help 'Delay frame output by the given amount of frames' of type 'Int' with default value '2147483647', minimum '0.000000' and maximum '2147483647.000000'.
|
|
||||||
|
|
||||||
Should probably add this.
|
|
||||||
[obs-ffmpeg-encoder] Option 'gpu' with unit (gpu) with help 'Selects which NVENC capable GPU to use. First GPU is 0, second is 1, and so on.' of type 'Int' with default value '-1', minimum '-2.000000' and maximum '2147483647.000000'.
|
|
||||||
[obs-ffmpeg-encoder] [gpu] Constant 'any' and help text 'Pick the first device available' with value '-1'.
|
|
||||||
[obs-ffmpeg-encoder] [gpu] Constant 'list' and help text 'List the available devices' with value '-2'.
|
|
||||||
|
|
||||||
Useless except for strict_gop maybe?
|
|
||||||
[obs-ffmpeg-encoder] Option 'forced-idr' with help 'If forcing keyframes, force them as IDR frames.' of type 'Bool' with default value 'false', minimum '-1.000000' and maximum '1.000000'.
|
|
||||||
[obs-ffmpeg-encoder] Option 'strict_gop' with help 'Set 1 to minimize GOP-to-GOP rate fluctuations' of type 'Bool' with default value 'false', minimum '0.000000' and maximum '1.000000'.
|
|
||||||
[obs-ffmpeg-encoder] Option 'bluray-compat' with help 'Bluray compatibility workarounds' of type 'Bool' with default value 'false', minimum '0.000000' and maximum '1.000000'.
|
|
||||||
*/
|
|
||||||
|
|
||||||
using namespace obsffmpeg::codecs::h264;
|
using namespace obsffmpeg::codecs::h264;
|
||||||
|
|
||||||
std::map<profile, std::string> profiles{
|
std::map<profile, std::string> profiles{
|
||||||
@@ -79,13 +59,15 @@ INITIALIZER(nvenc_h264_handler_init)
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
void obsffmpeg::ui::nvenc_h264_handler::override_visible_name(const AVCodec*, std::string& name)
|
void obsffmpeg::ui::nvenc_h264_handler::adjust_encoder_info(obsffmpeg::encoder_factory*, obsffmpeg::encoder_info* main,
|
||||||
|
obsffmpeg::encoder_info* fallback)
|
||||||
{
|
{
|
||||||
name = "H.264/AVC Encoder (NVidia NVENC)";
|
main->readable_name = "H.264/AVC NVidia NVENC (Hardware)";
|
||||||
|
fallback->readable_name = "H.264/AVC NVidia NVENC (Software)";
|
||||||
}
|
}
|
||||||
|
|
||||||
void obsffmpeg::ui::nvenc_h264_handler::get_defaults(obs_data_t* settings, const AVCodec* codec,
|
void obsffmpeg::ui::nvenc_h264_handler::get_defaults(obs_data_t* settings, const AVCodec* codec,
|
||||||
AVCodecContext* context)
|
AVCodecContext* context, bool)
|
||||||
{
|
{
|
||||||
nvenc::get_defaults(settings, codec, context);
|
nvenc::get_defaults(settings, codec, context);
|
||||||
|
|
||||||
@@ -93,8 +75,13 @@ void obsffmpeg::ui::nvenc_h264_handler::get_defaults(obs_data_t* settings, const
|
|||||||
obs_data_set_default_int(settings, P_H264_LEVEL, static_cast<int64_t>(codecs::h264::level::UNKNOWN));
|
obs_data_set_default_int(settings, P_H264_LEVEL, static_cast<int64_t>(codecs::h264::level::UNKNOWN));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool obsffmpeg::ui::nvenc_h264_handler::has_keyframe_support(obsffmpeg::encoder*)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void obsffmpeg::ui::nvenc_h264_handler::get_properties(obs_properties_t* props, const AVCodec* codec,
|
void obsffmpeg::ui::nvenc_h264_handler::get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||||
AVCodecContext* context)
|
AVCodecContext* context, bool)
|
||||||
{
|
{
|
||||||
if (!context) {
|
if (!context) {
|
||||||
this->get_encoder_properties(props, codec);
|
this->get_encoder_properties(props, codec);
|
||||||
@@ -125,6 +112,32 @@ void obsffmpeg::ui::nvenc_h264_handler::update(obs_data_t* settings, const AVCod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::nvenc_h264_handler::override_update(obsffmpeg::encoder* instance, obs_data_t* settings)
|
||||||
|
{
|
||||||
|
nvenc::override_update(instance, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::nvenc_h264_handler::log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context)
|
||||||
|
{
|
||||||
|
nvenc::log_options(settings, codec, context);
|
||||||
|
|
||||||
|
PLOG_INFO("[%s] H.265/HEVC:", codec->name);
|
||||||
|
ffmpeg::tools::print_av_option_string(context, "profile", " Profile", [](int64_t v) {
|
||||||
|
profile val = static_cast<profile>(v);
|
||||||
|
auto index = profiles.find(val);
|
||||||
|
if (index != profiles.end())
|
||||||
|
return index->second;
|
||||||
|
return std::string("<Unknown>");
|
||||||
|
});
|
||||||
|
ffmpeg::tools::print_av_option_string(context, "level", " Level", [](int64_t v) {
|
||||||
|
level val = static_cast<level>(v);
|
||||||
|
auto index = levels.find(val);
|
||||||
|
if (index != levels.end())
|
||||||
|
return index->second;
|
||||||
|
return std::string("<Unknown>");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void obsffmpeg::ui::nvenc_h264_handler::get_encoder_properties(obs_properties_t* props, const AVCodec* codec)
|
void obsffmpeg::ui::nvenc_h264_handler::get_encoder_properties(obs_properties_t* props, const AVCodec* codec)
|
||||||
{
|
{
|
||||||
nvenc::get_properties_pre(props, codec);
|
nvenc::get_properties_pre(props, codec);
|
||||||
|
|||||||
@@ -33,17 +33,28 @@ extern "C" {
|
|||||||
namespace obsffmpeg {
|
namespace obsffmpeg {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
class nvenc_h264_handler : public handler {
|
class nvenc_h264_handler : public handler {
|
||||||
public:
|
public /*factory*/:
|
||||||
virtual void override_visible_name(const AVCodec* codec, std::string& name) override;
|
virtual void adjust_encoder_info(obsffmpeg::encoder_factory* factory,
|
||||||
|
obsffmpeg::encoder_info* main,
|
||||||
|
obsffmpeg::encoder_info* fallback);
|
||||||
|
|
||||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec,
|
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context,
|
||||||
AVCodecContext* context) override;
|
bool hw_encode);
|
||||||
|
|
||||||
|
public /*settings*/:
|
||||||
|
virtual bool has_keyframe_support(obsffmpeg::encoder* instance);
|
||||||
|
|
||||||
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||||
AVCodecContext* context) override;
|
AVCodecContext* context, bool hw_encode);
|
||||||
|
|
||||||
virtual void update(obs_data_t* settings, const AVCodec* codec,
|
virtual void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||||
AVCodecContext* context) override;
|
|
||||||
|
virtual void override_update(obsffmpeg::encoder* instance, obs_data_t* settings);
|
||||||
|
|
||||||
|
virtual void log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||||
|
|
||||||
|
public /*instance*/:
|
||||||
|
//virtual void override_colorformat(AVPixelFormat& target_format, obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void get_encoder_properties(obs_properties_t* props, const AVCodec* codec);
|
void get_encoder_properties(obs_properties_t* props, const AVCodec* codec);
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
|
|
||||||
#include "nvenc_hevc_handler.hpp"
|
#include "nvenc_hevc_handler.hpp"
|
||||||
#include "codecs/hevc.hpp"
|
#include "codecs/hevc.hpp"
|
||||||
|
#include "encoder.hpp"
|
||||||
|
#include "ffmpeg/tools.hpp"
|
||||||
#include "nvenc_shared.hpp"
|
#include "nvenc_shared.hpp"
|
||||||
#include "plugin.hpp"
|
#include "plugin.hpp"
|
||||||
#include "strings.hpp"
|
#include "strings.hpp"
|
||||||
@@ -34,28 +36,6 @@ extern "C" {
|
|||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Missing Options:
|
|
||||||
|
|
||||||
Seem to be covered by initQP_* instead.
|
|
||||||
- [obs-ffmpeg-encoder] Option 'cq' with help 'Set target quality level (0 to 51, 0 means automatic) for constant quality mode in VBR rate control' of type 'Float' with default value '0.000000', minimum '0.000000' and maximum '51.000000'.
|
|
||||||
- [obs-ffmpeg-encoder] Option 'qp' with help 'Constant quantization parameter rate control method' of type 'Int' with default value '-1', minimum '-1.000000' and maximum '51.000000'.
|
|
||||||
|
|
||||||
Not sure what there are useful for.
|
|
||||||
[obs-ffmpeg-encoder] Option 'aud' with help 'Use access unit delimiters' of type 'Bool' with default value 'false', minimum '0.000000' and maximum '1.000000'.
|
|
||||||
[obs-ffmpeg-encoder] Option 'surfaces' with help 'Number of concurrent surfaces' of type 'Int' with default value '0', minimum '0.000000' and maximum '64.000000'.
|
|
||||||
[obs-ffmpeg-encoder] Option 'delay' with help 'Delay frame output by the given amount of frames' of type 'Int' with default value '2147483647', minimum '0.000000' and maximum '2147483647.000000'.
|
|
||||||
|
|
||||||
Should probably add this.
|
|
||||||
[obs-ffmpeg-encoder] Option 'gpu' with unit (gpu) with help 'Selects which NVENC capable GPU to use. First GPU is 0, second is 1, and so on.' of type 'Int' with default value '-1', minimum '-2.000000' and maximum '2147483647.000000'.
|
|
||||||
[obs-ffmpeg-encoder] [gpu] Constant 'any' and help text 'Pick the first device available' with value '-1'.
|
|
||||||
[obs-ffmpeg-encoder] [gpu] Constant 'list' and help text 'List the available devices' with value '-2'.
|
|
||||||
|
|
||||||
Useless except for strict_gop maybe?
|
|
||||||
[obs-ffmpeg-encoder] Option 'forced-idr' with help 'If forcing keyframes, force them as IDR frames.' of type 'Bool' with default value 'false', minimum '-1.000000' and maximum '1.000000'.
|
|
||||||
[obs-ffmpeg-encoder] Option 'strict_gop' with help 'Set 1 to minimize GOP-to-GOP rate fluctuations' of type 'Bool' with default value 'false', minimum '0.000000' and maximum '1.000000'.
|
|
||||||
[obs-ffmpeg-encoder] Option 'bluray-compat' with help 'Bluray compatibility workarounds' of type 'Bool' with default value 'false', minimum '0.000000' and maximum '1.000000'.
|
|
||||||
*/
|
|
||||||
|
|
||||||
using namespace obsffmpeg::codecs::hevc;
|
using namespace obsffmpeg::codecs::hevc;
|
||||||
|
|
||||||
std::map<profile, std::string> profiles{
|
std::map<profile, std::string> profiles{
|
||||||
@@ -82,13 +62,15 @@ INITIALIZER(nvenc_hevc_handler_init)
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
void obsffmpeg::ui::nvenc_hevc_handler::override_visible_name(const AVCodec*, std::string& name)
|
void obsffmpeg::ui::nvenc_hevc_handler::adjust_encoder_info(obsffmpeg::encoder_factory*, obsffmpeg::encoder_info* main,
|
||||||
|
obsffmpeg::encoder_info* fallback)
|
||||||
{
|
{
|
||||||
name = "H.265/HEVC Encoder (NVidia NVENC)";
|
main->readable_name = "H.265/HEVC Nvidia NVENC (Hardware)";
|
||||||
|
fallback->readable_name = "H.265/HEVC Nvidia NVENC (Software)";
|
||||||
}
|
}
|
||||||
|
|
||||||
void obsffmpeg::ui::nvenc_hevc_handler::get_defaults(obs_data_t* settings, const AVCodec* codec,
|
void obsffmpeg::ui::nvenc_hevc_handler::get_defaults(obs_data_t* settings, const AVCodec* codec,
|
||||||
AVCodecContext* context)
|
AVCodecContext* context, bool)
|
||||||
{
|
{
|
||||||
nvenc::get_defaults(settings, codec, context);
|
nvenc::get_defaults(settings, codec, context);
|
||||||
|
|
||||||
@@ -97,8 +79,13 @@ void obsffmpeg::ui::nvenc_hevc_handler::get_defaults(obs_data_t* settings, const
|
|||||||
obs_data_set_default_int(settings, P_HEVC_LEVEL, static_cast<int64_t>(codecs::hevc::level::UNKNOWN));
|
obs_data_set_default_int(settings, P_HEVC_LEVEL, static_cast<int64_t>(codecs::hevc::level::UNKNOWN));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool obsffmpeg::ui::nvenc_hevc_handler::has_keyframe_support(obsffmpeg::encoder*)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void obsffmpeg::ui::nvenc_hevc_handler::get_properties(obs_properties_t* props, const AVCodec* codec,
|
void obsffmpeg::ui::nvenc_hevc_handler::get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||||
AVCodecContext* context)
|
AVCodecContext* context, bool)
|
||||||
{
|
{
|
||||||
if (!context) {
|
if (!context) {
|
||||||
this->get_encoder_properties(props, codec);
|
this->get_encoder_properties(props, codec);
|
||||||
@@ -133,6 +120,39 @@ void obsffmpeg::ui::nvenc_hevc_handler::update(obs_data_t* settings, const AVCod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::nvenc_hevc_handler::override_update(obsffmpeg::encoder* instance, obs_data_t* settings)
|
||||||
|
{
|
||||||
|
nvenc::override_update(instance, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::nvenc_hevc_handler::log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context)
|
||||||
|
{
|
||||||
|
nvenc::log_options(settings, codec, context);
|
||||||
|
|
||||||
|
PLOG_INFO("[%s] H.265/HEVC:", codec->name);
|
||||||
|
ffmpeg::tools::print_av_option_string(context, "profile", " Profile", [](int64_t v) {
|
||||||
|
profile val = static_cast<profile>(v);
|
||||||
|
auto index = profiles.find(val);
|
||||||
|
if (index != profiles.end())
|
||||||
|
return index->second;
|
||||||
|
return std::string("<Unknown>");
|
||||||
|
});
|
||||||
|
ffmpeg::tools::print_av_option_string(context, "level", " Level", [](int64_t v) {
|
||||||
|
level val = static_cast<level>(v);
|
||||||
|
auto index = levels.find(val);
|
||||||
|
if (index != levels.end())
|
||||||
|
return index->second;
|
||||||
|
return std::string("<Unknown>");
|
||||||
|
});
|
||||||
|
ffmpeg::tools::print_av_option_string(context, "tier", " Tier", [](int64_t v) {
|
||||||
|
tier val = static_cast<tier>(v);
|
||||||
|
auto index = tiers.find(val);
|
||||||
|
if (index != tiers.end())
|
||||||
|
return index->second;
|
||||||
|
return std::string("<Unknown>");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void obsffmpeg::ui::nvenc_hevc_handler::get_encoder_properties(obs_properties_t* props, const AVCodec* codec)
|
void obsffmpeg::ui::nvenc_hevc_handler::get_encoder_properties(obs_properties_t* props, const AVCodec* codec)
|
||||||
{
|
{
|
||||||
nvenc::get_properties_pre(props, codec);
|
nvenc::get_properties_pre(props, codec);
|
||||||
|
|||||||
@@ -33,17 +33,28 @@ extern "C" {
|
|||||||
namespace obsffmpeg {
|
namespace obsffmpeg {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
class nvenc_hevc_handler : public handler {
|
class nvenc_hevc_handler : public handler {
|
||||||
public:
|
public /*factory*/:
|
||||||
virtual void override_visible_name(const AVCodec* codec, std::string& name) override;
|
virtual void adjust_encoder_info(obsffmpeg::encoder_factory* factory,
|
||||||
|
obsffmpeg::encoder_info* main,
|
||||||
|
obsffmpeg::encoder_info* fallback);
|
||||||
|
|
||||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec,
|
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context,
|
||||||
AVCodecContext* context) override;
|
bool hw_encode);
|
||||||
|
|
||||||
|
public /*settings*/:
|
||||||
|
virtual bool has_keyframe_support(obsffmpeg::encoder* instance);
|
||||||
|
|
||||||
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||||
AVCodecContext* context) override;
|
AVCodecContext* context, bool hw_encode);
|
||||||
|
|
||||||
virtual void update(obs_data_t* settings, const AVCodec* codec,
|
virtual void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||||
AVCodecContext* context) override;
|
|
||||||
|
virtual void override_update(obsffmpeg::encoder* instance, obs_data_t* settings);
|
||||||
|
|
||||||
|
virtual void log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||||
|
|
||||||
|
public /*instance*/:
|
||||||
|
//virtual void override_colorformat(AVPixelFormat& target_format, obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void get_encoder_properties(obs_properties_t* props, const AVCodec* codec);
|
void get_encoder_properties(obs_properties_t* props, const AVCodec* codec);
|
||||||
|
|||||||
+166
-54
@@ -20,7 +20,10 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
|
|
||||||
#include "nvenc_shared.hpp"
|
#include "nvenc_shared.hpp"
|
||||||
|
#include <algorithm>
|
||||||
#include "codecs/hevc.hpp"
|
#include "codecs/hevc.hpp"
|
||||||
|
#include "encoder.hpp"
|
||||||
|
#include "ffmpeg/tools.hpp"
|
||||||
#include "plugin.hpp"
|
#include "plugin.hpp"
|
||||||
#include "strings.hpp"
|
#include "strings.hpp"
|
||||||
#include "utility.hpp"
|
#include "utility.hpp"
|
||||||
@@ -57,7 +60,7 @@ extern "C" {
|
|||||||
#define ST_RATECONTROL_QP_I ST_RATECONTROL_QP ".I"
|
#define ST_RATECONTROL_QP_I ST_RATECONTROL_QP ".I"
|
||||||
#define ST_RATECONTROL_QP_I_INITIAL ST_RATECONTROL_QP_I ".Initial"
|
#define ST_RATECONTROL_QP_I_INITIAL ST_RATECONTROL_QP_I ".Initial"
|
||||||
#define ST_RATECONTROL_QP_P ST_RATECONTROL_QP ".P"
|
#define ST_RATECONTROL_QP_P ST_RATECONTROL_QP ".P"
|
||||||
#define ST_RATECONTROL_QP_ST_INITIAL ST_RATECONTROL_QP_P ".Initial"
|
#define ST_RATECONTROL_QP_P_INITIAL ST_RATECONTROL_QP_P ".Initial"
|
||||||
#define ST_RATECONTROL_QP_B ST_RATECONTROL_QP ".B"
|
#define ST_RATECONTROL_QP_B ST_RATECONTROL_QP ".B"
|
||||||
#define ST_RATECONTROL_QP_B_INITIAL ST_RATECONTROL_QP_B ".Initial"
|
#define ST_RATECONTROL_QP_B_INITIAL ST_RATECONTROL_QP_B ".Initial"
|
||||||
|
|
||||||
@@ -131,6 +134,36 @@ std::map<b_ref_mode, std::string> obsffmpeg::nvenc::b_ref_mode_to_opt{
|
|||||||
{b_ref_mode::MIDDLE, "middle"},
|
{b_ref_mode::MIDDLE, "middle"},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void obsffmpeg::nvenc::override_update(obsffmpeg::encoder* instance, obs_data_t*)
|
||||||
|
{
|
||||||
|
AVCodecContext* context = const_cast<AVCodecContext*>(instance->get_avcodeccontext());
|
||||||
|
|
||||||
|
int64_t rclookahead = 0;
|
||||||
|
int64_t surfaces = 0;
|
||||||
|
int64_t async_depth = 0;
|
||||||
|
|
||||||
|
av_opt_get_int(context, "rc-lookahead", AV_OPT_SEARCH_CHILDREN, &rclookahead);
|
||||||
|
av_opt_get_int(context, "surfaces", AV_OPT_SEARCH_CHILDREN, &surfaces);
|
||||||
|
av_opt_get_int(context, "async_depth", AV_OPT_SEARCH_CHILDREN, &async_depth);
|
||||||
|
|
||||||
|
// Calculate and set the number of surfaces to allocate (if not user overridden).
|
||||||
|
if (surfaces == 0) {
|
||||||
|
surfaces = std::max(4ll, (context->max_b_frames + 1ll) * 4ll);
|
||||||
|
if (rclookahead > 0) {
|
||||||
|
surfaces = std::max(1ll, std::max(surfaces, rclookahead + (context->max_b_frames + 5ll)));
|
||||||
|
} else if (context->max_b_frames > 0) {
|
||||||
|
surfaces = std::max(4ll, (context->max_b_frames + 1ll) * 4ll);
|
||||||
|
} else {
|
||||||
|
surfaces = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
av_opt_set_int(context, "surfaces", surfaces, AV_OPT_SEARCH_CHILDREN);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set delay
|
||||||
|
context->delay = static_cast<int>(std::min(std::max(async_depth, 3ll), surfaces - 1));
|
||||||
|
}
|
||||||
|
|
||||||
void obsffmpeg::nvenc::get_defaults(obs_data_t* settings, const AVCodec*, AVCodecContext*)
|
void obsffmpeg::nvenc::get_defaults(obs_data_t* settings, const AVCodec*, AVCodecContext*)
|
||||||
{
|
{
|
||||||
obs_data_set_default_int(settings, ST_PRESET, static_cast<int64_t>(preset::DEFAULT));
|
obs_data_set_default_int(settings, ST_PRESET, static_cast<int64_t>(preset::DEFAULT));
|
||||||
@@ -138,8 +171,8 @@ void obsffmpeg::nvenc::get_defaults(obs_data_t* settings, const AVCodec*, AVCode
|
|||||||
obs_data_set_default_int(settings, ST_RATECONTROL_MODE, static_cast<int64_t>(ratecontrolmode::CBR_HQ));
|
obs_data_set_default_int(settings, ST_RATECONTROL_MODE, static_cast<int64_t>(ratecontrolmode::CBR_HQ));
|
||||||
obs_data_set_default_int(settings, ST_RATECONTROL_TWOPASS, -1);
|
obs_data_set_default_int(settings, ST_RATECONTROL_TWOPASS, -1);
|
||||||
obs_data_set_default_int(settings, ST_RATECONTROL_LOOKAHEAD, 0);
|
obs_data_set_default_int(settings, ST_RATECONTROL_LOOKAHEAD, 0);
|
||||||
obs_data_set_default_bool(settings, ST_RATECONTROL_ADAPTIVEI, true);
|
obs_data_set_default_int(settings, ST_RATECONTROL_ADAPTIVEI, -1);
|
||||||
obs_data_set_default_bool(settings, ST_RATECONTROL_ADAPTIVEB, true);
|
obs_data_set_default_int(settings, ST_RATECONTROL_ADAPTIVEB, -1);
|
||||||
|
|
||||||
obs_data_set_default_int(settings, ST_RATECONTROL_BITRATE_TARGET, 6000);
|
obs_data_set_default_int(settings, ST_RATECONTROL_BITRATE_TARGET, 6000);
|
||||||
obs_data_set_default_int(settings, ST_RATECONTROL_BITRATE_MAXIMUM, 6000);
|
obs_data_set_default_int(settings, ST_RATECONTROL_BITRATE_MAXIMUM, 6000);
|
||||||
@@ -152,19 +185,19 @@ void obsffmpeg::nvenc::get_defaults(obs_data_t* settings, const AVCodec*, AVCode
|
|||||||
obs_data_set_default_int(settings, ST_RATECONTROL_QP_I, 21);
|
obs_data_set_default_int(settings, ST_RATECONTROL_QP_I, 21);
|
||||||
obs_data_set_default_int(settings, ST_RATECONTROL_QP_I_INITIAL, -1);
|
obs_data_set_default_int(settings, ST_RATECONTROL_QP_I_INITIAL, -1);
|
||||||
obs_data_set_default_int(settings, ST_RATECONTROL_QP_P, 21);
|
obs_data_set_default_int(settings, ST_RATECONTROL_QP_P, 21);
|
||||||
obs_data_set_default_int(settings, ST_RATECONTROL_QP_ST_INITIAL, -1);
|
obs_data_set_default_int(settings, ST_RATECONTROL_QP_P_INITIAL, -1);
|
||||||
obs_data_set_default_int(settings, ST_RATECONTROL_QP_B, 21);
|
obs_data_set_default_int(settings, ST_RATECONTROL_QP_B, 21);
|
||||||
obs_data_set_default_int(settings, ST_RATECONTROL_QP_B_INITIAL, -1);
|
obs_data_set_default_int(settings, ST_RATECONTROL_QP_B_INITIAL, -1);
|
||||||
|
|
||||||
obs_data_set_default_bool(settings, ST_AQ_SPATIAL, true);
|
obs_data_set_default_int(settings, ST_AQ_SPATIAL, -1);
|
||||||
obs_data_set_default_int(settings, ST_AQ_STRENGTH, 8);
|
obs_data_set_default_int(settings, ST_AQ_STRENGTH, 8);
|
||||||
obs_data_set_default_bool(settings, ST_AQ_TEMPORAL, true);
|
obs_data_set_default_int(settings, ST_AQ_TEMPORAL, -1);
|
||||||
|
|
||||||
obs_data_set_default_int(settings, ST_OTHER_BFRAMES, 2);
|
obs_data_set_default_int(settings, ST_OTHER_BFRAMES, 2);
|
||||||
obs_data_set_default_int(settings, ST_OTHER_BFRAME_REFERENCEMODE, static_cast<int64_t>(b_ref_mode::DISABLED));
|
obs_data_set_default_int(settings, ST_OTHER_BFRAME_REFERENCEMODE, static_cast<int64_t>(b_ref_mode::DISABLED));
|
||||||
obs_data_set_default_bool(settings, ST_OTHER_ZEROLATENCY, false);
|
obs_data_set_default_int(settings, ST_OTHER_ZEROLATENCY, -1);
|
||||||
obs_data_set_default_bool(settings, ST_OTHER_WEIGHTED_PREDICTION, false);
|
obs_data_set_default_int(settings, ST_OTHER_WEIGHTED_PREDICTION, -1);
|
||||||
obs_data_set_default_bool(settings, ST_OTHER_NONREFERENCE_PFRAMES, false);
|
obs_data_set_default_int(settings, ST_OTHER_NONREFERENCE_PFRAMES, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool modified_ratecontrol(obs_properties_t* props, obs_property_t*, obs_data_t* settings)
|
static bool modified_ratecontrol(obs_properties_t* props, obs_property_t*, obs_data_t* settings)
|
||||||
@@ -211,7 +244,7 @@ static bool modified_ratecontrol(obs_properties_t* props, obs_property_t*, obs_d
|
|||||||
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_P), have_qp);
|
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_P), have_qp);
|
||||||
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_B), have_qp);
|
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_B), have_qp);
|
||||||
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_I_INITIAL), have_qp_init);
|
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_I_INITIAL), have_qp_init);
|
||||||
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_ST_INITIAL), have_qp_init);
|
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_P_INITIAL), have_qp_init);
|
||||||
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_B_INITIAL), have_qp_init);
|
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_B_INITIAL), have_qp_init);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -227,7 +260,7 @@ static bool modified_quality(obs_properties_t* props, obs_property_t*, obs_data_
|
|||||||
|
|
||||||
static bool modified_aq(obs_properties_t* props, obs_property_t*, obs_data_t* settings)
|
static bool modified_aq(obs_properties_t* props, obs_property_t*, obs_data_t* settings)
|
||||||
{
|
{
|
||||||
bool spatial_aq = obs_data_get_bool(settings, ST_AQ_SPATIAL);
|
bool spatial_aq = obs_data_get_int(settings, ST_AQ_SPATIAL) == 1;
|
||||||
obs_property_set_visible(obs_properties_get(props, ST_AQ_STRENGTH), spatial_aq);
|
obs_property_set_visible(obs_properties_get(props, ST_AQ_STRENGTH), spatial_aq);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -266,12 +299,9 @@ void obsffmpeg::nvenc::get_properties_post(obs_properties_t* props, const AVCode
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
auto p = obs_properties_add_list(grp, ST_RATECONTROL_TWOPASS, TRANSLATE(ST_RATECONTROL_TWOPASS),
|
auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_RATECONTROL_TWOPASS,
|
||||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
TRANSLATE(ST_RATECONTROL_TWOPASS));
|
||||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_TWOPASS)));
|
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_TWOPASS)));
|
||||||
obs_property_list_add_int(p, TRANSLATE(S_STATE_DEFAULT), -1);
|
|
||||||
obs_property_list_add_int(p, TRANSLATE(S_STATE_DISABLED), 0);
|
|
||||||
obs_property_list_add_int(p, TRANSLATE(S_STATE_ENABLED), 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -281,13 +311,13 @@ void obsffmpeg::nvenc::get_properties_post(obs_properties_t* props, const AVCode
|
|||||||
obs_property_int_set_suffix(p, " frames");
|
obs_property_int_set_suffix(p, " frames");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto p =
|
auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_RATECONTROL_ADAPTIVEI,
|
||||||
obs_properties_add_bool(grp, ST_RATECONTROL_ADAPTIVEI, TRANSLATE(ST_RATECONTROL_ADAPTIVEI));
|
TRANSLATE(ST_RATECONTROL_ADAPTIVEI));
|
||||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_ADAPTIVEI)));
|
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_ADAPTIVEI)));
|
||||||
}
|
}
|
||||||
if (strcmp(codec->name, "h264_nvenc") == 0) {
|
if (strcmp(codec->name, "h264_nvenc") == 0) {
|
||||||
auto p =
|
auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_RATECONTROL_ADAPTIVEB,
|
||||||
obs_properties_add_bool(grp, ST_RATECONTROL_ADAPTIVEB, TRANSLATE(ST_RATECONTROL_ADAPTIVEB));
|
TRANSLATE(ST_RATECONTROL_ADAPTIVEB));
|
||||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_ADAPTIVEB)));
|
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_ADAPTIVEB)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -379,9 +409,9 @@ void obsffmpeg::nvenc::get_properties_post(obs_properties_t* props, const AVCode
|
|||||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_QP_P)));
|
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_QP_P)));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto p = obs_properties_add_int_slider(grp, ST_RATECONTROL_QP_ST_INITIAL,
|
auto p = obs_properties_add_int_slider(grp, ST_RATECONTROL_QP_P_INITIAL,
|
||||||
TRANSLATE(ST_RATECONTROL_QP_ST_INITIAL), -1, 51, 1);
|
TRANSLATE(ST_RATECONTROL_QP_P_INITIAL), -1, 51, 1);
|
||||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_QP_ST_INITIAL)));
|
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_QP_P_INITIAL)));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto p = obs_properties_add_int_slider(grp, ST_RATECONTROL_QP_B, TRANSLATE(ST_RATECONTROL_QP_B),
|
auto p = obs_properties_add_int_slider(grp, ST_RATECONTROL_QP_B, TRANSLATE(ST_RATECONTROL_QP_B),
|
||||||
@@ -403,7 +433,7 @@ void obsffmpeg::nvenc::get_properties_post(obs_properties_t* props, const AVCode
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
auto p = obs_properties_add_bool(grp, ST_AQ_SPATIAL, TRANSLATE(ST_AQ_SPATIAL));
|
auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_AQ_SPATIAL, TRANSLATE(ST_AQ_SPATIAL));
|
||||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_AQ_SPATIAL)));
|
obs_property_set_long_description(p, TRANSLATE(DESC(ST_AQ_SPATIAL)));
|
||||||
obs_property_set_modified_callback(p, modified_aq);
|
obs_property_set_modified_callback(p, modified_aq);
|
||||||
}
|
}
|
||||||
@@ -413,7 +443,7 @@ void obsffmpeg::nvenc::get_properties_post(obs_properties_t* props, const AVCode
|
|||||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_AQ_STRENGTH)));
|
obs_property_set_long_description(p, TRANSLATE(DESC(ST_AQ_STRENGTH)));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto p = obs_properties_add_bool(grp, ST_AQ_TEMPORAL, TRANSLATE(ST_AQ_TEMPORAL));
|
auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_AQ_TEMPORAL, TRANSLATE(ST_AQ_TEMPORAL));
|
||||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_AQ_TEMPORAL)));
|
obs_property_set_long_description(p, TRANSLATE(DESC(ST_AQ_TEMPORAL)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -444,18 +474,19 @@ void obsffmpeg::nvenc::get_properties_post(obs_properties_t* props, const AVCode
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
auto p = obs_properties_add_bool(grp, ST_OTHER_ZEROLATENCY, TRANSLATE(ST_OTHER_ZEROLATENCY));
|
auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_OTHER_ZEROLATENCY,
|
||||||
|
TRANSLATE(ST_OTHER_ZEROLATENCY));
|
||||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_OTHER_ZEROLATENCY)));
|
obs_property_set_long_description(p, TRANSLATE(DESC(ST_OTHER_ZEROLATENCY)));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
auto p = obs_properties_add_bool(grp, ST_OTHER_WEIGHTED_PREDICTION,
|
auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_OTHER_WEIGHTED_PREDICTION,
|
||||||
TRANSLATE(ST_OTHER_WEIGHTED_PREDICTION));
|
TRANSLATE(ST_OTHER_WEIGHTED_PREDICTION));
|
||||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_OTHER_WEIGHTED_PREDICTION)));
|
obs_property_set_long_description(p, TRANSLATE(DESC(ST_OTHER_WEIGHTED_PREDICTION)));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
auto p = obs_properties_add_bool(grp, ST_OTHER_NONREFERENCE_PFRAMES,
|
auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_OTHER_NONREFERENCE_PFRAMES,
|
||||||
TRANSLATE(ST_OTHER_NONREFERENCE_PFRAMES));
|
TRANSLATE(ST_OTHER_NONREFERENCE_PFRAMES));
|
||||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_OTHER_NONREFERENCE_PFRAMES)));
|
obs_property_set_long_description(p, TRANSLATE(DESC(ST_OTHER_NONREFERENCE_PFRAMES)));
|
||||||
}
|
}
|
||||||
@@ -483,7 +514,7 @@ void obsffmpeg::nvenc::get_runtime_properties(obs_properties_t* props, const AVC
|
|||||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_I), false);
|
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_I), false);
|
||||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_I_INITIAL), false);
|
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_I_INITIAL), false);
|
||||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_P), false);
|
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_P), false);
|
||||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_ST_INITIAL), false);
|
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_P_INITIAL), false);
|
||||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_B), false);
|
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_B), false);
|
||||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_B_INITIAL), false);
|
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_B_INITIAL), false);
|
||||||
obs_property_set_enabled(obs_properties_get(props, ST_AQ), false);
|
obs_property_set_enabled(obs_properties_get(props, ST_AQ), false);
|
||||||
@@ -523,6 +554,7 @@ void obsffmpeg::nvenc::update(obs_data_t* settings, const AVCodec* codec, AVCode
|
|||||||
av_opt_set(context->priv_data, "rc", rcopt->second.c_str(), 0);
|
av_opt_set(context->priv_data, "rc", rcopt->second.c_str(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
av_opt_set_int(context->priv_data, "cbr", 0, 0);
|
||||||
switch (rc) {
|
switch (rc) {
|
||||||
case ratecontrolmode::CQP:
|
case ratecontrolmode::CQP:
|
||||||
have_qp = true;
|
have_qp = true;
|
||||||
@@ -548,14 +580,18 @@ void obsffmpeg::nvenc::update(obs_data_t* settings, const AVCodec* codec, AVCode
|
|||||||
}
|
}
|
||||||
|
|
||||||
int la = static_cast<int>(obs_data_get_int(settings, ST_RATECONTROL_LOOKAHEAD));
|
int la = static_cast<int>(obs_data_get_int(settings, ST_RATECONTROL_LOOKAHEAD));
|
||||||
av_opt_set_int(context->priv_data, "lookahead", la, 0);
|
av_opt_set_int(context->priv_data, "rc-lookahead", la, 0);
|
||||||
if (la > 0) {
|
if (la > 0) {
|
||||||
bool adapt_i = obs_data_get_bool(settings, ST_RATECONTROL_ADAPTIVEI);
|
int64_t adapt_i = obs_data_get_int(settings, ST_RATECONTROL_ADAPTIVEI);
|
||||||
av_opt_set_int(context->priv_data, "no-scenecut", !adapt_i ? 1 : 0, 0);
|
if (!is_tristate_default(adapt_i)) {
|
||||||
|
av_opt_set_int(context->priv_data, "no-scenecut", adapt_i, AV_OPT_SEARCH_CHILDREN);
|
||||||
|
}
|
||||||
|
|
||||||
if (strcmp(codec->name, "h264_nvenc")) {
|
if (strcmp(codec->name, "h264_nvenc")) {
|
||||||
bool adapt_b = obs_data_get_bool(settings, ST_RATECONTROL_ADAPTIVEB);
|
int64_t adapt_b = obs_data_get_int(settings, ST_RATECONTROL_ADAPTIVEB);
|
||||||
av_opt_set_int(context->priv_data, "b_adapt", adapt_b ? 1 : 0, 0);
|
if (!is_tristate_default(adapt_b)) {
|
||||||
|
av_opt_set_int(context->priv_data, "b_adapt", adapt_b, AV_OPT_SEARCH_CHILDREN);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -597,44 +633,49 @@ void obsffmpeg::nvenc::update(obs_data_t* settings, const AVCodec* codec, AVCode
|
|||||||
av_opt_set_int(context->priv_data, "init_qpI",
|
av_opt_set_int(context->priv_data, "init_qpI",
|
||||||
obs_data_get_int(settings, ST_RATECONTROL_QP_I_INITIAL), 0);
|
obs_data_get_int(settings, ST_RATECONTROL_QP_I_INITIAL), 0);
|
||||||
av_opt_set_int(context->priv_data, "init_qpP",
|
av_opt_set_int(context->priv_data, "init_qpP",
|
||||||
obs_data_get_int(settings, ST_RATECONTROL_QP_ST_INITIAL), 0);
|
obs_data_get_int(settings, ST_RATECONTROL_QP_P_INITIAL), 0);
|
||||||
av_opt_set_int(context->priv_data, "init_qpB",
|
av_opt_set_int(context->priv_data, "init_qpB",
|
||||||
obs_data_get_int(settings, ST_RATECONTROL_QP_B_INITIAL), 0);
|
obs_data_get_int(settings, ST_RATECONTROL_QP_B_INITIAL), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // AQ
|
{ // AQ
|
||||||
bool saq = obs_data_get_bool(settings, ST_AQ_SPATIAL);
|
int64_t saq = obs_data_get_int(settings, ST_AQ_SPATIAL);
|
||||||
bool taq = obs_data_get_bool(settings, ST_AQ_TEMPORAL);
|
int64_t taq = obs_data_get_int(settings, ST_AQ_TEMPORAL);
|
||||||
|
|
||||||
if (strcmp(codec->name, "h264_nvenc")) {
|
if (strcmp(codec->name, "h264_nvenc") == 0) {
|
||||||
av_opt_set_int(context->priv_data, "spatial-aq", saq ? 1 : 0, 0);
|
if (!is_tristate_default(saq))
|
||||||
av_opt_set_int(context->priv_data, "temporal-aq", taq ? 1 : 0, 0);
|
av_opt_set_int(context->priv_data, "spatial-aq", saq, 0);
|
||||||
|
if (!is_tristate_default(taq))
|
||||||
|
av_opt_set_int(context->priv_data, "temporal-aq", taq, 0);
|
||||||
} else {
|
} else {
|
||||||
av_opt_set_int(context->priv_data, "spatial_aq", saq ? 1 : 0, 0);
|
if (!is_tristate_default(saq))
|
||||||
av_opt_set_int(context->priv_data, "temporal_aq", taq ? 1 : 0, 0);
|
av_opt_set_int(context->priv_data, "spatial_aq", saq, 0);
|
||||||
|
if (!is_tristate_default(taq))
|
||||||
|
av_opt_set_int(context->priv_data, "temporal_aq", taq, 0);
|
||||||
}
|
}
|
||||||
if (saq) {
|
if (is_tristate_enabled(saq))
|
||||||
av_opt_set_int(context->priv_data, "aq-strength",
|
av_opt_set_int(context->priv_data, "aq-strength",
|
||||||
static_cast<int>(obs_data_get_int(settings, ST_AQ_STRENGTH)), 0);
|
static_cast<int>(obs_data_get_int(settings, ST_AQ_STRENGTH)), 0);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
{ // Other
|
{ // Other
|
||||||
bool zl = obs_data_get_bool(settings, ST_OTHER_ZEROLATENCY);
|
int64_t zl = obs_data_get_int(settings, ST_OTHER_ZEROLATENCY);
|
||||||
bool wp = obs_data_get_bool(settings, ST_OTHER_WEIGHTED_PREDICTION);
|
int64_t wp = obs_data_get_int(settings, ST_OTHER_WEIGHTED_PREDICTION);
|
||||||
bool nrp = obs_data_get_bool(settings, ST_OTHER_NONREFERENCE_PFRAMES);
|
int64_t nrp = obs_data_get_int(settings, ST_OTHER_NONREFERENCE_PFRAMES);
|
||||||
|
|
||||||
context->max_b_frames = static_cast<int>(obs_data_get_int(settings, ST_OTHER_BFRAMES));
|
context->max_b_frames = static_cast<int>(obs_data_get_int(settings, ST_OTHER_BFRAMES));
|
||||||
|
|
||||||
av_opt_set_int(context->priv_data, "zerolatency", zl ? 1 : 0, 0);
|
if (!is_tristate_default(zl))
|
||||||
av_opt_set_int(context->priv_data, "nonref_p", nrp ? 1 : 0, 0);
|
av_opt_set_int(context->priv_data, "zerolatency", zl, 0);
|
||||||
|
if (!is_tristate_default(nrp))
|
||||||
|
av_opt_set_int(context->priv_data, "nonref_p", nrp, 0);
|
||||||
|
|
||||||
if ((context->max_b_frames != 0) && wp) {
|
if ((context->max_b_frames != 0) && is_tristate_enabled(wp)) {
|
||||||
PLOG_WARNING(
|
PLOG_WARNING("[%s] Weighted Prediction disabled because of B-Frames being used.", codec->name);
|
||||||
"Automatically disabled weighted prediction due to being incompatible with B-Frames.");
|
av_opt_set_int(context->priv_data, "weighted_pred", 0, 0);
|
||||||
} else {
|
} else if (!is_tristate_default(wp)) {
|
||||||
av_opt_set_int(context->priv_data, "weighted_pred", wp ? 1 : 0, 0);
|
av_opt_set_int(context->priv_data, "weighted_pred", wp, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -646,3 +687,74 @@ void obsffmpeg::nvenc::update(obs_data_t* settings, const AVCodec* codec, AVCode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void obsffmpeg::nvenc::log_options(obs_data_t*, const AVCodec* codec, AVCodecContext* context)
|
||||||
|
{
|
||||||
|
PLOG_INFO("[%s] Nvidia NVENC:", codec->name);
|
||||||
|
ffmpeg::tools::print_av_option_string(context, "preset", " Preset", [](int64_t v) {
|
||||||
|
preset val = static_cast<preset>(v);
|
||||||
|
std::string name = "<Unknown>";
|
||||||
|
auto index = preset_to_opt.find(val);
|
||||||
|
if (index != preset_to_opt.end())
|
||||||
|
name = index->second;
|
||||||
|
return name;
|
||||||
|
});
|
||||||
|
ffmpeg::tools::print_av_option_string(context, "rc", " Rate Control", [](int64_t v) {
|
||||||
|
ratecontrolmode val = static_cast<ratecontrolmode>(v);
|
||||||
|
std::string name = "<Unknown>";
|
||||||
|
auto index = ratecontrolmode_to_opt.find(val);
|
||||||
|
if (index != ratecontrolmode_to_opt.end())
|
||||||
|
name = index->second;
|
||||||
|
return name;
|
||||||
|
});
|
||||||
|
ffmpeg::tools::print_av_option_bool(context, "2pass", " Two Pass");
|
||||||
|
ffmpeg::tools::print_av_option_int(context, "rc-lookahead", " Look-Ahead", "Frames");
|
||||||
|
ffmpeg::tools::print_av_option_bool(context, "no-scenecut", " Adaptive I-Frames");
|
||||||
|
if (strcmp(codec->name, "h264_nvenc") == 0)
|
||||||
|
ffmpeg::tools::print_av_option_bool(context, "b_adapt", " Adaptive B-Frames");
|
||||||
|
|
||||||
|
PLOG_INFO("[%s] Bitrate:", codec->name);
|
||||||
|
ffmpeg::tools::print_av_option_int(context, "bitrate", " Target", "bits/sec");
|
||||||
|
ffmpeg::tools::print_av_option_int(context, "rc_max_rate", " Maximum", "bits/sec");
|
||||||
|
ffmpeg::tools::print_av_option_int(context, "rc_buffer_size", " Buffer", "bits");
|
||||||
|
PLOG_INFO("[%s] Quality:", codec->name);
|
||||||
|
ffmpeg::tools::print_av_option_int(context, "qmin", " Minimum", "");
|
||||||
|
ffmpeg::tools::print_av_option_int(context, "cq", " Target", "");
|
||||||
|
ffmpeg::tools::print_av_option_int(context, "qmax", " Maximum", "");
|
||||||
|
PLOG_INFO("[%s] Quantization Parameters:", codec->name);
|
||||||
|
ffmpeg::tools::print_av_option_int(context, "init_qpI", " I-Frame", "");
|
||||||
|
ffmpeg::tools::print_av_option_int(context, "init_qpP", " P-Frame", "");
|
||||||
|
ffmpeg::tools::print_av_option_int(context, "init_qpB", " B-Frame", "");
|
||||||
|
|
||||||
|
ffmpeg::tools::print_av_option_int(context, "max_b_frames", " B-Frames", "Frames");
|
||||||
|
ffmpeg::tools::print_av_option_string(context, "b_ref_mode", " Reference Mode", [](int64_t v) {
|
||||||
|
b_ref_mode val = static_cast<b_ref_mode>(v);
|
||||||
|
std::string name = "<Unknown>";
|
||||||
|
auto index = b_ref_mode_to_opt.find(val);
|
||||||
|
if (index != b_ref_mode_to_opt.end())
|
||||||
|
name = index->second;
|
||||||
|
return name;
|
||||||
|
});
|
||||||
|
|
||||||
|
PLOG_INFO("[%s] Adaptive Quantization:", codec->name);
|
||||||
|
if (strcmp(codec->name, "h264_nvenc") == 0) {
|
||||||
|
ffmpeg::tools::print_av_option_bool(context, "spatial-aq", " Spatial AQ");
|
||||||
|
ffmpeg::tools::print_av_option_int(context, "aq-strength", " Strength", "");
|
||||||
|
ffmpeg::tools::print_av_option_bool(context, "temporal-aq", " Temporal AQ");
|
||||||
|
} else {
|
||||||
|
ffmpeg::tools::print_av_option_bool(context, "spatial_aq", " Spatial AQ");
|
||||||
|
ffmpeg::tools::print_av_option_int(context, "aq-strength", " Strength", "");
|
||||||
|
ffmpeg::tools::print_av_option_bool(context, "temporal_aq", " Temporal AQ");
|
||||||
|
}
|
||||||
|
|
||||||
|
PLOG_INFO("[%s] Other:", codec->name);
|
||||||
|
ffmpeg::tools::print_av_option_bool(context, "zerolatency", " Zero Latency");
|
||||||
|
ffmpeg::tools::print_av_option_bool(context, "weighted_pred", " Weighted Prediction");
|
||||||
|
ffmpeg::tools::print_av_option_bool(context, "nonref_p", " Non-reference P-Frames");
|
||||||
|
ffmpeg::tools::print_av_option_bool(context, "strict_gop", " Strict GOP");
|
||||||
|
ffmpeg::tools::print_av_option_bool(context, "aud", " Access Unit Delimiters");
|
||||||
|
ffmpeg::tools::print_av_option_bool(context, "bluray-compat", " Bluray Compatibility");
|
||||||
|
if (strcmp(codec->name, "h264_nvenc") == 0)
|
||||||
|
ffmpeg::tools::print_av_option_bool(context, "a53cc", " A53 Closed Captions");
|
||||||
|
ffmpeg::tools::print_av_option_int(context, "dpb_size", " DPB Size", "");
|
||||||
|
}
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ extern "C" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace obsffmpeg {
|
namespace obsffmpeg {
|
||||||
|
class encoder;
|
||||||
|
|
||||||
namespace nvenc {
|
namespace nvenc {
|
||||||
enum class preset : int64_t {
|
enum class preset : int64_t {
|
||||||
DEFAULT,
|
DEFAULT,
|
||||||
@@ -46,6 +48,8 @@ namespace obsffmpeg {
|
|||||||
LOW_LATENCY_HIGH_QUALITY,
|
LOW_LATENCY_HIGH_QUALITY,
|
||||||
LOSSLESS,
|
LOSSLESS,
|
||||||
LOSSLESS_HIGH_PERFORMANCE,
|
LOSSLESS_HIGH_PERFORMANCE,
|
||||||
|
// Append things before this.
|
||||||
|
INVALID = -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ratecontrolmode : int64_t {
|
enum class ratecontrolmode : int64_t {
|
||||||
@@ -55,12 +59,16 @@ namespace obsffmpeg {
|
|||||||
CBR,
|
CBR,
|
||||||
CBR_HQ,
|
CBR_HQ,
|
||||||
CBR_LD_HQ,
|
CBR_LD_HQ,
|
||||||
|
// Append things before this.
|
||||||
|
INVALID = -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class b_ref_mode : int64_t {
|
enum class b_ref_mode : int64_t {
|
||||||
DISABLED,
|
DISABLED,
|
||||||
EACH,
|
EACH,
|
||||||
MIDDLE,
|
MIDDLE,
|
||||||
|
// Append things before this.
|
||||||
|
INVALID = -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
extern std::map<preset, std::string> presets;
|
extern std::map<preset, std::string> presets;
|
||||||
@@ -75,6 +83,8 @@ namespace obsffmpeg {
|
|||||||
|
|
||||||
extern std::map<b_ref_mode, std::string> b_ref_mode_to_opt;
|
extern std::map<b_ref_mode, std::string> b_ref_mode_to_opt;
|
||||||
|
|
||||||
|
void override_update(obsffmpeg::encoder* instance, obs_data_t* settings);
|
||||||
|
|
||||||
void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||||
|
|
||||||
void get_properties_pre(obs_properties_t* props, const AVCodec* codec);
|
void get_properties_pre(obs_properties_t* props, const AVCodec* codec);
|
||||||
@@ -84,5 +94,7 @@ namespace obsffmpeg {
|
|||||||
void get_runtime_properties(obs_properties_t* props, const AVCodec* codec, AVCodecContext* context);
|
void get_runtime_properties(obs_properties_t* props, const AVCodec* codec, AVCodecContext* context);
|
||||||
|
|
||||||
void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||||
|
|
||||||
|
void log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||||
} // namespace nvenc
|
} // namespace nvenc
|
||||||
} // namespace obsffmpeg
|
} // namespace obsffmpeg
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
|
|
||||||
#include "prores_aw_handler.hpp"
|
#include "prores_aw_handler.hpp"
|
||||||
|
#include "codecs/prores.hpp"
|
||||||
#include "plugin.hpp"
|
#include "plugin.hpp"
|
||||||
#include "utility.hpp"
|
#include "utility.hpp"
|
||||||
|
|
||||||
@@ -27,13 +28,6 @@ extern "C" {
|
|||||||
#include <obs-module.h>
|
#include <obs-module.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
#define P_PROFILE "AppleProRes.Profile"
|
|
||||||
#define P_PROFILE_APCO "AppleProRes.Profile.APCO"
|
|
||||||
#define P_PROFILE_APCS "AppleProRes.Profile.APCS"
|
|
||||||
#define P_PROFILE_APCN "AppleProRes.Profile.APCN"
|
|
||||||
#define P_PROFILE_APCH "AppleProRes.Profile.APCH"
|
|
||||||
#define P_PROFILE_AP4H "AppleProRes.Profile.AP4H"
|
|
||||||
|
|
||||||
INITIALIZER(prores_aw_handler_init)
|
INITIALIZER(prores_aw_handler_init)
|
||||||
{
|
{
|
||||||
obsffmpeg::initializers.push_back([]() {
|
obsffmpeg::initializers.push_back([]() {
|
||||||
@@ -41,44 +35,94 @@ INITIALIZER(prores_aw_handler_init)
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
void obsffmpeg::ui::prores_aw_handler::get_defaults(obs_data_t* settings, const AVCodec*, AVCodecContext*)
|
void obsffmpeg::ui::prores_aw_handler::override_colorformat(AVPixelFormat& target_format, obs_data_t* settings,
|
||||||
|
const AVCodec* codec, AVCodecContext*)
|
||||||
{
|
{
|
||||||
obs_data_set_default_int(settings, P_PROFILE, 0);
|
std::string profile = "";
|
||||||
|
|
||||||
|
int profile_id = static_cast<int>(obs_data_get_int(settings, P_PRORES_PROFILE));
|
||||||
|
for (auto ptr = codec->profiles; ptr->profile != FF_PROFILE_UNKNOWN; ptr++) {
|
||||||
|
if (ptr->profile == profile_id) {
|
||||||
|
profile = ptr->name;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_map<AVPixelFormat, std::list<std::string>> valid_formats = {
|
||||||
|
{AV_PIX_FMT_YUV422P10, {"apco", "apcs", "apcn", "apch"}}, {AV_PIX_FMT_YUV444P10, {"ap4h", "ap4x"}}};
|
||||||
|
|
||||||
|
for (auto kv : valid_formats) {
|
||||||
|
for (auto name : kv.second) {
|
||||||
|
if (profile == name) {
|
||||||
|
target_format = kv.first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::prores_aw_handler::get_defaults(obs_data_t* settings, const AVCodec*, AVCodecContext*, bool)
|
||||||
|
{
|
||||||
|
obs_data_set_default_int(settings, P_PRORES_PROFILE, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void obsffmpeg::ui::prores_aw_handler::get_properties(obs_properties_t* props, const AVCodec* codec,
|
void obsffmpeg::ui::prores_aw_handler::get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||||
AVCodecContext* context)
|
AVCodecContext* context, bool)
|
||||||
{
|
{
|
||||||
if (!context) {
|
if (!context) {
|
||||||
auto p = obs_properties_add_list(props, P_PROFILE, TRANSLATE(P_PROFILE), OBS_COMBO_TYPE_LIST,
|
auto p = obs_properties_add_list(props, P_PRORES_PROFILE, TRANSLATE(P_PRORES_PROFILE),
|
||||||
OBS_COMBO_FORMAT_INT);
|
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_PROFILE)));
|
obs_property_set_long_description(p, TRANSLATE(DESC(P_PRORES_PROFILE)));
|
||||||
for (auto ptr = codec->profiles; ptr->profile != FF_PROFILE_UNKNOWN; ptr++) {
|
for (auto ptr = codec->profiles; ptr->profile != FF_PROFILE_UNKNOWN; ptr++) {
|
||||||
if (strcmp("apco", ptr->name) == 0) {
|
if (strcmp("apco", ptr->name) == 0) {
|
||||||
obs_property_list_add_int(p, TRANSLATE(P_PROFILE_APCO),
|
obs_property_list_add_int(p, TRANSLATE(P_PRORES_PROFILE_APCO),
|
||||||
static_cast<int64_t>(ptr->profile));
|
static_cast<int64_t>(ptr->profile));
|
||||||
} else if (strcmp("apcs", ptr->name) == 0) {
|
} else if (strcmp("apcs", ptr->name) == 0) {
|
||||||
obs_property_list_add_int(p, TRANSLATE(P_PROFILE_APCS),
|
obs_property_list_add_int(p, TRANSLATE(P_PRORES_PROFILE_APCS),
|
||||||
static_cast<int64_t>(ptr->profile));
|
static_cast<int64_t>(ptr->profile));
|
||||||
} else if (strcmp("apcn", ptr->name) == 0) {
|
} else if (strcmp("apcn", ptr->name) == 0) {
|
||||||
obs_property_list_add_int(p, TRANSLATE(P_PROFILE_APCN),
|
obs_property_list_add_int(p, TRANSLATE(P_PRORES_PROFILE_APCN),
|
||||||
static_cast<int64_t>(ptr->profile));
|
static_cast<int64_t>(ptr->profile));
|
||||||
} else if (strcmp("apch", ptr->name) == 0) {
|
} else if (strcmp("apch", ptr->name) == 0) {
|
||||||
obs_property_list_add_int(p, TRANSLATE(P_PROFILE_APCH),
|
obs_property_list_add_int(p, TRANSLATE(P_PRORES_PROFILE_APCH),
|
||||||
static_cast<int64_t>(ptr->profile));
|
static_cast<int64_t>(ptr->profile));
|
||||||
} else if (strcmp("ap4h", ptr->name) == 0) {
|
} else if (strcmp("ap4h", ptr->name) == 0) {
|
||||||
obs_property_list_add_int(p, TRANSLATE(P_PROFILE_AP4H),
|
obs_property_list_add_int(p, TRANSLATE(P_PRORES_PROFILE_AP4H),
|
||||||
|
static_cast<int64_t>(ptr->profile));
|
||||||
|
} else if (strcmp("ap4x", ptr->name) == 0) {
|
||||||
|
obs_property_list_add_int(p, TRANSLATE(P_PRORES_PROFILE_AP4X),
|
||||||
static_cast<int64_t>(ptr->profile));
|
static_cast<int64_t>(ptr->profile));
|
||||||
} else {
|
} else {
|
||||||
obs_property_list_add_int(p, ptr->name, ptr->profile);
|
obs_property_list_add_int(p, ptr->name, ptr->profile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
obs_property_set_enabled(obs_properties_get(props, P_PROFILE), false);
|
obs_property_set_enabled(obs_properties_get(props, P_PRORES_PROFILE), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void obsffmpeg::ui::prores_aw_handler::update(obs_data_t* settings, const AVCodec*, AVCodecContext* context)
|
void obsffmpeg::ui::prores_aw_handler::update(obs_data_t* settings, const AVCodec*, AVCodecContext* context)
|
||||||
{
|
{
|
||||||
context->profile = static_cast<int>(obs_data_get_int(settings, P_PROFILE));
|
context->profile = static_cast<int>(obs_data_get_int(settings, P_PRORES_PROFILE));
|
||||||
|
}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::prores_aw_handler::log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext*)
|
||||||
|
{
|
||||||
|
for (auto ptr = codec->profiles; ptr->profile != FF_PROFILE_UNKNOWN; ptr++) {
|
||||||
|
if (ptr->profile == static_cast<int>(obs_data_get_int(settings, P_PRORES_PROFILE)))
|
||||||
|
PLOG_INFO("[%s] Profile: %s", codec->name, ptr->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::prores_aw_handler::process_avpacket(AVPacket& packet, const AVCodec*, AVCodecContext*)
|
||||||
|
{
|
||||||
|
//FFmpeg Bug:
|
||||||
|
// When ProRes content is stored in Matroska, FFmpeg strips the size
|
||||||
|
// from the atom. Later when the ProRes content is demuxed from Matroska,
|
||||||
|
// FFmpeg creates an atom with the incorrect size, as the ATOM size
|
||||||
|
// should be content + atom, but FFmpeg set it to only be content. This
|
||||||
|
// difference leads to decoders to be off by 8 bytes.
|
||||||
|
//Fix (until FFmpeg stops being broken):
|
||||||
|
// Pad the packet with 8 bytes of 0x00.
|
||||||
|
|
||||||
|
av_grow_packet(&packet, 8);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,14 +34,23 @@ namespace obsffmpeg {
|
|||||||
namespace ui {
|
namespace ui {
|
||||||
class prores_aw_handler : public handler {
|
class prores_aw_handler : public handler {
|
||||||
public:
|
public:
|
||||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec,
|
virtual void override_colorformat(AVPixelFormat& target_format, obs_data_t* settings,
|
||||||
AVCodecContext* context) override;
|
const AVCodec* codec, AVCodecContext* context) override;
|
||||||
|
|
||||||
|
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context,
|
||||||
|
bool hw_encode) override;
|
||||||
|
|
||||||
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||||
AVCodecContext* context) override;
|
AVCodecContext* context, bool hw_encode) override;
|
||||||
|
|
||||||
virtual void update(obs_data_t* settings, const AVCodec* codec,
|
virtual void update(obs_data_t* settings, const AVCodec* codec,
|
||||||
AVCodecContext* context) override;
|
AVCodecContext* context) override;
|
||||||
|
|
||||||
|
virtual void log_options(obs_data_t* settings, const AVCodec* codec,
|
||||||
|
AVCodecContext* context) override;
|
||||||
|
|
||||||
|
virtual void process_avpacket(AVPacket& packet, const AVCodec* codec,
|
||||||
|
AVCodecContext* context) override;
|
||||||
};
|
};
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
} // namespace obsffmpeg
|
} // namespace obsffmpeg
|
||||||
|
|||||||
@@ -18,3 +18,16 @@
|
|||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#include "utility.hpp"
|
||||||
|
#include "strings.hpp"
|
||||||
|
#include "plugin.hpp"
|
||||||
|
|
||||||
|
obs_property_t* obsffmpeg::obs_properties_add_tristate(obs_properties_t* props, const char* name, const char* desc)
|
||||||
|
{
|
||||||
|
obs_property_t* p = obs_properties_add_list(props, name, desc, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||||
|
obs_property_list_add_int(p, TRANSLATE(S_STATE_DEFAULT), -1);
|
||||||
|
obs_property_list_add_int(p, TRANSLATE(S_STATE_DISABLED), 0);
|
||||||
|
obs_property_list_add_int(p, TRANSLATE(S_STATE_ENABLED), 1);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|||||||
@@ -87,4 +87,29 @@ namespace obsffmpeg {
|
|||||||
{
|
{
|
||||||
return obs_get_version() < MAKE_SEMANTIC_VERSION(24, 0, 0);
|
return obs_get_version() < MAKE_SEMANTIC_VERSION(24, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct obs_graphics {
|
||||||
|
obs_graphics()
|
||||||
|
{
|
||||||
|
obs_enter_graphics();
|
||||||
|
}
|
||||||
|
~obs_graphics()
|
||||||
|
{
|
||||||
|
obs_leave_graphics();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
obs_property_t* obs_properties_add_tristate(obs_properties_t* props, const char* name, const char* desc);
|
||||||
|
|
||||||
|
inline bool is_tristate_enabled(int64_t tristate) {
|
||||||
|
return tristate == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_tristate_disabled(int64_t tristate) {
|
||||||
|
return tristate == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_tristate_default(int64_t tristate) {
|
||||||
|
return tristate == -1;
|
||||||
|
}
|
||||||
} // namespace obsffmpeg
|
} // namespace obsffmpeg
|
||||||
|
|||||||
Reference in New Issue
Block a user