mirror of https://github.com/HandBrake/HandBrake
340 lines
8.5 KiB
C
340 lines
8.5 KiB
C
/* hwaccel.c
|
|
*
|
|
* Copyright (c) 2003-2025 HandBrake Team
|
|
* This file is part of the HandBrake source code.
|
|
* Homepage: <http://handbrake.fr/>.
|
|
* 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;
|
|
}
|