DotNetCurry Logo

Building a YouTube like Media Portal using ASP.NET MVC and Azure Media Services

Posted by: Sumit Maitra , on 8/2/2013, in Category Microsoft Azure
Views: 81776
Abstract: Azure Media Services offer almost all the ingredients to enable you make a YouTube clone. Today we explore how to make one using ASP.NET MVC, Azure Media Services and the Player Framework JavaScript client.

Windows Azure has introduced a nice set of services on top of the Azure platform. These include the Mobile Services, the Service Bus, Media Services among a host of others.

Media Services primarily offers on demand streaming, variable bit rate or smooth streaming, encoding to various formats including smooth streaming and storage capabilities. Under, but under the covers, it uses Azure App Fabric for compute and Blob Storage for hosting data. Thus it’s a Platform as a Service(PaaS) offering additional capabilities over infrastructure provided Azure.

Today we’ll see how to leverage Media Services to build an ASP.NET MVC application that allows users to upload and encode their videos in a Web Portal and playback the content on demand.

 

The Application Architecture

application-achitecture

The diagram above gives us an overall view of how we can use Azure Media Services. The major steps involved are:

1. User logs into the ASP.NET MVC Web Application

2. Uploads their Video ‘assets’ to Azure Blob storage. The fact that it is going to Blob storage is transparent to the user, it’s a normal file upload for them.

3. Once uploaded, we can optionally encode the Video for smooth streaming.

4. Finally when the video (either smooth streaming version or the progressive download version) is requested by the user, it is streamed back to them.

5. On the client side, a Browser plugin is used to serve up the content.

With the basic premise out of the way, let’s get started with our application.

Setting up the ASP.NET MVC App and Pre-Requisites

Pre-Requisites – Setting up Azure Media Service

- We’ll need an active Azure account, if you don’t have one, you can avail a 90 day free trial at http://www.windowsazure.com. Keep an eye out for outgoing data and encoding charges if used.

- Login to the Azure Management Portal and click on the [+ New] button in the bottom toolbar and add a new Media Service by navigating as follows

App Service > Media Service > Quick Create

new-media-service

- In the Quick Create panel, provide the Name of your media service, the Region where you want it to be hosted and the Azure Storage account to use. If you don’t have a Storage Account already, you’ve to create a new one, else you can pick one of your existing Storage Accounts.

- Finally click on the ‘Create Media Service’ button to initiate service creation. Once the service is created, we’ll see a new Media Service and new Storage Service (if you opted for a new one) created.

new-media-created

Note: I am using an existing media service and storage account hence the names are different from the ‘Create New’ panel.

- Final step is to obtain the Service Keys. Click on the Media Service from the ‘All Items’ list.

service-created

Here you have multiple options to retrieve the Keys, you can download the sample project or click on the Manage Keys button to just view the keys.

media-keys

Note, the keys and save them securely for easy access later in the app. We are now set on the Azure side. Let’s setup our MVC Application.

Setting up the MVC Application – Dependencies and Model design

Project Setup and Dependencies

To setup the application, we start off with the ASP.NET MVC 4 template and use the ‘Internet’ project type. This gives us the forms authentication module out of the box. Once the project is setup, we add the following Nuget package for the media services dependencies

PM> install-package WindowsAzure.MediaServices

This installs all packages necessary to use Azure Media Services APIs

Model Design

Our data model will be simple for this example. We’ll save the UserId, Title, FileUrl and a Boolean indicating if the file is visible to others, for every media asset uploaded.

public class MediaElement
{
    public int Id { get; set; }
    public string UserId { get; set; }
    public string Title { get; set; }
    public string AssetId { get; set; }
    public bool IsPublic { get; set; }
}

We’ll generate the CRUD pages using the default ASP.NET MVC Scaffolding as follows

- Build the application

- Right click on Controller folder and select ‘Add’ > ’New Controller’

- Update the Template to use Entity Framework and the Model to use the MediaElement entity. Provide a new Data Context Class name and click Add to complete the codegen.

generate-crud-screens

Updating Controller to use Authorization

Once the code is generated open the MediaController class add the Authorize attribute so that all actions can executed only when a user is logged in.

[Authorize]
public class MediaController : Controller
{

}

Updating Home Page to include My Media tab

Open the _Layout.cshtml file and add an Action Link to navigate to the Media Browser

<li>@Html.ActionLink("My Media", "Index", "Media")</li>

You can also update the Title, remove the About and Contact links etc.

updated-layout-cshtml

Update the Index.html for the Home Controller (the landing page) by replacing the generic markup with something indicating that we have a ‘super special’ Media hosting Application.

updated-home-page

This wraps up the basics, let’s dive in and see how we can implement the Media management part.

Integrating Media Services

Uploading Media files in chunks

As per our architecture diagram, user login has been implemented thanks to ASP.NET project template. Next thing to implement is the uploading of Media.

Media files, especially video files, can be large, spanning hundreds of MBs. As a result, it is not possible to upload these files in one go due to limitation of Request size imposed by IIS. So we will upload the media files in chunks. The topic of uploading files in chunk has been discussed in details in our article Uploading Big files to Azure Storage from ASP.NET MVC. I will use the same technique, so I won’t be reproducing the code here. If you are new to Azure Cloud Storage, I suggest you go through the previous article.

Adding an Upload Client

We will modify the default Application flow by changing the ‘Create’ link to ‘Upload’ in the Index.cshtml and navigating to a New page called Upload.

<p>
    @Html.ActionLink("Upload New Media", "Upload")
</p>

Views/Media/Index.cshtml

[HttpGet]
public ActionResult Upload()
{
    return View();
}

Controllers/MediaController.cs

@{
    ViewBag.Title = "Upload";
}
<h2>Upload New Media</h2>
@using (Html.BeginForm())
{
    <fieldset>
        <legend>Media Element</legend>

        <div class="editor-label">
            Select Media File to Upload:
        </div>
        <div class="editor-field">
            <input type="file" id="selectFile" value=" " />
            <input type="button" id="fileUpload" value="Upload" />
        </div>
        <div id="progressBar" style="width: 50%; height: 20px; background-color: grey"></div>
        <br />
        <label id="statusMessage"></label>
    </fieldset>
}
<div>
    @Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
    <script src="~/Scripts/media-upload.js"></script>
    @Scripts.Render("~/bundles/jqueryui")
    @Scripts.Render("~/bundles/jqueryval")
}

Views/Media/Upload.cshtml

Add a JavaScript file media-upload.js and copy the script over from the Chunked Upload sample. Ensure the POST is going to the correct URL i.e. /Media/SetMetaData and /Media/UploadChunk.

Add reference to this script in the Upload.cshtml (as shown above). For the progress bar to show up properly, update the _Layout.cshtml to add the following css bundle

@Styles.Render("~/Content/themes/base/css")

Creating the Blob Storage Connection String

Before we continue we need to build the Blob Storage’s connection string. This is easy once you know the format:

DefaultEndpointsProtocol=<connectionType>;AccountName=<blobStorageAccountName>;AccountKey=<blobStorageAccessKey>

1. ConnectionType is either http or https

2. AccountName is the name of the storage account associated with your Media Service. You can go to your Azure Portal, Select Media Services (1) tab on the left, go to Linked Resources tab (2) and pick the name of the Storage account (3)

media-service-linked-resources

3. Account Key: In the above screen, click on the Storage account name to navigate to Storage Dashboard, from the bottom toolbar, click on ‘Manage Keys’ button to bring up the Key’s dialog, Copy the Primary Access Key.

storage-account-keys

4. Now that we have got all the three components, add a key to the Web.config’s appSettings section.

<add key="StorageConnectionString" value="DefaultEndpointsProtocol=https;AccountName=mediasvcc5hww8r75gwc0;
AccountKey=o+oXVH9PEVQ3AFC6xWBQHL9diuJ7jecU10oaGyw5wRhMbdLlA9f+lfoeGOsXgYQyaxrgFq8SFSj6nfFJa96cnA==" />

Finishing off the Upload functionality

Now that we’ve got the Storage connection string, add the following keys as well

<add key="StorageContainerReference" value ="temporary-media" />
    <add key="MediaAccountName" value="mediaservicedemo"/>
    <add key="MediaAccountKey" value="hSpRS8OJuhJIktmSX9HhAeZKD+paOt05W+uSZC6Y2W8=" />

The StorageContainerReference is name of the temporary container to which media will be uploaded. The MediaAccountName is the name of the MediaService that we gave when we created the Service

MediaAccountKey is the access key, you can go to the Media dashboard and use the Manage Keys button like you did from the Storage dashboard, to copy the MediaAccountKey.

Next we add the SetMetadata, UploadCurrentChunk and CommitAllChunks methods into our MediaController.cs. Wherever we are Update the configuration strings appropriately.

If you run the app and try to upload a file, it will get uploaded to the temporary-media storage container. However our work isn’t done yet. We’ve simply uploaded it to blob storage, our Media service still doesn’t know about the video and can’t serve it up, or encode it. Add the following method in the MediaController and add call it from CommitChunks method:

private void CreateMediaAsset(CloudFile model)
{

}

This code fetches the media in the blob storage and create a new MediaService Asset out of it and copies it to a container controlled by Media Services. I have broken down the code for the above method in the following steps:

Step 1: Retrieve account keys and names

string mediaAccountName = ConfigurationManager.AppSettings["MediaAccountName"];
string mediaAccountKey = ConfigurationManager.AppSettings["MediaAccountKey"];
string storageAccountName = ConfigurationManager.AppSettings["StorageAccountName"];
string storageAccountKey = ConfigurationManager.AppSettings["StorageAccountKey"];

Step 2: Create the media service context.

CloudMediaContext context = new CloudMediaContext(MediaServicesAccountName, 
MediaServicesAccountKey);

Step 3: Create instance of the CloudStorageAccount, this is the storage account associated with the Media Service.

CloudStorageAccount storageAccount = new CloudStorageAccount(new
  StorageCredentials(MediaServicesStorageAccountName, MediaServicesStorageAccountKey),
  true);

Step 4: Create a Storage Client instance from where we need to copy the file

var cloudBlobClient = storageAccount.CreateCloudBlobClient();
var mediaBlobContainer = cloudBlobClient.GetContainerReference(cloudBlobClient.BaseUri + "temporary-media");
mediaBlobContainer.CreateIfNotExists();

Step 5: Create a new Media Asset and a Write Policy.

IAsset asset = context.Assets.Create("NewAsset_" + Guid.NewGuid(),
  AssetCreationOptions.None);
IAccessPolicy writePolicy = context.AccessPolicies.Create("writePolicy",
  TimeSpan.FromMinutes(120), AccessPermissions.Write);

Step 6: Create a Destination Location in the Media Service and get the blob handle of the destination file (blob).

ILocator destinationLocator = context.Locators.CreateLocator(LocatorType.Sas, asset,
  writePolicy);
// Get the asset container URI and copy blobs from mediaContainer to assetContainer.
Uri uploadUri = new Uri(destinationLocator.Path);
string assetContainerName = uploadUri.Segments[1];
CloudBlobContainer assetContainer =
  cloudBlobClient.GetContainerReference(assetContainerName);

Step 7: Get Blob handle of the Source File

string fileName =
  HttpUtility.UrlDecode(Path.GetFileName(model.BlockBlob.Uri.AbsoluteUri));
var sourceCloudBlob = mediaBlobContainer.GetBlockBlobReference(fileName);
  sourceCloudBlob.FetchAttributes();

Step 8: Check for rudimentary properties to ensure the source file is valid and then create the file in the designation. Initiate copy from Blob.

Note: This is actually a job and takes a few seconds to reflect on the server if you are hitting refresh continuously.

if (sourceCloudBlob.Properties.Length > 0)
{
  IAssetFile assetFile = asset.AssetFiles.Create(fileName);
  var destinationBlob = assetContainer.GetBlockBlobReference(fileName);
  destinationBlob.DeleteIfExists();
  destinationBlob.StartCopyFromBlob(sourceCloudBlob);
  destinationBlob.FetchAttributes();
  if (sourceCloudBlob.Properties.Length != destinationBlob.Properties.Length)
   Console.WriteLine("Failed to copy");
}

Step 9: Once the copy is done delete the destination locator and the write policy.

destinationLocator.Delete();
writePolicy.Delete();

Step 10: Refresh the asset by retrieving it from the context

asset = context.Assets.Where(a => a.Id == asset.Id).FirstOrDefault();
var ismAssetFiles = asset.AssetFiles.ToList()
  . Where(f => f.Name.EndsWith(".mp4", StringComparison.OrdinalIgnoreCase))
  .ToArray();

if (ismAssetFiles.Count() != 1)
  throw new ArgumentException("The asset should have only one, .mp4 file");

ismAssetFiles.First().IsPrimary = true;
ismAssetFiles.First().Update();
model.UploadStatusMessage += " Created Media Asset '" + asset.Name + "' successfully.";
model.AssetId = asset.Id;
}

Next we save the new MediaElement details for the current User.

Saving the Media Element

Once we know that the file has been saved to the Media Service, we can save the Title and Asset details to the database. To do this, we first update the CommitAllChunks method to send back the AssetId in the Json that we were returning.

return Json(new
{
    error = errorInOperation,
    isLastBlock = model.IsUploadCompleted,
    message = model.UploadStatusMessage,
    assetId = model.AssetId
});

Next we update the Update.cshtml to add a panel with the Title and Save button. This panel becomes visible once the Upload is complete and file saved to the Media Service.

<div id="detailsPanel">
    <input type="hidden" id="assetId" />
    <label id="statusMessage"></label>
    <br />
    <div>
        Title <input type="text" id="title" />
    </div>
    <button id="saveDetails">Save</button>
</div>

To toggle it’s visibility, we update the media-upload.js to hide it on document load and show it once the last chunk upload has returned successfully.

Next we add a Save method to the media-upload.js to post the AssetId and Title.

var saveDetails = function ()
{
    var dataPost = {
        "Title": $("#title").val(),
        "AssetId": $("#assetId").val()
    }
    $.ajax({
        type: "POST",
        async: false,
        contentType: "application/json",
        data: JSON.stringify(dataPost),
        url: "/Media/Save"
    }).done(function (state)
    {
        if (state.Saved == true)
        {
            displayStatusMessage("Saved Successfully");
            $("#detailsPanel").hide();
        }
        else
        {
            displayStatusMessage("Saved Failed");
        }
    });
}

This posts the data to a Save action method in our MediaController. We don’t have a Save method so far, so we add one to save the data to the database as follows:

[HttpPost]
public JsonResult Save(MediaElement mediaelement)
{
    try
    {
        mediaelement.UserId = User.Identity.Name;
        mediaelement.FileUrl = GetStreamingUrl(mediaelement.AssetId);
        db.MediaElements.Add(mediaelement);
        db.SaveChanges();
        return Json(new { Saved = true, StreamingUrl =  mediaelement.FileUrl});
    }
    catch (Exception ex)
    {
        return Json(new { Saved = false });
    }
}

Before we save the Data to the Server, we call the GetStreamingUrl method. This method does the equivalent of ‘Publishing’ data from the Web Portal. It creates an access policy that’s valid for a year and generates an appropriate URL for the uploaded media.

private string GetStreamingUrl(string assetId)
{
CloudMediaContext context = new
  CloudMediaContext(ConfigurationManager.AppSettings["MediaAccountName"],
ConfigurationManager.AppSettings["MediaAccountKey"]);
var streamingAssetId = assetId; // "YOUR ASSET ID";
var daysForWhichStreamingUrlIsActive = 365;
var streamingAsset = context.Assets.Where(a => a.Id ==
  streamingAssetId).FirstOrDefault();

IAccessPolicy accessPolicy = context.AccessPolicies.Create(streamingAsset.Name, 
  TimeSpan.FromDays(daysForWhichStreamingUrlIsActive),
   AccessPermissions.Read | AccessPermissions.List);
    
string streamingUrl = string.Empty;
var assetFiles = streamingAsset.AssetFiles.ToList();
var streamingAssetFile = assetFiles.Where(f => f.Name.ToLower().EndsWith("m3u8-aapl.ism")).FirstOrDefault();
if (streamingAssetFile != null)
{
  var locator = context.Locators.CreateLocator(LocatorType.OnDemandOrigin,
   streamingAsset, accessPolicy);
  Uri hlsUri = new Uri(locator.Path + streamingAssetFile.Name + "/manifest(format=m3u8-
   aapl)");
  streamingUrl = hlsUri.ToString();
}
streamingAssetFile = assetFiles.Where(f =>
  f.Name.ToLower().EndsWith(".ism")).FirstOrDefault();
if (string.IsNullOrEmpty(streamingUrl) && streamingAssetFile != null)
{
  var locator = context.Locators.CreateLocator(LocatorType.OnDemandOrigin,
   streamingAsset, accessPolicy);
  Uri smoothUri = new Uri(locator.Path + streamingAssetFile.Name + "/manifest");
  streamingUrl = smoothUri.ToString();
}
streamingAssetFile = assetFiles.Where(f =>
  f.Name.ToLower().EndsWith(".mp4")).FirstOrDefault();
if (string.IsNullOrEmpty(streamingUrl) && streamingAssetFile != null)
{
  var locator = context.Locators.CreateLocator(LocatorType.Sas, streamingAsset,
   accessPolicy);
  var mp4Uri = new UriBuilder(locator.Path);
  mp4Uri.Path += "/" + streamingAssetFile.Name;
  streamingUrl = mp4Uri.ToString();
}
return streamingUrl;
}

With that we have all the data we need to keep track of files uploaded by each user. Next up the media player.

Playing uploaded Media in Browser

We will leverage the excellent Player Framework project from Microsoft Media Service team. This is an OSS project on Codeplex and provides a set of clients to serve up Media along with other features like Playlist, Ad insertion and so on.

You have a variety of clients to choose, on the Web you can use HTML5 player and/or the Silverlight player. Today we’ll use the HTML5 player only.

Step 1: Download the Player Framework for HTML5 client from here. This consists of the playerframework.js and the playerframework.css both in their minified form.

Step 2: Add the style reference to _Layout.css (Bundle it as a best practice).

Step 3: Add a new JavaScript file media-player.js. It has only one function, that is to initialize the player framework client and depends on playerframework.js.

var mediaPlayer =
{
initFunction : function (window, sourceUrl)
{
  var myPlayer = new PlayerFramework.Player(window,
  {
   mediaPluginFallbackOrder: ["VideoElementMediaPlugin", "SilverlightMediaPlugin"],
   width: "480px",
   height: "320px",
   sources:
   [
    {
     src: sourceUrl,
     type: 'video/mp4;'
    }
   ]
  });
}
}

Step 4: Adding the player in the ‘Edit’ page (Edit.cshtml). Update the markup to hide the UserId, AssetId and FileUrl. These are not directly updatable by the user.

@Html.HiddenFor(model => model.Id)
@Html.HiddenFor(model => model.AssetId)
@Html.HiddenFor(model => model.FileUrl, new { id = "fileUrl" })
@Html.HiddenFor(model => model.UserId)

Step 5: Add a <div> that will serve as the container and then use the media-player script to tie the div to the PlayerFramework client. The FileUrl value is passed to the videoPlayer as well.

<div id="videoPlayer">
</div>

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    <script src="~/Scripts/playerframework.min.js"></script>
    <script src="~/Scripts/media-player.js"></script>
    @Scripts.Render("~/bundles/jqueryval")
    <script type="text/javascript">
        mediaPlayer.initFunction("videoPlayer", $("#fileUrl").val());

    </script>
}

That’s all that needs to be done to play the video.

Step 6: We can cleanup the Index.cshtml as well to show the Title and Delete options only, with the Title hyperlinked to the Edit page.

@model IEnumerable<AzureMediaPortal.Models.MediaElement>

@{
    ViewBag.Title = "My Media Index";
}
<h2>My Media Index</h2>
<p>
@Html.ActionLink("Upload New Media", "Upload")
</p>
<table>
<tr>
  <th>
   @Html.DisplayNameFor(model => model.Title)
  </th>
  <th>
   @Html.DisplayNameFor(model => model.IsPublic)
  </th>
  <th></th>
</tr>
@foreach (var item in Model) {
  <tr>
   <td>
    @Html.ActionLink(@item.Title, "Edit", new { id=item.Id })
   </td>
   <td>
    @Html.DisplayFor(modelItem => item.IsPublic)
   </td>
   <td>
    @Html.ActionLink("Delete", "Delete", new { id=item.Id })
   </td>
  </tr>
}
</table>

Deleting Media

Finally we’ll update the Delete method in the controller to delete Assets from server as well. To do this, we again use the AssetId to create a context and delete the asset. Once the asset is deleted we delete the record from our database as well.

private void DeleteMedia(string assetId)
{
    string mediaAccountName = ConfigurationManager.AppSettings["MediaAccountName"];
    string mediaAccountKey = ConfigurationManager.AppSettings["MediaAccountKey"];
    CloudMediaContext context = new CloudMediaContext(mediaAccountName, mediaAccountKey);
    var streamingAsset = context.Assets.Where(a => a.Id == assetId).FirstOrDefault();
    if (streamingAsset != null)
    {
        streamingAsset.Delete();
    }
}

[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
    MediaElement mediaelement = db.MediaElements.Find(id);
    DeleteMedia(mediaelement.AssetId);
    db.MediaElements.Remove(mediaelement);
    db.SaveChanges();
    return RedirectToAction("Index");
}

With that we are ready to run our app, Demo Time!

Demo – Our personal Media Portal

Step 1: Run the application, Register yourself the first time and Login.

Step 2: Navigate to the My Media page and click on Upload New Media

add-new-media-open

Step 3: Browse and select a media file (only mp4 for this sample). Click upload to begin upload. You’ll notice the upload button gets hidden as the progress shows progress.

add-new-media-upload-in-progress

Step 4: After the file is uploaded 100%, you’ll notice a pause as the media is registered with Media Service and AssetID obtained.

add-new-media-upload-complete

Step 5: Once Media is registered with Media Service, you’ll see a Save button and an Input box to Save the Title for the uploaded file. Provide the title and hit Save. Once save completes, you will see a Video panel and be able to preview the uploaded video. Click on ‘Back to List’ to go back to the Index page.

add-new-media-add-title

Step 6: After saving you can navigate back to the index page.

index-page

Step 7: From the index page, you can click on the title to navigate to the Edit page.

edit-page

Step 8: Here you can view the media asset as well as change the Title if you want. Save will navigate back to the Index page, from which we can go to the delete page to Delete the asset if no longer required.

delete-page

More stuff TODO

So far we have seen how to upload asset to Azure Storage and move it to Media service, then generate policies and access the media over a web client (for Modern browsers only, tested in IE10).

We have not tried out encoding to various formats, this we will try in a future article.

Conclusion

The Windows Azure Media Services along with the Player Framework provide us with a big leg up while building Media oriented services. Today we saw how we could build our personal video library with not-too-much effort.

Download the entire source code of this article (Github)

Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+
Further Reading - Articles You May Like!
Author
Sumit is a .NET consultant and has been working on Microsoft Technologies since his college days. He edits, he codes and he manages content when at work. C# is his first love, but he is often seen flirting with Java and Objective C. You can follow him on twitter at @sumitkm or email him at sumitkm [at] gmail


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!
Comment posted by Dmitry Trifonov on Wednesday, August 7, 2013 11:02 PM
I took a print out of this article and distributed it to my team members to serve as a reference guide to a Azure + Media product we are creating. This is the best 15 minutes I have spent in the past few months! Thank you
Comment posted by Sumit on Thursday, August 8, 2013 8:33 AM
Thanks for your kind words Dimitry, you comment just made my day :)
Comment posted by Andrei on Saturday, August 10, 2013 1:23 AM
Love the smell of good learning material. That personal video portal idea is pretty neat!
Comment posted by abedon on Thursday, August 15, 2013 4:06 PM
Unfortunately, this is not working for me. I tried uploading a .mp4 file and the media asset was created with a publish URL pointing to blob storage. But the video can't be played in a browser or even in Azure management portal. It says invalid file path.

The generated publish URL is like
https://dat5.blob.core.windows.net/asset-a271c46b-88ff-4150-8ca4-36c084d3f060/interview2_H264_4500kbps_AAC_und_ch2_128kbps.mp4?sv=2012-02-12&se=2014-08-15T20%3A52%3A02Z&sr=c&si=7a3c6196-c675-4e8b-a6f9-5ebd18044323&sig=omOE5udxtATtETkmoB8j5subAqDi%2B5P6R0xKr7oDlVk%3D

Comparing to the publish URL of a working media asset (same .mp4 file) that I can play in browser:
https://dat5.blob.core.windows.net/asset-e650bd29-6ea9-46f5-ad5f-6114922b0452/interview2_H264_4500kbps_AAC_und_ch2_128kbps.mp4?sv=2012-02-12&st=2013-08-15T20%3A11%3A45Z&se=2015-08-15T20%3A11%3A45Z&sr=c&si=1283a94b-dfde-4e9a-a845-f6d3d40ea32b&sig=xV9AeJv3mQ8IbI34HC9mC2zYYJFon3pdX1Fh0W0LIVc%3D

I believe something's wrong in GetStreamingUrl function when generating the URL for .mp4 files.
Comment posted by abedon on Thursday, August 15, 2013 6:50 PM
OK, I figured it out. Did you notice the difference of the two sigs?
omOE5udxtATtETkmoB8j5subAqDi%2B5P6R0xKr7oDlVk%3D (Invalid as there's a "%" in the middle)
xV9AeJv3mQ8IbI34HC9mC2zYYJFon3pdX1Fh0W0LIVc%3D (Good)

But why did this happen? Because the content type of the blob in the asset container is not set properly.

To fix this, simply add the following lines:
destinationBlob.Properties.ContentType = "video/mp4";
destinationBlob.SetProperties();
Comment posted by Sumit on Monday, August 19, 2013 11:29 AM
Thanks Abedon, good find!
Comment posted by ANders2020 on Tuesday, October 1, 2013 11:00 AM
Youtube is free; I want to know if Azure media is also free or charged per MB of Traffic. Imagine if 10000 download your media eating up all your bandwidth quota.?
Nice article but I think authors should indicate costs of Azure if any other I will stick to Youtube thanks .
Comment posted by Dhanshree on Tuesday, December 17, 2013 3:35 AM
its very nice ,,,
  i want the website like as youtube plz help me the code of upload the video only that category means sad video goes only that plz help me

Comment posted by evolcoder on Thursday, March 6, 2014 9:42 AM
very good, thank you very much!
Comment posted by dotnetfreak on Monday, May 12, 2014 11:16 AM
Can this MVC application be built using SQL Server file instead of Azure? I do not have access to azure from where I am unfortunately but I would love to try my hand at this article.  Thank you!
Comment posted by jy on Tuesday, July 8, 2014 4:37 PM
Where is @Styles.Render("~/Content/themes/base/css") located?
Comment posted by jy on Wednesday, July 9, 2014 11:31 AM
delete my last post, thanks for linking to Github.    And thanks for sharing this awesome article!
Comment posted by jy on Wednesday, July 9, 2014 12:03 PM
what could cause this error message?:  Failed to send MetaData

I checked all creditals and my code is basically your code I got from Github.
Comment posted by jy on Wednesday, July 9, 2014 2:24 PM
delete my last post, i finally got everything working.   Thanks again...this is good stuff!
Comment posted by Nagen on Wednesday, July 9, 2014 4:19 PM
Sumit, walk through is awesome and I can see the code too in github. I wondering the below things before I register and try it out

- do I need Azure account to compile the github code
- will Azure support and will I be able to use the same code to support multi platform apps (WebAPI's/SPA application) like Xbox, Roku, AppleTV, Samsung TV, GoogleTV.

Appreciate your reply

@Nagen.
Comment posted by 67 on Thursday, July 31, 2014 4:11 AM
yt
Comment posted by wave on Thursday, July 31, 2014 4:15 AM
its very nice :-)
Comment posted by Keennary on Friday, August 29, 2014 1:39 PM
I am trying to create published URL programmatically. I have created streaming url though but when you try to copy and paste it in browser it is not working. I am trying to connect my ASP.NEt MVC4 project connect the Azure Media Service. First application upload the video file and gives me the url which I can use to stream the video. When I tried the streaming url https://resourcelibraryecn.blob.core.windows.net/asset-7288241b-55ed-4dac-a0e8-cfb3c2ddcfd1/videofile.wmv It is not showing any video.but when I published the same file on Media Azure it has created the publish URL
and that url is perfact to stream the video. Can anyone tell me how to create that Publish URl through program in C#.

Comment posted by Keennary on Wednesday, September 3, 2014 7:24 AM
I am trying to create published URL programmatically. I have created streaming url though but when you try to copy and paste it in browser it is not working. I am trying to connect my ASP.NEt MVC4 project connect the Azure Media Service. First application upload the video file and gives me the url which I can use to stream the video. When I tried the streaming url https://resourcelibraryecn.blob.core.windows.net/asset-7288241b-55ed-4dac-a0e8-cfb3c2ddcfd1/videofile.wmv It is not showing any video.but when I published the same file on Media Azure it has created the publish URL
and that url is perfact to stream the video. Can anyone tell me how to create that Publish URl through program in C#.

Comment posted by Keennary on Wednesday, September 3, 2014 7:25 AM
I am trying to create published URL programmatically. I have created streaming url though but when you try to copy and paste it in browser it is not working. I am trying to connect my ASP.NEt MVC4 project connect the Azure Media Service. First application upload the video file and gives me the url which I can use to stream the video. When I tried the streaming url https://resourcelibraryecn.blob.core.windows.net/asset-7288241b-55ed-4dac-a0e8-cfb3c2ddcfd1/videofile.wmv It is not showing any video.but when I published the same file on Media Azure it has created the publish URL
and that url is perfact to stream the video. Can anyone tell me how to create that Publish URl through program in C#.

Comment posted by ZainAlabdin Tawfiq on Saturday, September 13, 2014 7:35 PM
can u plz extend the project to allow thumbnail creation?
thank you
Comment posted by Al Rosner on Thursday, September 25, 2014 4:53 AM
Hi,

The javascript demo sample works fine with the given mp4 file. However, I am attempting to write an mvc 5 application that can display a grid of 4  asf files on the located on localhost. When I attempt to play the videos, I get the 4 black boxes each with a rotating circle in the middle. If I run the cursor over each circle, the tool tip reads "Buffering". The video never plays. No errors are reported.

I use the following javascript (media-player.js) to play the video:

var mediaPlayer =
{
    initFunction: function (window, sourceUrl) {
        var myPlayer = new PlayerFramework.Player(window,
        {
            mediaPluginFallbackOrder: ["VideoElementMediaPlugin", "SilverlightMediaPlugin"],
            width: "480px",
            height: "320px",
            sources:
            [
             {
                 src: sourceUrl,
                 type: 'video/asf; codecs="avc1.42E01E, mp4a.40.2"'
             }
            ]
        });
    }
}

My page looks like this:

@model Golf.Models.TokenModels
@{
    ViewBag.Title = "Game";
}
<link href="@Url.Content("~/Content/playerframework.min.css")" rel="stylesheet" type="text/css" />

<h2>Game</h2><br/>
<table>
    <tr><td><div id="videoPlayer1" class="pf-container">
</div></td><td><div id="videoPlayer2" class="pf-container">
</div></td></tr>
    <tr><td><div id="videoPlayer3" class="pf-container">
</div></td><td><div id="videoPlayer4" class="pf-container">
</div></td></tr>
</table>


<div>
    @Html.ActionLink("Back to Main", "Index")
</div>
@section Scripts {
    <script src="~/Scripts/playerframework.min.js"></script>
    <script src="~/Scripts/media-player.js"></script>
    @Scripts.Render("~/bundles/jqueryval")
    <script type="text/javascript">
    function GetFilePath(cameraNumber) {
        return 'http://localhost/' + '@Model.JobId' + '_' + cameraNumber + '/' + '@Model.Token' + '.asf';
    }
    mediaPlayer.initFunction("videoPlayer1", GetFilePath('1'));
    mediaPlayer.initFunction("videoPlayer2", GetFilePath('2'));
    mediaPlayer.initFunction("videoPlayer3", GetFilePath('3'));
    mediaPlayer.initFunction("videoPlayer4", GetFilePath('4'));
</script>
}

Thanks,

Al
Comment posted by fifty3 on Sunday, October 12, 2014 10:30 AM
Why would I be getting a Failed to upload metadata error?
Comment posted by Sumit on Sunday, November 30, 2014 1:54 PM
Hi All,
Apologies for the delay in response! Everyone should realize this code works only after you have added the required configuration strings from 'YOUR' respective Azure accounts. Yes, you need an Azure Account.

The first error you get without adding the requisite Azure connection settings is 'Failed to upload metadata'.

Read the 'Pre-requisite' section at the beginning of this article thoroughly, in case of doubt.

Cheers,
Sumit.
Comment posted by Brandon Schenz on Tuesday, February 10, 2015 8:36 AM
Trying to determine if I'm missing some JavaScript code.  In the media-upload.js, I'm getting a resharper error for the following:
displayStatusMessage(operationType.UNSUPPORTED_BROWSER);

and

resetControls();
uploader = null;

It appears that the variables are not declared, and the function not defined.  Is this just because the code was taken from another project and not required here?
Comment posted by Varun on Wednesday, April 8, 2015 6:37 AM
Hi,
I have used the code from github changing azure credentials but while uploading a video of size 2.04MB am receiving an error starting The remote name could not be resolved xyzabc.blob.core.windows.net xyzabc is the MediaAccountName i have created.

TIA
Comment posted by Harish Mohanan on Monday, April 20, 2015 10:59 AM
Hi,
Thank you very much for this piece of information. I just wanted to know if currentTime(seconds) function in azure media player accepts decimal values. If you know about this please help.
Regards,
Harish