mirror of
https://github.com/Xaymar/obs-StreamFX
synced 2024-11-10 22:05:06 +00:00
ffmpeg/tools: Set correct information when encoding
Improves the previous logic and makes it compatible with the new additions in 26.x, such as sRGB. This was previously broken as the focus was on existing features which could be tested without requiring a compiler to be installed. Incorrect understanding of how sRGB works with RGB and YCC/YUV formats also caused sRGB to be treated as RGB when I444 was selected. This should also now be fixed, hopefully permanently. Fixes #331
This commit is contained in:
parent
e8d5edc9eb
commit
9938422d5c
4 changed files with 100 additions and 82 deletions
|
@ -32,6 +32,7 @@
|
|||
|
||||
// Common C++ includes
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
|
|
@ -439,16 +439,18 @@ void ffmpeg_instance::initialize_sw(obs_data_t* settings)
|
|||
}
|
||||
}
|
||||
|
||||
_context->width = static_cast<int>(obs_encoder_get_width(_self));
|
||||
_context->height = static_cast<int>(obs_encoder_get_height(_self));
|
||||
::ffmpeg::tools::setup_obs_color(voi->colorspace, voi->range, _context);
|
||||
// Setup from OBS information.
|
||||
::ffmpeg::tools::context_setup_from_obs(voi, _context);
|
||||
|
||||
_context->pix_fmt = _pixfmt_target;
|
||||
_context->field_order = AV_FIELD_PROGRESSIVE;
|
||||
_context->ticks_per_frame = 1;
|
||||
_context->sample_aspect_ratio.num = _context->sample_aspect_ratio.den = 1;
|
||||
_context->framerate.num = _context->time_base.den = static_cast<int>(voi->fps_num);
|
||||
_context->framerate.den = _context->time_base.num = static_cast<int>(voi->fps_den);
|
||||
// Override with other information.
|
||||
_context->width = static_cast<int>(obs_encoder_get_width(_self));
|
||||
_context->height = static_cast<int>(obs_encoder_get_height(_self));
|
||||
_context->pix_fmt = _pixfmt_target;
|
||||
|
||||
// Prevent pixelation by sampling "center" instead of corners. This creates
|
||||
// a smoother look, which may not be H.264/AVC standard compliant, however it
|
||||
// provides better support for scaling algorithms, such as Bicubic.
|
||||
_context->chroma_sample_location = AVCHROMA_LOC_CENTER;
|
||||
|
||||
_scaler.set_source_size(static_cast<uint32_t>(_context->width), static_cast<uint32_t>(_context->height));
|
||||
_scaler.set_source_color(_context->color_range == AVCOL_RANGE_JPEG, _context->colorspace);
|
||||
|
@ -473,36 +475,37 @@ void ffmpeg_instance::initialize_sw(obs_data_t* settings)
|
|||
|
||||
void ffmpeg_instance::initialize_hw(obs_data_t*)
|
||||
{
|
||||
#ifdef D_PLATFORM_WINDOWS
|
||||
#ifndef D_PLATFORM_WINDOWS
|
||||
throw std::runtime_error("OBS Studio currently does not support zero copy encoding for this platform.");
|
||||
#else
|
||||
// Initialize Video Encoding
|
||||
auto voi = video_output_get_info(obs_encoder_video(_self));
|
||||
const video_output_info* voi = video_output_get_info(obs_encoder_video(_self));
|
||||
|
||||
_context->width = static_cast<int>(voi->width);
|
||||
_context->height = static_cast<int>(voi->height);
|
||||
_context->field_order = AV_FIELD_PROGRESSIVE;
|
||||
_context->ticks_per_frame = 1;
|
||||
_context->sample_aspect_ratio.num = _context->sample_aspect_ratio.den = 1;
|
||||
_context->framerate.num = _context->time_base.den = static_cast<int>(voi->fps_num);
|
||||
_context->framerate.den = _context->time_base.num = static_cast<int>(voi->fps_den);
|
||||
::ffmpeg::tools::setup_obs_color(voi->colorspace, voi->range, _context);
|
||||
_context->sw_pix_fmt = ::ffmpeg::tools::obs_videoformat_to_avpixelformat(voi->format);
|
||||
// Apply pixel format settings.
|
||||
::ffmpeg::tools::context_setup_from_obs(voi, _context);
|
||||
_context->sw_pix_fmt = _context->pix_fmt;
|
||||
_context->pix_fmt = AV_PIX_FMT_D3D11;
|
||||
|
||||
// Try to create a hardware context.
|
||||
_context->hw_device_ctx = _hwinst->create_device_context();
|
||||
_context->hw_frames_ctx = av_hwframe_ctx_alloc(_context->hw_device_ctx);
|
||||
if (!_context->hw_frames_ctx)
|
||||
throw std::runtime_error("Allocating hardware context failed, chosen pixel format is likely not supported.");
|
||||
if (!_context->hw_frames_ctx) {
|
||||
throw std::runtime_error("Creating hardware context failed.");
|
||||
}
|
||||
|
||||
// Initialize Hardware Context
|
||||
AVHWFramesContext* ctx = reinterpret_cast<AVHWFramesContext*>(_context->hw_frames_ctx->data);
|
||||
ctx->width = _context->width;
|
||||
ctx->height = _context->height;
|
||||
ctx->format = _context->pix_fmt;
|
||||
ctx->sw_format = _context->sw_pix_fmt;
|
||||
|
||||
if (av_hwframe_ctx_init(_context->hw_frames_ctx) < 0)
|
||||
throw std::runtime_error("Initializing hardware context failed, chosen pixel format is likely not supported.");
|
||||
#else
|
||||
throw std::runtime_error("OBS Studio currently does not support zero copy encoding for this platform.");
|
||||
if (int32_t res = av_hwframe_ctx_init(_context->hw_frames_ctx); res < 0) {
|
||||
std::array<char, 2048> buffer;
|
||||
size_t len = static_cast<size_t>(snprintf(buffer.data(), buffer.size(),
|
||||
"Initializing hardware context failed with error: %s (%" PRIu32 ")",
|
||||
::ffmpeg::tools::get_error_description(res), res));
|
||||
throw std::runtime_error(std::string(buffer.data(), buffer.data() + len));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -128,21 +128,7 @@ AVPixelFormat tools::get_least_lossy_format(const AVPixelFormat* haystack, AVPix
|
|||
return avcodec_find_best_pix_fmt_of_list(haystack, needle, 0, &data_loss);
|
||||
}
|
||||
|
||||
AVColorSpace tools::obs_videocolorspace_to_avcolorspace(video_colorspace v)
|
||||
{
|
||||
switch (v) {
|
||||
case VIDEO_CS_DEFAULT:
|
||||
case VIDEO_CS_709:
|
||||
return AVCOL_SPC_BT709;
|
||||
case VIDEO_CS_601:
|
||||
return AVCOL_SPC_BT470BG;
|
||||
case VIDEO_CS_SRGB:
|
||||
return AVCOL_SPC_RGB;
|
||||
}
|
||||
throw std::invalid_argument("unknown color space");
|
||||
}
|
||||
|
||||
AVColorRange tools::obs_videorangetype_to_avcolorrange(video_range_type v)
|
||||
AVColorRange tools::obs_to_av_color_range(video_range_type v)
|
||||
{
|
||||
switch (v) {
|
||||
case VIDEO_RANGE_DEFAULT:
|
||||
|
@ -151,7 +137,50 @@ AVColorRange tools::obs_videorangetype_to_avcolorrange(video_range_type v)
|
|||
case VIDEO_RANGE_FULL:
|
||||
return AVCOL_RANGE_JPEG;
|
||||
}
|
||||
throw std::invalid_argument("unknown range");
|
||||
throw std::invalid_argument("Unknown Color Range");
|
||||
}
|
||||
|
||||
AVColorSpace tools::obs_to_av_color_space(video_colorspace v)
|
||||
{
|
||||
switch (v) {
|
||||
case VIDEO_CS_601:
|
||||
return AVCOL_SPC_BT470BG;
|
||||
case VIDEO_CS_DEFAULT:
|
||||
case VIDEO_CS_709:
|
||||
case VIDEO_CS_SRGB:
|
||||
return AVCOL_SPC_BT709;
|
||||
default:
|
||||
throw std::invalid_argument("Unknown Color Space");
|
||||
}
|
||||
}
|
||||
|
||||
AVColorPrimaries ffmpeg::tools::obs_to_av_color_primary(video_colorspace v)
|
||||
{
|
||||
switch (v) {
|
||||
case VIDEO_CS_601:
|
||||
return AVCOL_PRI_BT470BG;
|
||||
case VIDEO_CS_DEFAULT:
|
||||
case VIDEO_CS_709:
|
||||
case VIDEO_CS_SRGB:
|
||||
return AVCOL_PRI_BT709;
|
||||
default:
|
||||
throw std::invalid_argument("Unknown Color Primaries");
|
||||
}
|
||||
}
|
||||
|
||||
AVColorTransferCharacteristic ffmpeg::tools::obs_to_av_color_transfer_characteristics(video_colorspace v)
|
||||
{
|
||||
switch (v) {
|
||||
case VIDEO_CS_601:
|
||||
return AVCOL_TRC_LINEAR;
|
||||
case VIDEO_CS_DEFAULT:
|
||||
case VIDEO_CS_709:
|
||||
return AVCOL_TRC_BT709;
|
||||
case VIDEO_CS_SRGB:
|
||||
return AVCOL_TRC_IEC61966_2_1;
|
||||
default:
|
||||
throw std::invalid_argument("Unknown Color Transfer Characteristics");
|
||||
}
|
||||
}
|
||||
|
||||
bool tools::can_hardware_encode(const AVCodec* codec)
|
||||
|
@ -204,43 +233,28 @@ std::vector<AVPixelFormat> tools::get_software_formats(const AVPixelFormat* list
|
|||
return std::move(fmts);
|
||||
}
|
||||
|
||||
void tools::setup_obs_color(video_colorspace colorspace, video_range_type range, AVCodecContext* context)
|
||||
void tools::context_setup_from_obs(const video_output_info* voi, AVCodecContext* context)
|
||||
{
|
||||
std::map<video_colorspace, std::tuple<AVColorSpace, AVColorPrimaries, AVColorTransferCharacteristic>> colorspaces =
|
||||
{
|
||||
{VIDEO_CS_601, {AVCOL_SPC_BT470BG, AVCOL_PRI_BT470BG, AVCOL_TRC_SMPTE170M}},
|
||||
{VIDEO_CS_709, {AVCOL_SPC_BT709, AVCOL_PRI_BT709, AVCOL_TRC_BT709}},
|
||||
{VIDEO_CS_SRGB, {AVCOL_SPC_RGB, AVCOL_PRI_BT709, AVCOL_TRC_IEC61966_2_1}},
|
||||
};
|
||||
std::map<video_range_type, AVColorRange> colorranges = {
|
||||
{VIDEO_RANGE_PARTIAL, AVCOL_RANGE_MPEG},
|
||||
{VIDEO_RANGE_FULL, AVCOL_RANGE_JPEG},
|
||||
};
|
||||
// Resolution
|
||||
context->width = static_cast<int>(voi->width);
|
||||
context->height = static_cast<int>(voi->height);
|
||||
|
||||
{
|
||||
if (colorspace == VIDEO_CS_DEFAULT)
|
||||
colorspace = VIDEO_CS_601;
|
||||
if (range == VIDEO_RANGE_DEFAULT)
|
||||
range = VIDEO_RANGE_PARTIAL;
|
||||
}
|
||||
// Framerate
|
||||
context->ticks_per_frame = 1;
|
||||
context->framerate.num = context->time_base.den = static_cast<int>(voi->fps_num);
|
||||
context->framerate.den = context->time_base.num = static_cast<int>(voi->fps_den);
|
||||
|
||||
{
|
||||
auto found = colorspaces.find(colorspace);
|
||||
if (found != colorspaces.end()) {
|
||||
context->colorspace = std::get<AVColorSpace>(found->second);
|
||||
context->color_primaries = std::get<AVColorPrimaries>(found->second);
|
||||
context->color_trc = std::get<AVColorTransferCharacteristic>(found->second);
|
||||
}
|
||||
}
|
||||
{
|
||||
auto found = colorranges.find(range);
|
||||
if (found != colorranges.end()) {
|
||||
context->color_range = found->second;
|
||||
}
|
||||
}
|
||||
// Aspect Ratio, Progressive
|
||||
context->sample_aspect_ratio.num = 1;
|
||||
context->sample_aspect_ratio.den = 1;
|
||||
context->field_order = AV_FIELD_PROGRESSIVE;
|
||||
|
||||
// Downscaling should result in downscaling, not pixelation
|
||||
context->chroma_sample_location = AVCHROMA_LOC_CENTER;
|
||||
// Decipher Pixel information
|
||||
context->pix_fmt = obs_videoformat_to_avpixelformat(voi->format);
|
||||
context->color_range = obs_to_av_color_range(voi->range);
|
||||
context->colorspace = obs_to_av_color_space(voi->colorspace);
|
||||
context->color_primaries = obs_to_av_color_primary(voi->colorspace);
|
||||
context->color_trc = obs_to_av_color_transfer_characteristics(voi->colorspace);
|
||||
}
|
||||
|
||||
const char* tools::get_std_compliance_name(int compliance)
|
||||
|
|
|
@ -43,20 +43,20 @@ namespace ffmpeg::tools {
|
|||
const char* get_error_description(int error);
|
||||
|
||||
AVPixelFormat obs_videoformat_to_avpixelformat(video_format v);
|
||||
|
||||
video_format avpixelformat_to_obs_videoformat(AVPixelFormat v);
|
||||
video_format avpixelformat_to_obs_videoformat(AVPixelFormat v);
|
||||
|
||||
AVPixelFormat get_least_lossy_format(const AVPixelFormat* haystack, AVPixelFormat needle);
|
||||
|
||||
AVColorSpace obs_videocolorspace_to_avcolorspace(video_colorspace v);
|
||||
|
||||
AVColorRange obs_videorangetype_to_avcolorrange(video_range_type v);
|
||||
AVColorRange obs_to_av_color_range(video_range_type v);
|
||||
AVColorSpace obs_to_av_color_space(video_colorspace v);
|
||||
AVColorPrimaries obs_to_av_color_primary(video_colorspace v);
|
||||
AVColorTransferCharacteristic obs_to_av_color_transfer_characteristics(video_colorspace v);
|
||||
|
||||
bool can_hardware_encode(const AVCodec* codec);
|
||||
|
||||
std::vector<AVPixelFormat> get_software_formats(const AVPixelFormat* list);
|
||||
|
||||
void setup_obs_color(video_colorspace colorspace, video_range_type range, AVCodecContext* context);
|
||||
void context_setup_from_obs(const video_output_info* voi, AVCodecContext* context);
|
||||
|
||||
const char* get_std_compliance_name(int compliance);
|
||||
|
||||
|
|
Loading…
Reference in a new issue