백그라운드에서 미디어 파일 처리

이 문서에서는 MediaProcessingTrigger 및 백그라운드 작업을 사용하여 백그라운드에서 미디어 파일을 처리하는 방법을 보여 줍니다.

이 문서에 설명된 예제 앱을 사용하여 코드 변환할 입력 미디어 파일을 선택하고 코드 변환 결과를 기록할 출력 파일을 지정할 수 있습니다. 그런 다음 코드 변환 작업을 수행하도록 백그라운드 작업을 시작합니다. MediaProcessingTrigger에서는 코드 변환 이외의 여러 다른 미디어 처리 시나리오(예: 디스크로 미디어 컴포지션 렌더링 및 처리를 완료한 후 처리된 미디어 파일 업로드)를 지원합니다.

이 샘플에서 활용하는 여러 다른 유니버설 Windows 앱 기능에 대한 자세한 내용은 다음을 참조하세요.

미디어 처리 백그라운드 작업 만들기

Microsoft Visual Studio에서 기존 솔루션에 백그라운드 작업을 추가하려면 구성 요소의 이름을 입력합니다.

  1. 파일 메뉴에서 추가를 선택한 다음 새 프로젝트...를 선택합니다.
  2. Windows 런타임 구성 요소(유니버설 Windows) 프로젝트 유형을 선택합니다.
  3. 새 구성 요소 프로젝트의 이름을 입력합니다. 이 예제에서는 MediaProcessingBackgroundTask 프로젝트 이름을 사용합니다.
  4. 확인을 클릭합니다.

솔루션 탐색기에서 기본적으로 생성되는 "Class1.cs" 파일의 아이콘을 마우스 오른쪽 단추로 클릭하고 이름 바꾸기를 선택합니다. 파일의 이름을 "MediaProcessingTask.cs"로 바꿉니다. Visual Studio에서 이 클래스의 모든 참조 이름을 바꾸려는지 물으면 를 클릭합니다.

이름이 바뀐 클래스 파일에서 다음 using 지시문을 추가하여 프로젝트에 네임스페이스를 포함시킵니다.

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입니다.
  • 비동기 코드 변환 작업을 취소하는 데 사용할 수 있는 CancellationTokenSource 개체입니다.
  • 미디어 파일의 코드를 변환하는 데 사용할 MediaTranscoder 개체입니다.
IBackgroundTaskInstance backgroundTaskInstance;
BackgroundTaskDeferral deferral;
CancellationTokenSource cancelTokenSource = new CancellationTokenSource();
MediaTranscoder transcoder;

작업이 시작되면 시스템에서 백그라운드 작업의 Run 메서드를 호출합니다. 메서드에 전달된 IBackgroundTask 개체를 해당 멤버 변수로 설정합니다. 시스템에서 백그라운드 작업을 종료해야 하는 경우 실행될 Canceled 이벤트의 처리기를 등록합니다. 그런 다음 Progress 속성을 0으로 설정합니다.

다음으로 백그라운드 작업 개체의 GetDeferral 메서드를 호출하여 지연을 확보합니다. 이를 통해 현재 비동기 작업을 수행 중이므로 작업을 종료하지 않아야 한다고 시스템에 지시합니다.

이제 다음 섹션에 정의되어 있는 도우미 메서드 TranscodeFileAsync를 호출합니다. 호출에 성공하면 사용자에게 코드 변환이 완료되었음을 알리기 위해 도우미 메서드를 호출하여 알림 메시지를 실행합니다.

Run 메서드 종료 시 지연 개체에서 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);
}

시스템이 백그라운드 작업을 취소할 때 호출되는 Canceled 이벤트에 대한 처리기에서 원격 분석을 위해 오류를 로깅할 수 있습니다.

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. 진입점 입력란에서 백그라운드 테스트의 네임스페이스와 클래스 이름을 마침표로 구분하여 지정합니다. 이 예제에서는 다음과 같이 입력합니다.
MediaProcessingBackgroundTask.MediaProcessingTask

다음으로 백그라운드 작업에 대한 참조를 포그라운드 앱에 추가해야 합니다.

  1. 솔루션 탐색기의 포그라운드 앱 프로젝트에서 참조 폴더를 마우스 오른쪽 단추로 클릭하고 참조 추가...를 선택합니다.
  2. 프로젝트 노드를 확장하고 솔루션을 선택합니다.
  3. 백그라운드 작업 프로젝트 옆의 상자를 선택하고 확인을 클릭합니다.

이 예의 나머지 코드는 포그라운드 앱에 추가해야 합니다. 먼저 다음 네임스페이스를 프로젝트에 추가해야 합니다.

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

다음으로 백그라운드 작업을 등록하는 데 필요한 다음 멤버 변수를 추가합니다.

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

PickFilesToTranscode 도우미 메서드에서는 FileOpenPickerFileSavePicker를 사용하여 코드 변환에 사용할 입력 및 출력 파일을 엽니다. 앱에서 액세스할 수 없는 위치에 있는 파일을 선택했을 가능성이 있습니다. 백그라운드 작업에서 파일을 열 수 있도록 앱의 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 컬렉션에서 루프 실행하고 BackgroundTaskBuilder.Name 속성에 지정한 이름의 작업에서 Unregister를 호출하여 이전에 등록한 작업의 등록을 취소하세요.

Register를 호출하여 백그라운드 작업을 등록합니다. CompletedProgress 이벤트에 대한 처리기를 등록합니다.

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

}

일반적인 앱은 UI 컨트롤의 클릭 이벤트와 같이 사용자 상호 작용에 대한 응답으로 백그라운드 작업을 시작합니다.

백그라운드 작업에서 조작의 진행률을 업데이트할 때 OnProgress 이벤트 처리기가 호출됩니다. 이때 진행률 정보로 UI를 업데이트할 수 있습니다.

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

백그라운드 작업에서 실행을 완료하면 OnCompleted 이벤트 처리기가 호출됩니다. 이때에도 사용자에게 상태 정보를 제공하도록 UI를 업데이트할 수 있습니다.

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