Changing MediaElement source without Flicker - wpf

I have a simple video player that plays a series of videos using the WPF MediaElement. The videos together form one continuous film that move around a still image. At the end of each video the movement freezes on the final frame of the currently playing video. When I press a button the next video plays, which continues the movement around the still image. It's an application I'm going to use to give a speech. Effectively I've got a series of videos for which the last frame of each video is the same as the first frame of the next video.
I'm using a WPF MediaElement and changing the Source property when the user clicks on the mouse.
The problem that I have is that, when I change the Source property, the MediaElement becomes transparent while the next video is loaded. This means there is a flicker between videos. Is there any way of preventing this flicker? What other strategies could I use?
Here's some code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.x_MediaElement.MouseLeftButtonDown += x_MediaElement_MouseLeftButtonDown;
this.MouseLeftButtonDown += MainWindow_MouseLeftButtonDown;
this.WindowStyle = WindowStyle.None;
this.WindowState = WindowState.Maximized;
}
void MainWindow_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
MoveNext();
}
private void MoveNext()
{
_sourceIndex++;
if (_sourceIndex >= _sources.Length)
_sourceIndex = 0;
Debug.WriteLine(string.Format("Playing {0}", _sources[_sourceIndex]));
this.x_MediaElement.Source = new Uri(_sources[_sourceIndex]);
this.x_MediaElement.Play();
}
private int _sourceIndex = -1;
private string[] _sources = new string[] {
//SOURCE GO HERE
};
void x_MediaElement_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
MoveNext();
e.Handled = true;
}
}

I am gonna be honest with you. MediaElement has more bugs than you can count with fingers. Starting from that the mediaElement blows up after playing 20 videos(no more MediaEnded event, it will crash or something like that). And ofcourse the performance. Its not synchronized with vertical sync. So the video might actually seem laggy.
I advise you to look into DirectShow technology(essentially what WPF is based on, but you can switch renderer which will avoid lag). COnsidering that you will not be developing any professional application, I guess MediaElement will be fine.
However, MediaElement is the simplest option, and if it works for you, then keep working with it. As for your problem, I think there are few possible solutions:
Have two MediaElements and switch between them. If one video ends, start another vid in another MediaElement, as long as you play first frame on second mediaElement, hide the first mediaElement, and vice versa. You can poll for position, and maybe MediaStarted event. This way the flicker will be almost impossible to notice.
If you want fluent video playing without ANY flicker at all, there is GMFPlay. You can check it out. Though it's not MediaElement. But it can play videos simultaneously without any flicker.
Take screenshot of the last frame(you can take screenshots with WPF) and show it as Image while MediaElement is secretly loading.

Related

Overlay WinForms controls on DirectX content

I'm trying to create a graphics library with graphics control. The control enables adding overlay controls. The graphics on the control uses DirectX and is part of the library, while the overlay controls are provided by end users of the library which are not graphics experts. Thus stability and convenient API are the most important issues.
For technical reasons I need to show the graphics by DirectX directly on the control and cannot host the DirectX scene using another framework as WPF (For more information see my previous question: Stereoscopic 3D on WPF).
I think the most problematic issues are:
Transparency (and semi-transparency...) within the area of the control itself.
Animations on the overlay control: That's why control rasterization (e.g. by WPF) is not an option.
My tries until now:
Following the lot of posts discussed similar issues I decided to use WS_EX_TRANSPARENT. I was surprised to see that although the Airspace issue, I can see the DirectX content under the transparent regions of the overlay control. However the overlay control was not shown except for when I resized the window - then it blinks and disappears again. Here is the code of the overlay control:
class GDIGraphicsControl : UserControl
{
private const int WS_EX_TRANSPARENT = 0x20;
protected override CreateParams CreateParams
{
get
{
CreateParams p = base.CreateParams;
p.ExStyle |= WS_EX_TRANSPARENT;
return p;
}
}
public GDIGraphicsControl()
{
this.Dock = DockStyle.Fill;
}
protected override void OnPaintBackground(PaintEventArgs e)
{
// Do nothing
}
protected override void OnPaint(PaintEventArgs e)
{
for (int i = 0; i < 10; ++i)
{
int alpha = 25 * i;
int yPos = 10 * i;
e.Graphics.FillRectangle(
new SolidBrush(Color.FromArgb(alpha, Color.Green)),
5, 5 + yPos, 100, 10);
}
e.Graphics.FillEllipse(new SolidBrush(Color.Red), 110, 5, 100, 100);
}
}
To analyze the blinking problem I tried to make things simpler. First for debugging purpose I tried to use GDI rendering instead of DirectX. I implemented it such that it will be very similar to the DirectX rendering - especially the Form.SetStyle and the Invalidate() call in every OnPaint() which I thought to be the cause to the problem. The problem do exist also in GDI-only rendering. Here is the GDI-rendering parent control code:
public partial class RenderingForm : Form
{
public RenderingForm()
{
InitializeComponent();
ControlStyles styles =
ControlStyles.AllPaintingInWmPaint |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.ResizeRedraw |
ControlStyles.UserPaint |
ControlStyles.Opaque;
this.SetStyle(styles, true);
Button button = new Button()
{
Text = "Just a button",
Left = 5,
Top = 210,
Width = 200
};
this.Controls.Add(button);
this.Controls.Add(new GDIGraphicsControl());
}
protected override void OnPaintBackground(PaintEventArgs e)
{
// Do nothing
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.Clear(Color.Blue);
this.Invalidate();
base.OnPaint(e);
}
}
I tried to add a simple overlay Button to check if the problem exist when no transparency is used (see in the above code). The button doesn't blink, but instead of the button I see garbage on the screen until resizing the control first time - then the button appears correctly.
I tried to invalidate the child overlay controls but it has no effect. I tried to invalidate it both in parent's OnPaint and in the child OnPaint to create the message-pump rendering effect and it has no effect. Setting the control's style as in parent rendering control didn't resolve the problem and caused the background to blink in black.
I also performed some tries using WPF but the question is long enough for now without hard Airspace issue...
Now for the questions:
Can someone explain how does GDI overlay over DirectX graphics work? Especially I don't understand it because I know that WinForms transparency is working by that the children controls render on the parent's Device Context - and DirectX has a hardware rendering context. Does it mean that the DirectX texture is copied back to software?
Why does overriding the CreateParams causes the Control to blink and how to prevent it?
What are performance cost do those transparency methods (overriding CreateParams, BackColorTransparency = True, Control.SetStyle+override OnPainBackground)? It is of high importance that the underlying graphics will be rendered efficiently but I don't care about the overlay performance (except for lightweight animation).
I've done a LOT of research in this area and I've come to the conclusion that Airspace is the way to go. I've collected sources from numerous projects around the web and put them into a single project that allows you to integrate WPF with XNA. I've also done a version of this that was pure DirectX, but to be honest the XNA version is a lot more straightforward and better suited to C#. I don't have enough room to post all the details here but you can download the demo from my website and probably figure out where to go from there. Good luck!

How to make label transparent without any flickering at load time

I have a panel and on that I've a picturebox. There are around 20 labels that I've to show in the panel. I want the background of Label to be transparent ie the image in picturebox is shown and the label displays only the text.
Now since labels do not exhibit true transparency I made the labels child of picturebox
this.lbl1.Parent = pictureBox1;
This has solved my immediate problem but now when the form loads, all the labels take a while to become visible and do so one at a time. I'd appreciate if you guys can give some solution for this.
Thanks in advance
The standard cure for flicker is double-buffering. But that cannot solve this kind of flicker. It is a different kind, caused by having multiple windows overlapping each other. Each label is its own window. When the form needs to paint itself, it draws its background leaving holes for the child windows. Each child window then takes a turn drawing itself. And their child windows draw themselves next. Etcetera.
This becomes noticeable when one control takes a while to draw, no doubt your picture box. Especially when it displays a large image that needs to be resized. The holes for the child windows stay unpainted while the picture box draws. They have a white background, black when you use the form's TransparencyKey or Opacity property. This can contrast badly with the image in your picture box, that effect is perceived by the user as flicker.
One immediate cure is to not use controls so you don't pay for their window. A Label is very convenient but it is a massive waste of system resources to burn up a window just to display a string. You can simply implement the picture box' Paint event and draw the strings with TextRenderer.DrawText(). PictureBox has double-buffering turned on by default so the image as well as the text is drawn completely smoothly, no more flicker. The obvious disadvantage is that you lose the convenience of point-and-click, you have to write code.
There are other fixes possible. One of them is to prevent the picture box from leaving holes for the child windows. It will draw the entire image, the labels pop on top of them. That's still flicker but not nearly as noticeable. Add a new class to your project and paste this code:
using System;
using System.Windows.Forms;
internal class MyPictureBox : PictureBox {
protected override CreateParams CreateParams {
get {
var parms = base.CreateParams;
parms.Style &= ~0x02000000; // Turn off WS_CLIPCHILDREN
return parms;
}
}
}
Compile and drop the new picture box control from the top of the toolbox onto your form.
Yet another possible workaround is to make the form and all of its children double-buffered. This doesn't speed up the painting at all but all of the windows get rendered into a memory buffer, the result is blitted to the screen. You'll notice a delay but the window suddenly pops on the screen. This is called compositing. Winforms doesn't support this directly since it can have side-effects but it is easy to enable. Paste this code into your form class:
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}
Supported by XP and later. Watch out for painting artifacts.
or you can ditch the labels and draw the text yourself:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
TextRenderer.DrawText(e.Graphics, "Label1", SystemFonts.DefaultFont,
new Point(10, 10), Color.Black, Color.Empty);
}
The label does not support transparency, you must create your own unique custom control, you can see these code examples.
http://www.codeproject.com/KB/dotnet/transparent_controls_net.aspx http://www.codeproject.com/KB/vb/uLabelX.aspx
Bye

Adding a minimum display time for Silverlight splash screen

When hosting a silverlight application on a webpage it is possible to use the splashscreensource parameter to specify a simple Silverlight 1.0 (xaml+javascript) control to be displayed while the real xap file is downloaded, and which can receive notification of the downloads progress through onSourceDownloadProgressChanged. If the xap file is in cache, the splash screen is not shown (and if the download only takes 1 second, the splash screen will only be shown for 1 second).
I know this is not best practice in general, but I am looking for a way to specify a minimum display time for the splash screen - even if the xap cached or the download is fast, the splash screen would remain up for at least, let's say, 5 seconds (for example to show a required legal disclaimer, corporate identity mark or other bug).
I do want to do it in the splash screen exclusively (rather then in the main xap) as I want it to be clean and uninterupted (for example a sound bug) and shown to the user as soon as they open the page, rather then after the download (which could take anywhere from 1 to 20+ seconds).
I'd prefer not to accomplish this with preloading - replacing the splash screen with a full Silverlight xap application (with it's own loading screen), which then programmably loads and displays the full xap after a minimum wait time.
Its a little known fact that the splash screen remains in place beyond the time that XAP takes to load. It doesn't get replaced until the application RootVisual loads. Hence if you don't assign the RootVisual in the application Startup event the splash screen displays forever.
Hence you can delay the display of the splash for a few seconds using code like this:-
private void Application_Startup(object sender, StartupEventArgs e)
{
var timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(10);
EventHandler eh = null;
eh = (s, args) =>
{
timer.Stop();
this.RootVisual = new Test();
timer.Tick -= eh;
};
timer.Tick += eh;
timer.Start();
}
This can be simplified with the Reactive framework:-
private void Application_Startup(object sender, StartupEventArgs e)
{
Observable.Timer(TimeSpan.FromSeconds(10), Scheduler.Dispatcher)
.Subscribe((l) =>
{
this.RootVisual = new Test();
});
}
However the Reactive framework adds at least 66KB to the size of your Xap so only use it you are already using the Reactive stuff for other things.
Note that if you were only interested in extending the display time of the splash screen during development then it is very simple to add a Fiddler rule which delays the response off the Xap file.
This is an example of a rule that would have this effect. See Pedro Forte's post for details on how to apply the rule - it is really very easy!
if (oSession.uriContains("Midwinter.ReasonableBasis.Client.xap")){
oSession["ui-color"]="orange";
oSession["ui-bold"]="true";
//Delay received data by X ms per KB downloaded.
oSession["response-trickle-delay"] = "10";
}

How can I change the way InkCanvas draws?

I've searched for examples for this, but the ones I've ran across seem to focus on simpler stuff like setting the InkCanvas DefaultDrawingAttributes such as Width, Height, Color etc. Doesn't seem like there's a lot of material for this.
For example, if I hold down the mouse button I can see it drawing lines. What if I want to draw ellipses instead of lines, or draw ellipses around sampled points between the start and end of the line?
I know I can get new points with the StrokeCollected event, but beyond that I have no idea where to go. This guy seemed like he got msdn's code working, but I couldn't do it. I only know how to build the interface using XAML, and there doesn't seem to be a sample either.
edit
Created a StrokeCollection class variable called thisIsNotNice, initialized in the constructor and did this:
private void InkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
{
myInkCanvas.Strokes = thisIsNotNice;
foreach (StylusPoint p in e.Stroke.StylusPoints)
{
StylusPointCollection spc = new StylusPointCollection();
spc.Add(p);
Stroke s = new Stroke(spc);
s.DrawingAttributes.Height = 3;
s.DrawingAttributes.Width = 3;
thisIsNotNice.Add(s);
}
e.Handled = true;
}
But it doesn't work as it should. The ellipses are drawn, but the lines drawn by the mouse are still there. Also, for some reason, the first time it works as it should, drawing just the ellipses, but afterward it draws both the ellipses and the lines. But, if I do this instead:
private void InkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
{
myInkCanvas.Strokes = new System.Windows.Ink.StrokeCollection();
e.Handled = true;
}
The lines aren't kept on the screen. So, I don't understand why they aren't being erased in the above code.
If I do this:
private void InkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
{
foreach (Stroke s in myInkCanvas.Strokes)
System.Diagnostics.Trace.WriteLine(s);
e.Handled = true;
}
I can also see that the canvas contains the line strokes.
While erasing the strokes after they have been added to the collection is far from ideal, it at least does what I want. I could set up the line color to be the same of the background, but then I wouldn't be able to retrieve just the ellipses. I could copy them to a separate collection too, but that's just awful.
It sounds like you want to customize the way strokes appear on your inkCanvas. There are two separate things to consider here:
1) The way they look as the ink flows off the pen, before it is lifted (the DynamicRenderer, who runs on another thread to ensure that ink is always fast, is responsible for this. It sounds like you're happy with your solution to this already.
2) The way the eventual stroke sitting on the canvas looks. To customize this you might try subclassing Stroke, overriding:
protected override void DrawCore(DrawingContext drawingContext, DrawingAttributes drawingAttributes);
Each time you get a strokeCollected (and here's the same horrible thing you were worried about but there you go), you remove the incoming stroke from the canvas and replace it with your custom implementation, stealing the stroke data from the incoming one.
Your implementation of DrawCore would look something like (pseudocode):
foreach(sp in this.StylusPoints)
drawingContext.DrawEllipse(RADIUS, sp.X, sp.Y)
And so as not to get the lines that normally happen you would not call base.DrawCore(context,attributes) at any point.

wpf: capturing mouse does not work

I am developing an kind of outlook calendar application where I need to make the appointment resizable from mouse.
My first try with a thumb did not work properly so I tried another way.
What I did is that:
1) on the botton of the appointmennt panel I added a rectangle to figure out the resize zone (the thumb). The appointment panel is put on a grid panel.
2) I intercept down event on the rectangle and send event to this code:
private Point startPoint;
private void OnResizeElementMouseDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
this.MouseMove += new MouseEventHandler(ResizeEndElement_MouseMove);
this.MouseLeftButtonUp += new MouseButtonEventHandler(OnResizeElementMouseUp);
// some code to perform new height computation
Mouse.Capture(this);
}
where this is the appointment panel that own the thumb.
Decreasing height works well.
But increasing is more difficult. If I move the mouse very very slowly it's OK, if I speed it up a little bit it tends to leave out the appointment panel and then all MouseMove event are lost.
I thought Mouse.Capture() was propose to solve this kind of problem, but in fact not.
Does anybody know what is wrong in my code?
You should be using an actual Thumb control. Check out MSDN for help:
How to: Use a Thumb to Enable Dragging
you should use a thumb, but to play with mouse capture, override the protected override void OnLostMouseCapture(MouseEventArgs e) method, then you will know if you have lost the capture.

Resources