This is an automated email from the git hooks/post-receive script.
Git pushed a commit to branch master
in repository ffmpeg.
The following commit(s) were added to refs/heads/master by this push:
new 5942e4e900 avcodec/liboapvenc: Added support for mastering display
color volume and content light level metadata
5942e4e900 is described below
commit 5942e4e90023ea4c91f6f16132b90c0cd3534eea
Author: Dawid Kozinski <[email protected]>
AuthorDate: Fri Jan 9 11:58:58 2026 +0100
Commit: James Almer <[email protected]>
CommitDate: Thu Jun 18 09:54:51 2026 -0300
avcodec/liboapvenc: Added support for mastering display color volume and
content light level metadata
---
libavcodec/liboapvenc.c | 154 ++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 142 insertions(+), 12 deletions(-)
diff --git a/libavcodec/liboapvenc.c b/libavcodec/liboapvenc.c
index 92640b74a3..727e1bf84c 100644
--- a/libavcodec/liboapvenc.c
+++ b/libavcodec/liboapvenc.c
@@ -33,6 +33,7 @@
#include "libavutil/opt.h"
#include "libavutil/pixdesc.h"
#include "libavutil/pixfmt.h"
+#include "libavutil/mastering_display_metadata.h"
#include "avcodec.h"
#include "apv.h"
@@ -45,6 +46,13 @@
#define FRM_IDX (0) // supports only 1-frame in an access unit
#define MAX_NUM_CC (OAPV_MAX_CC) // Max number of color components (upto
4:4:4:4)
+#define MAX_METADATA_PAYLOADS (8)
+
+static inline int64_t rescale_rational(AVRational a, int b)
+{
+ return av_rescale(a.num, b, a.den);
+}
+
/**
* The structure stores all the states associated with the instance of APV
encoder
*/
@@ -63,6 +71,12 @@ typedef struct ApvEncContext {
int qp; // quantization parameter (QP) [0,63]
+ oapvm_payload_mdcv_t mdcv;
+ oapvm_payload_cll_t cll;
+
+ oapvm_payload_t *payloads;
+ unsigned nb_payloads;
+
AVDictionary *oapv_params;
} ApvEncContext;
@@ -262,6 +276,37 @@ fail:
return NULL;
}
+static int apv_metadata_add_payload(const AVCodecContext *avctx, ApvEncContext
*apv,
+ uint32_t group_id, uint32_t type, const
void *data, uint32_t size)
+{
+ oapvm_payload_t *tmp;
+
+ if (apv->nb_payloads >= MAX_METADATA_PAYLOADS || !data || size == 0) {
+ return AVERROR_INVALIDDATA;
+ }
+
+ tmp = av_realloc_array(apv->payloads, apv->nb_payloads + 1,
sizeof(oapvm_payload_t));
+ if (!tmp) {
+ return AVERROR(ENOMEM);
+ }
+ apv->payloads = tmp;
+
+ // Copying data into internal buffer
+ void *new_data = av_malloc(size);
+ if (!new_data) {
+ return AVERROR(ENOMEM);
+ }
+ memcpy(new_data, data, size);
+
+ apv->payloads[apv->nb_payloads].group_id = group_id;
+ apv->payloads[apv->nb_payloads].type = type;
+ apv->payloads[apv->nb_payloads].size = size;
+ apv->payloads[apv->nb_payloads].data = new_data;
+ apv->nb_payloads++;
+
+ return 0;
+}
+
/**
* Populate the liboapv configuration from AVCodecContext and encoder options.
*
@@ -346,10 +391,68 @@ static int get_conf(AVCodecContext *avctx, oapve_cdesc_t
*cdsc)
cdsc->max_num_frms = MAX_NUM_FRMS;
const AVDictionaryEntry *en = NULL;
- while (en = av_dict_iterate(apv->oapv_params, en)) {
+ while ((en = av_dict_iterate(apv->oapv_params, en))) {
ret = oapve_param_parse(&cdsc->param[FRM_IDX], en->key, en->value);
- if (ret < 0)
+ if (ret < 0) {
av_log(avctx, AV_LOG_WARNING, "Error parsing option '%s = %s'.\n",
en->key, en->value);
+ }
+ }
+
+ return 0;
+}
+
+static int handle_side_data(AVCodecContext *avctx, ApvEncContext *apv)
+{
+ int size = 0;
+ uint8_t payload[64];
+
+ const AVFrameSideData *cll_sd =
+ av_frame_side_data_get(avctx->decoded_side_data,
+ avctx->nb_decoded_side_data, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL);
+ const AVFrameSideData *mdcv_sd =
+ av_frame_side_data_get(avctx->decoded_side_data,
+ avctx->nb_decoded_side_data,
+ AV_FRAME_DATA_MASTERING_DISPLAY_METADATA);
+
+ if (cll_sd) {
+ const AVContentLightMetadata *cll = (AVContentLightMetadata
*)cll_sd->data;
+
+ apv->cll.max_cll = cll->MaxCLL;
+ apv->cll.max_fall = cll->MaxFALL;
+
+ oapvm_write_cll(&apv->cll, payload, &size);
+
+ int ret = apv_metadata_add_payload(avctx, apv, 1, OAPV_METADATA_CLL,
payload, size);
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_WARNING, "Error adding content light
metadata\n");
+ return ret;
+ }
+ }
+
+ if (mdcv_sd) {
+ AVMasteringDisplayMetadata *mdcv = (AVMasteringDisplayMetadata
*)mdcv_sd->data;
+
+ // According to APV specification, i = 0, 1, 2 specifies Red, Green,
Blue respectively
+ apv->mdcv.primary_chromaticity_x[0] =
rescale_rational(mdcv->display_primaries[0][0], 50000); // Red X
+ apv->mdcv.primary_chromaticity_y[0] =
rescale_rational(mdcv->display_primaries[0][1], 50000); // Red Y
+ apv->mdcv.primary_chromaticity_x[1] =
rescale_rational(mdcv->display_primaries[1][0], 50000); // Green X
+ apv->mdcv.primary_chromaticity_y[1] =
rescale_rational(mdcv->display_primaries[1][1], 50000); // Green Y
+ apv->mdcv.primary_chromaticity_x[2] =
rescale_rational(mdcv->display_primaries[2][0], 50000); // Blue X
+ apv->mdcv.primary_chromaticity_y[2] =
rescale_rational(mdcv->display_primaries[2][1], 50000); // Blue Y
+
+ apv->mdcv.white_point_chromaticity_x =
rescale_rational(mdcv->white_point[0], 50000);
+ apv->mdcv.white_point_chromaticity_y =
rescale_rational(mdcv->white_point[1], 50000);
+
+ apv->mdcv.max_mastering_luminance =
rescale_rational(mdcv->max_luminance, 10000);
+ apv->mdcv.min_mastering_luminance =
rescale_rational(mdcv->min_luminance, 10000);
+
+ oapvm_write_mdcv(&apv->mdcv, payload, &size);
+
+ int ret = apv_metadata_add_payload(avctx, apv, 1, OAPV_METADATA_MDCV,
payload, size);
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_WARNING, "Error adding master display
metadata\n");
+ return ret;
+ }
}
ret = validate_profile(avctx, cdsc->param[FRM_IDX].profile_idc);
@@ -375,6 +478,9 @@ static av_cold int liboapve_init(AVCodecContext *avctx)
unsigned char *bs_buf;
int ret;
+ apv->nb_payloads = 0;
+ apv->payloads = NULL;
+
/* allocate bitstream buffer */
bs_buf = (unsigned char *)av_malloc(MAX_BS_BUF);
if (bs_buf == NULL) {
@@ -407,6 +513,21 @@ static av_cold int liboapve_init(AVCodecContext *avctx)
return AVERROR_EXTERNAL;
}
+ ret = handle_side_data(avctx, apv);
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Failed handling side data! (%s)\n",
+ av_err2str(ret));
+ return ret;
+ }
+
+ if (apv->nb_payloads > 0) {
+ ret = oapvm_set_all(apv->mid, apv->payloads, apv->nb_payloads);
+ if (OAPV_FAILED(ret)) {
+ av_log(avctx, AV_LOG_ERROR, "cannot set metadata\n");
+ return AVERROR_EXTERNAL;
+ }
+ }
+
int value = OAPV_CFG_VAL_AU_BS_FMT_NONE;
int size = 4;
ret = oapve_config(apv->id, OAPV_CFG_SET_AU_BS_FMT, &value, &size);
@@ -420,7 +541,7 @@ static av_cold int liboapve_init(AVCodecContext *avctx)
return AVERROR(ENOMEM);
apv->ifrms.num_frms++;
- /* color description values */
+ /* color description values */
if (cdsc->param[FRM_IDX].color_description_present_flag) {
avctx->color_primaries = cdsc->param[FRM_IDX].color_primaries;
avctx->color_trc = cdsc->param[FRM_IDX].transfer_characteristics;
@@ -504,6 +625,14 @@ static av_cold int liboapve_close(AVCodecContext *avctx)
{
ApvEncContext *apv = avctx->priv_data;
+ for (unsigned int i = 0; i < apv->nb_payloads; i++) {
+ if (apv->payloads[i].data) {
+ av_free(apv->payloads[i].data);
+ apv->payloads[i].data = NULL;
+ }
+ }
+ av_free(apv->payloads);
+
for (int i = 0; i < apv->ifrms.num_frms; i++) {
if (apv->ifrms.frm[i].imgb != NULL)
apv->ifrms.frm[i].imgb->release(apv->ifrms.frm[i].imgb);
@@ -534,15 +663,16 @@ static av_cold int liboapve_close(AVCodecContext *avctx)
static const AVOption liboapv_options[] = {
{ "preset", "Encoding preset for setting encoding speed (optimization
level control)", OFFSET(preset_id), AV_OPT_TYPE_INT, { .i64 =
OAPV_PRESET_DEFAULT }, OAPV_PRESET_FASTEST, OAPV_PRESET_PLACEBO, VE, .unit =
"preset" },
- { "fastest", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OAPV_PRESET_FASTEST },
INT_MIN, INT_MAX, VE, .unit = "preset" },
- { "fast", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OAPV_PRESET_FAST },
INT_MIN, INT_MAX, VE, .unit = "preset" },
- { "medium", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OAPV_PRESET_MEDIUM },
INT_MIN, INT_MAX, VE, .unit = "preset" },
- { "slow", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OAPV_PRESET_SLOW },
INT_MIN, INT_MAX, VE, .unit = "preset" },
- { "placebo", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OAPV_PRESET_PLACEBO },
INT_MIN, INT_MAX, VE, .unit = "preset" },
- { "default", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OAPV_PRESET_DEFAULT },
INT_MIN, INT_MAX, VE, .unit = "preset" },
-
- { "qp", "Quantization parameter value for CQP rate control mode",
OFFSET(qp), AV_OPT_TYPE_INT, { .i64 = 32 }, 0, 63, VE },
- { "oapv-params", "Override the apv configuration using a :-separated list
of key=value parameters", OFFSET(oapv_params), AV_OPT_TYPE_DICT, { 0 }, 0, 0,
VE },
+ { "fastest", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OAPV_PRESET_FASTEST },
0, 0, VE, .unit = "preset" },
+ { "fast", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OAPV_PRESET_FAST },
0, 0, VE, .unit = "preset" },
+ { "medium", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OAPV_PRESET_MEDIUM },
0, 0, VE, .unit = "preset" },
+ { "slow", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OAPV_PRESET_SLOW },
0, 0, VE, .unit = "preset" },
+ { "placebo", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OAPV_PRESET_PLACEBO },
0, 0, VE, .unit = "preset" },
+ { "default", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OAPV_PRESET_DEFAULT },
0, 0, VE, .unit = "preset" },
+
+ { "qp", "Quantization parameter value for CQP rate control mode",
OFFSET(qp), AV_OPT_TYPE_INT, { .i64 = 32 }, 0, 63, VE, .unit = NULL },
+ { "oapv-params", "Override the apv configuration using a :-separated list
of key=value parameters", OFFSET(oapv_params), AV_OPT_TYPE_DICT, { 0 }, 0, 0,
VE, .unit = NULL },
+
{ NULL }
};
_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]