feat: camera via webrtc

This commit is contained in:
Ian Chua
2026-09-01 18:59:20 +08:00
parent d9d9678a7e
commit 4320cc78d9
24 changed files with 1505 additions and 8 deletions
+96
View File
@@ -0,0 +1,96 @@
#pragma once
#include "IMediaController.hpp"
#include "WebRtcFrameAssembler.hpp"
#include <wx/image.h>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <cstdint>
#include <deque>
#include <functional>
#include <memory>
#include <mutex>
#include <thread>
#include <vector>
namespace rtc {
class DataChannel;
class PeerConnection;
class Track;
}
namespace Slic3r { namespace GUI {
class WebRtcMediaController : public IMediaController {
public:
struct Status {
enum Kind { Connecting, Playing, Stopped, Failed } kind = Stopped;
enum Code {
ICE_FAILED,
SIGNALING_CLOSED,
UNAVAILABLE_BUSY,
UNAVAILABLE_ERROR,
UNAVAILABLE_DISABLED,
DECODE_ERROR,
TIMEOUT,
} code = ICE_FAILED;
// Identifies the StartSession attempt this status belongs to, so the
// consumer can drop CallAfter-queued events from a superseded attempt.
std::uint64_t epoch = 0;
};
WebRtcMediaController(std::function<void(const wxImage&, wxSize)> frame_sink,
std::function<void(Status)> on_status);
~WebRtcMediaController() override;
void StartSession(std::unique_ptr<ICameraSignalingChannel> channel) override;
void StopSession() override;
std::uint64_t epoch() const { return m_epoch.load(); }
bool is_active() const { return m_alive.load(); }
void Load(wxURI) override {}
void Play() override {}
void Stop() override { StopSession(); }
wxMediaState GetState() override;
wxSize GetVideoSize() const override;
private:
void teardown(bool notify);
void report(Status status);
void bind_data_channel(const std::shared_ptr<rtc::DataChannel>& dc);
void on_ready(std::vector<CameraIceServer> servers);
void on_answer(std::string sdp);
void on_ice(std::string candidate, std::string mid);
void on_unavailable(CameraUnavailableReason reason, std::string detail);
void decode_loop();
void enqueue_chunk(std::vector<std::byte> chunk);
void enqueue_nal(std::vector<std::byte> nal);
void deliver_jpeg(std::vector<std::byte> jpeg);
mutable std::mutex m_mutex;
std::condition_variable m_cond;
std::deque<std::vector<std::byte>> m_chunk_queue;
std::deque<std::vector<std::byte>> m_nal_queue;
// Remote candidates can arrive before the answer; libdatachannel rejects
// addRemoteCandidate until a remote description is set, so buffer them.
std::vector<std::pair<std::string, std::string>> m_pending_candidates;
bool m_remote_description_set = false;
std::unique_ptr<ICameraSignalingChannel> m_signaling;
std::shared_ptr<rtc::PeerConnection> m_peer_connection;
std::shared_ptr<rtc::DataChannel> m_data_channel;
std::shared_ptr<rtc::Track> m_video_track;
std::thread m_decode_thread;
std::atomic<bool> m_alive{false};
std::atomic<std::uint64_t> m_epoch{0};
wxMediaState m_state = static_cast<wxMediaState>(3);
wxSize m_video_size = wxDefaultSize;
std::function<void(const wxImage&, wxSize)> m_frame_sink;
std::function<void(Status)> m_on_status;
bool m_has_frame = false;
std::chrono::steady_clock::time_point m_last_frame_time{};
};
}} // namespace Slic3r::GUI