Поделиться через


Обработка файлов мультимедиа в фоновом режиме

В этой статье показано, как использовать MediaProcessingTrigger и фоновую задачу для обработки файлов мультимедиа в фоновом режиме.

Пример приложения, описанного в этой статье, позволяет пользователю выбрать входной файл мультимедиа для транскодирования и указать выходной файл для результата перекодирования. Затем запускается фоновая задача для выполнения операции перекодирования. MediaProcessingTrigger предназначена для поддержки множества различных сценариев обработки медиа, помимо транскодирования, включая рендеринг медиа композиций на диск и загрузку обработанных медиа файлов после завершения обработки.

Дополнительные сведения о различных функциях универсальных приложений Windows, используемых в этом примере, см. в следующих примерах:

Создание фоновой задачи обработки мультимедиа

Чтобы добавить фоновую задачу в существующее решение в Microsoft Visual Studio, введите имя для вашего компонента.

  1. В меню файла выберите Добавить, а затем Новый проект....
  2. Выберите тип проекта компонента среды выполнения Windows (универсальная версия Windows).
  3. Введите имя нового проекта компонента. В этом примере используется имя проекта MediaProcessingBackgroundTask.
  4. Нажмите кнопку "ОК".

В Обозревателе решенийщелкните правой кнопкой мыши значок файла "Class1.cs", созданного по умолчанию, и выберите Переименовать. Переименуйте файл в "MediaProcessingTask.cs". Когда Visual Studio просит переименовать все ссылки на этот класс, нажмите кнопку "Да".

В переименованном файле класса добавьте следующие с помощью директив для включения этих пространства имен в проект.

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.

public sealed class MediaProcessingTask : IBackgroundTask
{

Добавьте в класс следующие переменные-члены:

  • IBackgroundTaskInstance, которая будет использоваться для обновления приложения переднего плана с выполнением фоновой задачи.
  • BackgroundTaskDeferral, которая предотвращает завершение фоновой задачи системой при асинхронном выполнении транскодирования мультимедиа.
  • Объект CancelTokenSource , который можно использовать для отмены асинхронной операции перекодирования.
  • Объект MediaTranscoder , который будет использоваться для перекодирования файлов мультимедиа.
IBackgroundTaskInstance backgroundTaskInstance;
BackgroundTaskDeferral deferral;
CancellationTokenSource cancelTokenSource = new CancellationTokenSource();
MediaTranscoder transcoder;

Системные вызовы метод выполнения фоновой задачи при запуске задачи. Задайте объект IBackgroundTask , переданный методу в соответствующую переменную-член. Зарегистрируйте обработчик для события "Отмена", которое будет вызываться, если системе необходимо завершить фоновую задачу. Затем задайте для свойства Progress значение нулю.

Затем вызовите метод GetDeferral объекта фоновой задачи, чтобы получить отсрочку. Это указывает системе не завершать работу вашей задачи, поскольку выполняются асинхронные операции.

Затем вызовите вспомогательный метод TranscodeFileAsync, который определен в следующем разделе. Если это выполнено успешно, вызывается вспомогательный метод, чтобы показать уведомление и оповестить пользователя о завершении перекодирования.

В конце метода запуска вызовите Complete в объекте отсрочки, чтобы сообщить системе, что фоновая задача завершена и может быть завершена.

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 для приложения. Эти значения будут заданы приложением переднего плана. Создайте объект StorageFile для входных и выходных файлов, а затем создайте профиль кодирования для перекодирования.

Вызов PrepareFileTranscodeAsync, передавая входной и выходной файлы, а также профиль кодирования. Объект PrepareTranscodeResult , возвращаемый из этого вызова, позволяет узнать, можно ли выполнять перекодирование. Если свойство CanTranscode имеет значение true, вызовите TranscodeAsync для выполнения операции транскодирования.

Метод AsTask позволяет отслеживать ход выполнения асинхронной операции или отменять ее. Создайте новый объект Progress , указав нужные единицы хода выполнения и имя метода, который будет вызываться для уведомления о текущем ходе выполнения задачи. Передайте объект Progress в метод AsTask вместе с маркером отмены, который позволяет отменить задачу.

  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, установите прогресс выполнения экземпляра фоновой задачи. Это передаст информацию о ходе выполнения в приложение на переднем плане, если оно запущено.

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

Вспомогательный метод sendToastNotification создает новое тост-уведомление, получая XML-документ шаблона для уведомления, с только текстовым содержимым. Текстовый элемент XML тоста настраивается, а затем из XML-документа создается новый объект ToastNotification. Наконец, всплывающее уведомление показывается пользователю вызовом метода 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);
}

В обработчике события Отменено, которое вызывается, когда система отменяет фоновую задачу, можно зарегистрировать ошибку в целях телеметрии.

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

Регистрация и запуск фоновой задачи

Прежде чем запустить фоновую задачу из приложения переднего плана, необходимо обновить файл package.appmanifest приложения переднего плана, чтобы сообщить системе, что приложение использует фоновую задачу.

  1. В обозревателе решений дважды щелкните значок файла Package.appmanifest, чтобы открыть редактор манифеста.
  2. Выберите вкладку "Декларации ".
  3. В доступных объявленийвыберите фоновые задачи и щелкните Добавить.
  4. В разделе Поддерживаемые декларации убедитесь, что элемент фоновых задач выбран. В разделе Свойстваустановите флажок для обработки мультимедиа.
  5. В текстовом поле Entry Point укажите пространство имен и имя класса для фонового теста, разделенные точкой. В этом примере запись следующая:
MediaProcessingBackgroundTask.MediaProcessingTask

Затем необходимо добавить ссылку на фоновую задачу в приложение переднего плана.

  1. В обозревателе решенийв проекте вашего основного приложения щелкните правой кнопкой мыши папку ссылки и выберите Добавить ссылку....
  2. Разверните узел проектов и выберите решение.
  3. Установите флажок рядом с проектом фоновой задачи и нажмите кнопку ОК.

Остальная часть кода в этом примере должна быть добавлена в приложение переднего плана. Сначала необходимо добавить в проект следующие пространства имен.

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

Затем добавьте следующие переменные-члены, необходимые для регистрации фоновой задачи.

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

Вспомогательный метод PickFilesToTranscode использует FileOpenPicker и FileSavePicker для открытия входных и выходных файлов для перекодирования. Пользователь может выбрать файлы в расположении, к которому ваше приложение не имеет доступа. Чтобы убедиться, что фоновая задача может открывать файлы, добавьте их в FutureAccessList для приложения.

Наконец, задайте значения для имен входных и выходных файлов в LocalSettings вашего приложения. Фоновая задача извлекает имена файлов из этого расположения.

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. Задайте имя построителя фоновых задач, чтобы его можно было определить позже. Установите TaskEntryPoint в то же пространство имен и строку имени класса, которые использованы в файле манифеста. Установите свойству Trigger значение экземпляра MediaProcessingTrigger.

Перед регистрацией задачи убедитесь, что вы отменили регистрацию всех ранее зарегистрированных задач, перебирая коллекцию AllTasks и вызывая Unregister для всех задач с именем, указанным в свойстве BackgroundTaskBuilder.Name.

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

Обычное приложение регистрирует свою фоновую задачу при начальном запуске приложения, например в событии OnNavigatedTo.

Запустите фоновую задачу, вызвав метод MediaProcessingTrigger объекта RequestAsync. Объект MediaProcessingTriggerResult , возвращаемый этим методом, позволяет узнать, успешно ли запущена фоновая задача, а если нет, вы узнаете, почему фоновая задача не была запущена.

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

}

Обычное приложение запустит фоновую задачу в ответ на взаимодействие с пользователем, например, в событии нажатия элемента управления пользовательского интерфейса.

Обработчик событий OnProgress вызывается, когда фоновая задача обновляет ход выполнения операции. Эту возможность можно использовать для обновления пользовательского интерфейса с информацией о ходе выполнения.

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

Обработчик событий OnCompleted вызывается после завершения фоновой задачи. Это еще одна возможность обновить пользовательский интерфейс, чтобы предоставить пользователю сведения о состоянии.

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