Blog:

1/2 Azure Media Services: Automatically Encode and Publish Media

Published on Mar 22, 2021
By Vladimir / Software Engineer

In recent years, quality of pictures and video has developed tremendously up to a point where even smartphones can be used to capture movie-quality footage. Unfortunately, the size of media files has also increased to match and has made the delivery of videos within a web app an ever so relevant issue. Luckily, Azure Media Services (AMS) makes it easy to tackle this and significantly more.

While AMS provides a ton of neat capabilities, in my experience there is some lack of resources, outlining how to use these in a practical way. This is where this article comes in, explaining the process of setting up an automated solution, which takes a video and prepares it for use in a web app without any additional effort from the user, apart from providing a file.

Table of Contents

  • What is encoding and why do we need it?
  • Why automate the encoding process?
  • Azure Portal Setup
  • So, what does the function look like?
    • Detect a blob has been uploaded to a container
    • Create an empty asset, which will be used as input for the transformation
    • Copy the blob from step 1 into the asset from step 2
    • Set up a transform, which defines the encoding, outputs, etc.
    • Set up a job, which executes the encoding process
    • Publish the asset so that it is accessible over a URL
    • The rest of the code
  • Using the encoded and published video file
  • Should I keep anything particular in mind when opting for this approach?

What is encoding and why do we need it?

From the AMS article ‘Encoding video and audio with Media Services’:

“The term encoding in Media Services applies to the process of converting files containing digital video and/or audio from one standard format to another, with the purpose of (a) reducing the size of the files, and/or (b) producing a format that’s compatible with a broad range of devices and apps. This process is also referred to as video compression, or transcoding.“

AMS allow the user to upload a raw file straight from a camera and compress it to a lighter format and resolution(s), which is meant to be used in a web environment. AMS also supports functionalities such as provision of multiple audio or subtitle tracks for videos, live streaming and many others, but these are not going to be the focus of this post.

Why automate the encoding process?

Encoding files through the Azure Portal is pretty easy and convenient, if it is being done just for a single file. With more files at hand, the task of converting them all quickly becomes rather time consuming.

To tackle this, I decided to implement a solution which automates the whole process. I am using an Azure Function (v3), which gets triggered once a blob is uploaded to a particular container and then initiates the encoding process for the said file. In my case, the output is an asset, which contains all the popular video formats (1080p, 720p, 540p, 360p, 270p and 180p) that are smaller than the input file’s original format.

The code is based on the official documentation for the Azure Functions (v3) and Azure Media Services (v3) as well as the useful examples provided by Azure in this repository.

Azure Portal Setup

You will need a set up AMS instance, as well as a Function App. Keep in mind the Media Services and Functions will both incur some small charges on your subscription.

Before we get to the code itself, some configuration is necessary.

Navigate to your AMS account and then to the API Access section (1). Within this section, select ‘Service principal authentication’ (2) and then the registered app (3) – which is going to be used by the function to authenticate before the Media Services – together with a secret. For instructions on how to make an app registration, consult the relevant documentation pages.

Just a bit lower in the same screen, you will see some connection information about your Media Services.

Take a note of the values within this screen. For example, select the JSON tab and copy the JSON object content to some file.

Next, head over to the function app where the encoding function will be deployed. Navigate to the Configuration screen (it’s a subsection of Settings). Add the properties, which the MediaServicesConfigWrapper* and MediaServiceClientCredentials* use, and insert the values, taken from the Media Service API access screen earlier. These should be:

  • SubscriptionId
  • ResourceGroup
  • AccountName (the Media Services account name)
  • Region (this is called Location in the API access screen)
  • AadEndpoint
  • AadTenantId
  • AadClientId
  • AadSecret
  • ArmEndpoint
  • ArmAadAudience

*Description of both classes can be found here or further down in this post.

So, what does the function look like?

I am implementing my solution in Visual Studio Code, but the code below can be used for development in the Azure Portal as well with slight adjustments.

The whole encoding process can be outlined in the following steps:

  1. Detect a blob has been uploaded to a container
  2. Create an empty asset, which will be used as input for the transformation
  3. Copy the blob from step 1 into the asset from step 2
  4. Set up a transform, which defines the encoding, outputs, etc.
  5. Set up a job, which executes the encoding process
  6. Publish the asset so that it is accessible over a URL
  7. Use the published URL in an application (more on that a bit later in the article)

Last piece of the preparation process before diving in the steps outlined above:

Naturally, some dependencies are also necessary. My .csproj file includes the following ones:

<ItemGroup>
  <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage" Version="3.0.6" />
  <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.6" />
  <PackageReference Include="Microsoft.Azure.Management.Media" Version="2.0.4" />
  <PackageReference Include="Microsoft.Rest.ClientRuntime.Azure.Authentication" Version="2.4.0" />
  <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="5.6.0" />
</ItemGroup>

The classes BlobStorageHelper, MediaServiceClientCredentials, MediaServicesConfigWrapper and MediaServicesHelper are referenced in quite a few places in the code below. These files are generally similar to the files, which can be found in the Azure Repo referenced above, as the actions performed in them are basically what the documentation advises to be done. Below is the code I used for them:

BlobStorageHelper.cs

using System;

using Microsoft.WindowsAzure.Storage.Blob;
using System.Threading.Tasks;

namespace Fourtress.SharedLibs
{
    public class BlobStorageHelper
    {
        static public Task<string> CopyBlobAsync(CloudBlob sourceBlob, CloudBlob destinationBlob)
        {
            var signature = sourceBlob.GetSharedAccessSignature(new SharedAccessBlobPolicy
            {
                Permissions = SharedAccessBlobPermissions.Read,
                SharedAccessExpiryTime = DateTime.UtcNow.AddHours(24)
            });
            return destinationBlob.StartCopyAsync(new Uri(sourceBlob.Uri.AbsoluteUri + signature));
        }
    }
}

MediaServiceClientCredentials.cs

using System;

namespace Fourtress.SharedLibs
{
    public class MediaServiceClientCredentials
    {
        public Uri AadEndpoint
        {
            get { return new Uri(Environment.GetEnvironmentVariable("AadEndpoint")); }
        }

        public string AadTenantId
        {
            get { return Environment.GetEnvironmentVariable("AadTenantId"); }
        }

        public string AadClientId
        {
            get { return Environment.GetEnvironmentVariable("AadClientId"); }
        }

        public string AadClientSecret
        {
            get { return Environment.GetEnvironmentVariable("AadSecret"); }
        }

        public Uri ArmEndpoint
        {
            get { return new Uri(Environment.GetEnvironmentVariable("ArmEndpoint")); }
        }

        public Uri ArmAadAudience
        {
            get { return new Uri(Environment.GetEnvironmentVariable("ArmAadAudience")); }
        }
    }
}

MediaServicesConfigWrapper.cs

using System;

namespace Fourtress.SharedLibs
{
    public class MediaServicesConfigWrapper
    {
        public MediaServiceClientCredentials mediaServiceClientCredentials = new MediaServiceClientCredentials();

        public string SubscriptionId
        {
            get { return Environment.GetEnvironmentVariable("SubscriptionId"); }
        }

        public string ResourceGroup
        {
            get { return Environment.GetEnvironmentVariable("ResourceGroup"); }
        }

        public string AccountName
        {
            get { return Environment.GetEnvironmentVariable("AccountName"); }
        }

        public string Region
        {
            get { return Environment.GetEnvironmentVariable("Region"); }
        }
    }
}

MediaServicesHelper.cs

using System.Threading.Tasks;

using Microsoft.Rest;
using Microsoft.Rest.Azure.Authentication;
using Microsoft.Azure.Management.Media;
using Microsoft.IdentityModel.Clients.ActiveDirectory;

namespace Fourtress.SharedLibs
{
    public class MediaServicesHelper
    {
        public static IAzureMediaServicesClient CreateMediaServicesClientAsync(MediaServicesConfigWrapper config)
        {
            var credentials = GetCredentialsAsync(config).Result;

            return new AzureMediaServicesClient(config.mediaServiceClientCredentials.ArmEndpoint, credentials)
            {
                SubscriptionId = config.SubscriptionId,
            };
        }

        private static async Task<ServiceClientCredentials> GetCredentialsAsync(MediaServicesConfigWrapper config)
        {
            ClientCredential clientCredential = new ClientCredential(config.mediaServiceClientCredentials.AadClientId, config.mediaServiceClientCredentials.AadClientSecret);
            return await ApplicationTokenProvider.LoginSilentAsync(config.mediaServiceClientCredentials.AadTenantId, clientCredential, ActiveDirectoryServiceSettings.Azure);
        }
    }
}


Click here for the second and last part. To start with the actual encoding process.