Vlastní video efekty

Tento článek popisuje, jak vytvořit prostředí Windows Runtime komponentu, která implementuje rozhraní IBasicVideoEffect pro vytváření vlastních efektů pro video streamy. Vlastní efekty můžete použít s MediaCapture a MediaComposition.

Note

Rozhraní IBasicVideoEffect je rozhraní API systému prostředí Windows Runtime v oboru názvů Windows.Media.Effects a obsahuje stejné členy, jaké jste implementovali v UWP. Desktopové aplikace WinUI 3 ale nemají šablonu projektu komponenty prostředí Windows Runtime, kterou používají projekty UPW. Místo toho vytvoříte efekt pomocí knihovny tříd C#/WinRT a musíte explicitně zaregistrovat komponentu pro prostředí Windows Runtime aktivaci, jak je popsáno v tomto článku.

Přidání komponenty prostředí Windows Runtime pro efekt videa

Desktopové aplikace WinUI 3 používají C#/WinRT k vytváření komponent prostředí prostředí Windows Runtime namísto šablony projektu prostředí Windows Runtime Component, která je k dispozici pouze pro UWP.

  1. V Průzkumník řešení klikněte pravým tlačítkem na řešení a vyberte Přidat>nový Project.

  2. Vyberte šablonu projektu Knihovna tříd . Pojmenujte projekt VideoEffectComponent.

  3. V souboru VideoEffectComponent.csproj nastavte cílovou architekturu tak, aby odpovídala vaší aplikaci WinUI 3, a označte projekt jako komponentu prostředí Windows Runtime:

    <PropertyGroup>
        <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
        <CsWinRTComponent>true</CsWinRTComponent>
    </PropertyGroup>
    
  4. Nainstalujte do projektu VideoEffectComponent nejnovější balíček NuGet Microsoft.Windows.CsWinRT.

  5. Do tohoto projektu komponenty přidejte odkaz na projekt vaší hlavní aplikace WinUI 3.

  6. Přejmenujte výchozí soubor třídy na ExampleVideoEffect.cs.

Další informace o vytváření komponent tímto způsobem najdete v tématu Návod – Vytvoření komponenty C#/WinRT.

Registrace součásti efektu pro aktivaci

VideoEffectDefinition aktivuje váš efekt pomocí ID aktivovatelné třídy prostředí Windows Runtime (úplný název typu, který předáte do typeof(...).FullName). Pokud toto ID třídy nezaregistrujete, aktivace za běhu selže s výjimkou „class not registered“, i když se kód úspěšně zkompiluje. Způsob registrace třídy závisí na tom, jestli je vaše aplikace zabalená.

Zabalené aplikace

Přidejte položku <Extensions> do Package.appxmanifest, která deklaruje efekt jako aktivovatelnou třídu v procesu hostovanou službou WinRT.Host.dll, což je hostitelské sestavení, které C#/WinRT přidá do výstupu sestavení:

<Extensions>
    <Extension Category="windows.activatableClass.inProcessServer">
        <InProcessServer>
            <Path>WinRT.Host.dll</Path>
            <ActivatableClass
                ActivatableClassId="VideoEffectComponent.ExampleVideoEffect"
                ThreadingModel="both" />
        </InProcessServer>
    </Extension>
</Extensions>

Note

ActivatableClassId musí přesně odpovídat plně kvalifikovanému názvu třídy včetně oboru názvů, který předáte do VideoEffectDefinition.

Rozbalené aplikace

Nebalené aplikace nemají Package.appxmanifest, takže místo toho zaregistrujete aktivovatelnou třídu v souboru manifestu aplikace. Do projektu aplikace přidejte nový textový soubor s názvem YourApp.exe.manifest , nastavte jeho vlastnost Content na Hodnotu True , aby se zkopírovala do výstupního adresáře, a přidejte stejnou registraci třídy v tomto formátu:

<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
    <assemblyIdentity version="1.0.0.0" name="YourApp"/>
    <file name="WinRT.Host.dll">
        <activatableClass
            name="VideoEffectComponent.ExampleVideoEffect"
            threadingModel="both"
            xmlns="urn:schemas-microsoft-com:winrt.v1" />
    </file>
</assembly>

Další informace o hostování a registraci komponent C#/WinRT najdete v tématu Hostování spravovaných komponent v úložišti C#/WinRT GitHub.

Implementace rozhraní IBasicVideoEffect pomocí zpracování softwaru

Váš video efekt musí implementovat všechny metody a vlastnosti IBasicVideoEffect rozhraní. Tato část ukazuje implementaci zpracování softwaru.

Definice třídy a obory názvů

using System.Collections.Generic;
using System.Runtime.InteropServices;
using Windows.Foundation.Collections;
using Windows.Graphics.Imaging;
using Windows.Media;
using Windows.Media.Effects;
using Windows.Media.MediaProperties;

namespace VideoEffectComponent
{
    public sealed class ExampleVideoEffect : IBasicVideoEffect
    {
        private VideoEncodingProperties _encodingProperties;
        private IPropertySet _configuration;
        private double _fadeValue = 0.5;

        // The following members implement the IBasicVideoEffect and
        // IMediaExtension interfaces. Each member is explained in its own
        // section later in this article.
        public void SetEncodingProperties(
            VideoEncodingProperties encodingProperties,
            Windows.Graphics.DirectX.Direct3D11.IDirect3DDevice device)
        {
            _encodingProperties = encodingProperties;
        }

        public void SetProperties(IPropertySet configuration)
        {
            _configuration = configuration;

            if (configuration != null &&
                configuration.TryGetValue("FadeValue", out object value))
            {
                _fadeValue = (double)value;
            }
        }

        public void ProcessFrame(ProcessVideoFrameContext context)
        {
            // See ProcessFrame method — software processing later in
            // this article for the full implementation.
        }

        public void DiscardQueuedFrames()
        {
            // Reset any cached frame data
        }

        public void Close(MediaEffectClosedReason reason)
        {
            // Clean up resources
        }

        public bool IsReadOnly => false;

        public bool TimeIndependent => true;

        public IReadOnlyList<VideoEncodingProperties> SupportedEncodingProperties
        {
            get
            {
                var properties = new List<VideoEncodingProperties>();
                properties.Add(new VideoEncodingProperties
                {
                    Subtype = "ARGB32"
                });
                return properties;
            }
        }

        public MediaMemoryTypes SupportedMemoryTypes => MediaMemoryTypes.Cpu;
    }
}

Note

Třída ExampleVideoEffect musí být deklarována uvnitř zde uvedeného oboru názvů VideoEffectComponent, protože volání typeof(VideoEffectComponent.ExampleVideoEffect).FullName dále v tomto článku a hodnota ActivatableClassId v registraci manifestu závisejí na tomto přesném názvu s kvalifikací oboru názvů. Níže uvedené části podrobně popisují jednotlivé členy rozhraní; metoda ProcessFrame, která je zde uvedena, slouží jako zástupný symbol, který je nahrazen úplnou implementací zpracování pixelů v metodě ProcessFrame – softwarové zpracování.

Metoda uzavření

Systém volá zavření , když se efekt vypne. Pomocí této metody můžete odstranit všechny prostředky, které jste vytvořili.

public void Close(MediaEffectClosedReason reason)
{
    // Clean up resources
}

Metoda DiscardQueuedFrames

Systém volá ZahoditQueuedFrames , když se má efekt resetovat. Tuto možnost použijte k odstranění dříve uložených rámců v mezipaměti.

public void DiscardQueuedFrames()
{
    // Reset any cached frame data
}

IsReadOnly – vlastnost

IsReadOnly vlastnost říká systému, zda váš efekt zapisuje do výstupu. Pokud váš efekt pouze analyzuje snímky, nastavte tuto hodnotu na true, aby systém kopíroval snímky ze vstupu na výstup.

public bool IsReadOnly
{
    get => false;
}

Tip

Pokud je IsReadOnlytrue, systém zkopíruje vstupní snímek do výstupního snímku před voláním ProcessFrame. Do ProcessFrame stále můžete zapisovat výstupní snímky.

Metoda SetEncodingProperties

Systém volá metodu SetEncodingProperties, aby vašemu efektu sdělil vlastnosti kódování datového proudu videa. Tato metoda také poskytuje odkaz na zařízení Direct3D pro vykreslování hardwaru.

private Windows.Media.MediaProperties.VideoEncodingProperties _encodingProperties;

public void SetEncodingProperties(
    VideoEncodingProperties encodingProperties,
    Windows.Graphics.DirectX.Direct3D11.IDirect3DDevice device)
{
    _encodingProperties = encodingProperties;
}

vlastnost SupportedEncodingProperties

Systém zkontroluje SupportedEncodingProperties a určí, které vlastnosti kódování váš efekt podporuje.

public IReadOnlyList<VideoEncodingProperties> SupportedEncodingProperties
{
    get
    {
        var properties = new List<VideoEncodingProperties>();
        properties.Add(new VideoEncodingProperties
        {
            Subtype = "ARGB32"
        });
        return properties;
    }
}

Note

Pokud vrátíte prázdný seznam VideoEncodingProperties objektů, systém ve výchozím nastavení nastaví kódování ARGB32.

SupportedMemoryTypes – vlastnost

Vlastnost SupportedMemoryTypes určuje, zda váš efekt přistupuje k snímkům videa v softwarové paměti nebo paměti GPU.

public MediaMemoryTypes SupportedMemoryTypes
{
    get => MediaMemoryTypes.Cpu;
}

Pokud vrátíte MediaMemoryTypes.Cpu, systém předává rámce jako objekty SoftwareBitmap. Pokud vrátíte MediaMemoryTypes.Gpu, systém předává snímky jako objekty IDirect3DSurface.

časově nezávislá vlastnost

Nastavte timeIndependent na true , pokud váš efekt nevyžaduje jednotné načasování. To umožňuje systému optimalizovat výkon.

public bool TimeIndependent
{
    get => true;
}

Metoda SetProperties

Metoda SetProperties umožňuje volající aplikaci předat konfigurační parametry vašemu efektu.

private double _fadeValue = 0.5;
private Windows.Foundation.Collections.IPropertySet _configuration;

public void SetProperties(IPropertySet configuration)
{
    _configuration = configuration;

    if (configuration != null &&
        configuration.TryGetValue("FadeValue", out object value))
    {
        _fadeValue = (double)value;
    }
}

Metoda ProcessFrame – zpracování softwaru

Metoda ProcessFrame je místem, kde váš efekt upravuje obrazová data. Tato metoda je volána jednou za rámec a přijímá ProcessVideoFrameContext objekt se vstupními a výstupními objekty VideoFrame .

Pokud chcete získat přístup k nezpracovaným pixelovým datům v objektu SoftwareBitmap, použijte zprostředkovatele komunikace com. Do oboru názvů efektu přidejte následující definici rozhraní:

[ComImport]
[System.Runtime.InteropServices.Guid("5B0D3235-4DBA-4D44-865E-8F1D0E4FD04D")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
unsafe interface IMemoryBufferByteAccess
{
    void GetBuffer(out byte* buffer, out uint capacity);
}

Note

Tato technika přistupuje k nativní nespravované vyrovnávací paměti obrázku. Projekt musíte nakonfigurovat tak, aby umožňoval nebezpečný kód. Ve vlastnostech projektu vyberte kartu Sestavení a povolte nebezpečný kód.

Následující příklad ztlumí každý pixel ve snímku o nakonfigurovanou hodnotu stmívání:

public unsafe void ProcessFrame(ProcessVideoFrameContext context)
{
    using (BitmapBuffer inputBuffer = context.InputFrame
        .SoftwareBitmap.LockBuffer(BitmapBufferAccessMode.Read))
    using (BitmapBuffer outputBuffer = context.OutputFrame
        .SoftwareBitmap.LockBuffer(BitmapBufferAccessMode.Write))
    {
        using (var inputRef = inputBuffer.CreateReference())
        using (var outputRef = outputBuffer.CreateReference())
        {
            byte* inputBytes;
            uint inputCapacity;
            ((IMemoryBufferByteAccess)inputRef)
                .GetBuffer(out inputBytes, out inputCapacity);

            byte* outputBytes;
            uint outputCapacity;
            ((IMemoryBufferByteAccess)outputRef)
                .GetBuffer(out outputBytes, out outputCapacity);

            var inputPlane =
                inputBuffer.GetPlaneDescription(0);

            for (int i = 0;
                 i < inputPlane.Height;
                 i++)
            {
                for (int j = 0;
                     j < inputPlane.Width;
                     j++)
                {
                    int offset = inputPlane.StartIndex
                        + inputPlane.Stride * i
                        + 4 * j;

                    // Apply fade to B, G, R channels
                    // (skip alpha at offset+3)
                    outputBytes[offset + 0] = (byte)(
                        inputBytes[offset + 0] * _fadeValue);
                    outputBytes[offset + 1] = (byte)(
                        inputBytes[offset + 1] * _fadeValue);
                    outputBytes[offset + 2] = (byte)(
                        inputBytes[offset + 2] * _fadeValue);
                    outputBytes[offset + 3] =
                        inputBytes[offset + 3]; // alpha
                }
            }
        }
    }
}

Zpracování hardwaru pomocí Win2D

Pro zpracování založené na GPU použijte Win2D místo manipulace s rastrovým obrázkem softwaru. Při použití hardwarového zpracování:

  1. Přidejte do projektu efektu balíček NuGet Microsoft.Graphics.Win2D.
  2. Vrátit MediaMemoryTypes.Gpu z SupportedMemoryTypes.
  3. Uložte odkaz na zařízení Direct3D z SetEncodingProperties.
  4. V ProcessFrame vytvořte CanvasDevice ze zařízení Direct3D a použijte kreslicí operace Win2D na Direct3DSurface výstupního snímku.

Note

Pro projekty WinUI 3 použijte balíček Microsoft.Graphics.Win2D namísto staršího balíčku Win2D.uwp.

Přidání efektu do streamu videa

Přidejte efekt videa do streamu videa MediaCapture :

var effectDefinition = new VideoEffectDefinition(
    typeof(VideoEffectComponent.ExampleVideoEffect).FullName);

await _mediaCapture.AddVideoEffectAsync(
    effectDefinition,
    MediaStreamType.VideoPreview);

Předání vlastností konfigurace:

var properties = new PropertySet();
properties["FadeValue"] = 0.7;

var effectDefinition = new VideoEffectDefinition(
    typeof(VideoEffectComponent.ExampleVideoEffect).FullName,
    properties);

Přidejte efekt do mediální kompozice

Přidání efektu videa do klipu v MediaComposition:

var effectDefinition = new VideoEffectDefinition(
    typeof(VideoEffectComponent.ExampleVideoEffect).FullName);

mediaClip.VideoEffectDefinitions.Add(effectDefinition);