36 Commits

Author SHA1 Message Date
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
27 changed files with 1729 additions and 190 deletions
+1 -1
View File
@@ -67,7 +67,7 @@ BreakBeforeBraces: Custom
BreakBeforeTernaryOperators: true BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon BreakConstructorInitializers: BeforeColon
#BreakInheritanceList: BeforeColon #BreakInheritanceList: BeforeColon
BreakStringLiterals: true BreakStringLiterals: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerAllOnOneLineOrOnePerLine: false
Cpp11BracedListStyle: true Cpp11BracedListStyle: true
+75 -53
View File
@@ -1,5 +1,5 @@
# A Plugin that integrates the AMD AMF encoder into OBS Studio # Integrates most FFmpeg supported encoders into OBS Studio
# Copyright (C) 2016 - 2017 Michael Fabian Dirks # Copyright (C) 2018 - 2019 Michael Fabian Dirks
# #
# This program is free software; you can redistribute it and/or modify # 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 # 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 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
# CMake Setup # CMake Setup
cmake_minimum_required(VERSION 3.1.0) CMake_Minimum_Required(VERSION 3.8.0)
include("cmake/util.cmake") Include("cmake/util.cmake")
# Automatic Versioning # Automatic Versioning
set(VERSION_MAJOR 0) set(VERSION_MAJOR 0)
@@ -52,13 +52,13 @@ endif()
# Define Project # Define Project
project( project(
enc-ffmpeg obs-ffmpeg-encoder
VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}.${VERSION_TWEAK} VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}.${VERSION_TWEAK}
) )
set(PROJECT_FULL_NAME "FFMPEG Encoder for OBS Studio") 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_AUTHORS "Michael Fabian 'Xaymar' Dirks <info@xaymar.com>")
set(PROJECT_COPYRIGHT_YEARS "2018") set(PROJECT_COPYRIGHT_YEARS "2018 - 2019")
################################################################################ ################################################################################
# Setup / Bootstrap # Setup / Bootstrap
@@ -97,14 +97,14 @@ configure_file(
) )
# Windows # Windows
if (WIN32)
## Installer (InnoSetup) ## Installer (InnoSetup)
Configure_File( configure_file(
"${PROJECT_SOURCE_DIR}/cmake/installer.iss.in" "${PROJECT_SOURCE_DIR}/cmake/installer.iss.in"
"${PROJECT_BINARY_DIR}/installer.iss" "${PROJECT_BINARY_DIR}/installer.iss"
) )
# Windows Specific Resource Definition # Windows Specific Resource Definition
if(WIN32)
set(PROJECT_PRODUCT_NAME "${PROJECT_FULL_NAME}") set(PROJECT_PRODUCT_NAME "${PROJECT_FULL_NAME}")
set(PROJECT_COMPANY_NAME "${PROJECT_AUTHORS}") set(PROJECT_COMPANY_NAME "${PROJECT_AUTHORS}")
set(PROJECT_COPYRIGHT "${PROJECT_AUTHORS} © ${PROJECT_COPYRIGHT_YEARS}") set(PROJECT_COPYRIGHT "${PROJECT_AUTHORS} © ${PROJECT_COPYRIGHT_YEARS}")
@@ -119,17 +119,26 @@ if(WIN32)
endif() endif()
# All Warnings, Extra Warnings, Pedantic # All Warnings, Extra Warnings, Pedantic
if(MSVC) 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 # Force to always compile with W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]") if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else() else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif() 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() endif()
# C++ Standard and Extensions
## Use C++17 and no non-standard extensions.
set(_CXX_STANDARD 17)
set(_CXX_EXTENSIONS OFF)
################################################################################ ################################################################################
# Options # Options
@@ -142,7 +151,7 @@ mark_as_advanced(FORCE OBS_NATIVE OBS_PACKAGE OBS_REFERENCE OBS_DOWNLOAD)
if(NOT TARGET libobs) if(NOT TARGET libobs)
set(${PropertyPrefix}OBS_STUDIO_DIR "" CACHE PATH "OBS Studio Source/Package Directory") set(${PropertyPrefix}OBS_STUDIO_DIR "" CACHE PATH "OBS Studio Source/Package Directory")
set(${PropertyPrefix}OBS_DOWNLOAD_VERSION "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() endif()
if(NOT ${PropertyPrefix}OBS_NATIVE) if(NOT ${PropertyPrefix}OBS_NATIVE)
@@ -171,7 +180,7 @@ else()
else() else()
message(STATUS "${PROJECT_NAME}: No OBS Studio detected, using downloadable prebuilt binaries.") message(STATUS "${PROJECT_NAME}: No OBS Studio detected, using downloadable prebuilt binaries.")
CacheSet(${PropertyPrefix}OBS_DOWNLOAD TRUE) 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()
endif() endif()
@@ -179,17 +188,9 @@ endif()
if(${PropertyPrefix}OBS_DOWNLOAD) if(${PropertyPrefix}OBS_DOWNLOAD)
include("cmake/DownloadProject.cmake") include("cmake/DownloadProject.cmake")
endif() endif()
if(NOT ${PropertyPrefix}OBS_NATIVE)
include("cmake/cppcheck.cmake")
endif()
# Load OBS Studio # Load OBS Studio
if(${PropertyPrefix}OBS_NATIVE) 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) elseif(${PropertyPrefix}OBS_PACKAGE)
include("${OBS_STUDIO_DIR}/cmake/LibObs/LibObsConfig.cmake") include("${OBS_STUDIO_DIR}/cmake/LibObs/LibObsConfig.cmake")
elseif(${PropertyPrefix}OBS_REFERENCE) elseif(${PropertyPrefix}OBS_REFERENCE)
@@ -203,7 +204,7 @@ elseif(${PropertyPrefix}OBS_DOWNLOAD)
) )
include("${libobs_SOURCE_DIR}/cmake/LibObs/LibObsConfig.cmake") include("${libobs_SOURCE_DIR}/cmake/LibObs/LibObsConfig.cmake")
else() else()
message(CRITICAL "Impossible case reached, very system stability.") message(CRITICAL "Impossible case reached, verify system stability.")
return() return()
endif() endif()
@@ -232,6 +233,29 @@ find_package(FFmpeg REQUIRED COMPONENTS avutil avcodec swscale)
################################################################################ ################################################################################
# Code # 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 set(PROJECT_PRIVATE
"${PROJECT_SOURCE_DIR}/source/encoder.cpp" "${PROJECT_SOURCE_DIR}/source/encoder.cpp"
"${PROJECT_SOURCE_DIR}/source/encoder.hpp" "${PROJECT_SOURCE_DIR}/source/encoder.hpp"
@@ -239,6 +263,8 @@ set(PROJECT_PRIVATE
"${PROJECT_SOURCE_DIR}/source/plugin.hpp" "${PROJECT_SOURCE_DIR}/source/plugin.hpp"
"${PROJECT_SOURCE_DIR}/source/utility.cpp" "${PROJECT_SOURCE_DIR}/source/utility.cpp"
"${PROJECT_SOURCE_DIR}/source/utility.hpp" "${PROJECT_SOURCE_DIR}/source/utility.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.hpp"
"${PROJECT_SOURCE_DIR}/source/encoders/prores_aw.cpp" "${PROJECT_SOURCE_DIR}/source/encoders/prores_aw.cpp"
"${PROJECT_SOURCE_DIR}/source/ffmpeg/avframe-queue.cpp" "${PROJECT_SOURCE_DIR}/source/ffmpeg/avframe-queue.cpp"
@@ -247,22 +273,28 @@ set(PROJECT_PRIVATE
"${PROJECT_SOURCE_DIR}/source/ffmpeg/swscale.cpp" "${PROJECT_SOURCE_DIR}/source/ffmpeg/swscale.cpp"
"${PROJECT_SOURCE_DIR}/source/ffmpeg/tools.hpp" "${PROJECT_SOURCE_DIR}/source/ffmpeg/tools.hpp"
"${PROJECT_SOURCE_DIR}/source/ffmpeg/tools.cpp" "${PROJECT_SOURCE_DIR}/source/ffmpeg/tools.cpp"
) "${PROJECT_SOURCE_DIR}/source/ui/handler.hpp"
SET(PROJECT_GENERATED "${PROJECT_SOURCE_DIR}/source/ui/handler.cpp"
"${PROJECT_BINARY_DIR}/source/module.cpp" "${PROJECT_SOURCE_DIR}/source/ui/debug_handler.hpp"
"${PROJECT_BINARY_DIR}/source/version.hpp" "${PROJECT_SOURCE_DIR}/source/ui/debug_handler.cpp"
) "${PROJECT_SOURCE_DIR}/source/ui/prores_aw_handler.hpp"
set(PROJECT_DATA "${PROJECT_SOURCE_DIR}/source/ui/prores_aw_handler.cpp"
"${PROJECT_SOURCE_DIR}/data/locale/en-US.ini"
"${PROJECT_SOURCE_DIR}/LICENSE"
)
set(PROJECT_LIBRARIES
) )
# Source Grouping # Source Grouping
source_group(TREE "${PROJECT_SOURCE_DIR}" PREFIX "Data Files" FILES ${PROJECT_DATA}) source_group(TREE "${PROJECT_SOURCE_DIR}" PREFIX "Data Files" FILES ${PROJECT_DATA})
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_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 # Target
@@ -367,6 +399,14 @@ if (WIN32)
) )
endif() endif()
# C++ Standard and Extensions
set_target_properties(
${PROJECT_NAME}
PROPERTIES
CXX_STANDARD ${_CXX_STANDARD}
CXX_EXTENSIONS ${_CXX_EXTENSIONS}
)
# File Version # File Version
if(WIN32) if(WIN32)
set_target_properties( set_target_properties(
@@ -384,24 +424,6 @@ else()
) )
endif() 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 # Installation
################################################################################ ################################################################################
+37 -20
View File
@@ -1,43 +1,49 @@
# Generic Settings
version: '{build}-{branch}' version: '{build}-{branch}'
pull_requests:
do_not_increment_build_number: true
matrix: matrix:
fast_finish: true fast_finish: true
# Build Image & Environment
platform: x64 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: install:
- cmd: ci/appveyor-install.bat - cmd: ci/appveyor-install.bat
build_script: build_script:
- cmd: ci/appveyor-build.bat - cmd: node ci/appveyor-build.js
after_build: after_build:
- cmd: ci/appveyor-package.bat - cmd: ci/appveyor-package.bat
cache: # Testing
- inno.exe test: off
- 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
# Artifacts
artifacts: artifacts:
- path: build/obs-ffmpeg-encoder-*.zip - path: build/obs-ffmpeg-encoder-*.zip
- path: build/obs-ffmpeg-encoder-*.7z - path: build/obs-ffmpeg-encoder-*.7z
- path: build/obs-ffmpeg-encoder-*.exe - path: build/obs-ffmpeg-encoder-*.exe
# Deploying
deploy: deploy:
- provider: GitHub - provider: GitHub
auth_token: auth_token:
@@ -48,4 +54,15 @@ deploy:
on: on:
appveyor_repo_tag: true 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 git submodule update --init --force --recursive
IF EXIST inno.exe ( IF EXIST inno.exe (
+1
View File
@@ -1,3 +1,4 @@
@ECHO OFF
ECHO -- Building 7z Archive -- ECHO -- Building 7z Archive --
cmake --build build/64 --target PACKAGE_7Z --config RelWithDebInfo cmake --build build/64 --target PACKAGE_7Z --config RelWithDebInfo
ECHO -- Building Zip Archive -- ECHO -- Building Zip Archive --
+27 -10
View File
@@ -1,15 +1,32 @@
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
ProRes.Profile.Proxy="Proxy (PXY)" ProRes.Profile.Proxy="Proxy (PXY)"
ProRes.Profile.Light="Light (LT)" ProRes.Profile.Light="Light (LT)"
ProRes.Profile.Standard="Standard" ProRes.Profile.Standard="Standard"
ProRes.Profile.HighQuality="High Quality (HQ)" 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"
+710
View File
@@ -0,0 +1,710 @@
// 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, 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), 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), *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_FRAME_THREADS) {
this->context->thread_type = FF_THREAD_FRAME;
} else if (this->codec->capabilities & AV_CODEC_CAP_SLICE_THREADS) {
this->context->thread_type = FF_THREAD_SLICE;
} 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 {
if (this->codec->capabilities & AV_CODEC_CAP_AUTO_THREADS) {
this->context->thread_count = 0;
this->lag_in_frames = std::thread::hardware_concurrency();
} 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));
}
if (obs_data_get_int(settings, P_FFMPEG_COLORFORMAT) != AV_PIX_FMT_NONE) {
// User specified override for color format.
this->context->pix_fmt =
static_cast<AVPixelFormat>(obs_data_get_int(settings, P_FFMPEG_COLORFORMAT));
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
+7 -9
View File
@@ -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; return false;
} }
@@ -258,12 +258,12 @@ bool obsffmpeg::encoder::prores_aw::get_extra_data(uint8_t** extra_data, size_t*
return true; 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; 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; return;
} }
@@ -274,7 +274,7 @@ bool obsffmpeg::encoder::prores_aw::encode(encoder_frame* frame, encoder_packet*
{ {
ScopeProfiler sp_frame("frame"); ScopeProfiler sp_frame("frame");
AVFrame* vframe = frame_queue.pop(); std::shared_ptr<AVFrame> vframe = frame_queue.pop();
vframe->pts = frame->pts; vframe->pts = frame->pts;
vframe->color_range = this->avcontext->color_range; 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"); ScopeProfiler profile("send");
res = avcodec_send_frame(this->avcontext, vframe); res = avcodec_send_frame(this->avcontext, vframe.get());
if (res < 0) { if (res < 0) {
PLOG_ERROR(LOG_PREFIX "Failed to encode frame: %s (%ld).", PLOG_ERROR(LOG_PREFIX "Failed to encode frame: %s (%ld).",
ffmpeg::tools::get_error_description(res), res); ffmpeg::tools::get_error_description(res), res);
@@ -320,12 +320,10 @@ bool obsffmpeg::encoder::prores_aw::encode(encoder_frame* frame, encoder_packet*
return false; return false;
} }
} else { } else {
AVFrame* uframe = frame_queue_used.pop_only(); std::shared_ptr<AVFrame> uframe = frame_queue_used.pop_only();
if (uframe) { if (uframe) {
if (frame_queue.empty()) { if (frame_queue.empty()) {
frame_queue.push(uframe); frame_queue.push(uframe);
} else {
av_frame_free(&uframe);
} }
} }
packet->type = OBS_ENCODER_VIDEO; packet->type = OBS_ENCODER_VIDEO;
+3 -8
View File
@@ -15,13 +15,11 @@
// along with this program; if not, write to the Free Software // along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA // 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 #pragma once
#include <encoder.hpp> #include <encoder.hpp>
#include "ffmpeg/avframe-queue.hpp"
#include <vector> #include <vector>
#include "ffmpeg/avframe-queue.hpp"
extern "C" { extern "C" {
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
@@ -36,7 +34,8 @@ namespace obsffmpeg {
Light = 1 /*FF_PROFILE_PRORES_LT*/, Light = 1 /*FF_PROFILE_PRORES_LT*/,
Standard = 2 /*FF_PROFILE_PRORES_STANDARD*/, Standard = 2 /*FF_PROFILE_PRORES_STANDARD*/,
HighQuality = 3 /*FF_PROFILE_PRORES_HQ*/, 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: private:
@@ -45,8 +44,6 @@ namespace obsffmpeg {
ffmpeg::avframe_queue frame_queue_used; ffmpeg::avframe_queue frame_queue_used;
AVPacket* current_packet = nullptr; AVPacket* current_packet = nullptr;
public: public:
prores_aw(obs_data_t* settings, obs_encoder_t* encoder); prores_aw(obs_data_t* settings, obs_encoder_t* encoder);
@@ -76,5 +73,3 @@ namespace obsffmpeg {
}; };
} // namespace encoder } // namespace encoder
} // namespace obsffmpeg } // namespace obsffmpeg
#endif OBS_FFMPEG_ENCODER_PRORES_AW
+10 -21
View File
@@ -18,13 +18,15 @@
#include "avframe-queue.hpp" #include "avframe-queue.hpp"
#include "tools.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->width = this->resolution.first;
frame->height = this->resolution.second; frame->height = this->resolution.second;
frame->format = this->format; frame->format = this->format;
int res = av_frame_get_buffer(frame, 0);
int res = av_frame_get_buffer(frame.get(), 0);
if (res < 0) { if (res < 0) {
throw std::exception(ffmpeg::tools::get_error_description(res)); throw std::exception(ffmpeg::tools::get_error_description(res));
} }
@@ -32,14 +34,6 @@ AVFrame* ffmpeg::avframe_queue::create_frame()
return 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() {}
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() void ffmpeg::avframe_queue::clear()
{ {
std::unique_lock<std::mutex> ulock(this->lock); std::unique_lock<std::mutex> ulock(this->lock);
for (AVFrame* frame : frames) {
destroy_frame(frame);
}
frames.clear(); 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); std::unique_lock<std::mutex> ulock(this->lock);
frames.push_back(frame); 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); std::unique_lock<std::mutex> ulock(this->lock);
std::shared_ptr<AVFrame> ret;
AVFrame* ret = nullptr;
while (ret == nullptr) { while (ret == nullptr) {
if (frames.size() == 0) { if (frames.size() == 0) {
ret = create_frame(); ret = create_frame();
@@ -117,7 +107,6 @@ AVFrame* ffmpeg::avframe_queue::pop()
frames.pop_front(); frames.pop_front();
if ((ret->width != this->resolution.first) || (ret->height != this->resolution.second) if ((ret->width != this->resolution.first) || (ret->height != this->resolution.second)
|| (ret->format != this->format)) { || (ret->format != this->format)) {
destroy_frame(ret);
ret = nullptr; ret = nullptr;
} }
} }
@@ -126,13 +115,13 @@ AVFrame* ffmpeg::avframe_queue::pop()
return ret; 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); std::unique_lock<std::mutex> ulock(this->lock);
if (frames.size() == 0) { if (frames.size() == 0) {
return nullptr; return nullptr;
} }
AVFrame* ret = frames.front(); std::shared_ptr<AVFrame> ret = frames.front();
if (ret == nullptr) { if (ret == nullptr) {
return nullptr; return nullptr;
} }
+9 -14
View File
@@ -15,27 +15,26 @@
// along with this program; if not, write to the Free Software // along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA // 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 #pragma once
#include <mutex>
#include <deque> #include <deque>
#include <mutex>
extern "C" { extern "C" {
#pragma warning(push)
#pragma warning(disable : 4244)
#include <libavutil/frame.h> #include <libavutil/frame.h>
#pragma warning(pop)
} }
namespace ffmpeg { namespace ffmpeg {
class avframe_queue { class avframe_queue {
std::deque<AVFrame*> frames; std::deque<std::shared_ptr<AVFrame>> frames;
std::mutex lock; std::mutex lock;
std::pair<uint32_t, uint32_t> resolution; std::pair<uint32_t, uint32_t> resolution;
AVPixelFormat format = AV_PIX_FMT_NONE; AVPixelFormat format = AV_PIX_FMT_NONE;
AVFrame* create_frame(); std::shared_ptr<AVFrame> create_frame();
void destroy_frame(AVFrame* frame);
public: public:
avframe_queue(); avframe_queue();
@@ -53,18 +52,14 @@ namespace ffmpeg {
void clear(); 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(); bool empty();
size_t size(); size_t size();
}; };
} // namespace ffmpeg } // 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; 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() std::pair<uint32_t, uint32_t> ffmpeg::swscale::get_target_size()
{ {
+3
View File
@@ -23,8 +23,11 @@
#include <utility> #include <utility>
extern "C" { extern "C" {
#pragma warning(push)
#pragma warning(disable : 4244)
#include <libavutil/pixfmt.h> #include <libavutil/pixfmt.h>
#include <libswscale/swscale.h> #include <libswscale/swscale.h>
#pragma warning(pop)
} }
namespace ffmpeg { namespace ffmpeg {
+90 -1
View File
@@ -16,11 +16,63 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#include "tools.hpp" #include "tools.hpp"
#include <sstream>
#include <stdexcept> #include <stdexcept>
extern "C" { extern "C" {
#pragma warning(push)
#pragma warning(disable : 4244)
#include <libavcodec/avcodec.h>
#include <libavutil/error.h> #include <libavutil/error.h>
#include <libavutil/pixdesc.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) 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) { switch (error) {
case AVERROR(EPERM): case AVERROR(EPERM):
return "Permission Denied"; 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): case AVERROR(ENOMEM):
return "Out Of Memory"; 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): 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"; return "Not Translated Yet";
} }
+4 -1
View File
@@ -20,6 +20,7 @@
#pragma once #pragma once
#include <obs.h> #include <obs.h>
#include <string>
extern "C" { extern "C" {
#include <libavutil/pixfmt.h> #include <libavutil/pixfmt.h>
@@ -27,6 +28,8 @@ extern "C" {
namespace ffmpeg { namespace ffmpeg {
namespace tools { namespace tools {
std::string translate_encoder_capabilities(int capabilities);
const char* get_pixel_format_name(AVPixelFormat v); const char* get_pixel_format_name(AVPixelFormat v);
const char* get_color_space_name(AVColorSpace v); const char* get_color_space_name(AVColorSpace v);
@@ -38,7 +41,7 @@ namespace ffmpeg {
AVColorSpace obs_videocolorspace_to_avcolorspace(video_colorspace v); AVColorSpace obs_videocolorspace_to_avcolorspace(video_colorspace v);
AVColorRange obs_videorangetype_to_avcolorrange(video_range_type v); AVColorRange obs_videorangetype_to_avcolorrange(video_range_type v);
} } // namespace tools
} // namespace ffmpeg } // namespace ffmpeg
#endif OBS_FFMPEG_FFMPEG_UTILITY #endif OBS_FFMPEG_FFMPEG_UTILITY
+57 -8
View File
@@ -16,40 +16,89 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#include "plugin.hpp" #include "plugin.hpp"
#include <obs-module.h> #include <memory>
#include <obs.h> #include "encoders/generic.hpp"
#include "encoders/prores_aw.hpp"
#include "ui/debug_handler.hpp"
#include "ui/handler.hpp"
#include "utility.hpp" #include "utility.hpp"
#include "encoders/prores_aw.hpp"
extern "C" { extern "C" {
#include <obs-module.h>
#include <obs.h>
#pragma warning(push) #pragma warning(push)
#pragma warning(disable : 4244) #pragma warning(disable : 4244)
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
#pragma warning(pop) #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)
{ {
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 { try {
// Initialize avcodec.
avcodec_register_all(); 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(); obsffmpeg::encoder::prores_aw::initialize();
return true; return true;
} catch (std::exception ex) { } catch (std::exception ex) {
PLOG_ERROR("Exception during initalization: %s.", ex.what()); PLOG_ERROR("Exception during initalization: %s.", ex.what());
return false;
} catch (...) { } catch (...) {
PLOG_ERROR("Unrecognized exception during initalization."); PLOG_ERROR("Unrecognized exception during initalization.");
}
return false; return false;
} }
MODULE_EXPORT void obs_module_unload(void) MODULE_EXPORT void obs_module_unload(void)
{
try { try {
obsffmpeg::encoder::prores_aw::finalize(); obsffmpeg::encoder::prores_aw::finalize();
// Run all finalizers.
for (auto func : obsffmpeg::finalizers) {
func();
}
} catch (std::exception ex) { } catch (std::exception ex) {
PLOG_ERROR("Exception during finalizing: %s.", ex.what()); PLOG_ERROR("Exception during finalizing: %s.", ex.what());
} catch (...) { } catch (...) {
PLOG_ERROR("Unrecognized exception during finalizing."); PLOG_ERROR("Unrecognized exception during finalizing.");
} }
}
+12 -5
View File
@@ -1,5 +1,5 @@
// FFMPEG Video Encoder Integration for OBS Studio // 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 // 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 // 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 // along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef OBS_FFMPEG_PLUGIN_HPP
#define OBS_FFMPEG_PLUGIN_HPP
#pragma once #pragma once
#include <functional> #include <functional>
#include <list> #include <list>
#include <memory>
#include <obs-module.h>
#include "ui/handler.hpp"
namespace obsffmpeg { 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 } // namespace obsffmpeg
#endif OBS_FFMPEG_PLUGIN_HPP MODULE_EXPORT bool obs_module_load(void);
MODULE_EXPORT void obs_module_unload(void);
+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 '%llX'.", 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
+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
+28 -3
View File
@@ -15,8 +15,6 @@
// along with this program; if not, write to the Free Software // along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef OBS_FFMPEG_UTILITY_HPP
#define OBS_FFMPEG_UTILITY_HPP
#pragma once #pragma once
#include "version.hpp" #include "version.hpp"
@@ -45,4 +43,31 @@
#define vstr(s) dstr(s) #define vstr(s) dstr(s)
#define dstr(s) #s #define dstr(s) #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