在背景處理媒體檔案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.

本文中所描述的 App 範例可讓使用者選取要轉碼的輸入媒體檔案,以及指定轉碼結果的輸出檔案。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) **的專案類型 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;

更新您的類別宣告,讓您的類別繼承自 IBackgroundTaskUpdate your class declaration to make your class inherit from IBackgroundTask.

public sealed class MediaProcessingTask : IBackgroundTask
{

將下列成員變數新增到您的類別:Add the following member variables to your class:

  • IBackgroundTaskInstance,將用來以背景工作的進度更新前景 App。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 協助程式方法中,轉碼作業的輸入檔案與輸出檔案的檔案名稱會從您 app 的 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. 這些值將由您的前景 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. 這會將進度傳入前景 app (如果正在執行)。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 的文字項目,然後從 XML 文件建立新的 ToastNotification 物件。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

您必須先更新您前景 App 的 Package.appmanifest 檔案讓系統知道您的 App 使用背景工作,您才能從前景 App 啟動背景工作。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. 方案總管中,按兩下 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

接著,您必須將背景工作參考新增到前景 app。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.

此範例中的其餘程式碼,應該新增到您的前景 App。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 協助程式方法使用 FileOpenPickerFileSavePicker,開啟轉碼的輸入檔案與輸出檔案。The PickFilesToTranscode helper method uses a FileOpenPicker and a FileSavePicker to open the input and output files for transcoding. 使用者可能會選取您的 app 無法存取的位置中的檔案。The user may select files in a location that your app does not have access to. 若要確定您的背景工作可以開啟檔案,請將這些檔案新增到您的 App 的 FutureAccessListTo make sure your background task can open the files, add them to the FutureAccessList for your app.

最後,在您 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 和新的 BackgroundTaskBuilderTo 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 集合執行迴圈,並且對您在 BackgroundTaskBuilder.Name 屬性中指定名稱的任何工作呼叫 Unregister,取消登錄任何之前已登錄的工作。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. 註冊 CompletedProgress 事件的處理常式。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);
        }
    }

}

一般的應用程式會啟動背景工作以回應使用者互動,例如在 UI 控制項的 Click 事件中。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. 您可以利用這個機會以進度資訊更新您的 UI。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. 這是另一個可以更新 UI 以提供狀態資訊給使用者的機會。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");
}