WPF: Implementing a MediaPlayer Audio / Video Seeker - c#

I am currently working on an MP3 player (in a WPF application) with a WPF MediaPlayer and basically, I want to implement a Song Seeker which moves along with the current playing song.
I already implemented a song slider (from Sacha Barber's application) and it works when the user drags the seeker manually (as in, the song continues from that position) but I cannot figure out how to make the seeker move according to the current position in the song.
Trouble is I don't think there is a way to check when the Position property of the MediaPlayer has changed, so I'm stumped as to how I should implement this feature.
Any ideas on how to go about such an issue?
[Update]
As regards incrementing the seeker with a timer, I actually thought of using the reason I didn't try it yet is because I think there is a better way to implement this using the MediaTimeline...but I'm yet to figure out how.

ARISE answer! and serve your master
OK, I've figured out how to work this. I'm sure I'm not doing it the completely correct way but it does work.
Here is the code-behind of a WPF application, with a Pause/Play button.
public partial class Main : Window
{
MediaPlayer MPlayer;
MediaTimeline MTimeline;
public Main()
{
InitializeComponent();
var uri = new Uri("C:\\Test.mp3");
MPlayer = new MediaPlayer();
MTimeline = new MediaTimeline(uri);
MTimeline.CurrentTimeInvalidated += new EventHandler(MTimeline_CurrentTimeInvalidated);
MPlayer.Clock = MTimeline.CreateClock(true) as MediaClock;
MPlayer.Clock.Controller.Stop();
}
void MTimeline_CurrentTimeInvalidated(object sender, EventArgs e)
{
Console.WriteLine(MPlayer.Clock.CurrentTime.Value.TotalSeconds);
}
private void btnPlayPause_Click(object sender, RoutedEventArgs e)
{
//Is Active
if (MPlayer.Clock.CurrentState == ClockState.Active)
{
//Is Paused
if (MPlayer.Clock.CurrentGlobalSpeed == 0.0)
MPlayer.Clock.Controller.Resume();
else //Is Playing
MPlayer.Clock.Controller.Pause();
}
else if (MPlayer.Clock.CurrentState == ClockState.Stopped) //Is Stopped
MPlayer.Clock.Controller.Begin();
}
}
The trick is that once you set the clock of a MediaPlayer, it becomes clock controlled, thus the use of MPlayer.Clock.Controller to do all of the controlling :)

Never played with media player but assuming you know the length of song could you not setup a timer that ticks every second while the song is playing. Therefore for every tick just increment the seeker in relation to how long the song is in total.
Song is 100 seconds long. Therefore every second/tick is worth 1 percent of total progress.
You'd have to stop the timer when pausing song etc...

MediaElement has a position property which you could use for this: http://msdn.microsoft.com/en-us/library/system.windows.controls.mediaelement.position.aspx

Have you checked out the WPF MediaKit yet?

Related

Windows Media Player - Hide player after it is done playing

I am creating a game, I want to display a short tutorial video to the player after they have registered. I am using a windows media player control. I don't know how to hide the video after it has finished playing ?
I tried using the following:
WMP.Ctlcontrols.play();
Thread.Sleep(3000);
WMP.Dispose();
I am using the disposing as a way to close down the video. I tried hide and close as well but they close the video before it's finished playing, after 3 seconds.
You can handle PlayStateChange event of control and check if e.newState==1, it means the playing has been stopped. Then you can hide the control.
void axWindowsMediaPlayer1_PlayStateChange(object sender,
AxWMPLib._WMPOCXEvents_PlayStateChangeEvent e)
{
if(e.newState== 1) // Stopped
axWindowsMediaPlayer1.Hide();
}

How to show an array of images, without freezing the whole programm for seconds

I have the following problem:
I want to make a little 2d-game in c#.
To display a background that consists of several images. To make animated gifs possible, i used MediaElement objects.
I added these MediaElements to my Canvas.
But when i run my programm, the performance is a disaster. It takes almost half a minute to display the images.
I obviously need a better idea to display an array of images, without slowing down everything.
This is my Starting-Point:
class BackgroundMediaElement : MediaElement
{
public BackgroundMediaElement(string imageSourcePath, int rowInCanvas, int columnInCanvas, int lengthOfSquare)
{
this.UnloadedBehavior = MediaState.Manual;
this.Source = new Uri(imageSourcePath, UriKind.Relative);
Canvas.SetTop(this, rowInCanvas*lengthOfSquare);
Canvas.SetLeft(this, columnInCanvas*lengthOfSquare);
this.Play();
this.MediaEnded += method_MediaEnded;
}
private void method_MediaEnded(object sender, RoutedEventArgs e)
{
//MediaElement m = (MediaElement)sender;
this.Position = TimeSpan.FromMilliseconds(1);
}
}
I created more than 200 elements and added them to my Canvas.
But it even slows down with 40 elements.
One image does not even have one kByte.
This is simply not the technology you are looking for. You can use C# very well to create games, but you should be looking for an appropriate Game Engine. Unity supports 2D games and allows you to code in C#. Click here for more information.
Focus on creating games rather than fighting with irrelevant stuff :)

WPF MediaElement. Play not from beginning

I can't achieve simple thing. I'm using MediaElement and I want to play my video not from the beginning, but from, let's say, position 5 sec.
Workflow:
LoadedBehavior <- Manual;
Source <- some_source;
Play();
Inside MediaOpened:
Position <- 5 sec
Problem: For the few moments I see the beginning of the video and then it switches to 5 sec.
I know that if I want to control Position manually I need to use LoadedBehavior Manual, but how can I make the video be Paused immediately after loading?
The way I have done it and works is this (_location & _position I want are passed into the containing method of course)
MediaE2.Source = _location;
MediaE2.Position = _position;
//MediaE2.Source = new Uri(#"C:\Users\Public\Videos\Sample Videos\Wildlife.wmv", UriKind.Absolute);
MediaE2.Play();
Set the position first then play.
Jim

Label.Image gradually increase with for loop

Can't figure out why image in label1 connected with imageList1 doesn't want to change more than once after pressing of a mouse button. Imagelist consists of 7 images which I want to have gradually appear in label element...that was the whole idea.
private void button1_Click(object sender, EventArgs e)
{
int number = 0;
for (int i = 0; i < imageList1.Images.Count; i++)
{
label1.Image = imageList1.Images[number++];
}
}
The default ImageIndex in label1 properties is set to 0 (first image) and after the for loop it gets to index1.
I am guessing that the last image stays? If you want the images to appear one by one with a timeout you should do something like
private async void button1_Click(object sender, EventArgs e)
{
foreach (Image image in imageList1)
{
await Task.Delay(1000); //wait for one second before changing
label1.Image = image;
}
}
Of course depending on your requirements you may want to disable the button and as pointed out by #nvoigt you may want to use some animation capabilities of the UI framework.
Your form will only repaint once the whole button event ran. That means you will only ever see the last image. Look into background workers or maybe timers to have animation. Maybe WPF is the way to go if animation is the main purpose of your program.
Your loop does assign the images OK but there is no time to show them because updating the UI is not happening before the loop is through.
You could force the UI update by inserting an Application.DoEvents()
label1.Image = imageList1.Images[number++];
Application.DoEvents();
You can try it but you should not actually use this as your solution! It has two serious issues, none of which you want:
It gives you no control over the animation speed.
Application.DoEvents can introduce serious problems in your code and you should not get into the habit of using it at all. Look it up or just believe it!
The best way to do any animation in Winforms is to use a Timer. In the Button click you set it up and start it. In its Tick you do the animation..
Have alook at this post for a button animation example! Instead of Mouse_Enter use your button click. Stop the Timer when the images have all been shown!
If all you want to do is playing around a little getting used to Timers is highly recommended and there is no need at all for WPF. If you will need a lot of high class animation WPF is indeed the way to go.
Here is the code to a minimal solution:
Timer timer1 = new Timer();
int imageIndex = 0;
private void timer1_Tick(object sender, EventArgs e)
{
if (imageIndex >= imageList1.Images.Count ) timer1.Stop();
label1.Image = imageList1.Images[imageIndex++];
}
private void button1_Click(object sender, EventArgs e)
{
imageIndex = 0;
timer1.Interval = 100; // change ms to suit your needs!
timer1.Start();
}
There are various issues with your code.
By incrementing number inside the loop, you force it to have the same value as i. If that's what you want to do, why not simply use i?
You never let the UI thread update the display. The UI thread will update the display only after the event handler finishes.
The result is that only the last image will be displayed.
To allow the UI thread to update the display, you need to use Application.DoEvents, eg:
foreach(Image image in imageList1.Images)
{
label1.Image = image;
Application.DoEvents();
}
Of course this will just go through all the images at once, so you'll just see a blur.
If you want a simple, smooth animation, use an [animated GIF2 or use WPF. Trying to do this in Windows Forms with individual images is not straightforward.
If you want to show an animation, you can put a delay as #Stilgar suggests, although this won't guarantee a smooth animation. Thread switching or high CPU load means that the delay between images will always be greater than the delay amount. The result will be a jerky animation.
You can use a timer event to update the image. This is better, but high CPU load can still delay processing of the event. Only WPF can guarantee the animation will be smooth without complex coding.

MediaElement & DispatcherTimer in strictly timing application

I'm currently developing a WPF application which requires strictly timing, says, being late 2 seconds matters.
I have a MediaElement mediaPlayer which seeks to a new position and play every time a Dispatcher timer is fired. But I notice that the mediaPlayer.Position is not very synced with the timer. In the example below, I set the dispatcherTimer fired after 55 seconds, but the value received from MessageBox in timer_Tick is 108.276746, which is late 2 seconds (55 + 55 = 110).
private void button1_Click(object sender, RoutedEventArgs e)
{
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(55);
timer.Tick += new EventHandler(timer_Tick);
mediaPlayer.Source = new Uri("test.wma", UriKind.Relative);
_currentPosition = 55;
mediaPlayer.Position = TimeSpan.FromSeconds(_currentPosition);
timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
MessageBox.Show("Position" + mediaPlayer.Position.TotalSeconds);// print around 108 seconds
}
This is the problem because I need the mediaPlayer position is perfectly synced with the dispatcher timer.
For more information, the root problem here is: the dispatcher timer to strictly follow the mediaElement progress, because I need to sync other controls with the position that mediaPlayer. Being late 2 seconds is unacceptable. Does anyone know how to achieve this effect?
UPDATE PURPOSE: I'm trying to "switch illustration image" according to the playing position from an audio. For example, when the narrator read to "... We have a beautiful house" in the audio, the program will show pictures of a beautiful building. But now since the position is late, it will show the picture long before the audio mentions it.
As pointed out, it takes time for the media elements to load, therefore it is not wise to use a countdown timer. You should do the the other way round: the DispatchTimer fires say like 1 or 2 times every second, and when it's fired you check the position of the media element. If it's at a certain position then show the picture.
This approach also limits the maximum error do the time interval of your DispatchTimer events, assuming that the system fires them accurately.
I've used the media element a lot.
My suspicion is that the timer is fine ( give or take a few ms )
but the mediaplayer position is definitely guaranteed not to be where you expect it to be.
The 2 seconds could well be accounted for in loading the video file and loading the video/audio codecs. further more, if there is any lag at all ( cpu or ram spike or other ) the mediaplayer will also lag while the timer will not.
perhaps setup a scenario where the video is guaranteed to be loaded ( for example pausing it somewhere in the middle of the video ) then start the timer and play the video from there to check.
If you want a video player that doesn't have the MediaElement's slow initialization time for videos, try Jeremiah Morrill's MediaKit project. He has made some great improvements including load times. It is also open source so if you need more or information on where you're at in the video, you can add that to the source.

Categories

Resources