C# BackgroundAudioPlayer是';玩';但不调用getSamPleaseSync()

C# BackgroundAudioPlayer是';玩';但不调用getSamPleaseSync(),c#,windows-phone-8,windows-phone,background-audio,C#,Windows Phone 8,Windows Phone,Background Audio,我在后台代理中使用自定义的MediaStreamSource从web流式播放音乐。在良好的网络条件下,这可以正常工作,但当网络连通性不稳定时,就会出现一个奇怪的问题 当一首曲目开始播放时,通过第一次调用MediaStreamSource.getSamPleaseSync(),一切都进展顺利。由于连接不稳定,如果没有足够的可用数据,源将调用ReportGetSampleProgress(double)并返回,而不报告样本。这符合MSDN文档和代码示例 奇怪的是,根本就没有进一步调用GetSamPl

我在后台代理中使用自定义的MediaStreamSource从web流式播放音乐。在良好的网络条件下,这可以正常工作,但当网络连通性不稳定时,就会出现一个奇怪的问题

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

奇怪的是,根本就没有进一步调用GetSamPleaseSync!当缓冲继续时,源继续报告GetSampleProgress,直到样本准备就绪,此时它调用
ReportGetSampleProgress(1.0)
以指示缓冲区已满

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

  • ReportGetSampleCompleted
    缓冲完成时;这会失败,因为下载事件发生在任意线程上,并且此方法显然对调用线程以及对GetSamPleaseSync的调用是否在堆栈上都很敏感;无效的调用环境会导致COM错误
  • 在准确的错误情况下,停止并启动BackgroundAudioPlayer:这无法重新启动流媒体

一旦读取样本的初始失败挂起,如何使流媒体再次运行?

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

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


但是,请注意,在涉及的各个线程之间可能会由于争用条件而出现故障。

碰巧,一个解决方案似乎是打破名称
getsamplesync
所建议的契约,并在缓冲数据不足时阻塞该方法。然后,流回调可以脉冲锁定对象,并且可以重试样本读取。类似这样的方法效果很好:

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);
}
异步方法中的阻塞似乎不是明智之举,但在设备上运行此代码的经验表明并非如此。根据,阻塞可能会阻止读取其他流。作为一个流媒体音乐应用程序,我们一次只能提供一个流,所以在这种情况下,我们似乎没问题


不过,我希望我知道一个通用的解决方案,因为这显然是作弊。

如果您没有任何可用数据,请用静默填充缓冲区并报告。这将给你时间来获取真实数据

您希望将数据集中到静音数据上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 );
MemoryStream stream=newmemoryStream();
字节[]沉默缓冲=位转换器.GetBytes((ushort)(short.MaxValue));
对于(int i=0;i<1000;i++)
stream.Write(沉默缓冲区,0,沉默缓冲区长度);

祝你好运。

在什么情况下调用ReportGetSampleCompleted是合适的?这似乎是问题的症结所在——我知道我唯一一次完成了缓冲,我在另一个线程上,无法安全地调用它。如果有
getsamplesync
无法提供示例,则可以安全地调用它。也许您在这里遇到了另一个问题…当然可能还有另一个问题,但我积极地观察到,报告来自线程池线程的样本(使用相同的样本读取代码路径)会导致从COM错误封送的异常;当从对GetSamPleaseSync的调用中调用时,所记录的代码会成功。这在设备上和模拟器中都存在。异常作为NullReferenceException进行封送。我已经在调试器中验证了我传递给ReportGetSampleCompleted的任何内容都不是空的:样本是构造的,它具有正确的流描述、属性键、,等等。堆栈跟踪显示抛出类为
XcpImports
,它只是将HRESULT映射到预定义的异常。@PaulAnnetts我尝试了您建议的方法,它确实有效。主要部分是在ReportGetSampleProgress(1.0)之后调用ReportGetSampleCompleted;但是PlayState从来没有进入缓冲区。我已经有一段时间没有看过这段代码了(现在是在另一个工作中),但我记得应用程序在缓冲区状态时没有出现问题。啊,我的没有进入这些状态。谢谢你提供的信息,本