Decklink SDK & QtQuick
Posted: Thu May 23, 2019 2:48 pm
Hello,
I successfully used the Decklink SDK samples to display a video from my Decklink card to a DeckLinkOpenGLWidget (subclass of QOpenGLWidget, from the SDK samples).
Now I'm trying to do the same in a QtQuick application. I'm able to draw some OpenGL stuff, so that part is working, but I can't get the video to show.
Here are some pieces of code:
VideoView.h
VideoView.cpp
VideoViewRenderer.h
VideoViewRenderer.cpp
The DrawFrame is called, the problem is on the drawing part. If I replace the initOpenGl & paint functions by those:
The triangle is correctly displayed.
Any idea?
Thanks in advance!
I successfully used the Decklink SDK samples to display a video from my Decklink card to a DeckLinkOpenGLWidget (subclass of QOpenGLWidget, from the SDK samples).
Now I'm trying to do the same in a QtQuick application. I'm able to draw some OpenGL stuff, so that part is working, but I can't get the video to show.
Here are some pieces of code:
VideoView.h
- Code: Select all
class VideoView : public QQuickItem {
Q_OBJECT
public:
VideoView();
public slots:
void sync();
void cleanup();
void refresh();
private slots:
void handleWindowChanged(QQuickWindow *win);
private:
VideoViewRenderer *m_renderer;
};
VideoView.cpp
- Code: Select all
VideoView::VideoView()
{
m_renderer = nullptr;
connect(this, &QQuickItem::windowChanged, this, &VideoView::handleWindowChanged);
}
void VideoView::handleWindowChanged(QQuickWindow *win)
{
if (win) {
connect(win, &QQuickWindow::beforeSynchronizing, this, &VideoView::sync, Qt::DirectConnection);
connect(win, &QQuickWindow::sceneGraphInvalidated, this, &VideoView::cleanup, Qt::DirectConnection);
// If we allow QML to do the clearing, they would clear what we paint and nothing would show.
win->setClearBeforeRendering(false);
}
}
void VideoView::cleanup()
{
if (m_renderer) {
delete m_renderer;
m_renderer = nullptr;
}
}
void VideoView::sync()
{
if (!m_renderer) {
m_renderer = new VideoViewRenderer(this, window()->size() * window()->devicePixelRatio(), window());
connect(window(), &QQuickWindow::beforeRendering, m_renderer, &VideoViewRenderer::paint, Qt::DirectConnection);
}
}
void VideoView::refresh() {
update();
}
VideoViewRenderer.h
- Code: Select all
class VideoView;
class VideoViewRenderer : public QObject, protected QOpenGLFunctions, public IDeckLinkScreenPreviewCallback {
Q_OBJECT
private:
void initOpenGl();
QSize m_viewportSize;
QQuickWindow* m_window;
QAtomicInt m_refCount;
QMutex m_mutex;
IDeckLinkGLScreenPreviewHelper* m_deckLinkScreenPreviewHelper;
DeckLinkDeviceDiscovery* m_deckLinkDiscovery;
DeckLinkInputDevice* m_selectedDevice;
VideoView* m_videoView;
QOpenGLShaderProgram* m_program = nullptr;
public:
explicit VideoViewRenderer(VideoView* videoView, QSize viewportSize, QQuickWindow* window);
~VideoViewRenderer() override;
// IUnknown
HRESULT QueryInterface(REFIID iid, LPVOID *ppv) override;
ULONG AddRef() override;
ULONG Release() override;
// IDeckLinkScreenPreviewCallback
HRESULT DrawFrame(IDeckLinkVideoFrame* theFrame) override;
// QObject
void customEvent(QEvent* event) override;
public slots:
void paint();
};
VideoViewRenderer.cpp
- Code: Select all
VideoViewRenderer::VideoViewRenderer(VideoView* videoView, QSize viewportSize, QQuickWindow* window)
{
m_videoView = videoView;
m_viewportSize = viewportSize;
m_window = window;
m_selectedDevice = nullptr;
m_deckLinkScreenPreviewHelper = CreateOpenGLScreenPreviewHelper();
if (m_deckLinkScreenPreviewHelper != nullptr)
{
// Create and initialise DeckLink device discovery and profile objects
m_deckLinkDiscovery = new DeckLinkDeviceDiscovery(this);
if (m_deckLinkDiscovery == nullptr || !m_deckLinkDiscovery->enable())
{
std::cout << "This application requires the DeckLink drivers installed. Please install the Blackmagic DeckLink drivers to use the features of this application." << std::endl;
}
else
{
initOpenGl();
}
}
}
VideoViewRenderer::~VideoViewRenderer()
{
if (m_deckLinkDiscovery != nullptr)
{
m_deckLinkDiscovery->Release();
m_deckLinkDiscovery = nullptr;
}
}
void VideoViewRenderer::initOpenGl()
{
m_mutex.lock();
m_deckLinkScreenPreviewHelper->InitializeGL();
QOpenGLFunctions::initializeOpenGLFunctions();
m_mutex.unlock();
}
void VideoViewRenderer::paint()
{
m_mutex.lock();
glLoadIdentity();
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
m_deckLinkScreenPreviewHelper->PaintGL();
m_mutex.unlock();
}
/// IUnknown methods
HRESULT VideoViewRenderer::QueryInterface(REFIID, LPVOID *ppv)
{
*ppv = nullptr;
return E_NOINTERFACE;
}
ULONG VideoViewRenderer::AddRef ()
{
return (ULONG) m_refCount.fetchAndAddAcquire(1);
}
ULONG VideoViewRenderer::Release()
{
auto newRefValue = (ULONG) m_refCount.fetchAndAddAcquire(-1);
if (newRefValue == 0)
{
delete this;
return 0;
}
return newRefValue;
}
/// IDeckLinkScreenPreviewCallback methods
HRESULT VideoViewRenderer::DrawFrame(IDeckLinkVideoFrame* theFrame)
{
if (m_deckLinkScreenPreviewHelper != nullptr)
{
m_deckLinkScreenPreviewHelper->SetFrame(theFrame);
m_videoView->refresh();
}
return S_OK;
}
/// QObject
void VideoViewRenderer::customEvent(QEvent *event)
{
if (event->type() == kAddDeviceEvent)
{
auto* discoveryEvent = dynamic_cast<DeckLinkDeviceDiscoveryEvent*>(event);
auto* newDevice = new DeckLinkInputDevice(this, discoveryEvent->DeckLink());
// Initialise new DeckLinkDevice object
if (!newDevice->Init())
{
// Device does not have IDeckLinkInput interface, eg it is a DeckLink Mini Monitor
newDevice->Release();
return;
}
if (newDevice->GetDeviceName() == "DeckLink 8K Pro (2)")
{
m_selectedDevice = newDevice;
m_selectedDevice->StartCapture(bmdMode4K2160p25, this, true);
}
}
}
The DrawFrame is called, the problem is on the drawing part. If I replace the initOpenGl & paint functions by those:
- Code: Select all
void VideoViewRenderer::initOpenGl()
{
QOpenGLFunctions::initializeOpenGLFunctions();
const char* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
const char* fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";
m_program = new QOpenGLShaderProgram();
m_program->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex,vertexShaderSource);
m_program->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
m_program->bindAttributeLocation("vertices", 0);
m_program->link();
}
- Code: Select all
void VideoViewRenderer::paint()
{
glClearColor(0.0f, 0.4f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
m_program->bind();
m_program->enableAttributeArray(0);
float values[] = {
-0.5f, -0.5f, 0.0f, // left
0.5f, -0.5f, 0.0f, // right
0.0f, 0.5f, 0.0f // top
};
m_program->setAttributeArray(0, GL_FLOAT, values, 3);
m_program->setUniformValue("t", (float) 0);
glViewport(0, 0, m_viewportSize.width(), m_viewportSize.height());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);
m_program->disableAttributeArray(0);
m_program->release();
}
The triangle is correctly displayed.
Any idea?
Thanks in advance!