Tag Archives: .NET Framework 4.5

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!

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!

[Spanish] Grabación disponible de webcast: Modernización de aplicaciones WPF de línea de negocio

For those who don’t read Spanish, this blog post provides details about a Spanish speaking webcast that Damian Schenkelman and I presented last week for the MSDN Latin American community.


Eventos y Webcasts de MicrosoftLa semana pasada junto con mi amigo y compañero de trabajo Damian Schenkelman presentamos un webcast sobre cómo utilizar algunas de las últimas tecnologías Microsoft para desarrollar aplicaciones Windows Presentation Foundation (WPF) 4.5. Antes que nada, queremos agradecerles a todos los que asistieron al evento y les repetimos que pueden contactarnos (@dschenkelman y @mconverti) en caso de que tengan preguntas o dudas sobre las tecnologías que presentamos.

Para los que no pudieron asistir, les dejamos a continuación los detalles del evento y los links para descargar la grabación o verla online:

El material que utilizamos para la presentación también esta disponible para ser descargado desde nuestro repositorio en GitHub:

Material Webcast: 'Modernización de aplicaciones WPF de línea de negocio'

Por úlitmo, les dejamos a continuación los links a los recursos adicionales utilizados para la charla:

Enjoy!