主要流程可分为开启录制,从摄像头获取码流,码流编码,编码保存,本录制程序中使用芯片为RV1126,摄像头为USB摄像头,分辨率为640*480,生图格式为YUYV422。
主要参考代码有原厂中rkmedia_vi_venc_test.c与rkmedia_venc_local_file_test.c两个文件,新文件命名为rkmedia_vi_venc_local_file_test.c
在同原厂SDK同一目录下sdk//external/rkmedia/examples中的CMakeList.txt中添加编译信息如下:
#-------------------------- # rkmedia_vi_venc_local_file_test #-------------------------- add_executable(rkmedia_vi_venc_local_file_test rkmedia_vi_venc_local_file_test.c ${COMMON_SRC}) add_dependencies(rkmedia_vi_venc_local_file_test easymedia) target_link_libraries(rkmedia_vi_venc_local_file_test easymedia) target_include_directories(rkmedia_vi_venc_local_file_test PRIVATE ${CMAKE_SOURCE_DIR}/include) install(TARGETS rkmedia_vi_venc_local_file_test RUNTIME DESTINATION "bin")
主要与rkmedia_vi_venc_test.c与rkmedia_venc_local_file_test.c文件中不同的地方有,
去除帧打包命令:video_packet_cb,并去除rkmedia_venc_local_file_test.c中相关读取文件相关代码,同时以开辟新线程的方式获取venc中编码码流代替vi_venc_test.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. // rkmedia_vi_venc_local_file_test.c #include <assert.h> #include <fcntl.h> #include <getopt.h> #include <signal.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.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 FILE *g_output_file; static RK_S32 g_s32FrameCnt = -1; //退出信号 static void sigterm_handler(int sig) { fprintf(stderr, "signal %d\n", sig); quit = true; } //捕获视频数据保存线程 static void *GetMediaBuffer(void *arg) { char *ot_path = (char *)arg; printf("#Start %s thread, arg:%p, out path: %s\n", __func__, arg, ot_path); FILE *save_file = fopen(ot_path, "w"); if (!save_file) printf("ERROR: Open %s failed!\n", ot_path); MEDIA_BUFFER mb = NULL; while (!quit) { //从VENC通道0中阻塞获取数据 mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, 0, -1); if (!mb) { printf("RK_MPI_SYS_GetMediaBuffer get null buffer!\n"); break; } printf("Get packet:ptr:%p, fd:%d, size:%zu, mode:%d, channel:%d, " "timestamp:%lld\n", RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetFD(mb), RK_MPI_MB_GetSize(mb), RK_MPI_MB_GetModeID(mb), RK_MPI_MB_GetChannelID(mb), RK_MPI_MB_GetTimestamp(mb)); if (save_file) fwrite(RK_MPI_MB_GetPtr(mb), 1, RK_MPI_MB_GetSize(mb), save_file); RK_MPI_MB_ReleaseBuffer(mb); } if (save_file) fclose(save_file); return NULL; } static RK_CHAR optstr[] = "?::a::w:h:c:o:e:d:I:M:"; static const struct option long_options[] = { {"aiq", optional_argument, NULL, 'a'}, {"device_name", required_argument, NULL, 'd'}, {"width", required_argument, NULL, 'w'}, {"height", required_argument, NULL, 'h'}, {"frame_cnt", required_argument, NULL, 'c'}, {"output_path", required_argument, NULL, 'o'}, {"encode", required_argument, NULL, 'e'}, {"camid", required_argument, NULL, 'I'}, {"multictx", required_argument, NULL, 'M'}, {"fps", required_argument, NULL, 'f'}, {"hdr_mode", required_argument, NULL, 'h' + 'm'}, {"vi_buf_cnt", required_argument, NULL, 'b' + 'c'}, {"help", optional_argument, NULL, '?'}, {NULL, 0, NULL, 0}, }; static void print_usage(const RK_CHAR *name) { printf("usage example:\n"); #ifdef RKAIQ printf("\t%s [-a [iqfiles_dir]] [-w 1920] " "[-h 1080]" "[-c 150] " "[-d rkispp_scale0] " "[-e 0] " "[-I 0] " "[-M 0] " "[-o output.h264] \n", name); printf("\t-a | --aiq: enable aiq with dirpath provided, eg:-a " "/oem/etc/iqfiles/, " "set dirpath emtpty to using path by default, without this option aiq " "should run in other application\n"); printf("\t-M | --multictx: switch of multictx in isp, set 0 to disable, set " "1 to enable. Default: 0\n"); printf("\t--fps fps of vi.\n"); printf("\t--hdr_mode [normal hdr2 hdr3].\n"); printf("\t--vi_buf_cnt buffer count of vi.\n"); #else printf("\t%s [-w 1920] " "[-h 1080]" "[-c 150] " "[-I 0] " "[-d rkispp_scale0] " "[-e 0] " "[-o output.h264] \n", name); #endif printf("\t-w | --width: VI width, Default:1920\n"); printf("\t-h | --heght: VI height, Default:1080\n"); printf("\t-c | --frame_cnt: frame number of output, Default:150\n"); printf("\t-I | --camid: camera ctx id, Default 0\n"); printf("\t-d | --device_name set pcDeviceName, Default:rkispp_scale0, " "Option:[rkispp_scale0, rkispp_scale1, rkispp_scale2]\n"); printf( "\t-e | --encode: encode type, Default:h264, Value:h264, h265, mjpeg\n"); printf("\t-o | --output_path: output path, Default:NULL\n"); } int main(int argc, char *argv[]) { RK_U32 u32Width = 1920; RK_U32 u32Height = 1080; RK_CHAR *pDeviceName = "rkispp_scale0"; RK_CHAR *pOutPath = NULL; RK_CHAR *pIqfilesPath = NULL; CODEC_TYPE_E enCodecType = RK_CODEC_TYPE_H264; RK_CHAR *pCodecName = "H264"; RK_S32 s32CamId = 0; RK_U32 u32BufCnt = 3; #ifdef RKAIQ RK_BOOL bMultictx = RK_FALSE; RK_U32 u32Fps = 30; rk_aiq_working_mode_t hdr_mode = RK_AIQ_WORKING_MODE_NORMAL; #endif int c; int ret = 0; while ((c = getopt_long(argc, argv, optstr, long_options, NULL)) != -1) { const char *tmp_optarg = optarg; switch (c) { case 'a': if (!optarg && NULL != argv[optind] && '-' != argv[optind][0]) { tmp_optarg = argv[optind++]; } if (tmp_optarg) { pIqfilesPath = (char *)tmp_optarg; } else { pIqfilesPath = "/oem/etc/iqfiles"; } break; case 'w': u32Width = atoi(optarg); break; case 'h': u32Height = atoi(optarg); break; case 'c': g_s32FrameCnt = atoi(optarg); break; case 'o': pOutPath = optarg; break; case 'd': pDeviceName = optarg; break; case 'e': if (!strcmp(optarg, "h264")) { enCodecType = RK_CODEC_TYPE_H264; pCodecName = "H264"; } else if (!strcmp(optarg, "h265")) { enCodecType = RK_CODEC_TYPE_H265; pCodecName = "H265"; } else if (!strcmp(optarg, "mjpeg")) { enCodecType = RK_CODEC_TYPE_MJPEG; pCodecName = "MJPEG"; } else { printf("ERROR: Invalid encoder type.\n"); return 0; } break; case 'I': s32CamId = atoi(optarg); break; #ifdef RKAIQ case 'M': if (atoi(optarg)) { bMultictx = RK_TRUE; } break; case 'f': u32Fps = atoi(optarg); printf("#u32Fps = %u.\n", u32Fps); break; case 'h' + 'm': if (strcmp(optarg, "normal") == 0) { hdr_mode = RK_AIQ_WORKING_MODE_NORMAL; } else if (strcmp(optarg, "hdr2") == 0) { hdr_mode = RK_AIQ_WORKING_MODE_ISP_HDR2; } else if (strcmp(optarg, "hdr3") == 0) { hdr_mode = RK_AIQ_WORKING_MODE_ISP_HDR3; } else { print_usage(argv[0]); return 0; } printf("#hdr_mode = %u.\n", hdr_mode); break; #endif case 'b' + 'c': u32BufCnt = atoi(optarg); printf("#vi buffer conunt = %u.\n", u32BufCnt); break; case '?': default: print_usage(argv[0]); return 0; } } printf("#Device: %s\n", pDeviceName); printf("#CodecName:%s\n", pCodecName); printf("#Resolution: %dx%d\n", u32Width, u32Height); printf("#Frame Count to save: %d\n", g_s32FrameCnt); printf("#Output Path: %s\n", pOutPath); printf("#CameraIdx: %d\n\n", s32CamId); #ifdef RKAIQ printf("#bMultictx: %d\n\n", bMultictx); printf("#Aiq xml dirpath: %s\n\n", pIqfilesPath); #endif if (pIqfilesPath) { #ifdef RKAIQ SAMPLE_COMM_ISP_Init(s32CamId, hdr_mode, bMultictx, pIqfilesPath); SAMPLE_COMM_ISP_Run(s32CamId); SAMPLE_COMM_ISP_SetFrameRate(s32CamId, u32Fps); #endif } // if (pOutPath) { // g_output_file = fopen(pOutPath, "w"); // if (!g_output_file) { // printf("ERROR: open file: %s fail, exit\n", pOutPath); // return 0; // } // } RK_MPI_SYS_Init(); VI_CHN_ATTR_S vi_chn_attr; vi_chn_attr.pcVideoNode = pDeviceName; vi_chn_attr.u32BufCnt = u32BufCnt; vi_chn_attr.u32Width = u32Width; vi_chn_attr.u32Height = u32Height; vi_chn_attr.enPixFmt = IMAGE_TYPE_YUYV422; vi_chn_attr.enBufType = VI_CHN_BUF_TYPE_MMAP; vi_chn_attr.enWorkMode = VI_WORK_MODE_NORMAL; ret = RK_MPI_VI_SetChnAttr(s32CamId, 0, &vi_chn_attr); ret |= RK_MPI_VI_EnableChn(s32CamId, 0); if (ret) { printf("ERROR: create VI[0] error! ret=%d\n", ret); return 0; } VENC_CHN_ATTR_S venc_chn_attr; memset(&venc_chn_attr, 0, sizeof(venc_chn_attr)); switch (enCodecType) { case RK_CODEC_TYPE_H265: venc_chn_attr.stVencAttr.enType = RK_CODEC_TYPE_H265; venc_chn_attr.stRcAttr.enRcMode = VENC_RC_MODE_H265CBR; venc_chn_attr.stRcAttr.stH265Cbr.u32Gop = 30; venc_chn_attr.stRcAttr.stH265Cbr.u32BitRate = u32Width * u32Height; // frame rate: in 30/1, out 30/1. venc_chn_attr.stRcAttr.stH265Cbr.fr32DstFrameRateDen = 1; venc_chn_attr.stRcAttr.stH265Cbr.fr32DstFrameRateNum = 30; venc_chn_attr.stRcAttr.stH265Cbr.u32SrcFrameRateDen = 1; venc_chn_attr.stRcAttr.stH265Cbr.u32SrcFrameRateNum = 30; break; case RK_CODEC_TYPE_MJPEG: venc_chn_attr.stVencAttr.enType = RK_CODEC_TYPE_MJPEG; venc_chn_attr.stRcAttr.enRcMode = VENC_RC_MODE_MJPEGCBR; venc_chn_attr.stRcAttr.stMjpegCbr.fr32DstFrameRateDen = 1; venc_chn_attr.stRcAttr.stMjpegCbr.fr32DstFrameRateNum = 30; venc_chn_attr.stRcAttr.stMjpegCbr.u32SrcFrameRateDen = 1; venc_chn_attr.stRcAttr.stMjpegCbr.u32SrcFrameRateNum = 30; venc_chn_attr.stRcAttr.stMjpegCbr.u32BitRate = u32Width * u32Height * 8; break; case RK_CODEC_TYPE_H264: default: venc_chn_attr.stVencAttr.enType = RK_CODEC_TYPE_H264; venc_chn_attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR; venc_chn_attr.stRcAttr.stH264Cbr.u32Gop = 30; venc_chn_attr.stRcAttr.stH264Cbr.u32BitRate = u32Width * u32Height; // frame rate: in 30/1, out 30/1. venc_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1; venc_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 30; venc_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1; venc_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 30; break; } venc_chn_attr.stVencAttr.imageType = IMAGE_TYPE_YUYV422; venc_chn_attr.stVencAttr.u32PicWidth = u32Width; venc_chn_attr.stVencAttr.u32PicHeight = u32Height; venc_chn_attr.stVencAttr.u32VirWidth = u32Width; venc_chn_attr.stVencAttr.u32VirHeight = u32Height; venc_chn_attr.stVencAttr.u32Profile = 77; ret = RK_MPI_VENC_CreateChn(0, &venc_chn_attr); if (ret) { printf("ERROR: create VENC[0] error! ret=%d\n", ret); return 0; } MPP_CHN_S stSrcChn; stSrcChn.enModId = RK_ID_VI; stSrcChn.s32DevId = 0; stSrcChn.s32ChnId = 0; MPP_CHN_S stDestChn; stDestChn.enModId = RK_ID_VENC; stDestChn.s32DevId = 0; stDestChn.s32ChnId = 0; ret = RK_MPI_SYS_Bind(&stSrcChn, &stDestChn); if (ret) { printf("ERROR: Bind VI[0] and VENC[0] error! ret=%d\n", ret); return 0; } printf("%s initial finish\n", __func__); signal(SIGINT, sigterm_handler); pthread_t read_thread; pthread_create(&read_thread, NULL, GetMediaBuffer, pOutPath); IMAGE_TYPE_YUYV422}; while (!quit) { usleep(500000); } // if (g_output_file) // fclose(g_output_file); printf("%s exit!\n", __func__); // unbind first ret = RK_MPI_SYS_UnBind(&stSrcChn, &stDestChn); if (ret) { printf("ERROR: UnBind VI[0] and VENC[0] error! ret=%d\n", ret); return 0; } // destroy venc before vi ret = RK_MPI_VENC_DestroyChn(0); if (ret) { printf("ERROR: Destroy VENC[0] error! ret=%d\n", ret); return 0; } // destroy vi ret = RK_MPI_VI_DisableChn(s32CamId, 0); if (ret) { printf("ERROR: Destroy VI[0] error! ret=%d\n", ret); return 0; } if (pIqfilesPath) { #ifdef RKAIQ SAMPLE_COMM_ISP_Stop(s32CamId); #endif } return 0; }
编写完成后,在/sdk目录下运行以下指令:
# SDK根目录,选择环境 source envsetup.sh firefly_rv1126_rv1109 # 重编rkmedia源码 make rkmedia-dirclean && make rkmedia
编译完成后,在SDK/buildroot/output/firefly_rv1126_rv1109/oem/usr/bin目录下找到rkmedia_vi_venc_local_file_test二进制可执行程序,移动到板子某一个目录下,再进行测试。
在开发板存放可执行程序目标终端下输入以下指令:
./rkmedia_vi_venc_local_file_test -w 640 -h 480 -d /dev/video25 -c 30 -e h264 -o /mnt/nfs/output_test.h264
其中参数w
为摄像头输入视频宽,h
为摄像头输入视频高,d
为摄像头设备号,c
为输入视频帧率,e
为编码格式,o
为视频保存路径