Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OPENTOK-51009: Create sample app for Qt #33

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions Qt-Sample-App/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
cmake_minimum_required(VERSION 3.12)

project(Qt-Sample-App VERSION 0.1 LANGUAGES CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Qt6 COMPONENTS OpenGL OpenGLWidgets Widgets Multimedia MultimediaWidgets REQUIRED)

set(PROJECT_SOURCES
main.cpp
mainwindow.cpp
mainwindow.h
mainwindow.ui
glvideowidget.cpp
glvideowidget.h
participant.h
qtsession.h
qtsession.cpp
qtpublisher.h
qtpublisher.cpp
qtsubscriber.h
qtsubscriber.cpp
)

add_executable(qt_sample_app
${PROJECT_SOURCES}
)

target_include_directories(qt_sample_app PRIVATE /home/parallels/opentok/include)
target_link_directories(qt_sample_app PRIVATE /home/parallels/opentok/lib)
find_library(OTK opentok PATHS /home/parallels/opentok/lib)
target_link_libraries(qt_sample_app Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::OpenGLWidgets Qt${QT_VERSION_MAJOR}::OpenGL Qt${QT_VERSION_MAJOR}::Multimedia Qt${QT_VERSION_MAJOR}::MultimediaWidgets ${OTK})

if(QT_VERSION_MAJOR EQUAL 6)
qt_finalize_executable(Qt-Sample-App)
endif()
165 changes: 165 additions & 0 deletions Qt-Sample-App/glvideowidget.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
#include "glvideowidget.h"
#include <algorithm>

namespace {
QVector<GLfloat> GetVertexData(float ratio)
{
const auto x = std::min(1.0f, ratio);
const auto y = std::min(1.0f, 1 / ratio);
return {-x, y, 0.0, 0.0, x, y, 1.0, 0.0, x, -y, 1.0, 1.0, -x, -y, 0.0, 1.0};
}
} // namespace

constexpr const char *vertexSource = "#version 330\n"
"layout(location = 0) in vec2 position;\n"
"layout(location = 1) in vec2 texCoord;\n"
"out vec4 texc;\n"
"void main( void )\n"
"{\n"
" gl_Position = vec4(position, 0.0, 1.0);\n"
" texc = vec4(texCoord, 0.0, 1.0);\n"
"}\n";

constexpr const char *fragmentSource = "#version 330\n"
"uniform sampler2D tex;\n"
"in vec4 texc;\n"
"out vec4 fragColor;\n"
"void main( void )\n"
"{\n"
" fragColor = texture(tex, texc.st);\n"
"}\n";

GLVideoWidget::GLVideoWidget(QWidget *parent) : QOpenGLWidget(parent), texture(0) {}

void GLVideoWidget::initializeGL()
{
initializeOpenGLFunctions();
glClearColor(0.0, 0.0, 0.0, 1.0);

// shader
shaderProgram = new QOpenGLShaderProgram;
shaderProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexSource);
shaderProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentSource);

// bind location for the vertex shader
shaderProgram->bindAttributeLocation("position", 0);
shaderProgram->link();
shaderProgram->bind();

// Vertex array
vaoQuad.create();
vaoQuad.bind();

// Vertex buffer
vboQuad.create();
vboQuad.setUsagePattern(QOpenGLBuffer::StaticDraw);
vboQuad.bind();
const auto vertexData = GetVertexData(1.0f);
vboQuad.allocate(vertexData.constData(), vertexData.count() * sizeof(GLfloat));

// Connect inputs to the shader
shaderProgram->enableAttributeArray(0);
shaderProgram->enableAttributeArray(1);
shaderProgram->setAttributeBuffer(0, GL_FLOAT, 0, 2, 4 * sizeof(GLfloat));
shaderProgram->setAttributeBuffer(1, GL_FLOAT, 2 * sizeof(GLfloat), 2, 4 * sizeof(GLfloat));

vaoQuad.release();
vboQuad.release();
shaderProgram->release();
}

void GLVideoWidget::resizeGL(int w, int h)
{
if (lastImageHeight == 0 || lastImageWidth == 0 || w == 0 || h == 0 || vboQuad.size() == 0)
return;
// Force update vertex data when we resize the widget
vboQuad.bind();
auto ptr = vboQuad.map(QOpenGLBuffer::WriteOnly);
const auto ratio = static_cast<float>(lastImageWidth) / lastImageHeight * height() / width();

const auto vertexData = GetVertexData(ratio);
memcpy(ptr, vertexData.constData(), vertexData.count() * sizeof(GLfloat));
vboQuad.unmap();
vboQuad.release();
}

void GLVideoWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (texture == nullptr)
return;
if (!shaderProgram->bind())
return;
vaoQuad.bind();
if (texture->isCreated())
texture->bind();
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
vaoQuad.release();
shaderProgram->release();
}

void GLVideoWidget::setTexture(const otc_video_frame *frame)
{
if ((otc_video_frame_get_width(frame) != lastImageWidth
|| otc_video_frame_get_height(frame) != lastImageHeight)) {
vboQuad.bind();
auto ptr = vboQuad.map(QOpenGLBuffer::WriteOnly);
const auto ratio = static_cast<float>(otc_video_frame_get_width(frame))
/ otc_video_frame_get_height(frame) * (float) height() / (float) width();

const auto vertexData = GetVertexData(ratio);
if (ptr != nullptr)
memcpy(ptr, vertexData.constData(), vertexData.count() * sizeof(GLfloat));
vboQuad.unmap();
vboQuad.release();

resizeTexture(frame);
lastImageWidth = otc_video_frame_get_width(frame);
lastImageHeight = otc_video_frame_get_height(frame);
}
setTextureData(frame);
update();
}

void GLVideoWidget::resizeTexture(const otc_video_frame *image)
{
if (image == nullptr) {
return;
}
if (texture != nullptr) {
delete texture;
}
makeCurrent();
texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
qWarning("QOpenGLTexture::setData() requires a valid current context");
return;
}
if (context->isOpenGLES() && context->format().majorVersion() < 3)
texture->setFormat(QOpenGLTexture::RGBAFormat);
else
texture->setFormat(QOpenGLTexture::RGBA8_UNorm);
texture->setSize(otc_video_frame_get_width(image), otc_video_frame_get_height(image));
texture->setMipLevels(texture->maximumMipLevels());
texture->allocateStorage(QOpenGLTexture::BGRA, QOpenGLTexture::UInt8);
doneCurrent();
}

void GLVideoWidget::setTextureData(const otc_video_frame *image)
{
QOpenGLPixelTransferOptions uploadOptions;
uploadOptions.setAlignment(1);
texture->setData(0,
QOpenGLTexture::BGRA,
QOpenGLTexture::UInt8,
otc_video_frame_get_buffer(image),
&uploadOptions);
}

void GLVideoWidget::frame(std::shared_ptr<const otc_video_frame> frame)
{
if (frame != nullptr) {
setTexture(frame.get());
}
}
57 changes: 57 additions & 0 deletions Qt-Sample-App/glvideowidget.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#ifndef GLVIDEOWIDGET_H
#define GLVIDEOWIDGET_H

#include "opentok.h"
#include <iostream>

#include <QMatrix4x4>
#include <QOpenGLBuffer>
#include <QOpenGLFunctions>
#include <QOpenGLPixelTransferOptions>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLWidget>

class GLVideoWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
GLVideoWidget(QWidget *parent);
~GLVideoWidget()
{
makeCurrent();

delete shaderProgram;
delete texture;

vboQuad.destroy();
vaoQuad.destroy();

doneCurrent();
}
void setTexture(const otc_video_frame *frame);

protected:
void paintGL() override;
void resizeGL(int w, int h) override;
void initializeGL() override;

private:
QOpenGLVertexArrayObject vaoQuad;
QOpenGLBuffer vboQuad;
QOpenGLShaderProgram *shaderProgram;

QOpenGLTexture *texture;

void resizeTexture(const otc_video_frame *image);
void setTextureData(const otc_video_frame *image);

int lastImageWidth = 0;
int lastImageHeight = 0;

public slots:
void frame(std::shared_ptr<const otc_video_frame> frame);
};

#endif // GLVIDEOWIDGET_H
28 changes: 28 additions & 0 deletions Qt-Sample-App/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include "mainwindow.h"

#include <QApplication>

#include <iostream>

int main(int argc, char *argv[])
{
QSurfaceFormat fmt;
fmt.setDepthBufferSize(24);
fmt.setVersion(3, 3);
fmt.setProfile(QSurfaceFormat::CoreProfile);
QSurfaceFormat::setDefaultFormat(fmt);
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
QApplication a(argc, argv);

if (otc_init(nullptr) != OTC_SUCCESS) {
std::cout << "Could not init OpenTok library" << std::endl;
}
otc_log_enable(OTC_LOG_LEVEL_TRACE);

MainWindow w;
w.show();
return a.exec();
otc_destroy();
}

#include "main.moc"
89 changes: 89 additions & 0 deletions Qt-Sample-App/mainwindow.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#include "mainwindow.h"
#include "./ui_mainwindow.h"

#include "qtsession.h"

constexpr const char *kApiKey = "";
constexpr const char *kSession = "";
constexpr const char *kToken = "";
constexpr const char *kConnecting = "Connecting";
constexpr const char *kConnect = "Connect";
constexpr const char *kDisconnect = "Disconnect";

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->lineEdit->setText(kApiKey);
ui->lineEdit_2->setText(kSession);
ui->lineEdit_3->setText(kToken);
connect(ui->pushButton, &QPushButton::clicked, this, &MainWindow::connectSession);
connect(ui->pushButton_2, &QPushButton::clicked, this, &MainWindow::newSessionWindow);
}

MainWindow::~MainWindow()
{
const auto session = findChild<opentok_qt::Session *>();
if (session != nullptr)
delete session;
delete ui;
}

void MainWindow::closeEvent(QCloseEvent *event)
{
const auto session = findChild<opentok_qt::Session *>();
if (session != nullptr)
delete session;
QMainWindow::closeEvent(event);
}

void MainWindow::connectSession()
{
if (findChild<opentok_qt::Session *>() == nullptr) {
new opentok_qt::Session(this, ui->lineEdit->text(), ui->lineEdit_2->text(), ui->lineEdit_3->text());
ui->pushButton->setDisabled(true);
ui->pushButton->setText(kConnecting);
} else {
destroySession();
}
}

void MainWindow::destroySession()
{
const auto session = findChild<opentok_qt::Session *>();
session->setParent(nullptr);
session->deleteLater();
}

void MainWindow::sessionConnected()
{
ui->pushButton->setText(kDisconnect);
ui->pushButton->setEnabled(true);
}

void MainWindow::sessionError()
{
if (ui->pushButton->text() == kConnecting) {
ui->pushButton->setText(kConnect);
ui->pushButton->setEnabled(true);
destroySession();
}
}

void MainWindow::sessionDisconnected()
{
destroySession();
ui->pushButton->setText(kConnect);
ui->pushButton->setEnabled(true);
}

void MainWindow::newSessionWindow()
{
auto newWindow = new MainWindow(nullptr);
newWindow->setAttribute(Qt::WA_DeleteOnClose, true);
newWindow->show();
}

void MainWindow::addMessage(QString message)
{
ui->textEdit->append(message);
}
Loading