Initial commit - realms platform
This commit is contained in:
parent
c590ab6d18
commit
c717c3751c
234 changed files with 74103 additions and 15231 deletions
|
|
@ -2,6 +2,7 @@
|
|||
#include "../controllers/StreamController.h"
|
||||
#include "../services/RedisHelper.h"
|
||||
#include "../services/OmeClient.h"
|
||||
#include "../services/RestreamService.h"
|
||||
#include <drogon/HttpClient.h>
|
||||
#include <drogon/utils/Utilities.h>
|
||||
#include <set>
|
||||
|
|
@ -116,19 +117,25 @@ void StatsService::pollOmeStats() {
|
|||
// Update each active stream
|
||||
for (const auto& streamKey : activeStreamKeys) {
|
||||
LOG_INFO << "Processing active stream: " << streamKey;
|
||||
|
||||
// IMMEDIATELY update database to mark as live
|
||||
|
||||
// IMMEDIATELY update database to mark as live and get realm ID
|
||||
auto dbClient = app().getDbClient();
|
||||
*dbClient << "UPDATE realms SET is_live = true, viewer_count = 0, "
|
||||
"updated_at = CURRENT_TIMESTAMP WHERE stream_key = $1"
|
||||
"updated_at = CURRENT_TIMESTAMP WHERE stream_key = $1 RETURNING id"
|
||||
<< streamKey
|
||||
>> [streamKey](const orm::Result&) {
|
||||
>> [streamKey](const orm::Result& r) {
|
||||
LOG_INFO << "Successfully marked realm as live: " << streamKey;
|
||||
|
||||
// Attempt reconnection for any disconnected restream destinations
|
||||
if (!r.empty()) {
|
||||
int64_t realmId = r[0]["id"].as<int64_t>();
|
||||
RestreamService::getInstance().attemptReconnections(streamKey, realmId);
|
||||
}
|
||||
}
|
||||
>> [streamKey](const orm::DrogonDbException& e) {
|
||||
LOG_ERROR << "Failed to update realm live status: " << e.base().what();
|
||||
};
|
||||
|
||||
|
||||
// Then update detailed stats
|
||||
updateStreamStats(streamKey);
|
||||
}
|
||||
|
|
@ -167,10 +174,12 @@ void StatsService::updateStreamStats(const std::string& streamKey) {
|
|||
fetchStatsFromOme(streamKey, [this, streamKey](bool success, const StreamStats& stats) {
|
||||
if (success) {
|
||||
StreamStats updatedStats = stats;
|
||||
updatedStats.uniqueViewers = getUniqueViewerCount(streamKey);
|
||||
|
||||
// Only count viewer tokens when stream is actually live
|
||||
// Offline streams should show 0 viewers (tokens may linger for 5 min after disconnect)
|
||||
updatedStats.uniqueViewers = stats.isLive ? getUniqueViewerCount(streamKey) : 0;
|
||||
|
||||
storeStatsInRedis(streamKey, updatedStats);
|
||||
|
||||
|
||||
// Update realm in database
|
||||
updateRealmLiveStatus(streamKey, updatedStats);
|
||||
|
||||
|
|
@ -267,31 +276,31 @@ void StatsService::fetchStatsFromOme(const std::string& streamKey,
|
|||
hasInput = true;
|
||||
const auto& input = data["input"];
|
||||
|
||||
// Get bitrate from input tracks
|
||||
// Get bitrate from input tracks (OME returns bytes/sec, convert to bits/sec)
|
||||
if (input.isMember("tracks") && input["tracks"].isArray()) {
|
||||
for (const auto& track : input["tracks"]) {
|
||||
if (track["type"].asString() == "video" && track.isMember("bitrate")) {
|
||||
stats.bitrate = track["bitrate"].asDouble();
|
||||
stats.bitrate = track["bitrate"].asDouble() * 8; // Convert bytes/sec to bits/sec
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Alternative: Check lastThroughputIn
|
||||
// Alternative: Check lastThroughputIn (OME returns bytes/sec, convert to bits/sec)
|
||||
if (!hasInput && data.isMember("lastThroughputIn")) {
|
||||
double throughput = data["lastThroughputIn"].asDouble();
|
||||
if (throughput > 0) {
|
||||
hasInput = true;
|
||||
stats.bitrate = throughput;
|
||||
stats.bitrate = throughput * 8; // Convert bytes/sec to bits/sec
|
||||
}
|
||||
}
|
||||
|
||||
// Alternative: Check avgThroughputIn
|
||||
|
||||
// Alternative: Check avgThroughputIn (OME returns bytes/sec, convert to bits/sec)
|
||||
if (!hasInput && data.isMember("avgThroughputIn")) {
|
||||
double avgThroughput = data["avgThroughputIn"].asDouble();
|
||||
if (avgThroughput > 0) {
|
||||
hasInput = true;
|
||||
stats.bitrate = avgThroughput;
|
||||
stats.bitrate = avgThroughput * 8; // Convert bytes/sec to bits/sec
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -479,8 +488,8 @@ void StatsService::getStreamStats(const std::string& streamKey,
|
|||
fetchStatsFromOme(streamKey, [this, callback, streamKey](bool success, const StreamStats& stats) {
|
||||
if (success) {
|
||||
StreamStats updatedStats = stats;
|
||||
// Set uniqueViewers on cache miss
|
||||
updatedStats.uniqueViewers = getUniqueViewerCount(streamKey);
|
||||
// Only count viewer tokens when stream is actually live
|
||||
updatedStats.uniqueViewers = stats.isLive ? getUniqueViewerCount(streamKey) : 0;
|
||||
callback(true, updatedStats);
|
||||
} else {
|
||||
callback(false, stats);
|
||||
|
|
@ -504,7 +513,7 @@ void StatsService::getStreamStats(const std::string& streamKey,
|
|||
stats.resolution = json["resolution"].asString();
|
||||
stats.fps = json["fps"].asDouble();
|
||||
stats.isLive = json["is_live"].asBool();
|
||||
|
||||
|
||||
// Parse protocol connections
|
||||
if (json.isMember("protocol_connections")) {
|
||||
const auto& pc = json["protocol_connections"];
|
||||
|
|
@ -513,19 +522,42 @@ void StatsService::getStreamStats(const std::string& streamKey,
|
|||
stats.protocolConnections.llhls = pc["llhls"].asInt64();
|
||||
stats.protocolConnections.dash = pc["dash"].asInt64();
|
||||
}
|
||||
|
||||
|
||||
stats.lastUpdated = std::chrono::system_clock::time_point(
|
||||
std::chrono::seconds(json["last_updated"].asInt64())
|
||||
);
|
||||
|
||||
callback(true, stats);
|
||||
|
||||
// Verify is_live from database (source of truth from webhooks)
|
||||
// This prevents stale cache from overriding the webhook-updated DB state
|
||||
auto dbClient = app().getDbClient();
|
||||
*dbClient << "SELECT is_live FROM realms WHERE stream_key = $1"
|
||||
<< streamKey
|
||||
>> [callback, stats](const orm::Result& r) mutable {
|
||||
if (!r.empty()) {
|
||||
bool dbIsLive = r[0]["is_live"].as<bool>();
|
||||
// If database says live but cache says offline, trust database
|
||||
// (webhooks update DB immediately, cache may be stale)
|
||||
if (dbIsLive && !stats.isLive) {
|
||||
LOG_DEBUG << "Overriding stale cache: DB says live, cache says offline";
|
||||
stats.isLive = true;
|
||||
}
|
||||
}
|
||||
callback(true, stats);
|
||||
}
|
||||
>> [callback, stats](const orm::DrogonDbException& e) {
|
||||
LOG_ERROR << "Failed to verify is_live from DB: " << e.base().what();
|
||||
// Fall back to cached value on DB error
|
||||
callback(true, stats);
|
||||
};
|
||||
LOG_DEBUG << "Retrieved cached stats for " << streamKey;
|
||||
return; // Callback handled async
|
||||
} else {
|
||||
// Fallback to fresh fetch if cached data is corrupted
|
||||
fetchStatsFromOme(streamKey, [this, callback, streamKey](bool success, const StreamStats& stats) {
|
||||
if (success) {
|
||||
StreamStats updatedStats = stats;
|
||||
updatedStats.uniqueViewers = getUniqueViewerCount(streamKey);
|
||||
// Only count viewer tokens when stream is actually live
|
||||
updatedStats.uniqueViewers = stats.isLive ? getUniqueViewerCount(streamKey) : 0;
|
||||
callback(true, updatedStats);
|
||||
} else {
|
||||
callback(false, stats);
|
||||
|
|
@ -538,7 +570,8 @@ void StatsService::getStreamStats(const std::string& streamKey,
|
|||
fetchStatsFromOme(streamKey, [this, callback, streamKey](bool success, const StreamStats& stats) {
|
||||
if (success) {
|
||||
StreamStats updatedStats = stats;
|
||||
updatedStats.uniqueViewers = getUniqueViewerCount(streamKey);
|
||||
// Only count viewer tokens when stream is actually live
|
||||
updatedStats.uniqueViewers = stats.isLive ? getUniqueViewerCount(streamKey) : 0;
|
||||
callback(true, updatedStats);
|
||||
} else {
|
||||
callback(false, stats);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue