如何生成平滑流式处理 Windows 应用商店应用程序How to Build a Smooth Streaming Windows Store Application

借助适用于 Windows 8 的平滑流式处理客户端 SDK,开发人员可以生成支持按需付费、直播平滑流式处理内容的 Windows 应用商店应用程序。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. 除了播放平滑流式处理内容这一基本功能以外,该 SDK 还提供其他丰富功能,例如 Microsoft PlayReady 保护、质量级别限制、实时 DVR、音频流切换、收听状态更新(如质量级别更改)和错误事件,等等。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. 有关详细信息,请参阅 适用于 Windows 8 的播放器框架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

备注

Visual Studio 2017 不支持 Windows 应用商店项目 8.1 版及更早的版本。Windows Store projects version 8.1 and earlier are not supported in Visual Studio 2017. 有关详细信息,请参阅 Visual Studio 2017 平台目标以及兼容性For more information, see Visual Studio 2017 Platform Targeting and Compatibility.

可从 MSDN 开发人员代码示例(代码库)下载每一课后生成的解决方案:The completed solution for each lesson can be downloaded from MSDN Developer Code Samples (Code Gallery):

  • 第 1 课 - 简单的 Windows 8 平滑流式处理媒体播放器,Lesson 1 - A Simple Windows 8 Smooth Streaming Media Player,
  • 第 2 课 - 带滚动条控件的简单 Windows 8 平滑流式处理媒体播放器,Lesson 2 - A Simple Windows 8 Smooth Streaming Media Player with a Slider Bar Control,
  • 第 3 课 - 支持流选择的 Windows 8 平滑流式处理媒体播放器,Lesson 3 - A Windows 8 Smooth Streaming Media Player with Stream Selection,
  • 第 4 课 - 支持轨迹选择的 Windows 8 平滑流式处理媒体播放器。Lesson 4 - A Windows 8 Smooth Streaming Media Player with Track Selection.

第 1 课:创建基本的平滑流式处理应用商店应用程序Lesson 1: Create a Basic Smooth Streaming Store Application

本课涉及使用 MediaElement 控件创建一个 Windows 应用商店应用程序,以播放平滑流内容。In this lesson, you will create a Windows Store application with a MediaElement control to play Smooth Stream content. 运行的应用程序如下所示:The running application looks like:

平滑流式处理 Windows 应用商店应用程序示例

有关开发 Windows 应用商店应用程序的详细信息,请参阅 开发适用于 Windows 8 的极佳应用For more information on developing Windows Store application, see Develop Great Apps for Windows 8. 本课包含以下过程:This lesson contains the following procedures:

  1. 创建 Windows 应用商店项目Create a Windows Store project
  2. 设计用户界面 (XAML)Design the user interface (XAML)
  3. 修改代码隐藏文件Modify the code behind file
  4. 编译和测试应用程序Compile and test the application

创建 Windows Store 项目To create a Windows Store project

  1. 运行 Visual Studio;支持版本 2012 到 2015。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 已安装/模板/Visual C#/Windows 应用商店Installed/Templates/Visual C#/Windows Store
    模板Template 空白应用 (XAML)Blank App (XAML)
    名称Name SSPlayerSSPlayer
    位置Location C:\SSTutorialsC:\SSTutorials
    解决方案名称Solution Name SSPlayerSSPlayer
    创建解决方案的目录Create directory for solution (选定)(selected)
  4. 单击 “确定”Click OK.

添加对平滑流式处理客户端 SDK 的引用To add a reference to the Smooth Streaming Client SDK

  1. 在解决方案资源管理器中,右键单击“SSPlayer” ,并单击“添加引用” 。From Solution Explorer, right-click SSPlayer, and then click Add Reference.

  2. 键入或选择以下值:Type or select the following values:

    名称Name Value
    Reference groupReference group Windows/ExtensionsWindows/Extensions
    ReferenceReference 选择适用于 Windows 8 和 Microsoft Visual C++ 运行时程序包的 Microsoft 平滑流式处理客户端 SDKSelect Microsoft Smooth Streaming Client SDK for Windows 8 and Microsoft Visual C++ Runtime Package
  3. 单击 “确定”Click OK.

添加引用后,必须选择目标平台(x64 或 x86),添加引用对于任何 CPU 平台配置都不起作用。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. 在解决方案资源管理器中,双击“MainPage.xaml” 以在设计视图中将其打开。From Solution Explorer, double click MainPage.xaml to open it in the design view.

  2. 在该 XAML 文件中找到 <Grid> 和 </Grid> 标记,并在这两个标记之间粘贴以下代码 :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>
    

    MediaElement 控件用于播放媒体。The MediaElement control is used to playback media. 在下一课中,我们使用名为 sliderProgress 的滚动条控件来控制媒体进度。The slider control named sliderProgress will be used in the next lesson to control the media progress.

  3. CTRL+S 保存文件。Press CTRL+S to save the file.

MediaElement 控件并非原本就支持平滑流式处理内容。The MediaElement control does not support Smooth Streaming content out-of-box. 若要启用平滑流式处理支持,必须按文件扩展名和 MIME 类型注册平滑流式处理字节流处理程序。To enable the Smooth Streaming support, you must register the Smooth Streaming byte-stream handler by file name extension and MIME type. 若要注册,可以使用 Windows.Media 命名空间的 MediaExtensionManager.RegisterByteStreamHandler 方法。To register, you use the MediaExtensionManager.RegisterByteStreamHandler method of the Windows.Media namespace.

在此 XAML 文件中,某些事件处理程序与控件关联。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. 在解决方案资源管理器中,右键单击“MainPage.xaml” ,并单击“查看代码” 。From Solution Explorer, right-click MainPage.xaml, and then click View Code.

  2. 在该文件的顶部,添加以下 using 语句:At the top of the file, add the following using statement:

     using Windows.Media;
    
  3. MainPage 类的开头,添加以下数据成员:At the beginning of the MainPage class, add the following data member:

      private MediaExtensionManager extensions = new MediaExtensionManager();
    
  4. MainPage 构造函数的末尾,添加以下两行: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. 在 MainPage 类的末尾,粘贴以下代码 :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
    

    现已定义 sliderProgress_PointerPressed 事件处理程序。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. CTRL+S 保存文件。Press CTRL+S to save the file.

完成的代码隐藏文件应如下所示:The finished the code behind file shall look like this:

创建平滑流式处理 Windows 应用商店应用程序时 Visual Studio 中的代码视图

编译和测试应用程序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. F6 编译项目。Press F6 to compile the project.
  4. F5 运行应用程序。Press F5 to run the application.
  5. 在应用程序的顶部,可以使用默认的平滑流式处理 URL,或输入一个不同的 URL。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.

第 1 课到此结束。You have completed lesson1. 在本课中,已学习如何使用 MediaElement 控件来播放平滑流式处理内容。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.

第 2 课:添加滚动条以控制媒体进度Lesson 2: Add a Slider Bar to Control the Media Progress

在第 1 课,已使用 MediaElement XAML 控件创建了一个 Windows 应用商店应用程序,用于播放平滑流式处理媒体内容。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.

在本教程中,我们使用一个计时器,基于 MediaElement 控件的当前位置更新该滚动条的位置。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. 源解析程序采用 URL 或字节流,并为该内容创建相应的媒体源。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. 添加 MediaElement 事件处理程序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. 在解决方案资源管理器中,右键单击“MainPage.xaml” ,并单击“查看代码” 。From Solution Explorer, right click MainPage.xaml, and then click View Code.

  2. 在该文件的开头,添加以下 using 语句:At the beginning of the file, add the following using statement:

         using Microsoft.Media.AdaptiveStreaming;
    
  3. 在 MainPage 类的开头,添加以下数据成员: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. MainPage 构造函数中的 this.Initialize Components(); 行以及在上一课编写的注册代码行的后面添加以下代码: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. MainPage 构造函数中,修改两个 RegisterByteStreamHandler 方法以添加第四个参数: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. CTRL+S 保存文件。Press CTRL+S to save the file.

添加自适应源管理器级别事件处理程序To add the adaptive source manager level event handler

  1. 在解决方案资源管理器中,右键单击“MainPage.xaml” ,并单击“查看代码” 。From Solution Explorer, right click MainPage.xaml, and then click View Code.

  2. MainPage 类中,添加以下数据成员:Inside the MainPage class, add the following data member:

      private AdaptiveSource adaptiveSource = null;
    
  3. MainPage 类的末尾,添加以下事件处理程序: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. MainPage 构造函数的末尾,添加以下行以订阅自适应源打开事件: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. CTRL+S 保存文件。Press CTRL+S to save the file.

添加自适应源级别事件处理程序To add adaptive source level event handlers

  1. 在解决方案资源管理器中,右键单击“MainPage.xaml” ,并单击“查看代码” 。From Solution Explorer, right click MainPage.xaml, and then click View Code.

  2. MainPage 类中,添加以下数据成员:Inside the MainPage class, add the following data member:

      private AdaptiveSourceStatusUpdatedEventArgs adaptiveSourceStatusUpdate; 
      private Manifest manifestObject;
    
  3. MainPage 类的末尾,添加以下事件处理程序: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. mediaElement AdaptiveSourceOpened 方法的末尾,添加以下代码以订阅事件: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. CTRL+S 保存文件。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. 每个 AdaptiveSource 包含其自身的事件,所有的 AdaptiveSource 事件将级联在 AdaptiveSourceManager 之下。Each AdaptiveSource includes its own events and all AdaptiveSource events will be cascaded under AdaptiveSourceManager.

添加媒体元素事件处理程序To add Media Element event handlers

  1. 在解决方案资源管理器中,右键单击“MainPage.xaml” ,并单击“查看代码” 。From Solution Explorer, right click MainPage.xaml, and then click View Code.

  2. MainPage 类的末尾,添加以下事件处理程序: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. MainPage 构造函数的末尾,添加以下代码以订阅事件: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. CTRL+S 保存文件。Press CTRL+S to save the file.

  1. 在解决方案资源管理器中,右键单击“MainPage.xaml” ,并单击“查看代码” 。From Solution Explorer, right click MainPage.xaml, and then click View Code.

  2. 在该文件的开头,添加以下 using 语句:At the beginning of the file, add the following using statement:

         using Windows.UI.Core;
    
  3. MainPage 类中,添加以下数据成员:Inside the MainPage class, add the following data members:

          public static CoreDispatcher _dispatcher;
          private DispatcherTimer sliderPositionUpdateDispatcher;
    
  4. MainPage 构造函数的末尾,添加以下代码: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. MainPage 类的末尾,添加以下代码: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
    

    备注

    CoreDispatcher 用于从非 UI 线程对 UI 线程进行更改。CoreDispatcher is used to make changes to the UI thread from non UI Thread. 如果调度程序线程出现瓶颈,开发人员可以选择使用他们想要更新的 UI 元素提供的调度程序。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. 在 mediaElement_AdaptiveSourceStatusUpdated 方法的末尾,添加以下代码 :At the end of the mediaElement_AdaptiveSourceStatusUpdated method, add the following code:

          setSliderStartTime(args.StartTime);
          setSliderEndTime(args.EndTime);
    
  7. MediaOpened 方法的末尾,添加以下代码:At the end of the MediaOpened method, add the following code:

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

编译和测试应用程序To compile and test the application

  1. F6 编译项目。Press F6 to compile the project.
  2. F5 运行应用程序。Press F5 to run the application.
  3. 在应用程序的顶部,可以使用默认的平滑流式处理 URL,或输入一个不同的 URL。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.

已完成第 2 课。You have completed lesson 2. 在本课中,已将一个滑块添加到应用程序。In this lesson you added a slider to application.

第 3 课:选择平滑流式处理流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. 修改 XAML 文件Modify the XAML file
  2. 修改代码隐藏文件Modify the code behind file
  3. 编译和测试应用程序Compile and test the application

修改 XAML 文件To modify the XAML file

  1. 在解决方案资源管理器中,右键单击“MainPage.xaml” ,并单击“查看设计器” 。From Solution Explorer, right-click MainPage.xaml, and then click View Designer.

  2. 找到 <Grid.RowDefinitions>,并按如下所示修改 RowDefinitions: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. 在 <Grid></Grid> 标记中,添加以下代码以定义一个列表框控件,使用户能够看到可用流的列表及选择流: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. CTRL+S 保存更改。Press CTRL+S to save the changes.

修改代码隐藏文件To modify the code behind file

  1. 在解决方案资源管理器中,右键单击“MainPage.xaml” ,并单击“查看代码” 。From Solution Explorer, right-click MainPage.xaml, and then click View Code.

  2. 在 SSPlayer 命名空间中添加一个新类: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. 在 MainPage 类的开头,添加以下变量定义: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. 在 MainPage 类中,添加以下区域: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. 找到 mediaElement_ManifestReady 方法,并在函数的末尾追加以下代码:Locate the mediaElement_ManifestReady method, append the following code at the end of the function:

         getStreams(manifestObject);
         refreshAvailableStreamsListBoxItemSource();
    

    因此,当 MediaElement 清单准备就绪时,该代码将获取可用流的列表,并将该列表的内容填充到 UI 列表框。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. 在 MainPage 类中,找到 UI 按钮单击事件区域,并添加以下函数定义: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. F6 编译项目。Press F6 to compile the project.
  2. F5 运行应用程序。Press F5 to run the application.
  3. 在应用程序的顶部,可以使用默认的平滑流式处理 URL,或输入一个不同的 URL。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. 默认语言为 audio_eng。The default language is audio_eng. 尝试在 audio_eng 和 audio_es 之间切换。Try to switch between audio_eng and audio_es. 每次选择一个新流时,都必须单击“提交”按钮。Every time, you select a new stream, you must click the Submit button.

已完成第 3 课。You have completed lesson 3. 本课中添加了用于选择流的功能。In this lesson, you add the functionality to choose streams.

第 4 课:选择平滑流式处理曲目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. 修改 XAML 文件Modify the XAML file
  2. 修改代码隐藏文件Modify the code behind file
  3. 编译和测试应用程序Compile and test the application

修改 XAML 文件To modify the XAML file

  1. 在解决方案资源管理器中,右键单击“MainPage.xaml” ,并单击“查看设计器” 。From Solution Explorer, right-click MainPage.xaml, and then click View Designer.
  2. 找到名为 gridStreamAndBitrateSelection 的 <Grid> 标记,并在该标记的末尾追加以下代码 :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. CTRL+S 保存更改Press CTRL+S to save the changes

修改代码隐藏文件To modify the code behind file

  1. 在解决方案资源管理器中,右键单击“MainPage.xaml” ,并单击“查看代码” 。From Solution Explorer, right-click MainPage.xaml, and then click View Code.
  2. 在 SSPlayer 命名空间中添加一个新类: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. 在 MainPage 类的开头,添加以下变量定义:At the beginning of the MainPage class, add the following variable definitions:
         private List<Track> availableTracks;
    
  4. 在 MainPage 类中,添加以下区域: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. 找到 mediaElement_ManifestReady 方法,并在函数的末尾追加以下代码:Locate the mediaElement_ManifestReady method, append the following code at the end of the function:
          getTracks(manifestObject);
          refreshAvailableTracksListBoxItemSource();
    
  6. 在 MainPage 类中,找到 UI 按钮单击事件区域,并添加以下函数定义: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. F6 编译项目。Press F6 to compile the project.
  2. F5 运行应用程序。Press F5 to run the application.
  3. 在应用程序的顶部,可以使用默认的平滑流式处理 URL,或输入一个不同的 URL。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.

已完成第 4 课。You have completed lesson 4. 在本课中,已添加了用于选择曲目的功能。In this lesson, you add the functionality to choose tracks.

媒体服务学习路径Media Services learning paths

媒体服务 v3(最新版本)Media Services v3 (latest)

查看最新版本的 Azure 媒体服务!Check out the latest version of Azure Media Services!

媒体服务 v2(旧版)Media Services v2 (legacy)

其他资源:Other Resources: