Compare commits
45 Commits
0.1.0
...
libvpx-vp9
| Author | SHA1 | Date | |
|---|---|---|---|
| 51223666e9 | |||
| 090dd9dbdc | |||
| d145c672ad | |||
| b7fdd5491a | |||
| cdb03a488e | |||
| bab3bb8853 | |||
| c8470d3b23 | |||
| c7f326d428 | |||
| 0aab0db34a | |||
| 8dbcaf192a | |||
| 830ebf31d9 | |||
| 076df05b37 | |||
| 3bbbea7b42 | |||
| 97a53d261f | |||
| e45a82350e | |||
| b6ad026eb3 | |||
| df63677529 | |||
| d1ff4a7adc | |||
| e5bf18f2a6 | |||
| b442914ebe | |||
| 8fb69d6870 | |||
| dbdf915a07 | |||
| 9ce8f39efc | |||
| 924c64cd37 | |||
| 99003d5193 | |||
| c5d81e41a6 | |||
| 2f0cd790ed | |||
| c5ff5d224d | |||
| 89fd38e604 | |||
| 56807370a9 | |||
| e7c12a52f3 | |||
| 976e5dba4f | |||
| 16015f180b | |||
| 042c934516 | |||
| 53651c29be | |||
| 5b05cc1504 | |||
| 0a20b750b8 | |||
| 9a58a6a5ff | |||
| 143b7f585f | |||
| 93c6f0b35a | |||
| 3154443e18 | |||
| 74fb3a3847 | |||
| 2d131b8566 | |||
| 35db9e714a | |||
| 01e1c70a02 |
+1
-1
@@ -67,7 +67,7 @@ BreakBeforeBraces: Custom
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
#BreakInheritanceList: BeforeColon
|
||||
BreakStringLiterals: true
|
||||
BreakStringLiterals: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
Cpp11BracedListStyle: true
|
||||
|
||||
|
||||
+92
-67
@@ -1,5 +1,5 @@
|
||||
# A Plugin that integrates the AMD AMF encoder into OBS Studio
|
||||
# Copyright (C) 2016 - 2017 Michael Fabian Dirks
|
||||
# Integrates most FFmpeg supported encoders into OBS Studio
|
||||
# Copyright (C) 2018 - 2019 Michael Fabian Dirks
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -16,8 +16,8 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
# CMake Setup
|
||||
cmake_minimum_required(VERSION 3.1.0)
|
||||
include("cmake/util.cmake")
|
||||
CMake_Minimum_Required(VERSION 3.8.0)
|
||||
Include("cmake/util.cmake")
|
||||
|
||||
# Automatic Versioning
|
||||
set(VERSION_MAJOR 0)
|
||||
@@ -50,15 +50,37 @@ if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/.git")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# All Warnings, Extra Warnings, Pedantic
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
# using Clang
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-missing-braces -Wmissing-field-initializers -Wno-c++98-compat-pedantic -Wold-style-cast -Wno-documentation -Wno-documentation-unknown-command -Wno-covered-switch-default -Wno-switch-enum")
|
||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
# GCC: -fpermissive is required as GCC does not allow the same template to be in different namespaces.
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wpedantic -fpermissive -Wno-long-long -Wno-missing-braces -Wmissing-field-initializers")
|
||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")
|
||||
# using Intel C++
|
||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||
# Force to always compile with W4
|
||||
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
|
||||
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
|
||||
endif()
|
||||
endif()
|
||||
# C++ Standard and Extensions
|
||||
## Use C++17 and no non-standard extensions.
|
||||
set(_CXX_STANDARD 17)
|
||||
set(_CXX_EXTENSIONS OFF)
|
||||
|
||||
# Define Project
|
||||
project(
|
||||
enc-ffmpeg
|
||||
obs-ffmpeg-encoder
|
||||
VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}.${VERSION_TWEAK}
|
||||
)
|
||||
set(PROJECT_FULL_NAME "FFMPEG Encoder for OBS Studio")
|
||||
set(PROJECT_DESCRIPTION "Plugin for OBS Studio to add FFMPEG options for Recording and Streaming")
|
||||
set(PROJECT_DESCRIPTION "FFmpeg Encoders for OBS Studio")
|
||||
set(PROJECT_AUTHORS "Michael Fabian 'Xaymar' Dirks <info@xaymar.com>")
|
||||
set(PROJECT_COPYRIGHT_YEARS "2018")
|
||||
set(PROJECT_COPYRIGHT_YEARS "2018 - 2019")
|
||||
|
||||
################################################################################
|
||||
# Setup / Bootstrap
|
||||
@@ -97,14 +119,14 @@ configure_file(
|
||||
)
|
||||
|
||||
# Windows
|
||||
## Installer (InnoSetup)
|
||||
Configure_File(
|
||||
"${PROJECT_SOURCE_DIR}/cmake/installer.iss.in"
|
||||
"${PROJECT_BINARY_DIR}/installer.iss"
|
||||
)
|
||||
if (WIN32)
|
||||
## Installer (InnoSetup)
|
||||
configure_file(
|
||||
"${PROJECT_SOURCE_DIR}/cmake/installer.iss.in"
|
||||
"${PROJECT_BINARY_DIR}/installer.iss"
|
||||
)
|
||||
|
||||
# Windows Specific Resource Definition
|
||||
if(WIN32)
|
||||
# Windows Specific Resource Definition
|
||||
set(PROJECT_PRODUCT_NAME "${PROJECT_FULL_NAME}")
|
||||
set(PROJECT_COMPANY_NAME "${PROJECT_AUTHORS}")
|
||||
set(PROJECT_COPYRIGHT "${PROJECT_AUTHORS} © ${PROJECT_COPYRIGHT_YEARS}")
|
||||
@@ -118,19 +140,6 @@ if(WIN32)
|
||||
)
|
||||
endif()
|
||||
|
||||
# All Warnings, Extra Warnings, Pedantic
|
||||
if(MSVC)
|
||||
# Force to always compile with W4
|
||||
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
|
||||
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
|
||||
endif()
|
||||
elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
# Update if necessary
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic")
|
||||
endif()
|
||||
|
||||
################################################################################
|
||||
# Options
|
||||
################################################################################
|
||||
@@ -142,7 +151,7 @@ mark_as_advanced(FORCE OBS_NATIVE OBS_PACKAGE OBS_REFERENCE OBS_DOWNLOAD)
|
||||
|
||||
if(NOT TARGET libobs)
|
||||
set(${PropertyPrefix}OBS_STUDIO_DIR "" CACHE PATH "OBS Studio Source/Package Directory")
|
||||
set(${PropertyPrefix}OBS_DOWNLOAD_VERSION "22.0.2" CACHE STRING "OBS Studio Version to download")
|
||||
set(${PropertyPrefix}OBS_DOWNLOAD_VERSION "23.2.1-ci" CACHE STRING "OBS Studio Version to download")
|
||||
endif()
|
||||
|
||||
if(NOT ${PropertyPrefix}OBS_NATIVE)
|
||||
@@ -171,7 +180,7 @@ else()
|
||||
else()
|
||||
message(STATUS "${PROJECT_NAME}: No OBS Studio detected, using downloadable prebuilt binaries.")
|
||||
CacheSet(${PropertyPrefix}OBS_DOWNLOAD TRUE)
|
||||
set(${PropertyPrefix}OBS_DOWNLOAD_URL "https://github.com/Xaymar/obs-studio/releases/download/${OBS_DOWNLOAD_VERSION}/obs-studio-${ARCH}-vs2017.7z")
|
||||
set(${PropertyPrefix}OBS_DOWNLOAD_URL "https://github.com/Xaymar/obs-studio/releases/download/${OBS_DOWNLOAD_VERSION}/obs-studio-${ARCH}-0.0.0.0-vs2017.7z")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -179,17 +188,9 @@ endif()
|
||||
if(${PropertyPrefix}OBS_DOWNLOAD)
|
||||
include("cmake/DownloadProject.cmake")
|
||||
endif()
|
||||
if(NOT ${PropertyPrefix}OBS_NATIVE)
|
||||
include("cmake/cppcheck.cmake")
|
||||
endif()
|
||||
|
||||
# Load OBS Studio
|
||||
if(${PropertyPrefix}OBS_NATIVE)
|
||||
option(BUILD_FFMPEG_ENCODER "Build AMD Encoder module" ON)
|
||||
if (NOT BUILD_FFMPEG_ENCODER)
|
||||
message(STATUS "Not building AMD Encoder")
|
||||
return()
|
||||
endif()
|
||||
elseif(${PropertyPrefix}OBS_PACKAGE)
|
||||
include("${OBS_STUDIO_DIR}/cmake/LibObs/LibObsConfig.cmake")
|
||||
elseif(${PropertyPrefix}OBS_REFERENCE)
|
||||
@@ -203,7 +204,7 @@ elseif(${PropertyPrefix}OBS_DOWNLOAD)
|
||||
)
|
||||
include("${libobs_SOURCE_DIR}/cmake/LibObs/LibObsConfig.cmake")
|
||||
else()
|
||||
message(CRITICAL "Impossible case reached, very system stability.")
|
||||
message(CRITICAL "Impossible case reached, verify system stability.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
@@ -232,6 +233,29 @@ find_package(FFmpeg REQUIRED COMPONENTS avutil avcodec swscale)
|
||||
################################################################################
|
||||
# Code
|
||||
################################################################################
|
||||
set(PROJECT_DATA
|
||||
"${PROJECT_SOURCE_DIR}/data/locale/en-US.ini"
|
||||
"${PROJECT_SOURCE_DIR}/LICENSE"
|
||||
)
|
||||
|
||||
set(PROJECT_LIBRARIES
|
||||
)
|
||||
|
||||
set(PROJECT_TEMPLATES
|
||||
"${PROJECT_SOURCE_DIR}/cmake/version.hpp.in"
|
||||
"${PROJECT_SOURCE_DIR}/cmake/module.cpp.in"
|
||||
)
|
||||
if(WIN32)
|
||||
list(APPEND PROJECT_TEMPLATES
|
||||
"${PROJECT_SOURCE_DIR}/cmake/installer.iss.in"
|
||||
"${PROJECT_SOURCE_DIR}/cmake/version.rc.in"
|
||||
)
|
||||
endif()
|
||||
|
||||
set(PROJECT_GENERATED
|
||||
"${PROJECT_BINARY_DIR}/source/module.cpp"
|
||||
"${PROJECT_BINARY_DIR}/source/version.hpp"
|
||||
)
|
||||
set(PROJECT_PRIVATE
|
||||
"${PROJECT_SOURCE_DIR}/source/encoder.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/encoder.hpp"
|
||||
@@ -239,6 +263,9 @@ set(PROJECT_PRIVATE
|
||||
"${PROJECT_SOURCE_DIR}/source/plugin.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/utility.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/utility.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/strings.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/encoders/generic.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/encoders/generic.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/encoders/prores_aw.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/encoders/prores_aw.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/ffmpeg/avframe-queue.cpp"
|
||||
@@ -247,22 +274,30 @@ set(PROJECT_PRIVATE
|
||||
"${PROJECT_SOURCE_DIR}/source/ffmpeg/swscale.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/ffmpeg/tools.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/ffmpeg/tools.cpp"
|
||||
)
|
||||
SET(PROJECT_GENERATED
|
||||
"${PROJECT_BINARY_DIR}/source/module.cpp"
|
||||
"${PROJECT_BINARY_DIR}/source/version.hpp"
|
||||
)
|
||||
set(PROJECT_DATA
|
||||
"${PROJECT_SOURCE_DIR}/data/locale/en-US.ini"
|
||||
"${PROJECT_SOURCE_DIR}/LICENSE"
|
||||
)
|
||||
set(PROJECT_LIBRARIES
|
||||
"${PROJECT_SOURCE_DIR}/source/ui/handler.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/ui/handler.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/ui/debug_handler.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/ui/debug_handler.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/ui/prores_aw_handler.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/ui/prores_aw_handler.cpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/ui/libvpx_vp9_handler.hpp"
|
||||
"${PROJECT_SOURCE_DIR}/source/ui/libvpx_vp9_handler.cpp"
|
||||
)
|
||||
|
||||
# Source Grouping
|
||||
source_group(TREE "${PROJECT_SOURCE_DIR}" PREFIX "Data Files" FILES ${PROJECT_DATA})
|
||||
source_group(TREE "${PROJECT_SOURCE_DIR}/source" PREFIX "Private Files" FILES ${PROJECT_PRIVATE})
|
||||
source_group(TREE "${PROJECT_BINARY_DIR}/source" PREFIX "Generated Files" FILES ${PROJECT_GENERATED})
|
||||
source_group(TREE "${PROJECT_SOURCE_DIR}/cmake" PREFIX "Template Files" FILES ${PROJECT_TEMPLATES})
|
||||
|
||||
# Filter Sources
|
||||
set(_TMP_SOURCE ${PROJECT_PRIVATE})
|
||||
list(FILTER _TMP_SOURCE INCLUDE REGEX "\.(c|cpp)$")
|
||||
source_group(TREE "${PROJECT_SOURCE_DIR}/source" PREFIX "Source Files" FILES ${_TMP_SOURCE})
|
||||
|
||||
# Filter Headers
|
||||
set(_TMP_HEADER ${PROJECT_PRIVATE})
|
||||
list(FILTER _TMP_HEADER INCLUDE REGEX "\.(h|hpp)$")
|
||||
source_group(TREE "${PROJECT_SOURCE_DIR}/source" PREFIX "Header Files" FILES ${_TMP_HEADER})
|
||||
|
||||
################################################################################
|
||||
# Target
|
||||
@@ -367,6 +402,14 @@ if (WIN32)
|
||||
)
|
||||
endif()
|
||||
|
||||
# C++ Standard and Extensions
|
||||
set_target_properties(
|
||||
${PROJECT_NAME}
|
||||
PROPERTIES
|
||||
CXX_STANDARD ${_CXX_STANDARD}
|
||||
CXX_EXTENSIONS ${_CXX_EXTENSIONS}
|
||||
)
|
||||
|
||||
# File Version
|
||||
if(WIN32)
|
||||
set_target_properties(
|
||||
@@ -384,24 +427,6 @@ else()
|
||||
)
|
||||
endif()
|
||||
|
||||
# CPPCheck
|
||||
if(NOT ${PropertyPrefix}OBS_NATIVE)
|
||||
set(excludes )
|
||||
list(APPEND excludes "${OBS_DEPENDENCIES_DIR}")
|
||||
if(${PropertyPrefix}OBS_REFERENCE)
|
||||
list(APPEND excludes "${OBS_STUDIO_DIR}/libobs")
|
||||
elseif(${PropertyPrefix}OBS_PACKAGE)
|
||||
list(APPEND excludes "${OBS_STUDIO_DIR}/libobs")
|
||||
elseif(${PropertyPrefix}OBS_DOWNLOAD)
|
||||
list(APPEND excludes "${libobs_SOURCE_DIR}")
|
||||
endif()
|
||||
|
||||
cppcheck(
|
||||
EXCLUDE ${excludes}
|
||||
)
|
||||
cppcheck_add_project(${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
################################################################################
|
||||
# Installation
|
||||
################################################################################
|
||||
|
||||
+37
-20
@@ -1,43 +1,49 @@
|
||||
# Generic Settings
|
||||
version: '{build}-{branch}'
|
||||
pull_requests:
|
||||
do_not_increment_build_number: true
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
# Build Image & Environment
|
||||
platform: x64
|
||||
image: Visual Studio 2017
|
||||
environment:
|
||||
CMAKE_SYSTEM_VERSION: 10.0.16299.91
|
||||
PACKAGE_PREFIX: obs-ffmpeg-encoder
|
||||
INNOSETUP_URL: http://www.jrsoftware.org/download.php/is-unicode.exe
|
||||
|
||||
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: ci/appveyor-build.bat
|
||||
- cmd: node ci/appveyor-build.js
|
||||
|
||||
after_build:
|
||||
- cmd: ci/appveyor-package.bat
|
||||
|
||||
cache:
|
||||
- inno.exe
|
||||
- build/32/obsdeps-build
|
||||
- build/32/obsdeps-download
|
||||
- build/32/obsdeps-src
|
||||
- build/32/libobs-build
|
||||
- build/32/libobs-download
|
||||
- build/32/libobs-src
|
||||
- build/64/libobs-build
|
||||
- build/64/libobs-download
|
||||
- build/64/libobs-src
|
||||
# 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:
|
||||
@@ -48,4 +54,15 @@ deploy:
|
||||
on:
|
||||
appveyor_repo_tag: true
|
||||
|
||||
test: off
|
||||
# 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,4 +0,0 @@
|
||||
cmake -H. -B"build/32" -G"Visual Studio 15 2017" -DCMAKE_INSTALL_PREFIX="%CD%/build/distrib" -DCMAKE_PACKAGE_PREFIX="%CD%/build" -DCMAKE_PACKAGE_NAME="obs-ffmpeg-encoder"
|
||||
cmake -H. -B"build/64" -G"Visual Studio 15 2017 Win64" -T"host=x64" -DCMAKE_INSTALL_PREFIX="%CD%/build/distrib" -DCMAKE_PACKAGE_PREFIX="%CD%/build" -DCMAKE_PACKAGE_NAME="obs-ffmpeg-encoder" -DOBS_DEPENDENCIES_DIR="%CD%/build/32/obsdeps-src"
|
||||
cmake --build build/32 --target INSTALL --config RelWithDebInfo -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||
cmake --build build/64 --target INSTALL --config RelWithDebInfo -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||
@@ -0,0 +1,98 @@
|
||||
const cp = require('child_process');
|
||||
|
||||
var config32 = cp.spawn(
|
||||
"cmake", [
|
||||
'-H.',
|
||||
'-B"build/32"',
|
||||
'-G"Visual Studio 15 2017"',
|
||||
'-DCMAKE_INSTALL_PREFIX="build/distrib"',
|
||||
'-DCMAKE_PACKAGE_PREFIX="build"',
|
||||
'-DCMAKE_PACKAGE_NAME="xmr-ffmpeg-encoders"'
|
||||
], {
|
||||
windowsVerbatimArguments: true,
|
||||
windowsHide: true
|
||||
}
|
||||
);
|
||||
config32.stdout.on('data', (data) => {
|
||||
process.stdout.write(`[32:Out] ${data}`);
|
||||
});
|
||||
config32.stderr.on('data', (data) => {
|
||||
console.log(`[32:Err] ${data}`);
|
||||
});
|
||||
config32.on('exit', (code, signal) => {
|
||||
if (code != 0) {
|
||||
process.exit(code)
|
||||
}
|
||||
var build32 = cp.spawn(
|
||||
"cmake", [
|
||||
'--build build/32',
|
||||
'--target INSTALL',
|
||||
'--config RelWithDebInfo',
|
||||
'--',
|
||||
'/logger:"C:\\Program Files\\AppVeyor\\BuildAgent\\Appveyor.MSBuildLogger.dll"'
|
||||
], {
|
||||
windowsVerbatimArguments: true,
|
||||
windowsHide: true
|
||||
}
|
||||
);
|
||||
build32.stdout.on('data', (data) => {
|
||||
process.stdout.write(`[32:Out] ${data}`);
|
||||
});
|
||||
build32.stderr.on('data', (data) => {
|
||||
process.stderr.write(`[32:Err] ${data}`);
|
||||
});
|
||||
build32.on('exit', (code, signal) => {
|
||||
if (code != 0) {
|
||||
process.exit(code)
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var config64 = cp.spawn(
|
||||
"cmake", [
|
||||
'-H.',
|
||||
'-B"build/64"',
|
||||
'-G"Visual Studio 15 2017 Win64"',
|
||||
'-DCMAKE_INSTALL_PREFIX="build/distrib"',
|
||||
'-DCMAKE_PACKAGE_PREFIX="build"',
|
||||
'-DCMAKE_PACKAGE_NAME="xmr-ffmpeg-encoders"'
|
||||
], {
|
||||
windowsVerbatimArguments: true,
|
||||
windowsHide: true
|
||||
}
|
||||
);
|
||||
config64.stdout.on('data', (data) => {
|
||||
process.stdout.write(`[64:Out] ${data}`);
|
||||
});
|
||||
config64.stderr.on('data', (data) => {
|
||||
console.log(`[64:Err] ${data}`);
|
||||
});
|
||||
config64.on('exit', (code, signal) => {
|
||||
if (code != 0) {
|
||||
process.exit(code)
|
||||
}
|
||||
var build64 = cp.spawn(
|
||||
"cmake", [
|
||||
'--build build/64',
|
||||
'--target INSTALL',
|
||||
'--config RelWithDebInfo',
|
||||
'--',
|
||||
'/logger:"C:\\Program Files\\AppVeyor\\BuildAgent\\Appveyor.MSBuildLogger.dll"'
|
||||
], {
|
||||
windowsVerbatimArguments: true,
|
||||
windowsHide: true
|
||||
}
|
||||
);
|
||||
build64.stdout.on('data', (data) => {
|
||||
process.stdout.write(`[32:Out] ${data}`);
|
||||
});
|
||||
build64.stderr.on('data', (data) => {
|
||||
process.stderr.write(`[32:Err] ${data}`);
|
||||
});
|
||||
build64.on('exit', (code, signal) => {
|
||||
if (code != 0) {
|
||||
process.exit(code)
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
@ECHO OFF
|
||||
git submodule update --init --force --recursive
|
||||
|
||||
IF EXIST inno.exe (
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
@ECHO OFF
|
||||
ECHO -- Building 7z Archive --
|
||||
cmake --build build/64 --target PACKAGE_7Z --config RelWithDebInfo
|
||||
ECHO -- Building Zip Archive --
|
||||
cmake --build build/64 --target PACKAGE_ZIP --config RelWithDebInfo
|
||||
ECHO -- Building Installer --
|
||||
"C:\Program Files (x86)\Inno Setup 5\ISCC.exe" /Qp ".\build\64\installer.iss" > nul
|
||||
"C:\Program Files (x86)\Inno Setup 5\ISCC.exe" /Qp ".\build\64\installer.iss" > nul
|
||||
|
||||
+57
-10
@@ -1,15 +1,62 @@
|
||||
Bitrate="Bitrate"
|
||||
Bitrate.Description="Bitrate in kbit/s"
|
||||
KeyFrame.Type="Key Frame Type"
|
||||
KeyFrame.Type.Frames="Frames"
|
||||
KeyFrame.Type.Seconds="Seconds"
|
||||
KeyFrame.Interval="Key Frame Interval"
|
||||
KeyFrame.Interval.Description="Interval in which a Key Frame is placed."
|
||||
CustomParameters="Custom Parameters"
|
||||
CustomParameters.Description="Format: key1=val1 key2=val2 key3='val3 val4'"
|
||||
|
||||
# ProRes
|
||||
ProRes.Profile.Proxy="Proxy (PXY)"
|
||||
ProRes.Profile.Light="Light (LT)"
|
||||
ProRes.Profile.Standard="Standard"
|
||||
ProRes.Profile.HighQuality="High Quality (HQ)"
|
||||
|
||||
# Generic
|
||||
Automatic="Automatic"
|
||||
|
||||
# FFmpeg
|
||||
FFmpeg="FFmpeg Options"
|
||||
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.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.ColorFormat="Override Color Format"
|
||||
FFmpeg.ColorFormat.Description="Overriding the color format can unlock higher quality, but might cause additional stress.\nNot all encoders support all color formats, and you might end up causing errors or corrupted video due to this."
|
||||
FFmpeg.StandardCompliance="Standard Compliance"
|
||||
FFmpeg.StandardCompliance.Description="How strict should the encoder keep to the standard? A strictness below 'Normal' may cause issues with playback."
|
||||
FFmpeg.StandardCompliance.VeryStrict="Very Strict"
|
||||
FFmpeg.StandardCompliance.Strict="Strict"
|
||||
FFmpeg.StandardCompliance.Normal="Normal"
|
||||
FFmpeg.StandardCompliance.Unofficial="Unofficial"
|
||||
FFmpeg.StandardCompliance.Experimental="Experimental"
|
||||
|
||||
# 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"
|
||||
|
||||
# VP9
|
||||
VP9.RateControl="Rate Control"
|
||||
VP9.RateControl.Mode="Mode"
|
||||
VP9.RateControl.Mode.ConstantBitrate="Constant Bitrate (CBR)"
|
||||
VP9.RateControl.Mode.AverageBitrate="Average Bitrate (ABR)"
|
||||
VP9.RateControl.Mode.VariableBitrate="Variable Bitrate (VBR)"
|
||||
VP9.RateControl.Mode.ConstantQuality="Constant Quality (CQ)"
|
||||
VP9.RateControl.Mode.VariableQuality="Variable Quality (VQ)"
|
||||
VP9.RateControl.Mode.Lossless="Lossless (LL)"
|
||||
VP9.RateControl.Bitrate.Target="Target Bitrate (kbit/s)"
|
||||
VP9.RateControl.Bitrate.Minimum="Minimum Bitrate (kbit/s)"
|
||||
VP9.RateControl.Bitrate.Maximum="Maximum Bitrate (kbit/s)"
|
||||
VP9.RateControl.BufferSize="Buffer Size (kbit/s)"
|
||||
VP9.RateControl.Quality="Target Quality"
|
||||
VP9.RateControl.Quality.Description="This is the quality to reach, with lower values being higher quality, and higher values being more compressed but lower quality.\nOnly affects Constant Quality and Variable Quality (Constrained Quality) modes."
|
||||
VP9.KeyFrames="Key Frames"
|
||||
VP9.KeyFrames.IntervalType="Interval Type"
|
||||
VP9.KeyFrames.IntervalType.Frames="Frames"
|
||||
VP9.KeyFrames.IntervalType.Seconds="Seconds"
|
||||
VP9.KeyFrames.Interval="Interval"
|
||||
VP9.Performance="Performance Settings"
|
||||
VP9.Performance.QualitySpeedRatio="Quality/Speed Ratio"
|
||||
VP9.Performance.QualitySpeedRatio.Description="Ratio of Quality to Speed. Values towards -16 favor quality, while values towards +16 favor speed."
|
||||
VP9.Performance.Deadline="Deadline Multiplier (%)"
|
||||
VP9.Performance.Deadline.Description="The deadline parameter is used to determine how many tests for quality should be done.\nFor real time capture a multiplier of 100.0 or below should be used, unless the CPU is powerful enough for more."
|
||||
VP9.Performance.Tiling="Tiling"
|
||||
VP9.Performance.Tiling.Columns="Columns"
|
||||
VP9.Performance.Tiling.Rows="Rows"
|
||||
|
||||
|
||||
@@ -0,0 +1,707 @@
|
||||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (C) 2018 - 2019 Michael Fabian Dirks
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "generic.hpp"
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <util/profiler.hpp>
|
||||
#include "ffmpeg/tools.hpp"
|
||||
#include "plugin.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <obs-module.h>
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include "libavutil/dict.h"
|
||||
#include "libavutil/frame.h"
|
||||
#include "libavutil/opt.h"
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
// Generic
|
||||
#define P_AUTOMATIC "Automatic"
|
||||
|
||||
// FFmpeg
|
||||
#define P_FFMPEG "FFmpeg"
|
||||
#define P_FFMPEG_CUSTOMSETTINGS "FFmpeg.CustomSettings"
|
||||
#define P_FFMPEG_THREADS "FFmpeg.Threads"
|
||||
#define P_FFMPEG_COLORFORMAT "FFmpeg.ColorFormat"
|
||||
#define P_FFMPEG_STANDARDCOMPLIANCE "FFmpeg.StandardCompliance"
|
||||
|
||||
enum class keyframe_type { Seconds, Frames };
|
||||
|
||||
encoder::generic_factory::generic_factory(AVCodec* codec) : avcodec_ptr(codec), info() {}
|
||||
|
||||
encoder::generic_factory::~generic_factory() {}
|
||||
|
||||
void encoder::generic_factory::register_encoder()
|
||||
{
|
||||
// Generate unique name from given Id
|
||||
{
|
||||
std::stringstream sstr;
|
||||
sstr << "ffmpeg-" << avcodec_ptr->name << "-0x" << std::uppercase << std::setfill('0') << std::setw(8)
|
||||
<< std::hex << avcodec_ptr->capabilities;
|
||||
this->info.uid = sstr.str();
|
||||
}
|
||||
|
||||
// Also generate a human readable name while we're at it.
|
||||
// TODO: Figure out a way to translate from names to other names.
|
||||
{
|
||||
std::stringstream sstr;
|
||||
sstr << "[FFmpeg] " << (avcodec_ptr->long_name ? avcodec_ptr->long_name : avcodec_ptr->name) << " ("
|
||||
<< avcodec_ptr->name << ")";
|
||||
std::string caps = ffmpeg::tools::translate_encoder_capabilities(avcodec_ptr->capabilities);
|
||||
if (caps.length() != 0) {
|
||||
sstr << " (" << caps << ")";
|
||||
}
|
||||
this->info.readable_name = sstr.str();
|
||||
}
|
||||
|
||||
// Assign Ids.
|
||||
{
|
||||
const AVCodecDescriptor* desc = avcodec_descriptor_get(this->avcodec_ptr->id);
|
||||
if (desc) {
|
||||
this->info.codec = desc->name;
|
||||
} else {
|
||||
this->info.codec = avcodec_ptr->name;
|
||||
}
|
||||
}
|
||||
this->info.oei.id = this->info.uid.c_str();
|
||||
this->info.oei.codec = this->info.codec.c_str();
|
||||
|
||||
// Detect encoder type (only Video and Audio supported)
|
||||
if (avcodec_ptr->type == AVMediaType::AVMEDIA_TYPE_VIDEO) {
|
||||
this->info.oei.type = obs_encoder_type::OBS_ENCODER_VIDEO;
|
||||
} else if (avcodec_ptr->type == AVMediaType::AVMEDIA_TYPE_AUDIO) {
|
||||
this->info.oei.type = obs_encoder_type::OBS_ENCODER_AUDIO;
|
||||
} else {
|
||||
throw std::invalid_argument("unsupported codec type");
|
||||
}
|
||||
|
||||
// Register functions.
|
||||
this->info.oei.create = [](obs_data_t* settings, obs_encoder_t* encoder) {
|
||||
try {
|
||||
return reinterpret_cast<void*>(new generic(settings, encoder));
|
||||
} catch (std::exception const& e) {
|
||||
PLOG_ERROR("exception: %s", e.what());
|
||||
return reinterpret_cast<void*>(0);
|
||||
} catch (...) {
|
||||
PLOG_ERROR("unknown exception");
|
||||
return reinterpret_cast<void*>(0);
|
||||
}
|
||||
};
|
||||
this->info.oei.destroy = [](void* ptr) {
|
||||
try {
|
||||
delete reinterpret_cast<generic*>(ptr);
|
||||
} catch (std::exception const& e) {
|
||||
PLOG_ERROR("exception: %s", e.what());
|
||||
throw e;
|
||||
} catch (...) {
|
||||
PLOG_ERROR("unknown exception");
|
||||
throw;
|
||||
}
|
||||
};
|
||||
this->info.oei.get_name = [](void* type_data) {
|
||||
try {
|
||||
return reinterpret_cast<generic_factory*>(type_data)->get_name();
|
||||
} catch (std::exception const& e) {
|
||||
PLOG_ERROR("exception: %s", e.what());
|
||||
throw e;
|
||||
} catch (...) {
|
||||
PLOG_ERROR("unknown exception");
|
||||
throw;
|
||||
}
|
||||
};
|
||||
this->info.oei.get_defaults2 = [](obs_data_t* settings, void* type_data) {
|
||||
try {
|
||||
reinterpret_cast<generic_factory*>(type_data)->get_defaults(settings);
|
||||
} catch (std::exception const& e) {
|
||||
PLOG_ERROR("exception: %s", e.what());
|
||||
throw e;
|
||||
} catch (...) {
|
||||
PLOG_ERROR("unknown exception");
|
||||
throw;
|
||||
}
|
||||
};
|
||||
this->info.oei.get_properties2 = [](void* ptr, void* type_data) {
|
||||
try {
|
||||
obs_properties_t* props = obs_properties_create();
|
||||
if (type_data != nullptr) {
|
||||
reinterpret_cast<generic_factory*>(type_data)->get_properties(props);
|
||||
}
|
||||
if (ptr != nullptr) {
|
||||
reinterpret_cast<generic*>(ptr)->get_properties(props);
|
||||
}
|
||||
return props;
|
||||
} catch (std::exception const& e) {
|
||||
PLOG_ERROR("exception: %s", e.what());
|
||||
throw e;
|
||||
} catch (...) {
|
||||
PLOG_ERROR("unknown exception");
|
||||
throw;
|
||||
}
|
||||
};
|
||||
this->info.oei.update = [](void* ptr, obs_data_t* settings) {
|
||||
try {
|
||||
return reinterpret_cast<generic*>(ptr)->update(settings);
|
||||
} catch (std::exception const& e) {
|
||||
PLOG_ERROR("exception: %s", e.what());
|
||||
throw e;
|
||||
} catch (...) {
|
||||
PLOG_ERROR("unknown exception");
|
||||
throw;
|
||||
}
|
||||
};
|
||||
this->info.oei.get_sei_data = [](void* ptr, uint8_t** sei_data, size_t* size) {
|
||||
try {
|
||||
return reinterpret_cast<generic*>(ptr)->get_sei_data(sei_data, size);
|
||||
} catch (std::exception const& e) {
|
||||
PLOG_ERROR("exception: %s", e.what());
|
||||
throw e;
|
||||
} catch (...) {
|
||||
PLOG_ERROR("unknown exception");
|
||||
throw;
|
||||
}
|
||||
};
|
||||
this->info.oei.get_extra_data = [](void* ptr, uint8_t** extra_data, size_t* size) {
|
||||
try {
|
||||
return reinterpret_cast<generic*>(ptr)->get_extra_data(extra_data, size);
|
||||
} catch (std::exception const& e) {
|
||||
PLOG_ERROR("exception: %s", e.what());
|
||||
throw e;
|
||||
} catch (...) {
|
||||
PLOG_ERROR("unknown exception");
|
||||
throw;
|
||||
}
|
||||
};
|
||||
|
||||
if (this->avcodec_ptr->type == AVMediaType::AVMEDIA_TYPE_VIDEO) {
|
||||
this->info.oei.get_video_info = [](void* ptr, struct video_scale_info* info) {
|
||||
try {
|
||||
reinterpret_cast<generic*>(ptr)->get_video_info(info);
|
||||
} catch (std::exception const& e) {
|
||||
PLOG_ERROR("exception: %s", e.what());
|
||||
throw e;
|
||||
} catch (...) {
|
||||
PLOG_ERROR("unknown exception");
|
||||
throw;
|
||||
}
|
||||
};
|
||||
this->info.oei.encode = [](void* ptr, struct encoder_frame* frame, struct encoder_packet* packet,
|
||||
bool* received_packet) {
|
||||
try {
|
||||
return reinterpret_cast<generic*>(ptr)->video_encode(frame, packet, received_packet);
|
||||
} catch (std::exception const& e) {
|
||||
PLOG_ERROR("exception: %s", e.what());
|
||||
throw e;
|
||||
} catch (...) {
|
||||
PLOG_ERROR("unknown exception");
|
||||
throw;
|
||||
}
|
||||
};
|
||||
this->info.oei.encode_texture = [](void* ptr, uint32_t handle, int64_t pts, uint64_t lock_key,
|
||||
uint64_t* next_key, struct encoder_packet* packet,
|
||||
bool* received_packet) {
|
||||
try {
|
||||
return reinterpret_cast<generic*>(ptr)->video_encode_texture(
|
||||
handle, pts, lock_key, next_key, packet, received_packet);
|
||||
} catch (std::exception const& e) {
|
||||
PLOG_ERROR("exception: %s", e.what());
|
||||
throw e;
|
||||
} catch (...) {
|
||||
PLOG_ERROR("unknown exception");
|
||||
throw;
|
||||
}
|
||||
};
|
||||
|
||||
} else if (this->avcodec_ptr->type == AVMediaType::AVMEDIA_TYPE_AUDIO) {
|
||||
this->info.oei.get_audio_info = [](void* ptr, struct audio_convert_info* info) {
|
||||
try {
|
||||
reinterpret_cast<generic*>(ptr)->get_audio_info(info);
|
||||
} catch (std::exception const& e) {
|
||||
PLOG_ERROR("exception: %s", e.what());
|
||||
throw e;
|
||||
} catch (...) {
|
||||
PLOG_ERROR("unknown exception");
|
||||
throw;
|
||||
}
|
||||
};
|
||||
this->info.oei.get_frame_size = [](void* ptr) {
|
||||
try {
|
||||
return reinterpret_cast<generic*>(ptr)->get_frame_size();
|
||||
} catch (std::exception const& e) {
|
||||
PLOG_ERROR("exception: %s", e.what());
|
||||
throw e;
|
||||
} catch (...) {
|
||||
PLOG_ERROR("unknown exception");
|
||||
throw;
|
||||
}
|
||||
};
|
||||
this->info.oei.encode = [](void* ptr, struct encoder_frame* frame, struct encoder_packet* packet,
|
||||
bool* received_packet) {
|
||||
try {
|
||||
return reinterpret_cast<generic*>(ptr)->audio_encode(frame, packet, received_packet);
|
||||
} catch (std::exception const& e) {
|
||||
PLOG_ERROR("exception: %s", e.what());
|
||||
throw e;
|
||||
} catch (...) {
|
||||
PLOG_ERROR("unknown exception");
|
||||
throw;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Finally store ourself as type data.
|
||||
this->info.oei.type_data = this;
|
||||
|
||||
obs_register_encoder(&this->info.oei);
|
||||
PLOG_INFO("Registered encoder #%llX with name '%s' and long name '%s' and caps %llX", avcodec_ptr,
|
||||
avcodec_ptr->name, avcodec_ptr->long_name, avcodec_ptr->capabilities);
|
||||
}
|
||||
|
||||
const char* encoder::generic_factory::get_name()
|
||||
{
|
||||
return this->info.readable_name.c_str();
|
||||
}
|
||||
|
||||
void encoder::generic_factory::get_defaults(obs_data_t* settings)
|
||||
{
|
||||
{ // Handler
|
||||
auto ptr = obsffmpeg::find_codec_handler(this->avcodec_ptr->name);
|
||||
if (ptr) {
|
||||
ptr->get_defaults(settings, this->avcodec_ptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
{ // Integrated Options
|
||||
// FFmpeg
|
||||
obs_data_set_default_string(settings, P_FFMPEG_CUSTOMSETTINGS, "");
|
||||
obs_data_set_default_int(settings, P_FFMPEG_COLORFORMAT, static_cast<int64_t>(AV_PIX_FMT_NONE));
|
||||
obs_data_set_default_int(settings, P_FFMPEG_THREADS, 0);
|
||||
obs_data_set_default_int(settings, P_FFMPEG_STANDARDCOMPLIANCE, FF_COMPLIANCE_STRICT);
|
||||
}
|
||||
}
|
||||
|
||||
void encoder::generic_factory::get_properties(obs_properties_t* props)
|
||||
{
|
||||
{ // Handler
|
||||
auto ptr = obsffmpeg::find_codec_handler(this->avcodec_ptr->name);
|
||||
if (ptr) {
|
||||
ptr->get_properties(props, this->avcodec_ptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto prs = obs_properties_create();
|
||||
{
|
||||
auto p =
|
||||
obs_properties_add_text(prs, P_FFMPEG_CUSTOMSETTINGS, TRANSLATE(P_FFMPEG_CUSTOMSETTINGS),
|
||||
obs_text_type::OBS_TEXT_DEFAULT);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_FFMPEG_CUSTOMSETTINGS)));
|
||||
}
|
||||
if (this->avcodec_ptr->pix_fmts) {
|
||||
auto p = obs_properties_add_list(prs, P_FFMPEG_COLORFORMAT, TRANSLATE(P_FFMPEG_COLORFORMAT),
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_FFMPEG_COLORFORMAT)));
|
||||
obs_property_list_add_int(p, TRANSLATE(P_AUTOMATIC), static_cast<int64_t>(AV_PIX_FMT_NONE));
|
||||
for (auto ptr = this->avcodec_ptr->pix_fmts; *ptr != AV_PIX_FMT_NONE; ptr++) {
|
||||
obs_property_list_add_int(p, ffmpeg::tools::get_pixel_format_name(*ptr),
|
||||
static_cast<int64_t>(*ptr));
|
||||
}
|
||||
}
|
||||
{
|
||||
auto p = obs_properties_add_int_slider(prs, P_FFMPEG_THREADS, TRANSLATE(P_FFMPEG_THREADS), 0,
|
||||
std::thread::hardware_concurrency() * 2, 1);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_FFMPEG_THREADS)));
|
||||
}
|
||||
{
|
||||
auto p = obs_properties_add_list(prs, P_FFMPEG_STANDARDCOMPLIANCE,
|
||||
TRANSLATE(P_FFMPEG_STANDARDCOMPLIANCE), OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_INT);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_FFMPEG_STANDARDCOMPLIANCE)));
|
||||
obs_property_list_add_int(p, TRANSLATE(P_FFMPEG_STANDARDCOMPLIANCE ".VeryStrict"),
|
||||
FF_COMPLIANCE_VERY_STRICT);
|
||||
obs_property_list_add_int(p, TRANSLATE(P_FFMPEG_STANDARDCOMPLIANCE ".Strict"),
|
||||
FF_COMPLIANCE_STRICT);
|
||||
obs_property_list_add_int(p, TRANSLATE(P_FFMPEG_STANDARDCOMPLIANCE ".Normal"),
|
||||
FF_COMPLIANCE_NORMAL);
|
||||
obs_property_list_add_int(p, TRANSLATE(P_FFMPEG_STANDARDCOMPLIANCE ".Unofficial"),
|
||||
FF_COMPLIANCE_UNOFFICIAL);
|
||||
obs_property_list_add_int(p, TRANSLATE(P_FFMPEG_STANDARDCOMPLIANCE ".Experimental"),
|
||||
FF_COMPLIANCE_EXPERIMENTAL);
|
||||
}
|
||||
obs_properties_add_group(props, P_FFMPEG, TRANSLATE(P_FFMPEG), OBS_GROUP_NORMAL, prs);
|
||||
};
|
||||
}
|
||||
|
||||
AVCodec* encoder::generic_factory::get_avcodec()
|
||||
{
|
||||
return this->avcodec_ptr;
|
||||
}
|
||||
|
||||
encoder::generic::generic(obs_data_t* settings, obs_encoder_t* encoder)
|
||||
: self(encoder), lag_in_frames(0), frame_count(0)
|
||||
{
|
||||
this->factory = reinterpret_cast<generic_factory*>(obs_encoder_get_type_data(self));
|
||||
|
||||
// Verify that the codec actually still exists.
|
||||
this->codec = avcodec_find_encoder_by_name(this->factory->get_avcodec()->name);
|
||||
if (!this->codec) {
|
||||
PLOG_ERROR("Failed to find encoder for codec '%s'.", this->factory->get_avcodec()->name);
|
||||
throw std::runtime_error("failed to find codec");
|
||||
}
|
||||
|
||||
// Initialize context.
|
||||
this->context = avcodec_alloc_context3(this->codec);
|
||||
if (!this->context) {
|
||||
PLOG_ERROR("Failed to create context for encoder '%s'.", this->codec->name);
|
||||
throw std::runtime_error("failed to create context");
|
||||
}
|
||||
|
||||
// Settings
|
||||
/// Rate Control
|
||||
this->context->strict_std_compliance =
|
||||
static_cast<int>(obs_data_get_int(settings, P_FFMPEG_STANDARDCOMPLIANCE));
|
||||
this->context->debug = 0;
|
||||
/// Threading
|
||||
if (this->codec->capabilities & AV_CODEC_CAP_SLICE_THREADS) {
|
||||
this->context->thread_type = FF_THREAD_SLICE;
|
||||
} else if (this->codec->capabilities & AV_CODEC_CAP_FRAME_THREADS) {
|
||||
this->context->thread_type = FF_THREAD_FRAME;
|
||||
} else {
|
||||
this->context->thread_type = 0;
|
||||
}
|
||||
int64_t threads = obs_data_get_int(settings, P_FFMPEG_THREADS);
|
||||
if (threads > 0) {
|
||||
this->context->thread_count = static_cast<int>(threads);
|
||||
this->lag_in_frames = this->context->thread_count;
|
||||
} else {
|
||||
this->context->thread_count = std::thread::hardware_concurrency();
|
||||
this->lag_in_frames = this->context->thread_count;
|
||||
}
|
||||
|
||||
// Video and Audio exclusive setup
|
||||
if (this->codec->type == AVMEDIA_TYPE_VIDEO) {
|
||||
// FFmpeg Video Settings
|
||||
auto encvideo = obs_encoder_video(this->self);
|
||||
auto voi = video_output_get_info(encvideo);
|
||||
|
||||
// Resolution
|
||||
this->context->width = voi->width;
|
||||
this->context->height = voi->height;
|
||||
this->swscale.set_source_size(this->context->width, this->context->height);
|
||||
this->swscale.set_target_size(this->context->width, this->context->height);
|
||||
|
||||
// Color
|
||||
this->context->colorspace = ffmpeg::tools::obs_videocolorspace_to_avcolorspace(voi->colorspace);
|
||||
this->context->color_range = ffmpeg::tools::obs_videorangetype_to_avcolorrange(voi->range);
|
||||
this->context->field_order = AV_FIELD_PROGRESSIVE;
|
||||
this->swscale.set_source_color(this->context->color_range, this->context->colorspace);
|
||||
this->swscale.set_target_color(this->context->color_range, this->context->colorspace);
|
||||
|
||||
// Pixel Format
|
||||
{
|
||||
// Due to unsupported color formats and ffmpeg not automatically converting formats from A to B,
|
||||
// we have to detect the closest format that we can still use and initialize our swscale instance
|
||||
// these formats. This has a massive cost attached unfortunately.
|
||||
|
||||
AVPixelFormat source = ffmpeg::tools::obs_videoformat_to_avpixelformat(voi->format);
|
||||
AVPixelFormat target = AV_PIX_FMT_NONE;
|
||||
|
||||
int loss = 0;
|
||||
target = avcodec_find_best_pix_fmt_of_list(this->codec->pix_fmts, source, false, &loss);
|
||||
|
||||
this->context->pix_fmt = target;
|
||||
this->swscale.set_source_format(source);
|
||||
this->swscale.set_target_format(this->context->pix_fmt);
|
||||
|
||||
PLOG_INFO("Automatically detected target format '%s' for source format '%s'.",
|
||||
ffmpeg::tools::get_pixel_format_name(target),
|
||||
ffmpeg::tools::get_pixel_format_name(source));
|
||||
}
|
||||
AVPixelFormat color_format_override =
|
||||
static_cast<AVPixelFormat>(obs_data_get_int(settings, P_FFMPEG_COLORFORMAT));
|
||||
if (color_format_override != AV_PIX_FMT_NONE) {
|
||||
// User specified override for color format.
|
||||
this->context->pix_fmt = color_format_override;
|
||||
this->swscale.set_target_format(this->context->pix_fmt);
|
||||
PLOG_INFO("User specified target format override '%s'.",
|
||||
ffmpeg::tools::get_pixel_format_name(this->context->pix_fmt));
|
||||
}
|
||||
|
||||
// Framerate
|
||||
this->context->time_base.num = voi->fps_num;
|
||||
this->context->time_base.den = voi->fps_den;
|
||||
this->context->ticks_per_frame = 1;
|
||||
} else if (this->codec->type == AVMEDIA_TYPE_AUDIO) {
|
||||
}
|
||||
|
||||
// Update settings
|
||||
this->update(settings);
|
||||
|
||||
// Initialize
|
||||
int res = avcodec_open2(this->context, this->codec, NULL);
|
||||
if (res < 0) {
|
||||
PLOG_ERROR("Failed to initialize encoder '%s' due to error code %lld: %s", this->codec->name, res,
|
||||
ffmpeg::tools::get_error_description(res));
|
||||
throw std::runtime_error(ffmpeg::tools::get_error_description(res));
|
||||
}
|
||||
|
||||
// Video/Audio exclusive setup part 2.
|
||||
if (this->codec->type == AVMEDIA_TYPE_VIDEO) {
|
||||
// Create Scaler
|
||||
if (!swscale.initialize(SWS_FAST_BILINEAR)) {
|
||||
PLOG_ERROR(
|
||||
" Failed to initialize Software Scaler for pixel format '%s' with color space '%s' and "
|
||||
"range '%s'.",
|
||||
ffmpeg::tools::get_pixel_format_name(this->context->pix_fmt),
|
||||
ffmpeg::tools::get_color_space_name(this->context->colorspace),
|
||||
this->swscale.is_source_full_range() ? "Full" : "Partial");
|
||||
throw std::runtime_error("failed to initialize swscaler.");
|
||||
}
|
||||
|
||||
// Create Frame queue
|
||||
this->frame_queue.set_pixel_format(this->context->pix_fmt);
|
||||
this->frame_queue.set_resolution(this->context->width, this->context->height);
|
||||
this->frame_queue.precache(std::thread::hardware_concurrency() / 4);
|
||||
} else if (this->codec->type == AVMEDIA_TYPE_AUDIO) {
|
||||
}
|
||||
|
||||
// Create Packet
|
||||
this->current_packet = av_packet_alloc();
|
||||
if (!this->current_packet) {
|
||||
PLOG_ERROR("Failed to allocate packet storage.");
|
||||
throw std::runtime_error("Failed to allocate packet storage.");
|
||||
}
|
||||
}
|
||||
|
||||
encoder::generic::~generic()
|
||||
{
|
||||
this->frame_queue.clear();
|
||||
this->frame_queue_used.clear();
|
||||
this->swscale.finalize();
|
||||
|
||||
if (this->context) {
|
||||
avcodec_close(this->context);
|
||||
avcodec_free_context(&this->context);
|
||||
}
|
||||
}
|
||||
|
||||
void encoder::generic::get_properties(obs_properties_t* props)
|
||||
{
|
||||
{ // Handler
|
||||
auto ptr = obsffmpeg::find_codec_handler(this->codec->name);
|
||||
if (ptr) {
|
||||
ptr->get_properties(props, this->codec, this->context);
|
||||
}
|
||||
}
|
||||
|
||||
obs_property_set_enabled(obs_properties_get(props, P_FFMPEG_COLORFORMAT), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, P_FFMPEG_THREADS), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, P_FFMPEG_STANDARDCOMPLIANCE), false);
|
||||
}
|
||||
|
||||
bool encoder::generic::update(obs_data_t* settings)
|
||||
{
|
||||
{ // Handler
|
||||
auto ptr = obsffmpeg::find_codec_handler(this->codec->name);
|
||||
if (ptr) {
|
||||
ptr->update(settings, this->codec, this->context);
|
||||
}
|
||||
}
|
||||
|
||||
{ // FFmpeg
|
||||
// Apply custom options.
|
||||
av_opt_set_from_string(this->context, obs_data_get_string(settings, P_FFMPEG_CUSTOMSETTINGS), nullptr,
|
||||
";", "=");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void encoder::generic::get_audio_info(audio_convert_info*) {}
|
||||
|
||||
size_t encoder::generic::get_frame_size()
|
||||
{
|
||||
return size_t();
|
||||
}
|
||||
|
||||
bool encoder::generic::audio_encode(encoder_frame*, encoder_packet*, bool*)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void encoder::generic::get_video_info(video_scale_info*) {}
|
||||
|
||||
bool encoder::generic::get_sei_data(uint8_t**, size_t*)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool encoder::generic::get_extra_data(uint8_t** extra_data, size_t* size)
|
||||
{
|
||||
if (!this->context->extradata) {
|
||||
return false;
|
||||
}
|
||||
*extra_data = this->context->extradata;
|
||||
*size = this->context->extradata_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool encoder::generic::video_encode(encoder_frame* frame, encoder_packet* packet, bool* received_packet)
|
||||
{
|
||||
// Convert frame.
|
||||
std::shared_ptr<AVFrame> vframe = frame_queue.pop(); // Retrieve an empty frame.
|
||||
{
|
||||
ScopeProfiler profile("convert");
|
||||
|
||||
vframe->color_range = this->context->color_range;
|
||||
vframe->colorspace = this->context->colorspace;
|
||||
|
||||
int res =
|
||||
swscale.convert(reinterpret_cast<uint8_t**>(frame->data), reinterpret_cast<int*>(frame->linesize),
|
||||
0, this->context->height, vframe->data, vframe->linesize);
|
||||
if (res <= 0) {
|
||||
PLOG_ERROR("Failed to convert frame: %s (%ld).", ffmpeg::tools::get_error_description(res),
|
||||
res);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Send and receive frames.
|
||||
{
|
||||
ScopeProfiler profile("loop");
|
||||
bool sent_frame = false;
|
||||
bool recv_packet = false;
|
||||
bool should_lag = (lag_in_frames - frame_count) <= 0;
|
||||
|
||||
auto loop_begin = std::chrono::high_resolution_clock::now();
|
||||
auto loop_end = loop_begin + std::chrono::milliseconds(50);
|
||||
|
||||
while ((!sent_frame || (should_lag && !recv_packet))
|
||||
&& !(std::chrono::high_resolution_clock::now() > loop_end)) {
|
||||
bool eagain_is_stupid = false;
|
||||
|
||||
if (!sent_frame) {
|
||||
ScopeProfiler profile_inner("send");
|
||||
|
||||
vframe->pts = frame->pts;
|
||||
|
||||
int res = send_frame(vframe);
|
||||
switch (res) {
|
||||
case 0:
|
||||
sent_frame = true;
|
||||
frame_count++;
|
||||
break;
|
||||
case AVERROR(EAGAIN):
|
||||
// This means we should call receive_packet again, but what do we do with that data?
|
||||
// Why can't we queue on both? Do I really have to implement threading for this stuff?
|
||||
if (*received_packet == true) {
|
||||
PLOG_WARNING(
|
||||
"Skipped frame due to EAGAIN when a packet was already returned.");
|
||||
sent_frame = true;
|
||||
frame_count++;
|
||||
}
|
||||
eagain_is_stupid = true;
|
||||
break;
|
||||
case AVERROR(EOF):
|
||||
PLOG_ERROR("Skipped frame due to end of stream.");
|
||||
sent_frame = true;
|
||||
break;
|
||||
default:
|
||||
PLOG_ERROR("Failed to encode frame: %s (%ld).",
|
||||
ffmpeg::tools::get_error_description(res), res);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!recv_packet) {
|
||||
ScopeProfiler profile_inner("recieve");
|
||||
|
||||
int res = receive_packet(received_packet, packet);
|
||||
switch (res) {
|
||||
case 0:
|
||||
recv_packet = true;
|
||||
break;
|
||||
case AVERROR(EOF):
|
||||
PLOG_ERROR("Received end of file.");
|
||||
recv_packet = true;
|
||||
break;
|
||||
case AVERROR(EAGAIN):
|
||||
if (sent_frame) {
|
||||
recv_packet = true;
|
||||
}
|
||||
if (eagain_is_stupid) {
|
||||
PLOG_ERROR("Both send and recieve returned EAGAIN, encoder is broken.");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
PLOG_ERROR("Failed to receive packet: %s (%ld).",
|
||||
ffmpeg::tools::get_error_description(res), res);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sent_frame || !recv_packet) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool encoder::generic::video_encode_texture(uint32_t, int64_t, uint64_t, uint64_t*, encoder_packet*, bool*)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int encoder::generic::receive_packet(bool* received_packet, struct encoder_packet* packet)
|
||||
{
|
||||
int res = avcodec_receive_packet(this->context, this->current_packet);
|
||||
if (res == 0) {
|
||||
packet->type = OBS_ENCODER_VIDEO;
|
||||
packet->pts = this->current_packet->pts;
|
||||
packet->dts = this->current_packet->pts;
|
||||
packet->data = this->current_packet->data;
|
||||
packet->size = this->current_packet->size;
|
||||
packet->keyframe = !!(this->current_packet->flags & AV_PKT_FLAG_KEY);
|
||||
packet->drop_priority = 0;
|
||||
*received_packet = true;
|
||||
|
||||
{
|
||||
std::shared_ptr<AVFrame> uframe = frame_queue_used.pop_only();
|
||||
frame_queue.push(uframe);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int encoder::generic::send_frame(std::shared_ptr<AVFrame> frame)
|
||||
{
|
||||
int res = avcodec_send_frame(this->context, frame.get());
|
||||
switch (res) {
|
||||
case 0:
|
||||
frame_count++;
|
||||
case AVERROR(EAGAIN):
|
||||
case AVERROR(EOF):
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (C) 2018 - 2019 Michael Fabian Dirks
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <encoder.hpp>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include "ffmpeg/avframe-queue.hpp"
|
||||
#include "ffmpeg/swscale.hpp"
|
||||
|
||||
namespace encoder {
|
||||
class generic_factory {
|
||||
struct info {
|
||||
std::string uid;
|
||||
std::string codec;
|
||||
std::string readable_name;
|
||||
obs_encoder_info oei;
|
||||
} info;
|
||||
AVCodec* avcodec_ptr;
|
||||
|
||||
public:
|
||||
generic_factory(AVCodec* codec);
|
||||
virtual ~generic_factory();
|
||||
|
||||
void register_encoder();
|
||||
|
||||
const char* get_name();
|
||||
|
||||
void get_defaults(obs_data_t* settings);
|
||||
|
||||
void get_properties(obs_properties_t* props);
|
||||
|
||||
AVCodec* get_avcodec();
|
||||
|
||||
public:
|
||||
static bool modified_ratecontrol_properties(void* priv, obs_properties_t* props, obs_property_t* prop,
|
||||
obs_data_t* settings);
|
||||
};
|
||||
|
||||
class generic {
|
||||
obs_encoder_t* self;
|
||||
generic_factory* factory;
|
||||
|
||||
AVCodec* codec;
|
||||
AVCodecContext* context;
|
||||
|
||||
ffmpeg::avframe_queue frame_queue;
|
||||
ffmpeg::avframe_queue frame_queue_used;
|
||||
ffmpeg::swscale swscale;
|
||||
AVPacket* current_packet = nullptr;
|
||||
|
||||
int64_t lag_in_frames;
|
||||
int64_t frame_count;
|
||||
|
||||
public:
|
||||
generic(obs_data_t* settings, obs_encoder_t* encoder);
|
||||
virtual ~generic();
|
||||
|
||||
public: // OBS API
|
||||
// Shared
|
||||
void get_properties(obs_properties_t* props);
|
||||
|
||||
bool update(obs_data_t* settings);
|
||||
|
||||
// Audio only
|
||||
void get_audio_info(struct audio_convert_info* info);
|
||||
|
||||
size_t get_frame_size();
|
||||
|
||||
bool audio_encode(struct encoder_frame* frame, struct encoder_packet* packet, bool* received_packet);
|
||||
|
||||
// Video only
|
||||
void get_video_info(struct video_scale_info* info);
|
||||
|
||||
bool get_sei_data(uint8_t** sei_data, size_t* size);
|
||||
|
||||
bool get_extra_data(uint8_t** extra_data, size_t* size);
|
||||
|
||||
bool video_encode(struct encoder_frame* frame, struct encoder_packet* packet, bool* received_packet);
|
||||
|
||||
bool video_encode_texture(uint32_t handle, int64_t pts, uint64_t lock_key, uint64_t* next_key,
|
||||
struct encoder_packet* packet, bool* received_packet);
|
||||
|
||||
int receive_packet(bool* received_packet, struct encoder_packet* packet);
|
||||
|
||||
int send_frame(std::shared_ptr<AVFrame> frame);
|
||||
};
|
||||
} // namespace encoder
|
||||
@@ -33,7 +33,7 @@ extern "C" {
|
||||
#include <util/profiler.hpp>
|
||||
|
||||
#define T_PROFILE "ProRes.Profile"
|
||||
#define T_PROFILE_(x) "ProRes.Profile." vstr(x)
|
||||
#define T_PROFILE_(x) "ProRes.Profile." D_VSTR(x)
|
||||
#define T_CUSTOM "Custom"
|
||||
|
||||
#define LOG_PREFIX "[prores_aw] "
|
||||
@@ -241,9 +241,9 @@ obsffmpeg::encoder::prores_aw::~prores_aw()
|
||||
}
|
||||
}
|
||||
|
||||
void obsffmpeg::encoder::prores_aw::get_properties(obs_properties_t* props) {}
|
||||
void obsffmpeg::encoder::prores_aw::get_properties(obs_properties_t*) {}
|
||||
|
||||
bool obsffmpeg::encoder::prores_aw::update(obs_data_t* settings)
|
||||
bool obsffmpeg::encoder::prores_aw::update(obs_data_t*)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -258,12 +258,12 @@ bool obsffmpeg::encoder::prores_aw::get_extra_data(uint8_t** extra_data, size_t*
|
||||
return true;
|
||||
}
|
||||
|
||||
bool obsffmpeg::encoder::prores_aw::get_sei_data(uint8_t** sei_data, size_t* size)
|
||||
bool obsffmpeg::encoder::prores_aw::get_sei_data(uint8_t**, size_t*)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void obsffmpeg::encoder::prores_aw::get_video_info(video_scale_info* info)
|
||||
void obsffmpeg::encoder::prores_aw::get_video_info(video_scale_info*)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -273,9 +273,9 @@ bool obsffmpeg::encoder::prores_aw::encode(encoder_frame* frame, encoder_packet*
|
||||
int res = 0;
|
||||
|
||||
{
|
||||
ScopeProfiler sp_frame("frame");
|
||||
AVFrame* vframe = frame_queue.pop();
|
||||
vframe->pts = frame->pts;
|
||||
ScopeProfiler sp_frame("frame");
|
||||
std::shared_ptr<AVFrame> vframe = frame_queue.pop();
|
||||
vframe->pts = frame->pts;
|
||||
|
||||
vframe->color_range = this->avcontext->color_range;
|
||||
vframe->colorspace = this->avcontext->colorspace;
|
||||
@@ -294,7 +294,7 @@ bool obsffmpeg::encoder::prores_aw::encode(encoder_frame* frame, encoder_packet*
|
||||
|
||||
{
|
||||
ScopeProfiler profile("send");
|
||||
res = avcodec_send_frame(this->avcontext, vframe);
|
||||
res = avcodec_send_frame(this->avcontext, vframe.get());
|
||||
if (res < 0) {
|
||||
PLOG_ERROR(LOG_PREFIX "Failed to encode frame: %s (%ld).",
|
||||
ffmpeg::tools::get_error_description(res), res);
|
||||
@@ -320,12 +320,10 @@ bool obsffmpeg::encoder::prores_aw::encode(encoder_frame* frame, encoder_packet*
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
AVFrame* uframe = frame_queue_used.pop_only();
|
||||
std::shared_ptr<AVFrame> uframe = frame_queue_used.pop_only();
|
||||
if (uframe) {
|
||||
if (frame_queue.empty()) {
|
||||
frame_queue.push(uframe);
|
||||
} else {
|
||||
av_frame_free(&uframe);
|
||||
}
|
||||
}
|
||||
packet->type = OBS_ENCODER_VIDEO;
|
||||
|
||||
@@ -15,13 +15,11 @@
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef OBS_FFMPEG_ENCODER_PRORES_AW
|
||||
#define OBS_FFMPEG_ENCODER_PRORES_AW
|
||||
#pragma once
|
||||
|
||||
#include <encoder.hpp>
|
||||
#include "ffmpeg/avframe-queue.hpp"
|
||||
#include <vector>
|
||||
#include "ffmpeg/avframe-queue.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
@@ -31,21 +29,20 @@ namespace obsffmpeg {
|
||||
namespace encoder {
|
||||
class prores_aw : base {
|
||||
enum class profile {
|
||||
Auto = FF_PROFILE_UNKNOWN,
|
||||
Proxy = 0 /*FF_PROFILE_PRORES_PROXY*/,
|
||||
Light = 1 /*FF_PROFILE_PRORES_LT*/,
|
||||
Standard = 2 /*FF_PROFILE_PRORES_STANDARD*/,
|
||||
HighQuality = 3 /*FF_PROFILE_PRORES_HQ*/,
|
||||
FourFourFourFour = 4 /*FF_PROFILE_PRORES_4444*/ // Automatically set if I444 or RGB input.
|
||||
Auto = FF_PROFILE_UNKNOWN,
|
||||
Proxy = 0 /*FF_PROFILE_PRORES_PROXY*/,
|
||||
Light = 1 /*FF_PROFILE_PRORES_LT*/,
|
||||
Standard = 2 /*FF_PROFILE_PRORES_STANDARD*/,
|
||||
HighQuality = 3 /*FF_PROFILE_PRORES_HQ*/,
|
||||
FourFourFourFour =
|
||||
4 /*FF_PROFILE_PRORES_4444*/ // Automatically set if I444 or RGB input.
|
||||
};
|
||||
|
||||
private:
|
||||
profile video_profile = profile::Auto;
|
||||
profile video_profile = profile::Auto;
|
||||
ffmpeg::avframe_queue frame_queue;
|
||||
ffmpeg::avframe_queue frame_queue_used;
|
||||
AVPacket* current_packet = nullptr;
|
||||
|
||||
|
||||
AVPacket* current_packet = nullptr;
|
||||
|
||||
public:
|
||||
prores_aw(obs_data_t* settings, obs_encoder_t* encoder);
|
||||
@@ -76,5 +73,3 @@ namespace obsffmpeg {
|
||||
};
|
||||
} // namespace encoder
|
||||
} // namespace obsffmpeg
|
||||
|
||||
#endif OBS_FFMPEG_ENCODER_PRORES_AW
|
||||
|
||||
@@ -18,13 +18,15 @@
|
||||
#include "avframe-queue.hpp"
|
||||
#include "tools.hpp"
|
||||
|
||||
AVFrame* ffmpeg::avframe_queue::create_frame()
|
||||
std::shared_ptr<AVFrame> ffmpeg::avframe_queue::create_frame()
|
||||
{
|
||||
AVFrame* frame = av_frame_alloc();
|
||||
frame->width = this->resolution.first;
|
||||
frame->height = this->resolution.second;
|
||||
frame->format = this->format;
|
||||
int res = av_frame_get_buffer(frame, 0);
|
||||
std::shared_ptr<AVFrame> frame =
|
||||
std::shared_ptr<AVFrame>(av_frame_alloc(), [](AVFrame* frame) { av_frame_free(&frame); });
|
||||
frame->width = this->resolution.first;
|
||||
frame->height = this->resolution.second;
|
||||
frame->format = this->format;
|
||||
|
||||
int res = av_frame_get_buffer(frame.get(), 0);
|
||||
if (res < 0) {
|
||||
throw std::exception(ffmpeg::tools::get_error_description(res));
|
||||
}
|
||||
@@ -32,14 +34,6 @@ AVFrame* ffmpeg::avframe_queue::create_frame()
|
||||
return frame;
|
||||
}
|
||||
|
||||
void ffmpeg::avframe_queue::destroy_frame(AVFrame* frame)
|
||||
{
|
||||
if (frame == nullptr)
|
||||
return;
|
||||
|
||||
av_frame_free(&frame);
|
||||
}
|
||||
|
||||
ffmpeg::avframe_queue::avframe_queue() {}
|
||||
|
||||
ffmpeg::avframe_queue::~avframe_queue()
|
||||
@@ -89,23 +83,19 @@ void ffmpeg::avframe_queue::precache(size_t count)
|
||||
void ffmpeg::avframe_queue::clear()
|
||||
{
|
||||
std::unique_lock<std::mutex> ulock(this->lock);
|
||||
for (AVFrame* frame : frames) {
|
||||
destroy_frame(frame);
|
||||
}
|
||||
frames.clear();
|
||||
}
|
||||
|
||||
void ffmpeg::avframe_queue::push(AVFrame* frame)
|
||||
void ffmpeg::avframe_queue::push(std::shared_ptr<AVFrame> frame)
|
||||
{
|
||||
std::unique_lock<std::mutex> ulock(this->lock);
|
||||
frames.push_back(frame);
|
||||
}
|
||||
|
||||
AVFrame* ffmpeg::avframe_queue::pop()
|
||||
std::shared_ptr<AVFrame> ffmpeg::avframe_queue::pop()
|
||||
{
|
||||
std::unique_lock<std::mutex> ulock(this->lock);
|
||||
|
||||
AVFrame* ret = nullptr;
|
||||
std::shared_ptr<AVFrame> ret;
|
||||
while (ret == nullptr) {
|
||||
if (frames.size() == 0) {
|
||||
ret = create_frame();
|
||||
@@ -117,7 +107,6 @@ AVFrame* ffmpeg::avframe_queue::pop()
|
||||
frames.pop_front();
|
||||
if ((ret->width != this->resolution.first) || (ret->height != this->resolution.second)
|
||||
|| (ret->format != this->format)) {
|
||||
destroy_frame(ret);
|
||||
ret = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -126,13 +115,13 @@ AVFrame* ffmpeg::avframe_queue::pop()
|
||||
return ret;
|
||||
}
|
||||
|
||||
AVFrame* ffmpeg::avframe_queue::pop_only()
|
||||
std::shared_ptr<AVFrame> ffmpeg::avframe_queue::pop_only()
|
||||
{
|
||||
std::unique_lock<std::mutex> ulock(this->lock);
|
||||
if (frames.size() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
AVFrame* ret = frames.front();
|
||||
std::shared_ptr<AVFrame> ret = frames.front();
|
||||
if (ret == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -15,27 +15,26 @@
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef OBS_FFMPEG_FFMPEG_AVFRAME_QUEUE
|
||||
#define OBS_FFMPEG_FFMPEG_AVFRAME_QUEUE
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
|
||||
extern "C" {
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavutil/frame.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
namespace ffmpeg {
|
||||
class avframe_queue {
|
||||
std::deque<AVFrame*> frames;
|
||||
std::mutex lock;
|
||||
std::deque<std::shared_ptr<AVFrame>> frames;
|
||||
std::mutex lock;
|
||||
|
||||
std::pair<uint32_t, uint32_t> resolution;
|
||||
AVPixelFormat format = AV_PIX_FMT_NONE;
|
||||
|
||||
AVFrame* create_frame();
|
||||
void destroy_frame(AVFrame* frame);
|
||||
std::shared_ptr<AVFrame> create_frame();
|
||||
|
||||
public:
|
||||
avframe_queue();
|
||||
@@ -53,18 +52,14 @@ namespace ffmpeg {
|
||||
|
||||
void clear();
|
||||
|
||||
void push(AVFrame* frame);
|
||||
void push(std::shared_ptr<AVFrame> frame);
|
||||
|
||||
AVFrame* pop();
|
||||
std::shared_ptr<AVFrame> pop();
|
||||
|
||||
AVFrame* pop_only();
|
||||
std::shared_ptr<AVFrame> pop_only();
|
||||
|
||||
bool empty();
|
||||
|
||||
size_t size();
|
||||
|
||||
|
||||
};
|
||||
} // namespace ffmpeg
|
||||
|
||||
#endif OBS_FFMPEG_FFMPEG_AVFRAME_QUEUE
|
||||
|
||||
@@ -94,7 +94,11 @@ void ffmpeg::swscale::set_target_size(uint32_t width, uint32_t height)
|
||||
target_size.second = height;
|
||||
}
|
||||
|
||||
void ffmpeg::swscale::get_target_size(uint32_t& width, uint32_t& height) {}
|
||||
void ffmpeg::swscale::get_target_size(uint32_t& width, uint32_t& height)
|
||||
{
|
||||
width = target_size.first;
|
||||
height = target_size.second;
|
||||
}
|
||||
|
||||
std::pair<uint32_t, uint32_t> ffmpeg::swscale::get_target_size()
|
||||
{
|
||||
|
||||
@@ -23,8 +23,11 @@
|
||||
#include <utility>
|
||||
|
||||
extern "C" {
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavutil/pixfmt.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
namespace ffmpeg {
|
||||
|
||||
+90
-1
@@ -16,11 +16,63 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "tools.hpp"
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
extern "C" {
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/error.h>
|
||||
#include <libavutil/pixdesc.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
std::string ffmpeg::tools::translate_encoder_capabilities(int capabilities)
|
||||
{
|
||||
// Sorted by relative importance.
|
||||
std::pair<int, std::string> caps[] = {
|
||||
{AV_CODEC_CAP_EXPERIMENTAL, "Experimental"},
|
||||
|
||||
// Quality
|
||||
{AV_CODEC_CAP_LOSSLESS, "Lossless"},
|
||||
{AV_CODEC_CAP_INTRA_ONLY, "I-Frames only"},
|
||||
|
||||
// Threading
|
||||
{AV_CODEC_CAP_FRAME_THREADS, "Frame-Threading"},
|
||||
{AV_CODEC_CAP_SLICE_THREADS, "Slice-Threading"},
|
||||
{AV_CODEC_CAP_AUTO_THREADS, "Automatic-Threading"},
|
||||
|
||||
// Features
|
||||
{AV_CODEC_CAP_PARAM_CHANGE, "Dynamic Parameter Change"},
|
||||
{AV_CODEC_CAP_SUBFRAMES, "Sub-Frames"},
|
||||
{AV_CODEC_CAP_VARIABLE_FRAME_SIZE, "Variable Frame Size"},
|
||||
{AV_CODEC_CAP_SMALL_LAST_FRAME, "Small Final Frame"},
|
||||
//{AV_CODEC_CAP_DR1, "Uses get_buffer"},
|
||||
{AV_CODEC_CAP_DELAY, "Requires Flush"},
|
||||
|
||||
// Other
|
||||
{AV_CODEC_CAP_TRUNCATED, "Truncated"},
|
||||
{AV_CODEC_CAP_CHANNEL_CONF, "AV_CODEC_CAP_CHANNEL_CONF"},
|
||||
{AV_CODEC_CAP_DRAW_HORIZ_BAND, "AV_CODEC_CAP_DRAW_HORIZ_BAND"},
|
||||
{AV_CODEC_CAP_HWACCEL_VDPAU, "AV_CODEC_CAP_HWACCEL_VDPAU"},
|
||||
{AV_CODEC_CAP_AVOID_PROBING, "AV_CODEC_CAP_AVOID_PROBING"},
|
||||
};
|
||||
|
||||
std::stringstream sstr;
|
||||
for (auto kv : caps) {
|
||||
if (capabilities & kv.first) {
|
||||
capabilities &= ~kv.first;
|
||||
sstr << kv.second;
|
||||
if (capabilities != 0) {
|
||||
sstr << ", ";
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
const char* ffmpeg::tools::get_pixel_format_name(AVPixelFormat v)
|
||||
@@ -67,10 +119,47 @@ const char* ffmpeg::tools::get_error_description(int error)
|
||||
switch (error) {
|
||||
case AVERROR(EPERM):
|
||||
return "Permission Denied";
|
||||
// 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 for Parameter";
|
||||
return "Invalid Value(s)";
|
||||
case AVERROR(ERANGE):
|
||||
return "Out of Range";
|
||||
// case AVERROR(EILSEQ):
|
||||
// case AVERROR(STRUNCATE):
|
||||
}
|
||||
return "Not Translated Yet";
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <obs.h>
|
||||
#include <string>
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/pixfmt.h>
|
||||
@@ -27,6 +28,8 @@ extern "C" {
|
||||
|
||||
namespace ffmpeg {
|
||||
namespace tools {
|
||||
std::string translate_encoder_capabilities(int capabilities);
|
||||
|
||||
const char* get_pixel_format_name(AVPixelFormat v);
|
||||
|
||||
const char* get_color_space_name(AVColorSpace v);
|
||||
@@ -38,7 +41,7 @@ namespace ffmpeg {
|
||||
AVColorSpace obs_videocolorspace_to_avcolorspace(video_colorspace v);
|
||||
|
||||
AVColorRange obs_videorangetype_to_avcolorrange(video_range_type v);
|
||||
}
|
||||
} // namespace tools
|
||||
} // namespace ffmpeg
|
||||
|
||||
#endif OBS_FFMPEG_FFMPEG_UTILITY
|
||||
|
||||
+69
-20
@@ -16,40 +16,89 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "plugin.hpp"
|
||||
#include <obs-module.h>
|
||||
#include <obs.h>
|
||||
#include <memory>
|
||||
#include "encoders/generic.hpp"
|
||||
#include "encoders/prores_aw.hpp"
|
||||
#include "ui/debug_handler.hpp"
|
||||
#include "ui/handler.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include "encoders/prores_aw.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <obs-module.h>
|
||||
#include <obs.h>
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavcodec/avcodec.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
MODULE_EXPORT bool obs_module_load(void)
|
||||
// Initializers and finalizers.
|
||||
std::list<std::function<void()>> obsffmpeg::initializers;
|
||||
|
||||
std::list<std::function<void()>> obsffmpeg::finalizers;
|
||||
|
||||
// Codec to Handler mapping.
|
||||
static std::map<std::string, std::shared_ptr<obsffmpeg::ui::handler>> codec_to_handler_map;
|
||||
static std::shared_ptr<obsffmpeg::ui::handler> debug_handler = std::make_shared<obsffmpeg::ui::debug_handler>();
|
||||
|
||||
void obsffmpeg::register_codec_handler(std::string codec, std::shared_ptr<obsffmpeg::ui::handler> handler)
|
||||
{
|
||||
try {
|
||||
avcodec_register_all();
|
||||
obsffmpeg::encoder::prores_aw::initialize();
|
||||
return true;
|
||||
} catch (std::exception ex) {
|
||||
PLOG_ERROR("Exception during initalization: %s.", ex.what());
|
||||
} catch (...) {
|
||||
PLOG_ERROR("Unrecognized exception during initalization.");
|
||||
codec_to_handler_map.emplace(codec, handler);
|
||||
}
|
||||
|
||||
std::shared_ptr<obsffmpeg::ui::handler> obsffmpeg::find_codec_handler(std::string codec)
|
||||
{
|
||||
auto found = codec_to_handler_map.find(codec);
|
||||
if (found == codec_to_handler_map.end())
|
||||
return debug_handler;
|
||||
return found->second;
|
||||
}
|
||||
|
||||
static std::map<AVCodec*, std::shared_ptr<encoder::generic_factory>> generic_factories;
|
||||
|
||||
MODULE_EXPORT bool obs_module_load(void)
|
||||
try {
|
||||
// Initialize avcodec.
|
||||
avcodec_register_all();
|
||||
|
||||
// Run all initializers.
|
||||
for (auto func : obsffmpeg::initializers) {
|
||||
func();
|
||||
}
|
||||
|
||||
// Register all codecs.
|
||||
AVCodec* cdc = nullptr;
|
||||
while ((cdc = av_codec_next(cdc)) != nullptr) {
|
||||
if (!av_codec_is_encoder(cdc))
|
||||
continue;
|
||||
|
||||
if ((cdc->type == AVMediaType::AVMEDIA_TYPE_AUDIO) || (cdc->type == AVMediaType::AVMEDIA_TYPE_VIDEO)) {
|
||||
auto ptr = std::make_shared<encoder::generic_factory>(cdc);
|
||||
ptr->register_encoder();
|
||||
generic_factories.emplace(cdc, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
obsffmpeg::encoder::prores_aw::initialize();
|
||||
return true;
|
||||
} catch (std::exception ex) {
|
||||
PLOG_ERROR("Exception during initalization: %s.", ex.what());
|
||||
return false;
|
||||
} catch (...) {
|
||||
PLOG_ERROR("Unrecognized exception during initalization.");
|
||||
return false;
|
||||
}
|
||||
|
||||
MODULE_EXPORT void obs_module_unload(void)
|
||||
{
|
||||
try {
|
||||
obsffmpeg::encoder::prores_aw::finalize();
|
||||
} catch (std::exception ex) {
|
||||
PLOG_ERROR("Exception during finalizing: %s.", ex.what());
|
||||
} catch (...) {
|
||||
PLOG_ERROR("Unrecognized exception during finalizing.");
|
||||
try {
|
||||
obsffmpeg::encoder::prores_aw::finalize();
|
||||
|
||||
// Run all finalizers.
|
||||
for (auto func : obsffmpeg::finalizers) {
|
||||
func();
|
||||
}
|
||||
} catch (std::exception ex) {
|
||||
PLOG_ERROR("Exception during finalizing: %s.", ex.what());
|
||||
} catch (...) {
|
||||
PLOG_ERROR("Unrecognized exception during finalizing.");
|
||||
}
|
||||
|
||||
+12
-5
@@ -1,5 +1,5 @@
|
||||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (C) 2018 - 2018 Michael Fabian Dirks
|
||||
// Copyright (C) 2018 - 2019 Michael Fabian Dirks
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -15,16 +15,23 @@
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef OBS_FFMPEG_PLUGIN_HPP
|
||||
#define OBS_FFMPEG_PLUGIN_HPP
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <obs-module.h>
|
||||
#include "ui/handler.hpp"
|
||||
|
||||
namespace obsffmpeg {
|
||||
extern std::list<std::function<void()>> initializers;
|
||||
|
||||
extern std::list<std::function<void()>> finalizers;
|
||||
|
||||
void register_codec_handler(std::string codec, std::shared_ptr<obsffmpeg::ui::handler> handler);
|
||||
|
||||
std::shared_ptr<obsffmpeg::ui::handler> find_codec_handler(std::string codec);
|
||||
|
||||
} // namespace obsffmpeg
|
||||
|
||||
#endif OBS_FFMPEG_PLUGIN_HPP
|
||||
MODULE_EXPORT bool obs_module_load(void);
|
||||
MODULE_EXPORT void obs_module_unload(void);
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (C) 2018 - 2019 Michael Fabian Dirks
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#pragma once
|
||||
#include "utility.hpp"
|
||||
@@ -0,0 +1,203 @@
|
||||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (C) 2018 - 2019 Michael Fabian Dirks
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "debug_handler.hpp"
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include "handler.hpp"
|
||||
#include "plugin.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <obs-properties.h>
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavutil/opt.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::debug_handler::get_defaults(obs_data_t* settings, AVCodec* codec, AVCodecContext* context) {}
|
||||
|
||||
template<typename T>
|
||||
std::string to_string(T value){};
|
||||
|
||||
template<>
|
||||
std::string to_string(int64_t value)
|
||||
{
|
||||
std::vector<char> buf(32);
|
||||
snprintf(buf.data(), buf.size(), "%lld", value);
|
||||
return std::string(buf.data(), buf.data() + buf.size());
|
||||
}
|
||||
|
||||
template<>
|
||||
std::string to_string(uint64_t value)
|
||||
{
|
||||
std::vector<char> buf(32);
|
||||
snprintf(buf.data(), buf.size(), "%llu", value);
|
||||
return std::string(buf.data(), buf.data() + buf.size());
|
||||
}
|
||||
|
||||
template<>
|
||||
std::string to_string(double_t value)
|
||||
{
|
||||
std::vector<char> buf(32);
|
||||
snprintf(buf.data(), buf.size(), "%f", value);
|
||||
return std::string(buf.data(), buf.data() + buf.size());
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::debug_handler::get_properties(obs_properties_t* props, AVCodec* codec, AVCodecContext* context)
|
||||
{
|
||||
if (context)
|
||||
return;
|
||||
|
||||
AVCodecContext* ctx = avcodec_alloc_context3(codec);
|
||||
if (!ctx->priv_data)
|
||||
return;
|
||||
|
||||
PLOG_INFO("Options for '%s':", codec->name);
|
||||
|
||||
std::pair<AVOptionType, std::string> opt_type_name[] = {
|
||||
{AV_OPT_TYPE_FLAGS, "Flags"},
|
||||
{AV_OPT_TYPE_INT, "Int"},
|
||||
{AV_OPT_TYPE_INT64, "Int64"},
|
||||
{AV_OPT_TYPE_DOUBLE, "Double"},
|
||||
{AV_OPT_TYPE_FLOAT, "Float"},
|
||||
{AV_OPT_TYPE_STRING, "String"},
|
||||
{AV_OPT_TYPE_RATIONAL, "Rational"},
|
||||
{AV_OPT_TYPE_BINARY, "Binary"},
|
||||
{AV_OPT_TYPE_DICT, "Dictionary"},
|
||||
{AV_OPT_TYPE_UINT64, "Unsigned Int64"},
|
||||
{AV_OPT_TYPE_CONST, "Constant"},
|
||||
{AV_OPT_TYPE_IMAGE_SIZE, "Image Size"},
|
||||
{AV_OPT_TYPE_PIXEL_FMT, "Pixel Format"},
|
||||
{AV_OPT_TYPE_SAMPLE_FMT, "Sample Format"},
|
||||
{AV_OPT_TYPE_VIDEO_RATE, "Video Rate"},
|
||||
{AV_OPT_TYPE_DURATION, "Duration"},
|
||||
{AV_OPT_TYPE_COLOR, "Color"},
|
||||
{AV_OPT_TYPE_CHANNEL_LAYOUT, "Layout"},
|
||||
{AV_OPT_TYPE_BOOL, "Bool"},
|
||||
};
|
||||
std::map<std::string, AVOptionType> unit_types;
|
||||
|
||||
const AVOption* opt = nullptr;
|
||||
while ((opt = av_opt_next(ctx->priv_data, opt)) != nullptr) {
|
||||
std::string type_name = "";
|
||||
for (auto kv : opt_type_name) {
|
||||
if (opt->type == kv.first) {
|
||||
type_name = kv.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (opt->type == AV_OPT_TYPE_CONST) {
|
||||
if (opt->unit == nullptr) {
|
||||
PLOG_INFO(" Constant '%s' and help text '%s' with unknown settings.", opt->name,
|
||||
opt->help);
|
||||
} else {
|
||||
auto unit_type = unit_types.find(opt->unit);
|
||||
if (unit_type == unit_types.end()) {
|
||||
PLOG_INFO(" [%s] Flag '%s' and help text '%s' with value '%lld'.", opt->unit,
|
||||
opt->name, opt->help, opt->default_val.i64);
|
||||
} else {
|
||||
std::string out;
|
||||
switch (unit_type->second) {
|
||||
case AV_OPT_TYPE_BOOL:
|
||||
out = opt->default_val.i64 ? "true" : "false";
|
||||
break;
|
||||
case AV_OPT_TYPE_INT:
|
||||
out = to_string(opt->default_val.i64);
|
||||
break;
|
||||
case AV_OPT_TYPE_UINT64:
|
||||
out = to_string(static_cast<uint64_t>(opt->default_val.i64));
|
||||
break;
|
||||
case AV_OPT_TYPE_FLAGS:
|
||||
out = to_string(static_cast<uint64_t>(opt->default_val.i64));
|
||||
break;
|
||||
case AV_OPT_TYPE_FLOAT:
|
||||
case AV_OPT_TYPE_DOUBLE:
|
||||
out = to_string(opt->default_val.dbl);
|
||||
break;
|
||||
case AV_OPT_TYPE_STRING:
|
||||
out = opt->default_val.str;
|
||||
break;
|
||||
case AV_OPT_TYPE_BINARY:
|
||||
case AV_OPT_TYPE_IMAGE_SIZE:
|
||||
case AV_OPT_TYPE_PIXEL_FMT:
|
||||
case AV_OPT_TYPE_SAMPLE_FMT:
|
||||
case AV_OPT_TYPE_VIDEO_RATE:
|
||||
case AV_OPT_TYPE_DURATION:
|
||||
case AV_OPT_TYPE_COLOR:
|
||||
case AV_OPT_TYPE_CHANNEL_LAYOUT:
|
||||
break;
|
||||
}
|
||||
|
||||
PLOG_INFO(" [%s] Constant '%s' and help text '%s' with value '%s'.", opt->unit,
|
||||
opt->name, opt->help, out.c_str());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (opt->unit != nullptr) {
|
||||
unit_types.emplace(opt->name, opt->type);
|
||||
}
|
||||
|
||||
std::string minimum = "", maximum = "", out;
|
||||
minimum = to_string(opt->min);
|
||||
maximum = to_string(opt->max);
|
||||
{
|
||||
switch (opt->type) {
|
||||
case AV_OPT_TYPE_BOOL:
|
||||
out = opt->default_val.i64 ? "true" : "false";
|
||||
break;
|
||||
case AV_OPT_TYPE_INT:
|
||||
out = to_string(opt->default_val.i64);
|
||||
break;
|
||||
case AV_OPT_TYPE_UINT64:
|
||||
out = to_string(static_cast<uint64_t>(opt->default_val.i64));
|
||||
break;
|
||||
case AV_OPT_TYPE_FLAGS:
|
||||
out = to_string(static_cast<uint64_t>(opt->default_val.i64));
|
||||
break;
|
||||
case AV_OPT_TYPE_FLOAT:
|
||||
case AV_OPT_TYPE_DOUBLE:
|
||||
out = to_string(opt->default_val.dbl);
|
||||
break;
|
||||
case AV_OPT_TYPE_STRING:
|
||||
out = opt->default_val.str ? opt->default_val.str : "<invalid>";
|
||||
break;
|
||||
case AV_OPT_TYPE_BINARY:
|
||||
case AV_OPT_TYPE_IMAGE_SIZE:
|
||||
case AV_OPT_TYPE_PIXEL_FMT:
|
||||
case AV_OPT_TYPE_SAMPLE_FMT:
|
||||
case AV_OPT_TYPE_VIDEO_RATE:
|
||||
case AV_OPT_TYPE_DURATION:
|
||||
case AV_OPT_TYPE_COLOR:
|
||||
case AV_OPT_TYPE_CHANNEL_LAYOUT:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PLOG_INFO(
|
||||
" Option '%s'%s%s%s with help '%s' of type '%s' with default value '%s', minimum '%s' and maximum '%s'.",
|
||||
opt->name, opt->unit ? " with unit (" : "", opt->unit ? opt->unit : "",
|
||||
opt->unit ? ")" : "", opt->help, type_name.c_str(), out.c_str(), minimum.c_str(),
|
||||
maximum.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::debug_handler::update(obs_data_t* settings, AVCodec* codec, AVCodecContext* context) {}
|
||||
@@ -0,0 +1,34 @@
|
||||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (C) 2018 - 2019 Michael Fabian Dirks
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#pragma once
|
||||
#include "handler.hpp"
|
||||
|
||||
namespace obsffmpeg {
|
||||
namespace ui {
|
||||
class debug_handler : public handler {
|
||||
public:
|
||||
virtual void get_defaults(obs_data_t* settings, AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
|
||||
virtual void get_properties(obs_properties_t* props, AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
|
||||
virtual void update(obs_data_t* settings, AVCodec* codec, AVCodecContext* context) override;
|
||||
};
|
||||
} // namespace ui
|
||||
} // namespace obsffmpeg
|
||||
@@ -0,0 +1,18 @@
|
||||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (C) 2018 - 2019 Michael Fabian Dirks
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "handler.hpp"
|
||||
@@ -0,0 +1,40 @@
|
||||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (C) 2018 - 2019 Michael Fabian Dirks
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#pragma once
|
||||
|
||||
extern "C" {
|
||||
#include <obs-properties.h>
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavcodec/avcodec.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
namespace obsffmpeg {
|
||||
namespace ui {
|
||||
class handler {
|
||||
public:
|
||||
virtual void get_defaults(obs_data_t* settings, AVCodec* codec, AVCodecContext* context) = 0;
|
||||
|
||||
virtual void get_properties(obs_properties_t* props, AVCodec* codec,
|
||||
AVCodecContext* context) = 0;
|
||||
|
||||
virtual void update(obs_data_t* settings, AVCodec* codec, AVCodecContext* context) = 0;
|
||||
};
|
||||
} // namespace ui
|
||||
} // namespace obsffmpeg
|
||||
@@ -0,0 +1,434 @@
|
||||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (C) 2018 - 2019 Michael Fabian Dirks
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "libvpx_vp9_handler.hpp"
|
||||
#include "plugin.hpp"
|
||||
#include "strings.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <obs-module.h>
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavutil/opt.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
/*
|
||||
[obs-ffmpeg-encoder] Options for 'libvpx-vp9':
|
||||
[obs-ffmpeg-encoder] Option 'auto-alt-ref' with help 'Enable use of alternate reference frames (2-pass only)' of type 'Int' with default value '-1', minimum '-1.000000' and maximum '2.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'lag-in-frames' with help 'Number of frames to look ahead for alternate reference frame selection' of type 'Int' with default value '-1', minimum '-1.000000' and maximum '2147483647.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'arnr-maxframes' with help 'altref noise reduction max frame count' of type 'Int' with default value '-1', minimum '-1.000000' and maximum '2147483647.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'arnr-strength' with help 'altref noise reduction filter strength' of type 'Int' with default value '-1', minimum '-1.000000' and maximum '2147483647.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'arnr-type' with unit (arnr_type) with help 'altref noise reduction filter type' of type 'Int' with default value '-1', minimum '-1.000000' and maximum '2147483647.000000'.
|
||||
[obs-ffmpeg-encoder] [arnr_type] Flag 'backward' and help text '(null)' with value '1'.
|
||||
[obs-ffmpeg-encoder] [arnr_type] Flag 'forward' and help text '(null)' with value '2'.
|
||||
[obs-ffmpeg-encoder] [arnr_type] Flag 'centered' and help text '(null)' with value '3'.
|
||||
[obs-ffmpeg-encoder] Option 'tune' with unit (tune) with help 'Tune the encoding to a specific scenario' of type 'Int' with default value '-1', minimum '-1.000000' and maximum '2147483647.000000'.
|
||||
[obs-ffmpeg-encoder] [tune] Constant 'psnr' and help text '(null)' with value '0'.
|
||||
[obs-ffmpeg-encoder] [tune] Constant 'ssim' and help text '(null)' with value '1'.
|
||||
[obs-ffmpeg-encoder] Option 'deadline' with unit (quality) with help 'Time to spend encoding, in microseconds.' of type 'Int' with default value '1000000', minimum '-2147483648.000000' and maximum '2147483647.000000'.
|
||||
[obs-ffmpeg-encoder] [quality] Flag 'best' and help text '(null)' with value '0'.
|
||||
[obs-ffmpeg-encoder] [quality] Flag 'good' and help text '(null)' with value 'F4240'.
|
||||
[obs-ffmpeg-encoder] [quality] Flag 'realtime' and help text '(null)' with value '1'.
|
||||
[obs-ffmpeg-encoder] Option 'error-resilient' with unit (er) with help 'Error resilience configuration' of type 'Flags' with default value '0', minimum '-2147483648.000000' and maximum '2147483647.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'max-intra-rate' with help 'Maximum I-frame bitrate (pct) 0=unlimited' of type 'Int' with default value '-1', minimum '-1.000000' and maximum '2147483647.000000'.
|
||||
[obs-ffmpeg-encoder] [er] Flag 'default' and help text 'Improve resiliency against losses of whole frames' with value '1'.
|
||||
[obs-ffmpeg-encoder] [er] Flag 'partitions' and help text 'The frame partitions are independently decodable by the bool decoder, meaning that partitions can be decoded even though earlier partitions have been lost. Note that intra predicition is still done over the partition boundary.' with value '2'.
|
||||
[obs-ffmpeg-encoder] Option 'crf' with help 'Select the quality for constant quality mode' of type 'Int' with default value '-1', minimum '-1.000000' and maximum '63.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'static-thresh' with help 'A change threshold on blocks below which they will be skipped by the encoder' of type 'Int' with default value '0', minimum '0.000000' and maximum '2147483647.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'drop-threshold' with help 'Frame drop threshold' of type 'Int' with default value '0', minimum '-2147483648.000000' and maximum '2147483647.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'noise-sensitivity' with help 'Noise sensitivity' of type 'Int' with default value '0', minimum '0.000000' and maximum '4.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'undershoot-pct' with help 'Datarate undershoot (min) target (%)' of type 'Int' with default value '-1', minimum '-1.000000' and maximum '100.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'overshoot-pct' with help 'Datarate overshoot (max) target (%)' of type 'Int' with default value '-1', minimum '-1.000000' and maximum '1000.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'cpu-used' with help 'Quality/Speed ratio modifier' of type 'Int' with default value '1', minimum '-8.000000' and maximum '8.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'lossless' with help 'Lossless mode' of type 'Int' with default value '-1', minimum '-1.000000' and maximum '1.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'tile-columns' with help 'Number of tile columns to use, log2' of type 'Int' with default value '-1', minimum '-1.000000' and maximum '6.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'tile-rows' with help 'Number of tile rows to use, log2' of type 'Int' with default value '-1', minimum '-1.000000' and maximum '2.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'frame-parallel' with help 'Enable frame parallel decodability features' of type 'Bool' with default value 'true', minimum '-1.000000' and maximum '1.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'aq-mode' with unit (aq_mode) with help 'adaptive quantization mode' of type 'Int' with default value '-1', minimum '-1.000000' and maximum '4.000000'.
|
||||
[obs-ffmpeg-encoder] [aq_mode] Flag 'none' and help text 'Aq not used' with value '0'.
|
||||
[obs-ffmpeg-encoder] [aq_mode] Flag 'variance' and help text 'Variance based Aq' with value '1'.
|
||||
[obs-ffmpeg-encoder] [aq_mode] Flag 'complexity' and help text 'Complexity based Aq' with value '2'.
|
||||
[obs-ffmpeg-encoder] [aq_mode] Flag 'cyclic' and help text 'Cyclic Refresh Aq' with value '3'.
|
||||
[obs-ffmpeg-encoder] [aq_mode] Flag 'equator360' and help text '360 video Aq' with value '4'.
|
||||
[obs-ffmpeg-encoder] Option 'level' with help 'Specify level' of type 'Float' with default value '-1.000000', minimum '-1.000000' and maximum '6.200000'.
|
||||
[obs-ffmpeg-encoder] Option 'row-mt' with help 'Row based multi-threading' of type 'Bool' with default value 'true', minimum '-1.000000' and maximum '1.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'speed' with help '' of type 'Int' with default value '1', minimum '-16.000000' and maximum '16.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'quality' with unit (quality) with help '' of type 'Int' with default value '1000000', minimum '-2147483648.000000' and maximum '2147483647.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'vp8flags' with unit (flags) with help '' of type 'Flags' with default value '0', minimum '0.000000' and maximum '4294967295.000000'.
|
||||
[obs-ffmpeg-encoder] [flags] Flag 'error_resilient' and help text 'enable error resilience' with value '1'.
|
||||
[obs-ffmpeg-encoder] [flags] Flag 'altref' and help text 'enable use of alternate reference frames (VP8/2-pass only)' with value '2'.
|
||||
[obs-ffmpeg-encoder] Option 'arnr_max_frames' with help 'altref noise reduction max frame count' of type 'Int' with default value '0', minimum '0.000000' and maximum '15.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'arnr_strength' with help 'altref noise reduction filter strength' of type 'Int' with default value '3', minimum '0.000000' and maximum '6.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'arnr_type' with help 'altref noise reduction filter type' of type 'Int' with default value '3', minimum '1.000000' and maximum '3.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'rc_lookahead' with help 'Number of frames to look ahead for alternate reference frame selection' of type 'Int' with default value '25', minimum '0.000000' and maximum '25.000000'.
|
||||
*/
|
||||
|
||||
INITIALIZER(libvpx_vp9_handler_init)
|
||||
{
|
||||
obsffmpeg::initializers.push_back([]() {
|
||||
obsffmpeg::register_codec_handler("libvpx-vp9", std::make_shared<obsffmpeg::ui::libvpx_vp9_handler>());
|
||||
});
|
||||
};
|
||||
|
||||
#define P_RATECONTROL "VP9.RateControl"
|
||||
#define P_RATECONTROL_MODE "VP9.RateControl.Mode"
|
||||
#define P_RATECONTROL_MODE_(x) "VP9.RateControl.Mode." D_VSTR(x)
|
||||
#define P_RATECONTROL_QUALITY "VP9.RateControl.Quality"
|
||||
#define P_RATECONTROL_BITRATE_TARGET "VP9.RateControl.Bitrate.Target"
|
||||
#define P_RATECONTROL_BITRATE_MINIMUM "VP9.RateControl.Bitrate.Minimum"
|
||||
#define P_RATECONTROL_BITRATE_MAXIMUM "VP9.RateControl.Bitrate.Maximum"
|
||||
#define P_RATECONTROL_BUFFERSIZE "VP9.RateControl.BufferSize"
|
||||
|
||||
#define P_KEYFRAMES "VP9.KeyFrames"
|
||||
#define P_KEYFRAMES_INTERVALTYPE "VP9.KeyFrames.IntervalType"
|
||||
#define P_KEYFRAMES_INTERVALTYPE_(x) "VP9.KeyFrames.IntervalType." D_VSTR(x)
|
||||
#define P_KEYFRAMES_INTERVAL "VP9.KeyFrames.Interval"
|
||||
#define P_KEYFRAMES_INTERVAL_SECONDS "VP9.KeyFrames.Interval.Seconds"
|
||||
#define P_KEYFRAMES_INTERVAL_FRAMES "VP9.KeyFrames.Interval.Frames"
|
||||
|
||||
#define P_PERFORMANCE "VP9.Performance"
|
||||
#define P_PERFORMANCE_QUALITYSPEEDRATIO P_PERFORMANCE ".QualitySpeedRatio"
|
||||
#define P_PERFORMANCE_DEADLINE P_PERFORMANCE ".Deadline"
|
||||
#define P_PERFORMANCE_TILING P_PERFORMANCE ".Tiling"
|
||||
#define P_PERFORMANCE_TILING_ROWS P_PERFORMANCE_TILING ".Rows"
|
||||
#define P_PERFORMANCE_TILING_COLUMNS P_PERFORMANCE_TILING ".Columns"
|
||||
|
||||
#define S_RATECONTROL_MODE_CBR "CBR"
|
||||
#define S_RATECONTROL_MODE_ABR "ABR"
|
||||
#define S_RATECONTROL_MODE_VBR "VBR"
|
||||
#define S_RATECONTROL_MODE_CQ "CQ"
|
||||
#define S_RATECONTROL_MODE_VQ "VQ"
|
||||
#define S_RATECONTROL_MODE_LL "LL"
|
||||
|
||||
std::pair<std::string, std::string> rc_modes[] = {
|
||||
|
||||
// CBR defines bitrate, minrate, maxrate, buffersize. Min and maxrate are bitrate.
|
||||
{S_RATECONTROL_MODE_CBR, P_RATECONTROL_MODE_(ConstantBitrate)},
|
||||
// ABR defines bitrate
|
||||
{S_RATECONTROL_MODE_ABR, P_RATECONTROL_MODE_(AverageBitrate)},
|
||||
// VBR defines bitrate, minrate, maxrate (and buffersize?)
|
||||
{S_RATECONTROL_MODE_VBR, P_RATECONTROL_MODE_(VariableBitrate)},
|
||||
// CQ defines "crf" and sets bitrate to 0.
|
||||
{S_RATECONTROL_MODE_CQ, P_RATECONTROL_MODE_(ConstantQuality)},
|
||||
// VQ defines "crf" and non-zero bitrate maximum.
|
||||
{S_RATECONTROL_MODE_VQ, P_RATECONTROL_MODE_(VariableQuality)},
|
||||
// Lossless defines nothing but lossless.
|
||||
{S_RATECONTROL_MODE_LL, P_RATECONTROL_MODE_(Lossless)},
|
||||
};
|
||||
|
||||
void obsffmpeg::ui::libvpx_vp9_handler::get_defaults(obs_data_t* settings, AVCodec*, AVCodecContext*)
|
||||
{
|
||||
obs_data_set_default_string(settings, P_RATECONTROL_MODE, S_RATECONTROL_MODE_CBR);
|
||||
obs_data_set_default_int(settings, P_RATECONTROL_QUALITY, 23);
|
||||
obs_data_set_default_int(settings, P_RATECONTROL_BITRATE_TARGET, 2500);
|
||||
obs_data_set_default_int(settings, P_RATECONTROL_BITRATE_MINIMUM, 2500);
|
||||
obs_data_set_default_int(settings, P_RATECONTROL_BITRATE_MAXIMUM, 2500);
|
||||
obs_data_set_default_int(settings, P_RATECONTROL_BUFFERSIZE, 5000);
|
||||
|
||||
obs_data_set_default_int(settings, P_KEYFRAMES_INTERVALTYPE, 0);
|
||||
obs_data_set_default_double(settings, P_KEYFRAMES_INTERVAL_SECONDS, 2.0);
|
||||
obs_data_set_default_int(settings, P_KEYFRAMES_INTERVAL_FRAMES, 300);
|
||||
|
||||
obs_data_set_default_int(settings, P_PERFORMANCE_QUALITYSPEEDRATIO, 1);
|
||||
obs_data_set_default_double(settings, P_PERFORMANCE_DEADLINE, 100.0);
|
||||
obs_data_set_default_bool(settings, P_PERFORMANCE_TILING, false);
|
||||
obs_data_set_default_int(settings, P_PERFORMANCE_TILING_COLUMNS, -1);
|
||||
obs_data_set_default_int(settings, P_PERFORMANCE_TILING_ROWS, -1);
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::libvpx_vp9_handler::get_properties_codec(obs_properties_t* props, AVCodec* codec)
|
||||
{
|
||||
{
|
||||
auto grp = obs_properties_create();
|
||||
{
|
||||
auto p = obs_properties_add_list(grp, P_RATECONTROL_MODE, TRANSLATE(P_RATECONTROL_MODE),
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_RATECONTROL_MODE)));
|
||||
obs_property_set_modified_callback(p, modified_ratecontrol);
|
||||
for (auto kv : rc_modes) {
|
||||
obs_property_list_add_string(p, TRANSLATE(kv.second.c_str()), kv.first.c_str());
|
||||
}
|
||||
}
|
||||
{
|
||||
auto p = obs_properties_add_int_slider(grp, P_RATECONTROL_QUALITY,
|
||||
TRANSLATE(P_RATECONTROL_QUALITY), 0, 63, 1);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_RATECONTROL_QUALITY)));
|
||||
}
|
||||
{
|
||||
auto p = obs_properties_add_int(grp, P_RATECONTROL_BITRATE_TARGET,
|
||||
TRANSLATE(P_RATECONTROL_BITRATE_TARGET), 1,
|
||||
std::numeric_limits<int32_t>::max(), 1);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_RATECONTROL_BITRATE_TARGET)));
|
||||
}
|
||||
{
|
||||
auto p = obs_properties_add_int(grp, P_RATECONTROL_BITRATE_MINIMUM,
|
||||
TRANSLATE(P_RATECONTROL_BITRATE_MINIMUM), 0,
|
||||
std::numeric_limits<int32_t>::max(), 1);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_RATECONTROL_BITRATE_MINIMUM)));
|
||||
}
|
||||
{
|
||||
auto p = obs_properties_add_int(grp, P_RATECONTROL_BITRATE_MAXIMUM,
|
||||
TRANSLATE(P_RATECONTROL_BITRATE_MAXIMUM), 0,
|
||||
std::numeric_limits<int32_t>::max(), 1);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_RATECONTROL_BITRATE_MAXIMUM)));
|
||||
}
|
||||
{
|
||||
auto p =
|
||||
obs_properties_add_int(grp, P_RATECONTROL_BUFFERSIZE, TRANSLATE(P_RATECONTROL_BUFFERSIZE),
|
||||
0, std::numeric_limits<int32_t>::max(), 1);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_RATECONTROL_BUFFERSIZE)));
|
||||
}
|
||||
|
||||
obs_properties_add_group(props, P_RATECONTROL, TRANSLATE(P_RATECONTROL), OBS_GROUP_NORMAL, grp);
|
||||
}
|
||||
{
|
||||
auto grp = obs_properties_create();
|
||||
{
|
||||
auto p =
|
||||
obs_properties_add_list(grp, P_KEYFRAMES_INTERVALTYPE, TRANSLATE(P_KEYFRAMES_INTERVALTYPE),
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_KEYFRAMES_INTERVALTYPE)));
|
||||
obs_property_set_modified_callback(p, &modified_keyframes);
|
||||
obs_property_list_add_int(p, TRANSLATE(P_KEYFRAMES_INTERVALTYPE_(Seconds)), 0);
|
||||
obs_property_list_add_int(p, TRANSLATE(P_KEYFRAMES_INTERVALTYPE_(Frames)), 1);
|
||||
}
|
||||
{
|
||||
auto p =
|
||||
obs_properties_add_float(grp, P_KEYFRAMES_INTERVAL_SECONDS, TRANSLATE(P_KEYFRAMES_INTERVAL),
|
||||
0.00, std::numeric_limits<int16_t>::max(), 0.01);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_KEYFRAMES_INTERVAL)));
|
||||
}
|
||||
{
|
||||
auto p =
|
||||
obs_properties_add_int(grp, P_KEYFRAMES_INTERVAL_FRAMES, TRANSLATE(P_KEYFRAMES_INTERVAL), 0,
|
||||
std::numeric_limits<int32_t>::max(), 1);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_KEYFRAMES_INTERVAL)));
|
||||
}
|
||||
|
||||
obs_properties_add_group(props, P_KEYFRAMES, TRANSLATE(P_KEYFRAMES), OBS_GROUP_NORMAL, grp);
|
||||
}
|
||||
{
|
||||
auto grp = obs_properties_create();
|
||||
{
|
||||
auto p = obs_properties_add_int_slider(grp, P_PERFORMANCE_QUALITYSPEEDRATIO,
|
||||
TRANSLATE(P_PERFORMANCE_QUALITYSPEEDRATIO), -16, 16, 1);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_PERFORMANCE_QUALITYSPEEDRATIO)));
|
||||
}
|
||||
{
|
||||
auto p = obs_properties_add_float_slider(grp, P_PERFORMANCE_DEADLINE,
|
||||
TRANSLATE(P_PERFORMANCE_DEADLINE), 0.0, 1000.0, 0.01);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_PERFORMANCE_DEADLINE)));
|
||||
}
|
||||
{
|
||||
auto grp2 = obs_properties_create();
|
||||
{
|
||||
auto p =
|
||||
obs_properties_add_int_slider(grp2, P_PERFORMANCE_TILING_COLUMNS,
|
||||
TRANSLATE(P_PERFORMANCE_TILING_COLUMNS), 0, 6, 1);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_PERFORMANCE_TILING_COLUMNS)));
|
||||
}
|
||||
{
|
||||
auto p = obs_properties_add_int_slider(grp2, P_PERFORMANCE_TILING_ROWS,
|
||||
TRANSLATE(P_PERFORMANCE_TILING_ROWS), 0, 2, 1);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_PERFORMANCE_TILING_ROWS)));
|
||||
}
|
||||
|
||||
obs_properties_add_group(grp, P_PERFORMANCE_TILING, TRANSLATE(P_PERFORMANCE_TILING),
|
||||
OBS_GROUP_CHECKABLE, grp2);
|
||||
}
|
||||
obs_properties_add_group(props, P_PERFORMANCE, TRANSLATE(P_PERFORMANCE), OBS_GROUP_NORMAL, grp);
|
||||
}
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::libvpx_vp9_handler::get_properties_encoder(obs_properties_t* props, AVCodec* codec,
|
||||
AVCodecContext* context)
|
||||
{
|
||||
obs_property_set_enabled(obs_properties_get(props, P_RATECONTROL), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, P_KEYFRAMES), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, P_PERFORMANCE), false);
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::libvpx_vp9_handler::get_properties(obs_properties_t* props, AVCodec* codec, AVCodecContext* context)
|
||||
{
|
||||
if (!context) {
|
||||
get_properties_codec(props, codec);
|
||||
} else {
|
||||
get_properties_encoder(props, codec, context);
|
||||
}
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::libvpx_vp9_handler::update(obs_data_t* settings, AVCodec*, AVCodecContext* context)
|
||||
{
|
||||
obs_video_info ovi;
|
||||
if (!obs_get_video_info(&ovi)) {
|
||||
throw std::runtime_error("no video info");
|
||||
}
|
||||
|
||||
{ // Rate Control
|
||||
bool has_quality = false;
|
||||
bool has_bitrate = false;
|
||||
bool has_bitrate_min = false;
|
||||
bool has_bitrate_max = false;
|
||||
bool has_buffer = false;
|
||||
const char* rc_mode = obs_data_get_string(settings, P_RATECONTROL_MODE);
|
||||
if (strcmp(rc_mode, S_RATECONTROL_MODE_LL) == 0) {
|
||||
} else if (strcmp(rc_mode, S_RATECONTROL_MODE_CBR) == 0) {
|
||||
has_bitrate = true;
|
||||
has_buffer = true;
|
||||
} else if (strcmp(rc_mode, S_RATECONTROL_MODE_ABR) == 0) {
|
||||
has_bitrate = true;
|
||||
} else if (strcmp(rc_mode, S_RATECONTROL_MODE_VBR) == 0) {
|
||||
has_bitrate = true;
|
||||
has_bitrate_max = true;
|
||||
has_bitrate_min = true;
|
||||
has_buffer = true;
|
||||
} else if (strcmp(rc_mode, S_RATECONTROL_MODE_CQ) == 0) {
|
||||
has_quality = true;
|
||||
} else if (strcmp(rc_mode, S_RATECONTROL_MODE_VQ) == 0) {
|
||||
has_quality = true;
|
||||
has_bitrate_max = true;
|
||||
}
|
||||
|
||||
if (has_quality) {
|
||||
av_opt_set_int(context->priv_data, "crf", obs_data_get_int(settings, P_RATECONTROL_QUALITY), 0);
|
||||
}
|
||||
if (has_bitrate) {
|
||||
context->bit_rate = static_cast<int>(obs_data_get_int(settings, P_RATECONTROL_BITRATE_TARGET));
|
||||
}
|
||||
if (has_bitrate_min) {
|
||||
context->rc_min_rate =
|
||||
static_cast<int>(obs_data_get_int(settings, P_RATECONTROL_BITRATE_MINIMUM));
|
||||
}
|
||||
if (has_bitrate_max) {
|
||||
context->rc_max_rate =
|
||||
static_cast<int>(obs_data_get_int(settings, P_RATECONTROL_BITRATE_MAXIMUM));
|
||||
}
|
||||
if (has_buffer) {
|
||||
context->rc_buffer_size =
|
||||
static_cast<int>(obs_data_get_int(settings, P_RATECONTROL_BUFFERSIZE));
|
||||
}
|
||||
}
|
||||
|
||||
{ // Key Frames
|
||||
int64_t kf_type = obs_data_get_int(settings, P_KEYFRAMES_INTERVALTYPE);
|
||||
bool is_seconds = (kf_type == 0);
|
||||
|
||||
if (is_seconds) {
|
||||
context->gop_size = static_cast<int>(obs_data_get_double(settings, P_KEYFRAMES_INTERVAL_SECONDS)
|
||||
* (ovi.fps_num / ovi.fps_den));
|
||||
} else {
|
||||
context->gop_size = static_cast<int>(obs_data_get_int(settings, P_KEYFRAMES_INTERVAL_FRAMES));
|
||||
}
|
||||
context->keyint_min = context->gop_size;
|
||||
}
|
||||
|
||||
{ // Performance
|
||||
// Row Multithreading is always on.
|
||||
av_opt_set_int(context->priv_data, "row-mt", 1, 0);
|
||||
|
||||
// Quality/Speed Ratio Modifier
|
||||
{
|
||||
int v = static_cast<int>(obs_data_get_int(settings, P_PERFORMANCE_QUALITYSPEEDRATIO));
|
||||
av_opt_set_int(context->priv_data, "cpu-used", v, 0);
|
||||
av_opt_set_int(context->priv_data, "speed", v, 0);
|
||||
}
|
||||
|
||||
{ // Deadline
|
||||
auto deadline_mul = obs_data_get_double(settings, P_PERFORMANCE_DEADLINE) / 100.0;
|
||||
auto frame_ms = 1000.0 / (ovi.fps_num * ovi.fps_den);
|
||||
int final_ms = static_cast<int>(deadline_mul * frame_ms);
|
||||
av_opt_set_int(context->priv_data, "deadline", final_ms, 0);
|
||||
}
|
||||
|
||||
{ // Tiling
|
||||
int columns, rows;
|
||||
|
||||
columns = context->width / 64;
|
||||
if (columns == 0) {
|
||||
columns = -1;
|
||||
} else if (columns > 6) {
|
||||
columns = 6;
|
||||
}
|
||||
|
||||
rows = context->height / 64;
|
||||
if (rows == 0) {
|
||||
rows = -1;
|
||||
} else if (rows > 2) {
|
||||
rows = 2;
|
||||
}
|
||||
|
||||
av_opt_set_int(context->priv_data, "tile-columns", columns, 0);
|
||||
av_opt_set_int(context->priv_data, "tile-rows", rows, 0);
|
||||
}
|
||||
}
|
||||
|
||||
av_opt_set_int(context->priv_data, "lag-in-frames", 0, 0);
|
||||
av_opt_set_int(context->priv_data, "arnr-maxframes", 0, 0);
|
||||
av_opt_set_int(context->priv_data, "aq-mode", 0, 0);
|
||||
av_opt_set_double(context->priv_data, "level", 6.2, 0);
|
||||
av_opt_set_int(context->priv_data, "rc_lookahead", 0, 0);
|
||||
}
|
||||
|
||||
bool obsffmpeg::ui::libvpx_vp9_handler::modified_ratecontrol(obs_properties_t* props, obs_property_t* property,
|
||||
obs_data_t* settings)
|
||||
{
|
||||
bool has_quality = false;
|
||||
bool has_bitrate = false;
|
||||
bool has_bitrate_min = false;
|
||||
bool has_bitrate_max = false;
|
||||
bool has_buffer = false;
|
||||
|
||||
const char* rc_mode = obs_data_get_string(settings, P_RATECONTROL_MODE);
|
||||
if (strcmp(rc_mode, S_RATECONTROL_MODE_LL) == 0) {
|
||||
} else if (strcmp(rc_mode, S_RATECONTROL_MODE_CBR) == 0) {
|
||||
has_bitrate = true;
|
||||
has_buffer = true;
|
||||
} else if (strcmp(rc_mode, S_RATECONTROL_MODE_ABR) == 0) {
|
||||
has_bitrate = true;
|
||||
} else if (strcmp(rc_mode, S_RATECONTROL_MODE_VBR) == 0) {
|
||||
has_bitrate = true;
|
||||
has_bitrate_max = true;
|
||||
has_bitrate_min = true;
|
||||
has_buffer = true;
|
||||
} else if (strcmp(rc_mode, S_RATECONTROL_MODE_CQ) == 0) {
|
||||
has_quality = true;
|
||||
} else if (strcmp(rc_mode, S_RATECONTROL_MODE_VQ) == 0) {
|
||||
has_quality = true;
|
||||
has_bitrate_max = true;
|
||||
}
|
||||
|
||||
obs_property_set_visible(obs_properties_get(props, P_RATECONTROL_QUALITY), has_quality);
|
||||
obs_property_set_visible(obs_properties_get(props, P_RATECONTROL_BITRATE_TARGET), has_bitrate);
|
||||
obs_property_set_visible(obs_properties_get(props, P_RATECONTROL_BITRATE_MINIMUM), has_bitrate_min);
|
||||
obs_property_set_visible(obs_properties_get(props, P_RATECONTROL_BITRATE_MAXIMUM), has_bitrate_max);
|
||||
obs_property_set_visible(obs_properties_get(props, P_RATECONTROL_BUFFERSIZE), has_buffer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool obsffmpeg::ui::libvpx_vp9_handler::modified_keyframes(obs_properties_t* props, obs_property_t* property,
|
||||
obs_data_t* settings)
|
||||
{
|
||||
bool is_seconds = obs_data_get_int(settings, P_KEYFRAMES_INTERVALTYPE) == 0;
|
||||
|
||||
obs_property_set_visible(obs_properties_get(props, P_KEYFRAMES_INTERVAL_FRAMES), !is_seconds);
|
||||
obs_property_set_visible(obs_properties_get(props, P_KEYFRAMES_INTERVAL_SECONDS), is_seconds);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (C) 2018 - 2019 Michael Fabian Dirks
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#pragma once
|
||||
#include "handler.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <obs-properties.h>
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavcodec/avcodec.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
namespace obsffmpeg {
|
||||
namespace ui {
|
||||
class libvpx_vp9_handler : public handler {
|
||||
public:
|
||||
virtual void get_defaults(obs_data_t* settings, AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
|
||||
void get_properties_codec(obs_properties_t* props, AVCodec* codec);
|
||||
|
||||
void get_properties_encoder(obs_properties_t* props, AVCodec* codec, AVCodecContext* context);
|
||||
|
||||
virtual void get_properties(obs_properties_t* props, AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
|
||||
virtual void update(obs_data_t* settings, AVCodec* codec, AVCodecContext* context) override;
|
||||
|
||||
public:
|
||||
static bool modified_ratecontrol(obs_properties_t* props, obs_property_t* property,
|
||||
obs_data_t* settings);
|
||||
|
||||
static bool modified_keyframes(obs_properties_t* props, obs_property_t* property,
|
||||
obs_data_t* settings);
|
||||
};
|
||||
} // namespace ui
|
||||
} // namespace obsffmpeg
|
||||
@@ -0,0 +1,79 @@
|
||||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (C) 2018 - 2019 Michael Fabian Dirks
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "prores_aw_handler.hpp"
|
||||
#include "plugin.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
extern "C" {
|
||||
#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)
|
||||
{
|
||||
obsffmpeg::initializers.push_back([]() {
|
||||
obsffmpeg::register_codec_handler("prores_aw", std::make_shared<obsffmpeg::ui::prores_aw_handler>());
|
||||
});
|
||||
};
|
||||
|
||||
void obsffmpeg::ui::prores_aw_handler::get_defaults(obs_data_t* settings, AVCodec*, AVCodecContext*)
|
||||
{
|
||||
obs_data_set_default_int(settings, P_PROFILE, 0);
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::prores_aw_handler::get_properties(obs_properties_t* props, AVCodec* codec, AVCodecContext* context)
|
||||
{
|
||||
if (!context) {
|
||||
auto p = obs_properties_add_list(props, P_PROFILE, TRANSLATE(P_PROFILE), OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_INT);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_PROFILE)));
|
||||
for (auto ptr = codec->profiles; ptr->profile != FF_PROFILE_UNKNOWN; ptr++) {
|
||||
if (strcmp("apco", ptr->name) == 0) {
|
||||
obs_property_list_add_int(p, TRANSLATE(P_PROFILE_APCO),
|
||||
static_cast<int64_t>(ptr->profile));
|
||||
} else if (strcmp("apcs", ptr->name) == 0) {
|
||||
obs_property_list_add_int(p, TRANSLATE(P_PROFILE_APCS),
|
||||
static_cast<int64_t>(ptr->profile));
|
||||
} else if (strcmp("apcn", ptr->name) == 0) {
|
||||
obs_property_list_add_int(p, TRANSLATE(P_PROFILE_APCN),
|
||||
static_cast<int64_t>(ptr->profile));
|
||||
} else if (strcmp("apch", ptr->name) == 0) {
|
||||
obs_property_list_add_int(p, TRANSLATE(P_PROFILE_APCH),
|
||||
static_cast<int64_t>(ptr->profile));
|
||||
} else if (strcmp("ap4h", ptr->name) == 0) {
|
||||
obs_property_list_add_int(p, TRANSLATE(P_PROFILE_AP4H),
|
||||
static_cast<int64_t>(ptr->profile));
|
||||
} else {
|
||||
obs_property_list_add_int(p, ptr->name, ptr->profile);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
obs_property_set_enabled(obs_properties_get(props, P_PROFILE), false);
|
||||
}
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::prores_aw_handler::update(obs_data_t* settings, AVCodec*, AVCodecContext* context)
|
||||
{
|
||||
context->profile = static_cast<int>(obs_data_get_int(settings, P_PROFILE));
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (C) 2018 - 2019 Michael Fabian Dirks
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#pragma once
|
||||
#include "handler.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <obs-properties.h>
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavcodec/avcodec.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
namespace obsffmpeg {
|
||||
namespace ui {
|
||||
class prores_aw_handler : public handler {
|
||||
public:
|
||||
virtual void get_defaults(obs_data_t* settings, AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
|
||||
virtual void get_properties(obs_properties_t* props, AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
|
||||
virtual void update(obs_data_t* settings, AVCodec* codec, AVCodecContext* context) override;
|
||||
};
|
||||
} // namespace ui
|
||||
} // namespace obsffmpeg
|
||||
+31
-6
@@ -15,14 +15,12 @@
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef OBS_FFMPEG_UTILITY_HPP
|
||||
#define OBS_FFMPEG_UTILITY_HPP
|
||||
#pragma once
|
||||
|
||||
#include "version.hpp"
|
||||
|
||||
// Logging
|
||||
#define PLOG(level, ...) blog(level, "["##PROJECT_NAME##"] " __VA_ARGS__);
|
||||
#define PLOG(level, ...) blog(level, "[obs-ffmpeg-encoder] " __VA_ARGS__);
|
||||
#define PLOG_ERROR(...) PLOG(LOG_ERROR, __VA_ARGS__)
|
||||
#define PLOG_WARNING(...) PLOG(LOG_WARNING, __VA_ARGS__)
|
||||
#define PLOG_INFO(...) PLOG(LOG_INFO, __VA_ARGS__)
|
||||
@@ -42,7 +40,34 @@
|
||||
#define DESC(x) x ".Description"
|
||||
|
||||
// Other
|
||||
#define vstr(s) dstr(s)
|
||||
#define dstr(s) #s
|
||||
#define D_STR(s) #s
|
||||
#define D_VSTR(s) D_STR(s)
|
||||
|
||||
#endif OBS_FFMPEG_UTILITY_HPP
|
||||
// Initializer
|
||||
#ifdef __cplusplus
|
||||
#define INITIALIZER(f) \
|
||||
static void f(void); \
|
||||
struct f##_t_ { \
|
||||
f##_t_(void) \
|
||||
{ \
|
||||
f(); \
|
||||
} \
|
||||
}; \
|
||||
static f##_t_ f##_; \
|
||||
static void f(void)
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma section(".CRT$XCU", read)
|
||||
#define INITIALIZER2_(f, p) \
|
||||
static void f(void); \
|
||||
__declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \
|
||||
__pragma(comment(linker, "/include:" p #f "_")) static void f(void)
|
||||
#ifdef _WIN64
|
||||
#define INITIALIZER(f) INITIALIZER2_(f, "")
|
||||
#else
|
||||
#define INITIALIZER(f) INITIALIZER2_(f, "_")
|
||||
#endif
|
||||
#else
|
||||
#define INITIALIZER(f) \
|
||||
static void f(void) __attribute__((constructor)); \
|
||||
static void f(void)
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user