On 20/02/17 05:37, wm4 wrote:
> On Sun, 19 Feb 2017 18:46:23 +0000
> Mark Thompson <[email protected]> wrote:
>
>> Creates a new device context from another of a different type which
>> refers to the same underlying hardware.
>> ---
>> libavutil/hwcontext.c | 78
>> ++++++++++++++++++++++++++++++++++++++++++
>> libavutil/hwcontext.h | 16 +++++++++
>> libavutil/hwcontext_internal.h | 8 +++++
>> 3 files changed, 102 insertions(+)
>>
>> diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c
>> index 608da6872..1f9442622 100644
>> --- a/libavutil/hwcontext.c
>> +++ b/libavutil/hwcontext.c
>> @@ -65,6 +65,8 @@ static void hwdevice_ctx_free(void *opaque, uint8_t *data)
>> if (ctx->free)
>> ctx->free(ctx);
>>
>> + av_buffer_unref(&ctx->internal->source_device);
>> +
>> av_freep(&ctx->hwctx);
>> av_freep(&ctx->internal->priv);
>> av_freep(&ctx->internal);
>> @@ -535,6 +537,82 @@ fail:
>> return ret;
>> }
>>
>> +int av_hwdevice_ctx_create_derived(AVBufferRef **dst_ref_ptr,
>> + enum AVHWDeviceType type,
>> + AVBufferRef *src_ref, int flags)
>> +{
>> + AVBufferRef *dst_ref = NULL, *tmp_ref;
>> + AVHWDeviceContext *dst_ctx, *src_ctx, *tmp_ctx;
>> + int ret = 0;
>> +
>> + src_ctx = (AVHWDeviceContext*)src_ref->data;
>> + if (src_ctx->type == type) {
>> + // Currently disallowed. It is possible that deriving a device of
>> + // the same type could do something useful (device partitioning?),
>> + // but for now just don't support it.
>> + return AVERROR(EINVAL);
>
> It could just be a no-nop (basically creating an alias). Whether that'd
> be useful, who knows.
If you can come up with any case where that is useful, then sure. I couldn't
think of one, so making it always fail rather than doing something unhelpful
seemed reasonable.
>> + }
>> +
>> + // If the source device was originally derived from a device of the
>> + // target type (possibly through more intermediates), then return a
>> + // new reference to the original device.
>> + tmp_ref = src_ctx->internal->source_device;
>> + while (tmp_ref) {
>> + tmp_ctx = (AVHWDeviceContext*)tmp_ref->data;
>> + if (tmp_ctx->type == type) {
>> + dst_ref = av_buffer_ref(tmp_ref);
>> + if (!dst_ref) {
>> + ret = AVERROR(ENOMEM);
>> + goto fail;
>> + }
>> + goto done;
>
> In fact, it seems this searches and returns a new reference if a
> "similar" device is found. So my suggestion above would make sense?
The point of this is that it allows X -> Y -> X derivation to succeed when
derivation is only possible in one direction. For example, VAAPI -> OpenCL ->
VAAPI: OpenCL -> VAAPI isn't possible in isolation, but we would still like it
to work in this case because it is helpful with hwmap in filter graphs.
>> + }
>> + tmp_ref = tmp_ctx->internal->source_device;
>> + }
>> +
>> + // Attempt to derive a new device from the given source device, or
>> + // any of it's ancestors.
>> + dst_ref = av_hwdevice_ctx_alloc(type);
>> + if (!dst_ref) {
>> + ret = AVERROR(ENOMEM);
>> + goto fail;
>> + }
>> + dst_ctx = (AVHWDeviceContext*)dst_ref->data;
>> +
>> + tmp_ref = src_ref;
>> + while (tmp_ref) {
>> + tmp_ctx = (AVHWDeviceContext*)tmp_ref->data;
>> + if (dst_ctx->internal->hw_type->device_derive) {
>> + ret = dst_ctx->internal->hw_type->device_derive(dst_ctx,
>> + tmp_ctx,
>> + flags);
>> + if (ret == 0) {
>> + dst_ctx->internal->source_device = av_buffer_ref(src_ref);
>> + if (!dst_ctx->internal->source_device) {
>> + ret = AVERROR(ENOMEM);
>> + goto fail;
>> + }
>> + goto done;
>> + }
>> + if (ret != AVERROR(ENOSYS))
>> + goto fail;
>> + }
>> + tmp_ref = tmp_ctx->internal->source_device;
>> + }
>
> Pretty scary, but I guess it does the trick. I hope we won't have to
> think about generalized device graphs, and it'll always stay a simple
> list.
Too late :) The example in 0/24 is initially VAAPI -> OpenCL, and we are
making use of this magic to do the OpenCL -> QSV derivation by going back up
the tree to the VAAPI device and deriving from there.
> One thing that makes me wonder is, if you derive a device type B from a
> device of type A multiple times, it will call device_derive() for each
> device of type A created. Except if device A is derived from a device C
> of type B, then the first loop will create a new reference to device C
> if a device of type B is requested. That makes me wonder whether the
> first loop is even worth having, since such use-cases imply the
> device_derive implementation should be either fast or cache the derived
> devices. Or maybe all "shared" devices should share a cache of derived
> devices.
The derivation structure is currently a tree with a clear root, though leaves
might be copies of devices higher in the tree. As long as we don't introduce
both X -> Y and Y -> X derivations which end up with different semantics then I
don't think this is a worry.
> (It's probably fine to ignore what I just wrote, I might be worrying
> about nothing.)
>
>> +
>> + ret = AVERROR(ENOSYS);
>> + goto fail;
>> +
>> +done:
>> + *dst_ref_ptr = dst_ref;
>> + return 0;
>> +
>> +fail:
>> + av_buffer_unref(&dst_ref);
>> + *dst_ref_ptr = NULL;
>> + return ret;
>> +}
>> +
>> static void ff_hwframe_unmap(void *opaque, uint8_t *data)
>> {
>> HWMapDescriptor *hwmap = (HWMapDescriptor*)data;
>> diff --git a/libavutil/hwcontext.h b/libavutil/hwcontext.h
>> index 037ca6422..a31799267 100644
>> --- a/libavutil/hwcontext.h
>> +++ b/libavutil/hwcontext.h
>> @@ -269,6 +269,22 @@ int av_hwdevice_ctx_create(AVBufferRef **device_ctx,
>> enum AVHWDeviceType type,
>> const char *device, AVDictionary *opts, int
>> flags);
>>
>> /**
>> + * Create a new device of the specified type from an existing device.
>
> Maybe it could mention that it can also return a new reference to an
> existing, cached derived device.
Yeah, it should just describe the process above.
>> + *
>> + * @param dst_ctx On success, a reference to the newly-created
>> + * AVHWDeviceContext.
>> + * @param type The type of the new device to create.
>> + * @param src_ctx A reference to an existing AVHWDeviceContext which will be
>> + * used to create the new device.
>> + * @param flags Currently unused; should be set to zero.
>
> I'm really not sure about all those unused "flags" parameters we're
> adding to the hwdevice APIs. If we need something new, the flags
> parameter will most likely be unused.
>
> (Just compare with those "reserved" parameters in win32 APIs.)
I add a use for one of the them in a later patch.
>> + * @return Zero on success, a negative AVERROR code on failure.
>> + */
>> +int av_hwdevice_ctx_create_derived(AVBufferRef **dst_ctx,
>> + enum AVHWDeviceType type,
>> + AVBufferRef *src_ctx, int flags);
>> +
>> +
>> +/**
>> * Allocate an AVHWFramesContext tied to a given device context.
>> *
>> * @param device_ctx a reference to a AVHWDeviceContext. This function will
>> make
>> diff --git a/libavutil/hwcontext_internal.h b/libavutil/hwcontext_internal.h
>> index 11e3a68e4..66f54142e 100644
>> --- a/libavutil/hwcontext_internal.h
>> +++ b/libavutil/hwcontext_internal.h
>> @@ -66,6 +66,8 @@ typedef struct HWContextType {
>>
>> int (*device_create)(AVHWDeviceContext *ctx, const char
>> *device,
>> AVDictionary *opts, int flags);
>> + int (*device_derive)(AVHWDeviceContext *dst_ctx,
>> + AVHWDeviceContext *src_ctx, int
>> flags);
>>
>> int (*device_init)(AVHWDeviceContext *ctx);
>> void (*device_uninit)(AVHWDeviceContext *ctx);
>> @@ -95,6 +97,12 @@ typedef struct HWContextType {
>> struct AVHWDeviceInternal {
>> const HWContextType *hw_type;
>> void *priv;
>> +
>> + /**
>> + * For a derived device, a reference to the original device
>> + * context it was derived from.
>> + */
>> + AVBufferRef *source_device;
>> };
>>
>> struct AVHWFramesInternal {
>
> In general seems like an ok idea.
Thanks,
- Mark
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel