Procesar archivos multimedia en segundo planoProcess media files in the background

En este artículo se muestra cómo usar MediaProcessingTrigger y una tarea en segundo plano para procesar archivos multimedia en segundo plano.This article shows you how to use the MediaProcessingTrigger and a background task to process media files in the background.

La aplicación de ejemplo descrita en este artículo permite al usuario seleccionar un archivo multimedia de entrada para transcodificar y especificar un archivo de salida para el resultado de la transcodificación.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. A continuación, se inicia una tarea en segundo plano para realizar la operación de transcodificación.Then, a background task is launched to perform the transcoding operation. La clase MediaProcessingTrigger se usa para admitir abundantes y diversos escenarios de procesamiento de medios entre los que se incluyen, además de la transcodificación, la representación de composiciones multimedia en el disco y la carga de archivos multimedia procesados una vez finalizado el procesamiento.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.

Para obtener información más detallada sobre las diferentes características de la aplicación universal de Windows que se usa en esta muestra, consulta:For more detailed information on the different Universal Windows app features utilized in this sample, see:

Crear una tarea en segundo plano de procesamiento de multimediaCreate a media processing background task

Para agregar una tarea en segundo plano a la solución existente en Microsoft Visual Studio, escribe un nombre para la composición.To add a background task to your existing solution in Microsoft Visual Studio, Enter a name for your comp

  1. En el menú archivo , seleccione Agregar y, a continuación, nuevo proyecto....From the File menu, select Add and then New Project....
  2. Seleccione el tipo de proyecto Windows Runtime componente (Windows universal).Select the project type Windows Runtime Component (Universal Windows).
  3. Escribe un nombre para el nuevo proyecto de componente.Enter a name for your new component project. En este ejemplo se usa el nombre de proyecto MediaProcessingBackgroundTask.This example uses the project name MediaProcessingBackgroundTask.
  4. Haga clic en Aceptar.Click OK.

En el Explorador de soluciones, haz clic con el botón secundario en el icono del archivo "Class1.cs" que se crea de forma predeterminada, y selecciona Cambiar nombre.In Solution Explorer, right-click the icon for the "Class1.cs" file that is created by default and select Rename. Cambia el nombre del archivo a "MediaProcessingTask.cs".Rename the file to "MediaProcessingTask.cs". Cuando Visual Studio le pregunte si desea cambiar el nombre de todas las referencias a esta clase, haga clic en .When Visual Studio asks if you want to rename all of the references to this class, click Yes.

En el archivo de clase que ha cambiado de nombre, agrega las siguientes directivas de tipo using para incluir estos espacios de nombres en el proyecto.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;

Actualice su declaración de clase para que la clase herede de IBackgroundTask.Update your class declaration to make your class inherit from IBackgroundTask.

public sealed class MediaProcessingTask : IBackgroundTask
{

Agrega las siguientes variables de miembro a tu clase:Add the following member variables to your class:

  • Una interfaz IBackgroundTaskInstance que se usará para actualizar la aplicación en primer plano mediante el progreso de la tarea en segundo plano.An IBackgroundTaskInstance that will be used to update the foreground app with the progress of the background task.
  • Una clase BackgroundTaskDeferral que evitará que el sistema cierre la tarea en segundo plano mientras se realiza la transcodificación de archivos multimedia de forma asincrónica.A BackgroundTaskDeferral that keeps the system from shutting down your background task while media transcoding is being performed asynchronously.
  • Un objeto CancellationTokenSource que se puede usar para cancelar la operación de transcodificación asincrónica.A CancellationTokenSource object that can be used to cancel the asynchronous transcoding operation.
  • El objeto MediaTranscoder, que se usará para transcodificar archivos multimedia.The MediaTranscoder object that will be used to transcode media files.
IBackgroundTaskInstance backgroundTaskInstance;
BackgroundTaskDeferral deferral;
CancellationTokenSource cancelTokenSource = new CancellationTokenSource();
MediaTranscoder transcoder;

Una vez hecho esto, el sistema llamará al método Run de una tarea en segundo plano cuando se inicie la tarea.The system calls Run method of a background task when the task is launched. Debes establecer el objeto IBackgroundTask que se pasó al método de la variable de miembro correspondiente.Set the IBackgroundTask object passed into the method to the corresponding member variable. A continuación, registra un controlador para el evento Canceled, que se producirá si el sistema lo necesita para cerrar la tarea en segundo plano.Register a handler for the Canceled event, which will be raised if the system needs to shut down the background task. Seguidamente, establece la propiedad Progress en cero.Then, set the Progress property to zero.

Deberás llamar al método GetDeferral del objeto de la tarea en segundo plano para obtener un aplazamiento.Next, call the background task object's GetDeferral method to obtain a deferral. Esta acción le dice al sistema que no cierre la tarea porque estás realizando operaciones asincrónicas.This tells the system not to shut down your task because you are performing asynchronous operations.

El siguiente paso es llamar al método auxiliar TranscodeFileAsync, que se define en la siguiente sección.Next, call the helper method TranscodeFileAsync, which is defined in the next section. Si esto se completa correctamente, se llama a un método auxiliar para que inicie una notificación del sistema que avisará al usuario de que se ha completado la transcodificación.If that completes successfully, a helper method is called to launch a toast notification to alert the user that transcoding is complete.

Al final del método Run, llama al método Complete del objeto de aplazamiento para que el sistema sepa que la tarea en segundo plano se ha completado y que se puede finalizar.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();
}

En el método auxiliar TranscodeFileAsync, los nombres de los archivos de entrada y salida de las operaciones de transcodificación se recuperan de la propiedad LocalSettings de la aplicación.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. La aplicación en primer plano se encargará de establecer estos valores.These values will be set by your foreground app. Crea un objeto StorageFile para los archivos de entrada y salida y, a continuación, crea un perfil de codificación para usar en la transcodificación.Create a StorageFile object for the input and output files and then create an encoding profile to use for transcoding.

Llama a PrepareFileTranscodeAsync y pasa el archivo de entrada, el archivo de salida y el perfil de codificación.Call PrepareFileTranscodeAsync, passing in the input file, output file, and encoding profile. Recuerda que el objeto PrepareTranscodeResult que devuelve esta llamada te permite saber si se puede realizar la transcodificación.The PrepareTranscodeResult object returned from this call lets you know if transcoding can be performed. Si resulta que la propiedad CanTranscode es "true", llama a TranscodeAsync para realizar la operación de transcodificación.If the CanTranscode property is true, call TranscodeAsync to perform the transcoding operation.

El método AsTask te permite realizar un seguimiento del progreso de la operación asincrónica o cancelarla.The AsTask method enables you to track the progress the asynchronous operation or cancel it. Puedes crear un objeto Progress y especificar las unidades de progreso que quieras y el nombre del método al que se llamará para notificarte el progreso actual de la tarea.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. A continuación, pasa el objeto Progress al método AsTask junto con el token de cancelación que te permitirá cancelar la tarea.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;
      }
  }

En el método que usaste para crear el objeto Progress en el paso anterior, establece el progreso de la instancia de la tarea en segundo plano.In the method you used to create the Progress object in the previous step, Progress, set the progress of the background task instance. Esta acción te permitirá pasar el progreso a la aplicación en primer plano, si se está ejecutando.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;
}

El método auxiliar SendToastNotification crea una nueva notificación del sistema mediante la obtención de un documento XML de plantilla de una notificación del sistema que solo tiene contenido de texto.The SendToastNotification helper method creates a new toast notification by getting a template XML document for a toast that only has text content. De esta manera se establecerá el elemento de texto de la notificación del sistema XML y se creará un nuevo objeto ToastNotification a partir del documento XML.The text element of the toast XML is set and then a new ToastNotification object is created from the XML document. Por último, puedes mostrar al usuario la notificación del sistema mediante una llamada a 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);
}

En el controlador para el evento Canceled, que se llama cuando el sistema cancela la tarea en segundo plano, puedes registrar el error con fines de telemetría.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());
}

Registrar y cargar la tarea en segundo planoRegister and launch the background task

Para poder iniciar la tarea en segundo plano desde la aplicación en primer plano, debes actualizar el archivo Package.appmanifest de la aplicación en primer plano para que el sistema sepa que esta aplicación usa una tarea en segundo plano.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. En el Explorador de soluciones, haz doble clic en el icono del archivo Package.appmanifest para abrir el editor del manifiesto.In Solution Explorer, double-click the Package.appmanifest file icon to open the manifest editor.
  2. Selecciona la pestaña Declaraciones.Select the Declarations tab.
  3. En Declaraciones disponibles, selecciona Tareas en segundo plano y haz clic en Agregar.From Available Declarations, select Background Tasks and click Add.
  4. En Declaraciones admitidas, asegúrate de que esté seleccionado el elemento Tareas en segundo plano.Under Supported Declarations make sure that the Background Tasks item is selected. En Propiedades, selecciona la casilla de verificación Procesamiento multimedia.Under Properties, select the checkbox for Media processing.
  5. En el cuadro de texto Punto de entrada, especifica el espacio de nombres y el nombre de clase para la prueba en segundo plano, separados por un punto.In the Entry Point text box, specify the namespace and class name for your background test, separated by a period. Para este ejemplo, la entrada es:For this example, the entry is:
MediaProcessingBackgroundTask.MediaProcessingTask

A continuación, debes agregar una referencia a la tarea en segundo plano a tu aplicación en primer plano.Next, you need to add a reference to your background task to your foreground app.

  1. En Explorador de soluciones, en el proyecto de la aplicación de primer plano, haga clic con el botón derecho en la carpeta referencias y seleccione Agregar referencia....In Solution Explorer, under your foreground app project, right-click the References folder and select Add Reference....
  2. Expande el nodo Proyectos y selecciona Solución.Expand the Projects node and select Solution.
  3. Active la casilla situada junto a su proyecto de tarea en segundo plano y haga clic en Aceptar.Check the box next to your background task project and click OK.

El resto del código de este ejemplo debe agregarse a la aplicación en primer plano.The rest of the code in this example should be added to your foreground app. En primer lugar, debes agregar los siguientes espacios de nombres al proyecto.First, you will need to add the following namespaces to your project.

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

A continuación, agrega las siguientes variables de miembro que son necesarias para registrar la tarea en segundo plano.Next, add the following member variables that are needed to register the background task.

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

El método auxiliar PickFilesToTranscode usa las clases FileOpenPicker y FileSavePicker para abrir los archivos de entrada y salida de la transcodificación.The PickFilesToTranscode helper method uses a FileOpenPicker and a FileSavePicker to open the input and output files for transcoding. Gracias a esto, el usuario puede seleccionar archivos en una ubicación a la que tu aplicación no tiene acceso.The user may select files in a location that your app does not have access to. Para asegurarte de que tu tarea en segundo plano pueda abrir los archivos, agrégalos a la propiedad FutureAccessList de la aplicación.To make sure your background task can open the files, add them to the FutureAccessList for your app.

Finalmente, establece los nombres de archivo de entrada y de salida en la propiedad LocalSettings de la aplicación.Finally, set entries for the input and output file names in the LocalSettings for your app. La tarea en segundo plano recupera los nombres de archivo de esta ubicación.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;
}

Para registrar la tarea en segundo plano, crea las clases MediaProcessingTrigger y BackgroundTaskBuilder.To register the background task, create a new MediaProcessingTrigger and a new BackgroundTaskBuilder. A continuación, establece el nombre del generador de la tarea en segundo plano para poder identificarla más tarde.Set the name of the background task builder so that you can identify it later. Establece la propiedad TaskEntryPoint en la misma cadena de nombres de clase y espacio de nombres que usaste en el archivo de manifiesto.Set the TaskEntryPoint to the same namespace and class name string you used in the manifest file. Una vez hecho esto, establece la propiedad Trigger en la instancia MediaProcessingTrigger.Set the Trigger property to the MediaProcessingTrigger instance.

Antes de registrar la tarea, asegúrate de anular el registro de cualquier tarea registrada previamente; para ello, revisa la colección AllTasks y llama al método Unregister de las tareas que tengan el nombre que especificaste en la propiedad 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.

Registra la tarea en segundo plano llamando al método Register.Register the background task by calling Register. Seguidamente, registra los controladores de los eventos Completed y 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 aplicación típica registrará su tarea en segundo plano cuando la aplicación se inicie inicialmente, como en el evento OnNavigatedTo .A typical app will register their background task when the app is initially launched, such as in the OnNavigatedTo event.

Inicia la tarea en segundo plano llamando al método RequestAsync del objeto MediaProcessingTrigger.Launch the background task by calling the MediaProcessingTrigger object's RequestAsync method. El objeto MediaProcessingTriggerResult que devuelve este método te permitirá saber si la tarea en segundo plano se inició correctamente y, si no es así, te permitirá saber el por qué.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 aplicación típica iniciará la tarea en segundo plano en respuesta a la interacción del usuario, como en el evento de clic de un control de IU.A typical app will launch the background task in response to user interaction, such as in the Click event of a UI control.

Se llama al controlador de eventos OnProgress cuando la tarea en segundo plano actualiza el progreso de la operación.The OnProgress event handler is called when the background task updates the progress of the operation. Puedes usar esta oportunidad para actualizar la interfaz de usuario con la información de progreso.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);
}

Se llama al controlador de eventos OnCompleted cuando la tarea en segundo plano termina de ejecutarse.The OnCompleted event handler is called when the background task has finished running. Esta es otra oportunidad para actualizar la interfaz de usuario y proporcionar información de estado al usuario.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");
}