# SDK Wrappers and GUI Integration
Building custom language wrappers and GUI applications with Zoom Meeting/Video SDKs.
## Overview
The native Zoom SDKs are written in C++. To use them from other languages or with GUI frameworks, you need wrappers or direct integration.
| Platform | Wrapper/Integration | Use Case |
|----------|---------------------|----------|
| Windows | C++/CLI → C# | WPF, WinForms, .NET apps |
| Linux | Native C++ | Qt, GTK desktop apps |
| Linux | Direct C++ | Headless bots, server-side |
## Windows: C++/CLI Wrapper for C#/.NET
### Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ C# Application Layer (WPF / WinForms / .NET) │
│ - UI controls, business logic │
│ - Uses managed ZOOM_SDK_DOTNET_WRAP namespace │
└─────────────────────────────────────────────────────────────┘
↕ Managed/Unmanaged Boundary
┌─────────────────────────────────────────────────────────────┐
│ C++/CLI Wrapper Layer (zoom_sdk_dotnet_wrap.dll) │
│ - Managed ref classes with ^ handles │
│ - Native C++ classes calling SDK │
│ - Event bridging: std::bind → C# delegates │
└─────────────────────────────────────────────────────────────┘
↕ Native C++ Interface
┌─────────────────────────────────────────────────────────────┐
│ Native Zoom SDK (videosdk.dll / sdk.dll) │
│ - Loaded dynamically via sdk_dll_path │
└─────────────────────────────────────────────────────────────┘
```
### Why C++/CLI (Not P/Invoke)?
| Aspect | P/Invoke | C++/CLI |
|--------|----------|---------|
| Complex C++ objects | ❌ Difficult | ✅ Native support |
| Callbacks/delegates | ❌ Manual marshaling | ✅ Automatic bridging |
| Object lifetime | ❌ Manual GC pinning | ✅ Automatic handling |
| std::function patterns | ❌ Not supported | ✅ Works with std::bind |
### Project Structure
```
videosdk-windows-dotnet-quickstart/
├── ZoomVideoSDK.CSharp.sln # Visual Studio solution
├── config.json # Runtime configuration
├── sdk/ # Zoom SDK files
│ └── x64/
│ ├── h/ # C++ headers
│ ├── lib/ # Static libraries (.lib)
│ └── bin/ # Runtime DLLs (.dll)
├── ZoomVideoSDK.Wrapper/ # C++/CLI wrapper project
│ ├── ZoomVideoSDK.Wrapper.vcxproj # CLRSupport=true
│ ├── ZoomSDKManager.h # Managed wrapper class
│ └── ZoomSDKManager.cpp
├── ZoomVideoSDK.WinForms/ # C# WinForms app
│ ├── MainForm.cs
│ └── ZoomSDKInterop.cs
└── ZoomVideoSDK.WPF/ # C# WPF app
├── MainWindow.xaml
└── MainWindow.xaml.cs
```
### C++/CLI Wrapper Implementation
**Project Configuration (.vcxproj)**:
```xml
DynamicLibrary
true
Unicode
```
**Wrapper Class (C++/CLI)**:
```cpp
// ZoomSDKManager.h
#pragma once
using namespace System;
namespace ZoomVideoSDK {
namespace Wrapper {
// Managed delegate (callable from C#)
public delegate void SessionStatusChangedHandler(String^ status, String^ message);
// Managed wrapper class
public ref class ZoomSDKManager sealed {
public:
static property ZoomSDKManager^ Instance {
ZoomSDKManager^ get() { return m_Instance; }
}
bool Initialize(String^ sdkDllPath);
bool JoinSession(String^ sessionName, String^ token, String^ userName);
void LeaveSession();
// Events exposed to C#
event SessionStatusChangedHandler^ SessionStatusChanged;
private:
static ZoomSDKManager^ m_Instance = gcnew ZoomSDKManager;
// Bridge to invoke C# events from native callbacks
void OnSessionStatusChanged(String^ status, String^ message) {
SessionStatusChanged(status, message);
}
};
}
}
```
**Callback Bridging Pattern**:
```cpp
// Native C++ handler class (unmanaged)
class NativeEventHandler {
public:
static NativeEventHandler& GetInst() {
static NativeEventHandler inst;
return inst;
}
void onSessionJoin() {
// Forward to managed wrapper using QMetaObject pattern
ZoomSDKManager::Instance->OnSessionStatusChanged("Joined", "Session joined successfully");
}
};
// Binding native callbacks to handler
void ZoomSDKManager::BindEvents() {
// Use std::bind to connect native callbacks
ZOOM_SDK::GetSessionService().m_cbonSessionJoin =
std::bind(&NativeEventHandler::onSessionJoin, &NativeEventHandler::GetInst());
}
```
### C# Application Usage
**WPF Example**:
```csharp
using ZoomVideoSDK.Wrapper;
public partial class MainWindow : Window
{
private ZoomSDKManager _sdk;
public MainWindow()
{
InitializeComponent();
_sdk = ZoomSDKManager.Instance;
_sdk.SessionStatusChanged += OnSessionStatusChanged;
}
private void JoinButton_Click(object sender, RoutedEventArgs e)
{
bool initialized = _sdk.Initialize(@".\sdk\x64\bin");
if (initialized)
{
_sdk.JoinSession(
sessionName: SessionNameTextBox.Text,
token: JwtTokenTextBox.Text,
userName: UserNameTextBox.Text
);
}
}
private void OnSessionStatusChanged(string status, string message)
{
// Must invoke on UI thread
Dispatcher.Invoke(() => {
StatusLabel.Content = $"{status}: {message}";
});
}
}
```
**WinForms Example**:
```csharp
public partial class MainForm : Form
{
private ZoomSDKManager _sdk;
private void OnSessionStatusChanged(string status, string message)
{
// Must invoke on UI thread
this.Invoke((MethodInvoker)delegate {
statusLabel.Text = $"{status}: {message}";
});
}
}
```
### Build Configuration
**DLL Dependencies**:
```
C# App (managed)
↓ references
ZoomVideoSDK.Wrapper.dll (C++/CLI mixed-mode)
↓ loads dynamically at runtime
videosdk.dll + dependencies (native)
```
**Post-Build Events**:
```batch
REM Copy SDK DLLs to output directory
xcopy /Y "$(SolutionDir)sdk\x64\bin\*.dll" "$(OutDir)"
```
---
## Linux: Qt GUI Integration
### Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ Qt Application (C++) │
│ - Qt Widgets / QML │
│ - Signals/Slots for event handling │
└─────────────────────────────────────────────────────────────┘
↕ Direct C++ calls
┌─────────────────────────────────────────────────────────────┐
│ Native Zoom SDK (libvideosdk.so) │
│ - Video/Audio processing │
│ - Network communication │
└─────────────────────────────────────────────────────────────┘
```
### Project Structure
```
videosdk-linux-qt-quickstart/
├── README.md
├── run_qt_demo.sh # Run script
├── src/
│ ├── CMakeLists.txt # Qt6/Qt5 build config
│ ├── config.json # Session config
│ ├── bin/ # Build output
│ │ └── VideoSDKQtDemo
│ ├── include/ # SDK headers
│ ├── lib/ # SDK libraries
│ └── Source Files:
│ ├── zoom_v-sdk_linux_bot_qt.cpp # Main entry
│ ├── QtMainWindow.h/cpp # Main window
│ ├── QtVideoWidget.h/cpp # Video display
│ ├── QtVideoRenderer.h/cpp # YUV→RGB rendering
│ ├── QtPreviewVideoHandler.h/cpp # Self video
│ └── QtRemoteVideoHandler.h/cpp # Remote video
```
### Qt Video Rendering
```cpp
// QtVideoWidget.h
class QtVideoWidget : public QWidget {
Q_OBJECT
public:
explicit QtVideoWidget(QWidget* parent = nullptr);
void updateFrame(const QImage& frame);
protected:
void paintEvent(QPaintEvent* event) override;
private:
QImage m_currentFrame;
QMutex m_mutex;
};
// QtVideoWidget.cpp
void QtVideoWidget::updateFrame(const QImage& frame) {
QMutexLocker lock(&m_mutex);
m_currentFrame = frame;
update(); // Trigger repaint
}
void QtVideoWidget::paintEvent(QPaintEvent* event) {
QPainter painter(this);
QMutexLocker lock(&m_mutex);
if (!m_currentFrame.isNull()) {
// Scale to fit with aspect ratio
QImage scaled = m_currentFrame.scaled(
size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
// Center the image
int x = (width() - scaled.width()) / 2;
int y = (height() - scaled.height()) / 2;
painter.drawImage(x, y, scaled);
}
}
```
### YUV to RGB Conversion
```cpp
// QtVideoRenderer.cpp
QImage convertYUV420ToRGB(const unsigned char* yuvData, int width, int height) {
QImage image(width, height, QImage::Format_RGB888);
const unsigned char* yPlane = yuvData;
const unsigned char* uPlane = yuvData + width * height;
const unsigned char* vPlane = uPlane + (width * height / 4);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int yIndex = y * width + x;
int uvIndex = (y / 2) * (width / 2) + (x / 2);
int Y = yPlane[yIndex];
int U = uPlane[uvIndex] - 128;
int V = vPlane[uvIndex] - 128;
// ITU-R BT.601 conversion
int R = qBound(0, (int)(Y + 1.402 * V), 255);
int G = qBound(0, (int)(Y - 0.344 * U - 0.714 * V), 255);
int B = qBound(0, (int)(Y + 1.772 * U), 255);
image.setPixel(x, y, qRgb(R, G, B));
}
}
return image;
}
```
### Qt Event Threading
```cpp
// Invoke SDK callbacks on Qt main thread
class VideoCallback : public IZoomVideoSDKRawDataPipeDelegate {
public:
void onRawDataFrameReceived(YUVRawDataI420* data) override {
QImage frame = convertYUV420ToRGB(data->getBuffer(),
data->getWidth(),
data->getHeight());
// Thread-safe UI update using Qt's event system
QMetaObject::invokeMethod(m_videoWidget, [this, frame]() {
m_videoWidget->updateFrame(frame);
}, Qt::QueuedConnection);
}
private:
QtVideoWidget* m_videoWidget;
};
```
### Build with CMake
```cmake
cmake_minimum_required(VERSION 3.14)
project(VideoSDKQtDemo)
set(CMAKE_CXX_STANDARD 17)
# Find Qt
find_package(Qt6 COMPONENTS Core Widgets REQUIRED)
# Or: find_package(Qt5 COMPONENTS Core Widgets REQUIRED)
# Find ALSA for audio
find_package(ALSA REQUIRED)
# Source files
set(SOURCES
zoom_v-sdk_linux_bot_qt.cpp
QtMainWindow.cpp
QtVideoWidget.cpp
QtVideoRenderer.cpp
)
add_executable(VideoSDKQtDemo ${SOURCES})
target_link_libraries(VideoSDKQtDemo
Qt6::Core Qt6::Widgets
${ALSA_LIBRARIES}
${CMAKE_SOURCE_DIR}/lib/libvideosdk.so
)
```
---
## Linux: GTK GUI Integration
### Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ GTKmm Application (C++) │
│ - GTK Widgets (Gtk::Window, Gtk::Box, etc.) │
│ - Glib signals for event handling │
│ - SDL2 for video rendering │
└─────────────────────────────────────────────────────────────┘
↕ Direct C++ calls
┌─────────────────────────────────────────────────────────────┐
│ Native Zoom SDK (libvideosdk.so) │
└─────────────────────────────────────────────────────────────┘
```
### Project Structure
```
videosdk-linux-gtk-quickstart/
├── README.md
├── src/
│ ├── CMakeLists.txt
│ ├── config.json
│ ├── bin/
│ │ └── SkeletonDemo
│ └── Source Files:
│ ├── zoom_v-sdk_linux_bot.cpp # Main entry + GTK UI
│ ├── VideoRenderer.h/cpp # SDL2 video rendering
│ ├── VideoDisplayBridge.h/cpp # SDK→Renderer bridge
│ ├── PreviewVideoHandler.h/cpp # Self video
│ └── RemoteVideoRawDataHandler.h/cpp # Remote video
```
### GTK Video Rendering (SDL2)
```cpp
// VideoRenderer.h
class VideoRenderer {
public:
VideoRenderer(Gtk::Widget* container);
~VideoRenderer();
void renderFrame(const unsigned char* yuvData, int width, int height);
private:
SDL_Window* m_window;
SDL_Renderer* m_renderer;
SDL_Texture* m_texture;
};
// VideoRenderer.cpp
void VideoRenderer::renderFrame(const unsigned char* yuvData, int width, int height) {
// Update YUV texture directly (SDL handles conversion)
SDL_UpdateYUVTexture(m_texture, nullptr,
yuvData, // Y plane
width, // Y pitch
yuvData + width * height, // U plane
width / 2, // U pitch
yuvData + width * height * 5/4, // V plane
width / 2); // V pitch
SDL_RenderClear(m_renderer);
SDL_RenderCopy(m_renderer, m_texture, nullptr, nullptr);
SDL_RenderPresent(m_renderer);
}
```
### GTK Main Window
```cpp
// zoom_v-sdk_linux_bot.cpp
class MainWindow : public Gtk::Window {
public:
MainWindow() {
set_title("Zoom Video SDK Demo");
set_default_size(1200, 800);
// Create layout
auto mainBox = Gtk::make_managed(Gtk::ORIENTATION_VERTICAL);
// Device selection
m_cameraCombo = Gtk::make_managed();
m_micCombo = Gtk::make_managed();
m_speakerCombo = Gtk::make_managed();
// Control buttons
m_joinButton = Gtk::make_managed("Join Session");
m_leaveButton = Gtk::make_managed("Leave Session");
// Video areas (SDL embedded)
m_selfVideoArea = Gtk::make_managed();
m_remoteVideoArea = Gtk::make_managed();
// Connect signals
m_joinButton->signal_clicked().connect(
sigc::mem_fun(*this, &MainWindow::on_join_clicked));
add(*mainBox);
show_all_children();
}
private:
void on_join_clicked() {
// Initialize SDK and join session
ZoomVideoSDKInitParams params;
params.domain = "https://zoom.us";
m_sdk->initialize(params);
ZoomVideoSDKSessionContext context;
context.sessionName = m_sessionEntry->get_text().c_str();
context.token = m_tokenEntry->get_text().c_str();
m_sdk->joinSession(context);
}
};
```
### Build Dependencies
```bash
# Ubuntu/Debian
sudo apt install build-essential cmake
sudo apt install libgtkmm-3.0-dev libsdl2-dev
sudo apt install libasound2-dev libcurl4-openssl-dev
```
```cmake
# CMakeLists.txt
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTKMM REQUIRED gtkmm-3.0)
pkg_check_modules(SDL2 REQUIRED sdl2)
pkg_check_modules(ALSA REQUIRED alsa)
target_link_libraries(SkeletonDemo
${GTKMM_LIBRARIES}
${SDL2_LIBRARIES}
${ALSA_LIBRARIES}
${CMAKE_SOURCE_DIR}/lib/libvideosdk.so
)
```
---
## Comparison: Qt vs GTK
| Aspect | Qt | GTK |
|--------|----|----|
| Language | C++ (native) | C++ (GTKmm wrapper) |
| Video Rendering | QPainter / QImage | SDL2 / Cairo |
| Threading | Signals/Slots + QMetaObject | Glib main loop + idle callbacks |
| Build System | CMake / qmake | CMake |
| Look & Feel | Platform-native | GTK theme |
| Learning Curve | Moderate | Moderate |
## Resources
### Sample Repositories
- **Windows C#**: [videosdk-windows-dotnet-desktop-framework-quickstart](https://github.com/tanchunsiong/videosdk-windows-dotnet-desktop-framework-quickstart)
- **Linux Qt**: [videosdk-linux-qt-quickstart](https://github.com/tanchunsiong/videosdk-linux-qt-quickstart)
- **Linux GTK**: [videosdk-linux-gtk-quickstart](https://github.com/tanchunsiong/videosdk-linux-gtk-quickstart)
### Official Documentation
- [Windows Meeting SDK](https://developers.zoom.us/docs/meeting-sdk/windows/)
- [Windows Video SDK](https://developers.zoom.us/docs/video-sdk/windows/)
- [Linux Video SDK](https://developers.zoom.us/docs/video-sdk/linux/)