Ctrl K
ComunidadGuías…

queue_room_internal.h

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
#pragma once
#include "core_queue_sink.h"
#include "lms_icon.h"
#include "queue_path_sink.h"
#include "queue_playlist.h"
#include "queue_protocol.h"
#include "queue_room.h"
#include "queue_lms_commands.h"
#include "queue_route.h"
#include "queue_lazy_item.h"
#include "queue_source.h"
#include "queue_speaker_changes.h"
#include "queue_window_edits.h"
#include "transport_source.h"
#include "stream_metrics.h"
#include "stream_session.h"
#include "upnp/queue_coordinator.h"
#include "upnp/queue_metadata.h"
#include <condition_variable>
#include <csignal>
#include <ctime>
#include <deque>
#include <set>
#include <thread>
extern "C" void yeney_queue_stream_handoff();
namespace bridge {
namespace queue_room_detail {
extern std::atomic_bool roomStop;
void stopRoom(int);
void logQueue(const std::string &);
unsigned pollSetting();
} // namespace queue_room_detail
using namespace queue_room_detail;
struct TrackFault : std::runtime_error { using std::runtime_error::runtime_error; };
struct QueueRoom::Impl {
    upnp::Speaker room;
    std::string host, mac, session;
    std::function<std::string()> streamUrl;
    std::shared_ptr<upnp::OwnSpeakerControl> speaker;
    queue_audio::CoreQueueSink queue;
    QueuePathSink paths;
    std::atomic_bool selectionDeferred{false};
    std::atomic<int> metadataIndex{-1};
    int selectedIndex = -1;
    bool selecting = false, selectionKnown = false, selectedLocal = true, stoppedPlay = true;
    bool selectedNewPlay = true;
    yeney::Command selectedCommand{'s', 0, false}, requestedCommand{'s', 0, false};
    std::atomic<uint64_t> selectionRequest{0};
    uint64_t selectedRequest = 0;
    queue_audio::PlaylistEntry selectedTrack;
    int selectedSourceIndex = -1;
    std::map<uint64_t, queue_audio::PlaylistEntry> sourceTracks;
    std::map<uint64_t, unsigned> sourceIndices;
    std::map<uint64_t, uint64_t> sourceTokens;
    uint64_t selectedToken = 0;
    queue_audio::QueueWindow window;
    queue_audio::QueueWindow::Policy publishedWindowPolicy;
    bool windowDirty = false;
    bool refreshUpcoming = false;
    std::chrono::steady_clock::time_point nextWindowPoll{};
    std::map<std::string, yeney::FlacSourceInfo> flacCounts;
    std::chrono::steady_clock::time_point handoffAt{};
    std::string pathReason = "local-source";
    queue_audio::Route route;
    upnp::QueueCoordinator coordinator;
    upnp::QueueActions actions;
    std::set<std::string> owned;
    upnp::QueueOwnership ownership;
    RoomStatus status;
    std::shared_ptr<SpeakerChanges> speakerChanges = std::make_shared<SpeakerChanges>();
    std::string publishedState, publishedUri;
    struct Entry {
        uint64_t stream;
        std::string uri;
        std::shared_ptr<queue_audio::Item> item;
        std::string sourceUrl;
        uint64_t frames;
        std::string title;
        uint64_t owner = 0, segmentOffset = 0;
        std::string didl;
        unsigned originMs = 0;
    };
    std::vector<Entry> items;
    bool lmsNavigation = false;
    bool navigationSelecting = false;
    unsigned navigationFrom = 0;
    uint64_t queueEdits = 0, navigationEdits = 0;
    std::optional<unsigned> publishedWindowIndex;
    uint64_t navigationFirst = 0;
    uint64_t navigationToken = 0;
    std::optional<Entry> navigationItem;
    std::chrono::steady_clock::time_point navigationDeadline{};
    std::map<uint64_t, std::shared_ptr<queue_audio::LazyItem>> lazyItems;
    // Worker-owned selections are independent for each LMS index.
    struct Selection {
        enum Phase { Queued, Producing, Published } phase;
        uint64_t token, first;
        std::optional<uint64_t> observed{};
        std::optional<uint64_t> rewindMs{};
    };
    std::map<unsigned, Selection> selections;
    std::deque<unsigned> selectionOrder;
    void advanceSelections();
    void selectionPublished(unsigned index);
    uint64_t lazySerial = 0;
    void arbitrateSelection(uint64_t token, const std::string &reason);
    bool acceptSnapshot(queue_audio::PlaylistSnapshot &snapshot);
    void activateLazy(uint64_t token);
    std::map<uint64_t, std::string> replacementReasons;
    std::map<uint64_t, unsigned> speakerNavCommands;
    std::map<uint64_t, uint64_t> speakerNavFirst;

    std::map<uint64_t, bool> remote;
    std::mutex mutex, sourceMutex;
    std::condition_variable changed;
    std::deque<std::pair<char, uint64_t>> commands;
    std::thread worker;
    bool roomStopped = false;
    std::deque<std::chrono::steady_clock::time_point> trackFaults;
    void roomFault(const std::string &reason, bool member = false);
    bool closing = false, started = false, faulted = false, releasing = false;
    uint64_t firstStream = 1, current = 0;
    queue_audio::PlaylistSnapshot playlist;
    queue_audio::PlaylistReader reader;
    std::string state = "STOPPED", fault, transportStatus;
    std::chrono::steady_clock::time_point playAt{};
    bool waitingForPlay = false;
    bool queueSelectedForAdmission = false;
    std::string takeoverSource = "unknown";
    bool externalReleased = false, stopAcknowledged = false, sourceAway = false;
    std::atomic<int64_t> resumeEchoUntil{0};
    std::atomic_bool resumeFlush{false}, finitePause{false};
    struct PodcastFields { std::string title, artist, album; bool complete = false; };
    std::map<std::string, PodcastFields> plainRemoteTitles;
    std::chrono::steady_clock::time_point deferredStop{};
    uint64_t deferredNext = 0;
    queue_audio::PlaylistSnapshot resumeSnapshot;
    bool pausedFlushRetained = false, remoteNaturalSuccessor = false;
    static int64_t milliseconds();
    std::string expectedState = "STOPPED";
    std::chrono::steady_clock::time_point ownUntil{};
    uint64_t lastElapsed = 0;
    std::chrono::steady_clock::time_point lastPositionAt{};
    // The only outgoing CLI mutation path: called solely from speaker evidence.
    void speakerCommand(const std::string &command, const std::string &reason);
    void externalStop(const std::string &reason);
    unsigned pollMs = pollSetting();
    std::chrono::steady_clock::time_point nextPoll{}, fastUntil{};
    std::shared_ptr<std::atomic_bool> eventDirty = std::make_shared<std::atomic_bool>(false);
    void fastPoll();
    upnp::QueueOwnershipIO ownershipIO();
    Impl(upnp::Speaker r, std::shared_ptr<upnp::StreamServer> http,
         std::shared_ptr<upnp::OwnSpeakerControl> s, std::string h, std::string m, const uint8_t *rawMac,
         std::function<std::string()> url);
    std::optional<yeney::FlacSourceInfo> sourceInfo(uint64_t occurrence,
                                                    std::optional<yeney::FlacSourceInfo> info);
    void relayEvents(bool enabled);
    void received(const yeney::Command &c);
    void beginSelection(uint64_t request);
    void selectPath();
    void handoffComplete();
    void publish();
    void release(bool stop = false);
    void releaseWindowParent(uint64_t token, uint64_t before = UINT64_MAX);
    void command(char c, uint64_t next);
    void append();
    void trimHistory();
    void reconcileWindow();
    void observe();
    void run();
    void shutdown();
    ~Impl();
};
} // namespace bridge