前言
刚接触RK平台,目前正在学习探索阶段,欢迎朋友们一起讨论,指出文章错误和可以优化的地方;如果想参照文中描述进行编译、执行程序,请先参考阅读rv1126 SDK编译和rv1126 数据流;版本说明,测试使用SDK版本是-0912版本,文中记录的问题,可能在新版本已经解决;文中使用的接口函数,可能老版本没有实现。
备注:后续重新购买了-1212版本SDK,旋转问题已经解决,不需要再修改源码。另外,编译时,需要多带上如下几个库:-lrknn_runtime -lod_share -lrockx -lOpenVX -lVSC -lGAL -lArchModelSw -lNNArchPerf
代码
直接上代码,代码将同一帧图像数据,利用RK平台的RGA功能,分别对图像进行了格式转换、缩放+旋转+格式转换、裁剪+格式转换,得到3路图像输出,并存储成文件。
代码是参考SDK包rv1126_1109/external/rkmedia/test/c_api/rkmedia_vi_rga_test.c
修改而成。
说明:
编译后,执行可能遇到旋转效果无法实现,可以参考后续说明的方法修改;RK平台提供的接口,缩放图的长宽应该是要16的倍数,如果参照rv1126_1109/external/rkmedia/test/c_api/rkmedia_vi_rga_test.c
里缩放输出960x540,会发现存储得到的图像数据有异常。修改输出尺寸为960x544后正常;
// Copyright 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.#include <assert.h>#include <fcntl.h>#include <signal.h>#include <stdbool.h>#include <stdio.h>#include <stdlib.h>#include <time.h>#include <unistd.h>#include <pthread.h>//#include "common/sample_common.h"#include "rkmedia_api.h"#include "rkmedia_venc.h"static bool quit = false;// 信号处理程序static void sigterm_handler(int sig){fprintf(stderr, "signal %d\n", sig);quit = true;}// 图像数据处理线程static void *GetMediaBuffer(void *arg){RGA_CHN rga_chn = *(RGA_CHN *)arg;char save_path[512];//printf("#Start %s thread, rga_chn:%d\n", __func__, rga_chn);sprintf(save_path, "/userdata/output_%d.nv12", rga_chn);//FILE *save_file = fopen(save_path, "w");if (!save_file)printf("ERROR: Open %s failed!\n", save_path);MEDIA_BUFFER mb = NULL;int save_cnt = 0;int recv_len;while (!quit){mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_RGA, rga_chn, 50);if (!mb){if (!quit){continue;}printf("chn-%d:RK_MPI_SYS_GetMediaBuffer get null buffer!\n", rga_chn);break;}recv_len = RK_MPI_MB_GetSize(mb);printf("Get Frame-chn-%d:ptr:%p, fd:%d, size:%zu, mode:%d, channel:%d, ""timestamp:%lld\n",rga_chn,RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetFD(mb), recv_len,RK_MPI_MB_GetModeID(mb), RK_MPI_MB_GetChannelID(mb),RK_MPI_MB_GetTimestamp(mb));if (save_file && (save_cnt < 1)){int rtn = fwrite(RK_MPI_MB_GetPtr(mb), 1, recv_len, save_file);fsync(fileno(save_file));printf("#Save frame-chn-%d:%d to %s, rtn = %d\n", rga_chn, save_cnt, save_path, rtn);save_cnt++;}RK_MPI_MB_ReleaseBuffer(mb);}if (save_file)fclose(save_file);printf("%s-chn-%d - exit\r\n", __func__, rga_chn);return NULL;}//int main(){int ret = -1;//VI_PIPE vi_pipe_0 = 0;VI_CHN vi_chn_1 = 1;//RGA_CHN rga_chn_0 = 0;RGA_CHN rga_chn_1 = 1;RGA_CHN rga_chn_2 = 2;// 初始化mpi sysRK_MPI_SYS_Init();//// 数据源,ISP20的输出:// rkispp_m_bypass, 不支持设置分辨率,不支持缩放, NV12/NV16/YUYV/FBC0/FBC2// rkispp_scale0, max width: 3264,最大支持 8 倍缩放, NV12/NV16/YUYV// rkispp_scale1, max width: 1280,最大支持 8 倍缩放, NV12/NV16/YUYV// rkispp_scale2, max width: 1280,最大支持 8 倍缩放, NV12/NV16/YUYV//VI_CHN_ATTR_S vi_chn_attr;vi_chn_attr.pcVideoNode = "rkispp_scale0";vi_chn_attr.u32BufCnt = 4;vi_chn_attr.u32Width = 1920;vi_chn_attr.u32Height = 1080;vi_chn_attr.enPixFmt = IMAGE_TYPE_NV12;vi_chn_attr.enWorkMode = VI_WORK_MODE_NORMAL;//ret = RK_MPI_VI_SetChnAttr(vi_pipe_0, vi_chn_1, &vi_chn_attr);ret |= RK_MPI_VI_EnableChn(vi_pipe_0, vi_chn_1);if (ret){printf("Create vi[%d] failed! ret=%d\n", vi_chn_1, ret);return -1;}//// 源数据配置//MPP_CHN_S stSrcChn;stSrcChn.enModId = RK_ID_VI;stSrcChn.s32DevId = vi_pipe_0;stSrcChn.s32ChnId = vi_chn_1;//// 输出-0// 格式转换 IMAGE_TYPE_NV12 -> IMAGE_TYPE_YUV420P//RGA_ATTR_S stRgaAttr_0;stRgaAttr_0.bEnBufPool = RK_TRUE;stRgaAttr_0.u16BufPoolCnt = 4;stRgaAttr_0.u16Rotaion = 0;stRgaAttr_0.stImgIn.u32X = 0;stRgaAttr_0.stImgIn.u32Y = 0;stRgaAttr_0.stImgIn.imgType = IMAGE_TYPE_NV12;stRgaAttr_0.stImgIn.u32Width = 1920;stRgaAttr_0.stImgIn.u32Height = 1080;stRgaAttr_0.stImgIn.u32HorStride = 1920;stRgaAttr_0.stImgIn.u32VirStride = 1080;stRgaAttr_0.stImgOut.u32X = 0;stRgaAttr_0.stImgOut.u32Y = 0;stRgaAttr_0.stImgOut.imgType = IMAGE_TYPE_YUV420P;stRgaAttr_0.stImgOut.u32Width = 1920;stRgaAttr_0.stImgOut.u32Height = 1080;stRgaAttr_0.stImgOut.u32HorStride = 1920;stRgaAttr_0.stImgOut.u32VirStride = 1080;//ret = RK_MPI_RGA_CreateChn(rga_chn_0, &stRgaAttr_0);if (ret){printf("Create rga[%d] falied! ret=%d\n", rga_chn_0, ret);goto EXIT_0;}pthread_t read_thread_0;pthread_create(&read_thread_0, NULL, GetMediaBuffer, &rga_chn_0);//MPP_CHN_S stDestChn_0;stDestChn_0.enModId = RK_ID_RGA;stDestChn_0.s32DevId = 0;stDestChn_0.s32ChnId = rga_chn_0;//ret = RK_MPI_SYS_Bind(&stSrcChn, &stDestChn_0);if (ret){printf("Bind vi[1] and rga[%d] failed! ret=%d\n", rga_chn_0, ret);goto EXIT_1;}//// 输出-1// 旋转 270 度// 缩放 1920 -> 960// 缩放 1080 -> 544// 格式转换 IMAGE_TYPE_NV12 -> IMAGE_TYPE_YUV420P//RGA_ATTR_S stRgaAttr_1;stRgaAttr_1.bEnBufPool = RK_TRUE;stRgaAttr_1.u16BufPoolCnt = 4;stRgaAttr_1.u16Rotaion = 270;stRgaAttr_1.stImgIn.u32X = 0;stRgaAttr_1.stImgIn.u32Y = 0;stRgaAttr_1.stImgIn.imgType = IMAGE_TYPE_NV12;stRgaAttr_1.stImgIn.u32Width = 1920;stRgaAttr_1.stImgIn.u32Height = 1080;stRgaAttr_1.stImgIn.u32HorStride = 1920;stRgaAttr_1.stImgIn.u32VirStride = 1080;stRgaAttr_1.stImgOut.u32X = 0;stRgaAttr_1.stImgOut.u32Y = 0;stRgaAttr_1.stImgOut.imgType = IMAGE_TYPE_YUV420P;stRgaAttr_1.stImgOut.u32Width = 544;stRgaAttr_1.stImgOut.u32Height = 960;stRgaAttr_1.stImgOut.u32HorStride = 544;stRgaAttr_1.stImgOut.u32VirStride = 960;//ret = RK_MPI_RGA_CreateChn(rga_chn_1, &stRgaAttr_1);if (ret){printf("Create rga[%d] falied! ret=%d\n", rga_chn_1, ret);goto EXIT_2;}pthread_t read_thread_1;pthread_create(&read_thread_1, NULL, GetMediaBuffer, &rga_chn_1);//MPP_CHN_S stDestChn_1;stDestChn_1.enModId = RK_ID_RGA;stDestChn_1.s32DevId = 0;stDestChn_1.s32ChnId = rga_chn_1;//ret = RK_MPI_SYS_Bind(&stSrcChn, &stDestChn_1);if (ret){printf("Bind vi[1] and rga[%d] failed! ret=%d\n", rga_chn_1, ret);goto EXIT_3;}//// 输出-2// 裁剪 1920 -> 1000, 取中间部分// 裁剪 1080 -> 800, 取中间部分// 格式转换 IMAGE_TYPE_NV12 -> IMAGE_TYPE_YUV420P//RGA_ATTR_S stRgaAttr_2;stRgaAttr_2.bEnBufPool = RK_TRUE;stRgaAttr_2.u16BufPoolCnt = 4;stRgaAttr_2.u16Rotaion = 0;stRgaAttr_2.stImgIn.u32X = 460;stRgaAttr_2.stImgIn.u32Y = 140;stRgaAttr_2.stImgIn.imgType = IMAGE_TYPE_NV12;stRgaAttr_2.stImgIn.u32Width = 1000;stRgaAttr_2.stImgIn.u32Height = 800;stRgaAttr_2.stImgIn.u32HorStride = 1920;stRgaAttr_2.stImgIn.u32VirStride = 1080;stRgaAttr_2.stImgOut.u32X = 0;stRgaAttr_2.stImgOut.u32Y = 0;stRgaAttr_2.stImgOut.imgType = IMAGE_TYPE_YUV420P;stRgaAttr_2.stImgOut.u32Width = 1000;stRgaAttr_2.stImgOut.u32Height = 800;stRgaAttr_2.stImgOut.u32HorStride = 1000;stRgaAttr_2.stImgOut.u32VirStride = 800;//ret = RK_MPI_RGA_CreateChn(rga_chn_2, &stRgaAttr_2);if (ret){printf("Create rga[%d] falied! ret=%d\n", rga_chn_2, ret);goto EXIT_4;}pthread_t read_thread_2;pthread_create(&read_thread_2, NULL, GetMediaBuffer, &rga_chn_2);//MPP_CHN_S stDestChn_2;stDestChn_2.enModId = RK_ID_RGA;stDestChn_2.s32DevId = 0;stDestChn_2.s32ChnId = rga_chn_2;//ret = RK_MPI_SYS_Bind(&stSrcChn, &stDestChn_2);if (ret){printf("Bind vi[1] and rga[%d] failed! ret=%d\n", rga_chn_2, ret);goto EXIT_5;}//// 监听退出信号//printf("%s initial finish\n", __func__);signal(SIGINT, sigterm_handler);//// 等待退出信号while (!quit){usleep(100);}//// 退出//ret = 0;EXIT_6:RK_MPI_SYS_UnBind(&stSrcChn, &stDestChn_2);EXIT_5:RK_MPI_RGA_DestroyChn(rga_chn_2);EXIT_4:RK_MPI_SYS_UnBind(&stSrcChn, &stDestChn_1);EXIT_3:RK_MPI_RGA_DestroyChn(rga_chn_1);EXIT_2:RK_MPI_SYS_UnBind(&stSrcChn, &stDestChn_0);EXIT_1:RK_MPI_RGA_DestroyChn(rga_chn_0);EXIT_0:RK_MPI_VI_DisableChn(vi_pipe_0, vi_chn_1);return ret;}
如果已经完成SDK包的编译,可以参考如下编译该源码:
arm-linux-gnueabihf-gcc -I/home/user/work/sdk/rv1126_1109/external/rkmedia/include/rkmedia -L/home/user/work/sdk/rv1126_1109/buildroot/output/rockchip_rv1126_rv1109/staging/usr/lib -o rkmedia_vi_get_frame_test rkmedia_vi_get_frame_test.c -lpthread -ldl -lm -lpcre -lz -lglib-2.0 -leasymedia -ldrm -lrockchip_mpp -lavformat -lavcodec -lswresample -lavutil -lliveMedia -lgroupsock -lBasicUsageEnvironment -lUsageEnvironment -lasound -lv4l2 -lv4lconvert -lrga -lRKAP_ANR -lRKAP_Common -lRKAP_AEC -lrknn_api -lrockface -lsqlite3 -lmd_share -lrkaiq -lssl -lcrypto
执行的部分输出如下。可以看到图像数据的时间戳是同一个,个人感觉应该可以说明图像输入的源为同一帧图像。
Get Frame-chn-0:ptr:0xa1d2a000, fd:13, size:3133440, mode:16, channel:0, timestamp:3698073427Get Frame-chn-1:ptr:0xa1100000, fd:18, size:783360, mode:16, channel:1, timestamp:3698073427Get Frame-chn-2:ptr:0xa026c000, fd:23, size:1209600, mode:16, channel:2, timestamp:3698073427Get Frame-chn-0:ptr:0xa2612000, fd:10, size:3133440, mode:16, channel:0, timestamp:3698106734Get Frame-chn-1:ptr:0xa1340000, fd:15, size:783360, mode:16, channel:1, timestamp:3698106734Get Frame-chn-2:ptr:0xa05db000, fd:20, size:1209600, mode:16, channel:2, timestamp:3698106734Get Frame-chn-0:ptr:0xa231a000, fd:11, size:3133440, mode:16, channel:0, timestamp:3698140083Get Frame-chn-1:ptr:0xa1280000, fd:16, size:783360, mode:16, channel:1, timestamp:3698140083Get Frame-chn-2:ptr:0xa04b6000, fd:21, size:1209600, mode:16, channel:2, timestamp:3698140083
旋转功能处理
在刚开始测试时,发现无论如何配置旋转参数,输出的图像都无法实现旋转,于是查看了一下rkmedia的源码。在rv1126_1109/external/rkmedia/src/c_api/
文件中发现了一些蹊跷,RK_S32 RK_MPI_RGA_CreateChn(RGA_CHN RgaChn, RGA_ATTR_S *pstRgaAttr)
函数里对输入的旋转参数进行校验后,就把他遗忘了,后续再也没有给他出场的机会:
RK_U16 u16Rotaion = pstRgaAttr->u16Rotaion;if ((u16Rotaion != 0) && (u16Rotaion != 90) && (u16Rotaion != 180) &&(u16Rotaion != 270)) {LOG("ERROR: %s rotation only support: 0/90/180/270!\n", __func__);return -RK_ERR_RGA_ILLEGAL_PARAM;}
所以,对以下源码进行了修改,大概在2373行:
// org code: // PARAM_STRING_APPEND_TO(filter_param, KEY_BUFFER_ROTATE, 0);// modify code: PARAM_STRING_APPEND_TO(filter_param, KEY_BUFFER_ROTATE, u16Rotaion);
修改后,参考rv1126 SDK编译,删除buildroot/output/rockchip_rv1126_rv1109/build/rkmedia
目录,再重新编译,将新编译得到的buildroot/output/rockchip_rv1126_rv1109/staging/usr/lib/libeasymedia.so.1.0.1
文件,替换单板上/usr/lib/libeasymedia.so.1.0.1
。替换后,重新执行测试程序,旋转功能正常。
出现这个问题,可能是因为我的SDK包是某宝上买的吧。