I’ve had a personal interest in figuring out how to play videos from the Office 365 Video Portal in a Univeral Windows App (UWP) in Windows 10 since Microsoft Ignite. In reality, the process isn’t that difficult, but there wasn’t a lot of details out there on how to put the pieces together until recently. Today, I am going to walk you through how to retrieve the first video in a channel and play it using a MediaElement.
This app could easily be extended to create a nice interface to browse a channel and pick a video. If you’re familiar with data binding in WPF though that shouldn’t be hard for you, so I won’t cover that today. We’re simply going to look at the necessary API and REST calls to get the secure playback URL. Once we have that URL, we can have the MediaElement start playing the video.
Getting started
First, you’ll need to have installed Windows 10 as well as the final version of Visual Studio 2015. We’re also assuming you have an Office 365 tenant, the Video Portal turned on, and you have some videos uploaded. If you have never created a Universal Windows App before you can find it under your favorite programming language –> Windows –> Universal –> Blank App (Universal Windows).
Playing a video from the Office 365 Video Portal involves several steps. If you know some of the values already along the way (such as the Video Portal URL), you can skip some steps. I am going to include all of them (which means this blog post is going to be long). The steps we need to go through are:
- Authenticate with Azure Active Directory / Office 365
- Access the Discovery Service to get the RootSite ServiceResourceId
- Determine the Video Portal Hub Url (i.e.: https://mytenant.sharepoint.com/hub)
- Get the list of channels
- Get the videos inside a channel
- Get the video playback URL
As you can see, that’s a lot of service calls. Caching is a good thing.
Connecting to Office 365
There are no shortage of ways to authenticate with Office 365 and Azure Active Directory. Windows 10 introduces a new way called the Web Account Provider. This will provide us our token that we can pass to the Discovery Service as well as give us tokens to other services such as SharePoint or Outlook. It will prompt the user to enter his or her credentials if we don’t have a token yet as well.
To connect to Office 365 bring up the context menu on the project in Solution Explorer and choose Add –> Connected Service. Select Office 365 APIs and then click Configure.
The nice thing about the Connected Service dialog in Visual Studio 2015 now is that it takes care of all of the manual Azure Active Directory application setup. You don’t need to go manually copy a ClientId, secret, permissions, or anything. This makes the new developer experience much better.
Now, you need to specify the domain of your tenant (i.e.: mytenant.onmicrosoft.com).
Clicking Next will prompt you for authentication. Enter your Office 365 credentials and then you can proceed to the next step.
Now, we need to tell Visual Studio to Create a new Azure AD application to access Office 365 API services. This will register the new application in Azure AD and then we can request permissions.
On the next six steps of the Wizard, we set the permissions that the app requires. For the purpose of playing a video, we don’t need access to Calendar, Contacts, Mail, or My Files. For Sites, we need the following permissions:
- Read and write items and lists in all site collections
- Read and write items in all site collections
- Read items in all site collections
For Users and Groups, select the following permissions:
- Sign you in and read your profile
Finally, hit Finish and Visual Studio will add the connected service to your project.
Designing the Page Layout
Now we’ll edit MainPage.xaml and add our controls to it. I kept this simple by just adding a Button control and a MediaElement. You can arrange them as you like.
Authenticating with Web Account Manager
Before we get too far, I am going to create a folder for the Models. I am going to skip a formal Controller class today since we’re not doing any real data binding. Normally you would have multiple controllers to pull up a list of channels, videos, etc. We’ll use a repository class, VideoRepository, that will connect to Office 365 and retrieve our data and our models we’ll use to deserialize the jSon that comes back from the REST API.
The VideoRepository class will also handle authentication. We’ll add the following using statements.
using System.Net.Http;
using System.Net.Http.Headers;
using Microsoft.Office365.Discovery;
using Microsoft.Office365.SharePoint.CoreServices;
using Windows.Security.Authentication.Web.Core;
using Windows.Security.Credentials;
using Newtonsoft.Json;
The first method we will create is GetAccessTokenForResource. If you have looked at other Universal App Samples, this should look fairly similar. The purpose of this method is to attempt to get a token silently (without prompting the user). If it can’t, then it it will show the the Office 365 login and the user can login which will provide the token.
public async Task<string> GetAccessTokenForResource(string resource)
{
string token = null;
//first try to get the token silently
WebAccountProvider aadAccountProvider
= awaitWebAuthenticationCoreManager.FindAccountProviderAsync("https://login.windows.net");
WebTokenRequest webTokenRequest
= new WebTokenRequest(aadAccountProvider, String.Empty, App.Current.Resources["ida:ClientID"].ToString(), WebTokenRequestPromptType.Default);
webTokenRequest.Properties.Add("authority", "https://login.windows.net");
webTokenRequest.Properties.Add("resource", resource);
WebTokenRequestResult webTokenRequestResult
= await WebAuthenticationCoreManager.GetTokenSilentlyAsync(webTokenRequest);
if (webTokenRequestResult.ResponseStatus == WebTokenRequestStatus.Success)
{
WebTokenResponse webTokenResponse = webTokenRequestResult.ResponseData[0];
token = webTokenResponse.Token;
}
else if (webTokenRequestResult.ResponseStatus == WebTokenRequestStatus.UserInteractionRequired)
{
//get token through prompt
webTokenRequest
= new WebTokenRequest(aadAccountProvider, String.Empty, App.Current.Resources["ida:ClientID"].ToString(), WebTokenRequestPromptType.ForceAuthentication);
webTokenRequest.Properties.Add("authority", "https://login.windows.net");
webTokenRequest.Properties.Add("resource", resource);
webTokenRequestResult
= await WebAuthenticationCoreManager.RequestTokenAsync(webTokenRequest);
if (webTokenRequestResult.ResponseStatus == WebTokenRequestStatus.Success)
{
WebTokenResponse webTokenResponse = webTokenRequestResult.ResponseData[0];
token = webTokenResponse.Token;
}
}
return token;
}
Let’s look at some of the highlights. First we get our
aadAccountProvider by calling WebAuthenticationCoreManager.FindAccountProviderAync(). This is the call into the Web Account Manager that I have been talking about. You always pass it the
https://login.windows.net (at least for this exercise). We then need a WebTokenRequest. This is where the ClientId of the application comes in. Remember Visual Studio created this for us when it registered our app with Azure Active Directory. The
resource property is where we pass in the Uri to the service we want to access. On our first call this will be the Uri of the discovery service. On subsequent calls it will be the Uri we get for accessing things in SharePoint.
Next, the method tries to silently authenticate the user. This method would return with a Success result if the user has authenticated inside this application before.
WebTokenRequestResult webTokenRequestResult
= await WebAuthenticationCoreManager.GetTokenSilentlyAsync(webTokenRequest);
If the user already has a token, then we are done. Otherwise user interaction is required. TheRequestTokenAsync call on the WebAuthenticationCoreManager is what actually prompts the user for credentials. Ultimately, this method returns a token for the resource you asked for. When we run the application and click the Play button, it will prompt us for credentials.
After you authenticate, you will get prompted to grant permissions to the app. These will match the ones we specified earlier when adding the Connected Service.
Calling the Discovery Service
We’re going to call the Discovery Service to determine the ServiceEndpointUri for the
RootSite capability. We’ll do this in a new method called
GetSharePointServiceEndpointUri. We do this by accessing for an access token from our GetAccessTokenForResource passing the URL
https://api.office.com/discovery.
string accessToken
= await GetAccessTokenForResource("https://api.office.com/discovery/");
DiscoveryClient discoveryClient = new DiscoveryClient(() =>
{
return accessToken;
});
After we get a DiscoveryClient, we can call DiscoverCapabilityAsync. This method also stores the access token used for accessing resources in the variable sharePointAccessToken. It also stores theServiceEndpointUri in the variable sharePointServiceEndpointUri. We’ll use this URI for the subsequent REST calls.
CapabilityDiscoveryResult result = awaitdiscoveryClient.DiscoverCapabilityAsync("RootSite");
sharePointAccessToken = await GetAccessTokenForResource(result.ServiceResourceId);
sharePointServiceEndpointUri = result.ServiceEndpointUri.ToString();
return sharePointServiceEndpointUri;
Remember, the full source code is available in GitHub.
Get the Video Portal Hub URL
If you have worked with Office 365, you probably know that it’s pretty easy to guess what the URL to the Video Portal Hub will be (the hub is the root site collection of the Video Portal). Since we want to do things right though, we’ll go through the Video Service’s Discover endpoint to determine the URL. In fact, once we construct these next two methods, the rest will be very similar.
We’ll create a new method called
GetVideoPortalHubUrl. The first thing we’ll do is call
GetSharePointServiceEndpointUri to authenticate and return us the endpoint for working with SharePoint. We append
VideoService.Discover to the Uri returned by our method. How did we know this was the URL, it’s in the
Video REST API reference (still in preview).
var requestUrl = String.Format("{0}/VideoService.Discover", awaitGetSharePointServiceEndpointUri());
We then, create a function that returns an HttpRequestMessage using the requestUrl.
Func<HttpRequestMessage> requestCreator = () =>
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
request.Headers.Add("Accept", "application/json;odata=verbose");
return request;
};
Then, we create a new httpClient and pass it, our access token, the HttpClient, and the RequestMessage to a custom method we will build named SendRequestAsync. This piece is based upon other’s code so I didn’t really tweak it since it works. After our call to that method, we’ll deserialize the jSon that comes back. I borrowed several of AC’s helper classes from his MVP example here.
Let’s take a look at SendRequestAsync. We’ll start with a using blog to get our HttpRequestMessage.
using (var request = requestCreator.Invoke())
We’ll then need to add the necessary headers. First, we add an AuthenticationHeaderValue with thesharePointAccessToken we got earlier. We also add another header, X-ClientService-ClientTag. I don’t know if this is required but Chaks included it in his examples. I updated the version to match the current version reported in the Connected Service wizard.
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
request.Headers.Add("X-ClientService-ClientTag", "Office 365 API Tools 1.5");
Then we invoke the request using httpClient.
var response = await httpClient.SendAsync(request);
The method then returns the response assuming it’s successful. The example that Chaks provided will actually make a second attempt to call the service if the user isn’t authorized. You can optionally include that if you like.
Now going back to our GetVideoHubUrl method after we successfully got a response back. We now need to parse the JSON that came back into a usable object. This is where Newtonsoft.Json comes in. We use the models that AC provided in the MVC project. In this case we serialize the responseString into an object of VideoServiceDiscovery. Now, we can get the VideoPortalUrl for our subsequent REST calls.
string responseString = await response.Content.ReadAsStringAsync();
var jsonResponse = JsonConvert.DeserializeObject<VideoServiceDiscovery>(responseString);
videoPortalUrl = jsonResponse.Data.VideoPortalUrl;
Get the video portal channels
Now that we have our Video Portal Hub URL, we can retrieve the list of channels. The
Video REST API Reference tells us we need to append
/_api/VideoService/Channels to the Video Portal Hub URL which we just retrieved. We’ll create a new method called
GetVideoChannels which will look similar to
GetVideoPortalHubUrl expect the
requestUrl used and the JSON serialization.
var requestUrl = string.Format("{0}/_api/VideoService/Channels", videoPortalUrl);
Since we return multiple channels, we loop through the results and add new VideoChannel objects to the list.
string responseString = await response.Content.ReadAsStringAsync();
var jsonResponse = JsonConvert.DeserializeObject<VideoChannelCollection>(responseString);
var channels = new List<VideoChannel>();
foreach (var videoChannel in jsonResponse.Data.Results)
{
var channel = new VideoChannel
{
Id = videoChannel.Id,
HtmlColor = videoChannel.TileHtmlColor,
Title = videoChannel.Title,
Description = videoChannel.Description,
ServerRelativeUrl = videoChannel.ServerRelativeUrl
};
channels.Add(channel);
}
At this point, you have enough data you can start doing some data binding. As I mentioned earlier, I am skipping that since data binding in a Universal Windows App for Windows 10 is quite similar to what was there in Windows 8.1 For our example, I am simply going to pass the Channel Id into our next method to retrieve a list of videos.
Getting the list of videos in a channel
Now that we have a list of Channels, we can make another
Video REST API call to get a list of videos in our new method
GetVideos. We build our
requestUrl by appending
/_api/VideoService/Channels(‘<channelid>’)/Videos. Remember we got the Channel Id from our last API call. Now this simply returns the metadata about the video. After this, we still have one more call to get the Playback URL.
var requestUrl = string.Format("{0}/_api/VideoService/Channels('{1}')/Videos", videoPortalHubUrl, channelId);
The rest of the method is similar to GetVideoChannels.
Get the video playback URL
For the purpose of our example, we’re also going to simply play the first Video in the Channel. To get the Playback URL, we’ll need the Video Id as well as the Channel Id we retrieved earlier. When it comes to streaming, we have a choice of either Smooth Streaming or HLS Streaming by specifying the
streamingFormatType parameter on the
Video REST API call. HLS streaming requires one fewer API call so we are going to use that for the example today. You may want to use Smooth Streaming for your application. To use HLS Streaming, specify a value of 0.
var requestUrl = string.Format("{0}/_api/VideoService/Channels('{1}')/Videos('{2}')/GetPlaybackUrl('{3}')",
new string[] { videoPortalHubUrl, channelId, videoId, streamingFormatType.ToString() });
The REST call will return a playback URL which we can then assign to the MediaElement.
Playing the video
On the click handler of the Play button we added in the beginning, we make our calls into theVideoRepository class. Once we get the Playback URL we binding it to the MediaElement and the video will start playing. I also set a few parameters to turn on the transport controls and enable full screen mode. You can set those as you like.
private async void button_Click(object sender, RoutedEventArgs e)
{
VideoRepository videoRepository = new VideoRepository();
string videoPortalUrl = await videoRepository.GetVideoPortalHubUrl();
var videoChannels = await videoRepository.GetVideoChannels();
string channelId = videoChannels[0].Id;
var videos = await videoRepository.GetVideos(channelId);
string videoId = videos[0].VideoId;
var videoPlayback = await videoRepository.GetVideoPlayback(channelId, videoId, 0);
mediaElement.Volume = 0;
mediaElement.AreTransportControlsEnabled = true;
mediaElement.IsFullWindow = true;
mediaElement.Source = new Uri(videoPlayback.Value);
}
There you have it a (somewhat) quick and dirty way to play a video from the Office 365 Video Portal in Windows 10. This should work on Windows 10 Mobile as well. Here is what our app looks like when playing a video.
Source Code
As promised the source code is available on
GitHub. I may extend it in the future to include all of the data binding and improve the look. Feel free to contribute to the repo as well. If you make use of any of the code, you’ll want to add error handling of course.
Feel free to ask me any questions by leaving a comment.