Use production studio mode
The code on this page works with either the default UI or the custom UI.
Production studio (PS) mode lets an authorized client publish custom raw video and audio into the Zoom meeting pipeline through the Meeting SDK's PSSender, instead of - or in addition to - a normal camera or mic path. Only the host or co-host can start production studio mode using CanStartPSMode().
- Obtain the
IMeetingProductionStudioControllerfrom the meeting service after joining. - Check support for
isSupportPSModeand permission forcanStartPSMode, then register for events. - Call
StartPSMode with a video capability declaration including width, height, and format. This is the contract for all subsequentsendVideoFrame` calls. - Wait for
onStartPSModeResult(true), then wait foronStartSendbefore pushing any media. - Push video frames and audio buffers only while
onStartSendis active, usingIZoomSDKPSSender. - Call
StopPSModewhen done, and release all resources inonStopSend.
Note Keep your IMeetingProductionStudioCtrlEvent instance alive for as long as the SDK holds a pointer to it. Call SetEvent(nullptr) before destroying the object if you need to unregister early.
Get the controller
Get the controller from the meeting service after joining a meeting. If the SDK is not in a meeting or if production studio mode is unavailable, the return value is nullptr.
#include "meeting_service_interface.h"
#include "meeting_service_components/meeting_production_studio_ctrl_interface.h"
using namespace ZOOM_SDK_NAMESPACE;
IMeetingProductionStudioController* psCtrl =
meetingService->GetMeetingProductionStudioController();
if (!psCtrl) {
// Not in a meeting, or PS is unavailable
}
Controller APIs
All APIs are available on IMeetingProductionStudioController.
| API | Method | Description |
|---|---|---|
| Register callback | SetEvent(pEvent) | Must call before StartPSMode. Returns SDKERR_SUCCESS on success. |
| Check support | IsSupportPSMode() | Whether PS is available in this meeting or client. Check before showing any PS UI. |
| Check permission | CanStartPSMode() | Whether the current user may start PS. Host or co-host only. |
| Start PS | StartPSMode(cap) | Declares the video capability contract. Asynchronous - result comes in onStartPSModeResult. |
| Stop PS | StopPSMode() | Initiates teardown. Cleanup must happen in onStopSend. |
| Is PS started | IsPSModeStarted() | Whether PS mode is currently active. Use for state checks, not as a send gate. |
| Get PS user ID | GetPSUserID() | User ID of the current PS user. Use for roster correlation. |
PSVideoSourceCapability
PSVideoSourceCapability is passed to StartPSMode. It declares the exact video format your app will send. All subsequent sendVideoFrame calls must match these values.
PSVideoSourceCapability cap{};
cap.width = 1920;
cap.height = 1080;
cap.format = FrameDataFormat_I420_FULL;
Start production studio mode
Always check support and permission before calling StartPSMode. Call SetEvent first.
class PsEvents : public IMeetingProductionStudioCtrlEvent { ... };
void BeginProductionStudio(IMeetingService* meetingService, PsEvents& events)
{
IMeetingProductionStudioController* psCtrl =
meetingService->GetMeetingProductionStudioController();
if (!psCtrl) return;
if (psCtrl->SetEvent(&events) != SDKERR_SUCCESS)
return; // Register before starting
if (!psCtrl->IsSupportPSMode()) return; // PS unavailable in this meeting
if (!psCtrl->CanStartPSMode()) return; // Not host/co-host
PSVideoSourceCapability cap{};
cap.width = 1920;
cap.height = 1080;
cap.format = FrameDataFormat_I420_FULL;
SDKError err = psCtrl->StartPSMode(cap);
if (err != SDKERR_SUCCESS) {
// Handle error — no callbacks will follow
}
}
Callbacks
onStartPSModeResult(bSuccess)
Fires after StartPSMode is processed by the SDK.
false- PS mode failed to start. Do not attempt to send media. Show an error if needed.true- PS mode accepted. Do not send yet. Wait foronStartSend.
void PsEvents::onStartPSModeResult(bool bSuccess)
{
if (!bSuccess) return;
// Accepted — wait for onStartSend
}
onStartSend(sender)
Fires when the SDK is ready to receive media. The sender pointer is valid only until onStopSend fires.
- Save the sender and begin your capture/encode loop.
- Call
sendVideoFrameandsendAudioonly from this point. - Sender is
nullptrif something went wrong. Make sure that the sender is not null before using.
void PsEvents::onStartSend(IZoomSDKPSSender* sender)
{
if (!sender) return;
m_sender = sender;
StartCapturePipeline();
}
onStopSend
Fires when the SDK requires all sending to stop. This can be triggered by:
- Your own
StopPSModecall. - Meeting end or the local user leaving.
- SDK internally stopping PS, like a role change.
Always stop sending and release the sender here, regardless of what caused the stop.
void PsEvents::onStopSend()
{
StopCapturePipeline();
m_sender = nullptr;
}
onPSUserStatusChanged(userID, bStart)
Fires when a PS user in the meeting starts or stops publishing.
userID- the user ID of the PS participant.bStart true- that user began PS sending;false:stopped.- Use for updating UI, such as showing a Live indicator for the PS user or for logging.
Does not affect your own send pipeline. Use onStartSend or onStopSend for that.
void PsEvents::onPSUserStatusChanged(unsigned int userID, bool bStart)
{
// bStart true = started, false = stopped
}
Send video and audio
Call sendVideoFrame and sendAudio only after onStartSend and before onStopSend on a single producer thread.
sendVideoFrame
Width, height, and format must exactly match the PSVideoSourceCapability passed to StartPSMode. Mismatch returns SDKERR_INVALID_PARAMETER.
SDKError e = sender->sendVideoFrame(
buffer,
1920,
1080,
1920 * 1080 * 3 / 2,
FrameDataFormat_I420_FULL
);
sendAudio
data_lengthmust be an even number.sample_rateis 32000 or 48000. 48000 is recommended.
SDKError e = sender->sendAudio(pcmBuffer, 1920, 48000, ZoomSDKAudioChannel_Mono);
Stop production studio mode
SDKError err = psCtrl->StopPSMode();
// All cleanup must happen in onStopSend()
StopPSMode initiates teardown asynchronously. Do not release the sender or stop your capture loop here. Wait for onStopSend.
Use the participant identity
IsProductionStudioUser and GetProductionStudioParent describe who a participant is in the roster. Use them for UI labeling or debugging. They have no connection to the send pipeline.
IUserInfo* info = participantsCtrl->GetUserByUserID(userId);
if (info && info->IsProductionStudioUser()) {
unsigned int parent = info->GetProductionStudioParent();
}
NOTE GetProductionStudioParent() only has a meaningful value for PS users. Always guard with IsProductionStudioUser() first.