Опубликован: 28.01.2014 | Доступ: свободный | Студентов: 2255 / 259 | Длительность: 14:33:00
Лекция 9:

Media Services

В Windows Azure Media Services необходимо создавать работы (jobs) обработки мультимедиа-контента: шифрования, кодирования, конвертирования и т.д.. Работа Media Services всегда содержит в себе определение нескольких задач. Далее мы создадим простую задачу кодирования, после чего запустим ее на выполнение с Windows Azure Media Encoder. Задача будет использовать специальную строку настройки – пресет (preset) – для того, чтобы определить тип кодирования. Windows Azure Media Services поддерживвает аналогичный Microsoft Expression Encoder набор форматов.

Ниже приведен метод CreateEncodingJob, который необходимо добавить в класс. Метод выполняет следующие задачи:

  1. Определяет новую работу
  2. Определяет обработчик мультимедиа (media processor) для обработки работы. Обработчик мультимедиа – это компонент, который выполняет шифрование, кодирование, конвертирование и другие задачи. Всего есть несколько доступных обработчиков, список которых можно получить, вызвав _context.MediaProcessors. Метод GetLatestMediaProcessorByName возвращает обработчик.
  3. Определяет новую задачу. Каждая работа содержит одну и более задач. Задача определяется ее именем, экземпляром обработчика мультимедиа, строки конфигурации задачи и различных опций создания задачи. Строка конфигурации задачи определяет настройки кодирования. Например, здесь используется настройка H264 Broadband 720p, и этот пресет ведет к созданию одного файла MP4.
  4. Добавляет в задачу входной ассет. В этом примере входной ассет был создан ранее.
  5. Добавляет в задачу выходной ассет. Для выходного ассета определяется имя, булево значение, обозначающее, сохранять результат на сервере или нет, и AssetCreationOptions.None, обозначающий, что результат не шифруется при передаче и транспортировке.
  6. Устанавливает работу в очередь.

Метод также демонстрирует выполнение других задач, таких как отслеживание статуса выполнения задачи и получение доступа к ассету, созданному работой кодирования.

static IJob CreateEncodingJob(IAsset asset, string inputMediaFilePath, string outputFolder)
{
    IJob job = _context.Jobs.Create("My encoding job");

    IMediaProcessor processor = GetLatestMediaProcessorByName("Windows Azure Media Encoder");

    ITask task = job.Tasks.AddNew("My encoding task",
        processor,
        "H264 Broadband 720p",
        Microsoft.WindowsAzure.MediaServices.Client.TaskOptions.ProtectedConfiguration);

    task.InputAssets.Add(asset);
    task.OutputAssets.AddNew("Output asset",
        AssetCreationOptions.None);
    job.StateChanged += new
            EventHandler<JobStateChangedEventArgs>(StateChanged);
    job.Submit();

    LogJobDetails(job.Id);

    // Проверка выполнения работы и ожидание ее окончания. 
    Task progressJobTask = job.GetExecutionProgressTask(CancellationToken.None);
    progressJobTask.Wait();

    // Код показывает, как можно получить доступ к ассетам-результату работы двумя способами – 
созданием URL-ов к ассетам и простому скачиванию 

    job = GetJob(job.Id);

    if (job.State == JobState.Error)
    {
        Console.WriteLine("\nExiting method due to job error.");
        return job;
    }

    IAsset outputAsset = job.OutputMediaAssets[0];
    IAccessPolicy policy = null;
    ILocator locator = null;

    policy =
        _context.AccessPolicies.Create("My 30 days readonly policy",
            TimeSpan.FromDays(30),
            AccessPermissions.Read);

    // Создание локатора Shared Access Signature для предоставления прямого доступа 
к ассету в хранилище блобов. Можно вызывать синхронный или асинхронный метод создания. 
Можно задавать опциональный параметр startTime, равный значению, 
на пять минут раннему нежели  Now, 
для выравнивания различий во времени между часами клиента и сервера. 

    locator = _context.Locators.CreateLocator(LocatorType.Sas, outputAsset,
        policy,
        DateTime.UtcNow.AddMinutes(-5));

    List<String> sasUrlList = GetAssetSasUrlList(outputAsset, locator);

    // Запись списка URL-ов в локальный файл. Можно использовать 
сохраненные URL-ы для получения файлов ассета. 
    if (sasUrlList != null)
    {
        string outFilePath = Path.GetFullPath(outputFolder + @"\" + "FileSasUrlList.txt");
        StringBuilder fileList = new StringBuilder();
        foreach (string url in sasUrlList)
        {
            fileList.AppendLine(url);
            fileList.AppendLine();
        }
        WriteToFile(outFilePath, fileList.ToString());

        DownloadAssetToLocal(job.Id, outputFolder);
    }


    return job;
}

Добавим в метод Main вызов метода CreateEncodingJob после добавленного ранее кода.

CreateEncodingJob(asset, _singleInputFilePath, _outputFilesFolder);

Добавим приведенный ниже код, необходимый для выполнения метода CreateEncodingJob. Он выполняет следующие задачи:

1. Метод GetLatestMediaProcessorByName возвращает обработчик мультимедиа, выполняющий различные задачи. Вы создаете обработчик, используя имя обработчика. Возможные значения имени: Windows Azure Media Encoder,Windows Azure Media Packager,Windows Azure Media Encryptor,Storage Decryption.

private static IMediaProcessor GetLatestMediaProcessorByName(string mediaProcessorName)
{
       var processor = _context.MediaProcessors.Where(p => p.Name == mediaProcessorName).
        ToList().OrderBy(p => new Version(p.Version)).LastOrDefault();

    if (processor == null)
        throw new ArgumentException(string.Format("Unknown media processor", mediaProcessorName));

    return processor;
}

Код ниже содержит обработчик события StateChanged, необходимый для отслеживания выполнения работы и логирования.

private static void StateChanged(object sender, JobStateChangedEventArgs e)
{
    Console.WriteLine("Job state changed event:");
    Console.WriteLine("  Previous state: " + e.PreviousState);
    Console.WriteLine("  Current state: " + e.CurrentState);

    switch (e.CurrentState)
    {
        case JobState.Finished:
            Console.WriteLine();
            Console.WriteLine("********************");
            Console.WriteLine("Job is finished.");
            Console.WriteLine("Please wait while local tasks or downloads complete...");
            Console.WriteLine("********************");
            Console.WriteLine();
            Console.WriteLine();
            break;
        case JobState.Canceling:
        case JobState.Queued:
        case JobState.Scheduled:
        case JobState.Processing:
            Console.WriteLine("Please wait...\n");
            break;
        case JobState.Canceled:
        case JobState.Error:
            // Приведение к типу
            IJob job = (IJob)sender;
            // Логирование
            LogJobStop(job.Id);
            break;
        default:
            break;
    }
}

private static void LogJobStop(string jobId)
{
    StringBuilder builder = new StringBuilder();
    IJob job = GetJob(jobId);

    builder.AppendLine("\nThe job stopped due to cancellation or an error.");
    builder.AppendLine("***************************");
    builder.AppendLine("Job ID: " + job.Id);
    builder.AppendLine("Job Name: " + job.Name);
    builder.AppendLine("Job State: " + job.State.ToString());
    builder.AppendLine("Media Services account name: " + _accountName);

    if (job.State == JobState.Error)
    {
        builder.Append("Error Details: \n");
        foreach (ITask task in job.Tasks)
        {
            foreach (ErrorDetail detail in task.ErrorDetails)
            {
                builder.AppendLine("  Task Id: " + task.Id);
                builder.AppendLine("    Error Code: " + detail.Code);
                builder.AppendLine("    Error Message: " + detail.Message + "\n");
            }
        }
    }
    builder.AppendLine("***************************\n");

    string outputFile = _outputFilesFolder + @"\JobStop-" + JobIdAsFileName(job.Id) + ".txt";
    WriteToFile(outputFile, builder.ToString());
    Console.Write(builder.ToString());
}

private static void LogJobDetails(string jobId)
{
    StringBuilder builder = new StringBuilder();
    IJob job = GetJob(jobId);

    builder.AppendLine("\nJob ID: " + job.Id);
    builder.AppendLine("Job Name: " + job.Name);
    builder.AppendLine("Job submitted (client UTC time): " + DateTime.UtcNow.ToString());
    builder.AppendLine("Media Services account name: " + _accountName);

    string outputFile = _outputFilesFolder + @"\JobDetails-" + JobIdAsFileName(job.Id) + ".txt";
    WriteToFile(outputFile, builder.ToString());
    Console.Write(builder.ToString());
}

private static string JobIdAsFileName(string jobID)
{
    return jobID.Replace(":", "_");
}

Метод WriteToFile записывает файл в указанную директорию.

static void WriteToFile(string outFilePath, string fileContent)
{
    StreamWriter sr = File.CreateText(outFilePath);
    sr.Write(fileContent);
    sr.Close();
}

После получения ассетов получить к ним доступ возможно двумя способами:

  • Создав SAS URL ассета на сервере
  • Скачав ассеты с сервера

Метод GetAssetSasUrlList создает список SAS URL-ов на все файл ассета.

static List<String> GetAssetSasUrlList(IAsset asset, ILocator locator)
{
    List<String> fileSasUrlList = new List<String>();

    foreach (IAssetFile file in asset.AssetFiles)
    {
        string sasUrl = BuildFileSasUrl(file, locator);
        fileSasUrlList.Add(sasUrl);
    }
    return fileSasUrlList;
}

static string BuildFileSasUrl(IAssetFile file, ILocator locator)
{
    // Получение пути-локатора, добавление имени файла для получения полного SAS URL для доступа к файлу. 
    var uriBuilder = new UriBuilder(locator.Path);
    uriBuilder.Path += "/" + file.Name;

    Console.WriteLine("Locator path: ");
    Console.WriteLine(locator.Path);
    Console.WriteLine();
    Console.WriteLine("Full URL to file: ");
    Console.WriteLine(uriBuilder.Uri.AbsoluteUri);
    Console.WriteLine();

    return uriBuilder.Uri.AbsoluteUri;
}

Метод DownloadAssetToLocal скачивает все файлы ассета в локальную директорию. В данном примере, так как ассет был создан из одного входящего файла, выходной ассет будет иметь колллекцию, содержащую два файла – закодированный файл (.mp4) и .xml файл с метаданными ассета. Метод скачивает оба файла.

static IAsset DownloadAssetToLocal(string jobId, string outputFolder)
{
    // Этот метод показывает, как скачивать один ассет. Доступ ко всем ассетам можно получить 
с помощью коллекции OutputAssets. 
    IJob job = GetJob(jobId);
    IAsset outputAsset = job.OutputMediaAssets[0];

    IAccessPolicy accessPolicy = _context.AccessPolicies.Create("File Download Policy", 
TimeSpan.FromDays(30), AccessPermissions.Read);
    ILocator locator = _context.Locators.CreateSasLocator(outputAsset, accessPolicy);
    BlobTransferClient blobTransfer = new BlobTransferClient
    {
        NumberOfConcurrentTransfers = 10,
        ParallelTransferThreadCount = 10
    };

    var downloadTasks = new List<Task>();
    foreach (IAssetFile outputFile in outputAsset.AssetFiles)
    {
        outputFile.DownloadProgressChanged += DownloadProgress;

        string localDownloadPath = Path.Combine(outputFolder, outputFile.Name);

        Console.WriteLine("File download path:  " + localDownloadPath);

        downloadTasks.Add(outputFile.DownloadAsync(Path.GetFullPath(localDownloadPath), 
blobTransfer, locator, CancellationToken.None));

        outputFile.DownloadProgressChanged -= DownloadProgress;
    }

    Task.WaitAll(downloadTasks.ToArray());

    return outputAsset;
}

static void DownloadProgress(object sender, DownloadProgressChangedEventArgs e)
{
    Console.WriteLine(string.Format("{0} % download progress. ", e.Progress));
}

Методы GetJob и GetAsset запрашивают и возвращают ссылки на работу и ассет с указанными идентификаторами.

static IJob GetJob(string jobId)
{

    var jobInstance =
        from j in _context.Jobs
        where j.Id == jobId
        select j;

    IJob job = jobInstance.FirstOrDefault();

    return job;
}
static IAsset GetAsset(string assetId)
{
    var assetInstance =
        from a in _context.Assets
        where a.Id == assetId
        select a;

    IAsset asset = assetInstance.FirstOrDefault();

    return asset;
}

Нажмем F5. В консоль должен быть выведен подобный приведенному ниже текст (ниже приведен сокращенный вариант):

Asset name: UploadSingleFile_11/14/2012 10:09:11 PM
Time created: 11/14/2012 12:00:00 AM
Created assetFile interview2.wmv
Upload interview2.wmv
Done uploading of interview2.wmv using Upload()

…

Locator path:
https://mediasvcd08mtz29tcpws.blob.core.windows-int.net/asset-4f5b42f4-3ade-4c2c
-9d48-44900d4f6b62?st=2012-11-14T22%3A07%3A01Z&se=2012-11-14T23%3A07%3A01Z&sr=c&
si=d07ec40c-02d7-4642-8e54-443b79f3ba3c&sig=XKMo0qJI5w8Fod3NsV%2FBxERnav8Jb6hL7f
xylq3oESc%3D

Full URL to file:
https://mediasvcd08mtz29tcpws.blob.core.windows-int.net/asset-4f5b42f4-3ade-4c2c
-9d48-44900d4f6b62/interview2_metadata.xml?st=2012-11-14T22%3A07%3A01Z&se=2012-1
1-14T23%3A07%3A01Z&sr=c&si=d07ec40c-02d7-4642-8e54-443b79f3ba3c&sig=XKMo0qJI5w8F
od3NsV%2FBxERnav8Jb6hL7fxylq3oESc%3D

Downloads are in progress, please wait.

File download path:  C:\supportFiles\outputfiles\interview2.mp4
1.70952185308162 % download progress.
3.68508804454907 % download progress.
6.48870388360293 % download progress.
6.83808741232649 % download progress.
. . . 
99.0763740574049 % download progress.
99.1522674787341 % download progress.
100 % download progress.
File download path:  C:\supportFiles\outputfiles\interview2_metadata.xml
100 % download progress.

После запуска приложения происходит следующая последовательность действий:

  • В Media Services загружается .wmv.
  • Файл кодируется с использованием H264 Broadband 720p. Windows Azure Media Encoder.
  • В supportFiles\outputFiles создается файл FileSasUrlList.txt, содержащий URL на закодированный ассет.
  • Файлы .mp4 и _metadata.xmml скачиваются в директорию outputFiles.
Руслан Муравьев
Руслан Муравьев

Сайт dreamspark пишет что код истек :(

Andriy Zymenko
Andriy Zymenko

Этот курс требует оновления https://portal.azure.com/#create/hub здесь нет пункта Web Site в разделе Compute. К тому же для создание трубуется подписка