On Sat, 5 Aug 2017 23:53:11 +0100
Mark Thompson <[email protected]> wrote:
> ---
> This is several patches squashed together to invite thoughts.
>
> The idea here is that we add a new array of possible hardware configurations
> to codec definitions, expressed as a hardware device type and the matching
> pixel format. We can then use that to set up hwaccels without any additional
> knowledge at all, creating the appropriate device at startup and then
> matching the format in get_format in order to use it.
>
> This is implemented here in lavc with the H.264 decoder and then used in
> avconv. There is quite a bit of churn in avconv hardware setup as the choice
> of device all moves around, but it is hopefully a bit clearer now (and less
> likely to try to give devices to things not needing them). "-hwaccel auto"
> is reimplemented completely as using the first device from the codec config
> list which can be instantiated.
>
>
> avtools/avconv.c | 57 +++++++++----
> avtools/avconv.h | 9 +-
> avtools/avconv_hw.c | 233
> ++++++++++++++++++++++++++++++++++-----------------
> avtools/avconv_opt.c | 41 ++++-----
> libavcodec/avcodec.h | 23 +++++
> libavcodec/h264dec.c | 18 ++++
> 6 files changed, 254 insertions(+), 127 deletions(-)
>
> diff --git a/avtools/avconv.c b/avtools/avconv.c
> index 4e3ffecde..c5b440e08 100644
> --- a/avtools/avconv.c
> +++ b/avtools/avconv.c
> @@ -1631,15 +1631,6 @@ static void print_sdp(void)
> av_freep(&avc);
> }
>
> -static const HWAccel *get_hwaccel(enum AVPixelFormat pix_fmt)
> -{
> - int i;
> - for (i = 0; hwaccels[i].name; i++)
> - if (hwaccels[i].pix_fmt == pix_fmt)
> - return &hwaccels[i];
> - return NULL;
> -}
> -
> static enum AVPixelFormat get_format(AVCodecContext *s, const enum
> AVPixelFormat *pix_fmts)
> {
> InputStream *ist = s->opaque;
> @@ -1647,17 +1638,50 @@ static enum AVPixelFormat get_format(AVCodecContext
> *s, const enum AVPixelFormat
> int ret;
>
> for (p = pix_fmts; *p != -1; p++) {
> - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(*p);
> - const HWAccel *hwaccel;
> + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(*p);
> + const HWAccel *hwaccel = NULL;
> + const AVCodecHWConfig *hwconfig = NULL;
> + int i;
>
> if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL))
> break;
>
> - hwaccel = get_hwaccel(*p);
> - if (!hwaccel ||
> - (ist->active_hwaccel_id && ist->active_hwaccel_id !=
> hwaccel->id) ||
> - (ist->hwaccel_id != HWACCEL_AUTO && ist->hwaccel_id !=
> hwaccel->id))
> - continue;
> + if (ist->hwaccel_id == HWACCEL_GENERIC && s->codec->hw_configs) {
> + for (i = 0;; i++) {
> + if (s->codec->hw_configs[i].device_type ==
> AV_HWDEVICE_TYPE_NONE)
> + break;
> + if (s->codec->hw_configs[i].pix_fmt == *p) {
> + hwconfig = &s->codec->hw_configs[i];
> + break;
> + }
> + }
> + }
> + if (hwconfig) {
> + if (hwconfig->device_type != ist->hwaccel_device_type) {
> + // Different hwaccel offered, ignore.
> + continue;
> + }
> + hwaccel = &hwaccels[0];
> + }
> + if (!hwaccel) {
> + int i;
> + hwaccel = NULL;
> + for (i = 1; hwaccels[i].name; i++) {
> + if (hwaccels[i].pix_fmt == *p) {
> + hwaccel = &hwaccels[i];
> + break;
> + }
> + }
> + if (!hwaccel) {
> + // No hwaccel supporting this pixfmt.
> + continue;
> + }
> + if (hwaccel->id != ist->hwaccel_id &&
> + hwaccel->id != HWACCEL_AUTO) {
> + // Does not match requested hwaccel.
> + continue;
> + }
> + }
>
> ret = hwaccel->init(s);
> if (ret < 0) {
> @@ -1677,7 +1701,6 @@ static enum AVPixelFormat get_format(AVCodecContext *s,
> const enum AVPixelFormat
> return AV_PIX_FMT_NONE;
> }
>
> - ist->active_hwaccel_id = hwaccel->id;
> ist->hwaccel_pix_fmt = *p;
> break;
> }
> diff --git a/avtools/avconv.h b/avtools/avconv.h
> index b5843fbc0..0d24c71a7 100644
> --- a/avtools/avconv.h
> +++ b/avtools/avconv.h
> @@ -52,13 +52,9 @@
> enum HWAccelID {
> HWACCEL_NONE = 0,
> HWACCEL_AUTO,
> - HWACCEL_VDPAU,
> - HWACCEL_DXVA2,
> + HWACCEL_GENERIC,
> HWACCEL_VDA,
> HWACCEL_QSV,
> - HWACCEL_VAAPI,
> - HWACCEL_D3D11VA,
> - HWACCEL_CUVID,
> };
>
> typedef struct HWAccel {
> @@ -66,7 +62,6 @@ typedef struct HWAccel {
> int (*init)(AVCodecContext *s);
> enum HWAccelID id;
> enum AVPixelFormat pix_fmt;
> - enum AVHWDeviceType device_type;
> } HWAccel;
>
> typedef struct HWDevice {
> @@ -301,11 +296,11 @@ typedef struct InputStream {
>
> /* hwaccel options */
> enum HWAccelID hwaccel_id;
> + enum AVHWDeviceType hwaccel_device_type;
> char *hwaccel_device;
> enum AVPixelFormat hwaccel_output_format;
>
> /* hwaccel context */
> - enum HWAccelID active_hwaccel_id;
> void *hwaccel_ctx;
> void (*hwaccel_uninit)(AVCodecContext *s);
> int (*hwaccel_get_buffer)(AVCodecContext *s, AVFrame *frame, int flags);
> diff --git a/avtools/avconv_hw.c b/avtools/avconv_hw.c
> index 36ef86692..a424e7055 100644
> --- a/avtools/avconv_hw.c
> +++ b/avtools/avconv_hw.c
> @@ -62,6 +62,31 @@ static HWDevice *hw_device_add(void)
> return hw_devices[nb_hw_devices++];
> }
>
> +static char *hw_device_default_name(enum AVHWDeviceType type)
> +{
> + // Make an automatic name of the form "type%d". We arbitrarily
> + // limit at 1000 anonymous devices of the same type - there is
> + // probably something else very wrong if you get to this limit.
> + const char *type_name = av_hwdevice_get_type_name(type);
> + char *name;
> + size_t index_pos;
> + int index, index_limit = 1000;
> + index_pos = strlen(type_name);
> + name = av_malloc(index_pos + 4);
> + if (!name)
> + return NULL;
> + for (index = 0; index < index_limit; index++) {
> + snprintf(name, index_pos + 4, "%s%d", type_name, index);
> + if (!hw_device_get_by_name(name))
> + break;
> + }
> + if (index >= index_limit) {
> + av_freep(&name);
> + return NULL;
> + }
> + return name;
> +}
> +
> int hw_device_init_from_string(const char *arg, HWDevice **dev_out)
> {
> // "type=name:device,key=value,key2=value2"
> @@ -109,27 +134,11 @@ int hw_device_init_from_string(const char *arg,
> HWDevice **dev_out)
>
> p += 1 + k;
> } else {
> - // Give the device an automatic name of the form "type%d".
> - // We arbitrarily limit at 1000 anonymous devices of the same
> - // type - there is probably something else very wrong if you
> - // get to this limit.
> - size_t index_pos;
> - int index, index_limit = 1000;
> - index_pos = strlen(type_name);
> - name = av_malloc(index_pos + 4);
> + name = hw_device_default_name(type);
> if (!name) {
> err = AVERROR(ENOMEM);
> goto fail;
> }
> - for (index = 0; index < index_limit; index++) {
> - snprintf(name, index_pos + 4, "%s%d", type_name, index);
> - if (!hw_device_get_by_name(name))
> - break;
> - }
> - if (index >= index_limit) {
> - errmsg = "too many devices";
> - goto invalid;
> - }
> }
>
> if (!*p) {
> @@ -212,6 +221,49 @@ fail:
> goto done;
> }
>
> +static int hw_device_init_from_type(enum AVHWDeviceType type,
> + const char *device,
> + HWDevice **dev_out)
> +{
> + AVBufferRef *device_ref = NULL;
> + HWDevice *dev;
> + char *name;
> + int err;
> +
> + name = hw_device_default_name(type);
> + if (!name) {
> + err = AVERROR(ENOMEM);
> + goto fail;
> + }
> +
> + err = av_hwdevice_ctx_create(&device_ref, type, device, NULL, 0);
> + if (err < 0) {
> + av_log(NULL, AV_LOG_ERROR,
> + "Device creation failed: %d.\n", err);
> + goto fail;
> + }
> +
> + dev = hw_device_add();
> + if (!dev) {
> + err = AVERROR(ENOMEM);
> + goto fail;
> + }
> +
> + dev->name = name;
> + dev->type = type;
> + dev->device_ref = device_ref;
> +
> + if (dev_out)
> + *dev_out = dev;
> +
> + return 0;
> +
> +fail:
> + av_freep(&name);
> + av_buffer_unref(&device_ref);
> + return err;
> +}
> +
> void hw_device_free_all(void)
> {
> int i;
> @@ -224,85 +276,116 @@ void hw_device_free_all(void)
> nb_hw_devices = 0;
> }
>
> -static enum AVHWDeviceType hw_device_match_type_by_hwaccel(enum HWAccelID
> hwaccel_id)
> +static HWDevice *hw_device_match_by_codec(const AVCodec *codec)
> {
> + enum AVHWDeviceType type;
> + HWDevice *dev = NULL;
> int i;
> - if (hwaccel_id == HWACCEL_NONE)
> + if (!codec->hw_configs)
> return AV_HWDEVICE_TYPE_NONE;
> - for (i = 0; hwaccels[i].name; i++) {
> - if (hwaccels[i].id == hwaccel_id)
> - return hwaccels[i].device_type;
> + for (i = 0;; i++) {
> + type = codec->hw_configs[i].device_type;
> + if (type == AV_HWDEVICE_TYPE_NONE)
> + break;
> + if ((dev = hw_device_get_by_type(type)))
> + break;
> }
> - return AV_HWDEVICE_TYPE_NONE;
> -}
> -
> -static enum AVHWDeviceType hw_device_match_type_in_name(const char
> *codec_name)
> -{
> - const char *type_name;
> - enum AVHWDeviceType type;
> - for (type = av_hwdevice_iterate_types(AV_HWDEVICE_TYPE_NONE);
> - type != AV_HWDEVICE_TYPE_NONE;
> - type = av_hwdevice_iterate_types(type)) {
> - type_name = av_hwdevice_get_type_name(type);
> - if (strstr(codec_name, type_name))
> - return type;
> - }
> - return AV_HWDEVICE_TYPE_NONE;
> + return dev;
> }
>
> int hw_device_setup_for_decode(InputStream *ist)
> {
> enum AVHWDeviceType type;
> - HWDevice *dev;
> - const char *type_name;
> - int err;
> + HWDevice *dev = NULL;
> + int err, auto_device = 0;
>
> if (ist->hwaccel_device) {
> dev = hw_device_get_by_name(ist->hwaccel_device);
> if (!dev) {
> - char *tmp;
> - size_t len;
> - type = hw_device_match_type_by_hwaccel(ist->hwaccel_id);
> - if (type == AV_HWDEVICE_TYPE_NONE) {
> - // No match - this isn't necessarily invalid, though,
> - // because an explicit device might not be needed or
> - // the hwaccel setup could be handled elsewhere.
> + if (ist->hwaccel_id == HWACCEL_AUTO) {
> + auto_device = 1;
> + } else if (ist->hwaccel_id == HWACCEL_GENERIC) {
> + type = ist->hwaccel_device_type;
> + err = hw_device_init_from_type(type, ist->hwaccel_device,
> + &dev);
> + } else {
> + // This will be dealt with by API-specific initialisation
> + // (using hwaccel_device), so nothing further needed here.
> return 0;
> }
> - type_name = av_hwdevice_get_type_name(type);
> - len = strlen(type_name) + 1 +
> - strlen(ist->hwaccel_device) + 1;
> - tmp = av_malloc(len);
> - if (!tmp)
> - return AVERROR(ENOMEM);
> - snprintf(tmp, len, "%s:%s", type_name, ist->hwaccel_device);
> - err = hw_device_init_from_string(tmp, &dev);
> - av_free(tmp);
> - if (err < 0)
> - return err;
> }
> } else {
> - if (ist->hwaccel_id != HWACCEL_NONE)
> - type = hw_device_match_type_by_hwaccel(ist->hwaccel_id);
> - else
> - type = hw_device_match_type_in_name(ist->dec->name);
> - if (type != AV_HWDEVICE_TYPE_NONE) {
> + if (ist->hwaccel_id == HWACCEL_AUTO) {
> + auto_device = 1;
> + } else if (ist->hwaccel_id == HWACCEL_GENERIC) {
> + type = ist->hwaccel_device_type;
> dev = hw_device_get_by_type(type);
> + if (!dev)
> + err = hw_device_init_from_type(type, NULL, &dev);
> + } else {
> + dev = hw_device_match_by_codec(ist->dec);
> if (!dev) {
> - hw_device_init_from_string(av_hwdevice_get_type_name(type),
> + // No device for this codec, but not using generic hwaccel
> + // and therefore may well not need one - ignore.
> + return 0;
> + }
> + }
> + }
> +
> + if (auto_device) {
> + int i;
> + if (!ist->dec->hw_configs) {
> + // Decoder does not support any hardware devices.
> + return 0;
> + }
> + for (i = 0; !dev; i++) {
> + type = ist->dec->hw_configs[i].device_type;
> + if (type == AV_HWDEVICE_TYPE_NONE)
> + break;
> + if ((dev = hw_device_get_by_type(type))) {
> + av_log(ist->dec_ctx, AV_LOG_INFO, "Using auto "
> + "hwaccel type %s with existing device %s.\n",
> + av_hwdevice_get_type_name(type), dev->name);
> + }
> + }
> + for (i = 0; !dev; i++) {
> + type = ist->dec->hw_configs[i].device_type;
> + if (type == AV_HWDEVICE_TYPE_NONE)
> + break;
> + // Try to make a new device of this type.
> + err = hw_device_init_from_type(type, ist->hwaccel_device,
> &dev);
> + if (err < 0) {
> + // Can't make a device of this type.
> + continue;
> + }
> + if (ist->hwaccel_device) {
> + av_log(ist->dec_ctx, AV_LOG_INFO, "Using auto "
> + "hwaccel type %s with new device created "
> + "from %s.\n", av_hwdevice_get_type_name(type),
> + ist->hwaccel_device);
> + } else {
> + av_log(ist->dec_ctx, AV_LOG_INFO, "Using auto "
> + "hwaccel type %s with new default device.\n",
> + av_hwdevice_get_type_name(type));
> }
> + }
> + if (dev) {
> + ist->hwaccel_id = HWACCEL_GENERIC;
> + ist->hwaccel_device_type = type;
> } else {
> - // No device required.
> + av_log(ist->dec_ctx, AV_LOG_INFO, "Auto hwaccel "
> + "disabled: no device found.\n");
> + ist->hwaccel_id = HWACCEL_NONE;
> return 0;
> }
> }
>
> if (!dev) {
> - av_log(ist->dec_ctx, AV_LOG_WARNING, "No device available "
> - "for decoder (device type %s for codec %s).\n",
> + av_log(ist->dec_ctx, AV_LOG_ERROR, "No device available "
> + "for decoder: device type %s needed for codec %s.\n",
> av_hwdevice_get_type_name(type), ist->dec->name);
> - return 0;
> + return err;
> }
>
> ist->dec_ctx->hw_device_ctx = av_buffer_ref(dev->device_ref);
> @@ -314,24 +397,16 @@ int hw_device_setup_for_decode(InputStream *ist)
>
> int hw_device_setup_for_encode(OutputStream *ost)
> {
> - enum AVHWDeviceType type;
> HWDevice *dev;
>
> - type = hw_device_match_type_in_name(ost->enc->name);
> - if (type != AV_HWDEVICE_TYPE_NONE) {
> - dev = hw_device_get_by_type(type);
> - if (!dev) {
> - av_log(ost->enc_ctx, AV_LOG_WARNING, "No device available "
> - "for encoder (device type %s for codec %s).\n",
> - av_hwdevice_get_type_name(type), ost->enc->name);
> - return 0;
> - }
> + dev = hw_device_match_by_codec(ost->enc);
> + if (dev) {
> ost->enc_ctx->hw_device_ctx = av_buffer_ref(dev->device_ref);
> if (!ost->enc_ctx->hw_device_ctx)
> return AVERROR(ENOMEM);
> return 0;
> } else {
> - // No device required.
> + // No device required, or no device available.
> return 0;
> }
> }
> diff --git a/avtools/avconv_opt.c b/avtools/avconv_opt.c
> index df693360a..f523f1566 100644
> --- a/avtools/avconv_opt.c
> +++ b/avtools/avconv_opt.c
> @@ -56,33 +56,13 @@
> }
>
> const HWAccel hwaccels[] = {
> -#if HAVE_VDPAU_X11
> - { "vdpau", hwaccel_decode_init, HWACCEL_VDPAU, AV_PIX_FMT_VDPAU,
> - AV_HWDEVICE_TYPE_VDPAU },
> -#endif
> -#if CONFIG_D3D11VA
> - { "d3d11va", hwaccel_decode_init, HWACCEL_D3D11VA, AV_PIX_FMT_D3D11,
> - AV_HWDEVICE_TYPE_D3D11VA },
> -#endif
> -#if CONFIG_DXVA2
> - { "dxva2", hwaccel_decode_init, HWACCEL_DXVA2, AV_PIX_FMT_DXVA2_VLD,
> - AV_HWDEVICE_TYPE_DXVA2 },
> -#endif
> + // Generic hwaccel must be first in this list.
> + { "generic", hwaccel_decode_init, HWACCEL_GENERIC },
> #if CONFIG_VDA
> - { "vda", vda_init, HWACCEL_VDA, AV_PIX_FMT_VDA,
> - AV_HWDEVICE_TYPE_NONE },
> + { "vda", vda_init, HWACCEL_VDA, AV_PIX_FMT_VDA },
> #endif
> #if CONFIG_LIBMFX
> - { "qsv", qsv_init, HWACCEL_QSV, AV_PIX_FMT_QSV,
> - AV_HWDEVICE_TYPE_NONE },
> -#endif
> -#if CONFIG_VAAPI
> - { "vaapi", hwaccel_decode_init, HWACCEL_VAAPI, AV_PIX_FMT_VAAPI,
> - AV_HWDEVICE_TYPE_VAAPI },
> -#endif
> -#if CONFIG_CUVID
> - { "cuvid", hwaccel_decode_init, HWACCEL_CUVID, AV_PIX_FMT_CUDA,
> - AV_HWDEVICE_TYPE_CUDA },
> + { "qsv", qsv_init, HWACCEL_QSV, AV_PIX_FMT_QSV },
> #endif
> { 0 },
> };
> @@ -201,9 +181,13 @@ static double parse_frame_aspect_ratio(const char *arg)
>
> static int show_hwaccels(void *optctx, const char *opt, const char *arg)
> {
> + enum AVHWDeviceType type = AV_HWDEVICE_TYPE_NONE;
> int i;
>
> printf("Supported hardware acceleration:\n");
> + while ((type = av_hwdevice_iterate_types(type)) !=
> + AV_HWDEVICE_TYPE_NONE)
> + printf("%s\n", av_hwdevice_get_type_name(type));
> for (i = 0; hwaccels[i].name; i++) {
> printf("%s\n", hwaccels[i].name);
> }
> @@ -623,6 +607,7 @@ static void add_input_streams(OptionsContext *o,
> AVFormatContext *ic)
> else if (!strcmp(hwaccel, "auto"))
> ist->hwaccel_id = HWACCEL_AUTO;
> else {
> + enum AVHWDeviceType type;
> int i;
> for (i = 0; hwaccels[i].name; i++) {
> if (!strcmp(hwaccels[i].name, hwaccel)) {
> @@ -632,6 +617,14 @@ static void add_input_streams(OptionsContext *o,
> AVFormatContext *ic)
> }
>
> if (!ist->hwaccel_id) {
> + type = av_hwdevice_find_type_by_name(hwaccel);
> + if (type != AV_HWDEVICE_TYPE_NONE) {
> + ist->hwaccel_id = HWACCEL_GENERIC;
> + ist->hwaccel_device_type = type;
> + }
> + }
> +
> + if (!ist->hwaccel_id) {
> av_log(NULL, AV_LOG_FATAL, "Unrecognized hwaccel:
> %s.\n",
> hwaccel);
> av_log(NULL, AV_LOG_FATAL, "Supported hwaccels: ");
> diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
> index 847b7f139..0dafe05ec 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"
> @@ -2771,6 +2772,19 @@ typedef struct AVProfile {
> const char *name; ///< short name for the profile
> } AVProfile;
>
> +typedef struct AVCodecHWConfig {
> + /**
> + * A device type which can be supplied to the codec.
> + */
> + enum AVHWDeviceType device_type;
> + /**
> + * The matching hardware pixel format which the codec can produce.
> + *
> + * Set to AV_PIX_FMT_NONE if no hardware pixel format is available.
> + */
> + enum AVPixelFormat pix_fmt;
> +} AVCodecHWConfig;
> +
> typedef struct AVCodecDefault AVCodecDefault;
>
> struct AVSubtitle;
> @@ -2806,6 +2820,15 @@ typedef struct AVCodec {
> const AVClass *priv_class; ///< AVClass for the private
> context
> const AVProfile *profiles; ///< array of recognized
> profiles, or NULL if unknown, array is terminated by {FF_PROFILE_UNKNOWN}
>
> + /**
> + * Array of hardware configurations supported by the codec, or NULL
> + * if no hardware supported.
> + *
> + * The array is terminated by a configuration with a device_type of
> + * AV_HW_DEVICE_TYPE_NONE.
> + */
> + const AVCodecHWConfig *hw_configs;
> +
> /*****************************************************************
> * No fields below this line are part of the public API. They
> * may not be used outside of libavcodec and can be changed and
> diff --git a/libavcodec/h264dec.c b/libavcodec/h264dec.c
> index 7a8293efa..0d3d87a64 100644
> --- a/libavcodec/h264dec.c
> +++ b/libavcodec/h264dec.c
> @@ -786,6 +786,24 @@ AVCodec ff_h264_decoder = {
> .capabilities = /*AV_CODEC_CAP_DRAW_HORIZ_BAND |*/
> AV_CODEC_CAP_DR1 |
> AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS
> |
> AV_CODEC_CAP_FRAME_THREADS,
> + .hw_configs = (const AVCodecHWConfig[]) {
> +#if CONFIG_H264_CUVID_HWACCEL
> + { AV_HWDEVICE_TYPE_CUVID, AV_PIX_FMT_CUVID },
> +#endif
> +#if CONFIG_H264_DXVA2_HWACCEL
> + { AV_HWDEVICE_TYPE_DXVA2, AV_PIX_FMT_DXVA2 },
> +#endif
> +#if CONFIG_H264_D3D11VA_HWACCEL
> + { AV_HWDEVICE_TYPE_D3D11VA, AV_PIX_FMT_D3D11 },
> +#endif
> +#if CONFIG_H264_VAAPI_HWACCEL
> + { AV_HWDEVICE_TYPE_VAAPI, AV_PIX_FMT_VAAPI },
> +#endif
> +#if CONFIG_H264_VDPAU_HWACCEL
> + { AV_HWDEVICE_TYPE_VDPAU, AV_PIX_FMT_VDPAU },
> +#endif
> + { AV_HWDEVICE_TYPE_NONE },
> + },
> .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE |
> FF_CODEC_CAP_EXPORTS_CROPPING,
> .flush = flush_dpb,
> .init_thread_copy =
> ONLY_IF_THREADS_ENABLED(decode_init_thread_copy),
Seems like AVHWAccel, except actually useful and much simpler. I like at
least the direction this is taking.
Eventually, I'd certainly love if get_format and identifying hwaccels
by codec/pixel format combination could be replaced by something nicer.
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel