scar-chat7/client/media/screen_capture.h

117 lines
3.5 KiB
C
Raw Normal View History

2025-12-07 12:00:44 -07:00
#pragma once
#include <memory>
#include <functional>
#include <vector>
#include <string>
#include <map>
#include <cstdint>
2025-12-07 12:00:44 -07:00
#ifdef __linux__
#include <spa/utils/hook.h>
#include <spa/pod/pod.h>
#endif
// Forward declarations for Pipewire/sdbus to avoid header pollution
struct pw_thread_loop;
struct pw_stream;
struct pw_context;
namespace sdbus {
class IConnection;
class IProxy;
class Variant;
}
2025-12-07 12:00:44 -07:00
namespace scar {
class VideoEncoder; // Forward declaration
2025-12-07 12:00:44 -07:00
enum class ScreenCaptureBackend {
FFMPEG_X11, // X11 via ffmpeg
FFMPEG_WAYLAND, // Wayland via ffmpeg + portal
FFMPEG_WINDOWS, // Windows GDI via ffmpeg
PORTAL_PIPEWIRE // xdg-desktop-portal + Pipewire (Wayland/Hyprland)
};
// Screen capture supporting multiple backends
class ScreenCapture {
public:
ScreenCapture();
~ScreenCapture();
// Auto-detect and start screen capture
bool start();
// Start with specific backend
bool start(ScreenCaptureBackend backend);
// Stop capturing
void stop();
// Check if currently capturing
bool isCapturing() const { return capturing_; }
// Set frame callback
using FrameCallback = std::function<void(const std::vector<uint8_t>& frameData, int width, int height)>;
void setFrameCallback(FrameCallback callback);
private:
ScreenCaptureBackend detectBestBackend();
bool startX11Capture();
bool startWaylandCapture();
bool startWindowsCapture();
bool startPortalCapture();
// sdbus Portal methods
bool initPortalConnection();
void cleanupPortalConnection();
std::string createPortalSession();
bool selectPortalSources(const std::string& session_handle);
bool startPortalSession(const std::string& session_handle, uint32_t& node_id);
int openPipeWireRemote(const std::string& session_handle);
uint32_t getStreamsNodeId(const std::string& session_handle);
// Portal async response handlers
void onCreateSessionResponse(uint32_t response, const std::map<std::string, sdbus::Variant>& results);
void onSelectSourcesResponse(uint32_t response, const std::map<std::string, sdbus::Variant>& results);
void onStartSessionResponse(uint32_t response, const std::map<std::string, sdbus::Variant>& results);
void setupResponseSignal(const std::string& request_handle,
std::function<void(uint32_t, const std::map<std::string, sdbus::Variant>&)> callback);
// Pipewire methods
bool initPipewire(int fd, uint32_t node_id);
void cleanupPipewire();
static void onStreamProcess(void* userdata);
static void onStreamParamChanged(void* userdata, uint32_t id, const struct spa_pod* param);
2025-12-07 12:00:44 -07:00
bool capturing_;
ScreenCaptureBackend backend_;
FrameCallback frameCallback_;
// sdbus members
std::shared_ptr<sdbus::IConnection> portal_connection_;
std::unique_ptr<sdbus::IProxy> screencast_proxy_;
std::vector<std::unique_ptr<sdbus::IProxy>> request_proxies_; // Keep request proxies alive for signal handlers
std::string session_path_;
std::string pending_request_path_;
bool portal_session_ready_;
bool portal_sources_selected_;
bool portal_started_;
// Pipewire members
pw_thread_loop* pw_loop_;
pw_stream* pw_stream_;
pw_context* pw_context_;
spa_hook stream_listener_;
int frame_width_;
int frame_height_;
// Video encoder
std::unique_ptr<VideoEncoder> encoder_;
2025-12-07 12:00:44 -07:00
};
} // namespace scar