Files
cuframes-docs/site/docs/reference/api-cpp.md
T
Claude Opus 7f45c36aa2 init
2026-05-26 23:23:25 +01:00

352 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
sidebar_position: 2
title: C++ API
---
# C++ API reference
`<cuframes/cuframes.hpp>` — header-only RAII wrapper над [C API](/docs/reference/api-c). Тонкий слой: handle-классы с automatic cleanup, exceptions вместо int return codes, `std::optional<FrameRef>` для `next`.
## Headers & linkage
```cpp
#include <cuframes/cuframes.hpp>
```
```bash
# C++17 минимум (нужен std::optional)
c++ -std=c++17 app.cpp -lcuframes
# при использовании своих CUDA streams
c++ -std=c++17 app.cpp -lcuframes -lcudart
```
Header-only — самой C++ библиотеки не существует, есть только wrapper над `libcuframes.so`. ABI-совместимость наследуется от C API.
Всё в namespace `cuframes`:
```cpp
namespace cuframes {
class Error;
class Frame;
class FrameRef;
class Publisher;
class Subscriber;
class AsyncSubscriber;
struct PublisherOptions;
struct SubscriberOptions;
inline int64_t now_ns();
inline size_t calc_frame_size(...);
}
```
## Exceptions
```cpp
class cuframes::Error : public std::runtime_error {
public:
Error(int code, const std::string &context);
int code() const noexcept;
};
```
Бросается из всех методов кроме explicitly `noexcept`. `code()` — оригинальный [`cuframes_error_t`](/docs/reference/api-c#error-codes); `what()``"<context>: <strerror>"`.
```cpp
try {
cuframes::Publisher pub({.key = "cam1", .width = 1920, .height = 1080});
} catch (const cuframes::Error &e) {
if (e.code() == CUFRAMES_ERR_ALREADY_EXISTS) {
// stale publisher с этим key
}
}
```
## Frame & FrameRef
`Frame` — read-only non-owning view над `cuframes_frame_t`. Используется в callback'ах async subscriber'а.
`FrameRef` — RAII owning handle: `release` вызывается автоматически в destructor'е. Moveable, не copyable.
```cpp
class Frame {
public:
void *cuda_ptr() const noexcept;
cuframes_format_t format() const noexcept;
int32_t width() const noexcept;
int32_t height() const noexcept;
int32_t pitch_y() const noexcept;
int32_t pitch_uv() const noexcept;
uint64_t seq() const noexcept;
int64_t pts_ns() const noexcept;
const cuframes_frame_t *raw() const noexcept;
};
class FrameRef {
public:
explicit operator bool() const noexcept;
Frame view() const noexcept;
// Shortcut accessors: cuda_ptr, width, height, pitch_y, pitch_uv, seq, pts_ns
};
```
Все accessor-методы `noexcept` — они идут в C accessor'ы которые ничего не аллоцируют.
## Publisher
```cpp
struct PublisherOptions {
std::string key;
int32_t width = 0;
int32_t height = 0;
cuframes_format_t format = CUFRAMES_FORMAT_NV12;
int32_t ring_size = 4;
cuframes_publisher_policy_t policy = CUFRAMES_POLICY_DROP_OLDEST;
int32_t consumer_ack_timeout_ms = 0;
int32_t cuda_device = 0;
};
class Publisher {
public:
explicit Publisher(const PublisherOptions &opt);
// EXTERNAL ownership — DEPRECATED в v0.4, бросает Error(INVALID_ARG)
Publisher(const PublisherOptions &opt,
void *const *cuda_ptrs, int32_t ptr_count, size_t frame_size);
~Publisher();
Publisher(Publisher &&) noexcept;
Publisher &operator=(Publisher &&) noexcept;
void *acquire();
void publish(void *stream, int64_t pts_ns);
// EXTERNAL mode — DEPRECATED в v0.4
void publish_external(void *cuda_ptr, void *stream, int64_t pts_ns);
// Packet ring
void enable_packets(const cuframes_packet_ring_options_t *opts = nullptr);
void set_codec_extradata(const void *data, size_t size);
int publish_packet(const void *data, size_t size,
int64_t pts_ns, int64_t dts_ns, uint32_t flags) noexcept;
cuframes_publisher_t *raw() noexcept;
};
```
**Note про deprecated EXTERNAL ownership constructor.** В v0.4 второй конструктор `Publisher(opt, cuda_ptrs, ...)` под капотом вызывает `cuframes_publisher_create_external` и сразу получает `INVALID_ARG` → бросает `cuframes::Error`. Для FFmpeg filter / custom decoder integration переходите на LIBRARY mode + одна `cudaMemcpyAsync(D2D)` в `acquire()`'нутый pointer. См. [C API note](/docs/reference/api-c#create--destroy).
`publish_packet` — единственный non-throwing метод (возвращает int). Это сделано чтобы в hot loop encoder'а не платить за exception unwind на каждом packet.
Минимальный publisher loop:
```cpp
cuframes::Publisher pub({
.key = "cam1",
.width = 1920, .height = 1080,
.format = CUFRAMES_FORMAT_NV12,
});
cudaStream_t stream;
cudaStreamCreate(&stream);
for (;;) {
void *slot = pub.acquire();
// ... NVDEC / kernel пишут в slot на `stream` ...
pub.publish(stream, cuframes::now_ns());
}
```
## Subscriber (sync)
```cpp
struct SubscriberOptions {
std::string key;
std::string consumer_name; // empty = auto-generate
cuframes_subscriber_mode_t mode = CUFRAMES_MODE_NEWEST_ONLY;
int32_t cuda_device = 0;
int32_t connect_timeout_ms = 5000;
};
class Subscriber {
public:
explicit Subscriber(const SubscriberOptions &opt);
~Subscriber();
Subscriber(Subscriber &&) noexcept;
Subscriber &operator=(Subscriber &&) noexcept;
std::optional<FrameRef> next(void *stream, int32_t timeout_ms = -1);
cuframes_subscriber_t *raw() noexcept;
};
```
`next` возвращает `std::nullopt` для recoverable conditions (`TIMEOUT`, `WOULD_BLOCK`, `DISCONNECTED`) и бросает `Error` для всего остального. Эта асимметрия сделана сознательно — три перечисленных случая ожидаемы в обычном loop'е и не должны платить за exception unwind.
```cpp
cuframes::Subscriber sub({
.key = "cam1",
.mode = CUFRAMES_MODE_NEWEST_ONLY,
});
cudaStream_t stream;
cudaStreamCreate(&stream);
while (auto frame = sub.next(stream, 1000)) {
// frame->cuda_ptr(), frame->width(), frame->pts_ns()
// release будет автоматически при выходе из scope
}
```
## Subscriber (async)
```cpp
class AsyncSubscriber {
public:
using OnFrame = std::function<void(const Frame &)>;
using OnError = std::function<void(int err, const std::string &msg)>;
AsyncSubscriber(const SubscriberOptions &opt,
OnFrame on_frame,
OnError on_error = {});
~AsyncSubscriber();
};
```
Callback-based wrapper. Library поднимает internal thread; `Frame` валиден **только** в течение callback'а (автоматический release после return).
```cpp
cuframes::AsyncSubscriber sub(
{.key = "cam1"},
[](const cuframes::Frame &f) {
std::printf("seq=%lu pts=%ld\n", f.seq(), f.pts_ns());
},
[](int err, const std::string &msg) {
std::fprintf(stderr, "cuframes error %d: %s\n", err, msg.c_str());
});
// держим subscriber alive...
std::this_thread::sleep_for(std::chrono::seconds(60));
// destructor join'ит worker thread
```
Не copyable. Move в текущей версии тоже запрещён (поля `std::function` хранят `this`-pointer для trampoline'а — move сломает).
## Packet ring
В C++ wrapper'е packet ring доступен через прямые C-функции и `Publisher::publish_packet`. Отдельных `cuframes::Packet` / `cuframes::PacketRef` классов нет — packet API проще и FFmpeg interop часто пишется напрямую через C.
```cpp
// Publisher-side
cuframes_packet_ring_options_t pkt_opts{};
pkt_opts.ring_slots = 64;
pkt_opts.data_size = 8 * 1024 * 1024;
pkt_opts.max_packet_size = 2 * 1024 * 1024;
pkt_opts.codec_id = AV_CODEC_ID_H264;
pub.enable_packets(&pkt_opts);
pub.set_codec_extradata(sps_pps.data(), sps_pps.size());
int rc = pub.publish_packet(nal, nal_size, pts, dts, CUFRAMES_PKT_FLAG_KEY);
if (rc < 0 && rc != CUFRAMES_ERR_PACKET_OVERSIZED) {
// log + skip; OVERSIZED безопасно игнорировать
}
```
Subscriber-side — pure C функции из `<cuframes/cuframes.h>`, см. [Packet subscriber API](/docs/reference/api-c#subscriber-side).
## Utilities
```cpp
inline int64_t cuframes::now_ns();
inline size_t cuframes::calc_frame_size(cuframes_format_t format,
int32_t w, int32_t h,
int32_t *pitch_y = nullptr,
int32_t *pitch_uv = nullptr);
```
`calc_frame_size` бросает `Error` на unknown format (в отличие от C-варианта который возвращает код).
## Examples
### Complete publisher (LIBRARY mode)
```cpp
#include <cuframes/cuframes.hpp>
#include <cuda_runtime.h>
int main() {
cuframes::Publisher pub({
.key = "cam1",
.width = 1920, .height = 1080,
.format = CUFRAMES_FORMAT_NV12,
.ring_size = 4,
});
cudaStream_t stream;
cudaStreamCreate(&stream);
for (int i = 0; i < 1000; i++) {
void *slot = pub.acquire();
// ... NVDEC decode или kernel write в slot ...
pub.publish(stream, cuframes::now_ns());
}
}
```
### Complete subscriber
```cpp
#include <cuframes/cuframes.hpp>
#include <cuda_runtime.h>
int main() {
cuframes::Subscriber sub({
.key = "cam1",
.consumer_name = "my-detector",
.mode = CUFRAMES_MODE_NEWEST_ONLY,
.connect_timeout_ms = 5000,
});
cudaStream_t stream;
cudaStreamCreate(&stream);
for (;;) {
auto frame = sub.next(stream, 1000);
if (!frame) continue; // timeout/disconnect
// frame->cuda_ptr() — VRAM pointer
// frame->width(), frame->height() — pixels
// frame->pitch_y(), frame->pitch_uv() — байт на строку
// ... ML inference / CUDA filter на stream ...
}
}
```
### Async with lambdas
```cpp
#include <cuframes/cuframes.hpp>
#include <atomic>
#include <thread>
int main() {
std::atomic<uint64_t> frames{0};
cuframes::AsyncSubscriber sub(
{.key = "cam1"},
[&](const cuframes::Frame &f) {
frames.fetch_add(1);
// pre-sync уже выполнен library-side; f.cuda_ptr() ready
});
std::this_thread::sleep_for(std::chrono::seconds(10));
std::printf("processed %lu frames\n", frames.load());
}
```
## See also
- [C API](/docs/reference/api-c) — underlying C functions.
- [Protocol reference](/docs/reference/protocol) — wire format spec.
- [First publisher](/docs/getting-started/first-publisher) — minimal end-to-end пример.