diff options
| author | 2025-01-20 20:38:33 -0500 | |
|---|---|---|
| committer | 2025-01-20 20:38:33 -0500 | |
| commit | f3590721df77b7557a80c437acca33b1a4514a9a (patch) | |
| tree | 3be6db7ca795b1ea8469ac1da15c9ab8b90936be /src/codec | |
| parent | 474ccfd0534e03cfcde94fe0f94e3028ed65366a (diff) | |
| download | camu-f3590721df77b7557a80c437acca33b1a4514a9a.tar.gz camu-f3590721df77b7557a80c437acca33b1a4514a9a.tar.bz2 camu-f3590721df77b7557a80c437acca33b1a4514a9a.zip | |
Hardware decoding on Android
- This is bad, hardware decoding selection needs to be completely
rethought.
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'src/codec')
| -rw-r--r-- | src/codec/ffmpeg/decoder.c | 155 | ||||
| -rw-r--r-- | src/codec/ffmpeg/decoder.h | 4 | ||||
| -rw-r--r-- | src/codec/meson.build | 2 |
3 files changed, 113 insertions, 48 deletions
diff --git a/src/codec/ffmpeg/decoder.c b/src/codec/ffmpeg/decoder.c index b502e88..36e59dd 100644 --- a/src/codec/ffmpeg/decoder.c +++ b/src/codec/ffmpeg/decoder.c @@ -14,6 +14,7 @@ static s32 get_buffer2(AVCodecContext *context, AVFrame *pic, s32 flags) return ret; } +#ifdef CAMU_FF_DECODER_HWACCEL static s32 init_hwframe_context(struct camu_ff_decoder *av, AVCodecContext *context, AVBufferRef *hw_device_context) { AVBufferRef *hw_frames_ref; @@ -52,9 +53,9 @@ static enum AVPixelFormat get_hw_format(AVCodecContext *context, const enum AVPi if (*fmt == av->hw_pix_fmt) { if (av->use_frames_context) { av->hw_context = NULL; - s32 ret = av_hwdevice_ctx_create(&av->hw_context, AV_HWDEVICE_TYPE_VAAPI, NULL, NULL, 0); + s32 ret = av_hwdevice_ctx_create(&av->hw_context, av->hw_device_type, NULL, NULL, 0); if (ret < 0 || !av->hw_context) { - al_log_error("ff_decoder", "Failed to initialize VAAPI device (%s).", av_err2str(ret)); + al_log_error("ff_decoder", "Failed to initialize hardware device (%s).", av_err2str(ret)); break; } init_hwframe_context(av, av->codec_context, av->hw_context); @@ -67,7 +68,6 @@ static enum AVPixelFormat get_hw_format(AVCodecContext *context, const enum AVPi return avcodec_default_get_format(context, pix_fmts); } -#endif static s32 init_hwdevice_context(struct camu_ff_decoder *av, AVCodecContext *context) { @@ -84,6 +84,52 @@ static s32 init_hwdevice_context(struct camu_ff_decoder *av, AVCodecContext *con return ret; } +static s32 hw_device_priority(enum AVHWDeviceType type) +{ + switch (type) { + case AV_HWDEVICE_TYPE_MEDIACODEC: + return 4; + case AV_HWDEVICE_TYPE_VAAPI: + return 3; + case AV_HWDEVICE_TYPE_VULKAN: + return 2; + default: + return -1; + }; +} + +static bool hw_device_supported(struct camu_ff_decoder *av, enum AVHWDeviceType type) +{ + enum AVHWDeviceType supported_type; + al_array_foreach(av->supported_hw_devices, i, supported_type) { + if (supported_type == type) return true; + } + return false; +} + +static enum AVHWDeviceType hw_device_supported_by_name(struct camu_ff_decoder *av, const char *name) +{ + enum AVHWDeviceType supported_type; + al_array_foreach(av->supported_hw_devices, i, supported_type) { + if (strcmp(av_hwdevice_get_type_name(supported_type), name) == 0) { + return supported_type; + } + } + return AV_HWDEVICE_TYPE_NONE; +} + +static bool hw_config_needs_frames_ctx(const AVCodecHWConfig *config) +{ + if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) && + !(config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)) { + return true; + } else { + return false; + } +} +#endif +#endif + static void close_internal(struct camu_ff_decoder *av) { if (av->codec_context) avcodec_free_context(&av->codec_context); @@ -97,13 +143,16 @@ static bool ff_decoder_init(struct camu_decoder *dec, struct camu_renderer *rend av->codec_context = NULL; AVCodecParameters *codecpar = stream->av.stream->codecpar; - const AVCodec *codec = avcodec_find_decoder(codecpar->codec_id); -#ifdef CAMU_FF_DECODER_HWACCEL - if (strcmp(codec->name, "libdav1d") == 0) { - codec = avcodec_find_decoder_by_name("av1"); + void *state = NULL; + const AVCodec *codec; + al_array_init(av->supported_hw_codecs); + while ((codec = av_codec_iterate(&state))) { + if (codec->capabilities & (AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_HYBRID)) { + al_array_push(av->supported_hw_codecs, codec); + } } -#endif + codec = avcodec_find_decoder(codecpar->codec_id); if (!codec) { al_log_error("ff_decoder", "Failed to find decoder."); goto err; @@ -112,53 +161,62 @@ static bool ff_decoder_init(struct camu_decoder *dec, struct camu_renderer *rend av->hw_device_type = AV_HWDEVICE_TYPE_NONE; #ifdef CAMU_FF_DECODER_HWACCEL if (codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { -#ifdef _WIN32 // TEMP - const char *hw_device = "vulkan"; -#else - const char *hw_device = "vaapi"; -#endif - av->hw_device_type = av_hwdevice_find_type_by_name(hw_device); - if (av->hw_device_type == AV_HWDEVICE_TYPE_NONE) { - al_log_info("ff_decoder", "Hardware device type \"%s\" not supported.", hw_device); - al_log_info("ff_decoder", "Types available on this system:"); - while ((av->hw_device_type = av_hwdevice_iterate_types(av->hw_device_type)) != AV_HWDEVICE_TYPE_NONE) { - al_log_info("ff_decoder", "\t%s", av_hwdevice_get_type_name(av->hw_device_type)); - } + al_array_init(av->supported_hw_devices); + enum AVHWDeviceType hw_device_type = AV_HWDEVICE_TYPE_NONE; + while ((hw_device_type = av_hwdevice_iterate_types(hw_device_type)) != AV_HWDEVICE_TYPE_NONE) { + al_array_push(av->supported_hw_devices, hw_device_type); } - } -#endif - if (av->hw_device_type != AV_HWDEVICE_TYPE_NONE) { - av->use_frames_context = false; + const AVCodecHWConfig *config = NULL; for (s32 i = 0; ; i++) { - const AVCodecHWConfig *config = avcodec_get_hw_config(codec, i); + config = avcodec_get_hw_config(codec, i); if (!config) { - if (av->use_frames_context) { - al_log_warn("ff_decoder", "Selected hardware decoder (%s) only supports" - "hardware frames for %s, which is unimplemented.", - av_hwdevice_get_type_name(av->hw_device_type), codec->name); - } else { - al_log_warn("ff_decoder", "Selected hardware decoder (%s) does not support %s.", - av_hwdevice_get_type_name(av->hw_device_type), codec->name); - } - av->hw_device_type = AV_HWDEVICE_TYPE_NONE; break; } - if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) { + if (!hw_device_supported(av, config->device_type)) { + continue; + } + + if (hw_device_priority(config->device_type) > hw_device_priority(av->hw_device_type)) { + av->hw_device_type = config->device_type; av->hw_pix_fmt = config->pix_fmt; - av->use_frames_context = true; + av->use_frames_context = hw_config_needs_frames_ctx(config); } + } - if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) { - if (config->device_type == av->hw_device_type) { - av->hw_pix_fmt = config->pix_fmt; - av->use_frames_context = false; - break; + if (av->hw_device_type == AV_HWDEVICE_TYPE_NONE) { + const AVCodec *hw_codec; + al_array_foreach(av->supported_hw_codecs, i, hw_codec) { + if (hw_codec->id == codecpar->codec_id) { + av->hw_device_type = hw_device_supported_by_name(av, hw_codec->wrapper_name); + if (av->hw_device_type != AV_HWDEVICE_TYPE_NONE) { + for (u32 j = 0; ; j++) { + config = avcodec_get_hw_config(hw_codec, j); + if (!config) { + al_log_warn("ff_decoder", "Hardware codec has no hw_configs."); + av->hw_device_type = AV_HWDEVICE_TYPE_NONE; + break; + } + codec = hw_codec; + av->hw_pix_fmt = config->pix_fmt; + av->use_frames_context = hw_config_needs_frames_ctx(config); + break; + } + } + if (av->hw_device_type != AV_HWDEVICE_TYPE_NONE) { + break; + } } } } + + if (av->hw_device_type == AV_HWDEVICE_TYPE_NONE) { + al_log_warn("ff_decoder", + "Hardware accelerated video decoding of %s not supported.", codec->name); + } } +#endif av->codec_context = avcodec_alloc_context3(codec); if (!av->codec_context) { @@ -171,14 +229,23 @@ static bool ff_decoder_init(struct camu_decoder *dec, struct camu_renderer *rend goto err; } +#ifdef CAMU_FF_DECODER_HWACCEL + if ((av->hw_device_type != AV_HWDEVICE_TYPE_NONE && !av->use_frames_context) && + (init_hwdevice_context(av, av->codec_context) < 0)) { + av->hw_device_type = AV_HWDEVICE_TYPE_NONE; + } +#endif + #if !defined CAMU_SINK_NO_VIDEO if (codecpar->codec_type == AVMEDIA_TYPE_VIDEO && renderer && renderer->get_buffer2) { av->renderer = renderer; av->codec_context->opaque = av; av->codec_context->get_buffer2 = get_buffer2; +#ifdef CAMU_FF_DECODER_HWACCEL if (av->hw_device_type != AV_HWDEVICE_TYPE_NONE) { av->codec_context->get_format = get_hw_format; } +#endif } #else (void)renderer; @@ -196,12 +263,6 @@ static bool ff_decoder_init(struct camu_decoder *dec, struct camu_renderer *rend al_log_debug("ff_decoder", "Using %i threads for decoder.", cpus); } - if ((av->hw_device_type != AV_HWDEVICE_TYPE_NONE) && - (init_hwdevice_context(av, av->codec_context) < 0)) { - av->hw_pix_fmt = AV_PIX_FMT_NONE; - return false; - } - if (avcodec_open2(av->codec_context, codec, NULL) < 0) { al_log_error("ff_decoder", "Failed to open codec (%s).", codec->name); goto err; diff --git a/src/codec/ffmpeg/decoder.h b/src/codec/ffmpeg/decoder.h index 827afd2..b1cbf19 100644 --- a/src/codec/ffmpeg/decoder.h +++ b/src/codec/ffmpeg/decoder.h @@ -14,9 +14,11 @@ struct camu_ff_decoder { struct camu_decoder dec; AVCodecContext *codec_context; struct camu_renderer *renderer; + array(const AVCodec *) supported_hw_codecs; + array(enum AVHWDeviceType) supported_hw_devices; AVBufferRef *hw_context; - enum AVPixelFormat hw_pix_fmt; enum AVHWDeviceType hw_device_type; + enum AVPixelFormat hw_pix_fmt; bool use_frames_context; void (*callback)(void *, struct camu_codec_frame *); void *userdata; diff --git a/src/codec/meson.build b/src/codec/meson.build index 2a72b55..2e89d95 100644 --- a/src/codec/meson.build +++ b/src/codec/meson.build @@ -39,6 +39,8 @@ if get_option('codecs').contains('ffmpeg') if is_windows # Extra static dependencies for Windows. ffmpeg_deps += [compiler.find_library('bcrypt')] + elif is_android + ffmpeg_deps += [compiler.find_library('mediandk')] endif soxr = compiler.find_library('soxr', required: false) if soxr.found() |