Setting the file. One moment.
Skill 126 · Zoom Meeting SDK Windows
Subchapter 126.18
examples/share-raw-data-capture.mdMarkdown16 KBView on GitHub
The Meeting SDK allows you to receive raw screen share data (YUV420 frames) from participants sharing their screen. This uses the same IZoomSDKRenderer and IZoomSDKRendererDelegate pattern as video capture, but subscribes to RAW_DATA_TYPE_SHARE instead of RAW_DATA_TYPE_VIDEO.
IMeetingService
├── GetMeetingShareController() → IMeetingShareController
│ └── GetViewableSharingUserList()
│
└── GetMeetingRecordingController() → IMeetingRecordingController
└── StartRawRecording()
createRenderer() → IZoomSDKRenderer
├── subscribe(userId, RAW_DATA_TYPE_SHARE)
└── unSubscribe()
IZoomSDKRendererDelegate (your implementation)
└── onRawDataFrameReceived(YUVRawDataI420*)#include <meeting_service_interface.h>
#include <meeting_service_components/meeting_sharing_interface.h>
#include <meeting_service_components/meeting_recording_interface.h>
#include <rawdata/zoom_rawdata_api.h>
#include <rawdata/rawdata_renderer_interface.h>// ZoomSDKShareRendererDelegate.h
#pragma once
#include <rawdata/rawdata_renderer_interface.h>
#include <fstream>
#include <string>
class ZoomSDKShareRendererDelegate : public ZOOMSDK::IZoomSDKRendererDelegate {
public:
ZoomSDKShareRendererDelegate();
virtual ~ZoomSDKShareRendererDelegate();
// Called when a share frame is received
virtual void onRawDataFrameReceived(ZOOMSDK::YUVRawDataI420* data) override;
// Called when renderer status changes
virtual void onRawDataStatusChanged(
ZOOMSDK::RawDataStatus status
) override;
// Called when resolution changes
virtual void onRendererBeDestroyed() override;
// Frame statistics
unsigned int getFrameCount() const { return m_frameCount; }
void resetFrameCount() { m_frameCount = 0; }
// Enable/disable file output
void enableFileOutput(const std::string& filename);
void disableFileOutput();
private:
unsigned int m_frameCount;
std::ofstream m_outputFile;
bool m_writeToFile;
int m_lastWidth;
int m_lastHeight;
};// ZoomSDKShareRendererDelegate.cpp
#include "ZoomSDKShareRendererDelegate.h"
#include <iostream>
using namespace ZOOMSDK;
ZoomSDKShareRendererDelegate::ZoomSDKShareRendererDelegate()
: m_frameCount(0)
, m_writeToFile(false)
, m_lastWidth(0)
, m_lastHeight(0) {}
ZoomSDKShareRendererDelegate::~ZoomSDKShareRendererDelegate() {
disableFileOutput();
}
void ZoomSDKShareRendererDelegate::onRawDataFrameReceived(YUVRawDataI420* data) {
if (!data) return;
m_frameCount++;
// Get frame dimensions
unsigned int width = data->GetStreamWidth();
unsigned int height = data->GetStreamHeight();
// Check for resolution change
if (width != m_lastWidth || height != m_lastHeight) {
std::cout << "[Share] Resolution changed: "
<< width << "x" << height << std::endl;
m_lastWidth = width;
m_lastHeight = height;
}
// Log periodically
if (m_frameCount % 30 == 0) {
std::cout << "[Share] Frame " << m_frameCount
<< " - " << width << "x" << height
<< std::endl;
}
// YUV420 data access
char* yBuffer = data->GetYBuffer();
char* uBuffer = data->GetUBuffer();
char* vBuffer = data->GetVBuffer();
// Calculate buffer sizes for YUV420
unsigned int ySize = width * height;
unsigned int uvSize = (width / 2) * (height / 2);
// Write to file if enabled
if (m_writeToFile && m_outputFile.is_open()) {
m_outputFile.write(yBuffer, ySize);
m_outputFile.write(uBuffer, uvSize);
m_outputFile.write(vBuffer, uvSize);
}
// Process the frame (e.g., display, encode, analyze)
// The data is in YUV I420 format:
// - Y plane: width * height bytes (luminance)
// - U plane: (width/2) * (height/2) bytes (chrominance)
// - V plane: (width/2) * (height/2) bytes (chrominance)
}
void ZoomSDKShareRendererDelegate::onRawDataStatusChanged(RawDataStatus status) {
switch (status) {
case RawData_On:
std::cout << "[Share] Raw data: ON" << std::endl;
break;
case RawData_Off:
std::cout << "[Share] Raw data: OFF" << std::endl;
break;
default:
std::cout << "[Share] Raw data status: " << status << std::endl;
}
}
void ZoomSDKShareRendererDelegate::onRendererBeDestroyed() {
std::cout << "[Share] Renderer being destroyed" << std::endl;
disableFileOutput();
}
void ZoomSDKShareRendererDelegate::enableFileOutput(const std::string& filename) {
disableFileOutput();
m_outputFile.open(filename, std::ios::binary);
if (m_outputFile.is_open()) {
m_writeToFile = true;
std::cout << "[Share] Writing to: " << filename << std::endl;
}
}
void ZoomSDKShareRendererDelegate::disableFileOutput() {
if (m_outputFile.is_open()) {
m_outputFile.close();
}
m_writeToFile = false;
}// Global variables
ZoomSDKShareRendererDelegate* g_shareDelegate = nullptr;
IZoomSDKRenderer* g_shareRenderer = nullptr;
IMeetingShareController* g_shareController = nullptr;
IMeetingRecordingController* g_recordController = nullptr;
void initializeShareCapture(IMeetingService* meetingService) {
// Get controllers
g_shareController = meetingService->GetMeetingShareController();
g_recordController = meetingService->GetMeetingRecordingController();
if (!g_shareController || !g_recordController) {
std::cerr << "Failed to get controllers" << std::endl;
return;
}
// Create delegate
g_shareDelegate = new ZoomSDKShareRendererDelegate();
std::cout << "Share capture initialized" << std::endl;
}unsigned int getSharingUserId() {
if (!g_shareController) return 0;
// Get list of users currently sharing
IList<unsigned int>* sharingUsers = g_shareController->GetViewableSharingUserList();
if (!sharingUsers || sharingUsers->GetCount() == 0) {
std::cout << "No one is sharing" << std::endl;
return 0;
}
// Get first sharing user
unsigned int userId = sharingUsers->GetItem(0);
std::cout << "User " << userId << " is sharing" << std::endl;
return userId;
}
void startShareCapture() {
if (!g_shareDelegate) return;
// Start raw recording first (required for raw data access)
SDKError err = g_recordController->StartRawRecording();
if (err != SDKERR_SUCCESS) {
std::cerr << "Failed to start raw recording: " << err << std::endl;
return;
}
// Create renderer
err = createRenderer(&g_shareRenderer, g_shareDelegate);
if (err != SDKERR_SUCCESS) {
std::cerr << "Failed to create renderer: " << err << std::endl;
return;
}
// Get sharing user
unsigned int userId = getSharingUserId();
if (userId == 0) {
std::cout << "No sharing user to subscribe to" << std::endl;
return;
}
// Subscribe to share raw data
err = g_shareRenderer->subscribe(userId, RAW_DATA_TYPE_SHARE);
if (err == SDKERR_SUCCESS) {
std::cout << "Subscribed to share from user " << userId << std::endl;
// Optionally save to file
g_shareDelegate->enableFileOutput("share_capture.yuv");
} else {
std::cerr << "Failed to subscribe: " << err << std::endl;
}
}
void stopShareCapture() {
if (g_shareRenderer) {
g_shareRenderer->unSubscribe();
std::cout << "Unsubscribed from share" << std::endl;
}
if (g_shareDelegate) {
g_shareDelegate->disableFileOutput();
}
}To know when sharing starts/stops, implement a share controller event listener:
// MeetingShareCtrlEventListener.h
#pragma once
#include <meeting_service_components/meeting_sharing_interface.h>
class MeetingShareCtrlEventListener : public ZOOMSDK::IMeetingShareCtrlEvent {
public:
using ShareStartedCallback = std::function<void(unsigned int)>;
using ShareStoppedCallback = std::function<void()>;
MeetingShareCtrlEventListener(
ShareStartedCallback onStarted = nullptr,
ShareStoppedCallback onStopped = nullptr
);
// Sharing status changed
virtual void onSharingStatus(
ZOOMSDK::SharingStatus status,
unsigned int userId
) override;
// Someone started sharing
virtual void onLockShareStatus(bool bLocked) override;
// Share content changed
virtual void onShareContentNotification(
ZOOMSDK::ShareInfo* shareInfo
) override;
// Multi-share
virtual void onMultiShareSwitchToSingleShareNeedConfirm(
ZOOMSDK::IShareSwitchMultiToSingleConfirmHandler* handler
) override;
virtual void onShareSettingTypeChangedNotification(
ZOOMSDK::ShareSettingType type
) override;
virtual void onSharedVideoEnded() override;
private:
ShareStartedCallback m_onStarted;
ShareStoppedCallback m_onStopped;
};// MeetingShareCtrlEventListener.cpp
#include "MeetingShareCtrlEventListener.h"
#include <iostream>
using namespace ZOOMSDK;
MeetingShareCtrlEventListener::MeetingShareCtrlEventListener(
ShareStartedCallback onStarted,
ShareStoppedCallback onStopped
) : m_onStarted(onStarted), m_onStopped(onStopped) {}
void MeetingShareCtrlEventListener::onSharingStatus(
SharingStatus status,
unsigned int userId
) {
switch (status) {
case Sharing_Self_Send_Begin:
std::cout << "Started sharing (self)" << std::endl;
break;
case Sharing_Self_Send_End:
std::cout << "Stopped sharing (self)" << std::endl;
break;
case Sharing_Other_Share_Begin:
std::cout << "User " << userId << " started sharing" << std::endl;
if (m_onStarted) m_onStarted(userId);
break;
case Sharing_Other_Share_End:
std::cout << "User " << userId << " stopped sharing" << std::endl;
if (m_onStopped) m_onStopped();
break;
case Sharing_View_Other_Sharing:
std::cout << "Viewing share from user " << userId << std::endl;
break;
case Sharing_Pause:
std::cout << "Sharing paused" << std::endl;
break;
case Sharing_Resume:
std::cout << "Sharing resumed" << std::endl;
break;
default:
std::cout << "Sharing status: " << status << std::endl;
}
}
void MeetingShareCtrlEventListener::onLockShareStatus(bool bLocked) {
std::cout << "Share lock: " << (bLocked ? "LOCKED" : "UNLOCKED") << std::endl;
}
void MeetingShareCtrlEventListener::onShareContentNotification(ShareInfo* shareInfo) {
if (shareInfo) {
std::cout << "Share content changed" << std::endl;
}
}
void MeetingShareCtrlEventListener::onMultiShareSwitchToSingleShareNeedConfirm(
IShareSwitchMultiToSingleConfirmHandler* handler
) {
// Handle multi-share to single-share switch
}
void MeetingShareCtrlEventListener::onShareSettingTypeChangedNotification(
ShareSettingType type
) {
std::cout << "Share setting changed" << std::endl;
}
void MeetingShareCtrlEventListener::onSharedVideoEnded() {
std::cout << "Shared video ended" << std::endl;
}// Global share event listener
MeetingShareCtrlEventListener* g_shareEventListener = nullptr;
void onInMeeting(IMeetingService* meetingService) {
// Initialize share capture
initializeShareCapture(meetingService);
// Set up share event listener
g_shareEventListener = new MeetingShareCtrlEventListener(
// On share started
[](unsigned int userId) {
std::cout << "Starting capture for user " << userId << std::endl;
startShareCapture();
},
// On share stopped
[]() {
std::cout << "Stopping capture" << std::endl;
stopShareCapture();
}
);
g_shareController->SetEvent(g_shareEventListener);
// Check if someone is already sharing
unsigned int sharingUser = getSharingUserId();
if (sharingUser != 0) {
startShareCapture();
}
}
// Cleanup
void cleanup() {
stopShareCapture();
if (g_shareRenderer) {
// Renderer will be destroyed when unsubscribed
g_shareRenderer = nullptr;
}
delete g_shareDelegate;
g_shareDelegate = nullptr;
delete g_shareEventListener;
g_shareEventListener = nullptr;
}enum SharingStatus {
Sharing_Self_Send_Begin, // You started sharing
Sharing_Self_Send_End, // You stopped sharing
Sharing_Other_Share_Begin, // Someone else started sharing
Sharing_Other_Share_End, // Someone else stopped sharing
Sharing_View_Other_Sharing, // Viewing someone's share
Sharing_Pause, // Sharing paused
Sharing_Resume, // Sharing resumed
Sharing_ContentTypeChange, // Content type changed
Sharing_SelfStartAudioShare, // Started audio share
Sharing_SelfStopAudioShare // Stopped audio share
};enum RawDataType {
RAW_DATA_TYPE_VIDEO = 0, // Video frames
RAW_DATA_TYPE_SHARE // Screen share frames
};To receive screen share raw data, you need:
StartRawRecording() before subscribingbool checkPermission() {
SDKError err = g_recordController->CanStartRecording(false, 0);
if (err != SDKERR_SUCCESS) {
std::cout << "Requesting recording permission..." << std::endl;
g_recordController->RequestLocalRecordingPrivilege();
return false;
}
return true;
}The raw data is in YUV420 (I420) format. To convert to RGB or save as image:
// Using OpenCV
#include <opencv2/opencv.hpp>
void saveFrameAsImage(YUVRawDataI420* data, const std::string& filename) {
int width = data->GetStreamWidth();
int height = data->GetStreamHeight();
// Create Mat from YUV data
cv::Mat yuvFrame(height + height/2, width, CV_8UC1);
// Copy Y plane
memcpy(yuvFrame.data, data->GetYBuffer(), width * height);
// Copy U and V planes (interleaved in I420)
memcpy(yuvFrame.data + width * height,
data->GetUBuffer(), (width/2) * (height/2));
memcpy(yuvFrame.data + width * height + (width/2) * (height/2),
data->GetVBuffer(), (width/2) * (height/2));
// Convert to BGR
cv::Mat bgrFrame;
cv::cvtColor(yuvFrame, bgrFrame, cv::COLOR_YUV2BGR_I420);
// Save
cv::imwrite(filename, bgrFrame);
}StartRawRecording() before subscribing to shareGetViewableSharingUserList() to get all sharing users| Aspect | Video Capture | Share Capture |
|---|---|---|
| Data Type | RAW_DATA_TYPE_VIDEO | RAW_DATA_TYPE_SHARE |
| Source | Webcam | Screen/Window/App |
| Resolution | Fixed camera resolution | Dynamic (window size) |
| Frame Rate | Consistent (30fps) | Variable (content-dependent) |
| User List | Participants controller | Share controller |
Both use the same IZoomSDKRenderer and IZoomSDKRendererDelegate interfaces.