/* * Copyright 2020 Rockchip Electronics Co. LTD * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #include #include #include #include #include #include #include #include "mpi_test_utils.h" #include "rk_defines.h" #include "rk_debug.h" #include "rk_mpi_vi.h" #include "rk_mpi_mb.h" #include "rk_mpi_sys.h" #include "rk_mpi_venc.h" #include "argparse.h" #define TEST_VENC_MAX 2 #define TEST_WITH_FD 0 #define TEST_WITH_FD_SWITCH 0 typedef struct _rkTestVencCfg { RK_BOOL bOutDebugCfg; VENC_CHN_ATTR_S stAttr; RK_CHAR dstFilePath[128]; RK_CHAR dstFileName[128]; RK_S32 s32ChnId; FILE *fp; RK_S32 selectFd; } TEST_VENC_CFG; typedef enum rkTestVIMODE_E { TEST_VI_MODE_VI_ONLY = 0, TEST_VI_MODE_BIND_VENC = 1, TEST_VI_MODE_BIND_VENC_MULTI = 2, } TEST_VI_MODE_E; typedef struct _rkMpiVICtx { RK_S32 width; RK_S32 height; RK_S32 devId; RK_S32 pipeId; RK_S32 channelId; RK_S32 loopCountSet; RK_S32 selectFd; VI_DEV_ATTR_S stDevAttr; VI_DEV_BIND_PIPE_S stBindPipe; VI_CHN_ATTR_S stChnAttr; VI_SAVE_FILE_INFO_S stDebugFile; VI_FRAME_S stViFrame; VI_CHN_STATUS_S stChnStatus; TEST_VI_MODE_E enMode; const char *aEntityName; // for venc TEST_VENC_CFG stVencCfg[TEST_VENC_MAX]; VENC_STREAM_S stFrame[TEST_VENC_MAX]; } TEST_VI_CTX_S; static RK_S32 create_venc(TEST_VI_CTX_S *ctx, RK_U32 u32Ch) { VENC_RECV_PIC_PARAM_S stRecvParam; RK_MPI_VENC_CreateChn(u32Ch, &ctx->stVencCfg[u32Ch].stAttr); RK_MPI_VENC_StartRecvFrame(u32Ch, &stRecvParam); return RK_SUCCESS; } void init_venc_cfg(TEST_VI_CTX_S *ctx, RK_U32 u32Ch, RK_CODEC_ID_E enType) { ctx->stVencCfg[u32Ch].stAttr.stVencAttr.enType = enType; ctx->stVencCfg[u32Ch].s32ChnId = u32Ch; ctx->stVencCfg[u32Ch].stAttr.stVencAttr.enPixelFormat = RK_FMT_YUV420SP; ctx->stVencCfg[u32Ch].stAttr.stVencAttr.u32PicWidth = ctx->width; ctx->stVencCfg[u32Ch].stAttr.stVencAttr.u32PicHeight = ctx->height; ctx->stVencCfg[u32Ch].stAttr.stVencAttr.u32VirWidth = ctx->width; ctx->stVencCfg[u32Ch].stAttr.stVencAttr.u32VirHeight = ctx->height; ctx->stVencCfg[u32Ch].stAttr.stVencAttr.u32StreamBufCnt = 5; ctx->stVencCfg[u32Ch].stAttr.stVencAttr.u32BufSize = ctx->width * ctx->height * 3 / 2; } RK_S32 test_vi_poll_event(RK_S32 timeoutMsec, RK_S32 fd) { RK_S32 num_fds = 1; struct pollfd pollFds[num_fds]; RK_S32 ret = 0; RK_ASSERT(fd > 0); memset(pollFds, 0, sizeof(pollFds)); pollFds[0].fd = fd; pollFds[0].events = (POLLPRI | POLLIN | POLLERR | POLLNVAL | POLLHUP); ret = poll(pollFds, num_fds, timeoutMsec); if (ret > 0 && (pollFds[0].revents & (POLLERR | POLLNVAL | POLLHUP))) { RK_LOGE("fd:%d polled error", fd); return -1; } return ret; } static RK_S32 test_vi_bind_venc_loop(TEST_VI_CTX_S *ctx) { MPP_CHN_S stSrcChn, stDestChn[TEST_VENC_MAX]; RK_S32 loopCount = 0; void *pData = RK_NULL; RK_S32 s32Ret = RK_FAILURE; RK_U32 i; RK_U32 u32DstCount = ((ctx->enMode == TEST_VI_MODE_BIND_VENC_MULTI) ? 2 : 1); /* vi init */ // 0. get dev config status s32Ret = RK_MPI_VI_GetDevAttr(ctx->devId, &ctx->stDevAttr); if (s32Ret == RK_ERR_VI_NOT_CONFIG) { // 0-1.config dev s32Ret = RK_MPI_VI_SetDevAttr(ctx->devId, &ctx->stDevAttr); if (s32Ret != RK_SUCCESS) { RK_LOGE("RK_MPI_VI_SetDevAttr %x", s32Ret); goto __FAILED1; } } else { RK_LOGE("RK_MPI_VI_SetDevAttr already"); } // 1.get dev enable status s32Ret = RK_MPI_VI_GetDevIsEnable(ctx->devId); if (s32Ret != RK_SUCCESS) { // 1-2.enable dev s32Ret = RK_MPI_VI_EnableDev(ctx->devId); if (s32Ret != RK_SUCCESS) { RK_LOGE("RK_MPI_VI_EnableDev %x", s32Ret); goto __FAILED1; } // 1-3.bind dev/pipe ctx->stBindPipe.u32Num = ctx->pipeId; ctx->stBindPipe.PipeId[0] = ctx->pipeId; s32Ret = RK_MPI_VI_SetDevBindPipe(ctx->devId, &ctx->stBindPipe); if (s32Ret != RK_SUCCESS) { RK_LOGE("RK_MPI_VI_SetDevBindPipe %x", s32Ret); goto __FAILED2; } } else { RK_LOGE("RK_MPI_VI_EnableDev already"); } // 2.config channel ctx->stChnAttr.stSize.u32Width = ctx->width; ctx->stChnAttr.stSize.u32Height = ctx->height; s32Ret = RK_MPI_VI_SetChnAttr(ctx->pipeId, ctx->channelId, &ctx->stChnAttr); if (s32Ret != RK_SUCCESS) { RK_LOGE("RK_MPI_VI_SetChnAttr %x", s32Ret); goto __FAILED2; } // 3.enable channel s32Ret = RK_MPI_VI_EnableChn(ctx->pipeId, ctx->channelId); if (s32Ret != RK_SUCCESS) { RK_LOGE("RK_MPI_VI_EnableChn %x", s32Ret); goto __FAILED2; } // 4.save debug file if (ctx->stDebugFile.bCfg) { s32Ret = RK_MPI_VI_ChnSaveFile(ctx->pipeId, ctx->channelId, &ctx->stDebugFile); RK_LOGD("RK_MPI_VI_ChnSaveFile %x", s32Ret); } /* venc */ for (i = 0; i < u32DstCount; i++) { // venc init and create init_venc_cfg(ctx, i, RK_VIDEO_ID_AVC); s32Ret = create_venc(ctx, ctx->stVencCfg[i].s32ChnId); if (s32Ret != RK_SUCCESS) { RK_LOGE("create %d ch venc failed", ctx->stVencCfg[i].s32ChnId); return s32Ret; } // bind vi to venc stSrcChn.enModId = RK_ID_VI; stSrcChn.s32DevId = ctx->devId; stSrcChn.s32ChnId = ctx->channelId; stDestChn[i].enModId = RK_ID_VENC; stDestChn[i].s32DevId = i; stDestChn[i].s32ChnId = ctx->stVencCfg[i].s32ChnId; s32Ret = RK_MPI_SYS_Bind(&stSrcChn, &stDestChn[i]); if (s32Ret != RK_SUCCESS) { RK_LOGE("create %d ch venc failed", ctx->stVencCfg[i].s32ChnId); goto __FAILED2; } ctx->stFrame[i].pstPack = reinterpret_cast(malloc(sizeof(VENC_PACK_S))); #if TEST_WITH_FD ctx->stVencCfg[i].selectFd = RK_MPI_VENC_GetFd(ctx->stVencCfg[i].s32ChnId); RK_LOGE("venc chn:%d, ctx->selectFd:%d ", ctx->stVencCfg[i].s32ChnId, ctx->stVencCfg[i].selectFd); #endif } while (loopCount < ctx->loopCountSet) { for (i = 0; i < u32DstCount; i++) { #if TEST_WITH_FD test_vi_poll_event(-1, ctx->stVencCfg[i].selectFd); #endif s32Ret = RK_MPI_VENC_GetStream(ctx->stVencCfg[i].s32ChnId, &ctx->stFrame[i], -1); if (s32Ret == RK_SUCCESS) { if (ctx->stVencCfg[i].bOutDebugCfg) { pData = RK_MPI_MB_Handle2VirAddr(ctx->stFrame[i].pstPack->pMbBlk); fwrite(pData, 1, ctx->stFrame[i].pstPack->u32Len, ctx->stVencCfg[i].fp); fflush(ctx->stVencCfg[i].fp); } RK_LOGE("chn:%d, loopCount:%d wd:%d\n", i, loopCount, ctx->stFrame[i].pstPack->u32Len); s32Ret = RK_MPI_VENC_ReleaseStream(ctx->stVencCfg[i].s32ChnId, &ctx->stFrame[i]); if (s32Ret != RK_SUCCESS) { RK_LOGE("RK_MPI_VENC_ReleaseStream fail %x", s32Ret); } loopCount++; } else { RK_LOGE("RK_MPI_VI_GetChnFrame fail %x", s32Ret); } } usleep(10*1000); } for (i = 0; i < u32DstCount; i++) { s32Ret = RK_MPI_SYS_UnBind(&stSrcChn, &stDestChn[i]); if (s32Ret != RK_SUCCESS) { RK_LOGE("RK_MPI_SYS_UnBind fail %x", s32Ret); } } // 5. disable one chn s32Ret = RK_MPI_VI_DisableChn(ctx->pipeId, ctx->channelId); RK_LOGE("RK_MPI_VI_DisableChn %x", s32Ret); for (i = 0; i < u32DstCount; i++) { s32Ret = RK_MPI_VENC_StopRecvFrame(ctx->stVencCfg[i].s32ChnId); if (s32Ret != RK_SUCCESS) { return s32Ret; } RK_LOGE("destroy enc chn:%d", ctx->stVencCfg[i].s32ChnId); s32Ret = RK_MPI_VENC_DestroyChn(ctx->stVencCfg[i].s32ChnId); if (s32Ret != RK_SUCCESS) { RK_LOGE("RK_MPI_VDEC_DestroyChn fail %x", s32Ret); } } // 6.disable dev(will diabled all chn) __FAILED2: s32Ret = RK_MPI_VI_DisableDev(ctx->devId); RK_LOGE("RK_MPI_VI_DisableDev %x", s32Ret); __FAILED1: for (i = 0; i < u32DstCount; i++) { if (ctx->stFrame[i].pstPack) free(ctx->stFrame[i].pstPack); if (ctx->stVencCfg[i].fp) fclose(ctx->stVencCfg[i].fp); } return s32Ret; } static RK_S32 test_vi_get_release_frame_loop(TEST_VI_CTX_S *ctx) { RK_S32 s32Ret; RK_S32 loopCount = 0; RK_S32 waitTime = 33; /* test use getframe&release_frame */ // 0. get dev config status s32Ret = RK_MPI_VI_GetDevAttr(ctx->devId, &ctx->stDevAttr); if (s32Ret == RK_ERR_VI_NOT_CONFIG) { // 0-1.config dev s32Ret = RK_MPI_VI_SetDevAttr(ctx->devId, &ctx->stDevAttr); if (s32Ret != RK_SUCCESS) { RK_LOGE("RK_MPI_VI_SetDevAttr %x", s32Ret); goto __FAILED1; } } else { RK_LOGE("RK_MPI_VI_SetDevAttr already"); } // 1.get dev enable status s32Ret = RK_MPI_VI_GetDevIsEnable(ctx->devId); if (s32Ret != RK_SUCCESS) { // 1-2.enable dev s32Ret = RK_MPI_VI_EnableDev(ctx->devId); if (s32Ret != RK_SUCCESS) { RK_LOGE("RK_MPI_VI_EnableDev %x", s32Ret); goto __FAILED1; } // 1-3.bind dev/pipe ctx->stBindPipe.u32Num = ctx->pipeId; ctx->stBindPipe.PipeId[0] = ctx->pipeId; s32Ret = RK_MPI_VI_SetDevBindPipe(ctx->devId, &ctx->stBindPipe); if (s32Ret != RK_SUCCESS) { RK_LOGE("RK_MPI_VI_SetDevBindPipe %x", s32Ret); goto __FAILED2; } } else { RK_LOGE("RK_MPI_VI_EnableDev already"); } // 2.config channel ctx->stChnAttr.stSize.u32Width = ctx->width; ctx->stChnAttr.stSize.u32Height = ctx->height; s32Ret = RK_MPI_VI_SetChnAttr(ctx->pipeId, ctx->channelId, &ctx->stChnAttr); if (s32Ret != RK_SUCCESS) { RK_LOGE("RK_MPI_VI_SetChnAttr %x", s32Ret); goto __FAILED2; } // open fd before enable chn will be better #if TEST_WITH_FD ctx->selectFd = RK_MPI_VI_GetChnFd(ctx->pipeId, ctx->channelId); RK_LOGE("ctx->pipeId=%d, ctx->channelId=%d, ctx->selectFd:%d ", ctx->pipeId, ctx->channelId, ctx->selectFd); #endif // 3.enable channel s32Ret = RK_MPI_VI_EnableChn(ctx->pipeId, ctx->channelId); if (s32Ret != RK_SUCCESS) { RK_LOGE("RK_MPI_VI_EnableChn %x", s32Ret); goto __FAILED2; } // 4.save debug file if (ctx->stDebugFile.bCfg) { s32Ret = RK_MPI_VI_ChnSaveFile(ctx->pipeId, ctx->channelId, &ctx->stDebugFile); RK_LOGE("RK_MPI_VI_ChnSaveFile %x", s32Ret); } while (loopCount < ctx->loopCountSet) { #if TEST_WITH_FD_SWITCH if (loopCount % 10 == 0 && ctx->selectFd != -1) { // test close/reopen the fd RK_MPI_VI_CloseChnFd(ctx->pipeId, ctx->channelId); RK_LOGE("close ctx->pipeId=%d, ctx->channelId=%d, ctx->selectFd:%d", ctx->pipeId, ctx->channelId, ctx->selectFd); ctx->selectFd = -1; } else { if (ctx->selectFd < 0) { ctx->selectFd = RK_MPI_VI_GetChnFd(ctx->pipeId, ctx->channelId); RK_LOGE("regetfd ctx->pipeId=%d, ctx->channelId=%d, ctx->selectFd:%d", ctx->pipeId, ctx->channelId, ctx->selectFd); // do not use non-block polling for the first time after switching fd test_vi_poll_event(33, ctx->selectFd); } else { test_vi_poll_event(-1, ctx->selectFd); } } #elif TEST_WITH_FD test_vi_poll_event(-1, ctx->selectFd); #endif // 5.get the frame s32Ret = RK_MPI_VI_GetChnFrame(ctx->pipeId, ctx->channelId, &ctx->stViFrame, waitTime); if (s32Ret == RK_SUCCESS) { void *data = RK_MPI_MB_Handle2VirAddr(ctx->stViFrame.pMbBlk); RK_LOGE("RK_MPI_VI_GetChnFrame ok:data %p loop:%d seq:%d pts:%lld ms", data, loopCount, ctx->stViFrame.s32Seq, ctx->stViFrame.s64PTS/1000); // 6.get the channel status s32Ret = RK_MPI_VI_QueryChnStatus(ctx->pipeId, ctx->channelId, &ctx->stChnStatus); RK_LOGE("RK_MPI_VI_QueryChnStatus ret %x, w:%d,h:%d,enable:%d,lost:%d,framerate:%d,vbfail:%d", s32Ret, ctx->stChnStatus.stSize.u32Width, ctx->stChnStatus.stSize.u32Height, ctx->stChnStatus.bEnable, ctx->stChnStatus.u32LostFrame, ctx->stChnStatus.u32FrameRate, ctx->stChnStatus.u32VbFail); // 7.release the frame s32Ret = RK_MPI_VI_ReleaseChnFrame(ctx->pipeId, ctx->channelId, &ctx->stViFrame); if (s32Ret != RK_SUCCESS) { RK_LOGE("RK_MPI_VI_ReleaseChnFrame fail %x", s32Ret); } loopCount++; } else { RK_LOGE("RK_MPI_VI_GetChnFrame timeout %x", s32Ret); } usleep(10*1000); } // 9. disable one chn s32Ret = RK_MPI_VI_DisableChn(ctx->pipeId, ctx->channelId); RK_LOGE("RK_MPI_VI_DisableChn %x", s32Ret); // 10.disable dev(will diabled all chn) __FAILED2: s32Ret = RK_MPI_VI_DisableDev(ctx->devId); RK_LOGE("RK_MPI_VI_DisableDev %x", s32Ret); __FAILED1: return s32Ret; } static void mpi_vi_test_show_options(const TEST_VI_CTX_S *ctx) { RK_PRINT("cmd parse result:\n"); RK_PRINT("output file open : %d\n", ctx->stDebugFile.bCfg); RK_PRINT("yuv output file name : %s/%s\n", ctx->stDebugFile.aFilePath, ctx->stDebugFile.aFileName); RK_PRINT("enc0 output file path : /%s/%s\n", ctx->stVencCfg[0].dstFilePath, ctx->stVencCfg[0].dstFileName); RK_PRINT("enc1 output file path : /%s/%s\n", ctx->stVencCfg[1].dstFilePath, ctx->stVencCfg[1].dstFileName); RK_PRINT("loop count : %d\n", ctx->loopCountSet); RK_PRINT("ctx->enMode : %d\n", ctx->enMode); RK_PRINT("enMemoryType : %d\n", ctx->stChnAttr.stIspOpt.enMemoryType); RK_PRINT("ctx->aEntityName : %s\n", ctx->stChnAttr.stIspOpt.aEntityName); } static const char *const usages[] = { "./rk_mpi_vi_test [-w 1920 -h 1080 -c 1 -y 1 -l 100 -t 4 -n rkispp_scale0]...", RK_NULL, }; int main(int argc, const char **argv) { RK_S32 i; RK_S32 s32Ret = RK_FAILURE; TEST_VI_CTX_S *ctx; ctx = reinterpret_cast(malloc(sizeof(TEST_VI_CTX_S))); memset(ctx, 0, sizeof(TEST_VI_CTX_S)); ctx->width = 1920; ctx->height = 1080; ctx->devId = 0; ctx->pipeId = ctx->devId; ctx->channelId = 1; ctx->loopCountSet = 100; ctx->enMode = TEST_VI_MODE_BIND_VENC; ctx->stChnAttr.stIspOpt.u32BufCount = 3; ctx->stChnAttr.stIspOpt.enMemoryType = VI_V4L2_MEMORY_TYPE_DMABUF; ctx->stChnAttr.u32Depth = 2; ctx->aEntityName = RK_NULL; ctx->stChnAttr.enPixelFormat = RK_FMT_YUV420SP; ctx->stChnAttr.stFrameRate.s32SrcFrameRate = -1; ctx->stChnAttr.stFrameRate.s32DstFrameRate = -1; RK_LOGE("test running enter!"); struct argparse_option options[] = { OPT_HELP(), OPT_GROUP("basic options:"), OPT_INTEGER('w', "width", &(ctx->width), "get width ", NULL, 0, 0), OPT_INTEGER('h', "height", &(ctx->height), "get height", NULL, 0, 0), OPT_INTEGER('d', "dev", &(ctx->devId), "set dev id", NULL, 0, 0), OPT_INTEGER('p', "pipe", &(ctx->pipeId), "set pipe id", NULL, 0, 0), OPT_INTEGER('c', "channel", &(ctx->channelId), "set channel id", NULL, 0, 0), OPT_INTEGER('l', "loopcount", &(ctx->loopCountSet), "set loopcount", NULL, 0, 0), OPT_INTEGER('o', "output", &(ctx->stDebugFile.bCfg), "output file ", NULL, 0, 0), OPT_INTEGER('m', "mode", &(ctx->enMode), "test mode", NULL, 0, 0), OPT_INTEGER('t', "memorytype", &(ctx->stChnAttr.stIspOpt.enMemoryType), "set the memorytype", NULL, 0, 0), OPT_STRING('n', "name", &(ctx->aEntityName), "set the entityName e.g.(rkispp_scale0 or /dev/video0)", NULL, 0, 0), OPT_END(), }; struct argparse argparse; argparse_init(&argparse, options, usages, 0); argparse_describe(&argparse, "\nselect a test case to run.", "\nuse --help for details."); argc = argparse_parse(&argparse, argc, argv); if (!ctx->width || !ctx->height) { argparse_usage(&argparse); goto __FAILED2; } if (ctx->stDebugFile.bCfg) { if (ctx->enMode == TEST_VI_MODE_BIND_VENC) { ctx->stVencCfg[0].bOutDebugCfg = ctx->stDebugFile.bCfg; } else if (ctx->enMode == TEST_VI_MODE_BIND_VENC_MULTI) { ctx->stVencCfg[0].bOutDebugCfg = ctx->stDebugFile.bCfg; ctx->stVencCfg[1].bOutDebugCfg = ctx->stDebugFile.bCfg; } memcpy(&ctx->stDebugFile.aFilePath, "/data", strlen("/data")); snprintf(ctx->stDebugFile.aFileName, MAX_VI_FILE_PATH_LEN, "test_%d_%d_%d.bin", ctx->devId, ctx->pipeId, ctx->channelId); } for (i = 0; i < ctx->enMode; i++) { if (ctx->stVencCfg[i].bOutDebugCfg) { char name[256] = {0}; memcpy(&ctx->stVencCfg[i].dstFilePath, "data", strlen("data")); snprintf(ctx->stVencCfg[i].dstFileName, sizeof(ctx->stVencCfg[i].dstFileName), "venc_%d.bin", i); snprintf(name, sizeof(name), "/%s/%s", ctx->stVencCfg[i].dstFilePath, ctx->stVencCfg[i].dstFileName); ctx->stVencCfg[i].fp = fopen(name, "wb"); if (ctx->stVencCfg[i].fp == RK_NULL) { RK_LOGE("chn %d can't open file %s in get picture thread!\n", i, name); goto __FAILED; } } } RK_LOGE("test running enter ctx->aEntityName=%s!", ctx->aEntityName); if (ctx->aEntityName != RK_NULL) memcpy(ctx->stChnAttr.stIspOpt.aEntityName, ctx->aEntityName, strlen(ctx->aEntityName)); mpi_vi_test_show_options(ctx); if (RK_MPI_SYS_Init() != RK_SUCCESS) { goto __FAILED; } switch (ctx->enMode) { case TEST_VI_MODE_VI_ONLY: s32Ret = test_vi_get_release_frame_loop(ctx); break; case TEST_VI_MODE_BIND_VENC: case TEST_VI_MODE_BIND_VENC_MULTI: s32Ret = test_vi_bind_venc_loop(ctx); break; default: RK_LOGE("no support such test mode:%d", ctx->enMode); break; } __FAILED: RK_LOGE("test running exit:%d", s32Ret); RK_MPI_SYS_Exit(); __FAILED2: if (ctx) { free(ctx); ctx = RK_NULL; } return 0; }