与众不同 windows phone (15) - Media(媒体)之后台播放音频
与众不同 windows phone (15) - Media(媒体)之后台播放音频
作者:webabcd
介绍
与众不同 windows phone 7.5 (sdk 7.1) 之媒体
- 通过 AudioPlayerAgent 实现在后台播放音频
示例
演示如何通过后台代理的方式来实现音频在后台的播放
1、后台代理
MyAudioPlayerAgent/AudioPlayer.cs
/* * 本例演示如何播放后台音频(以 AudioPlayerAgent 为例,另 AudioStreamingAgent 用于流式播放音频) * 建议使用 AudioPlaybackAgent 类型的模板创建此项目 * * BackgroundAgent - 后台代理类,抽象类,它是 ScheduledTaskAgent、AudioPlayerAgent 和 AudioStreamingAgent 的基类 * NotifyComplete() - 用于通知系统,代理已经完成了当前的任务,调用此方法后,系统才会去准备执行下一次任务 * Abort() - 用于通知系统,放弃此次和以后的任务 * OnCancel() - 后台代理被取消时所执行的方法(由系统调用,比如后台代理转到休眠状态或终止状态前会调用此方法) * * AudioPlayerAgent - 后台播放音频的代理类 * OnError() - 当播放中出现错误时,系统会调用此方法 * OnPlayStateChanged() - 当播放状态发生改变时,系统会调用此方法(错误状态除外) * OnUserAction() - 当程序改变播放行为时,系统会调用此方法 * * PlayState - Microsoft.Phone.BackgroundAudio.PlayState 枚举 * Unknown, Stopped, Paused, Playing, BufferingStarted, BufferingStopped, TrackReady, TrackEnded, Rewinding, FastForwarding, Shutdown, Error * * UserAction - Microsoft.Phone.BackgroundAudio.UserAction 枚举 * Stop, Pause, Play, SkipNext, SkipPrevious, FastForward, Rewind, Seek * * AudioTrack - 音频对象 * Source - 音频的地址,远程地址或独立存储地址均可 * Title - 音频名称 * Duration - 音频时长 * Album - 专辑名称 * AlbumArt - 专辑封面的 Uri 地址,远程地址或独立存储地址均可,如果使用独立存储地址则其目录必须是 Shared/Media * Artist - 艺术家 * PlayerControls - 锁屏等状态下的播放器控件显示(Microsoft.Phone.BackgroundAudio.EnabledPlayerControls 枚举) * None = 0, SkipNext = 1, SkipPrevious = 2, FastForward = 4, Rewind = 8, Pause = 16, All = 31 * Tag - 任意内容,即为 AudioTrack 绑定一个上下文数据 * BeginEdit() - 将 AudioTrack 设置为编辑状态(如果需要修改 AudioTrack 的属性则需要将 AudioTrack 设置为编辑状态) * EndEdit() - 结束 AudioTrack 的编辑状态 */ using System; using System.Windows; using Microsoft.Phone.BackgroundAudio; using System.Collections.Generic; namespace MyAudioPlayerAgent { public class AudioPlayer : AudioPlayerAgent { // 播放列表 private static List<AudioTrack> _playList = new List<AudioTrack> { new AudioTrack(new Uri("SuperMario.mp3", UriKind.Relative), "title", "artist", "album", null, null, EnabledPlayerControls.Pause), new AudioTrack(new Uri("http://traffic.libsyn.com/wpradio/WPRadio_29.mp3", UriKind.Absolute), "标题", "艺术家", "专辑", null, null, EnabledPlayerControls.All) }; // 当前播放的 Track 在整个播放列表中的位置 private static int _currentTrackNumber = 0; /* * _classInitialized - 用于标记 AudioPlayer 是否已经被初始化 * 标记成 volatile 是为了避免编译器认为此字段无外部修改,而将其优化放入寄存器(标记成 volatile 的字段只会放在内存中) * 一般来说,多任务环境下各任务间共享的字段应该被标记为 volatile */ private static volatile bool _classInitialized; public AudioPlayer() { if (!_classInitialized) { _classInitialized = true; Deployment.Current.Dispatcher.BeginInvoke(delegate { Application.Current.UnhandledException += AudioPlayer_UnhandledException; }); } } private void AudioPlayer_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e) { if (System.Diagnostics.Debugger.IsAttached) { System.Diagnostics.Debugger.Break(); } } // 播放状态发生改变时所调用的方法 protected override void OnPlayStateChanged(BackgroundAudioPlayer player, AudioTrack track, PlayState playState) { switch (playState) { case PlayState.TrackReady: player.Play(); break; case PlayState.TrackEnded: PlayNextTrack(player); break; case PlayState.Playing: break; case PlayState.Paused: break; case PlayState.Stopped: break; case PlayState.BufferingStarted: break; case PlayState.BufferingStopped: break; case PlayState.Rewinding: break; case PlayState.FastForwarding: break; case PlayState.Shutdown: break; case PlayState.Unknown: break; } NotifyComplete(); } // 当程序改变播放行为时所调用的方法 protected override void OnUserAction(BackgroundAudioPlayer player, AudioTrack track, UserAction action, object param) { switch (action) { case UserAction.Play: PlayTrack(player); break; case UserAction.SkipPrevious: PlayPreviousTrack(player); break; case UserAction.SkipNext: PlayNextTrack(player); break; case UserAction.Rewind: player.Rewind(); break; case UserAction.FastForward: player.FastForward(); break; case UserAction.Seek: player.Position = (TimeSpan)param; break; case UserAction.Pause: player.Pause(); break; case UserAction.Stop: player.Stop(); break; } NotifyComplete(); } // 播放下一个 Track private void PlayNextTrack(BackgroundAudioPlayer player) { if (++_currentTrackNumber >= _playList.Count) _currentTrackNumber = 0; PlayTrack(player); } // 播放上一个 Track private void PlayPreviousTrack(BackgroundAudioPlayer player) { if (--_currentTrackNumber < 0) _currentTrackNumber = _playList.Count - 1; PlayTrack(player); } // 播放当前 Track private void PlayTrack(BackgroundAudioPlayer player) { player.Track = _playList[_currentTrackNumber]; } // 出现异常时所调用的方法 protected override void OnError(BackgroundAudioPlayer player, AudioTrack track, Exception error, bool isFatal) { if (isFatal) Abort(); else NotifyComplete(); } // 当后台代理转到休眠状态或终止状态前,系统会自动调用此方法,需要在 5 秒内执行完毕 protected override void OnCancel() { } } } /* * 主程序引用此项目后,会在 manifest 中添加如下信息: * <ExtendedTask Name="BackgroundTask"> * <BackgroundServiceAgent Specifier="AudioPlayerAgent" Name="MyAudioPlayerAgent" Source="MyAudioPlayerAgent" Type="MyAudioPlayerAgent.AudioPlayer" /> * </ExtendedTask> */
2、前台调用后台代理播放音频
BackgroundAudio.xaml
<phone:PhoneApplicationPage x:Class="Demo.Media.BackgroundAudio" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" SupportedOrientations="Portrait" Orientation="Portrait" mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480" shell:SystemTray.IsVisible="True"> <Grid x:Name="LayoutRoot" Background="Transparent"> <StackPanel Orientation="Vertical"> <Button x:Name="btnPrev" Content="上一首" Click="btnPrev_Click" /> <Button x:Name="btnRewind" Content="快退" Click="btnRewind_Click" /> <Button x:Name="btnPlay" Content="播放" Click="btnPlay_Click" /> <Button x:Name="btnFastForward" Content="快进" Click="btnFastForward_Click" /> <Button x:Name="btnNext" Content="下一首" Click="btnNext_Click" /> <TextBlock x:Name="lblCurrentTrack" /> <TextBlock x:Name="lblPosition" /> </StackPanel> </Grid> </phone:PhoneApplicationPage>
BackgroundAudio.xaml.cs
/* * 本例演示如何播放后台音频 * * BackgroundAudioPlayer - 提供调用后台播放音频的相关功能 * Instance - 返回 BackgroundAudioPlayer 实例 * Track - 当前的 AudioTrack 对象。说明参见:MyAudioPlayerAgent/AudioPlayer.cs * PlayerState - 播放器状态(Microsoft.Phone.BackgroundAudio.PlayState 枚举)。说明参见:MyAudioPlayerAgent/AudioPlayer.cs * Position - 当前 AudioTrack 的播放位置 * CanPause - 是否可暂停 * CanSeek - 是否可以设置 Position 属性 * BufferingProgress - 缓冲百分比(0 - 1之间) * Volume - 音量(0 - 1之间,默认值为 0.85)。注:目前 wp 没有提供 uvc(Universal Volume Control) 的接口 * * Play - 播放当前 AudioTrack 的当前位置 * Pause - 暂停 * Stop - 停止 * Rewind - 快退 * FastForward - 快进 * SkipPrevious - 跳至上一个音频 * SkipNext - 跳至下一个音频 * Close - 关闭并释放所有资源 * * PlayStateChanged - PlayState 发生改变时所触发的事件 */ using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using Microsoft.Phone.Controls; using Microsoft.Phone.BackgroundAudio; using System.Windows.Navigation; using System.IO.IsolatedStorage; using System.Windows.Resources; namespace Demo.Media { public partial class BackgroundAudio : PhoneApplicationPage { public BackgroundAudio() { InitializeComponent(); Init(); } protected override void OnNavigatedTo(NavigationEventArgs e) { ProcessPlayState(); } private void Init() { // 由于播放本地音频时只能从独立存储中播放,所以此处把示例用音频文件从程序包中复制到独立存储 using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication()) { if (!storage.FileExists("SuperMario.mp3")) { StreamResourceInfo resource = Application.GetResourceStream(new Uri("Assets/SuperMario.mp3", UriKind.Relative)); using (IsolatedStorageFileStream file = storage.CreateFile("SuperMario.mp3")) { int chunkSize = 4096; byte[] bytes = new byte[chunkSize]; int byteCount; while ((byteCount = resource.Stream.Read(bytes, 0, chunkSize)) > 0) { file.Write(bytes, 0, byteCount); } } } } CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering); BackgroundAudioPlayer.Instance.PlayStateChanged += new EventHandler(Instance_PlayStateChanged); } // 实时更新当前音频播放的位置 void CompositionTarget_Rendering(object sender, EventArgs e) { if (BackgroundAudioPlayer.Instance != null) lblPosition.Text = BackgroundAudioPlayer.Instance.Position.TotalSeconds.ToString(); } void Instance_PlayStateChanged(object sender, EventArgs e) { ProcessPlayState(); } void ProcessPlayState() { // 指定播放按钮是显示“播放”还是“暂停” switch (BackgroundAudioPlayer.Instance.PlayerState) { case PlayState.Playing: btnPlay.Content = "暂停"; break; case PlayState.Paused: case PlayState.Stopped: btnPlay.Content = "播放"; break; } // 显示音频的 Title 和 Artist if (BackgroundAudioPlayer.Instance.Track != null) lblCurrentTrack.Text = BackgroundAudioPlayer.Instance.Track.Title + " by " + BackgroundAudioPlayer.Instance.Track.Artist; } // 播放或暂停音频 private void btnPlay_Click(object sender, RoutedEventArgs e) { if (PlayState.Playing == BackgroundAudioPlayer.Instance.PlayerState) { BackgroundAudioPlayer.Instance.Pause(); } else { BackgroundAudioPlayer.Instance.Play(); } } // 跳至下一首音频 private void btnNext_Click(object sender, RoutedEventArgs e) { BackgroundAudioPlayer.Instance.SkipNext(); } // 跳至上一首音频 private void btnPrev_Click(object sender, RoutedEventArgs e) { BackgroundAudioPlayer.Instance.SkipPrevious(); } // 快退 private void btnRewind_Click(object sender, RoutedEventArgs e) { if (PlayState.Playing == BackgroundAudioPlayer.Instance.PlayerState) BackgroundAudioPlayer.Instance.Rewind(); } // 快进 private void btnFastForward_Click(object sender, RoutedEventArgs e) { if (PlayState.Playing == BackgroundAudioPlayer.Instance.PlayerState) BackgroundAudioPlayer.Instance.FastForward(); } } }
OK
[源码下载]