[vlc-commits] [Git][videolan/vlc][master] 5 commits: video_output: use the output format of static filters to decide lateness

Steve Lhomme (@robUx4) gitlab at videolan.org
Mon Sep 30 11:48:22 UTC 2024



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
3d030a3b by Steve Lhomme at 2024-09-30T11:20:33+00:00
video_output: use the output format of static filters to decide lateness

The decoded video format may not use the final frame rate, which is used to
detect lateness.

- - - - -
e1c9c361 by Steve Lhomme at 2024-09-30T11:20:33+00:00
hw/amf: let the AMF system know what version was used to compile

With future drivers and old binaries it may trigger different code
pathes than the latest version.

- - - - -
c2256ac9 by Steve Lhomme at 2024-09-30T11:20:33+00:00
configure: detect AMD FRC.h

- - - - -
44833103 by Steve Lhomme at 2024-09-30T11:20:33+00:00
video_filter: add AMD FRC filter

- - - - -
b3379d35 by Steve Lhomme at 2024-09-30T11:20:33+00:00
video_output: allow the AMD FRC filter as a static filter

"Interactive" filters can only do one output per input.
Only "static" filters are allowed to generate more frames, like the deinterlacers.

There is no way at this point to probe the module that will be used
if it will output many frames or not. And even if it could the order of
filters would need to be respected. We cannot dynamically add filters
on one chain or the other.

- - - - -


5 changed files:

- configure.ac
- modules/hw/amf/amf_helper.c
- modules/video_filter/Makefile.am
- + modules/video_filter/amf_frc.c
- src/video_output/video_output.c


Changes:

=====================================
configure.ac
=====================================
@@ -2261,6 +2261,16 @@ AS_IF([test "$enable_amf_enhancer" != "no"], [
 ])
 AM_CONDITIONAL([HAVE_AMF_ENHANCER], [test "${have_amf_enhancer}" != "no"])
 
+AC_ARG_ENABLE([amf-frc], AS_HELP_STRING([--disable-amf-frc],
+  [disable AMD FRC (default auto)]))
+have_amf_frc="no"
+AS_IF([test "$enable_amf_frc" != "no"], [
+  AC_CHECK_HEADERS([AMF/core/PropertyStorage.h AMF/components/FRC.h], [
+    have_amf_frc="yes"
+  ])
+])
+AM_CONDITIONAL([HAVE_AMF_FRC], [test "${have_amf_frc}" != "no"])
+
 dnl
 dnl special access module for Blackmagic SDI cards
 dnl


=====================================
modules/hw/amf/amf_helper.c
=====================================
@@ -34,7 +34,7 @@ int vlc_AMFCreateContext(struct vlc_amf_context *c)
     c->Context = NULL;
 
     AMFInit_Fn init = (AMFInit_Fn)GetProcAddress(hLib, AMF_INIT_FUNCTION_NAME);
-    res = init(c->Version, &c->pFactory); // use the highest possible value for that DLL
+    res = init(AMF_FULL_VERSION, &c->pFactory);
     if (unlikely(res != AMF_OK))
         goto error;
 


=====================================
modules/video_filter/Makefile.am
=====================================
@@ -229,6 +229,15 @@ video_filter_LTLIBRARIES += libamf_vqenhancer_plugin.la
 endif
 endif
 
+if HAVE_AMF_FRC
+libamf_frc_plugin_la_SOURCES = video_filter/amf_frc.c \
+    hw/amf/amf_helper.c hw/amf/amf_helper.h
+if HAVE_WIN32
+libamf_frc_plugin_la_LIBADD = $(LIBCOM) libd3d11_common.la -ld3d11
+video_filter_LTLIBRARIES += libamf_frc_plugin.la
+endif
+endif
+
 # misc
 libblend_plugin_la_SOURCES = video_filter/blend.cpp
 video_filter_LTLIBRARIES += libblend_plugin.la


=====================================
modules/video_filter/amf_frc.c
=====================================
@@ -0,0 +1,367 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*****************************************************************************
+ * amf_frc: Frame Rate doubler video with low frame rate
+ *****************************************************************************
+ * Copyright © 2024 Videolabs, VLC authors and VideoLAN
+ *
+ * Authors: Steve Lhomme <robux4 at videolabs.io>
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_filter.h>
+#include <vlc_plugin.h>
+#include <vlc_configuration.h>
+
+#define COBJMACROS
+#include "../hw/amf/amf_helper.h"
+#include <AMF/components/FRC.h>
+
+#include "../video_chroma/d3d11_fmt.h"
+
+#include <assert.h>
+
+static const char *const ppsz_filter_options[] = {
+    "frc-indicator", NULL
+};
+
+static int D3D11CreateAMFFRC(filter_t *);
+
+vlc_module_begin()
+    set_description(N_("AMD Frame Rate Doubler"))
+    add_shortcut(N_("amf_frc"))
+    set_subcategory(SUBCAT_VIDEO_VFILTER)
+    set_callback_video_filter(D3D11CreateAMFFRC)
+
+    add_bool( "frc-indicator", false, N_("Show indicator"), NULL )
+vlc_module_end()
+
+
+struct filter_sys_t
+{
+    struct vlc_amf_context         amf;
+    AMFComponent                   *amf_frc;
+    AMFSurface                     *amfInput;
+    const d3d_format_t             *cfg;
+
+    enum AMF_FRC_MODE_TYPE         mode;
+    bool                           source_rate;
+    date_t                         next_output_pts;
+};
+
+static picture_t *PictureFromTexture(filter_t *filter, d3d11_device_t *d3d_dev, ID3D11Texture2D *out)
+{
+    struct filter_sys_t *sys = filter->p_sys;
+
+    struct d3d11_pic_context *pic_ctx = calloc(1, sizeof(*pic_ctx));
+    if (unlikely(pic_ctx == NULL))
+        return NULL;
+
+    picture_resource_t dummy_res = { .p_sys = NULL };
+    picture_t *p_dst = picture_NewFromResource(&filter->fmt_out.video, &dummy_res);
+    if (p_dst == NULL) {
+        msg_Err(filter, "Failed to map create the temporary picture.");
+        goto done;
+    }
+    p_dst->context = &pic_ctx->s;
+
+    D3D11_PictureAttach(p_dst, out, sys->cfg);
+
+    if (unlikely(D3D11_AllocateResourceView(vlc_object_logger(filter), d3d_dev->d3ddevice, sys->cfg,
+                                            pic_ctx->picsys.texture, 0, pic_ctx->picsys.renderSrc) != VLC_SUCCESS))
+        goto done;
+
+    pic_ctx->s = (picture_context_t) {
+        d3d11_pic_context_destroy, d3d11_pic_context_copy,
+        vlc_video_context_Hold(filter->vctx_out),
+    };
+    pic_ctx->picsys.sharedHandle = INVALID_HANDLE_VALUE;
+
+    return p_dst;
+done:
+    if (p_dst != NULL)
+        picture_Release(p_dst);
+    free(pic_ctx);
+    return NULL;
+}
+
+static picture_t * Filter(filter_t *filter, picture_t *p_pic)
+{
+    struct filter_sys_t *sys = filter->p_sys;
+
+    picture_sys_d3d11_t *src_sys = ActiveD3D11PictureSys(p_pic);
+
+    AMF_RESULT res;
+    AMFSurface *submitSurface;
+
+    AMFPlane *packedStaging = sys->amfInput->pVtbl->GetPlane(sys->amfInput, AMF_PLANE_PACKED);
+    ID3D11Resource *amfStaging = packedStaging->pVtbl->GetNative(packedStaging);
+
+#ifndef NDEBUG
+    ID3D11Texture2D *staging = (ID3D11Texture2D *)amfStaging;
+    D3D11_TEXTURE2D_DESC stagingDesc, inputDesc;
+    ID3D11Texture2D_GetDesc(staging, &stagingDesc);
+    ID3D11Texture2D_GetDesc(src_sys->texture[KNOWN_DXGI_INDEX], &inputDesc);
+    assert(stagingDesc.Width == inputDesc.Width);
+    assert(stagingDesc.Height == inputDesc.Height);
+    assert(stagingDesc.Format == inputDesc.Format);
+#endif
+
+    d3d11_decoder_device_t *dev_sys = GetD3D11OpaqueContext( filter->vctx_in );
+
+#if 0
+    if (src_sys->slice_index == 0)
+    sys->amf.Context->pVtbl->CreateSurfaceFromDX11Native(sys->amf.Context, )
+#endif
+    // copy source into staging as it may not be shared and we can't select a slice
+    d3d11_device_lock( &dev_sys->d3d_dev );
+    ID3D11DeviceContext_CopySubresourceRegion(dev_sys->d3d_dev.d3dcontext, amfStaging,
+                                            0,
+                                            0, 0, 0,
+                                            src_sys->resource[KNOWN_DXGI_INDEX],
+                                            src_sys->slice_index,
+                                            NULL);
+    d3d11_device_unlock( &dev_sys->d3d_dev );
+    submitSurface = sys->amfInput;
+
+    res = sys->amf_frc->pVtbl->SubmitInput(sys->amf_frc, (AMFData*)submitSurface);
+    if (res == AMF_INPUT_FULL)
+    {
+        msg_Dbg(filter, "filter input full, skip this frame");
+        return p_pic;
+    }
+    if (res != AMF_OK)
+    {
+        msg_Err(filter, "filter input failed (err=%d)", res);
+        return p_pic;
+    }
+
+    date_Set( &sys->next_output_pts, p_pic->date );
+    bool got_output = sys->mode != FRC_x2_PRESENT;
+    do {
+        AMFData *amfOutput = NULL;
+        res = sys->amf_frc->pVtbl->QueryOutput(sys->amf_frc, &amfOutput);
+        if (res != AMF_OK && res != AMF_REPEAT)
+        {
+            msg_Err(filter, "filter gave no output (err=%d)", res);
+            break;
+        }
+
+        AMFSurface *amfOutputSurface = (AMFSurface*)amfOutput;
+        AMFPlane *packed = amfOutputSurface->pVtbl->GetPlane(amfOutputSurface, AMF_PLANE_PACKED);
+
+        assert(amfOutput->pVtbl->GetMemoryType(amfOutput) == AMF_MEMORY_DX11);
+        ID3D11Texture2D *out = packed->pVtbl->GetNative(packed);
+        picture_t *dst = PictureFromTexture(filter, &dev_sys->d3d_dev, out);
+        amfOutput->pVtbl->Release(amfOutput);
+        if (dst == NULL)
+            break;
+
+        picture_CopyProperties(dst, p_pic);
+        if (!got_output)
+        {
+            picture_Release(p_pic);
+            p_pic = dst;
+            got_output = true;
+        }
+        else
+        {
+            if (sys->mode == FRC_x2_PRESENT)
+            {
+                // teh first frame is the interpolated one with the previous frame
+                dst->date = date_Get( &sys->next_output_pts );
+                p_pic->date = date_Decrement( &sys->next_output_pts, 1 );
+                vlc_picture_chain_AppendChain(p_pic, dst);
+            }
+            else
+            {
+                // past interpolated then source
+                vlc_picture_chain_AppendChain(dst, p_pic);
+                dst->date = date_Decrement( &sys->next_output_pts, 1 );
+                p_pic = dst;
+            }
+        }
+    } while (res == AMF_REPEAT);
+
+    return p_pic;
+}
+
+static void D3D11CloseAMFFRC(filter_t *filter)
+{
+    struct filter_sys_t *sys = filter->p_sys;
+    sys->amfInput->pVtbl->Release(sys->amfInput);
+    sys->amf_frc->pVtbl->Release(sys->amf_frc);
+    vlc_video_context_Release(filter->vctx_out);
+}
+
+static int D3D11CreateAMFFRC(filter_t *filter)
+{
+    if (!is_d3d11_opaque(filter->fmt_in.video.i_chroma))
+        return VLC_EGENERIC;
+    if ( GetD3D11ContextPrivate(filter->vctx_in) == NULL )
+        return VLC_EGENERIC;
+    if (!video_format_IsSimilar(&filter->fmt_in.video, &filter->fmt_out.video))
+        return VLC_EGENERIC;
+
+    d3d11_decoder_device_t *dev_sys = GetD3D11OpaqueContext( filter->vctx_in );
+    if (dev_sys->d3d_dev.adapterDesc.VendorId != GPU_MANUFACTURER_AMD)
+    {
+        msg_Err(filter, "AMF filter only supported with AMD GPUs");
+        return VLC_ENOTSUP;
+    }
+
+    config_ChainParse( filter, "", ppsz_filter_options, filter->p_cfg );
+
+    struct filter_sys_t *sys = vlc_obj_calloc(VLC_OBJECT(filter), 1, sizeof(*sys));
+    if (unlikely(sys == NULL))
+        return VLC_ENOMEM;
+
+    sys->mode = FRC_x2_PRESENT; //FRC_ONLY_INTERPOLATED;
+
+    d3d11_video_context_t *vctx_sys = GetD3D11ContextPrivate( filter->vctx_in );
+    DXGI_FORMAT input_format = vctx_sys->format;
+    const d3d_format_t *cfg;
+    for (cfg = DxgiGetRenderFormatList(); cfg->name != NULL; ++cfg)
+    {
+        if (cfg->formatTexture == input_format &&
+            is_d3d11_opaque(cfg->fourcc))
+            break;
+    }
+    assert(cfg != NULL);
+
+    AMF_SURFACE_FORMAT amf_fmt = DXGIToAMF(input_format);
+    if (amf_fmt == AMF_SURFACE_UNKNOWN)
+    {
+        msg_Err(filter, "Unsupported DXGI format %s", cfg->name);
+        return VLC_EGENERIC;
+    }
+
+    int err = vlc_AMFCreateContext(&sys->amf);
+    if (err != VLC_SUCCESS)
+        return err;
+
+#if 0
+    if (sys->amf.Version < AMF_MAKE_FULL_VERSION(1,4,34,0))
+    {
+        msg_Dbg(filter, "AMF version %u.%u.%u too old for FRC (1.4.34 needed)",
+            (unsigned)AMF_GET_MAJOR_VERSION(sys->amf.Version),
+            (unsigned)AMF_GET_MINOR_VERSION(sys->amf.Version),
+            (unsigned)AMF_GET_SUBMINOR_VERSION(sys->amf.Version));
+        goto error;
+    }
+#endif
+
+    AMF_RESULT res;
+    res = sys->amf.Context->pVtbl->InitDX11(sys->amf.Context, dev_sys->d3d_dev.d3ddevice, AMF_DX11_0);
+    if (res != AMF_OK)
+        goto error;
+
+    res = sys->amf.pFactory->pVtbl->CreateComponent(sys->amf.pFactory, sys->amf.Context, AMFFRC, &sys->amf_frc);
+    if (res != AMF_OK || sys->amf_frc == NULL)
+        goto error;
+
+// TODO AMF_STREAM_VIDEO_FRAME_RATE
+
+    AMFVariantStruct val;
+    val.int64Value = FRC_ENGINE_DX11;
+    val.type = AMF_VARIANT_INT64;
+    res = sys->amf_frc->pVtbl->SetProperty(sys->amf_frc, AMF_FRC_ENGINE_TYPE, val);
+    if (unlikely(res != AMF_OK))
+    {
+        msg_Err(filter, "Failed to set D3D11 engine type (err=%d)", res);
+        goto error;
+    }
+
+    val.boolValue = false;
+    val.type = AMF_VARIANT_BOOL;
+    res = sys->amf_frc->pVtbl->SetProperty(sys->amf_frc, AMF_FRC_ENABLE_FALLBACK, val);
+    if (unlikely(res != AMF_OK))
+    {
+        msg_Err(filter, "Failed to disable fallback (err=%d)", res);
+        goto error;
+    }
+
+    val.int64Value = FRC_PROFILE_HIGH;
+    val.type = AMF_VARIANT_INT64;
+    res = sys->amf_frc->pVtbl->SetProperty(sys->amf_frc, AMF_FRC_PROFILE, val);
+    if (unlikely(res != AMF_OK))
+    {
+        msg_Err(filter, "Failed to set FRC profile to %" PRId64 " (err=%d)", val.int64Value, res);
+        goto error;
+    }
+
+    val.int64Value = FRC_MV_SEARCH_NATIVE;
+    val.type = AMF_VARIANT_INT64;
+    res = sys->amf_frc->pVtbl->SetProperty(sys->amf_frc, AMF_FRC_MV_SEARCH_MODE, val);
+    if (unlikely(res != AMF_OK))
+    {
+        msg_Err(filter, "Failed to set FRC mv search to %" PRId64 " (err=%d)", val.int64Value, res);
+        goto error;
+    }
+
+    val.int64Value = sys->mode;
+    val.type = AMF_VARIANT_INT64;
+    res = sys->amf_frc->pVtbl->SetProperty(sys->amf_frc, AMF_FRC_MODE, val);
+    if (unlikely(res != AMF_OK))
+    {
+        msg_Err(filter, "Failed to set FRC mode to %" PRId64 " (err=%d)", val.int64Value, res);
+        goto error;
+    }
+
+    val.boolValue = var_GetBool (filter, "frc-indicator");
+    val.type = AMF_VARIANT_BOOL;
+    res = sys->amf_frc->pVtbl->SetProperty(sys->amf_frc, AMF_FRC_INDICATOR, val);
+    if (res != AMF_OK)
+        goto error;
+
+    res = sys->amf_frc->pVtbl->Init(sys->amf_frc, amf_fmt,
+                                    filter->fmt_in.video.i_width,
+                                    filter->fmt_in.video.i_height);
+    if (res != AMF_OK)
+        goto error;
+
+    res = sys->amf.Context->pVtbl->AllocSurface(sys->amf.Context, AMF_MEMORY_DX11,
+                                                amf_fmt,
+                                                filter->fmt_in.video.i_width,
+                                                filter->fmt_in.video.i_height,
+                                                &sys->amfInput);
+    if (res != AMF_OK)
+        goto error;
+
+    sys->cfg = cfg;
+    static const struct vlc_filter_operations filter_ops =
+    {
+        .filter_video = Filter,
+        .close = D3D11CloseAMFFRC,
+    };
+    filter->ops = &filter_ops;
+    filter->p_sys = sys;
+    filter->vctx_out = vlc_video_context_Hold(filter->vctx_in);
+
+    sys->source_rate = filter->fmt_out.video.i_frame_rate_base != 0 &&
+                       filter->fmt_out.video.i_frame_rate != 0;
+
+    if (!sys->source_rate)
+    {
+        msg_Warn( filter, "Missing frame rate, assuming 25fps source" );
+        filter->fmt_out.video.i_frame_rate = 25;
+        filter->fmt_out.video.i_frame_rate_base = 1;
+    }
+
+    filter->fmt_out.video.i_frame_rate = 2 * filter->fmt_out.video.i_frame_rate;
+
+    date_Init( &sys->next_output_pts,
+               filter->fmt_out.video.i_frame_rate, filter->fmt_out.video.i_frame_rate_base );
+
+    return VLC_SUCCESS;
+error:
+    if (sys->amfInput)
+        sys->amfInput->pVtbl->Release(sys->amfInput);
+    if (sys->amf_frc != NULL)
+        sys->amf_frc->pVtbl->Release(sys->amf_frc);
+    vlc_AMFReleaseContext(&sys->amf);
+    return VLC_EGENERIC;
+}


=====================================
src/video_output/video_output.c
=====================================
@@ -823,7 +823,7 @@ static void ChangeFilters(vout_thread_sys_t *vout)
             if (likely(e)) {
                 e->name = name;
                 e->cfg  = cfg;
-                if (!strcmp(e->name, "postproc"))
+                if (!strcmp(e->name, "postproc") || !strcmp(e->name, "amf_frc"))
                     vlc_array_append_or_abort(&array_static, e);
                 else
                     vlc_array_append_or_abort(&array_interactive, e);
@@ -967,14 +967,15 @@ static inline vlc_tick_t GetRenderDelay(vout_thread_sys_t *sys)
     return vout_chrono_GetHigh(&sys->chrono.render) + VOUT_MWAIT_TOLERANCE;
 }
 
-static bool IsPictureLateToStaticFilter(vout_thread_sys_t *vout, const video_format_t *fmt,
+static bool IsPictureLateToStaticFilter(vout_thread_sys_t *vout,
                                         vlc_tick_t time_until_display)
 {
     vout_thread_sys_t *sys = vout;
+    const es_format_t *static_es = filter_chain_GetFmtOut(sys->filter.chain_static);
     const vlc_tick_t prepare_decoded_duration =
         vout_chrono_GetHigh(&sys->chrono.render) +
         vout_chrono_GetHigh(&sys->chrono.static_filter);
-    return IsPictureLateToProcess(vout, fmt, time_until_display, prepare_decoded_duration);
+    return IsPictureLateToProcess(vout, &static_es->video, time_until_display, prepare_decoded_duration);
 }
 
 /* */
@@ -1025,8 +1026,7 @@ static picture_t *PreparePicture(vout_thread_sys_t *vout, bool reuse_decoded,
                 }
 
                 if (is_late_dropped
-                 && IsPictureLateToStaticFilter(vout, &decoded->format,
-                                                system_pts - system_now))
+                 && IsPictureLateToStaticFilter(vout, system_pts - system_now))
                 {
                     picture_Release(decoded);
                     vout_statistic_AddLost(&sys->statistic, 1);



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/8c1a92c7cf62f9539af393c236b5de7e32271249...b3379d35c9794b93f1558c06852174145c48da39

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/8c1a92c7cf62f9539af393c236b5de7e32271249...b3379d35c9794b93f1558c06852174145c48da39
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list