Implement Cast Streaming mirroring, DLNA casting, daemon+GUI, and breadd integration
Some checks failed
dev release / build (push) Failing after 12s
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:
parent
887c29002f
commit
8c745d18e0
283 changed files with 36788 additions and 0 deletions
205
breadcast-caststream-sys/vendor/openscreen/util/json/json_helpers.h
vendored
Normal file
205
breadcast-caststream-sys/vendor/openscreen/util/json/json_helpers.h
vendored
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
// 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_JSON_JSON_HELPERS_H_
|
||||
#define UTIL_JSON_JSON_HELPERS_H_
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "json/value.h"
|
||||
#include "platform/base/error.h"
|
||||
#include "util/chrono_helpers.h"
|
||||
#include "util/json/json_serialization.h"
|
||||
#include "util/simple_fraction.h"
|
||||
|
||||
// This file contains helper methods for parsing JSON, in an attempt to
|
||||
// reduce boilerplate code when working with JsonCpp.
|
||||
namespace openscreen::json {
|
||||
|
||||
inline bool TryParseBool(const Json::Value& value, bool* out) {
|
||||
if (!value.isBool()) {
|
||||
return false;
|
||||
}
|
||||
*out = value.asBool();
|
||||
return true;
|
||||
}
|
||||
|
||||
// A general note about parsing primitives. "Validation" in this context
|
||||
// generally means ensuring that the values are non-negative, excepting doubles
|
||||
// which may be negative in some cases.
|
||||
inline bool TryParseDouble(const Json::Value& value,
|
||||
double* out,
|
||||
bool allow_negative = false) {
|
||||
if (!value.isDouble()) {
|
||||
return false;
|
||||
}
|
||||
const double d = value.asDouble();
|
||||
if (std::isnan(d)) {
|
||||
return false;
|
||||
}
|
||||
if (!allow_negative && d < 0) {
|
||||
return false;
|
||||
}
|
||||
*out = d;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool TryParseInt(const Json::Value& value, int* out) {
|
||||
if (!value.isInt()) {
|
||||
return false;
|
||||
}
|
||||
int i = value.asInt();
|
||||
if (i < 0) {
|
||||
return false;
|
||||
}
|
||||
*out = i;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool TryParseUint(const Json::Value& value, uint32_t* out) {
|
||||
if (!value.isUInt()) {
|
||||
return false;
|
||||
}
|
||||
*out = value.asUInt();
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool TryParseString(const Json::Value& value, std::string* out) {
|
||||
if (!value.isString()) {
|
||||
return false;
|
||||
}
|
||||
*out = value.asString();
|
||||
return true;
|
||||
}
|
||||
|
||||
// We want to be more robust when we parse fractions then just
|
||||
// allowing strings, this will parse numeral values such as
|
||||
// value: 50 as well as value: "50" and value: "100/2".
|
||||
inline bool TryParseSimpleFraction(const Json::Value& value,
|
||||
SimpleFraction* out) {
|
||||
if (value.isInt()) {
|
||||
int parsed = value.asInt();
|
||||
if (parsed < 0) {
|
||||
return false;
|
||||
}
|
||||
*out = SimpleFraction{parsed, 1};
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value.isString()) {
|
||||
auto fraction_or_error = SimpleFraction::FromString(value.asString());
|
||||
if (!fraction_or_error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!fraction_or_error.value().is_positive() ||
|
||||
!fraction_or_error.value().is_defined()) {
|
||||
return false;
|
||||
}
|
||||
*out = std::move(fraction_or_error.value());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool TryParseMilliseconds(const Json::Value& value, milliseconds* out) {
|
||||
int out_ms;
|
||||
if (!TryParseInt(value, &out_ms) || out_ms < 0) {
|
||||
return false;
|
||||
}
|
||||
*out = milliseconds(out_ms);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using Parser = std::function<bool(const Json::Value&, T*)>;
|
||||
|
||||
// NOTE: array parsing methods reset the output vector to an empty vector in
|
||||
// any error case. This is especially useful for optional arrays.
|
||||
template <typename T>
|
||||
bool TryParseArray(const Json::Value& value,
|
||||
Parser<T> parser,
|
||||
std::vector<T>* out) {
|
||||
out->clear();
|
||||
if (!value.isArray() || value.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
out->reserve(value.size());
|
||||
for (Json::ArrayIndex i = 0; i < value.size(); ++i) {
|
||||
T v;
|
||||
if (!parser(value[i], &v)) {
|
||||
out->clear();
|
||||
return false;
|
||||
}
|
||||
out->push_back(v);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool TryParseIntArray(const Json::Value& value, std::vector<int>* out) {
|
||||
return TryParseArray<int>(value, TryParseInt, out);
|
||||
}
|
||||
|
||||
inline bool TryParseUintArray(const Json::Value& value,
|
||||
std::vector<uint32_t>* out) {
|
||||
return TryParseArray<uint32_t>(value, TryParseUint, out);
|
||||
}
|
||||
|
||||
inline bool TryParseStringArray(const Json::Value& value,
|
||||
std::vector<std::string>* out) {
|
||||
return TryParseArray<std::string>(value, TryParseString, out);
|
||||
}
|
||||
|
||||
inline bool TryParseNestedStringArray(
|
||||
const Json::Value& value,
|
||||
std::vector<std::vector<std::string>>* out) {
|
||||
return TryParseArray<std::vector<std::string>>(value, TryParseStringArray,
|
||||
out);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Json::Value PrimitiveVectorToJson(const std::vector<T>& vec) {
|
||||
Json::Value array(Json::ValueType::arrayValue);
|
||||
array.resize(vec.size());
|
||||
|
||||
for (Json::Value::ArrayIndex i = 0; i < vec.size(); ++i) {
|
||||
array[i] = Json::Value(vec[i]);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
inline Json::Value NestedStringArrayToJson(
|
||||
const std::vector<std::vector<std::string>>& vec) {
|
||||
Json::Value array(Json::ValueType::arrayValue);
|
||||
array.resize(vec.size());
|
||||
|
||||
for (Json::Value::ArrayIndex i = 0; i < vec.size(); ++i) {
|
||||
array[i] = PrimitiveVectorToJson(vec[i]);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
inline bool Contains(const Json::Value& array, std::string_view value) {
|
||||
if (!array.isArray()) {
|
||||
return false;
|
||||
}
|
||||
for (const Json::Value& entry : array) {
|
||||
if (entry.isString() && entry.asString() == value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // namespace openscreen::json
|
||||
|
||||
#endif // UTIL_JSON_JSON_HELPERS_H_
|
||||
62
breadcast-caststream-sys/vendor/openscreen/util/json/json_serialization.cc
vendored
Normal file
62
breadcast-caststream-sys/vendor/openscreen/util/json/json_serialization.cc
vendored
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
// 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.
|
||||
|
||||
#include "util/json/json_serialization.h"
|
||||
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "json/reader.h"
|
||||
#include "json/writer.h"
|
||||
#include "platform/base/error.h"
|
||||
#include "util/osp_logging.h"
|
||||
|
||||
namespace openscreen::json {
|
||||
|
||||
ErrorOr<Json::Value> Parse(std::string_view document) {
|
||||
Json::CharReaderBuilder builder;
|
||||
Json::CharReaderBuilder::strictMode(&builder.settings_);
|
||||
if (document.empty()) {
|
||||
return ErrorOr<Json::Value>(Error::Code::kJsonParseError, "empty document");
|
||||
}
|
||||
|
||||
Json::Value root_node;
|
||||
std::string error_msg;
|
||||
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
|
||||
const bool succeeded = reader->parse(&*document.begin(), &*document.end(),
|
||||
&root_node, &error_msg);
|
||||
if (!succeeded) {
|
||||
return ErrorOr<Json::Value>(Error::Code::kJsonParseError, error_msg);
|
||||
}
|
||||
|
||||
return root_node;
|
||||
}
|
||||
|
||||
ErrorOr<std::string> Stringify(const Json::Value& value) {
|
||||
Json::StreamWriterBuilder factory;
|
||||
#ifndef _DEBUG
|
||||
// Default is to "pretty print" the output JSON in a human readable
|
||||
// format. On non-debug builds, we can remove pretty printing by simply
|
||||
// getting rid of all indentation.
|
||||
factory["indentation"] = "";
|
||||
#endif
|
||||
|
||||
std::unique_ptr<Json::StreamWriter> const writer(factory.newStreamWriter());
|
||||
std::ostringstream stream;
|
||||
writer->write(value, &stream);
|
||||
|
||||
if (!stream) {
|
||||
// Note: jsoncpp doesn't give us more information about what actually
|
||||
// went wrong, just says to "check the stream". However, failures on
|
||||
// the stream should be rare, as we do not throw any errors in the jsoncpp
|
||||
// library.
|
||||
return ErrorOr<std::string>(Error::Code::kJsonWriteError, "Invalid stream");
|
||||
}
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
} // namespace openscreen::json
|
||||
24
breadcast-caststream-sys/vendor/openscreen/util/json/json_serialization.h
vendored
Normal file
24
breadcast-caststream-sys/vendor/openscreen/util/json/json_serialization.h
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
// 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_JSON_JSON_SERIALIZATION_H_
|
||||
#define UTIL_JSON_JSON_SERIALIZATION_H_
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "json/value.h"
|
||||
#include "platform/base/error.h"
|
||||
|
||||
namespace openscreen {
|
||||
|
||||
namespace json {
|
||||
|
||||
ErrorOr<Json::Value> Parse(std::string_view value);
|
||||
ErrorOr<std::string> Stringify(const Json::Value& value);
|
||||
|
||||
} // namespace json
|
||||
} // namespace openscreen
|
||||
|
||||
#endif // UTIL_JSON_JSON_SERIALIZATION_H_
|
||||
43
breadcast-caststream-sys/vendor/openscreen/util/json/json_value.cc
vendored
Normal file
43
breadcast-caststream-sys/vendor/openscreen/util/json/json_value.cc
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
// 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.
|
||||
|
||||
#include "util/json/json_value.h"
|
||||
|
||||
namespace openscreen {
|
||||
|
||||
std::optional<int> MaybeGetInt(const Json::Value& message,
|
||||
const char* first,
|
||||
const char* last) {
|
||||
const Json::Value* value = message.find(first, last);
|
||||
std::optional<int> result;
|
||||
if (value && value->isInt()) {
|
||||
result = value->asInt();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<std::string_view> MaybeGetString(const Json::Value& message) {
|
||||
if (message.isString()) {
|
||||
const char* begin = nullptr;
|
||||
const char* end = nullptr;
|
||||
message.getString(&begin, &end);
|
||||
if (begin && end >= begin) {
|
||||
return std::string_view(begin, end - begin);
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string_view> MaybeGetString(const Json::Value& message,
|
||||
const char* first,
|
||||
const char* last) {
|
||||
const Json::Value* value = message.find(first, last);
|
||||
std::optional<std::string_view> result;
|
||||
if (value && value->isString()) {
|
||||
return MaybeGetString(*value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace openscreen
|
||||
29
breadcast-caststream-sys/vendor/openscreen/util/json/json_value.h
vendored
Normal file
29
breadcast-caststream-sys/vendor/openscreen/util/json/json_value.h
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// 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_JSON_JSON_VALUE_H_
|
||||
#define UTIL_JSON_JSON_VALUE_H_
|
||||
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include "json/value.h"
|
||||
|
||||
#define JSON_EXPAND_FIND_CONSTANT_ARGS(s) (s), ((s) + sizeof(s) - 1)
|
||||
|
||||
namespace openscreen {
|
||||
|
||||
std::optional<int> MaybeGetInt(const Json::Value& message,
|
||||
const char* first,
|
||||
const char* last);
|
||||
|
||||
std::optional<std::string_view> MaybeGetString(const Json::Value& message);
|
||||
|
||||
std::optional<std::string_view> MaybeGetString(const Json::Value& message,
|
||||
const char* first,
|
||||
const char* last);
|
||||
|
||||
} // namespace openscreen
|
||||
|
||||
#endif // UTIL_JSON_JSON_VALUE_H_
|
||||
Loading…
Add table
Add a link
Reference in a new issue