Implement Cast Streaming mirroring, DLNA casting, daemon+GUI, and breadd integration
Some checks failed
dev release / build (push) Failing after 12s

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.
This commit is contained in:
Breadway 2026-08-03 09:07:21 +08:00
parent 887c29002f
commit 8c745d18e0
283 changed files with 36788 additions and 0 deletions

View file

@ -0,0 +1,195 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CAST_STREAMING_PUBLIC_STATISTICS_H_
#define CAST_STREAMING_PUBLIC_STATISTICS_H_
#include <stddef.h>
#include <stdint.h>
#include <string>
#include <utility>
#include <vector>
#include "cast/streaming/public/frame_id.h"
#include "cast/streaming/rtp_time.h"
#include "json/value.h"
#include "platform/api/time.h"
namespace openscreen::cast {
// This file must be updated whenever sender_stats.proto is updated.
enum class StatisticType {
// Frame enqueuing rate.
kEnqueueFps = 0,
// Average capture latency in milliseconds.
kAvgCaptureLatencyMs,
// Average encode duration in milliseconds.
kAvgEncodeTimeMs,
// Duration from when a frame is encoded to when the packet is first
// sent.
kAvgQueueingLatencyMs,
// Duration from when a packet is transmitted to when it is received.
// This measures latency from sender to receiver.
kAvgNetworkLatencyMs,
// Duration from when a frame is encoded to when the packet is first
// received.
kAvgPacketLatencyMs,
// Average latency between frame encoded and the moment when the frame
// is fully received.
kAvgFrameLatencyMs,
// Duration from when a frame is captured to when it should be played out.
kAvgEndToEndLatencyMs,
// Encode bitrate in kbps.
kEncodeRateKbps,
// Packet transmission bitrate in kbps.
kPacketTransmissionRateKbps,
// Duration in milliseconds since the estimated last time the receiver sent
// a response.
kTimeSinceLastReceiverResponseMs,
// Number of frames captured.
kNumFramesCaptured,
// Number of frames dropped by encoder.
kNumFramesDroppedByEncoder,
// Number of late frames.
kNumLateFrames,
// Number of packets that were sent.
kNumPacketsSent,
// Number of packets that were received by receiver.
kNumPacketsReceived,
// Unix time in milliseconds of first event since reset.
kFirstEventTimeMs,
// Unix time in milliseconds of last event since reset.
kLastEventTimeMs,
// The number of statistic types.
kNumTypes = kLastEventTimeMs + 1
};
enum class HistogramType {
// Histogram representing the capture latency (in milliseconds).
kCaptureLatencyMs,
// Histogram representing the encode time (in milliseconds).
kEncodeTimeMs,
// Histogram representing the queueing latency (in milliseconds).
kQueueingLatencyMs,
// Histogram representing the network latency (in milliseconds).
kNetworkLatencyMs,
// Histogram representing the packet latency (in milliseconds).
kPacketLatencyMs,
// Histogram representing the end to end latency (in milliseconds).
kEndToEndLatencyMs,
// Histogram representing how late frames are (in milliseconds).
kFrameLatenessMs,
// The number of histogram types.
kNumTypes = kFrameLatenessMs + 1
};
struct SimpleHistogram {
// This will create N+2 buckets where N = (max - min) / width:
// Underflow bucket: < min
// Bucket 0: [min, min + width - 1]
// Bucket 1: [min + width, min + 2 * width - 1]
// ...
// Bucket N-1: [max - width, max - 1]
// Overflow bucket: >= max
// `min` must be less than `max`.
// `width` must divide `max - min` evenly.
SimpleHistogram(int64_t min, int64_t max, int64_t width);
SimpleHistogram();
SimpleHistogram(const SimpleHistogram&);
SimpleHistogram(SimpleHistogram&&) noexcept;
SimpleHistogram& operator=(const SimpleHistogram&);
SimpleHistogram& operator=(SimpleHistogram&&);
~SimpleHistogram();
bool operator==(const SimpleHistogram&) const;
void Add(int64_t sample);
void Reset();
Json::Value ToJson() const;
std::string ToString() const;
int64_t min = 1;
int64_t max = 1;
int64_t width = 1;
std::vector<int> buckets;
private:
SimpleHistogram(int64_t min,
int64_t max,
int64_t width,
std::vector<int> buckets);
std::string GetBucketName(size_t index) const;
};
std::ostream& operator<<(std::ostream& out, const SimpleHistogram& histogram);
struct SenderStats {
using StatisticsList =
std::array<double, static_cast<size_t>(StatisticType::kNumTypes)>;
using HistogramsList =
std::array<SimpleHistogram,
static_cast<size_t>(HistogramType::kNumTypes)>;
// The current audio statistics.
StatisticsList audio_statistics = {};
// The current audio histograms.
HistogramsList audio_histograms = {};
// The current video statistics.
StatisticsList video_statistics = {};
// The current video histograms.
HistogramsList video_histograms = {};
Json::Value ToJson() const;
std::string ToString() const;
};
std::ostream& operator<<(std::ostream& out, const SenderStats& stats);
// The consumer may provide a statistics client if they are interested in
// getting statistics about the ongoing session.
class SenderStatsClient {
public:
// Gets called regularly with updated statistics while they are being
// generated.
virtual void OnStatisticsUpdated(const SenderStats& updated_stats) = 0;
protected:
virtual ~SenderStatsClient();
};
} // namespace openscreen::cast
#endif // CAST_STREAMING_PUBLIC_STATISTICS_H_