Tips For Working With Windows Azure Media Services

Thursday, May 8, 2014

I’ve been doing some work with Windows Azure Media services and making progress, although it takes some time and experimentation to work through the vocabulary of the API, documentation, and code snippets.

1. Uploading and encoding video into media services can be completed programmatically using the CloudMediaContext class from the NuGet package WindowsAzure.MediaServices.

2. Uploading creates an asset in media services. Each asset can contain multiple files, but you only want one video or audio file in the uploaded asset. WAMS will create a container in blob storage for each asset, so it seems best to create a new storage account dedicated to each media service.

3. For encoding you need to select a media processor by name, and perhaps a preset configuration by name. The processor names you can list using some C# code, and the values I currently see are:

        Windows Azure Media Encoder 3.7
        Windows Azure Media Packager 2.8
        Windows Azure Media Encryptor 2.8
        Windows Azure Media Encoder 2.3
        Storage Decryption 1.7

Preset names took some digging around, but I eventually found a complete list for the WAME at Media Services Encoder System Presets

What follows is a class that wraps CloudMediaContext and can list assets including the files inside, upload an asset, encode an asset, and list the available media processors. It is experimental code that assumes it is working inside a console application, but that behavior is easy to refactor out. Some of the LINQ queries are strange, but they work around the wonkiness of OData.

public class MediaService
{
    public MediaService()
    {            
        _context = new CloudMediaContext(
                Configuration.AzureAccountName, 
                Configuration.AzureAccountKey
            );
    }

    public void Upload(string filePath)
    {
        var assetName = Path.GetFileNameWithoutExtension(filePath) + "_i";
        var asset = _context.Assets.Create(assetName, AssetCreationOptions.None);

        var assetFileName = Path.GetFileName(filePath);
        var assetFile = asset.AssetFiles.Create(assetFileName);
        assetFile.UploadProgressChanged += (sender, args) => 
            Console.WriteLine("Up {0}:{1}", assetName, args.Progress);
        assetFile.Upload(filePath);
    }

    public void Encode(string filePath)
    {
        var assetName = Path.GetFileNameWithoutExtension(filePath) + "_i";
        var asset = GetAsset(assetName);
        var job = _context.Jobs.Create("Encoding job " + assetName);
        var processor = GetMediaProcessor();
        var task = job.Tasks.AddNew("Encoding task" + assetName, 
                        processor, Configuration.PresetName, TaskOptions.None);
        task.InputAssets.Add(asset);
        task.OutputAssets.AddNew(assetName + "_o", AssetCreationOptions.None);

        job.StateChanged += (sender, args) => 
            Console.WriteLine("Job: {0} {1}", job.Name, args.CurrentState);
        job.Submit();
        
        var progress = job.GetExecutionProgressTask(CancellationToken.None);
        progress.Wait();
    }

    public void ListMedia()
    {
        foreach (var asset in _context.Assets)
        {
            Console.WriteLine("{0}", asset.Name);
            foreach (var file in asset.AssetFiles)
            {
                Console.WriteLine("\t{0}", file.Name);
            }
        }
    }

    public void ListMediaProcessors()
    {
        Console.WriteLine("Available processors are:");
        foreach (var procesor in _context.MediaProcessors)
        {
            Console.WriteLine("\t{0} {1}", procesor.Name, procesor.Version);
        }
    }

    IMediaProcessor GetMediaProcessor()
    {
        var processors = _context.MediaProcessors
                                 .Where(p => p.Name == Configuration.EncoderName)
                                 .ToList()
                                 .OrderBy(p => new Version(p.Version));
                                 
        if (!processors.Any())
        {
            Console.WriteLine("Could not find processor {0}", Configuration.EncoderName);
            ListMediaProcessors();
            Environment.Exit(-1);
        }
        return processors.First();
    }        

    IAsset GetAsset(string name)
    {
        var assets = _context.Assets.Where(a => a.Name == name).ToList();
        if (!assets.Any())
        {
            Console.WriteLine("Could not find asset {0}", name);
            Environment.Exit(-1);
        }
        return assets.First();
    }

    readonly CloudMediaContext _context;
}

The above class also assumes you have a Configuration class to read config information, which looks like the following.

<appSettings>
    <add key="accountName" value="media services account name"/>
    <add key="accountKey" value="media services key"/>
    <add key="encoderName" value="Windows Azure Media Encoder"/>
    <add key="presetName" value="H264 Broadband SD 4x3"/>
</appSettings>

Comments
gravatar sergio Sunday, May 11, 2014
Azure Media Services are a complete mess! I tried to learn them because a customer asked me for a proposal, and finally I forgot them... Lack of documentation, lack of usefull samples, and finally a messy API. Microsoft, in what thing were you thinking about when you released these services? Regards sergio
Comments are now closed.
by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!