Branch data Line data Source code
1 : : /*
2 : : * This file is part of libplacebo.
3 : : *
4 : : * libplacebo is free software; you can redistribute it and/or
5 : : * modify it under the terms of the GNU Lesser General Public
6 : : * License as published by the Free Software Foundation; either
7 : : * version 2.1 of the License, or (at your option) any later version.
8 : : *
9 : : * libplacebo is distributed in the hope that it will be useful,
10 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : : * GNU Lesser General Public License for more details.
13 : : *
14 : : * You should have received a copy of the GNU Lesser General Public
15 : : * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
16 : : */
17 : :
18 : : #ifndef LIBPLACEBO_LIBAV_H_
19 : : #error This header should be included as part of <libplacebo/utils/libav.h>
20 : : #elif defined(__cplusplus)
21 : : #error This header cannot be included from C++ define PL_LIBAV_IMPLEMENTATION appropriately
22 : : #else
23 : :
24 : : #include <assert.h>
25 : :
26 : : #include <libplacebo/utils/dolbyvision.h>
27 : :
28 : : #include <libavutil/hwcontext.h>
29 : : #include <libavutil/hwcontext_drm.h>
30 : : #include <libavutil/imgutils.h>
31 : : #include <libavutil/pixdesc.h>
32 : : #include <libavutil/display.h>
33 : : #include <libavformat/version.h>
34 : : #include <libavcodec/version.h>
35 : :
36 : : // Try importing <vulkan.h> dynamically if it wasn't already
37 : : #if !defined(VK_API_VERSION_1_2) && defined(__has_include)
38 : : # if __has_include(<vulkan/vulkan.h>)
39 : : # include <vulkan/vulkan.h>
40 : : # endif
41 : : #endif
42 : :
43 : : #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(58, 11, 100) && \
44 : : defined(PL_HAVE_VULKAN) && defined(VK_API_VERSION_1_2) && \
45 : : VK_HEADER_VERSION >= 175
46 : : # define PL_HAVE_LAV_VULKAN
47 : : # include <libavutil/hwcontext_vulkan.h>
48 : : # include <libplacebo/vulkan.h>
49 : : #endif
50 : :
51 : : PL_LIBAV_API enum pl_color_system pl_system_from_av(enum AVColorSpace spc)
52 : : {
53 : : switch (spc) {
54 : : case AVCOL_SPC_RGB: return PL_COLOR_SYSTEM_RGB;
55 : : case AVCOL_SPC_BT709: return PL_COLOR_SYSTEM_BT_709;
56 : : case AVCOL_SPC_UNSPECIFIED: return PL_COLOR_SYSTEM_UNKNOWN;
57 : : case AVCOL_SPC_RESERVED: return PL_COLOR_SYSTEM_UNKNOWN;
58 : : case AVCOL_SPC_FCC: return PL_COLOR_SYSTEM_UNKNOWN; // missing
59 : : case AVCOL_SPC_BT470BG: return PL_COLOR_SYSTEM_BT_601;
60 : : case AVCOL_SPC_SMPTE170M: return PL_COLOR_SYSTEM_BT_601;
61 : : case AVCOL_SPC_SMPTE240M: return PL_COLOR_SYSTEM_SMPTE_240M;
62 : : case AVCOL_SPC_YCGCO: return PL_COLOR_SYSTEM_YCGCO;
63 : : case AVCOL_SPC_BT2020_NCL: return PL_COLOR_SYSTEM_BT_2020_NC;
64 : : case AVCOL_SPC_BT2020_CL: return PL_COLOR_SYSTEM_BT_2020_C;
65 : : case AVCOL_SPC_SMPTE2085: return PL_COLOR_SYSTEM_UNKNOWN; // missing
66 : : case AVCOL_SPC_CHROMA_DERIVED_NCL: return PL_COLOR_SYSTEM_UNKNOWN; // missing
67 : : case AVCOL_SPC_CHROMA_DERIVED_CL: return PL_COLOR_SYSTEM_UNKNOWN; // missing
68 : : // Note: this colorspace is confused between PQ and HLG, which libav*
69 : : // requires inferring from other sources, but libplacebo makes explicit.
70 : : // Default to PQ as it's the more common scenario.
71 : : case AVCOL_SPC_ICTCP: return PL_COLOR_SYSTEM_BT_2100_PQ;
72 : : case AVCOL_SPC_NB: return PL_COLOR_SYSTEM_COUNT;
73 : : }
74 : :
75 : : return PL_COLOR_SYSTEM_UNKNOWN;
76 : : }
77 : :
78 : : PL_LIBAV_API enum AVColorSpace pl_system_to_av(enum pl_color_system sys)
79 : : {
80 : : switch (sys) {
81 : : case PL_COLOR_SYSTEM_UNKNOWN: return AVCOL_SPC_UNSPECIFIED;
82 : : case PL_COLOR_SYSTEM_BT_601: return AVCOL_SPC_SMPTE170M;
83 : : case PL_COLOR_SYSTEM_BT_709: return AVCOL_SPC_BT709;
84 : : case PL_COLOR_SYSTEM_SMPTE_240M: return AVCOL_SPC_SMPTE240M;
85 : : case PL_COLOR_SYSTEM_BT_2020_NC: return AVCOL_SPC_BT2020_NCL;
86 : : case PL_COLOR_SYSTEM_BT_2020_C: return AVCOL_SPC_BT2020_CL;
87 : : case PL_COLOR_SYSTEM_BT_2100_PQ: return AVCOL_SPC_ICTCP;
88 : : case PL_COLOR_SYSTEM_BT_2100_HLG: return AVCOL_SPC_ICTCP;
89 : : case PL_COLOR_SYSTEM_DOLBYVISION: return AVCOL_SPC_UNSPECIFIED; // missing
90 : : case PL_COLOR_SYSTEM_YCGCO: return AVCOL_SPC_YCGCO;
91 : : case PL_COLOR_SYSTEM_RGB: return AVCOL_SPC_RGB;
92 : : case PL_COLOR_SYSTEM_XYZ: return AVCOL_SPC_UNSPECIFIED; // handled differently
93 : : case PL_COLOR_SYSTEM_COUNT: return AVCOL_SPC_NB;
94 : : }
95 : :
96 : : return AVCOL_SPC_UNSPECIFIED;
97 : : }
98 : :
99 : : PL_LIBAV_API enum pl_color_levels pl_levels_from_av(enum AVColorRange range)
100 : : {
101 : : switch (range) {
102 : : case AVCOL_RANGE_UNSPECIFIED: return PL_COLOR_LEVELS_UNKNOWN;
103 : : case AVCOL_RANGE_MPEG: return PL_COLOR_LEVELS_LIMITED;
104 : : case AVCOL_RANGE_JPEG: return PL_COLOR_LEVELS_FULL;
105 : : case AVCOL_RANGE_NB: return PL_COLOR_LEVELS_COUNT;
106 : : }
107 : :
108 : : return PL_COLOR_LEVELS_UNKNOWN;
109 : : }
110 : :
111 : : PL_LIBAV_API enum AVColorRange pl_levels_to_av(enum pl_color_levels levels)
112 : : {
113 : : switch (levels) {
114 : : case PL_COLOR_LEVELS_UNKNOWN: return AVCOL_RANGE_UNSPECIFIED;
115 : : case PL_COLOR_LEVELS_LIMITED: return AVCOL_RANGE_MPEG;
116 : : case PL_COLOR_LEVELS_FULL: return AVCOL_RANGE_JPEG;
117 : : case PL_COLOR_LEVELS_COUNT: return AVCOL_RANGE_NB;
118 : : }
119 : :
120 : : return AVCOL_RANGE_UNSPECIFIED;
121 : : }
122 : :
123 : : PL_LIBAV_API enum pl_color_primaries pl_primaries_from_av(enum AVColorPrimaries prim)
124 : : {
125 : : switch (prim) {
126 : : case AVCOL_PRI_RESERVED0: return PL_COLOR_PRIM_UNKNOWN;
127 : : case AVCOL_PRI_BT709: return PL_COLOR_PRIM_BT_709;
128 : : case AVCOL_PRI_UNSPECIFIED: return PL_COLOR_PRIM_UNKNOWN;
129 : : case AVCOL_PRI_RESERVED: return PL_COLOR_PRIM_UNKNOWN;
130 : : case AVCOL_PRI_BT470M: return PL_COLOR_PRIM_BT_470M;
131 : : case AVCOL_PRI_BT470BG: return PL_COLOR_PRIM_BT_601_625;
132 : : case AVCOL_PRI_SMPTE170M: return PL_COLOR_PRIM_BT_601_525;
133 : : case AVCOL_PRI_SMPTE240M: return PL_COLOR_PRIM_BT_601_525;
134 : : case AVCOL_PRI_FILM: return PL_COLOR_PRIM_FILM_C;
135 : : case AVCOL_PRI_BT2020: return PL_COLOR_PRIM_BT_2020;
136 : : case AVCOL_PRI_SMPTE428: return PL_COLOR_PRIM_CIE_1931;
137 : : case AVCOL_PRI_SMPTE431: return PL_COLOR_PRIM_DCI_P3;
138 : : case AVCOL_PRI_SMPTE432: return PL_COLOR_PRIM_DISPLAY_P3;
139 : : case AVCOL_PRI_JEDEC_P22: return PL_COLOR_PRIM_EBU_3213;
140 : : case AVCOL_PRI_NB: return PL_COLOR_PRIM_COUNT;
141 : : }
142 : :
143 : : return PL_COLOR_PRIM_UNKNOWN;
144 : : }
145 : :
146 : : PL_LIBAV_API enum AVColorPrimaries pl_primaries_to_av(enum pl_color_primaries prim)
147 : : {
148 : : switch (prim) {
149 : : case PL_COLOR_PRIM_UNKNOWN: return AVCOL_PRI_UNSPECIFIED;
150 : : case PL_COLOR_PRIM_BT_601_525: return AVCOL_PRI_SMPTE170M;
151 : : case PL_COLOR_PRIM_BT_601_625: return AVCOL_PRI_BT470BG;
152 : : case PL_COLOR_PRIM_BT_709: return AVCOL_PRI_BT709;
153 : : case PL_COLOR_PRIM_BT_470M: return AVCOL_PRI_BT470M;
154 : : case PL_COLOR_PRIM_EBU_3213: return AVCOL_PRI_JEDEC_P22;
155 : : case PL_COLOR_PRIM_BT_2020: return AVCOL_PRI_BT2020;
156 : : case PL_COLOR_PRIM_APPLE: return AVCOL_PRI_UNSPECIFIED; // missing
157 : : case PL_COLOR_PRIM_ADOBE: return AVCOL_PRI_UNSPECIFIED; // missing
158 : : case PL_COLOR_PRIM_PRO_PHOTO: return AVCOL_PRI_UNSPECIFIED; // missing
159 : : case PL_COLOR_PRIM_CIE_1931: return AVCOL_PRI_SMPTE428;
160 : : case PL_COLOR_PRIM_DCI_P3: return AVCOL_PRI_SMPTE431;
161 : : case PL_COLOR_PRIM_DISPLAY_P3: return AVCOL_PRI_SMPTE432;
162 : : case PL_COLOR_PRIM_V_GAMUT: return AVCOL_PRI_UNSPECIFIED; // missing
163 : : case PL_COLOR_PRIM_S_GAMUT: return AVCOL_PRI_UNSPECIFIED; // missing
164 : : case PL_COLOR_PRIM_FILM_C: return AVCOL_PRI_FILM;
165 : : case PL_COLOR_PRIM_ACES_AP0: return AVCOL_PRI_UNSPECIFIED; // missing
166 : : case PL_COLOR_PRIM_ACES_AP1: return AVCOL_PRI_UNSPECIFIED; // missing
167 : : case PL_COLOR_PRIM_COUNT: return AVCOL_PRI_NB;
168 : : }
169 : :
170 : : return AVCOL_PRI_UNSPECIFIED;
171 : : }
172 : :
173 : : PL_LIBAV_API enum pl_color_transfer pl_transfer_from_av(enum AVColorTransferCharacteristic trc)
174 : : {
175 : : switch (trc) {
176 : : case AVCOL_TRC_RESERVED0: return PL_COLOR_TRC_UNKNOWN;
177 : : case AVCOL_TRC_BT709: return PL_COLOR_TRC_BT_1886; // EOTF != OETF
178 : : case AVCOL_TRC_UNSPECIFIED: return PL_COLOR_TRC_UNKNOWN;
179 : : case AVCOL_TRC_RESERVED: return PL_COLOR_TRC_UNKNOWN;
180 : : case AVCOL_TRC_GAMMA22: return PL_COLOR_TRC_GAMMA22;
181 : : case AVCOL_TRC_GAMMA28: return PL_COLOR_TRC_GAMMA28;
182 : : case AVCOL_TRC_SMPTE170M: return PL_COLOR_TRC_BT_1886; // EOTF != OETF
183 : : case AVCOL_TRC_SMPTE240M: return PL_COLOR_TRC_BT_1886; // EOTF != OETF
184 : : case AVCOL_TRC_LINEAR: return PL_COLOR_TRC_LINEAR;
185 : : case AVCOL_TRC_LOG: return PL_COLOR_TRC_UNKNOWN; // missing
186 : : case AVCOL_TRC_LOG_SQRT: return PL_COLOR_TRC_UNKNOWN; // missing
187 : : case AVCOL_TRC_IEC61966_2_4: return PL_COLOR_TRC_BT_1886; // EOTF != OETF
188 : : case AVCOL_TRC_BT1361_ECG: return PL_COLOR_TRC_BT_1886; // ETOF != OETF
189 : : case AVCOL_TRC_IEC61966_2_1: return PL_COLOR_TRC_SRGB;
190 : : case AVCOL_TRC_BT2020_10: return PL_COLOR_TRC_BT_1886; // EOTF != OETF
191 : : case AVCOL_TRC_BT2020_12: return PL_COLOR_TRC_BT_1886; // EOTF != OETF
192 : : case AVCOL_TRC_SMPTE2084: return PL_COLOR_TRC_PQ;
193 : : case AVCOL_TRC_SMPTE428: return PL_COLOR_TRC_ST428;
194 : : case AVCOL_TRC_ARIB_STD_B67: return PL_COLOR_TRC_HLG;
195 : : case AVCOL_TRC_NB: return PL_COLOR_TRC_COUNT;
196 : : }
197 : :
198 : : return PL_COLOR_TRC_UNKNOWN;
199 : : }
200 : :
201 : : PL_LIBAV_API enum AVColorTransferCharacteristic pl_transfer_to_av(enum pl_color_transfer trc)
202 : : {
203 : : switch (trc) {
204 : : case PL_COLOR_TRC_UNKNOWN: return AVCOL_TRC_UNSPECIFIED;
205 : : case PL_COLOR_TRC_BT_1886: return AVCOL_TRC_BT709; // EOTF != OETF
206 : : case PL_COLOR_TRC_SRGB: return AVCOL_TRC_IEC61966_2_1;
207 : : case PL_COLOR_TRC_LINEAR: return AVCOL_TRC_LINEAR;
208 : : case PL_COLOR_TRC_GAMMA18: return AVCOL_TRC_UNSPECIFIED; // missing
209 : : case PL_COLOR_TRC_GAMMA20: return AVCOL_TRC_UNSPECIFIED; // missing
210 : : case PL_COLOR_TRC_GAMMA22: return AVCOL_TRC_GAMMA22;
211 : : case PL_COLOR_TRC_GAMMA24: return AVCOL_TRC_UNSPECIFIED; // missing
212 : : case PL_COLOR_TRC_GAMMA26: return AVCOL_TRC_UNSPECIFIED; // missing
213 : : case PL_COLOR_TRC_GAMMA28: return AVCOL_TRC_GAMMA28;
214 : : case PL_COLOR_TRC_ST428: return AVCOL_TRC_SMPTE428;
215 : : case PL_COLOR_TRC_PRO_PHOTO: return AVCOL_TRC_UNSPECIFIED; // missing
216 : : case PL_COLOR_TRC_PQ: return AVCOL_TRC_SMPTE2084;
217 : : case PL_COLOR_TRC_HLG: return AVCOL_TRC_ARIB_STD_B67;
218 : : case PL_COLOR_TRC_V_LOG: return AVCOL_TRC_UNSPECIFIED; // missing
219 : : case PL_COLOR_TRC_S_LOG1: return AVCOL_TRC_UNSPECIFIED; // missing
220 : : case PL_COLOR_TRC_S_LOG2: return AVCOL_TRC_UNSPECIFIED; // missing
221 : : case PL_COLOR_TRC_COUNT: return AVCOL_TRC_NB;
222 : : }
223 : :
224 : : return AVCOL_TRC_UNSPECIFIED;
225 : : }
226 : :
227 : : PL_LIBAV_API enum pl_chroma_location pl_chroma_from_av(enum AVChromaLocation loc)
228 : : {
229 : : switch (loc) {
230 : : case AVCHROMA_LOC_UNSPECIFIED: return PL_CHROMA_UNKNOWN;
231 : : case AVCHROMA_LOC_LEFT: return PL_CHROMA_LEFT;
232 : : case AVCHROMA_LOC_CENTER: return PL_CHROMA_CENTER;
233 : : case AVCHROMA_LOC_TOPLEFT: return PL_CHROMA_TOP_LEFT;
234 : : case AVCHROMA_LOC_TOP: return PL_CHROMA_TOP_CENTER;
235 : : case AVCHROMA_LOC_BOTTOMLEFT: return PL_CHROMA_BOTTOM_LEFT;
236 : : case AVCHROMA_LOC_BOTTOM: return PL_CHROMA_BOTTOM_CENTER;
237 : : case AVCHROMA_LOC_NB: return PL_CHROMA_COUNT;
238 : : }
239 : :
240 : : return PL_CHROMA_UNKNOWN;
241 : : }
242 : :
243 : : PL_LIBAV_API enum AVChromaLocation pl_chroma_to_av(enum pl_chroma_location loc)
244 : : {
245 : : switch (loc) {
246 : : case PL_CHROMA_UNKNOWN: return AVCHROMA_LOC_UNSPECIFIED;
247 : : case PL_CHROMA_LEFT: return AVCHROMA_LOC_LEFT;
248 : : case PL_CHROMA_CENTER: return AVCHROMA_LOC_CENTER;
249 : : case PL_CHROMA_TOP_LEFT: return AVCHROMA_LOC_TOPLEFT;
250 : : case PL_CHROMA_TOP_CENTER: return AVCHROMA_LOC_TOP;
251 : : case PL_CHROMA_BOTTOM_LEFT: return AVCHROMA_LOC_BOTTOMLEFT;
252 : : case PL_CHROMA_BOTTOM_CENTER: return AVCHROMA_LOC_BOTTOM;
253 : : case PL_CHROMA_COUNT: return AVCHROMA_LOC_NB;
254 : : }
255 : :
256 : : return AVCHROMA_LOC_UNSPECIFIED;
257 : : }
258 : :
259 : : #ifdef PL_HAVE_LAV_HDR
260 : 2 : PL_LIBAV_API void pl_map_hdr_metadata(struct pl_hdr_metadata *out,
261 : : const struct pl_av_hdr_metadata *data)
262 : : {
263 [ + + ]: 2 : if (data->mdm) {
264 [ + - ]: 1 : if (data->mdm->has_luminance) {
265 : 1 : out->max_luma = av_q2d(data->mdm->max_luminance);
266 : 1 : out->min_luma = av_q2d(data->mdm->min_luminance);
267 [ + - - + ]: 1 : if (out->max_luma < 5.0 || out->min_luma >= out->max_luma)
268 : 0 : out->max_luma = out->min_luma = 0; /* sanity */
269 : : }
270 [ + - ]: 1 : if (data->mdm->has_primaries) {
271 : 1 : out->prim = (struct pl_raw_primaries) {
272 : 1 : .red.x = av_q2d(data->mdm->display_primaries[0][0]),
273 : 1 : .red.y = av_q2d(data->mdm->display_primaries[0][1]),
274 : 1 : .green.x = av_q2d(data->mdm->display_primaries[1][0]),
275 : 1 : .green.y = av_q2d(data->mdm->display_primaries[1][1]),
276 : 1 : .blue.x = av_q2d(data->mdm->display_primaries[2][0]),
277 : 1 : .blue.y = av_q2d(data->mdm->display_primaries[2][1]),
278 : 1 : .white.x = av_q2d(data->mdm->white_point[0]),
279 : 1 : .white.y = av_q2d(data->mdm->white_point[1]),
280 : : };
281 : : }
282 : : }
283 : :
284 [ - + ]: 2 : if (data->clm) {
285 : 0 : out->max_cll = data->clm->MaxCLL;
286 : 0 : out->max_fall = data->clm->MaxFALL;
287 : : }
288 : :
289 [ - + - - ]: 2 : if (data->dhp && data->dhp->application_version < 2) {
290 : : float hist_max = 0;
291 : : const AVHDRPlusColorTransformParams *pars = &data->dhp->params[0];
292 [ # # ]: 0 : assert(data->dhp->num_windows > 0);
293 : 0 : out->scene_max[0] = 10000 * av_q2d(pars->maxscl[0]);
294 : 0 : out->scene_max[1] = 10000 * av_q2d(pars->maxscl[1]);
295 : 0 : out->scene_max[2] = 10000 * av_q2d(pars->maxscl[2]);
296 : 0 : out->scene_avg = 10000 * av_q2d(pars->average_maxrgb);
297 : :
298 : : // Calculate largest value from histogram to use as fallback for clips
299 : : // with missing MaxSCL information. Note that this may end up picking
300 : : // the "reserved" value at the 5% percentile, which in practice appears
301 : : // to track the brightest pixel in the scene.
302 [ # # ]: 0 : for (int i = 0; i < pars->num_distribution_maxrgb_percentiles; i++) {
303 : 0 : float hist_val = av_q2d(pars->distribution_maxrgb[i].percentile);
304 [ # # ]: 0 : if (hist_val > hist_max)
305 : : hist_max = hist_val;
306 : : }
307 : 0 : hist_max *= 10000;
308 [ # # ]: 0 : if (!out->scene_max[0])
309 : 0 : out->scene_max[0] = hist_max;
310 [ # # ]: 0 : if (!out->scene_max[1])
311 : 0 : out->scene_max[1] = hist_max;
312 [ # # ]: 0 : if (!out->scene_max[2])
313 : 0 : out->scene_max[2] = hist_max;
314 : :
315 [ # # ]: 0 : if (pars->tone_mapping_flag == 1) {
316 : 0 : out->ootf.target_luma = av_q2d(data->dhp->targeted_system_display_maximum_luminance);
317 : 0 : out->ootf.knee_x = av_q2d(pars->knee_point_x);
318 : 0 : out->ootf.knee_y = av_q2d(pars->knee_point_y);
319 [ # # ]: 0 : assert(pars->num_bezier_curve_anchors < 16);
320 [ # # ]: 0 : for (int i = 0; i < pars->num_bezier_curve_anchors; i++)
321 : 0 : out->ootf.anchors[i] = av_q2d(pars->bezier_curve_anchors[i]);
322 : 0 : out->ootf.num_anchors = pars->num_bezier_curve_anchors;
323 : : }
324 : : }
325 : 2 : }
326 : : #endif // PL_HAVE_LAV_HDR
327 : :
328 : : static inline void *pl_get_side_data_raw(const AVFrame *frame,
329 : : enum AVFrameSideDataType type)
330 : : {
331 : 6 : const AVFrameSideData *sd = av_frame_get_side_data(frame, type);
332 [ + + - + : 6 : return sd ? (void *) sd->data : NULL;
- + ]
333 : : }
334 : :
335 : 2 : PL_LIBAV_API void pl_color_space_from_avframe(struct pl_color_space *out_csp,
336 : : const AVFrame *frame)
337 : : {
338 : 2 : *out_csp = (struct pl_color_space) {
339 [ + - ]: 2 : .primaries = pl_primaries_from_av(frame->color_primaries),
340 [ + - ]: 2 : .transfer = pl_transfer_from_av(frame->color_trc),
341 : : };
342 : :
343 : : #ifdef PL_HAVE_LAV_HDR
344 : 6 : pl_map_hdr_metadata(&out_csp->hdr, &(struct pl_av_hdr_metadata) {
345 : : .mdm = pl_get_side_data_raw(frame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA),
346 : : .clm = pl_get_side_data_raw(frame, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL),
347 : : .dhp = pl_get_side_data_raw(frame, AV_FRAME_DATA_DYNAMIC_HDR_PLUS),
348 : : });
349 : : #endif
350 : 2 : }
351 : :
352 : : PL_LIBAV_API enum pl_field pl_field_from_avframe(const AVFrame *frame)
353 : : {
354 : : #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(58, 7, 100)
355 : : if (!frame || !(frame->flags & AV_FRAME_FLAG_INTERLACED))
356 : : return PL_FIELD_NONE;
357 : : return (frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)
358 : : ? PL_FIELD_TOP : PL_FIELD_BOTTOM;
359 : : #else
360 : : if (!frame || !frame->interlaced_frame)
361 : : return PL_FIELD_NONE;
362 : : return frame->top_field_first ? PL_FIELD_TOP : PL_FIELD_BOTTOM;
363 : : #endif
364 : : }
365 : :
366 : : #ifdef PL_HAVE_LAV_FILM_GRAIN
367 : 0 : PL_LIBAV_API void pl_film_grain_from_av(struct pl_film_grain_data *out_data,
368 : : const AVFilmGrainParams *fgp)
369 : : {
370 : 0 : out_data->seed = fgp->seed;
371 : :
372 [ # # # ]: 0 : switch (fgp->type) {
373 : : case AV_FILM_GRAIN_PARAMS_NONE: break;
374 : 0 : case AV_FILM_GRAIN_PARAMS_AV1: {
375 : : const AVFilmGrainAOMParams *src = &fgp->codec.aom;
376 : : struct pl_av1_grain_data *dst = &out_data->params.av1;
377 : 0 : out_data->type = PL_FILM_GRAIN_AV1;
378 : 0 : *dst = (struct pl_av1_grain_data) {
379 : 0 : .num_points_y = src->num_y_points,
380 : 0 : .chroma_scaling_from_luma = src->chroma_scaling_from_luma,
381 : 0 : .num_points_uv = { src->num_uv_points[0], src->num_uv_points[1] },
382 : 0 : .scaling_shift = src->scaling_shift,
383 : 0 : .ar_coeff_lag = src->ar_coeff_lag,
384 : 0 : .ar_coeff_shift = src->ar_coeff_shift,
385 : 0 : .grain_scale_shift = src->grain_scale_shift,
386 : 0 : .uv_mult = { src->uv_mult[0], src->uv_mult[1] },
387 : 0 : .uv_mult_luma = { src->uv_mult_luma[0], src->uv_mult_luma[1] },
388 : 0 : .uv_offset = { src->uv_offset[0], src->uv_offset[1] },
389 : 0 : .overlap = src->overlap_flag,
390 : : };
391 : :
392 : : assert(sizeof(dst->ar_coeffs_uv) == sizeof(src->ar_coeffs_uv));
393 : 0 : memcpy(dst->points_y, src->y_points, sizeof(dst->points_y));
394 : 0 : memcpy(dst->points_uv, src->uv_points, sizeof(dst->points_uv));
395 : 0 : memcpy(dst->ar_coeffs_y, src->ar_coeffs_y, sizeof(dst->ar_coeffs_y));
396 : 0 : memcpy(dst->ar_coeffs_uv, src->ar_coeffs_uv, sizeof(dst->ar_coeffs_uv));
397 : : break;
398 : : }
399 : : #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 2, 100)
400 : 0 : case AV_FILM_GRAIN_PARAMS_H274: {
401 : : const AVFilmGrainH274Params *src = &fgp->codec.h274;
402 : : struct pl_h274_grain_data *dst = &out_data->params.h274;
403 : 0 : out_data->type = PL_FILM_GRAIN_H274;
404 : 0 : *dst = (struct pl_h274_grain_data) {
405 : 0 : .model_id = src->model_id,
406 : 0 : .blending_mode_id = src->blending_mode_id,
407 : 0 : .log2_scale_factor = src->log2_scale_factor,
408 : : .component_model_present = {
409 : 0 : src->component_model_present[0],
410 : 0 : src->component_model_present[1],
411 : 0 : src->component_model_present[2],
412 : : },
413 : : .intensity_interval_lower_bound = {
414 : 0 : src->intensity_interval_lower_bound[0],
415 : 0 : src->intensity_interval_lower_bound[1],
416 : 0 : src->intensity_interval_lower_bound[2],
417 : : },
418 : : .intensity_interval_upper_bound = {
419 : 0 : src->intensity_interval_upper_bound[0],
420 : 0 : src->intensity_interval_upper_bound[1],
421 : 0 : src->intensity_interval_upper_bound[2],
422 : : },
423 : : .comp_model_value = {
424 : 0 : src->comp_model_value[0],
425 : 0 : src->comp_model_value[1],
426 : 0 : src->comp_model_value[2],
427 : : },
428 : : };
429 : 0 : memcpy(dst->num_intensity_intervals, src->num_intensity_intervals,
430 : : sizeof(dst->num_intensity_intervals));
431 : 0 : memcpy(dst->num_model_values, src->num_model_values,
432 : : sizeof(dst->num_model_values));
433 : : break;
434 : : }
435 : : #endif
436 : : }
437 : 0 : }
438 : : #endif // PL_HAVE_LAV_FILM_GRAIN
439 : :
440 : : static inline int pl_plane_data_num_comps(const struct pl_plane_data *data)
441 : : {
442 [ + + ]: 1098 : for (int i = 0; i < 4; i++) {
443 [ + + ]: 1077 : if (data->component_size[i] == 0)
444 : : return i;
445 : : }
446 : :
447 : : return 4;
448 : : }
449 : :
450 : 258 : PL_LIBAV_API int pl_plane_data_from_pixfmt(struct pl_plane_data out_data[4],
451 : : struct pl_bit_encoding *out_bits,
452 : : enum AVPixelFormat pix_fmt)
453 : : {
454 : 258 : const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
455 : 258 : int planes = av_pix_fmt_count_planes(pix_fmt);
456 : : struct pl_plane_data aligned_data[4];
457 : : struct pl_bit_encoding bits;
458 : : bool first;
459 [ - + ]: 258 : if (!desc || planes < 0) // e.g. AV_PIX_FMT_NONE
460 : : return 0;
461 : :
462 [ + + ]: 258 : if (desc->flags & AV_PIX_FMT_FLAG_BITSTREAM) {
463 : : // Bitstream formats will most likely never be supported
464 : : return 0;
465 : : }
466 : :
467 [ + + ]: 253 : if (desc->flags & AV_PIX_FMT_FLAG_PAL) {
468 : : // Palette formats are (currently) not supported
469 : : return 0;
470 : : }
471 : :
472 [ + + ]: 252 : if (desc->flags & AV_PIX_FMT_FLAG_BAYER) {
473 : : // Bayer format don't have valid `desc->offset` values, so we can't
474 : : // use `pl_plane_data_from_mask` on them.
475 : : return 0;
476 : : }
477 : :
478 [ + + ]: 240 : if (desc->nb_components == 0 || desc->nb_components > 4) {
479 : : // Bogus components, possibly fake/virtual/hwaccel format?
480 : : return 0;
481 : : }
482 : :
483 [ - + ]: 226 : if (planes > 4)
484 : : return 0; // This shouldn't ever happen
485 : :
486 : : // Fill in the details for each plane
487 [ + + ]: 694 : for (int p = 0; p < planes; p++) {
488 : 492 : struct pl_plane_data *data = &out_data[p];
489 : 492 : int size[4] = {0};
490 : 492 : int shift[4] = {0};
491 : 492 : data->swapped = desc->flags & AV_PIX_FMT_FLAG_BE;
492 : 984 : data->type = (desc->flags & AV_PIX_FMT_FLAG_FLOAT)
493 : : ? PL_FMT_FLOAT
494 [ + + ]: 492 : : PL_FMT_UNORM;
495 : :
496 : 492 : data->pixel_stride = 0;
497 : :
498 [ + + ]: 2042 : for (int c = 0; c < desc->nb_components; c++) {
499 : : const AVComponentDescriptor *comp = &desc->comp[c];
500 [ + + ]: 1574 : if (comp->plane != p)
501 : 909 : continue;
502 [ + + + + ]: 665 : if (data->swapped && comp->shift) {
503 : : // We cannot naively handle packed big endian formats because
504 : : // swapping the words also swaps the component order, so just
505 : : // exit out as a stupid safety measure
506 : 24 : return 0;
507 : : }
508 : :
509 : 647 : size[c] = comp->depth;
510 : 647 : shift[c] = comp->shift + comp->offset * 8;
511 : :
512 [ + + + + ]: 647 : if (data->pixel_stride && (int) data->pixel_stride != comp->step) {
513 : : // Pixel format contains components with different pixel stride
514 : : // (e.g. packed YUYV), this is currently not supported
515 : : return 0;
516 : : }
517 : 641 : data->pixel_stride = comp->step;
518 : : }
519 : :
520 : 468 : pl_plane_data_from_comps(data, size, shift);
521 : : }
522 : :
523 [ - + ]: 202 : if (!out_bits)
524 : : return planes;
525 : :
526 : : // Attempt aligning all of the planes for optimum compatibility
527 : : first = true;
528 [ + + ]: 656 : for (int p = 0; p < planes; p++) {
529 : 468 : aligned_data[p] = out_data[p];
530 : :
531 : : // Planes with only an alpha component should be ignored
532 [ + + ]: 468 : if (pl_plane_data_num_comps(&aligned_data[p]) == 1 &&
533 [ + + ]: 384 : aligned_data[p].component_map[0] == PL_CHANNEL_A)
534 : : {
535 : 37 : continue;
536 : : }
537 : :
538 [ + + ]: 431 : if (!pl_plane_data_align(&aligned_data[p], &bits))
539 : 14 : goto misaligned;
540 : :
541 [ + + ]: 417 : if (first) {
542 : 188 : *out_bits = bits;
543 : : first = false;
544 : : } else {
545 [ - + ]: 229 : if (!pl_bit_encoding_equal(&bits, out_bits))
546 : 0 : goto misaligned;
547 : : }
548 : : }
549 : :
550 : : // Overwrite the planes by their aligned versions
551 [ + + ]: 642 : for (int p = 0; p < planes; p++)
552 : 454 : out_data[p] = aligned_data[p];
553 : :
554 : : return planes;
555 : :
556 : 14 : misaligned:
557 : 14 : *out_bits = (struct pl_bit_encoding) {0};
558 : 14 : return planes;
559 : : }
560 : :
561 : : PL_LIBAV_API bool pl_test_pixfmt_caps(pl_gpu gpu, enum AVPixelFormat pixfmt,
562 : : enum pl_fmt_caps caps)
563 : : {
564 : : struct pl_bit_encoding bits;
565 : : struct pl_plane_data data[4];
566 : : pl_fmt fmt;
567 : : int planes;
568 : :
569 : : switch (pixfmt) {
570 : : case AV_PIX_FMT_DRM_PRIME:
571 : : case AV_PIX_FMT_VAAPI:
572 : : return gpu->import_caps.tex & PL_HANDLE_DMA_BUF;
573 : :
574 : : #ifdef PL_HAVE_LAV_VULKAN
575 : : case AV_PIX_FMT_VULKAN:
576 : : return pl_vulkan_get(gpu);
577 : : #endif
578 : :
579 : : default: break;
580 : : }
581 : :
582 : : planes = pl_plane_data_from_pixfmt(data, &bits, pixfmt);
583 : : if (!planes)
584 : : return false;
585 : :
586 : : for (int i = 0; i < planes; i++) {
587 : : data[i].row_stride = 0;
588 : : fmt = pl_plane_find_fmt(gpu, NULL, &data[i]);
589 : : if (!fmt || (fmt->caps & caps) != caps)
590 : : return false;
591 : :
592 : : }
593 : :
594 : : return true;
595 : : }
596 : :
597 : : PL_LIBAV_API bool pl_test_pixfmt(pl_gpu gpu, enum AVPixelFormat pixfmt)
598 : : {
599 : : return pl_test_pixfmt_caps(gpu, pixfmt, 0);
600 : : }
601 : :
602 : 1 : PL_LIBAV_API void pl_avframe_set_color(AVFrame *frame, struct pl_color_space csp)
603 : : {
604 : : const AVFrameSideData *sd;
605 : : (void) sd;
606 : :
607 [ + - ]: 1 : frame->color_primaries = pl_primaries_to_av(csp.primaries);
608 [ + - ]: 1 : frame->color_trc = pl_transfer_to_av(csp.transfer);
609 : :
610 : : #ifdef PL_HAVE_LAV_HDR
611 [ - + ]: 1 : if (csp.hdr.max_cll) {
612 : 0 : sd = av_frame_get_side_data(frame, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL);
613 [ # # ]: 0 : if (!sd) {
614 : 0 : sd = av_frame_new_side_data(frame, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL,
615 : : sizeof(AVContentLightMetadata));
616 : : }
617 : :
618 [ # # ]: 0 : if (sd) {
619 : 0 : AVContentLightMetadata *clm = (AVContentLightMetadata *) sd->data;
620 : 0 : *clm = (AVContentLightMetadata) {
621 : : .MaxCLL = csp.hdr.max_cll,
622 : 0 : .MaxFALL = csp.hdr.max_fall,
623 : : };
624 : : }
625 : : }
626 : :
627 [ - + - - ]: 1 : if (csp.hdr.max_luma || csp.hdr.prim.red.x) {
628 : 1 : sd = av_frame_get_side_data(frame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA);
629 [ + - ]: 1 : if (!sd) {
630 : 1 : sd = av_frame_new_side_data(frame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA,
631 : : sizeof(AVMasteringDisplayMetadata));
632 : : }
633 : :
634 [ + - ]: 1 : if (sd) {
635 : 1 : AVMasteringDisplayMetadata *mdm = (AVMasteringDisplayMetadata *) sd->data;
636 : 1 : *mdm = (AVMasteringDisplayMetadata) {
637 : 1 : .max_luminance = av_d2q(csp.hdr.max_luma, 1000000),
638 : 1 : .min_luminance = av_d2q(csp.hdr.min_luma, 1000000),
639 : 1 : .has_luminance = !!csp.hdr.max_luma,
640 : : .display_primaries = {
641 : : {
642 : 1 : av_d2q(csp.hdr.prim.red.x, 1000000),
643 : 1 : av_d2q(csp.hdr.prim.red.y, 1000000),
644 : : }, {
645 : 1 : av_d2q(csp.hdr.prim.green.x, 1000000),
646 : 1 : av_d2q(csp.hdr.prim.green.y, 1000000),
647 : : }, {
648 : 1 : av_d2q(csp.hdr.prim.blue.x, 1000000),
649 : 1 : av_d2q(csp.hdr.prim.blue.y, 1000000),
650 : : }
651 : : },
652 : : .white_point = {
653 : 1 : av_d2q(csp.hdr.prim.white.x, 1000000),
654 : 1 : av_d2q(csp.hdr.prim.white.y, 1000000),
655 : : },
656 : 1 : .has_primaries = !!csp.hdr.prim.red.x,
657 : : };
658 : : }
659 : : }
660 : : #endif // PL_HAVE_LAV_HDR
661 : 1 : }
662 : :
663 : : PL_LIBAV_API void pl_avframe_set_repr(AVFrame *frame, struct pl_color_repr repr)
664 : : {
665 [ + - ]: 1 : frame->colorspace = pl_system_to_av(repr.sys);
666 : 1 : frame->color_range = pl_levels_to_av(repr.levels);
667 : :
668 : : // No real way to map repr.bits, the image format already has to match
669 : : }
670 : :
671 : 1 : PL_LIBAV_API void pl_avframe_set_profile(AVFrame *frame, struct pl_icc_profile profile)
672 : : {
673 : : const AVFrameSideData *sd;
674 : 1 : av_frame_remove_side_data(frame, AV_FRAME_DATA_ICC_PROFILE);
675 : :
676 [ - + ]: 1 : if (!profile.len)
677 : : return;
678 : :
679 : 0 : sd = av_frame_new_side_data(frame, AV_FRAME_DATA_ICC_PROFILE, profile.len);
680 : 0 : memcpy(sd->data, profile.data, profile.len);
681 : : }
682 : :
683 : 2 : PL_LIBAV_API void pl_frame_from_avframe(struct pl_frame *out,
684 : : const AVFrame *frame)
685 : : {
686 : 2 : const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format);
687 : 2 : int planes = av_pix_fmt_count_planes(frame->format);
688 : : const AVFrameSideData *sd;
689 [ - + ]: 2 : assert(desc);
690 : :
691 [ - + ]: 2 : if (desc->flags & AV_PIX_FMT_FLAG_HWACCEL) {
692 : 0 : const AVHWFramesContext *hwfc = (AVHWFramesContext *) frame->hw_frames_ctx->data;
693 : 0 : desc = av_pix_fmt_desc_get(hwfc->sw_format);
694 : 0 : planes = av_pix_fmt_count_planes(hwfc->sw_format);
695 : : }
696 : :
697 : : // This should never fail, and there's nothing really useful we can do in
698 : : // this failure case anyway, since this is a `void` function.
699 [ - + ]: 2 : assert(planes <= 4);
700 : :
701 : 2 : *out = (struct pl_frame) {
702 : : .num_planes = planes,
703 : : .crop = {
704 : 2 : .x0 = frame->crop_left,
705 : 2 : .y0 = frame->crop_top,
706 : 2 : .x1 = frame->width - frame->crop_right,
707 : 2 : .y1 = frame->height - frame->crop_bottom,
708 : : },
709 : : .repr = {
710 [ + - ]: 2 : .sys = pl_system_from_av(frame->colorspace),
711 [ + + ]: 2 : .levels = pl_levels_from_av(frame->color_range),
712 : 2 : .alpha = (desc->flags & AV_PIX_FMT_FLAG_ALPHA)
713 : : ? PL_ALPHA_INDEPENDENT
714 [ - + ]: 2 : : PL_ALPHA_NONE,
715 : :
716 : : // For sake of simplicity, just use the first component's depth as
717 : : // the authoritative color depth for the whole image. Usually, this
718 : : // will be overwritten by more specific information when using e.g.
719 : : // `pl_map_avframe`, but for the sake of e.g. users wishing to map
720 : : // hwaccel frames manually, this is a good default.
721 : 2 : .bits.color_depth = desc->comp[0].depth,
722 : : },
723 : : };
724 : :
725 : 2 : pl_color_space_from_avframe(&out->color, frame);
726 : :
727 [ - + ]: 2 : if (frame->colorspace == AVCOL_SPC_ICTCP &&
728 : : frame->color_trc == AVCOL_TRC_ARIB_STD_B67)
729 : : {
730 : : // libav* makes no distinction between PQ and HLG ICtCp, so we need
731 : : // to manually fix it in the case that we have HLG ICtCp data.
732 : 0 : out->repr.sys = PL_COLOR_SYSTEM_BT_2100_HLG;
733 : :
734 [ - + ]: 2 : } else if (strncmp(desc->name, "xyz", 3) == 0) {
735 : :
736 : : // libav* handles this as a special case, but doesn't provide an
737 : : // explicit flag for it either, so we have to resort to this ugly
738 : : // hack...
739 : 0 : out->repr.sys = PL_COLOR_SYSTEM_XYZ;
740 : :
741 [ + - ]: 2 : } else if (desc->flags & AV_PIX_FMT_FLAG_RGB) {
742 : :
743 : 2 : out->repr.sys = PL_COLOR_SYSTEM_RGB;
744 : 2 : out->repr.levels = PL_COLOR_LEVELS_FULL; // libav* ignores levels for RGB
745 : :
746 [ # # ]: 0 : } else if (!pl_color_system_is_ycbcr_like(out->repr.sys)) {
747 : : // libav* likes leaving this as UNKNOWN (or even RGB) for YCbCr frames,
748 : : // which confuses libplacebo since we infer UNKNOWN as RGB. To get
749 : : // around this, explicitly infer a suitable colorspace.
750 : 0 : out->repr.sys = pl_color_system_guess_ycbcr(frame->width, frame->height);
751 : : }
752 : :
753 [ - + ]: 2 : if ((sd = av_frame_get_side_data(frame, AV_FRAME_DATA_ICC_PROFILE))) {
754 : 0 : out->profile = (struct pl_icc_profile) {
755 : 0 : .data = sd->data,
756 : 0 : .len = sd->size,
757 : : };
758 : :
759 : : // Needed to ensure profile uniqueness
760 : 0 : pl_icc_profile_compute_signature(&out->profile);
761 : : }
762 : :
763 [ - + ]: 2 : if ((sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX))) {
764 : 0 : double rot = av_display_rotation_get((const int32_t *) sd->data);
765 : 0 : out->rotation = pl_rotation_normalize(4.5 - rot / 90.0);
766 : : }
767 : :
768 : : #ifdef PL_HAVE_LAV_FILM_GRAIN
769 [ - + ]: 2 : if ((sd = av_frame_get_side_data(frame, AV_FRAME_DATA_FILM_GRAIN_PARAMS)))
770 : 0 : pl_film_grain_from_av(&out->film_grain, (AVFilmGrainParams *) sd->data);
771 : : #endif // HAVE_LAV_FILM_GRAIN
772 : :
773 [ + + ]: 4 : for (int p = 0; p < out->num_planes; p++) {
774 : : struct pl_plane *plane = &out->planes[p];
775 : :
776 : : // Fill in the component mapping array
777 [ + + ]: 10 : for (int c = 0; c < desc->nb_components; c++) {
778 [ + - ]: 8 : if (desc->comp[c].plane == p)
779 : 8 : plane->component_mapping[plane->components++] = c;
780 : : }
781 : :
782 : : // Clear the superfluous components
783 [ - + ]: 2 : for (int c = plane->components; c < 4; c++)
784 : 0 : plane->component_mapping[c] = PL_CHANNEL_NONE;
785 : : }
786 : :
787 : : // Only set the chroma location for definitely subsampled images, makes no
788 : : // sense otherwise
789 [ - + ]: 2 : if (desc->log2_chroma_w || desc->log2_chroma_h) {
790 [ # # ]: 0 : enum pl_chroma_location loc = pl_chroma_from_av(frame->chroma_location);
791 : 0 : pl_frame_set_chroma_location(out, loc);
792 : : }
793 : 2 : }
794 : :
795 : : #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(60, 15, 100)
796 : : PL_LIBAV_API const uint8_t *pl_av_stream_get_side_data(const AVStream *st,
797 : : enum AVPacketSideDataType type)
798 : : {
799 : : const AVPacketSideData *sd;
800 : : sd = av_packet_side_data_get(st->codecpar->coded_side_data,
801 : : st->codecpar->nb_coded_side_data,
802 : : type);
803 : : return sd ? sd->data : NULL;
804 : : }
805 : : #else
806 : : # define pl_av_stream_get_side_data(st, type) av_stream_get_side_data(st, type, NULL)
807 : : #endif
808 : :
809 : : PL_LIBAV_API void pl_frame_copy_stream_props(struct pl_frame *out,
810 : : const AVStream *stream)
811 : : {
812 : : const uint8_t *sd;
813 : : if ((sd = pl_av_stream_get_side_data(stream, AV_PKT_DATA_DISPLAYMATRIX))) {
814 : : double rot = av_display_rotation_get((const int32_t *) sd);
815 : : out->rotation = pl_rotation_normalize(4.5 - rot / 90.0);
816 : : }
817 : :
818 : : #ifdef PL_HAVE_LAV_HDR
819 : : pl_map_hdr_metadata(&out->color.hdr, &(struct pl_av_hdr_metadata) {
820 : : .mdm = (void *) pl_av_stream_get_side_data(stream,
821 : : AV_PKT_DATA_MASTERING_DISPLAY_METADATA),
822 : : .clm = (void *) pl_av_stream_get_side_data(stream,
823 : : AV_PKT_DATA_CONTENT_LIGHT_LEVEL),
824 : : # if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 2, 100)
825 : : .dhp = (void *) pl_av_stream_get_side_data(stream,
826 : : AV_PKT_DATA_DYNAMIC_HDR10_PLUS),
827 : : # endif
828 : : });
829 : : #endif
830 : : }
831 : :
832 : : #undef pl_av_stream_get_side_data
833 : :
834 : : #ifdef PL_HAVE_LAV_DOLBY_VISION
835 : : PL_LIBAV_API void pl_map_dovi_metadata(struct pl_dovi_metadata *out,
836 : : const AVDOVIMetadata *data)
837 : : {
838 : : const AVDOVIRpuDataHeader *header;
839 : : const AVDOVIDataMapping *mapping;
840 : : const AVDOVIColorMetadata *color;
841 : : if (!data)
842 : : return;
843 : :
844 : : header = av_dovi_get_header(data);
845 : : mapping = av_dovi_get_mapping(data);
846 : : color = av_dovi_get_color(data);
847 : :
848 : : for (int i = 0; i < 3; i++)
849 : : out->nonlinear_offset[i] = av_q2d(color->ycc_to_rgb_offset[i]);
850 : : for (int i = 0; i < 9; i++) {
851 : : float *nonlinear = &out->nonlinear.m[0][0];
852 : : float *linear = &out->linear.m[0][0];
853 : : nonlinear[i] = av_q2d(color->ycc_to_rgb_matrix[i]);
854 : : linear[i] = av_q2d(color->rgb_to_lms_matrix[i]);
855 : : }
856 : : for (int c = 0; c < 3; c++) {
857 : : const AVDOVIReshapingCurve *csrc = &mapping->curves[c];
858 : : struct pl_reshape_data *cdst = &out->comp[c];
859 : : cdst->num_pivots = csrc->num_pivots;
860 : : for (int i = 0; i < csrc->num_pivots; i++) {
861 : : const float scale = 1.0f / ((1 << header->bl_bit_depth) - 1);
862 : : cdst->pivots[i] = scale * csrc->pivots[i];
863 : : }
864 : : for (int i = 0; i < csrc->num_pivots - 1; i++) {
865 : : const float scale = 1.0f / (1 << header->coef_log2_denom);
866 : : cdst->method[i] = csrc->mapping_idc[i];
867 : : switch (csrc->mapping_idc[i]) {
868 : : case AV_DOVI_MAPPING_POLYNOMIAL:
869 : : for (int k = 0; k < 3; k++) {
870 : : cdst->poly_coeffs[i][k] = (k <= csrc->poly_order[i])
871 : : ? scale * csrc->poly_coef[i][k]
872 : : : 0.0f;
873 : : }
874 : : break;
875 : : case AV_DOVI_MAPPING_MMR:
876 : : cdst->mmr_order[i] = csrc->mmr_order[i];
877 : : cdst->mmr_constant[i] = scale * csrc->mmr_constant[i];
878 : : for (int j = 0; j < csrc->mmr_order[i]; j++) {
879 : : for (int k = 0; k < 7; k++)
880 : : cdst->mmr_coeffs[i][j][k] = scale * csrc->mmr_coef[i][j][k];
881 : : }
882 : : break;
883 : : }
884 : : }
885 : : }
886 : : }
887 : :
888 : : PL_LIBAV_API void pl_map_avdovi_metadata(struct pl_color_space *color,
889 : : struct pl_color_repr *repr,
890 : : struct pl_dovi_metadata *dovi,
891 : : const AVDOVIMetadata *metadata)
892 : : {
893 : : const AVDOVIRpuDataHeader *header;
894 : : const AVDOVIColorMetadata *dovi_color;
895 : : #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(59, 12, 100)
896 : : const AVDOVIDmData *dovi_ext;
897 : : #endif
898 : : if (!color || !repr || !dovi)
899 : : return;
900 : :
901 : : header = av_dovi_get_header(metadata);
902 : : dovi_color = av_dovi_get_color(metadata);
903 : : if (header->disable_residual_flag) {
904 : : pl_map_dovi_metadata(dovi, metadata);
905 : :
906 : : repr->dovi = dovi;
907 : : repr->sys = PL_COLOR_SYSTEM_DOLBYVISION;
908 : : color->primaries = PL_COLOR_PRIM_BT_2020;
909 : : color->transfer = PL_COLOR_TRC_PQ;
910 : : color->hdr.min_luma =
911 : : pl_hdr_rescale(PL_HDR_PQ, PL_HDR_NITS, dovi_color->source_min_pq / 4095.0f);
912 : : color->hdr.max_luma =
913 : : pl_hdr_rescale(PL_HDR_PQ, PL_HDR_NITS, dovi_color->source_max_pq / 4095.0f);
914 : :
915 : : #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(59, 12, 100)
916 : : if ((dovi_ext = av_dovi_find_level(metadata, 1))) {
917 : : color->hdr.max_pq_y = dovi_ext->l1.max_pq / 4095.0f;
918 : : color->hdr.avg_pq_y = dovi_ext->l1.avg_pq / 4095.0f;
919 : : }
920 : : #endif
921 : : }
922 : : }
923 : :
924 : : PL_LIBAV_API void pl_frame_map_avdovi_metadata(struct pl_frame *out_frame,
925 : : struct pl_dovi_metadata *dovi,
926 : : const AVDOVIMetadata *metadata)
927 : : {
928 : : if (!out_frame)
929 : : return;
930 : : pl_map_avdovi_metadata(&out_frame->color, &out_frame->repr, dovi, metadata);
931 : : }
932 : : #endif // PL_HAVE_LAV_DOLBY_VISION
933 : :
934 : : PL_LIBAV_API bool pl_frame_recreate_from_avframe(pl_gpu gpu,
935 : : struct pl_frame *out,
936 : : pl_tex tex[4],
937 : : const AVFrame *frame)
938 : : {
939 : : const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format);
940 : : struct pl_plane_data data[4] = {0};
941 : : int planes;
942 : :
943 : : pl_frame_from_avframe(out, frame);
944 : : planes = pl_plane_data_from_pixfmt(data, &out->repr.bits, frame->format);
945 : : if (!planes)
946 : : return false;
947 : :
948 : : for (int p = 0; p < planes; p++) {
949 : : bool is_chroma = p == 1 || p == 2; // matches lavu logic
950 : : data[p].width = AV_CEIL_RSHIFT(frame->width, is_chroma ? desc->log2_chroma_w : 0);
951 : : data[p].height = AV_CEIL_RSHIFT(frame->height, is_chroma ? desc->log2_chroma_h : 0);
952 : :
953 : : if (!pl_recreate_plane(gpu, &out->planes[p], &tex[p], &data[p]))
954 : : return false;
955 : : }
956 : :
957 : : return true;
958 : : }
959 : :
960 : : static void pl_avframe_free_cb(void *priv)
961 : : {
962 : : AVFrame *frame = priv;
963 : : av_frame_free(&frame);
964 : : }
965 : :
966 : : #define PL_MAGIC0 0xfb5b3b8b
967 : : #define PL_MAGIC1 0xee659f6d
968 : :
969 : : struct pl_avalloc {
970 : : uint32_t magic[2];
971 : : pl_gpu gpu;
972 : : pl_buf buf;
973 : : };
974 : :
975 : : // Attached to `pl_frame.user_data` for mapped AVFrames
976 : : struct pl_avframe_priv {
977 : : AVFrame *avframe;
978 : : struct pl_dovi_metadata dovi; // backing storage for per-frame dovi metadata
979 : : pl_tex planar; // for planar vulkan textures
980 : : };
981 : :
982 : : static void pl_fix_hwframe_sample_depth(struct pl_frame *out)
983 : : {
984 : : pl_fmt fmt = out->planes[0].texture->params.format;
985 : : struct pl_bit_encoding *bits = &out->repr.bits;
986 : : bits->sample_depth = fmt->component_depth[0];
987 : : }
988 : :
989 : : static bool pl_map_avframe_drm(pl_gpu gpu, struct pl_frame *out,
990 : : const AVFrame *frame)
991 : : {
992 : : const AVHWFramesContext *hwfc = (AVHWFramesContext *) frame->hw_frames_ctx->data;
993 : : const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(hwfc->sw_format);
994 : : const AVDRMFrameDescriptor *drm = (AVDRMFrameDescriptor *) frame->data[0];
995 : : assert(frame->format == AV_PIX_FMT_DRM_PRIME);
996 : : if (!(gpu->import_caps.tex & PL_HANDLE_DMA_BUF))
997 : : return false;
998 : :
999 : : assert(drm->nb_layers >= out->num_planes);
1000 : : for (int n = 0; n < out->num_planes; n++) {
1001 : : const AVDRMLayerDescriptor *layer = &drm->layers[n];
1002 : : const AVDRMPlaneDescriptor *plane = &layer->planes[0];
1003 : : const AVDRMObjectDescriptor *object = &drm->objects[plane->object_index];
1004 : : pl_fmt fmt = pl_find_fourcc(gpu, layer->format);
1005 : : bool is_chroma = n == 1 || n == 2;
1006 : : if (!fmt || !pl_fmt_has_modifier(fmt, object->format_modifier))
1007 : : return false;
1008 : :
1009 : : assert(layer->nb_planes == 1); // we only support planar formats
1010 : : assert(plane->pitch >= 0); // definitely requires special handling
1011 : : out->planes[n].texture = pl_tex_create(gpu, pl_tex_params(
1012 : : .w = AV_CEIL_RSHIFT(frame->width, is_chroma ? desc->log2_chroma_w : 0),
1013 : : .h = AV_CEIL_RSHIFT(frame->height, is_chroma ? desc->log2_chroma_h : 0),
1014 : : .format = fmt,
1015 : : .sampleable = true,
1016 : : .blit_src = fmt->caps & PL_FMT_CAP_BLITTABLE,
1017 : : .import_handle = PL_HANDLE_DMA_BUF,
1018 : : .shared_mem = {
1019 : : .handle.fd = object->fd,
1020 : : .size = object->size,
1021 : : .offset = plane->offset,
1022 : : .drm_format_mod = object->format_modifier,
1023 : : .stride_w = plane->pitch,
1024 : : },
1025 : : ));
1026 : : if (!out->planes[n].texture)
1027 : : return false;
1028 : : }
1029 : :
1030 : : pl_fix_hwframe_sample_depth(out);
1031 : :
1032 : : switch (hwfc->sw_format) {
1033 : : case AV_PIX_FMT_P010: out->repr.bits.bit_shift = 6; break;
1034 : : default: break;
1035 : : }
1036 : :
1037 : : return true;
1038 : : }
1039 : :
1040 : : // Derive a DMABUF from any other hwaccel format, and map that instead
1041 : : static bool pl_map_avframe_derived(pl_gpu gpu, struct pl_frame *out,
1042 : : const AVFrame *frame)
1043 : : {
1044 : : const int flags = AV_HWFRAME_MAP_READ | AV_HWFRAME_MAP_DIRECT;
1045 : : struct pl_avframe_priv *priv = out->user_data;
1046 : : AVFrame *derived = av_frame_alloc();
1047 : : derived->width = frame->width;
1048 : : derived->height = frame->height;
1049 : : derived->format = AV_PIX_FMT_DRM_PRIME;
1050 : : derived->hw_frames_ctx = av_buffer_ref(frame->hw_frames_ctx);
1051 : : if (av_hwframe_map(derived, frame, flags) < 0)
1052 : : goto error;
1053 : : if (av_frame_copy_props(derived, frame) < 0)
1054 : : goto error;
1055 : : if (!pl_map_avframe_drm(gpu, out, derived))
1056 : : goto error;
1057 : :
1058 : : av_frame_free(&priv->avframe);
1059 : : priv->avframe = derived;
1060 : : return true;
1061 : :
1062 : : error:
1063 : : av_frame_free(&derived);
1064 : : return false;
1065 : : }
1066 : :
1067 : : #ifdef PL_HAVE_LAV_VULKAN
1068 : : static bool pl_acquire_avframe(pl_gpu gpu, struct pl_frame *frame)
1069 : : {
1070 : : const struct pl_avframe_priv *priv = frame->user_data;
1071 : : AVHWFramesContext *hwfc = (void *) priv->avframe->hw_frames_ctx->data;
1072 : : AVVulkanFramesContext *vkfc = hwfc->hwctx;
1073 : : AVVkFrame *vkf = (AVVkFrame *) priv->avframe->data[0];
1074 : :
1075 : : vkfc->lock_frame(hwfc, vkf);
1076 : :
1077 : : for (int n = 0; n < frame->num_planes; n++) {
1078 : : pl_vulkan_release_ex(gpu, pl_vulkan_release_params(
1079 : : .tex = priv->planar ? priv->planar : frame->planes[n].texture,
1080 : : .layout = vkf->layout[n],
1081 : : .qf = VK_QUEUE_FAMILY_IGNORED,
1082 : : .semaphore = {
1083 : : .sem = vkf->sem[n],
1084 : : .value = vkf->sem_value[n],
1085 : : },
1086 : : ));
1087 : : if (priv->planar)
1088 : : break;
1089 : : }
1090 : :
1091 : : return true;
1092 : : }
1093 : :
1094 : : static void pl_release_avframe(pl_gpu gpu, struct pl_frame *frame)
1095 : : {
1096 : : const struct pl_avframe_priv *priv = frame->user_data;
1097 : : AVHWFramesContext *hwfc = (void *) priv->avframe->hw_frames_ctx->data;
1098 : : AVVulkanFramesContext *vkfc = hwfc->hwctx;
1099 : : AVVkFrame *vkf = (AVVkFrame *) priv->avframe->data[0];
1100 : :
1101 : : for (int n = 0; n < frame->num_planes; n++) {
1102 : : int ok = pl_vulkan_hold_ex(gpu, pl_vulkan_hold_params(
1103 : : .tex = priv->planar ? priv->planar : frame->planes[n].texture,
1104 : : .out_layout = &vkf->layout[n],
1105 : : .qf = VK_QUEUE_FAMILY_IGNORED,
1106 : : .semaphore = {
1107 : : .sem = vkf->sem[n],
1108 : : .value = vkf->sem_value[n] + 1,
1109 : : },
1110 : : ));
1111 : :
1112 : : vkf->access[n] = 0;
1113 : : vkf->sem_value[n] += !!ok;
1114 : : if (priv->planar)
1115 : : break;
1116 : : }
1117 : :
1118 : : vkfc->unlock_frame(hwfc, vkf);
1119 : : }
1120 : :
1121 : : static bool pl_map_avframe_vulkan(pl_gpu gpu, struct pl_frame *out,
1122 : : const AVFrame *frame)
1123 : : {
1124 : : const AVHWFramesContext *hwfc = (AVHWFramesContext *) frame->hw_frames_ctx->data;
1125 : : const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(hwfc->sw_format);
1126 : : const AVVulkanFramesContext *vkfc = hwfc->hwctx;
1127 : : AVVkFrame *vkf = (AVVkFrame *) frame->data[0];
1128 : : const VkFormat *vk_fmt = vkfc->format;
1129 : : struct pl_avframe_priv *priv = out->user_data;
1130 : : pl_vulkan vk = pl_vulkan_get(gpu);
1131 : :
1132 : : assert(frame->format == AV_PIX_FMT_VULKAN);
1133 : : priv->planar = NULL;
1134 : : if (!vk)
1135 : : return false;
1136 : :
1137 : : for (int n = 0; n < out->num_planes; n++) {
1138 : : struct pl_plane *plane = &out->planes[n];
1139 : : bool chroma = n == 1 || n == 2;
1140 : : int num_subplanes;
1141 : : assert(vk_fmt[n]);
1142 : :
1143 : : plane->texture = pl_vulkan_wrap(gpu, pl_vulkan_wrap_params(
1144 : : .image = vkf->img[n],
1145 : : .width = AV_CEIL_RSHIFT(hwfc->width, chroma ? desc->log2_chroma_w : 0),
1146 : : .height = AV_CEIL_RSHIFT(hwfc->height, chroma ? desc->log2_chroma_h : 0),
1147 : : .format = vk_fmt[n],
1148 : : .usage = vkfc->usage,
1149 : : ));
1150 : : if (!plane->texture)
1151 : : return false;
1152 : :
1153 : : num_subplanes = plane->texture->params.format->num_planes;
1154 : : if (num_subplanes) {
1155 : : assert(num_subplanes == out->num_planes);
1156 : : priv->planar = plane->texture;
1157 : : for (int i = 0; i < num_subplanes; i++)
1158 : : out->planes[i].texture = priv->planar->planes[i];
1159 : : break;
1160 : : }
1161 : : }
1162 : :
1163 : : out->acquire = pl_acquire_avframe;
1164 : : out->release = pl_release_avframe;
1165 : : pl_fix_hwframe_sample_depth(out);
1166 : : return true;
1167 : : }
1168 : :
1169 : : static void pl_unmap_avframe_vulkan(pl_gpu gpu, struct pl_frame *frame)
1170 : : {
1171 : : struct pl_avframe_priv *priv = frame->user_data;
1172 : : if (priv->planar) {
1173 : : pl_tex_destroy(gpu, &priv->planar);
1174 : : for (int n = 0; n < frame->num_planes; n++)
1175 : : frame->planes[n].texture = NULL;
1176 : : }
1177 : : }
1178 : : #endif
1179 : :
1180 : : PL_LIBAV_API bool pl_map_avframe_ex(pl_gpu gpu, struct pl_frame *out,
1181 : : const struct pl_avframe_params *params)
1182 : : {
1183 : : const AVFrame *frame = params->frame;
1184 : : const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format);
1185 : : struct pl_plane_data data[4] = {0};
1186 : : pl_tex *tex = params->tex;
1187 : : int planes;
1188 : :
1189 : : struct pl_avframe_priv *priv = malloc(sizeof(*priv));
1190 : : if (!priv)
1191 : : goto error;
1192 : :
1193 : : pl_frame_from_avframe(out, frame);
1194 : : priv->avframe = av_frame_clone(frame);
1195 : : out->user_data = priv;
1196 : :
1197 : : #ifdef PL_HAVE_LAV_DOLBY_VISION
1198 : : if (params->map_dovi) {
1199 : : AVFrameSideData *sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DOVI_METADATA);
1200 : : if (sd) {
1201 : : const AVDOVIMetadata *metadata = (AVDOVIMetadata *) sd->data;
1202 : : const AVDOVIRpuDataHeader *header = av_dovi_get_header(metadata);
1203 : : // Only automatically map DoVi RPUs that don't require an EL
1204 : : if (header->disable_residual_flag)
1205 : : pl_map_avdovi_metadata(&out->color, &out->repr, &priv->dovi, metadata);
1206 : : }
1207 : :
1208 : : #ifdef PL_HAVE_LIBDOVI
1209 : : sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DOVI_RPU_BUFFER);
1210 : : if (sd)
1211 : : pl_hdr_metadata_from_dovi_rpu(&out->color.hdr, sd->buf->data, sd->buf->size);
1212 : : #endif // PL_HAVE_LIBDOVI
1213 : : }
1214 : :
1215 : : #endif // PL_HAVE_LAV_DOLBY_VISION
1216 : :
1217 : : switch (frame->format) {
1218 : : case AV_PIX_FMT_DRM_PRIME:
1219 : : if (!pl_map_avframe_drm(gpu, out, frame))
1220 : : goto error;
1221 : : return true;
1222 : :
1223 : : case AV_PIX_FMT_VAAPI:
1224 : : if (!pl_map_avframe_derived(gpu, out, frame))
1225 : : goto error;
1226 : : return true;
1227 : :
1228 : : #ifdef PL_HAVE_LAV_VULKAN
1229 : : case AV_PIX_FMT_VULKAN:
1230 : : if (!pl_map_avframe_vulkan(gpu, out, frame))
1231 : : goto error;
1232 : : return true;
1233 : : #endif
1234 : :
1235 : : default: break;
1236 : : }
1237 : :
1238 : : // Backing textures are required from this point onwards
1239 : : if (!tex)
1240 : : goto error;
1241 : :
1242 : : planes = pl_plane_data_from_pixfmt(data, &out->repr.bits, frame->format);
1243 : : if (!planes)
1244 : : goto error;
1245 : :
1246 : : for (int p = 0; p < planes; p++) {
1247 : : AVBufferRef *buf = av_frame_get_plane_buffer((AVFrame *) frame, p);
1248 : : struct pl_avalloc *alloc = buf ? av_buffer_get_opaque(buf) : NULL;
1249 : : bool is_chroma = p == 1 || p == 2; // matches lavu logic
1250 : :
1251 : : data[p].width = AV_CEIL_RSHIFT(frame->width, is_chroma ? desc->log2_chroma_w : 0);
1252 : : data[p].height = AV_CEIL_RSHIFT(frame->height, is_chroma ? desc->log2_chroma_h : 0);
1253 : : if (frame->linesize[p] < 0) {
1254 : : data[p].pixels = frame->data[p] + frame->linesize[p] * (data[p].height - 1);
1255 : : data[p].row_stride = -frame->linesize[p];
1256 : : out->planes[p].flipped = true;
1257 : : } else {
1258 : : data[p].pixels = frame->data[p];
1259 : : data[p].row_stride = frame->linesize[p];
1260 : : }
1261 : :
1262 : : // Probe for frames allocated by pl_get_buffer2
1263 : : if (alloc && alloc->magic[0] == PL_MAGIC0 && alloc->magic[1] == PL_MAGIC1) {
1264 : : data[p].buf = alloc->buf;
1265 : : data[p].buf_offset = (uintptr_t) data[p].pixels - (uintptr_t) alloc->buf->data;
1266 : : data[p].pixels = NULL;
1267 : : } else if (gpu->limits.callbacks) {
1268 : : // Use asynchronous upload if possible
1269 : : data[p].callback = pl_avframe_free_cb;
1270 : : data[p].priv = av_frame_clone(frame);
1271 : : }
1272 : :
1273 : : if (!pl_upload_plane(gpu, &out->planes[p], &tex[p], &data[p])) {
1274 : : av_frame_free((AVFrame **) &data[p].priv);
1275 : : goto error;
1276 : : }
1277 : :
1278 : : out->planes[p].texture = tex[p];
1279 : : }
1280 : :
1281 : : return true;
1282 : :
1283 : : error:
1284 : : pl_unmap_avframe(gpu, out);
1285 : : return false;
1286 : : }
1287 : :
1288 : : // Backwards compatibility with previous versions of this API.
1289 : : PL_LIBAV_API bool pl_map_avframe(pl_gpu gpu, struct pl_frame *out_frame,
1290 : : pl_tex tex[4], const AVFrame *avframe)
1291 : : {
1292 : : return pl_map_avframe_ex(gpu, out_frame, &(struct pl_avframe_params) {
1293 : : .frame = avframe,
1294 : : .tex = tex,
1295 : : });
1296 : : }
1297 : :
1298 : : PL_LIBAV_API void pl_unmap_avframe(pl_gpu gpu, struct pl_frame *frame)
1299 : : {
1300 : : struct pl_avframe_priv *priv = frame->user_data;
1301 : : const AVPixFmtDescriptor *desc;
1302 : : if (!priv)
1303 : : goto done;
1304 : :
1305 : : #ifdef PL_HAVE_LAV_VULKAN
1306 : : if (priv->avframe->format == AV_PIX_FMT_VULKAN)
1307 : : pl_unmap_avframe_vulkan(gpu, frame);
1308 : : #endif
1309 : :
1310 : : desc = av_pix_fmt_desc_get(priv->avframe->format);
1311 : : if (desc->flags & AV_PIX_FMT_FLAG_HWACCEL) {
1312 : : for (int i = 0; i < 4; i++)
1313 : : pl_tex_destroy(gpu, &frame->planes[i].texture);
1314 : : }
1315 : :
1316 : : av_frame_free(&priv->avframe);
1317 : : free(priv);
1318 : :
1319 : : done:
1320 : : memset(frame, 0, sizeof(*frame)); // sanity
1321 : : }
1322 : :
1323 : : PL_LIBAV_API AVFrame *pl_get_mapped_avframe(const struct pl_frame *frame)
1324 : : {
1325 : : struct pl_avframe_priv *priv = frame->user_data;
1326 : : return priv->avframe;
1327 : : }
1328 : :
1329 : : static void pl_done_cb(void *priv)
1330 : : {
1331 : : bool *status = priv;
1332 : : *status = true;
1333 : : }
1334 : :
1335 : : PL_LIBAV_API bool pl_download_avframe(pl_gpu gpu,
1336 : : const struct pl_frame *frame,
1337 : : AVFrame *out_frame)
1338 : : {
1339 : : bool done[4] = {0};
1340 : : if (frame->num_planes != av_pix_fmt_count_planes(out_frame->format))
1341 : : return false;
1342 : :
1343 : : for (int p = 0; p < frame->num_planes; p++) {
1344 : : bool ok = pl_tex_download(gpu, pl_tex_transfer_params(
1345 : : .tex = frame->planes[p].texture,
1346 : : .row_pitch = out_frame->linesize[p],
1347 : : .ptr = out_frame->data[p],
1348 : : // Use synchronous transfer for the last plane
1349 : : .callback = (p+1) < frame->num_planes ? pl_done_cb : NULL,
1350 : : .priv = &done[p],
1351 : : ));
1352 : :
1353 : : if (!ok)
1354 : : return false;
1355 : : }
1356 : :
1357 : : for (int p = 0; p < frame->num_planes - 1; p++) {
1358 : : while (!done[p])
1359 : : pl_tex_poll(gpu, frame->planes[p].texture, UINT64_MAX);
1360 : : }
1361 : :
1362 : : return true;
1363 : : }
1364 : :
1365 : : #define PL_DIV_UP(x, y) (((x) + (y) - 1) / (y))
1366 : : #define PL_ALIGN(x, align) ((align) ? PL_DIV_UP(x, align) * (align) : (x))
1367 : : #define PL_MAX(x, y) ((x) > (y) ? (x) : (y))
1368 : : #define PL_LCM(x, y) ((x) * ((y) / av_gcd(x, y)))
1369 : :
1370 : : static inline void pl_avalloc_free(void *opaque, uint8_t *data)
1371 : : {
1372 : : struct pl_avalloc *alloc = opaque;
1373 : : assert(alloc->magic[0] == PL_MAGIC0);
1374 : : assert(alloc->magic[1] == PL_MAGIC1);
1375 : : assert(alloc->buf->data == data);
1376 : : pl_buf_destroy(alloc->gpu, &alloc->buf);
1377 : : free(alloc);
1378 : : }
1379 : :
1380 : : PL_LIBAV_API int pl_get_buffer2(AVCodecContext *avctx, AVFrame *pic, int flags)
1381 : : {
1382 : : int alignment[AV_NUM_DATA_POINTERS];
1383 : : int width = pic->width;
1384 : : int height = pic->height;
1385 : : size_t planesize[4];
1386 : : int ret = 0;
1387 : :
1388 : : pl_gpu *pgpu = avctx->opaque;
1389 : : pl_gpu gpu = pgpu ? *pgpu : NULL;
1390 : : struct pl_plane_data data[4];
1391 : : struct pl_avalloc *alloc;
1392 : : const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pic->format);
1393 : : int planes = pl_plane_data_from_pixfmt(data, NULL, pic->format);
1394 : :
1395 : : // Sanitize frame structs
1396 : : memset(pic->data, 0, sizeof(pic->data));
1397 : : memset(pic->linesize, 0, sizeof(pic->linesize));
1398 : : memset(pic->buf, 0, sizeof(pic->buf));
1399 : : pic->extended_data = pic->data;
1400 : : pic->extended_buf = NULL;
1401 : :
1402 : : if (!(avctx->codec->capabilities & AV_CODEC_CAP_DR1) || !planes)
1403 : : goto fallback;
1404 : : if (!gpu || !gpu->limits.thread_safe || !gpu->limits.max_mapped_size ||
1405 : : !gpu->limits.host_cached)
1406 : : {
1407 : : goto fallback;
1408 : : }
1409 : :
1410 : : avcodec_align_dimensions2(avctx, &width, &height, alignment);
1411 : : if ((ret = av_image_fill_linesizes(pic->linesize, pic->format, width)))
1412 : : return ret;
1413 : :
1414 : : for (int p = 0; p < planes; p++) {
1415 : : alignment[p] = PL_LCM(alignment[p], gpu->limits.align_tex_xfer_pitch);
1416 : : alignment[p] = PL_LCM(alignment[p], gpu->limits.align_tex_xfer_offset);
1417 : : alignment[p] = PL_LCM(alignment[p], data[p].pixel_stride);
1418 : : pic->linesize[p] = PL_ALIGN(pic->linesize[p], alignment[p]);
1419 : : }
1420 : :
1421 : : #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(56, 56, 100)
1422 : : ret = av_image_fill_plane_sizes(planesize, pic->format, height, (ptrdiff_t[4]) {
1423 : : pic->linesize[0], pic->linesize[1], pic->linesize[2], pic->linesize[3],
1424 : : });
1425 : : if (ret < 0)
1426 : : return ret;
1427 : : #else
1428 : : uint8_t *ptrs[4], * const base = (uint8_t *) 0x10000;
1429 : : ret = av_image_fill_pointers(ptrs, pic->format, height, base, pic->linesize);
1430 : : if (ret < 0)
1431 : : return ret;
1432 : : for (int p = 0; p < 4; p++)
1433 : : planesize[p] = (uintptr_t) ptrs[p] - (uintptr_t) base;
1434 : : #endif
1435 : :
1436 : : for (int p = 0; p < planes; p++) {
1437 : : const size_t buf_size = planesize[p] + alignment[p];
1438 : : if (buf_size > gpu->limits.max_mapped_size) {
1439 : : av_frame_unref(pic);
1440 : : goto fallback;
1441 : : }
1442 : :
1443 : : alloc = malloc(sizeof(*alloc));
1444 : : if (!alloc) {
1445 : : av_frame_unref(pic);
1446 : : return AVERROR(ENOMEM);
1447 : : }
1448 : :
1449 : : *alloc = (struct pl_avalloc) {
1450 : : .magic = { PL_MAGIC0, PL_MAGIC1 },
1451 : : .gpu = gpu,
1452 : : .buf = pl_buf_create(gpu, pl_buf_params(
1453 : : .size = buf_size,
1454 : : .memory_type = PL_BUF_MEM_HOST,
1455 : : .host_mapped = true,
1456 : : .storable = desc->flags & AV_PIX_FMT_FLAG_BE,
1457 : : )),
1458 : : };
1459 : :
1460 : : if (!alloc->buf) {
1461 : : free(alloc);
1462 : : av_frame_unref(pic);
1463 : : return AVERROR(ENOMEM);
1464 : : }
1465 : :
1466 : : pic->data[p] = (uint8_t *) PL_ALIGN((uintptr_t) alloc->buf->data, alignment[p]);
1467 : : pic->buf[p] = av_buffer_create(alloc->buf->data, buf_size, pl_avalloc_free, alloc, 0);
1468 : : if (!pic->buf[p]) {
1469 : : pl_buf_destroy(gpu, &alloc->buf);
1470 : : free(alloc);
1471 : : av_frame_unref(pic);
1472 : : return AVERROR(ENOMEM);
1473 : : }
1474 : : }
1475 : :
1476 : : return 0;
1477 : :
1478 : : fallback:
1479 : : return avcodec_default_get_buffer2(avctx, pic, flags);
1480 : : }
1481 : :
1482 : : #undef PL_MAGIC0
1483 : : #undef PL_MAGIC1
1484 : : #undef PL_ALIGN
1485 : : #undef PL_MAX
1486 : :
1487 : : #endif // LIBPLACEBO_LIBAV_H_
|