Tag Archives: NuGet

New Azure Media Services .NET SDK Extensions Release

Azure Media ServicesYesterday, the Azure Media Services team released a new version of the Azure Media Services .NET SDK Extensions nuget package (v3.7.0.1) that contains some fixes, updates and new features. The previous nuget package version (v3.5.2) is a broken build, so it’s now deprecated/unlisted; please make sure to update your projects to use the new and fixed package version: v3.7.0.1.

In this post, I will share the change log by describing all the changes that were included in this release.

Change Log

Upgraded to latest Azure Media Services .NET SDK (v3.7.0.1).

Marked as obsolete these processors in MediaProcessorNames class: Windows Azure Media Encoder, Azure Media Encoder, Windows Azure Media Packager, Windows Azure Media Encryptor.

[Obsolete]
public const string WindowsAzureMediaEncoder = "Windows Azure Media Encoder";

[Obsolete]
public const string AzureMediaEncoder = "Azure Media Encoder";

[Obsolete]
public const string WindowsAzureMediaPackager = "Windows Azure Media Packager";

[Obsolete]
public const string WindowsAzureMediaEncryptor = "Windows Azure Media Encryptor";

Marked as obsolete the task preset strings for Azure Media Encoder processor in MediaEncoderTaskPresetStrings class.

[Obsolete]
public static class MediaEncoderTaskPresetStrings

Added names for new Media Analytics’ processors in MediaProcessorNames class.

public const string AzureMediaFaceDetector = "Azure Media Face Detector";

public const string AzureMediaHyperlapse = "Azure Media Hyperlapse";

public const string AzureMediaIndexer = "Azure Media Indexer";

public const string AzureMediaIndexer2Preview = "Azure Media Indexer 2 Preview";

public const string AzureMediaMotionDetector = "Azure Media Motion Detector";

public const string AzureMediaOCR = "Azure Media OCR";

public const string AzureMediaStabilizer = "Azure Media Stabilizer";

public const string AzureMediaVideoThumbnails = "Azure Media Video Thumbnails";

Added task preset strings for Media Encoder Standard processor in MediaEncoderStandardTaskPresetStrings class.

public static class MediaEncoderStandardTaskPresetStrings
{
// H264 Multiple Bitrate Presets
public const string H264MultipleBitrate1080pAudio51 = "H264 Multiple Bitrate 1080p Audio 5.1";
public const string H264MultipleBitrate1080p = "H264 Multiple Bitrate 1080p";
public const string H264MultipleBitrate16x9foriOS = "H264 Multiple Bitrate 16x9 for iOS";
public const string H264MultipleBitrate16x9SDAudio51 = "H264 Multiple Bitrate 16x9 SD Audio 5.1";
public const string H264MultipleBitrate16x9SD = "H264 Multiple Bitrate 16x9 SD";
public const string H264MultipleBitrate4KAudio51 = "H264 Multiple Bitrate 4K Audio 5.1";
public const string H264MultipleBitrate4K = "H264 Multiple Bitrate 4K";
public const string H264MultipleBitrate4x3foriOS = "H264 Multiple Bitrate 4x3 for iOS";
public const string H264MultipleBitrate4x3SDAudio51 = "H264 Multiple Bitrate 4x3 SD Audio 5.1";
public const string H264MultipleBitrate4x3SD = "H264 Multiple Bitrate 4x3 SD";
public const string H264MultipleBitrate720pAudio51 = "H264 Multiple Bitrate 720p Audio 5.1";
public const string H264MultipleBitrate720p = "H264 Multiple Bitrate 720p";

// H264 Single Bitrate Presets
public const string H264SingleBitrate1080pAudio51 = "H264 Single Bitrate 1080p Audio 5.1";
public const string H264SingleBitrate1080p = "H264 Single Bitrate 1080p";
public const string H264SingleBitrate4KAudio51 = "H264 Single Bitrate 4K Audio 5.1";
public const string H264SingleBitrate4K = "H264 Single Bitrate 4K";
public const string H264SingleBitrate4x3SDAudio51 = "H264 Single Bitrate 4x3 SD Audio 5.1";
public const string H264SingleBitrate4x3SD = "H264 Single Bitrate 4x3 SD";
public const string H264SingleBitrate16x9SDAudio51 = "H264 Single Bitrate 16x9 SD Audio 5.1";
public const string H264SingleBitrate16x9SD = "H264 Single Bitrate 16x9 SD";
public const string H264SingleBitrate720pAudio51 = "H264 Single Bitrate 720p Audio 5.1";
public const string H264SingleBitrate720pforAndroid = "H264 Single Bitrate 720p for Android";
public const string H264SingleBitrate720p = "H264 Single Bitrate 720p";
public const string H264SingleBitrateHighQualitySDforAndroid = "H264 Single Bitrate High Quality SD for Android";
public const string H264SingleBitrateLowQualitySDforAndroid = "H264 Single Bitrate Low Quality SD for Android";
}

Added new CreateFromBlobAsync / CreateFromBlob extension methods for AssetBaseCollection class to create a new asset by copying a source blob. This extension works with a source blob belonging to any Storage account (not necessary bound to the Media Services account and even across different datacenters).

CloudMediaContext context = new CloudMediaContext("%accountName%", "%accountKey%");
StorageCredentials storageCredentials = new StorageCredentials("%storageAccountName%", "%storageAccountKey%");

// Get a reference to the source blob that will be copied in the new asset.
CloudBlockBlob sourceBlob = null;

// Create a new asset and copies the sourceBlob parameter using a single extension method.
IAsset asset = context.Assets.CreateFromBlob(sourceBlob, storageCredentials, AssetCreationOptions.None);

Added new CopyAsync / Copy extension methods for IAsset interface to copy all files in the source asset into the destination asset. This extension works with regular assets, live archive assets (FragBlob format) and source/destination assets belonging to different Media Services accounts (even across different datacenters).

CloudMediaContext context = new CloudMediaContext("%accountName%", "%accountKey%");

// Get a reference to the source asset.
string sourceAssetId = "%sourceAssetId%";
IAsset sourceAsset = context.Assets.Where(a => a.Id == sourceAssetId).First();

// Create an empty destination asset where the source asset files are going to be copied.
IAsset destinationAsset = context.Assets.Create("Asset Copy", AssetCreationOptions.None);
StorageCredentials destinationStorageCredentials = new StorageCredentials("%storageAccountName%", "%storageAccountKey%");

// Copy the files in the 'sourceAsset' instance into the 'destinationAsset' instance.
sourceAsset.Copy(destinationAsset, destinationStorageCredentials);

Added new CopyBlobHelpers static class with some helper methods for copying blob.

/// <summary>
/// Returns a <see cref="System.Threading.Tasks.Task"/> instance for the copy blobs operation from <paramref name="sourceContainer"/> to <paramref name="destinationContainer"/>.
/// </summary>
/// <param name="sourceContainer">The <see cref="Microsoft.WindowsAzure.Storage.Blob.CloudBlobContainer"/> instance that contains the blobs to be copied into <paramref name="destinationContainer"/>.</param>
/// <param name="destinationContainer">The <see cref="Microsoft.WindowsAzure.Storage.Blob.CloudBlobContainer"/> instance where the blobs from <paramref name="sourceContainer"/> will be copied.</param>
/// <param name="options">The <see cref="Microsoft.WindowsAzure.Storage.Blob.BlobRequestOptions"/>.</param>
/// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken"/> instance used for cancellation.</param>
/// <returns>A <see cref="System.Threading.Tasks.Task"/> instance for the copy blobs operation from <paramref name="sourceContainer"/> to <paramref name="destinationContainer"/>.</returns>
public static async Task CopyBlobsAsync(CloudBlobContainer sourceContainer, CloudBlobContainer destinationContainer, BlobRequestOptions options, CancellationToken cancellationToken);

/// <summary>
/// Returns a <see cref="System.Threading.Tasks.Task"/> instance for the copy blob operation from <paramref name="sourceBlob"/> to <paramref name="destinationBlob"/>.
/// </summary>
/// <param name="sourceBlob">The <see cref="Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob"/> instance to be copied to <paramref name="destinationBlob"/>.</param>
/// <param name="destinationBlob">The <see cref="Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob"/> instance where <paramref name="sourceBlob"/> will be copied.</param>
/// <param name="options">The <see cref="Microsoft.WindowsAzure.Storage.Blob.BlobRequestOptions"/>.</param>
/// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken"/> instance used for cancellation.</param>
/// <returns>A <see cref="System.Threading.Tasks.Task"/> instance for the copy blob operation from <paramref name="sourceBlob"/> to <paramref name="destinationBlob"/>.</returns>
public static async Task CopyBlobAsync(CloudBlockBlob sourceBlob, CloudBlockBlob destinationBlob, BlobRequestOptions options, CancellationToken cancellationToken);

 

As usual, feedback and contributions in the GitHub project are always welcome.

 

Enjoy!

Intro to .NET Core

.NET is a general purpose development platform. It has several key features that are attractive to many developers, including automatic memory management and modern programming languages, that make it easier to efficiently build high-quality apps. Multiple implementations of .NET are available, based on open .NET Standards that specify the fundamentals of the platform.

.NET Implementations

There are various implementations of .NET, some coming from Microsoft, some coming from other companies and groups:

  • The .NET Framework is the premier implementation of the .NET Platform available for Windows server and client developers.

    There are additional stacks built on top the .NET Framework, for example Windows Forms and Windows Presentation Foundation (WPF) for UI, Windows Communication Foundation (WCF) for middleware services and ASP.NET as a web framework.

  • Mono is an open source implementation of Microsoft’s .NET Framework based on the ECMA standards for C# and the Common Language Runtime.
  • .NET Native is the set of tools used to build .NET Universal Windows Platform (UWP) applications. .NET Native compiles C# to native machine code that performs like C++.

I’ll explain a little bit more on what is .NET Core below. But first, let’s take a look at the .NET Ecosystem.

.NET Ecosystem

The NET Ecosystem is undergoing a major shift and restructuring in 2015. There are a lot of “moving pieces” that need to be tied together in order for this new ecosystem and all of the recommended scenarios to work. As you can see, this is a very vibrant and diverse ecosystem.

You might not know but most of these projects are currently open sourced and fostered by the .NET Foundation (independent) organization. Yes! These projects are open source!

 

10kft_view

A wild .NET implementation appeared!

.NET Core is a cross-platform implementation of .NET that is primarily being driven by ASP.NET 5 workloads, but also by the need and desire to have a modern runtime that is modular and whose features and libraries can be cherry picked based on the application’s needs.

It includes a small runtime that is built from the same codebase as the .NET Framework CLR. The .NET Core runtime includes the same GC and JIT (RyuJIT), but doesn’t include features like Application Domains or Code Access Security.

There are several characteristics of .NET Core:

  • Cross-platform support is the first important feature. For applications, it is important to use those platforms that will provide the best environment for their execution. Thus, having an application platform that can enable the app to be ran on different operating systems with minimal or no changes provides a significant boon.
  • Open Source because it is proven to be a great way to enable a larger set of platforms, supported by community contribution.
  • Better packaging story – the framework is distributed as a set of packages that developers can pick and choose from, rather than a single, monolithic platform. .NET Core is the first implementation of .NET Platform that is distributed via NuGet package manager.
  • Better application isolation as one of the scenarios for .NET Core is to enable applications to “take” the needed runtime for their execution and deploy it with the application, not depending on shared components on the targeted machine. This plays well with the current trends of developing software and using container technologies like Docker for consolidation.
  • Modular – .NET Core is a set of runtime, library and compiler components. Microsoft uses these components in various configurations for device and cloud workloads.

NuGet as a 1st class delivery vehicle

In contrast to the .NET Framework, the .NET Core platform will be delivered as a set of NuGet packages.

Using NuGet allows for much more agile usage of the individual libraries that comprise .NET Core. It also means that an application can list a collection of NuGet packages (and associated version information) and this will comprise both system/framework as well as third-party dependencies required. Further, third-party dependencies can now also express their specific dependencies on framework features, making it much easier to ensure the proper packages and versions are pulled together during the development and build process.

If, for example, you need to use immutable collections, you can install the System.Collections.Immutable package via NuGet. The NuGet version will also align with the assembly version, and will use semantic versioning.

 

0841.Pic4

Open Source and Cross-Platform

Last year, the .NET Core main repositories were made open source: CoreFX (Framework libraries) and CoreCLR (Runtime) are public in GitHub. The main reasons for this is to leverage a stronger ecosystem and lay the foundation for a cross platform .NET and it is a natural progression on current .NET Foundation’s open source efforts:

However, as of only a few months ago (April) you can install .NET Core on Windows, Linux and OSX. This makes code written for it is also portable across application stacks, such as Mono, and platforms making it feasible to move applications across different environments with ease.

Windows_Linux_OSX

Further reading

http://blogs.msdn.com/b/dotnet/archive/2014/12/04/introducing-net-core.aspx

http://blogs.msdn.com/b/dotnet/archive/2014/11/12/net-core-is-open-source.aspx

http://dotnet.github.io/core/

http://dotnet.github.io/core/about/

http://dotnet.readthedocs.org/en/latest/getting-started/overview.html

http://dotnet.github.io/core/about/overview.html

.NET Core: Cross Platform – Windows

.NET Core: Cross Platform – OS X

Reusing Azure Media Services Locators to avoid facing the "5 Shared Access Policy" limitation

If you have developed VOD or Live workflows with Azure Media Services, you might have faced the following error when creating multiple Asset Locators: “Server does not support setting more than 5 shared access policy identifiers on a single container”.

<?xml version="1.0" encoding="utf-8"?> <m:error xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"> <m:code /> <m:message xml:lang="en-US">Server does not support setting more than 5 shared access policy identifiers on a single container.</m:message> </m:error>

To understand the reason behind this error (and how to avoid it), first let me clarify the differences among Azure Storage Stored Access Policies, Media Services Access Policies and Media Services Locators:

  • Stored Access Policies. This is an Azure Storage Services REST API feature that provides an additional level of control over shared access signatures (SAS) on the server-side for containers, queues, or tables. This feature has the limitation that you can include up to 5 Stored Access Policies for each container, queue or table.
  • Access Policies. This is an Azure Media Services REST API entity that it is just used as a “template” for creating Locators. There is no mapping between a Media Services Access Policy and a Storage Services Stored Access Policy and, therefore, there is no explicit limitation on the number of Access Policies you can create.
  • Locators. This is an Azure Media Services REST API entity that provides an entry point to access the files contained in an Asset container. An Access Policy is used to define the permissions and duration that a client has access to a given Asset. When a Locator is created, the Media Services REST API creates a Stored Access Policy in the container associated with the Asset. Therefore, the same Stored Access Policy limitation also applies for Locators: you can create up to 5 Locators for a given Asset.

As you can see, the error is thrown by an Azure Storage Services limitation on the number of Stored Access Policies for a container, and the same limitation is inherited by the number of Media Services Locators for an Asset.

There are different options to avoid getting this error (depending on your scenario):

  1. Delete the asset locators after you are done using them. For example, if you need to upload a new asset file, you have to create a SAS locator with Write permissions. Once the operation is complete, you no longer need the locator, so it is safe to delete it. Take into account that this approach does not apply to some scenarios: if you want to publish an asset for adaptive streaming (On-Demand Origin locator) or progressive download (SAS locator), the locator must persist; otherwise, deleting the locator will “unpublish” the asset.
  2. Reuse the locators that are available in the asset. Instead of creating a new locator every time, check if the asset already contains one that matches the type and access policy permissions you need. If you find one, make sure it is not expired (or near expiration) before using it; otherwise, create a new locator.
  3. Leverage the Azure Media Services Content Protection feature. If you are trying to get granular control over your content by creating a different Locator for each client, there is a better way now: you can dynamically encrypt your content with AES or PlayReady, set a token authorization policy for the content key or license, and make the token expire after a short period of time (long enough for the player to retrieve the content key or license and start the playback). This way, you will be using a single long-lived Locator for all your clients. For more details, you can check this blog post: Announcing public availability of Azure Media Services Content Protection Services.

 

In this post, I will focus on Option #2 and show you how to implement a helper extension method to let you reuse your Locators and also update the duration if it happens to be expired (or near expiration). Below, you can find a proposed implementation that takes care of this.

Note: This code uses the Windows Azure Media Services .NET SDK Extensions NuGet package.

namespace Microsoft.WindowsAzure.MediaServices.Client { using System; using System.Linq; using System.Threading.Tasks; public static class LocatorCollectionExtensions { public static readonly TimeSpan DefaultExpirationTimeThreshold = TimeSpan.FromMinutes(5); public static async Task<ILocator> GetOrCreateAsync(this LocatorBaseCollection locators, LocatorType locatorType, IAsset asset, AccessPermissions permissions, TimeSpan duration, DateTime? startTime = null, TimeSpan? expirationThreshold = null) { MediaContextBase context = locators.MediaContext; ILocator assetLocator = context .Locators .Where(l => (l.AssetId == asset.Id) && (l.Type == locatorType)) .OrderByDescending(l => l.ExpirationDateTime) .ToList() .Where(l => (l.AccessPolicy.Permissions & permissions) == permissions) .FirstOrDefault(); if (assetLocator == null) { // If there is no locator in the asset matching the type and permissions, then a new locator is created. assetLocator = await context.Locators.CreateAsync(locatorType, asset, permissions, duration, startTime).ConfigureAwait(false); } else if (assetLocator.ExpirationDateTime <= DateTime.UtcNow.Add(expirationThreshold ?? DefaultExpirationTimeThreshold)) { // If there is a locator in the asset matching the type and permissions but it is expired (or near expiration), then the locator is updated. await assetLocator.UpdateAsync(startTime, DateTime.UtcNow.Add(duration)).ConfigureAwait(false); } return assetLocator; } public static ILocator GetOrCreate(this LocatorBaseCollection locators, LocatorType locatorType, IAsset asset, AccessPermissions permissions, TimeSpan duration, DateTime? startTime = null, TimeSpan? expirationThreshold = null) { using (Task<ILocator> task = locators.GetOrCreateAsync(locatorType, asset, permissions, duration, startTime, expirationThreshold)) { return task.Result; } } } }

Every time you need a Locator for an Asset, you can use the GetOrCreate method as follows. Of course, if you call the GetOrCreate method multiple times using different parameter combinations (locator type and access policy permissions), you might end up facing the “5 shared access policy” limitation. That’s why it is also important to delete the locators that are not needed as explained in Option #1.

var myContext = new CloudMediaContext("%accountName%", "%accountKey%"); var myAsset = myContext.Assets.Where(a => a.Id == "%assetId%").First(); var myLocator = myContext.Locators.GetOrCreate(LocatorType.Sas, myAsset, AccessPermissions.Read, TimeSpan.FromDays(30));

 

Enjoy!

WindowsAzure.MediaServices.Extensions NuGet package 2.0.0 released!

Windows Azure Media ServicesLast week, the Windows Azure Media Services (WAMS) team published a new release of the WindowsAzure.MediaServices.Extensions NuGet package (2.0.0). Starting from this new version, the WAMS team will be taking ownership of the NuGet package and the Extensions source code to make sure that they are aligned and consistent with the Windows Azure Media Services .NET SDK. The Extensions source code was moved to a new GitHub repository under the WindowsAzure organization: https://github.com/WindowsAzure/azure-sdk-for-media-services-extensions (the previous GitHub repository is already deprecated).

The following are some of the highlights for this new NuGet package release:

  • It targets the WindowsAzure.MediaServices NuGet package 3.0.0 (or higher). This is latest version of the Windows Azure Media Services .NET SDK.
  • It does not add source code files to your project anymore. Now, the extensions are shipped in the Microsoft.WindowsAzure.MediaServices.Client.Extensions assembly that is added as a reference to your project.
  • It contains several breaking changes with the previous extensions (1.0.6). Most of them are related to a different organization of the extension methods (to avoid defining all of them for CloudMediaContext class) and renaming.
  • It contains new extensions for creating assets by automatically selecting one of the Storage accounts attached to the Media Services account based on different strategies.

Breaking Changes

The following are the breaking changes with the previous Nuget package version (1.0.6). For more information, you can check the README documentation.

Create Asset from single local file

// Original extension (there are additional overloads)

public static IAsset CreateAssetFromFile(

  this CloudMediaContext context, string filePath, AssetCreationOptions options)

 

// New extension (there are additional overloads)

public static IAsset CreateFromFile(

  this AssetBaseCollection assets, string filePath, AssetCreationOptions options)

Create Asset from local folder

// Original extension (there are additional overloads)

public static IAsset CreateAssetFromFolder(

  this CloudMediaContext context, string folderPath, AssetCreationOptions options)

 

// New extension (there are additional overloads)

public static IAsset CreateFromFolder(

  this AssetBaseCollection assets, string folderPath, AssetCreationOptions options)

Generate Asset Files from Blob storage

// Original extension

public static void CreateAssetFiles(this CloudMediaContext context, IAsset asset)

 

// New extension

public static void GenerateFromStorage(this IAsset asset)

Download Asset Files to local folder

// Original extension (there are additional overloads)

public static void DownloadAssetFilesToFolder(this CloudMediaContext context, IAsset asset, string folderPath)

 

// New extension (there are additional overloads)

public static void DownloadToFolder(this IAsset asset, string folderPath)

Create Locator

// Original extension (there are additional overloads)

public static ILocator CreateLocator(

  this CloudMediaContext context, IAsset asset, LocatorType locatorType, AccessPermissions permissions, TimeSpan duration)

 

// New extension (there are additional overloads)

public static ILocator Create(

  this LocatorBaseCollection locators, LocatorType locatorType, IAsset asset, AccessPermissions permissions, TimeSpan duration)

Create a Job with a single Task

// Original extension (there is an additional overload)

public static IJob PrepareJobWithSingleTask(

  this MediaContextBase context, string mediaProcessorName, string taskConfiguration, IAsset inputAsset, string outputAssetName, AssetCreationOptions outputAssetOptions)

 

// New extension (there is an additional overload)

public static IJob CreateWithSingleTask(

  this JobBaseCollection jobs, string mediaProcessorName, string taskConfiguration, IAsset inputAsset, string outputAssetName, AssetCreationOptions outputAssetOptions)

Start Job execution progress task

// Original extension (there is an additional overload)

public static Task<IJob> StartExecutionProgressTask(

  this MediaContextBase context, IJob job, Action<IJob> executionProgressChangedCallback, CancellationToken cancellationToken)

 

// New extension (there is an additional overload)

public static Task<IJob> StartExecutionProgressTask(

  this IJob job, Action<IJob> executionProgressChangedCallback, CancellationToken cancellationToken)

Removed extensions

// These extensions were removed because they were not related to Media Services.

public static void Save(this Uri uri, string filePath)

public static void Save(this string url, string filePath)

Sample Workflow using the Extensions

The following sample code shows a basic media workflow that uses the new Windows Azure Media Services .NET SDK Extensions NuGet package.

try

{

    MediaServicesCredentials credentials = new MediaServicesCredentials("%accountName%", "%accountKey%");

    CloudMediaContext context = new CloudMediaContext(credentials);

 

    Console.WriteLine("Creating new asset from local file...");

 

    // 1. Create a new asset by uploading a mezzanine file from a local path.

    IAsset inputAsset = context.Assets.CreateFromFile(

        "smallwmv1.wmv",

        AssetCreationOptions.None,

        (af, p) =>

        {

            Console.WriteLine("Uploading '{0}' - Progress: {1:0.##}%", af.Name, p.Progress);

        });

 

    Console.WriteLine("Asset created.");

 

    // 2. Prepare a job with a single task to transcode the previous mezzanine asset

    //    into a multi-bitrate asset.

    IJob job = context.Jobs.CreateWithSingleTask(

        MediaProcessorNames.WindowsAzureMediaEncoder,

        MediaEncoderTaskPresetStrings.H264AdaptiveBitrateMP4Set720p,

        inputAsset,

        "Sample Adaptive Bitrate MP4",

        AssetCreationOptions.None);

 

    Console.WriteLine("Submitting transcoding job...");

 

    // 3. Submit the job and wait until it is completed.

    job.Submit();

    job = job.StartExecutionProgressTask(

        j =>

        {

            Console.WriteLine("Job state: {0}", j.State);

            Console.WriteLine("Job progress: {0:0.##}%", j.GetOverallProgress());

        },

        CancellationToken.None).Result;

 

    Console.WriteLine("Transcoding job finished.");

 

    IAsset outputAsset = job.OutputMediaAssets[0];

 

    Console.WriteLine("Publishing output asset...");

 

    // 4. Publish the output asset by creating an Origin locator for adaptive streaming, 

    // and a SAS locator for progressive download.

    context.Locators.Create(

        LocatorType.OnDemandOrigin,

        outputAsset,

        AccessPermissions.Read,

        TimeSpan.FromDays(30));

    context.Locators.Create(

        LocatorType.Sas,

        outputAsset,

        AccessPermissions.Read,

        TimeSpan.FromDays(30));

 

    IEnumerable<IAssetFile> mp4AssetFiles = outputAsset

            .AssetFiles

            .ToList()

            .Where(af => af.Name.EndsWith(".mp4", StringComparison.OrdinalIgnoreCase));

 

    // 5. Generate the Smooth Streaming, HLS and MPEG-DASH URLs for adaptive streaming, 

    // and the Progressive Download URL.

    Uri smoothStreamingUri = outputAsset.GetSmoothStreamingUri();

    Uri hlsUri = outputAsset.GetHlsUri();

    Uri mpegDashUri = outputAsset.GetMpegDashUri();

    List<Uri> mp4ProgressiveDownloadUris = mp4AssetFiles.Select(af => af.GetSasUri()).ToList();

 

    // 6. Get the asset URLs.

    Console.WriteLine(smoothStreamingUri);

    Console.WriteLine(hlsUri);

    Console.WriteLine(mpegDashUri);

    mp4ProgressiveDownloadUris.ForEach(uri => Console.WriteLine(uri));

 

    Console.WriteLine("Output asset available for adaptive streaming and progressive download.");

 

    string outputFolder = "job-output";

    if (!Directory.Exists(outputFolder))

    {

        Directory.CreateDirectory(outputFolder);

    }

 

    Console.WriteLine("Downloading output asset files to local folder...");

 

    // 7. Download the output asset to a local folder.

    outputAsset.DownloadToFolder(

        outputFolder,

        (af, p) =>

        {

            Console.WriteLine("Downloading '{0}' - Progress: {1:0.##}%", af.Name, p.Progress);

        });

 

    Console.WriteLine("Output asset files available at '{0}'.", Path.GetFullPath(outputFolder));

 

    Console.WriteLine("VOD workflow finished.");

}

catch (Exception exception)

{

    // Parse the XML error message in the Media Services response and create a new 

    // exception with its content.

    exception = MediaServicesExceptionParser.Parse(exception);

 

    Console.Error.WriteLine(exception.Message);

}

finally

{

    Console.ReadLine();

}

As usual, if you want to report a bug, send feedback or propose new extensions, you can open a new issue in the azure-sdk-for-media-services-extensions GitHub repository.

Enjoy!

More helpers added to WindowsAzure.MediaServices.Extensions NuGet package

This week I published a new version of the WindowsAzure.MediaServices.Extensions NuGet package (v1.0.5), which includes some great contributions from Martin Cabral who has also been working extensively with Windows Azure Media Services here at Southworks. He put together all the available Media Processors names and Task Preset Strings for Windows Azure Media Encoder in a series of string constants. This way, you don’t need to search the Web for these special string values when you want to submit a job. Instead, you can use IntelliSense right from your Visual Studio coding window to list them along with their descriptions.

Media Processor names available as string constants

Media Encoder Task Preset Strings available as string constants

Additionally, I added a sample Console application showing how easy it is to write a VOD workflow leveraging the Extensions for Windows Azure Media Services .NET SDK NuGet package. You can find the SampleWorkflowUsingExtensions sample source code in the azure-sdk-for-media-services-extensions GitHub repository.

SampleWorkflowUsingExtensions sample

If you have any feedback, suggestions or new extensions/helpers to propose, please feel free to open an issue in the azure-sdk-for-media-services-extensions GitHub repository.

Enjoy!

New Extensions available in WindowsAzure.MediaServices.Extensions NuGet package

Yesterday I published a new version of the WindowsAzure.MediaServices.Extensions NuGet package (1.0.4). This version includes new extension methods proposed by Mingfei Yan (Program Manager in Microsoft Windows Azure Media Team) in this GitHub issue, and a different organization of the code into separate classes.

The following are the changes and new features available in this version (you can find the full documentation in the README document):

Split extension methods and helpers in separate classes

The MediaServicesExtensionsMediaServicesExtensions.cs file was split into the following files to have a better organization of the code:

  • MediaServicesExtensionsAssetExtensions.cs: Contains useful extension methods and helpers related to the IAsset interface.
  • MediaServicesExtensionsJobExtensions.cs: Contains useful extension methods and helpers related to the IJob interface.
  • MediaServicesExtensionsLocatorExtensions.cs: Contains useful extension methods and helpers related to the ILocator interface.
  • MediaServicesExtensionsUrlExtensionsFixture.cs: Contains extension methods and helpers related to the Uri and String classes.

Get Job overall progress

Get the overall progress of a job by calculating the average progress of all its tasks using a single extension method for the IJob interface.

   1: CloudMediaContext context = new CloudMediaContext("%accountName%", "%accountKey%");

   2:  

   3: // The input asset for the task. Get a reference to it from the context.

   4: IAsset inputAsset = null;

   5:  

   6: // Prepare a job ready to be submitted with a single task with one 

   7: // input/output asset using a single extension method.

   8: IJob job = context.PrepareJobWithSingleTask(

   9:     "Windows Azure Media Encoder",

  10:     "H264 Adaptive Bitrate MP4 Set 720p",

  11:     inputAsset,

  12:     "OutputAssetName", 

  13:     AssetCreationOptions.None);

  14:  

  15: // Submit the job.

  16: job.Submit();

  17:  

  18: // ...

  19:  

  20: // Refresh the job instance.

  21: job = context.Jobs.Where(j => j.Id == job.Id).First();

  22:  

  23: // Get the overall progress of the job by calculating the average progress

  24: // of all its tasks using a single extension method. 

  25: double jobOverallProgress = job.GetOverallProgress();

Start Job execution progress task to notify when its state or overall progress change

Start a Task to monitor a job progress using a single extension method for the IJob interface. The difference with the IJob.GetExecutionProgressTask method is that this extension invokes a callback when the job state or its overall progress change. There is an additional overload with different parameters.

   1: CloudMediaContext context = new CloudMediaContext("%accountName%", "%accountKey%");

   2:  

   3: // The input asset for the task. Get a reference to it from the context.

   4: IAsset inputAsset = null;

   5:  

   6: // Prepare a job ready to be submitted with a single task with one 

   7: // input/output asset using a single extension method.

   8: IJob job = context.PrepareJobWithSingleTask(

   9:     "Windows Azure Media Encoder",

  10:     "H264 Adaptive Bitrate MP4 Set 720p",

  11:     inputAsset,

  12:     "OutputAssetName",

  13:     AssetCreationOptions.None);

  14:  

  15: // Submit the job.

  16: job.Submit();

  17:  

  18: // Start a task to monitor the job progress by invoking a callback when

  19: // its state or overall progress change in a single extension method.

  20: job = await context.StartExecutionProgressTask(

  21:     job,

  22:     j =>

  23:     {

  24:         Console.WriteLine("Current job state: {0}", j.State);

  25:         Console.WriteLine("Current job progress: {0}", j.GetOverallProgress());

  26:     },

  27:     CancellationToken.None);

Get SAS URL for Asset File

Get the SAS URL of an asset file for progressive download using a single extension method for the IAssetFile interface. This methods requires the parent asset to contain a SAS locator for the asset; otherwise it returns null.

   1: // The asset with multi-bitrate MP4 content. Get a reference to it from the context.

   2: IAsset asset = null;

   3:  

   4: // Make sure to create a SAS locator for the asset.

   5:  

   6: // Get the SAS URL for progressive download of an MP4 asset file.

   7: IAssetFile assetFile = asset

   8:     .AssetFiles

   9:     .ToList()

  10:     .Where(af => af.Name.EndsWith(".mp4", StringComparison.OrdinalIgnoreCase))

  11:     .First();

  12: Uri sasUri = assetFile.GetSasUri();

Save Uri to file

Save an Uri to a local file using a extension method for the Uri class. It creates the file if does not exist; otherwise, appends a new line to the end.

   1: // The asset with multi-bitrate MP4 content. Get a reference to it from the context.

   2: IAsset asset = null;

   3:  

   4: // Make sure to create an Origin locator for the asset.

   5: // Make sure to create a SAS locator for the asset.

   6:  

   7: IEnumerable<IAssetFile> mp4AssetFiles = asset

   8:         .AssetFiles

   9:         .ToList()

  10:         .Where(af => af.Name.EndsWith(".mp4", StringComparison.OrdinalIgnoreCase));

  11:  

  12: // Get the adaptive streaming URL's for the asset.

  13: Uri smoothStreamingUri = asset.GetSmoothStreamingUri();

  14: Uri hlsUri = asset.GetHlsUri();

  15: Uri mpegDashUri = asset.GetMpegDashUri();

  16: List<Uri> mp4ProgressiveDownloadUris = mp4AssetFiles.Select(af => af.GetSasUri()).ToList();

  17:  

  18: string filePath = @"C:asset-urls.txt";

  19:  

  20: // Save the URL's to a file.

  21: smoothStreamingUri.Save(filePath);

  22: hlsUri.Save(filePath);

  23: mpegDashUri.Save(filePath);

  24: mp4ProgressiveDownloadUris.ForEach(uri => uri.Save(filePath));

 

If you have feedback, improvements or new extensions/helpers to propose, please feel free to open a new issue in the azure-sdk-for-media-services-extensions GitHub repository.

Enjoy!

New NuGet package available with extensions for Windows Azure Media Services .NET SDK

Windows Azure Media Services

During my last projects in Southwoks I had the chance to work with Windows Azure Media Services since the early beginning (with both the .NET SDK and the REST API). In all these projects I found myself writing the same common code to implement different media workflows, like creating an asset from a local file or folder, preparing a transcoding job, creating a locator for the output asset, generating the adaptive streaming URLs (leveraging dynamic packaging) and downloading the asset files. Of course, all this code is available and well documented in MSDN and the .NET SDK GitHub project samples, but I wanted to have it available in a more consumable and reusable way instead of having to search/copy/paste/tweak the code every time.

To make my work easier, I put together all this commonly used code in a series of extension methods and helpers for the Windows Azure Media Services .NET SDK and created the following NuGet package with them:

This NuGet package creates a MediaServicesExtensions folder in your project’s root directory containing the source code of these extension methods and helpers. This way you can easily review the implementation and tweak it if needed. To see the complete list of the extension methods and helpers with their usage, you can review the README document in the azure-sdk-for-media-services-extensions GitHub project.

The following sample code shows how easy is to write a basic media workflow using the Extensions for Windows Azure Media Services .NET SDK NuGet package:

   1: try

   2: {

   3:     CloudMediaContext context = new CloudMediaContext("%accountName%", "%accountKey%");

   4:  

   5:     // 1. Create a new asset by uploading a mezzanine file from a local path.

   6:     IAsset inputAsset = context.CreateAssetFromFile(

   7:         @"D:Mezzanine.mp4",

   8:         AssetCreationOptions.None);

   9:  

  10:     // 2. Prepare a job with a single task to transcode the previous mezzanine asset

  11:     //    into a multi-bitrate asset.

  12:     IJob job = context.PrepareJobWithSingleTask(

  13:         "Windows Azure Media Encoder",

  14:         "H264 Adaptive Bitrate MP4 Set 720p",

  15:         inputAsset,

  16:         "MultiBitrateAsset",

  17:         AssetCreationOptions.None);

  18:  

  19:     // 3. Submit the job and wait until it is completed.

  20:     job.Submit();

  21:     job.GetExecutionProgressTask(CancellationToken.None).Wait();

  22:  

  23:     // 4. Get the output asset of the job and publish it by creating an origin locator.

  24:     IAsset outputAsset = job.OutputMediaAssets[0];

  25:     context.CreateLocator(

  26:         outputAsset,

  27:         LocatorType.OnDemandOrigin,

  28:         AccessPermissions.Read,

  29:         TimeSpan.FromDays(30));

  30:  

  31:     // 5. Generate the Smooth Streaming, HLS and MPEG-DASH URLs for adaptive streaming.

  32:     Uri smoothStreaminUri = outputAsset.GetSmoothStreamingUri();

  33:     Uri hlsUri = outputAsset.GetHlsUri();

  34:     Uri mpegDashUri = outputAsset.GetMpegDashUri();

  35:  

  36:     // 6. Download the output asset files to a local folder.

  37:     DirectoryInfo outputDirectory = Directory.CreateDirectory(@"C:MultiBitrateAsset");

  38:     context.DownloadAssetFilesToFolder(outputAsset, outputDirectory.FullName);

  39: }

  40: catch (Exception exception)

  41: {

  42:     // Parse the XML error message in the Media Services response and create a new 

  43:     // exception with its content.

  44:     exception = MediaServicesExceptionParser.Parse(exception);

  45:  

  46:     Trace.TraceError(exception.Message);

  47: }

Feedback and contributions to the GitHub project are always welcome.

Enjoy!

Using the Windows Phone Storage NuGets for Windows Azure (Video)

While working with the Windows Phone Storage NuGet packages for Windows Azure with Damián Martinez Gelabert and Mariano Converti, we created a video to showcase these NuGet packages which Wade Wegner publishes to Channel 9.

Topics in the video:

  • Creating a new project, adding, and configuring the Storage NuGet package.
  • Walkthrough the Storage Sample NuGet package (Blobs, Tables and Queues).
  • Why you should use the proxies’ services?
  • Installing and configuring the proxies’ services.
  • Configuring your Windows Phone application to work with the proxies.
  • Using ACS with the Windows Phone Storage NuGet Packages.
  • Configuring your proxies and your Windows Phone application to use ACS.

If you haven’t seen it, here is your chance.

video

Windows Azure Toolkit for Windows Phone NuGet packages

watpw

Recently Wade Wegner and his team, release the NuGet version of their Windows Azure Toolkit for Windows Phone with lots of improvements including one of the most important, modularization of all the things that the toolkit provides. In this post, I will focus on the new WindowsAzure.Storage.Proxy NuGet package, which provides the Windows Azure Proxies with Anonymous Authentication.

The Windows Azure Toolkit for Windows Phone provides a group of different tools to scale your device application in a really easy way. One of the most important tools that you can use is the Windows Azure proxies. This simple but powerful module provides a way to use a Storage Account from any kind of client without the need to share your private key and compromise your account. These proxies are used by the Android Toolkit and the iOS toolkit, and even you should use them from a Rich Desktop client, since it’s not recommended to use your account settings in any kind of application that any kind of user can easily reflect.

The idea behind these proxies is really simple. All the traffic that passes thought them is redirect to the real Azure Storage adding the Authentication and the date headers needed to work with the service. In the case of Blob Storage instead of using this strategy, the proxies use the Shared Access Signature service to work directly with the real Storage.

Additionally, the proxies support adding authentication to the requests using ASP.Net Membership, Access Control Service, or even your own custom authentication in a really easy way. For example using Membership or ACS is as simple as install one more NuGet package and configure just one line in the “Activator” as it’s shown in the NuGet package readme file.

If you prefer writing a custom authentication method, you can also modify the same line using your own implementation.

Using an authentication method is a must since if your proxy is anonymous; it can be used by anyone with no control by yourself, which can give you lots of headaches. Take into consideration for instance, that the blob storage is paid by usage.

The proxies also provide an extensible point to add Authorization to the request. With this feature you can have much more control for your proxies, your account and your application. To add some kind of authorization method go to the “activator” class and just set the AuthorizeTablesAccess, AuthorizeQueuesAccess or/and the AuthorizeBlobsAccess properties with your own authorization method.

List of available NuGet packages
Windows Azure Storage Client for Windows Phone:

Windows Azure Storage proxies:

Push Notification resources for Windows Phone:

Push Notification Registration Web Service:

The *.Membership and the *.AccessControl packages add Authentication using Membership or ACS respectively. Additionally, the *.Sample or the *.BasePage packages shows some examples of how to use the libraries.

Finally, you can find the source code of these NuGet packages at github.

Introducing ‘Silverlight Activator for Windows Phone’ NuGet Package

NuGet-WindowsPhoneOver the past few months, I’ve been working with Damián Martinez Gelabert, Nicolás Bello Camilletti, Johnny Halife, Juan Pablo García and Mauro Krikorian in a set of NuGet packages for ASP.NET MVC 3 and Silverlight for Windows Phone projects, targeting server-side and client-side pieces of the same scenario. During the development, we realized that we needed to run initialization code at startup to configure some settings required for our NuGet packages. Additionally, we had a hierarchy of NuGet packages and each of them needed to register its own settings to extend the original scenario.

Resolving these requirements in server-side NuGet packages was easy by using WebActivator as a dependency. This package provides a clean solution for this problem (without having to manually modify the Global.asax file) by letting you use multiple PreApplicationStartMethod and PostApplicationStartMethod assembly attributes to indicate the methods that are intended to run at startup (notice that the Pre and Post attributes already give an implicit execution order). For more information on this package you can check the WebActivator wiki.

On the Windows Phone client-side, however, we couldn’t find a package that did a similar thing. We wanted to have the same attribute-based clean experience to specify the code that should run at startup, to avoid being intrusive modifying existing application code. That is why we created the Silverlight Activator for Windows Phone package, to enable these features in Windows Phone applications.

How do I use it in my NuGet packages?

As I already mentioned, the idea behind this package is to allow other Silverlight for Windows Phone NuGet packages to execute some code when the application is starting up and/or closing. So, if you are planning to build a NuGet package with these requirements, you can leverage the Silverlight Activator for Windows Phone NuGet package as follows:

  1. In your package’s .nuspec file, add a dependency on SilverlightActivator (version 1.0.4).
    <?xml version="1.0"?>
    <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
      <metadata>
        <id>YourSilverlightForWindowsPhonePackageId</id>
        <version>1.0.0</version>
        <!-- ... -->
        <dependencies>
          <dependency id="SilverlightActivator" version="1.0.4" />
        </dependencies>
      </metadata>
    </package>
    
  2. For consistency with WebActivator, include an App_Start folder to place all your activation classes.
  3. Under this folder, add the source code files with your initialization / finalization code as shown below (make sure to add the .pp extension to the file so it can be preprocessed).
    [assembly: SilverlightActivator.ApplicationStartupMethod(
        typeof($rootnamespace$.App_Start.MyClassInitializer),
        "ApplicationStartup",
        Order = 10)]
    [assembly: SilverlightActivator.ApplicationExitMethod(
        typeof($rootnamespace$.App_Start.MyClassInitializer),
        "ApplicationExit",
        Order = 10)]
    
    namespace $rootnamespace$.App_Start
    {
        public class MyClassInitializer
        {
            public static void ApplicationStartup()
            {
                // TODO: Add your startup code.
            }
    
            public static void ApplicationExit()
            {
                // TODO: Add your finalization code.
            }
        }
    }

The previous sample code uses the two available assembly attributes: ApplicationStartupMethod and ApplicationExitMethod. Take into account the following when using these attributes:

  • The methods marked by the attributes must be static.
  • The methods marked with the ApplicationStartupMethod attribute will be executed when the Application.Startup event is risen
  • The methods marked with the ApplicationExitMethod attribute will be executed when the Application.Exit event is risen
  • The Order property in the attributes lets you control the execution order of the methods. If not specified, its value is set to int.MaxValue by default (so it is the last to run).

How does it work?

The Silverlight Activator for Windows Phone package is composed of the following things:

  1. The SilverlightActivator library, which contains the ActivationManager class with the logic to handle the activation logic based on the ApplicationStartupMethod and ApplicationExitMethod assembly attributes.
  2. A transformation for the App.xaml file, which adds an application resource of type ActivationManager.

By adding the ActivationManager as an application resource, we are making sure that one instance of this class is created to register handlers for the Application.Startup and Application.Exit events.

public class ActivationManager
{
    private static bool initialized = false;
    // ...

    public ActivationManager()
    {
        if (!initialized)
        {
            Init();
            initialized = true;
        }
    }

    // ...

    private static void Init()
    {
        Application.Current.Startup += (s, e) => RunApplicationStartupMethods();
        Application.Current.Exit += (s, e) => RunApplicationExitMethods();
    }

    // ...
}

 

This way, when the Silverlight for Windows Phone engine rises those events, the ActivationManager iterates throughout all loaded assemblies in the phone application looking for ApplicationStartupMethod and ApplicationExitMethod assembly attributes and invoking the corresponding methods.

public class ActivationManager
{
    // ...
    private static bool loadAssemblyParts = false;
    private static bool assemblyPartsLoaded = false;

    // ...

    private static void RunActivationMethods<T>() where T : BaseActivationMethodAttribute
    {
        var deploymentParts = Deployment.Current.Parts.Cast<AssemblyPart>();

        // Load all deployment assembly parts based on the LoadAssemblyParts flag
        // in case they include activation attributes.
        if (loadAssemblyParts && !assemblyPartsLoaded)
        {
            Load(deploymentParts);
            assemblyPartsLoaded = true;
        }

        // Filter loaded assemblies to only get the deployment assembly parts.
        var activationAssemblies = AppDomain.CurrentDomain.GetAssemblies()
            .Where(a => deploymentParts.Any(p => p.Source.Equals(a.ManifestModule.Name, StringComparison.OrdinalIgnoreCase))
                        && (a != typeof(ActivationManager).Assembly))
            .ToList();
        var activationAttributes = new List<T>();

        // Iterate throughout all the loaded deployment assembly parts to look for activation attributes.
        activationAssemblies.ForEach(assembly => activationAttributes.AddRange(assembly.GetActivationAttributes<T>()));

        // Execute activation methods according to the order specified.
        foreach (var attribute in activationAttributes.OrderBy(at => at.Order))
        {
            attribute.InvokeMethod();
        }
    }

    // ...
}

 

By default, the ActivationManager class will not discover assembly attributes in assembly parts that are not loaded into the application domain. If you want to change this to let the ActivationManager load all the assembly parts at startup to look for activation attributes, you only need to set the LoadAssemblyParts property to True in the App.xaml application resource.

<?xml version="1.0" encoding="utf-8"?>
<Application
    x:Class="PhoneApp2.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:silverlightactivator="clr-namespace:SilverlightActivator;assembly=SilverlightActivator">
    
    <!--Application Resources-->
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
            </ResourceDictionary.MergedDictionaries>
            
            <silverlightactivator:ActivationManager 
                x:Name="SilverlightActivator"
                LoadAssemblyParts="True" />
        </ResourceDictionary>
    </Application.Resources>
    
    <!--...-->
</Application>

 

Feedback is always welcome,

Mariano