ScreenShare Issues on 7.0.5

Meeting SDK Type and Version

Meeting SDK for Linux, version 7.0.5.3529, architecture arm64. Running headless in Docker (Ubuntu 22.04, Xvfb virtual display, PulseAudio).


Description

A headless raw-recording bot captures raw audio and raw camera video for every participant correctly, but cannot detect or receive shared-screen content at all/

While a share is active:

  • IMeetingShareCtrlEvent::onSharingStatus is never called (nor onShareContentNotification).
  • IMeetingShareController::GetViewableSharingUserList() always returns count 0.
  • IZoomSDKRenderer::subscribe(userId, RAW_DATA_TYPE_SHARE) returns SDKERR_NO_SHARE_DATA (29) on every attempt.

Raw Data is Flowing:

  1. Camera video (RAW_DATA_TYPE_VIDEO) and audio raw data flow the whole time using the same permission/token, so raw-data access and the pipeline are working. Only shared content is missing.

The bot joins with SDK_UT_WITHOUT_LOGIN using a JWT for SDK auth plus a valid local recording token in JoinParam4WithoutLogin.app_privilege_token. The host has granted local recording (client shows “Don’t allow to record to computer” as an option when interacting with the meeting sdk in the participant sidepanel).

Questions:

  1. What do we need to do to get screenshare audio and video data to flow? I am happy to share a code sample if that would help reproductions

@z888888888
RAW_DATA_TYPE_SHARE must be subscribed with the shareSourceID

Register IMeetingShareCtrlEvent and wait for event 4(share start) and 5 (share end)

  • Sharing_Other_Share_Begin = 4
  • Sharing_Other_Share_End = 5

To get the raw share video, use IZoomSDKRenderer with RAW_DATA_TYPE_SHARE.

Here’s a sample

Hi @chunsiong.zoom,

Thanks for the response. My reproduction already implements exactly what you describe, and the failure is one step earlier — I never receive a shareSourceID to subscribe with:

  1. IMeetingShareCtrlEvent is registered before Join(), and it does receive callbacks (onShareSettingTypeChangedNotification fires), but onSharingStatus never fires during an active share, so no ZoomSDKSharingSourceInfo.shareSourceID ever arrives.
  2. As a fallback I poll GetViewableSharingUserList() → GetSharingSourceInfoList() every second — the list stays empty for the entire meeting while the host is actively sharing.
  3. When a shareSourceID does appear via either path, my code subscribes with subscribe(shareSourceID, RAW_DATA_TYPE_SHARE), it just never gets the chance

My question is: why share discovery itself (onSharingStatus + GetViewableSharingUserList()) produces nothing on Linux SDK 7.0.5.3529, arm64, headless (Ubuntu 22.04, Docker, Xvfb), while raw audio and camera video work fine with the same join flow. This type of flow has successfully worked on past versions of the SDK.

#include "h/rawdata/zoom_rawdata_api.h"
#include "h/rawdata/rawdata_audio_helper_interface.h"
#include "h/zoom_sdk_raw_data_def.h"
#include "h/meeting_service_components/meeting_recording_interface.h"
#include "h/meeting_service_components/meeting_audio_interface.h"
#include "h/meeting_service_components/meeting_sharing_interface.h"
#include "h/meeting_service_components/meeting_participants_ctrl_interface.h"
#include "h/rawdata/rawdata_renderer_interface.h"
#include "h/meeting_service_interface.h"
#include "h/zoom_sdk.h"
#include "h/zoom_sdk_def.h"
#include "h/auth_service_interface.h"
#include <glib.h>
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstdio>
#include <map>
#include <mutex>
#include <cstdint>
#include <ctime>
#include <vector>
#include <set>

bool g_sdkAuth   = false;
bool g_inMeeting = false;   // set by MeetingListener on INMEETING
bool g_canRecord = false;   // set by RecordingListener on privilege grant

class AuthListener: public ZOOMSDK::IAuthServiceEvent {
public:
        explicit AuthListener(GMainLoop* loop) : loop_(loop) {}

        void onAuthenticationReturn(ZOOMSDK::AuthResult ret) override {
                if (ret == ZOOMSDK::AUTHRET_SUCCESS) {
                        std::cout << "[AuthListener::onAuthenticationReturn] authenticated successfully\n";
                        g_sdkAuth = true;
                } else {
                        std::cout << "[AuthListener::onAuthenticationReturn] auth FAILED, AuthResult = " << ret << "\n";
                }
                g_main_loop_quit(loop_);   // stop blocking once we have a result
    }

    // required pure-virtual stubs (no-ops for now)
    void onLoginReturnWithReason(ZOOMSDK::LOGINSTATUS ret, ZOOMSDK::IAccountInfo* pAccountInfo,
                                 ZOOMSDK::LoginFailReason reason) override {
                // login callbacks don't fire during SDK auth; just log the codes
                std::cout << "[AuthListener::onLoginReturnWithReason] login status " << ret
                          << ", fail reason " << reason << "\n";
                (void)pAccountInfo;  // IAccountInfo has no operator<< and may be null
        }
    void onLogout() override {}
    void onZoomIdentityExpired() override {}
    void onZoomAuthIdentityExpired() override {}

private:
    GMainLoop* loop_;

};


class MeetingListener: public ZOOMSDK::IMeetingServiceEvent {
public:
        explicit MeetingListener(GMainLoop* loop) : loop_(loop) {};

        void onMeetingStatusChanged(ZOOMSDK::MeetingStatus status, int iResult = 0) override {
                std::cout << "[MeetingListener::onMeetingStatusChanged] status=" << status << " result=" << iResult << "\n";
                switch (status) {
                        case ZOOMSDK::MEETING_STATUS_CONNECTING:
                                std::cout << "[MeetingListener] connecting...\n";
                                break;
                        case ZOOMSDK::MEETING_STATUS_INMEETING:
                                std::cout << "[MeetingListener] JOINED the meeting\n";
                                g_inMeeting = true;
                                g_main_loop_quit(loop_);   // let join() return so main proceeds
                                break;
                        case ZOOMSDK::MEETING_STATUS_FAILED:
                        case ZOOMSDK::MEETING_STATUS_ENDED:
                                std::cout << "[MeetingListener] meeting ended/failed\n";
                                g_inMeeting = false;
                                g_main_loop_quit(loop_);
                                break;
                        default:
                                break;   // WAITINGFORHOST, IN_WAITING_ROOM, etc. - keep waiting
                }
        }

// required stubs
        void onMeetingStatisticsWarningNotification(ZOOMSDK::StatisticsWarningType) override {}
        void onMeetingParameterNotification(const ZOOMSDK::MeetingParameter*) override {}
        void onSuspendParticipantsActivities() override {}
        void onAICompanionActiveChangeNotice(bool) override {}
        void onMeetingTopicChanged(const zchar_t*) override {}
        void onMeetingFullToWatchLiveStream(const zchar_t*) override {}
        void onUserNetworkStatusChanged(ZOOMSDK::MeetingComponentType, ZOOMSDK::ConnectionQuality, unsigned int, bool) override {}
        // note: onAppSignalPanelUpdated is WIN32-only in the SDK header, so it is NOT overridden on Linux

private:
        GMainLoop* loop_;
};

class AudioRawListener : public ZOOMSDK::IZoomSDKAudioRawDataDelegate {
public:
        // NOTE: AudioRawData is a GLOBAL type (declared outside ZOOM_SDK_NAMESPACE), not ZOOMSDK::AudioRawData
        void onOneWayAudioRawDataReceived(AudioRawData* d, uint32_t user_id) override {
                std::lock_guard<std::mutex> lk(mu_);
                micBytes_[user_id] += d->GetBufferLen();
        }
        void onMixedAudioRawDataReceived(AudioRawData*) override {}   // single mixed stream, no source
        void onShareAudioRawDataReceived(AudioRawData* d, uint32_t user_id) override {
                std::lock_guard<std::mutex> lk(mu_);
                shareBytes_[user_id] += d->GetBufferLen();   // shared computer sound (separate stream, same user id)
        }
        void onOneWayInterpreterAudioRawDataReceived(AudioRawData*, const zchar_t*) override {}

        std::map<uint32_t, uint64_t> micSnapshot() {
                std::lock_guard<std::mutex> lk(mu_);
                return micBytes_;
        }
        std::map<uint32_t, uint64_t> shareSnapshot() {
                std::lock_guard<std::mutex> lk(mu_);
                return shareBytes_;
        }

private:
        std::mutex mu_;
        std::map<uint32_t, uint64_t> micBytes_;
        std::map<uint32_t, uint64_t> shareBytes_;
};

class VideoRawListener : public ZOOMSDK::IZoomSDKRendererDelegate {
public:
        // one delegate shared by all renderers; GetSourceID() distinguishes camera vs share source
        // NOTE: YUVRawDataI420 is a GLOBAL type (declared outside ZOOM_SDK_NAMESPACE)
        void onRawDataFrameReceived(YUVRawDataI420* d) override {
                std::lock_guard<std::mutex> lk(mu_);
                videoBytes_[d->GetSourceID()] += d->GetBufferLen();
        }
        void onRendererBeDestroyed() override {
                std::cout << "[Video] onRendererBeDestroyed\n";
        }
        void onRawDataStatusChanged(RawDataStatus status) override {
                std::cout << "[Video] onRawDataStatusChanged: "
                          << (status == RawData_On ? "RawData_On" : "RawData_Off") << "\n";
        }

        std::map<uint32_t, uint64_t> snapshot() {
                std::lock_guard<std::mutex> lk(mu_);
                return videoBytes_;
        }

private:
        std::mutex mu_;
        std::map<uint32_t, uint64_t> videoBytes_;
};

// Diagnostic: logs every sharing event so we can see if/when the SDK reports a share,
// and the share source id it assigns.
class ShareListener : public ZOOMSDK::IMeetingShareCtrlEvent {
public:
        void onSharingStatus(ZOOMSDK::ZoomSDKSharingSourceInfo info) override {
                std::cout << "[Share] onSharingStatus userid=" << info.userid
                          << " shareSourceID=" << info.shareSourceID
                          << " status=" << info.status
                          << " contentType=" << info.contentType << "\n";
        }
        void onShareContentNotification(ZOOMSDK::ZoomSDKSharingSourceInfo info) override {
                std::cout << "[Share] onShareContentNotification userid=" << info.userid
                          << " shareSourceID=" << info.shareSourceID
                          << " status=" << info.status << "\n";
        }
        void onFailedToStartShare() override { std::cout << "[Share] onFailedToStartShare\n"; }
        void onLockShareStatus(bool bLocked) override { std::cout << "[Share] onLockShareStatus locked=" << bLocked << "\n"; }
        void onMultiShareSwitchToSingleShareNeedConfirm(ZOOMSDK::IShareSwitchMultiToSingleConfirmHandler*) override {
                std::cout << "[Share] onMultiShareSwitchToSingleShareNeedConfirm\n";
        }
        void onShareSettingTypeChangedNotification(ZOOMSDK::ShareSettingType type) override {
                std::cout << "[Share] onShareSettingTypeChangedNotification type=" << type << "\n";
        }
        void onSharedVideoEnded() override { std::cout << "[Share] onSharedVideoEnded\n"; }
        void onVideoFileSharePlayError(ZOOMSDK::ZoomSDKVideoFileSharePlayError err) override {
                std::cout << "[Share] onVideoFileSharePlayError err=" << err << "\n";
        }
        void onOptimizingShareForVideoClipStatusChanged(ZOOMSDK::ZoomSDKSharingSourceInfo info) override {
                std::cout << "[Share] onOptimizingShareForVideoClipStatusChanged userid=" << info.userid
                          << " status=" << info.status << "\n";
        }
};

class RecordingListener : public ZOOMSDK::IMeetingRecordingCtrlEvent {
public:
        explicit RecordingListener(GMainLoop* loop) : loop_(loop) {}

        void onRecordPrivilegeChanged(bool bCanRec) override {
                std::cout << "[Recording] privilege changed: " << bCanRec << "\n";
                g_canRecord = bCanRec;
                g_main_loop_quit(loop_);
        }

        // required stubs
        void onRecordingStatus(ZOOMSDK::RecordingStatus status) override {
                std::cout << "[Recording] onRecordingStatus status=" << status << "\n";
        }
        void onCloudRecordingStatus(ZOOMSDK::RecordingStatus) override {}
        void onLocalRecordingPrivilegeRequestStatus(ZOOMSDK::RequestLocalRecordingStatus) override {}
        void onRequestCloudRecordingResponse(ZOOMSDK::RequestStartCloudRecordingStatus) override {}
        void onLocalRecordingPrivilegeRequested(ZOOMSDK::IRequestLocalRecordingPrivilegeHandler*) override {}
        void onStartCloudRecordingRequested(ZOOMSDK::IRequestStartCloudRecordingHandler*) override {}
        // onRecording2MP4Done / onRecording2MP4Processing / onCustomizedLocalRecordingSourceNotification are WIN32-only
        void onCloudRecordingStorageFull(time_t) override {}
        void onEnableAndStartSmartRecordingRequested(ZOOMSDK::IRequestEnableAndStartSmartRecordingHandler*) override {}
        void onSmartRecordingEnableActionCallback(ZOOMSDK::ISmartRecordingEnableActionHandler*) override {}
        void onTranscodingStatusChanged(ZOOMSDK::TranscodingStatus, const zchar_t*) override {}

private:
        GMainLoop* loop_;
};

class DemoBot {

private:
        long long meetingId_;
        std::string meetingPassword_;
        std::string meetingToken_;
        ZOOMSDK::IAuthService* authService_ = nullptr;
        ZOOMSDK::IMeetingService* meetingService_ = nullptr;
        GMainLoop* loop_ = nullptr;
        AuthListener* authListener_ = nullptr;
        MeetingListener* meetingListener_ = nullptr;
        ZOOMSDK::IMeetingRecordingController* recordingController_ = nullptr;
        RecordingListener* recordingListener_ = nullptr;
        AudioRawListener audioListener_;      // by value, lives with the bot
        VideoRawListener videoListener_;      // shared by all renderers
        ShareListener* shareListener_ = nullptr;
        std::vector<ZOOMSDK::IZoomSDKRenderer*> renderers_;
        std::set<uint32_t> subscribedVideo_;  // user ids we've made a video renderer for
        std::set<uint32_t> subscribedShare_;  // share source ids we've subscribed
        guint reportTimerId_ = 0;

public:
        DemoBot(long long meetingId,
            std::string meetingPassword,
            std::string meetingToken)
        : meetingId_(meetingId),
          meetingPassword_(std::move(meetingPassword)),
          meetingToken_(std::move(meetingToken))
        {
                // log the default information
                std::cout << "[DemoBot::DemoBot] constructed a bot with meetingId: "
                << meetingId_
                << ", meetingPassword: " << meetingPassword_
                << ", meetingToken: " << meetingToken_ << "\n";

                // initialize the zoom sdk
                ZOOMSDK::InitParam initParam;
                initParam.strWebDomain = "https://zoom.us";
                initParam.strSupportUrl = "https://zoom.us";
                initParam.emLanguageID = ZOOMSDK::LANGUAGE_English;
                initParam.enableLogByDefault = true;
                initParam.enableGenerateDump = true;

                ZOOMSDK::SDKError initResult = ZOOMSDK::InitSDK(initParam);
                std::cout << "[DemoBot::DemoBot] initialize the sdk result: " << initResult << "\n";
                std::cout << "[DemoBot::DemoBot] SDK version: " << ZOOMSDK::GetSDKVersion() << "\n";

        }

        ~DemoBot() {
                if (reportTimerId_) g_source_remove(reportTimerId_);
                for (auto* r : renderers_) ZOOMSDK::destroyRenderer(r);
                if (meetingListener_) delete meetingListener_;
                if (recordingListener_) delete recordingListener_;
                if (shareListener_) delete shareListener_;
                if (authListener_) delete authListener_;
                if (loop_) g_main_loop_unref(loop_);  // GMainLoop is refcounted, not delete-able
                // recordingController_ is owned by the SDK - do not delete
        }

        bool authenticate(const std::string& jwt) {
                loop_ = g_main_loop_new(nullptr, false);
                authListener_ = new AuthListener(loop_);
                ZOOMSDK::SDKError err = ZOOMSDK::CreateAuthService(&authService_);
                std::cout << "[DemoBot::authenticate] CreateAuthService exit code: " << err << "\n";

                if (err != ZOOMSDK::SDKERR_SUCCESS) {
                        return false;
                }

                authService_->SetEvent(authListener_);
                ZOOMSDK::AuthContext ctx;
                ctx.jwt_token = jwt.c_str();
                err = authService_->SDKAuth(ctx);
                std::cout << "[DemoBot::authenticate] sdk auth exit code: " << err << "\n";
                if (err != ZOOMSDK::SDKERR_SUCCESS) {
                        return false;
                }

                std::cout << "[DemoBot:authenticate] SDKAuth requested, waiting for callback...\n";
                g_main_loop_run(loop_);
                return true;
        }

        bool join() {
                std::cout << "[DemoBot::join] joining the meeting...\n";

                ZOOMSDK::SDKError err = ZOOMSDK::CreateMeetingService(&meetingService_);
                std::cout << "[DemoBot::join] CreateMeetingService outcome: " << err << "\n";
                if (err != ZOOMSDK::SDKERR_SUCCESS || !meetingService_) {
                        return false;
                }
                meetingListener_ = new MeetingListener(loop_);
                meetingService_->SetEvent(meetingListener_);


                if (auto* sc = meetingService_->GetMeetingShareController()) {
                        shareListener_ = new ShareListener();
                        ZOOMSDK::SDKError se = sc->SetEvent(shareListener_);
                        std::cout << "[DemoBot::join] share controller SetEvent: " << se << "\n";
                } else {
                        std::cout << "[DemoBot::join] NO share controller\n";
                }

                ZOOMSDK::JoinParam joinParam;
                joinParam.userType = ZOOMSDK::SDK_UT_WITHOUT_LOGIN;
                ZOOMSDK::JoinParam4WithoutLogin& p = joinParam.param.withoutloginuserJoin;
                p.meetingNumber = static_cast<UINT64>(meetingId_);
                p.userName      = "Test Screenshare Bot";
                p.psw           = meetingPassword_.c_str();
                p.isVideoOff    = true;    // bot has no camera
                p.isAudioOff    = false;   // keep audio for recording later
                if (!meetingToken_.empty())
                        p.app_privilege_token = meetingToken_.c_str();   // grants local recording at join

                err = meetingService_->Join(joinParam);
                std::cout << "[DemoBot::join] Join() returned: " << err << "\n";
                if (err != ZOOMSDK::SDKERR_SUCCESS) {
                        return false;
                }

                // Join is async - re-run the loop to process onMeetingStatusChanged
                // callbacks. MeetingListener quits the loop on ENDED/FAILED.
                std::cout << "[DemoBot::join] waiting for meeting status callbacks...\n";
                g_main_loop_run(loop_);   // quits on INMEETING (g_inMeeting=true) or FAILED
                return g_inMeeting;
        }

        bool record() {
                recordingController_ = meetingService_->GetMeetingRecordingController();
                if (!recordingController_) {
                        std::cout << "[DemoBot::record] no recording controller\n";
                        return false;
                }

                recordingListener_ = new RecordingListener(loop_);
                recordingController_->SetEvent(recordingListener_);
                // (share event listener is registered early in join(), before Join)

                // one-shot privilege gate (g_sdkAuth style)
                if (recordingController_->CanStartRawRecording() == ZOOMSDK::SDKERR_SUCCESS) {
                        g_canRecord = true;
                } else {
                        std::cout << "[DemoBot::record] requesting local recording privilege (host must approve)\n";
                        recordingController_->RequestLocalRecordingPrivilege();
                        g_main_loop_run(loop_);   // quits when onRecordPrivilegeChanged sets g_canRecord
                }
                if (!g_canRecord) {
                        std::cout << "[DemoBot::record] recording not permitted\n";
                        return false;
                }

                ZOOMSDK::SDKError err = recordingController_->StartRawRecording();
                std::cout << "[DemoBot::record] StartRawRecording: " << err << "\n";
                if (err != ZOOMSDK::SDKERR_SUCCESS) {
                        return false;
                }

                if (auto* mi = meetingService_->GetMeetingInfo())
                        std::cout << "[meeting] number=" << mi->GetMeetingNumber() << "\n";

                // raw audio requires the bot to be in the audio session, otherwise
                // subscribe returns SDKERR_NOT_JOIN_AUDIO (32).
                if (auto* audioCtrl = meetingService_->GetMeetingAudioController()) {
                        ZOOMSDK::SDKError ja = audioCtrl->JoinVoip();
                        std::cout << "[DemoBot::record] JoinVoip: " << ja << "\n";
                }

                if (auto* helper = ZOOMSDK::GetAudioRawdataHelper()) {
                        err = helper->subscribe(&audioListener_);
                        std::cout << "[DemoBot::record] audio subscribe: " << err << "\n";
                }

                reportTimerId_ = g_timeout_add_seconds(1, &DemoBot::reportTick, this);

                // capture loop: runs until the meeting ends (MeetingListener quits on ENDED/FAILED)
                std::cout << "[DemoBot::record] capturing; loop runs until meeting ends...\n";
                g_main_loop_run(loop_);
                return true;
        }

        ZOOMSDK::SDKError addRenderer(uint32_t id, ZOOMSDK::ZoomSDKRawDataType type, const char* label) {
                ZOOMSDK::IZoomSDKRenderer* r = nullptr;
                ZOOMSDK::SDKError err = ZOOMSDK::createRenderer(&r, &videoListener_);
                if (err != ZOOMSDK::SDKERR_SUCCESS || !r) {
                        std::cout << "[DemoBot] createRenderer " << label << " src=" << id << " failed: " << err << "\n";
                        return err;
                }
                r->setRawDataResolution(ZOOMSDK::ZoomSDKResolution_720P);
                err = r->subscribe(id, type);
                std::cout << "[DemoBot] subscribe " << label << " src=" << id << " -> " << err << "\n";
                if (err != ZOOMSDK::SDKERR_SUCCESS) {
                        ZOOMSDK::destroyRenderer(r);   // don't keep a renderer that failed to subscribe
                        return err;
                }
                renderers_.push_back(r);
                return err;
        }

        // poll current participants + shares and subscribe any new source (no event callbacks)
        void subscribeNewVideoSources() {
                if (auto* pc = meetingService_->GetMeetingParticipantsController()) {
                        if (auto* users = pc->GetParticipantsList()) {
                                for (int i = 0; i < users->GetCount(); ++i) {
                                        uint32_t uid = users->GetItem(i);
                                        if (subscribedVideo_.insert(uid).second) {
                                                if (auto* u = pc->GetUserByUserID(uid)) {
                                                        std::cout << "[participant] id=" << uid
                                                                  << " name='" << (u->GetUserName() ? u->GetUserName() : "") << "'"
                                                                  << " host=" << u->IsHost()
                                                                  << " self=" << u->IsMySelf()
                                                                  << " bot=" << u->IsBotUser()
                                                                  << " videoOn=" << u->IsVideoOn() << "\n";
                                                }
                                                addRenderer(uid, ZOOMSDK::RAW_DATA_TYPE_VIDEO, "video");
                                        }
                                        // SHARE subscribe returns SDKERR_NO_SHARE_DATA(29) until the user is
                                        // actually sharing; RETRY each tick until it succeeds so we capture
                                        // what subscribe(userId, RAW_DATA_TYPE_SHARE) does during an active share.
                                        if (subscribedShare_.find(uid) == subscribedShare_.end()) {
                                                if (addRenderer(uid, ZOOMSDK::RAW_DATA_TYPE_SHARE, "share") == ZOOMSDK::SDKERR_SUCCESS)
                                                        subscribedShare_.insert(uid);
                                        }
                                }
                        }
                }
                if (auto* sc = meetingService_->GetMeetingShareController()) {
                        auto* sharers = sc->GetViewableSharingUserList();
                        std::cout << "[poll] viewable sharers=" << (sharers ? sharers->GetCount() : -1) << "\n";
                        if (sharers) {
                                for (int i = 0; i < sharers->GetCount(); ++i) {
                                        uint32_t suid = sharers->GetItem(i);
                                        auto* infos = sc->GetSharingSourceInfoList(suid);
                                        std::cout << "[poll] sharer uid=" << suid
                                                  << " sources=" << (infos ? infos->GetCount() : -1) << "\n";
                                        if (infos) {
                                                for (int j = 0; j < infos->GetCount(); ++j) {
                                                        ZOOMSDK::ZoomSDKSharingSourceInfo info = infos->GetItem(j);
                                                        std::cout << "[poll] share source id=" << info.shareSourceID
                                                                  << " status=" << info.status
                                                                  << " type=" << info.contentType << "\n";
                                                        if (subscribedShare_.insert(info.shareSourceID).second)
                                                                addRenderer(info.shareSourceID, ZOOMSDK::RAW_DATA_TYPE_SHARE, "share");
                                                }
                                        }
                                }
                        }
                }
        }

        static gboolean reportTick(gpointer self) {
                auto* bot = static_cast<DemoBot*>(self);
                bot->subscribeNewVideoSources();   // pick up new cameras / screenshares

                for (const auto& kv : bot->audioListener_.micSnapshot())
                        std::cout << "[data] mic-audio   src=" << kv.first << " total_bytes=" << kv.second << "\n";
                for (const auto& kv : bot->audioListener_.shareSnapshot())
                        std::cout << "[data] share-audio src=" << kv.first << " total_bytes=" << kv.second << "\n";
                for (const auto& kv : bot->videoListener_.snapshot())
                        std::cout << "[data] video      src=" << kv.first << " total_bytes=" << kv.second << "\n";
                return TRUE;   // reschedule every second
        }

};


int main(int argc, char *argv[]) {
        setvbuf(stdout, nullptr, _IONBF, 0);   // unbuffered stdout so logs flush even while blocked in the loop
        std::cout << "meeting screenshare reproduction!\n";

        // step 1: read meeting + auth config from the environment
        const char* jwt          = std::getenv("ZOOM_SDK_JWT");
        const char* meetingIdEnv  = std::getenv("ZOOM_MEETING_ID");
        const char* meetingPwEnv  = std::getenv("ZOOM_MEETING_PASSWORD");
        if (!jwt || !meetingIdEnv) {
                std::cout << "[main] missing required env: ZOOM_SDK_JWT and/or ZOOM_MEETING_ID\n";
                return 1;
        }

        const char* recTokenEnv   = std::getenv("ZOOM_RECORDING_TOKEN");
        long long   meetingId       = std::stoll(meetingIdEnv);       // meeting number
        std::string meetingPassword = meetingPwEnv ? meetingPwEnv : "";
        std::string meetingToken    = recTokenEnv ? recTokenEnv : "";  // local-recording token (app_privilege_token)

        DemoBot bot(meetingId, meetingPassword, meetingToken);  // InitSDK happens here

        // step 2: authenticate (blocks until onAuthenticationReturn fires)
        bot.authenticate(jwt);
        if (!g_sdkAuth) {
                std::cout << "[main] auth did not succeed; aborting\n";
                ZOOMSDK::CleanUPSDK();
                return 1;
        }

        // step 3: join the meeting (blocks until INMEETING or FAILED)
        // NOTE: requires a real, active meeting; a fake id yields MEETING_STATUS_FAILED
        if (bot.join()) {
                // step 4: start raw recording + capture (blocks in the loop until the meeting ends)
                bot.record();
        }

        ZOOMSDK::CleanUPSDK();
        return 0;
}

@z888888888 I’ve just confirmed that share does not work on arm64

@chunsiong.zoom Thank you for helping validate this bug

  • Do you know which of the 7.x meeting SDKs do function/are supported?

@z888888888 Sorry I’m not sure. But I’ve tested Meeting SDK Linux x64 and it works for Video, Audio and Sharescreen Raw access.