0

I'm developing an application which captures frames using DXGI mechanism from an application.

First creation of the IDXGIOutputDuplication is correct. When the application changes its display (for example from fullscreen to windowed or vice versa), the frame acquire failed (expected behaviour), then I recreate the IDXGIOutputDuplication and regularly the call to DuplicateOutput crash.

Below creation of the D3d11 device:

D3D_FEATURE_LEVEL FeatureLevels[] =
{
  D3D_FEATURE_LEVEL_11_1,
  D3D_FEATURE_LEVEL_11_0,
  D3D_FEATURE_LEVEL_10_1,
  D3D_FEATURE_LEVEL_10_0,
  D3D_FEATURE_LEVEL_9_1
};
UINT NumFeatureLevels = ARRAYSIZE( FeatureLevels );
D3D_FEATURE_LEVEL FeatureLevel = D3D_FEATURE_LEVEL_11_1;
D3D11CreateDevice( _pDxgiAdapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0, FeatureLevels, NumFeatureLevels, D3D11_SDK_VERSION, &_pD3D11Device, &FeatureLevel, &_pD3D11DeviceCtx );

_pDxgiAdapter is created before the device.

Below creation of the IDXGIOutputDuplication:

int DxgiVideoCapture::open() {
  findDxgiAdapter();

  if( nullptr != _pDxgiAdapter ) {
    // Duplicate output
    uint32_t ui32OutputIndex = 0;
    HRESULT hr = S_OK;
    while( SUCCEEDED( hr ) ) {
      IDXGIOutput* pDxgiOutput = nullptr;
      hr = _pDxgiAdapter->EnumOutputs( ui32OutputIndex, &pDxgiOutput );
      if( SUCCEEDED( hr ) ) {
        IDXGIOutput1* pDxgiOutput1 = nullptr;
        if( SUCCEEDED ( pDxgiOutput->QueryInterface( __uuidof( IDXGIOutput1 ), ( void** )&pDxgiOutput1 ) ) ) {
          uint32_t ui32EnumMode = 0;
          uint32_t ui32Flags = 0xffffffff;
        }

        if( SUCCEEDED ( pDxgiOutput->QueryInterface( __uuidof( IDXGIOutput1 ), ( void** )&_pDxgiOutput1 ) ) ) {
          LOGI( "/!\\ Call which crash regularly /!\\" );
          HRESULT hrDup = _pDxgiOutput1->DuplicateOutput( _pD3D11Device, &_pOutputDuplication ); 
          if( SUCCEEDED( hrDup ) ) {
            LOGI( "Output duplication created." );
          } else {
            switch( hrDup ) {
              case E_INVALIDARG :
                {
                  LOGW( "Invalid device or output already duplicated" );
                }
                break;
              case E_ACCESSDENIED :
                {
                  LOGW( "Access denied" );
                
                }
                break;
              case DXGI_ERROR_UNSUPPORTED :
                {
                  LOGW( "DXGI_ERROR_UNSUPPORTED" );
                }
                break;
              case DXGI_ERROR_NOT_CURRENTLY_AVAILABLE :
                {
                   LOGW( "DXGI_ERROR_NOT_CURRENTLY_AVAILABLE" );
                }
                break;
              case DXGI_ERROR_SESSION_DISCONNECTED :
                {
                   LOGW( "DXGI_ERROR_NOT_CURRENTLY_AVAILABLE" );
                }
                break;
              default:
                {
                   LOGW( "default" );
                }
            }
        } else {
          LOGE( "Unable to retrieve interface creating the ouput duplication" );
        }
      }
      pDxgiOutput->Release();
    }
    ui32OutputIndex++;
  }

  return RET_OK;
}

Below the frame acquisition:

int DxgiVideoCapture::captureGameFrame() {
  int iRet = RET_ERROR;

  ID3D11Texture2D* pCapturedTexture = nullptr;

  DXGI_OUTDUPL_FRAME_INFO frameInfo;
  ZeroMemory(&frameInfo, sizeof(frameInfo));

  HRESULT hr = _pOutputDuplication->AcquireNextFrame( 1000, &frameInfo, &_pDxgiResource );
  if( FAILED( hr ) )    {
    if( hr == DXGI_ERROR_WAIT_TIMEOUT ) {
      LOGW( "Wait for %d ms timed out", 1000);
    }
    if (hr == DXGI_ERROR_INVALID_CALL) {
      LOGW( "Invalid Call, previous frame not released?" );
    }
    if (hr == DXGI_ERROR_ACCESS_LOST) {
      LOGW( "Error Access lost - is it a game end ?" );
    }
    iRet = RET_RESET_CAPTURE;

    return iRet;
  }

  if( FAILED( hr = _pDxgiResource->QueryInterface( __uuidof( ID3D11Texture2D ), ( void** ) &pCapturedTexture ) ) ) {
    LOGW( "unable to retrieve D3D11 texture 0x%x", hr );
    return RET_WARNING;
  } else {
    // Store window of the game
    D3D11_TEXTURE2D_DESC d3D11TextureDesc;
    pCapturedTexture->GetDesc( &d3D11TextureDesc );

    // Compute the zone to extract.
    D3D11_BOX srcBox;
    memset( &srcBox, 0, sizeof( srcBox ) );
    if( _pGameWindow->getLeftPos()  > 0 ) {
      srcBox.left = _pGameWindow->getLeftPos();
    }
    if( _pGameWindow->getTopPos() > 0 ) {
      srcBox.top = _pGameWindow->getTopPos();
    }
    srcBox.front = 0;
    srcBox.right = _pGameWindow->getLeftPos() + _pGameWindow->getWidth();
    if( srcBox.right > _pGameWindow->getMonitorWidth() ) {
      srcBox.right = _pGameWindow->getMonitorWidth();
    }
    if( ( srcBox.right - srcBox.left ) % 2 != 0 ) {
      srcBox.right--;
    } 
    srcBox.bottom = _pGameWindow->getTopPos() + _pGameWindow->getHeight();
    if( srcBox.bottom > _pGameWindow->getMonitorHeight() ) {
      srcBox.bottom = _pGameWindow->getMonitorHeight();
    }
    if( ( srcBox.bottom - srcBox.top ) % 2 != 0 ) {
      srcBox.bottom--;
    } 
    srcBox.back = 1;

    // AVFrame info are udpate just when the captured game window and the texture are diffrent.
    // In the same time texture is reallocated.
    if( ( ( srcBox.right - srcBox.left ) != _CapturedTextureDesc.Width )
        || ( ( srcBox.bottom - srcBox.top ) != _CapturedTextureDesc.Height )
      ) {
      LOGD( "Game window: %dx%d ; %d->%d", _pGameWindow->getLeftPos(), _pGameWindow->getTopPos(), _pGameWindow->getWidth(), _pGameWindow->getHeight() );
      LOGD( "Texture creation %dx%d -> %dx%d", srcBox.left, srcBox.top, srcBox.right, srcBox.bottom );

      // Create the new texture
      iRet = createCapturedTexture( srcBox.right - srcBox.left, srcBox.bottom - srcBox.top );
    }

    DirectX11Util::GetInstance()->getD3D11DeviceContext()->CopySubresourceRegion( _pGameTexture, 0, 0, 0, 0, pCapturedTexture, 0, &srcBox );
  }

  if( nullptr != _pDxgiResource ) {
    _pOutputDuplication->ReleaseFrame();
    pCapturedTexture->Release();
    _pDxgiResource->Release();
    _pDxgiResource = nullptr;
  }

  iRet = RET_OK;
  return iRet;
}

Below the release of the D3d11 capture before recreation of the IDXGIOutputDuplication:

int DxgiVideoCapture::close() {
  if( nullptr != _pGameTexture ) {
    ZeroMemory( &_CapturedTextureDesc, sizeof( D3D11_TEXTURE2D_DESC ) );
    _pGameTexture->Release();
    _pGameTexture = nullptr;
  }

  if( nullptr != _pDxgiResource ){
    _pOutputDuplication->ReleaseFrame();
    _pDxgiResource->Release();
    _pDxgiResource = nullptr;
  }

  if( nullptr != _pOutputDuplication ) {
    _pOutputDuplication->Release();
    _pOutputDuplication = nullptr;
  }

  return RET_OK;
}

What I would like to know is how can I invest this crash (application ends without any message). To be more accurate I cross compile my application, but the behaviour seems the same. Do you have any idea how to invest this issue ? Tell me if you want more details.

Thanks in advance !

  • 1
    You should collect more runtime information. Call stack during crash, prooduced log output. Also your code is incomplete, so you could probably give a better code or upload a runnale project. – Roman R. Dec 15 '21 at 12:21
  • FYI someone else's Desktop Duplication project from one of previous StackOverflow questions: [DuplicationAndMediaFoundation](https://github.com/roman380/DuplicationAndMediaFoundation) - both example how to share your code and also a starting point for troubleshooting/debugging. – Roman R. Dec 15 '21 at 12:23
  • Post your project somewhere. And you should use smart pointers (like ATL's CComPtr, WRL ComPtr, C++/WinRT, or anything similar) this will make your code cleaner and safer. – Simon Mourier Dec 16 '21 at 07:57

0 Answers0