Introducing Media Center App for SharePoint 2013

Media Center is a SharePoint app that allows you to integrate your Windows Azure Media Services (WAMS) assets within SharePoint.

Before I describe the app functionality, I think it useful to take a step back and briefly talk about why this app is needed and the design choices we had to make in order to build it. This is also a great opportunity for me to thank the team who worked very hard on building this app including Jason McNutt, Harin Sandhoo and Sam Larko. Shannon Gray helped out with the UI design. Building an app using technical preview bits with little documentation is always challenging, so the help provided by Anton Labunets and Vidya Srinivasan from the SharePoint team was so critical. Thank you!

At AIS, we focus on building custom applications on top of the SharePoint platform. Among the various SharePoint applications we have built in the past, the ability to host media assets within SharePoint is a request that has come up a few times. As you know, SharePoint 2010 added streaming functionality, so any media assets stored within the content database could be streamed to the SharePoint users directly. However, the streaming functionality in SharePoint 2010 was never intended to provide a heavy duty-streaming server. For instance, it does not support some of the advanced features like adaptive streaming. Additionally, most organizations don’t want to store large media files within SharePoint in order to avoid bloating the size of the content databases.

Enter Windows Azure Media Services (WAMS).

Windows Azure Media Services is a cloud-based media storage, encoding and streaming service that is part of the Windows Azure Platform. It can provide terabytes of elastic storage (powered by Windows Azure Storage), a host of encoding formats and elastic bandwidth for streaming content down to the consumers. Furthermore, WAMS is offered as a PaaS, so customers only pay for the resources they use, such as the compute cycles used for media encoding, the storage space used for hosting the media assets and the egress bandwidth used by clients consuming the content.

We saw an interesting opportunity to combine the capabilities of WAMS with the new SharePoint 2013 app model. At a high level, the approach seemed straightforward. Download the metadata about the WAMS assets into SharePoint. Once the metadata is available within a SharePoint site, users can play, search and tag media assets — all this without the users having to leave the SharePoint site.

However, the implementation turned out to be a bit more challenging (isn’t that always the case?!). Here’s a glimpse of some of the initial design decisions and the refactoring that that we had to undertake during the course of the implementation.

►Our objective was to build a SharePoint app that is available to both SharePoint Online and on-premises SharePoint customers. This is why we chose the SharePoint hosted app model. You can read up on the different styles of SharePoint apps here. The important thing to note is that the SharePoint-hosted apps are client-side only. In other words, no server-side code is allowed. You can of course use any of the built-in lists, libraries and web parts offered by SharePoint.

Our first challenge was download the WAMS metadata into SharePoint. We knew that BCS has been enhanced in 2013 and can now be configured at the App Web (SPWeb level). So our initial thought was to leverage the BCS OData connector to present assets stored in WAMS as an external list within the app. Note that WAMS exposes an OData feed for assets stored in it. However, SharePoint-hosted apps cannot navigate a secured OData feed. As you can imagine, WAMS OData feed will almost always be secured for corporate portals as the content is likely not public. This limitation meant that we could not use BCS. The alternative, of course, was to use client-side JavaScript code to make a call to WAMS directly.

The data downloaded from WAMS could be inserted into a list using CSOM. This list is part of the Media Center app web. Ultimately this is the approach we ended up with…although not without consequences. By maintaining a separate copy of the metadata (within the list), we were forced to deal with a synchronization issue (i.e. as new assets are added and deleted from WAMS, the list within the Media Center app needs to be updated as well). But recall that SharePoint-hosted apps are not allowed to have any server-side code. So the only recourse was to fall back on the client-side code to keep the list in sync with the WAMS.

One of our objectives was to build an app that is available to both on-premises and cloud-based SharePoint installations.

However, client-side code can only be triggered by a user action, such as someone browsing a Media Center app page. Clearly an auto-hosted or self-hosted app can deal with the synchronization challenge easily by setting up a server-side timer job. But as stated earlier, one of our objectives was to build an app that is available to both on-premises and cloud-based SharePoint installations. It is worth noting that in the current release, auto-hosted apps are limited to SharePoint Online-based apps. On the other hand, the self-hosted apps are limited to on-premises installations. So we decided to stay with the SharePoint hosted app model.

We worked around the client-only code limitation by checking for WAMS updates during the page load. This check is performed asynchronously so as to not block the user from interacting with the app. As a further optimization, this check is only performed after a configurable timeout period has elapsed. We also provided a “sync now” button within the app page allowing users to manually initiate synchronization.

►There is an additional challenge related to connecting to WAMS from the client side. Each SharePoint-hosted app is assigned a unique domain prefix (for security reasons). This meant that the calls to WAMS endpoints (typically media.azure.com) are treated as cross-domain calls by the browser. Now…this is a well-known issue with some well-known solutions including JSONP, CORS etc.

Turns out that SharePoint 2013 provides a web proxy to make it easy to make cross-domain calls. All you need to do is register the external site you want to communicate with inside the app manifest. All the registered sites can be reached via the web proxy, as shown in the example provided at the bottom of this post [1]. However, when you connect to WAMS, it can redirect the clients to a new URI. As a result, the clients are expected to fetch the new URI and use that for subsequent requests. However, the new URI is not the one registered as the external URI with the app. Consequently, the app would not be able to connect to the new URI. (Unfortunately the external site registration does not currently support wild cards.) To get around this issue, we created our own proxy hosted with a Windows Azure website. We can register the URI of the custom proxy with the app manifest and use a SharePoint-provided web proxy to route calls to it. Our custom proxy itself is quite simple: it simply forwards the incoming calls to the appropriate WAMS endpoints.

►The next challenge we ran into involved integrating WAMS with SharePoint features, such as search and managed metadata. It turns out that search cannot crawl external sources (such as WAMS) that an app may depend on.

Fortunately there was a workaround (albeit not an optimal one). The list containing the WAMS metadata could be crawled by search. We were not as lucky when it came to Integrating with the managed metadata store. It turns out that managed metadata is not available to SharePoint-hosted apps. This limitation means that our app will not support tagging, term sets, etc.

►Next, let us discuss the security mechanism used by the app. As you may know, WAMS requires an ACS token for authentication. So in order to access WAMS on behalf of the user, app needs to obtain an ACS token first. It does so by soliciting the WAMS credentials (at time of installation of the app) and using them to obtain an ACS token. Armed with the ACS token, the app makes the needed WAMS REST API calls in order to obtain metadata about the assets, as well as a Shared Access Signature to play the videos. It is important to note that users of the app will have access to all assets that are associated with the WAMS subscription provided to the app. In other words, WAMS assets cannot be granularly mapped to SharePoint users.

►There is one additional detail related to how WAMS credentials are secured. As discussed earlier, the Media Center app needs the WAMS credentials in order to download metadata about the assets. Based on earlier discussion related to synchronization, we know that calls to WAMS are made within the context of the user browsing the Media Center app. This means users of the app (not just the administrator who installed the app) potentially have access to WAMS credentials. To avoid exposing WAMS credentials to the users of the app, we encrypt them. Encryption (and decryption) is performed by our Azure-based web proxy, which we discussed earlier.

►The final architecturally significant decision is related to how the media assets are surfaced within the app. As mentioned earlier, we started out with a list to enumerate the asset metadata downloaded by WAMS. But rather than building the player functionality ourselves, we quickly turned towards leveraging the built-in asset library in SharePoint 2013. Asset library is a type of document library that allows storage of rich media files including image, audio and video files. It provides features such as thumbnail-based views, digital asset content to track the metadata associated with uploaded the media files, and callouts to play and download media files (play and download buttons are enabled as the pointer is moved over a thumbnail). However, the asset library is designed for files that are stored inside the content database. As a result, when we tried to access an external WAMS-based asset programmatically, we ran into the following two issues:

  1. Adding an item of content type Video to the asset library resulted in an error.
  2. The query string portion of WAMS access URL is not handled.

Fortunately Anton Labunets and Vidya Srinivasan provided us with workarounds for the two aforementioned issues. We added an item on type VideoSet and set the VideoSetExternalLink property to set the WAMS URL. We overrode the APD_GetFileExtension method to workaround the query string handling issue. See [2] for relevant code snippets.

Now that we understand the architecture of the Media Center app, let’s review the app functionality by walking through a few screenshots:

WAMS video playing inside Media Center App

Settings screen where the administrator can configure their WAMS credentials. Azure Media Services Account Key can be obtained from the WAMS portal as shown in the screenshot.

Define the synchronization interval in days. 

Default asset library view showing videos stored in WAMS.

And finally, here is a brief video demonstration of the app’s functionality.

The Media Center App is available for free at the Office store online. More information about the Media Center App can also be found at appliedis.com.

[1] Invoking acs.azure.com via the SharePoint web proxy:

$.ajax (
{
url: _surlweb + “_api/sp.webproxy.invoke”,
type: “post”,
data: json.stringify ( { ‘requestinfo’ :
{
‘__metadata’ : { ‘type’: ‘ SP.WebRequestInfo’},
‘Url’: ‘http://acs.Azure.com’,
‘Method’ : ‘GET’
}),
headers: { },
success: {}
})

[2] Adding a WAMS-based file to an asset library:

var list = web.get_lists().getByTitle('WAMS Library');
var folder = list.get_rootFolder();
var itemCreateInfo = new SP.ListItemCreationInformation();
itemCreateInfo.set_underlyingObjectType(SP.FileSystemObjectType.folder);
itemCreateInfo.set_folderUrl("");
itemCreateInfo.set_leafName("WAMS Test Video");
var listItem = list.addItem(itemCreateInfo);
listItem.set_item('ContentTypeId','0x0120D520A808006752632C7958C34AA3E07CE1D34081E9'); // videoset content type
listItem.set_item('VideoSetExternalLink', < WAMS URL>);
listItem.update();
ctx.load(listItem);