core_sonos.h
Rama main · réplica de lectura del repositorio original.
#include "consumption_diagnostic.h"
#pragma once
#include "third_party/yeney-core/core/player.h"
#include "pcm_pack.h"
#include <atomic>
#include <condition_variable>
#include <deque>
#include <mutex>
#include <thread>
#include <vector>
#include <memory>
namespace timing_probe { class TimingTap; }
// Only the feeder may wait for an HTTP consumer. Event-loop callbacks only
// copy bounded batches and update state under short-lived locks.
class CoreSonosSink : public yeney::Sink {
struct Batch {
std::vector<char> pcm;
uint64_t first, generation;
size_t frames;
bool boundary, continuous;
unsigned rate;
uint64_t shmGeneration=0, shmFirst=0;
bool exported=false;
};
static constexpr size_t capacity = 48000;
mutable std::mutex mutex;
std::condition_variable changed;
std::deque<Batch> queue;
std::deque<bool> starts;
std::thread feeder;
std::atomic<bool> running{true};
std::atomic<uint64_t> generation{0};
PcmDither dither;
size_t staged = 0;
bool rateChangedStream = false;
mutable bool awaitingStream = false;
uint64_t heldPosition = 0, heldAt = 0;
unsigned heldRate = 0;
bool paused = false, pending = false, continuous = false, inflight = false;
unsigned rate = 44100, streamRate = 0;
uint64_t next = 0, streamBase = 0, handed = 0, started = 0;
mutable uint64_t audible = 0;
std::unique_ptr<timing_probe::TimingTap> tap;
uint64_t (*clockMs)();
void feed();
public:
explicit CoreSonosSink(const uint8_t* mac=nullptr, uint64_t (*clock)()=nullptr);
~CoreSonosSink() override;
void command(const yeney::Command&);
uint32_t maxSampleRate() const override;
unsigned outputRate() const { std::lock_guard<std::mutex> lock(mutex); return streamRate; }
void trackBoundary(uint64_t, const yeney::Format&, bool) override;
size_t write(const yeney::Frame*, size_t) override;
size_t write(const yeney::Frame*, size_t, bool processed) override;
void pause() override;
void resume() override;
void stop() override;
void flush() override;
uint64_t audibleFrames() const override;
uint64_t startedFrames() const override;
bool drained(uint64_t) const override;
bool outputEmpty(uint64_t) const override;
void volume(uint32_t, uint32_t) override {}
void power(bool) override {}
// Remote Sonos has no local pacer schedule. Keep the core's optional
// playTiming callback unset; encoder/HTTP handoff time is not audible time.
void contentPosition(uint64_t frame, bool known) override { bridge::consumptionContent().set(frame, known); }
bool paced() const override { return false; }
};
void runCoreClient(const char* server, const uint8_t* mac, const char* name);
extern "C" int sonos_output_running();