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,282 @@
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UTIL_TRACE_LOGGING_H_
#define UTIL_TRACE_LOGGING_H_
#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "platform/base/trace_logging_types.h"
// All compile-time macros for tracing.
// NOTE: The ternary operator is used here to ensure that the TraceLogger object
// is only constructed if tracing is enabled, but at the same time is created in
// the caller's scope. The C++ standards guide guarantees that the constructor
// should only be called when IsTraceLoggingEnabled(...) evaluates to true.
// static_cast calls are used because if the type of the result of the ternary
// operator does not match the expected type, temporary storage is used for the
// created object, which results in an extra call to the constructor and
// destructor of the tracing objects.
//
// Further details about how these macros are used can be found in
// docs/trace_logging.md.
#if defined(ENABLE_TRACE_LOGGING)
#define INCLUDING_FROM_UTIL_TRACE_LOGGING_H_
#include "util/trace_logging/macro_support.h"
#undef INCLUDING_FROM_UTIL_TRACE_LOGGING_H_
#define TRACE_SET_RESULT(result) \
do { \
if (TRACE_IS_ENABLED(openscreen::TraceCategory::kAny)) { \
openscreen::internal::ScopedTraceOperation::set_result(result); \
} \
} while (false)
#define TRACE_SET_HIERARCHY(ids) TRACE_SET_HIERARCHY_INTERNAL(__LINE__, ids)
#define TRACE_HIERARCHY \
(TRACE_IS_ENABLED(openscreen::TraceCategory::kAny) \
? openscreen::internal::ScopedTraceOperation::hierarchy() \
: openscreen::TraceIdHierarchy::Empty())
#define TRACE_CURRENT_ID \
(TRACE_IS_ENABLED(openscreen::TraceCategory::kAny) \
? openscreen::internal::ScopedTraceOperation::current_id() \
: kEmptyTraceId)
#define TRACE_ROOT_ID \
(TRACE_IS_ENABLED(openscreen::TraceCategory::kAny) \
? openscreen::internal::ScopedTraceOperation::root_id() \
: kEmptyTraceId)
namespace openscreen::internal {
template <typename T>
std::string ToString(T&& val) {
using DecayT = std::decay_t<T>;
if constexpr (std::is_constructible_v<std::string, T>) {
return std::string(std::forward<T>(val));
} else if constexpr (std::is_arithmetic_v<DecayT>) {
return std::to_string(val);
} else {
std::ostringstream oss;
oss << val;
return oss.str();
}
}
// Helper to extract a flow ID from various types (arithmetic or wrappers like
// FrameId).
template <typename T>
constexpr uint64_t ToFlowId(const T& val) {
if constexpr (std::is_arithmetic_v<T>) {
return static_cast<uint64_t>(val);
} else {
// Assume it's a numeric wrapper like FrameId with a .value() method.
return static_cast<uint64_t>(val.value());
}
}
} // namespace openscreen::internal
template <typename V1 = std::string, typename V2 = std::string>
inline std::vector<openscreen::TraceEvent::Argument> ToArgumentArray(
const char* argname = nullptr,
V1&& argval = V1(),
const char* argname_two = nullptr,
V2&& argval_two = V2()) {
std::vector<openscreen::TraceEvent::Argument> out;
if (argname) {
out.emplace_back(argname,
openscreen::internal::ToString(std::forward<V1>(argval)));
}
if (argname_two) {
out.emplace_back(argname_two, openscreen::internal::ToString(
std::forward<V2>(argval_two)));
}
return out;
}
// Synchronous Trace Macros.
//
// Scoped traces with no arguments.
#define TRACE_SCOPED(category, name, ...) \
TRACE_SCOPED_INTERNAL(__LINE__, category, name, ToArgumentArray(), \
##__VA_ARGS__)
#define TRACE_DEFAULT_SCOPED(category, ...) \
TRACE_SCOPED(category, __PRETTY_FUNCTION__, ##__VA_ARGS__)
// Scoped traces with one argument.
#define TRACE_SCOPED1(category, name, argname, argval, ...) \
TRACE_SCOPED_INTERNAL(__LINE__, category, name, \
ToArgumentArray(argname, argval), ##__VA_ARGS__)
#define TRACE_DEFAULT_SCOPED1(category, argname, argval, ...) \
TRACE_SCOPED1(category, __PRETTY_FUNCTION__, argname, argval, ##__VA_ARGS__)
// Scoped traces with two arguments.
#define TRACE_SCOPED2(category, name, argname, argval, argname_two, \
argval_two, ...) \
TRACE_SCOPED_INTERNAL( \
__LINE__, category, name, \
ToArgumentArray(argname, argval, argname_two, argval_two), \
##__VA_ARGS__)
#define TRACE_DEFAULT_SCOPED2(category, argname, argval, argname_two, \
argval_two, ...) \
TRACE_SCOPED2(category, __PRETTY_FUNCTION__, argname, argval, argname_two, \
argval_two, ##__VA_ARGS__)
// Asynchronous Trace Macros.
#define TRACE_ASYNC_START(category, name, ...) \
TRACE_ASYNC_START_INTERNAL(__LINE__, category, name, ToArgumentArray(), \
##__VA_ARGS__)
#define TRACE_ASYNC_START1(category, name, argname, argval, ...) \
TRACE_ASYNC_START_INTERNAL(__LINE__, category, name, \
ToArgumentArray(argname, argval), ##__VA_ARGS__)
#define TRACE_ASYNC_START2(category, name, argname, argval, argname_two, \
argval_two, ...) \
TRACE_ASYNC_START_INTERNAL( \
__LINE__, category, name, \
ToArgumentArray(argname, argval, argname_two, argval_two), \
##__VA_ARGS__)
#define TRACE_ASYNC_END(category, id, result) \
TRACE_IS_ENABLED(category) \
? openscreen::internal::ScopedTraceOperation::TraceAsyncEnd( \
__LINE__, __FILE__, id, result) \
: false
// Flow events are used to link trace events across different threads or
// processes. Flows are linked by their flow_id.
// - Flows can span across different trace categories.
// - If a TRACE_FLOW_BEGIN is missing (e.g. because the embedder didn't
// instrument it),
// the first TRACE_FLOW_STEP encountered will effectively start the flow
// visualization.
#define TRACE_FLOW_BEGIN(category, name, flow_id) \
TRACE_IS_ENABLED(category) \
? openscreen::internal::ScopedTraceOperation::TraceFlow( \
category, name, __FILE__, __LINE__, \
openscreen::internal::ToFlowId(flow_id), \
openscreen::FlowType::kFlowBegin) \
: false
#define TRACE_FLOW_STEP(category, name, flow_id) \
TRACE_IS_ENABLED(category) \
? openscreen::internal::ScopedTraceOperation::TraceFlow( \
category, name, __FILE__, __LINE__, \
openscreen::internal::ToFlowId(flow_id), \
openscreen::FlowType::kFlowStep) \
: false
#define TRACE_FLOW_END(category, name, flow_id) \
TRACE_IS_ENABLED(category) \
? openscreen::internal::ScopedTraceOperation::TraceFlow( \
category, name, __FILE__, __LINE__, \
openscreen::internal::ToFlowId(flow_id), \
openscreen::FlowType::kFlowEnd) \
: false
#define TRACE_FLOW_BEGIN_WITH_TIME(category, name, flow_id, timestamp) \
TRACE_IS_ENABLED(category) \
? openscreen::internal::ScopedTraceOperation::TraceFlow( \
category, name, __FILE__, __LINE__, \
openscreen::internal::ToFlowId(flow_id), \
openscreen::FlowType::kFlowBegin, timestamp) \
: false
#define TRACE_FLOW_STEP_WITH_TIME(category, name, flow_id, timestamp) \
TRACE_IS_ENABLED(category) \
? openscreen::internal::ScopedTraceOperation::TraceFlow( \
category, name, __FILE__, __LINE__, \
openscreen::internal::ToFlowId(flow_id), \
openscreen::FlowType::kFlowStep, timestamp) \
: false
#define TRACE_FLOW_END_WITH_TIME(category, name, flow_id, timestamp) \
TRACE_IS_ENABLED(category) \
? openscreen::internal::ScopedTraceOperation::TraceFlow( \
category, name, __FILE__, __LINE__, \
openscreen::internal::ToFlowId(flow_id), \
openscreen::FlowType::kFlowEnd, timestamp) \
: false
#define TRACE_FLOW_DEFAULT_BEGIN(category, flow_id) \
TRACE_FLOW_BEGIN(category, __PRETTY_FUNCTION__, flow_id)
#define TRACE_FLOW_DEFAULT_STEP(category, flow_id) \
TRACE_FLOW_STEP(category, __PRETTY_FUNCTION__, flow_id)
#define TRACE_FLOW_DEFAULT_END(category, flow_id) \
TRACE_FLOW_END(category, __PRETTY_FUNCTION__, flow_id)
#else // ENABLE_TRACE_LOGGING not defined
namespace openscreen::internal {
// Consumes `args` (to avoid "warn unused variable" errors at compile time), and
// provides a "void" result type in the macros below.
template <typename... Args>
inline void DoNothingForTracing(Args... args) {}
} // namespace openscreen::internal
#define TRACE_SET_RESULT(result) \
openscreen::internal::DoNothingForTracing(result)
#define TRACE_SET_HIERARCHY(ids) openscreen::internal::DoNothingForTracing(ids)
#define TRACE_HIERARCHY openscreen::TraceIdHierarchy::Empty()
#define TRACE_CURRENT_ID openscreen::kEmptyTraceId
#define TRACE_ROOT_ID openscreen::kEmptyTraceId
#define TRACE_SCOPED(category, name, ...) \
openscreen::internal::DoNothingForTracing(category, name, ##__VA_ARGS__)
#define TRACE_DEFAULT_SCOPED(category, ...) \
TRACE_SCOPED(category, __PRETTY_FUNCTION__, ##__VA_ARGS__)
#define TRACE_SCOPED1(category, name, argname, argval, ...) \
openscreen::internal::DoNothingForTracing(category, name, argname, argval, \
##__VA_ARGS__)
#define TRACE_DEFAULT_SCOPED1(category, argname, argval, ...) \
TRACE_SCOPED1(category, __PRETTY_FUNCTION__, argname, argval, ##__VA_ARGS__)
#define TRACE_SCOPED2(category, name, argname, argval, argname_two, \
argval_two, ...) \
openscreen::internal::DoNothingForTracing( \
category, name, argname, argval, argname_two, argval_two, ##__VA_ARGS__)
#define TRACE_DEFAULT_SCOPED2(category, argname, argval, argname_two, \
argval_two, ...) \
TRACE_SCOPED2(category, __PRETTY_FUNCTION__, argname, argval, argname_two, \
argval_two, ##__VA_ARGS__)
#define TRACE_ASYNC_START(category, name, ...) \
openscreen::internal::DoNothingForTracing(category, name, ##__VA_ARGS__)
#define TRACE_ASYNC_END(category, id, result) \
openscreen::internal::DoNothingForTracing(category, id, result)
#define TRACE_FLOW_BEGIN(category, name, flow_id) \
openscreen::internal::DoNothingForTracing(category, name, flow_id)
#define TRACE_FLOW_STEP(category, name, flow_id) \
openscreen::internal::DoNothingForTracing(category, name, flow_id)
#define TRACE_FLOW_END(category, name, flow_id) \
openscreen::internal::DoNothingForTracing(category, name, flow_id)
#define TRACE_FLOW_BEGIN_WITH_TIME(category, name, flow_id, timestamp) \
openscreen::internal::DoNothingForTracing(category, name, flow_id, timestamp)
#define TRACE_FLOW_STEP_WITH_TIME(category, name, flow_id, timestamp) \
openscreen::internal::DoNothingForTracing(category, name, flow_id, timestamp)
#define TRACE_FLOW_END_WITH_TIME(category, name, flow_id, timestamp) \
openscreen::internal::DoNothingForTracing(category, name, flow_id, timestamp)
#define TRACE_FLOW_DEFAULT_BEGIN(category, flow_id) \
openscreen::internal::DoNothingForTracing(category, flow_id)
#define TRACE_FLOW_DEFAULT_STEP(category, flow_id) \
openscreen::internal::DoNothingForTracing(category, flow_id)
#define TRACE_FLOW_DEFAULT_END(category, flow_id) \
openscreen::internal::DoNothingForTracing(category, flow_id)
#endif // defined(ENABLE_TRACE_LOGGING)
#endif // UTIL_TRACE_LOGGING_H_