Freigeben über


Verarbeiten von Mediendateien im Hintergrund

In diesem Artikel erfahren Sie, wie Sie die MediaProcessingTrigger und eine Hintergrundaufgabe verwenden, um Mediendateien im Hintergrund zu verarbeiten.

Die in diesem Artikel beschriebene Beispiel-App ermöglicht es dem Benutzer, eine Eingabemediendatei zum Transcodieren auszuwählen und eine Ausgabedatei für das Transcodierungsergebnis anzugeben. Anschließend wird eine Hintergrundaufgabe gestartet, um den Transcodierungsvorgang auszuführen. Die MediaProcessingTrigger- soll zusätzlich zur Transcodierung viele verschiedene Medienverarbeitungsszenarien unterstützen, einschließlich des Renderns von Medienkompositionen auf Datenträger und des Hochladens verarbeiteter Mediendateien nach Abschluss der Verarbeitung.

Ausführlichere Informationen zu den verschiedenen Funktionen der Universellen Windows-App, die in diesem Beispiel verwendet werden, finden Sie unter:

Erstellen einer Hintergrundaufgabe für die Medienverarbeitung

Um Ihrer vorhandenen Lösung in Microsoft Visual Studio eine Hintergrundaufgabe hinzuzufügen, geben Sie einen Namen für Ihre Komponente ein.

  1. Wählen Sie im Menü Datei die Option Hinzufügen und dann Neues Projekt...aus.
  2. Wählen Sie den Projekttyp Windows-Runtime-Komponente (Universelle Windows-Komponente)aus.
  3. Geben Sie einen Namen für Ihr neues Komponentenprojekt ein. In diesem Beispiel wird der Projektname MediaProcessingBackgroundTask verwendet.
  4. Klicken Sie auf "OK".

Klicken Sie im Projektmappen-Explorermit der rechten Maustaste auf das Symbol für die Datei "Class1.cs", die standardmäßig erstellt wird, und wählen Sie Umbenennenaus. Benennen Sie die Datei in "MediaProcessingTask.cs" um. Wenn Visual Studio fragt, ob Sie alle Verweise auf diese Klasse umbenennen möchten, klicken Sie auf "Ja".

In der umbenannten Klassendatei fügen Sie die folgenden hinzu und verwenden Sie dabei-Direktiven, um diese Namespaces in Ihr Projekt einzuschließen.

using Windows.ApplicationModel.Background;
using Windows.Storage;
using Windows.UI.Notifications;
using Windows.Data.Xml.Dom;
using Windows.Media.MediaProperties;
using Windows.Media.Transcoding;
using System.Threading;

Aktualisieren Sie die Klassendeklaration, damit Ihre Klasse von IBackgroundTaskerbt.

public sealed class MediaProcessingTask : IBackgroundTask
{

Fügen Sie der Klasse die folgenden Mitgliedervariablen hinzu:

  • Eine IBackgroundTaskInstance-, die dazu verwendet wird, die Vordergrund-App mit dem Fortschritt der Hintergrundaufgabe zu aktualisieren.
  • Ein BackgroundTaskDeferral-, mit dem das System die Hintergrundaufgabe nicht herunterfahren kann, während die Medientranscodierung asynchron ausgeführt wird.
  • Ein CancellationTokenSource--Objekt, das zum Abbrechen des asynchronen Transcodierungsvorgangs verwendet werden kann.
  • Das MediaTranscoder Objekt, das zum Transcodieren von Mediendateien verwendet wird.
IBackgroundTaskInstance backgroundTaskInstance;
BackgroundTaskDeferral deferral;
CancellationTokenSource cancelTokenSource = new CancellationTokenSource();
MediaTranscoder transcoder;

Das System ruft die Run-Methode einer Hintergrundaufgabe auf, wenn die Aufgabe gestartet wird. Legen Sie das IBackgroundTask--Objekt, das an die Methode übergeben wurde, in die entsprechende Membervariable fest. Registrieren Sie einen Handler für das Ereignis Canceled, das ausgelöst wird, wenn das System die Hintergrundaufgabe beenden muss. Legen Sie dann die Eigenschaft Progress auf Null fest.

Rufen Sie als Nächstes die Methode GetDeferral- des Hintergrundaufgabenobjekts auf, um einen Aufschub zu erhalten. Dies weist das System an, Ihre Aktivität nicht abzubrechen, da Sie asynchrone Vorgänge ausführen.

Rufen Sie als Nächstes die Hilfsmethode TranscodeFileAsync auf, die im nächsten Abschnitt definiert ist. Wenn dies erfolgreich abgeschlossen ist, wird eine Hilfsmethode aufgerufen, um eine Toast-Benachrichtigung zu senden und den Benutzer darüber zu informieren, dass die Transkodierung abgeschlossen ist.

Am Ende der Run-Methode rufen Sie Complete beim Deferral-Objekt auf, um dem System mitzuteilen, dass Ihre Hintergrundaufgabe abgeschlossen ist und beendet werden kann.

public async void Run(IBackgroundTaskInstance taskInstance)
{
    Debug.WriteLine("In background task Run method");

    backgroundTaskInstance = taskInstance;
    taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);
    taskInstance.Progress = 0;

    deferral = taskInstance.GetDeferral();
    Debug.WriteLine("Background " + taskInstance.Task.Name + " is called @ " + (DateTime.Now).ToString());

    try
    {
        await TranscodeFileAsync();
        ApplicationData.Current.LocalSettings.Values["TranscodingStatus"] = "Completed Successfully";
        SendToastNotification("File transcoding complete.");

    }
    catch (Exception e)
    {
        Debug.WriteLine("Exception type: {0}", e.ToString());
        ApplicationData.Current.LocalSettings.Values["TranscodingStatus"] = "Error ocurred: " + e.ToString();
    }


    deferral.Complete();
}

In der TranscodeFileAsync-Hilfsmethode werden die Dateinamen für die Eingabe- und Ausgabedateien der Transcodierungsvorgänge aus den LocalSettings Ihrer App abgeholt. Diese Werte werden von der Vordergrund-App festgelegt. Erstellen Sie ein StorageFile--Objekt für die Eingabe- und Ausgabedateien, und erstellen Sie dann ein Codierungsprofil, das für die Transcodierung verwendet werden soll.

Rufen Sie auf, um PrepareFileTranscodeAsyncauszuführen, und übergeben Sie die Eingabedatei, die Ausgabedatei und das Codierungsprofil. Das PrepareTranscodeResult--Objekt, das von diesem Aufruf zurückgegeben wird, informiert Sie, ob die Transcodierung ausgeführt werden kann. Wenn die CanTranscode- eigenschaft "true" ist, rufen Sie TranscodeAsync- auf, um den Transcodierungsvorgang auszuführen.

Mit der AsTask--Methode können Sie den Fortschritt des asynchronen Vorgangs nachverfolgen oder abbrechen. Erstellen Sie ein neues Progress-Objekt , das die gewünschten Fortschrittseinheiten angibt, und den Namen der Methode, die aufgerufen wird, um Sie über den aktuellen Fortschritt der Aufgabe zu benachrichtigen. Übergeben Sie das Progress-Objekt zusammen mit dem Abbruchtoken, mit dem Sie die Aufgabe abbrechen können, an die AsTask--Methode.

  private async Task TranscodeFileAsync()
  {
      transcoder = new MediaTranscoder();

      try
      {
          var settings = ApplicationData.Current.LocalSettings;

          settings.Values["TranscodingStatus"] = "Started";

          var inputFileName = ApplicationData.Current.LocalSettings.Values["InputFileName"] as string;
          var outputFileName = ApplicationData.Current.LocalSettings.Values["OutputFileName"] as string;

          if (inputFileName == null || outputFileName == null)
          {
              return;
          }


          // retrieve the transcoding information
          var inputFile = await Windows.Storage.StorageFile.GetFileFromPathAsync(inputFileName);
          var outputFile = await Windows.Storage.StorageFile.GetFileFromPathAsync(outputFileName);

          // create video encoding profile                
          MediaEncodingProfile encodingProfile = MediaEncodingProfile.CreateMp4(VideoEncodingQuality.HD720p);

          Debug.WriteLine("PrepareFileTranscodeAsync");
          settings.Values["TranscodingStatus"] = "Preparing to transcode ";
          PrepareTranscodeResult preparedTranscodeResult = await transcoder.PrepareFileTranscodeAsync(
              inputFile, 
              outputFile, 
              encodingProfile);

          if (preparedTranscodeResult.CanTranscode)
          {
              var startTime = TimeSpan.FromMilliseconds(DateTime.Now.Millisecond);
              Debug.WriteLine("Starting transcoding @" + startTime);

              var progress = new Progress<double>(TranscodeProgress);
              settings.Values["TranscodingStatus"] = "Transcoding ";
              settings.Values["ProcessingFileName"] = inputFileName;
              await preparedTranscodeResult.TranscodeAsync().AsTask(cancelTokenSource.Token, progress);

          }
          else
          {
              Debug.WriteLine("Source content could not be transcoded.");
              Debug.WriteLine("Transcode status: " + preparedTranscodeResult.FailureReason.ToString());
              var endTime = TimeSpan.FromMilliseconds(DateTime.Now.Millisecond);
              Debug.WriteLine("End time = " + endTime);
          }
      }
      catch (Exception e)
      {
          Debug.WriteLine("Exception type: {0}", e.ToString());
          throw;
      }
  }

Legen Sie in der Methode, die Sie zum Erstellen des Progress-Objekts im vorherigen Schritt verwendet haben, Progressden Fortschritt der Hintergrundaufgabeninstanz fest. Dadurch wird der Fortschritt an die Vordergrund-App übergeben, wenn sie ausgeführt wird.

void TranscodeProgress(double percent)
{
    Debug.WriteLine("Transcoding progress:  " + percent.ToString().Split('.')[0] + "%");
    backgroundTaskInstance.Progress = (uint)percent;
}

Die SendToastNotification- Hilfsmethode erstellt eine neue Toastbenachrichtigung, indem ein XML-Vorlagendokument für eine Toastbenachrichtigung abgerufen wird, die nur Textinhalt enthält. Das Textelement des Toast-XML wird festgelegt, und dann wird ein neues ToastNotification-Objekt aus dem XML-Dokument erstellt. Schließlich wird dem Benutzer die Toast-Benachrichtigung angezeigt, indem ToastNotifier.Showaufgerufen wird.

private void SendToastNotification(string toastMessage)
{
    ToastTemplateType toastTemplate = ToastTemplateType.ToastText01;
    XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(toastTemplate);

    //Supply text content for your notification
    XmlNodeList toastTextElements = toastXml.GetElementsByTagName("text");
    toastTextElements[0].AppendChild(toastXml.CreateTextNode(toastMessage));

    //Create the toast notification based on the XML content you've specified.
    ToastNotification toast = new ToastNotification(toastXml);

    //Send your toast notification.
    ToastNotificationManager.CreateToastNotifier().Show(toast);
}

Im Handler für das Canceled-Ereignis, das aufgerufen wird, wenn das System die Hintergrundaufgabe abbricht, können Sie den Fehler zu Telemetriezwecken protokollieren.

private void OnCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
    Debug.WriteLine("Background " + sender.Task.Name + " Cancel Requested..." + reason.ToString());
}

Registriere und starte die Hintergrundaufgabe

Bevor Sie die Hintergrundaufgabe aus der Vordergrund-App starten können, müssen Sie die Datei "Package.appmanifest" der Vordergrund-App aktualisieren, um dem System mitzuteilen, dass Ihre App eine Hintergrundaufgabe verwendet.

  1. Doppelklicken Sie im Projektmappen-Explorerauf das Dateisymbol "Package.appmanifest", um den Manifest-Editor zu öffnen.
  2. Wählen Sie die Registerkarte Deklarationen aus.
  3. Wählen Sie in verfügbaren DeklarationenHintergrundaufgaben aus, und klicken Sie auf Hinzufügen.
  4. Stellen Sie unter Unterstützten Deklarationen sicher, dass der Hintergrundaufgaben Eintrag ausgewählt ist. Aktivieren Sie unter Eigenschaftendas Kontrollkästchen für Medienverarbeitung.
  5. Geben Sie im Textfeld Einstiegspunkt den Namespace und den Klassennamen für den Hintergrundtest an, getrennt durch einen Punkt. In diesem Beispiel lautet der Eintrag:
MediaProcessingBackgroundTask.MediaProcessingTask

Als Nächstes müssen Sie Ihrer Hintergrundaufgabe einen Verweis auf die Vordergrund-App hinzufügen.

  1. Klicken Sie im Projektmappen-Explorerunter Ihrem Vordergrundprojekt mit der rechten Maustaste auf den Ordner Verweise und wählen Sie Verweis hinzufügen...aus.
  2. Erweitern Sie den Knoten Projekte und wählen Sie Lösungaus.
  3. Aktivieren Sie das Kontrollkästchen neben Ihrem Hintergrundaufgabenprojekt, und klicken Sie auf OK.

Der restliche Code in diesem Beispiel sollte Ihrer App im Vordergrund hinzugefügt werden. Zuerst müssen Sie ihrem Projekt die folgenden Namespaces hinzufügen.

using Windows.ApplicationModel.Background;
using Windows.Storage;

Fügen Sie als Nächstes die folgenden Membervariablen hinzu, die zum Registrieren der Hintergrundaufgabe erforderlich sind.

MediaProcessingTrigger mediaProcessingTrigger;
string backgroundTaskBuilderName = "TranscodingBackgroundTask";
BackgroundTaskRegistration taskRegistration;

Die Hilfsmethode PickFilesToTranscode verwendet einen FileOpenPicker- und einen FileSavePicker-, um die Eingabe- und Ausgabedateien für die Transcodierung zu öffnen. Der Benutzer kann Dateien an einem Speicherort auswählen, auf den Ihre App nicht zugreifen kann. Um sicherzustellen, dass Ihre Hintergrundaufgabe die Dateien öffnen kann, fügen Sie sie der FutureAccess-Liste für Ihre App hinzu.

Legen Sie schließlich Einträge für die Eingabe- und Ausgabedateinamen in der LocalSettings- für Ihre App fest. Die Hintergrundaufgabe ruft die Dateinamen von diesem Speicherort ab.

private async void PickFilesToTranscode()
{
    var openPicker = new Windows.Storage.Pickers.FileOpenPicker();

    openPicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.VideosLibrary;
    openPicker.FileTypeFilter.Add(".wmv");
    openPicker.FileTypeFilter.Add(".mp4");

    StorageFile source = await openPicker.PickSingleFileAsync();

    var savePicker = new Windows.Storage.Pickers.FileSavePicker();

    savePicker.SuggestedStartLocation =
        Windows.Storage.Pickers.PickerLocationId.VideosLibrary;

    savePicker.DefaultFileExtension = ".mp4";
    savePicker.SuggestedFileName = "New Video";

    savePicker.FileTypeChoices.Add("MPEG4", new string[] { ".mp4" });

    StorageFile destination = await savePicker.PickSaveFileAsync();

    if(source == null || destination == null)
    {
        return;
    }

    var storageItemAccessList = Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList;
    storageItemAccessList.Add(source);
    storageItemAccessList.Add(destination);

    ApplicationData.Current.LocalSettings.Values["InputFileName"] = source.Path;
    ApplicationData.Current.LocalSettings.Values["OutputFileName"] = destination.Path;
}

Um die Hintergrundaufgabe zu registrieren, erstellen Sie einen neuen MediaProcessingTrigger- und einen neuen BackgroundTaskBuilder-. Legen Sie den Namen des Hintergrundaufgaben-Generators fest, damit Sie ihn später identifizieren können. Legen Sie TaskEntryPoint auf dieselbe Namespace- und Klassennamenzeichenfolge fest, die Sie in der Manifestdatei verwendet haben. Legen Sie die eigenschaft Trigger auf die MediaProcessingTrigger instanz fest.

Stellen Sie vor der Registrierung des Vorgangs sicher, dass Sie die Registrierung aller zuvor registrierten Aufgaben aufheben, indem Sie die AllTasks--Auflistung durchlaufen und Registrierung aufheben für alle Vorgänge, die den Namen aufweisen, den Sie in der eigenschaft BackgroundTaskBuilder.Name angegeben haben.

Registrieren Sie die Hintergrundaufgabe, indem Sie Registeraufrufen. Registrieren Sie Handler für die Ereignisse Completed und Progress.

private void RegisterBackgroundTask()
{
    // New a MediaProcessingTrigger
    mediaProcessingTrigger = new MediaProcessingTrigger();

    var builder = new BackgroundTaskBuilder();

    builder.Name = backgroundTaskBuilderName;
    builder.TaskEntryPoint = "MediaProcessingBackgroundTask.MediaProcessingTask";
    builder.SetTrigger(mediaProcessingTrigger);

    // unregister old ones
    foreach (var cur in BackgroundTaskRegistration.AllTasks)
    {
        if (cur.Value.Name == backgroundTaskBuilderName)
        {
            cur.Value.Unregister(true);
        }
    }

    taskRegistration = builder.Register();
    taskRegistration.Progress += new BackgroundTaskProgressEventHandler(OnProgress);
    taskRegistration.Completed += new BackgroundTaskCompletedEventHandler(OnCompleted);

    return;
}

Eine typische App registriert ihre Hintergrundaufgabe beim ersten Start der App, z. B. im OnNavigatedTo-Ereignis.

Starten Sie die Hintergrundaufgabe, indem Sie die MediaProcessingTrigger-methode des RequestAsync- Objekts aufrufen. Das von dieser Methode zurückgegebene MediaProcessingTriggerResult-Objekt informiert Sie darüber, ob die Hintergrundaufgabe erfolgreich gestartet wurde. Andernfalls erfahren Sie, warum die Hintergrundaufgabe nicht gestartet wurde.

private async void LaunchBackgroundTask()
{
    var success = true;

    if (mediaProcessingTrigger != null)
    {
        MediaProcessingTriggerResult activationResult;
        activationResult = await mediaProcessingTrigger.RequestAsync();

        switch (activationResult)
        {
            case MediaProcessingTriggerResult.Allowed:
                // Task starting successfully
                break;

            case MediaProcessingTriggerResult.CurrentlyRunning:
            // Already Triggered

            case MediaProcessingTriggerResult.DisabledByPolicy:
            // Disabled by system policy

            case MediaProcessingTriggerResult.UnknownError:
                // All other failures
                success = false;
                break;
        }

        if (!success)
        {
            // Unregister the media processing trigger background task
            taskRegistration.Unregister(true);
        }
    }

}

Eine typische App startet die Hintergrundaufgabe als Reaktion auf Benutzerinteraktionen, z. B. im Click-Ereignis eines Benutzeroberflächensteuerelements.

Der "OnProgress" Ereignishandler wird aufgerufen, wenn die Hintergrundaufgabe den Fortschritt des Vorgangs aktualisiert. Sie können diese Gelegenheit verwenden, um Ihre Benutzeroberfläche mit Statusinformationen zu aktualisieren.

private void OnProgress(IBackgroundTaskRegistration task, BackgroundTaskProgressEventArgs args)
{
    string progress = "Progress: " + args.Progress + "%";
    Debug.WriteLine(progress);
}

Der OnCompleted Ereignishandler wird aufgerufen, wenn die Ausführung der Hintergrundaufgabe abgeschlossen ist. Dies ist eine weitere Möglichkeit, die Benutzeroberfläche zu aktualisieren, um dem Benutzer Statusinformationen zu geben.

private void OnCompleted(IBackgroundTaskRegistration task, BackgroundTaskCompletedEventArgs args)
{
    Debug.WriteLine(" background task complete");
}