4 Commits

Author SHA1 Message Date
Michael Fabian 'Xaymar' Dirks 36222cc186 ci: Fix incorrect package name in builder script 2019-09-04 00:43:29 +02:00
Michael Fabian 'Xaymar' Dirks 9fcec9b9a9 project: Update libobs to v24.0.0-rc2 2019-09-04 00:43:29 +02:00
Michael Fabian 'Xaymar' Dirks 11f72c9bc7 ci: Integrate Github Actions
Integrates Github Actions which is much much faster than AppVeyor in all areas, and even supports multiple workflows instead of forcing everything into just one workflow like AppVeyor does. Plus we get 20 parallel builds that nearly instantly finish, which results in much faster feedback without having to run our own Jenkins CI.

The builder and packager scripts have been adjusted to add support for both Windows and Linux, and both AppVeyor and Github Actions. Additionally to that, the builder script now correctly executes x32 and x64 steps in a chain, instead of waiting for the other architecture to finish first. This further reduces build times.
2019-09-04 00:30:06 +02:00
Michael Fabian 'Xaymar' Dirks 5c8939b4a8 encoder: Delay init until resolution, framerate and color are finalized
Fixes a crash due to reading/writing out of bounds when the resolution, color and framerate end up different than initially expected during the creation of the encoder. While this does change the error message that appears when the encoder can't be initialized, it is better than outright crashing OBS Studio.
2019-08-11 21:46:21 +02:00
8 changed files with 380 additions and 442 deletions
+34
View File
@@ -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
+1 -1
View File
@@ -155,7 +155,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 "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()
if(NOT ${PropertyPrefix}OBS_NATIVE)
+6
View File
@@ -7,11 +7,16 @@ matrix:
# Build Image & Environment
platform: x64
# Build Tags only
skip_non_tags: true
image:
- Visual Studio 2017
environment:
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
INNOSETUP_URL: http://www.jrsoftware.org/download.php/is.exe
CURL_VERSION: 7.39.0
@@ -32,6 +37,7 @@ build_script:
- cmd: node ci/builder.js
after_build:
- cmd: node ci/packager.js
- cmd: ci/appveyor-package.bat
# Testing
+72 -86
View File
@@ -2,108 +2,94 @@
const process = require('process');
const runner = require('./runner.js');
let env = process.env;
// Steps
let configure_runners = [];
let build_runners = [];
let package_runners = [];
let x32_steps = [];
let x64_steps = [];
{
let cmake_configure_extra = [
if ((process.platform == "win32") || (process.platform == "win64")) {
// Windows
let extra_conf = [
`-DCMAKE_SYSTEM_VERSION=${process.env.CMAKE_SYSTEM_VERSION}`,
`-DCMAKE_PACKAGE_NAME=obs-ffmpeg-encoder`,
'-DCMAKE_INSTALL_PREFIX="build/distrib/"',
'-DCMAKE_PACKAGE_PREFIX="build/"',
`-DCMAKE_PACKAGE_NAME="${process.env.PACKAGE_PREFIX}"`,
];
let cmake_build_extra = [
let extra_build = [
];
// Configuration depends on platform
if (process.platform == "win32" || process.platform == "win64") {
configure_runners.push(new runner('32-bit', 'cmake', [
if(process.env.APPVEYOR) {
extra_build.concat(['--', '/logger:"C:\\Program Files\\AppVeyor\\BuildAgent\\Appveyor.MSBuildLogger.dll"']);
}
if ((process.env.CMAKE_GENERATOR_32 !== undefined) && (process.env.CMAKE_GENERATOR_32 !== "")) {
x32_steps.push(
[ 'cmake', [
'-H.',
'-Bbuild/32',
`-G"Visual Studio 15 2017"`,
].concat(cmake_configure_extra)));
configure_runners.push(new runner('64-bit', 'cmake', [
`-G"${process.env.CMAKE_GENERATOR_32}"`,
].concat(extra_conf), env ]
);
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.',
'-Bbuild/64',
`-G"Visual Studio 15 2017 Win64"`,
'-T"host=x64"',
].concat(cmake_configure_extra)));
// Extra build steps for AppVeyor on Windows for Logging purposes.
if(process.env.APPVEYOR) {
cmake_build_extra.concat(['--', '/logger:"C:\\Program Files\\AppVeyor\\BuildAgent\\Appveyor.MSBuildLogger.dll"']);
}
} else if (process.platform == "linux") {
configure_runners.push(new runner('32-bit', 'cmake', [
'-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}`,
}}));
}
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', [
`-G"${process.env.CMAKE_GENERATOR_64}"`,
'-T"host=x64"'
].concat(extra_conf), env ]
);
x64_steps.push(
[ '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)));
].concat(extra_build), env ]
);
}
} else {
// Unix
}
// Run Configure steps.
let configure_promises = [];
for (let config of configure_runners) {
configure_promises.push(config.run());
function runRunners(runnerArray, name) {
return new Promise(async (resolve, reject) => {
let local = runnerArray.reverse();
while (local.length > 0) {
try {
let task = local.pop();
let work = new runner(name, task[0], task[1], task[2]);
await work.run();
} catch (e) {
reject(e);
return;
}
}
resolve(0);
});
}
Promise.all(configure_promises).then(function(result) {
let build_promises = [];
for (let build of build_runners) {
build_promises.push(build.run());
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);
}
Promise.all(build_promises).then(function(result) {
let package_promises = [];
for (let pack of package_runners) {
package_promises.push(pack.run());
}
Promise.all(package_promises).then(function(result) {
process.exit(result);
}).catch(function(result) {
process.exit(result);
});
}).catch(function(result) {
process.exit(result);
});
}).catch(function(result) {
process.exit(result);
});
).catch(err => {
console.log(err);
process.exit(1);
})
+63
View File
@@ -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);
}
)
-1
View File
@@ -18,7 +18,6 @@ class Runner {
run() {
let self = this;
return new Promise(function(resolve, reject) {
console.log(self.cmd, self.args);
self.proc = cp.spawn(
self.cmd, self.args, {
windowsVerbatimArguments: true,
+194 -350
View File
@@ -21,15 +21,15 @@
#include "encoder.hpp"
#include <iomanip>
#include <map>
#include <set>
#include <sstream>
#include <thread>
#include <util/profiler.hpp>
#include "codecs/hevc.hpp"
#include "ffmpeg/tools.hpp"
#include "plugin.hpp"
#include "strings.hpp"
#include "utility.hpp"
#include "codecs/hevc.hpp"
extern "C" {
#include <obs-avc.h>
@@ -56,7 +56,7 @@ enum class keyframe_type { SECONDS, FRAMES };
obsffmpeg::encoder_factory::encoder_factory(const AVCodec* codec) : avcodec_ptr(codec), info()
{
// Unique Id is FFmpeg name.
this->info.uid = avcodec_ptr->name;
info.uid = avcodec_ptr->name;
// Also generate a human readable name while we're at it.
{
@@ -68,32 +68,32 @@ obsffmpeg::encoder_factory::encoder_factory(const AVCodec* codec) : avcodec_ptr(
if (avcodec_ptr->long_name) {
sstr << " (" << avcodec_ptr->name << ")";
}
this->info.readable_name = sstr.str();
info.readable_name = sstr.str();
// Allow UI Handler to replace visible name.
obsffmpeg::find_codec_handler(this->avcodec_ptr->name)
->override_visible_name(this->avcodec_ptr, this->info.readable_name);
obsffmpeg::find_codec_handler(avcodec_ptr->name)
->override_visible_name(avcodec_ptr, info.readable_name);
}
// Assign Ids.
{
const AVCodecDescriptor* desc = avcodec_descriptor_get(this->avcodec_ptr->id);
const AVCodecDescriptor* desc = avcodec_descriptor_get(avcodec_ptr->id);
if (desc) {
this->info.codec = desc->name;
info.codec = desc->name;
} else {
// Fall back to encoder name in the case that FFmpeg itself doesn't know
// what codec this actually is.
this->info.codec = avcodec_ptr->name;
info.codec = avcodec_ptr->name;
}
}
this->info.oei.id = this->info.uid.c_str();
this->info.oei.codec = this->info.codec.c_str();
info.oei.id = info.uid.c_str();
info.oei.codec = info.codec.c_str();
#ifndef _DEBUG
// Is this a deprecated encoder?
if (!obsffmpeg::has_codec_handler(avcodec_ptr->name)) {
this->info.oei.caps |= OBS_ENCODER_CAP_DEPRECATED;
info.oei.caps |= OBS_ENCODER_CAP_DEPRECATED;
}
#endif
}
@@ -104,15 +104,15 @@ void obsffmpeg::encoder_factory::register_encoder()
{
// 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;
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;
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) {
info.oei.create = [](obs_data_t* settings, obs_encoder_t* encoder) {
try {
return reinterpret_cast<void*>(new obsffmpeg::encoder(settings, encoder));
} catch (std::exception const& e) {
@@ -123,7 +123,7 @@ void obsffmpeg::encoder_factory::register_encoder()
return reinterpret_cast<void*>(0);
}
};
this->info.oei.destroy = [](void* ptr) {
info.oei.destroy = [](void* ptr) {
try {
delete reinterpret_cast<encoder*>(ptr);
} catch (std::exception const& e) {
@@ -134,7 +134,7 @@ void obsffmpeg::encoder_factory::register_encoder()
throw;
}
};
this->info.oei.get_name = [](void* type_data) {
info.oei.get_name = [](void* type_data) {
try {
return reinterpret_cast<encoder_factory*>(type_data)->get_name();
} catch (std::exception const& e) {
@@ -145,7 +145,7 @@ void obsffmpeg::encoder_factory::register_encoder()
throw;
}
};
this->info.oei.get_defaults2 = [](obs_data_t* settings, void* type_data) {
info.oei.get_defaults2 = [](obs_data_t* settings, void* type_data) {
try {
reinterpret_cast<encoder_factory*>(type_data)->get_defaults(settings);
} catch (std::exception const& e) {
@@ -156,7 +156,7 @@ void obsffmpeg::encoder_factory::register_encoder()
throw;
}
};
this->info.oei.get_properties2 = [](void* ptr, void* type_data) {
info.oei.get_properties2 = [](void* ptr, void* type_data) {
try {
obs_properties_t* props = obs_properties_create();
if (type_data != nullptr) {
@@ -174,7 +174,7 @@ void obsffmpeg::encoder_factory::register_encoder()
throw;
}
};
this->info.oei.update = [](void* ptr, obs_data_t* settings) {
info.oei.update = [](void* ptr, obs_data_t* settings) {
try {
return reinterpret_cast<encoder*>(ptr)->update(settings);
} catch (std::exception const& e) {
@@ -185,7 +185,7 @@ void obsffmpeg::encoder_factory::register_encoder()
throw;
}
};
this->info.oei.get_sei_data = [](void* ptr, uint8_t** sei_data, size_t* size) {
info.oei.get_sei_data = [](void* ptr, uint8_t** sei_data, size_t* size) {
try {
return reinterpret_cast<encoder*>(ptr)->get_sei_data(sei_data, size);
} catch (std::exception const& e) {
@@ -196,7 +196,7 @@ void obsffmpeg::encoder_factory::register_encoder()
throw;
}
};
this->info.oei.get_extra_data = [](void* ptr, uint8_t** extra_data, size_t* size) {
info.oei.get_extra_data = [](void* ptr, uint8_t** extra_data, size_t* size) {
try {
return reinterpret_cast<encoder*>(ptr)->get_extra_data(extra_data, size);
} catch (std::exception const& e) {
@@ -208,8 +208,8 @@ void obsffmpeg::encoder_factory::register_encoder()
}
};
if (this->avcodec_ptr->type == AVMediaType::AVMEDIA_TYPE_VIDEO) {
this->info.oei.get_video_info = [](void* ptr, struct video_scale_info* info) {
if (avcodec_ptr->type == AVMediaType::AVMEDIA_TYPE_VIDEO) {
info.oei.get_video_info = [](void* ptr, struct video_scale_info* info) {
try {
reinterpret_cast<encoder*>(ptr)->get_video_info(info);
} catch (std::exception const& e) {
@@ -220,7 +220,7 @@ void obsffmpeg::encoder_factory::register_encoder()
throw;
}
};
this->info.oei.encode = [](void* ptr, struct encoder_frame* frame, struct encoder_packet* packet,
info.oei.encode = [](void* ptr, struct encoder_frame* frame, struct encoder_packet* packet,
bool* received_packet) {
try {
return reinterpret_cast<encoder*>(ptr)->video_encode(frame, packet, received_packet);
@@ -232,9 +232,8 @@ void obsffmpeg::encoder_factory::register_encoder()
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) {
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<encoder*>(ptr)->video_encode_texture(
handle, pts, lock_key, next_key, packet, received_packet);
@@ -247,8 +246,8 @@ void obsffmpeg::encoder_factory::register_encoder()
}
};
} else if (this->avcodec_ptr->type == AVMediaType::AVMEDIA_TYPE_AUDIO) {
this->info.oei.get_audio_info = [](void* ptr, struct audio_convert_info* info) {
} else if (avcodec_ptr->type == AVMediaType::AVMEDIA_TYPE_AUDIO) {
info.oei.get_audio_info = [](void* ptr, struct audio_convert_info* info) {
try {
reinterpret_cast<encoder*>(ptr)->get_audio_info(info);
} catch (std::exception const& e) {
@@ -259,7 +258,7 @@ void obsffmpeg::encoder_factory::register_encoder()
throw;
}
};
this->info.oei.get_frame_size = [](void* ptr) {
info.oei.get_frame_size = [](void* ptr) {
try {
return reinterpret_cast<encoder*>(ptr)->get_frame_size();
} catch (std::exception const& e) {
@@ -270,7 +269,7 @@ void obsffmpeg::encoder_factory::register_encoder()
throw;
}
};
this->info.oei.encode = [](void* ptr, struct encoder_frame* frame, struct encoder_packet* packet,
info.oei.encode = [](void* ptr, struct encoder_frame* frame, struct encoder_packet* packet,
bool* received_packet) {
try {
return reinterpret_cast<encoder*>(ptr)->audio_encode(frame, packet, received_packet);
@@ -285,28 +284,28 @@ void obsffmpeg::encoder_factory::register_encoder()
}
// Finally store ourself as type data.
this->info.oei.type_data = this;
info.oei.type_data = this;
obs_register_encoder(&this->info.oei);
obs_register_encoder(&info.oei);
PLOG_DEBUG("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* obsffmpeg::encoder_factory::get_name()
{
return this->info.readable_name.c_str();
return info.readable_name.c_str();
}
void obsffmpeg::encoder_factory::get_defaults(obs_data_t* settings)
{
{ // Handler
auto ptr = obsffmpeg::find_codec_handler(this->avcodec_ptr->name);
auto ptr = obsffmpeg::find_codec_handler(avcodec_ptr->name);
if (ptr) {
ptr->get_defaults(settings, this->avcodec_ptr, nullptr);
ptr->get_defaults(settings, avcodec_ptr, nullptr);
}
}
if ((this->avcodec_ptr->capabilities & AV_CODEC_CAP_INTRA_ONLY) == 0) {
if ((avcodec_ptr->capabilities & AV_CODEC_CAP_INTRA_ONLY) == 0) {
obs_data_set_default_int(settings, S_KEYFRAMES_INTERVALTYPE, 0);
obs_data_set_default_double(settings, S_KEYFRAMES_INTERVAL_SECONDS, 2.0);
obs_data_set_default_int(settings, S_KEYFRAMES_INTERVAL_FRAMES, 300);
@@ -332,13 +331,13 @@ static bool modified_keyframes(obs_properties_t* props, obs_property_t*, obs_dat
void obsffmpeg::encoder_factory::get_properties(obs_properties_t* props)
{
{ // Handler
auto ptr = obsffmpeg::find_codec_handler(this->avcodec_ptr->name);
auto ptr = obsffmpeg::find_codec_handler(avcodec_ptr->name);
if (ptr) {
ptr->get_properties(props, this->avcodec_ptr, nullptr);
ptr->get_properties(props, avcodec_ptr, nullptr);
}
}
if ((this->avcodec_ptr->capabilities & AV_CODEC_CAP_INTRA_ONLY) == 0) {
if ((avcodec_ptr->capabilities & AV_CODEC_CAP_INTRA_ONLY) == 0) {
// Key-Frame Options
obs_properties_t* grp = props;
if (!obsffmpeg::are_property_groups_broken()) {
@@ -385,18 +384,18 @@ void obsffmpeg::encoder_factory::get_properties(obs_properties_t* props)
obs_text_type::OBS_TEXT_DEFAULT);
obs_property_set_long_description(p, TRANSLATE(DESC(ST_FFMPEG_CUSTOMSETTINGS)));
}
if (this->avcodec_ptr->pix_fmts) {
if (avcodec_ptr->pix_fmts) {
auto p = obs_properties_add_list(grp, ST_FFMPEG_COLORFORMAT, TRANSLATE(ST_FFMPEG_COLORFORMAT),
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
obs_property_set_long_description(p, TRANSLATE(DESC(ST_FFMPEG_COLORFORMAT)));
obs_property_list_add_int(p, TRANSLATE(S_STATE_AUTOMATIC),
static_cast<int64_t>(AV_PIX_FMT_NONE));
for (auto ptr = this->avcodec_ptr->pix_fmts; *ptr != AV_PIX_FMT_NONE; ptr++) {
for (auto ptr = avcodec_ptr->pix_fmts; *ptr != AV_PIX_FMT_NONE; ptr++) {
obs_property_list_add_int(p, ffmpeg::tools::get_pixel_format_name(*ptr),
static_cast<int64_t>(*ptr));
}
}
if (this->avcodec_ptr->capabilities & (AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_SLICE_THREADS)) {
if (avcodec_ptr->capabilities & (AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_SLICE_THREADS)) {
auto p = obs_properties_add_int_slider(grp, ST_FFMPEG_THREADS, TRANSLATE(ST_FFMPEG_THREADS), 0,
std::thread::hardware_concurrency() * 2, 1);
obs_property_set_long_description(p, TRANSLATE(DESC(ST_FFMPEG_THREADS)));
@@ -422,217 +421,167 @@ void obsffmpeg::encoder_factory::get_properties(obs_properties_t* props)
const AVCodec* obsffmpeg::encoder_factory::get_avcodec()
{
return this->avcodec_ptr;
return avcodec_ptr;
}
obsffmpeg::encoder::encoder(obs_data_t* settings, obs_encoder_t* encoder)
: _self(encoder), _lag_in_frames(0), _count_send_frames(0), _have_first_frame(false)
: _self(encoder), _lag_in_frames(0), _count_send_frames(0), _have_first_frame(false), _initialized(false)
{
this->_factory = reinterpret_cast<encoder_factory*>(obs_encoder_get_type_data(_self));
_factory = reinterpret_cast<encoder_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);
_codec = avcodec_find_encoder_by_name(_factory->get_avcodec()->name);
if (!_codec) {
PLOG_ERROR("Failed to find encoder for codec '%s'.", _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);
_context = avcodec_alloc_context3(_codec);
if (!_context) {
PLOG_ERROR("Failed to create context for encoder '%s'.", _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, ST_FFMPEG_STANDARDCOMPLIANCE));
this->_context->debug = 0;
_context->strict_std_compliance = static_cast<int>(obs_data_get_int(settings, ST_FFMPEG_STANDARDCOMPLIANCE));
_context->debug = 0;
/// Threading
if (this->_codec->capabilities
if (_codec->capabilities
& (AV_CODEC_CAP_AUTO_THREADS | AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_SLICE_THREADS)) {
if (this->_codec->capabilities & AV_CODEC_CAP_FRAME_THREADS) {
this->_context->thread_type |= FF_THREAD_FRAME;
if (_codec->capabilities & AV_CODEC_CAP_FRAME_THREADS) {
_context->thread_type |= FF_THREAD_FRAME;
}
if (this->_codec->capabilities & AV_CODEC_CAP_SLICE_THREADS) {
this->_context->thread_type |= FF_THREAD_SLICE;
if (_codec->capabilities & AV_CODEC_CAP_SLICE_THREADS) {
_context->thread_type |= FF_THREAD_SLICE;
}
int64_t threads = obs_data_get_int(settings, ST_FFMPEG_THREADS);
if (threads > 0) {
this->_context->thread_count = static_cast<int>(threads);
this->_lag_in_frames = this->_context->thread_count;
_context->thread_count = static_cast<int>(threads);
_lag_in_frames = _context->thread_count;
} else {
this->_context->thread_count = std::thread::hardware_concurrency();
this->_lag_in_frames = this->_context->thread_count;
_context->thread_count = std::thread::hardware_concurrency();
_lag_in_frames = _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;
// Prefer lossless or zero-change formats.
for (auto ptr = this->_codec->pix_fmts; *ptr != AV_PIX_FMT_NONE; ptr++) {
switch (source) {
case AV_PIX_FMT_RGBA:
switch (*ptr) {
case AV_PIX_FMT_RGB0:
case AV_PIX_FMT_RGBA:
target = *ptr;
break;
case AV_PIX_FMT_BGR0:
case AV_PIX_FMT_BGRA:
target = *ptr;
break;
}
break;
case AV_PIX_FMT_BGRA:
case AV_PIX_FMT_BGR0:
switch (*ptr) {
case AV_PIX_FMT_RGB0:
case AV_PIX_FMT_RGBA:
target = *ptr;
break;
case AV_PIX_FMT_BGR0:
case AV_PIX_FMT_BGRA:
target = *ptr;
break;
}
break;
case AV_PIX_FMT_YUV444P:
case AV_PIX_FMT_YUYV422:
case AV_PIX_FMT_YVYU422:
case AV_PIX_FMT_UYVY422:
case AV_PIX_FMT_YUV420P:
case AV_PIX_FMT_NV12:
if (*ptr == source) {
target = *ptr;
}
break;
}
}
if (target == AV_PIX_FMT_NONE) {
int loss = 0;
target =
avcodec_find_best_pix_fmt_of_list(this->_codec->pix_fmts, source, false, &loss);
}
this->_context->pix_fmt = target;
this->_swscale.set_source_format(source);
this->_swscale.set_target_format(this->_context->pix_fmt);
PLOG_INFO("Automatically detected target format '%s' for source format '%s'.",
ffmpeg::tools::get_pixel_format_name(target),
ffmpeg::tools::get_pixel_format_name(source));
}
AVPixelFormat color_format_override =
static_cast<AVPixelFormat>(obs_data_get_int(settings, ST_FFMPEG_COLORFORMAT));
if (color_format_override != AV_PIX_FMT_NONE) {
// User specified override for color format.
this->_context->pix_fmt = color_format_override;
this->_swscale.set_target_format(this->_context->pix_fmt);
PLOG_INFO("User specified target format override '%s'.",
ffmpeg::tools::get_pixel_format_name(this->_context->pix_fmt));
}
// Framerate
this->_context->time_base.num = voi->fps_den;
this->_context->time_base.den = voi->fps_num;
this->_context->ticks_per_frame = 1;
} else if (this->_codec->type == AVMEDIA_TYPE_AUDIO) {
}
// Update settings
this->update(settings);
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(2);
} else if (this->_codec->type == AVMEDIA_TYPE_AUDIO) {
}
av_init_packet(&this->_current_packet);
av_new_packet(&this->_current_packet, 8 * 1024 * 1024); // 8 MB precached Packet size.
av_init_packet(&_current_packet);
av_new_packet(&_current_packet, 8 * 1024 * 1024); // 8 MB precached Packet size.
}
obsffmpeg::encoder::~encoder()
{
if (this->_context) {
if (_context) {
// Flush encoders that require it.
if ((this->_codec->capabilities & AV_CODEC_CAP_DELAY) != 0) {
avcodec_send_frame(this->_context, nullptr);
while (avcodec_receive_packet(this->_context, &this->_current_packet) >= 0) {
avcodec_send_frame(this->_context, nullptr);
if ((_codec->capabilities & AV_CODEC_CAP_DELAY) != 0) {
avcodec_send_frame(_context, nullptr);
while (avcodec_receive_packet(_context, &_current_packet) >= 0) {
avcodec_send_frame(_context, nullptr);
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
// Close and free context.
avcodec_close(this->_context);
avcodec_free_context(&this->_context);
avcodec_close(_context);
avcodec_free_context(&_context);
}
av_packet_unref(&this->_current_packet);
av_packet_unref(&_current_packet);
this->_frame_queue.clear();
this->_frame_queue_used.clear();
this->_swscale.finalize();
_frame_queue.clear();
_frame_queue_used.clear();
_swscale.finalize();
}
bool obsffmpeg::encoder::initialize()
{
if (_initialized)
return true;
// Detect supported
bool is_format_supported = false;
for (auto ptr = _codec->pix_fmts; *ptr != AV_PIX_FMT_NONE; ptr++) {
if (*ptr == _format) {
is_format_supported = true;
}
}
if (!is_format_supported) {
std::stringstream sstr;
sstr << "The color format " << ffmpeg::tools::get_pixel_format_name(_format)
<< " is not supported by the encoder.";
throw std::exception(sstr.str().c_str());
}
if (_codec->type == AVMEDIA_TYPE_VIDEO) {
auto encvideo = obs_encoder_video(_self);
auto voi = video_output_get_info(encvideo);
// Set Resolution
_context->width = static_cast<int>(_resolution.first);
_context->height = static_cast<int>(_resolution.second);
// Set Color Space, Format and Range
_context->colorspace = ffmpeg::tools::obs_videocolorspace_to_avcolorspace(voi->colorspace);
_context->color_range = ffmpeg::tools::obs_videorangetype_to_avcolorrange(voi->range);
_context->pix_fmt = _format;
// Set Framerate and Field Order
_context->field_order = AV_FIELD_PROGRESSIVE;
_context->time_base.num = voi->fps_den;
_context->time_base.den = voi->fps_num;
_context->ticks_per_frame = 1;
}
// Initialize
int res = avcodec_open2(_context, _codec, NULL);
if (res < 0) {
std::stringstream sstr;
sstr << "Failed to initalized encoder '" << _codec->name
<< "' due to error: " << ffmpeg::tools::get_error_description(res) << "(" << res
<< ")";
throw std::runtime_error(sstr.str().c_str());
}
// Initialize Scaler and Frame Queue
if (_codec->type == AVMEDIA_TYPE_VIDEO) {
_swscale.set_source_format(_format);
_swscale.set_target_format(_context->pix_fmt);
_swscale.set_target_size(_context->width, _context->height);
_swscale.set_source_size(_context->width, _context->height);
_swscale.set_target_size(_context->width, _context->height);
_swscale.set_source_color(_context->color_range, _context->colorspace);
_swscale.set_target_color(_context->color_range, _context->colorspace);
// Create Scaler
if (!_swscale.initialize(SWS_FAST_BILINEAR)) {
std::stringstream sstr;
sstr << "Failed to initialize frame scaler for pixel format '"
<< ffmpeg::tools::get_pixel_format_name(_context->pix_fmt) << "' with color space '"
<< ffmpeg::tools::get_color_space_name(_context->colorspace) << "' and "
<< (_swscale.is_source_full_range() ? "full" : "partial") << " color range.";
throw std::runtime_error(sstr.str().c_str());
}
// Create Frame queue
_frame_queue.set_pixel_format(_context->pix_fmt);
_frame_queue.set_resolution(_context->width, _context->height);
_frame_queue.precache(2);
}
_initialized = true;
return true;
}
void obsffmpeg::encoder::get_properties(obs_properties_t* props)
{
{ // Handler
auto ptr = obsffmpeg::find_codec_handler(this->_codec->name);
auto ptr = obsffmpeg::find_codec_handler(_codec->name);
if (ptr) {
ptr->get_properties(props, this->_codec, this->_context);
ptr->get_properties(props, _codec, _context);
}
}
@@ -649,13 +598,13 @@ void obsffmpeg::encoder::get_properties(obs_properties_t* props)
bool obsffmpeg::encoder::update(obs_data_t* settings)
{
{ // Handler
auto ptr = obsffmpeg::find_codec_handler(this->_codec->name);
auto ptr = obsffmpeg::find_codec_handler(_codec->name);
if (ptr) {
ptr->update(settings, this->_codec, this->_context);
ptr->update(settings, _codec, _context);
}
}
if ((this->_codec->capabilities & AV_CODEC_CAP_INTRA_ONLY) == 0) {
if ((_codec->capabilities & AV_CODEC_CAP_INTRA_ONLY) == 0) {
// Key-Frame Options
obs_video_info ovi;
if (!obs_get_video_info(&ovi)) {
@@ -676,8 +625,8 @@ bool obsffmpeg::encoder::update(obs_data_t* settings)
{ // FFmpeg
// Apply custom options.
av_opt_set_from_string(this->_context->priv_data,
obs_data_get_string(settings, ST_FFMPEG_CUSTOMSETTINGS), nullptr, "=", ";");
av_opt_set_from_string(_context->priv_data, obs_data_get_string(settings, ST_FFMPEG_CUSTOMSETTINGS),
nullptr, "=", ";");
}
return false;
}
@@ -696,132 +645,18 @@ bool obsffmpeg::encoder::audio_encode(encoder_frame*, encoder_packet*, bool*)
void obsffmpeg::encoder::get_video_info(video_scale_info* vsi)
{
// Apply Video Format override.
{
obs_data_t* settings = obs_encoder_get_settings(this->_self);
AVPixelFormat format_av = static_cast<AVPixelFormat>(obs_data_get_int(settings, ST_FFMPEG_COLORFORMAT));
if (format_av != AV_PIX_FMT_NONE) {
video_format format_obs = ffmpeg::tools::avpixelformat_to_obs_videoformat(format_av);
if (format_obs != VIDEO_FORMAT_NONE) {
vsi->format = format_obs;
return;
}
}
obs_data_t* settings = obs_encoder_get_settings(_self);
AVPixelFormat avformat = static_cast<AVPixelFormat>(obs_data_get_int(settings, ST_FFMPEG_COLORFORMAT));
video_format obsformat = ffmpeg::tools::avpixelformat_to_obs_videoformat(avformat);
if (obsformat != VIDEO_FORMAT_NONE) {
vsi->format = obsformat;
}
// There was no override, make sure that we got a useful video format that requires no conversion.
std::map<video_format, bool> supported;
for (auto ptr = this->_codec->pix_fmts; *ptr != AV_PIX_FMT_NONE; ptr++) {
switch (*ptr) {
case AV_PIX_FMT_RGBA:
case AV_PIX_FMT_RGB0:
supported.insert_or_assign(VIDEO_FORMAT_RGBA, true);
break;
case AV_PIX_FMT_BGRA:
supported.insert_or_assign(VIDEO_FORMAT_BGRA, true);
break;
case AV_PIX_FMT_BGR0:
supported.insert_or_assign(VIDEO_FORMAT_BGRX, true);
break;
case AV_PIX_FMT_YUV420P:
supported.insert_or_assign(VIDEO_FORMAT_I420, true);
break;
case AV_PIX_FMT_NV12:
supported.insert_or_assign(VIDEO_FORMAT_NV12, true);
break;
case AV_PIX_FMT_YVYU422:
supported.insert_or_assign(VIDEO_FORMAT_YVYU, true);
break;
case AV_PIX_FMT_UYVY422:
supported.insert_or_assign(VIDEO_FORMAT_UYVY, true);
break;
case AV_PIX_FMT_YUYV422:
supported.insert_or_assign(VIDEO_FORMAT_YUY2, true);
break;
case AV_PIX_FMT_YUV444P:
supported.insert_or_assign(VIDEO_FORMAT_I444, true);
break;
case AV_PIX_FMT_GRAY8:
supported.insert_or_assign(VIDEO_FORMAT_Y800, true);
break;
}
}
// If the video format we target is supported, don't change anything.
if (supported.count(vsi->format) > 0) {
return;
}
// If not, find the next best video format.
// This is not the best code to do this, but if it works, it's better than nothing.
std::vector<video_format> best_quality;
if (vsi->format == VIDEO_FORMAT_NV12) {
// 4:2:0
best_quality = {
VIDEO_FORMAT_NV12, VIDEO_FORMAT_I420, VIDEO_FORMAT_YVYU, VIDEO_FORMAT_YUY2, VIDEO_FORMAT_UYVY,
VIDEO_FORMAT_RGBA, VIDEO_FORMAT_BGRA, VIDEO_FORMAT_BGRX, VIDEO_FORMAT_I444, VIDEO_FORMAT_Y800,
};
} else if (vsi->format == VIDEO_FORMAT_I420) {
// 4:2:0
best_quality = {
VIDEO_FORMAT_I420, VIDEO_FORMAT_NV12, VIDEO_FORMAT_YVYU, VIDEO_FORMAT_YUY2, VIDEO_FORMAT_UYVY,
VIDEO_FORMAT_RGBA, VIDEO_FORMAT_BGRA, VIDEO_FORMAT_BGRX, VIDEO_FORMAT_I444, VIDEO_FORMAT_Y800,
};
} else if (vsi->format == VIDEO_FORMAT_YVYU) {
// 4:2:2
best_quality = {
VIDEO_FORMAT_YVYU, VIDEO_FORMAT_YUY2, VIDEO_FORMAT_UYVY, VIDEO_FORMAT_RGBA, VIDEO_FORMAT_BGRA,
VIDEO_FORMAT_BGRX, VIDEO_FORMAT_I444, VIDEO_FORMAT_NV12, VIDEO_FORMAT_I420, VIDEO_FORMAT_Y800,
};
} else if (vsi->format == VIDEO_FORMAT_YUY2) {
// 4:2:2
best_quality = {
VIDEO_FORMAT_YUY2, VIDEO_FORMAT_YVYU, VIDEO_FORMAT_UYVY, VIDEO_FORMAT_RGBA, VIDEO_FORMAT_BGRA,
VIDEO_FORMAT_BGRX, VIDEO_FORMAT_I444, VIDEO_FORMAT_NV12, VIDEO_FORMAT_I420, VIDEO_FORMAT_Y800,
};
} else if (vsi->format == VIDEO_FORMAT_UYVY) {
// 4:2:2
best_quality = {
VIDEO_FORMAT_UYVY, VIDEO_FORMAT_YVYU, VIDEO_FORMAT_YUY2, VIDEO_FORMAT_RGBA, VIDEO_FORMAT_BGRA,
VIDEO_FORMAT_BGRX, VIDEO_FORMAT_I444, VIDEO_FORMAT_NV12, VIDEO_FORMAT_I420, VIDEO_FORMAT_Y800,
};
} else if (vsi->format == VIDEO_FORMAT_I444) {
// 4:4:4
best_quality = {
VIDEO_FORMAT_I444, VIDEO_FORMAT_RGBA, VIDEO_FORMAT_BGRA, VIDEO_FORMAT_BGRX, VIDEO_FORMAT_UYVY,
VIDEO_FORMAT_YVYU, VIDEO_FORMAT_YUY2, VIDEO_FORMAT_NV12, VIDEO_FORMAT_I420, VIDEO_FORMAT_Y800,
};
} else if (vsi->format == VIDEO_FORMAT_RGBA) {
// Packed Non-Planar
best_quality = {
VIDEO_FORMAT_RGBA, VIDEO_FORMAT_BGRA, VIDEO_FORMAT_BGRX, VIDEO_FORMAT_UYVY, VIDEO_FORMAT_YVYU,
VIDEO_FORMAT_YUY2, VIDEO_FORMAT_I444, VIDEO_FORMAT_NV12, VIDEO_FORMAT_I420, VIDEO_FORMAT_Y800,
};
} else if (vsi->format == VIDEO_FORMAT_BGRA) {
// Packed Non-Planar
best_quality = {
VIDEO_FORMAT_BGRA, VIDEO_FORMAT_RGBA, VIDEO_FORMAT_BGRX, VIDEO_FORMAT_UYVY, VIDEO_FORMAT_YVYU,
VIDEO_FORMAT_YUY2, VIDEO_FORMAT_I444, VIDEO_FORMAT_NV12, VIDEO_FORMAT_I420, VIDEO_FORMAT_Y800,
};
} else if (vsi->format == VIDEO_FORMAT_BGRX) {
// Packed Non-Planar
best_quality = {
VIDEO_FORMAT_BGRX, VIDEO_FORMAT_RGBA, VIDEO_FORMAT_BGRA, VIDEO_FORMAT_UYVY, VIDEO_FORMAT_YVYU,
VIDEO_FORMAT_YUY2, VIDEO_FORMAT_I444, VIDEO_FORMAT_NV12, VIDEO_FORMAT_I420, VIDEO_FORMAT_Y800,
};
} else if (vsi->format == VIDEO_FORMAT_Y800) {
// Packed Non-Planar
best_quality = {
VIDEO_FORMAT_Y800, VIDEO_FORMAT_RGBA, VIDEO_FORMAT_BGRA, VIDEO_FORMAT_BGRX, VIDEO_FORMAT_UYVY,
VIDEO_FORMAT_YVYU, VIDEO_FORMAT_YUY2, VIDEO_FORMAT_I444, VIDEO_FORMAT_NV12, VIDEO_FORMAT_I420,
};
}
for (auto v : best_quality) {
if (supported.count(v) > 0) {
vsi->format = v;
return;
}
}
_resolution.first = vsi->width;
_resolution.second = vsi->height;
_format = ffmpeg::tools::obs_videoformat_to_avpixelformat(vsi->format);
}
bool obsffmpeg::encoder::get_sei_data(uint8_t** data, size_t* size)
@@ -876,6 +711,15 @@ static inline void copy_data(encoder_frame* frame, AVFrame* vframe)
bool obsffmpeg::encoder::video_encode(encoder_frame* frame, encoder_packet* packet, bool* received_packet)
{
// Ensure that the encoder was initialized.
try {
if (!initialize())
return false;
} catch (std::exception& ex) {
PLOG_ERROR("%s", ex.what());
return false;
}
// Convert frame.
std::shared_ptr<AVFrame> vframe = _frame_queue.pop(); // Retrieve an empty frame.
{
@@ -883,10 +727,10 @@ bool obsffmpeg::encoder::video_encode(encoder_frame* frame, encoder_packet* pack
ScopeProfiler profile("convert");
#endif
vframe->height = this->_context->height;
vframe->format = this->_context->pix_fmt;
vframe->color_range = this->_context->color_range;
vframe->colorspace = this->_context->colorspace;
vframe->height = _context->height;
vframe->format = _context->pix_fmt;
vframe->color_range = _context->color_range;
vframe->colorspace = _context->colorspace;
vframe->pts = frame->pts;
if ((_swscale.is_source_full_range() == _swscale.is_target_full_range())
@@ -895,7 +739,7 @@ bool obsffmpeg::encoder::video_encode(encoder_frame* frame, encoder_packet* pack
copy_data(frame, vframe.get());
} else {
int res = _swscale.convert(reinterpret_cast<uint8_t**>(frame->data),
reinterpret_cast<int*>(frame->linesize), 0, this->_context->height,
reinterpret_cast<int*>(frame->linesize), 0, _context->height,
vframe->data, vframe->linesize);
if (res <= 0) {
PLOG_ERROR("Failed to convert frame: %s (%ld).",
@@ -1002,7 +846,7 @@ bool obsffmpeg::encoder::video_encode_texture(uint32_t, int64_t, uint64_t, uint6
int obsffmpeg::encoder::receive_packet(bool* received_packet, struct encoder_packet* packet)
{
int res = avcodec_receive_packet(this->_context, &this->_current_packet);
int res = avcodec_receive_packet(_context, &_current_packet);
if (res == 0) {
if (!_have_first_frame) {
if (_codec->id == AV_CODEC_ID_H264) {
@@ -1038,11 +882,11 @@ int obsffmpeg::encoder::receive_packet(bool* received_packet, struct encoder_pac
}
packet->type = OBS_ENCODER_VIDEO;
packet->pts = this->_current_packet.pts;
packet->dts = this->_current_packet.dts;
packet->data = this->_current_packet.data;
packet->size = this->_current_packet.size;
packet->keyframe = !!(this->_current_packet.flags & AV_PKT_FLAG_KEY);
packet->pts = _current_packet.pts;
packet->dts = _current_packet.dts;
packet->data = _current_packet.data;
packet->size = _current_packet.size;
packet->keyframe = !!(_current_packet.flags & AV_PKT_FLAG_KEY);
packet->drop_priority = packet->keyframe ? 0 : 1;
*received_packet = true;
@@ -1057,7 +901,7 @@ int obsffmpeg::encoder::receive_packet(bool* received_packet, struct encoder_pac
int obsffmpeg::encoder::send_frame(std::shared_ptr<AVFrame> const frame)
{
int res = avcodec_send_frame(this->_context, frame.get());
int res = avcodec_send_frame(_context, frame.get());
switch (res) {
case 0:
_frame_queue_used.push(frame);
+6
View File
@@ -69,6 +69,10 @@ namespace obsffmpeg {
const AVCodec* _codec;
AVCodecContext* _context;
bool _initialized;
std::pair<size_t, size_t> _resolution;
AVPixelFormat _format;
ffmpeg::avframe_queue _frame_queue;
ffmpeg::avframe_queue _frame_queue_used;
ffmpeg::swscale _swscale;
@@ -86,6 +90,8 @@ namespace obsffmpeg {
encoder(obs_data_t* settings, obs_encoder_t* encoder);
virtual ~encoder();
bool initialize();
public: // OBS API
// Shared
void get_properties(obs_properties_t* props);