diff options
-rw-r--r-- | libhb/colorspace.c | 19 | ||||
-rw-r--r-- | libhb/common.c | 2 | ||||
-rw-r--r-- | libhb/decavcodec.c | 23 | ||||
-rw-r--r-- | libhb/encx265.c | 116 | ||||
-rw-r--r-- | libhb/handbrake/common.h | 29 | ||||
-rw-r--r-- | libhb/handbrake/hbffmpeg.h | 4 | ||||
-rw-r--r-- | libhb/handbrake/hbtypes.h | 2 | ||||
-rw-r--r-- | libhb/hb_json.c | 115 | ||||
-rw-r--r-- | libhb/hbffmpeg.c | 56 | ||||
-rw-r--r-- | libhb/muxavformat.c | 40 | ||||
-rw-r--r-- | libhb/scan.c | 25 | ||||
-rw-r--r-- | libhb/stream.c | 13 | ||||
-rw-r--r-- | libhb/work.c | 22 |
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) |