feat: enqueue streams in running player

This commit is contained in:
2026-10-22 16:03:56 +02:00
parent ac8e284d80
commit 6a4afc9f0e
17 changed files with 416 additions and 61 deletions
+59 -16
View File
@@ -289,32 +289,72 @@ void ProgressReporter::finish() noexcept {
if (writer.joinable()) writer.join();
}
ProgressReports::ProgressReports(const std::string &path)
: single(std::make_unique<ProgressReporter>(path)), active(single.get()) {}
ProgressReports::ProgressReports(const std::string &path) { setSingleFile(path); }
void ProgressReports::setSingleFile(const std::string &path) {
std::lock_guard lock(mutex);
if (single || !entries.empty()) throw std::runtime_error("Progress reporting is already initialized");
single = std::make_unique<ProgressReporter>(path);
singlePath = path;
active = single.get();
}
void ProgressReports::registerFile(const std::string &identity, const std::string &path) {
const char *identities[] = {identity.c_str()}, *destinations[] = {path.c_str()};
registerFiles(identities, destinations, 1);
}
void ProgressReports::registerFiles(const char *const *identities, const char *const *destinations, int count) {
std::lock_guard lock(mutex);
if (single || !ProgressReporter::validMediaIdentity(identity) || entries.size() >= 4096 ||
path.empty() || path.front() != '/' || path.find('\0') != std::string::npos ||
entries.contains(identity))
throw std::runtime_error("Invalid or duplicate queue progress registration");
if (!paths.insert(path).second) throw std::runtime_error("Duplicate queue progress report path");
entries.emplace(identity, Entry{path, nullptr});
if (!identities || !destinations || count < 1 || count > 4096 ||
entries.size() + count > 4096)
throw std::runtime_error("Queue reports exceed the 4096 retained registration limit");
std::map<std::string, Entry> additions;
std::set<std::string> newPaths;
for (int index = 0; index < count; ++index) {
const auto *identity = identities[index], *path = destinations[index];
if (!identity || !path || !ProgressReporter::validMediaIdentity(identity) ||
!*path || *path != '/' || singleIdentity == identity || entries.contains(identity) || additions.contains(identity))
throw std::runtime_error("Invalid or conflicting queue progress identity");
if (singlePath == path || paths.contains(path) || !newPaths.insert(path).second)
throw std::runtime_error("Conflicting queue progress report path");
additions.emplace(identity, Entry{path, nullptr});
}
// Node merges allocate nothing: failed validation cannot partially register a batch.
entries.merge(additions);
paths.merge(newPaths);
}
void ProgressReports::observe(const PlayerState &state) noexcept {
std::lock_guard lock(mutex);
if (active) active->observe(state);
}
void ProgressReports::closeMedia() noexcept {
std::lock_guard lock(mutex);
if (active) active->closeMedia();
}
void ProgressReports::begin(const std::string &identity, bool live) noexcept {
std::lock_guard lock(mutex);
if (single) { active->begin(identity, live); return; }
const auto found = entries.find(identity);
auto *next = found == entries.end() ? nullptr : found->second.reporter.get();
const bool useSingle = found == entries.end() && single &&
(entries.empty() || singleIdentity.empty() || singleIdentity == identity);
auto *next = found == entries.end() ? (useSingle ? single.get() : nullptr) : found->second.reporter.get();
if (active != next && active) active->finish();
active = nullptr; // A failed or unregistered identity must never reuse the old report.
if (found == entries.end()) return;
if (found == entries.end() && !useSingle) return;
try {
auto &entry = found->second;
if (!entry.reporter) entry.reporter = std::make_unique<ProgressReporter>(entry.path);
else entry.reporter->resume();
active = entry.reporter.get();
if (found == entries.end()) {
singleIdentity = identity;
single->resume();
active = single.get();
} else {
auto &entry = found->second;
if (!entry.reporter) entry.reporter = std::make_unique<ProgressReporter>(entry.path);
else entry.reporter->resume();
active = entry.reporter.get();
}
active->begin(identity, live);
} catch (const std::exception &exception) {
try { if (failure.empty()) failure = exception.what(); } catch (...) {}
@@ -331,7 +371,10 @@ void ProgressReports::finish() noexcept {
std::string ProgressReports::error() const {
std::lock_guard lock(mutex);
if (!failure.empty()) return failure;
if (single) return single->error();
if (single) {
const auto message = single->error();
if (!message.empty()) return message;
}
for (const auto &[identity, entry] : entries) {
if (entry.reporter) {
auto message = entry.reporter->error();