#include "OpenGLVideoIOBridge.h" #include "OpenGLRenderer.h" #include "RuntimeHost.h" #include #include OpenGLVideoIOBridge::OpenGLVideoIOBridge( VideoIODevice& videoIO, OpenGLRenderer& renderer, OpenGLRenderPipeline& renderPipeline, RuntimeHost& runtimeHost, CRITICAL_SECTION& mutex, HDC hdc, HGLRC hglrc) : mVideoIO(videoIO), mRenderer(renderer), mRenderPipeline(renderPipeline), mRuntimeHost(runtimeHost), mMutex(mutex), mHdc(hdc), mHglrc(hglrc) { } void OpenGLVideoIOBridge::RecordFramePacing(VideoIOCompletionResult completionResult) { const auto now = std::chrono::steady_clock::now(); if (mLastPlayoutCompletionTime != std::chrono::steady_clock::time_point()) { mCompletionIntervalMilliseconds = std::chrono::duration_cast>(now - mLastPlayoutCompletionTime).count(); if (mSmoothedCompletionIntervalMilliseconds <= 0.0) mSmoothedCompletionIntervalMilliseconds = mCompletionIntervalMilliseconds; else mSmoothedCompletionIntervalMilliseconds = mSmoothedCompletionIntervalMilliseconds * 0.9 + mCompletionIntervalMilliseconds * 0.1; if (mCompletionIntervalMilliseconds > mMaxCompletionIntervalMilliseconds) mMaxCompletionIntervalMilliseconds = mCompletionIntervalMilliseconds; } mLastPlayoutCompletionTime = now; if (completionResult == VideoIOCompletionResult::DisplayedLate) ++mLateFrameCount; else if (completionResult == VideoIOCompletionResult::Dropped) ++mDroppedFrameCount; else if (completionResult == VideoIOCompletionResult::Flushed) ++mFlushedFrameCount; mRuntimeHost.TrySetFramePacingStats( mCompletionIntervalMilliseconds, mSmoothedCompletionIntervalMilliseconds, mMaxCompletionIntervalMilliseconds, mLateFrameCount, mDroppedFrameCount, mFlushedFrameCount); } void OpenGLVideoIOBridge::VideoFrameArrived(const VideoIOFrame& inputFrame) { const VideoIOState& state = mVideoIO.State(); mRuntimeHost.TrySetSignalStatus(!inputFrame.hasNoInputSource, state.inputFrameSize.width, state.inputFrameSize.height, state.inputDisplayModeName); if (inputFrame.hasNoInputSource || inputFrame.bytes == nullptr) return; // don't transfer texture when there's no input const long textureSize = inputFrame.rowBytes * static_cast(inputFrame.height); // Never let input upload stall the playout/render callback. If the GL bridge // is busy producing an output frame, skip this upload and use the next input. if (!TryEnterCriticalSection(&mMutex)) return; wglMakeCurrent(mHdc, mHglrc); // make OpenGL context current in this thread glPixelStorei(GL_UNPACK_ALIGNMENT, 4); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mRenderer.TextureUploadBuffer()); glBufferData(GL_PIXEL_UNPACK_BUFFER, textureSize, inputFrame.bytes, GL_DYNAMIC_DRAW); glBindTexture(GL_TEXTURE_2D, mRenderer.CaptureTexture()); // NULL for last arg indicates use current GL_PIXEL_UNPACK_BUFFER target as texture data. if (inputFrame.pixelFormat == VideoIOPixelFormat::V210) glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, state.captureTextureWidth, state.inputFrameSize.height, GL_RGBA, GL_UNSIGNED_BYTE, NULL); else glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, state.captureTextureWidth, state.inputFrameSize.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL); glBindTexture(GL_TEXTURE_2D, 0); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); wglMakeCurrent(NULL, NULL); LeaveCriticalSection(&mMutex); } void OpenGLVideoIOBridge::PlayoutFrameCompleted(const VideoIOCompletion& completion) { RecordFramePacing(completion.result); VideoIOOutputFrame outputFrame; if (!mVideoIO.BeginOutputFrame(outputFrame)) return; const VideoIOState& state = mVideoIO.State(); RenderPipelineFrameContext frameContext; frameContext.videoState = state; frameContext.completion = completion; EnterCriticalSection(&mMutex); // make GL context current in this thread wglMakeCurrent(mHdc, mHglrc); mRenderPipeline.RenderFrame(frameContext, outputFrame); wglMakeCurrent(NULL, NULL); LeaveCriticalSection(&mMutex); mVideoIO.EndOutputFrame(outputFrame); mVideoIO.AccountForCompletionResult(completion.result); // Schedule the next frame for playout after the GL bridge is released so // input uploads are not blocked by non-GL output bookkeeping. mVideoIO.ScheduleOutputFrame(outputFrame); }