2 * Copyright (c) 2022 Niklas Haas
3 * This file is part of FFmpeg.
5 * FFmpeg is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * FFmpeg is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with FFmpeg; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 * filter for generating ICC profiles
27 #include "libavutil/opt.h"
28 #include "libavutil/pixdesc.h"
35 typedef struct IccGenContext
{
42 /* (cached) generated ICC profile */
48 #define OFFSET(x) offsetof(IccGenContext, x)
49 #define VF AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
51 static const AVOption iccgen_options
[] = {
52 {"color_primaries", "select color primaries", OFFSET(color_prim
), AV_OPT_TYPE_INT
, {.i64
=0}, 0, AVCOL_PRI_NB
-1, VF
, .unit
= "color_primaries"},
53 {"auto", "infer based on frame", 0, AV_OPT_TYPE_CONST
, {.i64
=0}, 0, 0, VF
, .unit
= "color_primaries"},
54 {"bt709", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=AVCOL_PRI_BT709
}, 0, 0, VF
, .unit
= "color_primaries"},
55 {"bt470m", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=AVCOL_PRI_BT470M
}, 0, 0, VF
, .unit
= "color_primaries"},
56 {"bt470bg", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=AVCOL_PRI_BT470BG
}, 0, 0, VF
, .unit
= "color_primaries"},
57 {"smpte170m", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=AVCOL_PRI_SMPTE170M
}, 0, 0, VF
, .unit
= "color_primaries"},
58 {"smpte240m", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=AVCOL_PRI_SMPTE240M
}, 0, 0, VF
, .unit
= "color_primaries"},
59 {"film", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=AVCOL_PRI_FILM
}, 0, 0, VF
, .unit
= "color_primaries"},
60 {"bt2020", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=AVCOL_PRI_BT2020
}, 0, 0, VF
, .unit
= "color_primaries"},
61 {"smpte428", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=AVCOL_PRI_SMPTE428
}, 0, 0, VF
, .unit
= "color_primaries"},
62 {"smpte431", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=AVCOL_PRI_SMPTE431
}, 0, 0, VF
, .unit
= "color_primaries"},
63 {"smpte432", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=AVCOL_PRI_SMPTE432
}, 0, 0, VF
, .unit
= "color_primaries"},
64 {"jedec-p22", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=AVCOL_PRI_JEDEC_P22
}, 0, 0, VF
, .unit
= "color_primaries"},
65 {"ebu3213", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=AVCOL_PRI_EBU3213
}, 0, 0, VF
, .unit
= "color_primaries"},
66 {"color_trc", "select color transfer", OFFSET(color_trc
), AV_OPT_TYPE_INT
, {.i64
=0}, 0, AVCOL_TRC_NB
-1, VF
, .unit
= "color_trc"},
67 {"auto", "infer based on frame", 0, AV_OPT_TYPE_CONST
, {.i64
=0}, 0, 0, VF
, .unit
= "color_trc"},
68 {"bt709", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=AVCOL_TRC_BT709
}, 0, 0, VF
, .unit
= "color_trc"},
69 {"bt470m", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=AVCOL_TRC_GAMMA22
}, 0, 0, VF
, .unit
= "color_trc"},
70 {"bt470bg", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=AVCOL_TRC_GAMMA28
}, 0, 0, VF
, .unit
= "color_trc"},
71 {"smpte170m", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=AVCOL_TRC_SMPTE170M
}, 0, 0, VF
, .unit
= "color_trc"},
72 {"smpte240m", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=AVCOL_TRC_SMPTE240M
}, 0, 0, VF
, .unit
= "color_trc"},
73 {"linear", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=AVCOL_TRC_LINEAR
}, 0, 0, VF
, .unit
= "color_trc"},
74 {"iec61966-2-4", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=AVCOL_TRC_IEC61966_2_4
}, 0, 0, VF
, .unit
= "color_trc"},
75 {"bt1361e", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=AVCOL_TRC_BT1361_ECG
}, 0, 0, VF
, .unit
= "color_trc"},
76 {"iec61966-2-1", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=AVCOL_TRC_IEC61966_2_1
}, 0, 0, VF
, .unit
= "color_trc"},
77 {"bt2020-10", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=AVCOL_TRC_BT2020_10
}, 0, 0, VF
, .unit
= "color_trc"},
78 {"bt2020-12", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=AVCOL_TRC_BT2020_12
}, 0, 0, VF
, .unit
= "color_trc"},
79 {"smpte2084", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=AVCOL_TRC_SMPTE2084
}, 0, 0, VF
, .unit
= "color_trc"},
80 {"arib-std-b67", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=AVCOL_TRC_ARIB_STD_B67
}, 0, 0, VF
, .unit
= "color_trc"},
81 { "force", "overwrite existing ICC profile", OFFSET(force
), AV_OPT_TYPE_BOOL
, {.i64
=0}, 0, 1, VF
},
85 AVFILTER_DEFINE_CLASS(iccgen
);
87 static av_cold
void iccgen_uninit(AVFilterContext
*avctx
)
89 IccGenContext
*s
= avctx
->priv
;
90 cmsCloseProfile(s
->profile
);
91 ff_icc_context_uninit(&s
->icc
);
94 static av_cold
int iccgen_init(AVFilterContext
*avctx
)
96 IccGenContext
*s
= avctx
->priv
;
97 return ff_icc_context_init(&s
->icc
, avctx
);
100 static int iccgen_filter_frame(AVFilterLink
*inlink
, AVFrame
*frame
)
102 AVFilterContext
*avctx
= inlink
->dst
;
103 IccGenContext
*s
= avctx
->priv
;
104 enum AVColorTransferCharacteristic trc
;
105 enum AVColorPrimaries prim
;
108 if (av_frame_get_side_data(frame
, AV_FRAME_DATA_ICC_PROFILE
)) {
110 av_frame_remove_side_data(frame
, AV_FRAME_DATA_ICC_PROFILE
);
112 return ff_filter_frame(inlink
->dst
->outputs
[0], frame
);
116 trc
= s
->color_trc
? s
->color_trc
: frame
->color_trc
;
117 if (trc
== AVCOL_TRC_UNSPECIFIED
) {
118 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(frame
->format
);
120 return AVERROR_INVALIDDATA
;
122 if ((desc
->flags
& AV_PIX_FMT_FLAG_RGB
) || frame
->color_range
== AVCOL_RANGE_JPEG
) {
123 /* Default to sRGB for RGB or full-range content */
124 trc
= AVCOL_TRC_IEC61966_2_1
;
126 /* Default to an ITU-R transfer depending on the bit-depth */
127 trc
= desc
->comp
[0].depth
>= 12 ? AVCOL_TRC_BT2020_12
128 : desc
->comp
[0].depth
>= 10 ? AVCOL_TRC_BT2020_10
133 prim
= s
->color_prim
? s
->color_prim
: frame
->color_primaries
;
134 if (prim
== AVCOL_PRI_UNSPECIFIED
) {
135 /* Simply always default to sRGB/BT.709 primaries to avoid surprises */
136 prim
= AVCOL_PRI_BT709
;
139 if (s
->profile
&& prim
!= s
->profile_prim
&& trc
!= s
->profile_trc
) {
140 cmsCloseProfile(s
->profile
);
145 if ((ret
= ff_icc_profile_generate(&s
->icc
, prim
, trc
, &s
->profile
)) < 0)
147 s
->profile_prim
= prim
;
148 s
->profile_trc
= trc
;
151 if ((ret
= ff_icc_profile_attach(&s
->icc
, s
->profile
, frame
)) < 0)
154 return ff_filter_frame(inlink
->dst
->outputs
[0], frame
);
157 static const AVFilterPad iccgen_inputs
[] = {
160 .type
= AVMEDIA_TYPE_VIDEO
,
161 .filter_frame
= iccgen_filter_frame
,
165 const FFFilter ff_vf_iccgen
= {
167 .p
.description
= NULL_IF_CONFIG_SMALL("Generate and attach ICC profiles."),
168 .p
.priv_class
= &iccgen_class
,
169 .p
.flags
= AVFILTER_FLAG_METADATA_ONLY
,
170 .priv_size
= sizeof(IccGenContext
),
171 .init
= &iccgen_init
,
172 .uninit
= &iccgen_uninit
,
173 FILTER_INPUTS(iccgen_inputs
),
174 FILTER_OUTPUTS(ff_video_default_filterpad
),