MediaElement & DispatcherTimer in strictly timing application - c#

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.

Related

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.

C# winform, how to make the track bar move (increase its value) along with a sound file?

in my form, I have a small media player, it's pretty basic, it has the basic functionalists
nest, previous, play, stop, volume increasing and a track bar.
I was able to seek the song by dragging the track bar via this code::
(Assuming that the maximum value of the track bar is the total number of seconds of the sound file)
private void SeekBar_Scroll(object sender, EventArgs e)
{
Player.Position = TimeSpan.FromSeconds(SeekBar.Value);
}
But I wasn't able to make the bar move forward by itself when the music is playing ..
I came up with this idea:
let's say we have a 90 secs duration song.
now the track bar maximum value is 100
so:
100 --> 90
1 --> X
X = 0.9 sec
this is the interval between the ticks of the bar and the value that the track bar value should be increased in every tick. Here is my code:
while (SeekBar.Value < 100)
{
System.Windows.Duration duration = Player.NaturalDuration;
SeekBar.Value += duration.TimeSpan.Seconds / 100;
Thread.Sleep(duration.TimeSpan.Seconds * 10);
}
Now I think this should be run in a separate thread right ?
but the problem, when i do that, I get the annoying message of saying that the TrackBar is being used in a thread other than the thread it was created on ..
how can i get around that ?
I wanted to put this code in both the MouseLeave and MouseEnter events, but it's pointless, cuz it will be on the same thread and the app will freeze..
How can i make the bar move by it self ?
You should create a WinForms Timer with an interval or 1,000 milliseconds, and update the trackbar in its Tick event.
It sounds like you have the hardest part done (mapping the length of the scroll bar to the length of the audio file). The rest is easy.
You can tackle this with a Timer. Set the interval to some value (such as 1000ms or maybe 500ms, and in Tick, update the scrollbar (call SeekBar_Scroll). For the exact interval, test and see what looks the most natural.
A while-loop will probably eat unnecessary CPU, not to mention lock up the rest of your application.
To get around the control updating, you need to check this SO question. You need to check control.InvokeRequired and set it via the Invoke method if that's the case.

How To Make Moving News Bar in Windows Forms Application without Timer

I'm making a desktop application in C# which contains a moving News Bar labels.
I'm using a timer to move these labels but the problem is that when i make the interval of this timer low (1-10 for example) the application takes very high percentage of CPU Usage, And when i make it higher(200 -500 ) the movement of the labels becomes intermittent or not smooth movement even that the user may not be able to read the news in Comfortable way.
((More Information))
it is Windows form application. the way i move the labels is as follows : the news items from RSS feeds are represented in a group of linklabels. All these linklabels are added to a flowlayout container. The timer moves the whole flowlayout container. I found this way according to my knowledge the best way to making the news bar. If you have better idea or solution please help.
What does the timer interval represent? If it's milliseconds, then you can divide the number of updates per second that you want into one thousand and get the timer rate.
You could also use Sleep(100) or so, but it may just be that you're trying to do too much in your update. Maybe you can do "important changes" much less frequently, like only every 100 updates or so, or put those on their own timer, and do as little as possible to update the scrolling much more frequently.
It's not surprising that your application takes a lot of CPU when you have it set to do its update 100 or 1000 times per second. :)
I suspect the problem is that you're using the timer to move the ticker as well as populate the data?
If you want to use a timer to scroll the view, that should be fine. Your code needs to be extremely light (just update the vertical or horizontal positions and return). A better approach, however, would be to use something like a "game loop" to achieve whatever update frequency you're after (within each iteration, time how long it takes to move the view, then Sleep for the number of milliseconds remaining to hit your target frequency.)
Update the data from a separate timer/thread.
Look : Drag One Label and One Timer
Set Timer Interval = 100
then :
private void timer1_Tick(object sender, EventArgs e)
{
label1.Location = new Point(label1.Location.X + 5, label1.Location.Y);
if (label1.Location.X > this.Width)
{
label1.Location = new Point(0 - label1.Width, label1.Location.Y);
label1.Text = "Your Message Here ";
}
}
private void Form1_Load(object sender, EventArgs e)
{
timer1.Start();
}
You can create a custom marquee label which uses a timer and redraw itself and animates the text from left to right or top to down, like this horizontal marquee label or this vertical marquee label.
But since you have asked about something without a timer, a good option is using a WebBrowser control showing a marquee tag, easy and flexible.
You can set any content for that, setup behavior, direction, speed, appearance, width, height and fully customize it.
Example
this.webBrowser1.DocumentText = #"
<marquee>
<span style='color:#f00;'>Breaking news: </span>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</marquee>";

WPF: Implementing a MediaPlayer Audio / Video Seeker

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?

How to force a redraw of my application's entry in the taskbar?

I have a Windows form application written in C#. I update the title of the form frequently, but there's a substantial lag between the title changing and the title dislayed in the taskbar being updated.
What's a clean way to force an update / redraw of the task bar's entry for my program? Failing that, how can I force a redraw of the entire task bar?
Elaboration: It turns out that the delay in updating the taskbar is fixed at about 100ms, however this seems to be a delay based on when the Form.Text was last modified. If you modify the text faster then that - say, every 10ms, the taskbar is not updated until the Form.Text has been left unchanged for at least ~100ms.
OS: Vista 32.
Did you try to call Form.Refresh() after updating the title?
Edit:
If you are doing the title updates in a loop you might have to do something along the line of:
this.Invalidate();
this.Update();
Application.DoEvents();
A task bar update more than every 100ms is going to be too fast for the user to resolve anyway. Presumably you're showing some sort of progress or status indicator to the user?
If so, you're crippling the app needlessly doing so many UI updates. That processing time is better used getting the customer's job done.
I think you need to revisit the UI design aspects of what you're trying to do.
I just did a simple test. The changes are quite instantaneous. From the look of it, it's definitely less than 500ms. If you need to update the title at a higher rate, I won't really recommend it. Generally I've seen the fastest update rate of twice per second.
EDIT:
I tested using keypress event. When I hold down the key for a fast repeat, it won't update until I've release my key. Thus, same scenario as your setup.
Btw, why do you need to update every 10ms? Just keep in mind that Thread.Sleep(timeout) with timeout of less than 50ms is not accurate. Also, 10ms timeout will equal to 100Hz, unless you're using high end display, you'll have miss a few frame. Most general LCD have a refresh rate of 60Hz. And our eye can't differentiate anything faster than 25Hz. Thus 40ms delay is more than enough, if you want to animate. Generally I would recommend 15Hz (67ms) for simple animation. If just want to scroll some text, 2Hz is more than enough. Anything faster will make the user dizzy.
Are you using code similar to this in your form?:
private void Form1_Load(object sender, EventArgs e)
{
Timer t = new Timer();
t.Interval = 10;
t.Tick += new EventHandler(t_Tick);
t.Start();
}
int aa = 0;
void t_Tick(object sender, EventArgs e)
{
this.Text = aa++.ToString();
}
Works fine for me - no lag between form and taskbar at all.
Are you sure you aren't locking up the GUI thread and not calling Application.DoEvents on your loop?
I'm using the new Windows 7 beta, so it's a small chance that it's different behavior.

Categories

Resources