2

所以我在 WP 7.5 中遇到了后台音频播放代理的问题,我认为它在播放期间被操作系统随机终止。

我有一个应用程序,它实现了一个 BAP 代理,它根据 UI 中的选定章节播放许多 mp3 文件。每章都有多节经文,这些经文在独立存储中都有一个关联的 mp3 文件。

一旦在 UI 中选择了一个章节并且用户按下播放按钮BackgroundAudio.Instance.Play(),就会调用该章节的第一节经文(mp3 文件)作为AudioTrack. 当曲目结束时,在状态OnPlayStateChanged下的事件方法中加载下一曲目TrackEnded

我还有一些逻辑TrackEnded检查是否已到达章节结尾(即当前章节的最后一个 mp3 文件已播放),如果是,则将检索下一章的第一个 mp3 文件。

现在,当使用 Windows Phone 7 模拟器(512Mb 和 256Mb 模拟器)时,上述所有操作都可以正常工作,正确播放 mp3 文件,并且当到达一章结尾时,下一章的下一个 mp3 文件被正确加载和播放.

我遇到的问题是,当我将此应用程序部署到 Win 8 设备 (Lumia 920) 时,音频开始正常播放,并且突然且看似随机地停止了音频!没有错误消息,应用程序不会崩溃,只是音频停止播放。此外,当我单击设备上的 UVC 按钮时,AudioTrack不会显示任何信息,就像音频播放期间的情况一样或音频已暂停(仅显示音量信息)。

我不知道发生了什么,我认为操作系统可能正在终止后台音频播放代理,但我不知道为什么(我认为我没有达到任何内存限制,但我无法确认这一点,因为我没有不知道如何检查我是否在)。

任何建议/帮助将不胜感激。

谢谢

2014 年 14 月 1 日更新

为了确认我的 BAP 没有达到 15Mb(WP7) 和 20Mb(WP8) 的内存限制,我实现了一些代码,记录了 BAP 在执行过程中各个阶段的当前内存使用情况。

内存使用量没有达到操作系统对 BAP 施加的限制,我达到的峰值是 7Mb 我上面描述的问题仍然存在,我可以从日志中看到已经设置了下一个轨道但是状态Trackready永远不会被击中,也不会抛出异常/错误。这真的把我难住了!

2014 年 1 月 1日更新 以下是我如何实施 BAP 的示例:

    public AudioPlayer()
    {

       if (!_classInitialized)
        {
            _classInitialized = true;
            // Subscribe to the managed exception handler
            Deployment.Current.Dispatcher.BeginInvoke(delegate
            {
                Application.Current.UnhandledException += AudioPlayer_UnhandledException;
            });
            lastPlayedVerse = currentVerseNumber;

        }
    }

     /// Code to execute on Unhandled Exceptions
    private void AudioPlayer_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
    {
    //Helper class to help log any exceptions
        IsolatedStore.WriteToIS("unhandeled Ex: " + e.ExceptionObject.Message, IsolatedStore.MemLogFileName);
        if (System.Diagnostics.Debugger.IsAttached)
        {
            // An unhandled exception has occurred; break into the debugger
            System.Diagnostics.Debugger.Break();
        }
    }

    protected override void OnError(BackgroundAudioPlayer player, AudioTrack track, Exception error, bool isFatal)
    {

        //Helper class to help log any exceptions
        IsolatedStore.WriteToIS("OnError Called: " + error.Message, IsolatedStore.MemLogFileName);

        if (isFatal)
        {
            Abort();
        }
        else
        {
            NotifyComplete();
        }

    }

    protected override void OnPlayStateChanged(BackgroundAudioPlayer player, AudioTrack track, PlayState playState)
    {
        switch (playState)
        {
            case PlayState.TrackEnded:

                track = null;
    IsolatedStore.AppendToFileIS(string.Format("Track Ended::Time: {0}",DateTime.Now.ToLongTimeString()), IsolatedStore.MemLogFileName);

                #region Track Ended logic
                //IN here I have some logic to determine what the next track should be and then I call a function that returns an AudioTrack
                 player.Track = GetNextTrack();  //this method returns an AudioTrack                  
                #endregion
                break;
            case PlayState.TrackReady:
                IsolatedStore.AppendToFileIS(string.Format("Track Ready::Time: {0}, Track: {1}", DateTime.Now.ToLongTimeString(),track.Title), IsolatedStore.MemLogFileName);

                //Put this try catch in here becoz i thought that this is where the issue was (not sure if its needed as any exception should be caught by the AudioPlayer_UnhandledException function.
                try
                {
                    player.Play();
                }
                catch (Exception ex)
                {

                    IsolatedStore.AppendToFileIS(string.Format("Track Ready play exception: {0}", ex.Message), IsolatedStore.MemLogFileName);

                }           
                break;

        }

        NotifyComplete();
    }

    protected override void OnUserAction(BackgroundAudioPlayer player, AudioTrack track, UserAction action, object param)
    {
        switch (action)
        {
            case UserAction.Play:
                if (player.PlayerState != PlayState.Playing)
                {
                    IsolatedStore.AppendToFileIS(string.Format("UA-PLAY::Time: {0}, Track: {1}", DateTime.Now.ToLongTimeString(),track.Title), IsolatedStore.MemLogFileName);
                    player.Play();

                }
                break;
        }

        NotifyComplete();
    }

    private AudioTrack GetNextTrack(int audioType2Get, string filePath, int verserNum, bool incrementTrackCount)
    {

        #region Memusage

        //Code to log the memory usage
        long currMemUsage = (long)DeviceExtendedProperties.GetValue("ApplicationCurrentMemoryUsage");
        currMemUsage = (currMemUsage / 1024) / 1024;
        long peakMemUsage = (long)DeviceExtendedProperties.GetValue("ApplicationPeakMemoryUsage");
        peakMemUsage = (peakMemUsage / 1024) / 1024;
        IsolatedStore.AppendToFileIS(string.Format("Getting Track-Time: {0}, Curr:{1}, Track: {2}", DateTime.Now.ToLongTimeString(), currMemUsage, verserNum), IsolatedStore.MemLogFileName);

        #endregion
        AudioTrack track = null;
        #region AudioTrack Set region
        //Some logic to return the AudioTrack
        #endregion

    }

更新 24/01/2014 问题已解决

我终于有时间尝试@Soonts 在我标记为答案的答案中推荐的内容,首先我使用的是 WP8 设备,所以我跳过了他提到的第一个 setp,接下来我按照步骤2,最大内存使用量仅为 8Mb。

几天前,我的 WP8 设备(WP8 Update 3)有一个更新,在我安装了这个更新后,我试图重现这个问题并猜猜是什么!该问题不再发生!,我的音频连续播放了一个多小时,没有任何问题!内存使用量也稳定在 8Mb 左右。所以看起来 BG Audio 可能已经进行了无声更新。

我将@snoots 答案标记为答案的原因是因为他在该答案中提到该问题可以通过静默更新来解决。

4

2 回答 2

2
  1. 这可能发生在未处理的异常上。订阅Application.Current.UnhandledException(如果您正在使用 async-await for TaskScheduler.UnobservedTaskException)并将它们记录在某处。此外,覆盖代理的 OnError 方法并记录。

  2. 如果在处理完请求后忘记调用 BackgroundAgent.NotifyComplete() 可能会发生这种情况(即对于播放器代理,OnPlayStateChanged 和 OnUserAction)。在这种情况下,操作系统会认为您无法及时处理请求,并终止 BAP 进程。

  3. RAM 问题,但您已经弄清楚了。

PS 这是我的 Sky.fm 播放器应用程序的相关部分。它不播放本地 MP3,而是从 Internet 流式传输音乐,但是播放器代理代码应该或多或少相同。

/// <summary>This class wraps AudioPlayerAgent API into the async-friendly abstract class.</summary>
/// <remarks>Logging and exception handling are added, as well.</remarks>
public abstract class PlayerAgentAsync: AudioPlayerAgent
{
    static PlayerAgentAsync()
    {
        UnhandledExceptionHandler.subscribe();
    }

    public PlayerAgentAsync()
    {
        Logger.info( "constructed" );
    }

    protected override void OnError( BackgroundAudioPlayer player, AudioTrack track, Exception ex, bool isFatal )
    {
        if( isFatal )
        {
            BackgroundErrorNotifier.addError( ex );
            ex.log();
            Abort();
        }
        else
        {
            ex.logWarning();
            try
            {
                // Force the track to stop 
                // http://blogs.msdn.com/b/wpukcoe/archive/2012/02/11/background-audio-in-windows-phone-7-5-part-3.aspx
                player.Track = null;
            }
            catch (System.Exception ex2)
            {
                ex2.logWarning( "Exception while trying to stop da playa" );
            }
            NotifyComplete();
        }
    }

    /// <summary>Called when the play state changes, except for the error state.</summary>
    protected override async void OnPlayStateChanged( BackgroundAudioPlayer player, AudioTrack track, PlayState playState )
    {
        Logger.info( "new playState = {0}", playState.ToString() );

        try
        {
            await this.playStateChangedAsync( player, track, playState ).ConfigureAwait( false );
            NotifyComplete();
        }
        catch (System.Exception ex)
        {
            this.onException( ex );
        }
    }

    /// <summary>Called when the user requests an action using some application-provided UI or the Universal Volume Control (UVC) and the application has requested notification of the action.</summary>
    protected override async void OnUserAction( BackgroundAudioPlayer player, AudioTrack track, UserAction action, object param )
    {
        Logger.info( "action = {0};", action.ToString() );

        try
        {
            await this.userActionAsync( player, track, action, param ).ConfigureAwait( false );
            NotifyComplete();
        }
        catch( System.Exception ex )
        {
            this.onException( ex );
        }
    }

    private void onException( Exception ex )
    {
        if( ex.shouldBeIgnored() )
        {
            ex.logWarning();
            this.NotifyComplete();
            return;
        }
        ex.log();
        BackgroundErrorNotifier.addError( ex );
        this.Abort();
    }

    protected override void OnCancel()
    {
        Logger.trace();
        base.OnCancel();
    }

    /// <summary>Handle OnPlayStateChanged asyncronously.</summary>
    /// <param name="player">The Microsoft.Phone.BackgroundAudio.BackgroundAudioPlayer.</param>
    /// <param name="track">The track playing at the time that the play state changed.</param>
    /// <param name="playState">The new state of the player.</param>
    protected abstract Task playStateChangedAsync( BackgroundAudioPlayer player, AudioTrack track, PlayState playState );

    /// <summary>Handle OnUserAction asyncronously</summary>
    /// <param name="player">The Microsoft.Phone.BackgroundAudio.BackgroundAudioPlayer.</param>
    /// <param name="track">The track playing at the time of the user action.</param>
    /// <param name="action">The action that the user has requested.</param>
    /// <param name="param">The data associated with the requested action.</param>
    protected abstract Task userActionAsync( BackgroundAudioPlayer player, AudioTrack track, UserAction action, object param );
}
于 2014-01-14T21:03:35.163 回答
1

如果我是你,我可能会按顺序执行以下操作:

  1. 确保您拥有最新的操作系统更新。他们在 7.8 更新中默默地修复了一些与 BAP 相关的内存问题。如果您没有使用最新的操作系统,请升级并尝试重现。
  2. 在与 BAP 交互的 GUI 进程代码的每个位置放置断点,以确保您没有错误地调用例如 BackgroundAudioPlayer.Stop() 或 BackgroundAudioPlayer.Track=null,尝试重现。
  3. 代替“在各个阶段记录 BAP 的当前内存使用情况”,在某个代理类的静态构造函数中,创建一个线程,在无限循环中,附加到隔离存储中的某个 CSV 文件,记录当前时间戳 + 当前内存,然后休眠 500-1000 毫秒。重现问题,使用ISETool.exe下载日志,使用Excel构建RAM使用图……</li>
  4. 如果 (3) 不会显示 RAM 使用量接近限制,则实现对隔离存储或网络的跟踪(顺便说一句,我在我的 WP7 代码中实现了一个 SysLog RFC 5426 客户端,并安装了名为“SysRose Syslog Desktop”的免费软件我的电脑),然后尝试进行 printf 样式的调试。
于 2014-01-15T20:34:19.580 回答