diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui index 548e31e0c..fedd3a751 100644 --- a/UI/forms/OBSBasicSettings.ui +++ b/UI/forms/OBSBasicSettings.ui @@ -4809,6 +4809,11 @@ + + + sRGB + + 709 diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 382a4cab6..76d759e48 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -1383,7 +1383,7 @@ bool OBSBasic::InitBasicConfigDefaults() config_set_default_uint(basicConfig, "Video", "FPSDen", 1); config_set_default_string(basicConfig, "Video", "ScaleType", "bicubic"); config_set_default_string(basicConfig, "Video", "ColorFormat", "NV12"); - config_set_default_string(basicConfig, "Video", "ColorSpace", "601"); + config_set_default_string(basicConfig, "Video", "ColorSpace", "sRGB"); config_set_default_string(basicConfig, "Video", "ColorRange", "Partial"); @@ -3792,8 +3792,11 @@ int OBSBasic::ResetVideo() ovi.output_height = (uint32_t)config_get_uint(basicConfig, "Video", "OutputCY"); ovi.output_format = GetVideoFormatFromName(colorFormat); - ovi.colorspace = astrcmpi(colorSpace, "601") == 0 ? VIDEO_CS_601 - : VIDEO_CS_709; + ovi.colorspace = astrcmpi(colorSpace, "601") == 0 + ? VIDEO_CS_601 + : (astrcmpi(colorSpace, "709") == 0 + ? VIDEO_CS_709 + : VIDEO_CS_SRGB); ovi.range = astrcmpi(colorRange, "Full") == 0 ? VIDEO_RANGE_FULL : VIDEO_RANGE_PARTIAL; ovi.adapter = diff --git a/deps/media-playback/media-playback/media.c b/deps/media-playback/media-playback/media.c index 806186bf2..e1f584e5b 100644 --- a/deps/media-playback/media-playback/media.c +++ b/deps/media-playback/media-playback/media.c @@ -109,9 +109,15 @@ static inline enum speaker_layout convert_speaker_layout(uint8_t channels) } } -static inline enum video_colorspace convert_color_space(enum AVColorSpace s) +static inline enum video_colorspace +convert_color_space(enum AVColorSpace s, enum AVColorTransferCharacteristic trc) { - return s == AVCOL_SPC_BT709 ? VIDEO_CS_709 : VIDEO_CS_DEFAULT; + if (s == AVCOL_SPC_BT709) { + return (trc == AVCOL_TRC_IEC61966_2_1) ? VIDEO_CS_SRGB + : VIDEO_CS_709; + } + + return VIDEO_CS_DEFAULT; } static inline enum video_range_type convert_color_range(enum AVColorRange r) @@ -372,7 +378,7 @@ static void mp_media_next_video(mp_media_t *m, bool preload) frame->data[0] -= frame->linesize[0] * (f->height - 1); new_format = convert_pixel_format(m->scale_format); - new_space = convert_color_space(f->colorspace); + new_space = convert_color_space(f->colorspace, f->color_trc); new_range = m->force_range == VIDEO_RANGE_DEFAULT ? convert_color_range(f->color_range) : m->force_range; diff --git a/libobs/media-io/video-io.h b/libobs/media-io/video-io.h index fc2a3cb98..6db887ded 100644 --- a/libobs/media-io/video-io.h +++ b/libobs/media-io/video-io.h @@ -176,9 +176,10 @@ static inline const char *get_video_colorspace_name(enum video_colorspace cs) switch (cs) { case VIDEO_CS_709: return "709"; + case VIDEO_CS_SRGB: + return "sRGB"; case VIDEO_CS_601: - case VIDEO_CS_DEFAULT: - case VIDEO_CS_SRGB:; + case VIDEO_CS_DEFAULT:; } return "601"; diff --git a/libobs/media-io/video-matrices.c b/libobs/media-io/video-matrices.c index 8def5f7f7..ec603f799 100644 --- a/libobs/media-io/video-matrices.c +++ b/libobs/media-io/video-matrices.c @@ -173,6 +173,8 @@ bool video_format_get_parameters(enum video_colorspace color_space, #endif if (color_space == VIDEO_CS_DEFAULT) color_space = VIDEO_CS_601; + else if (color_space == VIDEO_CS_SRGB) + color_space = VIDEO_CS_709; for (size_t i = 0; i < NUM_FORMATS; i++) { if (format_info[i].color_space != color_space) diff --git a/libobs/media-io/video-scaler-ffmpeg.c b/libobs/media-io/video-scaler-ffmpeg.c index 5dd6e7dd6..4d2d997f5 100644 --- a/libobs/media-io/video-scaler-ffmpeg.c +++ b/libobs/media-io/video-scaler-ffmpeg.c @@ -92,12 +92,11 @@ static inline int get_ffmpeg_scale_type(enum video_scale_type type) static inline const int *get_ffmpeg_coeffs(enum video_colorspace cs) { switch (cs) { - case VIDEO_CS_DEFAULT: - return sws_getCoefficients(SWS_CS_ITU601); - case VIDEO_CS_601: - return sws_getCoefficients(SWS_CS_ITU601); case VIDEO_CS_709: + case VIDEO_CS_SRGB: return sws_getCoefficients(SWS_CS_ITU709); + case VIDEO_CS_DEFAULT: + case VIDEO_CS_601: default: return sws_getCoefficients(SWS_CS_ITU601); } diff --git a/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c b/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c index 8148e0eab..a7063d858 100644 --- a/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c +++ b/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c @@ -79,6 +79,10 @@ struct main_params { int height; int fps_num; int fps_den; + int color_primaries; + int color_trc; + int colorspace; + int color_range; char *acodec; char *muxer_settings; }; @@ -249,6 +253,18 @@ static bool init_params(int *argc, char ***argv, struct main_params *params, return false; if (!get_opt_int(argc, argv, ¶ms->height, "video height")) return false; + if (!get_opt_int(argc, argv, ¶ms->color_primaries, + "video color primaries")) + return false; + if (!get_opt_int(argc, argv, ¶ms->color_trc, + "video color trc")) + return false; + if (!get_opt_int(argc, argv, ¶ms->colorspace, + "video colorspace")) + return false; + if (!get_opt_int(argc, argv, ¶ms->color_range, + "video color range")) + return false; if (!get_opt_int(argc, argv, ¶ms->fps_num, "video fps num")) return false; if (!get_opt_int(argc, argv, ¶ms->fps_den, "video fps den")) @@ -327,6 +343,10 @@ static void create_video_stream(struct ffmpeg_mux *ffm) context->height = ffm->params.height; context->coded_width = ffm->params.width; context->coded_height = ffm->params.height; + context->color_primaries = ffm->params.color_primaries; + context->color_trc = ffm->params.color_trc; + context->colorspace = ffm->params.colorspace; + context->color_range = ffm->params.color_range; context->extradata = extradata; context->extradata_size = ffm->video_header.size; context->time_base = diff --git a/plugins/obs-ffmpeg/jim-nvenc.c b/plugins/obs-ffmpeg/jim-nvenc.c index 58561158b..097db6432 100644 --- a/plugins/obs-ffmpeg/jim-nvenc.c +++ b/plugins/obs-ffmpeg/jim-nvenc.c @@ -429,9 +429,25 @@ static bool init_encoder(struct nvenc_data *enc, obs_data_t *settings) vui_params->videoSignalTypePresentFlag = 1; vui_params->videoFullRangeFlag = (voi->range == VIDEO_RANGE_FULL); vui_params->colourDescriptionPresentFlag = 1; - vui_params->colourMatrix = (voi->colorspace == VIDEO_CS_709) ? 1 : 5; - vui_params->colourPrimaries = 1; - vui_params->transferCharacteristics = 1; + + switch (voi->colorspace) { + case VIDEO_CS_DEFAULT: + case VIDEO_CS_601: + vui_params->colourPrimaries = 6; + vui_params->transferCharacteristics = 6; + vui_params->colourMatrix = 6; + break; + case VIDEO_CS_709: + vui_params->colourPrimaries = 1; + vui_params->transferCharacteristics = 1; + vui_params->colourMatrix = 1; + break; + case VIDEO_CS_SRGB: + vui_params->colourPrimaries = 1; + vui_params->transferCharacteristics = 13; + vui_params->colourMatrix = 1; + break; + } enc->bframes = bf > 0; diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-mux.c b/plugins/obs-ffmpeg/obs-ffmpeg-mux.c index 72945c05b..1ff745d61 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-mux.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-mux.c @@ -156,10 +156,37 @@ static void add_video_encoder_params(struct ffmpeg_muxer *stream, obs_data_release(settings); - dstr_catf(cmd, "%s %d %d %d %d %d ", obs_encoder_get_codec(vencoder), - bitrate, obs_output_get_width(stream->output), - obs_output_get_height(stream->output), (int)info->fps_num, - (int)info->fps_den); + enum AVColorPrimaries pri = AVCOL_PRI_UNSPECIFIED; + enum AVColorTransferCharacteristic trc = AVCOL_TRC_UNSPECIFIED; + enum AVColorSpace spc = AVCOL_SPC_UNSPECIFIED; + switch (info->colorspace) { + case VIDEO_CS_DEFAULT: + case VIDEO_CS_601: + pri = AVCOL_PRI_SMPTE170M; + trc = AVCOL_TRC_SMPTE170M; + spc = AVCOL_SPC_SMPTE170M; + break; + case VIDEO_CS_709: + pri = AVCOL_PRI_BT709; + trc = AVCOL_TRC_BT709; + spc = AVCOL_SPC_BT709; + break; + case VIDEO_CS_SRGB: + pri = AVCOL_PRI_BT709; + trc = AVCOL_TRC_IEC61966_2_1; + spc = AVCOL_SPC_BT709; + break; + } + + const enum AVColorRange range = (info->range == VIDEO_RANGE_FULL) + ? AVCOL_RANGE_JPEG + : AVCOL_RANGE_MPEG; + + dstr_catf(cmd, "%s %d %d %d %d %d %d %d %d %d ", + obs_encoder_get_codec(vencoder), bitrate, + obs_output_get_width(stream->output), + obs_output_get_height(stream->output), (int)pri, (int)trc, + (int)spc, (int)range, (int)info->fps_num, (int)info->fps_den); } static void add_audio_encoder_params(struct dstr *cmd, obs_encoder_t *aencoder) diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c b/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c index 142e14d2c..74480a797 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c @@ -235,14 +235,30 @@ static bool nvenc_update(void *data, obs_data_t *settings) enc->context->height = obs_encoder_get_height(enc->encoder); enc->context->time_base = (AVRational){voi->fps_den, voi->fps_num}; enc->context->pix_fmt = obs_to_ffmpeg_video_format(info.format); - enc->context->colorspace = info.colorspace == VIDEO_CS_709 - ? AVCOL_SPC_BT709 - : AVCOL_SPC_BT470BG; enc->context->color_range = info.range == VIDEO_RANGE_FULL ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; enc->context->max_b_frames = bf; + switch (info.colorspace) { + case VIDEO_CS_DEFAULT: + case VIDEO_CS_601: + enc->context->color_trc = AVCOL_TRC_SMPTE170M; + enc->context->color_primaries = AVCOL_PRI_SMPTE170M; + enc->context->colorspace = AVCOL_SPC_SMPTE170M; + break; + case VIDEO_CS_709: + enc->context->color_trc = AVCOL_TRC_BT709; + enc->context->color_primaries = AVCOL_PRI_BT709; + enc->context->colorspace = AVCOL_SPC_BT709; + break; + case VIDEO_CS_SRGB: + enc->context->color_trc = AVCOL_TRC_IEC61966_2_1; + enc->context->color_primaries = AVCOL_PRI_BT709; + enc->context->colorspace = AVCOL_SPC_BT709; + break; + } + if (keyint_sec) enc->context->gop_size = keyint_sec * voi->fps_num / voi->fps_den; diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-output.c b/plugins/obs-ffmpeg/obs-ffmpeg-output.c index 50f046254..f56a147c7 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-output.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-output.c @@ -169,8 +169,10 @@ static bool open_video_codec(struct ffmpeg_data *data) data->vframe->format = context->pix_fmt; data->vframe->width = context->width; data->vframe->height = context->height; - data->vframe->colorspace = data->config.color_space; data->vframe->color_range = data->config.color_range; + data->vframe->color_primaries = data->config.color_primaries; + data->vframe->color_trc = data->config.color_trc; + data->vframe->colorspace = data->config.colorspace; ret = av_frame_get_buffer(data->vframe, base_get_alignment()); if (ret < 0) { @@ -233,8 +235,10 @@ static bool create_video_stream(struct ffmpeg_data *data) context->time_base = (AVRational){ovi.fps_den, ovi.fps_num}; context->gop_size = data->config.gop_size; context->pix_fmt = closest_format; - context->colorspace = data->config.color_space; context->color_range = data->config.color_range; + context->color_primaries = data->config.color_primaries; + context->color_trc = data->config.color_trc; + context->colorspace = data->config.colorspace; context->thread_count = 0; data->video->time_base = context->time_base; @@ -1081,16 +1085,39 @@ static bool try_connect(struct ffmpeg_output *output) config.audio_tracks = (int)obs_output_get_mixers(output->output); config.audio_mix_count = get_audio_mix_count(config.audio_tracks); + config.color_range = voi->range == VIDEO_RANGE_FULL ? AVCOL_RANGE_JPEG + : AVCOL_RANGE_MPEG; + switch (voi->colorspace) { + case VIDEO_CS_DEFAULT: + case VIDEO_CS_601: + config.color_primaries = AVCOL_PRI_SMPTE170M; + config.color_trc = AVCOL_TRC_SMPTE170M; + break; + case VIDEO_CS_709: + config.color_primaries = AVCOL_PRI_BT709; + config.color_trc = AVCOL_TRC_BT709; + break; + case VIDEO_CS_SRGB: + config.color_primaries = AVCOL_PRI_BT709; + config.color_trc = AVCOL_TRC_IEC61966_2_1; + break; + } + if (format_is_yuv(voi->format)) { - config.color_range = voi->range == VIDEO_RANGE_FULL - ? AVCOL_RANGE_JPEG - : AVCOL_RANGE_MPEG; - config.color_space = voi->colorspace == VIDEO_CS_709 - ? AVCOL_SPC_BT709 - : AVCOL_SPC_BT470BG; + switch (voi->colorspace) { + case VIDEO_CS_DEFAULT: + case VIDEO_CS_601: + config.colorspace = AVCOL_SPC_SMPTE170M; + break; + case VIDEO_CS_709: + config.colorspace = AVCOL_SPC_BT709; + break; + case VIDEO_CS_SRGB: + config.colorspace = AVCOL_SPC_BT709; + break; + } } else { - config.color_range = AVCOL_RANGE_UNSPECIFIED; - config.color_space = AVCOL_SPC_RGB; + config.colorspace = AVCOL_SPC_RGB; } if (config.format == AV_PIX_FMT_NONE) { diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-output.h b/plugins/obs-ffmpeg/obs-ffmpeg-output.h index f608d68aa..99f4dcd13 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-output.h +++ b/plugins/obs-ffmpeg/obs-ffmpeg-output.h @@ -23,7 +23,9 @@ struct ffmpeg_cfg { int audio_tracks; enum AVPixelFormat format; enum AVColorRange color_range; - enum AVColorSpace color_space; + enum AVColorPrimaries color_primaries; + enum AVColorTransferCharacteristic color_trc; + enum AVColorSpace colorspace; int scale_width; int scale_height; int width; diff --git a/plugins/obs-x264/obs-x264.c b/plugins/obs-x264/obs-x264.c index 2db39408c..5e8e40f56 100644 --- a/plugins/obs-x264/obs-x264.c +++ b/plugins/obs-x264/obs-x264.c @@ -362,23 +362,9 @@ static void log_x264(void *param, int level, const char *format, va_list args) UNUSED_PARAMETER(level); } -static inline const char *get_x264_colorspace_name(enum video_colorspace cs) -{ - switch (cs) { - case VIDEO_CS_DEFAULT: - case VIDEO_CS_601: - case VIDEO_CS_SRGB: - return "undef"; - case VIDEO_CS_709:; - } - - return "bt709"; -} - -static inline int get_x264_cs_val(enum video_colorspace cs, +static inline int get_x264_cs_val(const char *const name, const char *const names[]) { - const char *name = get_x264_colorspace_name(cs); int idx = 0; do { if (strcmp(names[idx], name) == 0) @@ -481,13 +467,38 @@ static void update_params(struct obs_x264 *obsx264, obs_data_t *settings, if (obs_data_has_user_value(settings, "bf")) obsx264->params.i_bframe = bf; - obsx264->params.vui.i_transfer = - get_x264_cs_val(info.colorspace, x264_transfer_names); - obsx264->params.vui.i_colmatrix = - get_x264_cs_val(info.colorspace, x264_colmatrix_names); - obsx264->params.vui.i_colorprim = - get_x264_cs_val(info.colorspace, x264_colorprim_names); + static const char *const smpte170m = "smpte170m"; + static const char *const bt709 = "bt709"; + static const char *const iec61966_2_1 = "iec61966-2-1"; + const char *colorprim = NULL; + const char *transfer = NULL; + const char *colmatrix = NULL; + switch (info.colorspace) { + case VIDEO_CS_DEFAULT: + case VIDEO_CS_601: + colorprim = smpte170m; + transfer = smpte170m; + colmatrix = smpte170m; + break; + case VIDEO_CS_709: + colorprim = bt709; + transfer = bt709; + colmatrix = bt709; + break; + case VIDEO_CS_SRGB: + colorprim = bt709; + transfer = iec61966_2_1; + colmatrix = bt709; + break; + } + obsx264->params.vui.b_fullrange = info.range == VIDEO_RANGE_FULL; + obsx264->params.vui.i_colorprim = + get_x264_cs_val(colorprim, x264_colorprim_names); + obsx264->params.vui.i_transfer = + get_x264_cs_val(transfer, x264_transfer_names); + obsx264->params.vui.i_colmatrix = + get_x264_cs_val(colmatrix, x264_colmatrix_names); /* use the new filler method for CBR to allow real-time adjusting of * the bitrate */