[FFmpeg-devel] [PATCH 06/16] avutil: add nvtegra hwcontext

Mark Thompson sw at jkqxz.net
Wed Jun 5 23:47:06 EEST 2024


On 30/05/2024 20:43, averne wrote:
> This includes hwdevice and hwframes objects.
> As the multimedia engines work with tiled surfaces (block linear in nvidia jargon), two frame transfer methods are implemented.
> The first makes use of the VIC to perform the copy. Since some revisions of the VIC (such as the one found in the tegra X1) did not support 10+ bit formats, these go through two separate copy steps for the luma and chroma planes.
> The second method copies on the CPU, and is used as a fallback if the VIC constraints are not satisfied.
> 
> Signed-off-by: averne <averne381 at gmail.com>
> ---
>  libavutil/Makefile             |   7 +-
>  libavutil/hwcontext.c          |   4 +
>  libavutil/hwcontext.h          |   1 +
>  libavutil/hwcontext_internal.h |   1 +
>  libavutil/hwcontext_nvtegra.c  | 880 +++++++++++++++++++++++++++++++++
>  libavutil/hwcontext_nvtegra.h  |  85 ++++
>  6 files changed, 976 insertions(+), 2 deletions(-)
>  create mode 100644 libavutil/hwcontext_nvtegra.c
>  create mode 100644 libavutil/hwcontext_nvtegra.h
> 
> ...> +
> +static int nvtegra_transfer_data(AVHWFramesContext *ctx, AVFrame *dst, const AVFrame *src) {
> +    const AVFrame *swframe;
> +    bool from;
> +    int num_planes, i;
> +
> +    from    = !dst->hw_frames_ctx;
> +    swframe = from ? dst : src;
> +
> +    if (swframe->hw_frames_ctx)
> +        return AVERROR(ENOSYS);
> +
> +    num_planes = av_pix_fmt_count_planes(swframe->format);
> +
> +    for (i = 0; i < num_planes; ++i) {
> +        if (((uintptr_t)swframe->data[i] & 0xff) || (swframe->linesize[i] & 0xff)) {
> +            av_log(ctx, AV_LOG_WARNING, "Frame address/pitch not aligned to 256, "
> +                                        "falling back to cpu transfer\n");
> +            return nvtegra_cpu_transfer_data(ctx, dst, src, num_planes, from);

Are you doing something somewhere to avoid this case?  It seems like it should be the normal one (given alignment is typically set signficantly lower than 256), so this warning would be very spammy.

> +        }
> +    }
> +
> +    return nvtegra_vic_transfer_data(ctx, dst, src, num_planes, from);
> +}
> +
> +const HWContextType ff_hwcontext_type_nvtegra = {
> +    .type                   = AV_HWDEVICE_TYPE_NVTEGRA,
> +    .name                   = "nvtegra",
> +
> +    .device_hwctx_size      = sizeof(NVTegraDevicePriv),
> +    .device_hwconfig_size   = 0,
> +    .frames_hwctx_size      = 0,
> +
> +    .device_create          = &nvtegra_device_create,
> +    .device_init            = &nvtegra_device_init,
> +    .device_uninit          = &nvtegra_device_uninit,
> +
> +    .frames_get_constraints = &nvtegra_frames_get_constraints,
> +    .frames_init            = &nvtegra_frames_init,
> +    .frames_uninit          = &nvtegra_frames_uninit,
> +    .frames_get_buffer      = &nvtegra_get_buffer,
> +
> +    .transfer_get_formats   = &nvtegra_transfer_get_formats,
> +    .transfer_data_to       = &nvtegra_transfer_data,
> +    .transfer_data_from     = &nvtegra_transfer_data,
> +
> +    .pix_fmts = (const enum AVPixelFormat[]) {
> +        AV_PIX_FMT_NVTEGRA,
> +        AV_PIX_FMT_NONE,
> +    },
> +};

What controls whether frames are linear or not?

It seems like the linear case could be exposed directly to the user rather than having to wrap it like this - the decoder could return read-only NV12 (or whatever) frames directly and they would work with other components.

> diff --git a/libavutil/hwcontext_nvtegra.h b/libavutil/hwcontext_nvtegra.h
> new file mode 100644
> index 0000000000..8a2383d304
> --- /dev/null
> +++ b/libavutil/hwcontext_nvtegra.h
> @@ -0,0 +1,85 @@
> +/*
> + * Copyright (c) 2024 averne <averne381 at gmail.com>
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 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 General Public License for more details.
> + *
> + * You should have received a copy of the GNU 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.
> + */
> +
> +#ifndef AVUTIL_HWCONTEXT_NVTEGRA_H
> +#define AVUTIL_HWCONTEXT_NVTEGRA_H
> +
> +#include <stdint.h>
> +
> +#include "hwcontext.h"
> +#include "buffer.h"
> +#include "frame.h"
> +#include "pixfmt.h"
> +
> +#include "nvtegra.h"
> +
> +/*
> + * Encode a hardware revision into a version number
> + */
> +#define AV_NVTEGRA_ENCODE_REV(maj, min) (((maj & 0xff) << 8) | (min & 0xff))
> +
> +/*
> + * Decode a version number
> + */
> +static inline void av_nvtegra_decode_rev(int rev, int *maj, int *min) {
> +    *maj = (rev >> 8) & 0xff;
> +    *min = (rev >> 0) & 0xff;
> +}
> +
> +/**
> + * @file
> + * API-specific header for AV_HWDEVICE_TYPE_NVTEGRA.
> + *
> + * For user-allocated pools, AVHWFramesContext.pool must return AVBufferRefs
> + * with the data pointer set to an AVNVTegraMap.
> + */
> +
> +typedef struct AVNVTegraDeviceContext {
> +    /*
> +     * Hardware multimedia engines
> +     */
> +    AVNVTegraChannel nvdec_channel, nvenc_channel, nvjpg_channel, vic_channel;

Does a user need to supply all of these when making a device?

> +
> +    /*
> +     * Hardware revisions for associated engines, or 0 if invalid
> +     */
> +    int nvdec_version, nvenc_version, nvjpg_version, vic_version;

Why does a user setting up a device context need to supply the version numbers for each thing?

> +} AVNVTegraDeviceContext;
> +
> +typedef struct AVNVTegraFrame {
> +    /*
> +     * Reference to an AVNVTegraMap object
> +     */
> +    AVBufferRef *map_ref;
> +} AVNVTegraFrame;

What is the indirection doing here?  Can't it return the buffer inside this structure instead of making an intermediate structure?

> +
> +/*
> + * Helper to retrieve a map object from the corresponding frame
> + */
> +static inline AVNVTegraMap *av_nvtegra_frame_get_fbuf_map(const AVFrame *frame) {
> +    return (AVNVTegraMap *)((AVNVTegraFrame *)frame->buf[0]->data)->map_ref->data;
> +}
> +
> +/*
> + * Converts a pixel format to the equivalent code for the VIC engine
> + */
> +int av_nvtegra_pixfmt_to_vic(enum AVPixelFormat fmt);
> +
> +#endif /* AVUTIL_HWCONTEXT_NVTEGRA_H */

The mix of normal implementation code and weird specific detail for the particular platform is pretty nasty.  It does seem like exporting more of this into a separate library rather than embedding it in ffmpeg would be a good idea.

Thanks,

- Mark


More information about the ffmpeg-devel mailing list