How to Build a Smooth Streaming Windows Store Application

The Smooth Streaming Client SDK for Windows 8 enables developers to build Windows Store applications that can play on-demand and live Smooth Streaming content. In addition to the basic playback of Smooth Streaming content, the SDK also provides rich features like Microsoft PlayReady protection, quality level restriction, Live DVR, audio stream switching, listening for status updates (such as quality level changes) and error events, and so on. For more information of the supported features, see the release notes. For more information, see Player Framework for Windows 8.

This tutorial contains four lessons:

  1. Create a Basic Smooth Streaming Store Application
  2. Add a Slider Bar to Control the Media Progress
  3. Select Smooth Streaming Streams
  4. Select Smooth Streaming Tracks

Prerequisites

Note

Windows Store projects version 8.1 and earlier are not supported in Visual Studio 2017. For more information, see Visual Studio 2017 Platform Targeting and Compatibility.

The completed solution for each lesson can be downloaded from MSDN Developer Code Samples (Code Gallery):

  • Lesson 1 - A Simple Windows 8 Smooth Streaming Media Player,
  • Lesson 2 - A Simple Windows 8 Smooth Streaming Media Player with a Slider Bar Control,
  • Lesson 3 - A Windows 8 Smooth Streaming Media Player with Stream Selection,
  • Lesson 4 - A Windows 8 Smooth Streaming Media Player with Track Selection.

Lesson 1: Create a Basic Smooth Streaming Store Application

In this lesson, you will create a Windows Store application with a MediaElement control to play Smooth Stream content. The running application looks like:

![Smooth Streaming Windows Store application example][PlayerApplication]

For more information on developing Windows Store application, see Develop Great Apps for Windows 8. This lesson contains the following procedures:

  1. Create a Windows Store project
  2. Design the user interface (XAML)
  3. Modify the code behind file
  4. Compile and test the application

To create a Windows Store project

  1. Run Visual Studio; versions 2012 through 2015 are supported.

  2. From the FILE menu, click New, and then click Project.

  3. From the New Project dialog, type or select the following values:

    Name Value
    Template group Installed/Templates/Visual C#/Windows Store
    Template Blank App (XAML)
    Name SSPlayer
    Location C:\SSTutorials
    Solution Name SSPlayer
    Create directory for solution (selected)
  4. Click OK.

To add a reference to the Smooth Streaming Client SDK

  1. From Solution Explorer, right-click SSPlayer, and then click Add Reference.

  2. Type or select the following values:

    Name Value
    Reference group Windows/Extensions
    Reference Select Microsoft Smooth Streaming Client SDK for Windows 8 and Microsoft Visual C++ Runtime Package
  3. Click OK.

After adding the references, you must select the targeted platform (x64 or x86), adding references will not work for Any CPU platform configuration. In solution explorer, you will see yellow warning mark for these added references.

To design the player user interface

  1. From Solution Explorer, double click MainPage.xaml to open it in the design view.

  2. Locate the <Grid> and </Grid> tags the XAML file, and paste the following code between the two tags:

          <Grid.RowDefinitions>
    
             <RowDefinition Height="20"/>    <!-- spacer -->
             <RowDefinition Height="50"/>    <!-- media controls -->
             <RowDefinition Height="100*"/>  <!-- media element -->
             <RowDefinition Height="80*"/>   <!-- media stream and track selection -->
             <RowDefinition Height="50"/>    <!-- status bar -->
          </Grid.RowDefinitions>
    
          <StackPanel Name="spMediaControl" Grid.Row="1" Orientation="Horizontal">
             <TextBlock x:Name="tbSource" Text="Source :  " FontSize="16" FontWeight="Bold" VerticalAlignment="Center" />
             <TextBox x:Name="txtMediaSource" Text="https://ecn.channel9.msdn.com/o9/content/smf/smoothcontent/elephantsdream/Elephants_Dream_1024-h264-st-aac.ism/manifest" FontSize="10" Width="700" Margin="0,4,0,10" />
             <Button x:Name="btnSetSource" Content="Set Source" Width="111" Height="43" Click="btnSetSource_Click"/>
             <Button x:Name="btnPlay" Content="Play" Width="111" Height="43" Click="btnPlay_Click"/>
             <Button x:Name="btnPause" Content="Pause"  Width="111" Height="43" Click="btnPause_Click"/>
             <Button x:Name="btnStop" Content="Stop"  Width="111" Height="43" Click="btnStop_Click"/>
             <CheckBox x:Name="chkAutoPlay" Content="Auto Play" Height="55" Width="Auto" IsChecked="{Binding AutoPlay, ElementName=mediaElement, Mode=TwoWay}"/>
             <CheckBox x:Name="chkMute" Content="Mute" Height="55" Width="67" IsChecked="{Binding IsMuted, ElementName=mediaElement, Mode=TwoWay}"/>
          </StackPanel>
    
          <StackPanel Name="spMediaElement" Grid.Row="2" Height="435" Width="1072"
                     HorizontalAlignment="Center" VerticalAlignment="Center">
             <MediaElement x:Name="mediaElement" Height="356" Width="924" MinHeight="225"
                           HorizontalAlignment="Center" VerticalAlignment="Center"
                           AudioCategory="BackgroundCapableMedia" />
             <StackPanel Orientation="Horizontal">
                 <Slider x:Name="sliderProgress" Width="924" Height="44"
                         HorizontalAlignment="Center" VerticalAlignment="Center"
                         PointerPressed="sliderProgress_PointerPressed"/>
                 <Slider x:Name="sliderVolume"
                         HorizontalAlignment="Right" VerticalAlignment="Center" Orientation="Vertical"
                         Height="79" Width="148" Minimum="0" Maximum="1" StepFrequency="0.1"
                         Value="{Binding Volume, ElementName=mediaElement, Mode=TwoWay}"
                         ToolTipService.ToolTip="{Binding Value, RelativeSource={RelativeSource Mode=Self}}"/>
             </StackPanel>
          </StackPanel>
    
          <StackPanel Name="spStatus" Grid.Row="4" Orientation="Horizontal">
             <TextBlock x:Name="tbStatus" Text="Status :  "
                FontSize="16" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Center" />
             <TextBox x:Name="txtStatus" FontSize="10" Width="700" VerticalAlignment="Center"/>
          </StackPanel>
    

    The MediaElement control is used to playback media. The slider control named sliderProgress will be used in the next lesson to control the media progress.

  3. Press CTRL+S to save the file.

The MediaElement control does not support Smooth Streaming content out-of-box. To enable the Smooth Streaming support, you must register the Smooth Streaming byte-stream handler by file name extension and MIME type. To register, you use the MediaExtensionManager.RegisterByteStreamHandler method of the Windows.Media namespace.

In this XAML file, some event handlers are associated with the controls. You must define those event handlers.

To modify the code behind file

  1. From Solution Explorer, right-click MainPage.xaml, and then click View Code.

  2. At the top of the file, add the following using statement:

        using Windows.Media;
    
  3. At the beginning of the MainPage class, add the following data member:

        private MediaExtensionManager extensions = new MediaExtensionManager();
    
  4. At the end of the MainPage constructor, add the following two lines:

        extensions.RegisterByteStreamHandler("Microsoft.Media.AdaptiveStreaming.SmoothByteStreamHandler", ".ism", "text/xml");
        extensions.RegisterByteStreamHandler("Microsoft.Media.AdaptiveStreaming.SmoothByteStreamHandler", ".ism", "application/vnd.ms-sstr+xml");
    
  5. At the end of the MainPage class, paste the following code:

          # region UI Button Click Events
          private void btnPlay_Click(object sender, RoutedEventArgs e)
          {
    
          mediaElement.Play();
          txtStatus.Text = "MediaElement is playing ...";
          }
          private void btnPause_Click(object sender, RoutedEventArgs e)
          {
    
          mediaElement.Pause();
          txtStatus.Text = "MediaElement is paused";
          }
          private void btnSetSource_Click(object sender, RoutedEventArgs e)
          {
    
          sliderProgress.Value = 0;
          mediaElement.Source = new Uri(txtMediaSource.Text);
    
          if (chkAutoPlay.IsChecked == true)
          {
              txtStatus.Text = "MediaElement is playing ...";
          }
          else
          {
              txtStatus.Text = "Click the Play button to play the media source.";
          }
          }
          private void btnStop_Click(object sender, RoutedEventArgs e)
          {
    
          mediaElement.Stop();
          txtStatus.Text = "MediaElement is stopped";
          }
          private void sliderProgress_PointerPressed(object sender, PointerRoutedEventArgs e)
          {
    
          txtStatus.Text = "Seek to position " + sliderProgress.Value;
          mediaElement.Position = new TimeSpan(0, 0, (int)(sliderProgress.Value));
          }
          # endregion
    

    The sliderProgress_PointerPressed event handler is defined here. There are more works to do to get it working, which will be covered in the next lesson of this tutorial.

  6. Press CTRL+S to save the file.

The finished the code behind file shall look like this:

![Codeview in Visual Studio of Smooth Streaming Windows Store application][CodeViewPic]

To compile and test the application

  1. From the BUILD menu, click Configuration Manager.
  2. Change Active solution platform to match your development platform.
  3. Press F6 to compile the project.
  4. Press F5 to run the application.
  5. At the top of the application, you can either use the default Smooth Streaming URL or enter a different one.
  6. Click Set Source. Because Auto Play is enabled by default, the media shall play automatically. You can control the media using the Play, Pause and Stop buttons. You can control the media volume using the vertical slider. However the horizontal slider for controlling the media progress is not fully implemented yet.

You have completed lesson1. In this lesson, you use a MediaElement control to playback Smooth Streaming content. In the next lesson, you will add a slider to control the progress of the Smooth Streaming content.

Lesson 2: Add a Slider Bar to Control the Media Progress

In lesson 1, you created a Windows Store application with a MediaElement XAML control to playback Smooth Streaming media content. It comes some basic media functions like start, stop and pause. In this lesson, you will add a slider bar control to the application.

In this tutorial, we will use a timer to update the slider position based on the current position of the MediaElement control. The slider start and end time also need to be updated in case of live content. This can be better handled in the adaptive source update event.

Media sources are objects that generate media data. The source resolver takes a URL or byte stream and creates the appropriate media source for that content. The source resolver is the standard way for the applications to create media sources.

This lesson contains the following procedures:

  1. Register the Smooth Streaming handler
  2. Add the adaptive source manager level event handlers
  3. Add the adaptive source level event handlers
  4. Add MediaElement event handlers
  5. Add slider bar related code
  6. Compile and test the application

To register the Smooth Streaming byte-stream handler and pass the propertyset

  1. From Solution Explorer, right click MainPage.xaml, and then click View Code.

  2. At the beginning of the file, add the following using statement:

         using Microsoft.Media.AdaptiveStreaming;
    
  3. At the beginning of the MainPage class, add the following data members:

          private Windows.Foundation.Collections.PropertySet propertySet = new Windows.Foundation.Collections.PropertySet();
          private IAdaptiveSourceManager adaptiveSourceManager;
    
  4. Inside the MainPage constructor, add the following code after the this.Initialize Components(); line and the registration code lines written in the previous lesson:

         // Gets the default instance of AdaptiveSourceManager which manages Smooth
         //Streaming media sources.
         adaptiveSourceManager = AdaptiveSourceManager.GetDefault();
         // Sets property key value to AdaptiveSourceManager default instance.
         // {A5CE1DE8-1D00-427B-ACEF-FB9A3C93DE2D}" must be hardcoded.
         propertySet["{A5CE1DE8-1D00-427B-ACEF-FB9A3C93DE2D}"] = adaptiveSourceManager;
    
  5. Inside the MainPage constructor, modify the two RegisterByteStreamHandler methods to add the forth parameters:

          // Registers Smooth Streaming byte-stream handler for ".ism" extension and,
          // "text/xml" and "application/vnd.ms-ss" mime-types and pass the propertyset.
          // http://*.ism/manifest URI resources will be resolved by Byte-stream handler.
          extensions.RegisterByteStreamHandler(
    
             "Microsoft.Media.AdaptiveStreaming.SmoothByteStreamHandler",
             ".ism",
             "text/xml",
             propertySet );
          extensions.RegisterByteStreamHandler(
    
             "Microsoft.Media.AdaptiveStreaming.SmoothByteStreamHandler",
             ".ism",
             "application/vnd.ms-sstr+xml",
          propertySet);
    
  6. Press CTRL+S to save the file.

To add the adaptive source manager level event handler

  1. From Solution Explorer, right click MainPage.xaml, and then click View Code.

  2. Inside the MainPage class, add the following data member:

      private AdaptiveSource adaptiveSource = null;
    
  3. At the end of the MainPage class, add the following event handler:

          # region Adaptive Source Manager Level Events
          private void mediaElement_AdaptiveSourceOpened(AdaptiveSource sender, AdaptiveSourceOpenedEventArgs args)
          {
    
             adaptiveSource = args.AdaptiveSource;
          }
    
          # endregion Adaptive Source Manager Level Events
    
  4. At the end of the MainPage constructor, add the following line to subscribe to the adaptive source open event:

          adaptiveSourceManager.AdaptiveSourceOpenedEvent +=
            new AdaptiveSourceOpenedEventHandler(mediaElement_AdaptiveSourceOpened);
    
  5. Press CTRL+S to save the file.

To add adaptive source level event handlers

  1. From Solution Explorer, right click MainPage.xaml, and then click View Code.

  2. Inside the MainPage class, add the following data member:

      private AdaptiveSourceStatusUpdatedEventArgs adaptiveSourceStatusUpdate;
      private Manifest manifestObject;
    
  3. At the end of the MainPage class, add the following event handlers:

          # region Adaptive Source Level Events
          private void mediaElement_ManifestReady(AdaptiveSource sender, ManifestReadyEventArgs args)
          {
    
             adaptiveSource = args.AdaptiveSource;
             manifestObject = args.AdaptiveSource.Manifest;
          }
    
          private void mediaElement_AdaptiveSourceStatusUpdated(AdaptiveSource sender, AdaptiveSourceStatusUpdatedEventArgs args)
          {
    
             adaptiveSourceStatusUpdate = args;
          }
    
          private void mediaElement_AdaptiveSourceFailed(AdaptiveSource sender, AdaptiveSourceFailedEventArgs args)
          {
    
             txtStatus.Text = "Error: " + args.HttpResponse;
          }
    
          # endregion Adaptive Source Level Events
    
  4. At the end of the mediaElement AdaptiveSourceOpened method, add the following code to subscribe to the events:

          adaptiveSource.ManifestReadyEvent +=
    
                     mediaElement_ManifestReady;
          adaptiveSource.AdaptiveSourceStatusUpdatedEvent +=
    
             mediaElement_AdaptiveSourceStatusUpdated;
          adaptiveSource.AdaptiveSourceFailedEvent +=
    
             mediaElement_AdaptiveSourceFailed;
    
  5. Press CTRL+S to save the file.

The same events are available on Adaptive Source manger level as well, which can be used for handling functionality common to all media elements in the app. Each AdaptiveSource includes its own events and all AdaptiveSource events will be cascaded under AdaptiveSourceManager.

To add Media Element event handlers

  1. From Solution Explorer, right click MainPage.xaml, and then click View Code.

  2. At the end of the MainPage class, add the following event handlers:

          # region Media Element Event Handlers
          private void MediaOpened(object sender, RoutedEventArgs e)
          {
    
             txtStatus.Text = "MediaElement opened";
          }
    
          private void MediaFailed(object sender, ExceptionRoutedEventArgs e)
          {
    
             txtStatus.Text= "MediaElement failed: " + e.ErrorMessage;
          }
    
          private void MediaEnded(object sender, RoutedEventArgs e)
          {
    
             txtStatus.Text ="MediaElement ended.";
          }
    
          # endregion Media Element Event Handlers
    
  3. At the end of the MainPage constructor, add the following code to subscript to the events:

          mediaElement.MediaOpened += MediaOpened;
          mediaElement.MediaEnded += MediaEnded;
          mediaElement.MediaFailed += MediaFailed;
    
  4. Press CTRL+S to save the file.

  1. From Solution Explorer, right click MainPage.xaml, and then click View Code.

  2. At the beginning of the file, add the following using statement:

         using Windows.UI.Core;
    
  3. Inside the MainPage class, add the following data members:

          public static CoreDispatcher _dispatcher;
          private DispatcherTimer sliderPositionUpdateDispatcher;
    
  4. At the end of the MainPage constructor, add the following code:

          _dispatcher = Window.Current.Dispatcher;
          PointerEventHandler pointerpressedhandler = new PointerEventHandler(sliderProgress_PointerPressed);
          sliderProgress.AddHandler(Control.PointerPressedEvent, pointerpressedhandler, true);
    
  5. At the end of the MainPage class, add the following code:

          # region sliderMediaPlayer
          private double SliderFrequency(TimeSpan timevalue)
          {
    
             long absvalue = 0;
             double stepfrequency = -1;
    
             if (manifestObject != null)
             {
                 absvalue = manifestObject.Duration - (long)manifestObject.StartTime;
             }
             else
             {
                 absvalue = mediaElement.NaturalDuration.TimeSpan.Ticks;
             }
    
             TimeSpan totalDVRDuration = new TimeSpan(absvalue);
    
             if (totalDVRDuration.TotalMinutes >= 10 && totalDVRDuration.TotalMinutes < 30)
             {
                stepfrequency = 10;
             }
             else if (totalDVRDuration.TotalMinutes >= 30
                      && totalDVRDuration.TotalMinutes < 60)
             {
                 stepfrequency = 30;
             }
             else if (totalDVRDuration.TotalHours >= 1)
             {
                 stepfrequency = 60;
             }
    
             return stepfrequency;
          }
    
          void updateSliderPositionoNTicks(object sender, object e)
          {
    
             sliderProgress.Value = mediaElement.Position.TotalSeconds;
          }
    
          public void setupTimer()
          {
    
             sliderPositionUpdateDispatcher = new DispatcherTimer();
             sliderPositionUpdateDispatcher.Interval = new TimeSpan(0, 0, 0, 0, 300);
             startTimer();
          }
    
          public void startTimer()
          {
    
             sliderPositionUpdateDispatcher.Tick += updateSliderPositionoNTicks;
             sliderPositionUpdateDispatcher.Start();
          }
    
          // Slider start and end time must be updated in case of live content
          public async void setSliderStartTime(long startTime)
          {
    
             await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
             {
                 TimeSpan timespan = new TimeSpan(adaptiveSourceStatusUpdate.StartTime);
                 double absvalue = (int)Math.Round(timespan.TotalSeconds, MidpointRounding.AwayFromZero);
                 sliderProgress.Minimum = absvalue;
             });
          }
    
          // Slider start and end time must be updated in case of live content
          public async void setSliderEndTime(long startTime)
          {
    
             await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
             {
                 TimeSpan timespan = new TimeSpan(adaptiveSourceStatusUpdate.EndTime);
                 double absvalue = (int)Math.Round(timespan.TotalSeconds, MidpointRounding.AwayFromZero);
                 sliderProgress.Maximum = absvalue;
             });
          }
    
          # endregion sliderMediaPlayer
    

    Note

    CoreDispatcher is used to make changes to the UI thread from non UI Thread. In case of bottleneck on dispatcher thread, developer can choose to use dispatcher provided by UI-element they intend to update. For example:

          await sliderProgress.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { TimeSpan
    
          timespan = new TimeSpan(adaptiveSourceStatusUpdate.EndTime);
          double absvalue  = (int)Math.Round(timespan.TotalSeconds, MidpointRounding.AwayFromZero);
    
          sliderProgress.Maximum = absvalue; });
    
  6. At the end of the mediaElement_AdaptiveSourceStatusUpdated method, add the following code:

          setSliderStartTime(args.StartTime);
          setSliderEndTime(args.EndTime);
    
  7. At the end of the MediaOpened method, add the following code:

          sliderProgress.StepFrequency = SliderFrequency(mediaElement.NaturalDuration.TimeSpan);
          sliderProgress.Width = mediaElement.Width;
          setupTimer();
    
  8. Press CTRL+S to save the file.

To compile and test the application

  1. Press F6 to compile the project.
  2. Press F5 to run the application.
  3. At the top of the application, you can either use the default Smooth Streaming URL or enter a different one.
  4. Click Set Source.
  5. Test the slider bar.

You have completed lesson 2. In this lesson you added a slider to application.

Lesson 3: Select Smooth Streaming Streams

Smooth Streaming is capable to stream content with multiple language audio tracks that are selectable by the viewers. In this lesson, you will enable viewers to select streams. This lesson contains the following procedures:

  1. Modify the XAML file
  2. Modify the code behind file
  3. Compile and test the application

To modify the XAML file

  1. From Solution Explorer, right-click MainPage.xaml, and then click View Designer.

  2. Locate <Grid.RowDefinitions>, and modify the RowDefinitions so they looks like:

          <Grid.RowDefinitions>
             <RowDefinition Height="20"/>
             <RowDefinition Height="50"/>
             <RowDefinition Height="100"/>
             <RowDefinition Height="80"/>
             <RowDefinition Height="50"/>
          </Grid.RowDefinitions>
    
  3. Inside the <Grid></Grid> tags, add the following code to define a listbox control, so users can see the list of available streams, and select streams:

          <Grid Name="gridStreamAndBitrateSelection" Grid.Row="3">
             <Grid.RowDefinitions>
                 <RowDefinition Height="300"/>
             </Grid.RowDefinitions>
             <Grid.ColumnDefinitions>
                 <ColumnDefinition Width="250*"/>
                 <ColumnDefinition Width="250*"/>
             </Grid.ColumnDefinitions>
             <StackPanel Name="spStreamSelection" Grid.Row="1" Grid.Column="0">
                 <StackPanel Orientation="Horizontal">
                     <TextBlock Name="tbAvailableStreams" Text="Available Streams:" FontSize="16" VerticalAlignment="Center"></TextBlock>
                     <Button Name="btnChangeStreams" Content="Submit" Click="btnChangeStream_Click"/>
                 </StackPanel>
                 <ListBox x:Name="lbAvailableStreams" Height="200" Width="200" Background="Transparent" HorizontalAlignment="Left"
                     ScrollViewer.VerticalScrollMode="Enabled" ScrollViewer.VerticalScrollBarVisibility="Visible">
                     <ListBox.ItemTemplate>
                         <DataTemplate>
                             <CheckBox Content="{Binding Name}" IsChecked="{Binding isChecked, Mode=TwoWay}" />
                         </DataTemplate>
                     </ListBox.ItemTemplate>
                 </ListBox>
             </StackPanel>
          </Grid>
    
  4. Press CTRL+S to save the changes.

To modify the code behind file

  1. From Solution Explorer, right-click MainPage.xaml, and then click View Code.

  2. Inside the SSPlayer namespace, add a new class:

         #region class Stream
    
         public class Stream
         {
             private IManifestStream stream;
             public bool isCheckedValue;
             public string name;
    
             public string Name
             {
                 get { return name; }
                 set { name = value; }
             }
    
             public IManifestStream ManifestStream
             {
                 get { return stream; }
                 set { stream = value; }
             }
    
             public bool isChecked
             {
                 get { return isCheckedValue; }
                 set
                 {
                     // mMke the video stream always checked.
                     if (stream.Type == MediaStreamType.Video)
                     {
                         isCheckedValue = true;
                     }
                     else
                     {
                         isCheckedValue = value;
                     }
                 }
             }
    
             public Stream(IManifestStream streamIn)
             {
                 stream = streamIn;
                 name = stream.Name;
             }
         }
         #endregion class Stream
    
  3. At the beginning of the MainPage class, add the following variable definitions:

          private List<Stream> availableStreams;
          private List<Stream> availableAudioStreams;
          private List<Stream> availableTextStreams;
          private List<Stream> availableVideoStreams;
    
  4. Inside the MainPage class, add the following region:

         #region stream selection
         ///<summary>
         ///Functionality to select streams from IManifestStream available streams
         /// </summary>
    
         // This function is called from the mediaElement_ManifestReady event handler
         // to retrieve the streams and populate them to the local data members.
         public void getStreams(Manifest manifestObject)
         {
             availableStreams = new List<Stream>();
             availableVideoStreams = new List<Stream>();
             availableAudioStreams = new List<Stream>();
             availableTextStreams = new List<Stream>();
    
             try
             {
                 for (int i = 0; i<manifestObject.AvailableStreams.Count; i++)
                 {
                     Stream newStream = new Stream(manifestObject.AvailableStreams[i]);
                     newStream.isChecked = false;
    
                     //populate the stream lists based on the types
                     availableStreams.Add(newStream);
    
                     switch (newStream.ManifestStream.Type)
                     {
                         case MediaStreamType.Video:
                             availableVideoStreams.Add(newStream);
                             break;
                         case MediaStreamType.Audio:
                             availableAudioStreams.Add(newStream);
                             break;
                         case MediaStreamType.Text:
                             availableTextStreams.Add(newStream);
                             break;
                     }
    
                     // Select the default selected streams from the manifest.
                     for (int j = 0; j<manifestObject.SelectedStreams.Count; j++)
                     {
                         string selectedStreamName = manifestObject.SelectedStreams[j].Name;
                         if (selectedStreamName.Equals(newStream.Name))
                         {
                             newStream.isChecked = true;
                             break;
                         }
                     }
                 }
             }
             catch (Exception e)
             {
                 txtStatus.Text = "Error: " + e.Message;
             }
         }
    
         // This function set the list box ItemSource
         private async void refreshAvailableStreamsListBoxItemSource()
         {
             try
             {
                 //update the stream check box list on the UI
                 await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, ()
                     => { lbAvailableStreams.ItemsSource = availableStreams; });
             }
             catch (Exception e)
             {
                 txtStatus.Text = "Error: " + e.Message;
             }
         }
    
         // This function creates a selected streams list
         private void createSelectedStreamsList(List<IManifestStream> selectedStreams)
         {
             bool isOneVideoSelected = false;
             bool isOneAudioSelected = false;
    
             // Only one video stream can be selected
             for (int j = 0; j<availableVideoStreams.Count; j++)
             {
                 if (availableVideoStreams[j].isChecked && (!isOneVideoSelected))
                 {
                     selectedStreams.Add(availableVideoStreams[j].ManifestStream);
                     isOneVideoSelected = true;
                 }
             }
    
             // Select the first video stream from the list if no video stream is selected
             if (!isOneVideoSelected)
             {
                 availableVideoStreams[0].isChecked = true;
                 selectedStreams.Add(availableVideoStreams[0].ManifestStream);
             }
    
             // Only one audio stream can be selected
             for (int j = 0; j<availableAudioStreams.Count; j++)
             {
                 if (availableAudioStreams[j].isChecked && (!isOneAudioSelected))
                 {
                     selectedStreams.Add(availableAudioStreams[j].ManifestStream);
                     isOneAudioSelected = true;
                     txtStatus.Text = "The audio stream is changed to " + availableAudioStreams[j].ManifestStream.Name;
                 }
             }
    
             // Select the first audio stream from the list if no audio steam is selected.
             if (!isOneAudioSelected)
             {
                 availableAudioStreams[0].isChecked = true;
                 selectedStreams.Add(availableAudioStreams[0].ManifestStream);
             }
    
             // Multiple text streams are supported.
             for (int j = 0; j < availableTextStreams.Count; j++)
             {
                 if (availableTextStreams[j].isChecked)
                 {
                     selectedStreams.Add(availableTextStreams[j].ManifestStream);
                 }
             }
         }
    
         // Change streams on a smooth streaming presentation with multiple video streams.
         private async void changeStreams(List<IManifestStream> selectStreams)
         {
             try
             {
                 IReadOnlyList<IStreamChangedResult> returnArgs =
                     await manifestObject.SelectStreamsAsync(selectStreams);
             }
             catch (Exception e)
             {
                 txtStatus.Text = "Error: " + e.Message;
             }
         }
         #endregion stream selection
    
  5. Locate the mediaElement_ManifestReady method, append the following code at the end of the function:

         getStreams(manifestObject);
         refreshAvailableStreamsListBoxItemSource();
    

    So when MediaElement manifest is ready, the code gets a list of the available streams, and populates the UI list box with the list.

  6. Inside the MainPage class, locate the UI buttons click events region, and then add the following function definition:

         private void btnChangeStream_Click(object sender, RoutedEventArgs e)
         {
             List<IManifestStream> selectedStreams = new List<IManifestStream>();
    
             // Create a list of the selected streams
             createSelectedStreamsList(selectedStreams);
    
             // Change streams on the presentation
             changeStreams(selectedStreams);
         }
    

To compile and test the application

  1. Press F6 to compile the project.
  2. Press F5 to run the application.
  3. At the top of the application, you can either use the default Smooth Streaming URL or enter a different one.
  4. Click Set Source.
  5. The default language is audio_eng. Try to switch between audio_eng and audio_es. Every time, you select a new stream, you must click the Submit button.

You have completed lesson 3. In this lesson, you add the functionality to choose streams.

Lesson 4: Select Smooth Streaming tracks

A Smooth Streaming presentation can contain multiple video files encoded with different quality levels (bit rates) and resolutions. In this lesson, you will enable users to select tracks. This lesson contains the following procedures:

  1. Modify the XAML file
  2. Modify the code behind file
  3. Compile and test the application

To modify the XAML file

  1. From Solution Explorer, right-click MainPage.xaml, and then click View Designer.
  2. Locate the <Grid> tag with the name gridStreamAndBitrateSelection, append the following code at the end of the tag:
          <StackPanel Name="spBitRateSelection" Grid.Row="1" Grid.Column="1">
          <StackPanel Orientation="Horizontal">
              <TextBlock Name="tbBitRate" Text="Available Bitrates:" FontSize="16" VerticalAlignment="Center"/>
              <Button Name="btnChangeTracks" Content="Submit" Click="btnChangeTrack_Click" />
          </StackPanel>
          <ListBox x:Name="lbAvailableVideoTracks" Height="200" Width="200" Background="Transparent" HorizontalAlignment="Left"
                   ScrollViewer.VerticalScrollMode="Enabled" ScrollViewer.VerticalScrollBarVisibility="Visible">
              <ListBox.ItemTemplate>
                  <DataTemplate>
                      <CheckBox Content="{Binding Bitrate}" IsChecked="{Binding isChecked, Mode=TwoWay}"/>
                  </DataTemplate>
              </ListBox.ItemTemplate>
          </ListBox>
          </StackPanel>
    
  3. Press CTRL+S to save the changes

To modify the code behind file

  1. From Solution Explorer, right-click MainPage.xaml, and then click View Code.
  2. Inside the SSPlayer namespace, add a new class:
         #region class Track
         public class Track
         {
             private IManifestTrack trackInfo;
             public string _bitrate;
             public bool isCheckedValue;
    
             public IManifestTrack TrackInfo
             {
                 get { return trackInfo; }
                 set { trackInfo = value; }
             }
    
             public string Bitrate
             {
                 get { return _bitrate; }
                 set { _bitrate = value; }
             }
    
             public bool isChecked
             {
                 get { return isCheckedValue; }
                 set
                 {
                     isCheckedValue = value;
                 }
             }
    
             public Track(IManifestTrack trackInfoIn)
             {
                 trackInfo = trackInfoIn;
                 _bitrate = trackInfoIn.Bitrate.ToString();
             }
             //public Track() { }
         }
         #endregion class Track
    
  3. At the beginning of the MainPage class, add the following variable definitions:
         private List<Track> availableTracks;
    
  4. Inside the MainPage class, add the following region:
         #region track selection
         /// <summary>
         /// Functionality to select video streams
         /// </summary>
    
         /// This Function gets the tracks for the selected video stream
         public void getTracks(Manifest manifestObject)
         {
             availableTracks = new List<Track>();
    
             IManifestStream videoStream = getVideoStream();
             IReadOnlyList<IManifestTrack> availableTracksLocal = videoStream.AvailableTracks;
             IReadOnlyList<IManifestTrack> selectedTracksLocal = videoStream.SelectedTracks;
    
             try
             {
                 for (int i = 0; i < availableTracksLocal.Count; i++)
                 {
                     Track thisTrack = new Track(availableTracksLocal[i]);
                     thisTrack.isChecked = true;
    
                     for (int j = 0; j < selectedTracksLocal.Count; j++)
                     {
                         string selectedTrackName = selectedTracksLocal[j].Bitrate.ToString();
                         if (selectedTrackName.Equals(thisTrack.Bitrate))
                         {
                             thisTrack.isChecked = true;
                             break;
                         }
                     }
                     availableTracks.Add(thisTrack);
                 }
             }
             catch (Exception e)
             {
                 txtStatus.Text = e.Message;
             }
         }
    
         // This function gets the video stream that is playing
         private IManifestStream getVideoStream()
         {
             IManifestStream videoStream = null;
             for (int i = 0; i < manifestObject.SelectedStreams.Count; i++)
             {
                 if (manifestObject.SelectedStreams[i].Type == MediaStreamType.Video)
                 {
                     videoStream = manifestObject.SelectedStreams[i];
                     break;
                 }
             }
             return videoStream;
         }
    
         // This function set the UI list box control ItemSource
         private async void refreshAvailableTracksListBoxItemSource()
         {
             try
             {
                 // Update the track check box list on the UI
                 await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, ()
                     => { lbAvailableVideoTracks.ItemsSource = availableTracks; });
             }
             catch (Exception e)
             {
                 txtStatus.Text = "Error: " + e.Message;
             }
         }
    
         // This function creates a list of the selected tracks.
         private void createSelectedTracksList(List<IManifestTrack> selectedTracks)
         {
             // Create a list of selected tracks
             for (int j = 0; j < availableTracks.Count; j++)
             {
                 if (availableTracks[j].isCheckedValue == true)
                 {
                     selectedTracks.Add(availableTracks[j].TrackInfo);
                 }
             }
         }
    
         // This function selects the tracks based on user selection
         private void changeTracks(List<IManifestTrack> selectedTracks)
         {
             IManifestStream videoStream = getVideoStream();
             try
             {
                 videoStream.SelectTracks(selectedTracks);
             }
             catch (Exception ex)
             {
                 txtStatus.Text = ex.Message;
             }
         }
         #endregion track selection
    
  5. Locate the mediaElement_ManifestReady method, append the following code at the end of the function:
          getTracks(manifestObject);
          refreshAvailableTracksListBoxItemSource();
    
  6. Inside the MainPage class, locate the UI buttons click events region, and then add the following function definition:
          private void btnChangeStream_Click(object sender, RoutedEventArgs e)
          {
             List<IManifestStream> selectedStreams = new List<IManifestStream>();
    
             // Create a list of the selected streams
             createSelectedStreamsList(selectedStreams);
    
             // Change streams on the presentation
             changeStreams(selectedStreams);
          }
    

To compile and test the application

  1. Press F6 to compile the project.
  2. Press F5 to run the application.
  3. At the top of the application, you can either use the default Smooth Streaming URL or enter a different one.
  4. Click Set Source.
  5. By default, all of the tracks of the video stream are selected. To experiment the bit rate changes, you can select the lowest bit rate available, and then select the highest bit rate available. You must click Submit after each change. You can see the video quality changes.

You have completed lesson 4. In this lesson, you add the functionality to choose tracks.