/* * Copyright 2018 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 #include "rk_debug.h" #include "rk_mpi_sys.h" #include "rk_mpi_mb.h" #include "rk_mpi_venc.h" #include "rk_mpi_cal.h" #include "argparse.h" #include "mpi_test_utils.h" #define MAX_TIME_OUT_MS 20 #define TEST_RC_MODE 0 typedef struct _rkMpiVENCCtx { const char *srcFileUri; const char *dstFilePath; RK_U32 u32SrcWidth; RK_U32 u32SrcHeight; RK_U32 u32srcVirWidth; RK_U32 u32srcVirHeight; RK_S32 s32LoopCount; RK_U32 u32ChnIndex; RK_U32 u32ChNum; RK_U32 u32SrcPixFormat; RK_U32 u32DstCodec; RK_U32 u32BufferSize; RK_U32 u32StreamBufCnt; RK_BOOL threadExit; MB_POOL vencPool; } TEST_VENC_CTX_S; static RK_S32 read_with_pixel_width(RK_U8 *pBuf, RK_U32 u32Width, RK_U32 u32Height, RK_U32 u32VirWidth, RK_U32 u32PixWidth, FILE *fp) { RK_U32 u32Row; RK_S32 s32ReadSize; for (u32Row = 0; u32Row < u32Height; u32Row++) { s32ReadSize = fread(pBuf + u32Row * u32VirWidth * u32PixWidth, 1, u32Width * u32PixWidth, fp); if (s32ReadSize != u32Width * u32PixWidth) { RK_LOGE("read file failed expect %d vs %d\n", u32Width * u32PixWidth, s32ReadSize); return RK_FAILURE; } } return RK_SUCCESS; } static RK_S32 read_image(RK_U8 *pVirAddr, RK_U32 u32Width, RK_U32 u32Height, RK_U32 u32VirWidth, RK_U32 u32VirHeight, RK_U32 u32PixFormat, FILE *fp) { RK_U32 u32Row = 0; RK_U32 u32ReadSize = 0; RK_S32 s32Ret = RK_SUCCESS; RK_U8 *pBufy = pVirAddr; RK_U8 *pBufu = pBufy + u32VirWidth * u32VirHeight; RK_U8 *pBufv = pBufu + u32VirWidth * u32VirHeight / 4; switch (u32PixFormat) { case RK_FMT_YUV420SP: { for (u32Row = 0; u32Row < u32Height; u32Row++) { u32ReadSize = fread(pBufy + u32Row * u32VirWidth, 1, u32Width, fp); if (u32ReadSize != u32Width) { return RK_FAILURE; } } for (u32Row = 0; u32Row < u32Height / 2; u32Row++) { u32ReadSize = fread(pBufu + u32Row * u32VirWidth, 1, u32Width, fp); if (u32ReadSize != u32Width) { return RK_FAILURE; } } } break; case RK_FMT_RGB888: case RK_FMT_BGR888: { s32Ret = read_with_pixel_width(pBufy, u32Width, u32Height, u32VirWidth, 3, fp); } break; default : { RK_LOGE("read image do not support fmt %d\n", u32PixFormat); return RK_FAILURE; } break; } return s32Ret; } static RK_S32 check_options(const TEST_VENC_CTX_S *ctx) { if (ctx->srcFileUri == RK_NULL) { goto __FAILED; } if (ctx->u32SrcPixFormat == RK_FMT_BUTT || ctx->u32DstCodec <= RK_VIDEO_ID_Unused || ctx->u32SrcWidth <= 0 || ctx->u32SrcHeight <= 0) { goto __FAILED; } return RK_SUCCESS; __FAILED: return RK_ERR_VENC_ILLEGAL_PARAM; } void* venc_get_stream(void *pArgs) { TEST_VENC_CTX_S *pstCtx = reinterpret_cast(pArgs); void *pData = RK_NULL; RK_S32 s32Ret = RK_SUCCESS; FILE *fp = RK_NULL; char name[256] = {0}; RK_U32 u32Ch = pstCtx->u32ChnIndex; RK_S32 s32StreamCnt = 0; VENC_STREAM_S stFrame; if (pstCtx->dstFilePath != RK_NULL) { mkdir(pstCtx->dstFilePath, 0777); snprintf(name, sizeof(name), "%s/test_%d.bin", pstCtx->dstFilePath, pstCtx->u32ChnIndex); fp = fopen(name, "wb"); if (fp == RK_NULL) { RK_LOGE("chn %d can't open file %s in get picture thread!\n", u32Ch, name); return RK_NULL; } } stFrame.pstPack = reinterpret_cast(malloc(sizeof(VENC_PACK_S))); while (!pstCtx->threadExit) { s32Ret = RK_MPI_VENC_GetStream(u32Ch, &stFrame, -1); if (s32Ret >= 0) { if (stFrame.pstPack->bStreamEnd == RK_TRUE) { RK_LOGI("chn %d reach EOS stream", u32Ch); RK_MPI_VENC_ReleaseStream(u32Ch, &stFrame); break; } s32StreamCnt++; RK_LOGI("get chn %d stream %d", u32Ch, s32StreamCnt); if (pstCtx->dstFilePath != RK_NULL) { pData = RK_MPI_MB_Handle2VirAddr(stFrame.pstPack->pMbBlk); fwrite(pData, 1, stFrame.pstPack->u32Len, fp); fflush(fp); } RK_MPI_VENC_ReleaseStream(u32Ch, &stFrame); } else { if (pstCtx->threadExit) { break; } usleep(1000llu); } } if (stFrame.pstPack) free(stFrame.pstPack); if (fp) fclose(fp); return RK_NULL; } void* venc_send_frame(void *pArgs) { TEST_VENC_CTX_S *pstCtx = reinterpret_cast(pArgs); RK_S32 s32Ret = RK_SUCCESS; RK_U8 *pVirAddr = RK_NULL; FILE *fp = RK_NULL; MB_BLK blk = RK_NULL; RK_S32 s32LoopCount = pstCtx->s32LoopCount; MB_POOL pool = pstCtx->vencPool; RK_U32 u32Ch = pstCtx->u32ChnIndex; RK_S32 s32FrameCount = 0; RK_S32 s32ReachEOS = 0; VIDEO_FRAME_INFO_S stFrame; fp = fopen(pstCtx->srcFileUri, "r"); if (fp == RK_NULL) { RK_LOGE("chn %d can't open file %s!\n", u32Ch, pstCtx->srcFileUri); return RK_NULL; } while (!pstCtx->threadExit) { blk = RK_MPI_MB_GetMB(pool, pstCtx->u32BufferSize, RK_TRUE); if (RK_NULL == blk) { usleep(2000llu); continue; } pVirAddr = reinterpret_cast(RK_MPI_MB_Handle2VirAddr(blk)); s32Ret = read_image(pVirAddr, pstCtx->u32SrcWidth, pstCtx->u32SrcHeight, pstCtx->u32srcVirWidth, pstCtx->u32srcVirHeight, pstCtx->u32SrcPixFormat, fp); if (s32Ret != RK_SUCCESS) { s32ReachEOS = 1; if (s32LoopCount > 0) { s32LoopCount--; RK_LOGI("finish venc count %d\n", pstCtx->s32LoopCount - s32LoopCount); if (s32LoopCount > 0) { s32ReachEOS = 0; RK_MPI_MB_ReleaseMB(blk); fseek(fp, 0L, SEEK_SET); RK_LOGI("seek finish ch %d", u32Ch); continue; } } } RK_MPI_SYS_MmzFlushCache(blk, RK_FALSE); stFrame.stVFrame.pMbBlk = blk; stFrame.stVFrame.u32Width = pstCtx->u32SrcWidth; stFrame.stVFrame.u32Height = pstCtx->u32SrcHeight; stFrame.stVFrame.u32VirWidth = pstCtx->u32srcVirWidth; stFrame.stVFrame.u32VirHeight = pstCtx->u32srcVirHeight; stFrame.stVFrame.enPixelFormat = (PIXEL_FORMAT_E)pstCtx->u32SrcPixFormat; stFrame.stVFrame.u32FrameFlag |= s32ReachEOS ? FRAME_FLAG_SNAP_END : 0; __RETRY: s32Ret = RK_MPI_VENC_SendFrame(u32Ch, &stFrame, -1); if (s32Ret < 0) { if (pstCtx->threadExit) { RK_MPI_MB_ReleaseMB(blk); break; } usleep(10000llu); goto __RETRY; } else { RK_MPI_MB_ReleaseMB(blk); s32FrameCount++; RK_LOGI("chn %d frame %d", u32Ch, s32FrameCount); } if (s32ReachEOS) { RK_LOGI("chn %d reach EOS.", u32Ch); break; } } if (fp) fclose(fp); return RK_NULL; } RK_S32 unit_test_mpi_venc(TEST_VENC_CTX_S *ctx) { RK_S32 s32Ret = RK_SUCCESS; RK_U32 u32Ch = 0; VENC_CHN_ATTR_S stAttr; VENC_RECV_PIC_PARAM_S stRecvParam; VENC_RC_PARAM_S stRcParam; MB_POOL_CONFIG_S stMbPoolCfg; TEST_VENC_CTX_S stVencCtx[VENC_MAX_CHN_NUM]; pthread_t vencThread[VENC_MAX_CHN_NUM]; pthread_t getStreamThread[VENC_MAX_CHN_NUM]; memset(&stAttr, 0, sizeof(VENC_CHN_ATTR_S)); memset(&stRecvParam, 0, sizeof(VENC_RECV_PIC_PARAM_S)); memset(&stRcParam, 0, sizeof(VENC_RC_PARAM_S)); if (ctx->u32BufferSize <= 0) { PIC_BUF_ATTR_S stPicBufAttr; MB_PIC_CAL_S stMbPicCalResult; stPicBufAttr.u32Width = ctx->u32SrcWidth; stPicBufAttr.u32Height = ctx->u32SrcHeight; stPicBufAttr.enPixelFormat = (PIXEL_FORMAT_E)ctx->u32SrcPixFormat; stPicBufAttr.enCompMode = COMPRESS_MODE_NONE; s32Ret = RK_MPI_CAL_COMM_GetPicBufferSize(&stPicBufAttr, &stMbPicCalResult); if (s32Ret != RK_SUCCESS) { RK_LOGE("get picture buffer size failed. err 0x%x", s32Ret); return s32Ret; } ctx->u32BufferSize = stMbPicCalResult.u32MBSize; RK_LOGD("calc picture size: %d", ctx->u32BufferSize); } if (ctx->u32BufferSize > 32 * 1024 * 1024) { RK_LOGE("too large picture size: %d", ctx->u32BufferSize); return RK_FAILURE; } for (u32Ch = 0; u32Ch < ctx->u32ChNum; u32Ch++) { if (ctx->u32ChNum >= 1) { ctx->u32ChnIndex = u32Ch; } #if TEST_RC_MODE stAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR; stAttr.stRcAttr.stH264Cbr.u32Gop = 77; stAttr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 30; stAttr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1; stAttr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 30; stAttr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1; stAttr.stRcAttr.stH264Cbr.u32BitRate = 314400; #endif stAttr.stVencAttr.enType = (RK_CODEC_ID_E)ctx->u32DstCodec; stAttr.stVencAttr.u32Profile = H264E_PROFILE_HIGH; stAttr.stVencAttr.enPixelFormat = (PIXEL_FORMAT_E)ctx->u32SrcPixFormat; stAttr.stVencAttr.u32PicWidth = ctx->u32SrcWidth; stAttr.stVencAttr.u32PicHeight = ctx->u32SrcHeight; if (ctx->u32srcVirWidth <= 0) { ctx->u32srcVirWidth = ctx->u32SrcWidth; } stAttr.stVencAttr.u32VirWidth = ctx->u32srcVirWidth; if (ctx->u32srcVirHeight <= 0) { ctx->u32srcVirHeight = ctx->u32SrcHeight; } stAttr.stVencAttr.u32VirHeight = ctx->u32srcVirHeight; stAttr.stVencAttr.u32StreamBufCnt = ctx->u32StreamBufCnt; stAttr.stVencAttr.u32BufSize = ctx->u32BufferSize; RK_MPI_VENC_CreateChn(u32Ch, &stAttr); #if TEST_RC_MODE stAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264AVBR; stAttr.stRcAttr.stH264Avbr.u32Gop = 99; stAttr.stRcAttr.stH264Avbr.u32SrcFrameRateNum = 25; stAttr.stRcAttr.stH264Avbr.u32SrcFrameRateDen = 1; stAttr.stRcAttr.stH264Avbr.fr32DstFrameRateNum = 25; stAttr.stRcAttr.stH264Avbr.fr32DstFrameRateDen = 1; stAttr.stRcAttr.stH264Avbr.u32BitRate = 614400; RK_MPI_VENC_SetChnAttr(u32Ch, &stAttr); #endif RK_MPI_VENC_StartRecvFrame(u32Ch, &stRecvParam); #if TEST_RC_MODE stRcParam.s32FirstFrameStartQp = 25; stRcParam.stParamH264.u32StepQp = 4; stRcParam.stParamH264.u32MaxQp = 40; stRcParam.stParamH264.u32MinQp = 20; RK_MPI_VENC_SetRcParam(u32Ch, &stRcParam); #endif memset(&stMbPoolCfg, 0, sizeof(MB_POOL_CONFIG_S)); stMbPoolCfg.u64MBSize = ctx->u32BufferSize; stMbPoolCfg.u32MBCnt = 10; stMbPoolCfg.enAllocType = MB_ALLOC_TYPE_DMA; ctx->vencPool = RK_MPI_MB_CreatePool(&stMbPoolCfg); memcpy(&(stVencCtx[u32Ch]), ctx, sizeof(TEST_VENC_CTX_S)); pthread_create(&vencThread[u32Ch], 0, venc_send_frame, reinterpret_cast(&stVencCtx[u32Ch])); pthread_create(&getStreamThread[u32Ch], 0, venc_get_stream, reinterpret_cast(&stVencCtx[u32Ch])); } for (u32Ch = 0; u32Ch < ctx->u32ChNum; u32Ch++) { pthread_join(vencThread[u32Ch], RK_NULL); pthread_join(getStreamThread[u32Ch], RK_NULL); stVencCtx[u32Ch].threadExit = RK_TRUE; RK_MPI_VENC_StopRecvFrame(u32Ch); RK_MPI_VENC_DestroyChn(u32Ch); RK_MPI_MB_DestroyPool(stVencCtx[u32Ch].vencPool); } return RK_SUCCESS; } static const char *const usages[] = { "./rk_mpi_venc_test [-i SRC_PATH] [-w SRC_WIDTH] [-h SRC_HEIGHT]", NULL, }; static void mpi_venc_test_show_options(const TEST_VENC_CTX_S *ctx) { RK_PRINT("cmd parse result:\n"); RK_PRINT("input file name : %s\n", ctx->srcFileUri); RK_PRINT("output file name : %s\n", ctx->dstFilePath); RK_PRINT("src width : %d\n", ctx->u32SrcWidth); RK_PRINT("src height : %d\n", ctx->u32SrcHeight); RK_PRINT("src virWidth : %d\n", ctx->u32srcVirWidth); RK_PRINT("src virHeight : %d\n", ctx->u32srcVirHeight); RK_PRINT("src pixel format : %d\n", ctx->u32SrcPixFormat); RK_PRINT("encode codec type : %d\n", ctx->u32DstCodec); RK_PRINT("loop count : %d\n", ctx->s32LoopCount); RK_PRINT("channel index : %d\n", ctx->u32ChnIndex); RK_PRINT("channel num : %d\n", ctx->u32ChNum); RK_PRINT("output buffer count : %d\n", ctx->u32StreamBufCnt); RK_PRINT("one picture size : %d\n", ctx->u32BufferSize); return; } int main(int argc, const char **argv) { RK_S32 s32Ret = RK_SUCCESS; TEST_VENC_CTX_S ctx; memset(&ctx, 0, sizeof(TEST_VENC_CTX_S)); ctx.s32LoopCount = 1; ctx.u32StreamBufCnt = 8; ctx.u32ChNum = 1; ctx.u32SrcPixFormat = RK_FMT_YUV420SP; ctx.u32DstCodec = RK_VIDEO_ID_AVC; struct argparse_option options[] = { OPT_HELP(), OPT_GROUP("basic options:"), OPT_STRING('i', "input", &(ctx.srcFileUri), "input file name. ", NULL, 0, 0), OPT_STRING('o', "output", &(ctx.dstFilePath), "the directory of encoder output", NULL, 0, 0), OPT_INTEGER('n', "loop_count", &(ctx.s32LoopCount), "loop running count. default(1)", NULL, 0, 0), OPT_INTEGER('w', "width", &(ctx.u32SrcWidth), "input source width. ", NULL, 0, 0), OPT_INTEGER('h', "height", &(ctx.u32SrcHeight), "input source height. ", NULL, 0, 0), OPT_INTEGER('\0', "vir_width", &(ctx.u32srcVirWidth), "input source virWidth.", NULL, 0, 0), OPT_INTEGER('\0', "vir_height", &(ctx.u32srcVirHeight), "input source virHeight.", NULL, 0, 0), OPT_INTEGER('f', "pixel_format", &(ctx.u32SrcPixFormat), "input source pixel format. default(0: NV12).", NULL, 0, 0), OPT_INTEGER('C', "codec", &(ctx.u32DstCodec), "venc encode codec(8:h264, 9:mjpeg, 12:h265,...). default(8)", NULL, 0, 0), OPT_INTEGER('c', "channel_count", &(ctx.u32ChNum), "venc channel count. default(1).", NULL, 0, 0), OPT_INTEGER('\0', "channel_index", &(ctx.u32ChnIndex), "venc channel index. default(0).", NULL, 0, 0), OPT_INTEGER('\0', "enc_buf_cnt", &(ctx.u32StreamBufCnt), "venc encode output buffer count, default(8)", NULL, 0, 0), OPT_INTEGER('\0', "src_pic_size", &(ctx.u32BufferSize), "the size of input single picture", 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); mpi_venc_test_show_options(&ctx); if (check_options(&ctx)) { argparse_usage(&argparse); return RK_FAILURE; } s32Ret = RK_MPI_SYS_Init(); if (s32Ret != RK_SUCCESS) { return s32Ret; } if (unit_test_mpi_venc(&ctx) < 0) { goto __FAILED; } s32Ret = RK_MPI_SYS_Exit(); if (s32Ret != RK_SUCCESS) { return s32Ret; } RK_LOGE("test running success!"); return RK_SUCCESS; __FAILED: RK_MPI_SYS_Exit(); RK_LOGE("test running failed!"); return s32Ret; }