3.远程人脸AI监控项目

3.1ROCKX+RV1126人脸识别推流项目

本项目主要呈现的功能是通过采集摄像头的视频数据,并通过人脸识别把名称显示到VI数据,并推送到流媒体服务器上面。这个项目运用的功能很多,包括了rockx人脸检测、识别功能,sqlite3数据库存储人脸功能,rkmedia的VENC编码功能,rkmedia的VI采集功能,ffmpeg推流、OPENCV画框描绘文字功能。

这个项目大部分功能都和第一阶段的功能点很像,包括vi采集数据、VENC编码、 FFMPEG推流等功能。所以我们这个项目的特色是在第一阶段推流的基础上添加了rockx识别VI摄像头的人脸识别、sqlite3存储人脸特征值、对比数据库人脸特征和摄像头的人脸数据特征、OPENCV画框描绘文字等功能。

程序的大体框图

上图是rv1126+rockx人脸识别推流的大体框图,这个项目首先要初始化模块和队列容器。模块包括两个VI模块,第一个VI模块是用于ROCKX的人脸检测、人脸识别的AI推理,第二个VI模块用于显示推理结果。(AI处理消耗cpu资源,要用另一个VI处理,不然会数据卡顿)除了模块外,还需要初始化容器和队列,容器包括Map容器(主要是存储人脸数据库的特征值和人脸名称)、视频编码队列。

初始化所有东西后,则需要使用四个线程去处理,分别是rockx_vi_handle_thread线程、show_vi_thread线程、camera_venc_thread线程、video_push_thread线程。rockx_vi_handle_thread线程主要功能是读取第一个模块的VI数据,并使用rockx框架对VI数据进行人脸检测和人脸识别:当检测到人脸数据后,先进行人脸过滤,然后再调用人脸识别API对其进行人脸特征值的提取,并把摄像头提取的人脸特征和数据库(Map容器)的人脸特征进行对比,若相似度<=1.2则认为是同一个人,若识别到同一个人则输出这个人的名字。

show_vi_thread线程主要是获取第二个VI模块的摄像头数据,然后获取rockx_vi_handle_thread线程的人脸坐标(在第一个线程中获取)和人脸名称(在Map中获取),并用opencv显示到第二个VI模块里面,然后把处理后的VI数据推送到VENC编码器里面。 

camera_venc_thread线程主要是获取VENC编码器的数据,并把每一帧的VENC码流数据存放到H264的VIDEO_QUEUE的视频队列。

video_push_thread线程从VIDEO_QUEUE的视频队列获取每一帧的视频编码数据,然后再利用FFMPEG流媒体推流框架,把视频编码数据传输到流媒体服务器(主要是RTMP服务器)。

三.本项目程序的思维导图

上图是rockx+1126人脸识别的程序思维导图框图,这个项目的main函数是整个项目的入口函数。在这里入口函数里面,需要做四个比较重要的步骤:分别是init_rkmedia_module_function初始化rkmdia的组件、初始化编码视频队列HIGH_VIDEO_QUEUEinit_face_data初始化数据库的人脸数据、init_rv1126_first_assignment开启RV1126的人脸识别推流任务。

2.1. init_rkmedia_module_function初始化组件:

这个函数主要是做RKMEDIA的组件初始化,组件包括:AI推理的VI模块的初始化、视频显示的VI模块初始化、VENC模块的初始化

2.1.1. AI推理模块的VI模块初始化:这个VI模块主要是用于rockx的人脸检测、人脸识别的推理

2.1.2.显示的VI模块初始化:这个VI模块主要是用于显示rockx处理的结果,包括人脸检测的坐标信息和人脸识别后的名称

2.1.3.VENC模块的初始化:初始化H264的编码模块

2.2. 初始化HIGH_VIDEO_QUEUE

初始化1920 * 1080分辨率编码数据队列,这个队列主要是存储1920 * 1080编码的视频数据,主要是用于多线程处理

2.3. init_face_data初始化数据库的人脸数据:

查询sqlite3人脸数据库的人脸特征值和姓名,并通过map容器进行key->value存储,key保存人的名称、value保存的是人脸特征值

2.4. init_rv1126_first_assignment开启RV1126的人脸识别推流任务

这个函数里面有五个步骤,分别是init_rkmedia_ffmpeg_context初始化1920 * 1080分辨率的ffmpeg推流器、创建rockx_vi_handle_thread线程、创建show_vi_handle_thread线程、创建camera_venc_thread线程、创建high_video_push_thread线程。

2.4.1. init_rkmedia_ffmpeg_context初始化1920 * 1080的推流器

在这个函数里面主要是对FFMPEG推流器参数进行设置,它需要对高分辨率(1920 * 1080)的FFMPEG推流器进行初始化。

2.4.2.创建rockx_vi_handle_thread线程

这个线程的作用是通过rockx框架对VI数据进行人脸检测、人脸识别处理,然后把人脸检测的坐标信息和人脸识别的名称保存起来。

2.4.3.创建show_vi_handle_thread线程

这个线程的作用获取显示VI的摄像头数据,然后获取rockx_vi_handle_thread线程处理的坐标信息和人脸名称,并用opencv显示上去,然后把处理后的VI数据发送到VENC编码器。

2.4.4.创建camera_venc_thread线程

这个线程主要功能是获取VENC编码器的码流数据,并且把VENC的码流数据存放到high_video_queue的视频编码队列里面,high_video_queue视频队列主要是为了推流线程用的。

2.4.5.创建video_push_thread线程

这个线程主要功能是获取high_video_queue队列的视频编码数据,并利用FFMPEG流媒体框架把H264码流推送到RTMP流媒体服务器。

3.2 RV1126的VI模块讲解

一.介绍:

介绍rockx+rv1126人脸识别推流项目的VI模块的初始化,这个项目的VI模块需要初始化两个,一个是用于rockx的人脸检测、人脸识别的处理。另外一个则用于显示AI处理结果。设置VI模块的代码在rkmedia_module_function.cpp里面。

二.VI模块的思维导图:

上面思维导图主要是描述VI模块设置的大致流程,这里要创建两个RV1126_VI_CONFIG结构体并进行参数设置,第一个是用于rockx人脸检测、识别,第二个是用于显示AI处理结果。设置完参数之后,则需要调用rkmedia_vi_init去初始化这两个VI模块并且使能,最后把这两个VI模块的ID号存放到数组里面(vi_containers)。

VI模块代码的截图

3.1. rockxVI模块设置

idVI模块的id号,用于初始化和使能VI模块

pcVideoNode: 摄像头的视频节点,这里默认是rkispp_scale0

u32BufCnt缓冲区计数,默认是3

u32WidthVI模块分辨率宽度1920

u32HeightVI模块分辨率高度1080

enPixFmt图像格式默认是NV12,这里填的是IMAGE_TYPE_NV12

enBufTypeVI模块捕捉视频的类型,这里默认填写MMAP

enWorkModeVI工作模式,这里写的是VI_WORK_MODE_NORMAL

设置完上述参数后,则需要调用set_vi_container把VI的模块号放到数组里面,这里数组的索引号是0。

3.2.显示AI结果的VI模块设置

这部分的参数跟上面大部分是相同的,区别在于pcVideoNoderockx VI模块的pcVideoNode是rkispp_scale0,而显示VI模块的pcVideoNode是rkispp_scale1,下面我们来看具体的参数设置。

idVI模块的id号,用于初始化和使能VI模块

pcVideoNode: 摄像头的视频节点,这里选择的是rkispp_scale1

u32BufCnt缓冲区计数,默认是3

u32WidthVI模块分辨率宽度1920

u32HeightVI模块分辨率高度1080

enPixFmt图像格式默认是NV12,这里填的是IMAGE_TYPE_NV12

enBufTypeVI模块捕捉视频的类型,这里默认填写MMAP

enWorkModeVI工作模式,这里写的是VI_WORK_MODE_NORMAL

设置完上述参数后,则需要调用set_vi_container把VI的模块号放到数组里面,这里数组的索引号是1。

3.3. 启动两个VI模块进行视频采集

设置完两个VI模块的参数之后,就需要启动模块进行视频数据的采集,这里直接调用RK_MPI_VI_StartStream去开启摄像头的两个视频模块进行采集。

3.4. rkmedia_vi_init函数的实现:

rkmedia_vi_init这个自定义函数里面,关键是对VI进行初始化和使能,它分别调用了RK_MPI_VI_SetChnAttr的API对VI模块的属性进行初始化,然后再调用RK_MPI_VI_EnableChn对其使能。

3.5. set_vi_container函数的实现:

上面是set_vi_container函数的实现,这里要把VI的模块号存放到RV1126_VI_CONTAINTER结构体容器里面。

源码  rkmedia_module_function.cpp

#include "rkmedia_module_function.h"
#include "rkmedia_assignment_manage.h"
#include "rkmedia_config_public.h"
#include "rkmedia_module.h"
#include "rkmedia_container.h"
#include "SDL.h"
#include "SDL_ttf.h"
#include <sys/time.h>

#define FILE_IMAGE_LENGTH (64 * 1024)

static int get_align16_value(int input_value, int align)
{
    int handle_value = 0;
    if (align && (input_value % align))
        handle_value = (input_value / align + 1) * align;
    return handle_value;
}

int read_image(char *filename, char *buffer)
{
    if (filename == NULL || buffer == NULL)
        return -1;
    FILE *fp = fopen(filename, "rb"); // 以二进制模式读取该文件
    if (fp == NULL)
    {
        printf("fopen failed\n");
        return -2;
    }

    // 检测文件大小file
    fseek(fp, 0, SEEK_END);
    int length = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    int size = fread(buffer, 1, length, fp);
    if (size != length)
    {
        printf("fread failed:%d\n", size);
        return -3;
    }

    fclose(fp);
    return size;
}

static int get_cur_time_ms(void)
{
    struct timeval tv;
    gettimeofday(&tv, NULL);                       // 使用gettimeofday获取当前系统时间
    return (tv.tv_sec * 1000 + tv.tv_usec / 1000); // 利用struct timeval结构体将时间转换为ms
}

int init_rkmedia_module_function()
{
    rkmedia_function_init();

    RV1126_VI_CONFIG rockx_rkmedia_vi_config;
    memset(&rockx_rkmedia_vi_config, 0, sizeof(rockx_rkmedia_vi_config));
    rockx_rkmedia_vi_config.id = 0;
    rockx_rkmedia_vi_config.attr.pcVideoNode = AI_CMOS_DEVICE_NAME;   // VIDEO视频节点路径,
    rockx_rkmedia_vi_config.attr.u32BufCnt = 3;                    // VI捕获视频缓冲区计数,默认是3
    rockx_rkmedia_vi_config.attr.u32Width = 1920;                  // 视频输入的宽度,一般和CMOS摄像头或者外设的宽度一致
    rockx_rkmedia_vi_config.attr.u32Height = 1080;                 // 视频输入的高度,一般和CMOS摄像头或者外设的高度一致
    rockx_rkmedia_vi_config.attr.enPixFmt = IMAGE_TYPE_NV12;       // 视频输入的图像格式,默认是NV12(IMAGE_TYPE_NV12)
    rockx_rkmedia_vi_config.attr.enBufType = VI_CHN_BUF_TYPE_MMAP; // VI捕捉视频的类型
    rockx_rkmedia_vi_config.attr.enWorkMode = VI_WORK_MODE_NORMAL; // VI的工作模式,默认是NORMAL(VI_WORK_MODE_NORMAL)
    int ret = rkmedia_vi_init(&rockx_rkmedia_vi_config);           // 初始化VI工作
    if (ret != 0)
    {
        printf("rockx_vi init error\n");
    }
    else
    {
        printf("rockx_vi init success\n");
        RV1126_VI_CONTAINTER vi_container;
        vi_container.id = 0;
        vi_container.vi_id = rockx_rkmedia_vi_config.id;
        set_vi_container(0, &vi_container); // 设置VI容器
    }


    RV1126_VI_CONFIG show_rkmedia_vi_config;
    memset(&show_rkmedia_vi_config, 0, sizeof(show_rkmedia_vi_config));
    show_rkmedia_vi_config.id = 1;
    show_rkmedia_vi_config.attr.pcVideoNode = SHOW_CMOS_DEVICE_NAME;   // VIDEO视频节点路径,
    show_rkmedia_vi_config.attr.u32BufCnt = 3;                    // VI捕获视频缓冲区计数,默认是3
    show_rkmedia_vi_config.attr.u32Width = 1920;                  // 视频输入的宽度,一般和CMOS摄像头或者外设的宽度一致
    show_rkmedia_vi_config.attr.u32Height = 1080;                 // 视频输入的高度,一般和CMOS摄像头或者外设的高度一致
    show_rkmedia_vi_config.attr.enPixFmt = IMAGE_TYPE_NV12;       // 视频输入的图像格式,默认是NV12(IMAGE_TYPE_NV12)
    show_rkmedia_vi_config.attr.enBufType = VI_CHN_BUF_TYPE_MMAP; // VI捕捉视频的类型
    show_rkmedia_vi_config.attr.enWorkMode = VI_WORK_MODE_NORMAL; // VI的工作模式,默认是NORMAL(VI_WORK_MODE_NORMAL)
    ret = rkmedia_vi_init(&show_rkmedia_vi_config);           // 初始化VI工作
    if (ret != 0)
    {
        printf("show_vi init error\n");
    }
    else
    {
        printf("show_vi init success\n");
        RV1126_VI_CONTAINTER vi_container;
        vi_container.id = 1;
        vi_container.vi_id = show_rkmedia_vi_config.id;
        set_vi_container(1, &vi_container); // 设置VI容器
    }
 
    RV1126_VENC_CONFIG rkmedia_venc_config = {0};
    memset(&rkmedia_venc_config, 0, sizeof(rkmedia_venc_config));
    rkmedia_venc_config.id = 0;
    rkmedia_venc_config.attr.stVencAttr.enType = RK_CODEC_TYPE_H264;          // 编码器协议类型
    rkmedia_venc_config.attr.stVencAttr.imageType = IMAGE_TYPE_NV12;          // 输入图像类型
    rkmedia_venc_config.attr.stVencAttr.u32PicWidth = 1920;                   // 编码图像宽度
    rkmedia_venc_config.attr.stVencAttr.u32PicHeight = 1080;                  // 编码图像高度
    rkmedia_venc_config.attr.stVencAttr.u32VirWidth = 1920;                   // 编码图像虚宽度,一般来说u32VirWidth和u32PicWidth是一致的
    rkmedia_venc_config.attr.stVencAttr.u32VirHeight = 1080;                  // 编码图像虚高度,一般来说u32VirHeight和u32PicHeight是一致的
    rkmedia_venc_config.attr.stVencAttr.u32Profile = 66;                      // 编码等级H.264: 66: Baseline; 77:Main Profile; 100:High Profile; H.265: default:Main; Jpege/MJpege: default:Baseline(编码等级的作用主要是改变画面质量,66的画面质量最差利于网络传输,100的质量最好)

    rkmedia_venc_config.attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;        // 编码器码率控制模式
    rkmedia_venc_config.attr.stRcAttr.stH264Cbr.u32Gop = 25;                  // GOPSIZE:关键帧间隔
    rkmedia_venc_config.attr.stRcAttr.stH264Cbr.u32BitRate = 1920 * 1080 * 3; // 码率
    rkmedia_venc_config.attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1;      // 目的帧率分子:填的是1固定
    rkmedia_venc_config.attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 25;     // 目的帧率分母:填的是25固定
    rkmedia_venc_config.attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1;       // 源头帧率分子:填的是1固定
    rkmedia_venc_config.attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 25;      // 源头帧率分母:填的是25固定

    ret = rkmedia_venc_init(&rkmedia_venc_config);                            // VENC模块的初始化
    if (ret != 0)
    {
        printf("venc init error\n");
    }
    else
    {
        RV1126_VENC_CONTAINER venc_container;
        venc_container.id = 0;
        venc_container.venc_id = rkmedia_venc_config.id;
        set_venc_container(0, &venc_container);
        printf("venc init success\n");
    }


    ret = RK_MPI_VI_StartStream(CAMERA_ID, 0);
    if(ret)
    {
        printf("RK_MPI_VI_StartStream_0 Failed....\n");
    }

    ret = RK_MPI_VI_StartStream(CAMERA_ID, 1);
    if(ret)
    {
        printf("RK_MPI_VI_StartStream_1 Failed....\n");
    }



    return 0;
}

3.3RV1126的VENC模块讲解

介绍rockx+rv1126项目的VENC编码模块,在这个项目中VENC编码模块参数的设置是至关重要的,它可以对VI数据进行硬件编码让其可以进行编码码流的推流,VENC模块的配置在源文件rkmedia_module_function.cpp里面。

VENC模块的思维导图

上面思维导图主要是描述VENC模块设置的大致流程,这里要创建RV1126_VENC_CONFIG结构体并进行参数设置,设置完参数之后,则需要调用rkmedia_venc_init去初始化这两个VENC模块,最后把VENC的ID号存放到数组里面(venc_containers)。

VENC模块设置的代码截图

3.1. venc参数的设置

上述的参数是高分辨率VENC编码器的参数设置,我们来看看每个参数设置的意义

stVencAttr.enType编码器协议类型,这里填写的是H264编码器类型,RK_CODEC_TYPE_H264

stVencAttr.imageType编码器图像类型,这里写的类型要和图像输入类型保持一致,所以填写IMAGE_TYPE_NV12

stVencAttr.enType.u32PicWidth编码图像的分辨率宽度,这里是高分辨率所以写1920

stVencAttr.enType.u32PicHeight编码图像的分辨率高度,这里是高分辨率所以写1080

stVencAttr.enType.u32VirWidth编码图像的分辨率虚宽,这里写的跟u32PicWidth保持一致,所以填写1920

stVencAttr.enType.u32VirHeight编码图像的分辨率虚高,这里写的跟u32PicHeight保持一致,所以填写1080

stVencAttr.enType.u32Profile编码等级,这里填写66,指的是Baseline,这种模式更加适用于视频传输

stRcAttr.enRcMode编码器码率控制类型,这里填写的是H264 CBR码率控制模式,VENC_RC_MODE_H264CBR,下面码率控制的结构体都是以stH264Cbr作为设置

stRcAttr.stH264Cbr.u32GopH264的CBR码率控制GOP设置,这里GOP的设置是25

stRcAttr.stH264Cbr.u32BitRateH264的CBR码率控制码率大小的设置,这里是1920 * 1080 * 3 = 6220800比特率/s ~= 700KB/s

stRcAttr.stH264Cbr.u32SrcFrameRateDenH264的CBR码率控制控制结构体的源帧率分母的设置,这里写的是25

stRcAttr.stH264Cbr.u32SrcFrameRateNumH264的CBR码率控制控制结构体的源帧率分子的设置,这里写的是1

stRcAttr.stH264Cbr.u32DstFrameRateDenH264的CBR码率控制控制结构体的目标帧率分母的设置,这里写的是25

stRcAttr.stH264Cbr.u32DstFrameRateNumH264的CBR码率控制控制结构体的目标帧率分子的设置,这里写的是1

(备注:通常情况下源帧率的分子、分母和目标帧率的分子、分母数值保持一致)

设置完上述VENC编码参数后,我们就要调用自己封装的函数rkmedia_venc_init函数,对VENC模块进行设置,具体的实现如

3.2.rkmedia_venc_init的实现

这个自定义函数还是非常简单的,就是把RK_MPI_VENC_CreateChn封装了一层,然后把RV1126_VENC_CONFIG的结构体指针传进去。

3.3.set_venc_container函数的实现:

设置完VENC模块后,就要把VENC模块的ID号设置到VENC容器数组里面,高分辨率VENC的ID号是0,调用自己封装的函数是set_venc_container在这个自定义的函数里面,最主要是把VENC的ID号存放在VENC模块数组里面(venc_containers),具体结构如下图。

3.4读取人脸图片并把特征值保存到sqlite3数据库

介绍如何通过rockx读取人脸图像并读取人脸特征值保存到sqlite3数据库里面,在真正的开发中人脸特征值都会存储到数据库里面,一般的数据库有sqlite3mysql等等。(关于数据库的内容,本项目不做一个详细的介绍,这边会带着大家做一个比较简单的数据库的增删改查操作)。

rockx读取人脸特征值保存数据库大体框图

                         

上图是读取人脸特征和图片数据写到数据库的流程。第一步:要连接和读取人脸的sqlite3数据库。第二步:创建三个rockx句柄,包括人脸检测句柄(face_det_handle)、人脸识别句柄(face_recognize_handle)、人脸关键点句柄(face_5landmarks_handle); 第三步:使用rockx_image_read读取人脸图片; 第四步:使用FILE的API来获取图片的长度和二进制的图片数据; 第五步:使用rockx_face_align对齐人脸图片;第六步:使用rockx_face_recognize提取人脸的特征值;第七步:把人脸特征值和图片二进制数据保存到sqlite3数据库。

rockx读取人脸特征值保存数据库的代码截图

3.1.Connection_sqlite3DataBase连接数据库

这里封装了一个了SQLITE3连接数据库的函数Connection_sqlite3Database,这个函数的实现如下图

这个函数直接调用了sqlite3的api sqlite3_open来初始化人脸数据库,若返回值不等于SQLITE_OK则初始化数据库失败,否则就初始化成功。

3.2.创建三个rockx句柄

这里需要通过rockx_create创建三个句柄来完成人脸检测和人脸识别,分别是

人脸检测句柄(ROCKX_MODULE_FACE_DETECTION_V2)、

人脸识别句柄(ROCKX_MODULE_FACE_RECOGNIZE)、

人脸关键点检测句柄(ROCKX_MODULE_FACE_LANDMARK_5)。

3.3.读取人脸图片

上图是读取人脸的图片,这里使用的是rockx_image_read来读取人脸图片,其中input_image是需要从命令行输入的。

3.4.使用FILE读取图片的二进制数据和长度

上面的代码是读取图片的数据和长度,这里直接使用的是系统的API。通过ftell来获取图片的长度,并使用fread来读取图片的二进制数据。

3.5.使用rockx_face_detect检测人脸数据并找到最精确的人脸

使用rockx_face_detect检测图片的人脸数据,并找到最精确的人脸。具体的查找过程在get_max_face里面,我们来看看这个的实现。

get_max_face的实现是循环人脸的数量,并计算当前人脸的面积(cur_face_box_area)和最大人脸面积(max_face_box_area)。如果当前人脸的面积(cur_face_box_area)>最大人脸面积(max_face_box_area),则把当前人脸面积赋值给最大人脸面积,max_face就是最精确的人脸数据了

3.6. 对齐人脸数据并且提取人脸特征值

找到最精确的人脸数据后,就需要把人脸进行对齐,对齐的API是rockx_face_align对齐了人脸数据后,就使用rockx_face_recognize来识别图片的人脸数据并提取特征值。

3.7. 把人脸特征值和图片数据保存到sqlite3数据库

                                         (图二)

最后一步就是把人脸特征值和图片数据保存到sqlite3数据库,要保存数据库要用到sql语句。这里用到的sql语句是

insert into face_data_table(name,face_feature,feature_size,image_data,image_size) values (?,?,?,?,?);”, insert语句是经典的sql添加数据的语句, 这其中”face_data_table”是我们需要查询的人脸数据表格,它包含了name(人脸名称)、face_feature(人脸特征值)、feature_size(特征值长度)、image_data(图片数据)、image_size(图片长度)(人脸表格数据如图二)。

sqlite3_prepare函数将SQL语句编译成可执行的字节码,从而进行各种SQL的操作,包括: 查询、插入、更新、 删除。

sqlite3_bind_text(stmt, 1, name, strlen(name), NULL)将name绑定到该位置,我们可以执行该预处理语句以将name插入到数据库中。

sqlite3_bind_blob(stmt, 2, feature, featureSize, NULL)将feature绑定到该位置,我们可以执行该预处理语句以将人脸特征值(feature)插入到数据库中。

sqlite3_bind_int(stmt, 3, featureSize)将feature_size绑定到该位置,我们可以执行该预处理语句以将人脸特征值长度(feature_size)插入到数据库中。

sqlite3_bind_blob(stmt, 4, image_data, image_length, NULL)image_data绑定到该位置,我们可以执行该预处理语句以将图片数据(image_data)插入到数据库中。

sqlite3_bind_int(stmt, 5, image_length)image_size绑定到该位置,我们可以执行该预处理语句以将图片长度(image_size)插入到数据库中。

在板子上查询数据库的人脸数据

4.1. 打开sqlite3数据库

使用sqlite3 face.db打开人脸数据库

4.2. 查询人脸的数据

使用SQL的查询语句查询人脸识别数据,使用的sql语句是select * from face_data_table,查询的结果就是上面截图。

3.5读取人脸数据库并保存到map

通过查询人脸数据库数据库表格把数据存储到map容器,map容器底层是内存处理,用map去处理数据能够达到高效,快速查询的效果。在这个项目里面,map起到了快速查询的功能,key存储的是人脸的结构体(People),value存储的是人脸具体的数据(rockx_face_feature_t)。人脸特征值比较大,频繁调用数据库完全不可取,数据卡顿,非常耗时。

  • 读取数据库保存到map流程框图

                 

上图是读取数据库并加载到Map的过程,分别有三步:第一步,加载并连接sqlite3数据库、第二步:通过select查询人脸表格的数据、第三步:把表格的数据循环插入到map容器。

  • 读取数据库保存到map的代码截图

init_face_data是读取数据库数据并插入到map容器的自定义函数,这里有几个重要的步骤分别是加载数据库(Connection_sqlite3DataBase)、查询数据库信息并存放到Map容器(QueryPeopleData)、把Map存放到全局变量里面(set_thread_map)

3.1. 加载并连接sqlite3数据库

这里封装了一个了SQLITE3连接数据库的函数Connection_sqlite3Database,这个函数的实现如下图

这个函数直接调用了sqlite3的api sqlite3_open来初始化人脸数据库,若返回值不等于SQLITE_OK则初始化数据库失败,否则就初始化成功。

3.2. 通过select查询人脸表格的数据

上图是通过sql语句查询人脸表格,”select name, feature_size, face_feature, image_size, image_data from face_data_table”, 其中name: 名字、feature_size特征值长度、face_feature: 人脸特征数据、image_size图片长度、image_data图片数据。face_data_table则是存储人脸的数据库表格。并使用sqlite3_prepare进行sql的预处理

3.3. 读取查询的结果并循环插入map容器

上图是读取sqlite3数据库的数据,并循环插入到map容器。这里的关键是使用了sqlite3提供的函数sqlite3_step来获取select的结果,若结果等于SQLITE_ROW(表示当前返回结果有数据)则表示有数据,就获取每一行的数据库数据

sqlite3_column_text(stmt, 0)表示的是查找select语句的第一个元素name,索引号是0,类型是字符串,并进行绑定。

sqlite3_column_int(stmt, 1)表示的是查找select语句的第二个元素feature_size,索引号是1,类型是int, 并进行绑定。

sqlite3_column_blob(stmt, 2)表示的是查找select语句的第三个元素face_feature,索引号是2,类型是blob, 并进行绑定。

sqlite3_column_int(stmt, 3)表示的是查找select语句的第四个元素image_size,索引号是3,类型是int, 并进行绑定。

sqlite3_column_blob(stmt, 4)表示的是查找select语句的第五个元素image_data,索引号是4,类型是blob, 并进行绑定。

读取完上述的数据后,就把上述的数据插到map。

3.6rockx_vi_handle_thread线程的讲解

本章节主要介绍rockx_vi_handle_thread线程的工作流程,这个线程最主要的功能是获取第一个VI模块的视频数据,并且使用rockx的人脸检测模块、人脸识别模块、人脸关键点模块对视频流数据进行人脸识别,最后把识别的结果(包括人脸检测区域、人脸识别名称)保存到全局变量里面。

  • rockx_vi_handle_thread线程的流程框图

上图是rockx_vi_handle_thread线程的工作流程。首先要初始化三个rockx模块,分别是人脸检测模块、人脸识别模块、人脸关键点检测模块。然后获取ROCKX的VI模块数据,然后调用rockx_face_detect检测VI数据的人脸区域数据,并保存到全局变量。并判断VI数据是否有人脸,若有人脸则使用rockx_face_filter过滤人脸。

若过滤到人脸,则调用rockx_face_recognize对RV1126视频人脸进行识别并且提取特征值。然后遍历Map容器的人脸特征值,并且和当前RV1126视频的人脸特征值进行相似度对比,使用的是rockx_face_feature_similarity,若相似度<=1.1则认定为同一个人,并从Map里面通过人脸特征值获取名字,否则就不处理。

  • rockx_vi_handle_thread线程的代码截图

3.1. 初始化三个rockx模块

这段代码是初始化rockx的三个模块,首先要使用rockx_create_config分配rockx_config_t结构体,并使用rockx_add_config把对应的rockx路径配置进去,在我们的板子里面在/userdata/rockx_data里面,并使用rockx_create创建三个rockx_handle_t句柄,分别是face_det_handle(人脸检测模块)face_recognize_handle(人脸识别模块)face_5landmarks_handle(人脸关键点模块)

3.2. 读取每一帧VI数据并进行人脸检测

初始化rockx_image_t结构体,初始化需要传三个值分别是width = WIDTH(1920)、height = HEIGHT(1080)、pixel_format=ROCKX_PIXEL_FORMAT_YUV420SP_NV12。这三个值都需要和VI模块的配置是一样的。

初始化rockx_image_t后,则需要通过RK_MPI_SYS_GetMediaBuffer获取每一帧VI模块的数据,并把每一帧VI模块的缓冲区和长度传输给rockx_image_t。具体的代码是rv1126_rockx_image.data = (uint8_t *)RK_MPI_MB_GetPtr(mb)(把每一帧VI缓冲区数据赋值到rockx_image_t的data)、rv1126_rockx_image.size = RK_MPI_MB_GetSize(mb)(把每一帧VI大小赋值到rockx_image_t的size)

赋值到rockx_image_t则调用rockx_face_detect对每一帧的rockx_image_t图像进行人脸检测,并把人脸检测的结果输出到rockx_object_array_trockx_object_array_t的内容主要存储的是人脸检测数量和人脸检测区域信息,并调用set_rockx_face_arrayrockx_object_array_t设置到全局变量。

3.3. 对人脸数据过滤并且进行对齐识别

若人脸检测的数量大于0,则认为成功检测到人脸。就对每一帧RV1126图像的人脸图像进行过滤,使用的API是rockx_face_filter。过滤完人脸之后,就通过rockx_face_align进行对齐,最后进行人脸识别提取人脸特征值,使用的API是rockx_face_recognize

3.4. 遍历map容器的人脸数据并和当前RV1126的人脸数据进行相似度比较

这一步是遍历map容器的所有数据,找出所有的value人脸特征值,其中key存储的是人脸的名称,value存储的是人脸特征值。然后通过rockx_face_feature_similarity(&database_iter->second,&rv1126_feature, &similarity)去逐一对比map容器的人脸特征值和当前RV1126的人脸特征值。database_iter->second指的是Map容器里面的人脸特征值,rv1126_feature指的是RV1126视频流的人脸特征值。

若对比的相似度<=1.0,则代表同一个人,并找出这个人脸特征对应的名字,在Map容器是KEY值。找到名字后,则把名字设置到全局变量里面set_rockx_people。下面是map容器查找和RV1126视频流匹配的图解,它是通过RV1126的人脸特征值和Map的人脸特征值进行对比,然后通过value获取KEY的值。

3.7show_vi_thread线程讲解

本章节主要介绍show_vi_thread线程的工作流程,这个线程最主要的功能是获取第二个VI模块的视频数据,同时获取AI推理线程(rockx_vi_handle_thread)处理的人脸名称和人脸区域信息(rockx_object_array_t)。并通过Opencv把人脸区域和人脸名称显示出来,然后把VI数据发送到VENC编码器。

在第二个流中获取第一个流中的信息,在第二个流中画框和显示名字。发送到编码器中。

二.show_vi_thread线程大体框图

上图是show_vi_thread线程的工作流程,首先要获取第二个VI模块的视频数据,然后获取人脸识别的名称(string )和人脸区域信息(rockx_object_array_t)、利用OPENCV描绘人脸区域的矩形框、利用OPENCV显示人脸名称、把处理后的VI数据发送到VENC编码器。

三.show_vi_thread线程代码截图

3.1. 获取第二个模块VI模块的视频数据

上图是通过RK_MPI_SYS_GetMediaBuffer获取第二个模块的VI数据,模块号是RK_ID_VI,通道号是1。

3.2. 创建OPENCV并获取人脸名称和区域信息

上图是通过OPENCV处理RKMEDIA获取的VI数据,这里的处理也是通过Mat矩阵(Mat rv1126_mat = Mat(HEIGHT, WIDTH, CV_8UC1, RK_MPI_MB_GetPtr(mb)))。然后获取rokx处理线程的区域信息(get_rockx_face_array)和人脸名称(get_rockx_people)。

3.3. 利用OPENCV显示人脸区域信息和人脸名称

这部分的代码是利用OPENCV显示人脸区域和名称,这里要用for循环去查找每个人脸的区域信息,每个人脸的区域信息都是一个box。box里面都存储了四个坐标参数(lefttoprightbottom), 然后计算出x,y(具体的计算在上图)。计算出x,y后,用opencv的rectangle去绘制人脸区域的矩形,这个矩形的width和height都固定成500。

绘制出人脸矩形后,则使用OPENCV的putText函数把get_rockx_people人脸的名称显示到VI模块里面。

3.4. 把处理后的VI数据发送到VENC编码器

把处理后的VI数据通过RK_MPI_SYS_SendMediaBufferAPI发送到VENC编码器,此时此刻VENC编码器就有H264的编码数据了。

3.8camera_venc_thread线程

本章节主要介绍camera_venc_thread线程的工作流程,这个线程最主要的功能是获取VENC的视频编码码流数据,并把VENC的码流存放到队列里面。

二.camera_venc_thread线程大体框图

上图是camera_venc_thread线程的大体流程图,总共有三步:先获取RK_MPI_SYS_GetMediaBuffer的API获取VENC的码流数据,然后把VENC码流赋值到video_data_packet_t结构体、最后把video_data_packet_t结构体放到队列里面。

三.camera_venc_thread线程代码截图

3.1. 获取VENC码流的视频数据

上图是通过RK_MPI_SYS_GetMediaBuffer获取VENC码流数据,VENC编码通道数是从外面传进来(默认是0),模块号是RK_ID_VENC。

3.2. VENC码流数据赋值到video_data_packet_t结构体并入队

分配video_data_packet_t结构体指针,然后把每一帧的VENC码流数据拷贝到video_data_packet_t结构体里面。视频缓冲区的赋值memcpy(video_data_packet->buffer, RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb));视频长度的赋值是video_data_packet->video_frame_size = RK_MPI_MB_GetSize(mb)

赋值完上述数据后则把video_data_packet_t的数据放到队列里面,入队的操作是high_video_queue->putVideoPacketQueue(video_data_packet);

3.9high_video_push_thread线程讲解

本章节主要介绍video_push_thread线程的工作流程,这个线程最主要的功能是获取VIDEO_QUEUE队列的视频数据并用FFMPEG把编码数据发送到流媒体服务器,主要是RTMP流媒体服务器。

二.video_push_thread线程

上图是video_push_thread线程的大体框图,首先要获取视频队列的数据getVideoPacketQueue第二步把视频队列的数据赋值到AVPacket、对AVPacket的数据进行PTS时间戳进行累加并转换、利用FFMPEG把AVPacket数据包传输到RTMP流媒体服务器。

. video_push_thread线程代码截图

3.1. 从视频队列获取数据并赋值到AVPacket

上图是从视频编码队列获取视频数据并赋值到AVPacket的过程,先调用getVideoPacketQueue从队列获取视频数据,然后把编码的视频数据赋值到AVPacket。具体的步骤是:用av_buffer_realloc分配每一个缓冲区数据,要注意的是AVPacket中缓冲区的buf是不能直接赋值的,比方说: memcpy(pkt->data, video_data_packet->buffer, video_data_packet->frame_size)否则程序就会出现core_dump情况。我们需要先把video_data_packet_t的视频数据(video_data_packet->buffer)先拷贝到pkt->buf->data,然后再把pkt->buf->data的数据赋值到pkt->data。(这里已经省略了FFMPEG的初始化代码)

然后把video_data_packet的video_frame_size长度直接赋值给AVPacket的pkt->size。最后需要添加标识符pkt->flags |= AV_PKT_FLAG_KEY;,添加了这个标识符后,每个AVPacket中都进行关键帧设置,这个标识符必须要加,否则播放器则无法正常解码出视频

3.2. AVPacket进行pts的时间戳的累加并转换

根据AVPacket的数据去计算视频的PTS,若AVPacket的数据不为空。则让视频video_packet->pts = ost->next_timestamp++把视频PTS进行时间基的转换,调用av_packet_rescale_ts把采集的视频时间基转换成复合流的时间基。

3.3. 把每一帧视频数据传输到流媒体服务器

时间基转换完成之后,就把视频数据写入到复合流文件里面,调用的API是av_interleaved_write_frame (注意:复合流文件可以是本地文件也可以是流媒体地址)。

源码:

ffmpeg_audio_queue.cpp

#include "ffmpeg_audio_queue.h"

//AUDIO队列的构造器,包含mutex的初始化和条件变量初始化
AUDIO_QUEUE::AUDIO_QUEUE()
{
    pthread_mutex_init(&audioMutex, NULL);//mutex的初始化
    pthread_cond_init(&audioCond, NULL);//条件变量初始化
}

//AUDIO队列的析构函数,锁的销毁和条件变量的销毁
AUDIO_QUEUE ::~AUDIO_QUEUE()
{
    pthread_mutex_destroy(&audioMutex);
    pthread_cond_destroy(&audioCond);
}

//AUDIO_QUEUE的插入音频队列操作
int AUDIO_QUEUE::putAudioPacketQueue(audio_data_packet *audio_packet)
{
    pthread_mutex_lock(&audioMutex);//上音频锁
    audio_packet_queue.push(audio_packet);//向音频队列插入audio_data_packet包
    pthread_cond_broadcast(&audioCond);//唤醒视音频队列
    pthread_mutex_unlock(&audioMutex);//解音频锁
    return 0;
}

//AUDIO_QUEUE取出音频包
audio_data_packet *AUDIO_QUEUE::getAudioPacketQueue()
{
    pthread_mutex_lock(&audioMutex);//上音频锁
    while (audio_packet_queue.size() == 0)
    {
        pthread_cond_wait(&audioCond, &audioMutex);//当音频队列没有数据的时候,等待被唤醒
    }
    audio_data_packet *item = audio_packet_queue.front();//把音频数据包移到最前面
    audio_packet_queue.pop();//pop取出音频数据并删除
    pthread_mutex_unlock(&audioMutex);//解音频锁
    return item;
}

//AUDIO_QUEUE音频队列长度
int AUDIO_QUEUE::getAudioPacketQueueSize()
{
    unsigned int count = 0;
    pthread_mutex_lock(&audioMutex);//上音频锁
    count = audio_packet_queue.size();//获取音频队列长度
    pthread_mutex_unlock(&audioMutex);//解音频锁
    return count;
}

ffmpeg_video_queue.cpp

#include "ffmpeg_video_queue.h"

//VIDEO队列的构造器,包含mutex的初始化和条件变量初始化
VIDEO_QUEUE::VIDEO_QUEUE()
{
    pthread_mutex_init(&videoMutex, NULL);//mutex的初始化
    pthread_cond_init(&videoCond, NULL);//条件变量初始化
}

 //VIDEO队列的析构函数,锁的销毁和条件变量的销毁
VIDEO_QUEUE ::~VIDEO_QUEUE()
{
    pthread_mutex_destroy(&videoMutex);//锁的销毁
    pthread_cond_destroy(&videoCond);//条件变量的销毁
}


//VIDEO_QUEUE的插入视频队列操作
int VIDEO_QUEUE::putVideoPacketQueue(video_data_packet_t *video_packet)
{
    pthread_mutex_lock(&videoMutex); //上视频锁
    video_packet_queue.push(video_packet);//向视频队列插入video_data_packet_t包
    pthread_cond_broadcast(&videoCond);//唤醒视频队列
    pthread_mutex_unlock(&videoMutex);//解视频锁
    return 0;
}

//VIDEO_QUEUE取出视频包
video_data_packet_t *VIDEO_QUEUE::getVideoPacketQueue()
{
    pthread_mutex_lock(&videoMutex);//上视频锁
    while (video_packet_queue.size() == 0)
    {
        pthread_cond_wait(&videoCond, &videoMutex);  //当视频队列没有数据的时候,等待被唤醒
    }
    video_data_packet_t *item = video_packet_queue.front();//把视频数据包移到最前面
    video_packet_queue.pop();//pop取出视频数据并删除
    pthread_mutex_unlock(&videoMutex);//解视频锁
    return item;
}

//VIDEO_QUEUE视频队列长度
int VIDEO_QUEUE::getVideoQueueSize()
{
    unsigned int count = 0;
    pthread_mutex_lock(&videoMutex);//上视频锁
    count = video_packet_queue.size();//获取视频队列长度
    pthread_mutex_unlock(&videoMutex);//解视频锁
    return count;
}

map_manage.cpp

#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include "map_manage.h"

S_THREAD_MAP g_thread_maps[MAX_MAP_NUM];//类对象数组
pthread_mutex_t g_thread_maps_mutex;//定义互斥变量

#if 1
int init_map_manage_function()
{
    pthread_mutex_init(&g_thread_maps_mutex, NULL);
    memset(g_thread_maps, 0, sizeof(g_thread_maps));
    return 0;
}


int set_map_id(unsigned int map_id, unsigned int id)
{
    pthread_mutex_lock(&g_thread_maps_mutex);
    g_thread_maps[id].map_id = map_id;
    pthread_mutex_unlock(&g_thread_maps_mutex);
    
    return 0;
}
  
int set_thread_map(unsigned int map_id, S_THREAD_MAP *map)
{
    pthread_mutex_lock(&g_thread_maps_mutex);
    g_thread_maps[map_id] = *map;
    pthread_mutex_unlock(&g_thread_maps_mutex);
    return 0;
}

unsigned int get_thread_map(unsigned int map_id, S_THREAD_MAP *map)
{
   //打开互斥锁
    pthread_mutex_lock(&g_thread_maps_mutex);
    //对map进行赋值操作
    *map = g_thread_maps[map_id];
    //关闭互斥锁
    pthread_mutex_unlock(&g_thread_maps_mutex);
    return 0;
}

#endif

rkmedia_assignment_manage.cpp

#include "rkmedia_assignment_manage.h"
#include "rkmedia_data_process.h"
#include "rkmedia_ffmpeg_config.h"
#include "rkmedia_module.h"

#include "rkmedia_ffmpeg_config.h"
#include "rkmedia_container.h"

int init_rv1126_first_assignment(int protocol_type, char * network_address)
{
    int ret;
    RKMEDIA_FFMPEG_CONFIG *ffmpeg_config = (RKMEDIA_FFMPEG_CONFIG *)malloc(sizeof(RKMEDIA_FFMPEG_CONFIG));
    if (ffmpeg_config == NULL)
    {
        printf("malloc ffmpeg_config failed\n");
    }
    ffmpeg_config->width = 1920;
    ffmpeg_config->height = 1080;
    ffmpeg_config->config_id = 0;
    ffmpeg_config->protocol_type = protocol_type;
    ffmpeg_config->video_codec = AV_CODEC_ID_H264;
    ffmpeg_config->audio_codec = AV_CODEC_ID_AAC;
    memcpy(ffmpeg_config->network_addr, network_address, strlen(network_address));
    //初始化ffmpeg输出模块
    init_rkmedia_ffmpeg_context(ffmpeg_config);

    pthread_t pid;
    VI_PROC_PARAM * vi_arg_params = (VI_PROC_PARAM *)malloc(sizeof(VI_PROC_PARAM));
    if(vi_arg_params == NULL)
    {
        printf("malloc venc arg error\n");
        free(vi_arg_params);
    }

    vi_arg_params->viId = 0;
    ret = pthread_create(&pid, NULL, rockx_vi_handle_thread, (void *)vi_arg_params);
    if (ret != 0)
    {
        printf("create camera_venc_thread failed\n");
    }


    VI_PROC_PARAM * show_vi_arg_params = (VI_PROC_PARAM *)malloc(sizeof(VI_PROC_PARAM));
    if(vi_arg_params == NULL)
    {
        printf("malloc venc arg error\n");
        free(vi_arg_params);
    }

    vi_arg_params->viId = 1;
    ret = pthread_create(&pid, NULL, show_vi_thread, (void *)show_vi_arg_params);
    if (ret != 0)
    {
        printf("create camera_venc_thread failed\n");
    }

    //VENC线程的参数
    VENC_PROC_PARAM *venc_arg_params = (VENC_PROC_PARAM *)malloc(sizeof(VENC_PROC_PARAM));
    if (venc_arg_params == NULL)
    {
        printf("malloc venc arg error\n");
        free(venc_arg_params);
    }

    venc_arg_params->vencId = 0;
    //创建VENC线程,获取摄像头编码数据
    ret = pthread_create(&pid, NULL, camera_venc_thread, (void *)venc_arg_params);
    if (ret != 0)
    {
        printf("create camera_venc_thread failed\n");
    }

    //创建VIDEO_PUSH线程
    ret = pthread_create(&pid, NULL, video_push_thread, (void *)ffmpeg_config);
    if (ret != 0)
    {
        printf("push_server_thread error\n");
    }

 
    return 0;
}

rkmedia_container.cpp

#include <pthread.h>
#include <string.h>
#include "rkmedia_container.h"

RV1126_ALL_CONTAINER all_containers;
pthread_mutex_t all_containers_mutex;

int init_all_container_function()
{
    pthread_mutex_init(&all_containers_mutex, NULL);
    memset(&all_containers, 0, sizeof(all_containers));

    return 0;
}

int set_vi_container(unsigned int index, RV1126_VI_CONTAINTER *vi_container)
{
    pthread_mutex_lock(&all_containers_mutex);
    all_containers.vi_containers[index] = *vi_container;
    pthread_mutex_unlock(&all_containers_mutex);

    return 0;
}

int get_vi_container(unsigned int index, RV1126_VI_CONTAINTER *vi_container)
{
    pthread_mutex_lock(&all_containers_mutex);
    *vi_container = all_containers.vi_containers[index];
    pthread_mutex_unlock(&all_containers_mutex);

    return 0;
}

int set_ai_container(unsigned int index, RV1126_AI_CONTAINTER *ai_container)
{
    pthread_mutex_lock(&all_containers_mutex);
    all_containers.ai_containers[index] = *ai_container;
    pthread_mutex_unlock(&all_containers_mutex);

    return 0;
}

int get_ai_container(unsigned int index, RV1126_AI_CONTAINTER *ai_container)
{
    pthread_mutex_lock(&all_containers_mutex);
    *ai_container = all_containers.ai_containers[index];
    pthread_mutex_unlock(&all_containers_mutex);

    return 0;
}


int set_venc_container(unsigned int index, RV1126_VENC_CONTAINER *venc_container)
{
    pthread_mutex_lock(&all_containers_mutex);
    all_containers.venc_containers[index] = *venc_container;
    pthread_mutex_unlock(&all_containers_mutex);
    return 0;
}

int get_venc_container(unsigned int index, RV1126_VENC_CONTAINER *venc_container)
{
    pthread_mutex_lock(&all_containers_mutex);
    *venc_container = all_containers.venc_containers[index];
    pthread_mutex_unlock(&all_containers_mutex);

    return 0;
}

int set_aenc_container(unsigned int index, RV1126_AENC_CONTAINER *aenc_container)
{
    pthread_mutex_lock(&all_containers_mutex);
    all_containers.aenc_containers[index] = *aenc_container;
    pthread_mutex_unlock(&all_containers_mutex);
    return 0;
}

int get_aenc_container(unsigned int index, RV1126_AENC_CONTAINER *aenc_container)
{
    pthread_mutex_lock(&all_containers_mutex);
    *aenc_container = all_containers.aenc_containers[index];
    pthread_mutex_unlock(&all_containers_mutex);
    return 0;
}

rkmedia_data_process.cpp

// #include "opencv_queue.h"
#include <opencv2/opencv.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgcodecs/legacy/constants_c.h>
#include "rkmedia_data_process.h"
#include "ffmpeg_video_queue.h"
#include "ffmpeg_audio_queue.h"
#include "rkmedia_module.h"
#include "rkmedia_ffmpeg_config.h"
#include "rkmedia_data_process.h"
#include "map_manage.h"
#include "rockx_data_manage.h"
#include "rockx.h"
#include "cJSON/cJSON.h"

using namespace cv;

extern VIDEO_QUEUE *high_video_queue;
//extern VIDEO_QUEUE *low_video_queue;

int can_read_pic = 0;

int is_upload_to_mqtt_flag = 0;
People mqtt_people;

void nv12ToBgr(uint8_t *data, int width, int height, uint8_t *bgr)
{
    cv::Mat yuv(height + height / 2, width, CV_8UC1, (uchar *)data);
    cv::Mat bgrMat(height, width, CV_8UC3, bgr);
    cv::cvtColor(yuv, bgrMat, cv::COLOR_YUV2BGR_NV12);
}

// 从RV1126视频编码数据赋值到FFMPEG的Video AVPacket中
AVPacket *get_ffmpeg_video_avpacket(AVPacket *pkt)
{
    video_data_packet_t *video_data_packet = high_video_queue->getVideoPacketQueue(); // 从视频队列获取数据

    if (video_data_packet != NULL)
    {
        /*
     重新分配给定的缓冲区
   1.  如果入参的 AVBufferRef 为空,直接调用 av_realloc 分配一个新的缓存区,并调用 av_buffer_create 返回一个新的 AVBufferRef 结构;
   2.  如果入参的缓存区长度和入参 size 相等,直接返回 0;
   3.  如果对应的 AVBuffer 设置了 BUFFER_FLAG_REALLOCATABLE 标志,或者不可写,再或者 AVBufferRef data 字段指向的数据地址和 AVBuffer 的 data 地址不同,递归调用 av_buffer_realloc 分配一个新
的 buffer,并将 data 拷贝过去;
   4.  不满足上面的条件,直接调用 av_realloc 重新分配缓存区。
 */
        int ret = av_buffer_realloc(&pkt->buf, video_data_packet->video_frame_size + 70);
        if (ret < 0)
        {
            return NULL;
        }
        pkt->size = video_data_packet->video_frame_size;                                        // rv1126的视频长度赋值到AVPacket Size
        memcpy(pkt->buf->data, video_data_packet->buffer, video_data_packet->video_frame_size); // rv1126的视频数据赋值到AVPacket data
        pkt->data = pkt->buf->data;                                                             // 把pkt->buf->data赋值到pkt->data
        pkt->flags |= AV_PKT_FLAG_KEY;                                                          // 默认flags是AV_PKT_FLAG_KEY
        if (video_data_packet != NULL)
        {
            free(video_data_packet);
            video_data_packet = NULL;
        }

        return pkt;
    }
    else
    {
        return NULL;
    }
}

#if 0
AVPacket *get_low_ffmpeg_video_avpacket(AVPacket *pkt)
{
    video_data_packet_t *video_data_packet = low_video_queue->getVideoPacketQueue(); // 从视频队列获取数据

    if (video_data_packet != NULL)
    {
        /*
     重新分配给定的缓冲区
   1.  如果入参的 AVBufferRef 为空,直接调用 av_realloc 分配一个新的缓存区,并调用 av_buffer_create 返回一个新的 AVBufferRef 结构;
   2.  如果入参的缓存区长度和入参 size 相等,直接返回 0;
   3.  如果对应的 AVBuffer 设置了 BUFFER_FLAG_REALLOCATABLE 标志,或者不可写,再或者 AVBufferRef data 字段指向的数据地址和 AVBuffer 的 data 地址不同,递归调用 av_buffer_realloc 分配一个新
的 buffer,并将 data 拷贝过去;
   4.  不满足上面的条件,直接调用 av_realloc 重新分配缓存区。
 */
        int ret = av_buffer_realloc(&pkt->buf, video_data_packet->video_frame_size + 70);
        if (ret < 0)
        {
            return NULL;
        }
        pkt->size = video_data_packet->video_frame_size;                                        // rv1126的视频长度赋值到AVPacket Size
        memcpy(pkt->buf->data, video_data_packet->buffer, video_data_packet->video_frame_size); // rv1126的视频数据赋值到AVPacket data
        pkt->data = pkt->buf->data;                                                             // 把pkt->buf->data赋值到pkt->data
        pkt->flags |= AV_PKT_FLAG_KEY;                                                          // 默认flags是AV_PKT_FLAG_KEY
        if (video_data_packet != NULL)
        {
            free(video_data_packet);
            video_data_packet = NULL;
        }

        return pkt;
    }
    else
    {
        return NULL;
    }
}
#endif

int write_ffmpeg_avpacket(AVFormatContext *fmt_ctx, const AVRational *time_base, AVStream *st, AVPacket *pkt)
{
    /*将输出数据包时间戳值从编解码器重新调整为流时基 */
    av_packet_rescale_ts(pkt, *time_base, st->time_base);
    pkt->stream_index = st->index;

    return av_interleaved_write_frame(fmt_ctx, pkt);
}

int deal_video_avpacket(AVFormatContext *oc, OutputStream *ost)
{
    int ret;
    AVCodecContext *c = ost->enc;
    AVPacket *video_packet = get_ffmpeg_video_avpacket(ost->packet); // 从RV1126视频编码数据赋值到FFMPEG的Video AVPacket中
    if (video_packet != NULL)
    {
        video_packet->pts = ost->next_timestamp++; // VIDEO_PTS按照帧率进行累加
    }

    ret = write_ffmpeg_avpacket(oc, &c->time_base, ost->stream, video_packet); // 向复合流写入视频数据
    if (ret != 0)
    {
        printf("write video avpacket error");
        return -1;
    }

    return 0;
}

/*int deal_low_video_avpacket(AVFormatContext *oc, OutputStream *ost)
{
    int ret;
    AVCodecContext *c = ost->enc;
    AVPacket *video_packet = get_low_ffmpeg_video_avpacket(ost->packet); // 从RV1126视频编码数据赋值到FFMPEG的Video AVPacket中
    if (video_packet != NULL)
    {
        video_packet->pts = ost->next_timestamp++; // VIDEO_PTS按照帧率进行累加
    }

    ret = write_ffmpeg_avpacket(oc, &c->time_base, ost->stream, video_packet); // 向复合流写入视频数据
    if (ret != 0)
    {
        printf("write video avpacket error");
        return -1;
    }

    return 0;
}*/

int nv12_border(char *pic, int pic_w, int pic_h, int rect_x, int rect_y,
                int rect_w, int rect_h, int R, int G, int B)
{
    /* Set up the rectangle border size */
    const int border = 5;
    /* RGB convert YUV */
    int Y, U, V;
    Y = 0.299 * R + 0.587 * G + 0.114 * B;
    U = -0.1687 * R + 0.3313 * G + 0.5 * B + 128;
    V = 0.5 * R - 0.4187 * G - 0.0813 * B + 128;
    /* Locking the scope of rectangle border range */
    int j, k;
    for (j = rect_y; j < rect_y + rect_h; j++)
    {
        for (k = rect_x; k < rect_x + rect_w; k++)
        {
            if (k < (rect_x + border) || k > (rect_x + rect_w - border) ||
                j < (rect_y + border) || j > (rect_y + rect_h - border))
            {
                /* Components of YUV's storage address index */
                int y_index = j * pic_w + k;
                int u_index =
                    (y_index / 2 - pic_w / 2 * ((j + 1) / 2)) * 2 + pic_w * pic_h;
                int v_index = u_index + 1;
                /* set up YUV's conponents value of rectangle border */
                pic[y_index] = Y;
                pic[u_index] = U;
                pic[v_index] = V;
            }
        }
    }

    return 0;
}

void *rockx_vi_handle_thread(void *args)
{
    pthread_detach(pthread_self());

    S_THREAD_MAP thread_map;
    get_thread_map(0, &thread_map);

    map<People, rockx_face_feature_t> database_face_map = thread_map.thread_people_map;
    map<People, rockx_face_feature_t>::iterator database_iter;

    MEDIA_BUFFER src_mb = NULL;

    rockx_module_t data_version;
    data_version = ROCKX_MODULE_FACE_DETECTION_V2;

    rockx_ret_t rockx_ret;
    rockx_handle_t face_det_handle;
    rockx_handle_t face_recognize_handle;
    rockx_handle_t face_5landmarks_handle;
    rockx_handle_t face_masks_det_handle;

    rockx_config_t *config = rockx_create_config();
    rockx_add_config(config, ROCKX_CONFIG_DATA_PATH, "/userdata/rockx_data/");

    rockx_ret = rockx_create(&face_det_handle, data_version, config, sizeof(rockx_config_t));
    if (rockx_ret != ROCKX_RET_SUCCESS)
    {
        printf("init face_detect error %d\n", rockx_ret);
        return NULL;
    }

    rockx_ret = rockx_create(&face_recognize_handle, ROCKX_MODULE_FACE_RECOGNIZE, config, sizeof(rockx_config_t));
    if (rockx_ret != ROCKX_RET_SUCCESS)
    {
        printf("init face_recognize error %d\n", rockx_ret);
        return NULL;
    }

    rockx_ret = rockx_create(&face_5landmarks_handle, ROCKX_MODULE_FACE_LANDMARK_5, config, 0);
    if (rockx_ret != ROCKX_RET_SUCCESS)
    {
        printf("init rockx module ROCKX_MODULE_FACE_LANDMARK_68 error %d\n",
               rockx_ret);
    }

    /*rockx_ret = rockx_create(&face_masks_det_handle,
                             ROCKX_MODULE_FACE_MASKS_DETECTION, config, 0);
    if (rockx_ret != ROCKX_RET_SUCCESS)
    {
        printf("init rockx module ROCKX_MODULE_FACE_LANDMARK_68 error %d\n",
               rockx_ret);
    }*/

    rockx_image_t input_image;
    input_image.width = 1920;
    input_image.height = 1080;
    input_image.pixel_format = ROCKX_PIXEL_FORMAT_YUV420SP_NV12;
    int ret;

    bool is_recognize = false;
    string predict;
    int face_count = 0;

    uint8_t *bgr = new uint8_t[1920 * 1080 * 3];
    int count = 0;

    People people;

    while (1)
    {
        src_mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VI, 0, -1);
        if (!src_mb)
        {
            printf("");
            break;
        }

        input_image.size = RK_MPI_MB_GetSize(src_mb);
        input_image.data = (unsigned char *)RK_MPI_MB_GetPtr(src_mb); // 从指定的MEDIA_BUFFER中获取缓冲区数据指针

        rockx_object_array_t face_array;
        memset(&face_array, 0, sizeof(face_array));

        // 开始人脸检测
        rockx_ret = rockx_face_detect(face_det_handle, &input_image, &face_array, NULL);
        if (rockx_ret != ROCKX_RET_SUCCESS)
        {
            printf("rockx_face_detect ERROR %d\n", rockx_ret);
        }

        set_rockx_face_array(face_array);
        // printf("face_count = %d\n", face_array.count);

        if (face_array.count > 0)
        {
            for (int i = 0; i < face_array.count; i++)
            {
                if (1)
                {
                    int is_false_face;
                    // 进行人脸过滤处理
                    ret = rockx_face_filter(face_5landmarks_handle, &input_image,
                                            &face_array.object[i].box, &is_false_face);
                    if (ret != ROCKX_RET_SUCCESS)
                    {
                        printf("rockx_face_filter error %d\n", ret);
                    }
                    if (is_false_face)
                        continue;
                }

#if 1
                // 人脸检测结果(包括人脸、车牌、头部、物体等)变量定义
                rockx_object_t max_face;
                rockx_object_t cur_face = face_array.object[i];
                // 进行人脸区域计算处理操作
                int cur_face_box_area = (cur_face.box.right - cur_face.box.left) *
                                        (cur_face.box.bottom - cur_face.box.top);
                int max_face_box_area = (max_face.box.right - max_face.box.left) *
                                        (max_face.box.bottom - max_face.box.top);
                if (cur_face_box_area > max_face_box_area)
                {
                    max_face = cur_face;
                }

                // 检测输出处理
                rockx_image_t out_img;
                memset(&out_img, 0, sizeof(rockx_image_t));
                // 进行面部矫正对齐
                ret = rockx_face_align(face_5landmarks_handle, &input_image, &(max_face.box), NULL, &out_img);
                if (ret != ROCKX_RET_SUCCESS)
                {
                    printf("face_align failed\n");
                }

                rockx_face_feature_t rv1126_feature;
                rockx_face_recognize(face_recognize_handle, &out_img, &rv1126_feature);

                for (database_iter = database_face_map.begin();
                     database_iter != database_face_map.end(); database_iter++)
                {
                    float similarity;
                    ret = rockx_face_feature_similarity(&database_iter->second,
                                                        &rv1126_feature, &similarity);
                    printf("simple_value = %lf\n", similarity);
                    if (similarity <= 1.0)
                    {
                        is_recognize = true;
                        break;
                    }
                    else
                    {
                        is_recognize = false;
                        continue;
                    }
                }

                if (is_recognize == true)
                {
                    people = database_iter->first;
                    printf("people_name = %s\n", people.people_name.c_str());
                }
                set_rockx_people(people);
#endif
            }
        }

        RK_MPI_MB_ReleaseBuffer(src_mb);
        src_mb = NULL;
    }
}

void *show_vi_thread(void *args)
{
    pthread_detach(pthread_self());
    MEDIA_BUFFER mb = NULL;

    float x_rate = (float)1920 / 1920;
    float y_rate = (float)1920 / 1080;

    Point pointer;
    pointer.x = 300;
    pointer.y = 300;

    while (1)
    {
        mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VI, 1, -1);
        if (!mb)
        {
            printf("RK_MPI_SYS_GetMediaBuffer Break.....\n");
            break;
        }

        Mat rv1126_mat = Mat(HEIGHT, WIDTH, CV_8UC1, RK_MPI_MB_GetPtr(mb));
        rockx_object_array_t face_array = get_rockx_face_array();
        People people = get_rockx_people();
        // string face_count_str = std::to_string(face_array.count);
        // cv::putText(rv1126_mat, face_count_str, pointer, cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(255, 0, 255), 3);

        for (int i = 0; i < face_array.count; i++)
        {
            int x = face_array.object[i].box.left * x_rate;
            int y = face_array.object[i].box.top * y_rate;
            int w = (face_array.object[i].box.right - face_array.object[i].box.left) * x_rate;
            int h = (face_array.object[i].box.bottom - face_array.object[i].box.top) * y_rate;

            if (x < 0)
                x = 0;
            if (y < 0)
                y = 0;

            while ((uint32_t)(x + w) >= 1920)
            {
                w -= 16;
            }
            while ((uint32_t)(y + h) >= 1080)
            {
                h -= 16;
            }

            Scalar color(255, 0, 255);
            Rect boundingBox(x, y, 500, 500);
            int thickness = 3;
            rectangle(rv1126_mat, boundingBox, color, thickness);

            int baseline;
            Size text_size = getTextSize(people.people_name, 2, 2, 2, &baseline);
            Point origin;
            origin.x = rv1126_mat.cols / 4 - text_size.width / 4;
            origin.y = rv1126_mat.rows / 4 + text_size.height / 4;
            cv::putText(rv1126_mat, people.people_name, origin, cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(255, 0, 255), 3);
        }

        RK_MPI_SYS_SendMediaBuffer(RK_ID_VENC, 0, mb);
        RK_MPI_MB_ReleaseBuffer(mb);
    }

    return NULL;
}

void *camera_venc_thread(void *args)
{
    pthread_detach(pthread_self());
    MEDIA_BUFFER mb = NULL;

    VENC_PROC_PARAM venc_arg = *(VENC_PROC_PARAM *)args;
    free(args);

    printf("video_venc_thread...\n");

    while (1)
    {
        // 从指定通道中获取VENC数据
        mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, venc_arg.vencId, -1);
        if (!mb)
        {
            printf("high_get venc media buffer error\n");
            break;
        }

        // int naluType = RK_MPI_MB_GetFlag(mb);
        // 分配video_data_packet_t结构体
        video_data_packet_t *video_data_packet = (video_data_packet_t *)malloc(sizeof(video_data_packet_t));
        // 把VENC视频缓冲区数据传输到video_data_packet的buffer中
        memcpy(video_data_packet->buffer, RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb));
        // 把VENC的长度赋值给video_data_packet的video_frame_size中
        video_data_packet->video_frame_size = RK_MPI_MB_GetSize(mb);
        // video_data_packet->frame_flag = naluType;
        // 入到视频压缩队列
        high_video_queue->putVideoPacketQueue(video_data_packet);
        // printf("#naluType = %d \n", naluType);
        // 释放VENC资源
        RK_MPI_MB_ReleaseBuffer(mb);
    }

    MPP_CHN_S vi_channel;
    MPP_CHN_S venc_channel;

    vi_channel.enModId = RK_ID_VI;
    vi_channel.s32ChnId = 0;

    venc_channel.enModId = RK_ID_VENC;
    venc_channel.s32ChnId = venc_arg.vencId;

    int ret;
    ret = RK_MPI_SYS_UnBind(&vi_channel, &venc_channel);
    if (ret != 0)
    {
        printf("VI UnBind failed \n");
    }
    else
    {
        printf("Vi UnBind success\n");
    }

    ret = RK_MPI_VENC_DestroyChn(0);
    if (ret)
    {
        printf("Destroy Venc error! ret=%d\n", ret);
        return 0;
    }
    // destroy vi
    ret = RK_MPI_VI_DisableChn(0, 0);
    if (ret)
    {
        printf("Disable Chn Venc error! ret=%d\n", ret);
        return 0;
    }

    return NULL;
}

void *get_rga_thread(void *args)
{
    MEDIA_BUFFER mb = NULL;

    while (1)
    {
        mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_RGA, 0, -1); // 获取RGA的数据
        if (!mb)
        {
            break;
        }

        RK_MPI_SYS_SendMediaBuffer(RK_ID_VENC, 1, mb); //
        RK_MPI_MB_ReleaseBuffer(mb);
    }

    return NULL;
}

#if 0
void *low_camera_venc_thread(void *args)
{
    pthread_detach(pthread_self());
    MEDIA_BUFFER mb = NULL;

    VENC_PROC_PARAM venc_arg = *(VENC_PROC_PARAM *)args;
    free(args);

    printf("low_video_venc_thread...\n");

    while (1)
    {
        // 从指定通道中获取VENC数据
        // mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, venc_arg.vencId, -1);
        mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, 1, -1);
        if (!mb)
        {
            printf("low_venc break....\n");
            break;
        }

        // int naluType = RK_MPI_MB_GetFlag(mb);
        // 分配video_data_packet_t结构体
        video_data_packet_t *video_data_packet = (video_data_packet_t *)malloc(sizeof(video_data_packet_t));
        // 把VENC视频缓冲区数据传输到video_data_packet的buffer中
        memcpy(video_data_packet->buffer, RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb));
        // 把VENC的长度赋值给video_data_packet的video_frame_size中
        video_data_packet->video_frame_size = RK_MPI_MB_GetSize(mb);
        // video_data_packet->frame_flag = naluType;
        // 入到视频压缩队列
        low_video_queue->putVideoPacketQueue(video_data_packet);
        // printf("#naluType = %d \n", naluType);
        // 释放VENC资源
        RK_MPI_MB_ReleaseBuffer(mb);
    }

    return NULL;
}
#endif

// 音视频合成推流线程
void *video_push_thread(void *args)
{
    pthread_detach(pthread_self());
    RKMEDIA_FFMPEG_CONFIG ffmpeg_config = *(RKMEDIA_FFMPEG_CONFIG *)args;
    free(args);
    AVOutputFormat *fmt = NULL;
    int ret;

    while (1)
    {
        ret = deal_video_avpacket(ffmpeg_config.oc, &ffmpeg_config.video_stream); // 处理FFMPEG视频数据
        if (ret == -1)
        {
            printf("deal_video_avpacket error\n");
            break;
        }
    }

    av_write_trailer(ffmpeg_config.oc);                         // 写入AVFormatContext的尾巴
    free_stream(ffmpeg_config.oc, &ffmpeg_config.video_stream); // 释放VIDEO_STREAM的资源
    free_stream(ffmpeg_config.oc, &ffmpeg_config.audio_stream); // 释放AUDIO_STREAM的资源
    avio_closep(&ffmpeg_config.oc->pb);                         // 释放AVIO资源
    avformat_free_context(ffmpeg_config.oc);                    // 释放AVFormatContext资源
    return NULL;
}

#if 0
void *low_video_push_thread(void *args)
{
    pthread_detach(pthread_self());
    RKMEDIA_FFMPEG_CONFIG ffmpeg_config = *(RKMEDIA_FFMPEG_CONFIG *)args;
    free(args);
    AVOutputFormat *fmt = NULL;
    int ret;

    while (1)
    {
        ret = deal_low_video_avpacket(ffmpeg_config.oc, &ffmpeg_config.video_stream); // 处理FFMPEG视频数据
        if (ret == -1)
        {
            printf("deal_video_avpacket error\n");
            break;
        }
    }

    av_write_trailer(ffmpeg_config.oc);                         // 写入AVFormatContext的尾巴
    free_stream(ffmpeg_config.oc, &ffmpeg_config.video_stream); // 释放VIDEO_STREAM的资源
    free_stream(ffmpeg_config.oc, &ffmpeg_config.audio_stream); // 释放AUDIO_STREAM的资源
    avio_closep(&ffmpeg_config.oc->pb);                         // 释放AVIO资源
    avformat_free_context(ffmpeg_config.oc);                    // 释放AVFormatContext资源
    return NULL;
}
#endif

void *mqtt_cjson_upload_thread(void *args)
{
    int mqtt_flag = 0;

    while (1)
    {
        if (is_upload_to_mqtt_flag == 1)
        {
            mqtt_flag = 1;
        }

        if (mqtt_flag == 1 && is_upload_to_mqtt_flag == 1)
        {
            cJSON *json = cJSON_CreateObject();
            cJSON_AddStringToObject(json, "name", mqtt_people.people_name.c_str());
            cJSON_AddStringToObject(json, "image_data", mqtt_people.images.data());
            // cJSON_AddStringToObject(json, "image_length", people.images.data());
            char *str_print = cJSON_Print(json);
            printf("JSON_Str = %s\n", str_print);
            mqtt_flag = 0;
        }
        else
        {
            printf("No Upload....\n");
        }
    }
}

rkmedia_ffmpeg_config.cpp

#include "rkmedia_ffmpeg_config.h"

RKMEDIA_FFMPEG_CONFIG rkmedia_ffmpeg_configs[NETWORK_NUM];
pthread_mutex_t rkmedia_ffmpeg_config_mutex;

int init_rkmedia_ffmpeg_function()
{
    pthread_mutex_init(&rkmedia_ffmpeg_config_mutex, NULL);
    memset(rkmedia_ffmpeg_configs, 0, sizeof(rkmedia_ffmpeg_configs));
    return 0;
}

int set_rkmedia_ffmpeg_config(unsigned int config_id, RKMEDIA_FFMPEG_CONFIG *ffmpeg_config)
{
    pthread_mutex_lock(&rkmedia_ffmpeg_config_mutex);
    rkmedia_ffmpeg_configs[config_id] = *ffmpeg_config;
    pthread_mutex_unlock(&rkmedia_ffmpeg_config_mutex);
    return 0;
}

unsigned int get_rkmedia_ffmpeg_config(unsigned int config_id, RKMEDIA_FFMPEG_CONFIG *ffmpeg_config)
{
    pthread_mutex_lock(&rkmedia_ffmpeg_config_mutex);
    *ffmpeg_config = rkmedia_ffmpeg_configs[config_id];
    pthread_mutex_unlock(&rkmedia_ffmpeg_config_mutex);
    return 0;
}


int add_stream(OutputStream *ost, AVFormatContext *oc, AVCodec **codec, enum AVCodecID codec_id, int width, int height)
{
    AVCodecContext *c = NULL;

    //创建输出码流的AVStream, AVStream是存储每一个视频/音频流信息的结构体
    ost->stream = avformat_new_stream(oc, NULL);
    if (!ost->stream)
    {
        printf("Can't not avformat_new_stream\n");
        return 0;
    }
    else
    {
        printf("Success avformat_new_stream\n");
    }

    //通过codecid找到CODEC
    *codec = avcodec_find_encoder(codec_id);
    if (!(*codec))
    {
        printf("Can't not find any encoder");
        return 0;
    }
    else
    {
        printf("Success find encoder");
    }

    //nb_streams 输入视频的AVStream 个数 就是当前有几种Stream,比如视频流、音频流、字幕,这样就算三种了,
    // s->nb_streams - 1其实对应的应是AVStream 中的 index
    ost->stream->id = oc->nb_streams - 1;
    //通过CODEC分配编码器上下文
    c = avcodec_alloc_context3(*codec);
    if (!c)
    {
        printf("Can't not allocate context3\n");
        return 0;
    }
    else
    {
        printf("Success allocate context3");
    }

    ost->enc = c;

    switch ((*codec)->type)
    {
    case AVMEDIA_TYPE_AUDIO:

        c->sample_fmt = (*codec)->sample_fmts ? (*codec)->sample_fmts[0] : AV_SAMPLE_FMT_FLTP; //FFMPEG采样格式
        c->bit_rate = 153600;  //FFMPEG音频码率
        c->sample_rate = 48000; //FFMPEG采样率
        c->channel_layout = AV_CH_LAYOUT_STEREO;//FFMPEG声道数2
        c->channels = av_get_channel_layout_nb_channels(c->channel_layout); //FFMPEG采样通道
        ost->stream->time_base = (AVRational){1, c->sample_rate};//FFMPEG音频时间基
        break;

    case AVMEDIA_TYPE_VIDEO:

        //c->codec_id = codec_id;
        c->bit_rate = width * height * 3; //FFMPEG视频码率
        //分辨率必须是2的倍数
        c->width = width; //FFMPEG视频宽度
        c->height = height;//FFMPEG视频高度

        ost->stream->r_frame_rate.den = 1; //FFMPEG帧率,分母
        ost->stream->r_frame_rate.num = 25;//FFMPEG帧率,分子
        ost->stream->time_base = (AVRational){1, 25};//Stream视频时间基,默认情况下等于帧率

        c->time_base = ost->stream->time_base; //编码器时间基
        c->gop_size = GOPSIZE; //GOPSIZE
        c->pix_fmt = AV_PIX_FMT_NV12;//图像格式

        break;

    default:
        break;
    }

    //在h264头部添加SPS,PPS
    if (oc->oformat->flags & AVFMT_GLOBALHEADER)
    {
        c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    }

    return 0;
}

//使能video编码器
int open_video(AVFormatContext *oc, AVCodec *codec, OutputStream *ost, AVDictionary *opt_arg)
{
    AVCodecContext *c = ost->enc;

    //打开编码器
    avcodec_open2(c, codec, NULL);

    //分配video avpacket包
    ost->packet = av_packet_alloc();

    /* 将AVCodecContext参数复制AVCodecParameters复用器 */
    avcodec_parameters_from_context(ost->stream->codecpar, c);
    return 0;
}

//使能audio编码器
int open_audio(AVFormatContext *oc, AVCodec *codec, OutputStream *ost, AVDictionary *opt_arg)
{
    AVCodecContext *c = ost->enc;

    //打开编码器
    avcodec_open2(c, codec, NULL);

    //分配 audio avpacket包
    ost->packet = av_packet_alloc();

    /* 将AVCodecContext参数复制AVCodecParameters复用器 */
    avcodec_parameters_from_context(ost->stream->codecpar, c); 
    return 0;
}

void free_stream(AVFormatContext *oc, OutputStream *ost)
{
    avcodec_close(ost->enc); //就是需要关闭的编码器的AVCodecContext
    avcodec_free_context(&ost->enc); //释放AVCodecContext上下文

    av_buffer_unref(&(ost->packet->buf));//释放AVBufferRef

    av_packet_unref(ost->packet); //释放AVPacket
    av_packet_free(&ost->packet); //释放AVPacket
}


int init_rkmedia_ffmpeg_context(RKMEDIA_FFMPEG_CONFIG *ffmpeg_config)
{
    AVOutputFormat *fmt = NULL;
    AVCodec *audio_codec = NULL;
    AVCodec *video_codec = NULL;
    int ret = 0;

    //FLV_PROTOCOL is RTMP TCP
    if (ffmpeg_config->protocol_type == FLV_PROTOCOL)
    {
        //初始化一个FLV的AVFormatContext
        ret = avformat_alloc_output_context2(&ffmpeg_config->oc, NULL, "flv", ffmpeg_config->network_addr); 
        if (ret < 0)
        {
            return -1;
        }
    }
    //TS_PROTOCOL is SRT UDP RTSP
    else if (ffmpeg_config->protocol_type == TS_PROTOCOL)
    {
        //初始化一个TS的AVFormatContext
        ret = avformat_alloc_output_context2(&ffmpeg_config->oc, NULL, "mpegts", ffmpeg_config->network_addr);
        if (ret < 0)
        {
            return -1;
        }
    }
  
    fmt = ffmpeg_config->oc->oformat;
    /*指定编码器*/
    fmt->video_codec = ffmpeg_config->video_codec;
    fmt->audio_codec = ffmpeg_config->audio_codec;

    if (fmt->video_codec != AV_CODEC_ID_NONE)
    {
        ret = add_stream(&ffmpeg_config->video_stream, ffmpeg_config->oc, &video_codec, fmt->video_codec,ffmpeg_config->width,ffmpeg_config->height);
        if (ret < 0)
        {
            avcodec_free_context(&ffmpeg_config->video_stream.enc);
            free_stream(ffmpeg_config->oc, &ffmpeg_config->video_stream);
            avformat_free_context(ffmpeg_config->oc);
            return -1;
        }

        ret = open_video(ffmpeg_config->oc, video_codec, &ffmpeg_config->video_stream, NULL);
        if (ret < 0)
        {
            avformat_free_context(ffmpeg_config->oc);
        }
    }

#if 0
    if (fmt->audio_codec != AV_CODEC_ID_NONE)
    {
        ret = add_stream(&ffmpeg_config->audio_stream, ffmpeg_config->oc, &audio_codec, fmt->audio_codec);
        if (ret < 0)
        {
            avcodec_free_context(&ffmpeg_config->audio_stream.enc);
            free_stream(ffmpeg_config->oc, &ffmpeg_config->audio_stream);
            avformat_free_context(ffmpeg_config->oc);
            return -1;
        }

        ret = open_audio(ffmpeg_config->oc, audio_codec, &ffmpeg_config->audio_stream, NULL);
        if (ret < 0)
        {
            avformat_free_context(ffmpeg_config->oc);
        }
    }
#endif

    av_dump_format(ffmpeg_config->oc, 0, ffmpeg_config->network_addr, 1);

    if (!(fmt->flags & AVFMT_NOFILE))
    {
        //打开输出文件
        ret = avio_open(&ffmpeg_config->oc->pb, ffmpeg_config->network_addr, AVIO_FLAG_WRITE);
        if (ret < 0)
        {
            free_stream(ffmpeg_config->oc, &ffmpeg_config->video_stream);
            free_stream(ffmpeg_config->oc, &ffmpeg_config->audio_stream);
            avformat_free_context(ffmpeg_config->oc);
            return -1;
        }
    }

    avformat_write_header(ffmpeg_config->oc, NULL);
    return 0;
}



rkmedia_module.cpp

#include "rkmedia_module.h"

int rkmedia_function_init()
{
    RK_MPI_SYS_Init();
    return 0;
}

int rkmedia_vi_init(RV1126_VI_CONFIG *rv1126_vi_config)
{
    int ret;
    VI_CHN_ATTR_S vi_attr = rv1126_vi_config->attr;
    unsigned int id = rv1126_vi_config->id;
    //vi_attr.pcVideoNode = CMOS_DEVICE_NAME;//
    //初始化VI模块
    ret = RK_MPI_VI_SetChnAttr(CAMERA_ID, id, &vi_attr);
    //使能VI模块
    ret |= RK_MPI_VI_EnableChn(CAMERA_ID, id);
    if (ret != 0)
    {
        printf("create vi failed.....\n", ret);
        return -1;
    }
    return 0;
}

int rkmedia_ai_init(RV1126_AI_CONFIG *rv1126_ai_config)
{
    int ret;
    AI_CHN_ATTR_S ai_attr = rv1126_ai_config->attr;
    unsigned int id = rv1126_ai_config->id;
    //ai_attr.pcAudioNode = AUDIO_PATH;
    ret = RK_MPI_AI_SetChnAttr(id, &ai_attr); //设置AI属性
    ret = RK_MPI_AI_EnableChn(id); //使能AI模块
    if (ret != 0) 
    {
        printf("create ai failed...\n");
        return -1;
    }

    return 0;
}

//VENC的初始化
int rkmedia_venc_init(RV1126_VENC_CONFIG *rv1126_venc_config)
{
    int ret;
    VENC_CHN_ATTR_S venc_chn_attr = rv1126_venc_config->attr;
    unsigned int venc_id = rv1126_venc_config->id;
    ret = RK_MPI_VENC_CreateChn(rv1126_venc_config->id, &venc_chn_attr);
    if (ret != 0)
    {
        printf("create rv1126_venc_module failed\n");
        return -1;
    }
    else
    {
        printf("create rv1126_venc_module success\n");
    }

    return 0;
}

//初始化AENC参数
int rkmedia_aenc_init(RV1126_AENC_CONFIG *rv1126_aenc_config)
{
    int ret;
    AENC_CHN_ATTR_S aenc_chn_attr = rv1126_aenc_config->attr;
    unsigned int aenc_id = rv1126_aenc_config->id;

    //创建AENC层
    ret = RK_MPI_AENC_CreateChn(aenc_id, &aenc_chn_attr);
    if (ret != 0)
    {
        printf("create aenc failed: %d\n");
        return -1;
    }

    return 0;
}

rkmedia_module_function.cpp

#include "rkmedia_module_function.h"
#include "rkmedia_assignment_manage.h"
#include "rkmedia_config_public.h"
#include "rkmedia_module.h"
#include "rkmedia_container.h"
#include "SDL.h"
#include "SDL_ttf.h"
#include <sys/time.h>

#define FILE_IMAGE_LENGTH (64 * 1024)

static int get_align16_value(int input_value, int align)
{
    int handle_value = 0;
    if (align && (input_value % align))
        handle_value = (input_value / align + 1) * align;
    return handle_value;
}

int read_image(char *filename, char *buffer)
{
    if (filename == NULL || buffer == NULL)
        return -1;
    FILE *fp = fopen(filename, "rb"); // 以二进制模式读取该文件
    if (fp == NULL)
    {
        printf("fopen failed\n");
        return -2;
    }

    // 检测文件大小file
    fseek(fp, 0, SEEK_END);
    int length = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    int size = fread(buffer, 1, length, fp);
    if (size != length)
    {
        printf("fread failed:%d\n", size);
        return -3;
    }

    fclose(fp);
    return size;
}

static int get_cur_time_ms(void)
{
    struct timeval tv;
    gettimeofday(&tv, NULL);                       // 使用gettimeofday获取当前系统时间
    return (tv.tv_sec * 1000 + tv.tv_usec / 1000); // 利用struct timeval结构体将时间转换为ms
}

int init_rkmedia_module_function()
{
    rkmedia_function_init();

    RV1126_VI_CONFIG rockx_rkmedia_vi_config;
    memset(&rockx_rkmedia_vi_config, 0, sizeof(rockx_rkmedia_vi_config));
    rockx_rkmedia_vi_config.id = 0;
    rockx_rkmedia_vi_config.attr.pcVideoNode = AI_CMOS_DEVICE_NAME;   // VIDEO视频节点路径,
    rockx_rkmedia_vi_config.attr.u32BufCnt = 3;                    // VI捕获视频缓冲区计数,默认是3
    rockx_rkmedia_vi_config.attr.u32Width = 1920;                  // 视频输入的宽度,一般和CMOS摄像头或者外设的宽度一致
    rockx_rkmedia_vi_config.attr.u32Height = 1080;                 // 视频输入的高度,一般和CMOS摄像头或者外设的高度一致
    rockx_rkmedia_vi_config.attr.enPixFmt = IMAGE_TYPE_NV12;       // 视频输入的图像格式,默认是NV12(IMAGE_TYPE_NV12)
    rockx_rkmedia_vi_config.attr.enBufType = VI_CHN_BUF_TYPE_MMAP; // VI捕捉视频的类型
    rockx_rkmedia_vi_config.attr.enWorkMode = VI_WORK_MODE_NORMAL; // VI的工作模式,默认是NORMAL(VI_WORK_MODE_NORMAL)
    int ret = rkmedia_vi_init(&rockx_rkmedia_vi_config);           // 初始化VI工作
    if (ret != 0)
    {
        printf("rockx_vi init error\n");
    }
    else
    {
        printf("rockx_vi init success\n");
        RV1126_VI_CONTAINTER vi_container;
        vi_container.id = 0;
        vi_container.vi_id = rockx_rkmedia_vi_config.id;
        set_vi_container(0, &vi_container); // 设置VI容器
    }


    RV1126_VI_CONFIG show_rkmedia_vi_config;
    memset(&show_rkmedia_vi_config, 0, sizeof(show_rkmedia_vi_config));
    show_rkmedia_vi_config.id = 1;
    show_rkmedia_vi_config.attr.pcVideoNode = SHOW_CMOS_DEVICE_NAME;   // VIDEO视频节点路径,
    show_rkmedia_vi_config.attr.u32BufCnt = 3;                    // VI捕获视频缓冲区计数,默认是3
    show_rkmedia_vi_config.attr.u32Width = 1920;                  // 视频输入的宽度,一般和CMOS摄像头或者外设的宽度一致
    show_rkmedia_vi_config.attr.u32Height = 1080;                 // 视频输入的高度,一般和CMOS摄像头或者外设的高度一致
    show_rkmedia_vi_config.attr.enPixFmt = IMAGE_TYPE_NV12;       // 视频输入的图像格式,默认是NV12(IMAGE_TYPE_NV12)
    show_rkmedia_vi_config.attr.enBufType = VI_CHN_BUF_TYPE_MMAP; // VI捕捉视频的类型
    show_rkmedia_vi_config.attr.enWorkMode = VI_WORK_MODE_NORMAL; // VI的工作模式,默认是NORMAL(VI_WORK_MODE_NORMAL)
    ret = rkmedia_vi_init(&show_rkmedia_vi_config);           // 初始化VI工作
    if (ret != 0)
    {
        printf("show_vi init error\n");
    }
    else
    {
        printf("show_vi init success\n");
        RV1126_VI_CONTAINTER vi_container;
        vi_container.id = 1;
        vi_container.vi_id = show_rkmedia_vi_config.id;
        set_vi_container(1, &vi_container); // 设置VI容器
    }
 
    RV1126_VENC_CONFIG rkmedia_venc_config = {0};
    memset(&rkmedia_venc_config, 0, sizeof(rkmedia_venc_config));
    rkmedia_venc_config.id = 0;
    rkmedia_venc_config.attr.stVencAttr.enType = RK_CODEC_TYPE_H264;          // 编码器协议类型
    rkmedia_venc_config.attr.stVencAttr.imageType = IMAGE_TYPE_NV12;          // 输入图像类型
    rkmedia_venc_config.attr.stVencAttr.u32PicWidth = 1920;                   // 编码图像宽度
    rkmedia_venc_config.attr.stVencAttr.u32PicHeight = 1080;                  // 编码图像高度
    rkmedia_venc_config.attr.stVencAttr.u32VirWidth = 1920;                   // 编码图像虚宽度,一般来说u32VirWidth和u32PicWidth是一致的
    rkmedia_venc_config.attr.stVencAttr.u32VirHeight = 1080;                  // 编码图像虚高度,一般来说u32VirHeight和u32PicHeight是一致的
    rkmedia_venc_config.attr.stVencAttr.u32Profile = 66;                      // 编码等级H.264: 66: Baseline; 77:Main Profile; 100:High Profile; H.265: default:Main; Jpege/MJpege: default:Baseline(编码等级的作用主要是改变画面质量,66的画面质量最差利于网络传输,100的质量最好)

    rkmedia_venc_config.attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;        // 编码器码率控制模式
    rkmedia_venc_config.attr.stRcAttr.stH264Cbr.u32Gop = 25;                  // GOPSIZE:关键帧间隔
    rkmedia_venc_config.attr.stRcAttr.stH264Cbr.u32BitRate = 1920 * 1080 * 3; // 码率
    rkmedia_venc_config.attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1;      // 目的帧率分子:填的是1固定
    rkmedia_venc_config.attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 25;     // 目的帧率分母:填的是25固定
    rkmedia_venc_config.attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1;       // 源头帧率分子:填的是1固定
    rkmedia_venc_config.attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 25;      // 源头帧率分母:填的是25固定

    ret = rkmedia_venc_init(&rkmedia_venc_config);                            // VENC模块的初始化
    if (ret != 0)
    {
        printf("venc init error\n");
    }
    else
    {
        RV1126_VENC_CONTAINER venc_container;
        venc_container.id = 0;
        venc_container.venc_id = rkmedia_venc_config.id;
        set_venc_container(0, &venc_container);
        printf("venc init success\n");
    }


    ret = RK_MPI_VI_StartStream(CAMERA_ID, 0);
    if(ret)
    {
        printf("RK_MPI_VI_StartStream_0 Failed....\n");
    }

    ret = RK_MPI_VI_StartStream(CAMERA_ID, 1);
    if(ret)
    {
        printf("RK_MPI_VI_StartStream_1 Failed....\n");
    }



    return 0;
}

rockx_data_manage.cpp

#include "rockx_data_manage.h"
#include <string>

rockx_object_array_t face_array_bak;
string predictName;
rockx_face_result_t rockx_face_result;
rockx_face_result_group_t face_result_group;

People rockx_people;

pthread_mutex_t g_face_array_mutex;
pthread_mutex_t g_face_predict_mutex;
pthread_mutex_t g_face_result_mutex;
pthread_mutex_t g_face_people_mutex;

int init_all_rockx_face_data()
{
    pthread_mutex_init(&g_face_array_mutex, NULL);
    pthread_mutex_init(&g_face_predict_mutex, NULL);
    pthread_mutex_init(&g_face_result_mutex, NULL);
    pthread_mutex_init(&g_face_people_mutex, NULL);
}

void set_rockx_face_array(rockx_object_array_t face_array)
{
    pthread_mutex_lock(&g_face_array_mutex);
    face_array_bak = face_array;
    pthread_mutex_unlock(&g_face_array_mutex);
}

rockx_object_array_t get_rockx_face_array()
{
    pthread_mutex_lock(&g_face_array_mutex);
    rockx_object_array_t face_array = face_array_bak;
    pthread_mutex_unlock(&g_face_array_mutex);
    return face_array;
}

void set_rockx_prdict_name(string name)
{
    pthread_mutex_lock(&g_face_array_mutex);
    predictName = name;
    pthread_mutex_unlock(&g_face_array_mutex);
}

string get_rockx_prdict_name()
{
    pthread_mutex_lock(&g_face_array_mutex);
    string predict_name = predictName;
    pthread_mutex_unlock(&g_face_array_mutex);
    return predict_name;
}

void set_rockx_face_resuslt_group(rockx_face_result_group_t face_result_group_t)
{
    pthread_mutex_lock(&g_face_result_mutex);
    face_result_group = face_result_group_t;
    pthread_mutex_unlock(&g_face_result_mutex);
}

rockx_face_result_group_t get_rockx_face_resuslt_group()
{
    pthread_mutex_lock(&g_face_result_mutex);
    rockx_face_result_group_t face_result_group_t = face_result_group;
    pthread_mutex_unlock(&g_face_result_mutex);
    return face_result_group_t;
}

void set_rockx_people(People people)
{
    pthread_mutex_lock(&g_face_people_mutex);
    rockx_people = people;
    pthread_mutex_unlock(&g_face_people_mutex);
}

People get_rockx_people()
{
    pthread_mutex_lock(&g_face_people_mutex);
    People rockx_people_t = rockx_people;
    pthread_mutex_unlock(&g_face_people_mutex);
    return rockx_people_t;
}

rv1126_ffmpeg_main.cpp

#include "rkmedia_ffmpeg_config.h"
#include "rkmedia_container.h"
#include "ffmpeg_audio_queue.h"
#include "ffmpeg_video_queue.h"
#include "rkmedia_module_function.h"
#include "rkmedia_assignment_manage.h"
#include "map_manage.h"
#include "sqlite3_operation.h"
#include "rv1126_v4l2_camera.h"
#include "rockx.h"

VIDEO_QUEUE *high_video_queue = NULL;

void init_face_data()
{
    Connection_sqlite3DataBase();
    int task_id = 0;
    S_THREAD_MAP thread_map;
    map<People, rockx_face_feature_t> maps = QueryPeopleData();
    thread_map.thread_people_map = maps;
    set_thread_map(task_id, &thread_map);
}

int main(int argc, char *argv[])
{
    if (argc < 3)
    {
        printf("Please Input ./rv1126_ffmpeg_main high_stream_type high_url_address . Notice URL_TYPE: 0-->FLV  1-->TS\n");
        return -1;
    }

    int high_protocol_type = atoi(argv[1]);
    char *high_network_address = argv[2];
    init_rkmedia_module_function();      // 初始化所有rkmedia的模块
    high_video_queue = new VIDEO_QUEUE(); // 初始化所有VIDEO队列
    init_face_data();    //初始化人脸数据                               
    init_rv1126_first_assignment(high_protocol_type, high_network_address); // 开启推流任务

    while (1)
    {
        sleep(20);
    }

    return 0;
}

rv1126_isp_function.cpp

#include "rv1126_isp_function.h"

int init_all_isp_function()
{
    rk_aiq_working_mode_t hdr_mode = RK_AIQ_WORKING_MODE_NORMAL;
    SAMPLE_COMM_ISP_Init(hdr_mode, RK_FALSE);
    SAMPLE_COMM_ISP_Run();
    SAMPLE_COMM_ISP_SetFrameRate(30);

    return 0;
}

sample_common_isp.c

// Copyright 2020 Fuzhou Rockchip Electronics Co., Ltd. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#if 1

#include "sample_common.h"
#include <assert.h>
#include <fcntl.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

static rk_aiq_sys_ctx_t *g_aiq_ctx;

RK_S32 SAMPLE_COMM_ISP_Init(rk_aiq_working_mode_t WDRMode, RK_BOOL bFECEnable) {
  char *iq_file_dir = "/etc/iqfiles/";
  setlinebuf(stdout);

  rk_aiq_sys_ctx_t *aiq_ctx;
  rk_aiq_static_info_t aiq_static_info;
  rk_aiq_uapi_sysctl_enumStaticMetas(0, &aiq_static_info);

  printf("sensor_name is %s, iqfiles is %s\n",
         aiq_static_info.sensor_info.sensor_name, iq_file_dir);

  aiq_ctx = rk_aiq_uapi_sysctl_init(aiq_static_info.sensor_info.sensor_name,
                                    iq_file_dir, NULL, NULL);

  printf("rk_aiq_uapi_sysctl_init bFECEnable %d\n", bFECEnable);

  rk_aiq_uapi_setFecEn(aiq_ctx, bFECEnable);

  printf("rk_aiq_uapi_setFecEn\n");
  if (rk_aiq_uapi_sysctl_prepare(aiq_ctx, 0, 0, WDRMode)) {
    printf("rkaiq engine prepare failed !\n");
    return -1;
  }
  printf("rk_aiq_uapi_sysctl_init/prepare succeed\n");
  g_aiq_ctx = aiq_ctx;
  return 0;
}

RK_VOID SAMPLE_COMM_ISP_Stop(void) {
  if (!g_aiq_ctx)
    return;

  printf("rk_aiq_uapi_sysctl_stop enter\n");
  rk_aiq_uapi_sysctl_stop(g_aiq_ctx, false);
  printf("rk_aiq_uapi_sysctl_deinit enter\n");
  rk_aiq_uapi_sysctl_deinit(g_aiq_ctx);
  printf("rk_aiq_uapi_sysctl_deinit exit\n");
  g_aiq_ctx = NULL;
}

RK_S32 SAMPLE_COMM_ISP_Run(void) {
  if (!g_aiq_ctx)
    return -1;

  if (rk_aiq_uapi_sysctl_start(g_aiq_ctx)) {
    printf("rk_aiq_uapi_sysctl_start  failed\n");
    return -1;
  }
  printf("rk_aiq_uapi_sysctl_start succeed\n");
  return 0;
}

RK_VOID SAMPLE_COMM_ISP_DumpExpInfo(rk_aiq_working_mode_t WDRMode) {
  char aStr[128] = {'\0'};
  Uapi_ExpQueryInfo_t stExpInfo;
  rk_aiq_wb_cct_t stCCT;

  rk_aiq_user_api_ae_queryExpResInfo(g_aiq_ctx, &stExpInfo);
  rk_aiq_user_api_awb_GetCCT(g_aiq_ctx, &stCCT);

  if (WDRMode == RK_AIQ_WORKING_MODE_NORMAL) {
    sprintf(aStr, "M:%.0f-%.1f LM:%.1f CT:%.1f",
            stExpInfo.CurExpInfo.LinearExp.exp_real_params.integration_time *
                1000 * 1000,
            stExpInfo.CurExpInfo.LinearExp.exp_real_params.analog_gain,
            stExpInfo.MeanLuma, stCCT.CCT);
  } else {
    sprintf(aStr, "S:%.0f-%.1f M:%.0f-%.1f L:%.0f-%.1f SLM:%.1f MLM:%.1f "
                  "LLM:%.1f CT:%.1f",
            stExpInfo.CurExpInfo.HdrExp[0].exp_real_params.integration_time *
                1000 * 1000,
            stExpInfo.CurExpInfo.HdrExp[0].exp_real_params.analog_gain,
            stExpInfo.CurExpInfo.HdrExp[1].exp_real_params.integration_time *
                1000 * 1000,
            stExpInfo.CurExpInfo.HdrExp[1].exp_real_params.analog_gain,
            stExpInfo.CurExpInfo.HdrExp[2].exp_real_params.integration_time *
                1000 * 1000,
            stExpInfo.CurExpInfo.HdrExp[2].exp_real_params.analog_gain,
            stExpInfo.HdrMeanLuma[0], stExpInfo.HdrMeanLuma[1],
            stExpInfo.HdrMeanLuma[2], stCCT.CCT);
  }
  printf("isp exp dump: %s\n", aStr);
}

RK_VOID SAMPLE_COMM_ISP_SetFrameRate(RK_U32 uFps) {
  if (!g_aiq_ctx)
    return;

  printf("SAMPLE_COMM_ISP_SetFrameRate start %d\n", uFps);

  frameRateInfo_t info;
  info.mode = OP_MANUAL;
  info.fps = uFps;
  rk_aiq_uapi_setFrameRate(g_aiq_ctx, info);

  printf("SAMPLE_COMM_ISP_SetFrameRate %d\n", uFps);
}

RK_VOID SAMPLE_COMM_ISP_SetLDCHLevel(RK_U32 level) {
  if (!g_aiq_ctx)
    return;
  rk_aiq_uapi_setLdchEn(g_aiq_ctx, level > 0);
  if (level > 0 && level <= 255)
    rk_aiq_uapi_setLdchCorrectLevel(g_aiq_ctx, level);
}

/*only support switch between HDR and normal*/
RK_VOID SAMPLE_COMM_ISP_SetWDRModeDyn(rk_aiq_working_mode_t WDRMode) {
  rk_aiq_uapi_sysctl_swWorkingModeDyn(g_aiq_ctx, WDRMode);
}

#endif

sqlite3_opencv_image.cpp

#include "sqlite3_operation.h"
#include <opencv2/imgcodecs.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgcodecs/legacy/constants_c.h>
#include <vector>
#include <fstream>

using namespace cv;

int main()
{
    Connection_sqlite3DataBase();
    

#if 0
    //vector<char> images = QueryImageData();
    People people = QueryImageData();
    printf("peope_name = %s, image_size = %d\n",people.people_name.c_str(),people.images.size());
    cv::Mat image_mat = cv::imdecode(people.images, CV_LOAD_IMAGE_COLOR);
    if (image_mat.empty())
    {
        printf("failed to decode image\n");
        return -1;
    }

    cv::imwrite("blob_output.jpg", image_mat);
#endif

     //map<People, rockx_face_feature_t> people_map = QueryPeopleData();
#if 0
    ifstream ifs("chenzhi02.png", ios::binary | ios::ate);
    int file_size = ifs.tellg();
    ifs.seekg(0, ios::beg);
    vector<char> buffer(file_size);
    ifs.read(buffer.data(), file_size);
    ifs.close();
    // 解码图像
    Mat img = imdecode(Mat(buffer), IMREAD_COLOR);
    if (img.empty())
    {
        printf("failed to decode image\n");
        return -1;
    }
    imwrite("output_blog.jpg",img);
#endif

#if 0
    vector<char> images = QueryImageData();
    vector<unsigned char> img_encode;
    printf("image_size = %d\n", images.size());
    cv::imencode(".jpg", images, img_encode);

    Mat img = imdecode(Mat(img_encode), IMREAD_COLOR);
    if (img.empty())
    {
        printf("failed to decode image\n");
        return -1;
    }
    imwrite("output_blog_02.jpg",img);
#endif

#if 0
    FILE *imageFile = fopen("chenzhi02.png", "rb"); // 打开名为image.jpg的图片文件(二进制模式)
    if (!imageFile)
    {
        printf("无法打开图片文件.\n");
        return -1;
    }

    fseek(imageFile, 0L, SEEK_END);   // 定位到文件尾部获取文件大小
    long fileSize = ftell(imageFile); // 获取文件大小
    rewind(imageFile);                // 重新定位到文件起始位置
    printf("fileSize = %ld\n", fileSize);

    unsigned char *buffer = new unsigned char[fileSize];                          // 分配足够大小的缓冲区
    size_t bytesRead = fread(buffer, sizeof(unsigned char), fileSize, imageFile); // 从文件中读取图像数据到缓冲区
    printf("bytesRead = %d\n", bytesRead);
    fclose(imageFile);   
    const char * name = "harry"; 
    float feature[512] ={};

    insert_face_data_toDataBase(name,feature,512,buffer,bytesRead);
#endif

    return 0;
}

sqlite3_operation.cpp

#include "sqlite3_operation.h"
#include <opencv2/core/hal/interface.h>
#include <iostream>
#include <fstream>

sqlite3 *db = NULL;
char zErrMsg = 0;
int rc, id;
sqlite3_stmt *stmt;

/*!
 * \fn     Connection_sqlite3DataBase
 * \brief  �������ݿ�
 *
 *
 * \retval int
 */
int Connection_sqlite3DataBase()
{
  rc = sqlite3_open("/userdata/face.db", &db);
  if (rc != SQLITE_OK)
  {
    fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
    sqlite3_close(db);
    exit(1);
  }
  else
  {
    printf("You have opened a sqlite3 database named bind.db successfully!\nCongratulation! Have fun!\n");
  }
  return 0;
}

/*!
 * \fn     insert_face_data_toDataBase
 * \brief  �������ݿ�
 *
 * \param  [in] const char * name       #
 * \param  [in] float feature [ 512 ]   #
 * \param  [in] int featureSize         #
 *
 * \retval void
 */
void insert_face_data_toDataBase(const char *name, float feature[512], int featureSize, uint8_t *image_data, int image_size)
{
  printf("face_size = %d\n", image_size);
  sqlite3_prepare(db, "insert into face_data_table(name,face_feature,feature_size,image_data,image_size) values (?,?,?,?,?);", -1, &stmt, NULL);
  sqlite3_bind_text(stmt, 1, name, strlen(name), NULL);
  sqlite3_bind_blob(stmt, 2, feature, featureSize, NULL);
  sqlite3_bind_int(stmt, 3, featureSize);
  sqlite3_bind_blob(stmt, 4, image_data, image_size, NULL);
  sqlite3_bind_int(stmt, 5, image_size);

  printf("insert face feature: ");
  for (int i = 0; i < 50; i++)
  {
    printf("%f ", feature[i]);
  }
  printf("\n");
  sqlite3_step(stmt);
}

/*!
 * \fn     QueryFaceFeature
 * \brief  ��ѯ��������
 *
 *
 * \retval map<string, rockx_face_feature_t>
 */
map<string, rockx_face_feature_t> QueryFaceFeature()
{
  rockx_face_feature_t rockx_face_feature = {0, 0};
  map<string, rockx_face_feature_t> rockx_face_feature_map;
  sqlite3_stmt *stmt;
  char *sql = "select name, feature_size, face_feature from face_data_table";

  int ret = sqlite3_prepare(db, sql, strlen(sql), &stmt, 0);
  int id = 0, len = 0;
  char *name;
  int feature_size;

  if (ret == SQLITE_OK)
  {
    while (sqlite3_step(stmt) == SQLITE_ROW)
    {
      name = (char *)sqlite3_column_text(stmt, 0);
      printf("name = %s\n", name);
      feature_size = sqlite3_column_int(stmt, 1);
      printf("feature_size = %d\n", feature_size);

      const void *feature = sqlite3_column_blob(stmt, 2);
      memset(rockx_face_feature.feature, 0, feature_size);
      memcpy(rockx_face_feature.feature, feature, feature_size);

      printf("feature: ");
      for (int i = 0; i < 50; i++)
      {
        printf("%f ", rockx_face_feature.feature[i]);
      }
      printf("\n");

      rockx_face_feature.len = feature_size;
      string str(name);
      rockx_face_feature_map.insert(pair<string, rockx_face_feature_t>(str, rockx_face_feature));
    }
  }

  sqlite3_finalize(stmt);
  sqlite3_close(db);

  return rockx_face_feature_map;
}

map<People, rockx_face_feature_t> QueryPeopleData()
{
  rockx_face_feature_t rockx_face_feature = {0, 0};
  map<People, rockx_face_feature_t> people_map;
  sqlite3_stmt *stmt;
  char *sql = "select name, feature_size, face_feature, image_size, image_data from face_data_table";

  int id = 0, len = 0;
  char *name;
  int feature_size;
  int image_size;
  vector<char> images;
  People first_people;

  int ret = sqlite3_prepare(db, sql, strlen(sql), &stmt, 0);
  if (ret == SQLITE_OK)
  {
    while (sqlite3_step(stmt) == SQLITE_ROW)
    {
      name = (char *)sqlite3_column_text(stmt, 0);
      printf("name = %s\n", name);
      feature_size = sqlite3_column_int(stmt, 1);
      printf("feature_size = %d\n", feature_size);

      const void *feature = sqlite3_column_blob(stmt, 2);
      memset(rockx_face_feature.feature, 0, feature_size);
      memcpy(rockx_face_feature.feature, feature, feature_size);
      rockx_face_feature.len = feature_size;

      image_size = sqlite3_column_int(stmt, 3);
      printf("image_size = %d\n", image_size);

      const char * image_data = (const char *)sqlite3_column_blob(stmt, 4);
      for (int i = 0; i < image_size; i++)
      {
         images.push_back(image_data[i]);
      }

      first_people.people_name = string(name);
      first_people.images = images;

      string str(name);
      //people_map.insert(pair<const People, rockx_face_feature_t>(first_people, rockx_face_feature));
      people_map.insert(make_pair(first_people, rockx_face_feature));
      //people_map.insert(first_people, rockx_face_feature);
    }
  }

  return people_map;
}

#if 0
vector<char> QueryImageData()
{
  vector<char> images;
  sqlite3_stmt *stmt;
  char *sql = "select image_data, image_size from face_data_table";

  int ret = sqlite3_prepare(db, sql, strlen(sql), &stmt, 0);
  int id = 0, len = 0;
  char *name;
  int feature_size;
  const char *feature;
  FILE *png_file = fopen("image_output.jpg", "w+");

  if (ret == SQLITE_OK)
  {
    printf("ssdsdasda\n");
    while (sqlite3_step(stmt) == SQLITE_ROW)
    {
      printf("ssssss\n");
      feature = (const char *)sqlite3_column_blob(stmt, 0);
      feature_size = sqlite3_column_int(stmt, 1);
      // printf("feature_size = %d\n", feature_size);
      // fwrite(feature, sizeof(char), 18064, png_file);
      for (int i = 0; i < feature_size; i++)
      {
        images.push_back(feature[i]);
      }

      // images(feature, feature + 18064);
      //  memset(rockx_face_feature.feature, 0, feature_size);
      //  memcpy(rockx_face_feature.feature, feature, feature_size);
    }
  }

  return images;
}
#endif

struct People QueryImageData()
{
  struct People people;
  vector<char> images;
  sqlite3_stmt *stmt;
  char *sql = "select name, image_data, image_size from face_data_table";

  int ret = sqlite3_prepare(db, sql, strlen(sql), &stmt, 0);
  int id = 0, len = 0;
  char *name;
  int feature_size;
  const char *feature;

  if (ret == SQLITE_OK)
  {
    printf("ssdsdasda\n");
    while (sqlite3_step(stmt) == SQLITE_ROW)
    {

      printf("ssssss\n");
      name = (char *)sqlite3_column_text(stmt, 0);
      feature = (const char *)sqlite3_column_blob(stmt, 1);
      feature_size = sqlite3_column_int(stmt, 2);
      for (int i = 0; i < feature_size; i++)
      {
        images.push_back(feature[i]);
      }

      string name_str(name);
      people.people_name = name_str;
      people.images = images;
    }
  }

  return people;
}

sqlite3_operation_test.cpp

*
 * ./sqlite3_operation_test name image_path
 */

#include "rockx.h"
#include "sqlite3_operation.h"

#define FEATURE_SIZE 512

rockx_handle_t face_det_handle;
rockx_handle_t face_5landmarks_handle;
rockx_handle_t face_recognize_handle;

rockx_object_t *get_max_face(rockx_object_array_t *face_array)
{
  if (face_array->count == 0)
  {
    return NULL;
  }
  rockx_object_t *max_face = NULL;
  int i;
  for (i = 0; i < face_array->count; i++)
  {
    rockx_object_t *cur_face = &(face_array->object[i]);
    if (max_face == NULL)
    {
      max_face = cur_face;
      continue;
    }
    int cur_face_box_area = (cur_face->box.right - cur_face->box.left) *
                            (cur_face->box.bottom - cur_face->box.top);
    int max_face_box_area = (max_face->box.right - max_face->box.left) *
                            (max_face->box.bottom - max_face->box.top);
    if (cur_face_box_area > max_face_box_area)
    {
      max_face = cur_face;
    }
  }
  printf("get_max_face %d\n", i - 1);

  return max_face;
}

int run_face_recognize(const char *name, rockx_image_t *in_image, rockx_face_feature_t *out_feature, unsigned char * buffer, int buffer_size)
{
  rockx_ret_t ret;

  /*************** FACE Detect ***************/
  // create rockx_face_array_t for store result
  rockx_object_array_t face_array;
  memset(&face_array, 0, sizeof(rockx_object_array_t));

  // detect face
  ret = rockx_face_detect(face_det_handle, in_image, &face_array, NULL);
  if (ret != ROCKX_RET_SUCCESS)
  {
    printf("rockx_face_detect error %d\n", ret);
    return -1;
  }

#if 0
  // process result
  for (int i = 0; i < face_array.count; i++)
  {
    int left = face_array.object[i].box.left;
    int top = face_array.object[i].box.top;
    int right = face_array.object[i].box.right;
    int bottom = face_array.object[i].box.bottom;
    float score = face_array.object[i].score;
    printf("%d box=(%d %d %d %d) score=%f\n", i, left, top, right, bottom,
           score);
  }
#endif

  rockx_object_t *max_face = get_max_face(&face_array);
  if (max_face == NULL)
  {
    printf("error no face detected\n");
    return -1;
  }

  // Face Align
  rockx_image_t out_img;
  memset(&out_img, 0, sizeof(rockx_image_t));
  ret = rockx_face_align(face_5landmarks_handle, in_image, &(max_face->box), NULL, &out_img);
  if (ret != ROCKX_RET_SUCCESS)
  {
    return -1;
  }

  // Face Recognition
  rockx_face_recognize(face_recognize_handle, &out_img, out_feature);

  insert_face_data_toDataBase(name, out_feature->feature, FEATURE_SIZE, buffer, buffer_size);

  // Release Aligned Image
  rockx_image_release(&out_img);

  return 0;
}

int main(int argc, char *argv[])
{
  rockx_ret_t ret;

  printf("----------------- main init ---------------------\n");
#if 1
  if (argc != 3)
  {
    printf("Usage: Insert DataBase  ./sqlite3_operation_test name image_path\n");
    return -1;
  }

  printf("Start Connection sqlite3......................\n");
  Connection_sqlite3DataBase();
  printf("End Connection_sqlite3DataBase......................\n");

  rockx_config_t *config = rockx_create_config();
  rockx_add_config(config, ROCKX_CONFIG_DATA_PATH, "/userdata/rockx_data/");

  /*************** Creat Handle ***************/
  // create a face detection handle
  ret = rockx_create(&face_det_handle, ROCKX_MODULE_FACE_DETECTION_V2, config, 0);
  if (ret != ROCKX_RET_SUCCESS)
  {
    printf("init rockx module ROCKX_MODULE_FACE_DETECTION error %d\n", ret);
    return -1;
  }

  // create a face landmark handle
  ret = rockx_create(&face_5landmarks_handle, ROCKX_MODULE_FACE_LANDMARK_5,
                     config, 0);
  if (ret != ROCKX_RET_SUCCESS)
  {
    printf("init rockx module ROCKX_MODULE_FACE_LANDMARK_68 error %d\n", ret);
    return -1;
  }

  // create a face recognize handle
  ret = rockx_create(&face_recognize_handle, ROCKX_MODULE_FACE_RECOGNIZE,
                     config, 0);
  if (ret != ROCKX_RET_SUCCESS)
  {
    printf("init rockx module ROCKX_MODULE_FACE_RECOGNIZE error %d\n", ret);
    return -1;
  }

  const char *name = argv[1];
  const char *image_path = argv[2];

  rockx_face_feature_t out_feature1;
  rockx_image_t input_image1;
  rockx_image_read(image_path, &input_image1, 1);

  FILE *imageFile = fopen(image_path, "rb"); // 打开名为image.jpg的图片文件(二进制模式)
  if (!imageFile)
  {
    printf("无法打开图片文件.\n");
    return -1;
  }

  fseek(imageFile, 0L, SEEK_END);   // 定位到文件尾部获取文件大小
  long fileSize = ftell(imageFile); // 获取文件大小
  rewind(imageFile);                // 重新定位到文件起始位置
  printf("fileSize = %ld\n", fileSize);

  unsigned char *buffer = new unsigned char[fileSize];                          // 分配足够大小的缓冲区
  size_t bytesRead = fread(buffer, sizeof(unsigned char), fileSize, imageFile); // 从文件中读取图像数据到缓冲区

  run_face_recognize(name, &input_image1, &out_feature1, buffer, bytesRead);
#endif
  return 0;
}

douyin_rv1126_rockx_face_project文件夹

编译完后

打开服务器

使用程序sqlite3_operation_test zhangjiahao face_02.jpg创建数据库

执行程序代码:./rv1126_ffmpeg_main_rockx 0 rtmp://192.168.100.80:1935/live/01

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐