Обработка файлов мультимедиа в фоновом режимеProcess media files in the background

В данной статье показано, как использовать MediaProcessingTrigger и фоновую задачу для обработки файлов мультимедиа в фоновом режиме.This article shows you how to use the MediaProcessingTrigger and a background task to process media files in the background.

Пример приложения, описанный в этой статье, позволяет пользователю выбрать входной файл мультимедиа для перекодирования и определить выходной файл для результата перекодирования.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. Затем запускается фоновая задача для выполнения операции перекодирования.Then, a background task is launched to perform the transcoding operation. MediaProcessingTrigger предназначен для поддержки многих других сценариев обработки мультимедиа, кроме перекодирования, в том числе записи мультимедийных композиций на диск и передачи обработанных файлов мультимедиа после завершения обработки.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.

Дополнительные сведения о различных функциях универсального приложения для Windows, применяемых в этом примере, см. в следующих статьях:For more detailed information on the different Universal Windows app features utilized in this sample, see:

Создание фоновой задачи обработки мультимедиаCreate a media processing background task

Для добавления фоновой задачи в существующее решение в Microsoft Visual Studio, введите имя для вашего компьютера.To add a background task to your existing solution in Microsoft Visual Studio, Enter a name for your comp

  1. В меню Файл выберите команду Добавить и затем Новый проект....From the File menu, select Add and then New Project....
  2. Выберите тип проекта Компонент среды выполнения Windows (универсальные приложения).Select the project type Windows Runtime Component (Universal Windows).
  3. Введите имя вашего нового проекта компонента.Enter a name for your new component project. В этом примере используется имя проекта MediaProcessingBackgroundTask.This example uses the project name MediaProcessingBackgroundTask.
  4. Щелкните ОК.Click OK.

В обозревателе решенийщелкните правой кнопкой мыши значок созданного по умолчанию файла Class1.cs и выберите пункт Переименовать.In Solution Explorer, right-click the icon for the "Class1.cs" file that is created by default and select Rename. Переименуйте файл в MediaProcessingTask.cs.Rename the file to "MediaProcessingTask.cs". Когда Visual Studio запросит, требуется ли переименовать все ссылки на этот класс, выберите Да.When Visual Studio asks if you want to rename all of the references to this class, click Yes.

В переименованном файле класса добавьте следующие директивы using, чтобы включить эти пространства имен в проект.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;

Обновите свое объявление класса, чтобы класс наследовал от IBackgroundTask.Update your class declaration to make your class inherit from IBackgroundTask.

public sealed class MediaProcessingTask : IBackgroundTask
{

Добавьте следующие переменные-члены в класс.Add the following member variables to your class:

  • IBackgroundTaskInstance, который будет использоваться для обновления сведений о ходе выполнения фоновой задачи в приложении переднего плана.An IBackgroundTaskInstance that will be used to update the foreground app with the progress of the background task.
  • BackgroundTaskDeferral, который не позволяет системе завершить фоновую задачу при выполнении перекодирования мультимедиа в асинхронном режиме.A BackgroundTaskDeferral that keeps the system from shutting down your background task while media transcoding is being performed asynchronously.
  • Объект CancellationTokenSource, который можно использовать для отмены асинхронной операции перекодирования.A CancellationTokenSource object that can be used to cancel the asynchronous transcoding operation.
  • Объект MediaTranscoder, который будет использоваться для перекодирования файлов мультимедиа.The MediaTranscoder object that will be used to transcode media files.
IBackgroundTaskInstance backgroundTaskInstance;
BackgroundTaskDeferral deferral;
CancellationTokenSource cancelTokenSource = new CancellationTokenSource();
MediaTranscoder transcoder;

Система вызывает метод Run фоновой задачи, когда эта задача запускается.The system calls Run method of a background task when the task is launched. Задайте для объекта IBackgroundTask, передаваемого в метод, соответствующую переменную-член.Set the IBackgroundTask object passed into the method to the corresponding member variable. Зарегистрируйте обработчик для события Canceled, которое возникает, когда системе нужно завершить работу фоновой задачи.Register a handler for the Canceled event, which will be raised if the system needs to shut down the background task. После этого установите для свойства Progress нулевое значение.Then, set the Progress property to zero.

Затем вызовите метод объекта GetDeferral фоновой задачи для получения отсроченного объекта.Next, call the background task object's GetDeferral method to obtain a deferral. Это сообщает системе, что завершать задачу не нужно, поскольку выполняются асинхронные операции.This tells the system not to shut down your task because you are performing asynchronous operations.

Затем вызовите вспомогательный метод TranscodeFileAsync, который определяется в следующем разделе.Next, call the helper method TranscodeFileAsync, which is defined in the next section. Если это успешно выполнено, вызывается вспомогательный метод, чтобы открыть всплывающее уведомление для оповещения пользователя о завершении перекодирования.If that completes successfully, a helper method is called to launch a toast notification to alert the user that transcoding is complete.

В конце метода Run вызовите Complete для отсроченного объекта, чтобы сообщить системе, что фоновая задача выполнена и может быть завершена.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();
}

Во вспомогательном методе TranscodeFileAsync имена входного и выходного файлов для операций перекодирования извлекаются из LocalSettings для приложения.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. Эти значения устанавливаются вашим приложением переднего плана.These values will be set by your foreground app. Создайте объект StorageFile для входного и выходного файлов, а затем создайте профиль кодирования для использования во время перекодирования.Create a StorageFile object for the input and output files and then create an encoding profile to use for transcoding.

Вызовите PrepareFileTranscodeAsync, передав входной файл, выходной файл и профиль кодирования.Call PrepareFileTranscodeAsync, passing in the input file, output file, and encoding profile. Объект PrepareTranscodeResult, возвращенный из этого вызова, позволяет узнать, можно ли выполнить перекодирование.The PrepareTranscodeResult object returned from this call lets you know if transcoding can be performed. Если свойство CanTranscode имеет значение true, вызовите TranscodeAsync для выполнения операции перекодирования.If the CanTranscode property is true, call TranscodeAsync to perform the transcoding operation.

Метод AsTask позволяет отслеживать ход выполнения асинхронной операции или отменить ее.The AsTask method enables you to track the progress the asynchronous operation or cancel it. Создайте новый объект Progress, указав нужные единицы хода выполнения и имя метода, который вызывается, чтобы уведомлять вас о ходе выполнения задачи.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. Передайте объект Progress в метод AsTask вместе с токеном отмены, который позволяет отменить задачу.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;
      }
  }

В методе, использовавшемся для создания объекта Progress в предыдущем шаге Progress, задайте ход выполнения экземпляра фоновой задачи.In the method you used to create the Progress object in the previous step, Progress, set the progress of the background task instance. Это позволит передать данные о ходе выполнения в приложение переднего плана, если оно запущено.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;
}

Вспомогательный метод SendToastNotification создает новое всплывающее уведомление, получая для него XML-документ шаблона, который содержит только текстовое содержимое.The SendToastNotification helper method creates a new toast notification by getting a template XML document for a toast that only has text content. Задается текстовый элемент XML-кода всплывающего уведомления, а затем создается новый объект ToastNotification из XML-документа.The text element of the toast XML is set and then a new ToastNotification object is created from the XML document. И наконец, всплывающее уведомление отображается пользователю путем вызова метода 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);
}

В обработчике события Canceled, который вызывается, когда система отменяет фоновую задачу, можно записать ошибку в журнал в целях телеметрии.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());
}

Регистрация и запуск фоновой задачиRegister and launch the background task

Прежде чем из приложения переднего плана можно будет запустить фоновую задачу, необходимо обновить файл Package.appmanifest приложения переднего плана, чтобы сообщить системе, что приложение использует фоновую задачу.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. Дважды щелкните значок файла Package.appmanifest в обозревателе решений, чтобы открыть редактор манифестов.In Solution Explorer, double-click the Package.appmanifest file icon to open the manifest editor.
  2. Выберите вкладку Объявления.Select the Declarations tab.
  3. В списке Доступные объявления выберите Фоновые задачи и щелкните Добавить.From Available Declarations, select Background Tasks and click Add.
  4. Убедитесь, что в области Поддерживаемые объявления выбран элемент Фоновые задачи.Under Supported Declarations make sure that the Background Tasks item is selected. В области Свойства установите флажок Обработка мультимедиа.Under Properties, select the checkbox for Media processing.
  5. В текстовом поле Точка входа укажите пространство имен и имя класса для фонового теста, разделив их запятой.In the Entry Point text box, specify the namespace and class name for your background test, separated by a period. В этом примере данная запись имеет следующий вид:For this example, the entry is:
MediaProcessingBackgroundTask.MediaProcessingTask

Далее необходимо добавить ссылку на фоновую задачу в приложение переднего плана.Next, you need to add a reference to your background task to your foreground app.

  1. В области вашего проекта приложения переднего плана в обозревателе решений щелкните правой кнопкой мыши папку Ссылки и выберите пункт Добавить ссылку....In Solution Explorer, under your foreground app project, right-click the References folder and select Add Reference....
  2. Разверните узел Проекты и выберите Решение.Expand the Projects node and select Solution.
  3. Установите флажок рядом с проектом фоновой задачи и нажмите кнопку ОК.Check the box next to your background task project and click OK.

Оставшуюся часть кода в этом примере следует добавить в ваше приложение переднего плана.The rest of the code in this example should be added to your foreground app. Для начала необходимо добавить в проект следующие пространства имен.First, you will need to add the following namespaces to your project.

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

Далее добавьте следующие переменные-члены, необходимые для регистрации фоновой задачи.Next, add the following member variables that are needed to register the background task.

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

Вспомогательный метод PickFilesToTranscode использует FileOpenPicker и FileSavePicker, чтобы открыть входной и выходной файлы для перекодирования.The PickFilesToTranscode helper method uses a FileOpenPicker and a FileSavePicker to open the input and output files for transcoding. Пользователь может выбрать файлы в недоступном для приложения расположении.The user may select files in a location that your app does not have access to. Чтобы убедиться, что ваша фоновая задача может открыть такие файлы, добавьте их в FutureAccessList для своего приложения.To make sure your background task can open the files, add them to the FutureAccessList for your app.

В заключение задайте записи для имен входного и выходного файлов в LocalSettings для приложения.Finally, set entries for the input and output file names in the LocalSettings for your app. Фоновая задача извлекает имена файлов из этого расположения.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;
}

Для регистрации фоновой задачи создайте новый MediaProcessingTrigger и новый BackgroundTaskBuilder.To register the background task, create a new MediaProcessingTrigger and a new BackgroundTaskBuilder. Задайте имя построителя фоновой задачи, чтобы позднее его можно было идентифицировать.Set the name of the background task builder so that you can identify it later. Задайте для TaskEntryPoint ту же строку пространства имен и имени класса, которую использовали в файле манифеста.Set the TaskEntryPoint to the same namespace and class name string you used in the manifest file. Задайте для свойства Trigger значение экземпляра MediaProcessingTrigger.Set the Trigger property to the MediaProcessingTrigger instance.

Перед регистрацией задачи убедитесь, что отменена регистрация всех ранее зарегистрированных задач, пройдя в цикле по коллекции AllTasks и вызывая Unregister для всех задач с указанным в свойстве 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.

Зарегистрируйте фоновую задачу, вызвав Register.Register the background task by calling Register. Зарегистрируйте обработчики для событий Completed и 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;
}

Типичное приложение регистрирует свою фоновую задачу при первоначальном запуске приложения, например в событии OnNavigatedTo .A typical app will register their background task when the app is initially launched, such as in the OnNavigatedTo event.

Запустите фоновую задачу, вызвав метод MediaProcessingTrigger объекта RequestAsync.Launch the background task by calling the MediaProcessingTrigger object's RequestAsync method. Объект MediaProcessingTriggerResult, возвращенный этим методом, позволяет узнать, была ли фоновая задача успешно запущена, а также определить причину, если эта фоновая задача не была запущена.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);
        }
    }

}

Типичное приложение запустит фоновую задачу в ответ на взаимодействие с пользователем, например в случае события щелчка элемента управления пользовательского интерфейса.A typical app will launch the background task in response to user interaction, such as in the Click event of a UI control.

Обработчик событий OnProgress вызывается каждый раз, когда фоновая задача обновляет данные о ходе выполнения операции.The OnProgress event handler is called when the background task updates the progress of the operation. Вы можете использовать эту возможность, чтобы обновить пользовательский интерфейс данными о ходе выполнения.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);
}

Обработчик событий OnCompleted вызывается каждый раз, когда фоновая задача завершает работу.The OnCompleted event handler is called when the background task has finished running. Это еще одна возможность обновить пользовательский интерфейс для предоставления сведений о состоянии пользователю.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");
}