在 Store Apps 中的 MediaElement 也是同樣除了 Uri 之外還可以指定 MediaStreamSource 來播放自訂的串流,但是 MediaStreamSource 的 Constructor 可以指定 MediaStreamDescriptor,這就是用來描述音檔格式的方法,目前可以利用 AudioEncodingProperties 建立 AAC、AAC-ADTS、MP3、PCM、WMA 這幾種格式的串流,如果正好你需要播放這些格式,那麼 Store Apps 的框架很貼心的幫你節省了上千行程式碼的撰寫時間。
其中 AAC 指的是 MP4 或 M4A 這種包裝的方式,與 ADTS 格式的 Frame 排列規則略有不同,有興趣的可以參考 AAC 的 Wiki,有需要播放 AAC 串流的開發者別忘了看看副檔名確認是哪一種 AAC。
以下我用很簡短的 Code 示範如何播放 MP3 的串流,串流來源是放在 Storage 底下的 Audio.mp3 這個檔案,首先在 XAML 中加入一個 MediaElement 元件及一顆播放按鍵。
<Page
x:Class="App1.PlayerPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<MediaElement x:Name="Player" AreTransportControlsEnabled="True" AudioCategory="BackgroundCapableMedia" />
<Button Content="Play" Click="OnPlayButtonClick"/>
</Grid>
</Page>
接著在 PlayerPage.xaml.cs 中利用 MediaStreamSource 將 Audio.mp3 檔案讀出來播放,這裡為了提供連貫的閱讀性,將流程全部寫在 Page 內,但理想上將各種格式的 MediaStreamSource 各別封裝成獨立的 Class 將可以得到更簡潔的播放流程程式碼,例如建立 MP3MediaStreamSource 或 AACMediaStreamSource 之類的來使用。
public sealed partial class PlayerPage : Page
{
private StorageFile inputMP3File;
private MediaStreamSource MSS = null;
private IRandomAccessStream mssStream;
private IInputStream inputStream;
private UInt64 byteOffset;
private TimeSpan timeOffset;
private const UInt32 sampleSize = 1152;
private TimeSpan sampleDuration = new TimeSpan(0, 0, 0, 0, 70);
public PlayerPage()
{
this.InitializeComponent();
}
private async void OnPlayButtonClick(object sender, RoutedEventArgs e)
{
inputMP3File = await ApplicationData.Current.LocalFolder.GetFileAsync("temp0.mp3");
byteOffset = 0;
uint sampleRate = 44100;
uint channelCount = 2;
uint bitRate = 128000;
MusicProperties mp3FileProperties = await inputMP3File.Properties.GetMusicPropertiesAsync();
TimeSpan songDuration = mp3FileProperties.Duration;
Debug.WriteLine("歌曲總長度 :: " + songDuration.TotalSeconds);
AudioEncodingProperties audioProps = AudioEncodingProperties.CreateMp3(sampleRate, channelCount, bitRate);
AudioStreamDescriptor audioDescriptor = new AudioStreamDescriptor(audioProps);
MSS = new MediaStreamSource(audioDescriptor);
MSS.CanSeek = true;
MSS.MusicProperties.Title = mp3FileProperties.Title;
MSS.Duration = songDuration;
MSS.Starting += OnMSSStarting;
MSS.SampleRequested += OnMSSSampleRequested;
MSS.Closed += OnMSSClosed;
Player.SetMediaStreamSource(MSS);
}
async void OnMSSStarting(Windows.Media.Core.MediaStreamSource sender, MediaStreamSourceStartingEventArgs args)
{
MediaStreamSourceStartingRequest request = args.Request;
if ((request.StartPosition != null) && request.StartPosition.Value <= MSS.Duration)
{
UInt64 sampleOffset = (UInt64)request.StartPosition.Value.Ticks / (UInt64)sampleDuration.Ticks;
timeOffset = new TimeSpan((long)sampleOffset * sampleDuration.Ticks);
byteOffset = sampleOffset * sampleSize;
}
if (mssStream == null)
{
MediaStreamSourceStartingRequestDeferral deferal = request.GetDeferral();
try
{
mssStream = await inputMP3File.OpenAsync(FileAccessMode.Read);
request.SetActualStartPosition(timeOffset);
deferal.Complete();
}
catch (Exception)
{
MSS.NotifyError(MediaStreamSourceErrorStatus.FailedToOpenFile);
deferal.Complete();
}
}
else
{
request.SetActualStartPosition(timeOffset);
}
}
async void OnMSSSampleRequested(Windows.Media.Core.MediaStreamSource sender, MediaStreamSourceSampleRequestedEventArgs args)
{
if (mssStream != null)
{
MediaStreamSourceSampleRequest request = args.Request;
if (byteOffset + sampleSize <= mssStream.Size)
{
MediaStreamSourceSampleRequestDeferral deferal = request.GetDeferral();
inputStream = mssStream.GetInputStreamAt(byteOffset);
MediaStreamSample sample = await MediaStreamSample.CreateFromStreamAsync(inputStream, sampleSize, timeOffset);
sample.Duration = sampleDuration;
sample.KeyFrame = true;
byteOffset += sampleSize;
timeOffset = timeOffset.Add(sampleDuration);
request.Sample = sample;
deferal.Complete();
}
}
}
void OnMSSClosed(Windows.Media.Core.MediaStreamSource sender, MediaStreamSourceClosedEventArgs args)
{
if (mssStream != null)
{
mssStream.Dispose();
mssStream = null;
}
sender.SampleRequested -= OnMSSSampleRequested;
sender.Starting -= OnMSSStarting;
sender.Closed -= OnMSSClosed;
if (sender == MSS)
{
MSS = null;
}
}
}
沒有留言:
張貼留言