Video: Save colorspace metadata based on display, remove HDR option

Now that there are Rec.2100 PQ and HLG displays, the additional HDR option
for video export is redundant. Typically you would now select a HDR display
early on and do all your video editing with it enabled.

For saving a HDR video, the encoding panel will now show the name of the color
space, and warn when the video codec or color depth is incompatible.

Since this is now based on interop IDs for the dislpay color spaces, we can
map more of those to the appropriate CICP code. This works fine for Display P3,
in my tests it looks identical to sRGB except that the wide gamut colors are
preserved.

However Rec.1886 and Rec.2020 are problematic regarding the transfer function,
although the latter at least has the correct primaries now. So it should be
a net improvement and this could be looked at later if anyone wants.

---

Background:

* Rec.1886 and Rec.2020 display color spaces in Blender use gamma 2.4.
* BT.709 trc is almost the same as gamma 2.4, so seems like the correct choice.
* We already write sRGB with BT.709 trc, which seems wrong.
* Yet sRGB matches exactly between Blender display and QuickTime, while
  Rec.1886 and Rec.2020 do not.
* Display P3 with BT.709 trc matches sRGB with BT.709 trc, just adding the wide
  gamut colors. So that is what is used for now. Also using the sRGB trc the
  file is not recognized by QuickTime.

There is apparently a well known "QuickTime gamma shift" issue, where the
interpretation of the BT.709 trc is different than other platforms. And you need
to do workarounds like writing gamma 2.4 metadata outside of CICP to get
things to display properly on macOS.

Not that QuickTime is necessarily the reference we should target, but just to
explain that changing the previous behavior would have consequences, and so
it this commit leaves that unchanged.

Pull Request: https://projects.blender.org/blender/blender/pulls/145373
This commit is contained in:
Brecht Van Lommel 2025-08-29 01:29:57 +02:00
parent 66bf994590
commit 5cc6ad6afe
27 changed files with 227 additions and 270 deletions

View file

@ -536,12 +536,30 @@ class RENDER_PT_encoding_video(RenderOutputButtonsPanel, Panel):
if use_bpp:
layout.prop(image_settings, "color_depth", expand=True)
# HDR options.
use_hdr = needs_codec and ffmpeg.codec in {
'H265', 'AV1'} and image_settings.color_depth in {
'10', '12'} and image_settings.color_mode != 'BW'
if use_hdr:
layout.prop(ffmpeg, "video_hdr")
# Color space
if image_settings.color_management == 'OVERRIDE':
display_settings = image_settings.display_settings
view_settings = image_settings.view_settings
else:
display_settings = context.scene.display_settings
view_settings = context.scene.view_settings
split = layout.split(factor=0.4)
col = split.column()
col.alignment = 'RIGHT'
col.label(text="Color Space")
col = split.column()
row = col.row()
row.enabled = False
row.prop(display_settings, "display_device", text="")
# HDR compatibility
if view_settings.is_hdr:
if not needs_codec or ffmpeg.codec not in {'H265', 'AV1'}:
col.label(text="HDR needs H.265 or AV1", icon='ERROR')
elif image_settings.color_depth not in {'10', '12'}:
col.label(text="HDR needs 10 or 12 bits", icon='ERROR')
if ffmpeg.codec == 'DNXHD':
layout.prop(ffmpeg, "use_lossless_output")

View file

@ -62,18 +62,6 @@ const char *IMB_colormanagement_get_float_colorspace(const ImBuf *ibuf);
const char *IMB_colormanagement_get_rect_colorspace(const ImBuf *ibuf);
const char *IMB_colormanagement_space_from_filepath_rules(const char *filepath);
/* Get colorspace name used for Rec.2100 PQ Display conversion.
*
* Searches for one of the color spaces or aliases: Rec.2100-PQ, Rec.2100-PQ - Display, rec2100_pq,
* rec2100_pq_display. If none found returns nullptr. */
const char *IMB_colormanagement_get_rec2100_pq_display_colorspace();
/* Get colorspace name used for Rec.2100 HLG Display conversion.
*
* Searches for one of the color spaces or aliases: Rec.2100-HLG, Rec.2100-HLG - Display,
* rec2100_hlg, rec2100_hlg_display. If none found returns nullptr. */
const char *IMB_colormanagement_get_rec2100_hlg_display_colorspace();
const ColorSpace *IMB_colormanagement_space_get_named(const char *name);
bool IMB_colormanagement_space_is_data(const ColorSpace *colorspace);
bool IMB_colormanagement_space_is_scene_linear(const ColorSpace *colorspace);

View file

@ -1235,35 +1235,6 @@ const char *IMB_colormanagement_space_from_filepath_rules(const char *filepath)
return g_config->get_color_space_from_filepath(filepath);
}
static const char *get_first_resolved_colorspace_name(const blender::Span<const char *> names)
{
for (const char *name : names) {
const ColorSpace *colorspace = IMB_colormanagement_space_get_named(name);
if (colorspace) {
return colorspace->name().c_str();
}
}
return nullptr;
}
const char *IMB_colormanagement_get_rec2100_pq_display_colorspace()
{
return get_first_resolved_colorspace_name({"Rec.2100-PQ",
"Rec.2100-PQ - Display",
"rec2100_pq",
"rec2100_pq_display",
"pq_rec2020_display"});
}
const char *IMB_colormanagement_get_rec2100_hlg_display_colorspace()
{
return get_first_resolved_colorspace_name({"Rec.2100-HLG",
"Rec.2100-HLG - Display",
"rec2100_hlg",
"rec2100_hlg_display",
"hlg_rec2020_display"});
}
const ColorSpace *IMB_colormanagement_space_get_named(const char *name)
{
return g_config->get_color_space(name);

View file

@ -16,7 +16,6 @@
#include <sys/types.h>
#include "BLI_path_utils.hh"
#include "BLI_span.hh"
#include "BLI_string.h"
#include "BLI_string_utf8.h"
#include "BLI_task.hh"
@ -124,28 +123,56 @@ static void probe_video_colorspace(MovieReader *anim, char r_colorspace_name[IM_
#ifdef WITH_FFMPEG
const AVColorTransferCharacteristic color_trc = anim->pCodecCtx->color_trc;
const AVColorSpace colorspace = anim->pCodecCtx->colorspace;
const AVColorPrimaries color_primaries = anim->pCodecCtx->color_primaries;
if (color_trc == AVCOL_TRC_ARIB_STD_B67 && color_primaries == AVCOL_PRI_BT2020 &&
colorspace == AVCOL_SPC_BT2020_NCL)
/* ASWF Color Interop Forum defined display spaces. The CICP codes there match the enum
* values defined by ffmpeg. Keep in sync with movie_write.cc.
*
* Note that pCodecCtx->color_space is ignored because it is only about choice of YUV
* encoding for best compression, and ffmpeg will decode to RGB for us. */
blender::StringRefNull interop_id;
if (color_primaries == AVCOL_PRI_BT2020 && color_trc == AVCOL_TRC_SMPTEST2084) {
interop_id = "pq_rec2020_display";
}
else if (color_primaries == AVCOL_PRI_BT2020 && color_trc == AVCOL_TRC_ARIB_STD_B67) {
interop_id = "hlg_rec2020_display";
}
else if ((color_primaries == AVCOL_PRI_SMPTE432 && color_trc == AVCOL_TRC_IEC61966_2_1) ||
(color_primaries == AVCOL_PRI_SMPTE432 && color_trc == AVCOL_TRC_BT709))
{
const char *hlg_name = IMB_colormanagement_get_rec2100_hlg_display_colorspace();
if (hlg_name) {
BLI_strncpy_utf8(r_colorspace_name, hlg_name, IM_MAX_SPACE);
}
interop_id = "srgb_p3d65_display";
}
else if (color_primaries == AVCOL_PRI_SMPTE432 && color_trc == AVCOL_TRC_SMPTEST2084) {
interop_id = "pq_p3d65_display";
}
else if (color_primaries == AVCOL_PRI_SMPTE432 && color_trc == AVCOL_TRC_SMPTE428) {
interop_id = "g26_p3d65_display";
}
else if (color_primaries == AVCOL_PRI_BT709 && color_trc == AVCOL_TRC_GAMMA22) {
interop_id = "g22_rec709_display";
}
else if (color_primaries == AVCOL_PRI_BT2020 && color_trc == AVCOL_TRC_BT709) {
interop_id = "g24_rec2020_display";
}
else if (color_primaries == AVCOL_PRI_BT709 && color_trc == AVCOL_TRC_IEC61966_2_1) {
interop_id = "srgb_rec709_display";
}
else if (color_primaries == AVCOL_PRI_BT709 && color_trc == AVCOL_TRC_BT709) {
/* Arguably this should be g24_rec709_display, but we write sRGB like this. */
interop_id = "srgb_rec709_display";
}
if (interop_id.is_empty()) {
return;
}
const ColorSpace *colorspace = IMB_colormanagement_space_from_interop_id(interop_id);
if (colorspace == nullptr) {
return;
}
if (color_trc == AVCOL_TRC_SMPTEST2084 && color_primaries == AVCOL_PRI_BT2020 &&
colorspace == AVCOL_SPC_BT2020_NCL)
{
const char *pq_name = IMB_colormanagement_get_rec2100_pq_display_colorspace();
if (pq_name) {
BLI_strncpy_utf8(r_colorspace_name, pq_name, IM_MAX_SPACE);
}
return;
}
BLI_strncpy_utf8(
r_colorspace_name, IMB_colormanagement_colorspace_get_name(colorspace), IM_MAX_SPACE);
#endif /* WITH_FFMPEG */
}

View file

@ -212,7 +212,7 @@ static bool write_video_frame(MovieWriter *context, AVFrame *frame, ReportList *
*
* No color space conversion is performed. The result float buffer might be in a non-linear space
* denoted by the float_buffer.colorspace. */
static ImBuf *alloc_imbuf_for_hdr_transform(const ImBuf *input_ibuf)
static ImBuf *alloc_imbuf_for_colorspace_transform(const ImBuf *input_ibuf)
{
if (!input_ibuf) {
return nullptr;
@ -261,103 +261,22 @@ static ImBuf *alloc_imbuf_for_hdr_transform(const ImBuf *input_ibuf)
return result_ibuf;
}
static ImBuf *do_pq_transform(const ImBuf *input_ibuf)
{
ImBuf *ibuf = alloc_imbuf_for_hdr_transform(input_ibuf);
if (!ibuf) {
/* Error in input or allocation has failed. */
return nullptr;
}
/* Get `Rec.2100-PQ Display` or its alias from the OpenColorIO configuration. */
const char *rec2100_pq_colorspace = IMB_colormanagement_get_rec2100_pq_display_colorspace();
if (!rec2100_pq_colorspace) {
/* TODO(sergey): Error reporting if the colorspace is not found. */
return ibuf;
}
/* Convert from the current floating point buffer colorspace to Rec.2100-PQ. */
IMB_colormanagement_transform_float(ibuf->float_buffer.data,
ibuf->x,
ibuf->y,
ibuf->channels,
IMB_colormanagement_get_float_colorspace(input_ibuf),
rec2100_pq_colorspace,
IMB_alpha_affects_rgb(ibuf));
return ibuf;
}
static ImBuf *do_hlg_transform(const ImBuf *input_ibuf)
{
ImBuf *ibuf = alloc_imbuf_for_hdr_transform(input_ibuf);
if (!ibuf) {
/* Error in input or allocation has failed. */
return nullptr;
}
/* Get `Rec.2100-HLG Display` or its alias from the OpenColorIO configuration.
* The color space is supposed to be Rec.2100-HLG, 1000 nit. */
const char *rec2100_hlg_colorspace = IMB_colormanagement_get_rec2100_hlg_display_colorspace();
if (!rec2100_hlg_colorspace) {
/* TODO(sergey): Error reporting if the colorspace is not found. */
return ibuf;
}
/* Convert from the current floating point buffer colorspace to Rec.2100-HLG, 1000 nit. */
IMB_colormanagement_transform_float(ibuf->float_buffer.data,
ibuf->x,
ibuf->y,
ibuf->channels,
IMB_colormanagement_get_float_colorspace(input_ibuf),
rec2100_hlg_colorspace,
IMB_alpha_affects_rgb(ibuf));
return ibuf;
}
static const ImBuf *do_hdr_transform_if_needed(MovieWriter *context, const ImBuf *input_ibuf)
{
if (!input_ibuf) {
return nullptr;
}
if (!context || !context->video_codec) {
return input_ibuf;
}
const AVCodecContext &codec = *context->video_codec;
const AVColorTransferCharacteristic color_trc = codec.color_trc;
const AVColorSpace colorspace = codec.colorspace;
const AVColorPrimaries color_primaries = codec.color_primaries;
if (color_trc == AVCOL_TRC_SMPTEST2084 && color_primaries == AVCOL_PRI_BT2020 &&
colorspace == AVCOL_SPC_BT2020_NCL)
{
return do_pq_transform(input_ibuf);
}
if (color_trc == AVCOL_TRC_ARIB_STD_B67 && color_primaries == AVCOL_PRI_BT2020 &&
colorspace == AVCOL_SPC_BT2020_NCL)
{
return do_hlg_transform(input_ibuf);
}
return input_ibuf;
}
/* read and encode a frame of video from the buffer */
static AVFrame *generate_video_frame(MovieWriter *context, const ImBuf *input_ibuf)
{
const ImBuf *image = do_hdr_transform_if_needed(context, input_ibuf);
/* Use float input if needed. */
const bool use_float =
context->img_convert_frame != nullptr &&
!(context->img_convert_frame->format == AV_PIX_FMT_RGBA &&
ELEM(context->img_convert_frame->colorspace, AVCOL_SPC_RGB, AVCOL_SPC_UNSPECIFIED));
const ImBuf *image = (use_float && input_ibuf->float_buffer.data == nullptr) ?
alloc_imbuf_for_colorspace_transform(input_ibuf) :
input_ibuf;
const uint8_t *pixels = image->byte_buffer.data;
const float *pixels_fl = image->float_buffer.data;
/* Use float input if needed. */
const bool use_float = context->img_convert_frame != nullptr &&
context->img_convert_frame->format != AV_PIX_FMT_RGBA;
if ((!use_float && (pixels == nullptr)) || (use_float && (pixels_fl == nullptr))) {
if (image != input_ibuf) {
IMB_freeImBuf(const_cast<ImBuf *>(image));
@ -800,6 +719,81 @@ static void set_quality_rate_options(const MovieWriter *context,
}
}
static void set_colorspace_options(AVCodecContext *c, blender::StringRefNull interop_id)
{
const AVPixFmtDescriptor *pix_fmt_desc = av_pix_fmt_desc_get(c->pix_fmt);
const bool is_rgb_format = (pix_fmt_desc->flags & AV_PIX_FMT_FLAG_RGB);
/* Full range for most color spaces. */
c->color_range = AVCOL_RANGE_JPEG;
/* ASWF Color Interop Forum defined display spaces. The CICP codes there match the enum
* values defined by ffmpeg. Keep in sync with movie_read.cc. */
if (interop_id == "pq_rec2020_display") {
c->color_primaries = AVCOL_PRI_BT2020;
c->color_trc = AVCOL_TRC_SMPTEST2084;
c->colorspace = AVCOL_SPC_BT2020_NCL;
}
else if (interop_id == "hlg_rec2020_display") {
c->color_primaries = AVCOL_PRI_BT2020;
c->color_trc = AVCOL_TRC_ARIB_STD_B67;
c->colorspace = AVCOL_SPC_BT2020_NCL;
}
else if (interop_id == "pq_p3d65_display") {
c->color_primaries = AVCOL_PRI_SMPTE432;
c->color_trc = AVCOL_TRC_SMPTEST2084;
c->colorspace = AVCOL_SPC_BT2020_NCL;
}
else if (interop_id == "g26_p3d65_display") {
c->color_primaries = AVCOL_PRI_SMPTE432;
c->color_trc = AVCOL_TRC_SMPTE428;
c->colorspace = AVCOL_SPC_BT709;
}
else if (interop_id == "g22_rec709_display") {
c->color_primaries = AVCOL_PRI_BT709;
c->color_trc = AVCOL_TRC_GAMMA22;
c->colorspace = AVCOL_SPC_BT709;
}
else if (interop_id == "g24_rec2020_display") {
/* There is no gamma 2.4 trc, but BT.709 is supposed to be close. But it's not
* clear this is right, as we use the same trc for sRGB which is clearly different. */
c->color_primaries = AVCOL_PRI_BT2020;
c->color_trc = AVCOL_TRC_BT709;
c->colorspace = AVCOL_SPC_BT2020_NCL;
}
else if (interop_id == "g24_rec709_display") {
/* There is no gamma 2.4 trc, but BT.709 is supposed to be close. But now this
* is identical to how we write sRGB so at least of the two must be wrong? */
c->color_primaries = AVCOL_PRI_BT709;
c->color_trc = AVCOL_TRC_BT709;
c->colorspace = AVCOL_SPC_BT709;
}
else if (interop_id == "srgb_p3d65_display" || interop_id == "srgbx_p3d65_display") {
c->color_primaries = AVCOL_PRI_SMPTE432;
/* This should be AVCOL_TRC_IEC61966_2_1, but Quicktime refuses to open the file.
* And we're currently also writing srgb_rec709_display the same way. */
c->color_trc = AVCOL_TRC_BT709;
c->colorspace = AVCOL_SPC_BT709;
}
/* Don't write sRGB as we weren't doing it before either, but maybe we should. */
# if 0
else if (interop_id == "srgb_rec709_display") {
c->color_primaries = AVCOL_PRI_BT709;
c->color_trc = AVCOL_TRC_IEC61966_2_1;
c->colorspace = AVCOL_SPC_BT709;
}
# endif
/* If we're not writing RGB, we must write a colorspace to define how
* the conversion to YUV happens. */
else if (!is_rgb_format) {
c->color_primaries = AVCOL_PRI_BT709;
c->color_trc = AVCOL_TRC_BT709;
c->colorspace = AVCOL_SPC_BT709;
/* TODO(sergey): Consider making the range an option to cover more use-cases. */
c->color_range = AVCOL_RANGE_MPEG;
}
}
static AVStream *alloc_video_stream(MovieWriter *context,
const RenderData *rd,
const ImageFormatData *imf,
@ -932,14 +926,6 @@ static AVStream *alloc_video_stream(MovieWriter *context,
const bool is_12_bpp = imf->depth == R_IMF_CHAN_DEPTH_12;
const bool is_16_bpp = imf->depth == R_IMF_CHAN_DEPTH_16;
eFFMpegVideoHdr hdr = eFFMpegVideoHdr(rd->ffcodecdata.video_hdr);
/* Never use HDR for non-10/12 bpp or grayscale outputs. */
if ((!is_10_bpp && !is_12_bpp) || rd->im_format.planes == R_IMF_PLANES_BW) {
hdr = FFM_VIDEO_HDR_NONE;
}
const bool is_hdr_pq = hdr == FFM_VIDEO_HDR_REC2100_PQ;
const bool is_hdr_hlg = hdr == FFM_VIDEO_HDR_REC2100_HLG;
if (is_10_bpp) {
c->pix_fmt = AV_PIX_FMT_YUV420P10LE;
}
@ -1083,29 +1069,14 @@ static AVStream *alloc_video_stream(MovieWriter *context,
c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
/* If output pixel format is not RGB(A), setup colorspace metadata. */
const AVPixFmtDescriptor *pix_fmt_desc = av_pix_fmt_desc_get(c->pix_fmt);
const bool set_bt709 = (pix_fmt_desc->flags & AV_PIX_FMT_FLAG_RGB) == 0;
if (is_hdr_pq) {
/* TODO(sergey): Consider making the range an option to cover more use-cases. */
c->color_range = AVCOL_RANGE_JPEG;
c->color_primaries = AVCOL_PRI_BT2020;
c->color_trc = AVCOL_TRC_SMPTEST2084;
c->colorspace = AVCOL_SPC_BT2020_NCL;
}
else if (is_hdr_hlg) {
/* TODO(sergey): Consider making the range an option to cover more use-cases. */
c->color_range = AVCOL_RANGE_JPEG;
c->color_primaries = AVCOL_PRI_BT2020;
c->color_trc = AVCOL_TRC_ARIB_STD_B67;
c->colorspace = AVCOL_SPC_BT2020_NCL;
}
else if (set_bt709) {
c->color_range = AVCOL_RANGE_MPEG;
c->color_primaries = AVCOL_PRI_BT709;
c->color_trc = AVCOL_TRC_BT709;
c->colorspace = AVCOL_SPC_BT709;
}
/* Set colorspace based on display space of image. */
const ColorSpace *display_colorspace = IMB_colormangement_display_get_color_space(
&imf->display_settings);
const blender::StringRefNull interop_id = (display_colorspace) ?
IMB_colormanagement_space_get_interop_id(
display_colorspace) :
"";
set_colorspace_options(c, interop_id);
/* xasp & yasp got float lately... */
@ -1144,46 +1115,29 @@ static AVStream *alloc_video_stream(MovieWriter *context,
/* FFMPEG expects its data in the output pixel format. */
context->current_frame = alloc_frame(c->pix_fmt, c->width, c->height);
if (c->pix_fmt == AV_PIX_FMT_RGBA) {
/* Output pixel format is the same we use internally, no conversion necessary. */
if (c->pix_fmt == AV_PIX_FMT_RGBA && ELEM(c->colorspace, AVCOL_SPC_RGB, AVCOL_SPC_UNSPECIFIED)) {
/* Output pixel format and colorspace is the same we use internally, no conversion needed. */
context->img_convert_frame = nullptr;
context->img_convert_ctx = nullptr;
}
else {
/* Output pixel format is different, allocate frame for conversion.
* Setup RGB->YUV conversion with proper coefficients (depending on whether it is SDR BT.709,
* or HDR BT.2020). */
* Setup RGB->YUV conversion with proper coefficients, depending on range and colorspace. */
const AVPixelFormat src_format = is_10_bpp || is_12_bpp || is_16_bpp ? AV_PIX_FMT_GBRAPF32LE :
AV_PIX_FMT_RGBA;
context->img_convert_frame = alloc_frame(src_format, c->width, c->height);
if (is_hdr_pq || is_hdr_hlg) {
/* Special conversion for the Rec.2100 PQ and HLG output: the result color space is BT.2020,
* and also use full range. */
context->img_convert_ctx = ffmpeg_sws_get_context(c->width,
c->height,
src_format,
true,
-1,
c->width,
c->height,
c->pix_fmt,
true,
AVCOL_SPC_BT2020_NCL,
SWS_BICUBIC);
}
else {
context->img_convert_ctx = ffmpeg_sws_get_context(c->width,
c->height,
src_format,
false,
-1,
c->width,
c->height,
c->pix_fmt,
false,
set_bt709 ? AVCOL_SPC_BT709 : -1,
SWS_BICUBIC);
}
context->img_convert_ctx = ffmpeg_sws_get_context(
c->width,
c->height,
src_format,
true,
-1,
c->width,
c->height,
c->pix_fmt,
c->color_range == AVCOL_RANGE_JPEG,
c->colorspace != AVCOL_SPC_RGB ? c->colorspace : -1,
SWS_BICUBIC);
}
avcodec_parameters_from_context(st->codecpar, c);

View file

@ -159,6 +159,9 @@ LibOCIOColorSpace::LibOCIOColorSpace(const int index,
else if (alias == "rec2100_hlg_display") {
interop_id_ = "hlg_rec2020_display";
}
else if (alias == "st2084_p3d65_display") {
interop_id_ = "pq_p3d65_display";
}
else if (alias == "lin_rec709_srgb" || alias == "lin_rec709") {
interop_id_ = "lin_rec709_scene";
}

View file

@ -155,12 +155,6 @@ typedef enum IMB_Ffmpeg_Codec_ID {
FFMPEG_CODEC_ID_OPUS = 86076,
} IMB_Ffmpeg_Codec_ID;
typedef enum eFFMpegVideoHdr {
FFM_VIDEO_HDR_NONE = 0,
FFM_VIDEO_HDR_REC2100_HLG = 1,
FFM_VIDEO_HDR_REC2100_PQ = 2,
} eFFMpegVideoHdr;
typedef struct FFMpegCodecData {
int type;
int codec; /* Use `codec_id_get()` instead! IMB_Ffmpeg_Codec_ID */
@ -184,7 +178,7 @@ typedef struct FFMpegCodecData {
int rc_buffer_size;
int mux_packet_size;
int mux_rate;
int video_hdr; /* eFFMpegVideoHdr */
int _pad;
#ifdef __cplusplus
IMB_Ffmpeg_Codec_ID codec_id_get() const

View file

@ -6566,21 +6566,6 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna)
{0, nullptr, 0, nullptr, nullptr},
};
static const EnumPropertyItem ffmpeg_hdr_items[] = {
{FFM_VIDEO_HDR_NONE, "NONE", 0, "None", "No High Dynamic Range"},
{FFM_VIDEO_HDR_REC2100_PQ,
"REQ2100_PQ",
0,
"Rec.2100 PQ",
"Rec.2100 color space with Perceptual Quantizer HDR encoding"},
{FFM_VIDEO_HDR_REC2100_HLG,
"REQ2100_HLG",
0,
"Rec.2100 HLG",
"Rec.2100 color space with Hybrid-Log Gamma HDR encoding"},
{0, nullptr, 0, nullptr, nullptr},
};
static const EnumPropertyItem ffmpeg_audio_codec_items[] = {
{FFMPEG_CODEC_ID_NONE,
"NONE",
@ -6643,14 +6628,6 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Bitrate", "Video bitrate (kbit/s)");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr);
prop = RNA_def_property(srna, "video_hdr", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "video_hdr");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_enum_items(prop, ffmpeg_hdr_items);
RNA_def_property_enum_default(prop, FFM_VIDEO_HDR_NONE);
RNA_def_property_ui_text(prop, "HDR", "High Dynamic Range options");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr);
prop = RNA_def_property(srna, "minrate", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, nullptr, "rc_min_rate");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);

View file

@ -1,14 +1,15 @@
.blend files used to generate:
- hdr_simple_export_hlg_12bit.mov
- hdr_simple_export_pq_12bit.mov
- sdr_simple_export_p3_aces_10bit.mov
Step 1:
Open and render hdr_simple_still_test_file.blend
It will generate hdr_simple_still_test_file.exr file
Step 2:
Open and render hdr_simple_export_hlg_12bit.blend and hdr_simple_export_pq_12bit.blend.
Open and render other blend files.
These files generate videos in the out/ folder.
Step 3:
Copy files from the out/ folder to their resired final destination.
Copy files from the out/ folder to their desired final destination.

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:981c1e83f0d6d9065f04acca51c9e74094d1975d7d554677a9178c6668602050
size 314536
oid sha256:9d6ac027fb8d03d4777efa876ed77ae4f1b003b67020b26f37c33fbc799fbce5
size 89657

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0c3f1b7419fd2a445ee0e555c6db5026417b8cc4651aa48415ae98ef05d0a6e9
size 314536
oid sha256:fa748ad062a26a2e3b4d4c02ac2df3ea40550788851405e5cf86dfb872802c0a
size 89563

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d6f369bf8037bee8246caa77b2179aec59203d5a34933e468e464342cf3fda33
size 89473

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fe04e475b2713d23f0b4543e5038658e4394a76a75983c78dbac2ca03f564e5d
size 21715

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:60d7a34fc586b0d55914ebff7b6e5693a281b3be1b40b0f3174caef66c3ffbcf
size 533775

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:115d78313dfab8acd0139f26228fa729e0073b21f47305873d76b2d46dce7333
size 88582

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:65c379238a5611f32187358300162178d59f188d91924e9f8b8b1041e0a5d6b3
size 164248
oid sha256:8553b7e0d54c850b315af1956dee9d518ee4f40e470f96dd1341747d2551730b
size 163515

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:99fa779482012f420251c2c251050a4f5b2df0bb764bcd2e20425718218f6c37
size 172909
oid sha256:2a5c72c1b4361eebc131db9f66f526efb1da4e28f7c0f83bb8d166449b8048f6
size 173101

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c9a229fb9d356b657782d85eb894c2d05feeeb94cb35daed30244db8727a2dc5
size 178637

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:497b9a1a83caf49af366e64f7d06164105b57a3238655618c57e465931af5da1
size 151337
oid sha256:10ea33f3dcb82ec35ff3d0f1150fda647b411164ddf811aeb48eb7566a801860
size 151136

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b32f68219d5a0ef354a5f927bdc67e407bcf6567a197a9021643a9f0dff40520
size 156812
oid sha256:5be2c75762c1510b793bcfbaeb1bd5ad4c3d75d10f5fae81a1e630c93a35b40a
size 157363

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:634c9c5680a3d61c647c9178ca22e7ce0bc8635a64a3a0fd935fa3e095894794
size 310316
oid sha256:fc07c905708f1281965a6fa9635db16119e42bea7b266158411da53617d18062
size 89656

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:034e8d9d81ad321291b3c2c3887271aa6df17ec10b68703e9fdceb1ef8c066ec
size 313840
oid sha256:9c2dd069226769c86d1a1f188da71bd98ddf655a22039ad157fe15418862e97b
size 89520

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d7f6b730f76bcb427bb676d5016d8e4625ad3150131289127ab3705b146a031d
size 89206

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:11665f820006e987c4034dc0cafaa7355669dcaa3029eb28c06c5cb3de64228e
size 310052
oid sha256:9a7b90a0ef5bc97e2eb1980306df888fd3cb689745b3a11a23fdf4a9943e9f28
size 89556

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:01fce87094967babfd14a51b5f988ba3a8cc48f0e5d6a63ec1cf38fc8aa630f4
size 313576
oid sha256:939983d9a28f701201d80179be151e60c45b99d1dd1cf2324b29307cf36a4001
size 89190

View file

@ -37,6 +37,11 @@ class FFmpegHDRColorspace(MovieInputTest):
self.assertEqual(self.get_movie_colorspace(prefix / "hdr_simple_export_hlg_12bit.mov"), "Rec.2100-HLG")
def test_p3(self):
prefix = TEST_DIR / Path("ffmpeg") / "media"
self.assertEqual(self.get_movie_colorspace(prefix / "sdr_simple_export_p3_aces_10bit.mov"), "Display P3")
def main():
global TEST_DIR

View file

@ -12,6 +12,7 @@ from pathlib import Path
BLOCKLIST = [
"hdr_simple_export_hlg_12bit.blend",
"hdr_simple_export_pq_12bit.blend",
"sdr_simple_export_p3_aces_10bit.blend",
"hdr_simple_still_test_file.blend",
]