Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8dbcaf192a | |||
| 830ebf31d9 | |||
| 076df05b37 | |||
| 3bbbea7b42 | |||
| 97a53d261f | |||
| e45a82350e | |||
| b6ad026eb3 | |||
| df63677529 | |||
| d1ff4a7adc | |||
| e5bf18f2a6 | |||
| b442914ebe | |||
| 8fb69d6870 | |||
| dbdf915a07 | |||
| 9ce8f39efc | |||
| 924c64cd37 | |||
| 99003d5193 | |||
| c5d81e41a6 | |||
| 2f0cd790ed | |||
| c5ff5d224d | |||
| 89fd38e604 | |||
| 56807370a9 | |||
| e7c12a52f3 | |||
| 976e5dba4f | |||
| 16015f180b | |||
| 042c934516 | |||
| 53651c29be | |||
| 5b05cc1504 | |||
| 0a20b750b8 | |||
| 9a58a6a5ff | |||
| 143b7f585f | |||
| 93c6f0b35a | |||
| 3154443e18 | |||
| 74fb3a3847 | |||
| 2d131b8566 | |||
| 35db9e714a | |||
| 01e1c70a02 |
+1
-1
@@ -67,7 +67,7 @@ BreakBeforeBraces: Custom
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
#BreakInheritanceList: BeforeColon
|
||||
BreakStringLiterals: true
|
||||
BreakStringLiterals: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
Cpp11BracedListStyle: true
|
||||
|
||||
|
||||
+80
-58
@@ -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)
|
||||
@@ -52,13 +52,13 @@ endif()
|
||||
|
||||
# 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 +97,14 @@ configure_file(
|
||||
)
|
||||
|
||||
# Windows
|
||||
## Installer (InnoSetup)
|
||||
Configure_File(
|
||||
"${PROJECT_SOURCE_DIR}/cmake/installer.iss.in"
|
||||
"${PROJECT_BINARY_DIR}/installer.iss"
|
||||
)
|
||||
if (WIN32)
|
||||
## Installer (InnoSetup)
|
||||
configure_file(
|
||||
"${PROJECT_SOURCE_DIR}/cmake/installer.iss.in"
|
||||
"${PROJECT_BINARY_DIR}/installer.iss"
|
||||
)
|
||||
|
||||
# Windows Specific Resource Definition
|
||||
if(WIN32)
|
||||
# Windows Specific Resource Definition
|
||||
set(PROJECT_PRODUCT_NAME "${PROJECT_FULL_NAME}")
|
||||
set(PROJECT_COMPANY_NAME "${PROJECT_AUTHORS}")
|
||||
set(PROJECT_COPYRIGHT "${PROJECT_AUTHORS} © ${PROJECT_COPYRIGHT_YEARS}")
|
||||
@@ -119,17 +119,26 @@ if(WIN32)
|
||||
endif()
|
||||
|
||||
# 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
|
||||
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()
|
||||
# C++ Standard and Extensions
|
||||
## Use C++17 and no non-standard extensions.
|
||||
set(_CXX_STANDARD 17)
|
||||
set(_CXX_EXTENSIONS OFF)
|
||||
|
||||
################################################################################
|
||||
# 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,8 @@ 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/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 +273,28 @@ 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"
|
||||
)
|
||||
|
||||
# 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 +399,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 +424,6 @@ else()
|
||||
)
|
||||
endif()
|
||||
|
||||
# CPPCheck
|
||||
if(NOT ${PropertyPrefix}OBS_NATIVE)
|
||||
set(excludes )
|
||||
list(APPEND excludes "${OBS_DEPENDENCIES_DIR}")
|
||||
if(${PropertyPrefix}OBS_REFERENCE)
|
||||
list(APPEND excludes "${OBS_STUDIO_DIR}/libobs")
|
||||
elseif(${PropertyPrefix}OBS_PACKAGE)
|
||||
list(APPEND excludes "${OBS_STUDIO_DIR}/libobs")
|
||||
elseif(${PropertyPrefix}OBS_DOWNLOAD)
|
||||
list(APPEND excludes "${libobs_SOURCE_DIR}")
|
||||
endif()
|
||||
|
||||
cppcheck(
|
||||
EXCLUDE ${excludes}
|
||||
)
|
||||
cppcheck_add_project(${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
################################################################################
|
||||
# Installation
|
||||
################################################################################
|
||||
|
||||
+37
-20
@@ -1,43 +1,49 @@
|
||||
# Generic Settings
|
||||
version: '{build}-{branch}'
|
||||
pull_requests:
|
||||
do_not_increment_build_number: true
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
# Build Image & Environment
|
||||
platform: x64
|
||||
image: Visual Studio 2017
|
||||
environment:
|
||||
CMAKE_SYSTEM_VERSION: 10.0.16299.91
|
||||
PACKAGE_PREFIX: obs-ffmpeg-encoder
|
||||
INNOSETUP_URL: http://www.jrsoftware.org/download.php/is-unicode.exe
|
||||
|
||||
image:
|
||||
- Visual Studio 2017
|
||||
|
||||
environment:
|
||||
CMAKE_SYSTEM_VERSION: 10.0.17134.0
|
||||
PACKAGE_PREFIX: obs-ffmpeg-encoder
|
||||
INNOSETUP_URL: http://www.jrsoftware.org/download.php/is.exe
|
||||
CURL_VERSION: 7.39.0
|
||||
|
||||
# Resource Cache
|
||||
cache:
|
||||
- inno.exe
|
||||
- build/32/libobs-download
|
||||
- build/32/libobs-src
|
||||
- build/64/libobs-download
|
||||
- build/64/libobs-src
|
||||
|
||||
# Building
|
||||
install:
|
||||
- cmd: ci/appveyor-install.bat
|
||||
|
||||
build_script:
|
||||
- cmd: ci/appveyor-build.bat
|
||||
- cmd: node ci/appveyor-build.js
|
||||
|
||||
after_build:
|
||||
- cmd: ci/appveyor-package.bat
|
||||
|
||||
cache:
|
||||
- inno.exe
|
||||
- build/32/obsdeps-build
|
||||
- build/32/obsdeps-download
|
||||
- build/32/obsdeps-src
|
||||
- build/32/libobs-build
|
||||
- build/32/libobs-download
|
||||
- build/32/libobs-src
|
||||
- build/64/libobs-build
|
||||
- build/64/libobs-download
|
||||
- build/64/libobs-src
|
||||
# Testing
|
||||
test: off
|
||||
|
||||
# Artifacts
|
||||
artifacts:
|
||||
- path: build/obs-ffmpeg-encoder-*.zip
|
||||
- path: build/obs-ffmpeg-encoder-*.7z
|
||||
- path: build/obs-ffmpeg-encoder-*.exe
|
||||
|
||||
# Deploying
|
||||
deploy:
|
||||
- provider: GitHub
|
||||
auth_token:
|
||||
@@ -48,4 +54,15 @@ deploy:
|
||||
on:
|
||||
appveyor_repo_tag: true
|
||||
|
||||
test: off
|
||||
# Notifications
|
||||
notifications:
|
||||
- provider: Webhook
|
||||
url:
|
||||
secure: PTtt5ALhmK0q42jYyx4/Qa1Uf18+gLMXKGdzJjDISJt8IE/K0Zyp58UYmDDbbyLp4pBRf/Ylj8rn/zYL/mqBoDVRIH5zasPqIvBD0ZhtvNjTOxQ3QoRkAmxgpWeMowm3A3I1rLizA2H4EctPpoAJGrvQ1G2HEYn9tVsGYeetFTo=
|
||||
on_build_success: false
|
||||
on_build_failure: false
|
||||
on_build_status_changed: true
|
||||
body: >-
|
||||
{
|
||||
"content": "**Build {{status}}**: [{{commitId}}] {{commitMessage}}\nBy {{commitAuthor}} on {{commitDate}}\n{{buildUrl}}"
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
cmake -H. -B"build/32" -G"Visual Studio 15 2017" -DCMAKE_INSTALL_PREFIX="%CD%/build/distrib" -DCMAKE_PACKAGE_PREFIX="%CD%/build" -DCMAKE_PACKAGE_NAME="obs-ffmpeg-encoder"
|
||||
cmake -H. -B"build/64" -G"Visual Studio 15 2017 Win64" -T"host=x64" -DCMAKE_INSTALL_PREFIX="%CD%/build/distrib" -DCMAKE_PACKAGE_PREFIX="%CD%/build" -DCMAKE_PACKAGE_NAME="obs-ffmpeg-encoder" -DOBS_DEPENDENCIES_DIR="%CD%/build/32/obsdeps-src"
|
||||
cmake --build build/32 --target INSTALL --config RelWithDebInfo -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||
cmake --build build/64 --target INSTALL --config RelWithDebInfo -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||
@@ -0,0 +1,98 @@
|
||||
const cp = require('child_process');
|
||||
|
||||
var config32 = cp.spawn(
|
||||
"cmake", [
|
||||
'-H.',
|
||||
'-B"build/32"',
|
||||
'-G"Visual Studio 15 2017"',
|
||||
'-DCMAKE_INSTALL_PREFIX="build/distrib"',
|
||||
'-DCMAKE_PACKAGE_PREFIX="build"',
|
||||
'-DCMAKE_PACKAGE_NAME="xmr-ffmpeg-encoders"'
|
||||
], {
|
||||
windowsVerbatimArguments: true,
|
||||
windowsHide: true
|
||||
}
|
||||
);
|
||||
config32.stdout.on('data', (data) => {
|
||||
process.stdout.write(`[32:Out] ${data}`);
|
||||
});
|
||||
config32.stderr.on('data', (data) => {
|
||||
console.log(`[32:Err] ${data}`);
|
||||
});
|
||||
config32.on('exit', (code, signal) => {
|
||||
if (code != 0) {
|
||||
process.exit(code)
|
||||
}
|
||||
var build32 = cp.spawn(
|
||||
"cmake", [
|
||||
'--build build/32',
|
||||
'--target INSTALL',
|
||||
'--config RelWithDebInfo',
|
||||
'--',
|
||||
'/logger:"C:\\Program Files\\AppVeyor\\BuildAgent\\Appveyor.MSBuildLogger.dll"'
|
||||
], {
|
||||
windowsVerbatimArguments: true,
|
||||
windowsHide: true
|
||||
}
|
||||
);
|
||||
build32.stdout.on('data', (data) => {
|
||||
process.stdout.write(`[32:Out] ${data}`);
|
||||
});
|
||||
build32.stderr.on('data', (data) => {
|
||||
process.stderr.write(`[32:Err] ${data}`);
|
||||
});
|
||||
build32.on('exit', (code, signal) => {
|
||||
if (code != 0) {
|
||||
process.exit(code)
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var config64 = cp.spawn(
|
||||
"cmake", [
|
||||
'-H.',
|
||||
'-B"build/64"',
|
||||
'-G"Visual Studio 15 2017 Win64"',
|
||||
'-DCMAKE_INSTALL_PREFIX="build/distrib"',
|
||||
'-DCMAKE_PACKAGE_PREFIX="build"',
|
||||
'-DCMAKE_PACKAGE_NAME="xmr-ffmpeg-encoders"'
|
||||
], {
|
||||
windowsVerbatimArguments: true,
|
||||
windowsHide: true
|
||||
}
|
||||
);
|
||||
config64.stdout.on('data', (data) => {
|
||||
process.stdout.write(`[64:Out] ${data}`);
|
||||
});
|
||||
config64.stderr.on('data', (data) => {
|
||||
console.log(`[64:Err] ${data}`);
|
||||
});
|
||||
config64.on('exit', (code, signal) => {
|
||||
if (code != 0) {
|
||||
process.exit(code)
|
||||
}
|
||||
var build64 = cp.spawn(
|
||||
"cmake", [
|
||||
'--build build/64',
|
||||
'--target INSTALL',
|
||||
'--config RelWithDebInfo',
|
||||
'--',
|
||||
'/logger:"C:\\Program Files\\AppVeyor\\BuildAgent\\Appveyor.MSBuildLogger.dll"'
|
||||
], {
|
||||
windowsVerbatimArguments: true,
|
||||
windowsHide: true
|
||||
}
|
||||
);
|
||||
build64.stdout.on('data', (data) => {
|
||||
process.stdout.write(`[32:Out] ${data}`);
|
||||
});
|
||||
build64.stderr.on('data', (data) => {
|
||||
process.stderr.write(`[32:Err] ${data}`);
|
||||
});
|
||||
build64.on('exit', (code, signal) => {
|
||||
if (code != 0) {
|
||||
process.exit(code)
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
@ECHO OFF
|
||||
git submodule update --init --force --recursive
|
||||
|
||||
IF EXIST inno.exe (
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
@ECHO OFF
|
||||
ECHO -- Building 7z Archive --
|
||||
cmake --build build/64 --target PACKAGE_7Z --config RelWithDebInfo
|
||||
ECHO -- Building Zip Archive --
|
||||
|
||||
+27
-10
@@ -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.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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -241,9 +241,9 @@ obsffmpeg::encoder::prores_aw::~prores_aw()
|
||||
}
|
||||
}
|
||||
|
||||
void obsffmpeg::encoder::prores_aw::get_properties(obs_properties_t* props) {}
|
||||
void obsffmpeg::encoder::prores_aw::get_properties(obs_properties_t*) {}
|
||||
|
||||
bool obsffmpeg::encoder::prores_aw::update(obs_data_t* settings)
|
||||
bool obsffmpeg::encoder::prores_aw::update(obs_data_t*)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -258,12 +258,12 @@ bool obsffmpeg::encoder::prores_aw::get_extra_data(uint8_t** extra_data, size_t*
|
||||
return true;
|
||||
}
|
||||
|
||||
bool obsffmpeg::encoder::prores_aw::get_sei_data(uint8_t** sei_data, size_t* size)
|
||||
bool obsffmpeg::encoder::prores_aw::get_sei_data(uint8_t**, size_t*)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void obsffmpeg::encoder::prores_aw::get_video_info(video_scale_info* info)
|
||||
void obsffmpeg::encoder::prores_aw::get_video_info(video_scale_info*)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -273,9 +273,9 @@ bool obsffmpeg::encoder::prores_aw::encode(encoder_frame* frame, encoder_packet*
|
||||
int res = 0;
|
||||
|
||||
{
|
||||
ScopeProfiler sp_frame("frame");
|
||||
AVFrame* vframe = frame_queue.pop();
|
||||
vframe->pts = frame->pts;
|
||||
ScopeProfiler sp_frame("frame");
|
||||
std::shared_ptr<AVFrame> vframe = frame_queue.pop();
|
||||
vframe->pts = frame->pts;
|
||||
|
||||
vframe->color_range = this->avcontext->color_range;
|
||||
vframe->colorspace = this->avcontext->colorspace;
|
||||
@@ -294,7 +294,7 @@ bool obsffmpeg::encoder::prores_aw::encode(encoder_frame* frame, encoder_packet*
|
||||
|
||||
{
|
||||
ScopeProfiler profile("send");
|
||||
res = avcodec_send_frame(this->avcontext, vframe);
|
||||
res = avcodec_send_frame(this->avcontext, vframe.get());
|
||||
if (res < 0) {
|
||||
PLOG_ERROR(LOG_PREFIX "Failed to encode frame: %s (%ld).",
|
||||
ffmpeg::tools::get_error_description(res), res);
|
||||
@@ -320,12 +320,10 @@ bool obsffmpeg::encoder::prores_aw::encode(encoder_frame* frame, encoder_packet*
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
AVFrame* uframe = frame_queue_used.pop_only();
|
||||
std::shared_ptr<AVFrame> uframe = frame_queue_used.pop_only();
|
||||
if (uframe) {
|
||||
if (frame_queue.empty()) {
|
||||
frame_queue.push(uframe);
|
||||
} else {
|
||||
av_frame_free(&uframe);
|
||||
}
|
||||
}
|
||||
packet->type = OBS_ENCODER_VIDEO;
|
||||
|
||||
@@ -15,13 +15,11 @@
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef OBS_FFMPEG_ENCODER_PRORES_AW
|
||||
#define OBS_FFMPEG_ENCODER_PRORES_AW
|
||||
#pragma once
|
||||
|
||||
#include <encoder.hpp>
|
||||
#include "ffmpeg/avframe-queue.hpp"
|
||||
#include <vector>
|
||||
#include "ffmpeg/avframe-queue.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
@@ -31,21 +29,20 @@ namespace obsffmpeg {
|
||||
namespace encoder {
|
||||
class prores_aw : base {
|
||||
enum class profile {
|
||||
Auto = FF_PROFILE_UNKNOWN,
|
||||
Proxy = 0 /*FF_PROFILE_PRORES_PROXY*/,
|
||||
Light = 1 /*FF_PROFILE_PRORES_LT*/,
|
||||
Standard = 2 /*FF_PROFILE_PRORES_STANDARD*/,
|
||||
HighQuality = 3 /*FF_PROFILE_PRORES_HQ*/,
|
||||
FourFourFourFour = 4 /*FF_PROFILE_PRORES_4444*/ // Automatically set if I444 or RGB input.
|
||||
Auto = FF_PROFILE_UNKNOWN,
|
||||
Proxy = 0 /*FF_PROFILE_PRORES_PROXY*/,
|
||||
Light = 1 /*FF_PROFILE_PRORES_LT*/,
|
||||
Standard = 2 /*FF_PROFILE_PRORES_STANDARD*/,
|
||||
HighQuality = 3 /*FF_PROFILE_PRORES_HQ*/,
|
||||
FourFourFourFour =
|
||||
4 /*FF_PROFILE_PRORES_4444*/ // Automatically set if I444 or RGB input.
|
||||
};
|
||||
|
||||
private:
|
||||
profile video_profile = profile::Auto;
|
||||
profile video_profile = profile::Auto;
|
||||
ffmpeg::avframe_queue frame_queue;
|
||||
ffmpeg::avframe_queue frame_queue_used;
|
||||
AVPacket* current_packet = nullptr;
|
||||
|
||||
|
||||
AVPacket* current_packet = nullptr;
|
||||
|
||||
public:
|
||||
prores_aw(obs_data_t* settings, obs_encoder_t* encoder);
|
||||
@@ -76,5 +73,3 @@ namespace obsffmpeg {
|
||||
};
|
||||
} // namespace encoder
|
||||
} // namespace obsffmpeg
|
||||
|
||||
#endif OBS_FFMPEG_ENCODER_PRORES_AW
|
||||
|
||||
@@ -18,13 +18,15 @@
|
||||
#include "avframe-queue.hpp"
|
||||
#include "tools.hpp"
|
||||
|
||||
AVFrame* ffmpeg::avframe_queue::create_frame()
|
||||
std::shared_ptr<AVFrame> ffmpeg::avframe_queue::create_frame()
|
||||
{
|
||||
AVFrame* frame = av_frame_alloc();
|
||||
frame->width = this->resolution.first;
|
||||
frame->height = this->resolution.second;
|
||||
frame->format = this->format;
|
||||
int res = av_frame_get_buffer(frame, 0);
|
||||
std::shared_ptr<AVFrame> frame =
|
||||
std::shared_ptr<AVFrame>(av_frame_alloc(), [](AVFrame* frame) { av_frame_free(&frame); });
|
||||
frame->width = this->resolution.first;
|
||||
frame->height = this->resolution.second;
|
||||
frame->format = this->format;
|
||||
|
||||
int res = av_frame_get_buffer(frame.get(), 0);
|
||||
if (res < 0) {
|
||||
throw std::exception(ffmpeg::tools::get_error_description(res));
|
||||
}
|
||||
@@ -32,14 +34,6 @@ AVFrame* ffmpeg::avframe_queue::create_frame()
|
||||
return frame;
|
||||
}
|
||||
|
||||
void ffmpeg::avframe_queue::destroy_frame(AVFrame* frame)
|
||||
{
|
||||
if (frame == nullptr)
|
||||
return;
|
||||
|
||||
av_frame_free(&frame);
|
||||
}
|
||||
|
||||
ffmpeg::avframe_queue::avframe_queue() {}
|
||||
|
||||
ffmpeg::avframe_queue::~avframe_queue()
|
||||
@@ -89,23 +83,19 @@ void ffmpeg::avframe_queue::precache(size_t count)
|
||||
void ffmpeg::avframe_queue::clear()
|
||||
{
|
||||
std::unique_lock<std::mutex> ulock(this->lock);
|
||||
for (AVFrame* frame : frames) {
|
||||
destroy_frame(frame);
|
||||
}
|
||||
frames.clear();
|
||||
}
|
||||
|
||||
void ffmpeg::avframe_queue::push(AVFrame* frame)
|
||||
void ffmpeg::avframe_queue::push(std::shared_ptr<AVFrame> frame)
|
||||
{
|
||||
std::unique_lock<std::mutex> ulock(this->lock);
|
||||
frames.push_back(frame);
|
||||
}
|
||||
|
||||
AVFrame* ffmpeg::avframe_queue::pop()
|
||||
std::shared_ptr<AVFrame> ffmpeg::avframe_queue::pop()
|
||||
{
|
||||
std::unique_lock<std::mutex> ulock(this->lock);
|
||||
|
||||
AVFrame* ret = nullptr;
|
||||
std::shared_ptr<AVFrame> ret;
|
||||
while (ret == nullptr) {
|
||||
if (frames.size() == 0) {
|
||||
ret = create_frame();
|
||||
@@ -117,7 +107,6 @@ AVFrame* ffmpeg::avframe_queue::pop()
|
||||
frames.pop_front();
|
||||
if ((ret->width != this->resolution.first) || (ret->height != this->resolution.second)
|
||||
|| (ret->format != this->format)) {
|
||||
destroy_frame(ret);
|
||||
ret = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -126,13 +115,13 @@ AVFrame* ffmpeg::avframe_queue::pop()
|
||||
return ret;
|
||||
}
|
||||
|
||||
AVFrame* ffmpeg::avframe_queue::pop_only()
|
||||
std::shared_ptr<AVFrame> ffmpeg::avframe_queue::pop_only()
|
||||
{
|
||||
std::unique_lock<std::mutex> ulock(this->lock);
|
||||
if (frames.size() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
AVFrame* ret = frames.front();
|
||||
std::shared_ptr<AVFrame> ret = frames.front();
|
||||
if (ret == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -15,27 +15,26 @@
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef OBS_FFMPEG_FFMPEG_AVFRAME_QUEUE
|
||||
#define OBS_FFMPEG_FFMPEG_AVFRAME_QUEUE
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
|
||||
extern "C" {
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavutil/frame.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
namespace ffmpeg {
|
||||
class avframe_queue {
|
||||
std::deque<AVFrame*> frames;
|
||||
std::mutex lock;
|
||||
std::deque<std::shared_ptr<AVFrame>> frames;
|
||||
std::mutex lock;
|
||||
|
||||
std::pair<uint32_t, uint32_t> resolution;
|
||||
AVPixelFormat format = AV_PIX_FMT_NONE;
|
||||
|
||||
AVFrame* create_frame();
|
||||
void destroy_frame(AVFrame* frame);
|
||||
std::shared_ptr<AVFrame> create_frame();
|
||||
|
||||
public:
|
||||
avframe_queue();
|
||||
@@ -53,18 +52,14 @@ namespace ffmpeg {
|
||||
|
||||
void clear();
|
||||
|
||||
void push(AVFrame* frame);
|
||||
void push(std::shared_ptr<AVFrame> frame);
|
||||
|
||||
AVFrame* pop();
|
||||
std::shared_ptr<AVFrame> pop();
|
||||
|
||||
AVFrame* pop_only();
|
||||
std::shared_ptr<AVFrame> pop_only();
|
||||
|
||||
bool empty();
|
||||
|
||||
size_t size();
|
||||
|
||||
|
||||
};
|
||||
} // namespace ffmpeg
|
||||
|
||||
#endif OBS_FFMPEG_FFMPEG_AVFRAME_QUEUE
|
||||
|
||||
@@ -94,7 +94,11 @@ void ffmpeg::swscale::set_target_size(uint32_t width, uint32_t height)
|
||||
target_size.second = height;
|
||||
}
|
||||
|
||||
void ffmpeg::swscale::get_target_size(uint32_t& width, uint32_t& height) {}
|
||||
void ffmpeg::swscale::get_target_size(uint32_t& width, uint32_t& height)
|
||||
{
|
||||
width = target_size.first;
|
||||
height = target_size.second;
|
||||
}
|
||||
|
||||
std::pair<uint32_t, uint32_t> ffmpeg::swscale::get_target_size()
|
||||
{
|
||||
|
||||
@@ -23,8 +23,11 @@
|
||||
#include <utility>
|
||||
|
||||
extern "C" {
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavutil/pixfmt.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
namespace ffmpeg {
|
||||
|
||||
+90
-1
@@ -16,11 +16,63 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "tools.hpp"
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
extern "C" {
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/error.h>
|
||||
#include <libavutil/pixdesc.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
std::string ffmpeg::tools::translate_encoder_capabilities(int capabilities)
|
||||
{
|
||||
// Sorted by relative importance.
|
||||
std::pair<int, std::string> caps[] = {
|
||||
{AV_CODEC_CAP_EXPERIMENTAL, "Experimental"},
|
||||
|
||||
// Quality
|
||||
{AV_CODEC_CAP_LOSSLESS, "Lossless"},
|
||||
{AV_CODEC_CAP_INTRA_ONLY, "I-Frames only"},
|
||||
|
||||
// Threading
|
||||
{AV_CODEC_CAP_FRAME_THREADS, "Frame-Threading"},
|
||||
{AV_CODEC_CAP_SLICE_THREADS, "Slice-Threading"},
|
||||
{AV_CODEC_CAP_AUTO_THREADS, "Automatic-Threading"},
|
||||
|
||||
// Features
|
||||
{AV_CODEC_CAP_PARAM_CHANGE, "Dynamic Parameter Change"},
|
||||
{AV_CODEC_CAP_SUBFRAMES, "Sub-Frames"},
|
||||
{AV_CODEC_CAP_VARIABLE_FRAME_SIZE, "Variable Frame Size"},
|
||||
{AV_CODEC_CAP_SMALL_LAST_FRAME, "Small Final Frame"},
|
||||
//{AV_CODEC_CAP_DR1, "Uses get_buffer"},
|
||||
{AV_CODEC_CAP_DELAY, "Requires Flush"},
|
||||
|
||||
// Other
|
||||
{AV_CODEC_CAP_TRUNCATED, "Truncated"},
|
||||
{AV_CODEC_CAP_CHANNEL_CONF, "AV_CODEC_CAP_CHANNEL_CONF"},
|
||||
{AV_CODEC_CAP_DRAW_HORIZ_BAND, "AV_CODEC_CAP_DRAW_HORIZ_BAND"},
|
||||
{AV_CODEC_CAP_HWACCEL_VDPAU, "AV_CODEC_CAP_HWACCEL_VDPAU"},
|
||||
{AV_CODEC_CAP_AVOID_PROBING, "AV_CODEC_CAP_AVOID_PROBING"},
|
||||
};
|
||||
|
||||
std::stringstream sstr;
|
||||
for (auto kv : caps) {
|
||||
if (capabilities & kv.first) {
|
||||
capabilities &= ~kv.first;
|
||||
sstr << kv.second;
|
||||
if (capabilities != 0) {
|
||||
sstr << ", ";
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
const char* ffmpeg::tools::get_pixel_format_name(AVPixelFormat v)
|
||||
@@ -67,10 +119,47 @@ const char* ffmpeg::tools::get_error_description(int error)
|
||||
switch (error) {
|
||||
case AVERROR(EPERM):
|
||||
return "Permission Denied";
|
||||
// case AVERROR(ENOENT):
|
||||
// case AVERROR(ESRCH):
|
||||
// case AVERROR(EINTR):
|
||||
// case AVERROR(EIO):
|
||||
// case AVERROR(ENXIO):
|
||||
// case AVERROR(E2BIG):
|
||||
// case AVERROR(ENOEXEC):
|
||||
// case AVERROR(EBADF):
|
||||
// case AVERROR(ECHILD):
|
||||
// case AVERROR(EAGAIN):
|
||||
case AVERROR(ENOMEM):
|
||||
return "Out Of Memory";
|
||||
// case AVERROR(EACCES):
|
||||
// case AVERROR(EFAULT):
|
||||
// case AVERROR(EBUSY):
|
||||
// case AVERROR(EEXIST):
|
||||
// case AVERROR(EXDEV):
|
||||
// case AVERROR(ENODEV):
|
||||
// case AVERROR(ENOTDIR):
|
||||
// case AVERROR(EISDIR):
|
||||
// case AVERROR(ENFILE):
|
||||
// case AVERROR(EMFILE):
|
||||
// case AVERROR(ENOTTY):
|
||||
// case AVERROR(EFBIG):
|
||||
// case AVERROR(ENOSPC):
|
||||
// case AVERROR(ESPIPE):
|
||||
// case AVERROR(EROFS):
|
||||
// case AVERROR(EMLINK):
|
||||
// case AVERROR(EPIPE):
|
||||
// case AVERROR(EDOM):
|
||||
// case AVERROR(EDEADLK):
|
||||
// case AVERROR(ENAMETOOLONG):
|
||||
// case AVERROR(ENOLCK):
|
||||
// case AVERROR(ENOSYS):
|
||||
// case AVERROR(ENOTEMPTY):
|
||||
case AVERROR(EINVAL):
|
||||
return "Invalid Value for Parameter";
|
||||
return "Invalid Value(s)";
|
||||
case AVERROR(ERANGE):
|
||||
return "Out of Range";
|
||||
// case AVERROR(EILSEQ):
|
||||
// case AVERROR(STRUNCATE):
|
||||
}
|
||||
return "Not Translated Yet";
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <obs.h>
|
||||
#include <string>
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/pixfmt.h>
|
||||
@@ -27,6 +28,8 @@ extern "C" {
|
||||
|
||||
namespace ffmpeg {
|
||||
namespace tools {
|
||||
std::string translate_encoder_capabilities(int capabilities);
|
||||
|
||||
const char* get_pixel_format_name(AVPixelFormat v);
|
||||
|
||||
const char* get_color_space_name(AVColorSpace v);
|
||||
@@ -38,7 +41,7 @@ namespace ffmpeg {
|
||||
AVColorSpace obs_videocolorspace_to_avcolorspace(video_colorspace v);
|
||||
|
||||
AVColorRange obs_videorangetype_to_avcolorrange(video_range_type v);
|
||||
}
|
||||
} // namespace tools
|
||||
} // namespace ffmpeg
|
||||
|
||||
#endif OBS_FFMPEG_FFMPEG_UTILITY
|
||||
|
||||
+69
-20
@@ -16,40 +16,89 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "plugin.hpp"
|
||||
#include <obs-module.h>
|
||||
#include <obs.h>
|
||||
#include <memory>
|
||||
#include "encoders/generic.hpp"
|
||||
#include "encoders/prores_aw.hpp"
|
||||
#include "ui/debug_handler.hpp"
|
||||
#include "ui/handler.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include "encoders/prores_aw.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <obs-module.h>
|
||||
#include <obs.h>
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavcodec/avcodec.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
MODULE_EXPORT bool obs_module_load(void)
|
||||
// Initializers and finalizers.
|
||||
std::list<std::function<void()>> obsffmpeg::initializers;
|
||||
|
||||
std::list<std::function<void()>> obsffmpeg::finalizers;
|
||||
|
||||
// Codec to Handler mapping.
|
||||
static std::map<std::string, std::shared_ptr<obsffmpeg::ui::handler>> codec_to_handler_map;
|
||||
static std::shared_ptr<obsffmpeg::ui::handler> debug_handler = std::make_shared<obsffmpeg::ui::debug_handler>();
|
||||
|
||||
void obsffmpeg::register_codec_handler(std::string codec, std::shared_ptr<obsffmpeg::ui::handler> handler)
|
||||
{
|
||||
try {
|
||||
avcodec_register_all();
|
||||
obsffmpeg::encoder::prores_aw::initialize();
|
||||
return true;
|
||||
} catch (std::exception ex) {
|
||||
PLOG_ERROR("Exception during initalization: %s.", ex.what());
|
||||
} catch (...) {
|
||||
PLOG_ERROR("Unrecognized exception during initalization.");
|
||||
codec_to_handler_map.emplace(codec, handler);
|
||||
}
|
||||
|
||||
std::shared_ptr<obsffmpeg::ui::handler> obsffmpeg::find_codec_handler(std::string codec)
|
||||
{
|
||||
auto found = codec_to_handler_map.find(codec);
|
||||
if (found == codec_to_handler_map.end())
|
||||
return debug_handler;
|
||||
return found->second;
|
||||
}
|
||||
|
||||
static std::map<AVCodec*, std::shared_ptr<encoder::generic_factory>> generic_factories;
|
||||
|
||||
MODULE_EXPORT bool obs_module_load(void)
|
||||
try {
|
||||
// Initialize avcodec.
|
||||
avcodec_register_all();
|
||||
|
||||
// Run all initializers.
|
||||
for (auto func : obsffmpeg::initializers) {
|
||||
func();
|
||||
}
|
||||
|
||||
// Register all codecs.
|
||||
AVCodec* cdc = nullptr;
|
||||
while ((cdc = av_codec_next(cdc)) != nullptr) {
|
||||
if (!av_codec_is_encoder(cdc))
|
||||
continue;
|
||||
|
||||
if ((cdc->type == AVMediaType::AVMEDIA_TYPE_AUDIO) || (cdc->type == AVMediaType::AVMEDIA_TYPE_VIDEO)) {
|
||||
auto ptr = std::make_shared<encoder::generic_factory>(cdc);
|
||||
ptr->register_encoder();
|
||||
generic_factories.emplace(cdc, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
obsffmpeg::encoder::prores_aw::initialize();
|
||||
return true;
|
||||
} catch (std::exception ex) {
|
||||
PLOG_ERROR("Exception during initalization: %s.", ex.what());
|
||||
return false;
|
||||
} catch (...) {
|
||||
PLOG_ERROR("Unrecognized exception during initalization.");
|
||||
return false;
|
||||
}
|
||||
|
||||
MODULE_EXPORT void obs_module_unload(void)
|
||||
{
|
||||
try {
|
||||
obsffmpeg::encoder::prores_aw::finalize();
|
||||
} catch (std::exception ex) {
|
||||
PLOG_ERROR("Exception during finalizing: %s.", ex.what());
|
||||
} catch (...) {
|
||||
PLOG_ERROR("Unrecognized exception during finalizing.");
|
||||
try {
|
||||
obsffmpeg::encoder::prores_aw::finalize();
|
||||
|
||||
// Run all finalizers.
|
||||
for (auto func : obsffmpeg::finalizers) {
|
||||
func();
|
||||
}
|
||||
} catch (std::exception ex) {
|
||||
PLOG_ERROR("Exception during finalizing: %s.", ex.what());
|
||||
} catch (...) {
|
||||
PLOG_ERROR("Unrecognized exception during finalizing.");
|
||||
}
|
||||
|
||||
+12
-5
@@ -1,5 +1,5 @@
|
||||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (C) 2018 - 2018 Michael Fabian Dirks
|
||||
// Copyright (C) 2018 - 2019 Michael Fabian Dirks
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -15,16 +15,23 @@
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef OBS_FFMPEG_PLUGIN_HPP
|
||||
#define OBS_FFMPEG_PLUGIN_HPP
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <obs-module.h>
|
||||
#include "ui/handler.hpp"
|
||||
|
||||
namespace obsffmpeg {
|
||||
extern std::list<std::function<void()>> initializers;
|
||||
|
||||
extern std::list<std::function<void()>> finalizers;
|
||||
|
||||
void register_codec_handler(std::string codec, std::shared_ptr<obsffmpeg::ui::handler> handler);
|
||||
|
||||
std::shared_ptr<obsffmpeg::ui::handler> find_codec_handler(std::string codec);
|
||||
|
||||
} // namespace obsffmpeg
|
||||
|
||||
#endif OBS_FFMPEG_PLUGIN_HPP
|
||||
MODULE_EXPORT bool obs_module_load(void);
|
||||
MODULE_EXPORT void obs_module_unload(void);
|
||||
|
||||
@@ -0,0 +1,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) {}
|
||||
@@ -0,0 +1,34 @@
|
||||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (C) 2018 - 2019 Michael Fabian Dirks
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#pragma once
|
||||
#include "handler.hpp"
|
||||
|
||||
namespace obsffmpeg {
|
||||
namespace ui {
|
||||
class debug_handler : public handler {
|
||||
public:
|
||||
virtual void get_defaults(obs_data_t* settings, AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
|
||||
virtual void get_properties(obs_properties_t* props, AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
|
||||
virtual void update(obs_data_t* settings, AVCodec* codec, AVCodecContext* context) override;
|
||||
};
|
||||
} // namespace ui
|
||||
} // namespace obsffmpeg
|
||||
@@ -0,0 +1,18 @@
|
||||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (C) 2018 - 2019 Michael Fabian Dirks
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "handler.hpp"
|
||||
@@ -0,0 +1,40 @@
|
||||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (C) 2018 - 2019 Michael Fabian Dirks
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#pragma once
|
||||
|
||||
extern "C" {
|
||||
#include <obs-properties.h>
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavcodec/avcodec.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
namespace obsffmpeg {
|
||||
namespace ui {
|
||||
class handler {
|
||||
public:
|
||||
virtual void get_defaults(obs_data_t* settings, AVCodec* codec, AVCodecContext* context) = 0;
|
||||
|
||||
virtual void get_properties(obs_properties_t* props, AVCodec* codec,
|
||||
AVCodecContext* context) = 0;
|
||||
|
||||
virtual void update(obs_data_t* settings, AVCodec* codec, AVCodecContext* context) = 0;
|
||||
};
|
||||
} // namespace ui
|
||||
} // namespace obsffmpeg
|
||||
@@ -0,0 +1,79 @@
|
||||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (C) 2018 - 2019 Michael Fabian Dirks
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "prores_aw_handler.hpp"
|
||||
#include "plugin.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <obs-module.h>
|
||||
}
|
||||
|
||||
#define P_PROFILE "AppleProRes.Profile"
|
||||
#define P_PROFILE_APCO "AppleProRes.Profile.APCO"
|
||||
#define P_PROFILE_APCS "AppleProRes.Profile.APCS"
|
||||
#define P_PROFILE_APCN "AppleProRes.Profile.APCN"
|
||||
#define P_PROFILE_APCH "AppleProRes.Profile.APCH"
|
||||
#define P_PROFILE_AP4H "AppleProRes.Profile.AP4H"
|
||||
|
||||
INITIALIZER(prores_aw_handler_init)
|
||||
{
|
||||
obsffmpeg::initializers.push_back([]() {
|
||||
obsffmpeg::register_codec_handler("prores_aw", std::make_shared<obsffmpeg::ui::prores_aw_handler>());
|
||||
});
|
||||
};
|
||||
|
||||
void obsffmpeg::ui::prores_aw_handler::get_defaults(obs_data_t* settings, AVCodec*, AVCodecContext*)
|
||||
{
|
||||
obs_data_set_default_int(settings, P_PROFILE, 0);
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::prores_aw_handler::get_properties(obs_properties_t* props, AVCodec* codec, AVCodecContext* context)
|
||||
{
|
||||
if (!context) {
|
||||
auto p = obs_properties_add_list(props, P_PROFILE, TRANSLATE(P_PROFILE), OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_INT);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_PROFILE)));
|
||||
for (auto ptr = codec->profiles; ptr->profile != FF_PROFILE_UNKNOWN; ptr++) {
|
||||
if (strcmp("apco", ptr->name) == 0) {
|
||||
obs_property_list_add_int(p, TRANSLATE(P_PROFILE_APCO),
|
||||
static_cast<int64_t>(ptr->profile));
|
||||
} else if (strcmp("apcs", ptr->name) == 0) {
|
||||
obs_property_list_add_int(p, TRANSLATE(P_PROFILE_APCS),
|
||||
static_cast<int64_t>(ptr->profile));
|
||||
} else if (strcmp("apcn", ptr->name) == 0) {
|
||||
obs_property_list_add_int(p, TRANSLATE(P_PROFILE_APCN),
|
||||
static_cast<int64_t>(ptr->profile));
|
||||
} else if (strcmp("apch", ptr->name) == 0) {
|
||||
obs_property_list_add_int(p, TRANSLATE(P_PROFILE_APCH),
|
||||
static_cast<int64_t>(ptr->profile));
|
||||
} else if (strcmp("ap4h", ptr->name) == 0) {
|
||||
obs_property_list_add_int(p, TRANSLATE(P_PROFILE_AP4H),
|
||||
static_cast<int64_t>(ptr->profile));
|
||||
} else {
|
||||
obs_property_list_add_int(p, ptr->name, ptr->profile);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
obs_property_set_enabled(obs_properties_get(props, P_PROFILE), false);
|
||||
}
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::prores_aw_handler::update(obs_data_t* settings, AVCodec*, AVCodecContext* context)
|
||||
{
|
||||
context->profile = static_cast<int>(obs_data_get_int(settings, P_PROFILE));
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// FFMPEG Video Encoder Integration for OBS Studio
|
||||
// Copyright (C) 2018 - 2019 Michael Fabian Dirks
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#pragma once
|
||||
#include "handler.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <obs-properties.h>
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavcodec/avcodec.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
namespace obsffmpeg {
|
||||
namespace ui {
|
||||
class prores_aw_handler : public handler {
|
||||
public:
|
||||
virtual void get_defaults(obs_data_t* settings, AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
|
||||
virtual void get_properties(obs_properties_t* props, AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
|
||||
virtual void update(obs_data_t* settings, AVCodec* codec, AVCodecContext* context) override;
|
||||
};
|
||||
} // namespace ui
|
||||
} // namespace obsffmpeg
|
||||
+28
-3
@@ -15,8 +15,6 @@
|
||||
// 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"
|
||||
@@ -45,4 +43,31 @@
|
||||
#define vstr(s) dstr(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
|
||||
|
||||
Reference in New Issue
Block a user