您的位置:首页 > 其它

快速构建Windows 8风格应用21-构建简单媒体播放器

2012-10-22 10:22 656 查看
本篇博文主要介绍如何构建一个简单的媒体播放器。

《快速构建Windows8风格应用20-MediaElement》博文中提到了如何使用MediaElement对象进行播放视频的简单功能,但是在实际应用中需要更复杂的功能,例如:控制视频播放的控件、全屏模式、进度条等等其他功能。

本篇博文中示例使用应用程序中包含的媒体文件,当然我们也可以通过网络或者本地[使用FileOpenPicker]进行加载某一媒体文件。

MSDN中关于媒体播放器的示例代码下载地址:XAMLmediaplaybacksample。

构建基本的MediaElement控件

首先我们创建一个MediaElement控件并添加到ContentControl对象中,这样做的目的是为了启用全盘模式功能。

XAML代码如下:

<ContentControlx:Name="videoContainer"KeyUp="VideoContainer_KeyUp"HorizontalContentAlignment="Center"VerticalContentAlignment="Center"Height="400"Grid.Row="0">

[code]<MediaElementName="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>

[/code]

MediaElement控件的Source属性指向要播放的音频或视频文件。,该属性可以设置为应用中的某一文件的URI或网络上的文件的URI。当然我们也可以使用SetSource方法将源设置为使用FileOpenPicker对象从本地系统检索的文件。

AutoPlay属性指定是否在加载MediaElement后开始播放媒体文件,默认值为true

MediaElement控件声明了MediaOpenedMediaEndedCurrentStateChangedMediaFailed事件。

最后设置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代码可如下:

<StackPanelOrientation="Horizontal">

[code]<ButtonName="btnPlay"Click="btnPlay_Click"
Style="{StaticResourcetransportStyle}"Content="Play"/>

<ButtonName="btnPause"Click="btnPause_Click"

Style="{StaticResourcetransportStyle}"Content="Pause"/>

<ButtonName="btnStop"Click="btnStop_Click"

Style="{StaticResourcetransportStyle}"Content="Stop"/>

<ButtonName="btnReverse"Click="btnReverse_Click"

Style="{StaticResourcetransportStyle}"Content="Rewind"/>

<ButtonName="btnForward"Click="btnForward_Click"

Style="{StaticResourcetransportStyle}"Content="Forward"/>

<ButtonName="btnMute"Click="btnMute_Click"

Style="{StaticResourcetransportStyle}"Content="Mute"/>

<ButtonName="btnFullScreenToggle"Click="btnFullScreenToggle_Click"

Style="{StaticResourcetransportStyle}"Content="Full"/>

<ComboBoxName="cbAudioTracks"

SelectionChanged="cbAudioTracks_SelectionChanged"

Width="75"/>

<ButtonName="btnVolumeUp"Click="btnVolumeUp_Click"

Style="{StaticResourcetransportStyle}"Content="-"/>

<ButtonName="btnVolumeDown"Click="btnVolumeDown_Click"

Style="{StaticResourcetransportStyle}"Content="+"/>

<TextBlockName="txtVolume"FontSize="14"

Text="{BindingVolume,ElementName=videoMediaElement}"

VerticalAlignment="Center"HorizontalAlignment="Right"/>

</StackPanel>

[/code]

相应的C#代码如下:

privatevoidbtnPlay_Click(objectsender,RoutedEventArgse)

[code]{
if(videoMediaElement.DefaultPlaybackRate!=1)

{

videoMediaElement.DefaultPlaybackRate=1.0;

}


videoMediaElement.Play();

}


privatevoidbtnPause_Click(objectsender,RoutedEventArgse)

{

videoMediaElement.Pause();

}


privatevoidbtnStop_Click(objectsender,RoutedEventArgse)

{

videoMediaElement.Stop();

}


privatevoidbtnForward_Click(objectsender,RoutedEventArgse)

{

videoMediaElement.DefaultPlaybackRate=2.0;

videoMediaElement.Play();

}


privatevoidbtnReverse_Click(objectsender,RoutedEventArgse)

{

videoMediaElement.DefaultPlaybackRate=-2;

videoMediaElement.Play();;

}


privatevoidbtnVolumeDown_Click(objectsender,RoutedEventArgse)

{

if(videoMediaElement.IsMuted)

{

videoMediaElement.IsMuted=false;

}


if(videoMediaElement.Volume<1)

{

videoMediaElement.Volume+=.1;

}

}


privatevoidbtnMute_Click(objectsender,RoutedEventArgse)

{

videoMediaElement.IsMuted=!videoMediaElement.IsMuted;

}


privatevoidbtnVolumeUp_Click(objectsender,RoutedEventArgse)

{

if(videoMediaElement.IsMuted)

{

videoMediaElement.IsMuted=false;

}


if(videoMediaElement.Volume>0)

{

videoMediaElement.Volume-=.1;

}

}

[/code]

构建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#代码可如下:


privatebool_isFullscreenToggle=false;

[code]publicboolIsFullscreen
{

get{return_isFullscreenToggle;}

set{_isFullscreenToggle=value;}

}


privateSize_previousVideoContainerSize=newSize();


privatevoidFullscreenToggle()

{

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;

}

}


privatevoidbtnFullScreenToggle_Click(objectsender,RoutedEventArgse)

{

FullscreenToggle();

}


privatevoidVideoContainer_KeyUp(objectsender,KeyRoutedEventArgse)

{

if(IsFullscreen&&e.Key==Windows.System.VirtualKey.Escape)

{

FullscreenToggle();

}


e.Handled=true;

}

[/code]

构建MediaElement的滑动进度条功能

通常我们使用Slider控件来显示或更改视频位置,大致思路为设置Slider控件并使用DispatcherTimer来保持滑块与MediaElement.Position属性的同步。

首先XAML中声明Slider控件。


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



注意:Slider控件的StepFrequency属性定义了滑块的刻度上步骤的频率。这里演示是基于MediaElement的NaturalDuration属性的值来设置StepFrequency属性。

如果我们想采用更高的精度可以使用设置为250毫秒的最低频率调节这些数字。

C#代码中声明以下代码:


privatedoubleSliderFrequency(TimeSpantimevalue)

[code]{
doublestepfrequency=-1;


doubleabsvalue=(int)Math.Round(

timevalue.TotalSeconds,MidpointRounding.AwayFromZero);


stepfrequency=(int)(Math.Round(absvalue/100));


if(timevalue.TotalMinutes>=10&&timevalue.TotalMinutes<30)

{

stepfrequency=10;

}

elseif(timevalue.TotalMinutes>=30&&timevalue.TotalMinutes<60)

{

stepfrequency=30;

}

elseif(timevalue.TotalHours>=1)

{

stepfrequency=60;

}


if(stepfrequency==0)stepfrequency+=1;


if(stepfrequency==1)

{

stepfrequency=absvalue/100;

}


returnstepfrequency;

}

[/code]
我们需要DispatcherTimer来保持Slider与媒体同步,并将DispatcherTimerInterval属性值设置为Slider的StepFrequency。对于每次计时器计时,Slider的Value属性设置为MediaElement.Position

另外我们需要在以下情况中关闭掉计时器:

1)MediaElement当前状态为暂停或停止时;

2)滑块的滑条移动时;

3)进度条不可见时,例如:全屏模式下。这里需要注意的是进度条在推出全屏模式后重新开始计时;

下面C#代码是如何创建和设置DispatcherTimer:


privateDispatcherTimer_timer;

[code]
privatevoidSetupTimer()

{

_timer=newDispatcherTimer();

_timer.Interval=TimeSpan.FromSeconds(timelineSlider.StepFrequency);

StartTimer();

}


privatevoid_timer_Tick(objectsender,objecte)

{

if(!_sliderpressed)

{

timelineSlider.Value=videoMediaElement.Position.TotalSeconds;

}

}


privatevoidStartTimer()

{

_timer.Tick+=_timer_Tick;

_timer.Start();

}


privatevoidStopTimer()

{

_timer.Stop();

_timer.Tick-=_timer_Tick;

}

[/code]

同时我们需要在页面的Loaded事件和MediaOpened事件中触发执行基本任务的处理程序。

CurrentStateChanged和MediaEnded事件触发时,进行处理MediaElement上的状态更改。

ValueChanged事件中处理Slider上的状态更改。

最后,PointerPressedEventPointerCaptureLostEvent事件处理程序处理与Slider的用户交互。

C#代码如下:


privatevoidMainPage_Loaded(objectsender,RoutedEventArgse)

[code]{
timelineSlider.ValueChanged+=timelineSlider_ValueChanged;


PointerEventHandlerpointerpressedhandler=newPointerEventHandler(slider_PointerEntered);

timelineSlider.AddHandler(Control.PointerPressedEvent,pointerpressedhandler,true);


PointerEventHandlerpointerreleasedhandler=newPointerEventHandler(slider_PointerCaptureLost);

timelineSlider.AddHandler(Control.PointerCaptureLostEvent,pointerreleasedhandler,true);

}


voidvideoElement_MediaOpened(objectsender,RoutedEventArgse)

{

doubleabsvalue=(int)Math.Round(

videoMediaElement.NaturalDuration.TimeSpan.TotalSeconds,

MidpointRounding.AwayFromZero);


timelineSlider.Maximum=absvalue;


timelineSlider.StepFrequency=

SliderFrequency(videoMediaElement.NaturalDuration.TimeSpan);


SetupTimer();


//Helpermethodtopopulatethecomboboxwithaudiotracks.

PopulateAudioTracks(videoMediaElement,cbAudioTracks);

}

[/code]

最后需要在C#代码中处理显示指针位置更改和ValueChangedCurrentStateChangedMediaEnded事件的事件处理程序。


privatebool_sliderpressed=false;

[code]
voidslider_PointerEntered(objectsender,PointerRoutedEventArgse)

{

_sliderpressed=true;

}


voidslider_PointerCaptureLost(objectsender,PointerRoutedEventArgse)

{

videoMediaElement.Position=TimeSpan.FromSeconds(timelineSlider.Value);

_sliderpressed=false;

}


voidtimelineSlider_ValueChanged(objectsender,Windows.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgse)

{

if(!_sliderpressed)

{

videoMediaElement.Position=TimeSpan.FromSeconds(e.NewValue);

}

}


voidvideoMediaElement_CurrentStateChanged(objectsender,RoutedEventArgse)

{

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;

}

}


voidvideoMediaElement_MediaEnded(objectsender,RoutedEventArgse)

{

StopTimer();

timelineSlider.Value=0.0;

}


privatevoidvideoMediaElement_MediaFailed(objectsender,ExceptionRoutedEventArgse)

{

//getHRESULTfromeventargs

stringhr=GetHresultFromErrorMessage(e);


//Handlemediafailedeventappropriately

}


privatestringGetHresultFromErrorMessage(ExceptionRoutedEventArgse)

{

Stringhr=String.Empty;

Stringtoken="HRESULT-";

constinthrLength=10;//eg"0xFFFFFFFF"


inttokenPos=e.ErrorMessage.IndexOf(token,StringComparison.Ordinal);

if(tokenPos!=-1)

{

hr=e.ErrorMessage.Substring(tokenPos+token.Length,hrLength);

}


returnhr;

}

[/code]

到此为止一个简单的媒体播放器就基本完成了!运行效果图如下:





注意:本博文介绍的媒体播放器在实际应用开发中,对于开发者是远远不够理想的,我们需要在此基础上进一步去优化。

最后引用MSDN中给到的性能注意事项

音频和视频播放是非常耗费资源的操作。有关媒体应用性能的详细信息,可参阅优化媒体资源。

1)尽可能显示全屏视频播放

XAML框架可以在只呈现视频时优化呈现的视频,这种情况类似于全屏播放的情况。此方法使用较少的电源,并且产生高于同时显示其他元素情况下的频率。若要优化媒体播放,请将MediaElement对象的大小设置为屏幕的宽度和高度,并且不显示其他XAML元素。在全屏模式下覆盖MediaElement顶部的XAML元素可能有正当理由—例如,隐藏的字幕或临时传输控件—但在不需要时可以隐藏这些元素。

2)延迟设置MediaElement源

XAML框架尽可能长地延迟加载DLL和创建大型对象。MediaElement在源由Source属性或SetSource方法初始设置时完成此操作。仅在用户准备好播放媒体后设置源,可减少与MediaElement关联的绝大部分性能成本。

3)将视频分辨率与设备分辨率匹配

解码视频主要使用内存和GPU,因此应选择与要在其上显示的设备的分辨率接近的视频格式。例如,解码高清(1080)视频,然后将其缩小至相当小的尺寸进行显示会占有不必要的资源。许多应用不会将相同的视频解码为不同的分辨率,但如果可用,请使用接近显示设备的分辨率的解码。

4)请勿动态显示MediaElement对象。

动画和媒体播放都会耗费大量系统资源。在合适的上下文中使用动画可以创建完美动人的效果。但是,出于性能方面考虑,请避免在使用C++、C#或VisualBasic的Metro风格应用中动态显示MediaElement对象。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: