Why Repaint the Windowless Control using DirectShow(.NET)? - c#

I am playing a video using IVMRWindowlessControl9 i.e. WindowlessControl w/ VMR-9. When I initially had difficulty setting the video up, I noticed many examples found online had this piece of code in the Window's Paint handler:
IntPtr hdc = e.Graphics.GetHdc();
int hr = 0;
hr = windowlessCtrl.RepaintVideo(this.Handle, hdc);
e.Graphics.ReleaseHdc(hdc);
My video is playing correctly yet I do not have the above code. What does the above code do and is there a point in using it?

In most cases you don't need to repaint the video explicitly because the video renderer does it automatically. Yes, if the hosting window needs - for any reason - to request forced replaint, such as as part of WM_PAINT window message handler, then RepaintVideo method is here at your service.
See more on MSDN, VMR Windowless Mode:
During playback, the application should notify the VMR of the
following Windows messages:
WM_PAINT: Call IVMRWindowlessControl::RepaintVideo to repaint the
image.

As to what it does:
if the video is being drawn using a destination colour key, then it paints the key colour into the destination window.
if the image is letterboxed, or part of it falls on another monitor, it will paint the parts that are not covered by the image.
So it may be that you won't see any difference if it is not done at all, if none of these things apply.

Related

How to get dwp thumbnail of borderless fullscreen wpf/uwp window

I tried capturing live thumbnail of running applications using these codes:
http://community.bartdesmet.net/blogs/bart/archive/2006/10/05/4495.aspx
I have successfully captured some applications except those borderless fullscreen windows.
I am actually trying to capture a UWP application that plays a video in a fullscreen mode and integrate the thumbnail into my application. I don't need to get a bitmap image of it, I just need to display in real-time.
I tried changing the values of
GWL_STYLE
WS_VISIBLE
WS_BORDER
but none of it works.
When trying to Alt+Tab windows, the Windows 10 DWM can handle it and displays the live thumbnail, so I believe this could work with some little modifications on the code.
Thanks!
I checked the window styles for the example uwp application in full screen with Spy++.
Styles were normal so then i hardcoded window handle and it worked. After a moment of debugging, it turned out that the EnumWindows method did not return this window, so it did not matter what styles were checked.
So I looked for a problem with this method and there are many topics, for example:
EnumWindows function in Win10 enumerates only desktop apps
Instead of using EnumWindows, get all windows using this method and insert your filter logic for your desired window handler
IntPtr thisWindow = IntPtr.Zero ;
IntPtr desktopWindow = GetDesktopWindow();
while (true)
{
if (desktopWindow == IntPtr.Zero)
break;
IntPtr nextWindow = FindWindowEx(desktopWindow, thisWindow, null, null);
if (nextWindow == IntPtr.Zero)
break;
/** your code here **/
thisWindow = nextWindow;
}

Omit handle from GDI window capture (DirectShow Filter)

I currently have implemented a DirectShow live source screen capture filter in C#. The DShow filter is being consumed by a separate process (ffmpeg) that is live encoding the stream.
I would like to render some UI/Controls on the screen for starting/stopping the capture of ffmpeg, (UI provided by a separate process) but I don't want the UI to be captured by the DShow filter and end up in the screen capture video.
Here is an excerpt, which contains the DirectShow FillBuffer method where I capture the screen frames.
public int FillBuffer(ref IMediaSampleImpl _sample)
{
IntPtr _ptr;
_sample.GetPointer(out _ptr);
IntPtr srcContext = User32.GetWindowDC(IntPtr.Zero);
IntPtr destContext = GDI32.CreateCompatibleDC(srcContext);
IntPtr destBitmap = GDI32.CreateCompatibleBitmap(srcContext, _captureWidth, _captureHeight);
IntPtr hOld = GDI32.SelectObject(destContext, destBitmap);
GDI32.BitBlt(destContext, 0, 0, _captureWidth, _captureHeight, srcContext, _captureX, _captureY, GDI32.SRCCOPY);
//Draw cursor, ommitted for brevity
GDI32.SelectObject(destContext, hOld);
GDI32.GetDIBits(destContext, destBitmap, 0, (uint)Math.Abs(_captureHeight), _ptr, ref m_bmi, 0);
GDI32.DeleteDC(destContext);
User32.ReleaseDC(IntPtr.Zero, srcContext);
GDI32.DeleteObject(destBitmap);
_sample.SetActualDataLength(_sample.GetSize());
_sample.SetSyncPoint(true);
return NOERROR;
}
I'm not exactly 100% sure how this would be possible, but here are the only things that remotely make sense to me (not saying they are possible, just brainstorming):
Render the UI controls via a passed window pointer from another process to srcContext each frame but only after its copied to destContext
how would you even render the passed pointer?
how do you refresh the screen so that it wont just get captured next frame (now that its rendered to the screen)?
could this really be viable performance wise (refreshing the screen each frame)?
Is it really going to look nice? - i can't imagine that rendering to the WindowDC is reccomended
is there a way to trigger a re-draw of just the UI control region before the next frame and not refresh the entire screen?
Some how capture the window with GDI but omit the UI window handle via some native API interface i'm unaware of
Capture the entire screen with current method, then re-draw the area that contains the UI controls with the contents of whatever window(s) were underneath (is this even possible?)
Capture individual windows, and then render each visible window separately in z-order but skip the UI window (i have a sneaking suspicion that this may be the only way to actually do this, but i doubt that it would meet reasonable performance requirements)
EDIT, i just thought of using PrintWindow of all of the windows behind the UI control, but PrintWindow is slow and unreliable, and this is performance critical code.. So i would really like a different solution.
I understand that Fraps and similar, use DirectX hooks to intercept paint messages further up the pipeline, and are able to draw overlays on the screen that way - but this is not an option, and neither is a display mirror driver.

Find info about a window

I found out the Yahoo Messenger window that notifies you when someone signs in or out is the only window that actually appears on top of a full screen movie or game and won't force you to exit full screen.
So my question is how can I find out what makes this window behave like this? I tried Spy++ but nothing interesting came up.
There are different ways to do this. Some video card drivers on older versions of windows will behave differently.
1) Grab the desktop hwnd and paint to it.
HWND hwnd = GetDesktopWindow();
HDC hdc = GetDC(hwnd);
RECT rect = {};
GetClientRect(hwnd, &rect); // dimensions of the primary monitor are rect.right,rect.bottom
// Use hdc to paint whatever you want to the screen
2) Just create a top-most window without a titlebar and use the WS_EX_TOPMOST style. Then draw whatever you want on it
CreateWindowEx(WS_EX_TOPMOST, ...);
Could be using the windows notification API (I don't have Yahoo messenger, so I'm not sure). Here's some more information about the notification area:
http://msdn.microsoft.com/en-us/library/aa511448.aspx

How to draw on an unfilled box on a video stream using the mouse

I am using dshownet(first time) and C#. I have got a sample to take the web cam input and display it on a form. I now need to draw a rectangle on top of the video stream using the mouse. (the intent is to track what is inside the box from there onwards).
I heard that there is something called VMR. So I went to the dshownet samples and went through them. I didnt find any samples that use the mouse to overlay a shape on the video stream. Someone on here suggested to use colorkey. Another person said to use GDI+ and mouse handling. I tried to compile the DXLogo sample but got this error :
Error 1 Cannot create an instance of the abstract class or interface 'System.Drawing.Image' C:\Documents and Settings\TLNA\Desktop\Final Year Project\Libraries\DirectShow library 2\DirectShowSamples-2010-February\Samples\Capture\DxLogo\Capture.cs 128 32 DxLogo-2008
for the code section:
if (fileName.Length > 0)
{
m_Bitmap = new Image(fileName); // error happened here
Rectangle r = new Rectangle(0, 0, m_Bitmap.Width, m_Bitmap.Height);
m_bmdLogo = m_Bitmap.LockBits(r, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
}
I know that I must go through the Bitmap9 Interface. But I really dont know where to start. Should I read the Directshow API docs.
btw I also have the book Programming Microsoft Directshow for digital video and television. I started reading that book and stopped after a few pages since the code is mainly in C++. Should I continue to read this is book ? How can I accomplish the certain mentioned tasks in C# ?
Any suggestions as how to draw on the video. Some useful links(tutorials) would be helpful.
Many Thanks
Tlna
I'm not sure why the DirectShow sample doesn't compile, but you may be able to change the problem line:
m_Bitmap = new Image(fileName);
to this:
m_Bitmap = new Bitmap(fileName);
and get it to work.
You're actually facing a pretty difficult problem here. DirectShow renders a video by drawing a series of still frames to a device context (like a PictureBox or a Form, or even the screen) many times a second (depending on whatever the frame rate is). You (as a programmer) can also (easily) render graphics directly to this same device context.
However, in order to make your drawn box appear over top of a running video, your code needs to draw the rectangle immediately after DirectShow draws each frame of the video; otherwise, the next frame will obliterate your rectangle. DirectShow objects probably have some sort of frame rendering event that you can handle, and then inside the event handler you just re-draw your box (based on the initial and current mouse coordinates, which you can get from the MouseDown and MouseMove events of whatever control you're drawing on).
Update: I just took a look at my code from when I was playing around with DirectShow.NET, and it looks like there is an event (DsEvCode.Repaint) that you could hook into and use to draw your box.
I haven't looked at the code sample you're working with, but do a search and see if you can find an OnGraphNotify() method in your code, you should be able to add something like this:
if (code == DsEvCode.Repaint)
{
// draw the box here
}
Presumably, this event is fired after each frame of the video is rendered, so if you redraw your box here every time it will appear as if the box is persisting.

How to effectively draw on desktop in C#?

I want to draw directly on the desktop in C#. From searching a bit, I ended up using a Graphics object from the Desktop HDC (null). Then, I painted normally using this Graphics object.
The problem is that my shapes get lost when any part of the screen is redrawn. I tried a While loop, but it actually ends up drawing as fast as the application can, which is not the update rate of the desktop.
Normally, I would need to put my drawing code in a "OnPaint" event, but such thing does not exist for the desktop.
How would I do it?
Example code: https://stackoverflow.com/questions/1536141/how-to-draw-directly-on-the-windows-desktop-c
I posted two solutions for a similar requirement here
Basically you have two options.
1- Get a graphics object for the desktop and start drawing to it. The problem is if you need to start clearing what you have previously drawn etc.
Point pt = Cursor.Position; // Get the mouse cursor in screen coordinates
using (Graphics g = Graphics.FromHwnd(IntPtr.Zero))
{
g.DrawEllipse(Pens.Black, pt.X - 10, pt.Y - 10, 20, 20);
}
2- The second option that I provide in the link above is to create a transparent top-most window and do all your drawing in that window. This basically provides a transparent overlay for the desktop which you can draw on. One possible downside to this, as I mention in the original answer, is that other windows which are also top-most and are created after your app starts will obscure your top most window. This can be solved if it is a problem though.
For option 2, making the form transparent is as simple as using a transparency key, this allows mouse clicks etc. to fall through to the underlying desktop.
BackColor = Color.LightGreen;
TransparencyKey = Color.LightGreen;
When you draw to HDC(NULL) you draw to the screen, in an unmanaged way. As you've discovered, as soon as windows refreshes that part of the screen, your changes are overwritten.
There are a couple of options, depending upon what you want to achieve:
create a borderless, possibly
non-rectangular window. (Use
SetWindowRgn to make a window
non-rectangular.) You can make this a child of the desktop window.
subclass the desktop window. This is not straightforward, and involves
injecting a DLL into the
Explorer.exe process.
To get an OnPaint for the desktop you would need to subclass the desktop window and use your drawing logic when it receives a WM_PAINT/WM_ERASEBKGND message.
As the thread you linked to says, you can only intercept messages sent to a window of an external process using a hook (SetWindowsHookEx from a DLL).
As mentioned a transparent window is another way to do it, or (depending on the update frequency) copying, drawing and setting a temporary wallpaper (as bginfo does).
This is difficult to do correctly.
It will be far easier, and more reliable, to make your own borderless form instead.

Categories

Resources