Video player in Xamarin Forms is gone when minimized and resumed - c#

I have implemented a video player in xamarin forms to play as a background video for my login screen. The video successfully loads at start and plays without sound and loops(as configured). But the problem is when I minimize the app and resume again, then the video is gone and I can't even play it back like video.play if paused or stopped
The links I tried are as follows
http://makanda.io/how-to-create-a-background-video-in-xamarin-forms/ (I tried this link first)
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/custom-renderer/video-player/ (then completely replaced with this)
Both links work as desired except the issue which is the same in both links.
What I have tried to solve the issue?
I tried resuming the video as if(videoPlayer.Status == Renderers.VideoStatus.Paused) videoPlayer.Play() inside my OnAppearing() method. But it doesn't work
I am currently trying to dynamically load the video from code behind inside a stack layout. But I don't know how to set the video source from code behind. I am doing it as follows
string source = "";
switch (Device.RuntimePlatform)
{
case Device.iOS:
source = "Videos/walkthroughvideo9_16.mp4";
break;
case Device.Android:
source = "walkthroughvideo9_16.mp4";
break;
default:
source = "walkthroughvideo9_16.mp4";
break;
}
VideoPlayer video = new VideoPlayer()
{
Source = (UriVideoSource)source
};
An error says, cannot convert string to UriVideoSource
AN HELP LINK:
https://developer.android.com/reference/android/widget/VideoView
Seems like the video view does not maintain its state when the app goes into the background.

I am currently trying to dynamically load the video from code behind inside a stack layout. But I don't know how to set the video source from code behind. I am doing it as follows
I notice that your path is from local Resource, you can use the following code to load the video. If your path is an URL, you can use videoPlayer.Source = VideoSource.FromUri(source); to load it.
var videoPlayer=new VideoPlayer();
string source = "";
switch (Device.RuntimePlatform)
{
case Device.iOS:
source = "Videos/iOSApiVideo.mp4";
break;
case Device.Android:
source = "AndroidApiVideo.mp4";
break;
default:
source = "AndroidApiVideo.mp4";
break;
}
videoPlayer.Source = VideoSource.FromResource(source);
I tried resuming the video as if(videoPlayer.Status == Renderers.VideoStatus.Paused) videoPlayer.Play() inside my OnAppearing() method. But it doesn't work
Here is my running demo result, when I am back to my application, it works. I also achieve the seek to the previous time span function as well.
Here is my demo.
https://drive.google.com/file/d/1kpYbMV5mA3UdbGD7r6tLv1S2ix7AwM5B/view?usp=sharing

I used a timer as a workaround to resume the video which pauses when the app goes into sleep mode.
protected override void OnAppearing()
{
base.OnAppearing();
shouldTimerRun = true;
Device.StartTimer(new TimeSpan(0, 0, 1), () =>
{
Device.BeginInvokeOnMainThread(() =>
{
if (videoPlayer.Status == VideoStatus.Paused)
{
//videoPlayer.TimeToEnd = timeSpan;
videoPlayer.Position = timeSpan;
videoPlayer.Play();
}
});
return shouldTimerRun;
});
}
protected override void OnDisappearing()
{
base.OnDisappearing();
shouldTimerRun = false;
timeSpan = videoPlayer.Position;
}

Related

How can I pop a navigation page when a value changes in a static class?

So I have a bit of a weird problem.
I've implemented a camera preview class (largely following this code here: https://learn.microsoft.com/en-us/samples/xamarin/xamarin-forms-samples/customrenderers-view/) but have added a button at the bottom to take a picture. This involves the use of both some xamarin forms code and some xamarin android code.
However, the CapturePage is only put on the stack when the user announces that they want to take a photo, and after the photo has been taken, I want to pop the Capture page to go back to the main screen. Currently, I have a static boolean value in the overall project that is changed from false to true when a capture has occurred. Is there some way to get my code in Main.xaml.cs to wait on this value changing, then pop whatever is on top of the navigation stack? Is this a use for a property changed? See code below:
The code in Project.Droid that handles the capturing and saving of the image:
void OnCapButtonClicked(object sender, EventArgs e)
{
// capButton.capture is an instance of Android.Hardware.Camera
capButton.capture.TakePicture(null, null, this);
// stop the preview when the capture happens
CameraInfoContainer.isPreviewing = false;
}
public void OnPictureTaken(byte[] data, Camera camera)
{
var filepath = string.Empty;
var clientInstanceId = Guid.NewGuid().ToString();
var saveLoc = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures);
filepath = System.IO.Path.Combine(saveLoc.AbsolutePath, clientInstanceId + ".jpg");
try
{
System.IO.File.WriteAllBytes(filepath, data);
//mediascan adds the saved image into the gallery
var mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
mediaScanIntent.SetData(Android.Net.Uri.FromFile(new File(filepath)));
Forms.Context.SendBroadcast(mediaScanIntent);
}
catch (Exception e)
{
System.Console.WriteLine(e.ToString());
}
// CameraInfoContainer is a static class in Project NOT in Project.Droid
CameraInfoContainer.savedCapture = filepath;
CameraInfoContainer.capType = CaptureType.Photo;
CameraInfoContainer.captureComplete = true; // here is where I set the value (in Project)
}
Now the code in Project that pushes the capture page on the stack and that I want to trigger when the capture has happened:
// this method puts the capture page on the stack and starts the whole process
private async Task ExecuteNewCapture()
{
var cp = new CapturePage();
var np = new NavigationPage(cp);
await Navigation.PushModalAsync(np, true);
}
// this is the method that I want to trigger when a photo is taken (in Project/Main.xaml.cs)
private async Task Complete(string fileLoc)
{
await Navigation.PopModalAsync();
}
Answer is in a comment from Junior Jiang. Ended up using Xamarin.Forms.MessagingCenter to get done what I needed.

C# UWP, reusing geofences in geofencemonitor

I'm developing UWP app which will assist people with public transportation in my city. It uses geofencing to notify when the device is near some station. All is working fine, but slow. On my lumia 830 it took 4-5 seconds to prepare data for all 250 geofences and create them. Since, geofencemonitor runs under system and can store my geofences even when my app is terminated, I thought it would be smart to not recreate all geofences again and again on every app startup, but create them once and then just reuse them.
But I cant figure out how. When I start my app, Geofencemonitor.Current.Count is 250 and GeofenceMonitor.Current.Status is Ready, but it never fires Geoface state changed event.
My problem:
I m trying to reuse geofences in geofencemonitor. When I run my app and during startup I dont call Geofencemonitor.Current.Geofences.Clear(); and then create and add all my geofences -> geofence state change event is never fired.
Edit: It seems, that geofence status change fires only once after I add geofence to geofencemonitor, independently on if the app was restarted or not. But "used" geofence still remains in geofence monitor. Does geofence have some supended state? Am I missing some geofence property?
Here is how I create geofence:
var geofence = new Geofence(id, geocircle, mask, false, dwellTime);
GeofenceMonitor.Current.Geofences.Add(geofence);
My questions:
"Since, geofencemonitor runs under system and can store my geofences even when my app is terminated" - Is this really true?
If my first questions is true, and it is possible to reuse geofences, what am I doing wrong or do you know some place where I can go deeper into Geofencing in UWP?
Thanks for advice.
Here is how I listen geofences state change event:
ItsValueIs250 = Geofencemonitor.Current.Geofences.Count;
ItsValueIsReady = Geofencemonitor.Current.Status;
public async void OnGeofenceStateChanged(GeofenceMonitor sender, object e)
{
var reports = sender.ReadReports();
await Dispatcher.DispatchAsync( () =>
{
foreach (GeofenceStateChangeReport report in reports)
{
GeofenceState state = report.NewState;
Geofence geofence = report.Geofence;
if (state == GeofenceState.Entered)
{
stationInRange.Add(geofence.Id);
StationInRangeCount = stationInRange.Count().ToString();
}
else if (state == GeofenceState.Exited)
{
stationInRange.Remove(geofence.Id);
}
}
});
}
And maybe this method which I call during startup.
async public Task<string> GeomonitorInitialize()
{
string message = string.Empty;
var accessStatus = await Geolocator.RequestAccessAsync();
switch (accessStatus)
{
case GeolocationAccessStatus.Allowed:
int i = 0;
i = GeofenceMonitor.Current.Geofences.Count;
return message;
case GeolocationAccessStatus.Denied:
message = "GeoDenied";
return message;
case GeolocationAccessStatus.Unspecified:
message = "GeoError";
return message;
default:
message = "Default";
return message;
}
}
I will add any piece of code I use as you will wish. Thank you.

The SystemMediaTransportControls were initialized in a different mode error in mytoolkit

When i run mytoolkit after background audio player start is return "The SystemMediaTransportControls were initialized in a different mode. Please use the API associated with that mode to complete this operation" Exception.
var url = await MyToolkit.Multimedia.YouTube.GetVideoUriAsync(ID, MyToolkit.Multimedia.YouTubeQuality.Quality480P);
it works fine when background audio player not started. please help me i cant find where is the error, is there any solution for that...
In your code for background audio player use
var systemMediaControls = SystemMediaTransportControls.GetForCurrentView();
systemMediaControls.ButtonPressed += systemMediaControls_ButtonPressed;
systemMediaControls.IsPlayEnabled = true;
systemMediaControls.IsPauseEnabled = true;
systemMediaControls.IsNextEnabled = true;
systemMediaControls.IsPreviousEnabled = true;
async void systemMediaControls_ButtonPressed(SystemMediaTransportControls sender, SystemMediaTransportControlsButtonPressedEventArgs args)
{
switch (args.Button)
{
case SystemMediaTransportControlsButton.Play:
break;
}
}
instead of
MediaControl.PlayPressed += MediaControl_PlayPressed;
MediaControl.PausePressed += MediaControl_PausePressed;
Control.
I think it will help for you..
Did you follow the directions as this guy did for windows 8 and not for 8.1?
"Do NOT use the MediaControl static class to attach handlers for you Windows 8.1 app or you will get the error message"

axWindowsMediaPlayer: Black Screen on looping

I have created a Windows Form Application having axWindowsMediaPlayer control. I haven't created a playlist on it, but I have stored my .mp4 files at a particular location. I pass the path to my next video at Media Ended state. For the first time, the player receives the correct path and play. But for the second video, I can only see a black screen although the player is receiving the correct path to play.
Here is my code for Media Ended State:
private void axWindowsMediaPlayer_PlayStateChange(object sender, AxWMPLib._WMPOCXEvents_PlayStateChangeEvent e)
{
if(e.newState == 8)
{
//Getting jumpTo of selected page
var selectedElementJumpToValue = MainContentAreaBl.GetSelectedElementValue(_currentId, "jumpTo");
if (selectedElementJumpToValue != null)
{
_currentId = selectedElementJumpToValue;
if (_currentId != "menu")
{
pagination.Text = MainContentAreaBl.GetPaginationText(_currentId);
LaunchPlayer(selectedElementJumpToValue);
}
else
{
this.Hide();
this.SendToBack();
menu.BringToFront();
}
}
}
}
private void LaunchPlayer(string id)
{
string selectedElementPageTypeValue = MainContentAreaBl.GetSelectedElementPageTypeValue(id);
var playerFile = Path.Combine(Common.ContentFolderPath, MainContentAreaBl.GetSelectedElementDataPathValue(id));
if (selectedElementPageTypeValue == "video")
{
InitialiseMediaPlayer();
axShockwaveFlash.Stop();
axShockwaveFlash.Visible = false;
if (File.Exists(playerFile))
{
axWindowsMediaPlayer.URL = playerFile;
}
}
else if (selectedElementPageTypeValue == "flash")
{
InitialiseShockwaveFlash();
axWindowsMediaPlayer.close();
axWindowsMediaPlayer.Visible = false;
if (File.Exists(playerFile))
{
axShockwaveFlash.Movie = playerFile;
axShockwaveFlash.Play();
}
}
}
private void InitialiseMediaPlayer()
{
axWindowsMediaPlayer.Visible = true;
axWindowsMediaPlayer.enableContextMenu = false
axWindowsMediaPlayer.uiMode = "none";
axWindowsMediaPlayer.Dock = DockStyle.Fill;
}
When I debugged my application, I saw Media Player getting the correct path after e.newState == 10 (Ready state). What am I doing wrong?
Edit 1: I found out that after my current video enters into Media Ended state, the player is stopped from playing. Even if I write axWindowsMediaPlayer.ctlControls.play();, it doesn't affect the media player. Is this a bug from in axWindowsMediaPlayer?
I have encountered this issue before as well. The most likely cause is that you are giving the command axWindowsMediaPlayer.ctlControls.play(); while the play state is still changing ( after Media Ended, it will change to Ready state). If a command is sent to the player while the play state is changing, it won't do anything. Another possible cause of your error is that sometimes Media State 9 (transitioning) needs to be included with if(e.newState == 8) such that you have if(e.newState == 8 | e.newState==9). I have had cases where it doesn't pick up on State 8 (media ending), possibly because it happens really fast and jumps to transitioning - not completely sure of this cause but I had a player that wasn't moving to the next video in the playlist because of this happening. To solve this I did something like:
if (e.newState == 8 | e.newState== 9 | e.newState== 10)
{
if (e.newState == 8)
{ //Insert your code here
}
This would vary slightly depending on what you are trying to achieve. Another thing to watch out for is using the PlayStateChange Event to set the Video URL, this causes problems as a result of re-entry problems with WMP - see other posts for further explanation on my last comment:
here is a good one and another here. Hope this helps!

Pause/Play MP3 file in C# using WMP

I am once again a bit stuck in my practising.
I want an MP3 file to play when i open my program - I can do that, i got music.
I also want a checkbox which allows to pause the music - But either I'm very tired, or the thing won't work - Nothing happens when i check/uncheck it. I've done it like this:
public void PlayPause(int Status)
{
WMPLib.WindowsMediaPlayer wmp = new WMPLib.WindowsMediaPlayer();
switch (Status)
{
case 0:
wmp.URL = "Musik.mp3";
break;
case 1:
wmp.controls.play();
break;
case 2:
wmp.controls.pause();
break;
}
}
Upon opening the program, the method is called with case 0. Music plays. All good.
However this doesn't work, and i don't get why, as it is pretty simple code.
public void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked == true)
{
PlayPause(2);
}
else if (checkBox1.Checked == false)
{
PlayPause(1);
}
}
Any idea as to why checking the checkbox doesn't pause/unpause the music?
You're instantiating a completely new WindowsMediaPlayer object each time you call that PlayPause function.
Thus, when you call pause later on, you're pausing nothing.
You need to hold or pass a reference to that WMP object around, so that you're operating on the same one.
Well it's because you are creating a new media player every time you call PlayPause. Create it in the constructor and it should be fine.

Categories