Elaborare file multimediali in backgroundProcess media files in the background

Questo articolo illustra come usare MediaProcessingTrigger e un'attività in background per elaborare i file multimediali in background.This article shows you how to use the MediaProcessingTrigger and a background task to process media files in the background.

L'app di esempio descritta in questo articolo consente all'utente di selezionare un file multimediale per la transcodifica e di specificare un file di output per il risultato della transcodifica.The example app described in this article allows the user to select an input media file to transcode and specify an output file for the transcoding result. Viene quindi avviata un'attività in background per eseguire l'operazione di transcodifica.Then, a background task is launched to perform the transcoding operation. Il MediaProcessingTrigger è progettato per supportare molti diversi scenari di elaborazione di contenuti multimediali oltre alla transcodifica, tra cui il rendering di composizioni di supporti su disco e il caricamento di file multimediali elaborati al termine dell'elaborazione.The MediaProcessingTrigger is intended to support many different media processing scenarios besides transcoding, including rendering media compositions to disk and uploading processed media files after processing is complete.

Per informazioni più dettagliate sulle diverse funzionalità dell'app di Windows universale usate in questo esempio, vedi:For more detailed information on the different Universal Windows app features utilized in this sample, see:

Creare un attività in background di elaborazione multimedialeCreate a media processing background task

Per aggiungere un'attività in background alla soluzione esistente in Microsoft Visual Studio, immetti un nome per il componente.To add a background task to your existing solution in Microsoft Visual Studio, Enter a name for your comp

  1. Scegli Aggiungi e Nuovo progetto dal menu File.From the File menu, select Add and then New Project....
  2. Seleziona il tipo di progetto Componente Windows Runtime (Windows universale).Select the project type Windows Runtime Component (Universal Windows).
  3. Immetti un nome per il nuovo progetto di componente.Enter a name for your new component project. In questo esempio viene usato il nome di progetto MediaProcessingBackgroundTask.This example uses the project name MediaProcessingBackgroundTask.
  4. Fare clic su OK.Click OK.

InEsplora soluzioni fai clic con il pulsante destro del mouse sull’icona per il file "Class1" creato per impostazione predefinita e scegli Rinomina.In Solution Explorer, right-click the icon for the "Class1.cs" file that is created by default and select Rename. Rinomina il file "MediaProcessingTask.cs".Rename the file to "MediaProcessingTask.cs". Quando Visual Studio richiede se vuoi rinominare tutti i riferimenti a questa classe, fai clic su .When Visual Studio asks if you want to rename all of the references to this class, click Yes.

Nel file di classe rinominato aggiungi le direttive seguenti using per includere questi spazi dei nomi nel progetto.In the renamed class file, add the following using directives to include these namespaces in your project.

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;

Aggiornare la dichiarazione di classe per fare in modo che la classe erediti da IBackgroundTask.Update your class declaration to make your class inherit from IBackgroundTask.

public sealed class MediaProcessingTask : IBackgroundTask
{

Aggiungi le variabili membro seguenti alla classe:Add the following member variables to your class:

  • Un IBackgroundTaskInstance che verrà usato per aggiornare l'app in primo piano con lo stato di avanzamento dell'attività in background.An IBackgroundTaskInstance that will be used to update the foreground app with the progress of the background task.
  • Un BackgroundTaskDeferral che impedisce al sistema di arrestare l'attività in background mentre la transcodifica multimediale viene eseguita in modo asincrono.A BackgroundTaskDeferral that keeps the system from shutting down your background task while media transcoding is being performed asynchronously.
  • Un oggetto CancellationTokenSource che può essere usato per annullare l’operazione di transcodifica asincrona.A CancellationTokenSource object that can be used to cancel the asynchronous transcoding operation.
  • L’oggetto MediaTranscoder che verrà usato per transcodificare i file multimediali.The MediaTranscoder object that will be used to transcode media files.
IBackgroundTaskInstance backgroundTaskInstance;
BackgroundTaskDeferral deferral;
CancellationTokenSource cancelTokenSource = new CancellationTokenSource();
MediaTranscoder transcoder;

Il sistema chiama il metodo Run di un’attività in background quando viene avviata l’attività.The system calls Run method of a background task when the task is launched. Imposta l’oggetto IBackgroundTask passato al metodo sulla variabile membro corrispondente.Set the IBackgroundTask object passed into the method to the corresponding member variable. Registrare un gestore per l'evento annullato , che verrà generato se il sistema deve arrestare l'attività in background.Register a handler for the Canceled event, which will be raised if the system needs to shut down the background task. Impostare quindi la proprietà Progress su zero.Then, set the Progress property to zero.

Chiama quindi il metodo GetDeferral dell’oggetto dell’attività in background per ottenere un differimento.Next, call the background task object's GetDeferral method to obtain a deferral. Ciò indica al sistema di non arrestare l'attività perché stai eseguendo operazioni asincrone.This tells the system not to shut down your task because you are performing asynchronous operations.

Chiama quindi il metodo helper TranscodeFileAsync, definito nella sezione successiva.Next, call the helper method TranscodeFileAsync, which is defined in the next section. Se viene completato correttamente, viene chiamato un metodo helper per avviare una notifica di tipo avviso popup per avvisare l'utente che la transcodifica è completata.If that completes successfully, a helper method is called to launch a toast notification to alert the user that transcoding is complete.

Alla fine del metodo Run chiama Complete sull’oggetto di differimento per segnalare al sistema che l’attività in background è completa e può essere terminata.At the end of the Run method, call Complete on the deferral object to let the system know that your background task is complete and can be terminated.

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

Nel metodo helper TranscodeFileAsync i nomi per i file di input e output per le operazioni di transcodifica vengono recuperati dalla proprietà LocalSettings per la tua app.In the TranscodeFileAsync helper method, the file names for the input and output files for the transcoding operations are retrieved from the LocalSettings for your app. Questi valori verranno impostati dall'app in primo piano.These values will be set by your foreground app. Crea un oggetto StorageFile per i file di input e output, quindi crea un profilo di codifica da usare per la transcodifica.Create a StorageFile object for the input and output files and then create an encoding profile to use for transcoding.

Chiama PrepareFileTranscodeAsync passando il file di input, il file di output e il profilo di codifica.Call PrepareFileTranscodeAsync, passing in the input file, output file, and encoding profile. L’oggetto PrepareTranscodeResult restituito dalla chiamata ti consente di verificare se è possibile eseguire la transcodifica.The PrepareTranscodeResult object returned from this call lets you know if transcoding can be performed. Se la proprietà CanTranscode è true, chiamare TranscodeAsync per eseguire l'operazione di transcodifica.If the CanTranscode property is true, call TranscodeAsync to perform the transcoding operation.

Il metodo AsTask ti consente di tenere traccia dello stato dell’operazione asincrona o di annullarla.The AsTask method enables you to track the progress the asynchronous operation or cancel it. Crea un nuovo oggetto Progress specificando le unità di stato desiderate e il nome del metodo che verrà chiamato per notificare lo stato corrente dell’attività.Create a new Progress object, specifying the units of progress you desire and the name of the method that will be called to notify you of the current progress of the task. Passa l’oggetto Progress al metodo AsTask insieme al token di annullamento che consente di annullare l’attività.Pass the Progress object into the AsTask method along with the cancellation token that allows you to cancel the task.

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

Nel metodo usato per creare l’oggetto Progress nel passaggio precedente,Progress, imposta lo stato dell’istanza dell’attività in background.In the method you used to create the Progress object in the previous step, Progress, set the progress of the background task instance. Lo stato verrà passato all'app in primo piano, se è in esecuzione.This will pass the progress to the foreground app, if it is running.

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

Il metodo helper SendToastNotification crea una nuova notifica di tipo avviso popup recuperando un documento XML modello per un avviso popup solo con contenuto di testo.The SendToastNotification helper method creates a new toast notification by getting a template XML document for a toast that only has text content. L'elemento di testo del file XML di tipo avviso popup viene impostato, quindi viene creato un nuovo oggetto ToastNotification dal documento XML.The text element of the toast XML is set and then a new ToastNotification object is created from the XML document. Infine, il popup viene visualizzato all'utente chiamando ToastNotifier. Show.Finally, the toast is shown to the user by calling 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);
}

Nel gestore dell'evento Canceled, che viene chiamato quando il sistema annulla la tua attività in background, puoi registrare l'errore a fini di telemetria.In the handler for the Canceled event, which is called when the system cancels your background task, you can log the error for telemetry purposes.

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

Registrare e avviare l'attività in backgroundRegister and launch the background task

Prima di poter avviare l'attività in background dalla tua app in primo piano, devi aggiornare il file Package.appmanifest dell'app in primo piano per segnalare al sistema che l'app usa un'attività in background.Before you can launch the background task from your foreground app, you must update your foreground app's Package.appmanifest file to let the system know that your app uses a background task.

  1. In Esplora soluzioni fai doppio clic su Package.appxmanifest per aprire l’editor del manifesto.In Solution Explorer, double-click the Package.appmanifest file icon to open the manifest editor.
  2. Seleziona la scheda Dichiarazioni.Select the Declarations tab.
  3. In Dichiarazioni disponibili seleziona Attività in background e fai clic su Aggiungi.From Available Declarations, select Background Tasks and click Add.
  4. In Dichiarazioni supportate assicurati che la voce Attività in background sia selezionata.Under Supported Declarations make sure that the Background Tasks item is selected. In Proprietà seleziona la casella di controllo Elaborazione multimediale.Under Properties, select the checkbox for Media processing.
  5. Nella casella di testo Punto di ingresso specifica lo spazio dei nomi e il nome della classe per l’attività in background, separati da un punto.In the Entry Point text box, specify the namespace and class name for your background test, separated by a period. Per questo esempio, l'immissione è:For this example, the entry is:
MediaProcessingBackgroundTask.MediaProcessingTask

È quindi necessario aggiungere un riferimento all'attività in background alla tua app in primo piano.Next, you need to add a reference to your background task to your foreground app.

  1. In Esplora soluzioni, nel progetto dell’app in primo piano, fai clic con il pulsante destro del mouse sulla cartella Riferimenti e scegli Aggiungi riferimento.In Solution Explorer, under your foreground app project, right-click the References folder and select Add Reference....
  2. Espandi il nodo Progetti e seleziona Soluzione.Expand the Projects node and select Solution.
  3. Seleziona la casella accanto al tuo progetto di attività in background e fai clic su OK.Check the box next to your background task project and click OK.

Il resto del codice in questo esempio deve essere aggiunto alla tua app in primo piano.The rest of the code in this example should be added to your foreground app. Prima di tutto dovrai aggiungere gli spazi dei nomi seguenti al tuo progetto.First, you will need to add the following namespaces to your project.

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

Aggiungi quindi le variabili membro seguenti che sono necessarie per registrare l'attività in background.Next, add the following member variables that are needed to register the background task.

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

Il metodo helper PickFilesToTranscode usa un FileOpenPicker e un FileSavePicker per aprire i file di input e di output per la transcodifica.The PickFilesToTranscode helper method uses a FileOpenPicker and a FileSavePicker to open the input and output files for transcoding. L'utente potrebbe selezionare file in un percorso non accessibile per la tua app.The user may select files in a location that your app does not have access to. Per assicurarti che l’attività in background possa aprire i file, aggiungili all’elenco FutureAccessList per la tua app.To make sure your background task can open the files, add them to the FutureAccessList for your app.

Infine, impostare le voci per i nomi dei file di input e di output in LocalSettings per l'app.Finally, set entries for the input and output file names in the LocalSettings for your app. L'attività in background recupera i nomi dei file da questa posizione.The background task retrieves the file names from this location.

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

Per registrare l’attività in background, crea un nuovo MediaProcessingTrigger e un nuovo BackgroundTaskBuilder.To register the background task, create a new MediaProcessingTrigger and a new BackgroundTaskBuilder. Imposta il nome del generatore di attività in background in modo da poterlo identificare in seguito.Set the name of the background task builder so that you can identify it later. Impostare TaskEntryPoint sullo stesso spazio dei nomi e sulla stessa stringa del nome di classe usati nel file manifesto.Set the TaskEntryPoint to the same namespace and class name string you used in the manifest file. Imposta la proprietà Trigger sull’istanza MediaProcessingTrigger.Set the Trigger property to the MediaProcessingTrigger instance.

Prima di registrare l’attività, assicurati di annullare la registrazione di qualsiasi attività registrata in precedenza scorrendo la raccolta AllTasks e chiamando Unregister su qualsiasi attività con il nome specificato nella proprietà BackgroundTaskBuilder.Name.Before registering the task, make sure you unregister any previously registered tasks by looping through the AllTasks collection and calling Unregister on any tasks that have the name you specified in the BackgroundTaskBuilder.Name property.

Registrare l'attività in background chiamando Register.Register the background task by calling Register. Registra i gestori per gli eventi Completed e Progress.Register handlers for the Completed and Progress events.

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

Una tipica app registrerà l'attività in background quando l'app viene avviata inizialmente, ad esempio nell'evento OnNavigatedTo .A typical app will register their background task when the app is initially launched, such as in the OnNavigatedTo event.

Avvia l’attività in background chiamando il metodo RequestAsync dell’oggetto MediaProcessingTrigger.Launch the background task by calling the MediaProcessingTrigger object's RequestAsync method. L’oggetto MediaProcessingTriggerResult restituito da questo metodo consente di sapere se l’attività in background è stata avviata e, in caso contrario, di scoprire il motivo del mancato avvio.The MediaProcessingTriggerResult object returned by this method lets you know whether the background task was started successfully, and if not, lets you know why the background task wasn't launched.

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

}

Una tipica app avvierà l'attività in background in risposta all'interazione dell'utente, ad esempio nell'evento Click di un controllo dell'interfaccia utente.A typical app will launch the background task in response to user interaction, such as in the Click event of a UI control.

Il gestore dell’evento OnProgress viene chiamato quando l’attività in background aggiorna lo stato dell’operazione.The OnProgress event handler is called when the background task updates the progress of the operation. Puoi usare questa opportunità per aggiornare l'interfaccia utente con le informazioni sullo stato.You can use this opportunity to update your UI with progress information.

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

Il gestore dell’evento OnCompleted viene chiamato al termine dell’esecuzione dell’attività in background.The OnCompleted event handler is called when the background task has finished running. Si tratta di un'altra opportunità di aggiornare l'interfaccia utente per fornire informazioni sullo stato all'utente.This is another opportunity to update your UI to give status information to the user.

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