與眾不同 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
[源碼下載]
