若该文为原创文章,转载请注明原文出处。
一、介绍
学习DRM的目的是想做类似NVR显示多路实时流,通过勇哥(Marc)的指导,大概流程是通过Zlmedia拉流,RK3568的MPP解码,DRM显示,可以使用HDMI或DIS屏幕,所以为了后面的实现,先测试摄像头采集DRM显示。
二、移植过程
测试过程在笔记八里,如有不懂的,请看笔迹八。
移植过程参考GitHub - MontaukLaw/cvv_test
里面有DRM显示两个屏的例子。
程序是以正点原子里的rknn_yolov5_demo例程修改。主要修改几个地方。
大概流程:
1、初始化屏幕 drm_dis_init();
2、opencv打开摄像头cv::VideoCapture cap(0);
3、循环读数据并显示
cap.read(orig_img);
draw_lcd_screen_rgb_960((uint8_t *)img.data, 720 * 1280 * 3);
代码:
main.cc
// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved.
//
// 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.
/*-------------------------------------------
Includes
-------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <dlfcn.h>
#include <vector>
#include <string>
#include "drm_func.h"
#include "rga_func.h"
#include "rknn_api.h"
#include "yolo.h"
#include "RgaUtils.h"
#include "im2d.h"
#include "opencv2/core/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include <opencv2/opencv.hpp>
#include "rga.h"
#include <xf86drm.h>
#include <xf86drmMode.h>
#include "dev.h"
#include "bo.h"
static sp_dev *mDev;
#define OUTPUT_DEVICE_LCD 1
static drmModeConnectorPtr lcdConnectorPtr = nullptr;
static struct sp_crtc *lcdCRPtr;
static drmModeEncoderPtr lcdEncoderPtr = nullptr;
static uint32_t lcdCrtcId = 0;
static drmModeModeInfoPtr lcdModInfoPtr;
static void get_connector(uint8_t outpuDevice)
{
int i, j = 0;
int ret = 0;
printf("mDev->num_connectors = %d\n", mDev->num_connectors);
for (j = 0; j < mDev->num_connectors; j++)
{
// name 是分辨率信息
printf("connector name:%d\n", j);
printf("connector_type:%d\n", j);
printf("connector_type_id:%d\n", j);
printf("connector status:%d\n", j);
// 对应不同的输出设备, 指定不同的connector跟encoder
if (outpuDevice == OUTPUT_DEVICE_LCD)
{
if (mDev->connectors[j]->connector_type == DRM_MODE_CONNECTOR_DSI &&
mDev->connectors[j]->connection == DRM_MODE_CONNECTED)
{
lcdConnectorPtr = mDev->connectors[j];
}
}
}
}
static void get_encoder(uint8_t outpuDevice)
{
int i;
for (i = 0; i < mDev->num_encoders; i++)
{
if (outpuDevice == OUTPUT_DEVICE_LCD)
{
if (mDev->encoders[i]->encoder_type == DRM_MODE_ENCODER_DSI)
{
lcdEncoderPtr = mDev->encoders[i];
lcdCrtcId = lcdEncoderPtr->crtc_id;
}
}
}
}
static void get_crtc()
{
int j;
printf("lcd crtc id:%d\n", lcdCrtcId);
for (j = 0; j < mDev->num_crtcs; j++)
{
printf("encoderPtr->crtc_id:%d\n", mDev->crtcs[j].crtc->crtc_id);
printf("mode_valid:%d\n", mDev->crtcs[j].crtc->mode_valid);
printf("mode_name:%s\n", mDev->crtcs[j].crtc->mode.name);
if (mDev->crtcs[j].crtc->crtc_id == lcdCrtcId && mDev->crtcs[j].crtc->mode_valid)
{
lcdCRPtr = &mDev->crtcs[j];
}
}
}
static int init_screens()
{
int ret = 0;
// 获取lcd connector
get_connector(OUTPUT_DEVICE_LCD);
if (!lcdConnectorPtr)
{
printf("failed to get hdmi connector or encoder.\n");
return -1;
}
printf("lcd connector id:%d\n", lcdConnectorPtr->connector_id);
// 获取lcd encoder
get_encoder(OUTPUT_DEVICE_LCD);
if (!lcdEncoderPtr)
{
printf("failed to get encoder.\n");
return -2;
}
printf("lcd encoder id:%d\n", lcdEncoderPtr->encoder_id);
// 获取一下显示分辨率之类
lcdModInfoPtr = &lcdConnectorPtr->modes[0];
// 把connector的encoder id赋值为encoder的id
lcdConnectorPtr->encoder_id = lcdEncoderPtr->encoder_id;
// 获取lcd crtc
get_crtc();
if (!lcdCRPtr)
{
printf("failed to get crtc.\n");
return -3;
}
if (lcdCRPtr->scanout)
{
printf("crtc already in use\n");
return -4;
}
printf("lcd crtc id:%d\n", lcdCRPtr->crtc->crtc_id);
// allset
// 获取bo, 只需要输入分辨率即可.
lcdCRPtr->scanout = create_sp_bo(mDev, lcdModInfoPtr->hdisplay, lcdModInfoPtr->vdisplay, 24, 32, DRM_FORMAT_XRGB8888, 0);
if (!lcdCRPtr->scanout)
{
printf("failed to create new scanout bo\n");
return -5;
}
printf("fill test color\n");
fill_bo(lcdCRPtr->scanout, 0xff, 0xff, 0x0, 0x0);
ret = drmModeSetCrtc(mDev->fd, lcdEncoderPtr->crtc_id, lcdCRPtr->scanout->fb_id, 0, 0, &lcdConnectorPtr->connector_id, 1, lcdModInfoPtr);
if (ret)
{
printf("failed to set crtc mode ret=%d\n", ret);
return -6;
}
lcdCRPtr->crtc = drmModeGetCrtc(mDev->fd, lcdCRPtr->crtc->crtc_id);
memcpy(&lcdCRPtr->crtc->mode, lcdModInfoPtr, sizeof(*lcdModInfoPtr));
return 0;
}
int drm_dis_init(void)
{
int ret = 0;
int i = 0;
printf("create sp dev\n");
// 创建显示设备
mDev = create_sp_dev();
if (!mDev)
{
printf("failed to exec create_sp_dev.\n");
return -10;
}
printf("init_screen\n");
// 初始化屏幕
ret = init_screens();
if (ret != 0)
{
printf("failed to exec initialize_screens.\n");
return -11;
}
return 0;
}
void draw_lcd_screen_rgb_960(uint8_t *data, uint32_t dataSize)
{
uint32_t colIdx = 0;
uint32_t rowIdx = 0;
uint8_t *dataPtr = data;
for (rowIdx = 0; rowIdx < 1280; rowIdx++)
{
uint8_t *rowPtr = (uint8_t *)lcdCRPtr->scanout->map_addr + rowIdx * lcdCRPtr->scanout->pitch;
for (colIdx = 0; colIdx < 720; colIdx++)
{
uint8_t *pixel = rowPtr + colIdx * 4;
pixel[0] = *dataPtr;
dataPtr++;
pixel[1] = *dataPtr;
dataPtr++;
pixel[2] = *dataPtr;
dataPtr++;
pixel[3] = 0xff;
}
}
}
int main(int argc, char **argv)
{
int i,j;
drm_dis_init();
cv::VideoCapture cap(0);
if (!cap.isOpened())
{
std::cout << "无法打开摄像头" << std::endl;
return -1;
}
cap.set(cv::CAP_PROP_FRAME_WIDTH, 1280);
cap.set(cv::CAP_PROP_FRAME_HEIGHT, 720);
// using opencv
using namespace cv;
using namespace std;
cv::Mat orig_img;
cv::Mat img;
char data_u8[4];
while(1)
{
cap.read(orig_img);
cv::rotate(orig_img, orig_img, ROTATE_90_COUNTERCLOCKWISE);
cv::cvtColor(orig_img, img, cv::COLOR_BGR2RGB);
draw_lcd_screen_rgb_960((uint8_t *)img.data, 720 * 1280 * 3);
}
exit(0);
}
这里需要注意的是显示部分
显示是32位的RGBA,所以需要封装一下,初始化得到了map_addr映射地址,直接填充数据,就会显示。
使用正点原子需要先关闭QT应用程序,不然运行一会就会退出。
CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
project(rknn_yolov5v7x_demo)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_SKIP_INSTALL_RPATH FALSE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
# rknn api
set(PLATFORM RK3566_3568)
set(LIB_ARCH aarch64)
set(RKNN_API_PATH ${CMAKE_CURRENT_SOURCE_DIR}/libs/rknpu2/runtime/RK356X/Linux/librknn_api)
set(RKNN_RT_LIB ${RKNN_API_PATH}/aarch64/librknnrt.so)
include_directories(${RKNN_API_PATH}/include)
# drm
set(DRM_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libs/common/drm)
set(DRM_RT_LIB ${CMAKE_CURRENT_SOURCE_DIR}/libs/libdrm/lib/libdrm.so)
include_directories(${DRM_DIR}/include)
include_directories(${DRM_DIR}/include/libdrm)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libs/rkdrm/)
#rga
set(RGA_PATH ${CMAKE_CURRENT_SOURCE_DIR}/libs/librga)
set(RGA_LIB ${RGA_PATH}/libs/Linux/gcc-aarch64/librga.so)
include_directories(${RGA_PATH}/include)
set(OPENCV_LIBS opencv_core opencv_flann opencv_videoio opencv_video opencv_highgui opencv_imgcodecs opencv_imgproc)
set(TOOLCHAIN_DIR /opt/atk-dlrk356x-toolchain)
set(CMAKE_C_COMPILER ${TOOLCHAIN_DIR}/usr/bin/aarch64-buildroot-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_DIR}/usr/bin/aarch64-buildroot-linux-gnu-c++)
set(SYSROOT ${TOOLCHAIN_DIR}/aarch64-buildroot-linux-gnu//sysroot/usr/include)
set(CMAKE_SYSROOT ${TOOLCHAIN_DIR}/aarch64-buildroot-linux-gnu/sysroot)
include_directories(${SYSROOT})
include_directories(${SYSROOT}/opencv4)
set(CMAKE_INSTALL_RPATH "lib")
# rknn_yolo_demo
include_directories( ${CMAKE_SOURCE_DIR}/include)
add_executable(rknn_yolo_demo
src/drm_func.c
src/rga_func.c
src/yolo.cc
src/main.cc
src/bo.cc
src/dev.cc
src/modeset.cc
)
target_link_libraries(rknn_yolo_demo
${RKNN_RT_LIB}
${RGA_LIB}
${OPENCV_LIBS}
${DRM_RT_LIB}
dl
)
# install target and libraries
set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install/atk_rknn_yolo_v5_demo)
install(TARGETS rknn_yolo_demo DESTINATION ./)
install(PROGRAMS ${RKNN_RT_LIB} DESTINATION lib)
install(PROGRAMS ${RGA_LIB} DESTINATION lib)
install(DIRECTORY model DESTINATION ./)
编译后,把可执行文件上传到板子运行后,测试正常。
如有侵权,或需要完整代码,请及时联系博主。