From b266ad56fe0e4ce5bb70118ba2e2b1dabfaf76ce Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Sat, 4 Mar 2017 23:57:28 +0000 Subject: [PATCH 1/8] hwcontext: Add device derivation Creates a new device context from another of a different type which refers to the same underlying hardware. --- doc/APIchanges | 3 ++ libavutil/hwcontext.c | 65 ++++++++++++++++++++++++++++++++++ libavutil/hwcontext.h | 26 ++++++++++++++ libavutil/hwcontext_internal.h | 8 +++++ libavutil/version.h | 2 +- 5 files changed, 103 insertions(+), 1 deletion(-) diff --git a/doc/APIchanges b/doc/APIchanges index 9367a5fc41..335c163479 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -13,6 +13,9 @@ libavutil: 2015-08-28 API changes, most recent first: +2017-03-xx - xxxxxxx - lavu 55.34.0 - hwcontext.h + Add av_hwdevice_ctx_create_derived(). + 2017-02-10 - xxxxxxx - lavu 55.33.0 - spherical.h Add AV_SPHERICAL_EQUIRECTANGULAR_TILE, av_spherical_tile_bounds(), and projection-specific properties (bound_left, bound_top, bound_right, diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c index 608da68727..252dd32e38 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,69 @@ 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, *tmp_ctx; + int ret = 0; + + tmp_ref = src_ref; + 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; + } + tmp_ref = tmp_ctx->internal->source_device; + } + + 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; + } + + 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 037ca64224..49c7a21c1e 100644 --- a/libavutil/hwcontext.h +++ b/libavutil/hwcontext.h @@ -268,6 +268,32 @@ int av_hwdevice_ctx_init(AVBufferRef *ref); 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. + * + * If the source device is a device of the target type or was originally + * derived from such a device (possibly through one or more intermediate + * devices of other types), then this will return a reference to the + * existing device of the same type as is requested. + * + * Otherwise, it will attempt to derive a new device from the given source + * device. If direct derivation to the new type is not implemented, it will + * attempt the same derivation from each ancestor of the source device in + * turn looking for an implemented derivation method. + * + * @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. + * @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. * diff --git a/libavutil/hwcontext_internal.h b/libavutil/hwcontext_internal.h index 11e3a68e4c..66f54142e8 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 { diff --git a/libavutil/version.h b/libavutil/version.h index 0f2b684fa8..7c0e852498 100644 --- a/libavutil/version.h +++ b/libavutil/version.h @@ -54,7 +54,7 @@ */ #define LIBAVUTIL_VERSION_MAJOR 55 -#define LIBAVUTIL_VERSION_MINOR 33 +#define LIBAVUTIL_VERSION_MINOR 34 #define LIBAVUTIL_VERSION_MICRO 0 #define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ From b7487f4f3c39b4b202e1ea7bb2de13902f2dee45 Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Sat, 4 Mar 2017 23:57:29 +0000 Subject: [PATCH 2/8] hwcontext: Make it easier to work with device types Adds functions to convert to/from strings and a function to iterate over all supported device types. Also adds a new invalid type AV_HWDEVICE_TYPE_NONE, which acts as a sentinel value. --- doc/APIchanges | 4 ++++ libavutil/hwcontext.c | 41 +++++++++++++++++++++++++++++++++++++++++ libavutil/hwcontext.h | 28 ++++++++++++++++++++++++++++ libavutil/version.h | 2 +- 4 files changed, 74 insertions(+), 1 deletion(-) diff --git a/doc/APIchanges b/doc/APIchanges index 335c163479..70b3391988 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -13,6 +13,10 @@ libavutil: 2015-08-28 API changes, most recent first: +2017-03-xx - xxxxxxx - lavu 55.35.0 - hwcontext.h + Add AV_HWDEVICE_TYPE_NONE, av_hwdevice_find_type_by_name(), + av_hwdevice_get_type_name() and av_hwdevice_iterate_types(). + 2017-03-xx - xxxxxxx - lavu 55.34.0 - hwcontext.h Add av_hwdevice_ctx_create_derived(). diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c index 252dd32e38..2c6d51ee55 100644 --- a/libavutil/hwcontext.c +++ b/libavutil/hwcontext.c @@ -47,6 +47,47 @@ static const HWContextType * const hw_table[] = { NULL, }; +const char *hw_type_names[] = { + [AV_HWDEVICE_TYPE_CUDA] = "cuda", + [AV_HWDEVICE_TYPE_DXVA2] = "dxva2", + [AV_HWDEVICE_TYPE_QSV] = "qsv", + [AV_HWDEVICE_TYPE_VAAPI] = "vaapi", + [AV_HWDEVICE_TYPE_VDPAU] = "vdpau", +}; + +enum AVHWDeviceType av_hwdevice_find_type_by_name(const char *name) +{ + int type; + for (type = 0; type < FF_ARRAY_ELEMS(hw_type_names); type++) { + if (hw_type_names[type] && !strcmp(hw_type_names[type], name)) + return type; + } + return AV_HWDEVICE_TYPE_NONE; +} + +const char *av_hwdevice_get_type_name(enum AVHWDeviceType type) +{ + if (type >= 0 && type < FF_ARRAY_ELEMS(hw_type_names)) + return hw_type_names[type]; + else + return NULL; +} + +enum AVHWDeviceType av_hwdevice_iterate_types(enum AVHWDeviceType prev) +{ + enum AVHWDeviceType next; + int i, set = 0; + for (i = 0; hw_table[i]; i++) { + if (prev != AV_HWDEVICE_TYPE_NONE && hw_table[i]->type <= prev) + continue; + if (!set || hw_table[i]->type < next) { + next = hw_table[i]->type; + set = 1; + } + } + return set ? next : AV_HWDEVICE_TYPE_NONE; +} + static const AVClass hwdevice_ctx_class = { .class_name = "AVHWDeviceContext", .item_name = av_default_item_name, diff --git a/libavutil/hwcontext.h b/libavutil/hwcontext.h index 49c7a21c1e..040e010b30 100644 --- a/libavutil/hwcontext.h +++ b/libavutil/hwcontext.h @@ -30,6 +30,7 @@ enum AVHWDeviceType { AV_HWDEVICE_TYPE_VAAPI, AV_HWDEVICE_TYPE_DXVA2, AV_HWDEVICE_TYPE_QSV, + AV_HWDEVICE_TYPE_NONE, }; typedef struct AVHWDeviceInternal AVHWDeviceInternal; @@ -222,6 +223,33 @@ typedef struct AVHWFramesContext { int width, height; } AVHWFramesContext; +/** + * Look up an AVHWDeviceType by name. + * + * @param name String name of the device type (case-insensitive). + * @return The type from enum AVHWDeviceType, or AV_HWDEVICE_TYPE_NONE if + * not found. + */ +enum AVHWDeviceType av_hwdevice_find_type_by_name(const char *name); + +/** Get the string name of an AVHWDeviceType. + * + * @param type Type from enum AVHWDeviceType. + * @return Pointer to a static string containing the name, or NULL if the type + * is not valid. + */ +const char *av_hwdevice_get_type_name(enum AVHWDeviceType type); + +/** + * Iterate over supported device types. + * + * @param type AV_HWDEVICE_TYPE_NONE initially, then the previous type + * returned by this function in subsequent iterations. + * @return The next usable device type from enum AVHWDeviceType, or + * AV_HWDEVICE_TYPE_NONE if there are no more. + */ +enum AVHWDeviceType av_hwdevice_iterate_types(enum AVHWDeviceType prev); + /** * Allocate an AVHWDeviceContext for a given pixel format. * diff --git a/libavutil/version.h b/libavutil/version.h index 7c0e852498..f952cc48dd 100644 --- a/libavutil/version.h +++ b/libavutil/version.h @@ -54,7 +54,7 @@ */ #define LIBAVUTIL_VERSION_MAJOR 55 -#define LIBAVUTIL_VERSION_MINOR 34 +#define LIBAVUTIL_VERSION_MINOR 35 #define LIBAVUTIL_VERSION_MICRO 0 #define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ From d2e6dd32a445b5744a51d090c0822dbd7e434592 Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Sat, 4 Mar 2017 23:57:30 +0000 Subject: [PATCH 3/8] avconv: Generic device setup Not yet enabled for any hwaccels. --- avtools/Makefile | 3 +- avtools/avconv.c | 22 +++ avtools/avconv.h | 17 ++ avtools/avconv_hw.c | 383 +++++++++++++++++++++++++++++++++++++++++++ avtools/avconv_opt.c | 33 +++- 5 files changed, 452 insertions(+), 6 deletions(-) create mode 100644 avtools/avconv_hw.c diff --git a/avtools/Makefile b/avtools/Makefile index d6d609f544..8effa995da 100644 --- a/avtools/Makefile +++ b/avtools/Makefile @@ -8,7 +8,8 @@ PROGS += $(AVPROGS) AVBASENAMES = avconv avplay avprobe ALLAVPROGS = $(AVBASENAMES:%=%$(EXESUF)) -OBJS-avconv += avtools/avconv_opt.o avtools/avconv_filter.o +OBJS-avconv += avtools/avconv_opt.o avtools/avconv_filter.o \ + avtools/avconv_hw.o OBJS-avconv-$(CONFIG_LIBMFX) += avtools/avconv_qsv.o OBJS-avconv-$(CONFIG_VAAPI) += avtools/avconv_vaapi.o OBJS-avconv-$(CONFIG_VDA) += avtools/avconv_vda.o diff --git a/avtools/avconv.c b/avtools/avconv.c index 5c36761c1d..3f5806788e 100644 --- a/avtools/avconv.c +++ b/avtools/avconv.c @@ -1704,6 +1704,17 @@ static int init_input_stream(int ist_index, char *error, int error_len) if (!av_dict_get(ist->decoder_opts, "threads", NULL, 0)) av_dict_set(&ist->decoder_opts, "threads", "auto", 0); + + ret = hw_device_setup_for_decode(ist); + if (ret < 0) { + char errbuf[128]; + av_strerror(ret, errbuf, sizeof(errbuf)); + snprintf(error, error_len, "Device setup failed for " + "decoder on input stream #%d:%d : %s", + ist->file_index, ist->st->index, errbuf); + return ret; + } + if ((ret = avcodec_open2(ist->dec_ctx, codec, &ist->decoder_opts)) < 0) { char errbuf[128]; if (ret == AVERROR_EXPERIMENTAL) @@ -2046,6 +2057,16 @@ static int init_output_stream(OutputStream *ost, char *error, int error_len) ost->enc_ctx->hw_frames_ctx = av_buffer_ref(ost->filter->filter->inputs[0]->hw_frames_ctx); if (!ost->enc_ctx->hw_frames_ctx) return AVERROR(ENOMEM); + } else { + ret = hw_device_setup_for_encode(ost); + if (ret < 0) { + char errbuf[128]; + av_strerror(ret, errbuf, sizeof(errbuf)); + snprintf(error, error_len, "Device setup failed for " + "encoder on output stream #%d:%d : %s", + ost->file_index, ost->index, errbuf); + return ret; + } } if ((ret = avcodec_open2(ost->enc_ctx, codec, &ost->encoder_opts)) < 0) { @@ -2795,6 +2816,7 @@ static int transcode(void) } av_buffer_unref(&hw_device_ctx); + hw_device_free_all(); /* finished ! */ ret = 0; diff --git a/avtools/avconv.h b/avtools/avconv.h index 3c3f0ef659..c578e2074b 100644 --- a/avtools/avconv.h +++ b/avtools/avconv.h @@ -40,6 +40,7 @@ #include "libavutil/avutil.h" #include "libavutil/dict.h" #include "libavutil/fifo.h" +#include "libavutil/hwcontext.h" #include "libavutil/pixfmt.h" #include "libavutil/rational.h" @@ -63,8 +64,15 @@ typedef struct HWAccel { int (*init)(AVCodecContext *s); enum HWAccelID id; enum AVPixelFormat pix_fmt; + enum AVHWDeviceType device_type; } HWAccel; +typedef struct HWDevice { + char *name; + enum AVHWDeviceType type; + AVBufferRef *device_ref; +} HWDevice; + /* select an input stream for an output stream */ typedef struct StreamMap { int disabled; /* 1 is this mapping is disabled by a negative map */ @@ -510,4 +518,13 @@ int qsv_transcode_init(OutputStream *ost); int vaapi_decode_init(AVCodecContext *avctx); int vaapi_device_init(const char *device); +HWDevice *hw_device_get_by_name(const char *name); +int hw_device_init_from_string(const char *arg, HWDevice **dev); +void hw_device_free_all(void); + +int hw_device_setup_for_decode(InputStream *ist); +int hw_device_setup_for_encode(OutputStream *ost); + +int hwaccel_decode_init(AVCodecContext *avctx); + #endif /* AVCONV_H */ diff --git a/avtools/avconv_hw.c b/avtools/avconv_hw.c new file mode 100644 index 0000000000..fd1618389b --- /dev/null +++ b/avtools/avconv_hw.c @@ -0,0 +1,383 @@ +/* + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "avconv.h" + +static int nb_hw_devices; +static HWDevice **hw_devices; + +static HWDevice *hw_device_get_by_type(enum AVHWDeviceType type) +{ + HWDevice *found = NULL; + int i; + for (i = 0; i < nb_hw_devices; i++) { + if (hw_devices[i]->type == type) { + if (found) + return NULL; + found = hw_devices[i]; + } + } + return found; +} + +HWDevice *hw_device_get_by_name(const char *name) +{ + int i; + for (i = 0; i < nb_hw_devices; i++) { + if (!strcmp(hw_devices[i]->name, name)) + return hw_devices[i]; + } + return NULL; +} + +static HWDevice *hw_device_add(void) +{ + int err; + err = av_reallocp_array(&hw_devices, nb_hw_devices + 1, + sizeof(*hw_devices)); + if (err) { + nb_hw_devices = 0; + return NULL; + } + hw_devices[nb_hw_devices] = av_mallocz(sizeof(HWDevice)); + if (!hw_devices[nb_hw_devices]) + return NULL; + return hw_devices[nb_hw_devices++]; +} + +int hw_device_init_from_string(const char *arg, HWDevice **dev_out) +{ + // "type=name:device,key=value,key2=value2" + // "type:device,key=value,key2=value2" + // -> av_hwdevice_ctx_create() + // "type=name@name" + // "type@name" + // -> av_hwdevice_ctx_create_derived() + + AVDictionary *options = NULL; + char *type_name = NULL, *name = NULL, *device = NULL; + enum AVHWDeviceType type; + HWDevice *dev, *src; + AVBufferRef *device_ref; + int err; + const char *errmsg, *p, *q; + size_t k; + + k = strcspn(arg, ":=@"); + p = arg + k; + + type_name = av_strndup(arg, k); + if (!type_name) { + err = AVERROR(ENOMEM); + goto fail; + } + type = av_hwdevice_find_type_by_name(type_name); + if (type == AV_HWDEVICE_TYPE_NONE) { + errmsg = "unknown device type"; + goto invalid; + } + + if (*p == '=') { + k = strcspn(p + 1, ":@"); + + name = av_strndup(p + 1, k); + if (!name) { + err = AVERROR(ENOMEM); + goto fail; + } + if (hw_device_get_by_name(name)) { + errmsg = "named device already exists"; + goto invalid; + } + + 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); + 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) { + // New device with no parameters. + err = av_hwdevice_ctx_create(&device_ref, type, + NULL, NULL, 0); + if (err < 0) + goto fail; + + } else if (*p == ':') { + // New device with some parameters. + ++p; + q = strchr(p, ','); + if (q) { + device = av_strndup(p, q - p); + if (!device) { + err = AVERROR(ENOMEM); + goto fail; + } + err = av_dict_parse_string(&options, q + 1, "=", ",", 0); + if (err < 0) { + errmsg = "failed to parse options"; + goto invalid; + } + } + + err = av_hwdevice_ctx_create(&device_ref, type, + device ? device : p, options, 0); + if (err < 0) + goto fail; + + } else if (*p == '@') { + // Derive from existing device. + + src = hw_device_get_by_name(p + 1); + if (!src) { + errmsg = "invalid source device name"; + goto invalid; + } + + err = av_hwdevice_ctx_create_derived(&device_ref, type, + src->device_ref, 0); + if (err < 0) + goto fail; + } else { + errmsg = "parse error"; + goto invalid; + } + + 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; + + name = NULL; + err = 0; +done: + av_freep(&type_name); + av_freep(&name); + av_freep(&device); + av_dict_free(&options); + return err; +invalid: + av_log(NULL, AV_LOG_ERROR, + "Invalid device specification \"%s\": %s\n", arg, errmsg); + err = AVERROR(EINVAL); + goto done; +fail: + av_log(NULL, AV_LOG_ERROR, + "Device creation failed: %d.\n", err); + goto done; +} + +void hw_device_free_all(void) +{ + int i; + for (i = 0; i < nb_hw_devices; i++) { + av_freep(&hw_devices[i]->name); + av_buffer_unref(&hw_devices[i]->device_ref); + av_freep(&hw_devices[i]); + } + av_freep(&hw_devices); + nb_hw_devices = 0; +} + +static enum AVHWDeviceType hw_device_match_type_by_hwaccel(enum HWAccelID hwaccel_id) +{ + int i; + if (hwaccel_id == HWACCEL_NONE) + return AV_HWDEVICE_TYPE_NONE; + for (i = 0; hwaccels[i].name; i++) { + if (hwaccels[i].id == hwaccel_id) + return hwaccels[i].device_type; + } + 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; +} + +int hw_device_setup_for_decode(InputStream *ist) +{ + enum AVHWDeviceType type; + HWDevice *dev; + const char *type_name; + int err; + + 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. + 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) { + dev = hw_device_get_by_type(type); + } else { + // No device required. + 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_hwdevice_get_type_name(type), ist->dec->name); + return 0; + } + + ist->dec_ctx->hw_device_ctx = av_buffer_ref(dev->device_ref); + if (!ist->dec_ctx->hw_device_ctx) + return AVERROR(ENOMEM); + + return 0; +} + +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; + } + 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. + return 0; + } +} + +static int hwaccel_retrieve_data(AVCodecContext *avctx, AVFrame *input) +{ + InputStream *ist = avctx->opaque; + AVFrame *output = NULL; + enum AVPixelFormat output_format = ist->hwaccel_output_format; + int err; + + if (input->format == output_format) { + // Nothing to do. + return 0; + } + + output = av_frame_alloc(); + if (!output) + return AVERROR(ENOMEM); + + output->format = output_format; + + err = av_hwframe_transfer_data(output, input, 0); + if (err < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to transfer data to " + "output frame: %d.\n", err); + goto fail; + } + + err = av_frame_copy_props(output, input); + if (err < 0) { + av_frame_unref(output); + goto fail; + } + + av_frame_unref(input); + av_frame_move_ref(input, output); + av_frame_free(&output); + + return 0; + +fail: + av_frame_free(&output); + return err; +} + +int hwaccel_decode_init(AVCodecContext *avctx) +{ + InputStream *ist = avctx->opaque; + + ist->hwaccel_retrieve_data = &hwaccel_retrieve_data; + + return 0; +} diff --git a/avtools/avconv_opt.c b/avtools/avconv_opt.c index e078a0b89d..c17bc15895 100644 --- a/avtools/avconv_opt.c +++ b/avtools/avconv_opt.c @@ -57,19 +57,24 @@ const HWAccel hwaccels[] = { #if HAVE_VDPAU_X11 - { "vdpau", vdpau_init, HWACCEL_VDPAU, AV_PIX_FMT_VDPAU }, + { "vdpau", vdpau_init, HWACCEL_VDPAU, AV_PIX_FMT_VDPAU, + AV_HWDEVICE_TYPE_NONE }, #endif #if HAVE_DXVA2_LIB - { "dxva2", dxva2_init, HWACCEL_DXVA2, AV_PIX_FMT_DXVA2_VLD }, + { "dxva2", dxva2_init, HWACCEL_DXVA2, AV_PIX_FMT_DXVA2_VLD, + AV_HWDEVICE_TYPE_NONE }, #endif #if CONFIG_VDA - { "vda", vda_init, HWACCEL_VDA, AV_PIX_FMT_VDA }, + { "vda", vda_init, HWACCEL_VDA, AV_PIX_FMT_VDA, + AV_HWDEVICE_TYPE_NONE }, #endif #if CONFIG_LIBMFX - { "qsv", qsv_init, HWACCEL_QSV, AV_PIX_FMT_QSV }, + { "qsv", qsv_init, HWACCEL_QSV, AV_PIX_FMT_QSV, + AV_HWDEVICE_TYPE_NONE }, #endif #if CONFIG_VAAPI - { "vaapi", vaapi_decode_init, HWACCEL_VAAPI, AV_PIX_FMT_VAAPI }, + { "vaapi", vaapi_decode_init, HWACCEL_VAAPI, AV_PIX_FMT_VAAPI, + AV_HWDEVICE_TYPE_NONE }, #endif { 0 }, }; @@ -337,6 +342,21 @@ static int opt_vaapi_device(void *optctx, const char *opt, const char *arg) } #endif +static int opt_init_hw_device(void *optctx, const char *opt, const char *arg) +{ + if (!strcmp(arg, "list")) { + enum AVHWDeviceType type = AV_HWDEVICE_TYPE_NONE; + printf("Supported hardware device types:\n"); + while ((type = av_hwdevice_iterate_types(type)) != + AV_HWDEVICE_TYPE_NONE) + printf("%s\n", av_hwdevice_get_type_name(type)); + printf("\n"); + exit_program(0); + } else { + return hw_device_init_from_string(arg, NULL); + } +} + /** * Parse a metadata specifier passed as 'arg' parameter. * @param arg metadata string to parse @@ -2741,5 +2761,8 @@ const OptionDef options[] = { "set VAAPI hardware device (DRM path or X11 display name)", "device" }, #endif + { "init_hw_device", HAS_ARG | OPT_EXPERT, { .func_arg = opt_init_hw_device }, + "initialise hardware device", "args" }, + { NULL, }, }; From 62a1ef9f26c654a3e988aa465c4ac1d776c4c356 Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Sat, 4 Mar 2017 23:57:31 +0000 Subject: [PATCH 4/8] avconv: Enable generic hwaccel support for VAAPI --- avtools/Makefile | 1 - avtools/avconv.h | 2 - avtools/avconv_opt.c | 20 +++- avtools/avconv_vaapi.c | 231 ----------------------------------------- 4 files changed, 16 insertions(+), 238 deletions(-) delete mode 100644 avtools/avconv_vaapi.c diff --git a/avtools/Makefile b/avtools/Makefile index 8effa995da..09233dea7e 100644 --- a/avtools/Makefile +++ b/avtools/Makefile @@ -11,7 +11,6 @@ ALLAVPROGS = $(AVBASENAMES:%=%$(EXESUF)) OBJS-avconv += avtools/avconv_opt.o avtools/avconv_filter.o \ avtools/avconv_hw.o OBJS-avconv-$(CONFIG_LIBMFX) += avtools/avconv_qsv.o -OBJS-avconv-$(CONFIG_VAAPI) += avtools/avconv_vaapi.o OBJS-avconv-$(CONFIG_VDA) += avtools/avconv_vda.o OBJS-avconv-$(HAVE_DXVA2_LIB) += avtools/avconv_dxva2.o OBJS-avconv-$(HAVE_VDPAU_X11) += avtools/avconv_vdpau.o diff --git a/avtools/avconv.h b/avtools/avconv.h index c578e2074b..4cd846134f 100644 --- a/avtools/avconv.h +++ b/avtools/avconv.h @@ -515,8 +515,6 @@ int dxva2_init(AVCodecContext *s); int vda_init(AVCodecContext *s); int qsv_init(AVCodecContext *s); int qsv_transcode_init(OutputStream *ost); -int vaapi_decode_init(AVCodecContext *avctx); -int vaapi_device_init(const char *device); HWDevice *hw_device_get_by_name(const char *name); int hw_device_init_from_string(const char *arg, HWDevice **dev); diff --git a/avtools/avconv_opt.c b/avtools/avconv_opt.c index c17bc15895..09edc1f161 100644 --- a/avtools/avconv_opt.c +++ b/avtools/avconv_opt.c @@ -73,8 +73,8 @@ const HWAccel hwaccels[] = { AV_HWDEVICE_TYPE_NONE }, #endif #if CONFIG_VAAPI - { "vaapi", vaapi_decode_init, HWACCEL_VAAPI, AV_PIX_FMT_VAAPI, - AV_HWDEVICE_TYPE_NONE }, + { "vaapi", hwaccel_decode_init, HWACCEL_VAAPI, AV_PIX_FMT_VAAPI, + AV_HWDEVICE_TYPE_VAAPI }, #endif { 0 }, }; @@ -334,10 +334,22 @@ static int opt_attach(void *optctx, const char *opt, const char *arg) #if CONFIG_VAAPI static int opt_vaapi_device(void *optctx, const char *opt, const char *arg) { + HWDevice *dev; + const char *prefix = "vaapi:"; + char *tmp; int err; - err = vaapi_device_init(arg); + tmp = av_malloc(strlen(prefix) + strlen(arg) + 1); + if (!tmp) + return AVERROR(ENOMEM); + strcpy(tmp, prefix); + strcat(tmp, arg); + err = hw_device_init_from_string(tmp, &dev); + av_free(tmp); if (err < 0) - exit_program(1); + return err; + hw_device_ctx = av_buffer_ref(dev->device_ref); + if (!hw_device_ctx) + return AVERROR(ENOMEM); return 0; } #endif diff --git a/avtools/avconv_vaapi.c b/avtools/avconv_vaapi.c deleted file mode 100644 index 584b8b4df0..0000000000 --- a/avtools/avconv_vaapi.c +++ /dev/null @@ -1,231 +0,0 @@ -/* - * This file is part of Libav. - * - * Libav is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * Libav is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "config.h" - -#include "libavutil/avassert.h" -#include "libavutil/frame.h" -#include "libavutil/hwcontext.h" -#include "libavutil/log.h" - -#include "avconv.h" - - -static AVClass vaapi_class = { - .class_name = "vaapi", - .item_name = av_default_item_name, - .version = LIBAVUTIL_VERSION_INT, -}; - -#define DEFAULT_SURFACES 20 - -typedef struct VAAPIDecoderContext { - const AVClass *class; - - AVBufferRef *device_ref; - AVHWDeviceContext *device; - AVBufferRef *frames_ref; - AVHWFramesContext *frames; - - // The output need not have the same format, width and height as the - // decoded frames - the copy for non-direct-mapped access is actually - // a whole vpp instance which can do arbitrary scaling and format - // conversion. - enum AVPixelFormat output_format; -} VAAPIDecoderContext; - - -static int vaapi_get_buffer(AVCodecContext *avctx, AVFrame *frame, int flags) -{ - InputStream *ist = avctx->opaque; - VAAPIDecoderContext *ctx = ist->hwaccel_ctx; - int err; - - err = av_hwframe_get_buffer(ctx->frames_ref, frame, 0); - if (err < 0) { - av_log(ctx, AV_LOG_ERROR, "Failed to allocate decoder surface.\n"); - } else { - av_log(ctx, AV_LOG_DEBUG, "Decoder given surface %#x.\n", - (unsigned int)(uintptr_t)frame->data[3]); - } - return err; -} - -static int vaapi_retrieve_data(AVCodecContext *avctx, AVFrame *input) -{ - InputStream *ist = avctx->opaque; - VAAPIDecoderContext *ctx = ist->hwaccel_ctx; - AVFrame *output = 0; - int err; - - av_assert0(input->format == AV_PIX_FMT_VAAPI); - - if (ctx->output_format == AV_PIX_FMT_VAAPI) { - // Nothing to do. - return 0; - } - - av_log(ctx, AV_LOG_DEBUG, "Retrieve data from surface %#x.\n", - (unsigned int)(uintptr_t)input->data[3]); - - output = av_frame_alloc(); - if (!output) - return AVERROR(ENOMEM); - - output->format = ctx->output_format; - - err = av_hwframe_transfer_data(output, input, 0); - if (err < 0) { - av_log(ctx, AV_LOG_ERROR, "Failed to transfer data to " - "output frame: %d.\n", err); - goto fail; - } - - err = av_frame_copy_props(output, input); - if (err < 0) { - av_frame_unref(output); - goto fail; - } - - av_frame_unref(input); - av_frame_move_ref(input, output); - av_frame_free(&output); - - return 0; - -fail: - if (output) - av_frame_free(&output); - return err; -} - -static void vaapi_decode_uninit(AVCodecContext *avctx) -{ - InputStream *ist = avctx->opaque; - VAAPIDecoderContext *ctx = ist->hwaccel_ctx; - - if (ctx) { - av_buffer_unref(&ctx->frames_ref); - av_buffer_unref(&ctx->device_ref); - av_free(ctx); - } - - av_buffer_unref(&ist->hw_frames_ctx); - - ist->hwaccel_ctx = NULL; - ist->hwaccel_uninit = NULL; - ist->hwaccel_get_buffer = NULL; - ist->hwaccel_retrieve_data = NULL; -} - -int vaapi_decode_init(AVCodecContext *avctx) -{ - InputStream *ist = avctx->opaque; - VAAPIDecoderContext *ctx; - int err; - int loglevel = (ist->hwaccel_id != HWACCEL_VAAPI ? AV_LOG_VERBOSE - : AV_LOG_ERROR); - - if (ist->hwaccel_ctx) - vaapi_decode_uninit(avctx); - - // We have -hwaccel without -vaapi_device, so just initialise here with - // the device passed as -hwaccel_device (if -vaapi_device was passed, it - // will always have been called before now). - if (!hw_device_ctx) { - err = vaapi_device_init(ist->hwaccel_device); - if (err < 0) - return err; - } - - ctx = av_mallocz(sizeof(*ctx)); - if (!ctx) - return AVERROR(ENOMEM); - ctx->class = &vaapi_class; - - ctx->device_ref = av_buffer_ref(hw_device_ctx); - ctx->device = (AVHWDeviceContext*)ctx->device_ref->data; - - ctx->output_format = ist->hwaccel_output_format; - avctx->pix_fmt = ctx->output_format; - - ctx->frames_ref = av_hwframe_ctx_alloc(ctx->device_ref); - if (!ctx->frames_ref) { - av_log(ctx, loglevel, "Failed to create VAAPI frame context.\n"); - err = AVERROR(ENOMEM); - goto fail; - } - - ctx->frames = (AVHWFramesContext*)ctx->frames_ref->data; - - ctx->frames->format = AV_PIX_FMT_VAAPI; - ctx->frames->width = avctx->coded_width; - ctx->frames->height = avctx->coded_height; - - // It would be nice if we could query the available formats here, - // but unfortunately we don't have a VAConfigID to do it with. - // For now, just assume an NV12 format (or P010 if 10-bit). - ctx->frames->sw_format = (avctx->sw_pix_fmt == AV_PIX_FMT_YUV420P10 ? - AV_PIX_FMT_P010 : AV_PIX_FMT_NV12); - - // For frame-threaded decoding, at least one additional surface - // is needed for each thread. - ctx->frames->initial_pool_size = DEFAULT_SURFACES; - if (avctx->active_thread_type & FF_THREAD_FRAME) - ctx->frames->initial_pool_size += avctx->thread_count; - - err = av_hwframe_ctx_init(ctx->frames_ref); - if (err < 0) { - av_log(ctx, loglevel, "Failed to initialise VAAPI frame " - "context: %d\n", err); - goto fail; - } - - ist->hw_frames_ctx = av_buffer_ref(ctx->frames_ref); - if (!ist->hw_frames_ctx) { - err = AVERROR(ENOMEM); - goto fail; - } - - ist->hwaccel_ctx = ctx; - ist->hwaccel_uninit = &vaapi_decode_uninit; - ist->hwaccel_get_buffer = &vaapi_get_buffer; - ist->hwaccel_retrieve_data = &vaapi_retrieve_data; - - return 0; - -fail: - vaapi_decode_uninit(avctx); - return err; -} - -static AVClass *vaapi_log = &vaapi_class; - -av_cold int vaapi_device_init(const char *device) -{ - int err; - - err = av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_VAAPI, - device, NULL, 0); - if (err < 0) { - av_log(&vaapi_log, AV_LOG_ERROR, "Failed to create a VAAPI device\n"); - return err; - } - - return 0; -} From 16a163b55a6558ed05702b91cc0777987295ef21 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 4 Mar 2017 23:57:32 +0000 Subject: [PATCH 5/8] lavc: Add hwaccel_flags field to AVCodecContext This "reuses" the flags introduced for the av_vdpau_bind_context() API function, and makes them available to all hwaccels. This does not affect the current vdpau API, as av_vdpau_bind_context() should obviously override the AVCodecContext.hwaccel_flags flags for the sake of compatibility. --- doc/APIchanges | 4 ++++ libavcodec/avcodec.h | 9 +++++++++ libavcodec/pthread_frame.c | 2 ++ libavcodec/version.h | 2 +- 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/doc/APIchanges b/doc/APIchanges index 70b3391988..c6a5d82b99 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -13,6 +13,10 @@ libavutil: 2015-08-28 API changes, most recent first: +2017-03-xx - xxxxxxx - lavc 57.37.0 - avcodec.h + Add AVCodecContext.hwaccel_flags field. This will control some hwaccels at + a later point. + 2017-03-xx - xxxxxxx - lavu 55.35.0 - hwcontext.h Add AV_HWDEVICE_TYPE_NONE, av_hwdevice_find_type_by_name(), av_hwdevice_get_type_name() and av_hwdevice_iterate_types(). diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 62a4fab8f9..1e2b13e0d7 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -3186,6 +3186,15 @@ typedef struct AVCodecContext { * contexts used must be created on the same device. */ AVBufferRef *hw_device_ctx; + + /** + * Bit set of AV_HWACCEL_FLAG_* flags, which affect hardware accelerated + * decoding (if active). + * - encoding: unused + * - decoding: Set by user (either before avcodec_open2(), or in the + * AVCodecContext.get_format callback) + */ + int hwaccel_flags; } AVCodecContext; /** diff --git a/libavcodec/pthread_frame.c b/libavcodec/pthread_frame.c index f3a74c0bd8..65a04d8e90 100644 --- a/libavcodec/pthread_frame.c +++ b/libavcodec/pthread_frame.c @@ -270,6 +270,8 @@ FF_ENABLE_DEPRECATION_WARNINGS return AVERROR(ENOMEM); } } + + dst->hwaccel_flags = src->hwaccel_flags; } if (for_user) { diff --git a/libavcodec/version.h b/libavcodec/version.h index 443cc8204a..349b2e9f6a 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -28,7 +28,7 @@ #include "libavutil/version.h" #define LIBAVCODEC_VERSION_MAJOR 57 -#define LIBAVCODEC_VERSION_MINOR 36 +#define LIBAVCODEC_VERSION_MINOR 37 #define LIBAVCODEC_VERSION_MICRO 0 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ From 1a7ddba5762b6c31d1338140550cd594b6d7d48b Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 4 Mar 2017 23:57:33 +0000 Subject: [PATCH 6/8] lavc: vdpau: add support for new hw_frames_ctx and hw_device_ctx API This supports retrieving the device from a provided hw_frames_ctx, and automatically creating a hw_frames_ctx if hw_device_ctx is set. The old API is not deprecated yet. The user can still use av_vdpau_bind_context() (with or without setting hw_frames_ctx), or use the API before that by allocating and setting hwaccel_context manually. --- libavcodec/vdpau.c | 85 ++++++++++++++++++++++++++++--------- libavcodec/vdpau_internal.h | 2 + libavcodec/version.h | 2 +- 3 files changed, 69 insertions(+), 20 deletions(-) diff --git a/libavcodec/vdpau.c b/libavcodec/vdpau.c index d3cb188d74..68d0813f65 100644 --- a/libavcodec/vdpau.c +++ b/libavcodec/vdpau.c @@ -118,29 +118,76 @@ int ff_vdpau_common_init(AVCodecContext *avctx, VdpDecoderProfile profile, vdctx->width = UINT32_MAX; vdctx->height = UINT32_MAX; - hwctx->reset = 0; - if (hwctx->context.decoder != VDP_INVALID_HANDLE) { - vdctx->decoder = hwctx->context.decoder; - vdctx->render = hwctx->context.render; - vdctx->device = VDP_INVALID_HANDLE; - return 0; /* Decoder created by user */ - } + if (av_vdpau_get_surface_parameters(avctx, &type, &width, &height)) + return AVERROR(ENOSYS); - vdctx->device = hwctx->device; - vdctx->get_proc_address = hwctx->get_proc_address; + if (hwctx) { + hwctx->reset = 0; - if (hwctx->flags & AV_HWACCEL_FLAG_IGNORE_LEVEL) - level = 0; - else if (level < 0) - return AVERROR(ENOTSUP); + if (hwctx->context.decoder != VDP_INVALID_HANDLE) { + vdctx->decoder = hwctx->context.decoder; + vdctx->render = hwctx->context.render; + vdctx->device = VDP_INVALID_HANDLE; + return 0; /* Decoder created by user */ + } - if (av_vdpau_get_surface_parameters(avctx, &type, &width, &height)) - return AVERROR(ENOSYS); + vdctx->device = hwctx->device; + vdctx->get_proc_address = hwctx->get_proc_address; + + if (hwctx->flags & AV_HWACCEL_FLAG_IGNORE_LEVEL) + level = 0; + + if (!(hwctx->flags & AV_HWACCEL_FLAG_ALLOW_HIGH_DEPTH) && + type != VDP_CHROMA_TYPE_420) + return AVERROR(ENOSYS); + } else { + AVHWFramesContext *frames_ctx = NULL; + AVVDPAUDeviceContext *dev_ctx; + + // We assume the hw_frames_ctx always survives until ff_vdpau_common_uninit + // is called. This holds true as the user is not allowed to touch + // hw_device_ctx, or hw_frames_ctx after get_format (and ff_get_format + // itself also uninits before unreffing hw_frames_ctx). + if (avctx->hw_frames_ctx) { + frames_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data; + } else if (avctx->hw_device_ctx) { + int ret; + + avctx->hw_frames_ctx = av_hwframe_ctx_alloc(avctx->hw_device_ctx); + if (!avctx->hw_frames_ctx) + return AVERROR(ENOMEM); + + frames_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data; + frames_ctx->format = AV_PIX_FMT_VDPAU; + frames_ctx->sw_format = avctx->sw_pix_fmt; + frames_ctx->width = avctx->coded_width; + frames_ctx->height = avctx->coded_height; + + ret = av_hwframe_ctx_init(avctx->hw_frames_ctx); + if (ret < 0) { + av_buffer_unref(&avctx->hw_frames_ctx); + return ret; + } + } - if (!(hwctx->flags & AV_HWACCEL_FLAG_ALLOW_HIGH_DEPTH) && - type != VDP_CHROMA_TYPE_420) - return AVERROR(ENOSYS); + if (!frames_ctx) { + av_log(avctx, AV_LOG_ERROR, "A hardware frames context is " + "required for VDPAU decoding.\n"); + return AVERROR(EINVAL); + } + + dev_ctx = frames_ctx->device_ctx->hwctx; + + vdctx->device = dev_ctx->device; + vdctx->get_proc_address = dev_ctx->get_proc_address; + + if (avctx->hwaccel_flags & AV_HWACCEL_FLAG_IGNORE_LEVEL) + level = 0; + } + + if (level < 0) + return AVERROR(ENOTSUP); status = vdctx->get_proc_address(vdctx->device, VDP_FUNC_ID_VIDEO_SURFACE_QUERY_CAPABILITIES, @@ -238,7 +285,7 @@ static int ff_vdpau_common_reinit(AVCodecContext *avctx) if (vdctx->device == VDP_INVALID_HANDLE) return 0; /* Decoder created by user */ if (avctx->coded_width == vdctx->width && - avctx->coded_height == vdctx->height && !hwctx->reset) + avctx->coded_height == vdctx->height && (!hwctx || !hwctx->reset)) return 0; avctx->hwaccel->uninit(avctx); diff --git a/libavcodec/vdpau_internal.h b/libavcodec/vdpau_internal.h index c7281a6b5e..a0eb46c48e 100644 --- a/libavcodec/vdpau_internal.h +++ b/libavcodec/vdpau_internal.h @@ -28,6 +28,8 @@ #include #include "libavutil/frame.h" +#include "libavutil/hwcontext.h" +#include "libavutil/hwcontext_vdpau.h" #include "avcodec.h" #include "vdpau.h" diff --git a/libavcodec/version.h b/libavcodec/version.h index 349b2e9f6a..f169793c15 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -29,7 +29,7 @@ #define LIBAVCODEC_VERSION_MAJOR 57 #define LIBAVCODEC_VERSION_MINOR 37 -#define LIBAVCODEC_VERSION_MICRO 0 +#define LIBAVCODEC_VERSION_MICRO 1 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ LIBAVCODEC_VERSION_MINOR, \ From aa6b2e081c504cb99f5e2e0ceb45295ef24bdac2 Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Sat, 4 Mar 2017 23:57:34 +0000 Subject: [PATCH 7/8] avconv: Enable generic hwaccel support for VDPAU --- avtools/Makefile | 1 - avtools/avconv.h | 1 - avtools/avconv_opt.c | 4 +- avtools/avconv_vdpau.c | 159 ----------------------------------------- 4 files changed, 2 insertions(+), 163 deletions(-) delete mode 100644 avtools/avconv_vdpau.c diff --git a/avtools/Makefile b/avtools/Makefile index 09233dea7e..c23a605a55 100644 --- a/avtools/Makefile +++ b/avtools/Makefile @@ -13,7 +13,6 @@ OBJS-avconv += avtools/avconv_opt.o avtools/avconv_filter.o \ OBJS-avconv-$(CONFIG_LIBMFX) += avtools/avconv_qsv.o OBJS-avconv-$(CONFIG_VDA) += avtools/avconv_vda.o OBJS-avconv-$(HAVE_DXVA2_LIB) += avtools/avconv_dxva2.o -OBJS-avconv-$(HAVE_VDPAU_X11) += avtools/avconv_vdpau.o define DOAVTOOL OBJS-$(1) += avtools/cmdutils.o avtools/$(1).o $(OBJS-$(1)-yes) diff --git a/avtools/avconv.h b/avtools/avconv.h index 4cd846134f..9415b208be 100644 --- a/avtools/avconv.h +++ b/avtools/avconv.h @@ -510,7 +510,6 @@ int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame); int avconv_parse_options(int argc, char **argv); -int vdpau_init(AVCodecContext *s); int dxva2_init(AVCodecContext *s); int vda_init(AVCodecContext *s); int qsv_init(AVCodecContext *s); diff --git a/avtools/avconv_opt.c b/avtools/avconv_opt.c index 09edc1f161..e970c8e46c 100644 --- a/avtools/avconv_opt.c +++ b/avtools/avconv_opt.c @@ -57,8 +57,8 @@ const HWAccel hwaccels[] = { #if HAVE_VDPAU_X11 - { "vdpau", vdpau_init, HWACCEL_VDPAU, AV_PIX_FMT_VDPAU, - AV_HWDEVICE_TYPE_NONE }, + { "vdpau", hwaccel_decode_init, HWACCEL_VDPAU, AV_PIX_FMT_VDPAU, + AV_HWDEVICE_TYPE_VDPAU }, #endif #if HAVE_DXVA2_LIB { "dxva2", dxva2_init, HWACCEL_DXVA2, AV_PIX_FMT_DXVA2_VLD, diff --git a/avtools/avconv_vdpau.c b/avtools/avconv_vdpau.c deleted file mode 100644 index 5fedceef95..0000000000 --- a/avtools/avconv_vdpau.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * This file is part of Libav. - * - * Libav is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * Libav is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include - -#include "avconv.h" - -#include "libavcodec/vdpau.h" - -#include "libavutil/buffer.h" -#include "libavutil/frame.h" -#include "libavutil/hwcontext.h" -#include "libavutil/hwcontext_vdpau.h" -#include "libavutil/pixfmt.h" - -typedef struct VDPAUContext { - AVBufferRef *hw_frames_ctx; - AVFrame *tmp_frame; -} VDPAUContext; - -static void vdpau_uninit(AVCodecContext *s) -{ - InputStream *ist = s->opaque; - VDPAUContext *ctx = ist->hwaccel_ctx; - - ist->hwaccel_uninit = NULL; - ist->hwaccel_get_buffer = NULL; - ist->hwaccel_retrieve_data = NULL; - - av_buffer_unref(&ctx->hw_frames_ctx); - av_frame_free(&ctx->tmp_frame); - - av_freep(&ist->hwaccel_ctx); - av_freep(&s->hwaccel_context); -} - -static int vdpau_get_buffer(AVCodecContext *s, AVFrame *frame, int flags) -{ - InputStream *ist = s->opaque; - VDPAUContext *ctx = ist->hwaccel_ctx; - - return av_hwframe_get_buffer(ctx->hw_frames_ctx, frame, 0); -} - -static int vdpau_retrieve_data(AVCodecContext *s, AVFrame *frame) -{ - InputStream *ist = s->opaque; - VDPAUContext *ctx = ist->hwaccel_ctx; - int ret; - - ret = av_hwframe_transfer_data(ctx->tmp_frame, frame, 0); - if (ret < 0) - return ret; - - ret = av_frame_copy_props(ctx->tmp_frame, frame); - if (ret < 0) { - av_frame_unref(ctx->tmp_frame); - return ret; - } - - av_frame_unref(frame); - av_frame_move_ref(frame, ctx->tmp_frame); - - return 0; -} - -static int vdpau_alloc(AVCodecContext *s) -{ - InputStream *ist = s->opaque; - int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR; - VDPAUContext *ctx; - int ret; - - AVBufferRef *device_ref = NULL; - AVHWDeviceContext *device_ctx; - AVVDPAUDeviceContext *device_hwctx; - AVHWFramesContext *frames_ctx; - - ctx = av_mallocz(sizeof(*ctx)); - if (!ctx) - return AVERROR(ENOMEM); - - ist->hwaccel_ctx = ctx; - ist->hwaccel_uninit = vdpau_uninit; - ist->hwaccel_get_buffer = vdpau_get_buffer; - ist->hwaccel_retrieve_data = vdpau_retrieve_data; - - ctx->tmp_frame = av_frame_alloc(); - if (!ctx->tmp_frame) - goto fail; - - ret = av_hwdevice_ctx_create(&device_ref, AV_HWDEVICE_TYPE_VDPAU, - ist->hwaccel_device, NULL, 0); - if (ret < 0) - goto fail; - device_ctx = (AVHWDeviceContext*)device_ref->data; - device_hwctx = device_ctx->hwctx; - - ctx->hw_frames_ctx = av_hwframe_ctx_alloc(device_ref); - if (!ctx->hw_frames_ctx) - goto fail; - av_buffer_unref(&device_ref); - - frames_ctx = (AVHWFramesContext*)ctx->hw_frames_ctx->data; - frames_ctx->format = AV_PIX_FMT_VDPAU; - frames_ctx->sw_format = s->sw_pix_fmt; - frames_ctx->width = s->coded_width; - frames_ctx->height = s->coded_height; - - ret = av_hwframe_ctx_init(ctx->hw_frames_ctx); - if (ret < 0) - goto fail; - - if (av_vdpau_bind_context(s, device_hwctx->device, device_hwctx->get_proc_address, 0)) - goto fail; - - av_log(NULL, AV_LOG_VERBOSE, "Using VDPAU to decode input stream #%d:%d.\n", - ist->file_index, ist->st->index); - - return 0; - -fail: - av_log(NULL, loglevel, "VDPAU init failed for stream #%d:%d.\n", - ist->file_index, ist->st->index); - av_buffer_unref(&device_ref); - vdpau_uninit(s); - return AVERROR(EINVAL); -} - -int vdpau_init(AVCodecContext *s) -{ - InputStream *ist = s->opaque; - - if (!ist->hwaccel_ctx) { - int ret = vdpau_alloc(s); - if (ret < 0) - return ret; - } - - ist->hwaccel_get_buffer = vdpau_get_buffer; - ist->hwaccel_retrieve_data = vdpau_retrieve_data; - - return 0; -} From 303fadf5963e01b8edf4ba2701e45f7e9e586aeb Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Sun, 19 Mar 2017 16:25:37 +0000 Subject: [PATCH 8/8] avconv: Document the -init_hw_device option --- doc/avconv.texi | 85 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 58 insertions(+), 27 deletions(-) diff --git a/doc/avconv.texi b/doc/avconv.texi index 6f1fbc79b2..7bcb787979 100644 --- a/doc/avconv.texi +++ b/doc/avconv.texi @@ -594,6 +594,56 @@ The timestamps must be specified in ascending order. When doing stream copy, copy also non-key frames found at the beginning. +@item -init_hw_device @var{type}[=@var{name}][:@var{device}[,@var{key=value}...]] +Initialise a new hardware device of type @var{type} called @var{name}, using the +given device parameters. +If no name is specified it will receive a default name of the form "@var{type}%d". + +The meaning of @var{device} and the following arguments depends on the +device type: +@table @option + +@item cuda +@var{device} is the number of the CUDA device. + +@item dxva2 +@var{device} is the number of the Direct3D 9 display adapter. + +@item vaapi +@var{device} is either an X11 display name or a DRM render node. +If not specified, it will attempt to open the default X11 display (@emph{$DISPLAY}) +and then the first DRM render node (@emph{/dev/dri/renderD128}). + +@item vdpau +@var{device} is an X11 display name. +If not specified, it will attempt to open the default X11 display (@emph{$DISPLAY}). + +@item qsv +@var{device} selects a value in @samp{MFX_IMPL_*}. Allowed values are: +@table @option +@item auto +@item sw +@item hw +@item auto_any +@item hw_any +@item hw2 +@item hw3 +@item hw4 +@end table +If not specified, @samp{auto_any} is used. +(Note that it may be easier to achieve the desired result for QSV by creating the +platform-appropriate subdevice (@samp{dxva2} or @samp{vaapi}) and then deriving a +QSV device from that.) + +@end table + +@item -init_hw_device @var{type}[=@var{name}]@@@var{source} +Initialise a new hardware device of type @var{type} called @var{name}, +deriving it from the existing device with the name @var{source}. + +@item -init_hw_device list +List all hardware device types supported in this build of avconv. + @item -hwaccel[:@var{stream_specifier}] @var{hwaccel} (@emph{input,per-stream}) Use hardware acceleration to decode the matching stream(s). The allowed values of @var{hwaccel} are: @@ -613,6 +663,9 @@ Use VDPAU (Video Decode and Presentation API for Unix) hardware acceleration. @item dxva2 Use DXVA2 (DirectX Video Acceleration) hardware acceleration. +@item vaapi +Use VAAPI (Video Acceleration API) hardware acceleration. + @item qsv Use the Intel QuickSync Video acceleration for video transcoding. @@ -636,33 +689,11 @@ useful for testing. @item -hwaccel_device[:@var{stream_specifier}] @var{hwaccel_device} (@emph{input,per-stream}) Select a device to use for hardware acceleration. -This option only makes sense when the @option{-hwaccel} option is also -specified. Its exact meaning depends on the specific hardware acceleration -method chosen. - -@table @option -@item vdpau -For VDPAU, this option specifies the X11 display/screen to use. If this option -is not specified, the value of the @var{DISPLAY} environment variable is used - -@item dxva2 -For DXVA2, this option should contain the number of the display adapter to use. -If this option is not specified, the default adapter is used. - -@item qsv -For QSV, this option corresponds to the values of MFX_IMPL_* . Allowed values -are: -@table @option -@item auto -@item sw -@item hw -@item auto_any -@item hw_any -@item hw2 -@item hw3 -@item hw4 -@end table -@end table +This option only makes sense when the @option{-hwaccel} option is also specified. +It can either refer to an existing device created with @option{-init_hw_device} +by name, or it can create a new device as if +@samp{-init_hw_device} @var{type}:@var{hwaccel_device} +were called immediately before. @item -hwaccels List all hardware acceleration methods supported in this build of avconv.