diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c index 31c7840dba..1a50635018 100644 --- a/libavutil/hwcontext.c +++ b/libavutil/hwcontext.c @@ -122,6 +122,7 @@ static const AVClass hwdevice_ctx_class = { static void hwdevice_ctx_free(void *opaque, uint8_t *data) { AVHWDeviceContext *ctx = (AVHWDeviceContext*)data; + int i; /* uninit might still want access the hw context and the user * free() callback might destroy it, so uninit has to be called first */ @@ -132,6 +133,8 @@ static void hwdevice_ctx_free(void *opaque, uint8_t *data) ctx->free(ctx); av_buffer_unref(&ctx->internal->source_device); + for (i = 0; i < AV_HWDEVICE_TYPE_NB; i++) + av_buffer_unref(&ctx->internal->derived_devices[i]); av_freep(&ctx->hwctx); av_freep(&ctx->internal->priv); @@ -643,6 +646,26 @@ fail: return ret; } +static AVBufferRef* find_derived_hwdevice_ctx(AVBufferRef *src_ref, enum AVHWDeviceType type) +{ + AVBufferRef *tmp_ref; + AVHWDeviceContext *src_ctx; + int i; + + src_ctx = (AVHWDeviceContext*)src_ref->data; + if (src_ctx->type == type) + return src_ref; + + for (i = 0; i < AV_HWDEVICE_TYPE_NB; i++) + if (src_ctx->internal->derived_devices[i]) { + tmp_ref = find_derived_hwdevice_ctx(src_ctx->internal->derived_devices[i], type); + if (tmp_ref) + return tmp_ref; + } + + return NULL; +} + int av_hwdevice_ctx_create_derived_opts(AVBufferRef **dst_ref_ptr, enum AVHWDeviceType type, AVBufferRef *src_ref, @@ -666,6 +689,16 @@ int av_hwdevice_ctx_create_derived_opts(AVBufferRef **dst_ref_ptr, tmp_ref = tmp_ctx->internal->source_device; } + tmp_ref = find_derived_hwdevice_ctx(src_ref, type); + if (tmp_ref) { + dst_ref = av_buffer_ref(tmp_ref); + if (!dst_ref) { + ret = AVERROR(ENOMEM); + goto fail; + } + goto done; + } + dst_ref = av_hwdevice_ctx_alloc(type); if (!dst_ref) { ret = AVERROR(ENOMEM); @@ -687,6 +720,11 @@ int av_hwdevice_ctx_create_derived_opts(AVBufferRef **dst_ref_ptr, ret = AVERROR(ENOMEM); goto fail; } + tmp_ctx->internal->derived_devices[type] = av_buffer_ref(dst_ref); + if (!tmp_ctx->internal->derived_devices[type]) { + ret = AVERROR(ENOMEM); + goto fail; + } ret = av_hwdevice_ctx_init(dst_ref); if (ret < 0) goto fail; diff --git a/libavutil/hwcontext.h b/libavutil/hwcontext.h index 04d19d89c2..0979fa2715 100644 --- a/libavutil/hwcontext.h +++ b/libavutil/hwcontext.h @@ -37,6 +37,7 @@ enum AVHWDeviceType { AV_HWDEVICE_TYPE_OPENCL, AV_HWDEVICE_TYPE_MEDIACODEC, AV_HWDEVICE_TYPE_VULKAN, + AV_HWDEVICE_TYPE_NB, ///< number of hw device types, not part of API/ABI. }; typedef struct AVHWDeviceInternal AVHWDeviceInternal; diff --git a/libavutil/hwcontext_internal.h b/libavutil/hwcontext_internal.h index e6266494ac..f6fb67c491 100644 --- a/libavutil/hwcontext_internal.h +++ b/libavutil/hwcontext_internal.h @@ -109,6 +109,12 @@ struct AVHWDeviceInternal { * context it was derived from. */ AVBufferRef *source_device; + + /** + * An array of reference to device contexts which + * were derived from this device. + */ + AVBufferRef *derived_devices[AV_HWDEVICE_TYPE_NB]; }; struct AVHWFramesInternal { diff --git a/libavutil/hwcontext_qsv.c b/libavutil/hwcontext_qsv.c index 853fb7f60d..91457c9c9e 100644 --- a/libavutil/hwcontext_qsv.c +++ b/libavutil/hwcontext_qsv.c @@ -274,7 +274,7 @@ static void qsv_frames_uninit(AVHWFramesContext *ctx) av_buffer_unref(&s->child_frames_ref); } -static void qsv_pool_release_dummy(void *opaque, uint8_t *data) +static void qsv_release_dummy(void *opaque, uint8_t *data) { } @@ -287,7 +287,7 @@ static AVBufferRef *qsv_pool_alloc(void *opaque, size_t size) if (s->nb_surfaces_used < hwctx->nb_surfaces) { s->nb_surfaces_used++; return av_buffer_create((uint8_t*)(s->surfaces_internal + s->nb_surfaces_used - 1), - sizeof(*hwctx->surfaces), qsv_pool_release_dummy, NULL, 0); + sizeof(*hwctx->surfaces), qsv_release_dummy, NULL, 0); } return NULL; @@ -1596,8 +1596,15 @@ static int qsv_device_create(AVHWDeviceContext *ctx, const char *device, child_device = (AVHWDeviceContext*)priv->child_device_ctx->data; impl = choose_implementation(device, child_device_type); + ret = qsv_device_derive_from_child(ctx, impl, child_device, 0); + if (ret >= 0) { + ctx->internal->source_device = av_buffer_ref(priv->child_device_ctx); + child_device->internal->derived_devices[ctx->type] = av_buffer_create((uint8_t*)ctx, sizeof(*ctx), qsv_release_dummy, ctx, 0); + if (!child_device->internal->derived_devices[ctx->type]) + return AVERROR(ENOMEM); + } - return qsv_device_derive_from_child(ctx, impl, child_device, 0); + return ret; } const HWContextType ff_hwcontext_type_qsv = {