Skip to content

Instantly share code, notes, and snippets.

@gzh
Created May 28, 2025 10:31
Show Gist options
  • Select an option

  • Save gzh/68d3aecbbb12aab923a3017593efbdfb to your computer and use it in GitHub Desktop.

Select an option

Save gzh/68d3aecbbb12aab923a3017593efbdfb to your computer and use it in GitHub Desktop.
a raw video source example for viinex
#include <iostream>
#include <functional>
#include <fstream>
#include <cstdlib>
#include <mutex>
#include <chrono>
#include <thread>
#include <memory>
#include <vnxvideo/vnxvideo.h>
void log_handler(void* usrptr, ELogLevel level, const char* subsystem, const char* message) {
std::cerr << level << " " << subsystem << " " << message << std::endl;
}
// for brevity let's declare a type for closures
typedef std::function<void(void)> TAction;
// helper to pass c++ closures as vnxvideo_action_t and usrptr
void invokeClosureCdecl(void* usrptr) {
TAction* pAction = reinterpret_cast<TAction*>(usrptr);
try {
(*pAction)();
}
catch (...) {
std::cerr << "Uncaught exception" << std::endl;
}
}
void checkVnxvideoRes(int res, const char* msg) {
if (res != vnxvideo_err_ok) {
throw std::runtime_error(msg);
}
}
// return a shared ptr which properly releases the undelying void* pointer
// this can be be used with vnxvideo_allocator_t, vnxvideo_raw_sample_t, vnxvideo_rawproc_t etc
template <typename T, void ReleaseFun(T)>
std::shared_ptr<void> wrapVnxvideoPtr(T t) {
return std::shared_ptr<void>(t.ptr, [](void* p) {ReleaseFun(T{ p }); });
}
typedef std::shared_ptr<void> PAllocator;
typedef std::shared_ptr<void> PRawProc;
typedef std::shared_ptr<void> PSample;
// wrappers to handle vnxvideo allocator type in C++:
PAllocator DuplicateShmAllocatorPtr() {
vnxvideo_allocator_t allocator;
vnxvideo_shm_allocator_duplicate(&allocator);
//return PAllocator(allocator.ptr, [](void* p) {vnxvideo_shm_allocator_free(vnxvideo_allocator_t{ p }); });
return wrapVnxvideoPtr<vnxvideo_allocator_t, vnxvideo_shm_allocator_free>(allocator);
}
// a couple of C++ helpers to wrap plain C functions exported from vnxvideo lib
void WithShmAllocatorStr(const char* name, int maxSizeMB, TAction action) {
int res = vnxvideo_with_shm_allocator_str(name, maxSizeMB, invokeClosureCdecl, reinterpret_cast<void*>(&action));
checkVnxvideoRes(res, "vnxvideo_with_shm_allocator_str failed");
}
void WithShmAllocatorPtr(PAllocator pAllocator, TAction action) {
vnxvideo_allocator_t allocator{ pAllocator.get() };
int res = vnxvideo_with_shm_allocator_ptr(allocator, invokeClosureCdecl, reinterpret_cast<void*>(&action));
checkVnxvideoRes(res, "vnxvideo_with_shm_allocator_ptr failed");
}
int main(int argc, char** argv) {
try {
int res = vnxvideo_init(log_handler, 0, VNXLOG_DEBUG);
checkVnxvideoRes(res, "vnxvideo_init failed");
const char* rendezvousPoint = (argc>1)?argv[1]:"TestRawSrc1";
const int shmApertureMb = 64;
PAllocator shmAllocator;
PRawProc localProducer;
WithShmAllocatorStr(rendezvousPoint, shmApertureMb, [&]() {
vnxvideo_rawproc_t rp;
res = vnxvideo_local_server_create(rendezvousPoint, shmApertureMb, &rp);
if (res != vnxvideo_err_ok)
return; // cannot throw exceptions here because this is enclosed with Cdecl helper functions
localProducer = wrapVnxvideoPtr<vnxvideo_rawproc_t, vnxvideo_rawproc_free> (rp);
shmAllocator = DuplicateShmAllocatorPtr();
});
checkVnxvideoRes(res, "vnxvideo_local_server_create failed");
const ERawMediaFormat format = EMF_I420;
const int width = 2688;
const int height = 1520;
// set format before sending any frames; set it again when color format or resolution changes
res = vnxvideo_rawproc_set_format(vnxvideo_rawproc_t{ localProducer.get() }, format, width, height);
checkVnxvideoRes(res, "vnxvideo_rawproc_set_format failed");
int frameNumber = 0;
for (;;) {
PSample sample;
WithShmAllocatorPtr(shmAllocator, [&]() {
vnxvideo_raw_sample_t s;
// allocate the sample within shared memory pool
res = vnxvideo_raw_sample_allocate(format, width, height, &s);
if (res != vnxvideo_err_ok)
return; // cannot throw exceptions here
sample = wrapVnxvideoPtr<vnxvideo_raw_sample_t, vnxvideo_raw_sample_free>(s);
});
checkVnxvideoRes(res, "vnxvideo_raw_sample_allocate failed");
// populate the sample memory with data
int strides[4];
uint8_t* planes[4];
res = vnxvideo_raw_sample_get_data(vnxvideo_raw_sample_t{ sample.get() }, strides, planes);
checkVnxvideoRes(res, "vnxvideo_raw_sample_get_data failed");
// the code for filling the data depends on format (color planes layout)
for (int y = 0; y < height; ++y) {
uint8_t* lineOffsetY = planes[0] + y * strides[0];
uint8_t* lineOffsetU = planes[1] + (y/2) * strides[1];
uint8_t* lineOffsetV = planes[2] + (y/2) * strides[2];
for (int x = 0; x < width; ++x) {
lineOffsetY[x] = x + y + frameNumber;
}
for (int x = 0; x < width/2; ++x) {
lineOffsetU[x] = x - y + frameNumber/2;
lineOffsetV[x] = y - x + frameNumber/2;
}
}
frameNumber++;
// in real app frame timestamp should be the number of milliseconds elapsed since UNIX epoch, in UTC tz.
// here we assume we start with epoch, incrementing by 40 ms every frame (25 fps).
uint64_t timestamp = frameNumber * 40;
// this sends the frame into local transport
res = vnxvideo_rawproc_process(vnxvideo_rawproc_t{ localProducer.get() }, vnxvideo_raw_sample_t{ sample.get() }, timestamp);
checkVnxvideoRes(res, "vnxvideo_rawproc_process failed");
std::this_thread::sleep_for(std::chrono::milliseconds(40));
}
return 0;
}
catch (const std::exception& e) {
std::cerr << "Caught exception in main: " << e.what() << std::endl;
return 1;
}
}

Comments are disabled for this gist.