当前位置: 首页 > news >正文

OPENCV——RV1126+OPENCV在视频中添加时间戳

一、在视频中添加时间戳大体流程图

本章节是利用RV1126的视频流结合OPENCV的API对视频流进行字符串叠加,字符串的内容是实时时间戳,然后把VI发送到H264编码器里面,最后把编码数据保存起来。要完成这个功能我们首先要初始化VI、VENC的模块,并且使能,然后需要创建两个线程。

第一个线程是opencv_vi_text_thread线程,这个线程主要的功能是获取VI原始数据,然后格式化字符串并且转换成string,并用OPENCV的API把每一帧转换成Mat,转换完之后用putText把字符串写到矩阵里面,最后把处理后的VI视频数据发送到VENC编码器里面。

第二个线程是get_venc_stream_thread它主要是获取H264的VENC码流数据,并且保存到H264文件。

二、代码实现

#include <assert.h> #include <fcntl.h> #include <getopt.h> #include <opencv2/imgproc.hpp> #include <opencv2/imgproc/imgproc_c.h> #include <pthread.h> #include <signal.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <time.h> #include <unistd.h> // #include "common/sample_common.h" #include "rkmedia_api.h" #include <opencv2/core.hpp> // #include <opencv2/imgoroc.hpp> #include <opencv2/highgui.hpp> #include <opencv2/opencv.hpp> using namespace cv; using namespace std; #define CAMERA_PATH "rkispp_scale0" #define CAMERA_ID 0 #define CAMERA_CHN 0 #define VENC_CHN 0 #define WIDTH 1920 #define HEIGHT 1080 //VI数据和时间戳添加处理线程 void * opencv_text_vi_thread(void * args) { pthread_detach(pthread_self()); MEDIA_BUFFER mb = NULL; int font_face = cv::FONT_HERSHEY_COMPLEX; double fontScale = 2.0; while(1) { mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VI, CAMERA_CHN,-1); if(!mb) { printf("Get vi break...\n"); break; } Mat time_mat = Mat(HEIGHT, WIDTH, CV_8UC1,RK_MPI_MB_GetPtr(mb)); time_t g_time; tm *p; time(&g_time); p = gmtime(&g_time); char date_ptr[100]; sprintf(date_ptr, "%4d-%2d-%2d %2d:%2d:%2d", 1900+p->tm_year, 1+p->tm_mon, p->tm_mday, 8+p->tm_hour, p->tm_min, p->tm_sec); string date = date_ptr; Point text_point; text_point.x = 100; text_point.y = 100; putText(time_mat, date, text_point, font_face, fontScale, Scalar(255,255,0), 2, 8); RK_MPI_SYS_SendMediaBuffer(RK_ID_VENC, VENC_CHN, mb); RK_MPI_MB_ReleaseBuffer(mb); } return NULL; } void * get_venc_stream_thread(void * args) { pthread_detach(pthread_self()); MEDIA_BUFFER mb; FILE * time_opencv_file = fopen("test_opencv_time.h264", "w+"); while(1) { mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, VENC_CHN,-1); if(!mb) { printf("Get VENC break...\n"); break; } printf("Get VENC success...\n"); fwrite(RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), 1, time_opencv_file); RK_MPI_MB_ReleaseBuffer(mb); } return NULL; } int main() { int ret; VI_CHN_ATTR_S vi_chn_attr; vi_chn_attr.pcVideoNode = CAMERA_PATH; // Path vi_chn_attr.u32Width = WIDTH; // Width vi_chn_attr.u32Height = HEIGHT; // Height vi_chn_attr.enPixFmt = IMAGE_TYPE_NV12; // ImageType vi_chn_attr.enBufType = VI_CHN_BUF_TYPE_MMAP; // BufType vi_chn_attr.u32BufCnt = 3; // Cnt vi_chn_attr.enWorkMode = VI_WORK_MODE_NORMAL; // Mode ret = RK_MPI_VI_SetChnAttr(CAMERA_ID, CAMERA_CHN, &vi_chn_attr); if (ret) { printf("Vi Set Attr Failed.....\n"); return 0; } else { printf("Vi Set Attr Success.....\n"); } ret = RK_MPI_VI_EnableChn(CAMERA_ID, CAMERA_CHN); if (ret) { printf("Vi Enable Attr Failed.....\n"); return 0; } else { printf("Vi Enable Attr Success.....\n"); } VENC_CHN_ATTR_S venc_chn_attr; memset(&venc_chn_attr, 0, sizeof(VENC_CHN_ATTR_S)); venc_chn_attr.stVencAttr.u32PicWidth = WIDTH; venc_chn_attr.stVencAttr.u32PicHeight = HEIGHT; venc_chn_attr.stVencAttr.u32VirWidth = WIDTH; venc_chn_attr.stVencAttr.u32VirHeight = HEIGHT; venc_chn_attr.stVencAttr.imageType = IMAGE_TYPE_NV12; venc_chn_attr.stVencAttr.enType = RK_CODEC_TYPE_H264; venc_chn_attr.stVencAttr.u32Profile = 66; venc_chn_attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR; venc_chn_attr.stRcAttr.stH264Cbr.u32Gop = 25; venc_chn_attr.stRcAttr.stH264Cbr.u32BitRate = WIDTH * HEIGHT * 3; venc_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1; venc_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 25; venc_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1; venc_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 25; ret = RK_MPI_VENC_CreateChn(VENC_CHN, &venc_chn_attr); if (ret) { printf("ERROR: Create venc failed!\n"); exit(0); } ret = RK_MPI_VI_StartStream(CAMERA_ID, CAMERA_CHN); if (ret) { printf("start vi failed....\n"); } else { printf("start vi success....\n"); } pthread_t pid1, pid2; pthread_create(&pid1, NULL, opencv_text_vi_thread, NULL); pthread_create(&pid2, NULL, get_venc_stream_thread, NULL); while (1) { sleep(2); } RK_MPI_VENC_DestroyChn(VENC_CHN); RK_MPI_VI_DisableChn(CAMERA_ID, CAMERA_CHN); return 0; }

2.1. RV1126模块初始化并启动VI工作

RV1126模块的初始化,包括VI模块的初始化(RK_MPI_VI_SetChnAttr)、使能VI模块(RK_MPI_VI_EnableChn)、VENC模块的初始化(RK_MPI_VENC_CreateChn)、启动VI工作(RK_MPI_VI_StartStream)。关于这方面的参数设置,我们就不详细说了,因为这方面的内容之前的课程已经详细说过。

2.2. opencv_vi_text_thread线程的讲解

//VI数据和时间戳添加处理线程 void * opencv_text_vi_thread(void * args) { pthread_detach(pthread_self()); MEDIA_BUFFER mb = NULL; int font_face = cv::FONT_HERSHEY_COMPLEX; double fontScale = 2.0; while(1) { mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VI, CAMERA_CHN,-1); if(!mb) { printf("Get vi break...\n"); break; } Mat time_mat = Mat(HEIGHT, WIDTH, CV_8UC1,RK_MPI_MB_GetPtr(mb)); time_t g_time; tm *p; time(&g_time); p = gmtime(&g_time); char date_ptr[100]; sprintf(date_ptr, "%4d-%2d-%2d %2d:%2d:%2d", 1900+p->tm_year, 1+p->tm_mon, p->tm_mday, 8+p->tm_hour, p->tm_min, p->tm_sec); string date = date_ptr; Point text_point; text_point.x = 100; text_point.y = 100; putText(time_mat, date, text_point, font_face, fontScale, Scalar(255,255,0), 2, 8); RK_MPI_SYS_SendMediaBuffer(RK_ID_VENC, VENC_CHN, mb); RK_MPI_MB_ReleaseBuffer(mb); } return NULL; }

上面是opencv_vi_text_thread线程的主要内容,首先我们要通过RK_MPI_SYS_GetMediaBuffer获取每一帧的VI视频原始数据。

然后使用time的时间函数获取系统时间,并用gmtime把当前时间转换成格林威治时间函数,并且调用sprintf格式化系统时间的字符串打印:打印需要遵循这种格式(”%4d-%2d-%2d %2d:%2d:%2d”, 1900 + p->tm_year(这里的年份需要加上1900,这是由于当前系统时间的年份都是从1900开始算起,所以真实年份都需要加上1900), 1+p->tm_mon(gmtime返回的月份是从0开始,换言之就是第一个月对应的索引值是0而不是1,所以我们要得到真正的月份都需要加1),p->tm_mday(日的输出,正常输出就行),8 + p->tm_hour(由于格林威治获取的小时和北京时间有8个小时的时差,因此我们要获取当前的本地时间需要+8小时才能够得到真实的小时),p->tm_min(分钟的输出,正常输出),p->tm_sec(输出秒数)。

利用OPENCV的Mat构造器把每一帧RV1126的VI视频原始数据转换成矩阵,Mat tmp_img = Mat(HEIGHT, WIDTH, CV_8UC1, RK_MPI_MB_GetPtr(mb));第一个参数是HEIGHT:1080,第二个参数WIDTH:1920,第三个参数:图像格式CV_8UC1,第四个参数:具体的图像数据RK_MPI_MB_GetPtr(mb)然后再调用putText把时间戳的字符串,叠加到OPENCV的矩阵上面,具体的实现cv::putText(tmp_img, date_text, origin, font_face, font_scale, cv::Scalar(0, 0, 0), thickness, 8, 0);

做完上述步骤后,把每一帧经过处理后的VI原始数据发送到H264的VENC编码器,调用的API是RK_MPI_SYS_SendMediaBuffer具体的实现是RK_MPI_SYS_SendMediaBuffer(RK_ID_VENC, VENC_CHN, mb);。

2.3. get_venc_stream_thread线程的讲解

void * get_venc_stream_thread(void * args) { pthread_detach(pthread_self()); MEDIA_BUFFER mb; FILE * time_opencv_file = fopen("test_opencv_time.h264", "w+"); while(1) { mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, VENC_CHN,-1); if(!mb) { printf("Get VENC break...\n"); break; } printf("Get VENC success...\n"); fwrite(RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), 1, time_opencv_file); RK_MPI_MB_ReleaseBuffer(mb); } return NULL; }

上面是get_venc_stream_thread的具体实现,在这个线程里面要通过RK_MPI_SYS_GetMediaBuffer获取每一帧H264的编码数据,然后用fwrite写入,具体的实现如mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, 0, -1);

我们来看看,最后的输出效果

上面这张图就是在RV1126的视频数据里面添加时间字符串,并把字符串放在画面中间。

这里要注意的一点,需要用date去修改板子日期,因为板子默认是1970的时间,如:

date -s "2024-1-27 12:15:35"

http://www.jsqmd.com/news/1100246/

相关文章:

  • Fiddler 的使用
  • 谱星航天连续完成两轮数亿融资,加速1024颗谱星星座建设,开启光谱定量遥感新时代
  • 2026 年靠谱的高清无线投屏芯片方案商选购参考汇总
  • Nginx安全配置实战:从基础加固到高级防护,构建Web应用第一道防线
  • 线上AI接口大面积超时:一次从告警到修复的完整排查记录
  • 云南本地线上营销策划推荐:2026实体商家全域获客选型指南
  • Pydantic AI 入门(二):客服 Agent 实战、FastAPI 部署与框架选型
  • 生物素不足会导致白发提前?一文说清生物素与头发健康的真相
  • 【课程设计/毕业设计】基于 SpringBoot 的仓储物流物资管控系统的设计与实现 基于 SpringBoot 的库房出入库数据统计分析系统【附源码、数据库、万字文档】
  • 环保工程师入门:工业废气治理主流技术选型与场景适配总结
  • 独立站建设:外贸企业结构化出海的基础路径
  • 别再手动调坐标轴了!用MATLAB gca/gcf对象批量设置figure属性(含去白边技巧)
  • 如何快速解包Godot游戏资源:godot-unpacker完整使用指南
  • 3d人物提示词
  • ChatGPT品牌优化如何落地:大鱼营销的内容与渠道实践观察
  • 户外空气净化优选雾森系统 吸附悬浮粉尘清新园区空气
  • 从零构建实时手势识别系统:基于YOLOv5与MobileNetV2的深度学习实战
  • 云服务器怎么选才不踩坑:从账单到稳定性的实用清单
  • 加密压缩包密码恢复实战:ArchivePasswordTestTool原理与使用指南
  • reaConverter Pro Portable注册中文版
  • 2026年6月30日复测:八字排盘的命理软件推荐:2026最新第三方测评看这几条硬指标
  • 沉浸式游乐项目开发落地常见踩坑与避坑要点
  • 真实提分——榜眼邦
  • AI客服项目上线90天复盘:我们踩过的7个坑和省下60%成本的决策
  • 蓝速科技会议预约门牌多场景落地与价值实战
  • 从零构建Linux内核操作系统:环境搭建、编译与QEMU测试实战
  • OpenAI放大招!Codex迎来史诗级“回血”更新,程序员直呼:终于熬出头了
  • 【Cluade Code】----Cluade Code实战利器review ,减少代码bug和代码自动审核!
  • 住宅物业全模块数字化转型的技术落地实践
  • 听脑企业版 教育行业教学效果评估专属解决方案 助力培训标准化留档