BackgroundAudioPlayer是’正在播放’但没有调用GetSampleAsync()

我使用自定义MediaStreamSource在后台代理中从Web流式传输音乐。 在良好的网络条件下,这种方法很好,但是当网络连接不稳定时,会出现一个奇怪的问题。

当曲目开始播放时,通过第一次调用MediaStreamSource.GetSampleAsync(),一切顺利。 由于连接不稳定,如果数据不足,则源调用ReportGetSampleProgress(double)并返回而不报告样本。 这符合MSDN文档和代码示例。

令人好奇的是,根本没有进一步调用GetSampleAsync! 当缓冲继续时,源继续ReportGetSampleProgress直到样本准备就绪,此时它调用ReportGetSampleProgress(1.0)来指示完整缓冲区。

我尝试了几种方法,包括:

  • 缓冲完成后, ReportGetSampleCompleted ; 这会失败,因为下载事件会进入任意线程,并且此方法对调用线程以及对GetSampleAsync的调用是否在堆栈上显然都很敏感; 无效的调用情况会导致COM错误。
  • 在精确的错误条件下,停止并启动BackgroundAudioPlayer:这无法重新启动流式传输。

一旦最初读取样本失败,我怎样才能再次进行流式传输?

一旦数据可用, ReportGetSampleCompleted就是这种情况下的正确方法。

您必须在MSS中跟踪是否需要立即报告任何新的样本数据或等待调用GetSampleAsync

但是,请注意所涉及的各种线程之间可能因竞争条件而导致的故障。

实际上,解决方案似乎是打破GetSampleAsync这个名称GetSampleAsync建议的契约,并在没有足够数据缓冲的情况下阻止该方法。 然后,流回调可以对锁定的对象进行脉冲,并且可以重试样本读取。 这样的事情效果很好:

 private void OnMoreDataDownloaded(object sender, EventArgs e) { // We're on an arbitrary thread, so instead of reporting // a sample here we should just pulse. lock (buffering_lock) { is_buffering = false; Monitor.Pulse(buffering_lock); } } protected override void GetSampleAsync() { while (we_need_more_data) { lock (buffering_lock) { is_buffering = true; while (is_buffering) { Monitor.Wait(buffering_lock); } } // code code code ReportGetSampleCompleted(sample); } 

看起来Async方法中的阻塞可能并不明智,但在设备上运行此代码的经验则表明不然。 根据http://msdn.microsoft.com/en-us/library/system.windows.media.mediastreamsource.getsampleasync(v=vs.95).aspx ,阻止可能会阻止其他流被读取。 作为一个流媒体音乐应用程序,我们一次只提供一个流,所以在这种情况下,似乎我们没关系。

我希望我在这里知道一个普遍的解决方案,因为这显然是在欺骗。

如果您没有任何可用数据,请用静音填充缓冲区并报告。 这将为您提供实时数据的时间。

您希望将数据置于静音数据的PCM范围中间,或者在静音时获得单击。

 MemoryStream stream = new MemoryStream(); byte[] silenceBuffer = BitConverter.GetBytes( (ushort)(short.MaxValue) ); for(int i=0; i < 1000; i++ ) stream.Write( silenceBuffer, 0, silenceBuffer.Length ); 

好运。