diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8d62a1d --- /dev/null +++ b/.gitignore @@ -0,0 +1,438 @@ +# Created by https://www.toptal.com/developers/gitignore/api/visualstudio,c++ +# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudio,c++ + +### C++ ### +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +# *.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +# *.lib + +# Executables +*.exe +*.out +*.app + +### VisualStudio ### +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +# x64/ +x64/bin/*.map +# x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +# [Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.iobj +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml + +### VisualStudio Patch ### +# Additional files built by Visual Studio + +# End of https://www.toptal.com/developers/gitignore/api/visualstudio,c++ \ No newline at end of file diff --git a/x64/demo/sdk_demo_v2/CustomizedUIRecordMgr.cpp b/x64/demo/sdk_demo_v2/CustomizedUIRecordMgr.cpp index 4cce4d9..5c0a77c 100644 --- a/x64/demo/sdk_demo_v2/CustomizedUIRecordMgr.cpp +++ b/x64/demo/sdk_demo_v2/CustomizedUIRecordMgr.cpp @@ -1,5 +1,9 @@ #include "stdafx.h" #include "CustomizedUIRecordMgr.h" +#include "ZoomSDKAudioRawDataDelegate.h" +#include "rawdata/rawdata_audio_helper_interface.h" +#include "zoom_sdk_raw_data_def.h" +#include "rawdata/zoom_rawdata_api.h" CustomizedUIRecordMgr* CustomizedUIRecordMgr::s_recordMgrObj=NULL; @@ -324,10 +328,22 @@ void CustomizedUIRecordMgr::onCloudRecordingStatus(ZOOM_SDK_NAMESPACE::Recording void CustomizedUIRecordMgr::onRecordPriviligeChanged(bool bCanRec) { - if (bCanRec) - ::MessageBox(NULL, _T("you can record now."), _T("winsdk demo"), MB_OK); - else + if (bCanRec) { + auto recordingController = SDKInterfaceWrap::GetInst().GetMeetingService()->GetMeetingRecordingController(); + + if (recordingController->CanStartRecording(false, 0) == ZOOMSDK::SDKError::SDKERR_SUCCESS) { + if (recordingController->StartRawRecording() == ZOOMSDK::SDKError::SDKERR_SUCCESS) { + ZoomSDKAudioRawDataDelegate *customDelegate; + //Todo fix potential memory leak + customDelegate = new ZoomSDKAudioRawDataDelegate(); + ZOOM_SDK_NAMESPACE::IZoomSDKAudioRawDataHelper* helper = ZOOM_SDK_NAMESPACE::GetAudioRawdataHelper(); + helper->subscribe(customDelegate); + } + } + } + else { ::MessageBox(NULL, _T("you can not record now."), _T("winsdk demo"), MB_OK); + } } void CustomizedUIRecordMgr::onCustomizedLocalRecordingSourceNotification(ZOOM_SDK_NAMESPACE::ICustomizedLocalRecordingLayoutHelper* layout_helper) diff --git a/x64/demo/sdk_demo_v2/LOGIN_sdk_login_UI.cpp b/x64/demo/sdk_demo_v2/LOGIN_sdk_login_UI.cpp index 0e4dd7d..5fd02fc 100644 --- a/x64/demo/sdk_demo_v2/LOGIN_sdk_login_UI.cpp +++ b/x64/demo/sdk_demo_v2/LOGIN_sdk_login_UI.cpp @@ -3,6 +3,9 @@ #include #include "auth_service_interface.h" #include "mess_info.h" +#include +#include "SNSService.h" +#include ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// //class CSDKLoginWithSSOUIGroup @@ -191,6 +194,8 @@ void CSDKWithoutLoginStartJoinMeetingUIGroup::DoWithoutLoginStartJoinMeetingBtnC std::wstring MeetingPassword = m_editMeetingPassword->GetText().GetData(); std::wstring ScreenName = m_editScreenName->GetText().GetData(); withoutloginParam.meetingNumber = _wtoi64(MeetingNumber.c_str()); + if (withoutloginParam.meetingNumber == 0) + return; withoutloginParam.vanityID = NULL; withoutloginParam.userName = ScreenName.c_str(); withoutloginParam.psw = MeetingPassword.c_str(); @@ -201,6 +206,7 @@ void CSDKWithoutLoginStartJoinMeetingUIGroup::DoWithoutLoginStartJoinMeetingBtnC withoutloginParam.isDirectShareDesktop = false; withoutloginParam.isVideoOff = false; withoutloginParam.isAudioOff = false; + withoutloginParam.join_token = NULL; if(MeetingNumber.size() > 0) { @@ -243,53 +249,121 @@ void CSDKWithoutLoginStartJoinMeetingUIGroup::DoWithoutLoginStartJoinMeetingBtnC void CSDKWithoutLoginStartJoinMeetingUIGroup::onMeetingStatusChanged(ZOOM_SDK_NAMESPACE::MeetingStatus status, int iResult) { + //Todo: better handling of notification and m_bInMeeting to help orchestrator decide whether + // 1/ bot has or is joining a meeting and is occupied, 2/ bot is free to be assigned 3/ unexpected error switch (status) { case ZOOM_SDK_NAMESPACE::MEETING_STATUS_RECONNECTING: if (SDKInterfaceWrap::GetInst().IsSelectCustomizedUIMode()) { CSDKDemoAppEvent* pAppEvent = m_parentFrame->GetAppEvent(); - if(pAppEvent) + if (pAppEvent) pAppEvent->InitCustomizedUI(); } + // m_bInMeeting = false; + PublishMeetingStatusChangedToSNS("MEETING_STATUS_RECONNECTING"); break; + case ZOOM_SDK_NAMESPACE::MEETING_STATUS_CONNECTING: + // m_bInMeeting = false; + PublishMeetingStatusChangedToSNS("MEETING_STATUS_CONNECTING"); + break; + case ZOOM_SDK_NAMESPACE::MEETING_STATUS_DISCONNECTING: + // m_bInMeeting = false; + PublishMeetingStatusChangedToSNS("MEETING_STATUS_DISCONNECTING"); break; + case ZOOM_SDK_NAMESPACE::MEETING_STATUS_ENDED: + if (m_parentFrame) { - if(m_parentFrame) - { - m_parentFrame->GetAppEvent()->onJoinFailed(); - m_parentFrame->SetCurrentPage(m_WithoutLoginStartJoinMeetingPage); - } - + // todo: maybe sns should get a failed + m_parentFrame->GetAppEvent()->onJoinFailed(); + m_parentFrame->SetCurrentPage(m_WithoutLoginStartJoinMeetingPage); } + m_bInMeeting = false; + PublishMeetingStatusChangedToSNS("MEETING_STATUS_ENDED"); break; + case ZOOM_SDK_NAMESPACE::MEETING_STATUS_FAILED: + if (m_parentFrame) { - if(m_parentFrame) - { - m_parentFrame->SetCurrentPage(m_WithoutLoginStartJoinMeetingPage); + m_parentFrame->SetCurrentPage(m_WithoutLoginStartJoinMeetingPage); - TCHAR szError[128] = { 0 }; - swprintf_s(szError,128, _T("Join meeting failed, error_code = %d"), iResult); - m_parentFrame->ShowErrorMessage(szError); - } + TCHAR szError[128] = { 0 }; + swprintf_s(szError, 128, _T("Join meeting failed, error_code = %d\n"), iResult); + m_parentFrame->ShowErrorMessage(szError); } + m_bInMeeting = false; + PublishMeetingStatusChangedToSNS("MEETING_STATUS_FAILED"); break; + case ZOOM_SDK_NAMESPACE::MEETING_STATUS_INMEETING: + if (m_parentFrame) { - if(m_parentFrame) - { - m_parentFrame->GetAppEvent()->onShowLoggedInUI(Demo_Meeting_Join_Only); - } + m_parentFrame->GetAppEvent()->onShowLoggedInUI(Demo_Meeting_Join_Only); } + m_bInMeeting = true; + PublishMeetingStatusChangedToSNS("MEETING_STATUS_INMEETING"); + break; + + case ZOOM_SDK_NAMESPACE::MEETING_STATUS_IN_WAITING_ROOM: + // m_bInMeeting = false; + PublishMeetingStatusChangedToSNS("MEETING_STATUS_IN_WAITING_ROOM"); + break; + + case ZOOM_SDK_NAMESPACE::MEETING_STATUS_WAITINGFORHOST: + // m_bInMeeting = false; + PublishMeetingStatusChangedToSNS("MEETING_STATUS_WAITINGFORHOST"); + break; + default: break; + } } +void CSDKWithoutLoginStartJoinMeetingUIGroup::PublishMeetingStatusChangedToSNS(std::string status) +{ + auto sns_topic_arn = std::getenv("SNS_TOPIC_ARN"); + + if (sns_topic_arn == NULL || strlen(sns_topic_arn) < 1) + { + std::cout << "SNS Topic ARN cannot be empty string or NULL" << std::endl; + return; + } + + if (status.empty()) { + std::cout << "SNS message cannot be empty string or NULL" << std::endl; + return; + } + + std::string message; + message += status; + + std::map message_attributes; + message_attributes["event_type"] = "MEETING_STATUS_CHANGE"; + message_attributes["meeting_status"] = status; + + ZOOM_SDK_NAMESPACE::IMeetingService* meeting_service = SDKInterfaceWrap::GetInst().GetMeetingService(); + if (meeting_service != NULL && meeting_service->GetMeetingInfo()) + { + std::ostringstream meeting_number_ostream; + meeting_number_ostream << meeting_service->GetMeetingInfo()->GetMeetingNumber(); + auto meeting_number = meeting_number_ostream.str(); + message_attributes["meeting_number"] = meeting_number; + + message += " for meeting number: " + meeting_number; + } + else + { + message += " for unknown meeting number"; + } + + std::shared_ptr sns = sns->instance(); + sns->Publish(sns_topic_arn, message, message_attributes); +} + ////////////////////////////////////////////////////////////////////////////////////////////// //class CSDKRestAPIUserUIGroup CSDKRestAPIUserUIGroup::CSDKRestAPIUserUIGroup() @@ -562,12 +636,43 @@ void CSDKLoginUIMgr::InitWindow() m_restApi_login_page = static_cast(m_PaintManager.FindControl(L"panel_RestAPI_Without_Login")); m_only_join_page = static_cast(m_PaintManager.FindControl(L"panel_Join_Meeting_Only")); - m_currentPage = m_sso_login_page; + m_currentPage = m_only_join_page; SwitchToWaitingPage(L"", false); - SwitchToPage(login_UseSSO_Page); + SwitchToPage(login_JoinMeetingOnly_Page); m_LoginCBHandler.SetUIEvent(this); } +bool CSDKLoginUIMgr::attemptJoin(string meeting_id, string meeting_passcode, string display_name) +{ + std::unique_lock lock(join_meeting_mutex); + std::cout << "&&^&** ===== " << (m_WithoutLoginStartJoinMeetingUIGroup.m_bInMeeting ? "IN MEETING" : "not in meeting"); + + if (m_WithoutLoginStartJoinMeetingUIGroup.m_bInMeeting || + NULL == m_WithoutLoginStartJoinMeetingUIGroup.m_editMeetingNumber || + NULL == m_WithoutLoginStartJoinMeetingUIGroup.m_editMeetingPassword || + NULL == m_WithoutLoginStartJoinMeetingUIGroup.m_editScreenName || + meeting_id.length() < 1 || meeting_passcode.length() < 1 || display_name.length() < 1) + return false; + + std::wstring_convert> widener; + auto wmeeting_id = widener.from_bytes(meeting_id); + auto wmeeting_passcode = widener.from_bytes(meeting_passcode); + auto wdisplay_name = widener.from_bytes(display_name); + + // todo: consider below if the right window is not showing + // SwitchToPage(login_JoinMeetingOnly_Page); + + //todo: DO better way to attempt join than via UI manipulation + + m_WithoutLoginStartJoinMeetingUIGroup.m_editMeetingNumber->SetText((LPCTSTR)wmeeting_id.c_str()); + m_WithoutLoginStartJoinMeetingUIGroup.m_editMeetingPassword->SetText((LPCTSTR)wmeeting_passcode.c_str()); + m_WithoutLoginStartJoinMeetingUIGroup.m_editScreenName->SetText((LPCTSTR)wdisplay_name.c_str()); + + m_WithoutLoginStartJoinMeetingUIGroup.DoWithoutLoginStartJoinMeetingBtnClick(); + return true; + +} + void CSDKLoginUIMgr::Notify(TNotifyUI& msg) { if(msg.sType == _T("click")) @@ -684,7 +789,12 @@ void CSDKLoginUIMgr::SwitchToWaitingPage(const wchar_t* waiting_message, bool sh void CSDKLoginUIMgr::ShowErrorMessage(const wchar_t* error_message) { if (error_message) - ::MessageBox(NULL, error_message, L"error", MB_OK); + { + // Todo better logging + std::wstring_convert> narrower; + std::cout << "error: " << narrower.to_bytes(error_message); + // ::MessageBox(NULL, error_message, L"error", MB_OK); + } } void CSDKLoginUIMgr::SwitchToPage(loginTabPage nPage) @@ -751,7 +861,7 @@ void CSDKLoginUIMgr::NotifyAuthDone() void CSDKLoginUIMgr::SwitchToPage(SwitchToLoginUIType type_) { if(SwitchToLoginUIType_AUTHDONE == type_) - SwitchToPage(login_UseSSO_Page); + SwitchToPage(login_JoinMeetingOnly_Page); } void CSDKLoginUIMgr::CleanUp() @@ -782,6 +892,7 @@ void CSDKLoginCBHandler::onLoginReturnWithReason(ZOOM_SDK_NAMESPACE::LOGINSTATUS { if (ZOOM_SDK_NAMESPACE::LOGIN_SUCCESS == ret) { + std::cout << "LOGIN SUCCESS" << endl; TCHAR szlog[MAX_PATH] = {0}; swprintf_s(szlog, MAX_PATH,_T("userName:%s\r\nloggin success"),pAccountInfo->GetDisplayName()); OutputDebugString(szlog); @@ -792,7 +903,7 @@ void CSDKLoginCBHandler::onLoginReturnWithReason(ZOOM_SDK_NAMESPACE::LOGINSTATUS } if (ZOOM_SDK_NAMESPACE::LOGIN_FAILED == ret) { - + std::cout << "LOGIN FAILED; Reason code: " << reason << endl; if(m_parentFrame && m_parentFrame->GetAppEvent()) { m_parentFrame->GetAppEvent()->onLoginFailed(); diff --git a/x64/demo/sdk_demo_v2/LOGIN_sdk_login_UI.h b/x64/demo/sdk_demo_v2/LOGIN_sdk_login_UI.h index 5e1c53f..e46f0e1 100644 --- a/x64/demo/sdk_demo_v2/LOGIN_sdk_login_UI.h +++ b/x64/demo/sdk_demo_v2/LOGIN_sdk_login_UI.h @@ -6,6 +6,8 @@ #include "LOGIN_join_meeting_only_workflow.h" #include "LOGIN_restapi_without_login_workflow.h" #include "sdk_demo_app_common.h" +#include +#include ///////////////////////// enum loginTabPage { @@ -71,16 +73,22 @@ public: virtual void onMeetingStatusChanged(ZOOM_SDK_NAMESPACE::MeetingStatus status, int iResult = 0); virtual void onMeetingStatisticsWarningNotification(ZOOM_SDK_NAMESPACE::StatisticsWarningType type) {}; virtual void onMeetingParameterNotification(const ZOOM_SDK_NAMESPACE::MeetingParameter* meeting_param) {}; + + //todo, setting these text fields as a data binding breaks abstractions! +public: + CRichEditUI* m_editMeetingNumber; + CRichEditUI* m_editScreenName; + CRichEditUI* m_editMeetingPassword; + std::atomic m_bInMeeting; protected: CVerticalLayoutUI* m_WithoutLoginStartJoinMeetingPage; - CRichEditUI* m_editMeetingNumber; - CRichEditUI* m_editScreenName; - CRichEditUI* m_editMeetingPassword; CButtonUI* m_btnJoin; CSDKLoginUIMgr* m_parentFrame; CSDKWithoutLoginStartJoinMeetingFlow m_withoutLoginJoinMeetingWorkFlow; - bool m_bInMeeting; + +private: + void PublishMeetingStatusChangedToSNS(std::string status); }; class CSDKRestAPIUserUIGroup : public CSDKRestAPIUserUIEvent @@ -125,6 +133,11 @@ public: virtual ~CSDKLoginUIMgr(); void SetEvent(CSDKDemoAppEvent* pAppEvent); + +public: + bool attemptJoin(string meeting_id, string meeting_passcode, string display_name); + std::mutex join_meeting_mutex; + public: virtual LPCTSTR GetWindowClassName() const { return _T("zSDKDemoUI"); } UINT GetClassStyle() const { return UI_CLASSSTYLE_FRAME | CS_DBLCLKS ; }; diff --git a/x64/demo/sdk_demo_v2/SNSService.cpp b/x64/demo/sdk_demo_v2/SNSService.cpp new file mode 100644 index 0000000..fc6ec99 --- /dev/null +++ b/x64/demo/sdk_demo_v2/SNSService.cpp @@ -0,0 +1,123 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#include "stdafx.h" +#include "SNSService.h" +#include "sdk_demo_app.h" + +SNSService::SNSService() { + sns = Aws::MakeShared("SNS_ALLOCATION_TAG"); +} + + +bool SNSService::CreateTopic(Aws::String topic_name, Aws::String& topic_arn) { + + Aws::SNS::Model::CreateTopicRequest ct_req; + ct_req.SetName(topic_name); + + auto ct_out = sns->CreateTopic(ct_req); + if (ct_out.IsSuccess()) + { + std::cout << "Successfully created topic " << topic_name << std::endl; + topic_arn = ct_out.GetResult().GetTopicArn(); + return true; + } + else + { + std::cout << "Error creating topic " << topic_name << ": " << + ct_out.GetError().GetMessage() << std::endl; + return false; + } + +} + +bool SNSService::SubscribeEndpoint(Aws::String topic_arn, Aws::String protocol, Aws::String endpoint) { + + Aws::SNS::Model::SubscribeRequest s_req; + s_req.SetTopicArn(topic_arn); + s_req.SetProtocol(protocol); + s_req.SetEndpoint(endpoint); + + auto s_out = sns->Subscribe(s_req); + + if (s_out.IsSuccess()) + { + std::cout << "Successfully subscribed to topic " << topic_arn << std::endl; + return true; + } + else + { + std::cout << "Error subscribing to topic " << topic_arn << ": " << + s_out.GetError().GetMessage() << std::endl; + return false; + } +} + +bool SNSService::Publish(Aws::String topic_arn, Aws::String message) +{ + Aws::SNS::Model::PublishRequest psms_req; + psms_req.SetMessage(message); + psms_req.SetTopicArn(topic_arn); + + auto psms_out = sns->Publish(psms_req); + + if (psms_out.IsSuccess()) + { + std::cout << "Message: '" << message << "' published successfully to SNS topic " << topic_arn << std::endl; + return true; + } + else + { + std::cout << "Error while publishing message to topic " << topic_arn << psms_out.GetError().GetMessage() + << std::endl; + return false; + } +} + +bool SNSService::Publish(Aws::String topic_arn, Aws::String message, Aws::Map attributes) +{ + Aws::SNS::Model::PublishRequest psms_req; + psms_req.SetMessage(message); + psms_req.SetTopicArn(topic_arn); + + attributes["vpf_uid"] = g_demoApp.vpf_uid; + + for (const auto& pair : attributes) { + Aws::SNS::Model::MessageAttributeValue attribute_value; + attribute_value.SetDataType("String"); + attribute_value.SetStringValue(pair.second.c_str()); + psms_req.AddMessageAttributes(pair.first, attribute_value); + } + + auto psms_out = sns->Publish(psms_req); + + if (psms_out.IsSuccess()) + { + std::cout << "Message: '" << message <<"' published successfully to SNS topic " << topic_arn << std::endl; + return true; + } + else + { + std::cout << "Error while publishing message to topic " << topic_arn << psms_out.GetError().GetMessage() + << std::endl; + return false; + } +} + +bool SNSService::DeleteTopic(Aws::String topic_arn) { + + Aws::SNS::Model::DeleteTopicRequest dt_req; + dt_req.SetTopicArn(topic_arn); + + auto dt_out = sns->DeleteTopic(dt_req); + if (dt_out.IsSuccess()) + { + std::cout << "Successfully deleted topic " << topic_arn << std::endl; + return true; + } + else { + std::cout << "Error deleting topic " << topic_arn << ": " << + dt_out.GetError().GetMessage() << std::endl; + return false; + } +} \ No newline at end of file diff --git a/x64/demo/sdk_demo_v2/SNSService.h b/x64/demo/sdk_demo_v2/SNSService.h new file mode 100644 index 0000000..e4d21af --- /dev/null +++ b/x64/demo/sdk_demo_v2/SNSService.h @@ -0,0 +1,38 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#include "stdafx.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class SNSService +{ +public: + SNSService(SNSService const&) = delete; + SNSService& operator=(SNSService const&) = delete; + + static std::shared_ptr instance() { + static std::shared_ptr s{ new SNSService }; + return s; + } + + std::shared_ptr sns; + + bool CreateTopic(Aws::String topic_name, Aws::String& topic_arn); + bool DeleteTopic(Aws::String topic_arn); + bool SubscribeEndpoint(Aws::String topic_arn, Aws::String protocol, Aws::String endpoint); + bool Publish(Aws::String topic_arn, Aws::String message); + bool Publish(Aws::String topic_arn, Aws::String message, Aws::Map attributes); + +private: + SNSService(); +}; diff --git a/x64/demo/sdk_demo_v2/ZoomSDKAudioRawDataDelegate.cpp b/x64/demo/sdk_demo_v2/ZoomSDKAudioRawDataDelegate.cpp new file mode 100644 index 0000000..f3c15b8 --- /dev/null +++ b/x64/demo/sdk_demo_v2/ZoomSDKAudioRawDataDelegate.cpp @@ -0,0 +1,330 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#include "stdafx.h" +#include "ZoomSDKAudioRawDataDelegate.h" +#include +#include "SNSService.h" + + +using namespace std; +using namespace ZOOM_SDK_NAMESPACE; + +ZoomSDKAudioRawDataDelegate::ZoomSDKAudioRawDataDelegate() +{ + // to do error checking for empty env + // TODO: subscribe a callback to capture errors + access_key = std::getenv("AWS_ACCESS_KEY_ID"); + secret_key = std::getenv("AWS_SECRET_ACCESS_KEY"); + aws_region = std::getenv("AWS_DEFAULT_REGION"); + + auto kvs_stream_suffix = std::getenv("KVS_STREAM_SUFFIX"); + std::ostringstream stream_name_ostream; + stream_name_ostream << "zoom-meeting"; + + //If meeting ID is missing passing empty to SNS is better than failing + ZOOM_SDK_NAMESPACE::IMeetingService* meeting_service = SDKInterfaceWrap::GetInst().GetMeetingService(); + if (meeting_service != NULL && meeting_service->GetMeetingInfo()) + { + std::ostringstream meeting_number_ostream; + meeting_number_ostream << meeting_service->GetMeetingInfo()->GetMeetingNumber(); + meeting_number = meeting_number_ostream.str(); + stream_name_ostream << '-' << meeting_number; + } + + if (kvs_stream_suffix != NULL && strlen(kvs_stream_suffix) > 0) + stream_name_ostream << '-' << kvs_stream_suffix; + + //stream name must be less than 256 bits + meeting_kvs_stream_name = stream_name_ostream.str().substr(0, 256); + + PublishVPFRecordingStatusChangedToSNS("VPF_RECORDING_STARTED"); + + if(USE_MIXED_AUDIO) + { + SetupGstPipeline(meeting_kvs_stream_name.c_str() , "kvs-mixedaudio-pipeline", mixedAudioData); + } + else + { + SetupGstPipeline("zoom-meetings-demo-caller", "kvs-caller-pipeline", callerData); + SetupGstPipeline("zoom-meetings-demo-agent", "kvs-agent-pipeline", agentData); + } +} + +void ZoomSDKAudioRawDataDelegate::PublishVPFRecordingStatusChangedToSNS(std::string vpf_recording_status) +{ + auto sns_topic_arn = std::getenv("SNS_TOPIC_ARN"); + if (sns_topic_arn == NULL || strlen(sns_topic_arn) < 1) + { + std::cout << "SNS Topic ARN cannot be empty string or NULL" << std::endl; + return; + } + + auto message = vpf_recording_status + " and publishing to kvs stream: " + meeting_kvs_stream_name + " for Zoom meeting number: " + meeting_number; + + std::map message_attributes; + message_attributes["event_type"] = "VPF_RECORDING_STATUS_CHANGE"; + message_attributes["vpf_recording_status"] = vpf_recording_status; + message_attributes["kvs_stream_name"] = meeting_kvs_stream_name; + message_attributes["meeting_number"] = meeting_number; + + std::shared_ptr sns = sns->instance(); + sns->Publish(sns_topic_arn, message, message_attributes); +} + + +void ZoomSDKAudioRawDataDelegate::SetupGstPipeline(const char * stream_name, const char * pipeline_name, GstCustomData& data) +{ + GstCaps* caps; + GstAudioInfo info; + + /* Initialize custom data structure */ + memset(&data, 0, sizeof(data)); + + /* Initialize GStreamer */ + gst_init(NULL, NULL); + + /* Create the elements */ + data.appsrc = gst_element_factory_make("appsrc", "appsrc"); + data.audiotestsrc = gst_element_factory_make("audiotestsrc", "audiotestsrc"); + data.audioconvert = gst_element_factory_make("audioconvert", "audioconvert"); + data.audio_queue1 = gst_element_factory_make("queue", "audio_queue1"); + // data.audioenc = gst_element_factory_make("voaacenc", "audioenc"); + data.audioenc = gst_element_factory_make("mulawenc", "audioenc"); + + /* video elements */ +#ifdef ENABLE_VIDEO + data.videotestsrc = gst_element_factory_make("videotestsrc", "videotestsrc"); + data.src_filter = gst_element_factory_make("capsfilter", "src_filter"); + data.video_queue = gst_element_factory_make("queue", "video_queue"); + data.x264enc = gst_element_factory_make("x264enc", "x264enc"); + data.h264parse = gst_element_factory_make("h264parse", "h264parse"); +#endif + + data.sink = gst_element_factory_make("kvssink", "sink"); + + /* Create the empty pipeline */ + data.pipeline = gst_pipeline_new(pipeline_name); + + if (!data.pipeline || !data.appsrc || !data.audiotestsrc || !data.audioconvert || !data.audio_queue1 || !data.audioenc || +#ifdef ENABLE_VIDEO + !data.videotestsrc || !data.video_queue || !data.x264enc || !data.h264parse || +#endif + !data.sink) + { + g_printerr("Not all elements could be created.\n"); + } + + /* Configure appsrc */ + gst_audio_info_set_format(&info, GST_AUDIO_FORMAT_S16, ZOOM_MEETING_AUDIO_SAMPLE_RATE, 1, NULL); + caps = gst_audio_info_to_caps(&info); + g_object_set(data.appsrc, "caps", caps, "format", GST_FORMAT_TIME, NULL); + gst_caps_unref(caps); + + /* Build the pipeline */ + gst_bin_add_many(GST_BIN(data.pipeline), data.appsrc, data.audio_queue1, data.audioconvert, data.audioenc, +#ifdef ENABLE_VIDEO + data.videotestsrc, data.src_filter, data.video_queue, data.x264enc, data.h264parse, +#endif + data.sink, NULL); + + /* convert pcm to aac for defult DASH playback (needs video) or mulaw for transcription*/ + if (!gst_element_link_many(data.appsrc, data.audio_queue1, data.audioconvert, data.audioenc, data.sink, NULL)) + { + g_printerr("Audio elements could not be linked.\n"); + gst_object_unref(data.pipeline); + } + + /* Modify sink */ + g_object_set(data.sink, "fragment-duration", 1000, NULL); + g_object_set(data.sink, "stream-name", stream_name, NULL); + g_object_set(data.sink, "access-key", access_key, NULL); + g_object_set(data.sink, "secret-key", secret_key, NULL); + g_object_set(data.sink, "aws-region", aws_region, NULL); + g_object_set(data.sink, "key-frame-fragmentation", true, NULL); + + + + /* Modify audio queue 1 - purpose is to create new thread, delay not important */ + g_object_set(data.audio_queue1, "max-size-time", 10 * GST_USECOND, NULL); + + + /* Video elements example */ +#ifdef ENABLE_VIDEO + if (!gst_element_link_many(data.videotestsrc, data.src_filter, data.video_queue, data.x264enc, data.h264parse, data.sink, NULL)) + { + g_printerr("Video elements could not be linked.\n"); + gst_object_unref(data.pipeline); + } + + /* Modify videotestsrc */ + g_object_set(data.videotestsrc, "pattern", 2, NULL); + + /* Modify src_filter */ + caps = gst_caps_new_simple("video/x-raw", + "width", G_TYPE_INT, 16, + "height", G_TYPE_INT, 16, + "framerate", GST_TYPE_FRACTION, 30, 1, + // "format", "I420", + NULL); + g_object_set(data.src_filter, "caps", caps, NULL); + gst_caps_unref(caps); + + /* Modify x264enc */ + g_object_set(data.x264enc, "tune", 0x00000004, NULL); + + /* Modify video queue */ + g_object_set(data.video_queue, "max-size-time", 10 * GST_USECOND, NULL); +#endif + + /* Start playing the pipeline to capture audio */ + gst_element_set_state(data.pipeline, GST_STATE_PLAYING); + +} + +void ZoomSDKAudioRawDataDelegate::onMixedAudioRawDataReceived(AudioRawData *data_) +{ + if (USE_MIXED_AUDIO) + { + onAudioRawDataReceivedForGstAppSrc(data_, mixedAudioData, true); + + } +} + +void ZoomSDKAudioRawDataDelegate::onOneWayAudioRawDataReceived(AudioRawData *data_, uint32_t node_id) +{ + if (!USE_MIXED_AUDIO) + { + // auto node_id_string = std::to_string(node_id); + // auto gst_warn_message = "^^^ Data recieved from node: " + node_id_string + " ^^^"; + // GST_WARNING(gst_warn_message.c_str()); + // std::cout << "+++ Data recieved from node: " << node_id << "^^^" << std::endl; + + //lazy load first and second particcipant IDs + if (first_participant_id == 0) + first_participant_id = node_id; + else if (second_participant_id == 0) + second_participant_id = node_id; + + // ignore any other participant audio frames -> only two KVS streams supported now + if (node_id != first_participant_id && node_id != second_participant_id) + { + // gst_warn_message = "#### Unexpected participant is speaking: " + node_id_string + " ####"; + // GST_WARNING(gst_warn_message.c_str()); + // std::cout << "$$$$ Unexpected participant is speaking " << node_id << "$$$$" << std::endl + // << "$$$$ first participant is " << first_participant_id << "$$$$" << std::endl + // << "$$$$ second participant is " << second_participant_id << "$$$$" << std::endl; + /*drop audio frame*/ + return; + } + + if(node_id == first_participant_id) + onAudioRawDataReceivedForGstAppSrc(data_, callerData, false); + else + onAudioRawDataReceivedForGstAppSrc(data_, agentData, false); + } +} + +void ZoomSDKAudioRawDataDelegate::onAudioRawDataReceivedForGstAppSrc(AudioRawData* data_, GstCustomData gstCustomData, boolean useMixedAudio) +{ + GstBuffer* buffer; + gint num_samples = data_->GetBufferLen() / 2; /* Because each sample is 16 bits */ + + /* Create a new empty buffer */ + buffer = gst_buffer_new_and_alloc(data_->GetBufferLen()); + + /* Set its timestamp and duration */ + GST_BUFFER_TIMESTAMP(buffer) = gst_util_uint64_scale(gstCustomData.num_samples, GST_SECOND, data_->GetSampleRate()); + GST_BUFFER_DURATION(buffer) = gst_util_uint64_scale(num_samples, GST_SECOND, data_->GetSampleRate()); + + // if(useMixedAudio) + // injectKeyFramAndMetaData(buffer, gstCustomData); + + gst_buffer_fill(buffer, 0, data_->GetBuffer(), data_->GetBufferLen()); + + gstCustomData.num_samples += num_samples; + + /* Push the buffer into the appsrc */ + gst_app_src_push_buffer((GstAppSrc*)gstCustomData.appsrc, gst_buffer_ref(buffer)); + + /* Free the buffer now that we are done with it */ + gst_buffer_unref(buffer); +} + +// todo: kvssink needs to be modified to accept keyframe fragmentation. 250ms may be an issue vs 1s fragmentaion +void ZoomSDKAudioRawDataDelegate::injectKeyFramAndMetaData(GstBuffer*& buffer, GstCustomData gstCustomData) +{ + /* inject a keyframe every 250 ms to start new fragment */ + if (msecond_counter >= 250) + { + msecond_counter = 0; + + GST_BUFFER_FLAG_SET(buffer, GST_BUFFER_FLAG_DELTA_UNIT); + GST_WARNING( "*** Flag set - key frame ***"); + + auto pts_sec = buffer->pts / GST_SECOND; + auto speaker_name = pts_sec % 4 < 2 ? "Speaker-1" : "Speaker-2"; + + std::ostringstream structure_stream; + structure_stream << KVS_ADD_METADATA_G_STRUCT_NAME << ", " << + KVS_ADD_METADATA_NAME << "=(string)" << "speaker" << ", " << + KVS_ADD_METADATA_VALUE << "=(string)" << speaker_name << '.' << buffer->pts << ", " << + KVS_ADD_METADATA_PERSISTENT << "=" << "true"; + auto structure_string = structure_stream.str().c_str(); + // std::cout << "Structure: " << structure_string << std::endl; + auto gstructure = gst_structure_from_string(structure_string, NULL); + if (gstructure != NULL) + { + auto event = gst_event_new_custom(GST_EVENT_CUSTOM_DOWNSTREAM, gstructure); + auto result = gst_element_send_event(gstCustomData.pipeline, event); + if (!result) + { + GST_ERROR("send metadata failed"); + } + } + else + { + GST_ERROR("structure string is wrong"); + + } + } + msecond_counter += buffer->duration / GST_MSECOND; +} + +ZoomSDKAudioRawDataDelegate::~ZoomSDKAudioRawDataDelegate() +{ + PublishVPFRecordingStatusChangedToSNS("VPF_RECORDING_STOPPED"); + + if (USE_MIXED_AUDIO) + { + TearDownGstPipeline(mixedAudioData); + } + else + { + TearDownGstPipeline(callerData); + TearDownGstPipeline(agentData); + } +} + +//Todo: implement deallocation/memory cleanup +void ZoomSDKAudioRawDataDelegate::TearDownGstPipeline(GstCustomData data) +{ + /* First set pipeline to NULL */ + /* + gst_element_set_state(data.pipeline, GST_STATE_NULL); + */ + + /* Then unref all the data elements */ + /*Multiplexed Audio elements*/ + // GstElement* pipeline, * appsrc, * audio_queue1, * audiotestsrc, * audioconvert, * audioenc; + + /*Sink element -> KVS Sink*/ + // GstElement* sink; + + /* Dropping support for video for now */ +#ifdef ENABLE_VIDEO + // GstElement* videotestsrc, * src_filter, * video_queue, * x264enc, * h264parse; +#endif + +} + diff --git a/x64/demo/sdk_demo_v2/ZoomSDKAudioRawDataDelegate.h b/x64/demo/sdk_demo_v2/ZoomSDKAudioRawDataDelegate.h new file mode 100644 index 0000000..7d16031 --- /dev/null +++ b/x64/demo/sdk_demo_v2/ZoomSDKAudioRawDataDelegate.h @@ -0,0 +1,75 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#include "stdafx.h" +#include "zoom_sdk.h" +#include "rawdata/rawdata_audio_helper_interface.h" +#include "zoom_sdk_raw_data_def.h" + +#include +#include +#include +#include + + +#ifndef ZOOM_MEETING_AUDIO_SAMPLE_RATE +#define ZOOM_MEETING_AUDIO_SAMPLE_RATE 32000 /* Samples per second we are sending */ +#endif +#define KVS_ADD_METADATA_G_STRUCT_NAME "kvs-add-metadata" +#define KVS_ADD_METADATA_NAME "name" +#define KVS_ADD_METADATA_VALUE "value" +#define KVS_ADD_METADATA_PERSISTENT "persist" + +// #define ENABLE_VIDEO true +#define USE_MIXED_AUDIO true + + +/* Structure to contain all our information, so we can pass it to callbacks */ +typedef struct _GstCustomData +{ + /*Multiplexed Audio elements*/ + GstElement* pipeline, * appsrc, * audio_queue1, * audiotestsrc, * audioconvert, * audioenc; + + /*Sink element -> KVS Sink*/ + GstElement* sink; + + /* Dropping support for video for now */ +#ifdef ENABLE_VIDEO + GstElement* videotestsrc, * src_filter, * video_queue, * x264enc, * h264parse; +#endif + + /* Number of samples generated so far (for timestamp generation) */ + guint64 num_samples; +} GstCustomData; + +class ZoomSDKAudioRawDataDelegate : public ZOOM_SDK_NAMESPACE::IZoomSDKAudioRawDataDelegate { +private: + GstCustomData mixedAudioData; + GstCustomData callerData; + GstCustomData agentData; + +private: + void PublishVPFRecordingStatusChangedToSNS(std::string vpf_recording_status); + void SetupGstPipeline(const char* stream_name, const char* pipeline_name, GstCustomData& data); + void onAudioRawDataReceivedForGstAppSrc(AudioRawData* data_, GstCustomData gstCustomData, boolean useMixedAudio); + void injectKeyFramAndMetaData(GstBuffer*& buffer, GstCustomData data); + void TearDownGstPipeline(GstCustomData data); + + char const* access_key, * secret_key, * aws_region; + std::string meeting_kvs_stream_name; + std::string meeting_number; + + std::atomic msecond_counter; + std::atomic first_participant_id = 0; + std::atomic second_participant_id = 0; + + + +public: + ZoomSDKAudioRawDataDelegate(); + virtual ~ZoomSDKAudioRawDataDelegate(); + // Inherited via IZoomSDKAudioRawDataDelegate + virtual void onMixedAudioRawDataReceived(AudioRawData* data_) override; + virtual void onOneWayAudioRawDataReceived(AudioRawData* data_, uint32_t node_id) override; +}; \ No newline at end of file diff --git a/x64/demo/sdk_demo_v2/gui_output_to_console.cpp b/x64/demo/sdk_demo_v2/gui_output_to_console.cpp new file mode 100644 index 0000000..44ee113 --- /dev/null +++ b/x64/demo/sdk_demo_v2/gui_output_to_console.cpp @@ -0,0 +1,85 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#include "stdafx.h" +#include "gui_output_to_console.h" + + +// Redirect stdin to CONIN$ and stedout and stderr CONOUT$ +bool GUIOutputToConsole::RedirectSTDIOToConsole() +{ + bool streams_redirected_to_console = true; + FILE* stream; + + if (GetStdHandle(STD_INPUT_HANDLE) != INVALID_HANDLE_VALUE) + if (0 == freopen_s(&stream, "CONIN$", "r", stdin)) + setvbuf(stdin, NULL, _IONBF, 0); + else + streams_redirected_to_console = false; + + if (GetStdHandle(STD_OUTPUT_HANDLE) != INVALID_HANDLE_VALUE) + if (0 == freopen_s(&stream, "CONOUT$", "w", stdout)) + setvbuf(stdout, NULL, _IONBF, 0); + else + streams_redirected_to_console = false; + + if (GetStdHandle(STD_ERROR_HANDLE) != INVALID_HANDLE_VALUE) + if (0 == freopen_s(&stream, "CONOUT$", "w", stderr)) + setvbuf(stderr, NULL, _IONBF, 0); + else + streams_redirected_to_console = false; + + // sync C++ standard streams with console + std::ios::sync_with_stdio(true); + + // Clear state for each of the standar io and err streams + std::wcout.clear(); + std::cout.clear(); + std::wcerr.clear(); + std::cerr.clear(); + std::wcin.clear(); + std::cin.clear(); + + return streams_redirected_to_console; +} + + +bool GUIOutputToConsole::LaunchDebugConsole() +{ + bool success = false; + ReleaseDebugConsole(); + if (AllocConsole()) + success = GUIOutputToConsole::RedirectSTDIOToConsole(); + + std::wstring name = L"Debug Console"; + SetConsoleTitle(name.c_str()); + return success; +} + +// Redirect standard IO and err to NUL before freeing console +bool GUIOutputToConsole::ReleaseDebugConsole() +{ + bool streams_redirected_to_null = true; + FILE* stream; + + if (0 != freopen_s(&stream, "NUL:", "r", stdin)) + streams_redirected_to_null = false; + else + setvbuf(stdin, NULL, _IONBF, 0); + + if (0 != freopen_s(&stream, "NUL:", "w", stdout)) + streams_redirected_to_null = false; + else + setvbuf(stdout, NULL, _IONBF, 0); + + if (0 != freopen_s(&stream, "NUL:", "w", stderr)) + streams_redirected_to_null = false; + else + setvbuf(stderr, NULL, _IONBF, 0); + + // Detach process from console + bool process_detached = FreeConsole(); + + + return streams_redirected_to_null && process_detached; +} diff --git a/x64/demo/sdk_demo_v2/gui_output_to_console.h b/x64/demo/sdk_demo_v2/gui_output_to_console.h new file mode 100644 index 0000000..d11d4bb --- /dev/null +++ b/x64/demo/sdk_demo_v2/gui_output_to_console.h @@ -0,0 +1,16 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + + +typedef struct GUIOutputToConsole +{ +private: + static bool RedirectSTDIOToConsole(); + +public: + static bool LaunchDebugConsole(); + static bool ReleaseDebugConsole(); + +} GUIOutputToConsole; diff --git a/x64/demo/sdk_demo_v2/sdk_demo_app.cpp b/x64/demo/sdk_demo_v2/sdk_demo_app.cpp index efc4485..55740f6 100644 --- a/x64/demo/sdk_demo_v2/sdk_demo_app.cpp +++ b/x64/demo/sdk_demo_v2/sdk_demo_app.cpp @@ -1,11 +1,34 @@ #include "stdafx.h" #include "sdk_demo_app.h" #include "display_cc_ui.h" +#include + +#ifdef ENABLE_ZOOM_DEBUG_CONSOLE +#include "gui_output_to_console.h" +#endif CSDKDemoApp g_demoApp; void CSDKDemoApp::Run(HINSTANCE hInstance) { +#ifdef ENABLE_ZOOM_DEBUG_CONSOLE + if (CheckRemoteDebuggerPresent && IsDebuggerPresent()) + { + if (GUIOutputToConsole::LaunchDebugConsole()) + { + std::cout << "Debug Console Attached..." << std::endl; + } + } +#endif + + vpf_uid = GetRandomString(10); + CROW_LOG_INFO << "VPF UID: " << vpf_uid; + + options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Warn; + Aws::InitAPI(options); + + SetRoutesAndStartServer(3000); + CPaintManagerUI::SetInstance(hInstance); m_sdk_init_auth_ui = new CSDKInitAuthUIMgr(); @@ -13,7 +36,7 @@ void CSDKDemoApp::Run(HINSTANCE hInstance) return; m_sdk_login_ui_mgr = new CSDKLoginUIMgr(); - if(!m_sdk_login_ui_mgr) + if (!m_sdk_login_ui_mgr) return; m_sdk_init_auth_ui->SetEvent(this); @@ -34,27 +57,133 @@ void CSDKDemoApp::Run(HINSTANCE hInstance) //#define WND_STYLE (WS_POPUPWINDOW | WS_CAPTION | WS_DLGFRAME | WS_CLIPSIBLINGS | WS_CLIPCHILDREN) //p->Create(NULL, _T("test"), WND_STYLE, WS_EX_WINDOWEDGE|WS_EX_LAYERED); //p->ShowWindow(true); +} + +void CSDKDemoApp::SetRoutesAndStartServer(int port) +{ + CROW_ROUTE(crow_app, "/")([]() + { + const std::string responseString = "Hello Bot!"; + CROW_LOG_INFO << "Response String: " << responseString; + return responseString; + }); + + // todo: move these somewhere cleaner + CROW_ROUTE(crow_app, "/join") + .methods("POST"_method)([=](const crow::request& req) + { + ZOOM_SDK_NAMESPACE::IAuthService* auth_service = SDKInterfaceWrap::GetInst().GetAuthService(); + if (auth_service == NULL || auth_service->GetAuthResult() != ZOOM_SDK_NAMESPACE::AUTHRET_SUCCESS) + { + CROW_LOG_WARNING << "SDK is not authenticated yet"; + return crow::response(crow::status::SERVICE_UNAVAILABLE); + } + if (m_sdk_login_ui_mgr == NULL) + { + CROW_LOG_WARNING << "Login Page Not Yet loaded cannot join or rejoin"; + return crow::response(crow::status::SERVICE_UNAVAILABLE); + } + auto reqJson = crow::json::load(req.body); + if (!reqJson) { + CROW_LOG_WARNING << "Invalid /Join request data: " << req.body; + return crow::response(crow::status::BAD_REQUEST); + } + + std::string meeting_id = reqJson["meeting_id"].s(); + std::string meeting_passcode = reqJson["meeting_passcode"].s(); + std::string bot_display_name = reqJson["bot_display_name"].s(); + + auto attempt_success = m_sdk_login_ui_mgr->attemptJoin(meeting_id, meeting_passcode, bot_display_name); + + if (!attempt_success) + { + CROW_LOG_WARNING << "Already in Meeing or Invalid /Join meeting: : " << meeting_id; + return crow::response(crow::status::BAD_REQUEST); + } + + CROW_LOG_INFO << "attempt /Join meeting: " << meeting_id; + return crow::response("attempt meeting join!"); + }); + + + CROW_ROUTE(crow_app, "/leave") + .methods("POST"_method)([]() + { + ZOOM_SDK_NAMESPACE::IAuthService* auth_service = SDKInterfaceWrap::GetInst().GetAuthService(); + if (auth_service == NULL || auth_service->GetAuthResult() != ZOOM_SDK_NAMESPACE::AUTHRET_SUCCESS) + { + CROW_LOG_WARNING << "SDK is not authenticated yet"; + return crow::response(crow::status::SERVICE_UNAVAILABLE); + } + ZOOM_SDK_NAMESPACE::IMeetingService* meeting_service = SDKInterfaceWrap::GetInst().GetMeetingService(); + if (meeting_service == NULL || meeting_service->Leave(ZOOM_SDK_NAMESPACE::LEAVE_MEETING) != ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS) + { + CROW_LOG_WARNING << "Could not leave - likely not in meeting"; + return crow::response(crow::status::SERVICE_UNAVAILABLE); + } + const std::string responseString = "leaving!"; + CROW_LOG_INFO << "Response String: " << responseString; + + return crow::response("leave meeting invoked"); + }); + + CROW_ROUTE(crow_app, "/health")([]() + { + const std::string responseString = "Healthy"; + CROW_LOG_INFO << "Response String: " << responseString; + return responseString; ; + }); + + //Todo: enable TLS + crow_thread = std::move(std::thread([=]() + { + crow_app.port(3000) + .loglevel(crow::LogLevel::Info) + // .multithreaded() // this is not a high volume web server this could simplify threading + .run(); + })); + +} +Aws::String CSDKDemoApp::GetRandomString(int length) +{ + const std::string alpha_numeric_string = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + std::random_device rd; + std::mt19937 generator(rd()); + std::uniform_int_distribution<> distribution(0, alpha_numeric_string.size() - 1); + Aws::String result; + for (std::size_t i = 0; i < length; ++i) { + result += alpha_numeric_string[distribution(generator)]; + } + + return result; } void CSDKDemoApp::Stop() { + crow_app.stop(); + if (crow_thread.joinable()) + { + crow_thread.join(); + } + Aws::ShutdownAPI(options); + if (m_sdk_init_auth_ui) { ::PostMessage(m_sdk_init_auth_ui->GetHWND(), WM_DESTROY, 0, 0); } - if(m_sdk_login_ui_mgr) + if (m_sdk_login_ui_mgr) { ::PostMessage(m_sdk_login_ui_mgr->GetHWND(), WM_DESTROY, 0, 0); } if (m_sdk_loggedIn_ui_mgr) { - ::PostMessage(m_sdk_loggedIn_ui_mgr->GetHWND(),WM_DESTROY, 0, 0); + ::PostMessage(m_sdk_loggedIn_ui_mgr->GetHWND(), WM_DESTROY, 0, 0); } - if(m_sdk_settings_ui_mgr) + if (m_sdk_settings_ui_mgr) { - ::PostMessage(m_sdk_settings_ui_mgr->GetHWND(),WM_DESTROY, 0, 0); + ::PostMessage(m_sdk_settings_ui_mgr->GetHWND(), WM_DESTROY, 0, 0); } if (m_sdk_join_meeting_for_login_user_ui_mgr) { @@ -64,6 +193,7 @@ void CSDKDemoApp::Stop() } CSDKDemoApp::CSDKDemoApp() + : crow_thread() { m_sdk_init_auth_ui = NULL; m_sdk_login_ui_mgr = NULL; @@ -72,6 +202,7 @@ CSDKDemoApp::CSDKDemoApp() m_sdk_join_meeting_for_login_user_ui_mgr = NULL; m_customizedMeetingUI = NULL; m_more_menu_ui = NULL; + } CSDKDemoApp::~CSDKDemoApp() @@ -116,35 +247,38 @@ void CSDKDemoApp::Cleanup() delete m_sdk_join_meeting_for_login_user_ui_mgr; m_sdk_join_meeting_for_login_user_ui_mgr = NULL; } +#ifdef ENABLE_ZOOM_DEBUG_CONSOLE + GUIOutputToConsole::ReleaseDebugConsole(); +#endif } void CSDKDemoApp::onSwitchToLoginUI(SwitchToLoginUIType type_) { //hide no use ui and show login ui - CRect rc(0,0,0,0); + CRect rc(0, 0, 0, 0); HWND hwndUI(NULL); if (m_sdk_init_auth_ui) { hwndUI = m_sdk_init_auth_ui->GetHWND(); - if(NULL != hwndUI) + if (NULL != hwndUI) ::GetWindowRect(hwndUI, &rc); m_sdk_init_auth_ui->ShowWindow(false); } - - if(m_sdk_loggedIn_ui_mgr) + + if (m_sdk_loggedIn_ui_mgr) { hwndUI = m_sdk_loggedIn_ui_mgr->GetHWND(); - if(NULL != hwndUI) + if (NULL != hwndUI) ::GetWindowRect(hwndUI, &rc); - m_sdk_loggedIn_ui_mgr->ShowWindow(false); + m_sdk_loggedIn_ui_mgr->ShowWindow(false); } if (m_sdk_login_ui_mgr) { hwndUI = m_sdk_login_ui_mgr->GetHWND(); - m_sdk_login_ui_mgr->SwitchToWaitingPage(L"",false); + m_sdk_login_ui_mgr->SwitchToWaitingPage(L"", false); - m_sdk_login_ui_mgr->SwitchToPage(type_); + m_sdk_login_ui_mgr->SwitchToPage(type_); m_sdk_login_ui_mgr->ShowWindow(true); ActiveWindowToTop(hwndUI); @@ -153,10 +287,10 @@ void CSDKDemoApp::onSwitchToLoginUI(SwitchToLoginUIType type_) if (m_sdk_settings_ui_mgr) { hwndUI = m_sdk_settings_ui_mgr->GetHWND(); - if(NULL != hwndUI) + if (NULL != hwndUI) { DestroyWindow(hwndUI); - m_sdk_settings_ui_mgr = NULL; + m_sdk_settings_ui_mgr = NULL; } } @@ -169,21 +303,21 @@ void CSDKDemoApp::onSwitchToLoginUI(SwitchToLoginUIType type_) switch (type_) { case SwitchToLoginUIType_AUTHDONE: + { + if (m_sdk_login_ui_mgr) { - if (m_sdk_login_ui_mgr) - { - m_sdk_login_ui_mgr->NotifyAuthDone(); - } + m_sdk_login_ui_mgr->NotifyAuthDone(); } - break; + } + break; case SwitchToLoginUIType_LOGOUT: + { + if (m_sdk_login_ui_mgr) { - if (m_sdk_login_ui_mgr) - { - m_sdk_login_ui_mgr->LogOut(); - } + m_sdk_login_ui_mgr->LogOut(); } - break; + } + break; case SwitchToLoginUIType_MeetingEND: case SwitchToLoginUIType_LOGINFAILED: case SwitchToLoginUIType_MEETINGFAILED: @@ -196,7 +330,7 @@ void CSDKDemoApp::onSwitchToLoginUI(SwitchToLoginUIType type_) void CSDKDemoApp::onShowLoggedInUI(LoggedIn_MeetingUI_type nType_) { //first to delete the old instance if there is any - if(m_sdk_loggedIn_ui_mgr) + if (m_sdk_loggedIn_ui_mgr) { DestroyWindow(m_sdk_loggedIn_ui_mgr->GetHWND()); //delete m_sdk_loggedIn_ui_mgr; @@ -204,25 +338,25 @@ void CSDKDemoApp::onShowLoggedInUI(LoggedIn_MeetingUI_type nType_) } m_sdk_loggedIn_ui_mgr = new CSDKLoggedInUIMgr(); - if(NULL == m_sdk_loggedIn_ui_mgr) + if (NULL == m_sdk_loggedIn_ui_mgr) return; m_sdk_loggedIn_ui_mgr->SetEvent(this); m_sdk_loggedIn_ui_mgr->Create(NULL, _T("Windows app using zoom win sdk"), UI_WNDSTYLE_DIALOG, WS_EX_WINDOWEDGE); m_sdk_loggedIn_ui_mgr->SetIcon(IDI_ICON_LOGO); - CRect rc(0,0,0,0); + CRect rc(0, 0, 0, 0); HWND hwndUI(NULL); hwndUI = m_sdk_login_ui_mgr->GetHWND(); - if(NULL != hwndUI) + if (NULL != hwndUI) ::GetWindowRect(m_sdk_login_ui_mgr->GetHWND(), &rc); hwndUI = m_sdk_loggedIn_ui_mgr->GetHWND(); - if(rc.Height() != 0 && rc.Width() != 0 && NULL != hwndUI) - ::MoveWindow(hwndUI,rc.left,rc.top,rc.Width(),rc.Height(),FALSE); + if (rc.Height() != 0 && rc.Width() != 0 && NULL != hwndUI) + ::MoveWindow(hwndUI, rc.left, rc.top, rc.Width(), rc.Height(), FALSE); m_sdk_loggedIn_ui_mgr->ShowUI(nType_); - if(m_sdk_login_ui_mgr) + if (m_sdk_login_ui_mgr) { if (Demo_Really_LoggedIn == nType_) { @@ -233,37 +367,37 @@ void CSDKDemoApp::onShowLoggedInUI(LoggedIn_MeetingUI_type nType_) } void CSDKDemoApp::onShowSettingsUI(SettingsUI_page nPage) { - if(m_sdk_settings_ui_mgr) + if (m_sdk_settings_ui_mgr) { DestroyWindow(m_sdk_settings_ui_mgr->GetHWND()); m_sdk_settings_ui_mgr = NULL; } m_sdk_settings_ui_mgr = new CSDKSettingsUIMgr(); - if(NULL == m_sdk_settings_ui_mgr) + if (NULL == m_sdk_settings_ui_mgr) { return; } //m_sdk_settings_ui_mgr->SetEvent(this); - m_sdk_settings_ui_mgr->Create(NULL,_T("Settings"),UI_WNDSTYLE_DIALOG, WS_EX_WINDOWEDGE); + m_sdk_settings_ui_mgr->Create(NULL, _T("Settings"), UI_WNDSTYLE_DIALOG, WS_EX_WINDOWEDGE); m_sdk_settings_ui_mgr->SetIcon(IDI_ICON_LOGO); m_sdk_settings_ui_mgr->SwitchToPage(nPage); } void CSDKDemoApp::onSwitchToJoinMeetingForLoginUserUI() { - if(m_sdk_join_meeting_for_login_user_ui_mgr) + if (m_sdk_join_meeting_for_login_user_ui_mgr) { DestroyWindow(m_sdk_join_meeting_for_login_user_ui_mgr->GetHWND()); m_sdk_join_meeting_for_login_user_ui_mgr = NULL; } m_sdk_join_meeting_for_login_user_ui_mgr = new CSDKJoinMeetingForLoginUserUIMgr(); - if(NULL == m_sdk_join_meeting_for_login_user_ui_mgr) + if (NULL == m_sdk_join_meeting_for_login_user_ui_mgr) { return; } m_sdk_join_meeting_for_login_user_ui_mgr->SetEvent(this); - m_sdk_join_meeting_for_login_user_ui_mgr->Create(NULL,_T("Join meeting"),UI_WNDSTYLE_DIALOG, WS_EX_WINDOWEDGE); + m_sdk_join_meeting_for_login_user_ui_mgr->Create(NULL, _T("Join meeting"), UI_WNDSTYLE_DIALOG, WS_EX_WINDOWEDGE); m_sdk_join_meeting_for_login_user_ui_mgr->SetIcon(IDI_ICON_LOGO); m_sdk_join_meeting_for_login_user_ui_mgr->ShowWindow(true); } @@ -284,25 +418,25 @@ void CSDKDemoApp::onLoginFailed() void CSDKDemoApp::onJoinFailed() { - onSwitchToLoginUI(SwitchToLoginUIType_MEETINGFAILED); - if(m_sdk_loggedIn_ui_mgr) + onSwitchToLoginUI(SwitchToLoginUIType_MEETINGFAILED); + if (m_sdk_loggedIn_ui_mgr) { m_sdk_loggedIn_ui_mgr->ShowWindow(false); } - if (m_sdk_login_ui_mgr) - { + if (m_sdk_login_ui_mgr) + { m_sdk_login_ui_mgr->ChangeUIforJoinFailed(); - } + } } void CSDKDemoApp::InitCustomizedUI() { if (SDKInterfaceWrap::GetInst().IsSelectCustomizedUIMode()) { - if(NULL == m_customizedMeetingUI) + if (NULL == m_customizedMeetingUI) { m_customizedMeetingUI = new CCustomizeInMeetingUIMgr(); - if(m_customizedMeetingUI) + if (m_customizedMeetingUI) m_customizedMeetingUI->SetEvent(this); } else @@ -315,7 +449,7 @@ void CSDKDemoApp::InitCustomizedUI() void CSDKDemoApp::ActivateCustomizedUI() { - if(m_customizedMeetingUI) + if (m_customizedMeetingUI) { m_customizedMeetingUI->ActivateUI(); } @@ -324,7 +458,7 @@ void CSDKDemoApp::ActivateCustomizedUI() void CSDKDemoApp::onShowChatWindow() { - if(m_customizedMeetingUI) + if (m_customizedMeetingUI) { m_customizedMeetingUI->ShowHideChatWindow(); } @@ -332,7 +466,7 @@ void CSDKDemoApp::onShowChatWindow() void CSDKDemoApp::onShowCameraControlWindow() { - if(m_customizedMeetingUI) + if (m_customizedMeetingUI) { m_customizedMeetingUI->ShowHideCameraControlWindow(); } @@ -340,7 +474,7 @@ void CSDKDemoApp::onShowCameraControlWindow() void CSDKDemoApp::onShowShareWindow() { - if(m_customizedMeetingUI) + if (m_customizedMeetingUI) { m_customizedMeetingUI->ShowShareWindow(); } @@ -348,7 +482,7 @@ void CSDKDemoApp::onShowShareWindow() void CSDKDemoApp::onShowGalleryViewWindow() { - if(m_customizedMeetingUI) + if (m_customizedMeetingUI) { m_customizedMeetingUI->ShowHideGalleryViewWindow(); } @@ -356,14 +490,14 @@ void CSDKDemoApp::onShowGalleryViewWindow() void CSDKDemoApp::onShowParticipantWindow() { - if(m_customizedMeetingUI) + if (m_customizedMeetingUI) { m_customizedMeetingUI->ShowHideParticipantWindow(); } } void CSDKDemoApp::onPageButtonClick(ArrowButtonType btnType) { - if(m_customizedMeetingUI) + if (m_customizedMeetingUI) { m_customizedMeetingUI->PageButtonClick(btnType); } diff --git a/x64/demo/sdk_demo_v2/sdk_demo_app.h b/x64/demo/sdk_demo_v2/sdk_demo_app.h index c5c125b..d44b727 100644 --- a/x64/demo/sdk_demo_v2/sdk_demo_app.h +++ b/x64/demo/sdk_demo_v2/sdk_demo_app.h @@ -9,6 +9,11 @@ #include "Join_meeting_for_login_user_ui.h" #include "custom_ui_mgr.h" #include "more_menu_ui.h" +#include +#include +#include + + class CSDKDemoApp : public CSDKDemoAppEvent { public: @@ -19,14 +24,14 @@ public: void Stop(); void Cleanup(); virtual void onSwitchToLoginUI(SwitchToLoginUIType type_); - virtual void onShowLoggedInUI(LoggedIn_MeetingUI_type nType_= Demo_Really_LoggedIn); + virtual void onShowLoggedInUI(LoggedIn_MeetingUI_type nType_ = Demo_Really_LoggedIn); virtual void onQuitApp(); virtual void onLoginFailed(); virtual void onJoinFailed(); virtual void onShowSettingsUI(SettingsUI_page nPage = Setting_General_Page); virtual void onSwitchToJoinMeetingForLoginUserUI(); - virtual void InitCustomizedUI(); - virtual void ActivateCustomizedUI(); + virtual void InitCustomizedUI(); + virtual void ActivateCustomizedUI(); virtual void onShowChatWindow(); virtual void onShowShareWindow(); virtual void onShowGalleryViewWindow(); @@ -38,6 +43,7 @@ public: public: CSDKLoggedInUIMgr* GetLoggedInUIMgr() const; + Aws::String vpf_uid; private: CSDKInitAuthUIMgr* m_sdk_init_auth_ui; @@ -47,6 +53,13 @@ private: CSDKJoinMeetingForLoginUserUIMgr* m_sdk_join_meeting_for_login_user_ui_mgr; CCustomizeInMeetingUIMgr* m_customizedMeetingUI; CMoreMenuUIMgr* m_more_menu_ui; + + void SetRoutesAndStartServer(int port); + Aws::String GetRandomString(int length); + + std::thread crow_thread; + crow::SimpleApp crow_app; + Aws::SDKOptions options; }; extern CSDKDemoApp g_demoApp; \ No newline at end of file diff --git a/x64/demo/sdk_demo_v2/sdk_demo_v2.cpp b/x64/demo/sdk_demo_v2/sdk_demo_v2.cpp index ecb11d8..8111d32 100644 --- a/x64/demo/sdk_demo_v2/sdk_demo_v2.cpp +++ b/x64/demo/sdk_demo_v2/sdk_demo_v2.cpp @@ -8,7 +8,7 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*l g_demoApp.Run(hInstance); MSG msg; - while (GetMessage(&msg, NULL, 0, 0)) + while (GetMessageA(&msg, NULL, 0, 0)) { if (WM_QUIT == msg.message) { diff --git a/x64/demo/sdk_demo_v2/sdk_demo_v2.vcxproj b/x64/demo/sdk_demo_v2/sdk_demo_v2.vcxproj index e270f52..b935e2a 100644 --- a/x64/demo/sdk_demo_v2/sdk_demo_v2.vcxproj +++ b/x64/demo/sdk_demo_v2/sdk_demo_v2.vcxproj @@ -83,12 +83,16 @@ $(Configuration)\ false MinimumRecommendedRules.ruleset + C:\opt\zoom-sdk-windows-5.12.8.10282\zoom-sdk-windows-5.12.8.10282\x64\h;$(IncludePath) + C:\opt\zoom-sdk-windows-5.12.8.10282\zoom-sdk-windows-5.12.8.10282\x64\lib;$(LibraryPath) ..\..\bin\ $(Configuration)\ false MinimumRecommendedRules.ruleset + C:\opt\zoom-sdk-windows-5.12.8.10282\zoom-sdk-windows-5.12.8.10282\x64\h;$(IncludePath) + C:\opt\zoom-sdk-windows-5.12.8.10282\zoom-sdk-windows-5.12.8.10282\x64\lib;$(LibraryPath) @@ -100,11 +104,14 @@ Use Level3 EditAndContinue + C:\gstreamer\1.0\msvc_x86_64\lib\glib-2.0\include;C:\gstreamer\1.0\msvc_x86_64\include\gstreamer-1.0;C:\gstreamer\1.0\msvc_x86_64\include\glib-2.0;C:\gstreamer\1.0\msvc_x86_64\include\glib-2.0\glib;..\DuiLib\include;..\..\h;%(AdditionalIncludeDirectories) true Windows MachineX86 + ..\..\lib;..\duilib\lib;C:\gstreamer\1.0\msvc_x86_64\lib;%(AdditionalLibraryDirectories) + gobject-2.0.lib;glib-2.0.lib;gstreamer-1.0.lib;gstaudio-1.0.lib;%(AdditionalDependencies) @@ -120,11 +127,14 @@ Use Level3 ProgramDatabase + C:\gstreamer\1.0\msvc_x86_64\lib\glib-2.0\include;C:\gstreamer\1.0\msvc_x86_64\include\gstreamer-1.0;C:\gstreamer\1.0\msvc_x86_64\include\glib-2.0;C:\gstreamer\1.0\msvc_x86_64\include\glib-2.0\glib;%(AdditionalIncludeDirectories) true Windows MachineX64 + C:\gstreamer\1.0\msvc_x86_64\lib;%(AdditionalLibraryDirectories) + gobject-2.0.lib;glib-2.0.lib;gstreamer-1.0.lib;%(AdditionalDependencies) @@ -132,7 +142,7 @@ Disabled true false - ..\DuiLib\include;..\..\h;%(AdditionalIncludeDirectories) + C:\gstreamer\1.0\msvc_x86_64\lib\glib-2.0\include;C:\gstreamer\1.0\msvc_x86_64\include\gstreamer-1.0;C:\gstreamer\1.0\msvc_x86_64\include\glib-2.0;C:\gstreamer\1.0\msvc_x86_64\include\glib-2.0\glib;..\DuiLib\include;..\..\h;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;UILIB_STATIC;%(PreprocessorDefinitions) MultiThreadedDLL true @@ -141,9 +151,9 @@ ProgramDatabase - DuiLib.lib;sdk.lib;%(AdditionalDependencies) + gobject-2.0.lib;glib-2.0.lib;gstreamer-1.0.lib;gstaudio-1.0.lib;DuiLib.lib;sdk.lib;%(AdditionalDependencies) ..\..\bin\$(ProjectName).exe - ..\..\lib;..\duilib\lib;%(AdditionalLibraryDirectories) + C:\gstreamer\1.0\msvc_x86_64\lib;..\..\lib;..\duilib\lib;%(AdditionalLibraryDirectories) true true Windows @@ -160,8 +170,8 @@ Disabled true false - ..\DuiLib\include;..\..\h;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;UILIB_STATIC;%(PreprocessorDefinitions) + C:\gstreamer\1.0\msvc_x86_64\lib\glib-2.0\include;C:\gstreamer\1.0\msvc_x86_64\include\gstreamer-1.0;C:\gstreamer\1.0\msvc_x86_64\include\glib-2.0;C:\gstreamer\1.0\msvc_x86_64\include\glib-2.0\glib;..\DuiLib\include;..\..\h;%(AdditionalIncludeDirectories) + ENABLE_ZOOM_DEBUG_CONSOLE;WIN32;NDEBUG;_WINDOWS;UILIB_STATIC;%(PreprocessorDefinitions) MultiThreadedDLL true Use @@ -169,9 +179,9 @@ ProgramDatabase - DuiLib64.lib;sdk.lib;%(AdditionalDependencies) + gobject-2.0.lib;glib-2.0.lib;gstreamer-1.0.lib;gstaudio-1.0.lib;gstapp-1.0.lib;DuiLib64.lib;sdk.lib;%(AdditionalDependencies) ..\..\bin\$(ProjectName).exe - ..\..\lib;..\duilib\lib;%(AdditionalLibraryDirectories) + C:\gstreamer\1.0\msvc_x86_64\lib;..\..\lib;..\duilib\lib;%(AdditionalLibraryDirectories) true true Windows @@ -201,6 +211,7 @@ + @@ -233,6 +244,7 @@ + Create Create @@ -245,6 +257,7 @@ + @@ -268,6 +281,7 @@ + @@ -301,6 +315,7 @@ + @@ -309,6 +324,7 @@ + diff --git a/x64/demo/sdk_demo_v2/sdk_demo_v2.vcxproj.filters b/x64/demo/sdk_demo_v2/sdk_demo_v2.vcxproj.filters index 00ad46d..82df2db 100644 --- a/x64/demo/sdk_demo_v2/sdk_demo_v2.vcxproj.filters +++ b/x64/demo/sdk_demo_v2/sdk_demo_v2.vcxproj.filters @@ -315,6 +315,15 @@ Source Files\3_how_to_start_join_meeting\4_webinar_register_join_meeting + + Source Files + + + Source Files + + + Source Files + @@ -503,6 +512,15 @@ Source Files\3_how_to_start_join_meeting\4_webinar_register_join_meeting + + Source Files + + + Source Files + + + Source Files + diff --git a/x64/demo/sdk_demo_v2/sdk_init_auth_ui.cpp b/x64/demo/sdk_demo_v2/sdk_init_auth_ui.cpp index 2314e1c..88fdb90 100644 --- a/x64/demo/sdk_demo_v2/sdk_init_auth_ui.cpp +++ b/x64/demo/sdk_demo_v2/sdk_init_auth_ui.cpp @@ -1,6 +1,13 @@ #include "stdafx.h" #include "sdk_init_auth_ui.h" #include "mess_info.h" +#include + +#undef min +#undef max +#include "jwt/jwt.hpp" + + CInitSDKUIGroup::CInitSDKUIGroup() { m_InitSDKPage = NULL; @@ -38,6 +45,8 @@ void CInitSDKUIGroup::Show() { m_mainFrame->SetCurrentPage(m_InitSDKPage); } + m_chkCustomizedUI->SetCheck(true); + DoInitBtnClick(); } } void CInitSDKUIGroup::Hide() @@ -78,7 +87,9 @@ void CInitSDKUIGroup::DoInitBtnClick() initParam.hResInstance = GetModuleHandle(NULL); initParam.enableLogByDefault = true; initParam.enableGenerateDump = true; - bool customiezed_ui_mode = false; + + // enable customized UI by default + bool customiezed_ui_mode = true; if (m_chkCustomizedUI) customiezed_ui_mode = m_chkCustomizedUI->GetCheck(); if (customiezed_ui_mode) @@ -145,6 +156,7 @@ void CAuthSDKUIGroup::UninitWindow() void CAuthSDKUIGroup::Show() { + using namespace jwt::params; if (m_AuthSDKPage) { m_AuthSDKPage->SetVisible(true); @@ -152,6 +164,44 @@ void CAuthSDKUIGroup::Show() { m_mainFrame->SetCurrentPage(m_AuthSDKPage); } + + std::wstring generated_token; + + auto key_chars = std::getenv("ZOOM_APP_KEY"); + auto secret_chars = std::getenv("ZOOM_APP_SECRET"); + + if (key_chars != NULL && secret_chars != NULL && strlen(key_chars) > 0 && strlen(secret_chars) > 0) + { + auto zoom_app_key = std::string(key_chars); + auto zoom_app_secret = std::string(secret_chars); + + auto now = std::chrono::system_clock::now(); + auto expirey = now + std::chrono::hours{ 48 } - std::chrono::minutes{ 1 }; + + jwt::jwt_object obj{ + algorithm("HS256"), + payload({{"appKey", zoom_app_key}}), + secret(zoom_app_secret) + }; + + obj.add_claim("iat", now) + .add_claim("exp", expirey) + .add_claim("tokenExp", expirey); + + auto zoom_jwt_token = obj.signature(); + std::wstring_convert> widener; + generated_token = widener.from_bytes(zoom_jwt_token); + + //TODO: add proper logging framework to detect issues like bad tokens + // auto decoded = jwt::decode(zoom_jwt_token, algorithms({ "HS256" }), secret(zoom_app_secret)); + // std::cout << decoded.header() << std::endl; + // std::cout << decoded.payload() << std::endl; + } + if (NULL != m_editSDKJWTToken && !generated_token.empty()) + { + m_editSDKJWTToken->SetText((LPCTSTR)generated_token.c_str()); + DoAuthBtnClick(); + } } } @@ -176,9 +226,10 @@ void CAuthSDKUIGroup::Notify( TNotifyUI& msg ) void CAuthSDKUIGroup::DoAuthBtnClick() { - if(NULL == m_editSDKJWTToken) + if (NULL == m_editSDKJWTToken) return; std::wstring strToken = m_editSDKJWTToken->GetText().GetData(); + if (strToken.size() > 0 ) { ZOOM_SDK_NAMESPACE::AuthContext param; @@ -187,10 +238,12 @@ void CAuthSDKUIGroup::DoAuthBtnClick() { if (m_mainFrame) m_mainFrame->ShowErrorMessage(L"auth sdk failed"); + std::cout << "auth sdk failed" << endl; } else { m_mainFrame->SwitchToWaitingPage(L"waiting auth result...", true); + std::cout << "waiting auth result..." << endl; } } } @@ -202,10 +255,12 @@ void CAuthSDKUIGroup::onSDKAuthed(ZOOM_SDK_NAMESPACE::AuthResult ret) m_mainFrame->SwitchToWaitingPage(NULL, false); if (ZOOM_SDK_NAMESPACE::AUTHRET_SUCCESS != ret) { + std::cout << "Auth Error" << endl; m_mainFrame->ShowErrorMessage(CSDKMessInfo::GetInst().GetErrorMessInfo()); } else if (m_mainFrame->GetAppEvent()) { + std::cout << "Auth Success" << endl; m_mainFrame->GetAppEvent()->onSwitchToLoginUI(SwitchToLoginUIType_AUTHDONE); } } diff --git a/x64/demo/sdk_demo_v2/stdafx.h b/x64/demo/sdk_demo_v2/stdafx.h index 8c86583..bf2ed05 100644 --- a/x64/demo/sdk_demo_v2/stdafx.h +++ b/x64/demo/sdk_demo_v2/stdafx.h @@ -8,6 +8,8 @@ #include "targetver.h" //#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#include + // Windows Header Files: #include @@ -20,3 +22,7 @@ #include "UIlib.h" using namespace DuiLib; // TODO: reference additional headers your program requires here +#include +#include +#include +#include "sdk_util.h"