C++ API
All types are in the nozzle namespace. Include the umbrella header:
#include <nozzle/nozzle.hpp>
Error Handling
Result<T>
All fallible operations return Result<T>. Check ok() before accessing the value.
auto result = nozzle::sender::create(desc);
if (!result.ok()) {
auto &err = result.error();
// err.code -> ErrorCode
// err.message -> std::string
return;
}
auto sender = std::move(result).value();
Result<void>
Operations that don’t return a value on success use Result<void>:
auto result = sender.commit_frame(frame);
if (!result.ok()) { /* handle error */ }
ErrorCode
| Value | Description |
|---|---|
Ok |
Success |
Unknown |
Unclassified error |
InvalidArgument |
Bad parameter |
UnsupportedBackend |
Backend not available on this platform |
UnsupportedFormat |
Format not supported by device |
DeviceMismatch |
Texture from wrong device |
ResourceCreationFailed |
GPU resource allocation failed |
SharedHandleFailed |
Cross-process handle sharing failed |
SenderNotFound |
No sender with given name |
SenderClosed |
Sender no longer alive |
Timeout |
Operation timed out |
BackendError |
Platform-specific GPU error |
Types
Enums
backend_type
enum class backend_type {
unknown,
d3d11,
metal,
opengl,
dma_buf,
};
texture_format
enum class texture_format {
unknown,
r8_unorm,
rg8_unorm,
rgba8_unorm,
bgra8_unorm,
rgba8_srgb,
bgra8_srgb,
r16_unorm,
rg16_unorm,
rgba16_unorm,
r16_float,
rg16_float,
rgba16_float,
r32_float,
rg32_float,
rgba32_float,
r32_uint,
rgba32_uint,
depth32_float,
};
transfer_mode
enum class transfer_mode {
unknown,
zero_copy_shared_texture,
gpu_copy,
cpu_copy,
};
sync_mode
enum class sync_mode {
none,
access_guarded,
gpu_fence_best_effort,
};
receive_mode
enum class receive_mode {
latest_only,
sequential_best_effort,
};
frame_status
enum class frame_status {
new_frame,
no_new_frame,
dropped_frames,
sender_closed,
error,
};
texture_usage
Bitfield. Combine with operator|.
enum class texture_usage : uint32_t {
none = 0,
shader_read = 1 << 0,
shader_write = 1 << 1,
render_target = 1 << 2,
shared = 1 << 3,
};
Structs
frame_info
struct frame_info {
uint64_t frame_index{0};
uint64_t timestamp_ns{0};
uint32_t width{0};
uint32_t height{0};
texture_format format{texture_format::unknown};
transfer_mode transfer_mode_val{transfer_mode::unknown};
sync_mode sync_mode_val{sync_mode::none};
uint32_t dropped_frame_count{0};
};
texture_desc
struct texture_desc {
uint32_t width{0};
uint32_t height{0};
texture_format format{texture_format::unknown};
texture_usage usage{texture_usage::shader_read | texture_usage::shared};
};
texture_layout
struct texture_layout {
uint32_t row_pitch_bytes{0};
uint32_t slice_pitch_bytes{0};
uint32_t alignment_bytes{0};
};
sender_info
struct sender_info {
std::string name{};
std::string application_name{};
std::string id{};
backend_type backend{backend_type::unknown};
};
connected_sender_info
struct connected_sender_info {
std::string name{};
std::string application_name{};
std::string id{};
backend_type backend{backend_type::unknown};
uint32_t width{0};
uint32_t height{0};
texture_format format{texture_format::unknown};
double estimated_fps{0.0};
uint64_t frame_counter{0};
uint64_t last_update_time_ns{0};
};
sender_desc
struct sender_desc {
std::string name{};
std::string application_name{};
uint32_t ring_buffer_size{3};
metadata_list metadata{};
bool allow_format_fallback{true};
};
receiver_desc
struct receiver_desc {
std::string name{};
std::string application_name{};
receive_mode receive_mode_val{receive_mode::latest_only};
};
acquire_desc
struct acquire_desc {
uint64_t timeout_ms{0};
};
key_value
struct key_value {
std::string key{};
std::string value{};
};
metadata_list is std::vector<key_value>.
sender
class sender {
public:
static Result<sender> create(const sender_desc &desc);
~sender();
sender();
sender(const sender &) = delete;
sender &operator=(const sender &) = delete;
sender(sender &&) noexcept;
sender &operator=(sender &&) noexcept;
Result<void> publish_external_texture(const texture &tex);
Result<writable_frame> acquire_writable_frame(const texture_desc &desc);
Result<void> commit_frame(writable_frame &frame);
sender_info info() const;
Result<void> set_metadata(const metadata_list &metadata);
bool valid() const;
private:
struct Impl;
std::unique_ptr<Impl> impl_;
};
sender::create
Creates a named sender with the given descriptor.
static Result<sender> create(const sender_desc &desc);
| Parameter | Type | Description |
|---|---|---|
desc |
const sender_desc & |
Sender configuration |
Returns: Result<sender> — the sender on success, error on failure.
publish_external_texture
Publish a user-owned texture (created via backend wrap_texture).
Result<void> publish_external_texture(const texture &tex);
| Parameter | Type | Description |
|---|---|---|
tex |
const texture & |
External texture to publish |
Returns: Result<void>
acquire_writable_frame
Acquire a nozzle-owned writable frame for GPU rendering.
Result<writable_frame> acquire_writable_frame(const texture_desc &desc);
| Parameter | Type | Description |
|---|---|---|
desc |
const texture_desc & |
Desired texture dimensions and format |
Returns: Result<writable_frame> — frame ready for writing on success.
commit_frame
Publish a writable frame to receivers.
Result<void> commit_frame(writable_frame &frame);
| Parameter | Type | Description |
|---|---|---|
frame |
writable_frame & |
Frame to commit |
Returns: Result<void>
info
Get sender metadata.
sender_info info() const;
Returns: sender_info
set_metadata
Update sender metadata visible to receivers.
Result<void> set_metadata(const metadata_list &metadata);
valid
Check if the sender is initialized.
bool valid() const;
receiver
class receiver {
public:
static Result<receiver> create(const receiver_desc &desc);
~receiver();
receiver();
receiver(const receiver &) = delete;
receiver &operator=(const receiver &) = delete;
receiver(receiver &&) noexcept;
receiver &operator=(receiver &&) noexcept;
Result<frame> acquire_frame();
Result<frame> acquire_frame(const acquire_desc &desc);
connected_sender_info connected_info() const;
metadata_list sender_metadata() const;
bool is_connected() const;
bool valid() const;
private:
struct Impl;
std::unique_ptr<Impl> impl_;
};
receiver::create
Creates a receiver that connects to a named sender.
static Result<receiver> create(const receiver_desc &desc);
| Parameter | Type | Description |
|---|---|---|
desc |
const receiver_desc & |
Receiver configuration. name matches the sender to connect to. |
Returns: Result<receiver>
acquire_frame
Acquire the latest frame from the connected sender.
Result<frame> acquire_frame();
Result<frame> acquire_frame(const acquire_desc &desc);
| Parameter | Type | Description |
|---|---|---|
desc |
const acquire_desc & |
Timeout configuration. timeout_ms = 0 for non-blocking. |
Returns: Result<frame> — acquired frame on success.
connected_info
Get information about the connected sender.
connected_sender_info connected_info() const;
Returns: connected_sender_info
sender_metadata
Get metadata from the connected sender.
metadata_list sender_metadata() const;
is_connected
Check if the receiver is currently connected to a live sender.
bool is_connected() const;
frame
class frame {
public:
frame();
~frame();
frame(const frame &) = delete;
frame &operator=(const frame &) = delete;
frame(frame &&) noexcept;
frame &operator=(frame &&) noexcept;
frame_info info() const;
const texture &get_texture() const;
bool valid() const;
Result<texture> clone_to_owned_texture(device &dev) const;
void release();
private:
struct Impl;
std::unique_ptr<Impl> impl_;
};
info
Get frame metadata (dimensions, format, frame index, timestamp).
frame_info info() const;
get_texture
Access the underlying shared texture.
const texture &get_texture() const;
clone_to_owned_texture
Create a GPU copy of the frame’s texture as an owned texture.
Result<texture> clone_to_owned_texture(device &dev) const;
release
Explicitly release the frame. Also called automatically on destruction.
void release();
writable_frame
class writable_frame {
public:
writable_frame() noexcept;
~writable_frame();
writable_frame(const writable_frame &) = delete;
writable_frame &operator=(const writable_frame &) = delete;
writable_frame(writable_frame &&) noexcept;
writable_frame &operator=(writable_frame &&) noexcept;
texture &get_texture();
const texture_desc &desc() const;
bool valid() const;
private:
struct Impl;
std::unique_ptr<Impl> impl_;
};
get_texture
Access the writable texture for GPU rendering.
texture &get_texture();
desc
Get the descriptor used to create this frame.
const texture_desc &desc() const;
texture
class texture {
public:
texture();
~texture();
texture(const texture &) = delete;
texture &operator=(const texture &) = delete;
texture(texture &&) noexcept;
texture &operator=(texture &&) noexcept;
const texture_desc &desc() const;
texture_layout layout() const;
bool valid() const;
private:
struct Impl;
std::unique_ptr<Impl> impl_;
};
desc
Get texture descriptor (dimensions, format, usage flags).
const texture_desc &desc() const;
layout
Get texture memory layout (row pitch, slice pitch, alignment).
texture_layout layout() const;
device
class device {
public:
static Result<device> default_device();
~device();
device();
device(const device &) = delete;
device &operator=(const device &) = delete;
device(device &&) noexcept;
device &operator=(device &&) noexcept;
bool supports_format(texture_format format, texture_usage usage) const;
bool supports_native_format(uint32_t native_format, texture_usage usage) const;
bool valid() const;
private:
struct Impl;
std::unique_ptr<Impl> impl_;
};
default_device
Get the system default GPU device.
static Result<device> default_device();
supports_format
Check if a nozzle format is supported by this device.
bool supports_format(texture_format format, texture_usage usage) const;
Discovery
#include <nozzle/discovery.hpp>
std::vector<sender_info> enumerate_senders();
Returns all active senders on the local machine.
auto senders = nozzle::enumerate_senders();
for (const auto &s : senders) {
std::cout << s.name << " (" << s.application_name << ")\n";
}
Pixel Access
#include <nozzle/pixel_access.hpp>
struct mapped_pixels {
void *data{nullptr};
uint32_t row_bytes{0};
uint32_t width{0};
uint32_t height{0};
texture_format format{texture_format::unknown};
};
Result<mapped_pixels> lock_frame_pixels(const frame &frm);
void unlock_frame_pixels(const frame &frm);
Result<mapped_pixels> lock_writable_pixels(writable_frame &frm);
void unlock_writable_pixels(writable_frame &frm);
Reading pixels from a received frame:
auto frame = receiver.acquire_frame({.timeout_ms = 1000}).value();
auto pixels = nozzle::lock_frame_pixels(frame).value();
auto *buf = static_cast<uint8_t *>(pixels.data);
// Read pixels: buf[y * pixels.row_bytes + x * bytes_per_pixel + channel]
nozzle::unlock_frame_pixels(frame);
Writing pixels to a writable frame:
auto frame = sender.acquire_writable_frame({
.width = 512, .height = 512,
.format = nozzle::texture_format::rgba8_unorm
}).value();
auto pixels = nozzle::lock_writable_pixels(frame).value();
auto *buf = static_cast<uint8_t *>(pixels.data);
// Write pixels...
nozzle::unlock_writable_pixels(frame);
sender.commit_frame(frame);
OpenGL Interop
#include <nozzle/backends/opengl.hpp>
namespace nozzle::gl {
struct gl_texture_desc {
uint32_t name{0}; // GLuint
uint32_t target{0x0DE1}; // GL_TEXTURE_2D
uint32_t width{0};
uint32_t height{0};
texture_format format{texture_format::rgba8_unorm};
};
Result<void> publish_gl_texture(sender &snd, const gl_texture_desc &gl_desc);
Result<void> copy_frame_to_gl_texture(const frame &frm, const gl_texture_desc &gl_desc);
}
Requires an active GL context on the calling thread.
Publish an existing OpenGL texture:
nozzle::gl::gl_texture_desc gl_desc{
.name = gl_tex_name,
.target = GL_TEXTURE_2D,
.width = 1920,
.height = 1080,
.format = nozzle::texture_format::bgra8_unorm
};
sender.publish_external_texture(...) // not needed
nozzle::gl::publish_gl_texture(sender, gl_desc).value();
Copy a received frame to an OpenGL texture:
auto frame = receiver.acquire_frame({.timeout_ms = 1000}).value();
nozzle::gl::copy_frame_to_gl_texture(frame, gl_desc).value();