| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * This file is part of FFmpeg. | ||
| 3 | * | ||
| 4 | * FFmpeg 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 | * FFmpeg 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 GNU | ||
| 12 | * 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 FFmpeg; if not, write to the Free Software | ||
| 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include "config_components.h" | ||
| 20 | |||
| 21 | #include "libavutil/avassert.h" | ||
| 22 | #include "libavutil/common.h" | ||
| 23 | #include "libavutil/mem.h" | ||
| 24 | #include "libavutil/pixdesc.h" | ||
| 25 | |||
| 26 | #include "avcodec.h" | ||
| 27 | #include "codec_desc.h" | ||
| 28 | #include "decode.h" | ||
| 29 | #include "internal.h" | ||
| 30 | #include "vaapi_decode.h" | ||
| 31 | #include "vaapi_hevc.h" | ||
| 32 | |||
| 33 | |||
| 34 | ✗ | int ff_vaapi_decode_make_param_buffer(AVCodecContext *avctx, | |
| 35 | VAAPIDecodePicture *pic, | ||
| 36 | int type, | ||
| 37 | const void *data, | ||
| 38 | size_t size) | ||
| 39 | { | ||
| 40 | ✗ | VAAPIDecodeContext *ctx = avctx->internal->hwaccel_priv_data; | |
| 41 | VAStatus vas; | ||
| 42 | |||
| 43 | ✗ | av_assert0(pic->nb_param_buffers <= pic->nb_param_buffers_allocated); | |
| 44 | ✗ | if (pic->nb_param_buffers == pic->nb_param_buffers_allocated) { | |
| 45 | VABufferID *tmp = | ||
| 46 | ✗ | av_realloc_array(pic->param_buffers, | |
| 47 | ✗ | pic->nb_param_buffers_allocated + 16, | |
| 48 | sizeof(*pic->param_buffers)); | ||
| 49 | ✗ | if (!tmp) | |
| 50 | ✗ | return AVERROR(ENOMEM); | |
| 51 | |||
| 52 | ✗ | pic->param_buffers = tmp; | |
| 53 | ✗ | pic->nb_param_buffers_allocated += 16; | |
| 54 | } | ||
| 55 | ✗ | av_assert0(pic->nb_param_buffers + 1 <= pic->nb_param_buffers_allocated); | |
| 56 | |||
| 57 | ✗ | vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context, | |
| 58 | type, size, 1, (void*)data, | ||
| 59 | ✗ | &pic->param_buffers[pic->nb_param_buffers]); | |
| 60 | ✗ | if (vas != VA_STATUS_SUCCESS) { | |
| 61 | ✗ | av_log(avctx, AV_LOG_ERROR, "Failed to create parameter " | |
| 62 | "buffer (type %d): %d (%s).\n", | ||
| 63 | type, vas, vaErrorStr(vas)); | ||
| 64 | ✗ | return AVERROR(EIO); | |
| 65 | } | ||
| 66 | |||
| 67 | ✗ | av_log(avctx, AV_LOG_DEBUG, "Param buffer (type %d, %zu bytes) " | |
| 68 | ✗ | "is %#x.\n", type, size, pic->param_buffers[pic->nb_param_buffers]); | |
| 69 | |||
| 70 | ✗ | ++pic->nb_param_buffers; | |
| 71 | |||
| 72 | ✗ | return 0; | |
| 73 | } | ||
| 74 | |||
| 75 | ✗ | int ff_vaapi_decode_make_slice_buffer(AVCodecContext *avctx, | |
| 76 | VAAPIDecodePicture *pic, | ||
| 77 | const void *params_data, | ||
| 78 | int nb_params, | ||
| 79 | size_t params_size, | ||
| 80 | const void *slice_data, | ||
| 81 | size_t slice_size) | ||
| 82 | { | ||
| 83 | ✗ | VAAPIDecodeContext *ctx = avctx->internal->hwaccel_priv_data; | |
| 84 | VAStatus vas; | ||
| 85 | int index; | ||
| 86 | |||
| 87 | ✗ | av_assert0(pic->nb_slices <= pic->nb_slice_buffers_allocated); | |
| 88 | ✗ | if (pic->nb_slices == pic->nb_slice_buffers_allocated) { | |
| 89 | VABufferID *tmp = | ||
| 90 | ✗ | av_realloc_array(pic->slice_buffers, | |
| 91 | ✗ | pic->nb_slice_buffers_allocated ? pic->nb_slice_buffers_allocated * 2 : 64, | |
| 92 | 2 * sizeof(*pic->slice_buffers)); | ||
| 93 | ✗ | if (!tmp) | |
| 94 | ✗ | return AVERROR(ENOMEM); | |
| 95 | |||
| 96 | ✗ | pic->slice_buffers = tmp; | |
| 97 | ✗ | pic->nb_slice_buffers_allocated = pic->nb_slice_buffers_allocated ? pic->nb_slice_buffers_allocated * 2 : 64; | |
| 98 | } | ||
| 99 | ✗ | av_assert0(pic->nb_slices + 1 <= pic->nb_slice_buffers_allocated); | |
| 100 | |||
| 101 | ✗ | index = 2 * pic->nb_slices; | |
| 102 | |||
| 103 | ✗ | vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context, | |
| 104 | VASliceParameterBufferType, | ||
| 105 | params_size, nb_params, (void*)params_data, | ||
| 106 | ✗ | &pic->slice_buffers[index]); | |
| 107 | ✗ | if (vas != VA_STATUS_SUCCESS) { | |
| 108 | ✗ | av_log(avctx, AV_LOG_ERROR, "Failed to create slice " | |
| 109 | "parameter buffer: %d (%s).\n", vas, vaErrorStr(vas)); | ||
| 110 | ✗ | return AVERROR(EIO); | |
| 111 | } | ||
| 112 | |||
| 113 | ✗ | av_log(avctx, AV_LOG_DEBUG, "Slice %d param buffer (%zu bytes) " | |
| 114 | "is %#x.\n", pic->nb_slices, params_size, | ||
| 115 | ✗ | pic->slice_buffers[index]); | |
| 116 | |||
| 117 | ✗ | vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context, | |
| 118 | VASliceDataBufferType, | ||
| 119 | slice_size, 1, (void*)slice_data, | ||
| 120 | ✗ | &pic->slice_buffers[index + 1]); | |
| 121 | ✗ | if (vas != VA_STATUS_SUCCESS) { | |
| 122 | ✗ | av_log(avctx, AV_LOG_ERROR, "Failed to create slice " | |
| 123 | "data buffer (size %zu): %d (%s).\n", | ||
| 124 | slice_size, vas, vaErrorStr(vas)); | ||
| 125 | ✗ | vaDestroyBuffer(ctx->hwctx->display, | |
| 126 | ✗ | pic->slice_buffers[index]); | |
| 127 | ✗ | return AVERROR(EIO); | |
| 128 | } | ||
| 129 | |||
| 130 | ✗ | av_log(avctx, AV_LOG_DEBUG, "Slice %d data buffer (%zu bytes) " | |
| 131 | "is %#x.\n", pic->nb_slices, slice_size, | ||
| 132 | ✗ | pic->slice_buffers[index + 1]); | |
| 133 | |||
| 134 | ✗ | ++pic->nb_slices; | |
| 135 | ✗ | return 0; | |
| 136 | } | ||
| 137 | |||
| 138 | ✗ | static void ff_vaapi_decode_destroy_buffers(AVCodecContext *avctx, | |
| 139 | VAAPIDecodePicture *pic) | ||
| 140 | { | ||
| 141 | ✗ | VAAPIDecodeContext *ctx = avctx->internal->hwaccel_priv_data; | |
| 142 | VAStatus vas; | ||
| 143 | int i; | ||
| 144 | |||
| 145 | ✗ | for (i = 0; i < pic->nb_param_buffers; i++) { | |
| 146 | ✗ | vas = vaDestroyBuffer(ctx->hwctx->display, | |
| 147 | ✗ | pic->param_buffers[i]); | |
| 148 | ✗ | if (vas != VA_STATUS_SUCCESS) { | |
| 149 | ✗ | av_log(avctx, AV_LOG_ERROR, "Failed to destroy " | |
| 150 | "parameter buffer %#x: %d (%s).\n", | ||
| 151 | ✗ | pic->param_buffers[i], vas, vaErrorStr(vas)); | |
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | ✗ | for (i = 0; i < 2 * pic->nb_slices; i++) { | |
| 156 | ✗ | vas = vaDestroyBuffer(ctx->hwctx->display, | |
| 157 | ✗ | pic->slice_buffers[i]); | |
| 158 | ✗ | if (vas != VA_STATUS_SUCCESS) { | |
| 159 | ✗ | av_log(avctx, AV_LOG_ERROR, "Failed to destroy slice " | |
| 160 | "slice buffer %#x: %d (%s).\n", | ||
| 161 | ✗ | pic->slice_buffers[i], vas, vaErrorStr(vas)); | |
| 162 | } | ||
| 163 | } | ||
| 164 | ✗ | } | |
| 165 | |||
| 166 | ✗ | int ff_vaapi_decode_issue(AVCodecContext *avctx, | |
| 167 | VAAPIDecodePicture *pic) | ||
| 168 | { | ||
| 169 | ✗ | VAAPIDecodeContext *ctx = avctx->internal->hwaccel_priv_data; | |
| 170 | VAStatus vas; | ||
| 171 | int err; | ||
| 172 | |||
| 173 | ✗ | if (pic->nb_slices <= 0) { | |
| 174 | ✗ | err = AVERROR(EINVAL); | |
| 175 | ✗ | goto fail; | |
| 176 | } | ||
| 177 | |||
| 178 | ✗ | av_log(avctx, AV_LOG_DEBUG, "Decode to surface %#x.\n", | |
| 179 | pic->output_surface); | ||
| 180 | |||
| 181 | ✗ | vas = vaBeginPicture(ctx->hwctx->display, ctx->va_context, | |
| 182 | pic->output_surface); | ||
| 183 | ✗ | if (vas != VA_STATUS_SUCCESS) { | |
| 184 | ✗ | av_log(avctx, AV_LOG_ERROR, "Failed to begin picture decode " | |
| 185 | "issue: %d (%s).\n", vas, vaErrorStr(vas)); | ||
| 186 | ✗ | err = AVERROR(EIO); | |
| 187 | ✗ | goto fail_with_picture; | |
| 188 | } | ||
| 189 | |||
| 190 | ✗ | vas = vaRenderPicture(ctx->hwctx->display, ctx->va_context, | |
| 191 | pic->param_buffers, pic->nb_param_buffers); | ||
| 192 | ✗ | if (vas != VA_STATUS_SUCCESS) { | |
| 193 | ✗ | av_log(avctx, AV_LOG_ERROR, "Failed to upload decode " | |
| 194 | "parameters: %d (%s).\n", vas, vaErrorStr(vas)); | ||
| 195 | ✗ | err = AVERROR(EIO); | |
| 196 | ✗ | goto fail_with_picture; | |
| 197 | } | ||
| 198 | |||
| 199 | ✗ | vas = vaRenderPicture(ctx->hwctx->display, ctx->va_context, | |
| 200 | ✗ | pic->slice_buffers, 2 * pic->nb_slices); | |
| 201 | ✗ | if (vas != VA_STATUS_SUCCESS) { | |
| 202 | ✗ | av_log(avctx, AV_LOG_ERROR, "Failed to upload slices: " | |
| 203 | "%d (%s).\n", vas, vaErrorStr(vas)); | ||
| 204 | ✗ | err = AVERROR(EIO); | |
| 205 | ✗ | goto fail_with_picture; | |
| 206 | } | ||
| 207 | |||
| 208 | ✗ | vas = vaEndPicture(ctx->hwctx->display, ctx->va_context); | |
| 209 | ✗ | if (vas != VA_STATUS_SUCCESS) { | |
| 210 | ✗ | av_log(avctx, AV_LOG_ERROR, "Failed to end picture decode " | |
| 211 | "issue: %d (%s).\n", vas, vaErrorStr(vas)); | ||
| 212 | ✗ | err = AVERROR(EIO); | |
| 213 | if (CONFIG_VAAPI_1 || ctx->hwctx->driver_quirks & | ||
| 214 | AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS) | ||
| 215 | ✗ | goto fail; | |
| 216 | else | ||
| 217 | goto fail_at_end; | ||
| 218 | } | ||
| 219 | |||
| 220 | if (CONFIG_VAAPI_1 || ctx->hwctx->driver_quirks & | ||
| 221 | AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS) | ||
| 222 | ✗ | ff_vaapi_decode_destroy_buffers(avctx, pic); | |
| 223 | |||
| 224 | ✗ | err = 0; | |
| 225 | ✗ | goto exit; | |
| 226 | |||
| 227 | ✗ | fail_with_picture: | |
| 228 | ✗ | vas = vaEndPicture(ctx->hwctx->display, ctx->va_context); | |
| 229 | ✗ | if (vas != VA_STATUS_SUCCESS) { | |
| 230 | ✗ | av_log(avctx, AV_LOG_ERROR, "Failed to end picture decode " | |
| 231 | "after error: %d (%s).\n", vas, vaErrorStr(vas)); | ||
| 232 | } | ||
| 233 | ✗ | fail: | |
| 234 | ✗ | ff_vaapi_decode_destroy_buffers(avctx, pic); | |
| 235 | ✗ | fail_at_end: | |
| 236 | ✗ | exit: | |
| 237 | ✗ | pic->nb_param_buffers = 0; | |
| 238 | ✗ | pic->nb_param_buffers_allocated = 0; | |
| 239 | ✗ | av_freep(&pic->param_buffers); | |
| 240 | ✗ | pic->nb_slices = 0; | |
| 241 | ✗ | pic->nb_slice_buffers_allocated = 0; | |
| 242 | ✗ | av_freep(&pic->slice_buffers); | |
| 243 | |||
| 244 | ✗ | return err; | |
| 245 | } | ||
| 246 | |||
| 247 | ✗ | int ff_vaapi_decode_cancel(AVCodecContext *avctx, | |
| 248 | VAAPIDecodePicture *pic) | ||
| 249 | { | ||
| 250 | ✗ | ff_vaapi_decode_destroy_buffers(avctx, pic); | |
| 251 | |||
| 252 | ✗ | pic->nb_param_buffers = 0; | |
| 253 | ✗ | pic->nb_param_buffers_allocated = 0; | |
| 254 | ✗ | av_freep(&pic->param_buffers); | |
| 255 | ✗ | pic->nb_slices = 0; | |
| 256 | ✗ | pic->nb_slice_buffers_allocated = 0; | |
| 257 | ✗ | av_freep(&pic->slice_buffers); | |
| 258 | |||
| 259 | ✗ | return 0; | |
| 260 | } | ||
| 261 | |||
| 262 | static const struct { | ||
| 263 | uint32_t fourcc; | ||
| 264 | enum AVPixelFormat pix_fmt; | ||
| 265 | } vaapi_format_map[] = { | ||
| 266 | #define MAP(va, av) { VA_FOURCC_ ## va, AV_PIX_FMT_ ## av } | ||
| 267 | // 4:0:0 | ||
| 268 | MAP(Y800, GRAY8), | ||
| 269 | // 4:2:0 | ||
| 270 | MAP(NV12, NV12), | ||
| 271 | MAP(YV12, YUV420P), | ||
| 272 | MAP(IYUV, YUV420P), | ||
| 273 | #ifdef VA_FOURCC_I420 | ||
| 274 | MAP(I420, YUV420P), | ||
| 275 | #endif | ||
| 276 | MAP(IMC3, YUV420P), | ||
| 277 | // 4:1:1 | ||
| 278 | MAP(411P, YUV411P), | ||
| 279 | // 4:2:2 | ||
| 280 | MAP(422H, YUV422P), | ||
| 281 | #ifdef VA_FOURCC_YV16 | ||
| 282 | MAP(YV16, YUV422P), | ||
| 283 | #endif | ||
| 284 | MAP(YUY2, YUYV422), | ||
| 285 | #ifdef VA_FOURCC_Y210 | ||
| 286 | MAP(Y210, Y210), | ||
| 287 | #endif | ||
| 288 | #ifdef VA_FOURCC_Y212 | ||
| 289 | MAP(Y212, Y212), | ||
| 290 | #endif | ||
| 291 | // 4:4:0 | ||
| 292 | MAP(422V, YUV440P), | ||
| 293 | // 4:4:4 | ||
| 294 | MAP(444P, YUV444P), | ||
| 295 | #ifdef VA_FOURCC_XYUV | ||
| 296 | MAP(XYUV, VUYX), | ||
| 297 | #endif | ||
| 298 | #ifdef VA_FOURCC_Y410 | ||
| 299 | MAP(Y410, XV30), | ||
| 300 | #endif | ||
| 301 | #ifdef VA_FOURCC_Y412 | ||
| 302 | MAP(Y412, XV36), | ||
| 303 | #endif | ||
| 304 | // 4:2:0 10-bit | ||
| 305 | #ifdef VA_FOURCC_P010 | ||
| 306 | MAP(P010, P010), | ||
| 307 | #endif | ||
| 308 | #ifdef VA_FOURCC_P012 | ||
| 309 | MAP(P012, P012), | ||
| 310 | #endif | ||
| 311 | #ifdef VA_FOURCC_I010 | ||
| 312 | MAP(I010, YUV420P10), | ||
| 313 | #endif | ||
| 314 | #undef MAP | ||
| 315 | }; | ||
| 316 | |||
| 317 | ✗ | static int vaapi_decode_find_best_format(AVCodecContext *avctx, | |
| 318 | AVHWDeviceContext *device, | ||
| 319 | VAConfigID config_id, | ||
| 320 | AVHWFramesContext *frames) | ||
| 321 | { | ||
| 322 | ✗ | AVVAAPIDeviceContext *hwctx = device->hwctx; | |
| 323 | VAStatus vas; | ||
| 324 | VASurfaceAttrib *attr; | ||
| 325 | enum AVPixelFormat source_format, best_format, format; | ||
| 326 | uint32_t best_fourcc, fourcc; | ||
| 327 | int i, j, nb_attr; | ||
| 328 | |||
| 329 | ✗ | source_format = avctx->sw_pix_fmt; | |
| 330 | ✗ | av_assert0(source_format != AV_PIX_FMT_NONE); | |
| 331 | |||
| 332 | ✗ | vas = vaQuerySurfaceAttributes(hwctx->display, config_id, | |
| 333 | NULL, &nb_attr); | ||
| 334 | ✗ | if (vas != VA_STATUS_SUCCESS) { | |
| 335 | ✗ | av_log(avctx, AV_LOG_ERROR, "Failed to query surface attributes: " | |
| 336 | "%d (%s).\n", vas, vaErrorStr(vas)); | ||
| 337 | ✗ | return AVERROR(ENOSYS); | |
| 338 | } | ||
| 339 | |||
| 340 | ✗ | attr = av_malloc_array(nb_attr, sizeof(*attr)); | |
| 341 | ✗ | if (!attr) | |
| 342 | ✗ | return AVERROR(ENOMEM); | |
| 343 | |||
| 344 | ✗ | vas = vaQuerySurfaceAttributes(hwctx->display, config_id, | |
| 345 | attr, &nb_attr); | ||
| 346 | ✗ | if (vas != VA_STATUS_SUCCESS) { | |
| 347 | ✗ | av_log(avctx, AV_LOG_ERROR, "Failed to query surface attributes: " | |
| 348 | "%d (%s).\n", vas, vaErrorStr(vas)); | ||
| 349 | ✗ | av_freep(&attr); | |
| 350 | ✗ | return AVERROR(ENOSYS); | |
| 351 | } | ||
| 352 | |||
| 353 | ✗ | best_format = AV_PIX_FMT_NONE; | |
| 354 | |||
| 355 | ✗ | for (i = 0; i < nb_attr; i++) { | |
| 356 | ✗ | if (attr[i].type != VASurfaceAttribPixelFormat) | |
| 357 | ✗ | continue; | |
| 358 | |||
| 359 | ✗ | fourcc = attr[i].value.value.i; | |
| 360 | ✗ | for (j = 0; j < FF_ARRAY_ELEMS(vaapi_format_map); j++) { | |
| 361 | ✗ | if (fourcc == vaapi_format_map[j].fourcc) | |
| 362 | ✗ | break; | |
| 363 | } | ||
| 364 | ✗ | if (j >= FF_ARRAY_ELEMS(vaapi_format_map)) { | |
| 365 | ✗ | av_log(avctx, AV_LOG_DEBUG, "Ignoring unknown format %#x.\n", | |
| 366 | fourcc); | ||
| 367 | ✗ | continue; | |
| 368 | } | ||
| 369 | ✗ | format = vaapi_format_map[j].pix_fmt; | |
| 370 | ✗ | av_log(avctx, AV_LOG_DEBUG, "Considering format %#x -> %s.\n", | |
| 371 | fourcc, av_get_pix_fmt_name(format)); | ||
| 372 | |||
| 373 | ✗ | best_format = av_find_best_pix_fmt_of_2(format, best_format, | |
| 374 | source_format, 0, NULL); | ||
| 375 | ✗ | if (format == best_format) | |
| 376 | ✗ | best_fourcc = fourcc; | |
| 377 | } | ||
| 378 | |||
| 379 | ✗ | av_freep(&attr); | |
| 380 | |||
| 381 | ✗ | if (best_format == AV_PIX_FMT_NONE) { | |
| 382 | ✗ | av_log(avctx, AV_LOG_ERROR, "No usable formats for decoding!\n"); | |
| 383 | ✗ | return AVERROR(EINVAL); | |
| 384 | } | ||
| 385 | |||
| 386 | ✗ | av_log(avctx, AV_LOG_DEBUG, "Picked %s (%#x) as best match for %s.\n", | |
| 387 | av_get_pix_fmt_name(best_format), best_fourcc, | ||
| 388 | av_get_pix_fmt_name(source_format)); | ||
| 389 | |||
| 390 | ✗ | frames->sw_format = best_format; | |
| 391 | ✗ | if (avctx->internal->hwaccel_priv_data) { | |
| 392 | ✗ | VAAPIDecodeContext *ctx = avctx->internal->hwaccel_priv_data; | |
| 393 | ✗ | AVVAAPIFramesContext *avfc = frames->hwctx; | |
| 394 | |||
| 395 | ✗ | ctx->pixel_format_attribute = (VASurfaceAttrib) { | |
| 396 | .type = VASurfaceAttribPixelFormat, | ||
| 397 | .flags = VA_SURFACE_ATTRIB_SETTABLE, | ||
| 398 | .value.type = VAGenericValueTypeInteger, | ||
| 399 | .value.value.i = best_fourcc, | ||
| 400 | }; | ||
| 401 | |||
| 402 | ✗ | avfc->attributes = &ctx->pixel_format_attribute; | |
| 403 | ✗ | avfc->nb_attributes = 1; | |
| 404 | } | ||
| 405 | |||
| 406 | ✗ | return 0; | |
| 407 | } | ||
| 408 | |||
| 409 | static const struct { | ||
| 410 | enum AVCodecID codec_id; | ||
| 411 | int codec_profile; | ||
| 412 | VAProfile va_profile; | ||
| 413 | VAProfile (*profile_parser)(AVCodecContext *avctx); | ||
| 414 | } vaapi_profile_map[] = { | ||
| 415 | #define MAP(c, p, v, ...) { AV_CODEC_ID_ ## c, AV_PROFILE_ ## p, VAProfile ## v, __VA_ARGS__ } | ||
| 416 | MAP(MPEG2VIDEO, MPEG2_SIMPLE, MPEG2Simple ), | ||
| 417 | MAP(MPEG2VIDEO, MPEG2_MAIN, MPEG2Main ), | ||
| 418 | MAP(H263, UNKNOWN, H263Baseline), | ||
| 419 | MAP(MPEG4, MPEG4_SIMPLE, MPEG4Simple ), | ||
| 420 | MAP(MPEG4, MPEG4_ADVANCED_SIMPLE, | ||
| 421 | MPEG4AdvancedSimple), | ||
| 422 | MAP(MPEG4, MPEG4_MAIN, MPEG4Main ), | ||
| 423 | #if VA_CHECK_VERSION(1, 18, 0) | ||
| 424 | MAP(H264, H264_HIGH_10_INTRA, | ||
| 425 | H264High10 ), | ||
| 426 | MAP(H264, H264_HIGH_10, H264High10 ), | ||
| 427 | #endif | ||
| 428 | MAP(H264, H264_CONSTRAINED_BASELINE, | ||
| 429 | H264ConstrainedBaseline), | ||
| 430 | MAP(H264, H264_MAIN, H264Main ), | ||
| 431 | MAP(H264, H264_HIGH, H264High ), | ||
| 432 | #if VA_CHECK_VERSION(0, 37, 0) | ||
| 433 | MAP(HEVC, HEVC_MAIN, HEVCMain ), | ||
| 434 | MAP(HEVC, HEVC_MAIN_10, HEVCMain10 ), | ||
| 435 | MAP(HEVC, HEVC_MAIN_STILL_PICTURE, | ||
| 436 | HEVCMain ), | ||
| 437 | #endif | ||
| 438 | #if VA_CHECK_VERSION(1, 2, 0) && CONFIG_HEVC_VAAPI_HWACCEL | ||
| 439 | MAP(HEVC, HEVC_REXT, None, | ||
| 440 | ff_vaapi_parse_hevc_rext_scc_profile ), | ||
| 441 | MAP(HEVC, HEVC_SCC, None, | ||
| 442 | ff_vaapi_parse_hevc_rext_scc_profile ), | ||
| 443 | #endif | ||
| 444 | MAP(MJPEG, MJPEG_HUFFMAN_BASELINE_DCT, | ||
| 445 | JPEGBaseline), | ||
| 446 | MAP(WMV3, VC1_SIMPLE, VC1Simple ), | ||
| 447 | MAP(WMV3, VC1_MAIN, VC1Main ), | ||
| 448 | MAP(WMV3, VC1_COMPLEX, VC1Advanced ), | ||
| 449 | MAP(WMV3, VC1_ADVANCED, VC1Advanced ), | ||
| 450 | MAP(VC1, VC1_SIMPLE, VC1Simple ), | ||
| 451 | MAP(VC1, VC1_MAIN, VC1Main ), | ||
| 452 | MAP(VC1, VC1_COMPLEX, VC1Advanced ), | ||
| 453 | MAP(VC1, VC1_ADVANCED, VC1Advanced ), | ||
| 454 | MAP(VP8, UNKNOWN, VP8Version0_3 ), | ||
| 455 | #if VA_CHECK_VERSION(0, 38, 0) | ||
| 456 | MAP(VP9, VP9_0, VP9Profile0 ), | ||
| 457 | #endif | ||
| 458 | #if VA_CHECK_VERSION(0, 39, 0) | ||
| 459 | MAP(VP9, VP9_1, VP9Profile1 ), | ||
| 460 | MAP(VP9, VP9_2, VP9Profile2 ), | ||
| 461 | MAP(VP9, VP9_3, VP9Profile3 ), | ||
| 462 | #endif | ||
| 463 | #if VA_CHECK_VERSION(1, 8, 0) | ||
| 464 | MAP(AV1, AV1_MAIN, AV1Profile0), | ||
| 465 | MAP(AV1, AV1_HIGH, AV1Profile1), | ||
| 466 | #endif | ||
| 467 | #if VA_CHECK_VERSION(1, 22, 0) | ||
| 468 | MAP(H266, VVC_MAIN_10, VVCMain10), | ||
| 469 | #endif | ||
| 470 | |||
| 471 | #undef MAP | ||
| 472 | }; | ||
| 473 | |||
| 474 | /* | ||
| 475 | * Set *va_config and the frames_ref fields from the current codec parameters | ||
| 476 | * in avctx. | ||
| 477 | */ | ||
| 478 | ✗ | static int vaapi_decode_make_config(AVCodecContext *avctx, | |
| 479 | AVBufferRef *device_ref, | ||
| 480 | VAConfigID *va_config, | ||
| 481 | AVBufferRef *frames_ref) | ||
| 482 | { | ||
| 483 | ✗ | AVVAAPIHWConfig *hwconfig = NULL; | |
| 484 | ✗ | AVHWFramesConstraints *constraints = NULL; | |
| 485 | VAStatus vas; | ||
| 486 | int err, i, j; | ||
| 487 | const AVCodecDescriptor *codec_desc; | ||
| 488 | ✗ | VAProfile *profile_list = NULL, matched_va_profile, va_profile; | |
| 489 | int profile_count, exact_match, matched_ff_profile, codec_profile; | ||
| 490 | |||
| 491 | ✗ | AVHWDeviceContext *device = (AVHWDeviceContext*)device_ref->data; | |
| 492 | ✗ | AVVAAPIDeviceContext *hwctx = device->hwctx; | |
| 493 | |||
| 494 | ✗ | codec_desc = avcodec_descriptor_get(avctx->codec_id); | |
| 495 | ✗ | if (!codec_desc) { | |
| 496 | ✗ | err = AVERROR(EINVAL); | |
| 497 | ✗ | goto fail; | |
| 498 | } | ||
| 499 | |||
| 500 | ✗ | profile_count = vaMaxNumProfiles(hwctx->display); | |
| 501 | ✗ | profile_list = av_malloc_array(profile_count, | |
| 502 | sizeof(VAProfile)); | ||
| 503 | ✗ | if (!profile_list) { | |
| 504 | ✗ | err = AVERROR(ENOMEM); | |
| 505 | ✗ | goto fail; | |
| 506 | } | ||
| 507 | |||
| 508 | ✗ | vas = vaQueryConfigProfiles(hwctx->display, | |
| 509 | profile_list, &profile_count); | ||
| 510 | ✗ | if (vas != VA_STATUS_SUCCESS) { | |
| 511 | ✗ | av_log(avctx, AV_LOG_ERROR, "Failed to query profiles: " | |
| 512 | "%d (%s).\n", vas, vaErrorStr(vas)); | ||
| 513 | ✗ | err = AVERROR(ENOSYS); | |
| 514 | ✗ | goto fail; | |
| 515 | } | ||
| 516 | |||
| 517 | ✗ | matched_va_profile = VAProfileNone; | |
| 518 | ✗ | exact_match = 0; | |
| 519 | |||
| 520 | ✗ | for (i = 0; i < FF_ARRAY_ELEMS(vaapi_profile_map); i++) { | |
| 521 | ✗ | int profile_match = 0; | |
| 522 | ✗ | if (avctx->codec_id != vaapi_profile_map[i].codec_id) | |
| 523 | ✗ | continue; | |
| 524 | ✗ | if (avctx->profile == vaapi_profile_map[i].codec_profile || | |
| 525 | ✗ | vaapi_profile_map[i].codec_profile == AV_PROFILE_UNKNOWN) | |
| 526 | ✗ | profile_match = 1; | |
| 527 | |||
| 528 | ✗ | va_profile = vaapi_profile_map[i].profile_parser ? | |
| 529 | ✗ | vaapi_profile_map[i].profile_parser(avctx) : | |
| 530 | vaapi_profile_map[i].va_profile; | ||
| 531 | ✗ | codec_profile = vaapi_profile_map[i].codec_profile; | |
| 532 | |||
| 533 | ✗ | for (j = 0; j < profile_count; j++) { | |
| 534 | ✗ | if (va_profile == profile_list[j]) { | |
| 535 | ✗ | exact_match = profile_match; | |
| 536 | ✗ | break; | |
| 537 | } | ||
| 538 | } | ||
| 539 | ✗ | if (j < profile_count) { | |
| 540 | ✗ | matched_va_profile = va_profile; | |
| 541 | ✗ | matched_ff_profile = codec_profile; | |
| 542 | ✗ | if (exact_match) | |
| 543 | ✗ | break; | |
| 544 | } | ||
| 545 | } | ||
| 546 | ✗ | av_freep(&profile_list); | |
| 547 | |||
| 548 | ✗ | if (matched_va_profile == VAProfileNone) { | |
| 549 | ✗ | av_log(avctx, AV_LOG_ERROR, "No support for codec %s " | |
| 550 | ✗ | "profile %d.\n", codec_desc->name, avctx->profile); | |
| 551 | ✗ | err = AVERROR(ENOSYS); | |
| 552 | ✗ | goto fail; | |
| 553 | } | ||
| 554 | ✗ | if (!exact_match) { | |
| 555 | ✗ | if (avctx->hwaccel_flags & | |
| 556 | AV_HWACCEL_FLAG_ALLOW_PROFILE_MISMATCH) { | ||
| 557 | ✗ | av_log(avctx, AV_LOG_VERBOSE, "Codec %s profile %d not " | |
| 558 | "supported for hardware decode.\n", | ||
| 559 | ✗ | codec_desc->name, avctx->profile); | |
| 560 | ✗ | av_log(avctx, AV_LOG_WARNING, "Using possibly-" | |
| 561 | "incompatible profile %d instead.\n", | ||
| 562 | matched_ff_profile); | ||
| 563 | } else { | ||
| 564 | ✗ | av_log(avctx, AV_LOG_VERBOSE, "Codec %s profile %d not " | |
| 565 | "supported for hardware decode.\n", | ||
| 566 | ✗ | codec_desc->name, avctx->profile); | |
| 567 | ✗ | err = AVERROR(EINVAL); | |
| 568 | ✗ | goto fail; | |
| 569 | } | ||
| 570 | } | ||
| 571 | |||
| 572 | ✗ | vas = vaCreateConfig(hwctx->display, matched_va_profile, | |
| 573 | VAEntrypointVLD, NULL, 0, | ||
| 574 | va_config); | ||
| 575 | ✗ | if (vas != VA_STATUS_SUCCESS) { | |
| 576 | ✗ | av_log(avctx, AV_LOG_ERROR, "Failed to create decode " | |
| 577 | "configuration: %d (%s).\n", vas, vaErrorStr(vas)); | ||
| 578 | ✗ | err = AVERROR(EIO); | |
| 579 | ✗ | goto fail; | |
| 580 | } | ||
| 581 | |||
| 582 | ✗ | hwconfig = av_hwdevice_hwconfig_alloc(device_ref); | |
| 583 | ✗ | if (!hwconfig) { | |
| 584 | ✗ | err = AVERROR(ENOMEM); | |
| 585 | ✗ | goto fail; | |
| 586 | } | ||
| 587 | ✗ | hwconfig->config_id = *va_config; | |
| 588 | |||
| 589 | ✗ | constraints = | |
| 590 | ✗ | av_hwdevice_get_hwframe_constraints(device_ref, hwconfig); | |
| 591 | ✗ | if (!constraints) { | |
| 592 | ✗ | err = AVERROR(ENOMEM); | |
| 593 | ✗ | goto fail; | |
| 594 | } | ||
| 595 | |||
| 596 | ✗ | if (avctx->coded_width < constraints->min_width || | |
| 597 | ✗ | avctx->coded_height < constraints->min_height || | |
| 598 | ✗ | avctx->coded_width > constraints->max_width || | |
| 599 | ✗ | avctx->coded_height > constraints->max_height) { | |
| 600 | ✗ | av_log(avctx, AV_LOG_ERROR, "Hardware does not support image " | |
| 601 | "size %dx%d (constraints: width %d-%d height %d-%d).\n", | ||
| 602 | avctx->coded_width, avctx->coded_height, | ||
| 603 | ✗ | constraints->min_width, constraints->max_width, | |
| 604 | ✗ | constraints->min_height, constraints->max_height); | |
| 605 | ✗ | err = AVERROR(EINVAL); | |
| 606 | ✗ | goto fail; | |
| 607 | } | ||
| 608 | ✗ | if (!constraints->valid_sw_formats || | |
| 609 | ✗ | constraints->valid_sw_formats[0] == AV_PIX_FMT_NONE) { | |
| 610 | ✗ | av_log(avctx, AV_LOG_ERROR, "Hardware does not offer any " | |
| 611 | "usable surface formats.\n"); | ||
| 612 | ✗ | err = AVERROR(EINVAL); | |
| 613 | ✗ | goto fail; | |
| 614 | } | ||
| 615 | |||
| 616 | ✗ | if (frames_ref) { | |
| 617 | ✗ | AVHWFramesContext *frames = (AVHWFramesContext *)frames_ref->data; | |
| 618 | |||
| 619 | ✗ | frames->format = AV_PIX_FMT_VAAPI; | |
| 620 | ✗ | frames->width = avctx->coded_width; | |
| 621 | ✗ | frames->height = avctx->coded_height; | |
| 622 | |||
| 623 | ✗ | err = vaapi_decode_find_best_format(avctx, device, | |
| 624 | *va_config, frames); | ||
| 625 | ✗ | if (err < 0) | |
| 626 | ✗ | goto fail; | |
| 627 | |||
| 628 | if (CONFIG_VAAPI_1) | ||
| 629 | ✗ | frames->initial_pool_size = 0; | |
| 630 | else { | ||
| 631 | frames->initial_pool_size = 1; | ||
| 632 | // Add per-codec number of surfaces used for storing reference frames. | ||
| 633 | switch (avctx->codec_id) { | ||
| 634 | case AV_CODEC_ID_H264: | ||
| 635 | case AV_CODEC_ID_H266: | ||
| 636 | case AV_CODEC_ID_HEVC: | ||
| 637 | case AV_CODEC_ID_AV1: | ||
| 638 | frames->initial_pool_size += 16; | ||
| 639 | break; | ||
| 640 | case AV_CODEC_ID_VP9: | ||
| 641 | frames->initial_pool_size += 8; | ||
| 642 | break; | ||
| 643 | case AV_CODEC_ID_VP8: | ||
| 644 | frames->initial_pool_size += 3; | ||
| 645 | break; | ||
| 646 | default: | ||
| 647 | frames->initial_pool_size += 2; | ||
| 648 | } | ||
| 649 | } | ||
| 650 | } | ||
| 651 | |||
| 652 | ✗ | av_hwframe_constraints_free(&constraints); | |
| 653 | ✗ | av_freep(&hwconfig); | |
| 654 | |||
| 655 | ✗ | return 0; | |
| 656 | |||
| 657 | ✗ | fail: | |
| 658 | ✗ | av_hwframe_constraints_free(&constraints); | |
| 659 | ✗ | av_freep(&hwconfig); | |
| 660 | ✗ | if (*va_config != VA_INVALID_ID) { | |
| 661 | ✗ | vaDestroyConfig(hwctx->display, *va_config); | |
| 662 | ✗ | *va_config = VA_INVALID_ID; | |
| 663 | } | ||
| 664 | ✗ | av_freep(&profile_list); | |
| 665 | ✗ | return err; | |
| 666 | } | ||
| 667 | |||
| 668 | ✗ | int ff_vaapi_common_frame_params(AVCodecContext *avctx, | |
| 669 | AVBufferRef *hw_frames_ctx) | ||
| 670 | { | ||
| 671 | ✗ | AVHWFramesContext *hw_frames = (AVHWFramesContext *)hw_frames_ctx->data; | |
| 672 | ✗ | AVHWDeviceContext *device_ctx = hw_frames->device_ctx; | |
| 673 | AVVAAPIDeviceContext *hwctx; | ||
| 674 | ✗ | VAConfigID va_config = VA_INVALID_ID; | |
| 675 | int err; | ||
| 676 | |||
| 677 | ✗ | if (device_ctx->type != AV_HWDEVICE_TYPE_VAAPI) | |
| 678 | ✗ | return AVERROR(EINVAL); | |
| 679 | ✗ | hwctx = device_ctx->hwctx; | |
| 680 | |||
| 681 | ✗ | err = vaapi_decode_make_config(avctx, hw_frames->device_ref, &va_config, | |
| 682 | hw_frames_ctx); | ||
| 683 | ✗ | if (err) | |
| 684 | ✗ | return err; | |
| 685 | |||
| 686 | ✗ | if (va_config != VA_INVALID_ID) | |
| 687 | ✗ | vaDestroyConfig(hwctx->display, va_config); | |
| 688 | |||
| 689 | ✗ | return 0; | |
| 690 | } | ||
| 691 | |||
| 692 | ✗ | int ff_vaapi_decode_init(AVCodecContext *avctx) | |
| 693 | { | ||
| 694 | ✗ | VAAPIDecodeContext *ctx = avctx->internal->hwaccel_priv_data; | |
| 695 | VAStatus vas; | ||
| 696 | int err; | ||
| 697 | |||
| 698 | ✗ | ctx->va_config = VA_INVALID_ID; | |
| 699 | ✗ | ctx->va_context = VA_INVALID_ID; | |
| 700 | |||
| 701 | ✗ | err = ff_decode_get_hw_frames_ctx(avctx, AV_HWDEVICE_TYPE_VAAPI); | |
| 702 | ✗ | if (err < 0) | |
| 703 | ✗ | goto fail; | |
| 704 | |||
| 705 | ✗ | ctx->frames = (AVHWFramesContext*)avctx->hw_frames_ctx->data; | |
| 706 | ✗ | ctx->hwfc = ctx->frames->hwctx; | |
| 707 | ✗ | ctx->device = ctx->frames->device_ctx; | |
| 708 | ✗ | ctx->hwctx = ctx->device->hwctx; | |
| 709 | |||
| 710 | ✗ | err = vaapi_decode_make_config(avctx, ctx->frames->device_ref, | |
| 711 | &ctx->va_config, NULL); | ||
| 712 | ✗ | if (err) | |
| 713 | ✗ | goto fail; | |
| 714 | |||
| 715 | ✗ | vas = vaCreateContext(ctx->hwctx->display, ctx->va_config, | |
| 716 | avctx->coded_width, avctx->coded_height, | ||
| 717 | VA_PROGRESSIVE, | ||
| 718 | ✗ | ctx->hwfc->surface_ids, | |
| 719 | ✗ | ctx->hwfc->nb_surfaces, | |
| 720 | &ctx->va_context); | ||
| 721 | ✗ | if (vas != VA_STATUS_SUCCESS) { | |
| 722 | ✗ | av_log(avctx, AV_LOG_ERROR, "Failed to create decode " | |
| 723 | "context: %d (%s).\n", vas, vaErrorStr(vas)); | ||
| 724 | ✗ | err = AVERROR(EIO); | |
| 725 | ✗ | goto fail; | |
| 726 | } | ||
| 727 | |||
| 728 | ✗ | av_log(avctx, AV_LOG_DEBUG, "Decode context initialised: " | |
| 729 | "%#x/%#x.\n", ctx->va_config, ctx->va_context); | ||
| 730 | |||
| 731 | ✗ | return 0; | |
| 732 | |||
| 733 | ✗ | fail: | |
| 734 | ✗ | ff_vaapi_decode_uninit(avctx); | |
| 735 | ✗ | return err; | |
| 736 | } | ||
| 737 | |||
| 738 | ✗ | int ff_vaapi_decode_uninit(AVCodecContext *avctx) | |
| 739 | { | ||
| 740 | ✗ | VAAPIDecodeContext *ctx = avctx->internal->hwaccel_priv_data; | |
| 741 | VAStatus vas; | ||
| 742 | |||
| 743 | ✗ | if (ctx->va_context != VA_INVALID_ID) { | |
| 744 | ✗ | vas = vaDestroyContext(ctx->hwctx->display, ctx->va_context); | |
| 745 | ✗ | if (vas != VA_STATUS_SUCCESS) { | |
| 746 | ✗ | av_log(avctx, AV_LOG_ERROR, "Failed to destroy decode " | |
| 747 | "context %#x: %d (%s).\n", | ||
| 748 | ctx->va_context, vas, vaErrorStr(vas)); | ||
| 749 | } | ||
| 750 | } | ||
| 751 | ✗ | if (ctx->va_config != VA_INVALID_ID) { | |
| 752 | ✗ | vas = vaDestroyConfig(ctx->hwctx->display, ctx->va_config); | |
| 753 | ✗ | if (vas != VA_STATUS_SUCCESS) { | |
| 754 | ✗ | av_log(avctx, AV_LOG_ERROR, "Failed to destroy decode " | |
| 755 | "configuration %#x: %d (%s).\n", | ||
| 756 | ctx->va_config, vas, vaErrorStr(vas)); | ||
| 757 | } | ||
| 758 | } | ||
| 759 | |||
| 760 | ✗ | return 0; | |
| 761 | } | ||
| 762 |