--- sidebar_position: 2 title: C++ API --- # C++ API reference `` — header-only RAII-wrapper над [C API](/docs/reference/api-c). Тонкий слой: handle-классы с automatic cleanup, exceptions вместо int return codes, `std::optional` для `next`. ## Headers & linkage ```cpp #include ``` ```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(...); } ``` ## Исключения ```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()` — `": "`. ```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-конструктор.** В 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 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; using OnError = std::function; 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-функции из ``, см. [Packet subscriber API](/docs/reference/api-c#subscriber-side). ## Утилиты ```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-варианта, который возвращает код). ## Примеры ### Complete publisher (LIBRARY mode) ```cpp #include #include 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 #include 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 #include #include int main() { std::atomic 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()); } ``` ## См. также - [C API](/docs/reference/api-c) — underlying C-функции. - [Protocol reference](/docs/reference/protocol) — wire format spec. - [First publisher](/docs/getting-started/first-publisher) — minimal end-to-end пример.