Unreal Notifications & Annotations Guide
This guide was written as a quick reference step by step guide for setting up a simple Unreal notification/annotation. For more in depth information on each step please check the documentation at https://www.genvidtech.com/for-developers/sdk-latest/
C++ Implementation
In BeginPlay we need to add 2 more streams. One for notifications and one for annotations. Create two functions. One called ClockNotificationStream and one called ClockAnnotationStream.
Both return a FGenvidStream and have no inputs
ClockNotificationStream should do the following:
FGenvidStream ClockNotification; ClockNotification.Name = "ClockNotification"; ClockNotification.OnGenvidStreamDelegate.BindUFunction(this, "OnSubmitClockNotification"); ClockNotification.Framerate = 30; return ClockNotification;
We create a new genvid stream named “ClockNotification”
We bind it to “OnSubmitClockNotification” which we will create later
We set it’s framerate to 30
ClockAnnotationStream should do the following:
FGenvidStream ClockAnnotation; ClockAnnotation.Name = "ClockAnnotation"; ClockAnnotation.OnGenvidStreamDelegate.BindUFunction(this, "OnSubmitClockAnnotation"); ClockAnnotation.Framerate = 30; return ClockAnnotation;
This function works exactly the same as above but instead binds to “OnSubmitClockAnnotation” which we will create later
Create two properties on the GenvidStream class of type bool called “ShouldSendNotification” and “ShouldSendAnnotation”.
When these booleans are true we will submit an notification or annotation
Create another property on the GenvidStream class of type int32 called “PreviousSecondUsed”
This will help track the notification or annotation to send
Override the “TickComponent” function of UGenvidStreams.
It has a return type of void
It has 3 parameters: float DeltaTime, enum ElevelTick TickType, and FActorComponenetTickFunction* ThisTickFunction
Due to the tick component on UGenvidStreams being private. We cannot call Super::TickComponent on it. This is scheduled to be changed soon. For now we can get around it by going to the function and copy and pasting all of its contents and replacing it’s super call with a call to UActorComponent::TickComponenet:
// UGenvidStreams Tick component is private. so have to do this for now (It will be protected in the future) UActorComponent::TickComponent(DeltaTime, TickType, ThisTickFunction); if (bIsCreated) { ReceiveTick(DeltaTime); for (auto& stream : Streams) { stream.TriggerStream(DeltaTime); } }
Add the following code after the above copy and pasted code from its parent:
// Actual new code: int32 CurrentSecond = FDateTime::Now().GetSecond(); if (PreviousUsedSecond != CurrentSecond && CurrentSecond % 15 == 0) { PreviousUsedSecond = CurrentSecond; ShouldSendNotification = true; ShouldSendAnnotation = true; }
We get the current second every tick. The first time it’s 0, 15, 30, or 45 we flip the booleans created. We also track what second flipped the booleans.
Create two new functions “OnSubmitClockNotification” and “OnSubmitClockAnnotation”
Both take in an FString named Id as a parameter and have a return type of void
OnSubmitClockNotification should do the following:
if (!ShouldSendNotification) { return; } ShouldSendNotification = false; FClockTime ClockTime; ClockTime.Second = PreviousUsedSecond; FString ClockTimeString; FJsonObjectConverter::UStructToJsonObjectString(ClockTime, ClockTimeString); SubmitNotification(Id, ClockTimeString);
If we should send a notification we proceed and flip the bool to false
We send a JSON object where only the second value matters. Note you can create a smaller structure to do this and not reuse FClockTime if desired. The second sent is the second that flipped the boolean.
We then call “SubmitNotification”
OnSubmitClockAnnotation should do the following:
if (!ShouldSendAnnotation) { return; } ShouldSendAnnotation = false; FClockTime ClockTime; ClockTime.Second = PreviousUsedSecond; FString ClockTimeString; FJsonObjectConverter::UStructToJsonObjectString(ClockTime, ClockTimeString); SubmitAnnotation(Id, ClockTimeString);
It is doing exactly the same thing as the notification function. But with it’s own boolean and using “SubmitAnnotation” instead
Web Implementation
Go back to your Web/public folder. Open index.html
Add two divs. One for notification and one for annotation:
<div class="child" id="notification"></div> <div class="child" id="annotation"></div>
Open overlay.js. Create two new functions. One called notificationUpdate and one the other called annotationUpdate. Both take in a parameter called data:
function notificationUpdate(data) { document.getElementById("notification").innerHTML = "Notification: " + Math.round(data.second); } function annotationUpdate(data) { document.getElementById("annotation").innerHTML = "Annotation: " + Math.round(data.second); }
We set the divs created with the second value we sent in the above steps
In the existing genvidClient.onDraw function add the following code after the existing “ClockTime” stream code:
if ("ClockAnnotation" in frame.annotations && frame.annotations["ClockAnnotation"].length) { for (let gameAnnotation of frame.annotations["ClockAnnotation"]) { if (gameAnnotation && gameAnnotation.user) { annotationUpdate(gameAnnotation.user); } } }
We check to see if a ClockAnnotation data value exists. If we pass the parsed JSON value to annotationUpdate. (It is already being parsed in onStreamsRecieved)
Similarly to genvidClient.onDraw we will define another function on genvidClient called “onNotificationsReceived” that triggers whenever a notification is sent:
genvidClient.onNotificationsReceived(function (notifications) { for (let notification of notifications.notifications) { try { notification.user = JSON.parse(notification.data); notificationUpdate(notification.user); } catch (e) { console.log(e, notification); } } });
We parse the notification received and pass in the data to notificationUpdate
Putting it all together
Package your project making sure that .exe ends up in the same location as before so that it can be picked up by the web code
If you need to - recreate your bastion instance, setup the genvid-sdk, and load using the python script
Open up your genvid monitor page using genvid-sdk monitor. Hit start all and open up your stream
Note that the annotation updates in sync with the video whenever the seconds hand hits a 15 second interval
Note that the notification updates out of sync with the video ASAP when the second hand hits a 15 second interval
Stream with a notification out of sync with the video and an annotation in sync