Coping with lost device in VMR9 custom Allocator-Presenter - c++

First some necessary pre-amble:
I'm using DirectX9 and have no choice I'm afraid. Dealing with legacy code. Sorry!
I'm using DirectX9 SDK June 2010.
What I'm trying to achieve is the following. I have an application which can render in both windowed mode and fullscreen using DirectX. I need to be able to play video when in fullscreen mode and having a visible transition between rendering of the application and playing video is unacceptable (i.e. kick out of fullscreen and go back in again). Thus I need to use the same DirectX device to render the application and the video.
I've achieved this by putting together a custom allocator/presenter as per the vmr9allocator example in the DirectX SDK (see e.g. C:\Program Files\Microsoft SDKs\Windows\v7.1\Samples\multimedia\directshow\vmr9\vmr9allocator). My version differs somewhat as the DirectX device used by the allocator class isn't owned by that class but is passed in from the rendering part of the application (actually from Angle GL layer to be precise, but this isn't particularly relevant for this discussion). However the setup of the filter is all the same.
Everything works fine apart from the following scenario. If the application loses focus then the fullscreen DirectX device is lost. In this situation I want to stop the video and terminate the device, enter windowed mode and create a new DirectX device for rendering in windowed mode. I can achieve this, but I seem to leave DirectShow in some unstable state. The problem manifests itself when I try to subsequently render video in windowed mode. Rather than using the DX device in this case, I do this by creating a subwindow of my main window for DirectShow to render to. So in particular for the DX rendering methodology I call
SetRenderingMode(VMR9Mode_Renderless)
and for the windowed version I call
SetRenderingMode(VMRMode_Windowless)
on the IVMRFilterConfig interface.
What I see (after the fullscreen device loss during video) is that the windowed video will not render to my manually specified window. Instead it insists on opening it's own parentless window exactly as if
SetRenderingMode(VMRMode_Windowed)
had been called. If I debug the code then I see that SetRenderingMode(VMRMode_Windowless) returns an "unknown error"!
It'll be difficult for me to get advice here on what's wrong with my code as there's a lot of it and posting it all is probably not helpful. So what I'd like to know is what the correct way to deal with loss of device during video rendering is. Maybe then I can pinpoint what's going wrong with my code. With reference to the afore mentioned DX sample, the key problem occurs in the CAllocator::PresentImage function:
HRESULT CAllocator::PresentImage(
/* [in] */ DWORD_PTR dwUserID,
/* [in] */ VMR9PresentationInfo *lpPresInfo)
{
HRESULT hr;
CAutoLock Lock(&m_ObjectLock);
if( NeedToHandleDisplayChange() )
{}
hr = PresentHelper( lpPresInfo );
if( hr == D3DERR_DEVICELOST)
{
if (m_D3DDev->TestCooperativeLevel() == D3DERR_DEVICENOTRESET)
{
DeleteSurfaces();
FAIL_RET( CreateDevice() );
HMONITOR hMonitor = m_D3D->GetAdapterMonitor( D3DADAPTER_DEFAULT );
FAIL_RET( m_lpIVMRSurfAllocNotify->ChangeD3DDevice( m_D3DDev, hMonitor ) );
}
hr = S_OK;
}
return hr;
}
This function is called every frame on a thread which is managed by DirectShow and which is started when the video is played. The example code indicates that you'd recreate the device here. Firstly this doesn't make any sense to me as you're only supposed to create/reset/TestCooperativeLevel on the window message thread for DX9, so this breaks that usage rule! Secondly, I can't actually do this anyway as the DX device is provided extraneously thus we can't reset it. However I can't find any sensible way to tell the system not to continue rendering. I can do nothing and return S_OK or some failure code but the problem persists.
So finally, the question! Does anyone know what the correct aproach is to handling this situation, i.e. on device lost just stop video!
N.B. I'm not ruling out some other problem deep in my code somewhere. But if I at least know what the correct approach to doing what I want is then I can hopefully rule in/out at least one part of the code.

Related

How to capture windows screen at 60 frames per second using api? [duplicate]

I want to write a screencasting program for the Windows platform, but am unsure of how to capture the screen. The only method I'm aware of is to use GDI, but I'm curious whether there are other ways to go about this, and, if there are, which incurs the least overhead? Speed is a priority.
The screencasting program will be for recording game footage, although, if this does narrow down the options, I'm still open for any other suggestions that fall out of this scope. Knowledge isn't bad, after all.
Edit: I came across this article: Various methods for capturing the screen. It has introduced me to the Windows Media API way of doing it and the DirectX way of doing it. It mentions in the Conclusion that disabling hardware acceleration could drastically improve the performance of the capture application. I'm curious as to why this is. Could anyone fill in the missing blanks for me?
Edit: I read that screencasting programs such as Camtasia use their own capture driver. Could someone give me an in-depth explanation on how it works, and why it is faster? I may also need guidance on implementing something like that, but I'm sure there is existing documentation anyway.
Also, I now know how FRAPS records the screen. It hooks the underlying graphics API to read from the back buffer. From what I understand, this is faster than reading from the front buffer, because you are reading from system RAM, rather than video RAM. You can read the article here.
This is what I use to collect single frames, but if you modify this and keep the two targets open all the time then you could "stream" it to disk using a static counter for the file name. - I can't recall where I found this, but it has been modified, thanks to whoever!
void dump_buffer()
{
IDirect3DSurface9* pRenderTarget=NULL;
IDirect3DSurface9* pDestTarget=NULL;
const char file[] = "Pickture.bmp";
// sanity checks.
if (Device == NULL)
return;
// get the render target surface.
HRESULT hr = Device->GetRenderTarget(0, &pRenderTarget);
// get the current adapter display mode.
//hr = pDirect3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&d3ddisplaymode);
// create a destination surface.
hr = Device->CreateOffscreenPlainSurface(DisplayMde.Width,
DisplayMde.Height,
DisplayMde.Format,
D3DPOOL_SYSTEMMEM,
&pDestTarget,
NULL);
//copy the render target to the destination surface.
hr = Device->GetRenderTargetData(pRenderTarget, pDestTarget);
//save its contents to a bitmap file.
hr = D3DXSaveSurfaceToFile(file,
D3DXIFF_BMP,
pDestTarget,
NULL,
NULL);
// clean up.
pRenderTarget->Release();
pDestTarget->Release();
}
EDIT: I can see that this is listed under your first edit link as "the GDI way". This is still a decent way to go even with the performance advisory on that site, you can get to 30fps easily I would think.
From this comment (I have no experience doing this, I'm just referencing someone who does):
HDC hdc = GetDC(NULL); // get the desktop device context
HDC hDest = CreateCompatibleDC(hdc); // create a device context to use yourself
// get the height and width of the screen
int height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
int width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
// create a bitmap
HBITMAP hbDesktop = CreateCompatibleBitmap( hdc, width, height);
// use the previously created device context with the bitmap
SelectObject(hDest, hbDesktop);
// copy from the desktop device context to the bitmap device context
// call this once per 'frame'
BitBlt(hDest, 0,0, width, height, hdc, 0, 0, SRCCOPY);
// after the recording is done, release the desktop context you got..
ReleaseDC(NULL, hdc);
// ..delete the bitmap you were using to capture frames..
DeleteObject(hbDesktop);
// ..and delete the context you created
DeleteDC(hDest);
I'm not saying this is the fastest, but the BitBlt operation is generally very fast if you're copying between compatible device contexts.
For reference, Open Broadcaster Software implements something like this as part of their "dc_capture" method, although rather than creating the destination context hDest using CreateCompatibleDC they use an IDXGISurface1, which works with DirectX 10+. If there is no support for this they fall back to CreateCompatibleDC.
To change it to use a specific application, you need to change the first line to GetDC(game) where game is the handle of the game's window, and then set the right height and width of the game's window too.
Once you have the pixels in hDest/hbDesktop, you still need to save it to a file, but if you're doing screen capture then I would think you would want to buffer a certain number of them in memory and save to the video file in chunks, so I will not point to code for saving a static image to disk.
I wrote a video capture software, similar to FRAPS for DirectX applications. The source code is available and my article explains the general technique. Look at http://blog.nektra.com/main/2013/07/23/instrumenting-direct3d-applications-to-capture-video-and-calculate-frames-per-second/
Respect to your questions related to performance,
DirectX should be faster than GDI except when you are reading from the frontbuffer which is very slow. My approach is similar to FRAPS (reading from backbuffer). I intercept a set of methods from Direct3D interfaces.
For video recording in realtime (with minimal application impact), a fast codec is essential. FRAPS uses it's own lossless video codec. Lagarith and HUFFYUV are generic lossless video codecs designed for realtime applications. You should look at them if you want to output video files.
Another approach to recording screencasts could be to write a Mirror Driver. According to Wikipedia: When video mirroring is active, each time the system draws to the primary video device at a location inside the mirrored area, a copy of the draw operation is executed on the mirrored video device in real-time. See mirror drivers at MSDN: http://msdn.microsoft.com/en-us/library/windows/hardware/ff568315(v=vs.85).aspx.
I use d3d9 to get the backbuffer, and save that to a png file using the d3dx library:
IDirect3DSurface9 *surface ;
// GetBackBuffer
idirect3ddevice9->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &surface ) ;
// save the surface
D3DXSaveSurfaceToFileA( "filename.png", D3DXIFF_PNG, surface, NULL, NULL ) ;
SAFE_RELEASE( surface ) ;
To do this you should create your swapbuffer with
d3dpps.SwapEffect = D3DSWAPEFFECT_COPY ; // for screenshots.
(So you guarantee the backbuffer isn't mangled before you take the screenshot).
In my Impression, the GDI approach and the DX approach are different in its nature.
painting using GDI applies the FLUSH method, the FLUSH approach draws the frame then clear it and redraw another frame in the same buffer, this will result in flickering in games require high frame rate.
WHY DX quicker?
in DX (or graphics world), a more mature method called double buffer rendering is applied, where two buffers are present, when present the front buffer to the hardware, you can render to the other buffer as well, then after the frame 1 is finished rendering, the system swap to the other buffer( locking it for presenting to hardware , and release the previous buffer ), in this way the rendering inefficiency is greatly improved.
WHY turning down hardware acceleration quicker?
although with double buffer rendering, the FPS is improved, but the time for rendering is still limited. modern graphic hardware usually involves a lot of optimization during rendering typically like anti-aliasing, this is very computation intensive, if you don't require that high quality graphics, of course you can just disable this option. and this will save you some time.
I think what you really need is a replay system, which I totally agree with what people discussed.
I wrote a class that implemented the GDI method for screen capture. I too wanted extra speed so, after discovering the DirectX method (via GetFrontBuffer) I tried that, expecting it to be faster.
I was dismayed to find that GDI performs about 2.5x faster. After 100 trials capturing my dual monitor display, the GDI implementation averaged 0.65s per screen capture, while the DirectX method averaged 1.72s. So GDI is definitely faster than GetFrontBuffer, according to my tests.
I was unable to get Brandrew's code working to test DirectX via GetRenderTargetData. The screen copy came out purely black. However, it could copy that blank screen super fast! I'll keep tinkering with that and hope to get a working version to see real results from it.
For C++ you can use: http://www.pinvoke.net/default.aspx/gdi32/BitBlt.html
This may hower not work on all types of 3D applications/video apps. Then this link may be more useful as it describes 3 different methods you can use.
Old answer (C#):
You can use System.Drawing.Graphics.Copy, but it is not very fast.
A sample project I wrote doing exactly this: http://blog.tedd.no/index.php/2010/08/16/c-image-analysis-auto-gaming-with-source/
I'm planning to update this sample using a faster method like Direct3D: http://spazzarama.com/2009/02/07/screencapture-with-direct3d/
And here is a link for capturing to video: How to capture screen to be video using C# .Net?
You want the Desktop Duplication API (available since Windows 8). That is the officially recommended way of doing it, and it's also the most CPU efficient.
One nice feature it has for screencasting is that it detects window movement, so you can transmit block deltas when windows get moved around, instead of raw pixels. Also, it tells you which rectangles have changed, from one frame to the next.
The Microsoft example code is quite complex, but the API is actually simple and easy to use. I've put together an example project that is much simpler:
Simplified Sample Code
WindowsDesktopDuplicationSample
Microsoft References
Desktop Duplication API
Official example code (my example above is a stripped down version of this)
A few things I've been able to glean: apparently using a "mirror driver" is fast though I'm not aware of an OSS one.
Why is RDP so fast compared to other remote control software?
Also apparently using some convolutions of StretchRect are faster than BitBlt
http://betterlogic.com/roger/2010/07/fast-screen-capture/comment-page-1/#comment-5193
And the one you mentioned (fraps hooking into the D3D dll's) is probably the only way for D3D applications, but won't work with Windows XP desktop capture. So now I just wish there were a fraps equivalent speed-wise for normal desktop windows...anybody?
(I think with aero you might be able to use fraps-like hooks, but XP users would be out of luck).
Also apparently changing screen bit depths and/or disabling hardware accel. might help (and/or disabling aero).
https://github.com/rdp/screen-capture-recorder-program includes a reasonably fast BitBlt based capture utility, and a benchmarker as part of its install, which can let you benchmark BitBlt speeds to optimize them.
VirtualDub also has an "opengl" screen capture module that is said to be fast and do things like change detection http://www.virtualdub.org/blog/pivot/entry.php?id=290
You can try the c++ open source project WinRobot #git, a powerful screen capturer
CComPtr<IWinRobotService> pService;
hr = pService.CoCreateInstance(__uuidof(ServiceHost) );
//get active console session
CComPtr<IUnknown> pUnk;
hr = pService->GetActiveConsoleSession(&pUnk);
CComQIPtr<IWinRobotSession> pSession = pUnk;
// capture screen
pUnk = 0;
hr = pSession->CreateScreenCapture(0,0,1280,800,&pUnk);
// get screen image data(with file mapping)
CComQIPtr<IScreenBufferStream> pBuffer = pUnk;
Support :
UAC Window
Winlogon
DirectShowOverlay
Screen Recording can be done in C# using VLC API. I have done a sample program to demonstrate this. It uses LibVLCSharp and VideoLAN.LibVLC.Windows libraries. You could achieve many more features related to video rendering using this cross platform API.
For API documentation see: LibVLCSharp API Github
using System;
using System.IO;
using System.Reflection;
using System.Threading;
using LibVLCSharp.Shared;
namespace ScreenRecorderNetApp
{
class Program
{
static void Main(string[] args)
{
Core.Initialize();
using (var libVlc = new LibVLC())
using (var mediaPlayer = new MediaPlayer(libVlc))
{
var media = new Media(libVlc, "screen://", FromType.FromLocation);
media.AddOption(":screen-fps=24");
media.AddOption(":sout=#transcode{vcodec=h264,vb=0,scale=0,acodec=mp4a,ab=128,channels=2,samplerate=44100}:file{dst=testvlc.mp4}");
media.AddOption(":sout-keep");
mediaPlayer.Play(media);
Thread.Sleep(10*1000);
mediaPlayer.Stop();
}
}
}
}
This might not be the fastest method, but it is leightweight and easy to use. The image is returned as an integer array containing the RGB colors.
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#include <Windows.h>
int* screenshot(int& width, int& height) {
HDC hdc = GetDC(NULL); // get the desktop device context
HDC cdc = CreateCompatibleDC(hdc); // create a device context to use yourself
height = (int)GetSystemMetrics(SM_CYVIRTUALSCREEN); // get the width and height of the screen
width = 16*height/9; // only capture left monitor for dual screen setups, for both screens use (int)GetSystemMetrics(SM_CXVIRTUALSCREEN);
HBITMAP hbitmap = CreateCompatibleBitmap(hdc, width, height); // create a bitmap
SelectObject(cdc, hbitmap); // use the previously created device context with the bitmap
BITMAPINFOHEADER bmi = { 0 };
bmi.biSize = sizeof(BITMAPINFOHEADER);
bmi.biPlanes = 1;
bmi.biBitCount = 32;
bmi.biWidth = width;
bmi.biHeight = -height; // flip image upright
bmi.biCompression = BI_RGB;
bmi.biSizeImage = 3*width*height;
BitBlt(cdc, 0, 0, width, height, hdc, 0, 0, SRCCOPY); // copy from desktop device context to bitmap device context
ReleaseDC(NULL, hdc);
int* image = new int[width*height];
GetDIBits(cdc, hbitmap, 0, height, image, (BITMAPINFO*)&bmi, DIB_RGB_COLORS);
DeleteObject(hbitmap);
DeleteDC(cdc);
return image;
}
The above code combines this answer and this answer.
Example on how to use it:
int main() {
int width=0, height=0;
int* image = screenshot(width, height);
// access pixel colors for position (x|y)
const int x=0, y=0;
const int color = image[x+y*width];
const int red = (color>>16)&255;
const int green = (color>> 8)&255;
const int blue = color &255;
delete[] image;
}
i myself do it with directx and think it's as fast as you would want it to be. i don't have a quick code sample, but i found this which should be useful. the directx11 version should not differ a lot, directx9 maybe a little more, but thats the way to go
DXGI Desktop Capture
Project that captures the desktop image with DXGI duplication. Saves the captured image to the file in different image formats (*.bmp; *.jpg; *.tif).
This sample is written in C++. You also need some experience with DirectX (D3D11, D2D1).
What the Application Can Do
If you have more than one desktop monitor, you can choose.
Resize the captured desktop image.
Choose different scaling modes.
You can show or hide the mouse icon in the output image.
You can rotate the image for the output picture, or leave it as default.
I realize the following suggestion doesn't answer your question, but the simplest method I have found to capture a rapidly-changing DirectX view, is to plug a video camera into the S-video port of the video card, and record the images as a movie. Then transfer the video from the camera back to an MPG, WMV, AVI etc. file on the computer.
Windows.Graphics.Capture
Enables apps to capture environments, application windows, and displays in a secure, easy to use way with the use of a system picker UI control.
https://blogs.windows.com/windowsdeveloper/2019/09/16/new-ways-to-do-screen-capture/

Using d3d9's backbuffer functionalities correctly

I'm trying to write a simple DLL which allow me to take a screenshot of the program calling it.
The program give me its Window Handle using the DLL call.
The CreateDevice functions, CreateOffScreenPlainSurface, works fine using the provided handle, according to HRESULT values returned.
But the output image is blank, then I think that I'm not using the DirectX's GetBackBuffer functions correctly.
Here is how I proceed:
HRESULT GFBD_r = g_pd3dDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pSurface);
HRESULT LR_r = pSurface->LockRect(&lr, &rect, D3DLOCK_READONLY);
memcpy(&Frame[0], lr.pBits, Width*Height*4);
pSurface->UnlockRect();
My Frame vector is still remaining empty. Maybe the problem came from Multisampling ? I don't exactly understand how does it works, except that i'm using d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE; in my D3DPRESENT_PARAMETERS .
Creating a D3D device for an existing window does not allow you to capture its contents, even if that window is rendered to using hardware acceleration. If there is already a D3D device for the window, the API call to create the device should fail (iirc). If the window is drawn to using regular Windows APIs (i.e. GDI), the D3D content will, depending on the Windows version, be either composed with that other content or behave in an undefined way. Such composition would happen after you access the back buffer, and (likely) elsewhere.

Fastest method of screen capturing on Windows

I want to write a screencasting program for the Windows platform, but am unsure of how to capture the screen. The only method I'm aware of is to use GDI, but I'm curious whether there are other ways to go about this, and, if there are, which incurs the least overhead? Speed is a priority.
The screencasting program will be for recording game footage, although, if this does narrow down the options, I'm still open for any other suggestions that fall out of this scope. Knowledge isn't bad, after all.
Edit: I came across this article: Various methods for capturing the screen. It has introduced me to the Windows Media API way of doing it and the DirectX way of doing it. It mentions in the Conclusion that disabling hardware acceleration could drastically improve the performance of the capture application. I'm curious as to why this is. Could anyone fill in the missing blanks for me?
Edit: I read that screencasting programs such as Camtasia use their own capture driver. Could someone give me an in-depth explanation on how it works, and why it is faster? I may also need guidance on implementing something like that, but I'm sure there is existing documentation anyway.
Also, I now know how FRAPS records the screen. It hooks the underlying graphics API to read from the back buffer. From what I understand, this is faster than reading from the front buffer, because you are reading from system RAM, rather than video RAM. You can read the article here.
This is what I use to collect single frames, but if you modify this and keep the two targets open all the time then you could "stream" it to disk using a static counter for the file name. - I can't recall where I found this, but it has been modified, thanks to whoever!
void dump_buffer()
{
IDirect3DSurface9* pRenderTarget=NULL;
IDirect3DSurface9* pDestTarget=NULL;
const char file[] = "Pickture.bmp";
// sanity checks.
if (Device == NULL)
return;
// get the render target surface.
HRESULT hr = Device->GetRenderTarget(0, &pRenderTarget);
// get the current adapter display mode.
//hr = pDirect3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&d3ddisplaymode);
// create a destination surface.
hr = Device->CreateOffscreenPlainSurface(DisplayMde.Width,
DisplayMde.Height,
DisplayMde.Format,
D3DPOOL_SYSTEMMEM,
&pDestTarget,
NULL);
//copy the render target to the destination surface.
hr = Device->GetRenderTargetData(pRenderTarget, pDestTarget);
//save its contents to a bitmap file.
hr = D3DXSaveSurfaceToFile(file,
D3DXIFF_BMP,
pDestTarget,
NULL,
NULL);
// clean up.
pRenderTarget->Release();
pDestTarget->Release();
}
EDIT: I can see that this is listed under your first edit link as "the GDI way". This is still a decent way to go even with the performance advisory on that site, you can get to 30fps easily I would think.
From this comment (I have no experience doing this, I'm just referencing someone who does):
HDC hdc = GetDC(NULL); // get the desktop device context
HDC hDest = CreateCompatibleDC(hdc); // create a device context to use yourself
// get the height and width of the screen
int height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
int width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
// create a bitmap
HBITMAP hbDesktop = CreateCompatibleBitmap( hdc, width, height);
// use the previously created device context with the bitmap
SelectObject(hDest, hbDesktop);
// copy from the desktop device context to the bitmap device context
// call this once per 'frame'
BitBlt(hDest, 0,0, width, height, hdc, 0, 0, SRCCOPY);
// after the recording is done, release the desktop context you got..
ReleaseDC(NULL, hdc);
// ..delete the bitmap you were using to capture frames..
DeleteObject(hbDesktop);
// ..and delete the context you created
DeleteDC(hDest);
I'm not saying this is the fastest, but the BitBlt operation is generally very fast if you're copying between compatible device contexts.
For reference, Open Broadcaster Software implements something like this as part of their "dc_capture" method, although rather than creating the destination context hDest using CreateCompatibleDC they use an IDXGISurface1, which works with DirectX 10+. If there is no support for this they fall back to CreateCompatibleDC.
To change it to use a specific application, you need to change the first line to GetDC(game) where game is the handle of the game's window, and then set the right height and width of the game's window too.
Once you have the pixels in hDest/hbDesktop, you still need to save it to a file, but if you're doing screen capture then I would think you would want to buffer a certain number of them in memory and save to the video file in chunks, so I will not point to code for saving a static image to disk.
I wrote a video capture software, similar to FRAPS for DirectX applications. The source code is available and my article explains the general technique. Look at http://blog.nektra.com/main/2013/07/23/instrumenting-direct3d-applications-to-capture-video-and-calculate-frames-per-second/
Respect to your questions related to performance,
DirectX should be faster than GDI except when you are reading from the frontbuffer which is very slow. My approach is similar to FRAPS (reading from backbuffer). I intercept a set of methods from Direct3D interfaces.
For video recording in realtime (with minimal application impact), a fast codec is essential. FRAPS uses it's own lossless video codec. Lagarith and HUFFYUV are generic lossless video codecs designed for realtime applications. You should look at them if you want to output video files.
Another approach to recording screencasts could be to write a Mirror Driver. According to Wikipedia: When video mirroring is active, each time the system draws to the primary video device at a location inside the mirrored area, a copy of the draw operation is executed on the mirrored video device in real-time. See mirror drivers at MSDN: http://msdn.microsoft.com/en-us/library/windows/hardware/ff568315(v=vs.85).aspx.
I use d3d9 to get the backbuffer, and save that to a png file using the d3dx library:
IDirect3DSurface9 *surface ;
// GetBackBuffer
idirect3ddevice9->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &surface ) ;
// save the surface
D3DXSaveSurfaceToFileA( "filename.png", D3DXIFF_PNG, surface, NULL, NULL ) ;
SAFE_RELEASE( surface ) ;
To do this you should create your swapbuffer with
d3dpps.SwapEffect = D3DSWAPEFFECT_COPY ; // for screenshots.
(So you guarantee the backbuffer isn't mangled before you take the screenshot).
In my Impression, the GDI approach and the DX approach are different in its nature.
painting using GDI applies the FLUSH method, the FLUSH approach draws the frame then clear it and redraw another frame in the same buffer, this will result in flickering in games require high frame rate.
WHY DX quicker?
in DX (or graphics world), a more mature method called double buffer rendering is applied, where two buffers are present, when present the front buffer to the hardware, you can render to the other buffer as well, then after the frame 1 is finished rendering, the system swap to the other buffer( locking it for presenting to hardware , and release the previous buffer ), in this way the rendering inefficiency is greatly improved.
WHY turning down hardware acceleration quicker?
although with double buffer rendering, the FPS is improved, but the time for rendering is still limited. modern graphic hardware usually involves a lot of optimization during rendering typically like anti-aliasing, this is very computation intensive, if you don't require that high quality graphics, of course you can just disable this option. and this will save you some time.
I think what you really need is a replay system, which I totally agree with what people discussed.
I wrote a class that implemented the GDI method for screen capture. I too wanted extra speed so, after discovering the DirectX method (via GetFrontBuffer) I tried that, expecting it to be faster.
I was dismayed to find that GDI performs about 2.5x faster. After 100 trials capturing my dual monitor display, the GDI implementation averaged 0.65s per screen capture, while the DirectX method averaged 1.72s. So GDI is definitely faster than GetFrontBuffer, according to my tests.
I was unable to get Brandrew's code working to test DirectX via GetRenderTargetData. The screen copy came out purely black. However, it could copy that blank screen super fast! I'll keep tinkering with that and hope to get a working version to see real results from it.
For C++ you can use: http://www.pinvoke.net/default.aspx/gdi32/BitBlt.html
This may hower not work on all types of 3D applications/video apps. Then this link may be more useful as it describes 3 different methods you can use.
Old answer (C#):
You can use System.Drawing.Graphics.Copy, but it is not very fast.
A sample project I wrote doing exactly this: http://blog.tedd.no/index.php/2010/08/16/c-image-analysis-auto-gaming-with-source/
I'm planning to update this sample using a faster method like Direct3D: http://spazzarama.com/2009/02/07/screencapture-with-direct3d/
And here is a link for capturing to video: How to capture screen to be video using C# .Net?
You want the Desktop Duplication API (available since Windows 8). That is the officially recommended way of doing it, and it's also the most CPU efficient.
One nice feature it has for screencasting is that it detects window movement, so you can transmit block deltas when windows get moved around, instead of raw pixels. Also, it tells you which rectangles have changed, from one frame to the next.
The Microsoft example code is quite complex, but the API is actually simple and easy to use. I've put together an example project that is much simpler:
Simplified Sample Code
WindowsDesktopDuplicationSample
Microsoft References
Desktop Duplication API
Official example code (my example above is a stripped down version of this)
A few things I've been able to glean: apparently using a "mirror driver" is fast though I'm not aware of an OSS one.
Why is RDP so fast compared to other remote control software?
Also apparently using some convolutions of StretchRect are faster than BitBlt
http://betterlogic.com/roger/2010/07/fast-screen-capture/comment-page-1/#comment-5193
And the one you mentioned (fraps hooking into the D3D dll's) is probably the only way for D3D applications, but won't work with Windows XP desktop capture. So now I just wish there were a fraps equivalent speed-wise for normal desktop windows...anybody?
(I think with aero you might be able to use fraps-like hooks, but XP users would be out of luck).
Also apparently changing screen bit depths and/or disabling hardware accel. might help (and/or disabling aero).
https://github.com/rdp/screen-capture-recorder-program includes a reasonably fast BitBlt based capture utility, and a benchmarker as part of its install, which can let you benchmark BitBlt speeds to optimize them.
VirtualDub also has an "opengl" screen capture module that is said to be fast and do things like change detection http://www.virtualdub.org/blog/pivot/entry.php?id=290
You can try the c++ open source project WinRobot #git, a powerful screen capturer
CComPtr<IWinRobotService> pService;
hr = pService.CoCreateInstance(__uuidof(ServiceHost) );
//get active console session
CComPtr<IUnknown> pUnk;
hr = pService->GetActiveConsoleSession(&pUnk);
CComQIPtr<IWinRobotSession> pSession = pUnk;
// capture screen
pUnk = 0;
hr = pSession->CreateScreenCapture(0,0,1280,800,&pUnk);
// get screen image data(with file mapping)
CComQIPtr<IScreenBufferStream> pBuffer = pUnk;
Support :
UAC Window
Winlogon
DirectShowOverlay
Screen Recording can be done in C# using VLC API. I have done a sample program to demonstrate this. It uses LibVLCSharp and VideoLAN.LibVLC.Windows libraries. You could achieve many more features related to video rendering using this cross platform API.
For API documentation see: LibVLCSharp API Github
using System;
using System.IO;
using System.Reflection;
using System.Threading;
using LibVLCSharp.Shared;
namespace ScreenRecorderNetApp
{
class Program
{
static void Main(string[] args)
{
Core.Initialize();
using (var libVlc = new LibVLC())
using (var mediaPlayer = new MediaPlayer(libVlc))
{
var media = new Media(libVlc, "screen://", FromType.FromLocation);
media.AddOption(":screen-fps=24");
media.AddOption(":sout=#transcode{vcodec=h264,vb=0,scale=0,acodec=mp4a,ab=128,channels=2,samplerate=44100}:file{dst=testvlc.mp4}");
media.AddOption(":sout-keep");
mediaPlayer.Play(media);
Thread.Sleep(10*1000);
mediaPlayer.Stop();
}
}
}
}
This might not be the fastest method, but it is leightweight and easy to use. The image is returned as an integer array containing the RGB colors.
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#include <Windows.h>
int* screenshot(int& width, int& height) {
HDC hdc = GetDC(NULL); // get the desktop device context
HDC cdc = CreateCompatibleDC(hdc); // create a device context to use yourself
height = (int)GetSystemMetrics(SM_CYVIRTUALSCREEN); // get the width and height of the screen
width = 16*height/9; // only capture left monitor for dual screen setups, for both screens use (int)GetSystemMetrics(SM_CXVIRTUALSCREEN);
HBITMAP hbitmap = CreateCompatibleBitmap(hdc, width, height); // create a bitmap
SelectObject(cdc, hbitmap); // use the previously created device context with the bitmap
BITMAPINFOHEADER bmi = { 0 };
bmi.biSize = sizeof(BITMAPINFOHEADER);
bmi.biPlanes = 1;
bmi.biBitCount = 32;
bmi.biWidth = width;
bmi.biHeight = -height; // flip image upright
bmi.biCompression = BI_RGB;
bmi.biSizeImage = 3*width*height;
BitBlt(cdc, 0, 0, width, height, hdc, 0, 0, SRCCOPY); // copy from desktop device context to bitmap device context
ReleaseDC(NULL, hdc);
int* image = new int[width*height];
GetDIBits(cdc, hbitmap, 0, height, image, (BITMAPINFO*)&bmi, DIB_RGB_COLORS);
DeleteObject(hbitmap);
DeleteDC(cdc);
return image;
}
The above code combines this answer and this answer.
Example on how to use it:
int main() {
int width=0, height=0;
int* image = screenshot(width, height);
// access pixel colors for position (x|y)
const int x=0, y=0;
const int color = image[x+y*width];
const int red = (color>>16)&255;
const int green = (color>> 8)&255;
const int blue = color &255;
delete[] image;
}
i myself do it with directx and think it's as fast as you would want it to be. i don't have a quick code sample, but i found this which should be useful. the directx11 version should not differ a lot, directx9 maybe a little more, but thats the way to go
DXGI Desktop Capture
Project that captures the desktop image with DXGI duplication. Saves the captured image to the file in different image formats (*.bmp; *.jpg; *.tif).
This sample is written in C++. You also need some experience with DirectX (D3D11, D2D1).
What the Application Can Do
If you have more than one desktop monitor, you can choose.
Resize the captured desktop image.
Choose different scaling modes.
You can show or hide the mouse icon in the output image.
You can rotate the image for the output picture, or leave it as default.
I realize the following suggestion doesn't answer your question, but the simplest method I have found to capture a rapidly-changing DirectX view, is to plug a video camera into the S-video port of the video card, and record the images as a movie. Then transfer the video from the camera back to an MPG, WMV, AVI etc. file on the computer.
Windows.Graphics.Capture
Enables apps to capture environments, application windows, and displays in a secure, easy to use way with the use of a system picker UI control.
https://blogs.windows.com/windowsdeveloper/2019/09/16/new-ways-to-do-screen-capture/

Problem with CreateDC and wglMakeCurrent

PIXELFORMATDESCRIPTOR pfd = { /* otherwise fine for a window with 32-bit color */ };
HDC hDC = CreateDC(TEXT("Display"),NULL,NULL,NULL); // always OK
int ipf = ChoosePixelFormat(hDC,&pfd); // always OK
SetPixelFormat(hDC,ipf,&pfd); // always OK
HGLRC hRC = wglCreateContext(hDC); // always OK
wglMakeCurrent(hDC,hRC); // ! read error: 0xbaadf039 (debug, obviously)
But the following works with the same hRC:
wglMakeCurrent(hSomeWindowDC,hRC);
The above is part of an OpenGL 3.0+ initialization system for Windows.
I am trying to avoid creating a dummy window for the sake of aesthetics.
I have never used CreateDC before, so perhaps I've missed something.
edit: hSomeWindowDC would point to a window DC with an appropriate pixel format.
More info:
I wish to create a window-independent OpenGL rendering context.
Due to the answer selected, it seems I need to use a dummy window (not really a big deal, just a handle to pass around all the same).
Why I would want to do this: Since it is possible to use the same rendering context for multiple windows with the same pixel format in the same thread, it is possible to create a rendering context (really, just a container for gl-related objects) that is independent of a particular window. In this way, one can create a clean separation between the graphics and UI initializations.The purpose of the context initially isn't for rendering (although I believe one could render into textures using it). If one wanted to change the contents of a buffer within a particular context, the desired context object itself would just need to be made current (since it's carrying the dummy window around with it, this is possible). Rendering into a window is simple: As implied by the above, the window's DC only needs to have the same pixel format. Simply make the rendering context and the window's DC current, and render.Please note that, at the time of this writing, this idea is still in testing. I will update this post should this change (or if I can remember :P ).
I've got a dormant brain cell from reading Petzold 15 years ago that just sprang back to life. The DC from CreateDC() is restricted. Good for getting info about the display device, measurement, that sort of stuff. Not good to use as a regular painting DC. You almost certainly need GetDC().
My current OpenGL 3+ initialization routine doesn't require a dummy window. You can simply attempt to make a second RC and make it current using the DC of the real window. Take a look at the OpenGL wiki Tutorial: OpenGL 3.1 The First Triangle (C++/Win)

Direct2D window black when not in focus

I have a Direct2D window which paints fine when in focus; however, when focus moves to another window (same application or another), the entire window goes black. I pinned the issue down to the use of ID2D1HwndRenderTarget::Clear. This function is vital to my application as without it, painting becomes rather... weird.
Is there a way an un-focused Direct2D window can paint as normal (note: WM_PAINT is being called as usual, as is Clear, BeginPaint, etc. all without error)
Thanks in advance.
From MSDN,
Handling Device Loss
While your program is running, the
graphics device that you are using
might become unavailable. For example,
the device can be lost if the display
resolution changes, or if the user
removes the display adapter. If the
device is lost, the render target also
becomes invalid, along with any
device-dependent resources that were
associated with the device. Direct2D
signals a lost device by returning the
error code D2DERR_RECREATE_TARGET from
the EndDraw method. If you receive
this error code, you must re-create
the render target and all
device-dependent resources.
Please check the link for more info. http://msdn.microsoft.com/en-us/library/ff684174(VS.85).aspx
With my experience with DirectX, if the screen turns back its because DX lost the device it was 'painting' to. This happens a lot - for instance - when switching between a full-screen DirectX window to another application, and vice-versa.
So what you need to do is re-intialize the DX device so it can resume painting operations.
This might help some more (not my website, but a quick Google search turned it up)
http://www.programmersheaven.com/2/FAQ-DIRECTX-Avoid-task-switching