// src/Services/AudioStreamService.h #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include "WhipClient.h" #include "ConfigService.h" #include "Security/TlsKeyUtil.h" namespace snoop { class AudioStreamService { std::shared_ptr m_cfg; // WHIP std::unique_ptr m_whip; std::mutex m_whipMutex; public: explicit AudioStreamService(std::shared_ptr cfg) : m_cfg(std::move(cfg)) {} ~AudioStreamService() { StopWhip(); } // Feed encoded Opus; frames = PCM samples per channel represented by this Opus frame void OnOpus(const unsigned char *opusData, size_t opusBytes, int pcmFramesPerChannel) { std::lock_guard lk(m_whipMutex); if (m_whip) { m_whip->PushOpus(opusData, opusBytes, pcmFramesPerChannel); } } bool StartWhip(const std::string &whipUrl, int sampleRate = 48000, int channels = 1) { std::lock_guard lk(m_whipMutex); if (m_whip) { spdlog::info("WHIP already started"); return true; } if (!m_cfg) { spdlog::error("StartWhip requires ConfigService"); return false; } // certs from enrollment std::filesystem::path ca = "/etc/iot/keys/issuing_ca.pem"; if (!std::filesystem::exists(ca)) { ca = "/etc/iot/keys/ca_chain.pem"; } std::filesystem::path crt = "/etc/iot/keys/device.crt.pem"; WhipClient::Params p{ .whipUrl = whipUrl, // may include ?token=..., client will normalize path and set Bearer .caPath = ca.string(), .crtPath = crt.string(), .sampleRate = sampleRate, .channels = channels}; m_whip = std::make_unique(p); try { m_whip->Start(); spdlog::info("WHIP started"); return true; } catch (const std::exception &e) { spdlog::error("WHIP start failed: {}", e.what()); m_whip.reset(); return false; } } void StopWhip() { std::lock_guard lk(m_whipMutex); if (m_whip) { m_whip->Stop(); m_whip.reset(); } } }; } // namespace snoop