# Integrate the Meeting SDK into your Unreal project When integrating with your Unreal project, you can integrate with either C++ or with Blueprints. ## Integrate with C++ Currently, the Zoom plugin wraps many of the Meeting SDK interfaces and callbacks. However, not all the interfaces have been wrapped, and not all of the functions within the wrapped interfaces have been implemented. Learn how to wrap a new interface and its corresponding callback and override unimplemented functions of an interface that is already wrapped. ### Create a new interface wrapper Wrap this new interface within the plugin defined in the Meeting SDK for Windows' `meeting_inmeeting_encryption_interface.h` file. ```cpp class IMeetingEncryptionController { public: virtual ~IMeetingEncryptionController() {} virtual void SetEvent(IMeetingEncryptionControllerEvent *pEvent) = 0; virtual EncryptionType GetEncryptionType() = 0; virtual const zchar_t *GetE2EEMeetingSecurityCode() = 0; virtual unsigned int GetE2EEMeetingSecurityCodePassedSeconds() = 0; virtual bool IsUnencryptedExceptionDataValid() = 0; virtual unsigned int GetUnencryptedExceptionCount() = 0; virtual const zchar_t *GetUnencryptedExceptionInfo() = 0; }; ``` 1. To wrap the previous interface, open your Unreal Engine project and launch Visual Studio. 1. Create a new class in Unreal Engine by selecting **Tools** > **New C++ Class…**. 1. In the **Choose Parent Class** screen of the **Add C++ Class** dialog, choose the **Common Classes** tab as your default parent class and select **None**. 1. Name your class `MeetingEncryptionController` and set the plugin runtime. 1. Finally, select **Create class**. 1. Reload Visual Studio to add the new header and source files to the project. Note: The plugin's sources are in `\Plugins\ZoomMeetingSDK\Source\ZoomMeetingSDK`. Header files are in `\Plugins\ZoomMeetingSDK\Source\ZoomMeetingSDK\Public`. Source files are in `\Plugins\ZoomMeetingSDK\Source\ZoomMeetingSDK\Private`. 1. Update the default header and source files. All the wrapped functions are in the `ZOOM_SDK_NAMESPACE::UE` namespace, defined in the header `ZoomUESDKDef.h`. - Add all the methods defined in the interface that may be overridden. - Add a `Create` method to create an instance of the wrapper class. This is optional and can be replaced with a different method if needed. - Add a pointer to the interface `IMeetingEncryptionController` which will point to the instance created by the Meeting SDK after the SDK is initialized. Use this to redirect calls to the default behavior. 1. Update the header file `MeetingEncryptionController.h`. ```cpp // Copyright : All rights reserved by Zoom Video Communications 2022- #pragma once #include "ZoomUESDKDef.h" #include "meeting_service_components/meeting_inmeeting_encryption_interface.h" BEGIN_ZOOM_SDK_UE_NAMESPACE /** * Wrapper around IMeetingEncryptionController. */ class ZOOMMEETINGSDK_API MeetingEncryptionController { public: ~MeetingEncryptionController() = delete; MeetingEncryptionController(const MeetingEncryptionController &other) = delete; MeetingEncryptionController(MeetingEncryptionController &&other) noexcept = delete; MeetingEncryptionController & operator=(const MeetingEncryptionController &other) = delete; MeetingEncryptionController & operator=(MeetingEncryptionController &&other) noexcept = delete; // Static function to create an object of IMeetingEncryptionController. static MeetingEncryptionController *Create(); // Methods to override from the default behavior. EncryptionType GetEncryptionType(); const zchar_t *GetE2EEMeetingSecurityCode(); unsigned int GetE2EEMeetingSecurityCodePassedSeconds(); bool IsUnencryptedExceptionDataValid(); unsigned int GetUnencryptedExceptionCount(); const zchar_t *GetUnencryptedExceptionInfo(); private: MeetingEncryptionController() = default; // Initializes MeetingEncryptionController. // // Returns true if initialization was successful. bool Init(); // Pointer to the encryption controller. IMeetingEncryptionController *m_meeting_encryption_ctrl = nullptr; }; END_ZOOM_SDK_UE_NAMESPACE ``` 1. Update the source file `MeetingEncryptionController.cpp`. ```cpp // Copyright : All rights reserved by Zoom Video Communications 2022- #include "MeetingEncryptionController.h" #include "SDKInterfaceWrap.h" BEGIN_ZOOM_SDK_UE_NAMESPACE MeetingEncryptionController *MeetingEncryptionController::Create() { TUniquePtr mec( new MeetingEncryptionController()); if (mec->Init()) { return mec.Release(); } return nullptr; } bool MeetingEncryptionController::Init() { m_meeting_encryption_ctrl = SDKInterfaceWrap::GetInst().GetMeetingEncryptionController(); return (m_meeting_encryption_ctrl) ? true : false; } // Override the required methods. EncryptionType MeetingEncryptionController::GetEncryptionType() { EncryptionType type(EncryptionType::EncryptionType_None); // Add your code that overrides the default behavior. return type; } const zchar_t *MeetingEncryptionController::GetE2EEMeetingSecurityCode() { // Add your code that overrides the default behavior. return nullptr; } unsigned int MeetingEncryptionController::GetE2EEMeetingSecurityCodePassedSeconds() { // Add your code that overrides the default behavior. return 0; } // Methods with default behavior. bool MeetingEncryptionController::IsUnencryptedExceptionDataValid() { return m_meeting_encryption_ctrl->IsUnencryptedExceptionDataValid(); } unsigned int MeetingEncryptionController::GetUnencryptedExceptionCount() { return m_meeting_encryption_ctrl->GetUnencryptedExceptionCount(); } const zchar_t *MeetingEncryptionController::GetUnencryptedExceptionInfo() { return m_meeting_encryption_ctrl->GetUnencryptedExceptionInfo(); } END_ZOOM_SDK_UE_NAMESPACE ``` 1. Get the Meeting SDK's instance of `IMeetingEncryptionController`, and then add the function declaration `GetMeetingEncryptionController()` in `SDKInterfaceWrap.h` and add the implementation in `SDKInterfaceWrap.cpp`. ```cpp #include "MeetingEncryptionController.h" BEGIN_ZOOM_SDK_UE_NAMESPACE class ZOOMMEETINGSDK_API SDKInterfaceWrap { public: ... // Returns a pointer to the IMeetingEncryptionController. // // Note: SDK needs to be initialized before calling this function. ZOOM_SDK_NAMESPACE::IMeetingEncryptionController * GetMeetingEncryptionController(); ... }; END_ZOOM_SDK_UE_NAMESPACE ``` When creating an instance of the interface, set the callback object if needed. The next code sample has more information about creating the callback in `SDKInterfaceWrap.cpp`. ```cpp #include "MeetingEncryptionControllerEvent.h" BEGIN_ZOOM_SDK_UE_NAMESPACE ... IMeetingEncryptionController * SDKInterfaceWrap::GetMeetingEncryptionController() { if (!isInitialized()) { return nullptr; } IMeetingEncryptionController *mec = GetMeetingService()->GetInMeetingEncryptionController(); if (mec) { // Set the callback event if need be. mec->SetEvent(MeetingEncryptionControllerEvent::Create()); } return mec; } ... END_ZOOM_SDK_UE_NAMESPACE ``` ### Create the callback To create the callback `IMeetingEncryptionControllerEvent`, create a new class in Unreal Engine `MeetingEncryptionControllerEvent.h` or 'MeetingEncryptionControllerEvent.cpp' like we did for the interface. Then update the default header and source. Note: - For callbacks, inherit the callback interface and have an implementation for all the virtual methods defined within that callback interface. - The callback functions can either trigger a Blueprint callback event as shown in the next example, or can be left empty so it is ignored. Trigger a Blueprint callback event in the `MeetingEncryptionControllerEvent.h` file. ```cpp // Copyright : All rights reserved by Zoom Video Communications 2022- #pragma once #include "ZoomUESDKDef.h" #include "meeting_service_components/meeting_inmeeting_encryption_interface.h" BEGIN_ZOOM_SDK_UE_NAMESPACE /** * Wrapper that inherits IMeetingEncryptionControllerEvent. */ class ZOOMMEETINGSDK_API MeetingEncryptionControllerEvent : public IMeetingEncryptionControllerEvent { public: ~MeetingEncryptionControllerEvent() = default; MeetingEncryptionControllerEvent( const MeetingEncryptionControllerEvent &other) = delete; MeetingEncryptionControllerEvent( MeetingEncryptionControllerEvent &&other) noexcept = delete; MeetingEncryptionControllerEvent & operator=(const MeetingEncryptionControllerEvent &other) = delete; MeetingEncryptionControllerEvent & operator=(MeetingEncryptionControllerEvent &&other) noexcept = delete; // Static function to create an object of IMeetingEncryptionControllerEvent. static IMeetingEncryptionControllerEvent *Create(); // Functions to override. void onE2EEMeetingSecurityCodeChanged() override; private: MeetingEncryptionControllerEvent() = default; }; END_ZOOM_SDK_UE_NAMESPACE ``` Implement the callback in the `MeetingEncryptionControllerEvent.cpp` file. ```cpp // Copyright : All rights reserved by Zoom Video Communications 2022- #include "MeetingEncryptionControllerEvent.h" #include "ZoomMeetingSDKEventHandler.h" BEGIN_ZOOM_SDK_UE_NAMESPACE IMeetingEncryptionControllerEvent *MeetingEncryptionControllerEvent::Create() { // Create a static MeetingEncryptionControllerEvent. // We would ideally only need to create a single callback object. // If need be, remove the singleton and use new to create a new object. static MeetingEncryptionControllerEvent s_mrce; return &s_mrce; } void MeetingEncryptionControllerEvent::onE2EEMeetingSecurityCodeChanged() { } END_ZOOM_SDK_UE_NAMESPACE ``` ## Integrate with Blueprints Integrate with Blueprints in a graphical user interface, or extend Blueprints' functionality using code. ### Use Blueprints A significant amount of Zoom functionality is exposed to the Blueprint layer for implementing behavior in Unreal's node-based editor. Participants are identified to many of the Blueprint functions using a unique integer ID code. Using the Blueprint function `GetUserByID` lets you get a Blueprint struct `FUEUserInfo` containing helpful information like the participant's name by using the ID. Use `GetParticipantsList` to get a list of `FUEUserInfo` structs representing everyone in the Zoom call. Not all Blueprints are direct wrappers of Meeting SDK functions. Some special nodes, such as SDK Init and Auth, are helper functions that combine multiple SDK components with additional logic to simplify the Blueprint building experience. In addition, the return type of many Blueprints in this toolkit is a boolean, while the C++ functions return an `SDKError` object. Working with booleans by folding down all of the errors makes it easier to do logic control at the Blueprint level. The underlying SDKError is printed to the logs. Finally there are some functions that use a different input type where the Blueprint implementation performs additional functions beyond a direct Meeting SDK call. In the case where a Blueprint function in the Zoom Meeting SDK for Unreal Engine is not a direct wrapper of an underlying Meeting SDK function, additional documentation is available describing the changes. See the [SDK Reference](/docs/meeting-sdk/unreal/reference/reference) section for more details. ### Extend Blueprints Use Blueprints to add your own functionality. This section shows two examples: creating a Blueprint C++ function, and creating a callback. #### Create a Blueprint C++ function Blueprint functions provide a way for Blueprint nodes from the Unreal Editor to interact with the Meeting SDK. Let's implement the Blueprint functions to interact with `MeetingEncryptionController`. The header file `ZoomMeetingSDKBPLibrary.h` has all the definitions for the Blueprint functions and the source file `ZoomMeetingSDKBPLibrary.cpp` has the implementation. To implement the function to get the meeting encryption type `GetEncryptionType`, add a new `UFUNCTION` within the header file. The return type of the function is an enum defined within `meeting_inmeeting_encryption_interface.h`. Since we can't return this enum as is, define a `BlueprintType enum` that can be used by the Blueprint nodes. They are currently defined in the header file `ZoomMeetingSDKBPDefine.h`, but you can also create your own header to define them. Create a new enum of `BlueprintType`. ```cpp // Enum to Meeting Service Encryption Interface Valid for both ZOOM style and // user custom interface mode. UENUM(BlueprintType) enum class MeetingEncryptionType : uint8 { MeetingEncryptionType_None UMETA(DisplayName = "None"), MeetingEncryptionType_Enhanced UMETA(DisplayName = "Enhanced"), MeetingEncryptionType_E2EE UMETA(DisplayName = "E2EE"), }; ``` Define the Blueprint function in the `ZoomMeetingSDKBPLibrary.h` file. ```cpp // Get meeting encryption type. // // Return encryption type. UFUNCTION( BlueprintCallable, meta = (DisplayName = "GetEncryptionType", Keywords = "Get Encryption type"), Category = "Zoom Meeting SDK Plug-in | Utils | IMeetingEncryptionController") static MeetingEncryptionType GetEncryptionType(); ``` Implement the function in the `ZoomMeetingSDKBPLibrary.cpp` file. ```cpp MeetingEncryptionType UZoomMeetingSDKBPLibrary::GetEncryptionType() { MeetingEncryptionType encyption_type( MeetingEncryptionType::MeetingEncryptionType_None); UE_LOG(BPZoomMeetingSDKLog, Display, TEXT("UZoomMeetingSDKBPLibrary::GetEncryptionType triggered")); TUniquePtr mec( MeetingEncryptionController::Create()); if (!mec.Get()) { UE_LOG(BPZoomMeetingSDKLog, Display, TEXT("UZoomMeetingSDKBPLibrary::GetEncryptionType SDK not initialized.")); return encyption_type; } // Convert the encryption type to a returnable value. switch (mec->GetEncryptionType()) { case ZOOM_SDK_NAMESPACE::EncryptionType::EncryptionType_None: encyption_type = MeetingEncryptionType::MeetingEncryptionType_None; break; case ZOOM_SDK_NAMESPACE::EncryptionType::EncryptionType_Enhanced: encyption_type = MeetingEncryptionType::MeetingEncryptionType_Enhanced; break; case ZOOM_SDK_NAMESPACE::EncryptionType::EncryptionType_E2EE: encyption_type = MeetingEncryptionType::MeetingEncryptionType_E2EE; break; } return encyption_type; } ``` #### Create a callback function to Blueprint All the callback functions' definitions and implementations are in the `ZoomMeetingSDKEventHandler.h` and `ZoomMeetingSDKEventHandler.cpp` files. The `IMeetingEncryptionControllerEvent` has a callback function `onE2EEMeetingSecurityCodeChanged`. To receive this callback, update the `ZoomMeetingSDKEventHandler.h` file. ```cpp ... DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnE2EEMeetingSecurityCodeChanged); ... UPROPERTY( BlueprintAssignable, meta = (DisplayName = "OnE2EEMeetingSecurityCodeChanged", Keywords = "IMeetingEncryptionControllerEvent.OnE2EEMeetingSecurityCodeChanged"), Category = Zoom Meeting SDK Plug-in | Events | IMeetingEncryptionControllerEvent") FOnE2EEMeetingSecurityCodeChanged OnE2EEMeetingSecurityCodeChanged; ... // IMeetingEncryptionControllerEvent void onE2EEMeetingSecurityCodeChanged(); ``` Add the implementation for the function in the `ZoomMeetingSDKEventHandler.cpp` file. ```cpp void UZoomMeetingSDKEventHandler::onE2EEMeetingSecurityCodeChanged() { UE_LOG(BPZoomMeetingSDKLog, Display, TEXT("ZoomMeetingSDKEventHandler::onE2EEMeetingSecurityCodeChanged triggered")); OnE2EEMeetingSecurityCodeChanged.Broadcast(); } ``` Now update the `MeetingEncryptionControllerEvent.cpp` file to call Blueprint event handler when the callback is triggered. ```cpp // Copyright : All rights reserved by Zoom Video Communications 2022- #include "MeetingEncryptionControllerEvent.h" #include "ZoomMeetingSDKEventHandler.h" BEGIN_ZOOM_SDK_UE_NAMESPACE IMeetingEncryptionControllerEvent *MeetingEncryptionControllerEvent::Create() { // Create a static MeetingEncryptionControllerEvent. // We would ideally only need to create a single callback object. // If needed, remove the singleton and use new to create a new object. static MeetingEncryptionControllerEvent s_mrce; return &s_mrce; } void MeetingEncryptionControllerEvent::onE2EEMeetingSecurityCodeChanged() { GetZMEventHandler()->onE2EEMeetingSecurityCodeChanged(); } END_ZOOM_SDK_UE_NAMESPACE ``` To see the Blueprint functions and callbacks, do a clean compile. Delete the binaries and intermediate folders from `.../Plugins/ZoomMeetingSDK/Binaries` and `.../Plugins/ZoomMeetingSDK/Intermediate` and restart Unreal Engine if needed. Open the Unreal Engine Blueprint editor to use the added Blueprint functions. ### Integrate video This section shows two examples of video integration with MSDK and Unreal: sending the video to the render targets, and sending the render targets to Zoom as cameras. #### Send Zoom video to render targets There's a special function in the Meeting SDK for Unreal called `SubscribeVideo` that lets you output a participant video feed to an Unreal Engine Render Target using the participant ID. To use this function, the Unreal Engine's Zoom meeting participant must have obtained recording or live streaming permission, or be in a role in Zoom that contains those privileges such as host or co-host. Helper functions for obtaining permissions and detecting role changes are available in the wrapper. The `SubscribeVideo` function also accepts a parameter that can specify the use of the participant video feed or the shared content feed. See [Use raw data in the Meeting SDK](/docs/meeting-sdk/windows/add-features/raw-data/) for more information. #### Send the render target to the meeting as a camera Use **Render Targets** to bring content from Unreal Engine back to Zoom as the camera source of the participant created by the Meeting SDK. The `EnableVideoSource` function takes a render target parameter and translates it to the camera feed in the Zoom client. The render target must have a minimum resolution of 360p and must have **Render Target Format** set to `RTF RGBA8 SRGB`. ---