1 |
|
|
/* |
2 |
|
|
* Copyright (c) 2016 Ronald S. Bultje <rsbultje@gmail.com> |
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 |
|
|
#include "libavutil/frame.h" |
21 |
|
|
#include "libavutil/mastering_display_metadata.h" |
22 |
|
|
#include "libavutil/pixdesc.h" |
23 |
|
|
|
24 |
|
|
#include "colorspace.h" |
25 |
|
|
|
26 |
|
|
|
27 |
|
|
void ff_matrix_invert_3x3(const double in[3][3], double out[3][3]) |
28 |
|
|
{ |
29 |
|
|
double m00 = in[0][0], m01 = in[0][1], m02 = in[0][2], |
30 |
|
|
m10 = in[1][0], m11 = in[1][1], m12 = in[1][2], |
31 |
|
|
m20 = in[2][0], m21 = in[2][1], m22 = in[2][2]; |
32 |
|
|
int i, j; |
33 |
|
|
double det; |
34 |
|
|
|
35 |
|
|
out[0][0] = (m11 * m22 - m21 * m12); |
36 |
|
|
out[0][1] = -(m01 * m22 - m21 * m02); |
37 |
|
|
out[0][2] = (m01 * m12 - m11 * m02); |
38 |
|
|
out[1][0] = -(m10 * m22 - m20 * m12); |
39 |
|
|
out[1][1] = (m00 * m22 - m20 * m02); |
40 |
|
|
out[1][2] = -(m00 * m12 - m10 * m02); |
41 |
|
|
out[2][0] = (m10 * m21 - m20 * m11); |
42 |
|
|
out[2][1] = -(m00 * m21 - m20 * m01); |
43 |
|
|
out[2][2] = (m00 * m11 - m10 * m01); |
44 |
|
|
|
45 |
|
|
det = m00 * out[0][0] + m10 * out[0][1] + m20 * out[0][2]; |
46 |
|
|
det = 1.0 / det; |
47 |
|
|
|
48 |
|
|
for (i = 0; i < 3; i++) { |
49 |
|
|
for (j = 0; j < 3; j++) |
50 |
|
|
out[i][j] *= det; |
51 |
|
|
} |
52 |
|
|
} |
53 |
|
|
|
54 |
|
|
void ff_matrix_mul_3x3(double dst[3][3], |
55 |
|
|
const double src1[3][3], const double src2[3][3]) |
56 |
|
|
{ |
57 |
|
|
int m, n; |
58 |
|
|
|
59 |
|
|
for (m = 0; m < 3; m++) |
60 |
|
|
for (n = 0; n < 3; n++) |
61 |
|
|
dst[m][n] = src2[m][0] * src1[0][n] + |
62 |
|
|
src2[m][1] * src1[1][n] + |
63 |
|
|
src2[m][2] * src1[2][n]; |
64 |
|
|
} |
65 |
|
|
/* |
66 |
|
|
* see e.g. http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html |
67 |
|
|
*/ |
68 |
|
|
void ff_fill_rgb2xyz_table(const struct PrimaryCoefficients *coeffs, |
69 |
|
|
const struct WhitepointCoefficients *wp, |
70 |
|
|
double rgb2xyz[3][3]) |
71 |
|
|
{ |
72 |
|
|
double i[3][3], sr, sg, sb, zw; |
73 |
|
|
|
74 |
|
|
rgb2xyz[0][0] = coeffs->xr / coeffs->yr; |
75 |
|
|
rgb2xyz[0][1] = coeffs->xg / coeffs->yg; |
76 |
|
|
rgb2xyz[0][2] = coeffs->xb / coeffs->yb; |
77 |
|
|
rgb2xyz[1][0] = rgb2xyz[1][1] = rgb2xyz[1][2] = 1.0; |
78 |
|
|
rgb2xyz[2][0] = (1.0 - coeffs->xr - coeffs->yr) / coeffs->yr; |
79 |
|
|
rgb2xyz[2][1] = (1.0 - coeffs->xg - coeffs->yg) / coeffs->yg; |
80 |
|
|
rgb2xyz[2][2] = (1.0 - coeffs->xb - coeffs->yb) / coeffs->yb; |
81 |
|
|
ff_matrix_invert_3x3(rgb2xyz, i); |
82 |
|
|
zw = 1.0 - wp->xw - wp->yw; |
83 |
|
|
sr = i[0][0] * wp->xw + i[0][1] * wp->yw + i[0][2] * zw; |
84 |
|
|
sg = i[1][0] * wp->xw + i[1][1] * wp->yw + i[1][2] * zw; |
85 |
|
|
sb = i[2][0] * wp->xw + i[2][1] * wp->yw + i[2][2] * zw; |
86 |
|
|
rgb2xyz[0][0] *= sr; |
87 |
|
|
rgb2xyz[0][1] *= sg; |
88 |
|
|
rgb2xyz[0][2] *= sb; |
89 |
|
|
rgb2xyz[1][0] *= sr; |
90 |
|
|
rgb2xyz[1][1] *= sg; |
91 |
|
|
rgb2xyz[1][2] *= sb; |
92 |
|
|
rgb2xyz[2][0] *= sr; |
93 |
|
|
rgb2xyz[2][1] *= sg; |
94 |
|
|
rgb2xyz[2][2] *= sb; |
95 |
|
|
} |
96 |
|
|
static const double ycgco_matrix[3][3] = |
97 |
|
|
{ |
98 |
|
|
{ 0.25, 0.5, 0.25 }, |
99 |
|
|
{ -0.25, 0.5, -0.25 }, |
100 |
|
|
{ 0.5, 0, -0.5 }, |
101 |
|
|
}; |
102 |
|
|
|
103 |
|
|
static const double gbr_matrix[3][3] = |
104 |
|
|
{ |
105 |
|
|
{ 0, 1, 0 }, |
106 |
|
|
{ 0, -0.5, 0.5 }, |
107 |
|
|
{ 0.5, -0.5, 0 }, |
108 |
|
|
}; |
109 |
|
|
|
110 |
|
|
/* |
111 |
|
|
* All constants explained in e.g. https://linuxtv.org/downloads/v4l-dvb-apis/ch02s06.html |
112 |
|
|
* The older ones (bt470bg/m) are also explained in their respective ITU docs |
113 |
|
|
* (e.g. https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.470-5-199802-S!!PDF-E.pdf) |
114 |
|
|
* whereas the newer ones can typically be copied directly from wikipedia :) |
115 |
|
|
*/ |
116 |
|
|
static const struct LumaCoefficients luma_coefficients[AVCOL_SPC_NB] = { |
117 |
|
|
[AVCOL_SPC_FCC] = { 0.30, 0.59, 0.11 }, |
118 |
|
|
[AVCOL_SPC_BT470BG] = { 0.299, 0.587, 0.114 }, |
119 |
|
|
[AVCOL_SPC_SMPTE170M] = { 0.299, 0.587, 0.114 }, |
120 |
|
|
[AVCOL_SPC_BT709] = { 0.2126, 0.7152, 0.0722 }, |
121 |
|
|
[AVCOL_SPC_SMPTE240M] = { 0.212, 0.701, 0.087 }, |
122 |
|
|
[AVCOL_SPC_YCOCG] = { 0.25, 0.5, 0.25 }, |
123 |
|
|
[AVCOL_SPC_RGB] = { 1, 1, 1 }, |
124 |
|
|
[AVCOL_SPC_BT2020_NCL] = { 0.2627, 0.6780, 0.0593 }, |
125 |
|
|
[AVCOL_SPC_BT2020_CL] = { 0.2627, 0.6780, 0.0593 }, |
126 |
|
|
}; |
127 |
|
|
|
128 |
|
|
const struct LumaCoefficients *ff_get_luma_coefficients(enum AVColorSpace csp) |
129 |
|
|
{ |
130 |
|
|
const struct LumaCoefficients *coeffs; |
131 |
|
|
|
132 |
|
|
if (csp >= AVCOL_SPC_NB) |
133 |
|
|
return NULL; |
134 |
|
|
coeffs = &luma_coefficients[csp]; |
135 |
|
|
if (!coeffs->cr) |
136 |
|
|
return NULL; |
137 |
|
|
|
138 |
|
|
return coeffs; |
139 |
|
|
} |
140 |
|
|
|
141 |
|
|
void ff_fill_rgb2yuv_table(const struct LumaCoefficients *coeffs, |
142 |
|
|
double rgb2yuv[3][3]) |
143 |
|
|
{ |
144 |
|
|
double bscale, rscale; |
145 |
|
|
|
146 |
|
|
// special ycgco matrix |
147 |
|
|
if (coeffs->cr == 0.25 && coeffs->cg == 0.5 && coeffs->cb == 0.25) { |
148 |
|
|
memcpy(rgb2yuv, ycgco_matrix, sizeof(double) * 9); |
149 |
|
|
return; |
150 |
|
|
} else if (coeffs->cr == 1 && coeffs->cg == 1 && coeffs->cb == 1) { |
151 |
|
|
memcpy(rgb2yuv, gbr_matrix, sizeof(double) * 9); |
152 |
|
|
return; |
153 |
|
|
} |
154 |
|
|
|
155 |
|
|
rgb2yuv[0][0] = coeffs->cr; |
156 |
|
|
rgb2yuv[0][1] = coeffs->cg; |
157 |
|
|
rgb2yuv[0][2] = coeffs->cb; |
158 |
|
|
bscale = 0.5 / (coeffs->cb - 1.0); |
159 |
|
|
rscale = 0.5 / (coeffs->cr - 1.0); |
160 |
|
|
rgb2yuv[1][0] = bscale * coeffs->cr; |
161 |
|
|
rgb2yuv[1][1] = bscale * coeffs->cg; |
162 |
|
|
rgb2yuv[1][2] = 0.5; |
163 |
|
|
rgb2yuv[2][0] = 0.5; |
164 |
|
|
rgb2yuv[2][1] = rscale * coeffs->cg; |
165 |
|
|
rgb2yuv[2][2] = rscale * coeffs->cb; |
166 |
|
|
} |
167 |
|
|
|
168 |
|
|
double ff_determine_signal_peak(AVFrame *in) |
169 |
|
|
{ |
170 |
|
|
AVFrameSideData *sd = av_frame_get_side_data(in, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); |
171 |
|
|
double peak = 0; |
172 |
|
|
|
173 |
|
|
if (sd) { |
174 |
|
|
AVContentLightMetadata *clm = (AVContentLightMetadata *)sd->data; |
175 |
|
|
peak = clm->MaxCLL / REFERENCE_WHITE; |
176 |
|
|
} |
177 |
|
|
|
178 |
|
|
sd = av_frame_get_side_data(in, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); |
179 |
|
|
if (!peak && sd) { |
180 |
|
|
AVMasteringDisplayMetadata *metadata = (AVMasteringDisplayMetadata *)sd->data; |
181 |
|
|
if (metadata->has_luminance) |
182 |
|
|
peak = av_q2d(metadata->max_luminance) / REFERENCE_WHITE; |
183 |
|
|
} |
184 |
|
|
|
185 |
|
|
// For untagged source, use peak of 10000 if SMPTE ST.2084 |
186 |
|
|
// otherwise assume HLG with reference display peak 1000. |
187 |
|
|
if (!peak) |
188 |
|
|
peak = in->color_trc == AVCOL_TRC_SMPTE2084 ? 100.0f : 10.0f; |
189 |
|
|
|
190 |
|
|
return peak; |
191 |
|
|
} |
192 |
|
|
|
193 |
|
|
void ff_update_hdr_metadata(AVFrame *in, double peak) |
194 |
|
|
{ |
195 |
|
|
AVFrameSideData *sd = av_frame_get_side_data(in, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); |
196 |
|
|
|
197 |
|
|
if (sd) { |
198 |
|
|
AVContentLightMetadata *clm = (AVContentLightMetadata *)sd->data; |
199 |
|
|
clm->MaxCLL = (unsigned)(peak * REFERENCE_WHITE); |
200 |
|
|
} |
201 |
|
|
|
202 |
|
|
sd = av_frame_get_side_data(in, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); |
203 |
|
|
if (sd) { |
204 |
|
|
AVMasteringDisplayMetadata *metadata = (AVMasteringDisplayMetadata *)sd->data; |
205 |
|
|
if (metadata->has_luminance) |
206 |
|
|
metadata->max_luminance = av_d2q(peak * REFERENCE_WHITE, 10000); |
207 |
|
|
} |
208 |
|
|
} |