/* * 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. * * author: added by * date: 2020-06-08 * module: eptz task node */ #include #include #include #include #include #include #include #include #include #include #include //#include #include #include #include #include #include #include //#include "RTMeshGenerator.h" #include "RTNodeVFilterFec.h" // NOLINT #include "RTMediaBuffer.h" // NOLINT #include "RTNodeCommon.h" #define ISPP_MAX_VIDEO_NODE 60 #define MESH_PATH "/oem/usr/share/mesh" #define MESH_PATH_1080 "/oem/usr/share/mesh1080" #define OPT_FEC_CAMEAR_IDX "cameraIdx" #define RKISPP_CMD_FEC_IN_OUT \ _IOW('V', BASE_VIDIOC_PRIVATE + 10, struct rkispp_fec_in_out) static int fecFd = -1; static pthread_mutex_t fecMutex = PTHREAD_MUTEX_INITIALIZER; #ifdef DEBUG_FEC #include pid_t gettid() { return syscall(SYS_gettid); } #endif int readFileList(const char *basePath) { DIR *dir; int found = -1; struct dirent *ptr; char filename[1000]; if ((dir = opendir(basePath)) == NULL) { RT_LOGE("Open dir error...\n"); return found; } while ((ptr = readdir(dir)) != NULL) { if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0) { /// current dir OR parrent dir continue; } else if (ptr->d_type == 8) { /// file, check video*/name whether has rkispp_fec info if (strcmp(ptr->d_name, "name") == 0) { // RT_LOGD("d_name:%s/%s",basePath,ptr->d_name); memset(filename, '\0', sizeof(filename)); strcpy(filename, basePath); strcat(filename, "/"); strcat(filename, ptr->d_name); FILE *fp = fopen(filename, "rb"); if (fp) { char buf[128]; fgets(buf, 128, fp); // printf("buf=%s\n", buf); if (strstr(buf, "rkispp_fec") != NULL) { printf("found ispp fec node"); found = 1; break; } } } } else if (ptr->d_type == 10) { // TODO: nothing } else if (ptr->d_type == 4) { // TODO: nothing } } closedir(dir); return found; } static int findFecEntry(char *entryName) { int found = -1; char path[128] = {0}; for (int i = 0; i < ISPP_MAX_VIDEO_NODE; i++) { memset(path, 0, sizeof(path)); snprintf(path, sizeof(path), "/sys/class/video4linux/video%d", i); if (0 == access(path, F_OK)) { found = readFileList(path); if (found > 0) { found = i; break; } } } if (found >= 0) { sprintf(entryName, "/dev/video%d", found); } else { RT_LOGE("failed to found fec entry:%d", found); } return found; } static int doFecProcess(struct rkispp_fec_in_out *fec) { pthread_mutex_lock(&fecMutex); int ret = -1; if (fecFd <= 0) { RT_LOGD("***************************\n"); char fecEntryNode[32]; ret = findFecEntry(fecEntryNode); RT_LOGD("fecEntryNode = %s", fecEntryNode); RT_LOGD("***************************\n"); fecFd = open(fecEntryNode, O_RDWR, 0); // findFecNode("/sys/class/video4linux"); } if (fecFd > 0) { ret = ioctl(fecFd, RKISPP_CMD_FEC_IN_OUT, fec); if (ret == -EAGAIN) // try again ret = ioctl(fecFd, RKISPP_CMD_FEC_IN_OUT, fec); } pthread_mutex_unlock(&fecMutex); return ret; } int alloc_drm_buffer(int fd, int width, int height, int bpp, struct drm_buf *buf) { struct drm_mode_create_dumb alloc_arg; struct drm_mode_map_dumb mmap_arg; struct drm_mode_destroy_dumb destory_arg; void *map; int ret; memset(&alloc_arg, 0, sizeof(alloc_arg)); alloc_arg.bpp = bpp; alloc_arg.width = width; alloc_arg.height = height; ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &alloc_arg); if (ret) { printf("failed to create dumb buffer\n"); return ret; } memset(&mmap_arg, 0, sizeof(mmap_arg)); mmap_arg.handle = alloc_arg.handle; ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mmap_arg); if (ret) { printf("failed to create map dumb\n"); ret = -EINVAL; goto destory_dumb; } map = mmap(0, alloc_arg.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, mmap_arg.offset); if (map == MAP_FAILED) { printf("failed to mmap buffer\n"); ret = -EINVAL; goto destory_dumb; } ret = drmPrimeHandleToFD(fd, alloc_arg.handle, 0, &buf->dmabuf_fd); if (ret) { printf("failed to get dmabuf fd\n"); munmap(map, alloc_arg.size); ret = -EINVAL; goto destory_dumb; } buf->size = alloc_arg.size; buf->map = map; destory_dumb: memset(&destory_arg, 0, sizeof(destory_arg)); destory_arg.handle = alloc_arg.handle; drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destory_arg); return ret; } int free_drm_buffer(int fd, struct drm_buf *buf) { if (buf) { close(buf->dmabuf_fd); munmap(buf->map, buf->size); free(buf); } return 0; } int fec_drm_open() { return drmOpen("rockchip", NULL); } void fec_drm_close(int drm_fd) { close(drm_fd); } int fec_open(char *dev) { return open(dev, O_RDWR, 0); } void fec_close(int fd) { close(fd); } int load_file(char *filename, int len, void *outbuf) { if (filename == nullptr) { RT_LOGE("mesh file is null , please check it\n"); return -1; } FILE *fp = fopen(filename, "rb"); int ret = -1; if (fp) { ret = fread(outbuf, 1, len, fp); fclose(fp); } return ret; } RTNodeVFilterFec::RTNodeVFilterFec() : width(1280), height(720), xintMesh(nullptr), xfraMesh(nullptr), yintMesh(nullptr), yfraMesh(nullptr), mProcessor(nullptr) { dumpCnt = 0; mLock = new RtMutex(); std::string path = "/oem/usr/share/mesh/"; // generateMapParms(path, width, height); RT_LOGE("create fec node\n"); } RTNodeVFilterFec::~RTNodeVFilterFec() { free_drm_buffer(drmFd, yfraMesh); yfraMesh = nullptr; free_drm_buffer(drmFd, yintMesh); yintMesh = nullptr; free_drm_buffer(drmFd, xfraMesh); xfraMesh = nullptr; free_drm_buffer(drmFd, xintMesh); xintMesh = nullptr; fec_drm_close(drmFd); rt_safe_delete(mLock); } int RTNodeVFilterFec::allocMeshBufs() { int ret = -1; drmFd = fec_drm_open(); if (drmFd < 0) { printf("failed to open rockchip drm"); goto err_alloc; } meshSize[0] = calFecMeshsize(width, height); meshSize[1] = meshSize[0] * 2; RT_LOGD("\nmesh_size:%d", meshSize[0]); xintMesh = (struct drm_buf *)malloc(sizeof(struct drm_buf)); ret = alloc_drm_buffer(drmFd, meshSize[0] * 2, 1, 8, xintMesh); if (ret) goto err_alloc; RT_LOGD("xint fd:%d size:%d", xintMesh->dmabuf_fd, xintMesh->size); xfraMesh = (struct drm_buf *)malloc(sizeof(struct drm_buf)); ret = alloc_drm_buffer(drmFd, meshSize[0], 1, 8, xfraMesh); if (ret) goto err_alloc; RT_LOGD("xfra fd:%d size:%d", xfraMesh->dmabuf_fd, xfraMesh->size); yintMesh = (struct drm_buf *)malloc(sizeof(struct drm_buf)); ret = alloc_drm_buffer(drmFd, meshSize[0] * 2, 1, 8, yintMesh); if (ret) goto err_alloc; RT_LOGD("yint fd:%d size:%d", yintMesh->dmabuf_fd, yintMesh->size); yfraMesh = (struct drm_buf *)malloc(sizeof(struct drm_buf)); ret = alloc_drm_buffer(drmFd, meshSize[0], 1, 8, yfraMesh); if (ret) goto err_alloc; RT_LOGD("yfra fd:%d size:%d\n", yfraMesh->dmabuf_fd, yfraMesh->size); return 0; err_alloc: free_drm_buffer(drmFd, yfraMesh); yfraMesh = nullptr; free_drm_buffer(drmFd, yintMesh); yintMesh = nullptr; free_drm_buffer(drmFd, xfraMesh); xfraMesh = nullptr; free_drm_buffer(drmFd, xintMesh); xintMesh = nullptr; fec_drm_close(drmFd); return -1; } int RTNodeVFilterFec::calFecMeshsize(int width, int height) { int mesh_size, mesh_left_height; int w = 32 * ((width + 31) / 32); int h = 32 * ((height + 31) / 32); int spb_num = (h + 127) >> 7; int left_height = h & 127; bool density = (width > 1920) ? true : false; int mesh_width = density ? (w / 32 + 1) : (w / 16 + 1); int mesh_height = density ? 9 : 17; if (!left_height) left_height = 128; mesh_left_height = density ? (left_height / 16 + 1) : (left_height / 8 + 1); mesh_size = (spb_num - 1) * mesh_width * mesh_height + mesh_width * mesh_left_height; return mesh_size; } int RTNodeVFilterFec::loadMeshfiles(const char *path) { if (!xintMesh || !xfraMesh || !yintMesh || !yfraMesh) return -1; char filename[64] = {0}; int nRead = 0; snprintf(filename, sizeof(filename), "%s/meshxi_level%d", path, cameraIdx); nRead = load_file(filename, meshSize[1], xintMesh->map); RT_LOGD("mesh file: %s %d (%d)", filename, nRead, meshSize[1]); if (nRead <= 0) goto err_read; snprintf(filename, sizeof(filename), "%s/meshxf_level%d", path, cameraIdx); nRead = load_file(filename, meshSize[0], xfraMesh->map); RT_LOGD("mesh file: %s %d(%d)", filename, nRead, meshSize[0]); if (nRead <= 0) goto err_read; snprintf(filename, sizeof(filename), "%s/meshyi_level%d", path, cameraIdx); nRead = load_file(filename, meshSize[1], yintMesh->map); RT_LOGD("mesh file: %s %d", filename, nRead); if (nRead <= 0) goto err_read; snprintf(filename, sizeof(filename), "%s/meshyf_level%d", path, cameraIdx); nRead = load_file(filename, meshSize[0], yfraMesh->map); if (nRead <= 0) goto err_read; RT_LOGD("mesh file: %s %d", filename, nRead); return 0; err_read: RT_LOGE("failed to read mesh files %s\n", path); return -1; } RT_RET RTNodeVFilterFec::open(RTTaskNodeContext *context) { RtMetaData *inputMeta = context->options(); char fecEntryNode[32]; RT_RET err = RT_OK; int ret = 0; fpsCtr = 0; fpsAbandonSet = 0; fpsInCount = 0; fpsAbandonCount = 0; cameraIdx = 0; inputMeta->findInt32(OPT_FEC_CAMEAR_IDX, &cameraIdx); inputMeta->findInt32("opt_width", &width); inputMeta->findInt32("opt_height", &height); inputMeta->findInt32("opt_fps_ctr", &fpsCtr); RT_LOGE("fec mesh files: %s cameraIdx=%d width=%d height=%d fpsCtr=%d\n", MESH_PATH, cameraIdx, width, height, fpsCtr); ret = findFecEntry(fecEntryNode); if (ret >= 0) { // fpsAbandonSet = fpsCtr; ret = allocMeshBufs(); if (ret == 0) { if (width == 1280) loadMeshSuccess = loadMeshfiles(MESH_PATH); else loadMeshSuccess = loadMeshfiles(MESH_PATH_1080); } else { RT_LOGE("failed to allock buffers for mesh files\n"); } } else { mProcessor = new RTFECProcessor(); } return err; } RT_RET RTNodeVFilterFec::close(RTTaskNodeContext *context) { RT_RET err = RT_OK; if (mProcessor) delete mProcessor; mProcessor = NULL; return err; } RT_RET RTNodeVFilterFec::doGpuProcess(RTTaskNodeContext *context) { // RT_LOGE("*******%d*", (int)gettid()); RT_RET err = RT_OK; RTMediaBuffer *inputBuffer = RT_NULL; RTMediaBuffer *outputBuffer = RT_NULL; RTFECInfo mInfos; int ret = 0; outputBuffer = context->dequeOutputBuffer(); if (outputBuffer == RT_NULL) { RT_LOGE("outputBuffer = RT_NULL"); return RT_OK; } inputBuffer = context->dequeInputBuffer(); if (inputBuffer == RT_NULL) { RT_LOGE("inputBuffer = RT_NULL"); return err; } if (mProcessor) { mInfos.inputBuffer = inputBuffer; mInfos.outputBuffer = outputBuffer; mInfos.inW = width; mInfos.inH = height; mInfos.outW = width; mInfos.outH = height; mInfos.inFormat = DRM_FORMAT_NV12; mInfos.outFormat = DRM_FORMAT_NV12; mInfos.context = context; mInfos.mtx = &fecLock; mInfos.cv = &cv; mProcessor->process(mInfos); std::unique_lock lck(fecLock); cv.wait(lck); } else { // TODO: send inputBuffer to next node ... inputBuffer = context->dequeInputBuffer(); inputBuffer->getMetaData()->setInt32(OPT_FILTER_WIDTH, width); inputBuffer->getMetaData()->setInt32(OPT_FILTER_HEIGHT, height); inputBuffer->getMetaData()->setCString(OPT_STREAM_FMT_IN, "image:nv12"); inputBuffer->getMetaData()->setInt32(kKeyFrameW, width); inputBuffer->getMetaData()->setInt32(kKeyFrameH, height); context->queueOutputBuffer(inputBuffer); } RT_LOGD("end"); return err; } RT_RET RTNodeVFilterFec::process(RTTaskNodeContext *context) { RT_RET err = RT_OK; RTMediaBuffer *inputBuffer = RT_NULL; RTMediaBuffer *outputBuffer = RT_NULL; RT_BOOL doFec = RT_TRUE; int ret = 0; RtMutex::RtAutolock autoLock(mLock); if (mProcessor) { doGpuProcess(context); return err; } if (!xintMesh || !xfraMesh || !yintMesh || !yfraMesh) return err; if (fpsCtr) { if (fpsAbandonCount >= fpsAbandonSet) { fpsAbandonCount = 0; } else { doFec = RT_FALSE; if (!context->inputIsEmpty()) { fpsAbandonCount++; inputBuffer = context->dequeInputBuffer(); inputBuffer->release(); } } } if (doFec) { if (!context->inputIsEmpty() && loadMeshSuccess == 0) { outputBuffer = context->dequeOutputBuffer(); if (outputBuffer == RT_NULL) { RT_LOGD("outputBuffer = RT_NULL"); return RT_OK; } inputBuffer = context->dequeInputBuffer(); struct rkispp_fec_in_out fec; fec.width = width; fec.height = height; fec.in_fourcc = V4L2_PIX_FMT_NV12; fec.out_fourcc = V4L2_PIX_FMT_NV12; fec.in_pic_fd = inputBuffer->getFd(); fec.out_pic_fd = outputBuffer->getFd(); fec.mesh_xint_fd = xintMesh->dmabuf_fd; fec.mesh_xfra_fd = xfraMesh->dmabuf_fd; fec.mesh_yint_fd = yintMesh->dmabuf_fd; fec.mesh_yfra_fd = yfraMesh->dmabuf_fd; outputBuffer->getMetaData()->setInt32(OPT_FILTER_WIDTH, width); outputBuffer->getMetaData()->setInt32(OPT_FILTER_HEIGHT, height); outputBuffer->getMetaData()->setCString(OPT_STREAM_FMT_IN, "image:nv12"); outputBuffer->getMetaData()->setInt32(kKeyFrameW, width); outputBuffer->getMetaData()->setInt32(kKeyFrameH, height); ret = doFecProcess(&fec); if (ret == 0) { context->queueOutputBuffer(outputBuffer); inputBuffer->release(); } else { RT_LOGE("failed to do fec"); inputBuffer->getMetaData()->setInt32(OPT_FILTER_WIDTH, width); inputBuffer->getMetaData()->setInt32(OPT_FILTER_HEIGHT, height); inputBuffer->getMetaData()->setCString(OPT_STREAM_FMT_IN, "image:nv12"); inputBuffer->getMetaData()->setInt32(kKeyFrameW, width); inputBuffer->getMetaData()->setInt32(kKeyFrameH, height); context->queueOutputBuffer(inputBuffer); outputBuffer->release(); // RT_LOGD("doFilter fail, release inputBuffer"); err = RT_OK; } } else if (!context->inputIsEmpty()) { // TODO: send inputBuffer to next node ... inputBuffer = context->dequeInputBuffer(); inputBuffer->getMetaData()->setInt32(OPT_FILTER_WIDTH, width); inputBuffer->getMetaData()->setInt32(OPT_FILTER_HEIGHT, height); inputBuffer->getMetaData()->setCString(OPT_STREAM_FMT_IN, "image:nv12"); inputBuffer->getMetaData()->setInt32(kKeyFrameW, width); inputBuffer->getMetaData()->setInt32(kKeyFrameH, height); context->queueOutputBuffer(inputBuffer); if (loadMeshSuccess != 0) { if (width == 1280) loadMeshSuccess = loadMeshfiles(MESH_PATH); else loadMeshSuccess = loadMeshfiles(MESH_PATH_1080); } } else { RT_LOGD("found no input buffer"); err = RT_OK; } } return err; } RT_RET RTNodeVFilterFec::invokeInternal(RtMetaData *meta) { if (fpsCtr) { const char *command; meta->findCString(kKeyPipeInvokeCmd, &command); RT_LOGD("invoke(%s) internally.", command); RTSTRING_SWITCH(command) { RTSTRING_CASE("nn_fps_set") : meta->findInt32("nn_fps_set", &fpsAbandonSet); RT_LOGE("fpsAbandonSet %d", fpsAbandonSet); break; default: RT_LOGD("unsupported command=%d", command); break; } } return RT_OK; } static RTTaskNode *createFecFilter() { return new RTNodeVFilterFec(); } /***************************************** * register node stub to RTTaskNodeFactory *****************************************/ RTNodeStub node_stub_filter_fec{ .mUid = MKTAG('f', 'e', 'c', 'p'), .mName = NODE_NAME_RK_FEC, .mVersion = "v1.0", .mCreateObj = createFecFilter, .mCapsSrc = { "video/x-raw", RT_PAD_SRC, RT_MB_TYPE_VFRAME, {RT_NULL, RT_NULL}, }, .mCapsSink = { "video/x-raw", RT_PAD_SINK, RT_MB_TYPE_VFRAME, {RT_NULL, RT_NULL} }, }; RT_NODE_FACTORY_REGISTER_STUB(node_stub_filter_fec);