docs: full content + landing + RU translations
Initial documentation site for cuframes:
- Landing page (src/pages/index.mdx) — hero, quick example (publisher +
subscriber), comparison table vs naive/DeepStream, honest "early but
production-tested" status
- /docs/intro — full overview
- /docs/getting-started/{install,first-publisher,first-subscriber}
- /docs/concepts/{frame-vs-packet-ring,ownership-modes,sync-vmm-stream}
with mermaid diagrams
- /docs/integration/{ffmpeg-demuxer,ffmpeg-filter,python}
- /docs/reference/{api-c,api-cpp,protocol} — full v4 wire protocol spec
incl. VMM_FDS message, magic 0xCC7C1DCE bump diff
- /docs/faq — comparison vs DeepStream/GStreamer, license, multi-host
limitations
- i18n/ru/ — parallel RU translation (tech terms latin, склонение апостроф)
Build:
- Docusaurus 3.10.1 + theme-mermaid + search-local
- Follows dagstack-* docs convention (canonical: dagstack-plugin-system-docs)
- Apache-2.0 license; cuframes lib itself remains LGPL-2.1+
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,351 @@
|
||||
---
|
||||
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(...);
|
||||
}
|
||||
```
|
||||
|
||||
## Исключения
|
||||
|
||||
```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-конструктор.** В 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).
|
||||
|
||||
## Утилиты
|
||||
|
||||
```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 <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());
|
||||
}
|
||||
```
|
||||
|
||||
## См. также
|
||||
|
||||
- [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 пример.
|
||||
Reference in New Issue
Block a user