跳到主要内容

3.4.2 JDK 示例参考

基础示例

JDK 数据帧格式

JDK 模块间默认使用 JdkFrame 数据结构进行数据传递,定义如下:

class JdkFrame {
public:
JdkFrame(int dma_fd_, size_t size_, int w, int h);
~JdkFrame();

// 拷贝 DMA 缓冲区数据到主机内存并返回指针
unsigned char* toHost() const;

// 克隆数据至 vector
std::vector<unsigned char> Clone() const;

// 手动释放映射(如有必要)
// 保存为 YUV NV12 格式文件
bool saveToFile(const std::string& filename) const;
bool loadFromFile(const std::string& filename, size_t expected_size);

int getDMAFd() const;
size_t getSize() const { return size_; }
int getWidth() const { return width_; }
int getHeight() const { return height_; }

int MemCopy(const uint8_t* nalu, int nalu_size, int offset = 0);

private:
size_t size_; // 数据总大小,NV12格式宽高相关
int width_;
int height_;
JdkDma dma_;
std::shared_ptr<JdkDmaBuffer> data;
};

MIPI 摄像头采集

// 打开摄像节点
auto camera = JdkCamera::create("/device/video50", 1920, 1080, V4L2_PIX_FMT_NV12);

// 获取一帧数据
auto frame = camera->getFrame();

JDK 编码

// 创建编码器
auto encoder = std::make_shared<JdkEncoder>(width, height, CODING_H264, PIXEL_FORMAT_NV12);

// 对单帧数据进行编码
auto encodedFrame = encoder->Encode(jdkFrame);

JDK 解码

// 创建解码器
auto decoder = std::make_shared<JdkDecoder>(width, height, CODING_H264, PIXEL_FORMAT_NV12);

// 对单帧数据进行解码
auto decodedFrame = decoder->Decode(jdkFrame);

JDK V2D 模块

// 创建 V2D 模块实例
auto v2d = std::make_shared<JdkV2D>();

// NV12 转换为 RGB8888 格式
auto rgba_frame = v2d->convert_format(input_nv12, V2D_FMT_RGB888);

// 绘制单个矩形框
v2d->draw_rect(input_nv12, box, 0xFFFFFF00, 4); // 黄色,线宽4

// 绘制多个矩形框
v2d->draw_rects(input_nv12, {{30, 20, 100, 80}, {60, 40, 200, 160}}, 0x00ffcc66, 4); // 线宽4

// 对帧数据缩放
auto resized_frame = v2d->resize(jdkFrame, 1920, 1080);

// 同时缩放并格式转换
auto converted_frame = v2d->resize_and_convert(jdkFrame, 1920, 1080, V2D_FMT_RGB888);

JDK SDL 显示

// 创建视频输出(VO)对象
auto jdkvo = std::make_shared<JdkVo>(width, height, PIXEL_FORMAT_NV12);

// 显示一帧图像
auto ret = jdkvo->sendFrame(jdkFrame);

JDK DRM 显示

// 创建 DRM 对象
auto drm = std::make_shared<JdkDrm>(width, height, width, PixelFmt::NV12, "/dev/dri/card1");

// 显示一帧图像
auto ret = drm->sendFrame(jdkFrame1);

JDK 算法推理

// 创建算法推理引擎实例
auto engine = YOLOV8Det::create_infer("yolov8n.q.onnx", "onnx");

// 对帧数据进行推理
auto result = engine->commit(jdkFrame).get();

// 绘制推理结果
draw_nv12(jdkFrame, std::any_cast<YOLOV8Det::Objects>(result));

综合示例

BRDK JDK 相关源码下载地址:brdk_jdk

下面简单介绍相关实例的代码实现。

USB 摄像头采集→ 解码→ 显示

// 打开摄像头节点
auto camera = JdkUsbCam::create(device, width, height, V4L2_PIX_FMT_MJPEG);

// 创建 JPEG 解码器
auto decoder = std::make_shared<JdkDecoder>(width, height, CODING_MJPEG, PIXEL_FORMAT_NV12);

// 初始化显示模块(vo)
auto jdkvo = std::make_shared<JdkVo>(width, height, PIXEL_FORMAT_NV12);

// 获取一帧图像数据
auto frame = camera->getFrame();

// 解码 JPEG 图像
auto decFrame = decoder->Decode(frame);

// 将图像帧发送至显示模块
auto ret = jdkvo->sendFrame(decFrame);

mipi 摄像头采集显示

// 打开摄像头节点
auto camera = JdkCamera::create("/device/video50", 1920, 1080, V4L2_PIX_FMT_NV12);

// 初始化显示模块(vo)
auto jdkvo = std::make_shared<JdkVo>(1920, 1080, PIXEL_FORMAT_NV12);

// 获取一帧图像数据
auto frame = camera->getFrame();

// 将图像帧发送至显示模块
auto ret = jdkvo->sendFrame(frame);

MIPI 摄像采集 → 编码 → RTSP 推流

// 创建摄像头采集对象
auto camera = JdkCamera::create("/dev/video50", width, height, V4L2_PIX_FMT_NV12);

// 创建编码器对象
auto encoder = std::make_shared<JdkEncoder>(width, height, CODING_H264, PIXEL_FORMAT_NV12);

// 创建 RTSP 服务器
auto rtsp_ = std::make_shared<RTSPServer>("test", 1, 8554, VideoCodecType::H264);

while (running) {
auto frame = camera->getFrame(); // 获取一帧图像
if (!frame) {
std::cerr << "Failed to capture frame\n";
continue;
}
auto encFrame = encoder->Encode(frame); // 编码
if (encFrame) {
size_t sz = encFrame->getSize();
uint8_t* data = (uint8_t*)encFrame->toHost();
rtsp_->send_nalu(data, sz, getTimestamp()); // 推送编码数据
}
}

RTSP 客户端接收 RTSP 码流 → 解码 → JDK DRM 显示

// 创建 RTSP 客户端
auto netclient = std::make_shared<NetClient>(device_id, channel_id, 0, "");

// 创建 JDK 解码器
auto decoder = std::make_shared<JdkDecoder>(width, height, CODING_H264, PIXEL_FORMAT_NV12);

// 启动接收码流
netclient->start("rtsp://admin:123456@169.254.202.148:8554/stream_8554");

// 绑定视频回调函数
video_cb_ = std::bind(&NetClient::rtsp_video_cb, this,
std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3, std::placeholders::_4,
std::placeholders::_5);

// 视频回调函数实现
int NetClient::rtsp_video_cb(LPBYTE pdata, int len, unsigned int ts,
unsigned short seq, void* puser) {
if (!decoder_) {
// 初始化解码器及 DRM 显示模块
decoder_ = std::make_shared<JdkDecoder>(info_tmp->video.width,
info_tmp->video.height,
CODING_H264, PIXEL_FORMAT_NV12);

drm_ = std::make_shared<JdkDrm>(info_tmp->video.width,
info_tmp->video.height,
info_tmp->video.width,
PixelFmt::NV12, "/dev/dri/card1");

drmframe = std::make_shared<JdkFrame>(-1,
info_tmp->video.width * info_tmp->video.height * 3 / 2,
info_tmp->video.width,
info_tmp->video.height);
} else {
frame = decoder_->Decode(pdata, len); // 解码
drmframe->MemCopy(frame->toHost(), frame->getSize()); // 拷贝数据到显示缓冲区
drm_->sendFrame(drmframe); // 显示图像
}
}

MIPI 摄像头采集→YOLOv8 检测→画框→显示

// 创建摄像头采集对象
auto camera = JdkCamera::create("/dev/video50", width, height, V4L2_PIX_FMT_NV12);
// 初始化显示模块(vo)
auto jdkvo = std::make_shared<JdkVo>(width, height, PIXEL_FORMAT_NV12);
// 创建算法推理引擎实例
auto engine = YOLOV8Det::create_infer("yolov8n.q.onnx", "onnx");
//初始化 v2d模块
auto v2d = std::make_shared<JdkV2D>();
// 获取一帧图像数据
auto jdkFrame = camera->getFrame();
// 对帧数据进行推理
auto result = engine->commit(jdkFrame).get();
//通过v2d模块进行叠框
draw_nv12(jdkFrame, std::any_cast<YOLOV8Det::Objects>(result));
int draw_nv12(std::shared_ptr<JdkFrame> frame, YOLOV8Det::Objects box_result)
{
// printf(" box_result.size:%d\r\n", box_result.size());
auto v2d = std::make_shared<JdkV2D>();
auto reult = v2d->resize(frame, 320, 320);
reult->saveToFile("320x320_resize.nv12");
auto boxs = box_result[0].boxs;
for (int i = 0; i < boxs.size(); ++i)
{
auto &ibox = boxs[i];
float left = ibox.rect.tl().x;
float top = ibox.rect.tl().y;
float right = ibox.rect.br().x;
float bottom = ibox.rect.br().y;
int class_label = ibox.label;
float confidence = ibox.prob;
v2d->draw_rect(reult, {ibox.rect.x, ibox.rect.y, ibox.rect.width, ibox.rect.height}, 0xFFFF00, 2);
}
// mkdir(save_dir);
// auto save_file = save_dir + "/" + file;
// cv::imwrite(save_file.data(), image);
// reult->saveToFile("320x320_OSD_result.nv12");
return 0;
}
// 将图像帧发送至显示模块进行显示
auto ret = jdkvo->sendFrame(jdkFrame);