Partager via


Traiter des fichiers multimédias en arrière-plan

Cet article vous montre comment utiliser MediaProcessingTrigger et une tâche en arrière-plan pour traiter les fichiers multimédias en arrière-plan.

L’exemple d’application décrit dans cet article permet à l’utilisateur de sélectionner un fichier multimédia d’entrée à transcoder et de spécifier un fichier de sortie pour le résultat du transcodage. Ensuite, une tâche en arrière-plan est lancée pour effectuer l’opération de transcodage. Outre le transcodage, MediaProcessingTrigger est destiné à prendre en charge différents scénarios de traitement multimédia, notamment le rendu de compositions multimédias sur disque et le chargement de fichiers multimédias traités une fois le traitement terminé.

Pour plus d’informations sur les différentes fonctionnalités d’application Windows universelle utilisées dans cet exemple, consultez :

Créer une tâche de traitement multimédia en arrière-plan

Pour ajouter une tâche en arrière-plan à votre solution existante dans Microsoft Visual Studio, entrez un nom pour votre composant

  1. Dans le menu Fichier, sélectionnez Ajouter, puis Nouveau projet....
  2. Sélectionnez le type de projet Composant Windows Runtime (Windows universel).
  3. Entrez un nom pour votre nouveau projet de composant. Cet exemple utilise le nom de projet MediaProcessingBackgroundTask.
  4. Cliquez sur OK.

Dans l’Explorateur de solutions, cliquez avec le bouton droit sur l’icône du fichier « Class1.cs » créé par défaut, puis sélectionnez Renommer. Renommez le fichier en « MediaProcessingTask.cs ». Lorsque Visual Studio vous demande si vous souhaitez renommer toutes les références à cette classe, cliquez sur Oui.

Dans le fichier de classe renommé, ajoutez les directives using suivantes pour inclure ces espaces de noms dans votre projet.

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;

Mettez à jour votre déclaration de classe pour faire en sorte que votre classe hérite de IBackgroundTask.

public sealed class MediaProcessingTask : IBackgroundTask
{

Ajoutez les variables membres suivantes à votre classe :

  • Un objet IBackgroundTaskInstance qui sera utilisé pour mettre à jour l’application au premier plan avec la progression de la tâche en arrière-plan.
  • Un objet BackgroundTaskDeferral qui empêche le système d’arrêter votre tâche en arrière-plan pendant que le transcodage multimédia est exécuté en mode asynchrone.
  • Un objet CancellationTokenSource qui peut être utilisé pour annuler l’opération de transcodage asynchrone.
  • L’objet MediaTranscoder qui sera utilisé pour transcoder des fichiers multimédias.
IBackgroundTaskInstance backgroundTaskInstance;
BackgroundTaskDeferral deferral;
CancellationTokenSource cancelTokenSource = new CancellationTokenSource();
MediaTranscoder transcoder;

Le système appelle la méthode Run d’une tâche en arrière-plan lors du lancement de la tâche. Définissez l’objet IBackgroundTask passé dans la méthode à la variable membre correspondante. Enregistrez un gestionnaire de l’événement Canceled, qui sera déclenché si le système a besoin d’arrêter la tâche en arrière-plan. Ensuite, définissez la propriété Progress sur zéro.

Appelez ensuite la méthode GetDeferral de l’objet de tâche en arrière-plan pour obtenir un report. Cela indique au système de ne pas arrêter votre tâche, car vous effectuez des opérations asynchrones.

Ensuite, appelez la méthode d’assistance TranscodeFileAsync, qui est définie dans la section suivante. Si cela se termine correctement, une méthode d’assistance est appelée pour lancer une notification toast pour avertir l’utilisateur que le transcodage est terminé.

À la fin de la méthode Run, appelez Complete sur l’objet de report pour informer le système que votre tâche en arrière-plan est terminée et peut être arrêtée.

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();
}

Dans la méthode d’assistance TranscodeFileAsync, les noms des fichiers d’entrée et de sortie pour les opérations de transcodage sont récupérés à partir de LocalSettings pour votre application. Ces valeurs seront définies par votre application de premier plan. Créez un objet StorageFile pour les fichiers d’entrée et de sortie, puis créez un profil d’encodage à utiliser pour le transcodage.

Appelez PrepareFileTranscodeAsync, en passant le fichier d’entrée, le fichier de sortie et le profil d’encodage. L’objet PrepareTranscodeResult renvoyé à partir de cet appel vous permet de savoir si le transcodage peut être effectué. Si la propriété CanTranscode a la valeur true, appelez TranscodeAsync pour effectuer l’opération de transcodage.

La méthode AsTask vous permet de suivre la progression de l’opération asynchrone ou de l’annuler. Créez un objet Progress, en spécifiant les unités de progression souhaitées et le nom de la méthode qui sera appelée pour vous informer de la progression actuelle de la tâche. Transmettez l’objet Progress à la méthode AsTask avec le jeton d’annulation qui vous permet d’annuler la tâche.

  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;
      }
  }

Dans la méthode que vous avez utilisée pour créer l’objet Progress à l’étape précédente, Progress, définissez la progression de l’instance de tâche en arrière-plan. Cela transmet la progression à l’application de premier plan, si elle est en cours d’exécution.

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

La méthode d’assistance SendToastNotification crée une notification toast en obtenant un modèle de document XML pour un toast contenant uniquement du contenu texte. L’élément texte du toast XML est défini, puis un nouvel objet ToastNotification est créé à partir du document XML. Enfin, le toast est affiché à l’utilisateur en appelant ToastNotifier.Show.

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);
}

Dans le gestionnaire de l’événement Canceled, appelé lorsque le système annule votre tâche en arrière-plan, vous pouvez consigner l’erreur à des fins de télémétrie.

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

Inscrire et lancer la tâche en arrière-plan

Avant de pouvoir lancer la tâche en arrière-plan à partir de votre application de premier plan, vous devez mettre à jour le fichier Package.appmanifest de votre application de premier plan pour informer le système que votre application utilise une tâche en arrière-plan.

  1. Dans l’Explorateur de solutions, double-cliquez sur l’icône du fichier Package.appmanifest pour ouvrir l’éditeur de manifeste.
  2. Sélectionnez l’onglet Déclarations.
  3. Dans Déclarations disponibles, sélectionnez Tâches en arrière-plan, puis cliquez sur Ajouter.
  4. Sous Déclarations prises en charge, vérifiez que l’élément Tâches en arrière-plan est sélectionné. Sous Propriétés, cochez la case Traitement multimédia.
  5. Dans la zone de texte Point d’entrée, spécifiez l’espace de noms et le nom de classe de votre test en arrière-plan, séparés par un point. Pour cet exemple, l’entrée est la suivante :
MediaProcessingBackgroundTask.MediaProcessingTask

Ensuite, vous devez ajouter une référence à votre tâche en arrière-plan à votre application de premier plan.

  1. Dans l’Explorateur de solutions, sous votre projet d’application de premier plan, cliquez avec le bouton droit sur le dossier Références, puis sélectionnez Ajouter une référence....
  2. Développez le nœud Projets et sélectionnez Solution.
  3. Cochez la case en regard de votre projet de tâche en arrière-plan, puis cliquez sur OK.

Le reste du code de cet exemple doit être ajouté à votre application de premier plan. Tout d’abord, vous devez ajouter les espaces de noms suivants à votre projet.

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

Ensuite, ajoutez les variables membres suivantes nécessaires pour inscrire la tâche en arrière-plan.

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

La méthode d’assistance PickFilesToTranscode utilise un FileOpenPicker et un FileSavePicker pour ouvrir les fichiers d’entrée et de sortie pour le transcodage. L’utilisateur peut sélectionner des fichiers à un emplacement auquel votre application n’a pas accès. Pour vous assurer que votre tâche en arrière-plan peut ouvrir les fichiers, ajoutez-les à FutureAccessList pour votre application.

Enfin, définissez les entrées pour les noms de fichiers d’entrée et de sortie dans l’objet LocalSettings de votre application. La tâche en arrière-plan récupère les noms de fichiers à partir de cet emplacement.

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;
}

Pour enregistrer la tâche en arrière-plan, créez des objets MediaProcessingTrigger et BackgroundTaskBuilder. Définissez le nom du générateur de tâches en arrière-plan pour pouvoir l’identifier ultérieurement. Définissez TaskEntryPoint sur la même chaîne d’espace de noms et de nom de classe que vous avez utilisée dans le fichier manifeste. Définissez la propriété Trigger sur l’instance MediaProcessingTrigger.

Avant d’inscrire la tâche, veillez à désinscrire toutes les tâches précédemment inscrites en passant en revue en boucle la collection AllTasks et en appelant Unregister sur toutes les tâches qui ont le nom que vous avez spécifié dans la propriété BackgroundTaskBuilder.Name.

Enregistrez la tâche en arrière-plan en appelant Register. Inscrivez des gestionnaires pour les événements Completed et 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;
}

Une application classique inscrit sa tâche en arrière-plan lors du lancement initial de l’application, par exemple dans l’événement OnNavigatedTo.

Lancez la tâche en arrière-plan en appelant la méthode RequestAsync de l’objet MediaProcessingTrigger. L’objet MediaProcessingTriggerResult retourné par cette méthode vous permet de savoir si la tâche en arrière-plan a été démarrée correctement et, si ce n’est pas le cas, vous indique pourquoi la tâche en arrière-plan n’a pas été lancée.

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);
        }
    }

}

Une application classique lance la tâche en arrière-plan en réponse à une interaction utilisateur, par exemple dans l’événement Click d’un contrôle d’interface utilisateur.

Le gestionnaire d’événements OnProgress est appelé lorsque la tâche en arrière-plan met à jour la progression de l’opération. Vous pouvez utiliser cette opportunité pour mettre à jour votre interface utilisateur avec des informations de progression.

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

Le gestionnaire d’événements OnCompleted est appelé lorsque la tâche en arrière-plan est terminée. Il s’agit d’une autre occasion de mettre à jour votre interface utilisateur pour fournir des informations d’état à l’utilisateur.

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