// Copyright 2019 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 "v4l2_stream.h" #include #include #include "control.h" ispp_t rkispp_hw_info; isp_t rkisp_hw_info; namespace easymedia { V4L2Context::V4L2Context(enum v4l2_buf_type cap_type, v4l2_io io_func, const std::string device) : fd(-1), capture_type(cap_type), vio(io_func), started(false) #ifndef NDEBUG , path(device) #endif { const char* dev = device.c_str(); fd = v4l2_open(dev, O_RDWR | O_CLOEXEC, 0); if (fd < 0) { LOG("open %s failed %m\n", dev); } LOG("open %s, fd %d\n", dev, fd); } V4L2Context::~V4L2Context() { if (fd >= 0) { SetStarted(false); v4l2_close(fd); LOGD("close %s, fd %d\n", path.c_str(), fd); } } bool V4L2Context::SetStarted(bool val) { std::lock_guard _lk(mtx); if (started == val) { return true; } enum v4l2_buf_type cap_type = capture_type; unsigned int request = val ? VIDIOC_STREAMON : VIDIOC_STREAMOFF; if (IoCtrl(request, &cap_type) < 0) { LOG("ioctl(%d): %m\n", (int)request); return false; } started = val; return true; } int V4L2Context::IoCtrl(unsigned long int request, void* arg) { if (fd < 0) { errno = EINVAL; return -1; } return V4L2IoCtl(&vio, fd, request, arg); } V4L2MediaCtl::V4L2MediaCtl() { } V4L2MediaCtl::~V4L2MediaCtl() { } int get_ispp_subdevs(struct media_device* device, const char* devpath, ispp_t* ispp_info) { media_entity* entity = NULL; const char* entity_name = NULL; if (!device || !ispp_info || !devpath) { return -1; } strncpy(ispp_info->media_dev_path, devpath, sizeof(ispp_info->media_dev_path)); entity = media_get_entity_by_name(device, "rkispp_m_bypass", strlen("rkispp_m_bypass")); if (entity) { entity_name = media_entity_get_devname(entity); if (entity_name) { strncpy(ispp_info->ispp_m_bypass_path, entity_name, sizeof(ispp_info->ispp_m_bypass_path)); } } entity = media_get_entity_by_name(device, "rkispp_scale0", strlen("rkispp_scale0")); if (entity) { entity_name = media_entity_get_devname(entity); if (entity_name) { strncpy(ispp_info->ispp_scale0_path, entity_name, sizeof(ispp_info->ispp_scale0_path)); } } entity = media_get_entity_by_name(device, "rkispp_scale1", strlen("rkispp_scale1")); if (entity) { entity_name = media_entity_get_devname(entity); if (entity_name) { strncpy(ispp_info->ispp_scale1_path, entity_name, sizeof(ispp_info->ispp_scale1_path)); } } entity = media_get_entity_by_name(device, "rkispp_scale2", strlen("rkispp_scale2")); if (entity) { entity_name = media_entity_get_devname(entity); if (entity_name) { strncpy(ispp_info->ispp_scale2_path, entity_name, sizeof(ispp_info->ispp_scale2_path)); } } return 0; } int get_isp_subdevs(struct media_device* device, const char* devpath, isp_t* isp_info) { media_entity* entity = NULL; const char* entity_name = NULL; if (!device || !isp_info || !devpath) { return -1; } strncpy(isp_info->media_dev_path, (char*)devpath, sizeof(isp_info->media_dev_path)); entity = media_get_entity_by_name(device, "rkisp_mainpath", strlen("rkisp_mainpath")); if (entity) { entity_name = media_entity_get_devname(entity); if (entity_name) { strncpy(isp_info->isp_main_path, (char*)entity_name, sizeof(isp_info->isp_main_path)); } } entity = media_get_entity_by_name(device, "rkisp_selfpath", strlen("rkisp_selfpath")); if (entity) { entity_name = media_entity_get_devname(entity); if (entity_name) { strncpy(isp_info->isp_self_path, (char*)entity_name, sizeof(isp_info->isp_self_path)); } } return 0; } int V4L2MediaCtl::InitHwInfos() { char sys_path[64]; FILE* fp = NULL; struct media_device* device = NULL; uint32_t i = 0; while (i < MAX_MEDIA_INDEX) { snprintf(sys_path, 64, "/dev/media%d", i++); fp = fopen(sys_path, "r"); if (!fp) { continue; } fclose(fp); device = media_device_new(sys_path); /* Enumerate entities, pads and links. */ media_device_enumerate(device); struct media_entity* entity = nullptr; entity = media_get_entity_by_name(device, RKISP_SUBDEV_NAME, strlen(RKISP_SUBDEV_NAME)); if (entity != NULL) { LOG("find %s: %s\n", sys_path, RKISP_SUBDEV_NAME); get_isp_subdevs(device, sys_path, &rkisp_hw_info); goto media_unref; } entity = media_get_entity_by_name(device, RKIISPP_SUBDEV_NAME, strlen(RKIISPP_SUBDEV_NAME)); if (entity != NULL) { LOG("find %s: %s\n", sys_path, RKIISPP_SUBDEV_NAME); get_ispp_subdevs(device, sys_path, &rkispp_hw_info); goto media_unref; } media_unref: media_device_unref(device); } return 0; } V4L2Stream::V4L2Stream(const char* param) : use_libv4l2(false), fd(-1), capture_type(V4L2_BUF_TYPE_VIDEO_CAPTURE) { memset(&vio, 0, sizeof(vio)); std::map params; std::list> req_list; std::string str_libv4l2; req_list.push_back(std::pair(KEY_USE_LIBV4L2, str_libv4l2)); req_list.push_back(std::pair(KEY_DEVICE, device)); req_list.push_back(std::pair(KEY_SUB_DEVICE, sub_device)); std::string cap_type; req_list.push_back(std::pair(KEY_V4L2_CAP_TYPE, cap_type)); int ret = parse_media_param_match(param, params, req_list); if (ret == 0) { return; } if (!str_libv4l2.empty()) { use_libv4l2 = !!std::stoi(str_libv4l2); } if (!cap_type.empty()) capture_type = static_cast(GetV4L2Type(cap_type.c_str())); v4l2_medctl = std::make_shared(); if (v4l2_medctl) { ret = v4l2_medctl->InitHwInfos(); if (ret) { return; } } } int V4L2Stream::Open() { if (!SetV4L2IoFunction(&vio, use_libv4l2)) { return -EINVAL; } if (!sub_device.empty()) { // TODO: } if (!strcmp(device.c_str(), MB_ENTITY_NAME)) { devname = std::string(rkispp_hw_info.ispp_m_bypass_path); } else if (!strcmp(device.c_str(), S0_ENTITY_NAME)) { devname = std::string(rkispp_hw_info.ispp_scale0_path); } else if (!strcmp(device.c_str(), S1_ENTITY_NAME)) { devname = std::string(rkispp_hw_info.ispp_scale1_path); } else if (!strcmp(device.c_str(), S2_ENTITY_NAME)) { devname = std::string(rkispp_hw_info.ispp_scale2_path); } else if (!strcmp(device.c_str(), "rkisp_mainpath")) { devname = std::string(rkisp_hw_info.isp_main_path); } else if (!strcmp(device.c_str(), "rkisp_selfpath")) { devname = std::string(rkisp_hw_info.isp_self_path); } else { devname = device; } v4l2_ctx = std::make_shared(capture_type, vio, devname); if (!v4l2_ctx) { return -ENOMEM; } fd = v4l2_ctx->GetDeviceFd(); if (fd < 0) { v4l2_ctx = nullptr; return -1; } return 0; } int V4L2Stream::Close() { if (v4l2_ctx) { v4l2_ctx->SetStarted(false); v4l2_ctx = nullptr; // release reference } fd = -1; return 0; } int V4L2Stream::IoCtrl(unsigned long int request, ...) { va_list vl; va_start(vl, request); void* arg = va_arg(vl, void*); va_end(vl); switch (request) { case S_SUB_REQUEST: { auto sub = (SubRequest*)arg; return V4L2IoCtl(&vio, fd, sub->sub_request, sub->arg); } case S_STREAM_OFF: { return v4l2_ctx->SetStarted(false) ? 0 : -1; } } return -1; } } // namespace easymedia