LCOV - code coverage report
Current view: top level - src/include/libplacebo/utils - libav_internal.h (source / functions) Hit Total Coverage
Test: Code coverage Lines: 133 228 58.3 %
Date: 2025-03-29 09:04:10 Functions: 6 7 85.7 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 86 155 55.5 %

           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_

Generated by: LCOV version 1.16