diff --git a/observability/roomobs/gen_reporter.go b/observability/roomobs/gen_reporter.go index 1b3cf6a65..82ed1529d 100644 --- a/observability/roomobs/gen_reporter.go +++ b/observability/roomobs/gen_reporter.go @@ -6,7 +6,7 @@ import ( "time" ) -const Version_HCR54L8 = true +const Version_5CO3ESG = true type KeyResolver interface { Resolve(string) @@ -19,6 +19,8 @@ type Reporter interface { } type projectReporter interface { + ReportFeatureName(v string) + ReportFeatureDuration(v uint64) } type ProjectTx interface { diff --git a/observability/roomobs/gen_reporter_noop.go b/observability/roomobs/gen_reporter_noop.go index ddedd1771..ea3cb20fe 100644 --- a/observability/roomobs/gen_reporter_noop.go +++ b/observability/roomobs/gen_reporter_noop.go @@ -50,6 +50,8 @@ func NewNoopProjectReporter() ProjectReporter { func (r *noopProjectReporter) RegisterFunc(f func(ts time.Time, tx ProjectTx) bool) {} func (r *noopProjectReporter) Tx(f func(ProjectTx)) {} func (r *noopProjectReporter) TxAt(ts time.Time, f func(ProjectTx)) {} +func (r *noopProjectReporter) ReportFeatureName(v string) {} +func (r *noopProjectReporter) ReportFeatureDuration(v uint64) {} func (r *noopProjectReporter) WithRoom(name string) RoomReporter { return &noopRoomReporter{} } @@ -59,6 +61,9 @@ func (r *noopProjectReporter) WithDeferredRoom() (RoomReporter, KeyResolver) { type noopProjectTx struct{} +func (t *noopProjectTx) ReportFeatureName(v string) {} +func (t *noopProjectTx) ReportFeatureDuration(v uint64) {} + type noopRoomReporter struct{} func NewNoopRoomReporter() RoomReporter { diff --git a/observability/roomobs/room.go b/observability/roomobs/room.go index 6186376a8..7f1bf8449 100644 --- a/observability/roomobs/room.go +++ b/observability/roomobs/room.go @@ -3,6 +3,7 @@ package roomobs import ( "fmt" "strings" + "time" "unsafe" "github.com/livekit/protocol/livekit" @@ -180,3 +181,44 @@ func ParticipantKindCode(k livekit.ParticipantInfo_Kind) int32 { func ParticipantKindDetailsCodes(d []livekit.ParticipantInfo_KindDetail) []int32 { return *(*[]int32)(unsafe.Pointer(&d)) } + +// GetFeatureNameFromFeature returns the canonical name used to identify a +// feature usage in the feature_usages rollup. It returns "" when info is nil or +// the feature is not recognized, so callers can skip reporting. +func GetFeatureNameFromFeature(info *livekit.FeatureUsageInfo) string { + if info == nil { + return "" + } + switch info.GetFeature() { + case livekit.FeatureUsageInfo_KRISP_NOISE_CANCELLATION: + return "krisp_noise_cancellation" + case livekit.FeatureUsageInfo_KRISP_BACKGROUND_VOICE_CANCELLATION: + return "krisp_background_voice_cancellation" + case livekit.FeatureUsageInfo_KRISP_VIVA: + return "krisp_viva" + case livekit.FeatureUsageInfo_AIC_AUDIO_ENHANCEMENT: + // AIC enhancement is keyed by the specific feature metadata (_). + for k, v := range info.GetFeatureInfo() { + return fmt.Sprintf("%s_%s", k, v) + } + return "aic_audio_enhancement" + default: + return "" + } +} + +// GetFeatureDurationFromFeature returns the total active duration of a feature +// usage, summed across its (non-negative) time ranges. +func GetFeatureDurationFromFeature(info *livekit.FeatureUsageInfo) time.Duration { + if info == nil { + return 0 + } + var d time.Duration + for _, tr := range info.GetTimeRanges() { + start, end := tr.GetStartedAt().AsTime(), tr.GetEndedAt().AsTime() + if end.After(start) { + d += end.Sub(start) + } + } + return d +}