I am working on my Project (a soundcloud client) and the app can play tracks just fine, but not when the app is minimized. I use the MediaElement-Object for playing the mp3 from the url. How can i force the music to continue playing the music, when the app is in the background. Or whats the easiest way/best explained tutorial to implement this. I searched alot for a good answer, but the ones, i found, was too good for me :D What means, that i didn't understand it.
To play audio in the background you will have to do a Declaration in Package.appxmanifest for a Background Tasks, enable audio and add an entry point like TestUWP.MainPage page.
Also for the user to easily be able to manage the audio you can use SystemMediaTransportControls
Here is a basic setup with Play and Pause.
xaml
<MediaElement x:Name="mediaElement" Height="100" Width="100" AreTransportControlsEnabled="True"/>
C#
public MainPage()
{
this.InitializeComponent();
systemControls = SystemMediaTransportControls.GetForCurrentView();
// Register to handle the following system transpot control buttons.
systemControls.ButtonPressed += SystemControls_ButtonPressed;
mediaElement.CurrentStateChanged += MediaElement_CurrentStateChanged;
systemControls.IsPlayEnabled = true;
systemControls.IsPauseEnabled = true;
}
private void MediaElement_CurrentStateChanged(object sender, RoutedEventArgs e)
{
switch (mediaElement.CurrentState)
{
case MediaElementState.Playing:
systemControls.PlaybackStatus = MediaPlaybackStatus.Playing;
break;
case MediaElementState.Paused:
systemControls.PlaybackStatus = MediaPlaybackStatus.Paused;
break;
case MediaElementState.Stopped:
systemControls.PlaybackStatus = MediaPlaybackStatus.Stopped;
break;
case MediaElementState.Closed:
systemControls.PlaybackStatus = MediaPlaybackStatus.Closed;
break;
default:
break;
}
}
void SystemControls_ButtonPressed(SystemMediaTransportControls sender, SystemMediaTransportControlsButtonPressedEventArgs args)
{
switch (args.Button)
{
case SystemMediaTransportControlsButton.Play:
PlayMedia();
break;
case SystemMediaTransportControlsButton.Pause:
PauseMedia();
break;
case SystemMediaTransportControlsButton.Stop:
StopMedia();
break;
default:
break;
}
}
private async void StopMedia()
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
mediaElement.Stop();
});
}
async void PlayMedia()
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
if (mediaElement.CurrentState == MediaElementState.Playing)
mediaElement.Pause();
else
mediaElement.Play();
});
}
async void PauseMedia()
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
mediaElement.Pause();
});
}
Output
If you like to enable more controls you can do using the available properties for ex.
systemControls.IsNextEnabled = true;
and you have to add the case in the button switch.
case SystemMediaTransportControlsButton.Next:
//handle next song
break;
Related
So I'm new in programming with Xamarin (actually even with C# tbh)
What I'm trying to acheave is a Task which should only work when a Switch (called S1) is Toggled.
My idea:
public async Task GetCon(){
for (; ; )
{
if (S1.IsToggled == true)
{
AI1.IsRunning = true;
bool CStat = await CrossConnectivity.Current.IsRemoteReachable("https://www.google.ch");
if (CStat == true)
{
StatLbl.Text = "Online";
}
else if (CStat == false)
{
StatLbl.Text = "Offline";
break;
}
off:;
await Task.Delay(3000);
}
All works fine but if I turn the Switch back off and make google unreachable, the StatLbl text doesn't change to offline.
Any idea?
As you're new to C#, I'm surprised no-one else has picked up on this, but here goes.
Instead of infinitely running a Task and waiting for the switch to be toggled, use the Toggled event to trigger when the toggle status changes. How does it work? When you toggle the switch, your program will automatically call that method. For example:
public MyConstructor()
{
S1.Toggled += S1_Toggled;
}
void S1_Toggled(object sender, ToggledEventArgs e)
{
System.Diagnostics.Debug.WriteLine(String.Format("Switch is now {0}", e.Value));
}
You can find more information (and some example) for the Switch at the Xamarin Forms Docs.
As Jason pointed out, you should be modifying UI properties from the UI thread. Properties like colour, visibility, text etc (anything that changes on the display) should be done in Device.BeginInvokeOnMainThread like so:
Device.BeginInvokeOnMainThread(() =>
{
StatLbl.Text = "Offline";
});
you need to execute UI changes on the UI thread
Device.BeginInvokeOnMainThread(() => {
StatLbl.Text = "Offline";
});
Most of the code executes only when the switch is on, you have this line that prevents from changing the label name when the switch is on:
if (S1.IsToggled == true)
I have recently decided to add Background Audio playback support for podcasts in my app. I have got most of it to work but, the pause button in the SystemTransportControls of the BackgroundMediaPlayer doesn't seem to do anything.
Here is my background audio task class file:
public sealed class AudioPlayer : IBackgroundTask
{
private BackgroundTaskDeferral _deferral;
public void Run(IBackgroundTaskInstance taskInstance)
{
_deferral = taskInstance.GetDeferral();
var control = BackgroundMediaPlayer.Current.SystemMediaTransportControls;
control.IsEnabled = true;
control.IsPauseEnabled = true;
control.IsPlayEnabled = true;
control.IsNextEnabled = false;
control.IsPreviousEnabled = false;
taskInstance.Canceled += TaskInstance_Canceled;
BackgroundMediaPlayer.MessageReceivedFromForeground += BackgroundMediaPlayer_MessageReceivedFromForeground;
}
private void TaskInstance_Canceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
BackgroundMediaPlayer.Shutdown();
_deferral.Complete();
}
void BackgroundMediaPlayer_MessageReceivedFromForeground(object sender, MediaPlayerDataReceivedEventArgs e)
{
object obj;
e.Data.TryGetValue("MessageBody", out obj);
string url = (string)obj;
url = url.Replace("\"", "");
var source = MediaSource.CreateFromUri(new Uri(url, UriKind.Absolute));
var list = new MediaPlaybackList();
list.Items.Add(new MediaPlaybackItem(source));
BackgroundMediaPlayer.Current.Source = list;
BackgroundMediaPlayer.Current.Play();
}
}
It is important to note that the audio does start playing in the background but I don't have the ability to pause the audio.
Thanks,
P.
To react to SystemMediaTransportControls (SMTC) and handle each Universal Volume Control (UVC) button: play, pause, next, and previous, we need handle the SystemMediaTransportControls.ButtonPressed event in Run method like following:
public void Run(IBackgroundTaskInstance taskInstance)
{
var control = BackgroundMediaPlayer.Current.SystemMediaTransportControls;
control.ButtonPressed += control_ButtonPressed;
...
}
And in control_ButtonPressed method, deal with different buttons like:
private void control_ButtonPressed(SystemMediaTransportControls sender, SystemMediaTransportControlsButtonPressedEventArgs args)
{
switch (args.Button)
{
case SystemMediaTransportControlsButton.Play:
//Todo with play
break;
case SystemMediaTransportControlsButton.Pause:
//Todo with pause
break;
case SystemMediaTransportControlsButton.Next:
//Todo with skip to next;
break;
case SystemMediaTransportControlsButton.Previous:
//Todo with skip to previous;
break;
}
}
For a complete sample, please refer to the official Background audio sample on GitHub, especially the MyBackgroundAudioTask class. And there is also a very nice post that will walk you through setting up background audio, reacting to device media controls, communicating with the background media player, and managing playlists. Read more at The Basics of Background Audio.
In my Windows Phone 8.1 App, I am using Media Element. I want it to continue to play Audio even if user navigated away from app. MediaElement is using video from remote source (.mp4) file. I also tried with sample video in here;
http://go.microsoft.com/fwlink/p/?LinkID=272585
I followed example in How to play audio in the background (XAML) but could not make it work. This example is specific to Windows 8.1 not Windows Phone.
While MediaElement is playing Video Clip when I press Windows button audio stops and when I hit back it continues to work.
My code is like below;
<MediaElement x:Name="MediaElement" VerticalAlignment="Top"
HorizontalAlignment="Stretch"
AudioCategory="BackgroundCapableMedia"
MediaEnded="MediaElement_MediaEnded"
MediaFailed="MediaElement_MediaFailed"
MediaOpened="MediaElement_MediaOpened" SeekCompleted="MediaElement_SeekCompleted"
DownloadProgressChanged="MediaElement_OnDownloadProgressChanged" BufferingProgressChanged="MediaElement_BufferingProgressChanged"
AreTransportControlsEnabled="True" CurrentStateChanged="MediaElement_CurrentStateChanged" />
I have also defined Audio as Supported task Types in Package.appxmanifest
public VideoPlayer()
{
InitializeComponent();
#region SystemMediaTransportControls
// Hook up app to system transport controls.
systemControls = SystemMediaTransportControls.GetForCurrentView();
systemControls.ButtonPressed += SystemControls_ButtonPressed;
// Register to handle the following system transpot control buttons.
systemControls.IsPlayEnabled = true;
systemControls.IsPauseEnabled = true;
systemControls.IsStopEnabled = true;
systemControls.IsEnabled = true;
#endregion
_navigationHelper = new NavigationHelper(this);
_navigationHelper.LoadState += NavigationHelper_LoadState;
_navigationHelper.SaveState += NavigationHelper_SaveState;
}
void SystemControls_ButtonPressed(SystemMediaTransportControls sender, SystemMediaTransportControlsButtonPressedEventArgs args)
{
switch (args.Button)
{
case SystemMediaTransportControlsButton.Play:
PlayMedia();
break;
case SystemMediaTransportControlsButton.Stop:
StopMedia();
break;
case SystemMediaTransportControlsButton.Pause:
PauseMedia();
break;
default:
break;
}
}
private async void StopMedia()
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
MediaElement.Stop();
});
}
async void PlayMedia()
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
MediaElement.Play();
});
}
async void PauseMedia()
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
MediaElement.Pause();
});
}
private void MediaElement_CurrentStateChanged(object sender, RoutedEventArgs e)
{
if (Debugger.IsAttached)
{
Debug.WriteLine("MediaElement.CurrentState: " + MediaElement.CurrentState);
}
switch (MediaElement.CurrentState)
{
case MediaElementState.Playing:
systemControls.PlaybackStatus = MediaPlaybackStatus.Playing;
break;
case MediaElementState.Paused:
systemControls.PlaybackStatus = MediaPlaybackStatus.Paused;
break;
case MediaElementState.Stopped:
systemControls.PlaybackStatus = MediaPlaybackStatus.Stopped;
break;
case MediaElementState.Closed:
systemControls.PlaybackStatus = MediaPlaybackStatus.Closed;
break;
default:
break;
}
}
Windows Phone doesn't use the same mechanism for Background Audio as Windows does, primarily because low-spec phones don't have enough resources to run two apps at once.
Instead, Windows Phone uses a dedicated background process to play music in the background. See the BackgroundMediaPlayer for more information on how to do this in a Windows Runtime phone app.
I'm having issues with the new SystemMediaTransportControls that replace MediaControl.
Currently, I have my app set up with:-
systemControls = SystemMediaTransportControls.GetForCurrentView();
systemControls.IsPlayEnabled = true;
systemControls.IsStopEnabled = true;
systemControls.IsPauseEnabled = true;
systemControls.ButtonPressed += SystemControls_ButtonPressed;
And
async void SystemControls_ButtonPressed(SystemMediaTransportControls sender, SystemMediaTransportControlsButtonPressedEventArgs args)
{
System.Diagnostics.Debug.WriteLine(args.Button);
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
switch (args.Button)
{
case SystemMediaTransportControlsButton.Play:
if (mediaElement1.CurrentState != MediaElementState.Playing)
{
restartSource();
}
else
{
completeClosure();
}
break;
case SystemMediaTransportControlsButton.Pause:
case SystemMediaTransportControlsButton.Stop:
completeClosure();
break;
default:
break;
}
});
}
And:
private async void completeClosure()
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
mediaElement1.Stop();
mediaElement1.Source = null;
timer.Stop();
});
}
private async void restartSource()
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
mediaElement1.Source = new Uri(holdThisSource, UriKind.Absolute);
mediaElement1.Play();
timer.Start();
});
}
When a user presses the Pause Button, args.Button shows up as "Play", hence the need for the checking for MediaElement's state. However, when I attempt to resume to media, it successfully resumes in restartSource() and updates the app accordingly but the icon on the Volume Control does not change from the Play sign, although hardware buttons still work.
Along with this, pressing the hardware Stop button NEVER works, and fails to even show up in Debug.WriteLine.
This is an online streaming app where the source does not allow resuming and thus I have to close the stream this way.
I'd love some help on this.
Since you did not update the systemControls.PlaybackStatus, the control button on transport control will not auto change to correct status.
You should always update the systemControls.PlaybackStatus property when the playback state has changed.
May this could solve your problems.
I'm currently developing a web radio app and if the user presses the pause/stop key the stream should stop and of course when he presses play again the stream should continue.
The problem I have is, that player.Stop() only pauses the track. If you press continue again, the first 5 secounds are not read from the stream but from a buffer, then it playes no sound for a few secounds and then begins to read from the stream again.
This is fatal for a web radio app. How can I fix it? Or how can I delete the buffer?
protected override void OnPlayStateChanged(BackgroundAudioPlayer player, AudioTrack track, PlayState playState)
{
switch (playState)
{
case PlayState.TrackReady:
player.Play();
break;
case PlayState.Stopped:
player.Stop();
break;
case PlayState.Paused:
player.Stop();
break;
}
NotifyComplete();
}
Allright.. just to let you guys know, this is what I did. I don't know if there's any better way but this works for me:
protected override void OnPlayStateChanged(BackgroundAudioPlayer player, AudioTrack track, PlayState playState)
{
switch (playState)
{
case PlayState.Stopped:
track.BeginEdit();
track.Tag = track.Source.OriginalString;
track.Source = new Uri("http://127.0.0.1", UriKind.Absolute);
track.EndEdit();
player.Track = track;
break;
protected override void OnUserAction(BackgroundAudioPlayer player, AudioTrack track, UserAction action, object param)
{
switch (action)
{
case UserAction.Play:
if (player.PlayerState != PlayState.Playing)
{
try
{
player.Play();
}
catch(Exception)
{
track.BeginEdit();
track.Source = new Uri(player.Track.Tag, UriKind.Absolute);
track.EndEdit();
player.Track = track;
player.Play();
}
}
break;
case UserAction.Stop:
case UserAction.Pause:
if (player.Track.Source.OriginalString != "http://127.0.0.1/")
{
player.Stop();
}
break;
How about ditching everything when you stop, and newing it up again when playback starts again? It seems the functionality you are using is more suited to pausing static content. Pause doesn't make much sense with radio.