encoders/generic: Reduce warnings, fix encoding, remove threading options, and profiling
* Improved encoding loop can now deal with early and late receive requests. * Lots of warnings fixed. * Removed all threading options (they were broken anyway). * Added some profiler calls.
This commit is contained in:
@@ -18,17 +18,6 @@ RateControl.KeyFrame.Type.Seconds="Seconds"
|
|||||||
RateControl.KeyFrame.Interval="Key Frame Interval"
|
RateControl.KeyFrame.Interval="Key Frame Interval"
|
||||||
RateControl.KeyFrame.Interval.Description="Interval in which a Key Frame is placed. Not all encoders support this."
|
RateControl.KeyFrame.Interval.Description="Interval in which a Key Frame is placed. Not all encoders support this."
|
||||||
|
|
||||||
# Threading
|
|
||||||
MultiThreading="Multi-Threading"
|
|
||||||
MultiThreading.Model="Threading Model"
|
|
||||||
MultiThreading.Model.Description="Some encoders offer multi threading capabilities which can take advantage of multi-core systems."
|
|
||||||
MultiThreading.Model.Automatic="Automatic"
|
|
||||||
MultiThreading.Model.None="Single Threaded"
|
|
||||||
MultiThreading.Model.Frame="Frame-Threading"
|
|
||||||
MultiThreading.Model.Slice="Slice-Threading"
|
|
||||||
MultiThreading.ThreadCount="Number of Threads"
|
|
||||||
MultiThreading.ThreadCount.Description="The number of threads to use, with 0 being automatic.\nEncoders with 'Automatic-Threading' in the name control threads themselves and will behave different with the 0 value."
|
|
||||||
|
|
||||||
# FFmpeg
|
# FFmpeg
|
||||||
FFmpeg="FFmpeg Options"
|
FFmpeg="FFmpeg Options"
|
||||||
FFmpeg.CustomSettings="Custom Settings"
|
FFmpeg.CustomSettings="Custom Settings"
|
||||||
|
|||||||
+145
-144
@@ -19,6 +19,7 @@
|
|||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <util/profiler.hpp>
|
||||||
#include "ffmpeg/tools.hpp"
|
#include "ffmpeg/tools.hpp"
|
||||||
#include "plugin.hpp"
|
#include "plugin.hpp"
|
||||||
#include "utility.hpp"
|
#include "utility.hpp"
|
||||||
@@ -43,11 +44,6 @@ extern "C" {
|
|||||||
#define P_RATECONTROL_KEYFRAME_TYPE "RateControl.KeyFrame.Type"
|
#define P_RATECONTROL_KEYFRAME_TYPE "RateControl.KeyFrame.Type"
|
||||||
#define P_RATECONTROL_KEYFRAME_INTERVAL "RateControl.KeyFrame.Interval"
|
#define P_RATECONTROL_KEYFRAME_INTERVAL "RateControl.KeyFrame.Interval"
|
||||||
|
|
||||||
// Threading
|
|
||||||
#define P_MULTITHREADING "MultiThreading"
|
|
||||||
#define P_MULTITHREADING_MODEL "MultiThreading.Model"
|
|
||||||
#define P_MULTITHREADING_THREADCOUNT "MultiThreading.ThreadCount"
|
|
||||||
|
|
||||||
// FFmpeg
|
// FFmpeg
|
||||||
#define P_FFMPEG "FFmpeg"
|
#define P_FFMPEG "FFmpeg"
|
||||||
#define P_FFMPEG_CUSTOMSETTINGS "FFmpeg.CustomSettings"
|
#define P_FFMPEG_CUSTOMSETTINGS "FFmpeg.CustomSettings"
|
||||||
@@ -82,8 +78,15 @@ void encoder::generic_factory::register_encoder()
|
|||||||
this->info.readable_name = sstr.str();
|
this->info.readable_name = sstr.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign codec (ffmpeg name).
|
// Assign Ids.
|
||||||
|
{
|
||||||
|
const AVCodecDescriptor* desc = avcodec_descriptor_get(this->avcodec_ptr->id);
|
||||||
|
if (desc) {
|
||||||
|
this->info.codec = desc->name;
|
||||||
|
} else {
|
||||||
this->info.codec = avcodec_ptr->name;
|
this->info.codec = avcodec_ptr->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
this->info.oei.id = this->info.uid.c_str();
|
this->info.oei.id = this->info.uid.c_str();
|
||||||
this->info.oei.codec = this->info.codec.c_str();
|
this->info.oei.codec = this->info.codec.c_str();
|
||||||
|
|
||||||
@@ -320,10 +323,6 @@ void encoder::generic_factory::get_defaults(obs_data_t* settings)
|
|||||||
obs_data_set_default_double(settings, P_RATECONTROL_KEYFRAME_INTERVAL ".Seconds", 2.0);
|
obs_data_set_default_double(settings, P_RATECONTROL_KEYFRAME_INTERVAL ".Seconds", 2.0);
|
||||||
obs_data_set_default_int(settings, P_RATECONTROL_KEYFRAME_INTERVAL ".Frames", 300);
|
obs_data_set_default_int(settings, P_RATECONTROL_KEYFRAME_INTERVAL ".Frames", 300);
|
||||||
|
|
||||||
// Threading
|
|
||||||
obs_data_set_default_int(settings, P_MULTITHREADING_MODEL, -1);
|
|
||||||
obs_data_set_default_int(settings, P_MULTITHREADING_THREADCOUNT, 0);
|
|
||||||
|
|
||||||
// FFmpeg
|
// FFmpeg
|
||||||
obs_data_set_default_string(settings, P_FFMPEG_CUSTOMSETTINGS, "");
|
obs_data_set_default_string(settings, P_FFMPEG_CUSTOMSETTINGS, "");
|
||||||
obs_data_set_default_int(settings, P_FFMPEG_STANDARDCOMPLIANCE, FF_COMPLIANCE_STRICT);
|
obs_data_set_default_int(settings, P_FFMPEG_STANDARDCOMPLIANCE, FF_COMPLIANCE_STRICT);
|
||||||
@@ -382,7 +381,9 @@ void encoder::generic_factory::get_properties(obs_properties_t* props)
|
|||||||
unit_property_map.emplace(opt->unit,
|
unit_property_map.emplace(opt->unit,
|
||||||
std::pair<obs_property_t*, const AVOption*>{p, opt});
|
std::pair<obs_property_t*, const AVOption*>{p, opt});
|
||||||
} else {
|
} else {
|
||||||
p = obs_properties_add_int(props, opt->name, opt->name, opt->min, opt->max, 1);
|
p = obs_properties_add_int(props, opt->name, opt->name,
|
||||||
|
static_cast<int>(opt->min),
|
||||||
|
static_cast<int>(opt->max), 1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case AV_OPT_TYPE_FLOAT:
|
case AV_OPT_TYPE_FLOAT:
|
||||||
@@ -420,7 +421,7 @@ void encoder::generic_factory::get_properties(obs_properties_t* props)
|
|||||||
case AV_OPT_TYPE_COLOR:
|
case AV_OPT_TYPE_COLOR:
|
||||||
case AV_OPT_TYPE_CHANNEL_LAYOUT:
|
case AV_OPT_TYPE_CHANNEL_LAYOUT:
|
||||||
PLOG_WARNING("Skipped option '%s' for codec '%s' as option type is not supported.",
|
PLOG_WARNING("Skipped option '%s' for codec '%s' as option type is not supported.",
|
||||||
opt->name, this->info.uid);
|
opt->name, this->info.uid.c_str());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,8 +456,10 @@ void encoder::generic_factory::get_properties(obs_properties_t* props)
|
|||||||
TRANSLATE(P_RATECONTROL_KEYFRAME_INTERVAL), 0,
|
TRANSLATE(P_RATECONTROL_KEYFRAME_INTERVAL), 0,
|
||||||
std::numeric_limits<int32_t>::max(), 1);
|
std::numeric_limits<int32_t>::max(), 1);
|
||||||
obs_property_set_long_description(p3, TRANSLATE(DESC(P_RATECONTROL_KEYFRAME_INTERVAL)));
|
obs_property_set_long_description(p3, TRANSLATE(DESC(P_RATECONTROL_KEYFRAME_INTERVAL)));
|
||||||
obs_properties_add_group(prs, P_RATECONTROL_KEYFRAME, TRANSLATE(P_RATECONTROL_KEYFRAME),
|
auto gp = obs_properties_add_group(prs, P_RATECONTROL_KEYFRAME,
|
||||||
OBS_GROUP_NORMAL, grp);
|
TRANSLATE(P_RATECONTROL_KEYFRAME), OBS_GROUP_NORMAL, grp);
|
||||||
|
|
||||||
|
obs_property_set_visible(gp, !(this->avcodec_ptr->capabilities & AV_CODEC_CAP_INTRA_ONLY));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto p = obs_properties_add_int(prs, P_RATECONTROL_BITRATE, TRANSLATE(P_RATECONTROL_BITRATE), 1,
|
auto p = obs_properties_add_int(prs, P_RATECONTROL_BITRATE, TRANSLATE(P_RATECONTROL_BITRATE), 1,
|
||||||
@@ -478,36 +481,6 @@ void encoder::generic_factory::get_properties(obs_properties_t* props)
|
|||||||
}
|
}
|
||||||
obs_properties_add_group(props, P_RATECONTROL, TRANSLATE(P_RATECONTROL), OBS_GROUP_NORMAL, prs);
|
obs_properties_add_group(props, P_RATECONTROL, TRANSLATE(P_RATECONTROL), OBS_GROUP_NORMAL, prs);
|
||||||
};
|
};
|
||||||
{ /// Threading
|
|
||||||
auto prs = obs_properties_create();
|
|
||||||
{
|
|
||||||
auto p = obs_properties_add_list(prs, P_MULTITHREADING_MODEL, TRANSLATE(P_MULTITHREADING_MODEL),
|
|
||||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
|
||||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_MULTITHREADING_MODEL)));
|
|
||||||
obs_property_set_modified_callback2(p, modified_threading_properties, this);
|
|
||||||
obs_property_list_add_int(p, TRANSLATE(P_MULTITHREADING_MODEL ".Automatic"), -1);
|
|
||||||
obs_property_list_add_int(p, TRANSLATE(P_MULTITHREADING_MODEL ".None"), 0);
|
|
||||||
{
|
|
||||||
auto idx = obs_property_list_add_int(p, TRANSLATE(P_MULTITHREADING_MODEL ".Frame"),
|
|
||||||
FF_THREAD_FRAME);
|
|
||||||
obs_property_list_item_disable(
|
|
||||||
p, idx, !(this->avcodec_ptr->capabilities & AV_CODEC_CAP_FRAME_THREADS));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto idx = obs_property_list_add_int(p, TRANSLATE(P_MULTITHREADING_MODEL ".Slice"),
|
|
||||||
FF_THREAD_SLICE);
|
|
||||||
obs_property_list_item_disable(
|
|
||||||
p, idx, !(this->avcodec_ptr->capabilities & AV_CODEC_CAP_SLICE_THREADS));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
{
|
|
||||||
auto p = obs_properties_add_int_slider(prs, P_MULTITHREADING_THREADCOUNT,
|
|
||||||
TRANSLATE(P_MULTITHREADING_THREADCOUNT), 0,
|
|
||||||
std::thread::hardware_concurrency() * 4, 1);
|
|
||||||
obs_property_set_long_description(p, TRANSLATE(DESC(P_MULTITHREADING_THREADCOUNT)));
|
|
||||||
};
|
|
||||||
obs_properties_add_group(props, P_MULTITHREADING, TRANSLATE(P_MULTITHREADING), OBS_GROUP_NORMAL, prs);
|
|
||||||
};
|
|
||||||
{
|
{
|
||||||
auto prs = obs_properties_create();
|
auto prs = obs_properties_create();
|
||||||
{
|
{
|
||||||
@@ -541,8 +514,8 @@ AVCodec* encoder::generic_factory::get_avcodec()
|
|||||||
return this->avcodec_ptr;
|
return this->avcodec_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool encoder::generic_factory::modified_ratecontrol_properties(void* priv, obs_properties_t* props,
|
bool encoder::generic_factory::modified_ratecontrol_properties(void*, obs_properties_t* props, obs_property_t*,
|
||||||
obs_property_t* prop, obs_data_t* settings)
|
obs_data_t* settings)
|
||||||
{
|
{
|
||||||
keyframe_type kft = static_cast<keyframe_type>(obs_data_get_int(settings, P_RATECONTROL_KEYFRAME_TYPE));
|
keyframe_type kft = static_cast<keyframe_type>(obs_data_get_int(settings, P_RATECONTROL_KEYFRAME_TYPE));
|
||||||
obs_property_set_visible(obs_properties_get(props, P_RATECONTROL_KEYFRAME_INTERVAL ".Seconds"),
|
obs_property_set_visible(obs_properties_get(props, P_RATECONTROL_KEYFRAME_INTERVAL ".Seconds"),
|
||||||
@@ -552,15 +525,8 @@ bool encoder::generic_factory::modified_ratecontrol_properties(void* priv, obs_p
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool encoder::generic_factory::modified_threading_properties(void* priv, obs_properties_t* props, obs_property_t* prop,
|
encoder::generic::generic(obs_data_t* settings, obs_encoder_t* encoder)
|
||||||
obs_data_t* settings)
|
: self(encoder), lag_in_frames(0), frame_count(0)
|
||||||
{
|
|
||||||
int64_t tt = obs_data_get_int(settings, P_MULTITHREADING_MODEL);
|
|
||||||
obs_property_set_visible(obs_properties_get(props, P_MULTITHREADING_THREADCOUNT), tt != 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
encoder::generic::generic(obs_data_t* settings, obs_encoder_t* encoder) : self(encoder)
|
|
||||||
{
|
{
|
||||||
this->factory = reinterpret_cast<generic_factory*>(obs_encoder_get_type_data(self));
|
this->factory = reinterpret_cast<generic_factory*>(obs_encoder_get_type_data(self));
|
||||||
|
|
||||||
@@ -580,18 +546,12 @@ encoder::generic::generic(obs_data_t* settings, obs_encoder_t* encoder) : self(e
|
|||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
/// Rate Control
|
/// Rate Control
|
||||||
this->context->profile = obs_data_get_int(settings, P_RATECONTROL_PROFILE);
|
this->context->profile = static_cast<int>(obs_data_get_int(settings, P_RATECONTROL_PROFILE));
|
||||||
this->context->bit_rate = obs_data_get_int(settings, P_RATECONTROL_BITRATE);
|
this->context->bit_rate = static_cast<int>(obs_data_get_int(settings, P_RATECONTROL_BITRATE));
|
||||||
this->context->strict_std_compliance = obs_data_get_int(settings, P_FFMPEG_STANDARDCOMPLIANCE);
|
this->context->strict_std_compliance =
|
||||||
|
static_cast<int>(obs_data_get_int(settings, P_FFMPEG_STANDARDCOMPLIANCE));
|
||||||
this->context->debug = 0;
|
this->context->debug = 0;
|
||||||
/// Threading
|
/// Threading
|
||||||
{
|
|
||||||
int64_t tt = obs_data_get_int(settings, P_MULTITHREADING_MODEL);
|
|
||||||
if (tt == 0) {
|
|
||||||
this->context->thread_count = 1;
|
|
||||||
this->context->thread_type = 0;
|
|
||||||
this->lag_in_frames = 0;
|
|
||||||
} else if (tt == -1) {
|
|
||||||
if (this->codec->capabilities & AV_CODEC_CAP_SLICE_THREADS) {
|
if (this->codec->capabilities & AV_CODEC_CAP_SLICE_THREADS) {
|
||||||
this->context->thread_type = FF_THREAD_SLICE;
|
this->context->thread_type = FF_THREAD_SLICE;
|
||||||
} else if (this->codec->capabilities & AV_CODEC_CAP_FRAME_THREADS) {
|
} else if (this->codec->capabilities & AV_CODEC_CAP_FRAME_THREADS) {
|
||||||
@@ -599,19 +559,12 @@ encoder::generic::generic(obs_data_t* settings, obs_encoder_t* encoder) : self(e
|
|||||||
} else {
|
} else {
|
||||||
this->context->thread_type = 0;
|
this->context->thread_type = 0;
|
||||||
}
|
}
|
||||||
} else {
|
if (this->codec->capabilities & AV_CODEC_CAP_AUTO_THREADS) {
|
||||||
this->context->thread_type = tt;
|
this->context->thread_count = 0;
|
||||||
}
|
|
||||||
if (tt != 0) {
|
|
||||||
this->context->thread_count = obs_data_get_int(settings, P_MULTITHREADING_THREADCOUNT);
|
|
||||||
this->lag_in_frames = this->context->thread_count;
|
|
||||||
if (!(this->codec->capabilities & AV_CODEC_CAP_AUTO_THREADS)) {
|
|
||||||
if (this->context->thread_count == 0) {
|
|
||||||
this->context->thread_count = std::thread::hardware_concurrency();
|
|
||||||
this->lag_in_frames = std::thread::hardware_concurrency();
|
this->lag_in_frames = std::thread::hardware_concurrency();
|
||||||
}
|
} else {
|
||||||
}
|
this->context->thread_count = std::thread::hardware_concurrency();
|
||||||
}
|
this->lag_in_frames = this->context->thread_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Video and Audio exclusive setup
|
// Video and Audio exclusive setup
|
||||||
@@ -658,10 +611,11 @@ encoder::generic::generic(obs_data_t* settings, obs_encoder_t* encoder) : self(e
|
|||||||
/// Group of Pictures
|
/// Group of Pictures
|
||||||
if (static_cast<keyframe_type>(obs_data_get_int(settings, P_RATECONTROL_KEYFRAME_TYPE))
|
if (static_cast<keyframe_type>(obs_data_get_int(settings, P_RATECONTROL_KEYFRAME_TYPE))
|
||||||
== keyframe_type::Frames) {
|
== keyframe_type::Frames) {
|
||||||
this->context->gop_size = obs_data_get_int(settings, P_RATECONTROL_KEYFRAME_INTERVAL ".Frames");
|
this->context->gop_size =
|
||||||
|
static_cast<int>(obs_data_get_int(settings, P_RATECONTROL_KEYFRAME_INTERVAL ".Frames"));
|
||||||
} else {
|
} else {
|
||||||
double_t real_gop = obs_data_get_double(settings, P_RATECONTROL_KEYFRAME_INTERVAL ".Seconds");
|
double_t real_gop = obs_data_get_double(settings, P_RATECONTROL_KEYFRAME_INTERVAL ".Seconds");
|
||||||
this->context->gop_size = static_cast<int64_t>(real_gop * voi->fps_num / voi->fps_den);
|
this->context->gop_size = static_cast<int>(real_gop * voi->fps_num / voi->fps_den);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (this->codec->type == AVMEDIA_TYPE_AUDIO) {
|
} else if (this->codec->type == AVMEDIA_TYPE_AUDIO) {
|
||||||
@@ -719,116 +673,125 @@ encoder::generic::~generic()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void encoder::generic::get_properties(obs_properties_t* props) {}
|
void encoder::generic::get_properties(obs_properties_t*) {}
|
||||||
|
|
||||||
bool encoder::generic::update(obs_data_t* settings)
|
bool encoder::generic::update(obs_data_t*)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool encoder::generic::video_encode(encoder_frame* frame, encoder_packet* packet, bool* received_packet)
|
bool encoder::generic::video_encode(encoder_frame* frame, encoder_packet* packet, bool* received_packet)
|
||||||
{
|
{
|
||||||
int res = 0;
|
// Retrieve empty frame.
|
||||||
|
|
||||||
bool have_pushed_frame = false;
|
|
||||||
bool have_pulled_frame = false;
|
|
||||||
|
|
||||||
auto loop_begin = std::chrono::high_resolution_clock::now();
|
|
||||||
auto loop_time = std::chrono::nanoseconds(
|
|
||||||
static_cast<int64_t>(1000.0 * this->context->time_base.den / this->context->time_base.num) * lag_in_frames);
|
|
||||||
if ((lag_in_frames - frame_count) > 0) {
|
|
||||||
loop_time = std::chrono::nanoseconds(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!have_pulled_frame || !have_pushed_frame) {
|
|
||||||
if (!have_pushed_frame) {
|
|
||||||
AVFrame* vframe = frame_queue.pop();
|
AVFrame* vframe = frame_queue.pop();
|
||||||
vframe->pts = frame->pts;
|
|
||||||
|
// Convert frame.
|
||||||
|
{
|
||||||
|
ScopeProfiler profile("convert");
|
||||||
|
|
||||||
vframe->color_range = this->context->color_range;
|
vframe->color_range = this->context->color_range;
|
||||||
vframe->colorspace = this->context->colorspace;
|
vframe->colorspace = this->context->colorspace;
|
||||||
|
|
||||||
{
|
int res =
|
||||||
res = swscale.convert(reinterpret_cast<uint8_t**>(frame->data),
|
swscale.convert(reinterpret_cast<uint8_t**>(frame->data), reinterpret_cast<int*>(frame->linesize),
|
||||||
reinterpret_cast<int*>(frame->linesize), 0, this->context->height,
|
0, this->context->height, vframe->data, vframe->linesize);
|
||||||
vframe->data, vframe->linesize);
|
|
||||||
if (res <= 0) {
|
if (res <= 0) {
|
||||||
PLOG_ERROR("Failed to convert frame: %s (%ld).",
|
PLOG_ERROR("Failed to convert frame: %s (%ld).", ffmpeg::tools::get_error_description(res),
|
||||||
ffmpeg::tools::get_error_description(res), res);
|
res);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try and receive packet early.
|
||||||
{
|
{
|
||||||
res = avcodec_send_frame(this->context, vframe);
|
ScopeProfiler profile_inner("recieve_early");
|
||||||
if (res < 0) {
|
|
||||||
PLOG_ERROR("Failed to encode frame: %s (%ld).",
|
int res = receive_packet(received_packet, packet);
|
||||||
ffmpeg::tools::get_error_description(res), res);
|
switch (res) {
|
||||||
|
case 0:
|
||||||
|
case AVERROR(EAGAIN):
|
||||||
|
case AVERROR(EOF):
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
PLOG_ERROR("Failed to receive packet: %s (%ld).", ffmpeg::tools::get_error_description(res),
|
||||||
|
res);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
frame_queue_used.push(vframe);
|
// Send a new frame.
|
||||||
have_pushed_frame = true;
|
{
|
||||||
frame_count++;
|
ScopeProfiler profile_inner("send");
|
||||||
|
|
||||||
|
vframe->pts = frame->pts;
|
||||||
|
|
||||||
|
int res = send_frame(vframe);
|
||||||
|
switch (res) {
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case AVERROR(EAGAIN):
|
||||||
|
// This means we should call receive_packet again, but what do we do with that data?
|
||||||
|
// Why can't we queue on both? Do I really have to implement threading for this stuff?
|
||||||
|
if (*received_packet == true) {
|
||||||
|
PLOG_ERROR(
|
||||||
|
"Encoder wanted us to retrieve a packet before allowing us to submit more. Skipped "
|
||||||
|
"frame.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AVERROR(EOF):
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
PLOG_ERROR("Failed to encode frame: %s (%ld).", ffmpeg::tools::get_error_description(res), res);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!have_pulled_frame) {
|
// Try and receive packet late.
|
||||||
res = avcodec_receive_packet(this->context, this->current_packet);
|
{
|
||||||
if (res < 0) {
|
ScopeProfiler profile_inner("recieve_late");
|
||||||
if (res == AVERROR(EAGAIN)) {
|
|
||||||
*received_packet = false;
|
bool should_lag = (lag_in_frames - frame_count) <= 0;
|
||||||
have_pulled_frame = true;
|
bool early_exit = false;
|
||||||
} else if (res == AVERROR(EOF)) {
|
|
||||||
*received_packet = false;
|
while ((should_lag && !*received_packet) && !early_exit) {
|
||||||
have_pulled_frame = true;
|
int res = receive_packet(received_packet, packet);
|
||||||
} else {
|
switch (res) {
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case AVERROR(EAGAIN):
|
||||||
|
case AVERROR(EOF):
|
||||||
|
early_exit = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
PLOG_ERROR("Failed to receive packet: %s (%ld).",
|
PLOG_ERROR("Failed to receive packet: %s (%ld).",
|
||||||
ffmpeg::tools::get_error_description(res), res);
|
ffmpeg::tools::get_error_description(res), res);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
AVFrame* uframe = frame_queue_used.pop_only();
|
|
||||||
if (uframe) {
|
|
||||||
frame_queue.push(uframe);
|
|
||||||
}
|
|
||||||
packet->type = OBS_ENCODER_VIDEO;
|
|
||||||
packet->pts = this->current_packet->pts;
|
|
||||||
packet->dts = this->current_packet->pts;
|
|
||||||
packet->data = this->current_packet->data;
|
|
||||||
packet->size = this->current_packet->size;
|
|
||||||
packet->keyframe = !!(this->current_packet->flags & AV_PKT_FLAG_KEY);
|
|
||||||
packet->drop_priority = 0;
|
|
||||||
*received_packet = true;
|
|
||||||
have_pulled_frame = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((std::chrono::high_resolution_clock::now() - loop_begin) >= loop_time) {
|
if (!*received_packet) {
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void encoder::generic::get_audio_info(audio_convert_info* info) {}
|
void encoder::generic::get_audio_info(audio_convert_info*) {}
|
||||||
|
|
||||||
size_t encoder::generic::get_frame_size()
|
size_t encoder::generic::get_frame_size()
|
||||||
{
|
{
|
||||||
return size_t();
|
return size_t();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool encoder::generic::audio_encode(encoder_frame* frame, encoder_packet* packet, bool* received_packet)
|
bool encoder::generic::audio_encode(encoder_frame*, encoder_packet*, bool*)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void encoder::generic::get_video_info(video_scale_info* info) {}
|
void encoder::generic::get_video_info(video_scale_info*) {}
|
||||||
|
|
||||||
bool encoder::generic::get_sei_data(uint8_t** sei_data, size_t* size)
|
bool encoder::generic::get_sei_data(uint8_t**, size_t*)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -843,8 +806,46 @@ bool encoder::generic::get_extra_data(uint8_t** extra_data, size_t* size)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool encoder::generic::video_encode_texture(uint32_t handle, int64_t pts, uint64_t lock_key, uint64_t* next_key,
|
bool encoder::generic::video_encode_texture(uint32_t, int64_t, uint64_t, uint64_t*, encoder_packet*, bool*)
|
||||||
encoder_packet* packet, bool* received_packet)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int encoder::generic::receive_packet(bool* received_packet, struct encoder_packet* packet)
|
||||||
|
{
|
||||||
|
int res = avcodec_receive_packet(this->context, this->current_packet);
|
||||||
|
if (res == 0) {
|
||||||
|
packet->type = OBS_ENCODER_VIDEO;
|
||||||
|
packet->pts = this->current_packet->pts;
|
||||||
|
packet->dts = this->current_packet->pts;
|
||||||
|
packet->data = this->current_packet->data;
|
||||||
|
packet->size = this->current_packet->size;
|
||||||
|
packet->keyframe = !!(this->current_packet->flags & AV_PKT_FLAG_KEY);
|
||||||
|
packet->drop_priority = 0;
|
||||||
|
*received_packet = true;
|
||||||
|
|
||||||
|
{
|
||||||
|
AVFrame* uframe = frame_queue_used.pop_only();
|
||||||
|
if (frame_queue.empty()) {
|
||||||
|
frame_queue.push(uframe);
|
||||||
|
} else {
|
||||||
|
av_frame_free(&uframe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int encoder::generic::send_frame(AVFrame* frame)
|
||||||
|
{
|
||||||
|
int res = avcodec_send_frame(this->context, frame);
|
||||||
|
switch (res) {
|
||||||
|
case 0:
|
||||||
|
frame_queue_used.push(frame);
|
||||||
|
frame_count++;
|
||||||
|
case AVERROR(EAGAIN):
|
||||||
|
case AVERROR(EOF):
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,7 +17,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
#include <encoder.hpp>
|
#include <encoder.hpp>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
#include "ffmpeg/avframe-queue.hpp"
|
#include "ffmpeg/avframe-queue.hpp"
|
||||||
#include "ffmpeg/swscale.hpp"
|
#include "ffmpeg/swscale.hpp"
|
||||||
|
|
||||||
@@ -48,8 +51,6 @@ namespace encoder {
|
|||||||
public:
|
public:
|
||||||
static bool modified_ratecontrol_properties(void* priv, obs_properties_t* props, obs_property_t* prop,
|
static bool modified_ratecontrol_properties(void* priv, obs_properties_t* props, obs_property_t* prop,
|
||||||
obs_data_t* settings);
|
obs_data_t* settings);
|
||||||
static bool modified_threading_properties(void* priv, obs_properties_t* props, obs_property_t* prop,
|
|
||||||
obs_data_t* settings);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class generic {
|
class generic {
|
||||||
@@ -72,7 +73,6 @@ namespace encoder {
|
|||||||
virtual ~generic();
|
virtual ~generic();
|
||||||
|
|
||||||
// Shared
|
// Shared
|
||||||
|
|
||||||
void get_properties(obs_properties_t* props);
|
void get_properties(obs_properties_t* props);
|
||||||
|
|
||||||
bool update(obs_data_t* settings);
|
bool update(obs_data_t* settings);
|
||||||
@@ -95,5 +95,9 @@ namespace encoder {
|
|||||||
|
|
||||||
bool video_encode_texture(uint32_t handle, int64_t pts, uint64_t lock_key, uint64_t* next_key,
|
bool video_encode_texture(uint32_t handle, int64_t pts, uint64_t lock_key, uint64_t* next_key,
|
||||||
struct encoder_packet* packet, bool* received_packet);
|
struct encoder_packet* packet, bool* received_packet);
|
||||||
|
|
||||||
|
int receive_packet(bool* received_packet, struct encoder_packet* packet);
|
||||||
|
|
||||||
|
int send_frame(AVFrame* frame);
|
||||||
};
|
};
|
||||||
} // namespace encoder
|
} // namespace encoder
|
||||||
|
|||||||
Reference in New Issue
Block a user