/* hwaccel.c * * Copyright (c) 2003-2025 HandBrake Team * This file is part of the HandBrake source code. * Homepage: . * It may be used under the terms of the GNU General Public License v2. * For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "handbrake/hwaccel.h" #include "handbrake/handbrake.h" #include "handbrake/qsv_common.h" static hb_buffer_t * upload(void *hw_frames_ctx, hb_buffer_t **buf_in) { int ret = 0; AVFrame frame = {{0}}; AVFrame *hw_frame = av_frame_alloc(); if (hw_frame == NULL) { goto fail; } hb_video_buffer_to_avframe(&frame, buf_in); ret = av_hwframe_get_buffer(hw_frames_ctx, hw_frame, 0); if (ret < 0) { hb_log("hwaccel: failed to get hwframe buffer"); goto fail; } av_frame_copy_props(hw_frame, &frame); if (ret < 0) { hb_log("hwaccel: failed to copy props"); goto fail; } av_hwframe_transfer_data(hw_frame, &frame, 0); if (ret < 0) { hb_log("hwaccel: failed to transfer data"); goto fail; } hb_buffer_t *out = hb_avframe_to_video_buffer(hw_frame, (AVRational){1,1}); av_frame_unref(&frame); av_frame_unref(hw_frame); return out; fail: av_frame_unref(&frame); av_frame_unref(hw_frame); return NULL; } static int can_filter(hb_list_t *filters) { return 0; } static void * find_decoder(int codec_param) { if (codec_param == AV_CODEC_ID_AV1) { return (void *)avcodec_find_decoder_by_name("av1"); } else { return (void *)avcodec_find_decoder(codec_param); } } static hb_list_t *hb_list_hwaccel = NULL; void hb_register_hwaccel(hb_hwaccel_t *hwaccel) { if (hb_list_hwaccel == NULL) { hb_list_hwaccel = hb_list_init(); } hb_list_add(hb_list_hwaccel, hwaccel); } void hb_hwaccel_common_hwaccel_init() { for (int ii = 0; ii < hb_list_count(hb_list_hwaccel); ii++) { hb_hwaccel_t *hwaccel = hb_list_item(hb_list_hwaccel, ii); if (hwaccel->can_filter == NULL) { hwaccel->can_filter = can_filter; } if (hwaccel->find_decoder == NULL) { hwaccel->find_decoder = find_decoder; } if (hwaccel->upload == NULL) { hwaccel->upload = upload; } } } hb_hwaccel_t * hb_get_hwaccel(int hw_decode) { hw_decode &= ~HB_DECODE_FORCE_HW; for (int ii = 0; ii < hb_list_count(hb_list_hwaccel); ii++) { hb_hwaccel_t *hwaccel = hb_list_item(hb_list_hwaccel, ii); if (hw_decode == hwaccel->id) { return hwaccel; } } return NULL; } hb_hwaccel_t * hb_get_hwaccel_from_pix_fmt(enum AVPixelFormat hw_pix_fmt) { for (int ii = 0; ii < hb_list_count(hb_list_hwaccel); ii++) { hb_hwaccel_t *hwaccel = hb_list_item(hb_list_hwaccel, ii); if (hw_pix_fmt == hwaccel->hw_pix_fmt) { return hwaccel; } } return NULL; } static int is_encoder_supported(hb_hwaccel_t *hwaccel, int encoder) { const int *encoders = hwaccel->encoders; while (*encoders != HB_VCODEC_INVALID) { if (*encoders == encoder) { return 1; } encoders++; } return 0; } static int is_rotation_supported(hb_hwaccel_t *hwaccel, int rotation) { return rotation != HB_ROTATION_0 && (hwaccel->caps & HB_HWACCEL_CAP_ROTATE) == 0 ? 0 : 1; } static int is_color_range_supported(hb_hwaccel_t *hwaccel, int color_range) { return color_range != 0 && (hwaccel->caps & HB_HWACCEL_CAP_COLOR_RANGE) == 0 ? 0 : 1; } int hb_hwaccel_can_use_full_hw_pipeline(hb_hwaccel_t *hwaccel, hb_list_t *list_filter, int encoder, int rotation, int color_range) { return hwaccel != NULL && hwaccel->can_filter(list_filter) && is_rotation_supported(hwaccel, rotation) && is_color_range_supported(hwaccel, color_range) && is_encoder_supported(hwaccel, encoder); } const AVCodecHWConfig *get_hw_config(const AVCodec *codec, enum AVHWDeviceType device_type) { for (int i = 0;; i++) { const AVCodecHWConfig *config = avcodec_get_hw_config(codec, i); if (config == NULL) { return NULL; } if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == device_type) { return config; } } return NULL; } int hb_hwaccel_is_available(hb_hwaccel_t *hwaccel, int codec_id) { if (hb_is_hardware_disabled() || hwaccel == NULL) { return 0; } const AVCodec *codec = hwaccel->find_decoder(codec_id); const AVCodecHWConfig *config = get_hw_config(codec, hwaccel->type); return config != NULL; } enum AVPixelFormat hw_hwaccel_get_hw_format(AVCodecContext *ctx, const enum AVPixelFormat *pix_fmts) { const hb_job_t *job = ctx->opaque; const enum AVPixelFormat *p; for (p = pix_fmts; *p != AV_PIX_FMT_NONE; p++) { if (job && job->hw_pix_fmt != AV_PIX_FMT_NONE) { if (*p == job->hw_pix_fmt) { return *p; } } else { return *p; } } hb_error("hwaccel: failed to get HW surface format"); return AV_PIX_FMT_NONE; } int hb_hwaccel_hw_device_ctx_init(enum AVHWDeviceType device_type, int device_index, void **hw_device_ctx) { int err = 0; AVBufferRef *ctx; AVDictionary *dict = NULL; if (device_index > -1) { char device[32]; snprintf(device, 32, "%u", device_index); av_dict_set(&dict, "child_device", device, 0); } #if defined(_WIN32) || defined(__MINGW32__) if (device_type == AV_HWDEVICE_TYPE_QSV) { av_dict_set(&dict, "child_device_type", "d3d11va", 0); } #endif if ((err = av_hwdevice_ctx_create(&ctx, device_type, NULL, dict, 0)) < 0) { hb_error("hwaccel: failed to create hwdevice"); } else { *hw_device_ctx = ctx; } av_dict_free(&dict); return err; } void hb_hwaccel_hw_device_ctx_close(void **hw_device_ctx) { if (hw_device_ctx && *hw_device_ctx) { av_buffer_unref((AVBufferRef **)hw_device_ctx); } } AVBufferRef *hb_hwaccel_init_hw_frames_ctx(AVBufferRef *hw_device_ctx, enum AVPixelFormat sw_fmt, enum AVPixelFormat hw_fmt, int width, int height, int initial_pool_size) { AVBufferRef *hw_frames_ctx = av_hwframe_ctx_alloc(hw_device_ctx); AVHWFramesContext *frames_ctx = (AVHWFramesContext *)hw_frames_ctx->data; frames_ctx->format = hw_fmt; frames_ctx->sw_format = sw_fmt; frames_ctx->width = width; frames_ctx->height = height; if (initial_pool_size > 0) { frames_ctx->initial_pool_size = initial_pool_size; } if (av_hwframe_ctx_init(hw_frames_ctx) != 0) { hb_error("hwaccel: failed to initialize hw frames context"); av_buffer_unref(&hw_frames_ctx); return NULL; } return hw_frames_ctx; } int hb_hwaccel_hwframes_ctx_init(AVCodecContext *ctx, enum AVPixelFormat sw_pix_fmt, enum AVPixelFormat hw_pix_fmt) { if (!ctx->hw_device_ctx) { hb_error("hwaccel: failed to initialize hw frames context - no hw_device_ctx"); return 1; } ctx->get_format = hw_hwaccel_get_hw_format; ctx->pix_fmt = hw_pix_fmt; ctx->sw_pix_fmt = sw_pix_fmt; ctx->hw_frames_ctx = av_hwframe_ctx_alloc(ctx->hw_device_ctx); AVHWFramesContext *frames_ctx = (AVHWFramesContext *)ctx->hw_frames_ctx->data; frames_ctx->format = hw_pix_fmt; frames_ctx->sw_format = sw_pix_fmt; frames_ctx->width = ctx->width; frames_ctx->height = ctx->height; #if HB_PROJECT_FEATURE_QSV if (hw_pix_fmt == AV_PIX_FMT_QSV) { ctx->extra_hw_frames = HB_QSV_FFMPEG_EXTRA_HW_FRAMES; frames_ctx->initial_pool_size = HB_QSV_FFMPEG_INITIAL_POOL_SIZE; AVQSVFramesContext *frames_hwctx = frames_ctx->hwctx; frames_hwctx->frame_type = MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET; } #endif if (av_hwframe_ctx_init(ctx->hw_frames_ctx) != 0) { hb_error("hwaccel: failed to initialize hw frames context - av_hwframe_ctx_init"); return 1; } return 0; }