breadcast/breadcast-caststream-sys/vendor/openscreen/cast/streaming/public/statistics.cc
Breadway 8c745d18e0
Some checks failed
dev release / build (push) Failing after 12s
Implement Cast Streaming mirroring, DLNA casting, daemon+GUI, and breadd integration
Builds out the full v1 scope: a vendored+patched openscreen subset for
low-latency Cast Streaming (Mirroring receiver 0F5096E8) alongside the
existing Cast V2/HLS and new DLNA/AVTransport casting paths, breadcastd's
Idle/Casting state machine with a private IPC socket, the breadcast GTK4
popup as a thin IPC client, and bread.cast.*/bread.command.cast.* breadd
integration (device discovery, start/stop, mirroring lifecycle events).
Also adds bakery/systemd/Forgejo CI packaging.

Validated end-to-end against a real Chromecast/Google TV: negotiated
Cast Streaming session, live pipeline playback, and daemon+GUI click-to-cast/
stop through the actual popup.
2026-08-03 09:07:21 +08:00

183 lines
6.2 KiB
C++

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cast/streaming/public/statistics.h"
#include <algorithm>
#include <iomanip>
#include <iostream>
#include "util/enum_name_table.h"
#include "util/json/json_helpers.h"
#include "util/json/json_serialization.h"
#include "util/stringprintf.h"
namespace openscreen::cast {
namespace {
template <typename Type>
Json::Value ToJson(const Type& t) {
return t.ToJson();
}
template <>
Json::Value ToJson(const double& t) {
return t;
}
template <typename T, typename Type>
Json::Value ArrayToJson(
const std::array<T, static_cast<size_t>(Type::kNumTypes)>& list,
const EnumNameTable<Type, static_cast<size_t>(Type::kNumTypes)>& names) {
Json::Value out;
for (size_t i = 0; i < list.size(); ++i) {
ErrorOr<const char*> name = GetEnumName(names, static_cast<Type>(i));
OSP_CHECK(name);
out[name.value()] = ToJson(list[i]);
}
return out;
}
} // namespace
// External linkage for unit test
extern const EnumNameTable<StatisticType,
static_cast<size_t>(StatisticType::kNumTypes)>
kStatisticTypeNames = {
{{"EnqueueFps", StatisticType::kEnqueueFps},
{"AvgCaptureLatencyMs", StatisticType::kAvgCaptureLatencyMs},
{"AvgEncodeTimeMs", StatisticType::kAvgEncodeTimeMs},
{"AvgQueueingLatencyMs", StatisticType::kAvgQueueingLatencyMs},
{"AvgNetworkLatencyMs", StatisticType::kAvgNetworkLatencyMs},
{"AvgPacketLatencyMs", StatisticType::kAvgPacketLatencyMs},
{"AvgFrameLatencyMs", StatisticType::kAvgFrameLatencyMs},
{"AvgEndToEndLatencyMs", StatisticType::kAvgEndToEndLatencyMs},
{"EncodeRateKbps", StatisticType::kEncodeRateKbps},
{"PacketTransmissionRateKbps",
StatisticType::kPacketTransmissionRateKbps},
{"TimeSinceLastReceiverResponseMs",
StatisticType::kTimeSinceLastReceiverResponseMs},
{"NumFramesCaptured", StatisticType::kNumFramesCaptured},
{"NumFramesDroppedByEncoder",
StatisticType::kNumFramesDroppedByEncoder},
{"NumLateFrames", StatisticType::kNumLateFrames},
{"NumPacketsSent", StatisticType::kNumPacketsSent},
{"NumPacketsReceived", StatisticType::kNumPacketsReceived},
{"FirstEventTimeMs", StatisticType::kFirstEventTimeMs},
{"LastEventTimeMs", StatisticType::kLastEventTimeMs}}};
// External linkage for unit test
extern const EnumNameTable<HistogramType,
static_cast<size_t>(HistogramType::kNumTypes)>
kHistogramTypeNames = {
{{"CaptureLatencyMs", HistogramType::kCaptureLatencyMs},
{"EncodeTimeMs", HistogramType::kEncodeTimeMs},
{"QueueingLatencyMs", HistogramType::kQueueingLatencyMs},
{"NetworkLatencyMs", HistogramType::kNetworkLatencyMs},
{"PacketLatencyMs", HistogramType::kPacketLatencyMs},
{"EndToEndLatencyMs", HistogramType::kEndToEndLatencyMs},
{"FrameLatenessMs", HistogramType::kFrameLatenessMs}}};
SimpleHistogram::SimpleHistogram() = default;
SimpleHistogram::SimpleHistogram(int64_t min, int64_t max, int64_t width)
: min(min), max(max), width(width), buckets((max - min) / width + 2) {
OSP_CHECK_GT(buckets.size(), 2u);
OSP_CHECK_EQ(0, (max - min) % width);
}
SimpleHistogram::SimpleHistogram(const SimpleHistogram&) = default;
SimpleHistogram::SimpleHistogram(SimpleHistogram&&) noexcept = default;
SimpleHistogram& SimpleHistogram::operator=(const SimpleHistogram&) = default;
SimpleHistogram& SimpleHistogram::operator=(SimpleHistogram&&) = default;
SimpleHistogram::~SimpleHistogram() = default;
bool SimpleHistogram::operator==(const SimpleHistogram& other) const {
return min == other.min && max == other.max && width == other.width &&
buckets == other.buckets;
}
void SimpleHistogram::Add(int64_t sample) {
if (sample < min) {
++buckets.front();
} else if (sample >= max) {
++buckets.back();
} else {
size_t index = 1 + (sample - min) / width;
OSP_CHECK_LT(index, buckets.size());
++buckets[index];
}
}
void SimpleHistogram::Reset() {
buckets.assign(buckets.size(), 0);
}
Json::Value SimpleHistogram::ToJson() const {
// Nest the bucket values in an array instead of a dictionary, so we sort
// numerically instead of alphabetically.
Json::Value out(Json::ValueType::arrayValue);
for (size_t i = 0; i < buckets.size(); ++i) {
if (buckets[i] != 0) {
Json::Value entry;
entry[GetBucketName(i)] = buckets[i];
out.append(entry);
}
}
return out;
}
std::string SimpleHistogram::ToString() const {
return json::Stringify(ToJson()).value();
}
SimpleHistogram::SimpleHistogram(int64_t min,
int64_t max,
int64_t width,
std::vector<int> buckets)
: SimpleHistogram(min, max, width) {
this->buckets = std::move(buckets);
}
std::string SimpleHistogram::GetBucketName(size_t index) const {
if (index == 0) {
return "<" + std::to_string(min);
}
if (index == buckets.size() - 1) {
return ">=" + std::to_string(max);
}
// See the constructor comment for an example of how these bucket bounds
// are calculated.
const int bucket_min = min + width * (index - 1);
const int bucket_max = min + index * width - 1;
return StringFormat("{}-{}", bucket_min, bucket_max);
}
Json::Value SenderStats::ToJson() const {
Json::Value out;
out["audio_statistics"] = ArrayToJson(audio_statistics, kStatisticTypeNames);
out["audio_histograms"] = ArrayToJson(audio_histograms, kHistogramTypeNames);
out["video_statistics"] = ArrayToJson(video_statistics, kStatisticTypeNames);
out["video_histograms"] = ArrayToJson(video_histograms, kHistogramTypeNames);
return out;
}
std::string SenderStats::ToString() const {
return json::Stringify(ToJson()).value();
}
std::ostream& operator<<(std::ostream& out, const SenderStats& stats) {
return out << stats.ToString();
}
std::ostream& operator<<(std::ostream& out, const SimpleHistogram& histogram) {
return out << histogram.ToString();
}
SenderStatsClient::~SenderStatsClient() {}
} // namespace openscreen::cast