程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Windows 8風格應用開發入門 二十一 構建簡單媒體播放器

Windows 8風格應用開發入門 二十一 構建簡單媒體播放器

編輯:關於.NET

《快速構建Windows 8風格應用20-MediaElement》博文中提到了如何使用MediaElement對象進行播放視頻的簡單功能,但是在實際應用中需要更復雜的功能,例如:控制視頻播放的控件、全屏模式、進度條等等其他功能。

本篇博文中示例使用應用程序中包含的媒體文件,當然我們也可以通過網絡或者本地[使用FileOpenPicker]進行加載某一媒體文件。

MSDN中關於媒體播放器的示例代碼下載地址:XAML media playback sample。

構建基本的MediaElement控件

首先我們創建一個MediaElement控件並添加到ContentControl對象中,這樣做的目的是為了啟用全盤模式功能。

XAML代碼如下:

<ContentControl x:Name="videoContainer" KeyUp="VideoContainer_KeyUp" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"  Height="400" Grid.Row="0" >    
            <MediaElement Name="videoMediaElement" Source="Video/Azure_Tmobile_500k.wmv"  
                              MediaOpened="videoElement_MediaOpened"  
                              MediaEnded="videoMediaElement_MediaEnded"  
                              MediaFailed="videoMediaElement_MediaFailed"
                              CurrentStateChanged="videoMediaElement_CurrentStateChanged"
                              PosterSource="Media/1.png"
                              AutoPlay="False" />    
</ContentControl>

MediaElement控件的Source屬性指向要播放的音頻或視頻文件。,該屬性可以設置為應用中的某一文件的URI或網絡上的文件的 URI。當然我們也可以使用 SetSource 方法將源設置為使用 FileOpenPicker 對象從本地系統檢索的文件。

AutoPlay屬性指定是否在加載 MediaElement 後開始播放媒體文件,默認值為 true。

MediaElement控件聲明了MediaOpened、MediaEnded、CurrentStateChanged和 MediaFailed 事件。

最後設置PosterSource屬性值,PosterSource是一個圖像,它在媒體加載前為MediaElement控件提供視覺展示。

通常PosterSource在以下情況下顯示:

1)未設置有效的源,例如:未設置Source、Source設置為Null、或源無效;

2)加載媒體時;

3)“播放到”流式播放期間;

構建控制MediaElement播放控件

一般控制MediaElement播放包括播放,停止和暫停等功能。

常用控制MediaElement播放功能包括:

1)停止:調用 Stop 方法;

2)暫停:調用 Pause 方法;

3)快進:將MediaElement控件的DefaultPlaybackRate屬性的值設置為 2.0,我們可以調整此值,以提高或降低快進的速率。 然後,處理程序調用 Play 方法;

4)快退:將MediaElement控件的DefaultPlaybackRate屬性的值設置為 -2.0,我們可以調整此值,以提高或降低快退的速率。然後,處理程序調用 Play 方法;

5)播放:如果MediaElement控件的DefaultPlaybackRate屬性值不是 1.0,則將DefaultPlaybackRate 設置為 1.0。然後,處理程序調用 Play 方法。

6)靜音:在 true 和false 間切換 IsMuted 屬性;

7)音量增加、音量降低:如果IsMuted為true,則取消音量靜音,然後處理程序按 0.1 增加或降低Volume屬性。 注意:音量級別范圍從 0.0 到 1.0;

對應這些控制功能的XAML代碼可如下:

<StackPanel Orientation="Horizontal">   
    <Button Name="btnPlay" Click="btnPlay_Click"
            Style="{StaticResource transportStyle}" Content="Play" />   
    <Button Name="btnPause" Click="btnPause_Click"
            Style="{StaticResource transportStyle}" Content="Pause" />   
    <Button Name="btnStop" Click="btnStop_Click"
            Style="{StaticResource transportStyle}" Content="Stop" />   
    <Button Name="btnReverse" Click="btnReverse_Click"
            Style="{StaticResource transportStyle}" Content="Rewind" />   
    <Button Name="btnForward" Click="btnForward_Click"
            Style="{StaticResource transportStyle}" Content="Forward" />   
    <Button Name="btnMute" Click="btnMute_Click"
            Style="{StaticResource transportStyle}" Content="Mute" />   
    <Button Name="btnFullScreenToggle" Click="btnFullScreenToggle_Click"
            Style="{StaticResource transportStyle}" Content="Full" />   
    <ComboBox Name="cbAudioTracks"
              SelectionChanged="cbAudioTracks_SelectionChanged"
              Width="75" />   
    <Button Name="btnVolumeUp" Click="btnVolumeUp_Click"
            Style="{StaticResource transportStyle}" Content="-" />   
    <Button Name="btnVolumeDown" Click="btnVolumeDown_Click"
            Style="{StaticResource transportStyle}" Content="+" />   
    <TextBlock Name="txtVolume" FontSize="14"
               Text="{Binding Volume, ElementName=videoMediaElement}"
               VerticalAlignment="Center" HorizontalAlignment="Right" />   
</StackPanel>

相應的C#代碼如下:

private void btnPlay_Click(object sender, RoutedEventArgs e)   
{   
    if (videoMediaElement.DefaultPlaybackRate != 1)   
    {   
        videoMediaElement.DefaultPlaybackRate = 1.0;   
    }   
        
    videoMediaElement.Play();   
}   
        
private void btnPause_Click(object sender, RoutedEventArgs e)   
{   
        videoMediaElement.Pause();   
}   
        
private void btnStop_Click(object sender, RoutedEventArgs e)   
{   
    videoMediaElement.Stop();   
}   
        
private void btnForward_Click(object sender, RoutedEventArgs e)   
{   
    videoMediaElement.DefaultPlaybackRate = 2.0;   
    videoMediaElement.Play();   
}   
        
private void btnReverse_Click(object sender, RoutedEventArgs e)   
{   
    videoMediaElement.DefaultPlaybackRate = -2;   
    videoMediaElement.Play();;   
}   
        
private void btnVolumeDown_Click(object sender, RoutedEventArgs e)   
{   
    if (videoMediaElement.IsMuted)   
    {   
        videoMediaElement.IsMuted = false;   
    }   
        
    if (videoMediaElement.Volume < 1)   
    {   
        videoMediaElement.Volume += .1;   
    }   
}   
        
private void btnMute_Click(object sender, RoutedEventArgs e)   
{   
    videoMediaElement.IsMuted = !videoMediaElement.IsMuted;   
}   
        
private void btnVolumeUp_Click(object sender, RoutedEventArgs e)   
{   
    if (videoMediaElement.IsMuted)   
    {   
        videoMediaElement.IsMuted = false;   
    }   
        
    if (videoMediaElement.Volume > 0)   
    {   
        videoMediaElement.Volume -= .1;   
    }   
}

構建MediaElement的全屏播放功能

啟用全屏視頻播放功能,需要將MediaElement的Width和Height設置為當前窗口的Windows.Bounds(使用的是Window.Current.Bounds.Width和 Window.Current.Bounds.Height)。

啟用全屏視頻播放功能步驟如下:

1)隱藏應用程序中的所有UI 元素;

2)將MediaElement的Width和Height設置為顯示的最大范圍,這會讓MediaElement控件的高度和寬度與窗口的對應尺寸一致。但是需要首先保存當前Height和Width,方便在應用退出全屏模式時將控件恢復為正確的大小。然後,可以將ContentControl和MediaElement的尺寸設置為當前窗口的Window.Bounds;

退出全屏視頻播放功能步驟如下:

1)偵聽鍵盤事件以檢測用戶希望何時退出全屏模式,通常我們使用Esc 鍵通來退出全屏模式。在ContentControl控件中添加KeyUp事件。在KeyUp事件處理程序中,若應用處於全屏模式並且按下的鍵為Windows.System.VirtualKey.Escape時退出全屏模式。實際應用中我們還應該添加Manipulation觸摸事件來處理觸摸手勢,進行退出全盤模式;

2)恢復 UI 元素的可見性;

3)將ContentControl和MediaElement的Width和Height恢復為其原來的尺寸;

C#代碼可如下:

private bool _isFullscreenToggle = false;   
public bool IsFullscreen   
{   
    get { return _isFullscreenToggle; }   
    set { _isFullscreenToggle = value; }   
}   
        
private Size _previousVideoContainerSize = new Size();   
        
private void FullscreenToggle()   
{   
    this.IsFullscreen = !this.IsFullscreen;   
        
    if (this.IsFullscreen)   
    {      
        TransportControlsPanel.Visibility = Visibility.Collapsed;   
        
        _previousVideoContainerSize.Width = videoContainer.ActualWidth;   
        _previousVideoContainerSize.Height = videoContainer.ActualHeight;   
        
        videoContainer.Width = Window.Current.Bounds.Width;   
        videoContainer.Height = Window.Current.Bounds.Height;   
        videoMediaElement.Width = Window.Current.Bounds.Width;   
        videoMediaElement.Height = Window.Current.Bounds.Height;   
    }   
    else
    {   
        TransportControlsPanel.Visibility = Visibility.Visible;   
        
        videoContainer.Width = _previousVideoContainerSize.Width;   
        videoContainer.Height = _previousVideoContainerSize.Height;   
        videoMediaElement.Width = _previousVideoContainerSize.Width;   
        videoMediaElement.Height = _previousVideoContainerSize.Height;   
    }   
}   
        
private void btnFullScreenToggle_Click(object sender, RoutedEventArgs e)   
{   
    FullscreenToggle();   
}   
        
private void VideoContainer_KeyUp(object sender, KeyRoutedEventArgs e)   
{   
    if (IsFullscreen && e.Key == Windows.System.VirtualKey.Escape)   
    {   
        FullscreenToggle();   
    }   
        
    e.Handled = true;   
}

構建MediaElement的滑動進度條功能

通常我們使用Slider控件來顯示或更改視頻位置,大致思路為設置Slider控件並使用DispatcherTimer來保持滑塊與MediaElement.Position屬性的同步。

首先XAML中聲明Slider控件。

<SliderName="timelineSlider"Margin="10,0"Width="200"/>

注意:Slider控件的StepFrequency屬性定義了滑塊的刻度上步驟的頻率。這裡演示是基於MediaElement的NaturalDuration屬性的值來設置StepFrequency屬性。

如果我們想采用更高的精度可以使用設置為250 毫秒的最低頻率調節這些數字。

C#代碼中聲明以下代碼:

private double SliderFrequency(TimeSpan timevalue)   
{   
    double stepfrequency = -1;   
        
    double absvalue = (int)Math.Round(   
        timevalue.TotalSeconds, MidpointRounding.AwayFromZero);   
        
    stepfrequency = (int)(Math.Round(absvalue / 100));   
        
    if (timevalue.TotalMinutes >= 10 && timevalue.TotalMinutes < 30)   
    {   
        stepfrequency = 10;   
    }   
    else if (timevalue.TotalMinutes >= 30 && timevalue.TotalMinutes < 60)   
    {   
        stepfrequency = 30;   
    }   
    else if (timevalue.TotalHours >= 1)   
    {   
        stepfrequency = 60;   
    }   
        
    if (stepfrequency == 0) stepfrequency += 1;   
        
    if (stepfrequency == 1)   
    {   
        stepfrequency = absvalue / 100;   
    }   
        
    return stepfrequency;   
}

我們需要DispatcherTimer來保持Slider與媒體同步,並將DispatcherTimer的Interval屬性值設置為Slider 的StepFrequency。對於每次計時器計時,Slider的Value屬性設置為MediaElement.Position。

另外我們需要在以下情況中關閉掉計時器:

1)MediaElement當前狀態為暫停或停止時;

2)滑塊的滑條移動時;

3)進度條不可見時,例如:全屏模式下。這裡需要注意的是進度條在推出全屏模式後重新開始計時;

下面C#代碼是如何創建和設置DispatcherTimer:

private DispatcherTimer _timer;   
        
private void SetupTimer()   
{   
    _timer = new DispatcherTimer();   
    _timer.Interval = TimeSpan.FromSeconds(timelineSlider.StepFrequency);   
    StartTimer();   
}   
        
private void _timer_Tick(object sender, object e)   
{   
    if (!_sliderpressed)   
    {   
        timelineSlider.Value = videoMediaElement.Position.TotalSeconds;   
    }   
}   
        
private void StartTimer()   
{   
    _timer.Tick += _timer_Tick;   
    _timer.Start();   
}   
        
private void StopTimer()   
{   
    _timer.Stop();   
    _timer.Tick -= _timer_Tick;   
  
}

同時我們需要在頁面的Loaded事件和MediaOpened事件中觸發執行基本任務的處理程序。

當CurrentStateChanged和MediaEnded事件觸發時,進行處理MediaElement上的狀態更改。

在ValueChanged事件中處理Slider上的狀態更改。

最後,PointerPressedEvent 和 PointerCaptureLostEvent 事件處理程序處理與Slider的用戶交互。

C#代碼如下:

private void MainPage_Loaded(object sender, RoutedEventArgs e)   
{   
    timelineSlider.ValueChanged += timelineSlider_ValueChanged;   
        
    PointerEventHandler pointerpressedhandler = new PointerEventHandler(slider_PointerEntered);   
    timelineSlider.AddHandler(Control.PointerPressedEvent, pointerpressedhandler, true);   
        
    PointerEventHandler pointerreleasedhandler = new PointerEventHandler(slider_PointerCaptureLost);   
    timelineSlider.AddHandler(Control.PointerCaptureLostEvent, pointerreleasedhandler, true);   
}   
        
void videoElement_MediaOpened(object sender, RoutedEventArgs e)   
{   
    double absvalue = (int)Math.Round(   
        videoMediaElement.NaturalDuration.TimeSpan.TotalSeconds,   
        MidpointRounding.AwayFromZero);   
        
    timelineSlider.Maximum = absvalue;   
        
    timelineSlider.StepFrequency =   
        SliderFrequency(videoMediaElement.NaturalDuration.TimeSpan);   
        
    SetupTimer();   
        
    // Helper method to populate the combobox with audio tracks.   
    PopulateAudioTracks(videoMediaElement, cbAudioTracks);   
}

最後需要在C#代碼中處理顯示指針位置更改和ValueChanged、CurrentStateChanged和 MediaEnded 事件的事件處理程序。

private bool _sliderpressed = false;   
        
void slider_PointerEntered(object sender, PointerRoutedEventArgs e)   
{   
    _sliderpressed = true;   
}   
        
void slider_PointerCaptureLost(object sender, PointerRoutedEventArgs e)   
{   
    videoMediaElement.Position = TimeSpan.FromSeconds(timelineSlider.Value);   
    _sliderpressed = false;   
}   
        
void timelineSlider_ValueChanged(object sender, Windows.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs e)   
{   
    if (!_sliderpressed)   
    {   
        videoMediaElement.Position = TimeSpan.FromSeconds(e.NewValue);   
    }   
}   
        
void videoMediaElement_CurrentStateChanged(object sender, RoutedEventArgs e)   
{   
    if (videoMediaElement.CurrentState == MediaElementState.Playing)   
    {   
        if (_sliderpressed)   
        {   
            _timer.Stop();   
        }   
        else
        {   
            _timer.Start();   
        }   
    }   
        
    if (videoMediaElement.CurrentState == MediaElementState.Paused)   
    {   
        _timer.Stop();   
    }   
        
    if (videoMediaElement.CurrentState == MediaElementState.Stopped)   
    {   
        _timer.Stop();   
        timelineSlider.Value = 0;   
    }   
}   
        
void videoMediaElement_MediaEnded(object sender, RoutedEventArgs e)   
{   
    StopTimer();   
    timelineSlider.Value = 0.0;   
}   
        
private void videoMediaElement_MediaFailed(object sender, ExceptionRoutedEventArgs e)   
{   
    // get HRESULT from event args   
    string hr = GetHresultFromErrorMessage(e);   
        
    // Handle media failed event appropriately   
}   
        
private string GetHresultFromErrorMessage(ExceptionRoutedEventArgs e)   
{   
    String hr = String.Empty;   
    String token = "HRESULT - ";   
    const int hrLength = 10;     // eg "0xFFFFFFFF"   
        
    int tokenPos = e.ErrorMessage.IndexOf(token, StringComparison.Ordinal);   
    if (tokenPos != -1)   
    {   
        hr = e.ErrorMessage.Substring(tokenPos + token.Length, hrLength);   
    }   
        
    return hr;   
}

到此為止一個簡單的媒體播放器就基本完成了!運行效果圖如下:

注意:本博文介紹的媒體播放器在實際應用開發中,對於開發者是遠遠不夠理想的,我們需要在此基礎上進一步去優化。

最後引用MSDN中給到的性能注意事項:

音頻和視頻播放是非常耗費資源的操作。有關媒體應用性能的詳細信息,可參閱優化媒體資源。

1)盡可能顯示全屏視頻播放

XAML 框架可以在只呈現視頻時優化呈現的視頻,這種情況類似於全屏播放的情況。 此方法使用較少的電源,並且產生高於同時顯示其他元素情況下的頻率。若要優化媒體播放,請將 MediaElement 對象的大小設置為屏幕的寬度和高度,並且不顯示其他 XAML 元素。在全屏模式下覆蓋 MediaElement頂部的 XAML 元素可能有正當理由 — 例如,隱藏的字幕或臨時傳輸控件 — 但在不需要時可以隱藏這些元素。

2)延遲設置 MediaElement 源

XAML 框架盡可能長地延遲加載 DLL 和創建大型對象。MediaElement在源由 Source 屬性或 SetSource 方法初始設置時完成此操作。僅在用戶准備好播放媒體後設置源,可減少與 MediaElement關聯的絕大部分性能成本。

3)將視頻分辨率與設備分辨率匹配

解碼視頻主要使用內存和 GPU,因此應選擇與要在其上顯示的設備的分辨率接近的視頻格式。例如,解碼高清 (1080) 視頻,然後將其縮小至相當小的尺寸進行顯示會占有不必要的資源。許多應用不會將相同的視頻解碼為不同的分辨率,但如果可用,請使用接近顯示設備的分辨率的解碼。

4)請勿動態顯示 MediaElement 對象。

動畫和媒體播放都會耗費大量系統資源。 在合適的上下文中使用動畫可以創建完美動人的效果。 但是,出於性能方面考慮,請避免在使用 C++、C# 或 Visual Basic 的 Metro 風格應用中動態顯示 MediaElement 對象。

本文出自 “王祖康” 博客,請務必保留此出處http://wzk89.blog.51cto.com/1660752/1033384

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved