Medienkompositionen und -bearbeitung

Dieser Artikel zeigt Ihnen, wie Sie die APIs im Namespace Windows.Media.Editing verwenden können, um schnell Anwendungen zu entwickeln, die es den Benutzern ermöglichen, Medienkompositionen aus Audio- und Videoquelldateien zu erstellen. Zu den Funktionen des Frameworks gehört die Möglichkeit, mehrere Videoclips programmgesteuert aneinanderzuhängen, Video- und Bildüberlagerungen hinzuzufügen, Hintergrund-Audio hinzuzufügen und sowohl Audio- als auch Videoeffekte anzuwenden. Einmal erstellte Medienkompositionen können in eine flache Mediendatei für die Wiedergabe oder Weitergabe gerendert werden, aber Kompositionen können auch auf der Festplatte serialisiert und deserialisiert werden, so dass der Benutzer Kompositionen laden und ändern kann, die er zuvor erstellt hat. All diese Funktionen werden in einer benutzerfreundlichen Windows-Laufzeitschnittstelle bereitgestellt, die den Umfang und die Komplexität des Codes, der zur Durchführung dieser Aufgaben erforderlich ist, im Vergleich zur Low-Level Microsoft Media Foundation-API drastisch reduziert.

Erstellen einer neuen Medienkomposition

Die Klasse MediaComposition ist der Container für alle Medienclips, aus denen die Komposition besteht, und ist für das Rendern der endgültigen Komposition, das Laden und Speichern von Kompositionen auf Datenträger und die Bereitstellung eines Vorschaustreams der Komposition verantwortlich, damit der Benutzer sie in der Benutzeroberfläche anzeigen kann. Um MediaComposition in Ihrer Anwendung zu verwenden, schließen Sie den Windows.Media.Editing Namespace sowie den Windows.Media.Core Namespace ein, der verwandte APIs bereitstellt, die Sie benötigen werden.

using Windows.Media.Editing;
using Windows.Media.Core;
using Windows.Media.Playback;
using System.Threading.Tasks;

Auf das Objekt MediaComposition wird von mehreren Stellen in Ihrem Code aus zugegriffen werden, so dass Sie in der Regel eine Member-Variable deklarieren, in der Sie es speichern.

private MediaComposition composition;

Der Konstruktor für MediaComposition benötigt keine Argumente.

composition = new MediaComposition();

Hinzufügen von Medienclips zu einer Komposition

Medienkompositionen enthalten in der Regel einen oder mehrere Videoclips. Sie können einen FileOpenPicker verwenden, um dem Benutzer die Auswahl einer Videodatei zu ermöglichen. Sobald die Datei ausgewählt wurde, erstellen Sie ein neues MediaClip-Objekt, das den Videoclip enthält, indem Sie MediaClip.CreateFromFileAsync aufrufen. Dann fügen Sie den Clip zur Liste Clips des Objekts MediaComposition hinzu.

private async Task PickFileAndAddClip()
{
    var picker = new Windows.Storage.Pickers.FileOpenPicker();
    picker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.VideosLibrary;
    picker.FileTypeFilter.Add(".mp4");
    Windows.Storage.StorageFile pickedFile = await picker.PickSingleFileAsync();
    if (pickedFile == null)
    {
        ShowErrorMessage("File picking cancelled");
        return;
    }

    // These files could be picked from a location that we won't have access to later
    var storageItemAccessList = Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList;
    storageItemAccessList.Add(pickedFile);

    var clip = await MediaClip.CreateFromFileAsync(pickedFile);
    composition.Clips.Add(clip);

}
  • Die Medienclips erscheinen in der MediaComposition in der gleichen Reihenfolge wie in der Liste Clips.

  • Ein MediaClip kann nur einmal in eine Komposition aufgenommen werden. Der Versuch, einen MediaClip hinzuzufügen, der bereits von der Komposition verwendet wird, führt zu einem Fehler. Um einen Videoclip mehrfach in einer Komposition zu verwenden, rufen Sie Clone auf, um neue MediaClip-Objekte zu erstellen, die dann der Komposition hinzugefügt werden können.

  • Universelle Windows-Anwendungen haben keine Berechtigung zum Zugriff auf das gesamte Dateisystem. Die Eigenschaft FutureAccessList der Klasse StorageApplicationPermissions ermöglicht es Ihrer Anwendung, einen Datensatz einer Datei zu speichern, die vom Benutzer ausgewählt wurde, so dass Sie die Zugriffsrechte für die Datei beibehalten können. Die FutureAccessList hat eine maximale Anzahl von 1000 Einträgen, so dass Ihre Anwendung die Liste verwalten muss, um sicherzustellen, dass sie nicht voll wird. Dies ist besonders wichtig, wenn Sie planen, das Laden und Ändern von zuvor erstellten Kompositionen zu unterstützen.

  • Eine MediaComposition unterstützt Videoclips im MP4-Format.

  • Wenn eine Videodatei mehrere eingebettete Audiospuren enthält, können Sie auswählen, welche Audiospur in der Komposition verwendet wird, indem Sie die Eigenschaft SelectedEmbeddedAudioTrackIndex festlegen.

  • Erstellen Sie einen MediaClip mit einer einzigen Farbe, die den gesamten Frame ausfüllt, indem Sie CreateFromColor aufrufen und eine Farbe und eine Dauer für den Clip angeben.

  • Erstellen Sie einen MediaClip aus einer Bilddatei, indem Sie CreateFromImageFileAsync aufrufen und eine Bilddatei und eine Dauer für den Clip angeben.

  • Erstellen Sie einen MediaClip aus einer IDirect3DSurface durch Aufruf von CreateFromSurface und Angabe einer Oberfläche und einer Dauer aus dem Clip.

Vorschau der Komposition in einem MediaElement

Um dem Benutzer die Anzeige der Medienkomposition zu ermöglichen, fügen Sie der XAML-Datei, die Ihre Benutzeroberfläche definiert, ein MediaPlayerElement hinzu.

<MediaPlayerElement x:Name="mediaPlayerElement" AutoPlay="False" Margin="5" HorizontalAlignment="Stretch" AreTransportControlsEnabled="True" />

Deklarieren Sie eine Member-Variable des Typs MediaStreamSource.

private MediaStreamSource mediaStreamSource;

Rufen Sie die Methode GeneratePreviewMediaStreamSource des MediaComposition-Objekts auf, um eine MediaStreamSource für die Komposition zu erstellen. Erstellen Sie ein MediaSource-Objekt, indem Sie die Factory-Methode CreateFromMediaStreamSource aufrufen, und weisen Sie es der Source Eigenschaft des MediaPlayerElement zu. Jetzt kann die Komposition in der Benutzeroberfläche angezeigt werden.

public void UpdateMediaElementSource()
{

    mediaStreamSource = composition.GeneratePreviewMediaStreamSource(
        (int)mediaPlayerElement.ActualWidth,
        (int)mediaPlayerElement.ActualHeight);

    mediaPlayerElement.Source = MediaSource.CreateFromMediaStreamSource(mediaStreamSource);

}
  • Die MediaComposition muss mindestens einen Medienclip enthalten, bevor GeneratePreviewMediaStreamSource aufgerufen wird, sonst ist das zurückgegebene Objekt null.

  • Die Zeitleiste MediaElement wird nicht automatisch aktualisiert, um Änderungen in der Komposition zu berücksichtigen. Es wird empfohlen, sowohl GeneratePreviewMediaStreamSource aufzurufen als auch die Eigenschaft MediaPlayerElementSource jedes Mal zu setzen, wenn Sie eine Reihe von Änderungen an der Komposition vornehmen und die Benutzeroberfläche aktualisieren möchten.

Es wird empfohlen, das Objekt MediaStreamSource und die Eigenschaft Source des MediaPlayerElement auf null zu setzen, wenn der Benutzer die Seite verlässt, um die zugehörigen Ressourcen freizugeben.

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
    mediaPlayerElement.Source = null;
    mediaStreamSource = null;
    base.OnNavigatedFrom(e);

}

Rendern der Komposition in eine Videodatei

Um eine Medienkomposition in eine flache Videodatei umzuwandeln, damit sie auf anderen Geräten freigegeben und angezeigt werden kann, müssen Sie APIs aus dem Namespace Windows.Media.Transcoding verwenden. Um die Benutzeroberfläche über den Fortschritt des asynchronen Vorgangs zu aktualisieren, benötigen Sie außerdem APIs aus dem Namespace Windows.UI.Core.

using Windows.Media.Transcoding;
using Windows.UI.Core;

Nachdem Sie dem Benutzer die Möglichkeit gegeben haben, eine Ausgabedatei mit einem FileSavePicker auszuwählen, rendern Sie die Komposition in die ausgewählte Datei, indem Sie den RenderToFileAsync des MediaComposition-Objekts aufrufen. Der Rest des Codes im folgenden Beispiel folgt einfach dem Muster der Handhabung einer AsyncOperationWithProgress.

private async Task RenderCompositionToFile()
{
    var picker = new Windows.Storage.Pickers.FileSavePicker();
    picker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.VideosLibrary;
    picker.FileTypeChoices.Add("MP4 files", new List<string>() { ".mp4" });
    picker.SuggestedFileName = "RenderedComposition.mp4";

    Windows.Storage.StorageFile file = await picker.PickSaveFileAsync();
    if (file != null)
    {
        // Call RenderToFileAsync
        var saveOperation = composition.RenderToFileAsync(file, MediaTrimmingPreference.Precise);

        saveOperation.Progress = new AsyncOperationProgressHandler<TranscodeFailureReason, double>(async (info, progress) =>
        {
            await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler(() =>
            {
                ShowErrorMessage(string.Format("Saving file... Progress: {0:F0}%", progress));
            }));
        });
        saveOperation.Completed = new AsyncOperationWithProgressCompletedHandler<TranscodeFailureReason, double>(async (info, status) =>
        {
            await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler(() =>
            {
                try
                {
                    var results = info.GetResults();
                    if (results != TranscodeFailureReason.None || status != AsyncStatus.Completed)
                    {
                        ShowErrorMessage("Saving was unsuccessful");
                    }
                    else
                    {
                        ShowErrorMessage("Trimmed clip saved to file");
                    }
                }
                finally
                {
                        // Update UI whether the operation succeeded or not
                    }

            }));
        });
    }
    else
    {
        ShowErrorMessage("User cancelled the file selection");
    }
}
  • Die MediaTrimmingPreference ermöglicht es Ihnen, die Geschwindigkeit des Transcodierungsvorgangs gegenüber der Präzision des Trimmens von benachbarten Medienclips zu priorisieren. Schnell bewirkt eine schnellere Transcodierung mit weniger präzisem Zuschnitt, Präzise bewirkt eine langsamere Transcodierung, aber mit präziserem Zuschnitt.

Schneiden eines Videoclips

Kürzen Sie die Dauer eines Videoclips in einer Komposition, indem Sie die Eigenschaften MediaClip objects TrimTimeFromStart, die Eigenschaft TrimTimeFromEnd oder beide festlegen.

private void TrimClipBeforeCurrentPosition()
{
    var currentClip = composition.Clips.FirstOrDefault(
        mc => mc.StartTimeInComposition <= mediaPlayerElement.MediaPlayer.PlaybackSession.Position &&
        mc.EndTimeInComposition >= mediaPlayerElement.MediaPlayer.PlaybackSession.Position);

    TimeSpan positionFromStart = mediaPlayerElement.MediaPlayer.PlaybackSession.Position - currentClip.StartTimeInComposition;
    currentClip.TrimTimeFromStart = positionFromStart;

}
  • Sie können jede beliebige Benutzeroberfläche verwenden, um dem Benutzer die Möglichkeit zu geben, die Anfangs- und Endtrimmwerte anzugeben. Im obigen Beispiel wird die Eigenschaft Position der MediaPlaybackSession verwendet, die mit dem MediaPlayerElement verknüpft ist, um zunächst zu ermitteln, welcher MediaClip an der aktuellen Position in der Komposition wiedergegeben wird, indem die StartTimeInComposition und EndTimeInComposition überprüft werden. Dann werden die Eigenschaften Position und StartTimeInComposition erneut verwendet, um die Zeitspanne zu berechnen, die vom Anfang des Clips an getrimmt werden soll. Die Methode FirstOrDefault ist eine Erweiterungsmethode aus dem Namespace System.Linq, die den Code für die Auswahl von Elementen aus einer Liste vereinfacht.
  • Die Eigenschaft OriginalDuration des Objekts MediaClip gibt Auskunft über die Dauer des Medienclips ohne angewandtes Clipping.
  • Die Eigenschaft TrimmedDuration gibt Ihnen Auskunft über die Dauer des Medienclips nach dem Trimmen.
  • Die Angabe eines Trimmwertes, der größer ist als die ursprüngliche Dauer des Clips, führt nicht zu einem Fehler. Wenn eine Komposition jedoch nur einen einzigen Clip enthält und dieser durch Angabe eines großen Trimmwerts auf eine Länge von Null getrimmt wird, gibt ein anschließender Aufruf von GeneratePreviewMediaStreamSource null zurück, als ob die Komposition keine Clips hätte.

Hinzufügen einer Hintergrund-Audiospur zu einer Komposition

Um einer Komposition einen Hintergrundtrack hinzuzufügen, laden Sie eine Audiodatei und erstellen Sie dann ein BackgroundAudioTrack-Objekt, indem Sie die Factory-Methode BackgroundAudioTrack.CreateFromFileAsync aufrufen. Fügen Sie dann die Eigenschaft BackgroundAudioTrack zur Eigenschaft BackgroundAudioTracks der Komposition hinzu.

private async Task AddBackgroundAudioTrack()
{
    // Add background audio
    var picker = new Windows.Storage.Pickers.FileOpenPicker();
    picker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.MusicLibrary;
    picker.FileTypeFilter.Add(".mp3");
    picker.FileTypeFilter.Add(".wav");
    picker.FileTypeFilter.Add(".flac");
    Windows.Storage.StorageFile audioFile = await picker.PickSingleFileAsync();
    if (audioFile == null)
    {
        ShowErrorMessage("File picking cancelled");
        return;
    }

    // These files could be picked from a location that we won't have access to later
    var storageItemAccessList = Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList;
    storageItemAccessList.Add(audioFile);

    var backgroundTrack = await BackgroundAudioTrack.CreateFromFileAsync(audioFile);

    composition.BackgroundAudioTracks.Add(backgroundTrack);

}
  • Eine MediaComposition unterstützt Hintergrund-Audiospuren in den folgenden Formaten: MP3, WAV, FLAC

  • Eine Audiospur im Hintergrund

  • Wie bei Videodateien sollten Sie die Klasse StorageApplicationPermissions verwenden, um den Zugriff auf Dateien in der Komposition zu erhalten.

  • Wie bei MediaClip kann auch ein BackgroundAudioTrack nur einmal in eine Komposition aufgenommen werden. Der Versuch, einen BackgroundAudioTrack hinzuzufügen, der bereits von der Komposition verwendet wird, führt zu einem Fehler. Um einen Audiotrack mehrfach in einer Komposition zu verwenden, rufen Sie Clone auf, um neue MediaClip-Objekte zu erstellen, die dann der Komposition hinzugefügt werden können.

  • Standardmäßig beginnen die Hintergrund-Audiospuren mit der Wiedergabe am Anfang der Komposition. Wenn mehrere Hintergrundspuren vorhanden sind, werden alle Spuren am Anfang der Komposition abgespielt. Um die Wiedergabe einer Hintergrund-Audiospur zu einem anderen Zeitpunkt beginnen zu lassen, setzen Sie die Eigenschaft Delay auf den gewünschten Zeitversatz.

Hinzufügen eines Overlays zu einer Komposition

Mit Überlagerungen können Sie mehrere Videoebenen in einer Komposition übereinander legen. Eine Komposition kann mehrere Überlagerungsebenen enthalten, von denen jede mehrere Überlagerungen enthalten kann. Erstellen Sie ein MediaOverlay-Objekt, indem Sie einen MediaClip in dessen Konstruktor übergeben. Legen Sie die Position und Deckkraft des Overlays fest, erstellen Sie dann einen neuen MediaOverlayLayer und fügen Sie das MediaOverlay zu seiner Overlays Liste hinzu. Fügen Sie schließlich den MediaOverlayLayer zur Liste OverlayLayers der Komposition hinzu.

private void AddOverlay(MediaClip overlayMediaClip, double scale, double left, double top, double opacity)
{
    Windows.Media.MediaProperties.VideoEncodingProperties encodingProperties =
        overlayMediaClip.GetVideoEncodingProperties();

    Rect overlayPosition;

    overlayPosition.Width = (double)encodingProperties.Width * scale;
    overlayPosition.Height = (double)encodingProperties.Height * scale;
    overlayPosition.X = left;
    overlayPosition.Y = top;

    MediaOverlay mediaOverlay = new MediaOverlay(overlayMediaClip);
    mediaOverlay.Position = overlayPosition;
    mediaOverlay.Opacity = opacity;

    MediaOverlayLayer mediaOverlayLayer = new MediaOverlayLayer();
    mediaOverlayLayer.Overlays.Add(mediaOverlay);

    composition.OverlayLayers.Add(mediaOverlayLayer);
}
  • Überlagerungen innerhalb einer Ebene sind z-geordnet, basierend auf ihrer Reihenfolge in der Liste Overlays der Ebene, die sie enthält. Höhere Indizes innerhalb der Liste werden über den niedrigeren Indizes angezeigt. Das Gleiche gilt für Überlagerungsebenen innerhalb einer Komposition. Eine Ebene mit einem höheren Index in der Liste OverlayLayers der Komposition wird über den niedrigeren Indizes gerendert.

  • Da die Overlays übereinander gestapelt werden, anstatt nacheinander abgespielt zu werden, beginnt die Wiedergabe aller Overlays standardmäßig am Anfang der Komposition. Um die Wiedergabe eines Overlays zu einem anderen Zeitpunkt zu starten, setzen Sie die Eigenschaft Verzögerung auf den gewünschten Zeitversatz.

Effekte zu einem Medienclip hinzufügen

Jeder MediaClip in einer Komposition verfügt über eine Liste von Audio- und Videoeffekten, denen mehrere Effekte hinzugefügt werden können. Die Effekte müssen IAudioEffectDefinition und IVideoEffectDefinition implementieren. Das folgende Beispiel verwendet die aktuelle MediaPlayerElement Position, um den aktuell angezeigten MediaClip auszuwählen und erstellt dann eine neue Instanz der VideoStabilizationEffectDefinition und fügt sie an die VideoEffectDefinitions Liste des MediaClips an.

private void AddVideoEffect()
{
    var currentClip = composition.Clips.FirstOrDefault(
        mc => mc.StartTimeInComposition <= mediaPlayerElement.MediaPlayer.PlaybackSession.Position &&
        mc.EndTimeInComposition >= mediaPlayerElement.MediaPlayer.PlaybackSession.Position);

    VideoStabilizationEffectDefinition videoEffect = new VideoStabilizationEffectDefinition();
    currentClip.VideoEffectDefinitions.Add(videoEffect);
}

Speichern einer Komposition in einer Datei

Medienkompositionen können in eine Datei serialisiert werden, um sie zu einem späteren Zeitpunkt zu ändern. Wählen Sie eine Ausgabedatei und rufen Sie dann die Methode MediaCompositionSaveAsync auf, um die Komposition zu speichern.

private async Task SaveComposition()
{
    var picker = new Windows.Storage.Pickers.FileSavePicker();
    picker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.VideosLibrary;
    picker.FileTypeChoices.Add("Composition files", new List<string>() { ".cmp" });
    picker.SuggestedFileName = "SavedComposition";

    Windows.Storage.StorageFile compositionFile = await picker.PickSaveFileAsync();
    if (compositionFile == null)
    {
        ShowErrorMessage("User cancelled the file selection");
    }
    else
    {
        var action = composition.SaveAsync(compositionFile);
        action.Completed = (info, status) =>
        {
            if (status != AsyncStatus.Completed)
            {
                ShowErrorMessage("Error saving composition");
            }

        };
    }
}

Laden einer Komposition aus einer Datei

Medienkompositionen können aus einer Datei deserialisiert werden, damit der Benutzer die Komposition anzeigen und ändern kann. Wählen Sie eine Kompositionsdatei und rufen Sie dann die Methode MediaCompositionLoadAsync auf, um die Komposition zu laden.

private async Task OpenComposition()
{
    var picker = new Windows.Storage.Pickers.FileOpenPicker();
    picker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.VideosLibrary;
    picker.FileTypeFilter.Add(".cmp");

    Windows.Storage.StorageFile compositionFile = await picker.PickSingleFileAsync();
    if (compositionFile == null)
    {
        ShowErrorMessage("File picking cancelled");
    }
    else
    {
        composition = null;
        composition = await MediaComposition.LoadAsync(compositionFile);

        if (composition != null)
        {
            UpdateMediaElementSource();

        }
        else
        {
            ShowErrorMessage("Unable to open composition");
        }
    }
}
  • Wenn sich eine Mediendatei in der Komposition nicht an einem Speicherort befindet, auf den Ihre Anwendung zugreifen kann, und nicht in der Eigenschaft FutureAccessList der Klasse StorageApplicationPermissions für Ihre Anwendung enthalten ist, wird beim Laden der Komposition ein Fehler ausgelöst.