Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In diesem Artikel wird beschrieben, wie Sie eine Windows-Runtime Komponente erstellen, die die IBasicVideoEffect-Schnittstelle implementiert, um benutzerdefinierte Effekte für Videostreams zu erstellen. Sie können benutzerdefinierte Effekte mit "MediaCapture" und "MediaComposition" verwenden.
Note
Die IBasicVideoEffect-Schnittstelle ist eine Windows-Runtime-API im Namespace Windows.Media.Effects, und die Schnittstellenmitglieder sind dieselben, die Sie in UWP implementiert haben. WinUI 3-Desktop-Apps verfügen jedoch nicht über die Windows-Runtime Komponentenprojektvorlage, die UWP-Projekte verwendet haben. Stattdessen erstellen Sie den Effekt mithilfe einer C#/WinRT-Klassenbibliothek, und Sie müssen die Komponente explizit für Windows-Runtime Aktivierung registrieren, wie in diesem Artikel beschrieben.
Fügen Sie eine Windows-Runtime-Komponente für Ihren Videoeffekt hinzu.
WinUI 3-Desktop-Apps verwenden C#/WinRT, um Windows-Runtime Komponenten zu erstellen, anstelle der Nur-UWP-Windows-Runtime Komponentenprojektvorlage.
Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf Ihre Lösung, und wählen Sie"Neue Project> aus.
Wählen Sie die Projektvorlage "Klassenbibliothek " aus. Nennen Sie das Projekt "VideoEffectComponent".
Legen Sie in VideoEffectComponent.csproj das Zielframework so fest, dass sie Ihrer WinUI 3-App entspricht, und markieren Sie das Projekt als Windows-Runtime Komponente:
<PropertyGroup> <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework> <CsWinRTComponent>true</CsWinRTComponent> </PropertyGroup>Installieren Sie das neueste Microsoft.Windows.CsWinRT-NuGet-Paket im VideoEffectComponent-Projekt.
Fügen Sie diesem Komponentenprojekt einen Projektverweis aus Ihrer WinUI 3-Haupt-App hinzu.
Benennen Sie die Standardklassendatei in ExampleVideoEffect.cs um.
Weitere Informationen zum Erstellen von Komponenten auf diese Weise finden Sie unter Walkthrough – Create a C#/WinRT component.
Registrieren der Effektkomponente für die Aktivierung
VideoEffectDefinition aktiviert Ihren Effekt anhand seiner aktivierbaren Windows-Runtime-Klassen-ID (der vollständige Typname, den Sie an typeof(...).FullName übergeben). Sofern Sie diese Klassen-ID nicht registrieren, schlägt die Aktivierung zur Laufzeit mit einer Ausnahme "Klasse nicht registriert" fehl, auch wenn der Code kompiliert wird. Wie Sie die Klasse registrieren, hängt davon ab, ob Ihre App verpackt ist.
Verpackte Apps
Fügen Sie Package.appxmanifest einen <Extensions>-Eintrag hinzu, der den Effekt als prozessinterne aktivierbare Klasse deklariert, die von WinRT.Host.dll gehostet wird. Dies ist die Hostassembly, die C#/WinRT Ihrer Buildausgabe hinzufügt:
<Extensions>
<Extension Category="windows.activatableClass.inProcessServer">
<InProcessServer>
<Path>WinRT.Host.dll</Path>
<ActivatableClass
ActivatableClassId="VideoEffectComponent.ExampleVideoEffect"
ThreadingModel="both" />
</InProcessServer>
</Extension>
</Extensions>
Note
ActivatableClassId muss genau mit dem Namespace-qualifizierten Klassennamen übereinstimmen, den Sie an VideoEffectDefinition übergeben.
Entpackte Apps
Nicht paketierte Apps haben kein Package.appxmanifest, daher registrieren Sie die aktivierbare Klasse stattdessen in einer Anwendungsmanifestdatei. Fügen Sie ihrem App-Projekt eine neue Textdatei namens YourApp.exe.manifest hinzu, legen Sie die Inhaltseigenschaft auf "True" fest, sodass sie in das Ausgabeverzeichnis kopiert wird, und fügen Sie die gleiche Klassenregistrierung in diesem Format hinzu:
<?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>
Weitere Informationen zum Hosten und Registrieren von C#/WinRT-Komponenten finden Sie unter Verwaltete Komponentenhosting im C#/WinRT-GitHub-Repository.
Implementieren der IBasicVideoEffect-Schnittstelle mithilfe der Softwareverarbeitung
Ihr Videoeffekt muss alle Methoden und Eigenschaften der IBasicVideoEffect-Schnittstelle implementieren. In diesem Abschnitt wird eine Softwareverarbeitungsimplementierung gezeigt.
Klassendefinition und Namespaces
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
Die ExampleVideoEffect Klasse muss innerhalb des VideoEffectComponent hier gezeigten Namespace deklariert werden, da die typeof(VideoEffectComponent.ExampleVideoEffect).FullName Aufrufe weiter unten in diesem Artikel und der ActivatableClassId Wert in der Manifestregistrierung von diesem genauen namespacequalifizierten Namen abhängen. In den folgenden Abschnitten werden die einzelnen Schnittstellenelemente im Detail erläutert; die hier gezeigte ProcessFrame-Methode ist ein Platzhalter, der in Methode „ProcessFrame“ – Softwareverarbeitung durch die vollständige Pixelverarbeitungsimplementierung ersetzt wird.
Close-Methode
Das System ruft Close auf, wenn der Effekt beendet wird. Verwenden Sie diese Methode, um alle von Ihnen erstellten Ressourcen zu löschen.
public void Close(MediaEffectClosedReason reason)
{
// Clean up resources
}
DiscardQueuedFrames-Methode
Das System ruft DiscardQueuedFrames auf, wenn der Effekt zurückgesetzt werden soll. Verwenden Sie dies, um zuvor zwischengespeicherte Frames zu löschen.
public void DiscardQueuedFrames()
{
// Reset any cached frame data
}
IsReadOnly-Eigenschaft
Die IsReadOnly-Eigenschaft teilt dem System mit, ob Ihr Effekt in die Ausgabe schreibt. Wenn Ihr Effekt nur Frames analysiert, legen Sie dies so true fest, dass das System Frames von der Eingabe in die Ausgabe kopiert.
public bool IsReadOnly
{
get => false;
}
Tip
Wenn IsReadOnlytrue ist, kopiert das System den Eingabe-Frame in den Ausgabe-Frame, bevor ProcessFrame aufgerufen wird. Sie können in ProcessFrame weiterhin in die Ausgabe-Frames schreiben.
SetEncodingProperties-Methode
Das System ruft SetEncodingProperties auf, um Ihren Effekt über die Kodierungseigenschaften des Videostreams zu informieren. Diese Methode stellt auch einen Verweis auf das Direct3D-Gerät für das Hardwarerendering bereit.
private Windows.Media.MediaProperties.VideoEncodingProperties _encodingProperties;
public void SetEncodingProperties(
VideoEncodingProperties encodingProperties,
Windows.Graphics.DirectX.Direct3D11.IDirect3DDevice device)
{
_encodingProperties = encodingProperties;
}
SupportedEncodingProperties-Eigenschaft
Das System überprüft SupportedEncodingProperties , um zu bestimmen, welche Codierungseigenschaften ihr Effekt unterstützt.
public IReadOnlyList<VideoEncodingProperties> SupportedEncodingProperties
{
get
{
var properties = new List<VideoEncodingProperties>();
properties.Add(new VideoEncodingProperties
{
Subtype = "ARGB32"
});
return properties;
}
}
Note
Wenn Sie eine leere Liste von VideoEncodingProperties Objekten zurückgeben, wird standardmäßig die ARGB32-Codierung vom System verwendet.
SupportedMemoryTypes-Eigenschaft
Die SupportedMemoryTypes-Eigenschaft bestimmt, ob Der Effekt auf Videoframes im Softwarespeicher oder GPU-Speicher zugreift.
public MediaMemoryTypes SupportedMemoryTypes
{
get => MediaMemoryTypes.Cpu;
}
Wenn Sie MediaMemoryTypes.Cpu zurückgeben, übergibt das System Frames als SoftwareBitmap-Objekte. Wenn Sie MediaMemoryTypes.Gpu zurückgeben, übergibt das System Frames als IDirect3DSurface-Objekte.
TimeIndependent-Eigenschaft
Setzen Sie TimeIndependent auf true, wenn Ihr Effekt keine einheitliche Zeitsteuerung erfordert. Auf diese Weise kann das System die Leistung optimieren.
public bool TimeIndependent
{
get => true;
}
SetProperties-Methode
Mit der SetProperties-Methode kann die aufrufende App Konfigurationsparameter an Ihren Effekt übergeben.
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;
}
}
ProcessFrame-Methode – Softwareverarbeitung
Die ProcessFrame-Methode ist der Ort, an dem der Effekt die Bilddaten ändert. Diese Methode wird einmal pro Frame aufgerufen und empfängt ein ProcessVideoFrameContext-Objekt mit Eingabe- und Ausgabe-VideoFrame-Objekten .
Verwenden Sie COM-Interop, um auf die rohen Pixeldaten eines SoftwareBitmap zuzugreifen. Fügen Sie die folgende Schnittstellendefinition in Ihrem Effektnamespace hinzu:
[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
Diese Technik greift auf einen systemeigenen, nicht verwalteten Bildpuffer zu. Sie müssen Ihr Projekt so konfigurieren, dass unsicherer Code zulässig ist. Wählen Sie in den Projekteigenschaften die Registerkarte " Build " aus, und aktivieren Sie "Unsicheren Code zulassen".
Im folgenden Beispiel wird jedes Pixel im Bild um den konfigurierten Dimmwert gedimmt:
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
}
}
}
}
}
Hardwareverarbeitung mit Win2D
Verwenden Sie für die GPU-basierte Verarbeitung Win2D anstelle der Softwarebitmap-Manipulation. Bei Verwendung der Hardwareverarbeitung:
- Fügen Sie das Microsoft.Graphics.Win2D-NuGet-Paket zu Ihrem Effektprojekt hinzu.
-
MediaMemoryTypes.GpuausSupportedMemoryTypeszurückgeben. - Speichern Sie den Direct3D-Geräteverweis von
SetEncodingProperties. - Erstellen Sie in
ProcessFrameeinCanvasDeviceaus dem Direct3D-Gerät, und verwenden Sie Win2D-Zeichenvorgänge fürDirect3DSurfacedes Ausgabe-Frames.
Note
Verwenden Sie für WinUI 3-Projekte die Microsoft. Graphics.Win2D-Paket anstelle des älteren Win2D.uwp-Pakets.
Hinzufügen des Effekts zu einem Videostream
Hinzufügen des Videoeffekts zu einem MediaCapture-Videostream :
var effectDefinition = new VideoEffectDefinition(
typeof(VideoEffectComponent.ExampleVideoEffect).FullName);
await _mediaCapture.AddVideoEffectAsync(
effectDefinition,
MediaStreamType.VideoPreview);
So übergeben Sie Konfigurationseigenschaften:
var properties = new PropertySet();
properties["FadeValue"] = 0.7;
var effectDefinition = new VideoEffectDefinition(
typeof(VideoEffectComponent.ExampleVideoEffect).FullName,
properties);
Hinzufügen des Effekts zu einer Medienkomposition
Fügen Sie den Videoeffekt zu einem Clip in einer MediaComposition hinzu:
var effectDefinition = new VideoEffectDefinition(
typeof(VideoEffectComponent.ExampleVideoEffect).FullName);
mediaClip.VideoEffectDefinitions.Add(effectDefinition);
Verwandte Inhalte
Windows developer