0

I am using this code to take a series of screen shot using desktop duplication api.

The code is only for a single shot. Thus I am looping over it. However looping the entire code gives fps of 3-4. So I am not looping over the declarations and stuff.

hr = lDesktopResource->QueryInterface(IID_PPV_ARGS(&lAcquiredDesktopImage)); this line only executes once. On the second loop it breaks. in other words, 2nd loop executes fine until it hits this line, after which the program terminates automatically. Commenting this line out makes 2nd and further loops work, however the image generated is black. Looping over the entire code in main() fixes the issue but gives terrible performance. I have tried to replace it with hr = lDesktopResource->QueryInterface(&lAcquiredDesktopImage); It does not remove the issue.

I have looked into other similar codes like this. But this code does not work either. It is suppose to do screen capture for 10 seconds, however the program terminates immediately. The output file cant be opened.

I have tried comparing my code with the code given in this question. But i can't figure out the issue.

I have searched for similar codes on the internet, however they are mostly for single shot, not series of shots in quick succession using desktop duplication.

I have tried adding a Sleep(100) in the loop. It does not help. How can I fix this?

int main()
{
int lresult(-1);
D3D_FEATURE_LEVEL lFeatureLevel;
HRESULT hr(E_FAIL);
// Create device
for (UINT DriverTypeIndex = 0; DriverTypeIndex < gNumDriverTypes; ++DriverTypeIndex){
    hr = D3D11CreateDevice(
    nullptr,
    gDriverTypes[DriverTypeIndex],
    nullptr,
    0,
    gFeatureLevels,
    gNumFeatureLevels,
    D3D11_SDK_VERSION,
    &lDevice,
    &lFeatureLevel,
    &lImmediateContext);
    if (SUCCEEDED(hr)){
        // Device creation success, no need to loop anymore
        break;
        }

    lDevice.Release();
    lImmediateContext.Release();
        }
    Sleep(100);
// Get DXGI device
    CComPtrCustom<IDXGIDevice> lDxgiDevice;
    hr = lDevice->QueryInterface(IID_PPV_ARGS(&lDxgiDevice));
// Get DXGI adapter
    CComPtrCustom<IDXGIAdapter> lDxgiAdapter;
    hr = lDxgiDevice->GetParent(
         __uuidof(IDXGIAdapter),
    reinterpret_cast<void**>(&lDxgiAdapter));
    lDxgiDevice.Release();

    UINT Output = 0;

    // Get output
    CComPtrCustom<IDXGIOutput> lDxgiOutput;
    hr = lDxgiAdapter->EnumOutputs(
            Output,
            &lDxgiOutput);


    lDxgiAdapter.Release();

    hr = lDxgiOutput->GetDesc(
            &lOutputDesc);

        

    // QI for Output 1
    CComPtrCustom<IDXGIOutput1> lDxgiOutput1;

    hr = lDxgiOutput->QueryInterface(IID_PPV_ARGS(&lDxgiOutput1));

    lDxgiOutput.Release();

    // Create desktop duplication
    hr = lDxgiOutput1->DuplicateOutput(
            lDevice,
            &lDeskDupl);


    lDxgiOutput1.Release();

    // Create GUI drawing texture
    lDeskDupl->GetDesc(&lOutputDuplDesc);

    D3D11_TEXTURE2D_DESC desc;

    desc.Width = lOutputDuplDesc.ModeDesc.Width;

    desc.Height = lOutputDuplDesc.ModeDesc.Height;

    desc.Format = lOutputDuplDesc.ModeDesc.Format;

    desc.ArraySize = 1;

    desc.BindFlags = D3D11_BIND_FLAG::D3D11_BIND_RENDER_TARGET;

    desc.MiscFlags = D3D11_RESOURCE_MISC_GDI_COMPATIBLE;

    desc.SampleDesc.Count = 1;

    desc.SampleDesc.Quality = 0;

    desc.MipLevels = 1;

    desc.CPUAccessFlags = 0;

    desc.Usage = D3D11_USAGE_DEFAULT;

    hr = lDevice->CreateTexture2D(&desc, NULL, &lGDIImage);

    // Create CPU access texture

    desc.Width = lOutputDuplDesc.ModeDesc.Width;

    desc.Height = lOutputDuplDesc.ModeDesc.Height;

    desc.Format = lOutputDuplDesc.ModeDesc.Format;

    desc.ArraySize = 1;

    desc.BindFlags = 0;

    desc.MiscFlags = 0;

    desc.SampleDesc.Count = 1;

    desc.SampleDesc.Quality = 0;

    desc.MipLevels = 1;

    desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
    desc.Usage = D3D11_USAGE_STAGING;

    hr = lDevice->CreateTexture2D(&desc, NULL, &lDestImage);


    int index_main = 0;
    while (index_main< 10) {
            

        CComPtrCustom<IDXGIResource> lDesktopResource;
        DXGI_OUTDUPL_FRAME_INFO lFrameInfo;

        int lTryCount = 4;
            
        do
            {
            Sleep(100);

            // Get new frame
            hr = lDeskDupl->AcquireNextFrame(
            0,
            &lFrameInfo,
            &lDesktopResource);

            if (SUCCEEDED(hr))
                    break;

            if (hr == DXGI_ERROR_WAIT_TIMEOUT)
                {
                    continue;
                }
            else if (FAILED(hr))
                    break;

            } while (--lTryCount > 0);
            
            // QI for ID3D11Texture2D
            std::cout << index_main;
            hr = lDesktopResource->QueryInterface(IID_PPV_ARGS(&lAcquiredDesktopImage));
//this is beaking the loop
        
        

    lDesktopResource.Release();
    // Copy image into GDI drawing texture
            
    lImmediateContext->CopyResource(lGDIImage, lAcquiredDesktopImage);

        
    // Draw cursor image into GDI drawing texture

    CComPtrCustom<IDXGISurface1> lIDXGISurface1;

    hr = lGDIImage->QueryInterface(IID_PPV_ARGS(&lIDXGISurface1));

            
    CURSORINFO lCursorInfo = { 0 };

    lCursorInfo.cbSize = sizeof(lCursorInfo);

    auto lBoolres = GetCursorInfo(&lCursorInfo);
            
    if (lBoolres == TRUE)
            {
                if (lCursorInfo.flags == CURSOR_SHOWING)
                {
                    auto lCursorPosition = lCursorInfo.ptScreenPos;

                    auto lCursorSize = lCursorInfo.cbSize;

                    HDC  lHDC;

                    lIDXGISurface1->GetDC(FALSE, &lHDC);

                    DrawIconEx(
                        lHDC,
                        lCursorPosition.x,
                        lCursorPosition.y,
                        lCursorInfo.hCursor,
                        0,
                        0,
                        0,
                        0,
                        DI_NORMAL | DI_DEFAULTSIZE);

                    lIDXGISurface1->ReleaseDC(nullptr);
                }

            }

            // Copy image into CPU access texture

            lImmediateContext->CopyResource(lDestImage, lGDIImage);


            // Copy from CPU access texture to bitmap buffer

            D3D11_MAPPED_SUBRESOURCE resource;
            UINT subresource = D3D11CalcSubresource(0, 0, 0);
            lImmediateContext->Map(lDestImage, subresource, D3D11_MAP_READ_WRITE, 0, &resource);
            index_main = index_main + 1;
        }
    
    
    return lresult;
}
Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
user541396
  • 163
  • 1
  • 9
  • 1
    IDXGIOutputDuplication is not a screen capture API. It just duplicates the video frames (DXGI) to a buffer when there are new video frames (or changes) available. Sleep(100) in the loop is a typical bad smell. You should study Microsoft's official that uses multiple threads: sample: https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/DXGIDesktopDuplication I've got Michael Chourdakis' work from github https://github.com/WindowsNT/ScreenCapture and it seems to work with .ASF file at least. – Simon Mourier Apr 28 '22 at 15:56
  • @SimonMourier Do you have any idea why Michael's code would not work? The console just says 10 seconds are over immediately and when I try to open the file, VlC says, cache_read error: cannot pre fill buffer mjpeg error: cannot peek – user541396 Apr 28 '22 at 16:09
  • I've not investigated but the ASF (uncomment this https://github.com/WindowsNT/ScreenCapture/blob/master/main.cpp#L17) produces a file that's ok (on my PC it's a bit too fast but that's another frequency problem). You can ask on the repo. – Simon Mourier Apr 28 '22 at 16:27
  • 1
    You are not validating that `lDeskDupl->AcquireNextFrame(..., &lDesktopResource)` is actually successful before calling `lDesktopResource->QueryInterface()`. You are validating the result of `AcquireNextFrame()` only inside of the retry loop, but you are not validating the result again after the retry loop ends. If the retry loop fails to acquire the frame, you call `QueryInterface()` on an invalid pointer. – Remy Lebeau Apr 28 '22 at 16:54

0 Answers0