Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b07365cdc4 | |||
| f21cbe9aba | |||
| 403b43e77b | |||
| 58d8713369 | |||
| 2f8acc58cf | |||
| e4e76dae8f | |||
| 4cac28a8a3 | |||
| d0dc4be985 | |||
| 4836f9dda6 | |||
| 650b397ced | |||
| d3f7b15633 |
+2
-1
@@ -25,7 +25,7 @@ Include("cmake/util.cmake")
|
||||
|
||||
# Automatic Versioning
|
||||
set(VERSION_MAJOR 0)
|
||||
set(VERSION_MINOR 2)
|
||||
set(VERSION_MINOR 3)
|
||||
set(VERSION_PATCH 0)
|
||||
set(VERSION_TWEAK 0)
|
||||
set(PROJECT_COMMIT "N/A")
|
||||
@@ -325,6 +325,7 @@ add_library(${PROJECT_NAME} MODULE
|
||||
${PROJECT_GENERATED}
|
||||
${PROJECT_PRIVATE}
|
||||
${PROJECT_DATA}
|
||||
${PROJECT_TEMPLATES}
|
||||
)
|
||||
|
||||
# Include Directories
|
||||
|
||||
+217
-120
@@ -147,6 +147,17 @@ static void _get_defaults(obs_data_t* settings, void* type_data) noexcept try {
|
||||
PLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__);
|
||||
};
|
||||
|
||||
static void _get_defaults_texture(obs_data_t* settings, void* type_data) noexcept try {
|
||||
#ifdef DEBUG_CALL_ORDER
|
||||
PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, settings, type_data);
|
||||
#endif
|
||||
reinterpret_cast<obsffmpeg::encoder_factory*>(type_data)->get_defaults(settings, true);
|
||||
} catch (const std::exception& ex) {
|
||||
PLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what());
|
||||
} catch (...) {
|
||||
PLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__);
|
||||
};
|
||||
|
||||
static obs_properties_t* _get_properties(void* ptr, void* type_data) noexcept try {
|
||||
#ifdef DEBUG_CALL_ORDER
|
||||
PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, ptr, type_data);
|
||||
@@ -167,6 +178,26 @@ static obs_properties_t* _get_properties(void* ptr, void* type_data) noexcept tr
|
||||
return reinterpret_cast<obs_properties_t*>(0);
|
||||
}
|
||||
|
||||
static obs_properties_t* _get_properties_texture(void* ptr, void* type_data) noexcept try {
|
||||
#ifdef DEBUG_CALL_ORDER
|
||||
PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, ptr, type_data);
|
||||
#endif
|
||||
obs_properties_t* props = obs_properties_create();
|
||||
if (type_data != nullptr) {
|
||||
reinterpret_cast<obsffmpeg::encoder_factory*>(type_data)->get_properties(props, true);
|
||||
}
|
||||
if (ptr != nullptr) {
|
||||
reinterpret_cast<obsffmpeg::encoder*>(ptr)->get_properties(props, true);
|
||||
}
|
||||
return props;
|
||||
} catch (const std::exception& ex) {
|
||||
PLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what());
|
||||
return reinterpret_cast<obs_properties_t*>(0);
|
||||
} catch (...) {
|
||||
PLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__);
|
||||
return reinterpret_cast<obs_properties_t*>(0);
|
||||
}
|
||||
|
||||
static bool _update(void* ptr, obs_data_t* settings) noexcept try {
|
||||
#ifdef DEBUG_CALL_ORDER
|
||||
PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, ptr, settings);
|
||||
@@ -384,8 +415,10 @@ void obsffmpeg::encoder_factory::register_encoder()
|
||||
info.oei.type_data = this;
|
||||
|
||||
if (ffmpeg::tools::can_hardware_encode(avcodec_ptr)) {
|
||||
info.oei.create = _create_texture;
|
||||
info.oei.encode_texture = _encode_texture;
|
||||
info.oei.create = _create_texture;
|
||||
info.oei.encode_texture = _encode_texture;
|
||||
info.oei.get_defaults2 = _get_defaults_texture;
|
||||
info.oei.get_properties2 = _get_properties_texture;
|
||||
|
||||
info_fallback.oei.type = info.oei.type;
|
||||
info_fallback.oei.create = _create;
|
||||
@@ -413,10 +446,10 @@ void obsffmpeg::encoder_factory::register_encoder()
|
||||
avcodec_ptr->name, avcodec_ptr->long_name, avcodec_ptr->capabilities);
|
||||
}
|
||||
|
||||
void obsffmpeg::encoder_factory::get_defaults(obs_data_t* settings)
|
||||
void obsffmpeg::encoder_factory::get_defaults(obs_data_t* settings, bool hw_encode)
|
||||
{
|
||||
if (_handler)
|
||||
_handler->get_defaults(settings, avcodec_ptr, nullptr);
|
||||
_handler->get_defaults(settings, avcodec_ptr, nullptr, hw_encode);
|
||||
|
||||
if ((avcodec_ptr->capabilities & AV_CODEC_CAP_INTRA_ONLY) == 0) {
|
||||
obs_data_set_default_int(settings, S_KEYFRAMES_INTERVALTYPE, 0);
|
||||
@@ -427,8 +460,11 @@ void obsffmpeg::encoder_factory::get_defaults(obs_data_t* settings)
|
||||
{ // Integrated Options
|
||||
// FFmpeg
|
||||
obs_data_set_default_string(settings, ST_FFMPEG_CUSTOMSETTINGS, "");
|
||||
obs_data_set_default_int(settings, ST_FFMPEG_COLORFORMAT, static_cast<int64_t>(AV_PIX_FMT_NONE));
|
||||
obs_data_set_default_int(settings, ST_FFMPEG_THREADS, 0);
|
||||
if (!hw_encode) {
|
||||
obs_data_set_default_int(settings, ST_FFMPEG_COLORFORMAT,
|
||||
static_cast<int64_t>(AV_PIX_FMT_NONE));
|
||||
obs_data_set_default_int(settings, ST_FFMPEG_THREADS, 0);
|
||||
}
|
||||
obs_data_set_default_int(settings, ST_FFMPEG_STANDARDCOMPLIANCE, FF_COMPLIANCE_STRICT);
|
||||
}
|
||||
}
|
||||
@@ -446,10 +482,10 @@ static bool modified_keyframes(obs_properties_t* props, obs_property_t*, obs_dat
|
||||
return false;
|
||||
}
|
||||
|
||||
void obsffmpeg::encoder_factory::get_properties(obs_properties_t* props)
|
||||
void obsffmpeg::encoder_factory::get_properties(obs_properties_t* props, bool hw_encode)
|
||||
{
|
||||
if (_handler)
|
||||
_handler->get_properties(props, avcodec_ptr, nullptr);
|
||||
_handler->get_properties(props, avcodec_ptr, nullptr, hw_encode);
|
||||
|
||||
if ((avcodec_ptr->capabilities & AV_CODEC_CAP_INTRA_ONLY) == 0) {
|
||||
// Key-Frame Options
|
||||
@@ -498,21 +534,25 @@ 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 (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 = 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 (!hw_encode) {
|
||||
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 = 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 (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)));
|
||||
}
|
||||
}
|
||||
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)));
|
||||
}
|
||||
{
|
||||
auto p = obs_properties_add_list(grp, ST_FFMPEG_STANDARDCOMPLIANCE,
|
||||
@@ -560,8 +600,7 @@ void obsffmpeg::encoder::initialize_sw(obs_data_t* settings)
|
||||
static_cast<AVPixelFormat>(obs_data_get_int(settings, ST_FFMPEG_COLORFORMAT));
|
||||
if (_pixfmt_target == AV_PIX_FMT_NONE) {
|
||||
// Find the best conversion format.
|
||||
std::vector<AVPixelFormat> fmts = ffmpeg::tools::get_software_formats(_codec->pix_fmts);
|
||||
_pixfmt_target = ffmpeg::tools::get_best_compatible_format(fmts.data(), _pixfmt_source);
|
||||
_pixfmt_target = ffmpeg::tools::get_least_lossy_format(_codec->pix_fmts, _pixfmt_source);
|
||||
|
||||
if (_handler) // Allow Handler to override the automatic color format for sanity reasons.
|
||||
_handler->override_colorformat(_pixfmt_target, settings, _codec, _context);
|
||||
@@ -612,15 +651,10 @@ void obsffmpeg::encoder::initialize_sw(obs_data_t* settings)
|
||||
<< (_swscale.is_source_full_range() ? "full" : "partial") << " range.";
|
||||
throw std::runtime_error(sstr.str());
|
||||
}
|
||||
|
||||
// Create Frame queue
|
||||
_frame_queue.set_pixel_format(_context->pix_fmt);
|
||||
_frame_queue.set_resolution(_context->width, _context->height);
|
||||
_frame_queue.precache(2);
|
||||
}
|
||||
}
|
||||
|
||||
void obsffmpeg::encoder::initialize_hw(obs_data_t* settings)
|
||||
void obsffmpeg::encoder::initialize_hw(obs_data_t*)
|
||||
{
|
||||
// Initialize Video Encoding
|
||||
auto voi = video_output_get_info(obs_encoder_video(_self));
|
||||
@@ -655,6 +689,61 @@ void obsffmpeg::encoder::initialize_hw(obs_data_t* settings)
|
||||
throw std::runtime_error("Failed to initialize AVHWFramesContext.");
|
||||
}
|
||||
|
||||
void obsffmpeg::encoder::push_free_frame(std::shared_ptr<AVFrame> frame)
|
||||
{
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
if (_free_frames.size() > 0) {
|
||||
if ((now - _free_frames_last_used) < std::chrono::seconds(1)) {
|
||||
_free_frames.push(frame);
|
||||
}
|
||||
} else {
|
||||
_free_frames.push(frame);
|
||||
_free_frames_last_used = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<AVFrame> obsffmpeg::encoder::pop_free_frame()
|
||||
{
|
||||
std::shared_ptr<AVFrame> frame;
|
||||
if (_free_frames.size() > 0) {
|
||||
// Re-use existing frames first.
|
||||
frame = _free_frames.top();
|
||||
_free_frames.pop();
|
||||
} else {
|
||||
if (_hwinst) {
|
||||
frame = _hwinst->allocate_frame(_context->hw_frames_ctx);
|
||||
} else {
|
||||
frame = std::shared_ptr<AVFrame>(av_frame_alloc(), [](AVFrame* frame) {
|
||||
av_frame_unref(frame);
|
||||
av_frame_free(&frame);
|
||||
});
|
||||
|
||||
frame->width = _context->width;
|
||||
frame->height = _context->height;
|
||||
frame->format = _context->pix_fmt;
|
||||
|
||||
int res = av_frame_get_buffer(frame.get(), 32);
|
||||
if (res < 0) {
|
||||
throw std::runtime_error(ffmpeg::tools::get_error_description(res));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
void obsffmpeg::encoder::push_used_frame(std::shared_ptr<AVFrame> frame)
|
||||
{
|
||||
_used_frames.push(frame);
|
||||
}
|
||||
|
||||
std::shared_ptr<AVFrame> obsffmpeg::encoder::pop_used_frame()
|
||||
{
|
||||
auto frame = _used_frames.front();
|
||||
_used_frames.pop();
|
||||
return frame;
|
||||
}
|
||||
|
||||
obsffmpeg::encoder::encoder(obs_data_t* settings, obs_encoder_t* encoder, bool is_texture_encode)
|
||||
: _self(encoder), _lag_in_frames(0), _count_send_frames(0), _have_first_frame(false)
|
||||
{
|
||||
@@ -700,14 +789,27 @@ obsffmpeg::encoder::encoder(obs_data_t* settings, obs_encoder_t* encoder, bool i
|
||||
|
||||
// Log Encoder info
|
||||
PLOG_INFO("[%s] Initializing...", _codec->name);
|
||||
PLOG_INFO("[%s] Video Input: %ldx%ld %s %s %s", _codec->name, _swscale.get_source_width(),
|
||||
_swscale.get_source_height(), ffmpeg::tools::get_pixel_format_name(_swscale.get_source_format()),
|
||||
ffmpeg::tools::get_color_space_name(_swscale.get_source_colorspace()),
|
||||
_swscale.is_source_full_range() ? "Full" : "Partial");
|
||||
PLOG_INFO("[%s] Video Output: %ldx%ld %s %s %s", _codec->name, _swscale.get_target_width(),
|
||||
_swscale.get_target_height(), ffmpeg::tools::get_pixel_format_name(_swscale.get_target_format()),
|
||||
ffmpeg::tools::get_color_space_name(_swscale.get_target_colorspace()),
|
||||
_swscale.is_target_full_range() ? "Full" : "Partial");
|
||||
if (_hwinst) {
|
||||
PLOG_INFO("[%s] Video Input: %ldx%ld %s %s %s", _codec->name, _context->width, _context->height,
|
||||
ffmpeg::tools::get_pixel_format_name(_context->sw_pix_fmt),
|
||||
ffmpeg::tools::get_color_space_name(_context->colorspace),
|
||||
_swscale.is_source_full_range() ? "Full" : "Partial");
|
||||
PLOG_INFO("[%s] Video Output: %ldx%ld %s %s %s", _codec->name, _context->width, _context->height,
|
||||
ffmpeg::tools::get_pixel_format_name(_context->sw_pix_fmt),
|
||||
ffmpeg::tools::get_color_space_name(_context->colorspace),
|
||||
_swscale.is_target_full_range() ? "Full" : "Partial");
|
||||
} else {
|
||||
PLOG_INFO("[%s] Video Input: %ldx%ld %s %s %s", _codec->name, _swscale.get_source_width(),
|
||||
_swscale.get_source_height(),
|
||||
ffmpeg::tools::get_pixel_format_name(_swscale.get_source_format()),
|
||||
ffmpeg::tools::get_color_space_name(_swscale.get_source_colorspace()),
|
||||
_swscale.is_source_full_range() ? "Full" : "Partial");
|
||||
PLOG_INFO("[%s] Video Output: %ldx%ld %s %s %s", _codec->name, _swscale.get_target_width(),
|
||||
_swscale.get_target_height(),
|
||||
ffmpeg::tools::get_pixel_format_name(_swscale.get_target_format()),
|
||||
ffmpeg::tools::get_color_space_name(_swscale.get_target_colorspace()),
|
||||
_swscale.is_target_full_range() ? "Full" : "Partial");
|
||||
}
|
||||
PLOG_INFO("[%s] Framerate: %ld/%ld (%f FPS)", _codec->name, _context->time_base.den, _context->time_base.num,
|
||||
static_cast<double_t>(_context->time_base.den) / static_cast<double_t>(_context->time_base.num));
|
||||
PLOG_INFO("[%s] Custom Settings: %s", _codec->name, obs_data_get_string(settings, ST_FFMPEG_CUSTOMSETTINGS));
|
||||
@@ -744,15 +846,13 @@ obsffmpeg::encoder::~encoder()
|
||||
|
||||
av_packet_unref(&_current_packet);
|
||||
|
||||
_frame_queue.clear();
|
||||
_frame_queue_used.clear();
|
||||
_swscale.finalize();
|
||||
}
|
||||
|
||||
void obsffmpeg::encoder::get_properties(obs_properties_t* props)
|
||||
void obsffmpeg::encoder::get_properties(obs_properties_t* props, bool hw_encode)
|
||||
{
|
||||
if (_handler)
|
||||
_handler->get_properties(props, _codec, _context);
|
||||
_handler->get_properties(props, _codec, _context, hw_encode);
|
||||
|
||||
obs_property_set_enabled(obs_properties_get(props, S_KEYFRAMES), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, S_KEYFRAMES_INTERVALTYPE), false);
|
||||
@@ -771,8 +871,8 @@ bool obsffmpeg::encoder::update(obs_data_t* settings)
|
||||
_context->strict_std_compliance = static_cast<int>(obs_data_get_int(settings, ST_FFMPEG_STANDARDCOMPLIANCE));
|
||||
_context->debug = 0;
|
||||
/// Threading
|
||||
if (_codec->capabilities
|
||||
& (AV_CODEC_CAP_AUTO_THREADS | AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_SLICE_THREADS)) {
|
||||
if (_codec->capabilities & (AV_CODEC_CAP_AUTO_THREADS | AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_SLICE_THREADS)
|
||||
&& !_hwinst) {
|
||||
if (_codec->capabilities & AV_CODEC_CAP_FRAME_THREADS) {
|
||||
_context->thread_type |= FF_THREAD_FRAME;
|
||||
}
|
||||
@@ -787,6 +887,10 @@ bool obsffmpeg::encoder::update(obs_data_t* settings)
|
||||
_context->thread_count = std::thread::hardware_concurrency();
|
||||
_lag_in_frames = _context->thread_count;
|
||||
}
|
||||
} else {
|
||||
_context->thread_count = 1;
|
||||
_context->thread_type = 0;
|
||||
_lag_in_frames = 1;
|
||||
}
|
||||
|
||||
if (_handler)
|
||||
@@ -817,6 +921,9 @@ bool obsffmpeg::encoder::update(obs_data_t* settings)
|
||||
nullptr, "=", ";");
|
||||
}
|
||||
|
||||
if (_handler)
|
||||
_handler->override_lag_in_frames(_lag_in_frames, settings, _codec, _context);
|
||||
|
||||
// Handler Logging
|
||||
if (_handler)
|
||||
_handler->log_options(settings, _codec, _context);
|
||||
@@ -895,8 +1002,9 @@ static inline void copy_data(encoder_frame* frame, AVFrame* vframe)
|
||||
|
||||
bool obsffmpeg::encoder::video_encode(encoder_frame* frame, encoder_packet* packet, bool* received_packet)
|
||||
{
|
||||
std::shared_ptr<AVFrame> vframe = pop_free_frame(); // Retrieve an empty frame.
|
||||
|
||||
// Convert frame.
|
||||
std::shared_ptr<AVFrame> vframe = _frame_queue.pop(); // Retrieve an empty frame.
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
ScopeProfiler profile("convert");
|
||||
@@ -929,10 +1037,6 @@ bool obsffmpeg::encoder::video_encode(encoder_frame* frame, encoder_packet* pack
|
||||
if (!encode_avframe(vframe, packet, received_packet))
|
||||
return false;
|
||||
|
||||
if (vframe != nullptr) {
|
||||
_frame_queue.push(vframe);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -945,12 +1049,8 @@ bool obsffmpeg::encoder::video_encode_texture(uint32_t handle, int64_t pts, uint
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<AVFrame> frame{av_frame_alloc(), [](void* ptr) {
|
||||
av_frame_unref(reinterpret_cast<AVFrame*>(ptr));
|
||||
av_frame_free(reinterpret_cast<AVFrame**>(&ptr));
|
||||
}};
|
||||
|
||||
std::shared_ptr<AVFrame> vframe = _hwinst->avframe_from_obs(_context->hw_frames_ctx, handle, lock_key, next_lock_key);
|
||||
std::shared_ptr<AVFrame> vframe = pop_free_frame();
|
||||
_hwinst->copy_from_obs(_context->hw_frames_ctx, handle, lock_key, next_lock_key, vframe);
|
||||
|
||||
vframe->color_range = _context->color_range;
|
||||
vframe->colorspace = _context->colorspace;
|
||||
@@ -971,82 +1071,76 @@ int obsffmpeg::encoder::receive_packet(bool* received_packet, struct encoder_pac
|
||||
av_packet_unref(&_current_packet);
|
||||
|
||||
int res = avcodec_receive_packet(_context, &_current_packet);
|
||||
if (res == 0) {
|
||||
if (!_have_first_frame) {
|
||||
if (_codec->id == AV_CODEC_ID_H264) {
|
||||
uint8_t* tmp_packet;
|
||||
uint8_t* tmp_header;
|
||||
uint8_t* tmp_sei;
|
||||
size_t sz_packet, sz_header, sz_sei;
|
||||
|
||||
obs_extract_avc_headers(_current_packet.data, _current_packet.size, &tmp_packet,
|
||||
&sz_packet, &tmp_header, &sz_header, &tmp_sei, &sz_sei);
|
||||
|
||||
if (sz_header) {
|
||||
_extra_data.resize(sz_header);
|
||||
std::memcpy(_extra_data.data(), tmp_header, sz_header);
|
||||
}
|
||||
|
||||
if (sz_sei) {
|
||||
_sei_data.resize(sz_sei);
|
||||
std::memcpy(_sei_data.data(), tmp_sei, sz_sei);
|
||||
}
|
||||
|
||||
// Not required, we only need the Extra Data and SEI Data anyway.
|
||||
//std::memcpy(_current_packet.data, tmp_packet, sz_packet);
|
||||
//_current_packet.size = static_cast<int>(sz_packet);
|
||||
|
||||
bfree(tmp_packet);
|
||||
bfree(tmp_header);
|
||||
bfree(tmp_sei);
|
||||
} else if (_codec->id == AV_CODEC_ID_HEVC) {
|
||||
obsffmpeg::codecs::hevc::extract_header_sei(_current_packet.data, _current_packet.size,
|
||||
_extra_data, _sei_data);
|
||||
} else if (_context->extradata != nullptr) {
|
||||
_extra_data.resize(_context->extradata_size);
|
||||
std::memcpy(_extra_data.data(), _context->extradata, _context->extradata_size);
|
||||
}
|
||||
_have_first_frame = true;
|
||||
}
|
||||
|
||||
// Allow Handler Post-Processing
|
||||
if (_handler)
|
||||
_handler->process_avpacket(_current_packet, _codec, _context);
|
||||
|
||||
packet->type = OBS_ENCODER_VIDEO;
|
||||
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;
|
||||
|
||||
{
|
||||
std::shared_ptr<AVFrame> uframe = _frame_queue_used.pop_only();
|
||||
_frame_queue.push(uframe);
|
||||
}
|
||||
if (res != 0) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (!_have_first_frame) {
|
||||
if (_codec->id == AV_CODEC_ID_H264) {
|
||||
uint8_t* tmp_packet;
|
||||
uint8_t* tmp_header;
|
||||
uint8_t* tmp_sei;
|
||||
size_t sz_packet, sz_header, sz_sei;
|
||||
|
||||
obs_extract_avc_headers(_current_packet.data, _current_packet.size, &tmp_packet, &sz_packet,
|
||||
&tmp_header, &sz_header, &tmp_sei, &sz_sei);
|
||||
|
||||
if (sz_header) {
|
||||
_extra_data.resize(sz_header);
|
||||
std::memcpy(_extra_data.data(), tmp_header, sz_header);
|
||||
}
|
||||
|
||||
if (sz_sei) {
|
||||
_sei_data.resize(sz_sei);
|
||||
std::memcpy(_sei_data.data(), tmp_sei, sz_sei);
|
||||
}
|
||||
|
||||
// Not required, we only need the Extra Data and SEI Data anyway.
|
||||
//std::memcpy(_current_packet.data, tmp_packet, sz_packet);
|
||||
//_current_packet.size = static_cast<int>(sz_packet);
|
||||
|
||||
bfree(tmp_packet);
|
||||
bfree(tmp_header);
|
||||
bfree(tmp_sei);
|
||||
} else if (_codec->id == AV_CODEC_ID_HEVC) {
|
||||
obsffmpeg::codecs::hevc::extract_header_sei(_current_packet.data, _current_packet.size,
|
||||
_extra_data, _sei_data);
|
||||
} else if (_context->extradata != nullptr) {
|
||||
_extra_data.resize(_context->extradata_size);
|
||||
std::memcpy(_extra_data.data(), _context->extradata, _context->extradata_size);
|
||||
}
|
||||
_have_first_frame = true;
|
||||
}
|
||||
|
||||
// Allow Handler Post-Processing
|
||||
if (_handler)
|
||||
_handler->process_avpacket(_current_packet, _codec, _context);
|
||||
|
||||
packet->type = OBS_ENCODER_VIDEO;
|
||||
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;
|
||||
|
||||
push_free_frame(pop_used_frame());
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int obsffmpeg::encoder::send_frame(std::shared_ptr<AVFrame> const frame)
|
||||
{
|
||||
int res = avcodec_send_frame(_context, frame.get());
|
||||
switch (res) {
|
||||
case 0:
|
||||
if (!_hwapi)
|
||||
_frame_queue_used.push(frame);
|
||||
_count_send_frames++;
|
||||
case AVERROR(EAGAIN):
|
||||
case AVERROR(EOF):
|
||||
break;
|
||||
if (res == 0) {
|
||||
push_used_frame(frame);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool obsffmpeg::encoder::encode_avframe(std::shared_ptr<AVFrame>& frame, encoder_packet* packet, bool* received_packet)
|
||||
bool obsffmpeg::encoder::encode_avframe(std::shared_ptr<AVFrame> frame, encoder_packet* packet, bool* received_packet)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
ScopeProfiler profile("loop");
|
||||
@@ -1054,7 +1148,7 @@ bool obsffmpeg::encoder::encode_avframe(std::shared_ptr<AVFrame>& frame, encoder
|
||||
|
||||
bool sent_frame = false;
|
||||
bool recv_packet = false;
|
||||
bool should_lag = (_lag_in_frames - _count_send_frames) <= 0;
|
||||
bool should_lag = (_count_send_frames >= _lag_in_frames);
|
||||
|
||||
auto loop_begin = std::chrono::high_resolution_clock::now();
|
||||
auto loop_end = loop_begin + std::chrono::milliseconds(50);
|
||||
@@ -1127,5 +1221,8 @@ bool obsffmpeg::encoder::encode_avframe(std::shared_ptr<AVFrame>& frame, encoder
|
||||
}
|
||||
}
|
||||
|
||||
if (!sent_frame)
|
||||
push_free_frame(frame);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+24
-14
@@ -23,6 +23,8 @@
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <stack>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include "ffmpeg/avframe-queue.hpp"
|
||||
@@ -50,7 +52,7 @@ namespace obsffmpeg {
|
||||
std::string uid;
|
||||
std::string codec;
|
||||
std::string readable_name;
|
||||
obs_encoder_info oei;
|
||||
obs_encoder_info oei = {0};
|
||||
};
|
||||
|
||||
class encoder_factory {
|
||||
@@ -66,9 +68,9 @@ namespace obsffmpeg {
|
||||
|
||||
void register_encoder();
|
||||
|
||||
void get_defaults(obs_data_t* settings);
|
||||
void get_defaults(obs_data_t* settings, bool hw_encoder = false);
|
||||
|
||||
void get_properties(obs_properties_t* props);
|
||||
void get_properties(obs_properties_t* props, bool hw_encoder = false);
|
||||
|
||||
const AVCodec* get_avcodec();
|
||||
|
||||
@@ -81,38 +83,46 @@ namespace obsffmpeg {
|
||||
obs_encoder_t* _self;
|
||||
encoder_factory* _factory;
|
||||
|
||||
const AVCodec* _codec;
|
||||
AVCodecContext* _context;
|
||||
AVHWFramesContext* _hwcontext;
|
||||
const AVCodec* _codec;
|
||||
AVCodecContext* _context;
|
||||
|
||||
std::shared_ptr<obsffmpeg::ui::handler> _handler;
|
||||
|
||||
std::shared_ptr<obsffmpeg::hwapi::base> _hwapi;
|
||||
std::shared_ptr<obsffmpeg::hwapi::instance> _hwinst;
|
||||
|
||||
ffmpeg::avframe_queue _frame_queue;
|
||||
ffmpeg::avframe_queue _frame_queue_used;
|
||||
ffmpeg::swscale _swscale;
|
||||
AVPacket _current_packet;
|
||||
ffmpeg::swscale _swscale;
|
||||
AVPacket _current_packet;
|
||||
|
||||
int64_t _lag_in_frames;
|
||||
int64_t _count_send_frames;
|
||||
size_t _lag_in_frames;
|
||||
size_t _count_send_frames;
|
||||
|
||||
// Extra Data
|
||||
bool _have_first_frame;
|
||||
std::vector<uint8_t> _extra_data;
|
||||
std::vector<uint8_t> _sei_data;
|
||||
|
||||
// Frame Stack and Queue
|
||||
std::stack<std::shared_ptr<AVFrame>> _free_frames;
|
||||
std::queue<std::shared_ptr<AVFrame>> _used_frames;
|
||||
std::chrono::high_resolution_clock::time_point _free_frames_last_used;
|
||||
|
||||
void initialize_sw(obs_data_t* settings);
|
||||
void initialize_hw(obs_data_t* settings);
|
||||
|
||||
void push_free_frame(std::shared_ptr<AVFrame> frame);
|
||||
std::shared_ptr<AVFrame> pop_free_frame();
|
||||
|
||||
void push_used_frame(std::shared_ptr<AVFrame> frame);
|
||||
std::shared_ptr<AVFrame> pop_used_frame();
|
||||
|
||||
public:
|
||||
encoder(obs_data_t* settings, obs_encoder_t* encoder, bool is_texture_encode = false);
|
||||
virtual ~encoder();
|
||||
|
||||
public: // OBS API
|
||||
// Shared
|
||||
void get_properties(obs_properties_t* props);
|
||||
void get_properties(obs_properties_t* props, bool hw_encode = false);
|
||||
|
||||
bool update(obs_data_t* settings);
|
||||
|
||||
@@ -139,7 +149,7 @@ namespace obsffmpeg {
|
||||
|
||||
int send_frame(std::shared_ptr<AVFrame> frame);
|
||||
|
||||
bool encode_avframe(std::shared_ptr<AVFrame>& frame, struct encoder_packet* packet,
|
||||
bool encode_avframe(std::shared_ptr<AVFrame> frame, struct encoder_packet* packet,
|
||||
bool* received_packet);
|
||||
};
|
||||
} // namespace obsffmpeg
|
||||
|
||||
@@ -279,106 +279,6 @@ std::vector<AVPixelFormat> ffmpeg::tools::get_software_formats(const AVPixelForm
|
||||
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>>
|
||||
|
||||
@@ -56,8 +56,6 @@ namespace ffmpeg {
|
||||
|
||||
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 ffmpeg
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <cinttypes>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
@@ -54,8 +55,14 @@ namespace obsffmpeg {
|
||||
public:
|
||||
virtual AVBufferRef* create_device_context() = 0;
|
||||
|
||||
virtual std::shared_ptr<AVFrame> avframe_from_obs(AVBufferRef* frames, uint32_t handle, uint64_t lock_key,
|
||||
uint64_t* next_lock_key) = 0;
|
||||
virtual std::shared_ptr<AVFrame> allocate_frame(AVBufferRef* frames) = 0;
|
||||
|
||||
virtual void copy_from_obs(AVBufferRef* frames, uint32_t handle, uint64_t lock_key,
|
||||
uint64_t* next_lock_key, std::shared_ptr<AVFrame> frame) = 0;
|
||||
|
||||
virtual std::shared_ptr<AVFrame> avframe_from_obs(AVBufferRef* frames, uint32_t handle,
|
||||
uint64_t lock_key,
|
||||
uint64_t* next_lock_key) = 0;
|
||||
};
|
||||
} // namespace hwapi
|
||||
} // namespace obsffmpeg
|
||||
|
||||
+26
-19
@@ -146,7 +146,7 @@ AVBufferRef* obsffmpeg::hwapi::d3d11_instance::create_device_context()
|
||||
AVD3D11VADeviceContext* d3d11va = reinterpret_cast<AVD3D11VADeviceContext*>(dctx->hwctx);
|
||||
|
||||
// TODO: Determine if these need an additional reference.
|
||||
d3d11va->device = _device;
|
||||
d3d11va->device = _device;
|
||||
d3d11va->device->AddRef();
|
||||
d3d11va->device_context = _context;
|
||||
d3d11va->device_context->AddRef();
|
||||
@@ -157,20 +157,31 @@ AVBufferRef* obsffmpeg::hwapi::d3d11_instance::create_device_context()
|
||||
return dctx_ref;
|
||||
}
|
||||
|
||||
std::shared_ptr<AVFrame> obsffmpeg::hwapi::d3d11_instance::avframe_from_obs(AVBufferRef* frames, uint32_t handle,
|
||||
uint64_t lock_key, uint64_t* next_lock_key)
|
||||
std::shared_ptr<AVFrame> obsffmpeg::hwapi::d3d11_instance::allocate_frame(AVBufferRef* frames)
|
||||
{
|
||||
auto frame = std::shared_ptr<AVFrame>(av_frame_alloc(), [](AVFrame* frame) {
|
||||
av_frame_unref(frame);
|
||||
av_frame_free(&frame);
|
||||
});
|
||||
|
||||
if (av_hwframe_get_buffer(frames, frame.get(), 0) < 0) {
|
||||
throw std::runtime_error("Failed to create AVFrame.");
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
void obsffmpeg::hwapi::d3d11_instance::copy_from_obs(AVBufferRef*, uint32_t handle, uint64_t lock_key,
|
||||
uint64_t* next_lock_key, std::shared_ptr<AVFrame> frame)
|
||||
{
|
||||
AVFrame* frame = av_frame_alloc();
|
||||
ATL::CComPtr<IDXGIKeyedMutex> mutex;
|
||||
ATL::CComPtr<ID3D11Texture2D> input;
|
||||
D3D11_TEXTURE2D_DESC input_desc;
|
||||
D3D11_TEXTURE2D_DESC output_desc;
|
||||
//ATL::CComPtr<ID3D11Texture2D> output;
|
||||
|
||||
if (FAILED(_device->OpenSharedResource(reinterpret_cast<HANDLE>(static_cast<uintptr_t>(handle)),
|
||||
__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&input)))) {
|
||||
throw std::runtime_error("Failed to open shared texture resource.");
|
||||
}
|
||||
|
||||
if (FAILED(input->QueryInterface(__uuidof(IDXGIKeyedMutex), reinterpret_cast<void**>(&mutex)))) {
|
||||
throw std::runtime_error("Failed to retrieve mutex for texture resource.");
|
||||
}
|
||||
@@ -182,13 +193,6 @@ std::shared_ptr<AVFrame> obsffmpeg::hwapi::d3d11_instance::avframe_from_obs(AVBu
|
||||
// Set some parameters on the input texture, and get its description.
|
||||
UINT evict = input->GetEvictionPriority();
|
||||
input->SetEvictionPriority(DXGI_RESOURCE_PRIORITY_MAXIMUM);
|
||||
input->GetDesc(&input_desc);
|
||||
|
||||
if (av_hwframe_get_buffer(frames, frame, 0) < 0) {
|
||||
throw std::runtime_error("Failed to create AVFrame.");
|
||||
}
|
||||
|
||||
reinterpret_cast<ID3D11Texture2D*>(frame->data[0])->GetDesc(&output_desc);
|
||||
|
||||
// Clone the content of the input texture.
|
||||
_context->CopyResource(reinterpret_cast<ID3D11Texture2D*>(frame->data[0]), input);
|
||||
@@ -202,9 +206,12 @@ std::shared_ptr<AVFrame> obsffmpeg::hwapi::d3d11_instance::avframe_from_obs(AVBu
|
||||
|
||||
// TODO: Determine if this is necessary.
|
||||
mutex->ReleaseSync(*next_lock_key);
|
||||
|
||||
return std::shared_ptr<AVFrame>(frame, [](AVFrame* frame) {
|
||||
av_frame_unref(frame);
|
||||
av_frame_free(&frame);
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<AVFrame> obsffmpeg::hwapi::d3d11_instance::avframe_from_obs(AVBufferRef* frames, uint32_t handle,
|
||||
uint64_t lock_key, uint64_t* next_lock_key)
|
||||
{
|
||||
auto frame = this->allocate_frame(frames);
|
||||
this->copy_from_obs(frames, handle, lock_key, next_lock_key, frame);
|
||||
return frame;
|
||||
}
|
||||
|
||||
@@ -21,13 +21,13 @@
|
||||
|
||||
#include <atlutil.h>
|
||||
#include <d3d11.h>
|
||||
#include <d3d11_1.h>
|
||||
#include <dxgi.h>
|
||||
#include <windows.h>
|
||||
#include "base.hpp"
|
||||
|
||||
namespace obsffmpeg {
|
||||
namespace hwapi {
|
||||
class d3d11 : public base {
|
||||
class d3d11 : public ::obsffmpeg::hwapi::base {
|
||||
typedef HRESULT(__stdcall* CreateDXGIFactory_t)(REFIID, void**);
|
||||
typedef HRESULT(__stdcall* CreateDXGIFactory1_t)(REFIID, void**);
|
||||
typedef HRESULT(__stdcall* D3D11CreateDevice_t)(_In_opt_ IDXGIAdapter*, D3D_DRIVER_TYPE,
|
||||
@@ -55,7 +55,7 @@ namespace obsffmpeg {
|
||||
create(obsffmpeg::hwapi::device target) override;
|
||||
};
|
||||
|
||||
class d3d11_instance : public instance {
|
||||
class d3d11_instance : public ::obsffmpeg::hwapi::instance {
|
||||
ATL::CComPtr<ID3D11Device> _device;
|
||||
ATL::CComPtr<ID3D11DeviceContext> _context;
|
||||
|
||||
@@ -65,9 +65,14 @@ namespace obsffmpeg {
|
||||
|
||||
virtual AVBufferRef* create_device_context() override;
|
||||
|
||||
virtual std::shared_ptr<AVFrame> allocate_frame(AVBufferRef* frames) override;
|
||||
|
||||
virtual void copy_from_obs(AVBufferRef* frames, uint32_t handle, uint64_t lock_key,
|
||||
uint64_t* next_lock_key, std::shared_ptr<AVFrame> frame) override;
|
||||
|
||||
virtual std::shared_ptr<AVFrame> avframe_from_obs(AVBufferRef* frames, uint32_t handle,
|
||||
uint64_t lock_key,
|
||||
uint64_t* next_lock_key) override;
|
||||
uint64_t* next_lock_key) override;
|
||||
};
|
||||
} // namespace hwapi
|
||||
} // namespace obsffmpeg
|
||||
|
||||
@@ -35,7 +35,7 @@ extern "C" {
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::debug_handler::get_defaults(obs_data_t*, const AVCodec*, AVCodecContext*) {}
|
||||
void obsffmpeg::ui::debug_handler::get_defaults(obs_data_t*, const AVCodec*, AVCodecContext*, bool) {}
|
||||
|
||||
template<typename T>
|
||||
std::string to_string(T value){};
|
||||
@@ -64,7 +64,8 @@ std::string to_string(double_t value)
|
||||
return std::string(buf.data(), buf.data() + buf.size());
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::debug_handler::get_properties(obs_properties_t*, const AVCodec* codec, AVCodecContext* context)
|
||||
void obsffmpeg::ui::debug_handler::get_properties(obs_properties_t*, const AVCodec* codec, AVCodecContext* context,
|
||||
bool)
|
||||
{
|
||||
if (context)
|
||||
return;
|
||||
|
||||
@@ -26,11 +26,11 @@ namespace obsffmpeg {
|
||||
namespace ui {
|
||||
class debug_handler : public handler {
|
||||
public:
|
||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context,
|
||||
bool hw_encode) override;
|
||||
|
||||
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
AVCodecContext* context, bool hw_encode) override;
|
||||
|
||||
virtual void update(obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
|
||||
+13
-16
@@ -23,34 +23,31 @@
|
||||
|
||||
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_info(obs_encoder_info*, obs_encoder_info*) {}
|
||||
|
||||
void obsffmpeg::ui::handler::override_colorformat(AVPixelFormat& target_format, obs_data_t* settings,
|
||||
const AVCodec* codec, AVCodecContext* context)
|
||||
{}
|
||||
void obsffmpeg::ui::handler::override_colorformat(AVPixelFormat&, obs_data_t*, const AVCodec*, AVCodecContext*) {}
|
||||
|
||||
void obsffmpeg::ui::handler::get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context) {}
|
||||
void obsffmpeg::ui::handler::override_lag_in_frames(size_t&, obs_data_t*, const AVCodec*, AVCodecContext*) {}
|
||||
|
||||
void obsffmpeg::ui::handler::get_properties(obs_properties_t* props, const AVCodec* codec, AVCodecContext* context) {}
|
||||
void obsffmpeg::ui::handler::get_defaults(obs_data_t*, const AVCodec*, AVCodecContext*, bool) {}
|
||||
|
||||
obsffmpeg::hwapi::device obsffmpeg::ui::handler::find_hw_device(std::shared_ptr<obsffmpeg::hwapi::base> api,
|
||||
const AVCodec* codec, AVCodecContext* context)
|
||||
void obsffmpeg::ui::handler::get_properties(obs_properties_t*, const AVCodec*, AVCodecContext*, bool) {}
|
||||
|
||||
obsffmpeg::hwapi::device obsffmpeg::ui::handler::find_hw_device(std::shared_ptr<obsffmpeg::hwapi::base>, const AVCodec*,
|
||||
AVCodecContext*)
|
||||
{
|
||||
return obsffmpeg::hwapi::device();
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::handler::update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context) {}
|
||||
void obsffmpeg::ui::handler::update(obs_data_t*, const AVCodec*, AVCodecContext*) {}
|
||||
|
||||
void obsffmpeg::ui::handler::log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context) {}
|
||||
void obsffmpeg::ui::handler::log_options(obs_data_t*, const AVCodec*, AVCodecContext*) {}
|
||||
|
||||
void obsffmpeg::ui::handler::import_from_ffmpeg(const std::string ffmpeg, obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context)
|
||||
{}
|
||||
void obsffmpeg::ui::handler::import_from_ffmpeg(const std::string, obs_data_t*, const AVCodec*, AVCodecContext*) {}
|
||||
|
||||
std::string obsffmpeg::ui::handler::export_for_ffmpeg(obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context)
|
||||
std::string obsffmpeg::ui::handler::export_for_ffmpeg(obs_data_t*, const AVCodec*, AVCodecContext*)
|
||||
{
|
||||
return std::string();
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::handler::process_avpacket(AVPacket& packet, const AVCodec* codec, AVCodecContext* context) {}
|
||||
void obsffmpeg::ui::handler::process_avpacket(AVPacket&, const AVCodec*, AVCodecContext*) {}
|
||||
|
||||
@@ -47,10 +47,13 @@ namespace obsffmpeg {
|
||||
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 override_lag_in_frames(size_t& lag, obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context);
|
||||
|
||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context, bool hw_encode);
|
||||
|
||||
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||
AVCodecContext* context);
|
||||
AVCodecContext* context, bool hw_encode);
|
||||
|
||||
virtual obsffmpeg::hwapi::device find_hw_device(std::shared_ptr<obsffmpeg::hwapi::base> api,
|
||||
const AVCodec* codec, AVCodecContext* context);
|
||||
|
||||
@@ -85,8 +85,14 @@ void obsffmpeg::ui::nvenc_h264_handler::override_visible_name(const AVCodec*, st
|
||||
name = "H.264/AVC Encoder (NVidia NVENC)";
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_h264_handler::override_lag_in_frames(size_t& lag, obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context)
|
||||
{
|
||||
nvenc::override_lag_in_frames(lag, settings, codec, context);
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_h264_handler::get_defaults(obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context)
|
||||
AVCodecContext* context, bool)
|
||||
{
|
||||
nvenc::get_defaults(settings, codec, context);
|
||||
|
||||
@@ -95,7 +101,7 @@ void obsffmpeg::ui::nvenc_h264_handler::get_defaults(obs_data_t* settings, const
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_h264_handler::get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||
AVCodecContext* context)
|
||||
AVCodecContext* context, bool)
|
||||
{
|
||||
if (!context) {
|
||||
this->get_encoder_properties(props, codec);
|
||||
|
||||
@@ -36,11 +36,14 @@ namespace obsffmpeg {
|
||||
public:
|
||||
virtual void override_visible_name(const AVCodec* codec, std::string& name) override;
|
||||
|
||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
virtual void override_lag_in_frames(size_t& lag, obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
|
||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context,
|
||||
bool hw_encode) override;
|
||||
|
||||
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
AVCodecContext* context, bool hw_encode) override;
|
||||
|
||||
virtual void update(obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
|
||||
@@ -87,8 +87,14 @@ void obsffmpeg::ui::nvenc_hevc_handler::override_visible_name(const AVCodec*, st
|
||||
name = "H.265/HEVC Encoder (NVidia NVENC)";
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_hevc_handler::override_lag_in_frames(size_t& lag, obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context)
|
||||
{
|
||||
nvenc::override_lag_in_frames(lag, settings, codec, context);
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_hevc_handler::get_defaults(obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context)
|
||||
AVCodecContext* context, bool)
|
||||
{
|
||||
nvenc::get_defaults(settings, codec, context);
|
||||
|
||||
@@ -98,7 +104,7 @@ void obsffmpeg::ui::nvenc_hevc_handler::get_defaults(obs_data_t* settings, const
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_hevc_handler::get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||
AVCodecContext* context)
|
||||
AVCodecContext* context, bool)
|
||||
{
|
||||
if (!context) {
|
||||
this->get_encoder_properties(props, codec);
|
||||
|
||||
@@ -36,11 +36,14 @@ namespace obsffmpeg {
|
||||
public:
|
||||
virtual void override_visible_name(const AVCodec* codec, std::string& name) override;
|
||||
|
||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
virtual void override_lag_in_frames(size_t& lag, obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
|
||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context,
|
||||
bool hw_encode) override;
|
||||
|
||||
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
AVCodecContext* context, bool hw_encode) override;
|
||||
|
||||
virtual void update(obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
// SOFTWARE.
|
||||
|
||||
#include "nvenc_shared.hpp"
|
||||
#include <algorithm>
|
||||
#include "codecs/hevc.hpp"
|
||||
#include "plugin.hpp"
|
||||
#include "strings.hpp"
|
||||
@@ -131,6 +132,19 @@ std::map<b_ref_mode, std::string> obsffmpeg::nvenc::b_ref_mode_to_opt{
|
||||
{b_ref_mode::MIDDLE, "middle"},
|
||||
};
|
||||
|
||||
void obsffmpeg::nvenc::override_lag_in_frames(size_t& lag, obs_data_t*, const AVCodec*, AVCodecContext* context)
|
||||
{
|
||||
// With NVENC, the number of frames lagged before we get our first
|
||||
// packet is determined by the number of b-frames. Threads, lookahead
|
||||
// frames and various other settings are ignored.
|
||||
// The minimum number of lagged frames is 1.
|
||||
|
||||
int64_t rcla = 0;
|
||||
av_opt_get_int(context, "rc-lookahead", AV_OPT_SEARCH_CHILDREN, &rcla);
|
||||
|
||||
lag = static_cast<size_t>(std::max(1ll + static_cast<int64_t>(context->max_b_frames), rcla));
|
||||
}
|
||||
|
||||
void obsffmpeg::nvenc::get_defaults(obs_data_t* settings, const AVCodec*, AVCodecContext*)
|
||||
{
|
||||
obs_data_set_default_int(settings, ST_PRESET, static_cast<int64_t>(preset::DEFAULT));
|
||||
@@ -678,7 +692,7 @@ void obsffmpeg::nvenc::log_options(obs_data_t* settings, const AVCodec* codec, A
|
||||
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);
|
||||
double_t cfg_rc_quality_tgt = obs_data_get_double(settings, ST_RATECONTROL_QUALITY_TARGET) / 100.0 * 51.0;
|
||||
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);
|
||||
|
||||
@@ -75,6 +75,9 @@ namespace obsffmpeg {
|
||||
|
||||
extern std::map<b_ref_mode, std::string> b_ref_mode_to_opt;
|
||||
|
||||
void override_lag_in_frames(size_t& lag, obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context);
|
||||
|
||||
void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||
|
||||
void get_properties_pre(obs_properties_t* props, const AVCodec* codec);
|
||||
|
||||
@@ -36,7 +36,7 @@ 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)
|
||||
const AVCodec* codec, AVCodecContext*)
|
||||
{
|
||||
std::string profile = "";
|
||||
|
||||
@@ -60,13 +60,13 @@ void obsffmpeg::ui::prores_aw_handler::override_colorformat(AVPixelFormat& targe
|
||||
}
|
||||
}
|
||||
|
||||
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*, bool)
|
||||
{
|
||||
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,
|
||||
AVCodecContext* context)
|
||||
AVCodecContext* context, bool)
|
||||
{
|
||||
if (!context) {
|
||||
auto p = obs_properties_add_list(props, P_PRORES_PROFILE, TRANSLATE(P_PRORES_PROFILE),
|
||||
@@ -105,7 +105,7 @@ void obsffmpeg::ui::prores_aw_handler::update(obs_data_t* settings, const AVCode
|
||||
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)
|
||||
void obsffmpeg::ui::prores_aw_handler::log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext*)
|
||||
{
|
||||
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)))
|
||||
@@ -113,7 +113,7 @@ void obsffmpeg::ui::prores_aw_handler::log_options(obs_data_t* settings, const A
|
||||
}
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::prores_aw_handler::process_avpacket(AVPacket& packet, const AVCodec* codec, AVCodecContext* context)
|
||||
void obsffmpeg::ui::prores_aw_handler::process_avpacket(AVPacket& packet, const AVCodec*, AVCodecContext*)
|
||||
{
|
||||
//FFmpeg Bug:
|
||||
// When ProRes content is stored in Matroska, FFmpeg strips the size
|
||||
|
||||
@@ -38,10 +38,10 @@ namespace obsffmpeg {
|
||||
const AVCodec* codec, AVCodecContext* context) override;
|
||||
|
||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
AVCodecContext* context, bool hw_encode) override;
|
||||
|
||||
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
AVCodecContext* context, bool hw_encode) override;
|
||||
|
||||
virtual void update(obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
|
||||
Reference in New Issue
Block a user