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

📅 2026/7/1 4:25:36
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, 1900p-tm_year, 1p-tm_mon, p-tm_mday, 8p-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, 1900p-tm_year, 1p-tm_mon, p-tm_mday, 8p-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), 1p-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));第一个参数是HEIGHT1080第二个参数WIDTH1920第三个参数图像格式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