45 Commits

Author SHA1 Message Date
Michael Fabian 'Xaymar' Dirks 51223666e9 ui/libvpx_vp9_handler: Add UI handler for libvpx-vp9 2019-07-13 00:22:15 +02:00
Michael Fabian 'Xaymar' Dirks 090dd9dbdc encoders/generic: Fix threading priority and thread count override 2019-07-13 00:16:42 +02:00
Michael Fabian 'Xaymar' Dirks d145c672ad utility: Don't use PROJECT_NAME macro for logging 2019-07-13 00:16:42 +02:00
Michael Fabian 'Xaymar' Dirks b7fdd5491a ui/debug_handler: Print flags as normal values instead of hexadecimal 2019-07-13 00:16:42 +02:00
Michael Fabian 'Xaymar' Dirks cdb03a488e encoders/generic: Clean up code slightly 2019-07-13 00:16:42 +02:00
Michael Fabian 'Xaymar' Dirks bab3bb8853 strings: Add text for bitrate and buffer 2019-07-13 00:16:42 +02:00
Michael Fabian 'Xaymar' Dirks c8470d3b23 strings: Add shared translation strings 2019-07-13 00:16:38 +02:00
Michael Fabian 'Xaymar' Dirks c7f326d428 utility: Change vstr and dstr macros to D_STR and D_VSTR 2019-07-13 00:15:54 +02:00
Michael Fabian 'Xaymar' Dirks 0aab0db34a cmake: Ensure that the changed flags appear before project() 2019-07-13 00:15:51 +02:00
Michael Fabian 'Xaymar' Dirks 8dbcaf192a cmake: Remove useless option 2019-07-07 17:12:42 +02:00
Michael Fabian 'Xaymar' Dirks 830ebf31d9 ui/prores_aw_handler: Disable profile file while encoding 2019-07-07 16:55:02 +02:00
Michael Fabian 'Xaymar' Dirks 076df05b37 ui/debug_handler: Fix flags 2019-07-07 16:54:48 +02:00
Michael Fabian 'Xaymar' Dirks 3bbbea7b42 ui/debug_handler: Add debug handler for implementing new UI Handlers 2019-07-07 16:34:15 +02:00
Michael Fabian 'Xaymar' Dirks 97a53d261f encoders/generic: Fix getting stuck in EAGAIN endless loop
Also removes some left over debug logs.
2019-07-07 15:07:09 +02:00
Michael Fabian 'Xaymar' Dirks e45a82350e encoders/generic: Fix crash and disable static properties 2019-07-07 14:44:08 +02:00
Michael Fabian 'Xaymar' Dirks b6ad026eb3 encoders/generic: Allow overriding of thread count 2019-07-07 14:33:58 +02:00
Michael Fabian 'Xaymar' Dirks df63677529 encoders/generic: Allow overriding color format
Some encoders don't have all options available with the automatically detected color format, so allowing the user to override the detected color format opens up more options.
2019-07-07 14:26:51 +02:00
Michael Fabian 'Xaymar' Dirks d1ff4a7adc encoders/generic: Don't instantly drop frames on EAGAIN
Instead of instantly dropping frames on EAGAIN, try first recieving a packet (if not already done so) and then resubmit the same frame. If it still doesn't work, drop the frame as we have no way to deal with that situation yet.
2019-07-07 13:50:47 +02:00
Michael Fabian 'Xaymar' Dirks e5bf18f2a6 ui/prores_aw_handler: Implement UI Handler for prores_aw 2019-07-07 13:46:11 +02:00
Michael Fabian 'Xaymar' Dirks b442914ebe encoders/generic: Replace generated UI with the new UI Handlers 2019-07-07 13:46:09 +02:00
Michael Fabian 'Xaymar' Dirks 8fb69d6870 plugin: Add global codec to UI Handler map 2019-07-07 13:42:39 +02:00
Michael Fabian 'Xaymar' Dirks dbdf915a07 ui/handler: Implement UI Handler interface class
Adds the ability to override the UI for very specific encoders in order to properly support customized and detailed UI for each encoder. This design is flexible and allows for any kind of UI generator to work, without relying on an actual object too much.
2019-07-07 13:42:22 +02:00
Michael Fabian 'Xaymar' Dirks 9ce8f39efc plugin: Implement support for initializers and finalizers 2019-07-07 13:39:23 +02:00
Michael Fabian 'Xaymar' Dirks 924c64cd37 clang-format: Don't ever break string literals automatically
This option is very broken and just results in unreadable text.
2019-07-07 13:28:53 +02:00
Michael Fabian 'Xaymar' Dirks 99003d5193 utility: Add initializer functions 2019-07-07 12:57:30 +02:00
Michael Fabian 'Xaymar' Dirks c5d81e41a6 encoders/prores_aw: Update to match new std::shared_ptr<AVFrame> code 2019-07-07 12:18:43 +02:00
Michael Fabian 'Xaymar' Dirks 2f0cd790ed encoders/generic: Update to match new std::shared_ptr<AVFrame> code 2019-07-07 12:14:46 +02:00
Michael Fabian 'Xaymar' Dirks c5ff5d224d ffmpeg/avframe-queue: Upgrade to shared_ptr AVFrame
Removes problems with memory leaks due to lost references and other issues.
2019-07-07 12:14:24 +02:00
Michael Fabian 'Xaymar' Dirks 89fd38e604 ci: Fix package name 2019-07-07 01:51:35 +02:00
Michael Fabian 'Xaymar' Dirks 56807370a9 cmake: Update to 23.2.1 obs-studio binaries 2019-07-07 01:47:32 +02:00
Michael Fabian 'Xaymar' Dirks e7c12a52f3 ffmpeg/generic: Strip generated options and always apply bitrate and profile in update 2019-07-07 01:12:06 +02:00
Michael Fabian 'Xaymar' Dirks 976e5dba4f encoders/generic: Reduce warnings, fix encoding, remove threading options, and profiling
* Improved encoding loop can now deal with early and late receive requests.
* Lots of warnings fixed.
* Removed all threading options (they were broken anyway).
* Added some profiler calls.
2019-07-07 00:34:56 +02:00
Michael Fabian 'Xaymar' Dirks 16015f180b plugin: Use av_codec_is_encoder instead of custom check 2019-07-07 00:32:21 +02:00
Michael Fabian 'Xaymar' Dirks 042c934516 ffmpeg/tools: Remove a few warnings and translate more errors 2019-07-07 00:32:05 +02:00
Michael Fabian 'Xaymar' Dirks 53651c29be ffmpeg/avframe-queue: Fix a few warnings 2019-07-07 00:31:33 +02:00
Michael Fabian 'Xaymar' Dirks 5b05cc1504 locale: Remove text for frame queue (unused) 2019-07-06 14:22:28 +02:00
Michael Fabian 'Xaymar' Dirks 0a20b750b8 encoders/generic: Implement video encoding and always use frame queue
This implements video encoding for the generic encoder by using frame queue and a set lag amount (in frames). The lag amount determines for how many frames no repeated attempts at submitting or retrieving a frame should be made, and after that many frames have been submitted the generic encoder will try to retrieve packets if at all possible. If not possible, it will delay for as long as possible to show an Encoder overloaded message.
2019-07-06 14:22:15 +02:00
Michael Fabian 'Xaymar' Dirks 9a58a6a5ff encoders/generic: Implement generic encoder for all ffmpeg encoders 2019-07-06 13:10:00 +02:00
Michael Fabian 'Xaymar' Dirks 143b7f585f ffmpeg/tools: Add function to translate encoder capabilities 2019-07-06 13:09:21 +02:00
Michael Fabian 'Xaymar' Dirks 93c6f0b35a encoders/prores_aw: Fix warnings and formatting 2019-07-06 13:09:21 +02:00
Michael Fabian 'Xaymar' Dirks 3154443e18 ffmpeg/swscale: Fix warning and broken get_target_size 2019-07-06 13:09:21 +02:00
Michael Fabian 'Xaymar' Dirks 74fb3a3847 ci: Fix package names and always build packages 2019-06-28 20:45:56 +02:00
Michael Fabian 'Xaymar' Dirks 2d131b8566 cmake: Fix broken if/endif check for Windows 2019-06-28 20:39:42 +02:00
Michael Fabian 'Xaymar' Dirks 35db9e714a ci: New scripts for AppVeyor 2019-06-28 20:34:26 +02:00
Michael Fabian 'Xaymar' Dirks 01e1c70a02 cmake: Apply improvements from Stream Effects
* Copyright updated to include current year.
* Description updated to no longer talk about AMD encoder.
* Increased minimum CMake version to 3.8 from 3.1.
* Renamed project to xmr-ffmpeg-encoders from enc-ffmpeg.
* Add additional compiler options for Clang, GNU GCC, Intel C and MSVC.
* Set the C++ standard to C++17 with no non-standard extensions.
* Slightly improve project generation.
2019-06-28 20:25:19 +02:00
30 changed files with 2278 additions and 203 deletions
+1 -1
View File
@@ -67,7 +67,7 @@ BreakBeforeBraces: Custom
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
#BreakInheritanceList: BeforeColon
BreakStringLiterals: true
BreakStringLiterals: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
Cpp11BracedListStyle: true
+90 -65
View File
@@ -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(
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
View File
@@ -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}}"
}
-4
View File
@@ -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"
+98
View File
@@ -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
View File
@@ -1,3 +1,4 @@
@ECHO OFF
git submodule update --init --force --recursive
IF EXIST inno.exe (
+1
View File
@@ -1,3 +1,4 @@
@ECHO OFF
ECHO -- Building 7z Archive --
cmake --build build/64 --target PACKAGE_7Z --config RelWithDebInfo
ECHO -- Building Zip Archive --
+57 -10
View File
@@ -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"
+707
View File
@@ -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;
}
+104
View File
@@ -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
+8 -10
View File
@@ -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;
}
@@ -274,7 +274,7 @@ bool obsffmpeg::encoder::prores_aw::encode(encoder_frame* frame, encoder_packet*
{
ScopeProfiler sp_frame("frame");
AVFrame* vframe = frame_queue.pop();
std::shared_ptr<AVFrame> vframe = frame_queue.pop();
vframe->pts = frame->pts;
vframe->color_range = this->avcontext->color_range;
@@ -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;
+3 -8
View File
@@ -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>
@@ -36,7 +34,8 @@ namespace obsffmpeg {
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.
FourFourFourFour =
4 /*FF_PROFILE_PRORES_4444*/ // Automatically set if I444 or RGB input.
};
private:
@@ -45,8 +44,6 @@ namespace obsffmpeg {
ffmpeg::avframe_queue frame_queue_used;
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
+10 -21
View File
@@ -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();
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, 0);
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;
}
+9 -14
View File
@@ -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::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
+5 -1
View File
@@ -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()
{
+3
View File
@@ -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
View File
@@ -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";
}
+4 -1
View File
@@ -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
+64 -15
View File
@@ -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 {
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) {
} catch (std::exception ex) {
PLOG_ERROR("Exception during initalization: %s.", ex.what());
} catch (...) {
return false;
} catch (...) {
PLOG_ERROR("Unrecognized exception during initalization.");
}
return false;
}
MODULE_EXPORT void obs_module_unload(void)
{
try {
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.");
// 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
View File
@@ -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);
+19
View File
@@ -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"
+203
View File
@@ -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) {}
+34
View File
@@ -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
+18
View File
@@ -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"
+40
View File
@@ -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
+434
View File
@@ -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;
}
+53
View File
@@ -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
+79
View File
@@ -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));
}
+42
View File
@@ -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
View File
@@ -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