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 !