WP7 - Background Audio Playlist Application

Windows Phone 7 – Background Audio Playerlist Application

在Windows Phone 7.0的時候,如果有下載過KKBOX的用戶一定覺得…為什麼明明在執行KKBOX,只要按下

Start鍵或Screen Locked之後,音樂就沒了就要重新在開啟程式,為了讓音樂可以正常的播放,有時也要開成

不自動關閉螢幕,非常的不方便。到了WP7.1之後,在Mango的500個API中針對這個問題提供了解決的方案,

也是此篇要學習的主要內容。不多說,往下來看組合背景音樂程式必要的元素與架構:

 

〉Background Audio Architecture

在整個Background運作架構裡,WP7提供了一個Background Agent的概念,提供開發人員可以用簡易方式來控制背景任務,

完成背景播放音樂、下載、設定提醒等API提供的背景功能。然而,在Windows Phone 7播放音樂/影片均需一個多媒體模組:

Zune Media Queue來完成播放(例如,開啟Zune來開啟音樂、Radio、Video等),因此,在Background Audio Application裡面

將需要向Zune Media Queue下達操作的指令進一步控制需要播送的多媒體檔案或串流。

 

要實作一個Background Audio Application來與Zune Media互動之前有幾個最重要的控制元件需要先了解一下,如下:

a. BackgroundAudioPlayer

    主要透過該類別BackgroundAudioPlayer.Instance實例化之後,與Zune Media Queue進行互動。因此,可以透過

    BackgroundAudioPlayer.Instance取得目前Zune播放的多媒體資訊、播放狀態等。另外要注意的是:

    當使用該類別後會影響目前手機正在播放的程式。例如:如果正開啟Zune在播放音樂,再開啟自己的App將會關掉Zune的程式。

 

b. Universal Volume Control (UVC)

    它是個多媒體控制項目的集合,當在螢幕鎖定(Screen Lock)時,用戶操作硬體音量鍵盤時或播放目前歌曲資訊時,

    類別出現Zune原版的控制面板(好處是Windows Phone 7的開發者不用自己實作這一塊),如下圖:

    image

 

c. AudioPlayerAgent

    它為控制實際播放的元件,接受與UVC事件所傳達的任務(如:播放下一首、暫停等),它與BackgroundAudioPlayer.Instance不一樣,

    BackgroundAudioPlayer.Instace是用於與Zune Media Queue互動,真正負責播放Background Audio Application裡的多媒體與接受用

    戶操作需求的是由該類別來負責。除此之外該類別也支援實際播放清單(Playlist logic)。

 

f. AudioTrack

    該類別用於呈現目前播放音軌的metedata,包括:標題、作者、URI…等資料。如果該Track的URI被設定為null時,系統會自動假設

    您指定的音軌為MediaStreamSource,在這個情況下可以透過Tag屬性來從AudioPlayerAgent傳遞資料給AudioStreamingAgent

 

g. AudioStreamer

    該物件被實作是經由BackgroundAudioPlayer呼叫OnBeginStreaming(AudioTrack, AudioStreamer)所產生。

 

有了上述元件組合一個Background Audio Application的概念後,接下來針對Background Audio Application的類型,

可以針對播放的資料來源類型分成二種:Background Audio Playlist Application與Background Audio Streaming Application

本篇針對Background Audio Playlist Application進行說明。另一個類型可到<>進行閱讀。

 

(1) Background Audio Playlist Application

     該類型的Application主要實作一個播放清單(Playlist Logic)的應用,播放清單中記錄每一個播放媒體的URI,藉由指定每一個URI載入

要播放的多媒體至Zune Media Queue之中,並且讓用戶操作UVC播放自己指定的音樂清單。然而,該URI可以是Local手機檔案或是遠端

的檔案內容,但在選擇Audio檔案時,需要注意選擇是Zune可以播放的格式內容,詳細內容可參考<Supported Media Codecs for Windows Phone>。

 

至於要實作Background audio playlist application的話,需要實作二個重點要元素,透過MSDN所繪製的下圖加以說明:

    Background audio playlist app architecture

透過上圖的定義,可以看出一個Background Audio Playlist Application的運作方式:

(1) 透過實作Application UI來操作Background Audio Player的Instance進行播放清單、多媒體播放進度的控制或存取多媒體資訊;

(2) Background Audio Player收到指令後,向Zune Media Queue下達對應的操作事件讓Audio進行播放等動作;

(3) 實作AudioPlayerAgent是讓Application啟動播放後,向Windows Phone 7註冊一個Background Agent,讓Agent

     可以在背景接收Application UI向BackgroundAudioPlayer下達的指定,完成播放的動作。

 

往下細部說明:

a. 綠色的部分是需要實作的Code

    a-1. Application控制播放的UI(Application UI)

           該Application UI如果是使用Visual Studio建立的Template那MainPage.xaml即是Application UI,在MainPAge.xaml.cs則需要

           實作BackgroundAudioPlayer.Instance去控制Zune Media Queue,包括初始化、播放、暫停、取得目前播放資訊等功能。

    a-2. AudioPlayerAgent真正控制的類別

           該Application使用的AudioPlayerAgent會經由系統自動實作一個Agent配置到您的Application,該Agent負責處理所以User

           在Application UI透過BackgroundAudioPlayer.Instace所下達的任何動作,甚至接收UVC的操作來影響Zune Media Queue的播放。

 

b. 白色的部分是系統自己實作的部分,但透過實作的部分即可以間接操作播放的程序

  

c. AudioPlayerAgent需要實作的重要事件處理

    c-1. OnUserAction(BackgroundAudioPlayer, AudioTrack, UserAction, Object)

           當用戶透過Application UI或UVC操作目前播放的Agent時,即會觸發該事件,並且透過識別UserAction(如下表)的類型,

           完成控制與處理目前要求的任務。

           識別目前接收到用戶下達的動作類型;主要以列舉值呈現,如下:

  類型 說明
  Stop 停止曲目。
  Pause 暫停曲目。
  Play 播放或繼續曲目。
  SikpNext 跳過下一個曲目。
  SkipPrevious 跳過上一個曲目。
  FastFoward 快進目前播放的曲目。
  Rewind 倒帶目前播放的曲目。
  Seek 在目前曲目中移動播放進度至指定的位置。

 

 

    c-2. OnPlayStateChanged(BackgroundAudioPlayer,AudioTrack, PlayState)

           當播放的曲目狀態改變時,即會觸發該事件。

  類型 說明
  Unknown 目前的狀態是無法定義的。例如:當新的AudioTrack有設定,但playback未被初始化。
  Stopped 目前應用程式沒有播放任何曲目。
  Paused 應用程式暫停播放曲目。
  Playing 應用程式目前正在播放曲目。
  BuffingStarted 現在曲目正在啟動緩衝。
  BufferingStoped 現在曲目正在停止緩衝。
  TrackReady 現在曲目準備好可以進行播放。
  TrackEnded 最後一個曲目結束與應用程式將被觸發轉換曲目。
  Rewinding 目前的曲目被要求倒帶。
  FastFowarding 目前的曲目被要求快進播放。
  Shutdown 應用程式被關閉。
  Error 播放時發生錯誤。

 

    c-3. OnError(BackgroundAudioPlayer, AudioTrack, Exception, Boolean)

           當播放曲目發生錯誤時,例如:下載曲目發生錯誤、播放音檔有問題…等問題都會觸發該事件,此時,可以透過參數中的

           BackgroundAudioPlayer參數進行接下來要播放的控制,並且透過Exception識別錯誤的原因。

           由於該事件被呼叫預設會關閉Agent的運作,因此,需要特別對它進行處理,以免讓自己的程式整個被關掉

 

    c-4. NotifyComplete

           當Agent已經完成了所有的任務透過呼叫該方式,通知系統該Agent完成任務可以回到當前執行的系統流程。如果Agent執行完,

           沒有呼叫該方法會怎樣呢?那系統分配給Agent執行用到的資源就不會回收,將會造成多個thread(包括背景)都在爭資源最後自己

           的Application可能就會被自動關閉或釋放。所以要記得加。

 

[範例說明]

了解了Background Audio Playlist Application的概念之後,往下便參考<How to: Play Background Audio for Windows Phone>中的介紹,

我針對多媒體資源的部分加以處理。如下:

 

a. 建立二個專案:Application UI與AudioPlayerAgent

    擷取

    a-1. 記得在主專案中加入Agent的專案參考,例如:BackMedia要加入ApAgent;

 

    a-2. 在BackMedia的WMAppManifest.xml中加入指定參考的<Task />與指定的類型

<Tasks>
  <DefaultTask Name="_default" NavigationPage="MainPage.xaml" />
  <!-- 加入指定的ExtenedTask 為BackgroundTask,
                並且specifier = AudioPlayAgent -->
  <ExtendedTask Name="BackgroundTask">
    <BackgroundServiceAgent Specifier="AudioPlayerAgent" Name="ApAgent" 
                          Source="ApAgent" Type="ApAgent.AudioPlayer" />
  </ExtendedTask>
</Tasks>

 

b. 先實作Application UI需要操作,包括:播放、暫停、上/下一首曲目、擷取目前曲目資訊

    [注意]

    在開發之前,記得在MainPage.xaml.cs檔中引入「using Microsoft.Phone.BackgroundAudio;」這樣才能取得Background Audio Agent的實例。

    b-1. 註冊三個按鈕項目:播放、上一首、下一首的處理事件;

   1: //處理按下Applicaiton UI中上一首曲目的事件
   2: private void btnPrevTrack_Click(object sender, RoutedEventArgs e)
   3: {
   4:     BackgroundAudioPlayer.Instance.SkipPrevious();
   5: }
   6:  
   7: //處理按下Applicaiton UI中播放/暫停的按鈕的事件
   8: private void btnPlayTrack_Click(object sender, RoutedEventArgs e)
   9: {
  10:     //PlayState需先引用using Microsoft.Phone.BackgroundAudio;才能取得到
  11:          //識別目前的播放狀態為Playing,則代表要暫停播放
  12:     if (PlayState.Playing == BackgroundAudioPlayer.Instance.PlayerState)
  13:     {
  14:         BackgroundAudioPlayer.Instance.Pause();
  15:     }
  16:     else
  17:     {
  18:         //識別目前的播放狀態非Playing,則代表要播放
  19:         BackgroundAudioPlayer.Instance.Play();
  20:     }
  21: }
  22:  
  23: //處理按下Applicaiton UI中下一首的按鈕的事件
  24: private void btnNextTrack_Click(object sender, RoutedEventArgs e)
  25: {
  26:     BackgroundAudioPlayer.Instance.SkipNext();
  27: }

    b-2. 註冊處理BackgroundAudioPlayer.Instance.PlayStateChanged的事件;

           透過處理PlayStateChanged的事件來監控目前播放的狀況,並且配合識別目前播放的Track是否為空,來取得播放曲目資訊。

   1: public MainPage()
   2: {
   3:     InitializeComponent();
   4:     //在畫面初始化時,就註冊PlayStateChanged事件
   5:     BackgroundAudioPlayer.Instance.PlayStateChanged += new EventHandler(Instance_PlayStateChanged);
   6: }
   7:  
   8: /// <summary>
   9: /// 處理BackgroundAudioPlayer.Instance.PlayerState狀態改變的事件。
  10: /// </summary>
  11: protected void Instance_PlayStateChanged(object sender, EventArgs e)
  12: {
  13:     switch (BackgroundAudioPlayer.Instance.PlayerState)
  14:     {
  15:         case PlayState.Playing:
  16:             btnPlayTrack.Content = "暫停";
  17:             break;
  18:         //Paused與Stopped都識別接下來是要按播放才會繼續
  19:         case PlayState.Paused:
  20:         case PlayState.Stopped:
  21:             playButton.Content = "播放";
  22:             break;
  23:     }
  24:     
  25:     //識別如果目前播放的Track不為null,則可以取得目前播放的資訊
  26:     if (BackgroundAudioPlayer.Instance.Track != null)
  27:     {
  28:         txtTrackInfo.Text = string.Format("Title: {0}, Artist: {1}",
  29:                                 BackgroundAudioPlayer.Instance.Track.Title,
  30:                                 BackgroundAudioPlayer.Instance.Track.Artist);
  31:     }
  32: }

 

c. 實作AudioPlayerAgent

    當透過Visual Studio的Template建立AudioPlayerAgent專案時,它會自動建立一份繼承AudioPlayerAgent的.cs檔,其中也註冊了一些事件:

    c-1. 事件介紹:

           c-1-1. Application.Current.UnhandledException (object, ApplicationUnhandledExceptionEventArgs)

                     註冊該事件處理當BackgroundAudioAgent執行時遇到不可預期的Exception時,可以透過該事件關掉Agent。

           c-1-2. OnPlayStateChanged(BackgroundAudioPlayer, AudioTrack, PlayState)

                     註冊該事件處理當播放狀態改變時需要處理的事件。它可以透過參數的BackgroundAudioPlayer取得目前正在操作的Instace、

                     正在播放的Track與指定的PlayState狀態,接著依照指定的PlayState處理對應的工作,最後呼叫「NotifyComplete()」結束任務。

           c-1-3. OnUserAction(BackgroundAudioPlayer, AudioTrack, UserAction, object)

                     註冊該事件處理使用者操作BackgroundAudioPlayer.Instance或是UVC的動作。該事件的重點在於:UserAction的參數,

                     透過該參數識別要對播放的Instance進行何種動作,最後記得呼叫「NotifyComplete()」結束這個任務。

           c-1-4. OnError(BackgroundAudioPlayer, AudioTrack, Exception, bool):

                     當BackgroundAudioPlayer.Instace執行時遇到Exception即會進入該事件,最常發生的情境在於指定的AudioTrack無法找到URI

                     或是下載失敗等問題。

           c-1-5. OnCancel()

                     該事件發生於當系統通知Agent進行取消任務時,Agent需要釋放所有的資源等任務,最後在配合NotifyCompleteAbort告訴系統

                     已完成任務。該事件常用於當取消Agent使用時,例如:ScheduleTaskAgent取消或不需要在播放背景音樂時。

 

    c-2. 實作要播放的AudioTrack清單:

   1: private static List<AudioTrack> gPlayList = new List<AudioTrack>()
   2: {
   3:     //此處的範例是使用MMS為例
   4:     new AudioTrack(new Uri("mms://bcr.media.hinet.net/RA000040", UriKind.Absolute),"大眾廣播","KissRadio","",null),
   5:     new AudioTrack(new Uri("mms://bcr.media.hinet.net/RA000036", UriKind.Absolute), "HitFM聯播網", "HitFM","北部",null)
   6: };

    c-3. 實作在OnPlayStateChanged事件觸發時,針對PlayState.TrackReady狀態進行播放的動作:

   1: protected override void OnPlayStateChanged(BackgroundAudioPlayer player, AudioTrack track, PlayState playState)
   2: {
   3:     switch (playState)
   4:     {
   5:         case PlayState.TrackEnded:
   6:             break;
   7:         case PlayState.TrackReady:
   8:             //針對Track連線成功後,進行播放動作
   9:             player.Play();
  10:             break;
  11:         case PlayState.Shutdown:
  12:             // TODO: Handle the shutdown state here (e.g. save state)
  13:             break;
  14:         case PlayState.Unknown:
  15:             break;
  16:         case PlayState.Stopped:
  17:             break;
  18:         case PlayState.Paused:
  19:             break;
  20:         case PlayState.Playing:
  21:             break;
  22:         case PlayState.BufferingStarted:
  23:             break;
  24:         case PlayState.BufferingStopped:
  25:             break;
  26:         case PlayState.Rewinding:
  27:             break;
  28:         case PlayState.FastForwarding:
  29:             break;
  30:     }
  31:  
  32:     NotifyComplete();
  33: }

    c-4. 實作在OnUserAction事件觸發時,針對Play、Pause、SkipNext與SkipPrevious進行處理:

   1: //定義一個static的變數記錄目前播放的Index
   2: private static int gCurrentTrack = 0;
   3:  
   4: protected override void OnUserAction(BackgroundAudioPlayer player, AudioTrack track, UserAction action, object param)
   5: {
   6:     switch (action)
   7:     {
   8:         case UserAction.Play:
   9:             if (player.PlayerState != PlayState.Playing)
  10:             {
  11:                 player.Track = gPlayList[gCurrentTrack];
  12:                 player.Play();
  13:             }
  14:             break;
  15:         case UserAction.Stop:
  16:             player.Stop();
  17:             break;
  18:         case UserAction.Pause:
  19:             player.Pause();
  20:             break;
  21:         case UserAction.FastForward:
  22:             player.FastForward();
  23:             break;
  24:         case UserAction.Rewind:
  25:             player.Rewind();
  26:             break;
  27:         case UserAction.Seek:
  28:             player.Position = (TimeSpan)param;
  29:             break;
  30:         case UserAction.SkipNext:
  31:             if (++gCurrentTrack >= gPlayList.Count)
  32:                 gCurrentTrack = 0;
  33:             player.Track = gPlayList[gCurrentTrack];
  34:             break;
  35:         case UserAction.SkipPrevious:
  36:             if (--gCurrentTrack < 0)
  37:             {
  38:                 gCurrentTrack = gPlayList.Count - 1;
  39:             }
  40:             player.Track = gPlayList[gCurrentTrack];                    
  41:             break;
  42:     }
  43:  
  44:     NotifyComplete();
  45: }

 

d. 實作結果

擷取0 1

 

[範例程式]

 

上述實作Application UI的部分可以看出只需要實作操作BackgroundAudioPlayer.Instance與PlayerState改變對應的事件,

就可以完成透過UI來控制背景播放的功能。至於UVC的部分呢,不用擔心,它會自動由系統產生並擷取Track的資訊。另外,

在實作AudioPlayerAgent時就可以清楚分出,UI就像我們的看電視的搖控器,透過搖控器告訴電視要播放什麼節目台,接著

AudioPlayerAgent就相似電視機幫我們轉到指定的電視進行實際的播放。

 

[注意事項]

a. Application UI與Background Agent二個是屬於不同的程式,因此,IsolatedStorageSettings是不能共用的

b. Application UI與Background Agent需要透過IsolatedStorage中的資源來進行參數傳遞

c. 控制放清單時,UVC的操作會直接影響BackgroundAudioPlayer,

    如果Application UI在前景,將會觸發PlayStateChanged事件;如果在背景執行會直接先觸發OnUserAction事件,

    因此,Application UI與Background Agent都會影響播放索引值,要特別注意管理它的變化才會二邊都一致。

d. 撰寫播放音樂/影片的App時,需要在播放時加上判斷目前是否有其他程式正在播放音樂,如下程式碼:

   1: using Microsoft.Xna.Framework;
   2:  
   3: //確認是否目前有多媒體正在播放
   4: public static void CheckAvailablePlay()
   5: {
   6:      //using Microsoft.Xna.Framework.Media.MediaPlayer進行識別
   7:      if (MediaPlayer.State == MediaState.Playing)
   8:     {
   9:         MessageBoxResult tChoice = MessageBox.Show("目前有播放的程式,是否取代?",
  10:                                     "Information", MessageBoxButton.OKCancel);
  11:         if (tChoice == MessageBoxResult.OK)
  12:         {
  13:             //記得加上Update的方法,可以通知Zune要進行狀態更新
  14:             FrameworkDispatcher.Update();
  15:             MediaPlayer.Stop();
  16:             BackgroundAudioPlayer.Instance.Play();
  17:         }
  18:     }
  19: }

f. 使用Background Agent由於與Application是同的控制單位,所以沒辦法用MessageBox,如果需要彈出訊息,

    請改用ShellToast

 

======

以上的內容從< Background Audio Overview for Windows Phone >取得較多,並加上一些開發時遇到的觀念問題。

WP 7.1 SDK裡針對Background Agent的部分提供了許多好用的Agent,讓開發者可以專注於開發Agent對應Service

要完成的任務,放較少心思去管理執行緒之間的關係,這也是開發其他App中我覺得相對而言比較容易的部分,但是

不代表執行緒的管理就不重要,因為一個App用的Background Agent是有限,主要是避免同一個App用了過多的Agent,

造成效能不佳或管理上的問題。接下來還會有幾篇針對Background Agent的說明與筆記。謝謝。

 

References:

Multitasking for Windows Phone (原理,必讀)

Background Audio Overview for Windows Phone (重要)

How to: Play Background Audio for Windows Phone (必讀)

WP7 Mango: How to handle skip next/previous from UVC outside of the Audio Playback Agent class library?

Scheduled Actions Overview for Windows Phone

Microsoft.Phone.Scheduler Namespace

Windows Phone Mango–What’s New? (“Background Agents” - Part 5 of 8) (必讀)

How to: Create Reminders for Windows Phone

Scheduled Tasks Overview for Windows Phone

Windows Phone 7.1 Overview – App Connect (必讀)

Windows Phone 7.1 Overview (2) – Background Agents (必讀)

Background agents will be limited to 10% CPU and 5 MB memory (重點要注意的部分)

CaptureSource.CaptureImageAsync Method & VideoBrush.SetSource Method

Supported Media Codecs for Windows Phone

PlayState Enumeration  & AudioPlayerAgent.OnUserAction Method

 

Dotblogs Tags: