// Draw the captured video frame texture onto a box, rendering to the off-screen frame buffer. // Read the rendered scene back from the frame buffer and schedule it for playout. void OpenGLComposite::PlayoutFrameCompleted(IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult completionResult) { EnterCriticalSection(&pMutex); // Get the first frame from the queue IDeckLinkMutableVideoFrame* outputVideoFrame = mDLOutputVideoFrameQueue.front(); mDLOutputVideoFrameQueue.push_back(outputVideoFrame); mDLOutputVideoFrameQueue.pop_front(); void* pFrame; outputVideoFrame->GetBytes(&pFrame); long rowbytes = outputVideoFrame->GetRowBytes(); long height = outputVideoFrame->GetHeight(); long memSize = rowbytes * height; // make GL context current in this thread wglMakeCurrent( hGLDC, hGLRC ); // Draw OpenGL scene to the off-screen frame buffer glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mIdFrameBuf); // Setup view and projection GLfloat aspectRatio = (GLfloat)mFrameWidth / (GLfloat)mFrameHeight; glViewport (0, 0, mFrameWidth, mFrameHeight); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); gluPerspective( 45.0f, aspectRatio, 0.1f, 100.0f ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glScalef( aspectRatio, 1.0f, 1.0f ); // Scale x for correct aspect ratio glTranslatef( 0.0f, 0.0f, -4.0f ); // Move into screen glRotatef( mRotateAngle, 1.0f, 1.0f, 1.0f ); // Rotate model around a vector mRotateAngle -= mRotateAngleRate; // update the rotation angle for next iteration glFinish(); // Ensure changes to GL state are complete // Draw a colourful frame around the front face of the box // (provides a pleasing nesting effect when you connect the playout output to the capture input) glBegin(GL_QUAD_STRIP); glColor3f( 1.0f, 0.0f, 0.0f ); glVertex3f( 1.2f, 1.2f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); glColor3f( 0.0f, 0.0f, 1.0f ); glVertex3f( 1.2f, -1.2f, 1.0f); glVertex3f( 1.0f, -1.0f, 1.0f); glColor3f( 0.0f, 1.0f, 0.0f ); glVertex3f(-1.2f, -1.2f, 1.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glColor3f( 1.0f, 1.0f, 0.0f ); glVertex3f(-1.2f, 1.2f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glColor3f( 1.0f, 0.0f, 0.0f ); glVertex3f( 1.2f, 1.2f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); glEnd(); if (mHasNoInputSource) { // Draw a big X when no input is available on capture glBegin( GL_QUADS ); glColor3f( 1.0f, 0.0f, 1.0f ); glVertex3f( 0.8f, 0.9f, 1.0f ); glVertex3f( 0.9f, 0.8f, 1.0f ); glColor3f( 1.0f, 1.0f, 0.0f ); glVertex3f( -0.8f, -0.9f, 1.0f ); glVertex3f( -0.9f, -0.8f, 1.0f ); glColor3f( 1.0f, 0.0f, 1.0f ); glVertex3f( -0.8f, 0.9f, 1.0f ); glVertex3f( -0.9f, 0.8f, 1.0f ); glColor3f( 1.0f, 1.0f, 0.0f ); glVertex3f( 0.8f, -0.9f, 1.0f ); glVertex3f( 0.9f, -0.8f, 1.0f ); glEnd(); } else { if (mFastTransferExtensionAvailable) { // Signal that we're about to draw using mCaptureTexture onto mFBOTexture mCaptureAllocator->beginTextureInUse(); } // Pass texture unit 0 to the fragment shader as a uniform variable glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, mCaptureTexture); glUseProgram(mProgram); GLint locUYVYtex = glGetUniformLocation(mProgram, "UYVYtex"); glUniform1i(locUYVYtex, 0); // Bind texture unit 0 // Draw front and back faces of box applying video texture to each face glBegin(GL_QUADS); glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f ); // Top right of front side glTexCoord2f(0.0f, 0.0f); glVertex3f( -1.0f, 1.0f, 1.0f ); // Top left of front side glTexCoord2f(0.0f, 1.0f); glVertex3f( -1.0f, -1.0f, 1.0f ); // Bottom left of front side glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, -1.0f, 1.0f ); // Bottom right of front side glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f ); // Top right of back side glTexCoord2f(0.0f, 1.0f); glVertex3f( -1.0f, -1.0f, -1.0f ); // Top left of back side glTexCoord2f(0.0f, 0.0f); glVertex3f( -1.0f, 1.0f, -1.0f ); // Bottom left of back side glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, -1.0f ); // Bottom right of back side glEnd(); // Draw left and right sides of box with partially transparent video texture glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBegin(GL_QUADS); glTexCoord2f(0.1f, 0.0f); glVertex3f( -1.0f, 1.0f, 1.0f ); // Top right of left side glTexCoord2f(1.0f, 0.0f); glVertex3f( -1.0f, 1.0f, -1.0f ); // Top left of left side glTexCoord2f(1.0f, 1.0f); glVertex3f( -1.0f, -1.0f, -1.0f ); // Bottom left of left side glTexCoord2f(0.1f, 1.0f); glVertex3f( -1.0f, -1.0f, 1.0f ); // Bottom right of left side glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, -1.0f ); // Top right of right side glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f ); // Top left of right side glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, 1.0f ); // Bottom left of right side glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f ); // Bottom right of right side glEnd(); glDisable(GL_BLEND); glUseProgram(0); glDisable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); } if (mFastTransferExtensionAvailable) { // Finished with mCaptureTexture mCaptureAllocator->endTextureInUse(); if (! mPlayoutAllocator->transferFrame(pFrame, mFBOTexture)) OutputDebugStringA("Playback: transferFrame() failed\n"); paintGL(); // Wait for transfer to system memory to complete mPlayoutAllocator->waitForTransferComplete(pFrame); } else { glReadPixels(0, 0, mFrameWidth, mFrameHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pFrame); paintGL(); } // If the last completed frame was late or dropped, bump the scheduled time further into the future if (completionResult == bmdOutputFrameDisplayedLate || completionResult == bmdOutputFrameDropped) mTotalPlayoutFrames += 2; // Schedule the next frame for playout HRESULT hr = mDLOutput->ScheduleVideoFrame(outputVideoFrame, (mTotalPlayoutFrames * mFrameDuration), mFrameDuration, mFrameTimescale); if (SUCCEEDED(hr)) mTotalPlayoutFrames++; wglMakeCurrent( NULL, NULL ); LeaveCriticalSection(&pMutex); }