Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fe71944199 | |||
| cbd39a8c2a | |||
| 62eae3827b | |||
| 38e7639862 | |||
| 6bc1cb9c88 | |||
| c63900d575 | |||
| 9efda8af8d | |||
| 0c9764a15c | |||
| 5c5a235502 | |||
| 2ebf90ffd7 | |||
| a32f8dd28b | |||
| d8a692de93 | |||
| a9f39527f6 | |||
| 71440ed3c5 | |||
| 3bd147e6e7 | |||
| 8b6af720bf | |||
| 993a4f8110 | |||
| 36222cc186 | |||
| 9fcec9b9a9 | |||
| 11f72c9bc7 | |||
| 5c8939b4a8 |
@@ -0,0 +1,34 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [windows-2016, windows-2019]
|
||||||
|
include:
|
||||||
|
- os: windows-2016
|
||||||
|
generator_32: "Visual Studio 15 2017"
|
||||||
|
generator_64: "Visual Studio 15 2017 Win64"
|
||||||
|
sysversion: "10.0.17763.0"
|
||||||
|
- os: windows-2019
|
||||||
|
generator_32:
|
||||||
|
generator_64: "Visual Studio 16 2019"
|
||||||
|
sysversion: "10.0.18362.0"
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Clone Repository
|
||||||
|
uses: actions/checkout@v1
|
||||||
|
- name: Update Submodules
|
||||||
|
run: git submodule update --init --force --recursive
|
||||||
|
- name: Install Node.JS 10.x
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 10
|
||||||
|
- name: Build
|
||||||
|
env:
|
||||||
|
CMAKE_GENERATOR_32: ${{ matrix.generator_32 }}
|
||||||
|
CMAKE_GENERATOR_64: ${{ matrix.generator_64 }}
|
||||||
|
CMAKE_SYSTEM_VERSION: ${{ matrix.sysversion }}
|
||||||
|
run: node ./ci/builder.js
|
||||||
+3
-1
@@ -155,7 +155,7 @@ mark_as_advanced(FORCE OBS_NATIVE OBS_PACKAGE OBS_REFERENCE OBS_DOWNLOAD)
|
|||||||
|
|
||||||
if(NOT TARGET libobs)
|
if(NOT TARGET libobs)
|
||||||
set(${PropertyPrefix}OBS_STUDIO_DIR "" CACHE PATH "OBS Studio Source/Package Directory")
|
set(${PropertyPrefix}OBS_STUDIO_DIR "" CACHE PATH "OBS Studio Source/Package Directory")
|
||||||
set(${PropertyPrefix}OBS_DOWNLOAD_VERSION "23.2.1-ci" CACHE STRING "OBS Studio Version to download")
|
set(${PropertyPrefix}OBS_DOWNLOAD_VERSION "24.0.0-rc2-ci" CACHE STRING "OBS Studio Version to download")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT ${PropertyPrefix}OBS_NATIVE)
|
if(NOT ${PropertyPrefix}OBS_NATIVE)
|
||||||
@@ -272,6 +272,8 @@ set(PROJECT_PRIVATE
|
|||||||
"${PROJECT_SOURCE_DIR}/source/codecs/hevc.cpp"
|
"${PROJECT_SOURCE_DIR}/source/codecs/hevc.cpp"
|
||||||
"${PROJECT_SOURCE_DIR}/source/codecs/h264.hpp"
|
"${PROJECT_SOURCE_DIR}/source/codecs/h264.hpp"
|
||||||
"${PROJECT_SOURCE_DIR}/source/codecs/h264.cpp"
|
"${PROJECT_SOURCE_DIR}/source/codecs/h264.cpp"
|
||||||
|
"${PROJECT_SOURCE_DIR}/source/codecs/prores.hpp"
|
||||||
|
"${PROJECT_SOURCE_DIR}/source/codecs/prores.cpp"
|
||||||
"${PROJECT_SOURCE_DIR}/source/ffmpeg/avframe-queue.cpp"
|
"${PROJECT_SOURCE_DIR}/source/ffmpeg/avframe-queue.cpp"
|
||||||
"${PROJECT_SOURCE_DIR}/source/ffmpeg/avframe-queue.hpp"
|
"${PROJECT_SOURCE_DIR}/source/ffmpeg/avframe-queue.hpp"
|
||||||
"${PROJECT_SOURCE_DIR}/source/ffmpeg/swscale.hpp"
|
"${PROJECT_SOURCE_DIR}/source/ffmpeg/swscale.hpp"
|
||||||
|
|||||||
@@ -7,11 +7,16 @@ matrix:
|
|||||||
# Build Image & Environment
|
# Build Image & Environment
|
||||||
platform: x64
|
platform: x64
|
||||||
|
|
||||||
|
# Build Tags only
|
||||||
|
skip_non_tags: true
|
||||||
|
|
||||||
image:
|
image:
|
||||||
- Visual Studio 2017
|
- Visual Studio 2017
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
CMAKE_SYSTEM_VERSION: 10.0.17134.0
|
CMAKE_SYSTEM_VERSION: 10.0.17134.0
|
||||||
|
CMAKE_GENERATOR_32: "Visual Studio 15 2017"
|
||||||
|
CMAKE_GENERATOR_64: "Visual Studio 15 2017 Win64"
|
||||||
PACKAGE_PREFIX: obs-ffmpeg-encoder
|
PACKAGE_PREFIX: obs-ffmpeg-encoder
|
||||||
INNOSETUP_URL: http://www.jrsoftware.org/download.php/is.exe
|
INNOSETUP_URL: http://www.jrsoftware.org/download.php/is.exe
|
||||||
CURL_VERSION: 7.39.0
|
CURL_VERSION: 7.39.0
|
||||||
@@ -32,6 +37,7 @@ build_script:
|
|||||||
- cmd: node ci/builder.js
|
- cmd: node ci/builder.js
|
||||||
|
|
||||||
after_build:
|
after_build:
|
||||||
|
- cmd: node ci/packager.js
|
||||||
- cmd: ci/appveyor-package.bat
|
- cmd: ci/appveyor-package.bat
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
|
|||||||
+73
-87
@@ -2,108 +2,94 @@
|
|||||||
|
|
||||||
const process = require('process');
|
const process = require('process');
|
||||||
const runner = require('./runner.js');
|
const runner = require('./runner.js');
|
||||||
|
let env = process.env;
|
||||||
|
|
||||||
// Steps
|
let x32_steps = [];
|
||||||
let configure_runners = [];
|
let x64_steps = [];
|
||||||
let build_runners = [];
|
|
||||||
let package_runners = [];
|
|
||||||
|
|
||||||
{
|
if ((process.platform == "win32") || (process.platform == "win64")) {
|
||||||
let cmake_configure_extra = [
|
// Windows
|
||||||
|
let extra_conf = [
|
||||||
`-DCMAKE_SYSTEM_VERSION=${process.env.CMAKE_SYSTEM_VERSION}`,
|
`-DCMAKE_SYSTEM_VERSION=${process.env.CMAKE_SYSTEM_VERSION}`,
|
||||||
|
`-DCMAKE_PACKAGE_NAME=obs-ffmpeg-encoder`,
|
||||||
'-DCMAKE_INSTALL_PREFIX="build/distrib/"',
|
'-DCMAKE_INSTALL_PREFIX="build/distrib/"',
|
||||||
'-DCMAKE_PACKAGE_PREFIX="build/"',
|
'-DCMAKE_PACKAGE_PREFIX="build/"',
|
||||||
`-DCMAKE_PACKAGE_NAME="${process.env.PACKAGE_PREFIX}"`,
|
|
||||||
];
|
];
|
||||||
let cmake_build_extra = [
|
let extra_build = [
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// Configuration depends on platform
|
if(process.env.APPVEYOR) {
|
||||||
if (process.platform == "win32" || process.platform == "win64") {
|
extra_build.concat(['--', '/logger:"C:\\Program Files\\AppVeyor\\BuildAgent\\Appveyor.MSBuildLogger.dll"']);
|
||||||
configure_runners.push(new runner('32-bit', 'cmake', [
|
}
|
||||||
|
|
||||||
|
if ((process.env.CMAKE_GENERATOR_32 !== undefined) && (process.env.CMAKE_GENERATOR_32 !== "")) {
|
||||||
|
x32_steps.push(
|
||||||
|
[ 'cmake', [
|
||||||
'-H.',
|
'-H.',
|
||||||
'-Bbuild/32',
|
'-Bbuild/32',
|
||||||
`-G"Visual Studio 15 2017"`,
|
`-G"${process.env.CMAKE_GENERATOR_32}"`,
|
||||||
].concat(cmake_configure_extra)));
|
].concat(extra_conf), env ]
|
||||||
configure_runners.push(new runner('64-bit', 'cmake', [
|
);
|
||||||
|
x32_steps.push(
|
||||||
|
[ 'cmake', [
|
||||||
|
'--build', 'build/32',
|
||||||
|
'--config', 'RelWithDebInfo',
|
||||||
|
'--target', 'INSTALL'
|
||||||
|
].concat(extra_build), env ]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ((process.env.CMAKE_GENERATOR_64 !== undefined) && (process.env.CMAKE_GENERATOR_64 !== "")) {
|
||||||
|
x64_steps.push(
|
||||||
|
[ 'cmake', [
|
||||||
'-H.',
|
'-H.',
|
||||||
'-Bbuild/64',
|
'-Bbuild/64',
|
||||||
`-G"Visual Studio 15 2017 Win64"`,
|
`-G"${process.env.CMAKE_GENERATOR_64}"`,
|
||||||
'-T"host=x64"',
|
'-T"host=x64"'
|
||||||
].concat(cmake_configure_extra)));
|
].concat(extra_conf), env ]
|
||||||
|
);
|
||||||
// Extra build steps for AppVeyor on Windows for Logging purposes.
|
x64_steps.push(
|
||||||
if(process.env.APPVEYOR) {
|
[ 'cmake', [
|
||||||
cmake_build_extra.concat(['--', '/logger:"C:\\Program Files\\AppVeyor\\BuildAgent\\Appveyor.MSBuildLogger.dll"']);
|
'--build', 'build/64',
|
||||||
}
|
'--config', 'RelWithDebInfo',
|
||||||
} else if (process.platform == "linux") {
|
'--target', 'INSTALL'
|
||||||
configure_runners.push(new runner('32-bit', 'cmake', [
|
].concat(extra_build), env ]
|
||||||
'-H.',
|
);
|
||||||
'-Bbuild32',
|
|
||||||
`-G"Unix Makefiles"`,
|
|
||||||
`-DCOPIED_DEPENDENCIES=false`,
|
|
||||||
].concat(cmake_configure_extra),
|
|
||||||
{ ...process.env, ...{
|
|
||||||
CFLAGS: `${process.env.COMPILER_FLAGS_32}`,
|
|
||||||
CXXFLAGS: `${process.env.COMPILER_FLAGS_32}`,
|
|
||||||
}}));
|
|
||||||
configure_runners.push(new runner('64-bit', 'cmake', [
|
|
||||||
'-H.',
|
|
||||||
'-Bbuild64',
|
|
||||||
`-G"Unix Makefiles"`,
|
|
||||||
`-DCOPIED_DEPENDENCIES=false`,
|
|
||||||
].concat(cmake_configure_extra),
|
|
||||||
{ ...process.env, ...{
|
|
||||||
CFLAGS: `${process.env.COMPILER_FLAGS_64}`,
|
|
||||||
CXXFLAGS: `${process.env.COMPILER_FLAGS_64}`,
|
|
||||||
}}));
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Unix
|
||||||
|
|
||||||
build_runners.push(new runner('32-bit', 'cmake', [
|
|
||||||
'--build', 'build/32',
|
|
||||||
'--config', 'RelWithDebInfo',
|
|
||||||
'--target', 'INSTALL'
|
|
||||||
].concat(cmake_build_extra)));
|
|
||||||
build_runners.push(new runner('64-bit', 'cmake', [
|
|
||||||
'--build', 'build/64',
|
|
||||||
'--config', 'RelWithDebInfo',
|
|
||||||
'--target', 'INSTALL'
|
|
||||||
].concat(cmake_build_extra)));
|
|
||||||
package_runners.push(new runner('32-bit', 'cmake', [
|
|
||||||
'--build', 'build/32',
|
|
||||||
'--target', 'PACKAGE_7Z',
|
|
||||||
'--config', 'RelWithDebInfo'
|
|
||||||
].concat(cmake_build_extra)));
|
|
||||||
package_runners.push(new runner('64-bit', 'cmake', [
|
|
||||||
'--build', 'build/64',
|
|
||||||
'--target', 'PACKAGE_ZIP',
|
|
||||||
'--config', 'RelWithDebInfo'
|
|
||||||
].concat(cmake_build_extra)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run Configure steps.
|
function runRunners(runnerArray, name) {
|
||||||
let configure_promises = [];
|
return new Promise(async (resolve, reject) => {
|
||||||
for (let config of configure_runners) {
|
let local = runnerArray.reverse();
|
||||||
configure_promises.push(config.run());
|
while (local.length > 0) {
|
||||||
}
|
try {
|
||||||
Promise.all(configure_promises).then(function(result) {
|
let task = local.pop();
|
||||||
let build_promises = [];
|
let work = new runner(name, task[0], task[1], task[2]);
|
||||||
for (let build of build_runners) {
|
await work.run();
|
||||||
build_promises.push(build.run());
|
} catch (e) {
|
||||||
}
|
reject(e);
|
||||||
Promise.all(build_promises).then(function(result) {
|
return;
|
||||||
let package_promises = [];
|
}
|
||||||
for (let pack of package_runners) {
|
|
||||||
package_promises.push(pack.run());
|
|
||||||
}
|
}
|
||||||
Promise.all(package_promises).then(function(result) {
|
resolve(0);
|
||||||
process.exit(result);
|
|
||||||
}).catch(function(result) {
|
|
||||||
process.exit(result);
|
|
||||||
});
|
|
||||||
}).catch(function(result) {
|
|
||||||
process.exit(result);
|
|
||||||
});
|
});
|
||||||
}).catch(function(result) {
|
}
|
||||||
process.exit(result);
|
|
||||||
});
|
let promises = [];
|
||||||
|
promises.push(runRunners(x32_steps, "32-Bit"));
|
||||||
|
promises.push(runRunners(x64_steps, "64-Bit"));
|
||||||
|
Promise.all(promises).then(
|
||||||
|
res => {
|
||||||
|
process.exit(0);
|
||||||
|
},
|
||||||
|
err => {
|
||||||
|
console.log(err);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
).catch(err => {
|
||||||
|
console.log(err);
|
||||||
|
process.exit(1);
|
||||||
|
})
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const process = require('process');
|
||||||
|
const runner = require('./runner.js');
|
||||||
|
|
||||||
|
function runRunners(runnerArray, name) {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
let local = runnerArray.reverse();
|
||||||
|
while (local.length > 0) {
|
||||||
|
let task = local.pop();
|
||||||
|
let work = new runner(name, task[0], task[1], task[2]);
|
||||||
|
await work.run();
|
||||||
|
}
|
||||||
|
resolve(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let env = process.env;
|
||||||
|
let steps = [];
|
||||||
|
|
||||||
|
if ((process.env.CMAKE_GENERATOR_64 !== undefined) && (process.env.CMAKE_GENERATOR_64 !== "")) {
|
||||||
|
steps.push(
|
||||||
|
[ 'cmake', [
|
||||||
|
'--build', 'build/64',
|
||||||
|
'--config', 'RelWithDebInfo',
|
||||||
|
'--target', 'PACKAGE_7Z'
|
||||||
|
], env ]
|
||||||
|
);
|
||||||
|
steps.push(
|
||||||
|
[ 'cmake', [
|
||||||
|
'--build', 'build/64',
|
||||||
|
'--config', 'RelWithDebInfo',
|
||||||
|
'--target', 'PACKAGE_ZIP'
|
||||||
|
], env ]
|
||||||
|
);
|
||||||
|
} else if ((process.env.CMAKE_GENERATOR_32 !== undefined) && (process.env.CMAKE_GENERATOR_32 !== "")) {
|
||||||
|
steps.push(
|
||||||
|
[ 'cmake', [
|
||||||
|
'--build', 'build/32',
|
||||||
|
'--config', 'RelWithDebInfo',
|
||||||
|
'--target', 'PACKAGE_7Z'
|
||||||
|
], env ]
|
||||||
|
);
|
||||||
|
steps.push(
|
||||||
|
[ 'cmake', [
|
||||||
|
'--build', 'build/32',
|
||||||
|
'--config', 'RelWithDebInfo',
|
||||||
|
'--target', 'PACKAGE_ZIP'
|
||||||
|
], env ]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let promises = [];
|
||||||
|
promises.push(runRunners(steps, "32-Bit"));
|
||||||
|
Promise.all(promises).then(
|
||||||
|
res => {
|
||||||
|
process.exit(0);
|
||||||
|
},
|
||||||
|
err => {
|
||||||
|
console.log(err);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -18,7 +18,6 @@ class Runner {
|
|||||||
run() {
|
run() {
|
||||||
let self = this;
|
let self = this;
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
console.log(self.cmd, self.args);
|
|
||||||
self.proc = cp.spawn(
|
self.proc = cp.spawn(
|
||||||
self.cmd, self.args, {
|
self.cmd, self.args, {
|
||||||
windowsVerbatimArguments: true,
|
windowsVerbatimArguments: true,
|
||||||
|
|||||||
+9
-14
@@ -48,20 +48,6 @@ KeyFrames.IntervalType.Frames="Frames"
|
|||||||
KeyFrames.IntervalType.Seconds="Seconds"
|
KeyFrames.IntervalType.Seconds="Seconds"
|
||||||
KeyFrames.Interval="Interval"
|
KeyFrames.Interval="Interval"
|
||||||
|
|
||||||
# Apple ProRes
|
|
||||||
AppleProRes.Profile="Profile"
|
|
||||||
AppleProRes.Profile.APCO="Proxy"
|
|
||||||
AppleProRes.Profile.APCS="LT"
|
|
||||||
AppleProRes.Profile.APCN="Standard Definition"
|
|
||||||
AppleProRes.Profile.APCH="High Quality"
|
|
||||||
AppleProRes.Profile.AP4H="4444"
|
|
||||||
|
|
||||||
# ProRes
|
|
||||||
ProRes.Profile.Proxy="Proxy (PXY)"
|
|
||||||
ProRes.Profile.Light="Light (LT)"
|
|
||||||
ProRes.Profile.Standard="Standard"
|
|
||||||
ProRes.Profile.HighQuality="High Quality (HQ)"
|
|
||||||
|
|
||||||
# Codec: H264
|
# Codec: H264
|
||||||
Codec.H264="H264"
|
Codec.H264="H264"
|
||||||
Codec.H264.Profile="Profile"
|
Codec.H264.Profile="Profile"
|
||||||
@@ -82,6 +68,15 @@ Codec.HEVC.Tier.main="Main"
|
|||||||
Codec.HEVC.Tier.high="High"
|
Codec.HEVC.Tier.high="High"
|
||||||
Codec.HEVC.Level="Level"
|
Codec.HEVC.Level="Level"
|
||||||
|
|
||||||
|
# Codec: Apple ProRes
|
||||||
|
Codec.ProRes.Profile="Profile"
|
||||||
|
Codec.ProRes.Profile.APCO="422 Proxy/PXY (APCO)"
|
||||||
|
Codec.ProRes.Profile.APCS="422 Light/LT (APCS)"
|
||||||
|
Codec.ProRes.Profile.APCN="422 Standard (APCN)"
|
||||||
|
Codec.ProRes.Profile.APCH="422 High Quality/HQ (APCH)"
|
||||||
|
Codec.ProRes.Profile.AP4H="4444 Standard (AP4H)"
|
||||||
|
Codec.ProRes.Profile.AP4X="4444 Extra Quality/XQ (AP4X)"
|
||||||
|
|
||||||
# NVENC
|
# NVENC
|
||||||
NVENC.Preset="Preset"
|
NVENC.Preset="Preset"
|
||||||
NVENC.Preset.Default="Default"
|
NVENC.Preset.Default="Default"
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// FFMPEG Video Encoder Integration for OBS Studio
|
||||||
|
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#include "prores.hpp"
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
// FFMPEG Video Encoder Integration for OBS Studio
|
||||||
|
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Codec: ProRes
|
||||||
|
#define P_PRORES "Codec.ProRes"
|
||||||
|
#define P_PRORES_PROFILE "Codec.ProRes.Profile"
|
||||||
|
#define P_PRORES_PROFILE_APCS "Codec.ProRes.Profile.APCS"
|
||||||
|
#define P_PRORES_PROFILE_APCO "Codec.ProRes.Profile.APCO"
|
||||||
|
#define P_PRORES_PROFILE_APCN "Codec.ProRes.Profile.APCN"
|
||||||
|
#define P_PRORES_PROFILE_APCH "Codec.ProRes.Profile.APCH"
|
||||||
|
#define P_PRORES_PROFILE_AP4H "Codec.ProRes.Profile.AP4H"
|
||||||
|
#define P_PRORES_PROFILE_AP4X "Codec.ProRes.Profile.AP4X"
|
||||||
+495
-516
File diff suppressed because it is too large
Load Diff
+25
-9
@@ -24,8 +24,10 @@
|
|||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
#include "ffmpeg/avframe-queue.hpp"
|
#include "ffmpeg/avframe-queue.hpp"
|
||||||
#include "ffmpeg/swscale.hpp"
|
#include "ffmpeg/swscale.hpp"
|
||||||
|
#include "ui/handler.hpp"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <obs-properties.h>
|
#include <obs-properties.h>
|
||||||
@@ -38,28 +40,40 @@ extern "C" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace obsffmpeg {
|
namespace obsffmpeg {
|
||||||
|
class unsupported_gpu_exception : public std::runtime_error {
|
||||||
|
public:
|
||||||
|
unsupported_gpu_exception(const std::string& reason) : runtime_error(reason) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct encoder_info {
|
||||||
|
std::string uid;
|
||||||
|
std::string codec;
|
||||||
|
std::string readable_name;
|
||||||
|
obs_encoder_info oei;
|
||||||
|
};
|
||||||
|
|
||||||
class encoder_factory {
|
class encoder_factory {
|
||||||
struct info {
|
encoder_info info;
|
||||||
std::string uid;
|
encoder_info info_fallback;
|
||||||
std::string codec;
|
|
||||||
std::string readable_name;
|
|
||||||
obs_encoder_info oei;
|
|
||||||
} info;
|
|
||||||
const AVCodec* avcodec_ptr;
|
const AVCodec* avcodec_ptr;
|
||||||
|
|
||||||
|
std::shared_ptr<obsffmpeg::ui::handler> _handler;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
encoder_factory(const AVCodec* codec);
|
encoder_factory(const AVCodec* codec);
|
||||||
virtual ~encoder_factory();
|
virtual ~encoder_factory();
|
||||||
|
|
||||||
void register_encoder();
|
void register_encoder();
|
||||||
|
|
||||||
const char* get_name();
|
|
||||||
|
|
||||||
void get_defaults(obs_data_t* settings);
|
void get_defaults(obs_data_t* settings);
|
||||||
|
|
||||||
void get_properties(obs_properties_t* props);
|
void get_properties(obs_properties_t* props);
|
||||||
|
|
||||||
const AVCodec* get_avcodec();
|
const AVCodec* get_avcodec();
|
||||||
|
|
||||||
|
const encoder_info& get_info();
|
||||||
|
|
||||||
|
const encoder_info& get_fallback();
|
||||||
};
|
};
|
||||||
|
|
||||||
class encoder {
|
class encoder {
|
||||||
@@ -69,6 +83,8 @@ namespace obsffmpeg {
|
|||||||
const AVCodec* _codec;
|
const AVCodec* _codec;
|
||||||
AVCodecContext* _context;
|
AVCodecContext* _context;
|
||||||
|
|
||||||
|
std::shared_ptr<obsffmpeg::ui::handler> _handler;
|
||||||
|
|
||||||
ffmpeg::avframe_queue _frame_queue;
|
ffmpeg::avframe_queue _frame_queue;
|
||||||
ffmpeg::avframe_queue _frame_queue_used;
|
ffmpeg::avframe_queue _frame_queue_used;
|
||||||
ffmpeg::swscale _swscale;
|
ffmpeg::swscale _swscale;
|
||||||
@@ -83,7 +99,7 @@ namespace obsffmpeg {
|
|||||||
std::vector<uint8_t> _sei_data;
|
std::vector<uint8_t> _sei_data;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
encoder(obs_data_t* settings, obs_encoder_t* encoder);
|
encoder(obs_data_t* settings, obs_encoder_t* encoder, bool is_texture_encode = false);
|
||||||
virtual ~encoder();
|
virtual ~encoder();
|
||||||
|
|
||||||
public: // OBS API
|
public: // OBS API
|
||||||
|
|||||||
+220
-44
@@ -20,6 +20,8 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
|
|
||||||
#include "tools.hpp"
|
#include "tools.hpp"
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
@@ -159,59 +161,49 @@ const char* ffmpeg::tools::get_error_description(int error)
|
|||||||
return "Not Translated Yet";
|
return "Not Translated Yet";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::map<video_format, AVPixelFormat> obs_to_av_format_map = {
|
||||||
|
{VIDEO_FORMAT_I420, AV_PIX_FMT_YUV420P}, // YUV 4:2:0
|
||||||
|
{VIDEO_FORMAT_NV12, AV_PIX_FMT_NV12}, // NV12 Packed YUV
|
||||||
|
{VIDEO_FORMAT_YVYU, AV_PIX_FMT_YVYU422}, // YVYU Packed YUV
|
||||||
|
{VIDEO_FORMAT_YUY2, AV_PIX_FMT_YUYV422}, // YUYV Packed YUV
|
||||||
|
{VIDEO_FORMAT_UYVY, AV_PIX_FMT_UYVY422}, // UYVY Packed YUV
|
||||||
|
{VIDEO_FORMAT_RGBA, AV_PIX_FMT_RGBA}, //
|
||||||
|
{VIDEO_FORMAT_BGRA, AV_PIX_FMT_BGRA}, //
|
||||||
|
{VIDEO_FORMAT_BGRX, AV_PIX_FMT_BGR0}, //
|
||||||
|
{VIDEO_FORMAT_Y800, AV_PIX_FMT_GRAY8}, //
|
||||||
|
{VIDEO_FORMAT_I444, AV_PIX_FMT_YUV444P}, //
|
||||||
|
{VIDEO_FORMAT_BGR3, AV_PIX_FMT_BGR24}, //
|
||||||
|
{VIDEO_FORMAT_I422, AV_PIX_FMT_YUV422P}, //
|
||||||
|
{VIDEO_FORMAT_I40A, AV_PIX_FMT_YUVA420P}, //
|
||||||
|
{VIDEO_FORMAT_I42A, AV_PIX_FMT_YUVA422P}, //
|
||||||
|
{VIDEO_FORMAT_YUVA, AV_PIX_FMT_YUVA444P}, //
|
||||||
|
//{VIDEO_FORMAT_AYUV, AV_PIX_FMT_AYUV444P}, //
|
||||||
|
};
|
||||||
|
|
||||||
AVPixelFormat ffmpeg::tools::obs_videoformat_to_avpixelformat(video_format v)
|
AVPixelFormat ffmpeg::tools::obs_videoformat_to_avpixelformat(video_format v)
|
||||||
{
|
{
|
||||||
switch (v) {
|
auto found = obs_to_av_format_map.find(v);
|
||||||
// 32-Bits
|
if (found != obs_to_av_format_map.end()) {
|
||||||
case VIDEO_FORMAT_BGRX:
|
return found->second;
|
||||||
return AV_PIX_FMT_BGR0;
|
|
||||||
case VIDEO_FORMAT_BGRA:
|
|
||||||
return AV_PIX_FMT_BGRA;
|
|
||||||
case VIDEO_FORMAT_RGBA:
|
|
||||||
return AV_PIX_FMT_RGBA;
|
|
||||||
case VIDEO_FORMAT_I444:
|
|
||||||
return AV_PIX_FMT_YUV444P;
|
|
||||||
case VIDEO_FORMAT_YUY2:
|
|
||||||
return AV_PIX_FMT_YUYV422;
|
|
||||||
case VIDEO_FORMAT_YVYU:
|
|
||||||
return AV_PIX_FMT_YVYU422;
|
|
||||||
case VIDEO_FORMAT_UYVY:
|
|
||||||
return AV_PIX_FMT_UYVY422;
|
|
||||||
case VIDEO_FORMAT_I420:
|
|
||||||
return AV_PIX_FMT_YUV420P;
|
|
||||||
case VIDEO_FORMAT_NV12:
|
|
||||||
return AV_PIX_FMT_NV12;
|
|
||||||
}
|
}
|
||||||
throw std::invalid_argument("unknown format");
|
return AV_PIX_FMT_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
video_format ffmpeg::tools::avpixelformat_to_obs_videoformat(AVPixelFormat v)
|
video_format ffmpeg::tools::avpixelformat_to_obs_videoformat(AVPixelFormat v)
|
||||||
{
|
{
|
||||||
switch (v) {
|
for (const auto& kv : obs_to_av_format_map) {
|
||||||
case AV_PIX_FMT_YUV420P:
|
if (kv.second == v)
|
||||||
return VIDEO_FORMAT_I420;
|
return kv.first;
|
||||||
case AV_PIX_FMT_NV12:
|
|
||||||
return VIDEO_FORMAT_NV12;
|
|
||||||
case AV_PIX_FMT_YVYU422:
|
|
||||||
return VIDEO_FORMAT_YVYU;
|
|
||||||
case AV_PIX_FMT_YUYV422:
|
|
||||||
return VIDEO_FORMAT_YUY2;
|
|
||||||
case AV_PIX_FMT_UYVY422:
|
|
||||||
return VIDEO_FORMAT_UYVY;
|
|
||||||
case AV_PIX_FMT_RGBA:
|
|
||||||
return VIDEO_FORMAT_RGBA;
|
|
||||||
case AV_PIX_FMT_BGRA:
|
|
||||||
return VIDEO_FORMAT_BGRA;
|
|
||||||
case AV_PIX_FMT_BGR0:
|
|
||||||
return VIDEO_FORMAT_BGRX;
|
|
||||||
case AV_PIX_FMT_GRAY8:
|
|
||||||
return VIDEO_FORMAT_Y800;
|
|
||||||
case AV_PIX_FMT_YUV444P:
|
|
||||||
return VIDEO_FORMAT_I444;
|
|
||||||
}
|
}
|
||||||
return VIDEO_FORMAT_NONE;
|
return VIDEO_FORMAT_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AVPixelFormat ffmpeg::tools::get_least_lossy_format(const AVPixelFormat* haystack, AVPixelFormat needle)
|
||||||
|
{
|
||||||
|
int data_loss = 0;
|
||||||
|
return avcodec_find_best_pix_fmt_of_list(haystack, needle, 0, &data_loss);
|
||||||
|
}
|
||||||
|
|
||||||
AVColorSpace ffmpeg::tools::obs_videocolorspace_to_avcolorspace(video_colorspace v)
|
AVColorSpace ffmpeg::tools::obs_videocolorspace_to_avcolorspace(video_colorspace v)
|
||||||
{
|
{
|
||||||
switch (v) {
|
switch (v) {
|
||||||
@@ -228,10 +220,194 @@ AVColorRange ffmpeg::tools::obs_videorangetype_to_avcolorrange(video_range_type
|
|||||||
{
|
{
|
||||||
switch (v) {
|
switch (v) {
|
||||||
case VIDEO_RANGE_DEFAULT:
|
case VIDEO_RANGE_DEFAULT:
|
||||||
case VIDEO_RANGE_FULL:
|
|
||||||
return AVCOL_RANGE_JPEG;
|
|
||||||
case VIDEO_RANGE_PARTIAL:
|
case VIDEO_RANGE_PARTIAL:
|
||||||
return AVCOL_RANGE_MPEG;
|
return AVCOL_RANGE_MPEG;
|
||||||
|
case VIDEO_RANGE_FULL:
|
||||||
|
return AVCOL_RANGE_JPEG;
|
||||||
}
|
}
|
||||||
throw std::invalid_argument("unknown range");
|
throw std::invalid_argument("unknown range");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ffmpeg::tools::can_hardware_encode(const AVCodec* codec)
|
||||||
|
{
|
||||||
|
AVPixelFormat hardware_formats[] = {AV_PIX_FMT_D3D11};
|
||||||
|
|
||||||
|
for (const AVPixelFormat* fmt = codec->pix_fmts; (fmt != nullptr) && (*fmt != AV_PIX_FMT_NONE); fmt++) {
|
||||||
|
for (auto cmp : hardware_formats) {
|
||||||
|
if (*fmt == cmp) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<AVPixelFormat> ffmpeg::tools::get_software_formats(const AVPixelFormat* list)
|
||||||
|
{
|
||||||
|
AVPixelFormat hardware_formats[] = {
|
||||||
|
#if FF_API_VAAPI
|
||||||
|
AV_PIX_FMT_VAAPI_MOCO,
|
||||||
|
AV_PIX_FMT_VAAPI_IDCT,
|
||||||
|
#endif
|
||||||
|
AV_PIX_FMT_VAAPI,
|
||||||
|
AV_PIX_FMT_DXVA2_VLD,
|
||||||
|
AV_PIX_FMT_VDPAU,
|
||||||
|
AV_PIX_FMT_QSV,
|
||||||
|
AV_PIX_FMT_MMAL,
|
||||||
|
AV_PIX_FMT_D3D11VA_VLD,
|
||||||
|
AV_PIX_FMT_CUDA,
|
||||||
|
AV_PIX_FMT_XVMC,
|
||||||
|
AV_PIX_FMT_VIDEOTOOLBOX,
|
||||||
|
AV_PIX_FMT_MEDIACODEC,
|
||||||
|
AV_PIX_FMT_D3D11,
|
||||||
|
AV_PIX_FMT_OPENCL,
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<AVPixelFormat> fmts;
|
||||||
|
for (auto fmt = list; fmt && (*fmt != AV_PIX_FMT_NONE); fmt++) {
|
||||||
|
bool is_blacklisted = false;
|
||||||
|
for (auto blacklisted : hardware_formats) {
|
||||||
|
if (*fmt == blacklisted)
|
||||||
|
is_blacklisted = true;
|
||||||
|
}
|
||||||
|
if (!is_blacklisted)
|
||||||
|
fmts.push_back(*fmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
fmts.push_back(AV_PIX_FMT_NONE);
|
||||||
|
|
||||||
|
return std::move(fmts);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::map<std::pair<AVPixelFormat, AVPixelFormat>, double_t> format_compatibility = {
|
||||||
|
{{AV_PIX_FMT_NV12, AV_PIX_FMT_NV12}, std::numeric_limits<double_t>::max()},
|
||||||
|
{{AV_PIX_FMT_NV12, AV_PIX_FMT_NV21}, 65535.0},
|
||||||
|
|
||||||
|
{{AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P}, std::numeric_limits<double_t>::max()},
|
||||||
|
{{AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVA420P}, 65535.0},
|
||||||
|
{{AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P9}, 58981.5},
|
||||||
|
{{AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P10}, 53083.35},
|
||||||
|
{{AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P12}, 47775.015},
|
||||||
|
{{AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P14}, 42997.5135},
|
||||||
|
{{AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P16}, 38697.76215},
|
||||||
|
|
||||||
|
{{AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA420P}, std::numeric_limits<double_t>::max()},
|
||||||
|
{{AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA420P9}, 65535.0},
|
||||||
|
{{AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA420P10}, 58981.5},
|
||||||
|
{{AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA420P16}, 53083.35},
|
||||||
|
{{AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P}, 32767.0},
|
||||||
|
|
||||||
|
{{AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV422P}, std::numeric_limits<double_t>::max()},
|
||||||
|
{{AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA422P}, 65535.0},
|
||||||
|
{{AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV422P9}, 58981.5},
|
||||||
|
{{AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV422P10}, 53083.35},
|
||||||
|
{{AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV422P12}, 47775.015},
|
||||||
|
{{AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV422P14}, 42997.5135},
|
||||||
|
{{AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV422P16}, 38697.76215},
|
||||||
|
|
||||||
|
{{AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA422P}, std::numeric_limits<double_t>::max()},
|
||||||
|
{{AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA422P9}, 65535.0},
|
||||||
|
{{AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA422P10}, 58981.5},
|
||||||
|
{{AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA422P16}, 53083.35},
|
||||||
|
{{AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P}, 32767.0},
|
||||||
|
|
||||||
|
{{AV_PIX_FMT_YVYU422, AV_PIX_FMT_YVYU422}, std::numeric_limits<double_t>::max()},
|
||||||
|
{{AV_PIX_FMT_YVYU422, AV_PIX_FMT_YUYV422}, 65535.0},
|
||||||
|
|
||||||
|
{{AV_PIX_FMT_UYVY422, AV_PIX_FMT_UYVY422}, std::numeric_limits<double_t>::max()},
|
||||||
|
{{AV_PIX_FMT_UYVY422, AV_PIX_FMT_YVYU422}, 65535.0},
|
||||||
|
|
||||||
|
{{AV_PIX_FMT_YUYV422, AV_PIX_FMT_YUYV422}, std::numeric_limits<double_t>::max()},
|
||||||
|
|
||||||
|
{{AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV444P}, std::numeric_limits<double_t>::max()},
|
||||||
|
{{AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVA444P}, 65535.0},
|
||||||
|
{{AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV444P9}, 58981.5},
|
||||||
|
{{AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV444P10}, 53083.35},
|
||||||
|
{{AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV444P12}, 47775.015},
|
||||||
|
{{AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV444P14}, 42997.5135},
|
||||||
|
{{AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV444P16}, 38697.76215},
|
||||||
|
|
||||||
|
{{AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA444P}, std::numeric_limits<double_t>::max()},
|
||||||
|
{{AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA444P9}, 65535.0},
|
||||||
|
{{AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA444P10}, 58981.5},
|
||||||
|
{{AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA444P16}, 53083.35},
|
||||||
|
{{AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P}, 32767.0},
|
||||||
|
|
||||||
|
{{AV_PIX_FMT_RGBA, AV_PIX_FMT_RGBA}, std::numeric_limits<double_t>::max()},
|
||||||
|
{{AV_PIX_FMT_RGBA, AV_PIX_FMT_RGB0}, 65535.0},
|
||||||
|
{{AV_PIX_FMT_RGBA, AV_PIX_FMT_0RGB}, 32767.0},
|
||||||
|
{{AV_PIX_FMT_RGBA, AV_PIX_FMT_RGB24}, 16384.0},
|
||||||
|
|
||||||
|
{{AV_PIX_FMT_BGRA, AV_PIX_FMT_BGRA}, std::numeric_limits<double_t>::max()},
|
||||||
|
{{AV_PIX_FMT_BGRA, AV_PIX_FMT_BGR0}, 65535.0},
|
||||||
|
{{AV_PIX_FMT_BGRA, AV_PIX_FMT_0BGR}, 32767.0},
|
||||||
|
{{AV_PIX_FMT_BGRA, AV_PIX_FMT_BGR24}, 16384.0},
|
||||||
|
|
||||||
|
{{AV_PIX_FMT_BGR0, AV_PIX_FMT_BGR0}, std::numeric_limits<double_t>::max()},
|
||||||
|
{{AV_PIX_FMT_BGR0, AV_PIX_FMT_BGRA}, 65535.0},
|
||||||
|
{{AV_PIX_FMT_BGR0, AV_PIX_FMT_BGR24}, 32767.0},
|
||||||
|
|
||||||
|
{{AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY8}, std::numeric_limits<double_t>::max()},
|
||||||
|
{{AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9}, 65535.0},
|
||||||
|
{{AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY10}, 58981.5},
|
||||||
|
{{AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY12}, 53083.35},
|
||||||
|
{{AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY14}, 47775.015},
|
||||||
|
{{AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY16}, 42997.5135},
|
||||||
|
|
||||||
|
{{AV_PIX_FMT_BGR24, AV_PIX_FMT_BGR24}, std::numeric_limits<double_t>::max()},
|
||||||
|
{{AV_PIX_FMT_BGR24, AV_PIX_FMT_RGB24}, 32767.0},
|
||||||
|
};
|
||||||
|
|
||||||
|
AVPixelFormat ffmpeg::tools::get_best_compatible_format(const AVPixelFormat* list, AVPixelFormat source)
|
||||||
|
{
|
||||||
|
double_t score = std::numeric_limits<double_t>::min();
|
||||||
|
AVPixelFormat best = source;
|
||||||
|
|
||||||
|
for (auto fmt = list; fmt && (*fmt != AV_PIX_FMT_NONE); fmt++) {
|
||||||
|
auto found = format_compatibility.find(std::pair{source, *fmt});
|
||||||
|
if (found != format_compatibility.end() && (score < found->second)) {
|
||||||
|
score = found->second;
|
||||||
|
best = *fmt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (score <= 0) {
|
||||||
|
int data_loss = 0;
|
||||||
|
return avcodec_find_best_pix_fmt_of_list(list, source, 0, &data_loss);
|
||||||
|
}
|
||||||
|
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ffmpeg::tools::setup_obs_color(video_colorspace colorspace, video_range_type range, AVCodecContext* context)
|
||||||
|
{
|
||||||
|
std::map<video_colorspace, std::tuple<AVColorSpace, AVColorPrimaries, AVColorTransferCharacteristic>>
|
||||||
|
colorspaces = {
|
||||||
|
{VIDEO_CS_DEFAULT, {AVCOL_SPC_BT470BG, AVCOL_PRI_BT470BG, AVCOL_TRC_SMPTE170M}},
|
||||||
|
{VIDEO_CS_601, {AVCOL_SPC_BT470BG, AVCOL_PRI_BT470BG, AVCOL_TRC_SMPTE170M}},
|
||||||
|
{VIDEO_CS_709, {AVCOL_SPC_BT709, AVCOL_PRI_BT709, AVCOL_TRC_BT709}},
|
||||||
|
};
|
||||||
|
std::map<video_range_type, AVColorRange> colorranges = {
|
||||||
|
{VIDEO_RANGE_DEFAULT, AVCOL_RANGE_MPEG},
|
||||||
|
{VIDEO_RANGE_PARTIAL, AVCOL_RANGE_MPEG},
|
||||||
|
{VIDEO_RANGE_FULL, AVCOL_RANGE_JPEG},
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
auto found = colorspaces.find(colorspace);
|
||||||
|
if (found != colorspaces.end()) {
|
||||||
|
context->colorspace = std::get<AVColorSpace>(found->second);
|
||||||
|
context->color_primaries = std::get<AVColorPrimaries>(found->second);
|
||||||
|
context->color_trc = std::get<AVColorTransferCharacteristic>(found->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto found = colorranges.find(range);
|
||||||
|
if (found != colorranges.end()) {
|
||||||
|
context->color_range = found->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Downscaling should result in downscaling, not pixelation
|
||||||
|
context->chroma_sample_location = AVCHROMA_LOC_CENTER;
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,8 +25,10 @@
|
|||||||
|
|
||||||
#include <obs.h>
|
#include <obs.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
#include <libavcodec/avcodec.h>
|
||||||
#include <libavutil/pixfmt.h>
|
#include <libavutil/pixfmt.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,9 +46,19 @@ namespace ffmpeg {
|
|||||||
|
|
||||||
video_format avpixelformat_to_obs_videoformat(AVPixelFormat v);
|
video_format avpixelformat_to_obs_videoformat(AVPixelFormat v);
|
||||||
|
|
||||||
|
AVPixelFormat get_least_lossy_format(const AVPixelFormat* haystack, AVPixelFormat needle);
|
||||||
|
|
||||||
AVColorSpace obs_videocolorspace_to_avcolorspace(video_colorspace v);
|
AVColorSpace obs_videocolorspace_to_avcolorspace(video_colorspace v);
|
||||||
|
|
||||||
AVColorRange obs_videorangetype_to_avcolorrange(video_range_type v);
|
AVColorRange obs_videorangetype_to_avcolorrange(video_range_type v);
|
||||||
|
|
||||||
|
bool can_hardware_encode(const AVCodec* codec);
|
||||||
|
|
||||||
|
std::vector<AVPixelFormat> get_software_formats(const AVPixelFormat* list);
|
||||||
|
|
||||||
|
AVPixelFormat get_best_compatible_format(const AVPixelFormat* list, AVPixelFormat source);
|
||||||
|
|
||||||
|
void setup_obs_color(video_colorspace colorspace, video_range_type range, AVCodecContext* context);
|
||||||
} // namespace tools
|
} // namespace tools
|
||||||
} // namespace ffmpeg
|
} // namespace ffmpeg
|
||||||
|
|
||||||
|
|||||||
@@ -22,3 +22,29 @@
|
|||||||
#include "handler.hpp"
|
#include "handler.hpp"
|
||||||
|
|
||||||
void obsffmpeg::ui::handler::override_visible_name(const AVCodec*, std::string&) {}
|
void obsffmpeg::ui::handler::override_visible_name(const AVCodec*, std::string&) {}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::handler::override_info(obs_encoder_info* main, obs_encoder_info* fallback) {}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::handler::override_colorformat(AVPixelFormat& target_format, obs_data_t* settings,
|
||||||
|
const AVCodec* codec, AVCodecContext* context)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::handler::get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context) {}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::handler::get_properties(obs_properties_t* props, const AVCodec* codec, AVCodecContext* context) {}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::handler::update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context) {}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::handler::log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context) {}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::handler::import_from_ffmpeg(const std::string ffmpeg, obs_data_t* settings, const AVCodec* codec,
|
||||||
|
AVCodecContext* context)
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::string obsffmpeg::ui::handler::export_for_ffmpeg(obs_data_t* settings, const AVCodec* codec,
|
||||||
|
AVCodecContext* context)
|
||||||
|
{
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::handler::process_avpacket(AVPacket& packet, const AVCodec* codec, AVCodecContext* context) {}
|
||||||
|
|||||||
+21
-4
@@ -24,7 +24,10 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
#include <obs.h>
|
||||||
|
|
||||||
#include <obs-data.h>
|
#include <obs-data.h>
|
||||||
|
#include <obs-encoder.h>
|
||||||
#include <obs-properties.h>
|
#include <obs-properties.h>
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
#pragma warning(disable : 4244)
|
#pragma warning(disable : 4244)
|
||||||
@@ -38,13 +41,27 @@ namespace obsffmpeg {
|
|||||||
public:
|
public:
|
||||||
virtual void override_visible_name(const AVCodec* codec, std::string& name);
|
virtual void override_visible_name(const AVCodec* codec, std::string& name);
|
||||||
|
|
||||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec,
|
virtual void override_info(obs_encoder_info* main, obs_encoder_info* fallback);
|
||||||
AVCodecContext* context) = 0;
|
|
||||||
|
virtual void override_colorformat(AVPixelFormat& target_format, obs_data_t* settings,
|
||||||
|
const AVCodec* codec, AVCodecContext* context);
|
||||||
|
|
||||||
|
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||||
|
|
||||||
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||||
AVCodecContext* context) = 0;
|
AVCodecContext* context);
|
||||||
|
|
||||||
virtual void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context) = 0;
|
virtual void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||||
|
|
||||||
|
virtual void log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||||
|
|
||||||
|
virtual void import_from_ffmpeg(const std::string ffmpeg, obs_data_t* settings,
|
||||||
|
const AVCodec* codec, AVCodecContext* context);
|
||||||
|
|
||||||
|
virtual std::string export_for_ffmpeg(obs_data_t* settings, const AVCodec* codec,
|
||||||
|
AVCodecContext* context);
|
||||||
|
|
||||||
|
virtual void process_avpacket(AVPacket& packet, const AVCodec* codec, AVCodecContext* context);
|
||||||
};
|
};
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
} // namespace obsffmpeg
|
} // namespace obsffmpeg
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ Useless except for strict_gop maybe?
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
using namespace obsffmpeg::codecs::h264;
|
using namespace obsffmpeg::codecs::h264;
|
||||||
|
using namespace obsffmpeg::nvenc;
|
||||||
|
|
||||||
std::map<profile, std::string> profiles{
|
std::map<profile, std::string> profiles{
|
||||||
{profile::BASELINE, "baseline"},
|
{profile::BASELINE, "baseline"},
|
||||||
@@ -125,6 +126,22 @@ void obsffmpeg::ui::nvenc_h264_handler::update(obs_data_t* settings, const AVCod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::nvenc_h264_handler::log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context)
|
||||||
|
{
|
||||||
|
nvenc::log_options(settings, codec, context);
|
||||||
|
|
||||||
|
profile cfg_profile = static_cast<profile>(obs_data_get_int(settings, P_H264_PROFILE));
|
||||||
|
level cfg_level = static_cast<level>(obs_data_get_int(settings, P_H264_LEVEL));
|
||||||
|
|
||||||
|
auto found1 = profiles.find(cfg_profile);
|
||||||
|
if (found1 != profiles.end())
|
||||||
|
PLOG_INFO("[%s] H.264 Profile: %s", codec->name, found1->second.c_str());
|
||||||
|
|
||||||
|
auto found2 = levels.find(cfg_level);
|
||||||
|
if (found2 != levels.end())
|
||||||
|
PLOG_INFO("[%s] H.264 Level: %s", codec->name, found2->second.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
void obsffmpeg::ui::nvenc_h264_handler::get_encoder_properties(obs_properties_t* props, const AVCodec* codec)
|
void obsffmpeg::ui::nvenc_h264_handler::get_encoder_properties(obs_properties_t* props, const AVCodec* codec)
|
||||||
{
|
{
|
||||||
nvenc::get_properties_pre(props, codec);
|
nvenc::get_properties_pre(props, codec);
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ namespace obsffmpeg {
|
|||||||
virtual void update(obs_data_t* settings, const AVCodec* codec,
|
virtual void update(obs_data_t* settings, const AVCodec* codec,
|
||||||
AVCodecContext* context) override;
|
AVCodecContext* context) override;
|
||||||
|
|
||||||
|
virtual void log_options(obs_data_t* settings, const AVCodec* codec,
|
||||||
|
AVCodecContext* context) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void get_encoder_properties(obs_properties_t* props, const AVCodec* codec);
|
void get_encoder_properties(obs_properties_t* props, const AVCodec* codec);
|
||||||
|
|
||||||
|
|||||||
@@ -133,6 +133,27 @@ void obsffmpeg::ui::nvenc_hevc_handler::update(obs_data_t* settings, const AVCod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::nvenc_hevc_handler::log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context)
|
||||||
|
{
|
||||||
|
nvenc::log_options(settings, codec, context);
|
||||||
|
|
||||||
|
profile cfg_profile = static_cast<profile>(obs_data_get_int(settings, P_HEVC_PROFILE));
|
||||||
|
tier cfg_tier = static_cast<tier>(obs_data_get_int(settings, P_HEVC_TIER));
|
||||||
|
level cfg_level = static_cast<level>(obs_data_get_int(settings, P_HEVC_LEVEL));
|
||||||
|
|
||||||
|
auto found1 = profiles.find(cfg_profile);
|
||||||
|
if (found1 != profiles.end())
|
||||||
|
PLOG_INFO("[%s] H.265 Profile: %s", codec->name, found1->second.c_str());
|
||||||
|
|
||||||
|
auto found2 = levels.find(cfg_level);
|
||||||
|
if (found2 != levels.end())
|
||||||
|
PLOG_INFO("[%s] H.265 Level: %s", codec->name, found2->second.c_str());
|
||||||
|
|
||||||
|
auto found3 = tiers.find(cfg_tier);
|
||||||
|
if (found3 != tiers.end())
|
||||||
|
PLOG_INFO("[%s] H.265 Tier: %s", codec->name, found3->second.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
void obsffmpeg::ui::nvenc_hevc_handler::get_encoder_properties(obs_properties_t* props, const AVCodec* codec)
|
void obsffmpeg::ui::nvenc_hevc_handler::get_encoder_properties(obs_properties_t* props, const AVCodec* codec)
|
||||||
{
|
{
|
||||||
nvenc::get_properties_pre(props, codec);
|
nvenc::get_properties_pre(props, codec);
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ namespace obsffmpeg {
|
|||||||
virtual void update(obs_data_t* settings, const AVCodec* codec,
|
virtual void update(obs_data_t* settings, const AVCodec* codec,
|
||||||
AVCodecContext* context) override;
|
AVCodecContext* context) override;
|
||||||
|
|
||||||
|
virtual void log_options(obs_data_t* settings, const AVCodec* codec,
|
||||||
|
AVCodecContext* context) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void get_encoder_properties(obs_properties_t* props, const AVCodec* codec);
|
void get_encoder_properties(obs_properties_t* props, const AVCodec* codec);
|
||||||
|
|
||||||
|
|||||||
+118
-8
@@ -57,7 +57,7 @@ extern "C" {
|
|||||||
#define ST_RATECONTROL_QP_I ST_RATECONTROL_QP ".I"
|
#define ST_RATECONTROL_QP_I ST_RATECONTROL_QP ".I"
|
||||||
#define ST_RATECONTROL_QP_I_INITIAL ST_RATECONTROL_QP_I ".Initial"
|
#define ST_RATECONTROL_QP_I_INITIAL ST_RATECONTROL_QP_I ".Initial"
|
||||||
#define ST_RATECONTROL_QP_P ST_RATECONTROL_QP ".P"
|
#define ST_RATECONTROL_QP_P ST_RATECONTROL_QP ".P"
|
||||||
#define ST_RATECONTROL_QP_ST_INITIAL ST_RATECONTROL_QP_P ".Initial"
|
#define ST_RATECONTROL_QP_P_INITIAL ST_RATECONTROL_QP_P ".Initial"
|
||||||
#define ST_RATECONTROL_QP_B ST_RATECONTROL_QP ".B"
|
#define ST_RATECONTROL_QP_B ST_RATECONTROL_QP ".B"
|
||||||
#define ST_RATECONTROL_QP_B_INITIAL ST_RATECONTROL_QP_B ".Initial"
|
#define ST_RATECONTROL_QP_B_INITIAL ST_RATECONTROL_QP_B ".Initial"
|
||||||
|
|
||||||
@@ -152,7 +152,7 @@ void obsffmpeg::nvenc::get_defaults(obs_data_t* settings, const AVCodec*, AVCode
|
|||||||
obs_data_set_default_int(settings, ST_RATECONTROL_QP_I, 21);
|
obs_data_set_default_int(settings, ST_RATECONTROL_QP_I, 21);
|
||||||
obs_data_set_default_int(settings, ST_RATECONTROL_QP_I_INITIAL, -1);
|
obs_data_set_default_int(settings, ST_RATECONTROL_QP_I_INITIAL, -1);
|
||||||
obs_data_set_default_int(settings, ST_RATECONTROL_QP_P, 21);
|
obs_data_set_default_int(settings, ST_RATECONTROL_QP_P, 21);
|
||||||
obs_data_set_default_int(settings, ST_RATECONTROL_QP_ST_INITIAL, -1);
|
obs_data_set_default_int(settings, ST_RATECONTROL_QP_P_INITIAL, -1);
|
||||||
obs_data_set_default_int(settings, ST_RATECONTROL_QP_B, 21);
|
obs_data_set_default_int(settings, ST_RATECONTROL_QP_B, 21);
|
||||||
obs_data_set_default_int(settings, ST_RATECONTROL_QP_B_INITIAL, -1);
|
obs_data_set_default_int(settings, ST_RATECONTROL_QP_B_INITIAL, -1);
|
||||||
|
|
||||||
@@ -211,7 +211,7 @@ static bool modified_ratecontrol(obs_properties_t* props, obs_property_t*, obs_d
|
|||||||
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_P), have_qp);
|
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_P), have_qp);
|
||||||
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_B), have_qp);
|
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_B), have_qp);
|
||||||
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_I_INITIAL), have_qp_init);
|
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_I_INITIAL), have_qp_init);
|
||||||
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_ST_INITIAL), have_qp_init);
|
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_P_INITIAL), have_qp_init);
|
||||||
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_B_INITIAL), have_qp_init);
|
obs_property_set_visible(obs_properties_get(props, ST_RATECONTROL_QP_B_INITIAL), have_qp_init);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -379,9 +379,9 @@ void obsffmpeg::nvenc::get_properties_post(obs_properties_t* props, const AVCode
|
|||||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_QP_P)));
|
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_QP_P)));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto p = obs_properties_add_int_slider(grp, ST_RATECONTROL_QP_ST_INITIAL,
|
auto p = obs_properties_add_int_slider(grp, ST_RATECONTROL_QP_P_INITIAL,
|
||||||
TRANSLATE(ST_RATECONTROL_QP_ST_INITIAL), -1, 51, 1);
|
TRANSLATE(ST_RATECONTROL_QP_P_INITIAL), -1, 51, 1);
|
||||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_QP_ST_INITIAL)));
|
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_QP_P_INITIAL)));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto p = obs_properties_add_int_slider(grp, ST_RATECONTROL_QP_B, TRANSLATE(ST_RATECONTROL_QP_B),
|
auto p = obs_properties_add_int_slider(grp, ST_RATECONTROL_QP_B, TRANSLATE(ST_RATECONTROL_QP_B),
|
||||||
@@ -483,7 +483,7 @@ void obsffmpeg::nvenc::get_runtime_properties(obs_properties_t* props, const AVC
|
|||||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_I), false);
|
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_I), false);
|
||||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_I_INITIAL), false);
|
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_I_INITIAL), false);
|
||||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_P), false);
|
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_P), false);
|
||||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_ST_INITIAL), false);
|
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_P_INITIAL), false);
|
||||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_B), false);
|
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_B), false);
|
||||||
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_B_INITIAL), false);
|
obs_property_set_enabled(obs_properties_get(props, ST_RATECONTROL_QP_B_INITIAL), false);
|
||||||
obs_property_set_enabled(obs_properties_get(props, ST_AQ), false);
|
obs_property_set_enabled(obs_properties_get(props, ST_AQ), false);
|
||||||
@@ -597,7 +597,7 @@ void obsffmpeg::nvenc::update(obs_data_t* settings, const AVCodec* codec, AVCode
|
|||||||
av_opt_set_int(context->priv_data, "init_qpI",
|
av_opt_set_int(context->priv_data, "init_qpI",
|
||||||
obs_data_get_int(settings, ST_RATECONTROL_QP_I_INITIAL), 0);
|
obs_data_get_int(settings, ST_RATECONTROL_QP_I_INITIAL), 0);
|
||||||
av_opt_set_int(context->priv_data, "init_qpP",
|
av_opt_set_int(context->priv_data, "init_qpP",
|
||||||
obs_data_get_int(settings, ST_RATECONTROL_QP_ST_INITIAL), 0);
|
obs_data_get_int(settings, ST_RATECONTROL_QP_P_INITIAL), 0);
|
||||||
av_opt_set_int(context->priv_data, "init_qpB",
|
av_opt_set_int(context->priv_data, "init_qpB",
|
||||||
obs_data_get_int(settings, ST_RATECONTROL_QP_B_INITIAL), 0);
|
obs_data_get_int(settings, ST_RATECONTROL_QP_B_INITIAL), 0);
|
||||||
}
|
}
|
||||||
@@ -646,3 +646,113 @@ void obsffmpeg::nvenc::update(obs_data_t* settings, const AVCodec* codec, AVCode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void obsffmpeg::nvenc::log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context)
|
||||||
|
{
|
||||||
|
preset cfg_preset = static_cast<preset>(obs_data_get_int(settings, ST_PRESET));
|
||||||
|
|
||||||
|
auto found1 = preset_to_opt.find(cfg_preset);
|
||||||
|
if (found1 != preset_to_opt.end())
|
||||||
|
PLOG_INFO("[%s] Preset: %s", codec->name, found1->second.c_str());
|
||||||
|
|
||||||
|
ratecontrolmode cfg_rc_mode = static_cast<ratecontrolmode>(obs_data_get_int(settings, ST_RATECONTROL_MODE));
|
||||||
|
int64_t cfg_rc_2pass = obs_data_get_int(settings, ST_RATECONTROL_TWOPASS);
|
||||||
|
int64_t cfg_rc_lahead = obs_data_get_int(settings, ST_RATECONTROL_LOOKAHEAD);
|
||||||
|
bool cfg_rc_adapti = obs_data_get_bool(settings, ST_RATECONTROL_ADAPTIVEI);
|
||||||
|
bool cfg_rc_adaptb = obs_data_get_bool(settings, ST_RATECONTROL_ADAPTIVEB);
|
||||||
|
|
||||||
|
auto found2 = ratecontrolmode_to_opt.find(cfg_rc_mode);
|
||||||
|
if (found2 != ratecontrolmode_to_opt.end())
|
||||||
|
PLOG_INFO("[%s] Rate Control: %s", codec->name, found2->second.c_str());
|
||||||
|
PLOG_INFO("[%s] Two Pass: %s", codec->name,
|
||||||
|
cfg_rc_2pass == 1 ? "Enabled" : (cfg_rc_2pass == 0 ? "Disabled" : "Default"));
|
||||||
|
PLOG_INFO("[%s] Lookahead: %" PRId64 " Frames", codec->name, cfg_rc_lahead);
|
||||||
|
if (cfg_rc_adapti && cfg_rc_lahead > 0)
|
||||||
|
PLOG_INFO("[%s] Adaptive I-Frames Enabled", codec->name);
|
||||||
|
if (cfg_rc_adaptb && cfg_rc_lahead > 0)
|
||||||
|
PLOG_INFO("[%s] Adaptive B-Frames Enabled", codec->name);
|
||||||
|
|
||||||
|
int64_t cfg_rc_bitrate = obs_data_get_int(settings, ST_RATECONTROL_BITRATE_TARGET);
|
||||||
|
int64_t cfg_rc_max_bitrate = obs_data_get_int(settings, ST_RATECONTROL_BITRATE_MAXIMUM);
|
||||||
|
int64_t cfg_rc_bufsize = obs_data_get_int(settings, S_RATECONTROL_BUFFERSIZE);
|
||||||
|
bool cfg_rc_quality = obs_data_get_bool(settings, ST_RATECONTROL_QUALITY);
|
||||||
|
int64_t cfg_rc_quality_min = obs_data_get_int(settings, ST_RATECONTROL_QUALITY_MINIMUM);
|
||||||
|
int64_t cfg_rc_quality_max = obs_data_get_int(settings, ST_RATECONTROL_QUALITY_MAXIMUM);
|
||||||
|
double_t cfg_rc_quality_tgt = obs_data_get_int(settings, ST_RATECONTROL_QUALITY_TARGET);
|
||||||
|
int64_t cfg_rc_qp_i = obs_data_get_int(settings, ST_RATECONTROL_QP_I);
|
||||||
|
int64_t cfg_rc_qp_p = obs_data_get_int(settings, ST_RATECONTROL_QP_P);
|
||||||
|
int64_t cfg_rc_qp_b = obs_data_get_int(settings, ST_RATECONTROL_QP_B);
|
||||||
|
int64_t cfg_rc_qp_i_init = obs_data_get_int(settings, ST_RATECONTROL_QP_I_INITIAL);
|
||||||
|
int64_t cfg_rc_qp_p_init = obs_data_get_int(settings, ST_RATECONTROL_QP_P_INITIAL);
|
||||||
|
int64_t cfg_rc_qp_b_init = obs_data_get_int(settings, ST_RATECONTROL_QP_B_INITIAL);
|
||||||
|
|
||||||
|
{
|
||||||
|
bool have_bitrate = false;
|
||||||
|
bool have_bitrate_max = false;
|
||||||
|
bool have_quality = false;
|
||||||
|
bool have_qp = false;
|
||||||
|
bool have_qp_init = false;
|
||||||
|
|
||||||
|
switch (cfg_rc_mode) {
|
||||||
|
case ratecontrolmode::CQP:
|
||||||
|
have_qp = true;
|
||||||
|
break;
|
||||||
|
case ratecontrolmode::CBR:
|
||||||
|
case ratecontrolmode::CBR_HQ:
|
||||||
|
case ratecontrolmode::CBR_LD_HQ:
|
||||||
|
have_bitrate = true;
|
||||||
|
av_opt_set_int(context->priv_data, "cbr", 1, 0);
|
||||||
|
break;
|
||||||
|
case ratecontrolmode::VBR:
|
||||||
|
case ratecontrolmode::VBR_HQ:
|
||||||
|
have_bitrate_max = true;
|
||||||
|
have_bitrate = true;
|
||||||
|
have_quality = true;
|
||||||
|
have_qp_init = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
PLOG_INFO("[%s] Buffer Size: %" PRId64, codec->name, cfg_rc_bufsize);
|
||||||
|
if (have_bitrate)
|
||||||
|
PLOG_INFO("[%s] Bitrate Target: %" PRId64, codec->name, cfg_rc_bitrate);
|
||||||
|
if (have_bitrate_max)
|
||||||
|
PLOG_INFO("[%s] Bitrate Maximum: %" PRId64, codec->name, cfg_rc_max_bitrate);
|
||||||
|
if (have_quality && cfg_rc_quality) {
|
||||||
|
PLOG_INFO("[%s] Quality Limits:", codec->name);
|
||||||
|
PLOG_INFO("[%s] Minimum: %" PRId64, codec->name, cfg_rc_quality_min);
|
||||||
|
PLOG_INFO("[%s] Maximum: %" PRId64, codec->name, cfg_rc_quality_max);
|
||||||
|
PLOG_INFO("[%s] Target: %f", codec->name, cfg_rc_quality_tgt);
|
||||||
|
}
|
||||||
|
if (have_qp)
|
||||||
|
PLOG_INFO("[%s] QP Values: %" PRId64 "/%" PRId64 "/%" PRId64, codec->name, cfg_rc_qp_i,
|
||||||
|
cfg_rc_qp_p, cfg_rc_qp_b);
|
||||||
|
if (have_qp_init)
|
||||||
|
PLOG_INFO("[%s] Initial QP Values: %" PRId64 "/%" PRId64 "/%" PRId64, codec->name,
|
||||||
|
cfg_rc_qp_i_init, cfg_rc_qp_p_init, cfg_rc_qp_b_init);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cfg_aq_spatial = obs_data_get_bool(settings, ST_AQ_SPATIAL);
|
||||||
|
int64_t cfg_aq_strength = obs_data_get_int(settings, ST_AQ_STRENGTH);
|
||||||
|
bool cfg_aq_temporal = obs_data_get_bool(settings, ST_AQ_TEMPORAL);
|
||||||
|
if (cfg_aq_spatial)
|
||||||
|
PLOG_INFO("[%s] Spatial AQ Enabled: Strength %" PRId64, codec->name, cfg_aq_strength);
|
||||||
|
if (cfg_aq_temporal)
|
||||||
|
PLOG_INFO("[%s] Temporal AQ Enabled", codec->name);
|
||||||
|
|
||||||
|
int64_t cfg_bf = obs_data_get_int(settings, ST_OTHER_BFRAMES);
|
||||||
|
b_ref_mode cfg_bf_mode = static_cast<b_ref_mode>(obs_data_get_int(settings, ST_OTHER_BFRAME_REFERENCEMODE));
|
||||||
|
bool cfg_zerolatency = obs_data_get_bool(settings, ST_OTHER_ZEROLATENCY);
|
||||||
|
bool cfg_weightp = obs_data_get_bool(settings, ST_OTHER_WEIGHTED_PREDICTION);
|
||||||
|
bool cfg_nonrefp = obs_data_get_bool(settings, ST_OTHER_NONREFERENCE_PFRAMES);
|
||||||
|
|
||||||
|
PLOG_INFO("[%s] B-Frames: %" PRId64, codec->name, cfg_bf);
|
||||||
|
auto found3 = b_ref_mode_to_opt.find(cfg_bf_mode);
|
||||||
|
if (found3 != b_ref_mode_to_opt.end())
|
||||||
|
PLOG_INFO("[%s] Reference Mode: %s", codec->name, found3->second.c_str());
|
||||||
|
if (cfg_zerolatency)
|
||||||
|
PLOG_INFO("[%s] Zero Latency Enabled", codec->name);
|
||||||
|
if (cfg_weightp)
|
||||||
|
PLOG_INFO("[%s] Weighted Prediction Enabled", codec->name);
|
||||||
|
if (cfg_nonrefp)
|
||||||
|
PLOG_INFO("[%s] Non-Ref P-Frames Enabled", codec->name);
|
||||||
|
}
|
||||||
|
|||||||
@@ -84,5 +84,7 @@ namespace obsffmpeg {
|
|||||||
void get_runtime_properties(obs_properties_t* props, const AVCodec* codec, AVCodecContext* context);
|
void get_runtime_properties(obs_properties_t* props, const AVCodec* codec, AVCodecContext* context);
|
||||||
|
|
||||||
void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||||
|
|
||||||
|
void log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||||
} // namespace nvenc
|
} // namespace nvenc
|
||||||
} // namespace obsffmpeg
|
} // namespace obsffmpeg
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
|
|
||||||
#include "prores_aw_handler.hpp"
|
#include "prores_aw_handler.hpp"
|
||||||
|
#include "codecs/prores.hpp"
|
||||||
#include "plugin.hpp"
|
#include "plugin.hpp"
|
||||||
#include "utility.hpp"
|
#include "utility.hpp"
|
||||||
|
|
||||||
@@ -27,13 +28,6 @@ extern "C" {
|
|||||||
#include <obs-module.h>
|
#include <obs-module.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
#define P_PROFILE "AppleProRes.Profile"
|
|
||||||
#define P_PROFILE_APCO "AppleProRes.Profile.APCO"
|
|
||||||
#define P_PROFILE_APCS "AppleProRes.Profile.APCS"
|
|
||||||
#define P_PROFILE_APCN "AppleProRes.Profile.APCN"
|
|
||||||
#define P_PROFILE_APCH "AppleProRes.Profile.APCH"
|
|
||||||
#define P_PROFILE_AP4H "AppleProRes.Profile.AP4H"
|
|
||||||
|
|
||||||
INITIALIZER(prores_aw_handler_init)
|
INITIALIZER(prores_aw_handler_init)
|
||||||
{
|
{
|
||||||
obsffmpeg::initializers.push_back([]() {
|
obsffmpeg::initializers.push_back([]() {
|
||||||
@@ -41,44 +35,94 @@ INITIALIZER(prores_aw_handler_init)
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void obsffmpeg::ui::prores_aw_handler::override_colorformat(AVPixelFormat& target_format, obs_data_t* settings,
|
||||||
|
const AVCodec* codec, AVCodecContext* context)
|
||||||
|
{
|
||||||
|
std::string profile = "";
|
||||||
|
|
||||||
|
int profile_id = static_cast<int>(obs_data_get_int(settings, P_PRORES_PROFILE));
|
||||||
|
for (auto ptr = codec->profiles; ptr->profile != FF_PROFILE_UNKNOWN; ptr++) {
|
||||||
|
if (ptr->profile == profile_id) {
|
||||||
|
profile = ptr->name;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_map<AVPixelFormat, std::list<std::string>> valid_formats = {
|
||||||
|
{AV_PIX_FMT_YUV422P10, {"apco", "apcs", "apcn", "apch"}}, {AV_PIX_FMT_YUV444P10, {"ap4h", "ap4x"}}};
|
||||||
|
|
||||||
|
for (auto kv : valid_formats) {
|
||||||
|
for (auto name : kv.second) {
|
||||||
|
if (profile == name) {
|
||||||
|
target_format = kv.first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void obsffmpeg::ui::prores_aw_handler::get_defaults(obs_data_t* settings, const AVCodec*, AVCodecContext*)
|
void obsffmpeg::ui::prores_aw_handler::get_defaults(obs_data_t* settings, const AVCodec*, AVCodecContext*)
|
||||||
{
|
{
|
||||||
obs_data_set_default_int(settings, P_PROFILE, 0);
|
obs_data_set_default_int(settings, P_PRORES_PROFILE, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void obsffmpeg::ui::prores_aw_handler::get_properties(obs_properties_t* props, const AVCodec* codec,
|
void obsffmpeg::ui::prores_aw_handler::get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||||
AVCodecContext* context)
|
AVCodecContext* context)
|
||||||
{
|
{
|
||||||
if (!context) {
|
if (!context) {
|
||||||
auto p = obs_properties_add_list(props, P_PROFILE, TRANSLATE(P_PROFILE), OBS_COMBO_TYPE_LIST,
|
auto p = obs_properties_add_list(props, P_PRORES_PROFILE, TRANSLATE(P_PRORES_PROFILE),
|
||||||
OBS_COMBO_FORMAT_INT);
|
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_PROFILE)));
|
obs_property_set_long_description(p, TRANSLATE(DESC(P_PRORES_PROFILE)));
|
||||||
for (auto ptr = codec->profiles; ptr->profile != FF_PROFILE_UNKNOWN; ptr++) {
|
for (auto ptr = codec->profiles; ptr->profile != FF_PROFILE_UNKNOWN; ptr++) {
|
||||||
if (strcmp("apco", ptr->name) == 0) {
|
if (strcmp("apco", ptr->name) == 0) {
|
||||||
obs_property_list_add_int(p, TRANSLATE(P_PROFILE_APCO),
|
obs_property_list_add_int(p, TRANSLATE(P_PRORES_PROFILE_APCO),
|
||||||
static_cast<int64_t>(ptr->profile));
|
static_cast<int64_t>(ptr->profile));
|
||||||
} else if (strcmp("apcs", ptr->name) == 0) {
|
} else if (strcmp("apcs", ptr->name) == 0) {
|
||||||
obs_property_list_add_int(p, TRANSLATE(P_PROFILE_APCS),
|
obs_property_list_add_int(p, TRANSLATE(P_PRORES_PROFILE_APCS),
|
||||||
static_cast<int64_t>(ptr->profile));
|
static_cast<int64_t>(ptr->profile));
|
||||||
} else if (strcmp("apcn", ptr->name) == 0) {
|
} else if (strcmp("apcn", ptr->name) == 0) {
|
||||||
obs_property_list_add_int(p, TRANSLATE(P_PROFILE_APCN),
|
obs_property_list_add_int(p, TRANSLATE(P_PRORES_PROFILE_APCN),
|
||||||
static_cast<int64_t>(ptr->profile));
|
static_cast<int64_t>(ptr->profile));
|
||||||
} else if (strcmp("apch", ptr->name) == 0) {
|
} else if (strcmp("apch", ptr->name) == 0) {
|
||||||
obs_property_list_add_int(p, TRANSLATE(P_PROFILE_APCH),
|
obs_property_list_add_int(p, TRANSLATE(P_PRORES_PROFILE_APCH),
|
||||||
static_cast<int64_t>(ptr->profile));
|
static_cast<int64_t>(ptr->profile));
|
||||||
} else if (strcmp("ap4h", ptr->name) == 0) {
|
} else if (strcmp("ap4h", ptr->name) == 0) {
|
||||||
obs_property_list_add_int(p, TRANSLATE(P_PROFILE_AP4H),
|
obs_property_list_add_int(p, TRANSLATE(P_PRORES_PROFILE_AP4H),
|
||||||
|
static_cast<int64_t>(ptr->profile));
|
||||||
|
} else if (strcmp("ap4x", ptr->name) == 0) {
|
||||||
|
obs_property_list_add_int(p, TRANSLATE(P_PRORES_PROFILE_AP4X),
|
||||||
static_cast<int64_t>(ptr->profile));
|
static_cast<int64_t>(ptr->profile));
|
||||||
} else {
|
} else {
|
||||||
obs_property_list_add_int(p, ptr->name, ptr->profile);
|
obs_property_list_add_int(p, ptr->name, ptr->profile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
obs_property_set_enabled(obs_properties_get(props, P_PROFILE), false);
|
obs_property_set_enabled(obs_properties_get(props, P_PRORES_PROFILE), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void obsffmpeg::ui::prores_aw_handler::update(obs_data_t* settings, const AVCodec*, AVCodecContext* context)
|
void obsffmpeg::ui::prores_aw_handler::update(obs_data_t* settings, const AVCodec*, AVCodecContext* context)
|
||||||
{
|
{
|
||||||
context->profile = static_cast<int>(obs_data_get_int(settings, P_PROFILE));
|
context->profile = static_cast<int>(obs_data_get_int(settings, P_PRORES_PROFILE));
|
||||||
|
}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::prores_aw_handler::log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context)
|
||||||
|
{
|
||||||
|
for (auto ptr = codec->profiles; ptr->profile != FF_PROFILE_UNKNOWN; ptr++) {
|
||||||
|
if (ptr->profile == static_cast<int>(obs_data_get_int(settings, P_PRORES_PROFILE)))
|
||||||
|
PLOG_INFO("[%s] Profile: %s", codec->name, ptr->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void obsffmpeg::ui::prores_aw_handler::process_avpacket(AVPacket& packet, const AVCodec* codec, AVCodecContext* context)
|
||||||
|
{
|
||||||
|
//FFmpeg Bug:
|
||||||
|
// When ProRes content is stored in Matroska, FFmpeg strips the size
|
||||||
|
// from the atom. Later when the ProRes content is demuxed from Matroska,
|
||||||
|
// FFmpeg creates an atom with the incorrect size, as the ATOM size
|
||||||
|
// should be content + atom, but FFmpeg set it to only be content. This
|
||||||
|
// difference leads to decoders to be off by 8 bytes.
|
||||||
|
//Fix (until FFmpeg stops being broken):
|
||||||
|
// Pad the packet with 8 bytes of 0x00.
|
||||||
|
|
||||||
|
av_grow_packet(&packet, 8);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ namespace obsffmpeg {
|
|||||||
namespace ui {
|
namespace ui {
|
||||||
class prores_aw_handler : public handler {
|
class prores_aw_handler : public handler {
|
||||||
public:
|
public:
|
||||||
|
virtual void override_colorformat(AVPixelFormat& target_format, obs_data_t* settings,
|
||||||
|
const AVCodec* codec, AVCodecContext* context) override;
|
||||||
|
|
||||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec,
|
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec,
|
||||||
AVCodecContext* context) override;
|
AVCodecContext* context) override;
|
||||||
|
|
||||||
@@ -42,6 +45,11 @@ namespace obsffmpeg {
|
|||||||
|
|
||||||
virtual void update(obs_data_t* settings, const AVCodec* codec,
|
virtual void update(obs_data_t* settings, const AVCodec* codec,
|
||||||
AVCodecContext* context) override;
|
AVCodecContext* context) override;
|
||||||
|
|
||||||
|
virtual void log_options(obs_data_t* settings, const AVCodec* codec,
|
||||||
|
AVCodecContext* context) override;
|
||||||
|
|
||||||
|
virtual void process_avpacket(AVPacket& packet, const AVCodec* codec, AVCodecContext* context) override;
|
||||||
};
|
};
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
} // namespace obsffmpeg
|
} // namespace obsffmpeg
|
||||||
|
|||||||
Reference in New Issue
Block a user