On 28/03/17 11:18, wm4 wrote: > On Mon, 27 Mar 2017 21:38:12 +0100 > Mark Thompson <s...@jkqxz.net> wrote: > >> This also adds a new field to AVHWAccel to set supported hardware device >> types, so that we can query the right hwaccel. >> --- >> Not entirely sure that the AVCodecContext should be the first argument of >> probe_hw() - AVCodecParameters might be nicer, but we are likely to end up >> needing an AVCodecContext anyway. (More generally, please bikeshed freely >> over naming and structures used.) >> >> The probe_hw() function on the D*VA hwaccel would be where the extra >> alignment gets set (when AVCodecContext.hw_device_ctx is not used). >> >> >> doc/APIchanges | 3 +++ >> libavcodec/avcodec.h | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ >> libavcodec/decode.c | 53 >> ++++++++++++++++++++++++++++++++++++++++++++------- >> libavcodec/encode.c | 13 +++++++++++++ >> libavcodec/internal.h | 10 ++++++++++ >> libavcodec/utils.c | 15 +++++++++++++++ >> 6 files changed, 138 insertions(+), 7 deletions(-) >> >> diff --git a/doc/APIchanges b/doc/APIchanges >> index 5da821c90..649d35a08 100644 >> --- a/doc/APIchanges >> +++ b/doc/APIchanges >> @@ -13,6 +13,9 @@ libavutil: 2017-03-23 >> >> API changes, most recent first: >> >> +2017-04-xx - xxxxxxx - lavc 58.x+1.0 - avcodec.h >> + Add AVHWAccel.device_type and avcodec_probe_hw(). >> + >> 2017-04-xx - xxxxxxx - lavc 58.x.y+1 - avcodec.h >> Add AV_HWACCEL_FLAG_ALLOW_PROFILE_MISMATCH. >> >> diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h >> index 2aa70ca4f..8f7293d02 100644 >> --- a/libavcodec/avcodec.h >> +++ b/libavcodec/avcodec.h >> @@ -35,6 +35,7 @@ >> #include "libavutil/cpu.h" >> #include "libavutil/dict.h" >> #include "libavutil/frame.h" >> +#include "libavutil/hwcontext.h" >> #include "libavutil/log.h" >> #include "libavutil/pixfmt.h" >> #include "libavutil/rational.h" >> @@ -2857,6 +2858,15 @@ typedef struct AVCodec { >> * packets before decoding. >> */ >> const char *bsfs; >> + >> + /** >> + * Test support for the given encode/decode parameters, and if possible >> + * set the fields in the frames context to a set of usable values. >> + * >> + * If a hwaccel is used with a decoder and it also has a probe_hw() >> + * function, that will be called instead of this. >> + */ >> + int (*probe_hw)(AVCodecContext *avctx, AVHWFramesContext *hw_frames); >> } AVCodec; >> >> /** >> @@ -2898,6 +2908,11 @@ typedef struct AVHWAccel { >> */ >> int capabilities; >> >> + /** >> + * Hardware device type which this hwaccel can use. >> + */ >> + enum AVHWDeviceType device_type; >> + >> /***************************************************************** >> * No fields below this line are part of the public API. They >> * may not be used outside of libavcodec and can be changed and >> @@ -2988,6 +3003,11 @@ typedef struct AVHWAccel { >> * Internal hwaccel capabilities. >> */ >> int caps_internal; >> + >> + /** >> + * Probe hardware for support and parameters. >> + */ >> + int (*probe_hw)(AVCodecContext *avctx, AVHWFramesContext *hw_frames); >> } AVHWAccel; >> >> /** >> @@ -4927,6 +4947,37 @@ const AVCodecDescriptor >> *avcodec_descriptor_get_by_name(const char *name); >> AVCPBProperties *av_cpb_properties_alloc(size_t *size); >> >> /** >> + * Probe hardware support for the given codec parameters. >> + * >> + * For decoders, it tests whether a stream with the given parameters can be >> + * decoded on the given device. For encoders, it tests whether encoding >> with >> + * the given parameters is possible on the device. >> + * >> + * On successful return, the fields of hw_frames_ref are set such that, once >> + * initialised, it will be usable as AVCodecContext.hw_frames_ctx. If the >> + * user has additional requirements to apply to the frames context (for >> + * example, for a larger pool size or for frames created with some system- >> + * specific attribute) then they should be applied after this call but >> before >> + * calling av_hwframe_ctx_init(). >> + * >> + * The device to use is provided as the allocating context of hw_frames_ref. >> + * (Even if set, the hw_frames_ctx and hw_device_ctx fields of avctx are >> + * ignored.) >> + * >> + * @param avctx Codec context with relevant fields set. It need not have >> been >> + * opened. >> + * @param hw_frames_ref A reference to a newly-allocated AVHWFramesContext, >> + * which will be used to return the result. >> + * @return 0 on success, otherwise negative error code: >> + * AVERROR(ENOSYS): This function is not implemented (hardware support >> + * may or may not be available). >> + * AVERROR(ENODEV): The device is not usable. >> + * AVERROR(EINVAL): The codec parameters are not supported. >> + * Other errors: Probing failed for some other reason. >> + */ >> +int avcodec_probe_hw(AVCodecContext *avctx, AVBufferRef *hw_frames_ref); > > This API still makes me a bit uneasy. Regular get_format hwaccel init > relies on the fact that the decoder has set some fields from parsing > the bit stream, like the current sw_pixfmt or frame size. These aren't > necessarily available on a "fresh" AVCodecContext (not even if > extradata is available). > > So normally, the only time this will work properly is in the get_format > callback. Which is ok, but which I think isn't the entire intention? I > think I'd be fine with allowing this call only during get_format or so.
It's certainly the intent that this is usable outside get format. In the most general case, probing with a codec, a profile, and maybe some dimensions can test the hardware support without any stream at all. > Or maybe it's supposed to return "approximate" results outside of > get_format in the first place. > > (For encoding on the other hand it's probably fine - though I'm not > sure if this is supposed to be called before or after opening the > context.) Before. I didn't provide a VAAPI implementation for encode because with the current design it requires some nontrivial rearrangement of code to work (stuff done by the current per-codec init functions needs to move to a callback so that it's usable at other times - I'll do this if once we have some agreement on the API). > As a minor nit, this can't distinguish between required initial pool > size and upper bound on pool size. Users who want to preallocate frames > with e.g. vdpau would want to know the latter. (Well, I don't want > that, and see no reason why anyone would want it, so I officially don't > care about this argument.) My intent is that for now the decoder would always allocate the returned initial_pool_size + extra_hw_frames, so a user would do the same. (They control extra_hw_frames so how many more they have is completely up to them, but it works the same whether they are doing the allocation or lavc is.) > I'll make full use of my permission to bikeshed, so here is a different > API that impractically changes everything: > > Proposal 1: > > Add new init_hw_device and init_hw_frames callbacks: > > /* > * Create a hw device and set ctx->hw_device_ctx. This might be used > * for hardware decoding. If ENOSYS is returned, the next hwaccel > * method is tried, and if all fail, software decoding is used. > * > * If hw_device_ctx is already set, this callback is not used, and > * does not need to be set. > * > * If internal reinit is done (like on flush or if the codec > * parameters change), hw_device_ctx is cleared, and this callback > * can be called again. > */ > int (*init_hw_device)(AVCodecContext *ctx, AVHWDeviceType type); > > /* > * When this is called, ctx->hw_frames_ctx is already setup in a basic > * way (such as frame dimensions and surface format). This callback > * can be used to refine the frame parameters further. > */ > int (*init_hw_frames)(AVCodecContext *ctx); Hmm, that only covers the inline decode case, but it does do it rather well. > Proposal 2: > > Add a mechanism to let the caller setup parameters in advance, and > letting the hwaccel code use these parameters without needing callbacks > back to user code. > > Proposal 2a: > > Add these fields to AVCodecContext: > > AVDictionary *hw_frames_params; > void *hw_frames_opaque_params; // AVDictionary can't have pointers > AVHWDeviceType hw_frames_opaque_params_type; > > Proposal 2b: > > Add this field to AVCodecContext: > > /* > * When allocating a new hw_frames_ctx, use the parameters found in > * this context as defaults (if the device type matches). The pixel > * format and size will be changed, and the number of initial pool > * frames can be extended. > */ > AHWFramesContext *hw_frames_template; I think this proposal is horrible. > Proposal 3: > > Use your API, and restrict when/where it's allowed to be called. Proposal 13: Do both 1 and 3. 1 as above for inline decode, then: int avcodec_probe_hw(AVCodecParameters *par, AVSomething *returned_stuff); for the non-inline cases (both encode and decode). Unclear what type the something should be - AVHWFramesContext is less useful here, though it would be neat for decode when you already have the dimensions. >> + >> +/** >> * @} >> */ >> >> diff --git a/libavcodec/decode.c b/libavcodec/decode.c >> index e4f6a0d72..4029683e6 100644 >> --- a/libavcodec/decode.c >> +++ b/libavcodec/decode.c >> @@ -701,13 +701,6 @@ static int is_hwaccel_pix_fmt(enum AVPixelFormat >> pix_fmt) >> return desc->flags & AV_PIX_FMT_FLAG_HWACCEL; >> } >> >> -enum AVPixelFormat avcodec_default_get_format(struct AVCodecContext *s, >> const enum AVPixelFormat *fmt) >> -{ >> - while (*fmt != AV_PIX_FMT_NONE && is_hwaccel_pix_fmt(*fmt)) >> - ++fmt; >> - return fmt[0]; >> -} >> - >> static AVHWAccel *find_hwaccel(enum AVCodecID codec_id, >> enum AVPixelFormat pix_fmt) >> { >> @@ -753,6 +746,28 @@ static int setup_hwaccel(AVCodecContext *avctx, >> return 0; >> } >> >> +enum AVPixelFormat avcodec_default_get_format(struct AVCodecContext *s, >> const enum AVPixelFormat *fmt) >> +{ >> + if (s->hw_device_ctx) { >> + const AVHWDeviceContext *device = (const >> AVHWDeviceContext*)s->hw_device_ctx->data; >> + const AVHWAccel *hwaccel; >> + int i; >> + for (i = 0; fmt[i] != AV_PIX_FMT_NONE; i++) { >> + if (!is_hwaccel_pix_fmt(fmt[i])) >> + continue; >> + hwaccel = find_hwaccel(s->codec_id, fmt[i]); >> + if (!hwaccel) >> + continue; >> + if (hwaccel->device_type == device->type) >> + return fmt[i]; >> + } >> + } >> + >> + while (*fmt != AV_PIX_FMT_NONE && is_hwaccel_pix_fmt(*fmt)) >> + ++fmt; >> + return fmt[0]; >> +} > > Does this change normal decode behavior to using hwaccels if > hw_device_ctx is used and get_format isn't set by the user? That's > great, but could use a mention in the commit message. (And I suppose > some would say this should be a separate commit.) I was actually trying to change it to match by device type to make sure it gets the same answer as probe_hw() does, but yes it does do somewhat more than that. I'll split it into another patch and make that clear. >> + >> int ff_get_format(AVCodecContext *avctx, const enum AVPixelFormat *fmt) >> { >> const AVPixFmtDescriptor *desc; >> @@ -1210,3 +1225,27 @@ void ff_decode_bsfs_uninit(AVCodecContext *avctx) >> av_freep(&s->bsfs); >> s->nb_bsfs = 0; >> } >> + >> +int ff_decode_probe_hw(AVCodecContext *avctx, AVHWFramesContext *hw_frames) >> +{ >> + const AVCodec *codec = avctx->codec; >> + const AVHWAccel *hwaccel = NULL; >> + >> + if (!codec) >> + return AVERROR(EINVAL); >> + >> + if (!hw_frames->device_ctx) >> + return AVERROR(ENODEV); >> + >> + while ((hwaccel = av_hwaccel_next(hwaccel))) { >> + if (hwaccel->id == avctx->codec_id && >> + hwaccel->device_type == hw_frames->device_ctx->type) >> + break; >> + } >> + if (hwaccel && hwaccel->probe_hw) >> + return hwaccel->probe_hw(avctx, hw_frames); >> + else if (codec->probe_hw) >> + return codec->probe_hw(avctx, hw_frames); >> + else >> + return AVERROR(ENOSYS); >> +} >> diff --git a/libavcodec/encode.c b/libavcodec/encode.c >> index 9bb7ae5bd..d4914d7da 100644 >> --- a/libavcodec/encode.c >> +++ b/libavcodec/encode.c >> @@ -354,3 +354,16 @@ int attribute_align_arg >> avcodec_receive_packet(AVCodecContext *avctx, AVPacket * >> avctx->internal->buffer_pkt_valid = 0; >> return 0; >> } >> + >> +int ff_encode_probe_hw(AVCodecContext *avctx, AVHWFramesContext *hw_frames) >> +{ >> + const AVCodec *codec = avctx->codec; >> + >> + if (!codec) >> + return AVERROR(EINVAL); >> + >> + if (codec->probe_hw) >> + return codec->probe_hw(avctx, hw_frames); >> + else >> + return AVERROR(ENOSYS); >> +} >> diff --git a/libavcodec/internal.h b/libavcodec/internal.h >> index 403fb4a09..930b32fd6 100644 >> --- a/libavcodec/internal.h >> +++ b/libavcodec/internal.h >> @@ -285,4 +285,14 @@ int ff_decode_frame_props(AVCodecContext *avctx, >> AVFrame *frame); >> */ >> AVCPBProperties *ff_add_cpb_side_data(AVCodecContext *avctx); >> >> +/** >> + * Probe decoder/hwaccel hardware support. >> + */ >> +int ff_decode_probe_hw(AVCodecContext *avctx, AVHWFramesContext *hw_frames); >> + >> +/** >> + * Probe encoder hardware support. >> + */ >> +int ff_encode_probe_hw(AVCodecContext *avctx, AVHWFramesContext *hw_frames); >> + >> #endif /* AVCODEC_INTERNAL_H */ >> diff --git a/libavcodec/utils.c b/libavcodec/utils.c >> index bc421f67f..2a73098ec 100644 >> --- a/libavcodec/utils.c >> +++ b/libavcodec/utils.c >> @@ -1685,3 +1685,18 @@ int avcodec_parameters_to_context(AVCodecContext >> *codec, >> >> return 0; >> } >> + >> +int avcodec_probe_hw(AVCodecContext *avctx, AVBufferRef *hw_frames_ref) >> +{ >> + AVHWFramesContext *hw_frames = (AVHWFramesContext*)hw_frames_ref->data; >> + >> + switch (avctx->codec_type) { >> + case AVMEDIA_TYPE_VIDEO: >> + if (av_codec_is_encoder(avctx->codec)) >> + return ff_encode_probe_hw(avctx, hw_frames); >> + else >> + return ff_decode_probe_hw(avctx, hw_frames); >> + default: >> + return AVERROR(ENOSYS); >> + } >> +} > > Otherwise all seems fine. Thanks, - Mark _______________________________________________ libav-devel mailing list libav-devel@libav.org https://lists.libav.org/mailman/listinfo/libav-devel