Reputation: 35
So I have a problem with the Background Audio Playback agent in WP 7.5, where I think it is getting terminated by the OS randomly during playback.
I have an app which implements a BAP agent which plays a number of mp3 files based the selected chapter in UI. Each chapter has multiple verses, these verses have an associated mp3 file in isolated storage.
Once a chapter has been selected in the UI and the user presses the play button BackgroundAudio.Instance.Play()
is called and the first verse (mp3 file) for that chapter is loaded as an AudioTrack
. When the track has ended the next track is loaded in the OnPlayStateChanged
event method under the TrackEnded
state.
I also have some logic in the TrackEnded
which checks if the end of the chapter has been reached (i.e. the last mp3 file for the current chapter has been played) and if so the first mp3 file for the next chapter will retrieved.
Now all the above works fine when using the Windows Phone 7 Emulator (both 512Mb and 256Mb Emulators), the mp3 files are played correctly and when the end of a chapter has been reached the next mp3 file for the next chapter is correctly loaded and played.
The problem I encounter is when I deploy this app to a Win 8 Device (Lumia 920), the audio starts playing fine and the suddenly and seemingly randomly the audio stops! No error messages, the app does NOT crash, just the Audio Stops playing. Also when I click on the UVC buttons on the device NO AudioTrack
info is displayed as is the case during audio playback or audio has been paused (just the volume info shows).
I have no idea what’s going on, I think the OS may be terminating the Background Audio Playback agent but I have no idea why (I don’t think I am reaching any of the memory limitations but I can not confirm this as I don’t know how to check if I am).
Any advice/help will be appreciated.
Thanks
Update 14/01/14
To confirm that the memory limit of 15Mb(WP7) and 20Mb(WP8) was not being reached by my BAP I implemented some code which logged the current memory usage of the BAP at various stages through out its execution.
The memory usage does not reach anywhere near the limits imposed by the OS on the BAP, the peak i reach is 7Mb the issue I described above is still occurring, I can see from the log that the next track has already been set but the state Trackready
is never hit, also no exceptions/errors are thrown. This has really stumped me!
Update 15/01/14 Below is an example of how I have implemented the 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
}
Update 24/01/2014 ISSUE RESOLVED
I finally got some time to get around to attempting what @Soonts recommended in the answer I marked as the answer, and firstly I'm using a WP8 device so I skipped the first setp that he mentioned, next i did as was mentioned in step 2 and again the max memory usage was only 8Mb.
Then a few days back there was an update for my WP8 device (WP8 Update 3), after I installed this update I attempted to reproduce the problem and guess what! The issue DOES NOT OCCUR ANY MORE!, I had my audio on continuous play for over an hour with no issues! also the memory usage was stable at around 8Mb. So it looks like there may have been a silent update to the BG Audio.
The reason why I marked @snoots answer as the answer was because he mentioned in that answer that the issue may be fixed by a silent update.
Upvotes: 2
Views: 931
Reputation: 21936
If I were you, I would probably do the following, in that order:
Upvotes: 1
Reputation: 21936
This may happen on unhandled exceptions. Subscribe for Application.Current.UnhandledException
(and if you're using async-await for TaskScheduler.UnobservedTaskException
) and log them somewhere. Also, override OnError method of your agent, and log.
This may happen if forget to call BackgroundAgent.NotifyComplete() after you've finished processing the requests (i.e. for player agent, the OnPlayStateChanged and OnUserAction). In this case, the OS consludes you was unable to process the request in a timely manner, and terminates the BAP process.
RAM issues, but you've figured it out.
P.S. Here's the relevant part of my Sky.fm player application. It doesn't play local MP3s , instead it streams the music from the Internets, however the player agent code should be more or less the same.
/// <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 );
}
Upvotes: 2