diff mbox

[FFmpeg-devel,v2] hwcontext: Add test for device creation and derivation

Message ID 20180515221503.32511-1-sw@jkqxz.net
State Accepted
Commit ba7d1377e85662f7b4ae728719c10d5427cc6b9b
Headers show

Commit Message

Mark Thompson May 15, 2018, 10:15 p.m. UTC
This uses any devices it can find on the host system - on a system with no
hardware device support or in builds with no support included it will do
nothing and pass.
---

It now comes under a new target "fate-hw", which is not run as part of "fate".

I found the CMP option, so it no longer has a reference file.  All output is therefore now on stderr.

Thanks,

- Mark

(In terms of further tests under fate-hw, I'm intending to add a hwframes test which tries to make frames contexts and do upload/download.  There should probably be something with frames context mapping too, but I'm unsure exactly what.  I'd also like to have some tests for the complex header construction code in VAAPI encode, but how to actually run that needs a bit more thought.)


 libavutil/Makefile         |   1 +
 libavutil/tests/.gitignore |   1 +
 libavutil/tests/hwdevice.c | 226 +++++++++++++++++++++++++++++++++++++++++++++
 tests/Makefile             |   5 +
 tests/fate/hw.mak          |   6 ++
 5 files changed, 239 insertions(+)
 create mode 100644 libavutil/tests/hwdevice.c
 create mode 100644 tests/fate/hw.mak

Comments

Xiang, Haihao May 16, 2018, 1:14 a.m. UTC | #1
On Tue, 2018-05-15 at 23:15 +0100, Mark Thompson wrote:
> This uses any devices it can find on the host system - on a system with no

> hardware device support or in builds with no support included it will do

> nothing and pass.

> ---

> 

> It now comes under a new target "fate-hw", which is not run as part of "fate".

> 

> I found the CMP option, so it no longer has a reference file.  All output is

> therefore now on stderr.

> 

> Thanks,

> 

> - Mark

> 

> (In terms of further tests under fate-hw, I'm intending to add a hwframes test

> which tries to make frames contexts and do upload/download.  There should

> probably be something with frames context mapping too, but I'm unsure exactly

> what.  I'd also like to have some tests for the complex header construction

> code in VAAPI encode, but how to actually run that needs a bit more thought.)

> 

> 

>  libavutil/Makefile         |   1 +

>  libavutil/tests/.gitignore |   1 +

>  libavutil/tests/hwdevice.c | 226

> +++++++++++++++++++++++++++++++++++++++++++++

>  tests/Makefile             |   5 +

>  tests/fate/hw.mak          |   6 ++

>  5 files changed, 239 insertions(+)

>  create mode 100644 libavutil/tests/hwdevice.c

>  create mode 100644 tests/fate/hw.mak

> 

> diff --git a/libavutil/Makefile b/libavutil/Makefile

> index 4fe470748c..d0632f16a6 100644

> --- a/libavutil/Makefile

> +++ b/libavutil/Makefile

> @@ -206,6 +206,7 @@ TESTPROGS =

> adler32                                                     \

>              fifo                                                        \

>              hash                                                        \

>              hmac                                                        \

> +            hwdevice                                                    \

>              integer                                                     \

>              imgutils                                                    \

>              lfg                                                         \

> diff --git a/libavutil/tests/.gitignore b/libavutil/tests/.gitignore

> index 8ede070887..71f75a8ee9 100644

> --- a/libavutil/tests/.gitignore

> +++ b/libavutil/tests/.gitignore

> @@ -22,6 +22,7 @@

>  /file

>  /hash

>  /hmac

> +/hwdevice

>  /imgutils

>  /lfg

>  /lls

> diff --git a/libavutil/tests/hwdevice.c b/libavutil/tests/hwdevice.c

> new file mode 100644

> index 0000000000..7eb355c988

> --- /dev/null

> +++ b/libavutil/tests/hwdevice.c

> @@ -0,0 +1,226 @@

> +/*

> + * This file is part of FFmpeg.

> + *

> + * FFmpeg 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.

> + *

> + * FFmpeg 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 FFmpeg; if not, write to the Free Software

> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301

> USA

> + */

> +

> +#include <stdio.h>

> +

> +#include "libavutil/hwcontext.h"

> +

> +static int test_derivation(AVBufferRef *src_ref, const char *src_name)

> +{

> +    enum AVHWDeviceType derived_type;

> +    const char *derived_name;

> +    AVBufferRef *derived_ref = NULL, *back_ref = NULL;

> +    AVHWDeviceContext *src_dev, *derived_dev;

> +    int err;

> +

> +    src_dev = (AVHWDeviceContext*)src_ref->data;

> +

> +    derived_type = AV_HWDEVICE_TYPE_NONE;

> +    while (1) {

> +        derived_type = av_hwdevice_iterate_types(derived_type);

> +        if (derived_type == AV_HWDEVICE_TYPE_NONE)

> +            break;

> +

> +        derived_name = av_hwdevice_get_type_name(derived_type);

> +

> +        err = av_hwdevice_ctx_create_derived(&derived_ref, derived_type,

> +                                             src_ref, 0);

> +        if (err < 0) {

> +            fprintf(stderr, "Unable to derive %s -> %s: %d.\n",

> +                    src_name, derived_name, err);

> +            continue;

> +        }

> +

> +        derived_dev = (AVHWDeviceContext*)derived_ref->data;

> +        if (derived_dev->type != derived_type) {

> +            fprintf(stderr, "Device derived as type %d has type %d.\n",

> +                    derived_type, derived_dev->type);

> +            goto fail;

> +        }

> +

> +        if (derived_type == src_dev->type) {

> +            if (derived_dev != src_dev) {

> +                fprintf(stderr, "Derivation of %s from itself succeeded "

> +                        "but did not return the same device.\n", src_name);

> +                goto fail;

> +            }

> +            av_buffer_unref(&derived_ref);

> +            continue;

> +        }

> +

> +        err = av_hwdevice_ctx_create_derived(&back_ref, src_dev->type,

> +                                             derived_ref, 0);

> +        if (err < 0) {

> +            fprintf(stderr, "Derivation %s to %s succeeded, but derivation "

> +                    "back again failed: %d.\n",

> +                    src_name, derived_name, err);

> +            goto fail;

> +        }

> +

> +        if (back_ref->data != src_ref->data) {

> +            fprintf(stderr, "Derivation %s to %s succeeded, but derivation "

> +                    "back again did not return the original device.\n",

> +                   src_name, derived_name);

> +            goto fail;

> +        }

> +

> +        fprintf(stderr, "Successfully tested derivation %s -> %s.\n",

> +                src_name, derived_name);

> +

> +        av_buffer_unref(&derived_ref);

> +        av_buffer_unref(&back_ref);

> +    }

> +

> +    return 0;

> +

> +fail:

> +    av_buffer_unref(&derived_ref);

> +    av_buffer_unref(&back_ref);

> +    return -1;

> +}

> +

> +static int test_device(enum AVHWDeviceType type, const char *name,

> +                       const char *device, AVDictionary *opts, int flags)

> +{

> +    AVBufferRef *ref;

> +    AVHWDeviceContext *dev;

> +    int err;

> +

> +    err = av_hwdevice_ctx_create(&ref, type, device, opts, flags);

> +    if (err < 0) {

> +        fprintf(stderr, "Failed to create %s device: %d.\n", name, err);

> +        return 1;

> +    }

> +

> +    dev = (AVHWDeviceContext*)ref->data;

> +    if (dev->type != type) {

> +        fprintf(stderr, "Device created as type %d has type %d.\n",

> +                type, dev->type);

> +        av_buffer_unref(&ref);

> +        return -1;

> +    }

> +

> +    fprintf(stderr, "Device type %s successfully created.\n", name);

> +

> +    err = test_derivation(ref, name);

> +

> +    av_buffer_unref(&ref);

> +

> +    return err;

> +}

> +

> +static const struct {

> +    enum AVHWDeviceType type;

> +    const char *possible_devices[5];

> +} test_devices[] = {

> +    { AV_HWDEVICE_TYPE_CUDA,

> +      { "0", "1", "2" } },

> +    { AV_HWDEVICE_TYPE_DRM,

> +      { "/dev/dri/card0", "/dev/dri/card1",

> +        "/dev/dri/renderD128", "/dev/dri/renderD129" } },

> +    { AV_HWDEVICE_TYPE_DXVA2,

> +      { "0", "1", "2" } },

> +    { AV_HWDEVICE_TYPE_D3D11VA,

> +      { "0", "1", "2" } },

> +    { AV_HWDEVICE_TYPE_OPENCL,

> +      { "0.0", "0.1", "1.0", "1.1" } },

> +    { AV_HWDEVICE_TYPE_VAAPI,

> +      { "/dev/dri/renderD128", "/dev/dri/renderD129", ":0" } },

> +};

> +

> +static int test_device_type(enum AVHWDeviceType type)

> +{

> +    enum AVHWDeviceType check;

> +    const char *name;

> +    int i, j, found, err;

> +

> +    name = av_hwdevice_get_type_name(type);

> +    if (!name) {

> +        fprintf(stderr, "No name available for device type %d.\n", type);

> +        return -1;

> +    }

> +

> +    check = av_hwdevice_find_type_by_name(name);

> +    if (check != type) {

> +        fprintf(stderr, "Type %d maps to name %s maps to type %d.\n",

> +               type, name, check);

> +        return -1;

> +    }

> +

> +    found = 0;

> +

> +    err = test_device(type, name, NULL, NULL, 0);

> +    if (err < 0) {

> +        fprintf(stderr, "Test failed for %s with default options.\n", name);

> +        return -1;

> +    }

> +    if (err == 0) {

> +        fprintf(stderr, "Test passed for %s with default options.\n", name);

> +        ++found;

> +    }

> +

> +    for (i = 0; i < FF_ARRAY_ELEMS(test_devices); i++) {

> +        if (test_devices[i].type != type)

> +            continue;

> +

> +        for (j = 0; test_devices[i].possible_devices[j]; j++) {

> +            err = test_device(type, name,

> +                              test_devices[i].possible_devices[j],

> +                              NULL, 0);

> +            if (err < 0) {

> +                fprintf(stderr, "Test failed for %s with device %s.\n",

> +                       name, test_devices[i].possible_devices[j]);

> +                return -1;

> +            }

> +            if (err == 0) {

> +                fprintf(stderr, "Test passed for %s with device %s.\n",

> +                        name, test_devices[i].possible_devices[j]);

> +                ++found;

> +            }

> +        }

> +    }

> +

> +    return !found;

> +}

> +

> +int main(void)

> +{

> +    enum AVHWDeviceType type = AV_HWDEVICE_TYPE_NONE;

> +    int pass, fail, skip, err;

> +

> +    pass = fail = skip = 0;

> +    while (1) {

> +        type = av_hwdevice_iterate_types(type);

> +        if (type == AV_HWDEVICE_TYPE_NONE)

> +            break;

> +

> +        err = test_device_type(type);

> +        if (err == 0)

> +            ++pass;

> +        else if (err < 0)

> +            ++fail;

> +        else

> +            ++skip;

> +    }

> +

> +    fprintf(stderr, "Attempted to test %d device types: "

> +            "%d passed, %d failed, %d skipped.\n",

> +            pass + fail + skip, pass, fail, skip);

> +

> +    return fail > 0;

> +}

> diff --git a/tests/Makefile b/tests/Makefile

> index 6074ac748e..98d7b6d608 100644

> --- a/tests/Makefile

> +++ b/tests/Makefile

> @@ -131,6 +131,7 @@ include $(SRC_PATH)/tests/fate/gif.mak

>  include $(SRC_PATH)/tests/fate/h264.mak

>  include $(SRC_PATH)/tests/fate/hap.mak

>  include $(SRC_PATH)/tests/fate/hevc.mak

> +include $(SRC_PATH)/tests/fate/hw.mak

>  include $(SRC_PATH)/tests/fate/id3v2.mak

>  include $(SRC_PATH)/tests/fate/image.mak

>  include $(SRC_PATH)/tests/fate/indeo.mak

> @@ -215,6 +216,10 @@ $(addprefix fate-, $(IGNORE_TESTS)): REPORT=ignore

>  

>  fate:: $(FATE)

>  

> +# Tests requiring hardware support are not included in a default fate run.

> +fate-hw: $(FATE_HW-yes)

> +FATE += $(FATE_HW-yes)

> +

>  $(FATE) $(FATE_TESTS-no): export PROGSUF = $(PROGSSUF)

>  $(FATE) $(FATE_TESTS-no): $(FATE_UTILS:%=tests/%$(HOSTEXESUF))

>  	@echo "TEST    $(@:fate-%=%)"

> diff --git a/tests/fate/hw.mak b/tests/fate/hw.mak

> new file mode 100644

> index 0000000000..d606cdeab6

> --- /dev/null

> +++ b/tests/fate/hw.mak

> @@ -0,0 +1,6 @@

> +FATE_HWCONTEXT += fate-hwdevice

> +fate-hwdevice: libavutil/tests/hwdevice$(EXESUF)

> +fate-hwdevice: CMD = run libavutil/tests/hwdevice

> +fate-hwdevice: CMP = null

> +

> +FATE_HW-$(CONFIG_AVUTIL) += $(FATE_HWCONTEXT)


LGTM, thanks
Mark Thompson May 21, 2018, 9:48 p.m. UTC | #2
On 16/05/18 02:14, Xiang, Haihao wrote:
> On Tue, 2018-05-15 at 23:15 +0100, Mark Thompson wrote:
>> This uses any devices it can find on the host system - on a system with no
>> hardware device support or in builds with no support included it will do
>> nothing and pass.
>> ---
>>
>> It now comes under a new target "fate-hw", which is not run as part of "fate".
>>
>> I found the CMP option, so it no longer has a reference file.  All output is
>> therefore now on stderr.
>>
>> Thanks,
>>
>> - Mark
>>
>> (In terms of further tests under fate-hw, I'm intending to add a hwframes test
>> which tries to make frames contexts and do upload/download.  There should
>> probably be something with frames context mapping too, but I'm unsure exactly
>> what.  I'd also like to have some tests for the complex header construction
>> code in VAAPI encode, but how to actually run that needs a bit more thought.)
>>
>>
>>  libavutil/Makefile         |   1 +
>>  libavutil/tests/.gitignore |   1 +
>>  libavutil/tests/hwdevice.c | 226
>> +++++++++++++++++++++++++++++++++++++++++++++
>>  tests/Makefile             |   5 +
>>  tests/fate/hw.mak          |   6 ++
>>  5 files changed, 239 insertions(+)
>>  create mode 100644 libavutil/tests/hwdevice.c
>>  create mode 100644 tests/fate/hw.mak
>>
>> diff --git a/libavutil/Makefile b/libavutil/Makefile
>> index 4fe470748c..d0632f16a6 100644
>> --- a/libavutil/Makefile
>> +++ b/libavutil/Makefile
>> @@ -206,6 +206,7 @@ TESTPROGS =
>> adler32                                                     \
>>              fifo                                                        \
>>              hash                                                        \
>>              hmac                                                        \
>> +            hwdevice                                                    \
>>              integer                                                     \
>>              imgutils                                                    \
>>              lfg                                                         \
>> diff --git a/libavutil/tests/.gitignore b/libavutil/tests/.gitignore
>> index 8ede070887..71f75a8ee9 100644
>> --- a/libavutil/tests/.gitignore
>> +++ b/libavutil/tests/.gitignore
>> @@ -22,6 +22,7 @@
>>  /file
>>  /hash
>>  /hmac
>> +/hwdevice
>>  /imgutils
>>  /lfg
>>  /lls
>> diff --git a/libavutil/tests/hwdevice.c b/libavutil/tests/hwdevice.c
>> new file mode 100644
>> index 0000000000..7eb355c988
>> --- /dev/null
>> +++ b/libavutil/tests/hwdevice.c
>> @@ -0,0 +1,226 @@
>> +/*
>> + * This file is part of FFmpeg.
>> + *
>> + * FFmpeg 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.
>> + *
>> + * FFmpeg 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 FFmpeg; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
>> USA
>> + */
>> +
>> +#include <stdio.h>
>> +
>> +#include "libavutil/hwcontext.h"
>> +
>> +static int test_derivation(AVBufferRef *src_ref, const char *src_name)
>> +{
>> +    enum AVHWDeviceType derived_type;
>> +    const char *derived_name;
>> +    AVBufferRef *derived_ref = NULL, *back_ref = NULL;
>> +    AVHWDeviceContext *src_dev, *derived_dev;
>> +    int err;
>> +
>> +    src_dev = (AVHWDeviceContext*)src_ref->data;
>> +
>> +    derived_type = AV_HWDEVICE_TYPE_NONE;
>> +    while (1) {
>> +        derived_type = av_hwdevice_iterate_types(derived_type);
>> +        if (derived_type == AV_HWDEVICE_TYPE_NONE)
>> +            break;
>> +
>> +        derived_name = av_hwdevice_get_type_name(derived_type);
>> +
>> +        err = av_hwdevice_ctx_create_derived(&derived_ref, derived_type,
>> +                                             src_ref, 0);
>> +        if (err < 0) {
>> +            fprintf(stderr, "Unable to derive %s -> %s: %d.\n",
>> +                    src_name, derived_name, err);
>> +            continue;
>> +        }
>> +
>> +        derived_dev = (AVHWDeviceContext*)derived_ref->data;
>> +        if (derived_dev->type != derived_type) {
>> +            fprintf(stderr, "Device derived as type %d has type %d.\n",
>> +                    derived_type, derived_dev->type);
>> +            goto fail;
>> +        }
>> +
>> +        if (derived_type == src_dev->type) {
>> +            if (derived_dev != src_dev) {
>> +                fprintf(stderr, "Derivation of %s from itself succeeded "
>> +                        "but did not return the same device.\n", src_name);
>> +                goto fail;
>> +            }
>> +            av_buffer_unref(&derived_ref);
>> +            continue;
>> +        }
>> +
>> +        err = av_hwdevice_ctx_create_derived(&back_ref, src_dev->type,
>> +                                             derived_ref, 0);
>> +        if (err < 0) {
>> +            fprintf(stderr, "Derivation %s to %s succeeded, but derivation "
>> +                    "back again failed: %d.\n",
>> +                    src_name, derived_name, err);
>> +            goto fail;
>> +        }
>> +
>> +        if (back_ref->data != src_ref->data) {
>> +            fprintf(stderr, "Derivation %s to %s succeeded, but derivation "
>> +                    "back again did not return the original device.\n",
>> +                   src_name, derived_name);
>> +            goto fail;
>> +        }
>> +
>> +        fprintf(stderr, "Successfully tested derivation %s -> %s.\n",
>> +                src_name, derived_name);
>> +
>> +        av_buffer_unref(&derived_ref);
>> +        av_buffer_unref(&back_ref);
>> +    }
>> +
>> +    return 0;
>> +
>> +fail:
>> +    av_buffer_unref(&derived_ref);
>> +    av_buffer_unref(&back_ref);
>> +    return -1;
>> +}
>> +
>> +static int test_device(enum AVHWDeviceType type, const char *name,
>> +                       const char *device, AVDictionary *opts, int flags)
>> +{
>> +    AVBufferRef *ref;
>> +    AVHWDeviceContext *dev;
>> +    int err;
>> +
>> +    err = av_hwdevice_ctx_create(&ref, type, device, opts, flags);
>> +    if (err < 0) {
>> +        fprintf(stderr, "Failed to create %s device: %d.\n", name, err);
>> +        return 1;
>> +    }
>> +
>> +    dev = (AVHWDeviceContext*)ref->data;
>> +    if (dev->type != type) {
>> +        fprintf(stderr, "Device created as type %d has type %d.\n",
>> +                type, dev->type);
>> +        av_buffer_unref(&ref);
>> +        return -1;
>> +    }
>> +
>> +    fprintf(stderr, "Device type %s successfully created.\n", name);
>> +
>> +    err = test_derivation(ref, name);
>> +
>> +    av_buffer_unref(&ref);
>> +
>> +    return err;
>> +}
>> +
>> +static const struct {
>> +    enum AVHWDeviceType type;
>> +    const char *possible_devices[5];
>> +} test_devices[] = {
>> +    { AV_HWDEVICE_TYPE_CUDA,
>> +      { "0", "1", "2" } },
>> +    { AV_HWDEVICE_TYPE_DRM,
>> +      { "/dev/dri/card0", "/dev/dri/card1",
>> +        "/dev/dri/renderD128", "/dev/dri/renderD129" } },
>> +    { AV_HWDEVICE_TYPE_DXVA2,
>> +      { "0", "1", "2" } },
>> +    { AV_HWDEVICE_TYPE_D3D11VA,
>> +      { "0", "1", "2" } },
>> +    { AV_HWDEVICE_TYPE_OPENCL,
>> +      { "0.0", "0.1", "1.0", "1.1" } },
>> +    { AV_HWDEVICE_TYPE_VAAPI,
>> +      { "/dev/dri/renderD128", "/dev/dri/renderD129", ":0" } },
>> +};
>> +
>> +static int test_device_type(enum AVHWDeviceType type)
>> +{
>> +    enum AVHWDeviceType check;
>> +    const char *name;
>> +    int i, j, found, err;
>> +
>> +    name = av_hwdevice_get_type_name(type);
>> +    if (!name) {
>> +        fprintf(stderr, "No name available for device type %d.\n", type);
>> +        return -1;
>> +    }
>> +
>> +    check = av_hwdevice_find_type_by_name(name);
>> +    if (check != type) {
>> +        fprintf(stderr, "Type %d maps to name %s maps to type %d.\n",
>> +               type, name, check);
>> +        return -1;
>> +    }
>> +
>> +    found = 0;
>> +
>> +    err = test_device(type, name, NULL, NULL, 0);
>> +    if (err < 0) {
>> +        fprintf(stderr, "Test failed for %s with default options.\n", name);
>> +        return -1;
>> +    }
>> +    if (err == 0) {
>> +        fprintf(stderr, "Test passed for %s with default options.\n", name);
>> +        ++found;
>> +    }
>> +
>> +    for (i = 0; i < FF_ARRAY_ELEMS(test_devices); i++) {
>> +        if (test_devices[i].type != type)
>> +            continue;
>> +
>> +        for (j = 0; test_devices[i].possible_devices[j]; j++) {
>> +            err = test_device(type, name,
>> +                              test_devices[i].possible_devices[j],
>> +                              NULL, 0);
>> +            if (err < 0) {
>> +                fprintf(stderr, "Test failed for %s with device %s.\n",
>> +                       name, test_devices[i].possible_devices[j]);
>> +                return -1;
>> +            }
>> +            if (err == 0) {
>> +                fprintf(stderr, "Test passed for %s with device %s.\n",
>> +                        name, test_devices[i].possible_devices[j]);
>> +                ++found;
>> +            }
>> +        }
>> +    }
>> +
>> +    return !found;
>> +}
>> +
>> +int main(void)
>> +{
>> +    enum AVHWDeviceType type = AV_HWDEVICE_TYPE_NONE;
>> +    int pass, fail, skip, err;
>> +
>> +    pass = fail = skip = 0;
>> +    while (1) {
>> +        type = av_hwdevice_iterate_types(type);
>> +        if (type == AV_HWDEVICE_TYPE_NONE)
>> +            break;
>> +
>> +        err = test_device_type(type);
>> +        if (err == 0)
>> +            ++pass;
>> +        else if (err < 0)
>> +            ++fail;
>> +        else
>> +            ++skip;
>> +    }
>> +
>> +    fprintf(stderr, "Attempted to test %d device types: "
>> +            "%d passed, %d failed, %d skipped.\n",
>> +            pass + fail + skip, pass, fail, skip);
>> +
>> +    return fail > 0;
>> +}
>> diff --git a/tests/Makefile b/tests/Makefile
>> index 6074ac748e..98d7b6d608 100644
>> --- a/tests/Makefile
>> +++ b/tests/Makefile
>> @@ -131,6 +131,7 @@ include $(SRC_PATH)/tests/fate/gif.mak
>>  include $(SRC_PATH)/tests/fate/h264.mak
>>  include $(SRC_PATH)/tests/fate/hap.mak
>>  include $(SRC_PATH)/tests/fate/hevc.mak
>> +include $(SRC_PATH)/tests/fate/hw.mak
>>  include $(SRC_PATH)/tests/fate/id3v2.mak
>>  include $(SRC_PATH)/tests/fate/image.mak
>>  include $(SRC_PATH)/tests/fate/indeo.mak
>> @@ -215,6 +216,10 @@ $(addprefix fate-, $(IGNORE_TESTS)): REPORT=ignore
>>  
>>  fate:: $(FATE)
>>  
>> +# Tests requiring hardware support are not included in a default fate run.
>> +fate-hw: $(FATE_HW-yes)
>> +FATE += $(FATE_HW-yes)
>> +
>>  $(FATE) $(FATE_TESTS-no): export PROGSUF = $(PROGSSUF)
>>  $(FATE) $(FATE_TESTS-no): $(FATE_UTILS:%=tests/%$(HOSTEXESUF))
>>  	@echo "TEST    $(@:fate-%=%)"
>> diff --git a/tests/fate/hw.mak b/tests/fate/hw.mak
>> new file mode 100644
>> index 0000000000..d606cdeab6
>> --- /dev/null
>> +++ b/tests/fate/hw.mak
>> @@ -0,0 +1,6 @@
>> +FATE_HWCONTEXT += fate-hwdevice
>> +fate-hwdevice: libavutil/tests/hwdevice$(EXESUF)
>> +fate-hwdevice: CMD = run libavutil/tests/hwdevice
>> +fate-hwdevice: CMP = null
>> +
>> +FATE_HW-$(CONFIG_AVUTIL) += $(FATE_HWCONTEXT)
> 
> LGTM, thanks

Applied.

Thanks,

- Mark
diff mbox

Patch

diff --git a/libavutil/Makefile b/libavutil/Makefile
index 4fe470748c..d0632f16a6 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -206,6 +206,7 @@  TESTPROGS = adler32                                                     \
             fifo                                                        \
             hash                                                        \
             hmac                                                        \
+            hwdevice                                                    \
             integer                                                     \
             imgutils                                                    \
             lfg                                                         \
diff --git a/libavutil/tests/.gitignore b/libavutil/tests/.gitignore
index 8ede070887..71f75a8ee9 100644
--- a/libavutil/tests/.gitignore
+++ b/libavutil/tests/.gitignore
@@ -22,6 +22,7 @@ 
 /file
 /hash
 /hmac
+/hwdevice
 /imgutils
 /lfg
 /lls
diff --git a/libavutil/tests/hwdevice.c b/libavutil/tests/hwdevice.c
new file mode 100644
index 0000000000..7eb355c988
--- /dev/null
+++ b/libavutil/tests/hwdevice.c
@@ -0,0 +1,226 @@ 
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+
+#include "libavutil/hwcontext.h"
+
+static int test_derivation(AVBufferRef *src_ref, const char *src_name)
+{
+    enum AVHWDeviceType derived_type;
+    const char *derived_name;
+    AVBufferRef *derived_ref = NULL, *back_ref = NULL;
+    AVHWDeviceContext *src_dev, *derived_dev;
+    int err;
+
+    src_dev = (AVHWDeviceContext*)src_ref->data;
+
+    derived_type = AV_HWDEVICE_TYPE_NONE;
+    while (1) {
+        derived_type = av_hwdevice_iterate_types(derived_type);
+        if (derived_type == AV_HWDEVICE_TYPE_NONE)
+            break;
+
+        derived_name = av_hwdevice_get_type_name(derived_type);
+
+        err = av_hwdevice_ctx_create_derived(&derived_ref, derived_type,
+                                             src_ref, 0);
+        if (err < 0) {
+            fprintf(stderr, "Unable to derive %s -> %s: %d.\n",
+                    src_name, derived_name, err);
+            continue;
+        }
+
+        derived_dev = (AVHWDeviceContext*)derived_ref->data;
+        if (derived_dev->type != derived_type) {
+            fprintf(stderr, "Device derived as type %d has type %d.\n",
+                    derived_type, derived_dev->type);
+            goto fail;
+        }
+
+        if (derived_type == src_dev->type) {
+            if (derived_dev != src_dev) {
+                fprintf(stderr, "Derivation of %s from itself succeeded "
+                        "but did not return the same device.\n", src_name);
+                goto fail;
+            }
+            av_buffer_unref(&derived_ref);
+            continue;
+        }
+
+        err = av_hwdevice_ctx_create_derived(&back_ref, src_dev->type,
+                                             derived_ref, 0);
+        if (err < 0) {
+            fprintf(stderr, "Derivation %s to %s succeeded, but derivation "
+                    "back again failed: %d.\n",
+                    src_name, derived_name, err);
+            goto fail;
+        }
+
+        if (back_ref->data != src_ref->data) {
+            fprintf(stderr, "Derivation %s to %s succeeded, but derivation "
+                    "back again did not return the original device.\n",
+                   src_name, derived_name);
+            goto fail;
+        }
+
+        fprintf(stderr, "Successfully tested derivation %s -> %s.\n",
+                src_name, derived_name);
+
+        av_buffer_unref(&derived_ref);
+        av_buffer_unref(&back_ref);
+    }
+
+    return 0;
+
+fail:
+    av_buffer_unref(&derived_ref);
+    av_buffer_unref(&back_ref);
+    return -1;
+}
+
+static int test_device(enum AVHWDeviceType type, const char *name,
+                       const char *device, AVDictionary *opts, int flags)
+{
+    AVBufferRef *ref;
+    AVHWDeviceContext *dev;
+    int err;
+
+    err = av_hwdevice_ctx_create(&ref, type, device, opts, flags);
+    if (err < 0) {
+        fprintf(stderr, "Failed to create %s device: %d.\n", name, err);
+        return 1;
+    }
+
+    dev = (AVHWDeviceContext*)ref->data;
+    if (dev->type != type) {
+        fprintf(stderr, "Device created as type %d has type %d.\n",
+                type, dev->type);
+        av_buffer_unref(&ref);
+        return -1;
+    }
+
+    fprintf(stderr, "Device type %s successfully created.\n", name);
+
+    err = test_derivation(ref, name);
+
+    av_buffer_unref(&ref);
+
+    return err;
+}
+
+static const struct {
+    enum AVHWDeviceType type;
+    const char *possible_devices[5];
+} test_devices[] = {
+    { AV_HWDEVICE_TYPE_CUDA,
+      { "0", "1", "2" } },
+    { AV_HWDEVICE_TYPE_DRM,
+      { "/dev/dri/card0", "/dev/dri/card1",
+        "/dev/dri/renderD128", "/dev/dri/renderD129" } },
+    { AV_HWDEVICE_TYPE_DXVA2,
+      { "0", "1", "2" } },
+    { AV_HWDEVICE_TYPE_D3D11VA,
+      { "0", "1", "2" } },
+    { AV_HWDEVICE_TYPE_OPENCL,
+      { "0.0", "0.1", "1.0", "1.1" } },
+    { AV_HWDEVICE_TYPE_VAAPI,
+      { "/dev/dri/renderD128", "/dev/dri/renderD129", ":0" } },
+};
+
+static int test_device_type(enum AVHWDeviceType type)
+{
+    enum AVHWDeviceType check;
+    const char *name;
+    int i, j, found, err;
+
+    name = av_hwdevice_get_type_name(type);
+    if (!name) {
+        fprintf(stderr, "No name available for device type %d.\n", type);
+        return -1;
+    }
+
+    check = av_hwdevice_find_type_by_name(name);
+    if (check != type) {
+        fprintf(stderr, "Type %d maps to name %s maps to type %d.\n",
+               type, name, check);
+        return -1;
+    }
+
+    found = 0;
+
+    err = test_device(type, name, NULL, NULL, 0);
+    if (err < 0) {
+        fprintf(stderr, "Test failed for %s with default options.\n", name);
+        return -1;
+    }
+    if (err == 0) {
+        fprintf(stderr, "Test passed for %s with default options.\n", name);
+        ++found;
+    }
+
+    for (i = 0; i < FF_ARRAY_ELEMS(test_devices); i++) {
+        if (test_devices[i].type != type)
+            continue;
+
+        for (j = 0; test_devices[i].possible_devices[j]; j++) {
+            err = test_device(type, name,
+                              test_devices[i].possible_devices[j],
+                              NULL, 0);
+            if (err < 0) {
+                fprintf(stderr, "Test failed for %s with device %s.\n",
+                       name, test_devices[i].possible_devices[j]);
+                return -1;
+            }
+            if (err == 0) {
+                fprintf(stderr, "Test passed for %s with device %s.\n",
+                        name, test_devices[i].possible_devices[j]);
+                ++found;
+            }
+        }
+    }
+
+    return !found;
+}
+
+int main(void)
+{
+    enum AVHWDeviceType type = AV_HWDEVICE_TYPE_NONE;
+    int pass, fail, skip, err;
+
+    pass = fail = skip = 0;
+    while (1) {
+        type = av_hwdevice_iterate_types(type);
+        if (type == AV_HWDEVICE_TYPE_NONE)
+            break;
+
+        err = test_device_type(type);
+        if (err == 0)
+            ++pass;
+        else if (err < 0)
+            ++fail;
+        else
+            ++skip;
+    }
+
+    fprintf(stderr, "Attempted to test %d device types: "
+            "%d passed, %d failed, %d skipped.\n",
+            pass + fail + skip, pass, fail, skip);
+
+    return fail > 0;
+}
diff --git a/tests/Makefile b/tests/Makefile
index 6074ac748e..98d7b6d608 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -131,6 +131,7 @@  include $(SRC_PATH)/tests/fate/gif.mak
 include $(SRC_PATH)/tests/fate/h264.mak
 include $(SRC_PATH)/tests/fate/hap.mak
 include $(SRC_PATH)/tests/fate/hevc.mak
+include $(SRC_PATH)/tests/fate/hw.mak
 include $(SRC_PATH)/tests/fate/id3v2.mak
 include $(SRC_PATH)/tests/fate/image.mak
 include $(SRC_PATH)/tests/fate/indeo.mak
@@ -215,6 +216,10 @@  $(addprefix fate-, $(IGNORE_TESTS)): REPORT=ignore
 
 fate:: $(FATE)
 
+# Tests requiring hardware support are not included in a default fate run.
+fate-hw: $(FATE_HW-yes)
+FATE += $(FATE_HW-yes)
+
 $(FATE) $(FATE_TESTS-no): export PROGSUF = $(PROGSSUF)
 $(FATE) $(FATE_TESTS-no): $(FATE_UTILS:%=tests/%$(HOSTEXESUF))
 	@echo "TEST    $(@:fate-%=%)"
diff --git a/tests/fate/hw.mak b/tests/fate/hw.mak
new file mode 100644
index 0000000000..d606cdeab6
--- /dev/null
+++ b/tests/fate/hw.mak
@@ -0,0 +1,6 @@ 
+FATE_HWCONTEXT += fate-hwdevice
+fate-hwdevice: libavutil/tests/hwdevice$(EXESUF)
+fate-hwdevice: CMD = run libavutil/tests/hwdevice
+fate-hwdevice: CMP = null
+
+FATE_HW-$(CONFIG_AVUTIL) += $(FATE_HWCONTEXT)