Elaborare file multimediali in background
Questo articolo mostra come usare il MediaProcessingTrigger e un'attività in background per elaborare i file multimediali in background.
L'app di esempio descritta in questo articolo consente all'utente di selezionare un file multimediale di input da transcodificare e specificare un file di output per il risultato della transcodifica. Viene quindi avviata un'attività in background per eseguire l'operazione di transcodifica. Il MediaProcessingTrigger è progettato per supportare molti scenari di elaborazione multimediale diversi oltre alla transcodifica, tra cui il rendering delle composizioni multimediali su disco e il caricamento di file multimediali elaborati al termine dell'elaborazione.
Per informazioni più dettagliate sulle diverse funzionalità dell'app di Windows universale usate in questo esempio, vedere:
- Eseguire la transcodifica di file multimediali
- Avvio, ripresa e attività in background
- Riquadri e notifiche
Creare un'attività in background di elaborazione multimediale
Per aggiungere un'attività in background alla soluzione esistente in Microsoft Visual Studio, immettere un nome per il comp
- Dal menu File, selezionare Aggiungere e quindi Nuovo progetto...
- Selezionare il tipo di progetto Componente Windows Runtime (Windows universale).
- Immettere un nome per il nuovo progetto di componente. In questo esempio viene usato il nome del progetto MediaProcessingBackgroundTask.
- Fare clic su OK.
In Esplora soluzioni, fare clic con il pulsante destro del mouse sull'icona per il file "Class1.cs" creato per impostazione predefinita e selezionare Rinomina. Rinominare il file in "MediaProcessingTask.cs". Quando Visual Studio chiede se si desidera rinominare tutti i riferimenti a questa classe, fare clic su Sì.
Nel file di classe rinominato aggiungere le direttive seguenti usando per includere questi spazi dei nomi nel progetto.
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;
Aggiornare la dichiarazione di classe per fare in modo che la classe erediti da IBackgroundTask.
public sealed class MediaProcessingTask : IBackgroundTask
{
Aggiungere le variabili membro seguenti alla classe:
- Una IBackgroundTaskInstance che verrà usata per aggiornare l'app in primo piano con lo stato di avanzamento dell'attività in background.
- Una BackgroundTaskDeferral che impedisce al sistema di arrestare l'attività in background mentre la transcodifica multimediale viene eseguita in modo asincrono.
- Un oggetto CancellationTokenSource che può essere utilizzato per annullare l'operazione di transcodifica asincrona.
- Un oggetto MediaTranscoder che verrà utilizzato per transcodificare i file multimediali.
IBackgroundTaskInstance backgroundTaskInstance;
BackgroundTaskDeferral deferral;
CancellationTokenSource cancelTokenSource = new CancellationTokenSource();
MediaTranscoder transcoder;
Il sistema chiama il metodo Run di un'attività in background all'avvio dell'attività. Impostare l'oggetto IBackgroundTask passato al metodo sulla variabile membro corrispondente. Registrare un gestore per l'evento Annullato, che verrà generato se il sistema deve arrestare l'attività in background. Impostare quindi la proprietà Progress su zero.
Chiamare quindi il metodo GetDeferral dell'oggetto attività in background per ottenere un differimento. Ciò indica al sistema di non arrestare l'attività perché si stanno eseguendo operazioni asincrone.
Chiamare quindi il metodo di supporto TranscodeFileAsync, definito nella sezione successiva. Se l'operazione viene completata correttamente, viene chiamato un metodo di supporto per avviare una notifica di tipo avviso popup per avvisare l'utente che la transcodifica è stata completata.
Alla fine del metodo Run, chiamare Complete sull'oggetto del differimento per informare il sistema che l'attività in background è stata completata e può essere terminata.
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();
}
Nel metodo di supporto TranscodeFileAsync, i nomi dei file di input e output per le operazioni di transcodifica vengono recuperati da LocalSettings per l'app. Questi valori verranno impostati dall'app in primo piano. Creare un oggetto StorageFile per i file di input e output e quindi creare un profilo di codifica da usare per la transcodifica.
Chiamare PrepareFileTranscodeAsync, passando il file di input, il file di output e il profilo di codifica. L'oggetto PrepareTranscodeResult restituito da questa chiamata consente di sapere se è possibile eseguire la transcodifica. Se la proprietà CanTranscode è true, chiamare TranscodeAsync per eseguire l'operazione di transcodifica.
Il metodo AsTask consente di tenere traccia dello stato di avanzamento dell'operazione asincrona o annullarla. Creare un nuovo oggetto Progress, specificando le unità di avanzamento desiderate e il nome del metodo che verrà chiamato per notificare l'avanzamento corrente dell'attività. Passare l'oggetto Progress al metodo AsTask insieme al token di annullamento che consente di annullare l'attività.
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;
}
}
Nel metodo usato per creare l'oggetto Progress nel passaggio precedente, Progress, impostare lo stato di avanzamento dell'istanza dell'attività in background. Questo passerà lo stato di avanzamento all'app in primo piano, se è in esecuzione.
void TranscodeProgress(double percent)
{
Debug.WriteLine("Transcoding progress: " + percent.ToString().Split('.')[0] + "%");
backgroundTaskInstance.Progress = (uint)percent;
}
Il metodo di supporto SendToastNotification crea una nuova notifica di tipo avviso popup ottenendo un documento XML modello per un avviso popup con solo contenuto di testo. L'elemento di testo del codice XML di tipo avviso popup viene impostato e quindi viene creato un nuovo oggetto ToastNotification dal documento XML. Infine, l'avviso popup viene visualizzato all'utente chiamando 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);
}
Nel gestore per l'evento Canceled, che viene chiamato quando il sistema annulla l'attività in background, è possibile registrare l'errore a scopo di telemetria.
private void OnCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
Debug.WriteLine("Background " + sender.Task.Name + " Cancel Requested..." + reason.ToString());
}
Registrare e avviare l'attività in background
Prima di poter avviare l'attività in background dall'app in primo piano, occorre aggiornare il file Package.appmanifest dell'app in primo piano per informare il sistema che l'app usa un'attività in background.
- In Esplora soluzioni, fare doppio clic sull'icona del file Package.appmanifest per aprire l'editor del manifesto.
- Selezionare la scheda Dichiarazioni.
- Da Dichiarazioni disponibili, selezionare Attività in background e fare clic su Aggiungi.
- In Dichiarazioni supportate assicurarsi che l'elemento Attività in background sia selezionato. In Proprietà, selezionare la casella di controllo Elaborazione multimediale.
- Nella casella di testo Punto di ingresso, specificare lo spazio dei nomi e il nome della classe per il test in background, separati da un punto. Per questo esempio, la voce è:
MediaProcessingBackgroundTask.MediaProcessingTask
Successivamente, è necessario aggiungere un riferimento all'attività in background all'app in primo piano.
- In Esplora soluzioni, sotto il progetto di app in primo piano, fare clic con il pulsante destro del mouse sulla cartella Riferimenti e selezionare Aggiungi riferimento....
- Espandere il nodo Progetti e selezionare Soluzione.
- Selezionare la casella accanto al progetto di attività in background e fare clic su OK.
Il resto del codice in questo esempio deve essere aggiunto all'app in primo piano. Prima di tutto, è necessario aggiungere gli spazi dei nomi seguenti al progetto.
using Windows.ApplicationModel.Background;
using Windows.Storage;
Aggiungere quindi le variabili membro seguenti necessarie per registrare l'attività in background.
MediaProcessingTrigger mediaProcessingTrigger;
string backgroundTaskBuilderName = "TranscodingBackgroundTask";
BackgroundTaskRegistration taskRegistration;
Il metodo di supporto PickFilesToTranscode usa un FileOpenPicker e un FileSavePicker per aprire i file di input e output per la transcodifica. L'utente può selezionare i file in un percorso a cui l'app non ha accesso. Per assicurarsi che l'attività in background possa aprire i file, aggiungerli a FutureAccessList per l'app.
Infine, impostare le voci per i nomi di file di input e output in LocalSettings per l'app. L'attività in background recupera i nomi di file da questo percorso.
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;
}
Per registrare l'attività in background, creare un nuovo MediaProcessingTriggere un nuovo BackgroundTaskBuilder. Impostare il nome del generatore di attività in background in modo da poterlo identificare in un secondo momento. Impostare TaskEntryPoint sulla stessa stringa dello spazio dei nomi e della classe usata nel file manifesto. Impostare la proprietà Trigger sull'istanza MediaProcessingTrigger.
Prima di registrare l'attività, assicurarsi di annullare la registrazione di tutte le attività registrate in precedenza riproducendo a ciclo continuo nell'insieme AllTasks e chiamando Unregister in tutte le attività con il nome specificato nella proprietà BackgroundTaskBuilder.Name.
Registrare l'attività in background chiamando Register. Registrare i gestori per gli eventi Completed e 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;
}
Un'app tipica registrerà l'attività in background all'avvio iniziale dell'app, ad esempio nell'evento OnNavigatedTo.
Avviare l'attività in background chiamando il metodo RequestAsync dell'oggetto MediaProcessingTrigger. L'oggetto MediaProcessingTriggerResult restituito da questo metodo consente di sapere se l'attività in background è stata avviata correttamente e, in caso contrario, indica perché l'attività in background non è stata avviata.
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);
}
}
}
Un'app tipica avvierà l'attività in background in risposta all'interazione dell'utente, ad esempio nell'evento Click di un controllo dell'interfaccia utente.
Il gestore eventi OnProgress viene chiamato quando l'attività in background aggiorna lo stato di avanzamento dell'operazione. È possibile usare questa opportunità per aggiornare l'interfaccia utente con informazioni sullo stato di avanzamento.
private void OnProgress(IBackgroundTaskRegistration task, BackgroundTaskProgressEventArgs args)
{
string progress = "Progress: " + args.Progress + "%";
Debug.WriteLine(progress);
}
Il gestore eventi OnCompleted viene chiamato al termine dell'esecuzione dell'attività in background. Questa è un'altra opportunità per aggiornare l'interfaccia utente per fornire informazioni sullo stato all'utente.
private void OnCompleted(IBackgroundTaskRegistration task, BackgroundTaskCompletedEventArgs args)
{
Debug.WriteLine(" background task complete");
}