Issue Description
When using the Agora Unreal Engine SDK (Blueprint version), developers may need to display the local video feed in multiple locations — for example, showing the same camera view in both a 3D viewport and a UI widget simultaneously.
By default, the SetupLocalVideo() API allows binding the local video stream to only one view object, and calling it multiple times replaces the previous binding.
Additionally, developers may wish to apply custom masking or UI effects (e.g., rounded frames, custom shapes) on the local video feed, but encounter issues as Agora’s renderer overrides the base image layer.
Platform/SDK
Product: Video Calling
Platform: Unreal Engine (Blueprint and C++ SDK)
SDK Version: 4.5.1
OS: Windows
Example Error/Behavior
Calling
SetupLocalVideo()twice results in only the last image slot displaying the video.The first image remains blank (white).
Attempts to mask or stylize the Agora video feed fail because Agora directly renders over the base image widget.
Root Cause
The Agora Unreal SDK supports only one active local video binding per SetupLocalVideo() call.
When you invoke SetupLocalVideo() a second time, it detaches the previous rendering context and rebinds it to the new one.
For reusing the local video feed, you must capture the rendered texture (or raw video data) and reuse it in multiple locations.
Step-by-Step Solution
Option 1: Using TextureRenderTarget2D (Recommended for C++ SDK)
You can render the local video once and reuse the output in multiple UI elements or materials.
-
First Render (Standard Setup)
Bind the local camera feed to aViewportusingSetupLocalVideo():agora::rtc::VideoCanvas Canvas; Canvas.uid = 0; // Local user UID Canvas.view = PC->GetLocalPlayer()->ViewportClient->Viewport; Canvas.renderMode = agora::rtc::RENDER_MODE_FIT; AgoraRtcEngine->setupLocalVideo(Canvas); -
Capture the Rendered Output
Create aUTextureRenderTarget2Dand capture the output:UTextureRenderTarget2D* RenderTarget = NewObject<UTextureRenderTarget2D>(this); RenderTarget->InitAutoFormat(1920, 1080); UKismetRenderingLibrary::DrawMaterialToRenderTarget( GetWorld(), RenderTarget, YourMaterialInstance ); -
Display in Multiple Places
Assign the capturedRenderTargetas a brush texture to any number of UI elements:SecondaryImage->Brush.SetResourceObject(RenderTarget); SecondaryImage->Brush.SetImageSize(FVector2D(1920, 1080)); -
Apply Mask or Shape
Use a UMG Mask or custom material with alpha blending.
Apply your mask (e.g., circle, rounded rectangle) as a shader on top of the RenderTarget.
Option 2: Using Raw Video Data (Advanced, Requires C++ SDK)
If you want complete control over the video frames (e.g., to apply custom shaders or transformations):
-
Register the Raw Video Observer
ImplementIVideoFrameObserverin your C++ class.class FCustomVideoObserver : public agora::media::IVideoFrameObserver { bool onCaptureVideoFrame(VideoFrame& frame) override { // Access raw frame data (YUV or RGBA) return true; } }; -
Register Observer
rtcEngine->registerVideoFrameObserver(yourVideoObserverInstance); Render the Frame
Convert and display frames using Unreal’s rendering pipeline (e.g.,UpdateTextureRegions()).
Reference: Agora Unreal C++ SDK Example – ProcessVideoRawData
Option 3: Blueprint-Only Workaround
If you must remain within Blueprints, you cannot use raw data directly.
However, you can:
Render once using
SetupLocalVideo()Duplicate the rendered texture by assigning the same Texture object to multiple UMG images using the “Set Brush From Texture” node.
This allows visual duplication without re-binding the video feed.
Prevention / Best Practices
Use the C++ SDK when working with advanced rendering (multi-display, masking, raw frame access).
Do not call
SetupLocalVideo()multiple times with different view targets — use captured textures instead.Always ensure you destroy or release custom observers when leaving the channel to prevent memory leaks.