WP8 - 操作Microsoft.Xna.Framework.Media

Windows Phone 8–操作Microsoft.Xna.Framework.Media

最近比較有在撰寫處理設備裡圖檔與音樂檔案的功能,除了控制自己App中的檔案,有時候也需要存取共用的區域。

剛好看到<Data for Windows Phone>的一個重點,如下圖:

image

得知MediaLibrary API在Windows Phone 8支援了很多功能,因此,該篇將針對Microsoft.Xna.Framework.Media中重的類別加以說明。

 

Microsoft.Xna.Framework.Media

    該命名空間定義了許多相關於play、view songs、albums、playlist與pictures或是enumerate等。簡單列出如下:

‧類別

名稱 說明
Album 提供media library中一個相關的album。
AlbumCollection 提供meida library中album的集合。
Artist 提供media library指定artist的information。
ArtistCollection 提供media library中所有artists的information。
Genre 提供media library中的genre information(曲風資訊)。
GenreCollection 提供media library中的所有genres information。
MediaLibrary 提供連結media library中的songs、playlists與pictures。
MeidaPlayer 提供方法與屬性去控制play、pause、resume與stop songs。MediaPlayer也支持隨機(shuffle),重複(repeat),音量(volume),播放位置(play position),和可視化功能(visualization)。
MediaQueue 提供方法與屬性連結與控件播放音樂中的queue。
MediaSource 提供方法與屬性取得media將讀取的source或sources資訊。
Picture 提供連結在media library中的圖像。
PictureAlbum 提供連結在media library中的圖像Album。
PictureAlbumCollection 提供連結在media library中的圖像Album集合。
PictureCollection 提供連結在media library中的圖像集合。
Playlist 提供連結在media library中的playlist。
PlaylistCollection 提供連結在media library中的playlist集合。
Song 提供連結在song library中的song。
SongCollection 提供連結在song library中的song集合。
Video 代表一個Video。
VideoPlayer 提供方法與屬性去控制play、pause、resume與stop videos。VideoPlayer支持重複(repeat),音量(volume),播放位置(play position)。
VisualizationData Encapsulates visualization (frequency and sample) data for the currently-playing song.

 

‧列舉

名稱 類別
MediaSourceType Type of the media source.
MediaState Media playback state (playing, paused, or stopped).
VideoSoundtrackType Type of sounds in a video

 

以上是擷錄MSDN說明Microsoft.Xna.Framework.Media中重要的類別,其中有關MediaPlayer與相關Songs的操作,如果您有閱讀過

處理Background Audio PlayList的文件相信不陌生,可參考<Windows Phone 7 – Background Audio Playlist Application>,因為裡面用

到了Song、Playlist與MediaPlayer的類別搭配播放音樂。

 

大致上了解有多少個類別後,往下針對幾個比較特別類別加以說明:

 

MediaLibrary

    提供連結media library中的songs、playlists與pictures。也代表透過該類別可以取得device中所有多媒體資訊,該類別每一個屬性

回傳的為media collection,所以可以針對所需類型加入至應用程式中畫面的控件項目做Binding。

但需要特別注意,media collection無法直接檢索或實例化來用,需要將指定的項目從collection找出單一物件,例如:song或artist物件。

 

其用法非常容易,以下便舉例處理pictures、songs的範例:

 

(1) 取得所有media library中的圖像,並且顯示於List中

     1-1. 在xaml中加一個ListBox,設定ListBox.ItemTemplate顯示預覽的圖像;

<phone:PhoneApplicationPage.Resources>
    <MyApp:PreviewPictureConverter x:Key="PreviewPictureConverter" />
</phone:PhoneApplicationPage.Resources>

 
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <ListBox x:Name="lstPictures" Width="430" Height="710">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <!-- 使用自行開發的Convert來Binding Source -->
                <Image Source="{Binding Converter={StaticResource PreviewPictureConverter}}"
                       HorizontalAlignment="Center" VerticalAlignment="Center" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>

           上述的xaml寫法我相信大家不陌生,另外,也可以參考<Windows phone: listbox with images out-of-memory>的說明;

 

     1-2. 由於透過MediaLibrary取得PictureCollection,每一個Picture並無法直接套入Image控件,所以加個Convert;

public class PreviewPictureConverter:System.Windows.Data.IValueConverter
{
    public object Convert(object value, Type targetType, 
            object parameter, System.Globalization.CultureInfo culture)
    {
        Picture tValue = value as Picture;
        if (tValue == null) return null;
        BitmapImage tBitmap = new BitmapImage();
        // 利用GetThumbnail()用縮圖,降低發生Out of memory的問題。
        tBitmap.SetSource(tValue.GetThumbnail());
        return tBitmap;
    }

 
    public object ConvertBack(object value, Type targetType, 
            object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

             在此處主要是使用GetThumbnail()的方式,利用縮圖顯示於ListBox中,降低記憶體爆掉的問題。使用GetImage()的話,

             在滑動ListBox時就會出現out of memory的錯誤了。

             可改用<PhotoHub - Windows Phone 8 XAML LongListSelector Grid Layout sample>的方法避免;

 

       1-3. 在畫面載入時,取得所有圖像並且加入至ListBox中;

protected override void OnNavigatedTo(NavigationEventArgs e)
{
        // <Capability Name="ID_CAP_MEDIALIB_PHOTO" />
        // 這個要記得加上去。
        using (MediaLibrary tLib = new MediaLibrary())
        {
            PictureCollection tPicSets = tLib.Pictures;
            if (tPicSets.Count == 0)
                MessageBox.Show("no pictures in the device!");
            else
                lstPictures.ItemsSource = tPicSets;
        }
}

              如果上述功能取不到圖像,主要因為capability未加入,記得加入ID_CAP_MEDIALIB_PHOTO。

 

[補充]

如果你遇到out of memory的話,建議可以將要捉取的圖像分類載入,不要一次把所有設備中的圖像全部加入至畫面,

可改成下列的程式段:

(A) 定義多個PivotItem分別放置不同的圖像來源,並且註冊Loaded事件,如下

<phone:Pivot Title="MY APPLICATION" x:Name="pvtRoot">
    <!--Pivot item one-->
    <phone:PivotItem Header="item1" x:Name="pvt1" Loaded="pvt1_Loaded">
        <Grid>
            <ListBox x:Name="lstType1" Width="430" Height="600">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Margin="0,5,0,0">
                            <Image Source="{Binding Converter={StaticResource PvtPictureConvert}}"
                                  Stretch="Fill"   
                                   />
                            <TextBlock 
            Text="{Binding Name}" 
            TextWrapping="Wrap" 
            Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </phone:PivotItem>

 
    <!--Pivot item two-->
    <phone:PivotItem Header="item2" x:Name="pvt2" Loaded="pvt2_Loaded">
        <Grid>
            <ListBox x:Name="lstType2" Width="430" Height="600">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Margin="0,5,0,0">
                            <Image Source="{Binding Converter={StaticResource PvtPictureConvert}}"
                                    Stretch="Fill" />
                            <TextBlock 
            Text="{Binding Name}" 
            TextWrapping="Wrap" 
            Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </phone:PivotItem>

 
    <phone:PivotItem Header="item3" x:Name="pvt3" Loaded="pvt3_Loaded">
        <Grid>
            <ListBox x:Name="lstType3" Width="430" Height="600">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Margin="0,5,0,0">
                            <Image Source="{Binding Converter={StaticResource PvtPictureConvert}}"
                                    Stretch="Fill" />
                            <TextBlock 
            Text="{Binding Name}" 
            TextWrapping="Wrap" 
            Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </phone:PivotItem>

 
    <phone:PivotItem Header="item4" x:Name="pvt4" Loaded="pvt4_Loaded">
        <Grid>
            <ListBox x:Name="lstType4" Width="430" Height="600">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Margin="0,5,0,0">
                            <Image Source="{Binding Converter={StaticResource PvtPictureConvert}}"
                                    Stretch="Fill" />
                            <TextBlock 
            Text="{Binding Name}" 
            TextWrapping="Wrap" 
            Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </phone:PivotItem>
</phone:Pivot>

    採用與上述介紹Convert的作法,但載入資料是放置在PivotItem.Loaded的事件。為何會這樣做,主要就是分散記憶體的載量,

    這樣也可以讓產生效果比較好。

 

(B) 初始化載入所有Pictures的Album.Name

MediaLibrary gMediaLib = null;

 
private void Initialization()
{
    // 取得所有圖像的Album.Name,並且填入PivotItem.Header
    var tCollection = (from Collection in gMediaLib.Pictures
                       select Collection.Album.Name).Distinct();
    int tCount = 0;
    foreach (string tName in tCollection)
    {
        ((PivotItem)pvtRoot.Items[tCount]).Header = tName;
        tCount += 1;
    }
}

 
protected override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);

 
    Initialization();
}

 

(C) 註冊每一個PivotItem.Loaded,加上讀取的處理邏輯

private void LoadTypePictures(string pTypeName, ListBox pTarget)
{
   if (pTarget.Items.Count == 0)
   {
       var tPictures = from Collection in gMediaLib.Pictures
                       where Collection.Album.Name == pTypeName
                       select Collection;
       pTarget.ItemsSource = tPictures;
   }
}

 
private void pvt1_Loaded(object sender, RoutedEventArgs e)
{
   LoadTypePictures(pvt1.Header.ToString(), lstType1);
}

 
private void pvt2_Loaded(object sender, RoutedEventArgs e)
{
   LoadTypePictures(pvt2.Header.ToString(), lstType2);
}

 
private void pvt3_Loaded(object sender, RoutedEventArgs e)
{
   LoadTypePictures(pvt3.Header.ToString(), lstType3);
}

 
private void pvt4_Loaded(object sender, RoutedEventArgs e)
{
   LoadTypePictures(pvt4.Header.ToString(), lstType4);
}

      詳細的程式內容,可以下載範例程式參考{PivotPage.xaml}。

 

 

(2) 取得所有的音樂檔,並且顯示於ListBox中,根據用戶選擇的曲目進行播放

       2-1. 在xaml中加入一個ListBox,設定ListBox.ItemTemplate顯示曲目的名稱與作曲人;

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <ListBox 
        x:Name="lstSongs" 
        SelectionChanged="SongSelectionChanged" Width="430" Height="700">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Margin="0,0,0,17" 
                Width="432" Height="100">
                <TextBlock 
                    Text="{Binding Name}" TextWrapping="Wrap" 
                    Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                <TextBlock 
                    Text="{Binding Artist.Name}" TextWrapping="Wrap" 
                    Margin="12,-6,12,0" 
                    Style="{StaticResource PhoneTextSubtleStyle}"/>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
    </ListBox>
</Grid>

 

       2-2. 撰寫在畫面載入時,透過MediaLibrary取得所有曲目;

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);

 
    lstSongs.Items.Clear();
    using (MediaLibrary tLibrary = new MediaLibrary())
    {
        if (tLibrary.Songs.Count == 0)
            MessageBox.Show("設備中沒有任何曲目!");
        else
            lstSongs.ItemsSource = tLibrary.Songs;
    }
}

 

        2-3. 撰寫用戶選擇ListBox中的Item時進行曲目的播放;

private void SongSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    // 透過FrameworkDispatcher.Update()來更新目前播放的主導權
    FrameworkDispatcher.Update();
    if (MediaPlayer.State == MediaState.Playing)
    {
        MessageBoxResult tResult = MessageBox.Show(
                    "是否取代目前正在播放的項目?", "提示", 
                    MessageBoxButton.OKCancel);
        if (tResult == MessageBoxResult.OK)
        {
            MediaPlayer.Stop();                    
        }
    }
    // 播放指定的Song物件
    Song tSong = lstSongs.SelectedItem as Song;
    MediaPlayer.Play(tSong);
}

         為何會需要透過FrameworkDispatcher.Update()?

         可以參考<Windows Phone 7 – Background Audio Playlist Application>的補充說明。

 

        2-4. 刪除選擇的曲目;

if (MessageBox.Show("Is deleted the song?", "Tip", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
{
    Song tItem = ((MenuItem)sender).DataContext as Song;
    using (MediaLibrary tLibrary = new MediaLibrary())
    {
        // 利用MediaLibraryExtensions
        MediaLibraryExtensions.Delete(tLibrary, tItem);
    }
}

 

[範例程式]

======

以上是說明如何操作MediaLibrary來取得設備中的所有圖像、音樂與影片。希望對大家有所幫助。

 

References

Data for Windows Phone

Windows Phone Runtime API

Basics of Using Local Storage on Windows Phone 8

Reading from an SD Card in Windows Phone 8 Applications

System.OutOfMemoryException when using GetImage() from MediaLibrary Picture Collection (重要)

How to create a base camera app for Windows Phone

windows phone 7 中怎样定义和使用资源(Resource)

Theme resources for Windows Phone (重要的參考資源)

Windows phone: listbox with images out-of-memory

31 Days of Mango | Day #28: MediaLibrary

Windows Phone - Microsoft Advertising的使用心得

Advanced photo capture for Windows Phone 8

 

Dotblogs 的標籤: