|
|
|
|
@@ -192,6 +192,28 @@ bool ResolveConfiguredDisplayMode(const std::string& videoFormat, const std::str
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FindDeckLinkDisplayMode(IDeckLinkDisplayModeIterator* iterator, BMDDisplayMode targetMode, IDeckLinkDisplayMode** foundMode)
|
|
|
|
|
{
|
|
|
|
|
if (!iterator || !foundMode)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
*foundMode = NULL;
|
|
|
|
|
IDeckLinkDisplayMode* candidate = NULL;
|
|
|
|
|
while (iterator->Next(&candidate) == S_OK)
|
|
|
|
|
{
|
|
|
|
|
if (candidate->GetDisplayMode() == targetMode)
|
|
|
|
|
{
|
|
|
|
|
*foundMode = candidate;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
candidate->Release();
|
|
|
|
|
candidate = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class ScopedGlShader
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
@@ -295,8 +317,10 @@ OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) :
|
|
|
|
|
mCaptureDelegate(NULL), mPlayoutDelegate(NULL),
|
|
|
|
|
mDLInput(NULL), mDLOutput(NULL), mDLKeyer(NULL),
|
|
|
|
|
mPlayoutAllocator(NULL),
|
|
|
|
|
mFrameWidth(0), mFrameHeight(0),
|
|
|
|
|
mDisplayModeName("1080p59.94"),
|
|
|
|
|
mInputFrameWidth(0), mInputFrameHeight(0),
|
|
|
|
|
mOutputFrameWidth(0), mOutputFrameHeight(0),
|
|
|
|
|
mInputDisplayModeName("1080p59.94"),
|
|
|
|
|
mOutputDisplayModeName("1080p59.94"),
|
|
|
|
|
mHasNoInputSource(true),
|
|
|
|
|
mDeckLinkSupportsInternalKeying(false),
|
|
|
|
|
mDeckLinkSupportsExternalKeying(false),
|
|
|
|
|
@@ -307,10 +331,12 @@ OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) :
|
|
|
|
|
mDecodedTexture(0),
|
|
|
|
|
mLayerTempTexture(0),
|
|
|
|
|
mFBOTexture(0),
|
|
|
|
|
mOutputTexture(0),
|
|
|
|
|
mUnpinnedTextureBuffer(0),
|
|
|
|
|
mDecodeFrameBuf(0),
|
|
|
|
|
mLayerTempFrameBuf(0),
|
|
|
|
|
mIdFrameBuf(0),
|
|
|
|
|
mOutputFrameBuf(0),
|
|
|
|
|
mIdColorBuf(0),
|
|
|
|
|
mIdDepthBuf(0),
|
|
|
|
|
mFullscreenVAO(0),
|
|
|
|
|
@@ -407,6 +433,10 @@ OpenGLComposite::~OpenGLComposite()
|
|
|
|
|
glDeleteTextures(1, &mLayerTempTexture);
|
|
|
|
|
if (mFBOTexture != 0)
|
|
|
|
|
glDeleteTextures(1, &mFBOTexture);
|
|
|
|
|
if (mOutputTexture != 0)
|
|
|
|
|
glDeleteTextures(1, &mOutputTexture);
|
|
|
|
|
if (mOutputFrameBuf != 0)
|
|
|
|
|
glDeleteFramebuffers(1, &mOutputFrameBuf);
|
|
|
|
|
if (mUnpinnedTextureBuffer != 0)
|
|
|
|
|
glDeleteBuffers(1, &mUnpinnedTextureBuffer);
|
|
|
|
|
|
|
|
|
|
@@ -427,10 +457,14 @@ bool OpenGLComposite::InitDeckLink()
|
|
|
|
|
IDeckLinkIterator* pDLIterator = NULL;
|
|
|
|
|
IDeckLink* pDL = NULL;
|
|
|
|
|
IDeckLinkProfileAttributes* deckLinkAttributes = NULL;
|
|
|
|
|
IDeckLinkDisplayModeIterator* pDLDisplayModeIterator = NULL;
|
|
|
|
|
IDeckLinkDisplayMode* pDLDisplayMode = NULL;
|
|
|
|
|
BMDDisplayMode displayMode = bmdModeHD1080p5994; // mode to use for capture and playout
|
|
|
|
|
std::string displayModeName = "1080p59.94";
|
|
|
|
|
IDeckLinkDisplayModeIterator* pDLInputDisplayModeIterator = NULL;
|
|
|
|
|
IDeckLinkDisplayModeIterator* pDLOutputDisplayModeIterator = NULL;
|
|
|
|
|
IDeckLinkDisplayMode* pDLInputDisplayMode = NULL;
|
|
|
|
|
IDeckLinkDisplayMode* pDLOutputDisplayMode = NULL;
|
|
|
|
|
BMDDisplayMode inputDisplayMode = bmdModeHD1080p5994;
|
|
|
|
|
BMDDisplayMode outputDisplayMode = bmdModeHD1080p5994;
|
|
|
|
|
std::string inputDisplayModeName = "1080p59.94";
|
|
|
|
|
std::string outputDisplayModeName = "1080p59.94";
|
|
|
|
|
int outputFrameRowBytes;
|
|
|
|
|
HRESULT result;
|
|
|
|
|
|
|
|
|
|
@@ -446,15 +480,23 @@ bool OpenGLComposite::InitDeckLink()
|
|
|
|
|
|
|
|
|
|
if (mRuntimeHost)
|
|
|
|
|
{
|
|
|
|
|
if (!ResolveConfiguredDisplayMode(mRuntimeHost->GetVideoFormat(), mRuntimeHost->GetFrameRate(), displayMode, displayModeName))
|
|
|
|
|
if (!ResolveConfiguredDisplayMode(mRuntimeHost->GetInputVideoFormat(), mRuntimeHost->GetInputFrameRate(), inputDisplayMode, inputDisplayModeName))
|
|
|
|
|
{
|
|
|
|
|
const std::string error = "Unsupported DeckLink video format/frameRate in config/runtime-host.json: " +
|
|
|
|
|
mRuntimeHost->GetVideoFormat() + " / " + mRuntimeHost->GetFrameRate();
|
|
|
|
|
MessageBoxA(NULL, error.c_str(), "DeckLink mode configuration error", MB_OK);
|
|
|
|
|
const std::string error = "Unsupported DeckLink inputVideoFormat/inputFrameRate in config/runtime-host.json: " +
|
|
|
|
|
mRuntimeHost->GetInputVideoFormat() + " / " + mRuntimeHost->GetInputFrameRate();
|
|
|
|
|
MessageBoxA(NULL, error.c_str(), "DeckLink input mode configuration error", MB_OK);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!ResolveConfiguredDisplayMode(mRuntimeHost->GetOutputVideoFormat(), mRuntimeHost->GetOutputFrameRate(), outputDisplayMode, outputDisplayModeName))
|
|
|
|
|
{
|
|
|
|
|
const std::string error = "Unsupported DeckLink outputVideoFormat/outputFrameRate in config/runtime-host.json: " +
|
|
|
|
|
mRuntimeHost->GetOutputVideoFormat() + " / " + mRuntimeHost->GetOutputFrameRate();
|
|
|
|
|
MessageBoxA(NULL, error.c_str(), "DeckLink output mode configuration error", MB_OK);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
mDisplayModeName = displayModeName;
|
|
|
|
|
mInputDisplayModeName = inputDisplayModeName;
|
|
|
|
|
mOutputDisplayModeName = outputDisplayModeName;
|
|
|
|
|
|
|
|
|
|
result = CoCreateInstance(CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**)&pDLIterator);
|
|
|
|
|
if (FAILED(result))
|
|
|
|
|
@@ -538,35 +580,48 @@ bool OpenGLComposite::InitDeckLink()
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mDLOutput->GetDisplayModeIterator(&pDLDisplayModeIterator) != S_OK)
|
|
|
|
|
if (mDLInput->GetDisplayModeIterator(&pDLInputDisplayModeIterator) != S_OK)
|
|
|
|
|
{
|
|
|
|
|
MessageBox(NULL, _T("Cannot get Display Mode Iterator."), _T("DeckLink error."), MB_OK);
|
|
|
|
|
MessageBox(NULL, _T("Cannot get input Display Mode Iterator."), _T("DeckLink error."), MB_OK);
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (pDLDisplayModeIterator->Next(&pDLDisplayMode) == S_OK)
|
|
|
|
|
if (!FindDeckLinkDisplayMode(pDLInputDisplayModeIterator, inputDisplayMode, &pDLInputDisplayMode))
|
|
|
|
|
{
|
|
|
|
|
if (pDLDisplayMode->GetDisplayMode() == displayMode)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
pDLDisplayMode->Release();
|
|
|
|
|
pDLDisplayMode = NULL;
|
|
|
|
|
const std::string error = "Cannot get specified input BMDDisplayMode for configured mode: " + inputDisplayModeName;
|
|
|
|
|
MessageBoxA(NULL, error.c_str(), "DeckLink input error.", MB_OK);
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
pDLDisplayModeIterator->Release();
|
|
|
|
|
pDLDisplayModeIterator = NULL;
|
|
|
|
|
pDLInputDisplayModeIterator->Release();
|
|
|
|
|
pDLInputDisplayModeIterator = NULL;
|
|
|
|
|
|
|
|
|
|
if (pDLDisplayMode == NULL)
|
|
|
|
|
if (mDLOutput->GetDisplayModeIterator(&pDLOutputDisplayModeIterator) != S_OK)
|
|
|
|
|
{
|
|
|
|
|
const std::string error = "Cannot get specified BMDDisplayMode for configured mode: " + displayModeName;
|
|
|
|
|
MessageBoxA(NULL, error.c_str(), "DeckLink error.", MB_OK);
|
|
|
|
|
MessageBox(NULL, _T("Cannot get output Display Mode Iterator."), _T("DeckLink error."), MB_OK);
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mFrameWidth = pDLDisplayMode->GetWidth();
|
|
|
|
|
mFrameHeight = pDLDisplayMode->GetHeight();
|
|
|
|
|
if (!FindDeckLinkDisplayMode(pDLOutputDisplayModeIterator, outputDisplayMode, &pDLOutputDisplayMode))
|
|
|
|
|
{
|
|
|
|
|
const std::string error = "Cannot get specified output BMDDisplayMode for configured mode: " + outputDisplayModeName;
|
|
|
|
|
MessageBoxA(NULL, error.c_str(), "DeckLink output error.", MB_OK);
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
pDLOutputDisplayModeIterator->Release();
|
|
|
|
|
pDLOutputDisplayModeIterator = NULL;
|
|
|
|
|
|
|
|
|
|
mInputFrameWidth = pDLInputDisplayMode->GetWidth();
|
|
|
|
|
mInputFrameHeight = pDLInputDisplayMode->GetHeight();
|
|
|
|
|
mOutputFrameWidth = pDLOutputDisplayMode->GetWidth();
|
|
|
|
|
mOutputFrameHeight = pDLOutputDisplayMode->GetHeight();
|
|
|
|
|
|
|
|
|
|
if (! CheckOpenGLExtensions())
|
|
|
|
|
goto error;
|
|
|
|
|
if (mInputFrameWidth != mOutputFrameWidth || mInputFrameHeight != mOutputFrameHeight)
|
|
|
|
|
{
|
|
|
|
|
mFastTransferExtensionAvailable = false;
|
|
|
|
|
OutputDebugStringA("Input/output dimensions differ; using regular OpenGL transfer fallback instead of fast transfer.\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! InitOpenGLState())
|
|
|
|
|
goto error;
|
|
|
|
|
@@ -586,18 +641,18 @@ bool OpenGLComposite::InitDeckLink()
|
|
|
|
|
mDeckLinkStatusMessage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pDLDisplayMode->GetFrameRate(&mFrameDuration, &mFrameTimescale);
|
|
|
|
|
pDLOutputDisplayMode->GetFrameRate(&mFrameDuration, &mFrameTimescale);
|
|
|
|
|
|
|
|
|
|
// Resize window to match video frame, but scale large formats down by half for viewing
|
|
|
|
|
if (mFrameWidth < 1920)
|
|
|
|
|
resizeWindow(mFrameWidth, mFrameHeight);
|
|
|
|
|
// Resize window to match output video frame, but scale large formats down by half for viewing.
|
|
|
|
|
if (mOutputFrameWidth < 1920)
|
|
|
|
|
resizeWindow(mOutputFrameWidth, mOutputFrameHeight);
|
|
|
|
|
else
|
|
|
|
|
resizeWindow(mFrameWidth / 2, mFrameHeight / 2);
|
|
|
|
|
resizeWindow(mOutputFrameWidth / 2, mOutputFrameHeight / 2);
|
|
|
|
|
|
|
|
|
|
if (mFastTransferExtensionAvailable)
|
|
|
|
|
{
|
|
|
|
|
// Initialize fast video frame transfers
|
|
|
|
|
if (! VideoFrameTransfer::initialize(mFrameWidth, mFrameHeight, mCaptureTexture, mFBOTexture))
|
|
|
|
|
if (! VideoFrameTransfer::initialize(mInputFrameWidth, mInputFrameHeight, mCaptureTexture, mOutputTexture))
|
|
|
|
|
{
|
|
|
|
|
MessageBox(NULL, _T("Cannot initialize video transfers."), _T("VideoFrameTransfer error."), MB_OK);
|
|
|
|
|
goto error;
|
|
|
|
|
@@ -608,7 +663,7 @@ bool OpenGLComposite::InitDeckLink()
|
|
|
|
|
// Use custom allocators so we pin only once then recycle them
|
|
|
|
|
CComPtr<IDeckLinkVideoBufferAllocatorProvider> captureAllocator(new (std::nothrow) InputAllocatorPool(hGLDC, hGLRC));
|
|
|
|
|
|
|
|
|
|
if (mDLInput->EnableVideoInputWithAllocatorProvider(displayMode, bmdFormat8BitYUV, bmdVideoInputFlagDefault, captureAllocator) != S_OK)
|
|
|
|
|
if (mDLInput->EnableVideoInputWithAllocatorProvider(inputDisplayMode, bmdFormat8BitYUV, bmdVideoInputFlagDefault, captureAllocator) != S_OK)
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -616,13 +671,13 @@ bool OpenGLComposite::InitDeckLink()
|
|
|
|
|
if (mDLInput->SetCallback(mCaptureDelegate) != S_OK)
|
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
|
|
if (mDLOutput->RowBytesForPixelFormat(bmdFormat8BitBGRA, mFrameWidth, &outputFrameRowBytes) != S_OK)
|
|
|
|
|
if (mDLOutput->RowBytesForPixelFormat(bmdFormat8BitBGRA, mOutputFrameWidth, &outputFrameRowBytes) != S_OK)
|
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
|
|
// Use a custom allocator so we pin only once then recycle them
|
|
|
|
|
mPlayoutAllocator = new PinnedMemoryAllocator(hGLDC, hGLRC, VideoFrameTransfer::GPUtoCPU, 1, outputFrameRowBytes * mFrameHeight);
|
|
|
|
|
mPlayoutAllocator = new PinnedMemoryAllocator(hGLDC, hGLRC, VideoFrameTransfer::GPUtoCPU, 1, outputFrameRowBytes * mOutputFrameHeight);
|
|
|
|
|
|
|
|
|
|
if (mDLOutput->EnableVideoOutput(displayMode, bmdVideoOutputFlagDefault) != S_OK)
|
|
|
|
|
if (mDLOutput->EnableVideoOutput(outputDisplayMode, bmdVideoOutputFlagDefault) != S_OK)
|
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
|
|
if (mDLOutput->QueryInterface(IID_IDeckLinkKeyer, (void**)&mDLKeyer) == S_OK && mDLKeyer != NULL)
|
|
|
|
|
@@ -680,7 +735,7 @@ bool OpenGLComposite::InitDeckLink()
|
|
|
|
|
if (mPlayoutAllocator->AllocateVideoBuffer(&outputFrameBuffer) != S_OK)
|
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
|
|
if (mDLOutput->CreateVideoFrameWithBuffer(mFrameWidth, mFrameHeight, outputFrameRowBytes, bmdFormat8BitBGRA, bmdFrameFlagFlipVertical, outputFrameBuffer, &outputFrame) != S_OK)
|
|
|
|
|
if (mDLOutput->CreateVideoFrameWithBuffer(mOutputFrameWidth, mOutputFrameHeight, outputFrameRowBytes, bmdFormat8BitBGRA, bmdFrameFlagFlipVertical, outputFrameBuffer, &outputFrame) != S_OK)
|
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
|
|
mDLOutputVideoFrameQueue.push_back(outputFrame);
|
|
|
|
|
@@ -723,10 +778,28 @@ error:
|
|
|
|
|
pDL = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pDLDisplayMode != NULL)
|
|
|
|
|
if (pDLInputDisplayMode != NULL)
|
|
|
|
|
{
|
|
|
|
|
pDLDisplayMode->Release();
|
|
|
|
|
pDLDisplayMode = NULL;
|
|
|
|
|
pDLInputDisplayMode->Release();
|
|
|
|
|
pDLInputDisplayMode = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pDLOutputDisplayMode != NULL)
|
|
|
|
|
{
|
|
|
|
|
pDLOutputDisplayMode->Release();
|
|
|
|
|
pDLOutputDisplayMode = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pDLInputDisplayModeIterator != NULL)
|
|
|
|
|
{
|
|
|
|
|
pDLInputDisplayModeIterator->Release();
|
|
|
|
|
pDLInputDisplayModeIterator = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pDLOutputDisplayModeIterator != NULL)
|
|
|
|
|
{
|
|
|
|
|
pDLOutputDisplayModeIterator->Release();
|
|
|
|
|
pDLOutputDisplayModeIterator = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pDLIterator != NULL)
|
|
|
|
|
@@ -757,9 +830,9 @@ void OpenGLComposite::paintGL()
|
|
|
|
|
int destX = 0;
|
|
|
|
|
int destY = 0;
|
|
|
|
|
|
|
|
|
|
if (mFrameWidth > 0 && mFrameHeight > 0 && mViewWidth > 0 && mViewHeight > 0)
|
|
|
|
|
if (mOutputFrameWidth > 0 && mOutputFrameHeight > 0 && mViewWidth > 0 && mViewHeight > 0)
|
|
|
|
|
{
|
|
|
|
|
const double frameAspect = static_cast<double>(mFrameWidth) / static_cast<double>(mFrameHeight);
|
|
|
|
|
const double frameAspect = static_cast<double>(mOutputFrameWidth) / static_cast<double>(mOutputFrameHeight);
|
|
|
|
|
const double viewAspect = static_cast<double>(mViewWidth) / static_cast<double>(mViewHeight);
|
|
|
|
|
|
|
|
|
|
if (viewAspect > frameAspect)
|
|
|
|
|
@@ -776,12 +849,12 @@ void OpenGLComposite::paintGL()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, mIdFrameBuf);
|
|
|
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, mOutputFrameBuf);
|
|
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
|
|
|
glViewport(0, 0, mViewWidth, mViewHeight);
|
|
|
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
|
glBlitFramebuffer(0, 0, mFrameWidth, mFrameHeight, destX, destY, destX + destWidth, destY + destHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
|
|
|
|
glBlitFramebuffer(0, 0, mOutputFrameWidth, mOutputFrameHeight, destX, destY, destX + destWidth, destY + destHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
|
|
|
|
|
|
|
|
|
SwapBuffers(hGLDC);
|
|
|
|
|
ValidateRect(hGLWnd, NULL);
|
|
|
|
|
@@ -893,7 +966,7 @@ bool OpenGLComposite::InitOpenGLState()
|
|
|
|
|
// Create texture with empty data, we will update it using glTexSubImage2D each frame.
|
|
|
|
|
// The captured video is YCbCr 4:2:2 packed into a UYVY macropixel. OpenGL has no YCbCr format
|
|
|
|
|
// so treat it as RGBA 4:4:4:4 by halving the width and using GL_RGBA internal format.
|
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mFrameWidth/2, mFrameHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mInputFrameWidth / 2, mInputFrameHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
|
|
|
|
|
|
glGenTextures(1, &mDecodedTexture);
|
|
|
|
|
@@ -902,7 +975,7 @@ bool OpenGLComposite::InitOpenGLState()
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mFrameWidth, mFrameHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mInputFrameWidth, mInputFrameHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
|
|
|
|
|
|
glGenTextures(1, &mLayerTempTexture);
|
|
|
|
|
@@ -911,7 +984,7 @@ bool OpenGLComposite::InitOpenGLState()
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mFrameWidth, mFrameHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mInputFrameWidth, mInputFrameHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -920,6 +993,7 @@ bool OpenGLComposite::InitOpenGLState()
|
|
|
|
|
glGenFramebuffers(1, &mDecodeFrameBuf);
|
|
|
|
|
glGenFramebuffers(1, &mLayerTempFrameBuf);
|
|
|
|
|
glGenFramebuffers(1, &mIdFrameBuf);
|
|
|
|
|
glGenFramebuffers(1, &mOutputFrameBuf);
|
|
|
|
|
glGenRenderbuffers(1, &mIdColorBuf);
|
|
|
|
|
glGenRenderbuffers(1, &mIdDepthBuf);
|
|
|
|
|
glGenVertexArrays(1, &mFullscreenVAO);
|
|
|
|
|
@@ -952,11 +1026,11 @@ bool OpenGLComposite::InitOpenGLState()
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mFrameWidth, mFrameHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mInputFrameWidth, mInputFrameHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
|
|
|
|
|
|
|
|
// Attach a depth buffer
|
|
|
|
|
glBindRenderbuffer(GL_RENDERBUFFER, mIdDepthBuf);
|
|
|
|
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, mFrameWidth, mFrameHeight);
|
|
|
|
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, mInputFrameWidth, mInputFrameHeight);
|
|
|
|
|
|
|
|
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER, mIdDepthBuf);
|
|
|
|
|
|
|
|
|
|
@@ -970,6 +1044,23 @@ bool OpenGLComposite::InitOpenGLState()
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glGenTextures(1, &mOutputTexture);
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, mOutputTexture);
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mOutputFrameWidth, mOutputFrameHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
|
|
|
|
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, mOutputFrameBuf);
|
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mOutputTexture, 0);
|
|
|
|
|
glStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
|
|
|
|
if (glStatus != GL_FRAMEBUFFER_COMPLETE)
|
|
|
|
|
{
|
|
|
|
|
MessageBox(NULL, _T("Cannot initialize output framebuffer."), _T("OpenGL initialization error."), MB_OK);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
|
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
|
@@ -991,7 +1082,7 @@ void OpenGLComposite::VideoFrameArrived(IDeckLinkVideoInputFrame* inputFrame, bo
|
|
|
|
|
{
|
|
|
|
|
mHasNoInputSource = hasNoInputSource;
|
|
|
|
|
if (mRuntimeHost)
|
|
|
|
|
mRuntimeHost->SetSignalStatus(!hasNoInputSource, mFrameWidth, mFrameHeight, mDisplayModeName);
|
|
|
|
|
mRuntimeHost->SetSignalStatus(!hasNoInputSource, mInputFrameWidth, mInputFrameHeight, mInputDisplayModeName);
|
|
|
|
|
|
|
|
|
|
if (mHasNoInputSource)
|
|
|
|
|
return; // don't transfer texture when there's no input
|
|
|
|
|
@@ -1030,7 +1121,7 @@ void OpenGLComposite::VideoFrameArrived(IDeckLinkVideoInputFrame* inputFrame, bo
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, mCaptureTexture);
|
|
|
|
|
|
|
|
|
|
// NULL for last arg indicates use current GL_PIXEL_UNPACK_BUFFER target as texture data
|
|
|
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, mFrameWidth/2, mFrameHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
|
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, mInputFrameWidth / 2, mInputFrameHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
|
|
|
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
|
|
|
|
@@ -1064,6 +1155,10 @@ void OpenGLComposite::PlayoutFrameCompleted(IDeckLinkVideoFrame* completedFrame,
|
|
|
|
|
VideoFrameTransfer::beginTextureInUse(VideoFrameTransfer::GPUtoCPU);
|
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, mIdFrameBuf);
|
|
|
|
|
renderEffect();
|
|
|
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, mIdFrameBuf);
|
|
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mOutputFrameBuf);
|
|
|
|
|
glBlitFramebuffer(0, 0, mInputFrameWidth, mInputFrameHeight, 0, 0, mOutputFrameWidth, mOutputFrameHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, mOutputFrameBuf);
|
|
|
|
|
glFlush();
|
|
|
|
|
if (mFastTransferExtensionAvailable)
|
|
|
|
|
VideoFrameTransfer::endTextureInUse(VideoFrameTransfer::GPUtoCPU);
|
|
|
|
|
@@ -1101,7 +1196,7 @@ void OpenGLComposite::PlayoutFrameCompleted(IDeckLinkVideoFrame* completedFrame,
|
|
|
|
|
// Finished with mCaptureTexture
|
|
|
|
|
VideoFrameTransfer::endTextureInUse(VideoFrameTransfer::CPUtoGPU);
|
|
|
|
|
|
|
|
|
|
if (! mPlayoutAllocator->transferFrame(pFrame, mFBOTexture))
|
|
|
|
|
if (! mPlayoutAllocator->transferFrame(pFrame, mOutputTexture))
|
|
|
|
|
OutputDebugStringA("Playback: transferFrame() failed\n");
|
|
|
|
|
|
|
|
|
|
paintGL();
|
|
|
|
|
@@ -1111,8 +1206,8 @@ void OpenGLComposite::PlayoutFrameCompleted(IDeckLinkVideoFrame* completedFrame,
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, mIdFrameBuf);
|
|
|
|
|
glReadPixels(0, 0, mFrameWidth, mFrameHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pFrame);
|
|
|
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, mOutputFrameBuf);
|
|
|
|
|
glReadPixels(0, 0, mOutputFrameWidth, mOutputFrameHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pFrame);
|
|
|
|
|
paintGL();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1158,7 +1253,7 @@ bool OpenGLComposite::Start()
|
|
|
|
|
|
|
|
|
|
void* pFrame;
|
|
|
|
|
outputVideoFrameBuffer->GetBytes((void**)&pFrame);
|
|
|
|
|
memset(pFrame, 0, outputVideoFrame->GetRowBytes() * mFrameHeight); // 0 is black in RGBA format
|
|
|
|
|
memset(pFrame, 0, outputVideoFrame->GetRowBytes() * mOutputFrameHeight); // 0 is black in BGRA format
|
|
|
|
|
|
|
|
|
|
outputVideoFrameBuffer->EndAccess(bmdBufferAccessWrite);
|
|
|
|
|
outputVideoFrameBuffer->Release();
|
|
|
|
|
@@ -1347,7 +1442,7 @@ bool OpenGLComposite::compileSingleLayerProgram(const RuntimeRenderState& state,
|
|
|
|
|
|
|
|
|
|
bool OpenGLComposite::compileLayerPrograms(int errorMessageSize, char* errorMessage)
|
|
|
|
|
{
|
|
|
|
|
const std::vector<RuntimeRenderState> layerStates = mRuntimeHost ? mRuntimeHost->GetLayerRenderStates(mFrameWidth, mFrameHeight) : std::vector<RuntimeRenderState>();
|
|
|
|
|
const std::vector<RuntimeRenderState> layerStates = mRuntimeHost ? mRuntimeHost->GetLayerRenderStates(mInputFrameWidth, mInputFrameHeight) : std::vector<RuntimeRenderState>();
|
|
|
|
|
std::string temporalError;
|
|
|
|
|
if (!validateTemporalTextureUnitBudget(layerStates, temporalError))
|
|
|
|
|
{
|
|
|
|
|
@@ -1650,7 +1745,7 @@ bool OpenGLComposite::createHistoryRing(HistoryRing& ring, unsigned effectiveLen
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mFrameWidth, mFrameHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mInputFrameWidth, mInputFrameHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
|
|
|
|
|
|
|
|
glGenFramebuffers(1, &slot.framebuffer);
|
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, slot.framebuffer);
|
|
|
|
|
@@ -1768,7 +1863,7 @@ void OpenGLComposite::pushFramebufferToHistoryRing(GLuint sourceFramebuffer, His
|
|
|
|
|
HistorySlot& targetSlot = ring.slots[ring.nextWriteIndex];
|
|
|
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, sourceFramebuffer);
|
|
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, targetSlot.framebuffer);
|
|
|
|
|
glBlitFramebuffer(0, 0, mFrameWidth, mFrameHeight, 0, 0, mFrameWidth, mFrameHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
|
|
|
|
glBlitFramebuffer(0, 0, mInputFrameWidth, mInputFrameHeight, 0, 0, mInputFrameWidth, mInputFrameHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
|
|
|
|
ring.nextWriteIndex = (ring.nextWriteIndex + 1) % ring.slots.size();
|
|
|
|
|
ring.filledCount = std::min<std::size_t>(ring.filledCount + 1, ring.slots.size());
|
|
|
|
|
}
|
|
|
|
|
@@ -1837,12 +1932,12 @@ void OpenGLComposite::renderEffect()
|
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
|
renderDecodePass();
|
|
|
|
|
|
|
|
|
|
const std::vector<RuntimeRenderState> layerStates = mRuntimeHost ? mRuntimeHost->GetLayerRenderStates(mFrameWidth, mFrameHeight) : std::vector<RuntimeRenderState>();
|
|
|
|
|
const std::vector<RuntimeRenderState> layerStates = mRuntimeHost ? mRuntimeHost->GetLayerRenderStates(mInputFrameWidth, mInputFrameHeight) : std::vector<RuntimeRenderState>();
|
|
|
|
|
if (layerStates.empty() || mLayerPrograms.empty())
|
|
|
|
|
{
|
|
|
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, mDecodeFrameBuf);
|
|
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mIdFrameBuf);
|
|
|
|
|
glBlitFramebuffer(0, 0, mFrameWidth, mFrameHeight, 0, 0, mFrameWidth, mFrameHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
|
|
|
|
glBlitFramebuffer(0, 0, mInputFrameWidth, mInputFrameHeight, 0, 0, mInputFrameWidth, mInputFrameHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, mIdFrameBuf);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
@@ -1874,7 +1969,7 @@ void OpenGLComposite::renderEffect()
|
|
|
|
|
void OpenGLComposite::renderShaderProgram(GLuint sourceTexture, GLuint destinationFrameBuffer, const LayerProgram& layerProgram, const RuntimeRenderState& state)
|
|
|
|
|
{
|
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, destinationFrameBuffer);
|
|
|
|
|
glViewport(0, 0, mFrameWidth, mFrameHeight);
|
|
|
|
|
glViewport(0, 0, mInputFrameWidth, mInputFrameHeight);
|
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
|
glActiveTexture(GL_TEXTURE0 + kDecodedVideoTextureUnit);
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, sourceTexture);
|
|
|
|
|
@@ -1908,7 +2003,7 @@ void OpenGLComposite::renderShaderProgram(GLuint sourceTexture, GLuint destinati
|
|
|
|
|
void OpenGLComposite::renderDecodePass()
|
|
|
|
|
{
|
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, mDecodeFrameBuf);
|
|
|
|
|
glViewport(0, 0, mFrameWidth, mFrameHeight);
|
|
|
|
|
glViewport(0, 0, mInputFrameWidth, mInputFrameHeight);
|
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
|
glActiveTexture(GL_TEXTURE0 + kPackedVideoTextureUnit);
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, mCaptureTexture);
|
|
|
|
|
@@ -1918,9 +2013,9 @@ void OpenGLComposite::renderDecodePass()
|
|
|
|
|
const GLint packedResolutionLocation = glGetUniformLocation(mDecodeProgram, "uPackedVideoResolution");
|
|
|
|
|
const GLint decodedResolutionLocation = glGetUniformLocation(mDecodeProgram, "uDecodedVideoResolution");
|
|
|
|
|
if (packedResolutionLocation >= 0)
|
|
|
|
|
glUniform2f(packedResolutionLocation, static_cast<float>(mFrameWidth / 2), static_cast<float>(mFrameHeight));
|
|
|
|
|
glUniform2f(packedResolutionLocation, static_cast<float>(mInputFrameWidth / 2), static_cast<float>(mInputFrameHeight));
|
|
|
|
|
if (decodedResolutionLocation >= 0)
|
|
|
|
|
glUniform2f(decodedResolutionLocation, static_cast<float>(mFrameWidth), static_cast<float>(mFrameHeight));
|
|
|
|
|
glUniform2f(decodedResolutionLocation, static_cast<float>(mInputFrameWidth), static_cast<float>(mInputFrameHeight));
|
|
|
|
|
|
|
|
|
|
glDrawArrays(GL_TRIANGLES, 0, 3);
|
|
|
|
|
|
|
|
|
|
|