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:
Claude Opus
2026-05-26 23:31:03 +01:00
parent 7f45c36aa2
commit 8c3c43709d
10 changed files with 1579 additions and 8 deletions
@@ -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 пример.