• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Niklas Haas
3  * This file is part of FFmpeg.
4  *
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.
9  *
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.
14  *
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
18  */
19 
20 /**
21  * @file
22  * filter for generating ICC profiles
23  */
24 
25 #include <lcms2.h>
26 
27 #include "libavutil/csp.h"
28 #include "libavutil/opt.h"
29 #include "libavutil/pixdesc.h"
30 
31 #include "avfilter.h"
32 #include "fflcms2.h"
33 #include "internal.h"
34 
35 typedef struct IccDetectContext {
36     const AVClass *class;
37     FFIccContext icc;
38     int force;
39     /* (cached) detected ICC profile values */
40     AVBufferRef *profile;
41     enum AVColorPrimaries profile_prim;
42     enum AVColorTransferCharacteristic profile_trc;
43 } IccDetectContext;
44 
45 #define OFFSET(x) offsetof(IccDetectContext, x)
46 #define VF AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
47 
48 static const AVOption iccdetect_options[] = {
49     { "force", "overwrite existing tags", OFFSET(force), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, VF },
50     { NULL }
51 };
52 
53 AVFILTER_DEFINE_CLASS(iccdetect);
54 
iccdetect_uninit(AVFilterContext * avctx)55 static av_cold void iccdetect_uninit(AVFilterContext *avctx)
56 {
57     IccDetectContext *s = avctx->priv;
58     av_buffer_unref(&s->profile);
59     ff_icc_context_uninit(&s->icc);
60 }
61 
iccdetect_init(AVFilterContext * avctx)62 static av_cold int iccdetect_init(AVFilterContext *avctx)
63 {
64     IccDetectContext *s = avctx->priv;
65     return ff_icc_context_init(&s->icc, avctx);
66 }
67 
iccdetect_filter_frame(AVFilterLink * inlink,AVFrame * frame)68 static int iccdetect_filter_frame(AVFilterLink *inlink, AVFrame *frame)
69 {
70     AVFilterContext *avctx = inlink->dst;
71     IccDetectContext *s = avctx->priv;
72     const AVFrameSideData *sd;
73     AVColorPrimariesDesc coeffs;
74     cmsHPROFILE profile;
75     int ret;
76 
77     sd = av_frame_get_side_data(frame, AV_FRAME_DATA_ICC_PROFILE);
78     if (!sd)
79         return ff_filter_frame(inlink->dst->outputs[0], frame);
80 
81     if (s->profile && s->profile->data == sd->buf->data) {
82         /* No change from previous ICC profile */
83         goto done;
84     }
85 
86     if ((ret = av_buffer_replace(&s->profile, sd->buf)) < 0)
87         return ret;
88     s->profile_prim = AVCOL_PRI_UNSPECIFIED;
89     s->profile_trc = AVCOL_TRC_UNSPECIFIED;
90 
91     profile = cmsOpenProfileFromMemTHR(s->icc.ctx, sd->data, sd->size);
92     if (!profile)
93         return AVERROR_INVALIDDATA;
94 
95     ret = ff_icc_profile_read_primaries(&s->icc, profile, &coeffs);
96     if (!ret)
97         ret = ff_icc_profile_detect_transfer(&s->icc, profile, &s->profile_trc);
98     cmsCloseProfile(profile);
99     if (ret < 0)
100         return ret;
101 
102     s->profile_prim = av_csp_primaries_id_from_desc(&coeffs);
103 
104 done:
105     if (s->profile_prim != AVCOL_PRI_UNSPECIFIED) {
106         if (s->force || frame->color_primaries == AVCOL_PRI_UNSPECIFIED)
107             frame->color_primaries = s->profile_prim;
108     }
109 
110     if (s->profile_trc != AVCOL_TRC_UNSPECIFIED) {
111         if (s->force || frame->color_trc == AVCOL_TRC_UNSPECIFIED)
112             frame->color_trc = s->profile_trc;
113     }
114 
115     return ff_filter_frame(inlink->dst->outputs[0], frame);
116 }
117 
118 static const AVFilterPad iccdetect_inputs[] = {
119     {
120         .name           = "default",
121         .type           = AVMEDIA_TYPE_VIDEO,
122         .filter_frame   = iccdetect_filter_frame,
123     },
124 };
125 
126 static const AVFilterPad iccdetect_outputs[] = {
127     {
128         .name           = "default",
129         .type           = AVMEDIA_TYPE_VIDEO,
130     },
131 };
132 
133 const AVFilter ff_vf_iccdetect = {
134     .name        = "iccdetect",
135     .description = NULL_IF_CONFIG_SMALL("Detect and parse ICC profiles."),
136     .priv_size   = sizeof(IccDetectContext),
137     .priv_class  = &iccdetect_class,
138     .flags       = AVFILTER_FLAG_METADATA_ONLY,
139     .init        = &iccdetect_init,
140     .uninit      = &iccdetect_uninit,
141     FILTER_INPUTS(iccdetect_inputs),
142     FILTER_OUTPUTS(iccdetect_outputs),
143 };
144