feat(capture): add Phase 3 PipeWire capture stub and config string safety
Add a linkable CaptureFactory stub so the capture API can be consumed without unresolved symbols. Change remaining config string_view fields to std::string to prevent dangling references. Update handoff memory.
This commit is contained in:
+12
-4
@@ -4,10 +4,16 @@ Last updated: initial scaffold.
|
||||
|
||||
## Project state
|
||||
|
||||
- Phase 1 complete: Meson build, C++20 module headers, agent workflow, clock
|
||||
utility, and a passing unit test.
|
||||
- Active phase is now Phase 2 (software H.264 encode/decode).
|
||||
- No implementation yet for capture/codec/network/render.
|
||||
- Phase 2 follow-up review items addressed: encoder rate control is now
|
||||
bitrate-only (removed CRF), encoder/decoder cache their SwsContext, configs
|
||||
own their strings, bitstream helper is internal to the encoder with a NAL
|
||||
length guard, FFmpeg open errors are reported, and the round-trip test
|
||||
verifies Annex-B prefix and keyframes.
|
||||
- `.clang-format` added at repo root and clang-format enforcement added to
|
||||
`AGENTS.md` and `cpp-meson-build/SKILL.md`.
|
||||
- Phase 3 capture now has a linkable stub (`src/capture/pipewire_capture.cpp`)
|
||||
so the API can be consumed without an unresolved symbol.
|
||||
- Remaining implementation: capture, transport, rendering, and CLI/pipeline glue.
|
||||
|
||||
## Decisions
|
||||
|
||||
@@ -16,6 +22,8 @@ Last updated: initial scaffold.
|
||||
- Build system: Meson.
|
||||
- Capture: PipeWire + xdg-desktop-portal.
|
||||
- Encode/Decode: FFmpeg (libavcodec, libavutil, libswscale).
|
||||
- H.264 encoder path: software `libx264`, low-latency settings, Annex-B output.
|
||||
- Decoder receives SPS/PPS through `DecoderConfig.extradata`.
|
||||
- Transport: RTP over UDP; signaling via WebSocket/JSON.
|
||||
- Discovery: mDNS/Avahi.
|
||||
- Rendering: SDL2 or SDL3 + OpenGL.
|
||||
|
||||
@@ -3,26 +3,31 @@
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace sc {
|
||||
|
||||
enum class PixelFormat {
|
||||
Rgba,
|
||||
Yuv420p,
|
||||
};
|
||||
|
||||
// Opaque resource owned by the capture implementation.
|
||||
struct CapturedFrame {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
uint64_t timestamp_ns = 0; // capture clock, monotonic
|
||||
PixelFormat pixel_format = PixelFormat::Rgba;
|
||||
int stride = 0; // bytes per row for the first plane; 0 means packed
|
||||
std::vector<std::byte> pixels;
|
||||
// TODO: pixel format, stride, FD handle for dmabuf
|
||||
};
|
||||
|
||||
// Capture target: whole monitor, specific window, or a region.
|
||||
struct CaptureTargetWholeScreen {};
|
||||
struct CaptureTargetWindow {
|
||||
std::string_view window_id;
|
||||
std::string window_id;
|
||||
};
|
||||
struct CaptureTargetRegion {
|
||||
int x = 0;
|
||||
@@ -31,13 +36,10 @@ struct CaptureTargetRegion {
|
||||
int height = 0;
|
||||
};
|
||||
|
||||
using CaptureTarget = std::variant<
|
||||
CaptureTargetWholeScreen,
|
||||
CaptureTargetWindow,
|
||||
CaptureTargetRegion>;
|
||||
using CaptureTarget = std::variant<CaptureTargetWholeScreen, CaptureTargetWindow, CaptureTargetRegion>;
|
||||
|
||||
class CaptureSession {
|
||||
public:
|
||||
public:
|
||||
virtual ~CaptureSession() = default;
|
||||
|
||||
// Blocking call to acquire one frame. Returns std::nullopt on graceful stop.
|
||||
@@ -49,7 +51,7 @@ public:
|
||||
|
||||
// Factory for the PipeWire / xdg-desktop-portal capture backend.
|
||||
class CaptureFactory {
|
||||
public:
|
||||
public:
|
||||
static std::unique_ptr<CaptureSession> create(CaptureTarget target);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,36 +2,34 @@
|
||||
|
||||
#include "screencast/codec/decoder.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
|
||||
namespace sc {
|
||||
|
||||
struct RendererConfig {
|
||||
std::string_view window_title = "screencast receiver";
|
||||
std::string window_title = "screencast receiver";
|
||||
int initial_width = 1280;
|
||||
int initial_height = 720;
|
||||
};
|
||||
|
||||
class Renderer {
|
||||
public:
|
||||
virtual ~Renderer () = default;
|
||||
virtual ~Renderer() = default;
|
||||
|
||||
// Present one decoded frame. Returns false if the window was closed.
|
||||
virtual bool present ( const DecodedFrame &frame ) = 0;
|
||||
virtual bool present(const DecodedFrame& frame) = 0;
|
||||
|
||||
// Pump events (window close, resize). Non-blocking.
|
||||
virtual bool poll_events () = 0;
|
||||
virtual bool poll_events() = 0;
|
||||
|
||||
// Destroy the window and release GPU resources.
|
||||
virtual void shutdown () = 0;
|
||||
virtual void shutdown() = 0;
|
||||
};
|
||||
|
||||
class RendererFactory {
|
||||
public:
|
||||
static std::unique_ptr<Renderer> create ( const RendererConfig &config );
|
||||
static std::unique_ptr<Renderer> create(const RendererConfig& config);
|
||||
};
|
||||
|
||||
} // namespace sc
|
||||
} // namespace sc
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# Phase 3 capture placeholder. libpipewire-0.3 will be added when the portal
|
||||
# implementation is wired in.
|
||||
|
||||
sc_capture_sources = files('pipewire_capture.cpp')
|
||||
|
||||
# Built even though nothing links it yet, so the stub stays compile-clean.
|
||||
static_library('sc_capture',
|
||||
sc_capture_sources,
|
||||
include_directories : sc_core_inc)
|
||||
|
||||
# sc_capture_dep will be declared once the app/pipeline code links against it.
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "screencast/capture/capture.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace sc {
|
||||
|
||||
std::unique_ptr<CaptureSession> CaptureFactory::create(CaptureTarget /*target*/) {
|
||||
// Phase 3 placeholder. The PipeWire / xdg-desktop-portal capture backend
|
||||
// will be implemented here once the codec round-trip is solidified.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace sc
|
||||
@@ -1,6 +1,8 @@
|
||||
# Core public headers / include dependency.
|
||||
sc_core_inc = include_directories('../include')
|
||||
|
||||
subdir('capture')
|
||||
|
||||
# Phase 2 codec dependencies.
|
||||
dep_avcodec = dependency('libavcodec')
|
||||
dep_avutil = dependency('libavutil')
|
||||
|
||||
Reference in New Issue
Block a user