summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libhb/colorspace.c19
-rw-r--r--libhb/common.c2
-rw-r--r--libhb/decavcodec.c23
-rw-r--r--libhb/encx265.c116
-rw-r--r--libhb/handbrake/common.h29
-rw-r--r--libhb/handbrake/hbffmpeg.h4
-rw-r--r--libhb/handbrake/hbtypes.h2
-rw-r--r--libhb/hb_json.c115
-rw-r--r--libhb/hbffmpeg.c56
-rw-r--r--libhb/muxavformat.c40
-rw-r--r--libhb/scan.c25
-rw-r--r--libhb/stream.c13
-rw-r--r--libhb/work.c22
13 files changed, 462 insertions, 4 deletions
diff --git a/libhb/colorspace.c b/libhb/colorspace.c
index 01b7dd9ee..c535d2220 100644
--- a/libhb/colorspace.c
+++ b/libhb/colorspace.c
@@ -143,6 +143,22 @@ static const char * get_range_name(int color_range)
return "limited";
}
+#define REFERENCE_WHITE 100.0f
+
+static double determine_signal_peak(hb_filter_init_t * init)
+{
+ double peak = init->job->coll.max_cll / REFERENCE_WHITE;
+ if (!peak && init->job->mastering.has_luminance)
+ {
+ peak = hb_q2d(init->job->mastering.max_luminance) / REFERENCE_WHITE;
+ }
+ if (!peak || peak < 1)
+ {
+ peak = init->color_transfer == HB_COLR_TRA_SMPTEST2084 ? 100.0f : 10.0f;
+ }
+ return peak;
+}
+
static int colorspace_init(hb_filter_object_t * filter, hb_filter_init_t * init)
{
hb_filter_private_t * pv = NULL;
@@ -246,6 +262,9 @@ static int colorspace_init(hb_filter_object_t * filter, hb_filter_init_t * init)
hb_dict_set_double(avsettings, "param", param);
}
hb_dict_set_double(avsettings, "desat", desat);
+ // FIXME: this could be automated by passing through side data.
+ double peak = determine_signal_peak(init);
+ hb_dict_set_double(avsettings, "peak", peak);
hb_dict_set(avfilter, "tonemap", avsettings);
hb_value_array_append(avfilters, avfilter);
diff --git a/libhb/common.c b/libhb/common.c
index cda3683ee..451af24f6 100644
--- a/libhb/common.c
+++ b/libhb/common.c
@@ -3844,6 +3844,8 @@ static void job_setup(hb_job_t * job, hb_title_t * title)
job->color_prim_override = HB_COLR_PRI_UNDEF;
job->color_transfer_override = HB_COLR_TRA_UNDEF;
job->color_matrix_override = HB_COLR_MAT_UNDEF;
+ job->mastering = title->mastering;
+ job->coll = title->coll;
job->mux = HB_MUX_MP4;
diff --git a/libhb/decavcodec.c b/libhb/decavcodec.c
index 1e4d94d38..23b0a5a2e 100644
--- a/libhb/decavcodec.c
+++ b/libhb/decavcodec.c
@@ -1098,6 +1098,29 @@ static hb_buffer_t *copy_frame( hb_work_private_t *pv )
}
}
+ // Check for HDR mastering data
+ sd = av_frame_get_side_data(pv->frame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA);
+ if (sd != NULL)
+ {
+ if (!pv->job && pv->title && sd->size > 0)
+ {
+ AVMasteringDisplayMetadata *mastering = (AVMasteringDisplayMetadata *)sd->data;
+ pv->title->mastering = hb_mastering_ff_to_hb(*mastering);
+ }
+ }
+
+ // Check for HDR content light level data
+ sd = av_frame_get_side_data(pv->frame, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL);
+ if (sd != NULL)
+ {
+ if (!pv->job && pv->title && sd->size > 0)
+ {
+ AVContentLightMetadata *coll = (AVContentLightMetadata *)sd->data;
+ pv->title->coll.max_cll = coll->MaxCLL;
+ pv->title->coll.max_fall = coll->MaxFALL;
+ }
+ }
+
return out;
}
diff --git a/libhb/encx265.c b/libhb/encx265.c
index fe6ef34ca..c781da0b2 100644
--- a/libhb/encx265.c
+++ b/libhb/encx265.c
@@ -34,6 +34,9 @@ hb_work_object_t hb_encx265 =
#define FRAME_INFO_SIZE (1 << (FRAME_INFO_MIN2 - FRAME_INFO_MAX2 + 1))
#define FRAME_INFO_MASK (FRAME_INFO_SIZE - 1)
+#define MASTERING_CHROMA_DEN 50000
+#define MASTERING_LUMA_DEN 10000
+
static const char * const hb_x265_encopt_synonyms[][2] =
{
{ "me", "motion", },
@@ -63,6 +66,11 @@ struct hb_work_private_s
int bit_depth;
};
+static inline int64_t rescale(hb_rational_t q, int b)
+{
+ return av_rescale(q.num, b, q.den);
+}
+
static int param_parse(hb_work_private_t *pv, x265_param *param,
const char *key, const char *value)
{
@@ -107,6 +115,7 @@ int apply_h265_level(hb_work_private_t *pv, x265_param *param,
return X265_PARAM_BAD_VALUE;
}
+
/***********************************************************************
* hb_work_encx265_init
***********************************************************************
@@ -195,11 +204,67 @@ int encx265Init(hb_work_object_t *w, hb_job_t *job)
goto fail;
}
+ /*
+ * HDR10 Static metadata
+ */
+ if (job->color_transfer == HB_COLR_TRA_SMPTEST2084)
+ {
+ if (depth > 8)
+ {
+ if (param_parse(pv, param, "hdr-opt", "1"))
+ {
+ goto fail;
+ }
+ }
+
+ /*
+ * Mastering display metadata.
+ */
+ if (job->mastering.has_primaries && job->mastering.has_luminance)
+ {
+ char masteringDisplayColorVolume[256];
+ snprintf(masteringDisplayColorVolume, sizeof(masteringDisplayColorVolume),
+ "G(%hu,%hu)B(%hu,%hu)R(%hu,%hu)WP(%hu,%hu)L(%u,%u)",
+ (unsigned short)rescale(job->mastering.display_primaries[0][0], MASTERING_CHROMA_DEN),
+ (unsigned short)rescale(job->mastering.display_primaries[0][1], MASTERING_CHROMA_DEN),
+ (unsigned short)rescale(job->mastering.display_primaries[1][0], MASTERING_CHROMA_DEN),
+ (unsigned short)rescale(job->mastering.display_primaries[1][1], MASTERING_CHROMA_DEN),
+ (unsigned short)rescale(job->mastering.display_primaries[2][0], MASTERING_CHROMA_DEN),
+ (unsigned short)rescale(job->mastering.display_primaries[2][1], MASTERING_CHROMA_DEN),
+ (unsigned short)rescale(job->mastering.white_point[0], MASTERING_CHROMA_DEN),
+ (unsigned short)rescale(job->mastering.white_point[1], MASTERING_CHROMA_DEN),
+ (unsigned)rescale(job->mastering.max_luminance, MASTERING_LUMA_DEN),
+ (unsigned)rescale(job->mastering.min_luminance, MASTERING_LUMA_DEN));
+
+ if (param_parse(pv, param, "master-display", masteringDisplayColorVolume))
+ {
+ goto fail;
+ }
+ }
+
+ /*
+ * Content light level.
+ */
+ if (job->coll.max_cll && job->coll.max_fall)
+ {
+ char contentLightLevel[256];
+ snprintf(contentLightLevel, sizeof(contentLightLevel),
+ "%hu,%hu",
+ (unsigned short)job->coll.max_cll, (unsigned short)job->coll.max_fall);
+
+ if (param_parse(pv, param, "max-cll", contentLightLevel))
+ {
+ goto fail;
+ }
+ }
+ }
+
/* Bit depth */
pv->bit_depth = hb_get_bit_depth(job->pix_fmt);
/* iterate through x265_opts and parse the options */
hb_dict_t *x265_opts;
+ int override_mastering = 0, override_coll = 0;
x265_opts = hb_encopts_to_dict(job->encoder_options, job->vcodec);
hb_dict_iter_t iter;
@@ -211,6 +276,15 @@ int encx265Init(hb_work_object_t *w, hb_job_t *job)
hb_value_t *value = hb_dict_iter_value(iter);
char *str = hb_value_get_string_xform(value);
+ if (!strcmp(key, "master-display"))
+ {
+ override_mastering = 1;
+ }
+ if (!strcmp(key, "max-cll"))
+ {
+ override_coll = 1;
+ }
+
// here's where the strings are passed to libx265 for parsing
// unknown options or bad values are non-fatal, see encx264.c
param_parse(pv, param, key, str);
@@ -227,6 +301,48 @@ int encx265Init(hb_work_object_t *w, hb_job_t *job)
job->color_matrix_override = param->vui.matrixCoeffs;
/*
+ * Reload mastering settings in case custom
+ * values were set in the encoder_options string.
+ */
+ if (override_mastering)
+ {
+ uint16_t display_primaries_x[3];
+ uint16_t display_primaries_y[3];
+ uint16_t white_point_x, white_point_y;
+ uint32_t max_luminance, min_luminance;
+
+ if (sscanf(param->masteringDisplayColorVolume, "G(%hu,%hu)B(%hu,%hu)R(%hu,%hu)WP(%hu,%hu)L(%u,%u)",
+ &display_primaries_x[0], &display_primaries_y[0],
+ &display_primaries_x[1], &display_primaries_y[1],
+ &display_primaries_x[2], &display_primaries_y[2],
+ &white_point_x, &white_point_y,
+ &max_luminance, &min_luminance) == 10)
+ {
+ for (int i = 0; i < 3; i++)
+ {
+ job->mastering.display_primaries[i][0] = hb_make_q(display_primaries_x[i], MASTERING_CHROMA_DEN);
+ job->mastering.display_primaries[i][1] = hb_make_q(display_primaries_y[i], MASTERING_CHROMA_DEN);
+ }
+
+ job->mastering.white_point[0] = hb_make_q(white_point_x, MASTERING_CHROMA_DEN);
+ job->mastering.white_point[1] = hb_make_q(white_point_y, MASTERING_CHROMA_DEN);
+
+ job->mastering.min_luminance = hb_make_q(min_luminance, MASTERING_LUMA_DEN);
+ job->mastering.max_luminance = hb_make_q(max_luminance, MASTERING_LUMA_DEN);
+ }
+ }
+
+ /*
+ * Reload content light level settings in case custom
+ * values were set in the encoder_options string.
+ */
+ if (override_coll)
+ {
+ job->coll.max_fall = param->maxFALL;
+ job->coll.max_cll = param->maxCLL;
+ }
+
+ /*
* Settings which can't be overridden in the encodeer_options string
* (muxer-specific settings, resolution, ratecontrol, etc.).
*/
diff --git a/libhb/handbrake/common.h b/libhb/handbrake/common.h
index 8a1faa37f..9b5805b36 100644
--- a/libhb/handbrake/common.h
+++ b/libhb/handbrake/common.h
@@ -254,6 +254,16 @@ struct hb_rational_s
int den;
};
+static inline hb_rational_t hb_make_q(int num, int den)
+{
+ hb_rational_t r = { num, den };
+ return r;
+}
+
+static inline double hb_q2d(hb_rational_t a){
+ return a.num / (double) a.den;
+}
+
struct hb_geometry_s
{
int width;
@@ -309,6 +319,20 @@ struct hb_subtitle_config_s
int64_t offset;
};
+struct hb_mastering_display_metadata_s {
+ hb_rational_t display_primaries[3][2];
+ hb_rational_t white_point[2];
+ hb_rational_t min_luminance;
+ hb_rational_t max_luminance;
+ int has_primaries;
+ int has_luminance;
+};
+
+struct hb_content_light_metadata_s {
+ unsigned max_cll;
+ unsigned max_fall;
+};
+
/*******************************************************************************
* Lists of rates, mixdowns, encoders etc.
*******************************************************************************
@@ -626,6 +650,9 @@ struct hb_job_s
#define HB_COLR_MAT_ICTCP 14 // ITU-R BT.2100-0, ICtCp
// 0, 3-5, 8, 11-65535: reserved/not implemented
+ hb_mastering_display_metadata_t mastering;
+ hb_content_light_metadata_t coll;
+
hb_list_t * list_chapter;
/* List of audio settings. */
@@ -1090,6 +1117,8 @@ struct hb_title_s
int color_transfer;
int color_matrix;
int color_range;
+ hb_mastering_display_metadata_t mastering;
+ hb_content_light_metadata_t coll;
hb_rational_t vrate;
int crop[4];
enum {HB_DVD_DEMUXER, HB_TS_DEMUXER, HB_PS_DEMUXER, HB_NULL_DEMUXER} demuxer;
diff --git a/libhb/handbrake/hbffmpeg.h b/libhb/handbrake/hbffmpeg.h
index 726eb8765..1206cb4a4 100644
--- a/libhb/handbrake/hbffmpeg.h
+++ b/libhb/handbrake/hbffmpeg.h
@@ -19,6 +19,7 @@
#include "libavutil/avutil.h"
#include "libavutil/downmix_info.h"
#include "libavutil/display.h"
+#include "libavutil/mastering_display_metadata.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "handbrake/common.h"
@@ -41,6 +42,9 @@ int hb_colr_pri_ff_to_hb(int colr_prim);
int hb_colr_tra_ff_to_hb(int colr_tra);
int hb_colr_mat_ff_to_hb(int colr_mat);
+hb_mastering_display_metadata_t hb_mastering_ff_to_hb(AVMasteringDisplayMetadata mastering);
+AVMasteringDisplayMetadata hb_mastering_hb_to_ff(hb_mastering_display_metadata_t mastering);
+
struct SwsContext*
hb_sws_get_context(int srcW, int srcH, enum AVPixelFormat srcFormat,
int dstW, int dstH, enum AVPixelFormat dstFormat,
diff --git a/libhb/handbrake/hbtypes.h b/libhb/handbrake/hbtypes.h
index 726d9c5b3..f9537140b 100644
--- a/libhb/handbrake/hbtypes.h
+++ b/libhb/handbrake/hbtypes.h
@@ -43,5 +43,7 @@ typedef struct hb_buffer_settings_s hb_buffer_settings_t;
typedef struct hb_image_format_s hb_image_format_t;
typedef struct hb_fifo_s hb_fifo_t;
typedef struct hb_lock_s hb_lock_t;
+typedef struct hb_mastering_display_metadata_s hb_mastering_display_metadata_t;
+typedef struct hb_content_light_metadata_s hb_content_light_metadata_t;
#endif // HANDBRAKE_TYPES_H
diff --git a/libhb/hb_json.c b/libhb/hb_json.c
index 3a40205a6..f794c7ab7 100644
--- a/libhb/hb_json.c
+++ b/libhb/hb_json.c
@@ -688,6 +688,55 @@ hb_dict_t* hb_job_to_dict( const hb_job_t * job )
hb_value_int(job->color_matrix_override));
}
+ // Mastering metadata
+ hb_dict_t *mastering_dict;
+ if (job->mastering.has_primaries || job->mastering.has_luminance)
+ {
+ mastering_dict = json_pack_ex(&error, 0,
+ "{"
+ // DisplayPrimaries[3][2]
+ "s:[[[ii],[ii]],[[ii],[ii]],[[ii],[ii]]],"
+ // WhitePoint[2],
+ "s:[[i,i],[i,i]],"
+ // MinLuminance, MaxLuminance, HasPrimaries, HasLuminance
+ "s:[i,i],s:[i,i],s:b,s:b"
+ "}",
+ "DisplayPrimaries", job->mastering.display_primaries[0][0].num,
+ job->mastering.display_primaries[0][0].den,
+ job->mastering.display_primaries[0][1].num,
+ job->mastering.display_primaries[0][1].den,
+ job->mastering.display_primaries[1][0].num,
+ job->mastering.display_primaries[1][0].den,
+ job->mastering.display_primaries[1][1].num,
+ job->mastering.display_primaries[1][1].den,
+ job->mastering.display_primaries[2][0].num,
+ job->mastering.display_primaries[2][0].den,
+ job->mastering.display_primaries[2][1].num,
+ job->mastering.display_primaries[2][1].den,
+ "WhitePoint", job->mastering.white_point[0].num,
+ job->mastering.white_point[0].den,
+ job->mastering.white_point[1].num,
+ job->mastering.white_point[1].den,
+ "MinLuminance", job->mastering.min_luminance.num,
+ job->mastering.min_luminance.den,
+ "MaxLuminance", job->mastering.max_luminance.num,
+ job->mastering.max_luminance.den,
+ "HasPrimaries", job->mastering.has_primaries,
+ "HasLuminance", job->mastering.has_luminance
+ );
+ hb_dict_set(video_dict, "Mastering", mastering_dict);
+ }
+
+ // Content Light Level metadata
+ hb_dict_t *coll_dict;
+ if (job->coll.max_cll && job->coll.max_fall)
+ {
+ coll_dict = json_pack_ex(&error, 0, "{s:i, s:i}",
+ "MaxCLL", job->coll.max_cll,
+ "MaxFALL", job->coll.max_fall);
+ hb_dict_set(video_dict, "ContentLightLevel", coll_dict);
+ }
+
if (job->vquality > HB_INVALID_VIDEO_QUALITY)
{
hb_dict_set(video_dict, "Quality", hb_value_double(job->vquality));
@@ -928,6 +977,7 @@ typedef const char * const_str_t;
static double* unpack_f(double *f) { return f; }
static int* unpack_i(int *i) { return i; }
+static unsigned* unpack_u(unsigned *u) { return u; }
static json_int_t* unpack_I(json_int_t *i) { return i; }
static int * unpack_b(int *b) { return b; }
static const_str_t* unpack_s(const_str_t *s){ return s; }
@@ -1026,6 +1076,8 @@ hb_job_t* hb_dict_to_job( hb_handle_t * h, hb_dict_t *dict )
hb_value_array_t * subtitle_list = NULL;
hb_value_array_t * filter_list = NULL;
hb_value_t * mux = NULL, * vcodec = NULL;
+ hb_dict_t * mastering_dict = NULL;
+ hb_dict_t * coll_dict = NULL;
hb_value_t * acodec_copy_mask = NULL, * acodec_fallback = NULL;
const char * destfile = NULL;
const char * range_type = NULL;
@@ -1059,12 +1111,16 @@ hb_job_t* hb_dict_to_job( hb_handle_t * h, hb_dict_t *dict )
// TwoPass, Turbo,
// ColorFormat, ColorRange,
// ColorPrimaries, ColorTransfer, ColorMatrix,
+ // Mastering,
+ // ContentLightLevel,
// ColorPrimariesOverride, ColorTransferOverride, ColorMatrixOverride,
// QSV {Decode, AsyncDepth}}
"s:{s:o, s?F, s?i, s?s, s?s, s?s, s?s, s?s,"
" s?b, s?b,"
" s?i, s?i,"
" s?i, s?i, s?i,"
+ " s?o,"
+ " s?o,"
" s?i, s?i, s?i,"
" s?{s?b, s?i}},"
// Audio {CopyMask, FallbackEncoder, AudioList}
@@ -1113,6 +1169,8 @@ hb_job_t* hb_dict_to_job( hb_handle_t * h, hb_dict_t *dict )
"ColorPrimaries", unpack_i(&job->color_prim),
"ColorTransfer", unpack_i(&job->color_transfer),
"ColorMatrix", unpack_i(&job->color_matrix),
+ "Mastering", unpack_o(&mastering_dict),
+ "ContentLightLevel", unpack_o(&coll_dict),
"ColorPrimariesOverride", unpack_i(&job->color_prim_override),
"ColorTransferOverride", unpack_i(&job->color_transfer_override),
"ColorMatrixOverride", unpack_i(&job->color_matrix_override),
@@ -1236,6 +1294,63 @@ hb_job_t* hb_dict_to_job( hb_handle_t * h, hb_dict_t *dict )
job->select_subtitle_config.dest = subtitle_search_burn ?
RENDERSUB : PASSTHRUSUB;
+
+ if (mastering_dict != NULL)
+ {
+ result = json_unpack_ex(mastering_dict, &error, 0,
+ "{"
+ // DisplayPrimaries[3][2]
+ "s:[[[ii],[ii]],[[ii],[ii]],[[ii],[ii]]],"
+ // WhitePoint[2],
+ "s:[[i,i],[i,i]],"
+ // MinLuminance, MaxLuminance, HasPrimaries, HasLuminance
+ "s:[i,i],s:[i,i],s:b,s:b"
+ "}",
+ "DisplayPrimaries", unpack_i(&job->mastering.display_primaries[0][0].num),
+ unpack_i(&job->mastering.display_primaries[0][0].den),
+ unpack_i(&job->mastering.display_primaries[0][1].num),
+ unpack_i(&job->mastering.display_primaries[0][1].den),
+ unpack_i(&job->mastering.display_primaries[1][0].num),
+ unpack_i(&job->mastering.display_primaries[1][0].den),
+ unpack_i(&job->mastering.display_primaries[1][1].num),
+ unpack_i(&job->mastering.display_primaries[1][1].den),
+ unpack_i(&job->mastering.display_primaries[2][0].num),
+ unpack_i(&job->mastering.display_primaries[2][0].den),
+ unpack_i(&job->mastering.display_primaries[2][1].num),
+ unpack_i(&job->mastering.display_primaries[2][1].den),
+ "WhitePoint", unpack_i(&job->mastering.white_point[0].num),
+ unpack_i(&job->mastering.white_point[0].den),
+ unpack_i(&job->mastering.white_point[1].num),
+ unpack_i(&job->mastering.white_point[1].den),
+ "MinLuminance", unpack_i(&job->mastering.min_luminance.num),
+ unpack_i(&job->mastering.min_luminance.den),
+ "MaxLuminance", unpack_i(&job->mastering.max_luminance.num),
+ unpack_i(&job->mastering.max_luminance.den),
+ "HasPrimaries", unpack_b(&job->mastering.has_primaries),
+ "HasLuminance", unpack_b(&job->mastering.has_luminance)
+ );
+ if (result < 0)
+ {
+ hb_error("hb_dict_to_job: failed to parse mastering_dict: %s", error.text);
+ goto fail;
+ }
+ }
+
+ if (coll_dict != NULL)
+ {
+ result = json_unpack_ex(coll_dict, &error, 0,
+ // {MaxCLL, MaxFALL}
+ "{s:i, s:i}",
+ "MaxCLL", unpack_u(&job->coll.max_cll),
+ "MaxFALL", unpack_u(&job->coll.max_fall)
+ );
+ if (result < 0)
+ {
+ hb_error("hb_dict_to_job: failed to parse coll_dict: %s", error.text);
+ goto fail;
+ }
+ }
+
if (meta_dict != NULL)
{
// By default, the job is populated with the metadata
diff --git a/libhb/hbffmpeg.c b/libhb/hbffmpeg.c
index f9edc4bd1..b2e64ca64 100644
--- a/libhb/hbffmpeg.c
+++ b/libhb/hbffmpeg.c
@@ -423,6 +423,62 @@ int hb_colr_mat_ff_to_hb(int colr_mat)
}
}
+static hb_rational_t hb_rational_ff_to_hb(AVRational rational)
+{
+ hb_rational_t hb_rational = {rational.num, rational.den};
+ return hb_rational;
+}
+
+static AVRational hb_rational_hb_to_ff(hb_rational_t rational)
+{
+ AVRational ff_rational = {rational.num, rational.den};
+ return ff_rational;
+}
+
+hb_mastering_display_metadata_t hb_mastering_ff_to_hb(AVMasteringDisplayMetadata mastering)
+{
+ hb_mastering_display_metadata_t hb_mastering;
+
+ for (int i = 0; i < 3; i++)
+ {
+ hb_mastering.display_primaries[i][0] = hb_rational_ff_to_hb(mastering.display_primaries[i][0]);
+ hb_mastering.display_primaries[i][1] = hb_rational_ff_to_hb(mastering.display_primaries[i][1]);
+ }
+
+ hb_mastering.white_point[0] = hb_rational_ff_to_hb(mastering.white_point[0]);
+ hb_mastering.white_point[1] = hb_rational_ff_to_hb(mastering.white_point[1]);
+
+ hb_mastering.min_luminance = hb_rational_ff_to_hb(mastering.min_luminance);
+ hb_mastering.max_luminance = hb_rational_ff_to_hb(mastering.max_luminance);
+
+ hb_mastering.has_primaries = mastering.has_primaries;
+ hb_mastering.has_luminance = mastering.has_luminance;
+
+ return hb_mastering;
+}
+
+AVMasteringDisplayMetadata hb_mastering_hb_to_ff(hb_mastering_display_metadata_t mastering)
+{
+ AVMasteringDisplayMetadata ff_mastering;
+
+ for (int i = 0; i < 3; i++)
+ {
+ ff_mastering.display_primaries[i][0] = hb_rational_hb_to_ff(mastering.display_primaries[i][0]);
+ ff_mastering.display_primaries[i][1] = hb_rational_hb_to_ff(mastering.display_primaries[i][1]);
+ }
+
+ ff_mastering.white_point[0] = hb_rational_hb_to_ff(mastering.white_point[0]);
+ ff_mastering.white_point[1] = hb_rational_hb_to_ff(mastering.white_point[1]);
+
+ ff_mastering.min_luminance = hb_rational_hb_to_ff(mastering.min_luminance);
+ ff_mastering.max_luminance = hb_rational_hb_to_ff(mastering.max_luminance);
+
+ ff_mastering.has_primaries = mastering.has_primaries;
+ ff_mastering.has_luminance = mastering.has_luminance;
+
+ return ff_mastering;
+}
+
uint64_t hb_ff_mixdown_xlat(int hb_mixdown, int *downmix_mode)
{
uint64_t ff_layout = 0;
diff --git a/libhb/muxavformat.c b/libhb/muxavformat.c
index 9c8898c28..391782e80 100644
--- a/libhb/muxavformat.c
+++ b/libhb/muxavformat.c
@@ -154,9 +154,9 @@ static int avformatInit( hb_mux_object_t * m )
av_dict_set(&av_opts, "brand", "mp42", 0);
if (job->mp4_optimize)
- av_dict_set(&av_opts, "movflags", "faststart+disable_chpl", 0);
+ av_dict_set(&av_opts, "movflags", "faststart+disable_chpl+write_colr", 0);
else
- av_dict_set(&av_opts, "movflags", "+disable_chpl", 0);
+ av_dict_set(&av_opts, "movflags", "+disable_chpl+write_colr", 0);
break;
case HB_MUX_AV_MKV:
@@ -445,6 +445,42 @@ static int avformatInit( hb_mux_object_t * m )
track->st->codecpar->height = job->height;
track->st->disposition |= AV_DISPOSITION_DEFAULT;
+ track->st->codecpar->color_primaries = job->color_prim;
+ track->st->codecpar->color_trc = job->color_transfer;
+ track->st->codecpar->color_space = job->color_matrix;
+ track->st->codecpar->color_range = job->color_range;
+
+ if (job->color_transfer == HB_COLR_TRA_SMPTEST2084)
+ {
+ if (job->mastering.has_primaries || job->mastering.has_luminance)
+ {
+ AVMasteringDisplayMetadata mastering = hb_mastering_hb_to_ff(job->mastering);
+
+ uint8_t *mastering_data = av_malloc(sizeof(AVMasteringDisplayMetadata));
+ memcpy(mastering_data, &mastering, sizeof(AVMasteringDisplayMetadata));
+
+ av_stream_add_side_data(track->st,
+ AV_PKT_DATA_MASTERING_DISPLAY_METADATA,
+ mastering_data,
+ sizeof(AVMasteringDisplayMetadata));
+ }
+
+ if (job->coll.max_cll && job->coll.max_fall)
+ {
+ AVContentLightMetadata coll;
+ coll.MaxCLL = job->coll.max_cll;
+ coll.MaxFALL = job->coll.max_fall;
+
+ uint8_t *coll_data = av_malloc(sizeof(AVContentLightMetadata));
+ memcpy(coll_data, &coll, sizeof(AVContentLightMetadata));
+
+ av_stream_add_side_data(track->st,
+ AV_PKT_DATA_CONTENT_LIGHT_LEVEL,
+ coll_data,
+ sizeof(AVContentLightMetadata));
+ }
+ }
+
hb_rational_t vrate = job->vrate;
// If the vrate is the internal clock rate, there's a good chance
diff --git a/libhb/scan.c b/libhb/scan.c
index 91f6c511a..5dafcb9d1 100644
--- a/libhb/scan.c
+++ b/libhb/scan.c
@@ -1084,12 +1084,33 @@ skip_preview:
}
hb_log( "scan: %d previews, %dx%d, %.3f fps, autocrop = %d/%d/%d/%d, "
- "aspect %s, PAR %d:%d",
+ "aspect %s, PAR %d:%d, color profile: %d-%d-%d",
npreviews, title->geometry.width, title->geometry.height,
(float)title->vrate.num / title->vrate.den,
title->crop[0], title->crop[1], title->crop[2], title->crop[3],
aspect_to_string(&title->dar),
- title->geometry.par.num, title->geometry.par.den);
+ title->geometry.par.num, title->geometry.par.den,
+ title->color_prim, title->color_transfer, title->color_matrix);
+
+ if (title->mastering.has_primaries || title->mastering.has_luminance)
+ {
+ hb_log("scan: mastering display metadata: r(%5.4f,%5.4f) g(%5.4f,%5.4f) b(%5.4f %5.4f) wp(%5.4f, %5.4f) min_luminance=%f, max_luminance=%f",
+ hb_q2d(title->mastering.display_primaries[0][0]),
+ hb_q2d(title->mastering.display_primaries[0][1]),
+ hb_q2d(title->mastering.display_primaries[1][0]),
+ hb_q2d(title->mastering.display_primaries[1][1]),
+ hb_q2d(title->mastering.display_primaries[2][0]),
+ hb_q2d(title->mastering.display_primaries[2][1]),
+ hb_q2d(title->mastering.white_point[0]), hb_q2d(title->mastering.white_point[1]),
+ hb_q2d(title->mastering.min_luminance), hb_q2d(title->mastering.max_luminance));
+ }
+
+ if (title->coll.max_cll || title->coll.max_fall)
+ {
+ hb_log("scan: content light level: max_cll=%u, max_fall=%u",
+ title->coll.max_cll,
+ title->coll.max_fall);
+ }
if (title->video_decode_support != HB_DECODE_SUPPORT_SW)
{
diff --git a/libhb/stream.c b/libhb/stream.c
index 3fe47155b..639ed022e 100644
--- a/libhb/stream.c
+++ b/libhb/stream.c
@@ -5831,6 +5831,19 @@ static hb_title_t *ffmpeg_title_scan( hb_stream_t *stream, hb_title_t *title )
}
break;
}
+ case AV_PKT_DATA_MASTERING_DISPLAY_METADATA:
+ {
+ AVMasteringDisplayMetadata *mastering = (AVMasteringDisplayMetadata *)sd.data;
+ title->mastering = hb_mastering_ff_to_hb(*mastering);
+ break;
+ }
+ case AV_PKT_DATA_CONTENT_LIGHT_LEVEL:
+ {
+ AVContentLightMetadata *coll = (AVContentLightMetadata *)sd.data;
+ title->coll.max_cll = coll->MaxCLL;
+ title->coll.max_fall = coll->MaxFALL;
+ break;
+ }
default:
break;
}
diff --git a/libhb/work.c b/libhb/work.c
index 28ccebcbf..ea313f323 100644
--- a/libhb/work.c
+++ b/libhb/work.c
@@ -593,6 +593,28 @@ void hb_display_job_info(hb_job_t *job)
hb_log(" + color profile: %d-%d-%d",
job->color_prim, job->color_transfer, job->color_matrix);
+
+ if (job->color_transfer == HB_COLR_TRA_SMPTEST2084)
+ {
+ if (job->mastering.has_primaries || job->mastering.has_luminance)
+ {
+ hb_log(" + mastering display metadata: r(%5.4f,%5.4f) g(%5.4f,%5.4f) b(%5.4f %5.4f) wp(%5.4f, %5.4f) min_luminance=%f, max_luminance=%f",
+ hb_q2d(job->mastering.display_primaries[0][0]),
+ hb_q2d(job->mastering.display_primaries[0][1]),
+ hb_q2d(job->mastering.display_primaries[1][0]),
+ hb_q2d(job->mastering.display_primaries[1][1]),
+ hb_q2d(job->mastering.display_primaries[2][0]),
+ hb_q2d(job->mastering.display_primaries[2][1]),
+ hb_q2d(job->mastering.white_point[0]), hb_q2d(job->mastering.white_point[1]),
+ hb_q2d(job->mastering.min_luminance), hb_q2d(job->mastering.max_luminance));
+ }
+ if (job->coll.max_cll && job->coll.max_fall)
+ {
+ hb_log(" + content light level: max_cll=%u, max_fall=%u",
+ job->coll.max_cll,
+ job->coll.max_fall);
+ }
+ }
}
if (job->indepth_scan)