Browse Source

libcamera-apps: add libcamera-apps package from raspberrypi project to test libcamera

1. add starfive support
2. add select camera options
3. add FbPreview support YUYV/YVYU display
4. add DrmPreview support NV12 display

Signed-off-by: sw.multimedia <sw.multimedia@starfivetech.com>
Signed-off-by: andy.hu <andy.hu@starfivetech.com>
andy.hu 2 years ago
parent
commit
3b56209386

+ 1 - 0
package/starfive/Config.in

@@ -5,3 +5,4 @@ source "package/starfive/sf-gst-omx/Config.in"
 source "package/starfive/sf-omx-il/Config.in"
 source "package/starfive/v4l2_test/Config.in"
 source "package/starfive/pp_test/Config.in"
+source "package/starfive/libcamera-apps/Config.in"

+ 489 - 0
package/starfive/libcamera-apps/0001-add-starfive-support.patch

@@ -0,0 +1,489 @@
+From 6abadc7414f0fdfea160374326cea32bd2b40d7d Mon Sep 17 00:00:00 2001
+From: sw.multimedia <sw.multimedia@starfivetech.com>
+Date: Fri, 15 Oct 2021 10:55:01 +0800
+Subject: [PATCH] add starfive support
+
+---
+ CMakeLists.txt         |   2 +-
+ core/CMakeLists.txt    |   2 +-
+ core/libcamera_app.cpp |  38 ++++--
+ core/options.hpp       |   3 +
+ preview/CMakeLists.txt |   9 ++
+ preview/fb_preview.cpp | 289 +++++++++++++++++++++++++++++++++++++++++
+ preview/preview.cpp    |   7 +-
+ 7 files changed, 338 insertions(+), 12 deletions(-)
+ create mode 100644 preview/fb_preview.cpp
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 2293211..253ec8d 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -12,7 +12,7 @@ endif()
+ set (CMAKE_EXPORT_COMPILE_COMMANDS ON)
+ set (CMAKE_CXX_STANDARD 17)
+ set (CMAKE_CXX_FLAGS "-Wall -Wextra -pedantic -Wno-unused-parameter -faligned-new")
+-add_definitions(-Werror)
++# add_definitions(-Werror)
+ add_definitions(-Wfatal-errors)
+ add_definitions(-D_FILE_OFFSET_BITS=64)
+ 
+diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt
+index 1618541..84316b2 100644
+--- a/core/CMakeLists.txt
++++ b/core/CMakeLists.txt
+@@ -11,6 +11,6 @@ add_library(libcamera_app libcamera_app.cpp post_processor.cpp version.cpp)
+ add_dependencies(libcamera_app VersionCpp)
+ 
+ set_target_properties(libcamera_app PROPERTIES PREFIX "" IMPORT_PREFIX "")
+-target_link_libraries(libcamera_app pthread preview ${LIBCAMERA_LINK_LIBRARIES} ${Boost_LIBRARIES} post_processing_stages)
++target_link_libraries(libcamera_app pthread preview ${LIBCAMERA_LIBRARIES} ${Boost_LIBRARIES} post_processing_stages)
+ 
+ install(TARGETS libcamera_app LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+diff --git a/core/libcamera_app.cpp b/core/libcamera_app.cpp
+index 7d4e28f..3c4d862 100644
+--- a/core/libcamera_app.cpp
++++ b/core/libcamera_app.cpp
+@@ -134,7 +134,12 @@ void LibcameraApp::ConfigureViewfinder()
+ 	}
+ 
+ 	// Now we get to override any of the default settings from the options_->
+-	configuration_->at(0).pixelFormat = libcamera::formats::YUV420;
++	std::cout << "viewfinder pixelformat: " << options_->pixelformat << std::endl;
++	if (options_->pixelformat.length())
++		configuration_->at(0).pixelFormat =
++			libcamera::PixelFormat::fromString(options_->pixelformat);
++	else
++		configuration_->at(0).pixelFormat = libcamera::formats::YUV420;
+ 	configuration_->at(0).size = size;
+ 
+ 	if (have_lores_stream)
+@@ -143,7 +148,11 @@ void LibcameraApp::ConfigureViewfinder()
+ 		lores_size.alignDownTo(2, 2);
+ 		if (lores_size.width > size.width || lores_size.height > size.height)
+ 			throw std::runtime_error("Low res image larger than viewfinder");
+-		configuration_->at(1).pixelFormat = libcamera::formats::YUV420;
++		if (options_->pixelformat.length())
++			configuration_->at(0).pixelFormat =
++				libcamera::PixelFormat::fromString(options_->pixelformat);
++		else
++			configuration_->at(1).pixelFormat = libcamera::formats::YUV420;
+ 		configuration_->at(1).size = lores_size;
+ 		configuration_->at(1).bufferCount = configuration_->at(0).bufferCount;
+ 	}
+@@ -186,8 +195,13 @@ void LibcameraApp::ConfigureStill(unsigned int flags)
+ 		configuration_->at(0).pixelFormat = libcamera::formats::BGR888;
+ 	else if (flags & FLAG_STILL_RGB)
+ 		configuration_->at(0).pixelFormat = libcamera::formats::RGB888;
+-	else
+-		configuration_->at(0).pixelFormat = libcamera::formats::YUV420;
++	else {
++		if (options_->pixelformat.length())
++			configuration_->at(0).pixelFormat =
++				libcamera::PixelFormat::fromString(options_->pixelformat);
++		else
++			configuration_->at(0).pixelFormat = libcamera::formats::YUV420;
++	}
+ 	if ((flags & FLAG_STILL_BUFFER_MASK) == FLAG_STILL_DOUBLE_BUFFER)
+ 		configuration_->at(0).bufferCount = 2;
+ 	else if ((flags & FLAG_STILL_BUFFER_MASK) == FLAG_STILL_TRIPLE_BUFFER)
+@@ -241,7 +255,11 @@ void LibcameraApp::ConfigureVideo(unsigned int flags)
+ 		throw std::runtime_error("failed to generate video configuration");
+ 
+ 	// Now we get to override any of the default settings from the options_->
+-	configuration_->at(0).pixelFormat = libcamera::formats::YUV420;
++	if (options_->pixelformat.length())
++		configuration_->at(0).pixelFormat =
++			libcamera::PixelFormat::fromString(options_->pixelformat);
++	else
++		configuration_->at(0).pixelFormat = libcamera::formats::YUV420;
+ 	configuration_->at(0).bufferCount = 6; // 6 buffers is better than 4
+ 	if (options_->width)
+ 		configuration_->at(0).size.width = options_->width;
+@@ -267,7 +285,11 @@ void LibcameraApp::ConfigureVideo(unsigned int flags)
+ 		if (lores_size.width > configuration_->at(0).size.width ||
+ 			lores_size.height > configuration_->at(0).size.height)
+ 			throw std::runtime_error("Low res image larger than video");
+-		configuration_->at(lores_index).pixelFormat = libcamera::formats::YUV420;
++		if (options_->pixelformat.length())
++			configuration_->at(0).pixelFormat =
++				libcamera::PixelFormat::fromString(options_->pixelformat);
++		else
++			configuration_->at(lores_index).pixelFormat = libcamera::formats::YUV420;
+ 		configuration_->at(lores_index).size = lores_size;
+ 		configuration_->at(lores_index).bufferCount = configuration_->at(0).bufferCount;
+ 	}
+@@ -713,8 +735,8 @@ void LibcameraApp::previewThread()
+ 				preview_cond_var_.wait(lock);
+ 		}
+ 
+-		if (item.stream->configuration().pixelFormat != libcamera::formats::YUV420)
+-			throw std::runtime_error("Preview windows only support YUV420");
++		// if (item.stream->configuration().pixelFormat != libcamera::formats::YUV420)
++		// 	throw std::runtime_error("Preview windows only support YUV420");
+ 
+ 		unsigned int w, h, stride;
+ 		StreamDimensions(item.stream, &w, &h, &stride);
+diff --git a/core/options.hpp b/core/options.hpp
+index 447983f..5c68088 100644
+--- a/core/options.hpp
++++ b/core/options.hpp
+@@ -43,6 +43,8 @@ struct Options
+ 			 "Set the output image width (0 = use default value)")
+ 			("height", value<unsigned int>(&height)->default_value(0),
+ 			 "Set the output image height (0 = use default value)")
++			("pixelformat", value<std::string>(&pixelformat),
++			 "Set the pixelformat name")
+ 			("timeout,t", value<uint64_t>(&timeout)->default_value(5000),
+ 			 "Time (in ms) for which program runs")
+ 			("output,o", value<std::string>(&output),
+@@ -119,6 +121,7 @@ struct Options
+ 	std::string post_process_file;
+ 	unsigned int width;
+ 	unsigned int height;
++	std::string pixelformat;
+ 	bool rawfull;
+ 	bool nopreview;
+ 	std::string preview;
+diff --git a/preview/CMakeLists.txt b/preview/CMakeLists.txt
+index 1a7481f..a772e14 100644
+--- a/preview/CMakeLists.txt
++++ b/preview/CMakeLists.txt
+@@ -63,6 +63,15 @@ else()
+     message(STATUS "QT display mode will be unavailable!")
+ endif()
+ 
++set(ENABLE_FB 1)
++if (ENABLE_FB)
++    set(SRC ${SRC} fb_preview.cpp)
++    set(FB_FOUND 1)
++    message(STATUS "FB display mode enabled")
++else()
++    message(STATUS "FB display mode will be unavailable!")
++endif()
++
+ add_library(preview null_preview.cpp ${SRC})
+ target_link_libraries(preview ${TARGET_LIBS})
+ 
+diff --git a/preview/fb_preview.cpp b/preview/fb_preview.cpp
+new file mode 100644
+index 0000000..1a88b79
+--- /dev/null
++++ b/preview/fb_preview.cpp
+@@ -0,0 +1,289 @@
++/* SPDX-License-Identifier: BSD-2-Clause */
++/*
++ * Copyright (C) 2021, Raspberry Pi (Trading) Ltd.
++ *
++ * null_preview.cpp - dummy "show nothing" preview window.
++ */
++
++#include <iostream>
++#include <sys/mman.h>
++#include <stdio.h>
++#include <unistd.h>
++#include <errno.h>
++#include <malloc.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++#include <sys/ioctl.h>
++#include <linux/fb.h>
++#include <string.h>
++#include <malloc.h>
++#include <linux/videodev2.h>
++#include <libcamera/formats.h>
++#include <chrono>
++
++#include "core/options.hpp"
++
++#include "preview.hpp"
++
++#define FBIOPAN_GET_PP_MODE        0x4609
++#define FBIOPAN_SET_PP_MODE        0x460a
++
++enum COLOR_FORMAT {
++	COLOR_YUV422_UYVY = 0,  // 00={Y1,V0,Y0,U0}
++	COLOR_YUV422_VYUY = 1,  // 01={Y1,U0,Y0,V0}
++	COLOR_YUV422_YUYV = 2,  // 10={V0,Y1,U0,Y0}
++	COLOR_YUV422_YVYU = 3,  // 11={U0,Y1,V0,Y0}
++
++	COLOR_YUV420P,          // 4
++	COLOR_YUV420_NV21,      // 5
++	COLOR_YUV420_NV12,      // 6
++
++	COLOR_RGB888_ARGB,      // 7
++	COLOR_RGB888_ABGR,      // 8
++	COLOR_RGB888_RGBA,      // 9
++	COLOR_RGB888_BGRA,      // 10
++	COLOR_RGB565,           // 11
++};
++
++struct pp_video_mode {
++	enum COLOR_FORMAT format;
++	unsigned int height;
++	unsigned int width;
++	unsigned int addr;
++};
++
++struct pp_mode {
++	char pp_id;
++	bool bus_out;        /*out to ddr*/
++	bool fifo_out;        /*out to lcdc*/
++	bool inited;
++	struct pp_video_mode src;
++	struct pp_video_mode dst;
++};
++
++class FbPreview : public Preview
++{
++public:
++	FbPreview(Options const *options);
++	~FbPreview();
++	void SetInfoText(const std::string &text) override 
++	{ 
++		std::cout << "Camera fps: " << text << std::endl;
++	}
++	// Display the buffer. You get given the fd back in the BufferDoneCallback
++	// once its available for re-use.
++	virtual void Show(int fd, libcamera::Span<uint8_t> span, int width, int height, int stride) override;
++	// Reset the preview window, clearing the current buffers and being ready to
++	// show new ones.
++	void Reset() override {}
++	// Return the maximum image size allowed. Zeroes mean "no limit".
++	virtual void MaxImageSize(unsigned int &w, unsigned int &h) const override { w = h = 0; }
++
++private:
++	int NVResize(unsigned char *inBuf,
++		unsigned char *outBuf, int imgWidth, int imgHeight);
++	int g_fb_fd;
++	int g_stfbc_fd;
++	struct fb_var_screeninfo g_vinfo;
++	struct fb_fix_screeninfo g_finfo;
++	unsigned char *g_fb_buf;
++	unsigned char *tmpBuf;
++	unsigned int g_screensize;
++	enum COLOR_FORMAT pixformat;
++};
++
++static enum COLOR_FORMAT v4l2fmt_to_fbfmt(unsigned int format)
++{
++	enum COLOR_FORMAT pixformat = COLOR_RGB565;
++
++	switch (format) {
++	case V4L2_PIX_FMT_RGB565:
++		pixformat = COLOR_RGB565;
++		break;
++	case V4L2_PIX_FMT_RGB24:
++		pixformat = COLOR_RGB888_ARGB;
++		break;
++	case V4L2_PIX_FMT_YUV420:
++		pixformat = COLOR_YUV420P;
++		break;
++	case V4L2_PIX_FMT_YUYV:
++		pixformat = COLOR_YUV422_YUYV;
++		break;
++	case V4L2_PIX_FMT_NV21:
++		pixformat = COLOR_YUV420_NV21;
++		break;
++	case V4L2_PIX_FMT_NV12:
++		pixformat = COLOR_YUV420_NV12;
++		break;
++	case V4L2_PIX_FMT_YVYU:
++		pixformat = COLOR_YUV422_YVYU;
++		break;
++	default:
++		pixformat = COLOR_RGB565;
++		break;
++	}
++
++	return pixformat;
++}
++
++FbPreview::FbPreview(Options const *options) : Preview(options)
++{
++	g_fb_fd = open("/dev/fb0", O_RDWR);
++	if (g_fb_fd == -1)
++		throw std::runtime_error("Couldn't open fb display");
++	g_stfbc_fd = open("/dev/stfbcdev", O_RDWR);
++	if (g_stfbc_fd == -1)
++		throw std::runtime_error("Couldn't open stfbcdev display");
++
++	struct pp_mode pp_info[3];
++	libcamera::PixelFormat pixelFormat =
++		libcamera::PixelFormat::fromString(options_->pixelformat);
++
++	if (pixelFormat.fourcc() != V4L2_PIX_FMT_NV12)
++		throw std::runtime_error("FB onlu support NV12 display");
++
++	pixformat = v4l2fmt_to_fbfmt(pixelFormat.fourcc());
++	if (-1 == ioctl(g_stfbc_fd, FBIOPAN_GET_PP_MODE, &pp_info[0]))
++		throw std::runtime_error("Error reading variable information.");
++
++	std::cout << "get pp format : "
++		<< pp_info[1].src.format
++		<< std::endl;
++	
++	pp_info[1].src.format = pixformat;
++	
++	if (-1 == ioctl(g_stfbc_fd, FBIOPAN_SET_PP_MODE, &pp_info[0]))
++		throw std::runtime_error("Error reading variable information.");
++	
++	if (-1 == ioctl(g_stfbc_fd, FBIOPAN_GET_PP_MODE, &pp_info[0]))
++		throw std::runtime_error("Error reading variable information.");
++
++	std::cout << "get pp format : "
++		<< pp_info[1].src.format
++		<< std::endl;
++
++	pixformat = pp_info[1].src.format;
++	
++	// Get fixed screen information
++	if (-1 == ioctl(g_fb_fd, FBIOGET_FSCREENINFO, &g_finfo))
++		throw std::runtime_error("Error reading fixed information.");
++	
++	// Get variable screen information
++	if (-1 == ioctl(g_fb_fd, FBIOGET_VSCREENINFO, &g_vinfo))
++		throw std::runtime_error("Error reading variable information.");
++	
++	std::cout << "g_vinfo.xres = " << g_vinfo.xres
++		<< ", g_vinfo.yres = " << g_vinfo.yres
++		<< ", grayscale = " << g_vinfo.grayscale
++		<< std::endl;
++	std::cout << "g_vinfo.xoffset = " << g_vinfo.xoffset
++		<< ", g_vinfo.yoffset = " << g_vinfo.yoffset
++		<< std::endl;
++	std::cout << "g_vinfo.bits_per_pixel = " << g_vinfo.bits_per_pixel
++		<< ", g_finfo.line_length = " << g_finfo.line_length
++		<< std::endl;
++	
++	g_screensize = g_vinfo.xres * g_vinfo.yres * g_vinfo.bits_per_pixel / 8;
++	g_fb_buf = (unsigned char *)mmap(NULL, g_screensize, PROT_READ | PROT_WRITE, MAP_SHARED, g_fb_fd, 0);
++	if (g_fb_buf == (void *)(-1))
++		throw std::runtime_error("Error: failed to map framebuffer device to memory.");
++
++	memset(g_fb_buf, 0x00, g_screensize);
++	tmpBuf = static_cast<unsigned char *>(malloc(g_screensize));
++	if (!tmpBuf)
++		throw std::runtime_error("Error: failed to malloc memory.");
++	memset(tmpBuf, 0x00, g_screensize);
++
++	if (options->verbose)
++		std::cout << "Running fb preview window" << std::endl;
++}
++
++FbPreview::~FbPreview()
++{
++	free(tmpBuf);
++	munmap((void *)g_fb_buf, g_screensize);
++	close(g_fb_fd);
++	close(g_stfbc_fd);
++	std::cout << "fb preview window ~FbPreview" << std::endl;
++}
++
++int FbPreview::NVResize(unsigned char *inBuf,
++		unsigned char *outBuf, int imgWidth, int imgHeight)
++{
++	int rows, cols;
++	unsigned char *OutNVdata, *InNVdata;
++	int Ypos, Upos, Vpos;
++	int fb_Ypos, fb_Upos, fb_Vpos;
++	int width, height;
++	int x_offset, y_offset;
++	unsigned int start_timems;
++	unsigned int end_timems;
++	struct timeval ts_start, ts_end;
++
++	width = imgWidth > g_vinfo.xres ? g_vinfo.xres : imgWidth;
++	height = imgHeight > g_vinfo.yres ? g_vinfo.yres : imgHeight;
++	x_offset = (g_vinfo.xres - width) / 2;
++	y_offset = (g_vinfo.yres - height) / 2;
++
++	InNVdata = inBuf;
++	OutNVdata = tmpBuf;
++
++	if (imgWidth == g_vinfo.xres) {
++		fb_Ypos = y_offset * g_vinfo.xres + x_offset;
++		fb_Upos = (y_offset / 2 * g_vinfo.xres / 2 + x_offset / 2) * 2;
++		fb_Upos = g_vinfo.xres * g_vinfo.yres + fb_Upos;
++		Upos = imgWidth * imgHeight;
++		memcpy(&tmpBuf[fb_Ypos], inBuf, imgWidth * height);
++		memcpy(&tmpBuf[fb_Upos], &inBuf[Upos], imgWidth * height / 2);
++		memcpy(&outBuf[fb_Ypos], &tmpBuf[fb_Ypos], imgWidth * height * 2);
++		memcpy(&outBuf[fb_Upos], &tmpBuf[fb_Upos], imgWidth * height / 2);
++		return 0;
++	}
++
++	/* two bytes for every pixels */
++	for(rows = 0; rows < height; rows+=2)
++	{
++		// g_vinfo.xres, g_vinfo.yres g_vinfo.bits_per_pixel
++		fb_Ypos = ((rows + y_offset) * g_vinfo.xres + x_offset);
++		fb_Upos = ((rows + y_offset) / 2 * g_vinfo.xres / 2 + x_offset / 2) * 2;
++		fb_Upos = g_vinfo.xres * g_vinfo.yres + fb_Upos;
++		fb_Vpos = fb_Upos + 1;
++
++		Ypos = rows * imgWidth;
++		Upos = imgWidth * imgHeight + Ypos / 2;
++		Vpos = Upos + 1;
++		memcpy(&OutNVdata[fb_Ypos], &InNVdata[Ypos], width);
++		memcpy(&OutNVdata[fb_Ypos+g_vinfo.xres], &InNVdata[Ypos+imgWidth], width);
++
++		memcpy(&OutNVdata[fb_Upos], &InNVdata[Upos], width);
++	}
++
++	memcpy(outBuf, tmpBuf, g_screensize);
++
++	return 0;
++}
++
++void FbPreview::Show(int fd, libcamera::Span<uint8_t> span, int width, int height, int stride)
++{
++	int imgsize = stride * height + stride * height / 2;
++	auto startTime = std::chrono::high_resolution_clock::now();
++
++	if (pixformat == COLOR_YUV420_NV21 || pixformat == COLOR_YUV420_NV12)
++		NVResize(span.data(), g_fb_buf, width, height);
++	else
++		std::cout << "FbPreview unsupport pixformat: " << pixformat << std::endl;
++
++	auto endTime = std::chrono::high_resolution_clock::now();
++
++	std::chrono::duration<double,std::ratio<1,1000000>> duration_mcs =
++		std::chrono::duration_cast<std::chrono::duration<double,std::ratio<1,1000000>>>(endTime-startTime);
++	std::cout << "FbPreview copy fps " << 1000000 / duration_mcs.count() << "." << std::endl;
++
++	done_callback_(fd);
++}
++
++Preview *make_fb_preview(Options const *options)
++{
++	return new FbPreview(options);
++}
+diff --git a/preview/preview.cpp b/preview/preview.cpp
+index 68d193a..0086d83 100644
+--- a/preview/preview.cpp
++++ b/preview/preview.cpp
+@@ -13,6 +13,7 @@ Preview *make_null_preview(Options const *options);
+ Preview *make_egl_preview(Options const *options);
+ Preview *make_drm_preview(Options const *options);
+ Preview *make_qt_preview(Options const *options);
++Preview *make_fb_preview(Options const *options);
+ 
+ Preview *make_preview(Options const *options)
+ {
+@@ -48,8 +49,10 @@ Preview *make_preview(Options const *options)
+ 			}
+ 			catch (std::exception const &e)
+ 			{
+-				std::cout << "Preview window unavailable" << std::endl;
+-				return make_null_preview(options);
++				std::cout << "FB Preview window." << std::endl;
++				return make_fb_preview(options);
++				// std::cout << "Preview window unavailable" << std::endl;
++				// return make_null_preview(options);
+ 			}
+ 		}
+ 	}
+-- 
+2.17.1
+

+ 92 - 0
package/starfive/libcamera-apps/0002-add-select-camera-options.patch

@@ -0,0 +1,92 @@
+From aec56c70bdc7f037a88deaa4cc84854ec2c961e9 Mon Sep 17 00:00:00 2001
+From: sw.multimedia <sw.multimedia@starfivetech.com>
+Date: Mon, 22 Nov 2021 10:16:15 +0800
+Subject: [PATCH] add select camera options
+
+---
+ core/libcamera_app.cpp | 29 +++++++++++++++++++++--------
+ core/options.hpp       |  5 +++++
+ 2 files changed, 26 insertions(+), 8 deletions(-)
+
+diff --git a/core/libcamera_app.cpp b/core/libcamera_app.cpp
+index 3c4d862..22896a7 100644
+--- a/core/libcamera_app.cpp
++++ b/core/libcamera_app.cpp
+@@ -58,17 +58,30 @@ void LibcameraApp::OpenCamera()
+ 	if (camera_manager_->cameras().size() == 0)
+ 		throw std::runtime_error("no cameras available");
+ 
+-	std::string const &cam_id = camera_manager_->cameras()[0]->id();
+-	camera_ = camera_manager_->get(cam_id);
+-	if (!camera_)
+-		throw std::runtime_error("failed to find camera " + cam_id);
+-
+-	if (camera_->acquire())
+-		throw std::runtime_error("failed to acquire camera " + cam_id);
++	if (options_->camera_name.length()) {
++		char *endptr;
++		unsigned long index = strtoul(options_->camera_name.c_str(), &endptr, 10);
++		std::cout << "Acquired camera name:  " << options_->camera_name << std::endl;
++		if (*endptr == '\0' && index > 0 && index <= camera_manager_->cameras().size())
++			camera_ = camera_manager_->cameras()[index - 1];
++		else
++			camera_ = camera_manager_->get(options_->camera_name);
++		if (!camera_)
++			throw std::runtime_error("failed to find camera " + options_->camera_name);
++		if (camera_->acquire())
++			throw std::runtime_error("failed to acquire camera " + options_->camera_name);
++	} else {
++		std::string const &cam_id = camera_manager_->cameras()[0]->id();
++		camera_ = camera_manager_->get(cam_id);
++		if (!camera_)
++			throw std::runtime_error("failed to find camera " + cam_id);
++		if (camera_->acquire())
++			throw std::runtime_error("failed to acquire camera " + cam_id);
++	}
+ 	camera_acquired_ = true;
+ 
+ 	if (options_->verbose)
+-		std::cout << "Acquired camera " << cam_id << std::endl;
++		std::cout << "Acquired camera " << camera_->id() << std::endl;
+ 
+ 	if (!options_->post_process_file.empty())
+ 		post_processor_.Read(options_->post_process_file);
+diff --git a/core/options.hpp b/core/options.hpp
+index 5c68088..de49beb 100644
+--- a/core/options.hpp
++++ b/core/options.hpp
+@@ -30,6 +30,8 @@ struct Options
+ 			 "Displays the build version number")
+ 			("verbose,v", value<bool>(&verbose)->default_value(false)->implicit_value(true),
+ 			 "Output extra debug and diagnostics")
++			("camera", value<std::string>(&camera_name),
++			 "Specify which camera to operate on, by id or by index")
+ 			("config,c", value<std::string>(&config_file)->implicit_value("config.txt"),
+ 			 "Read the options from a file. If no filename is specified, default to config.txt. "
+ 			 "In case of duplicate options, the ones provided on the command line will be used. "
+@@ -115,6 +117,7 @@ struct Options
+ 	bool help;
+ 	bool version;
+ 	bool verbose;
++	std::string camera_name;
+ 	uint64_t timeout; // in ms
+ 	std::string config_file;
+ 	std::string output;
+@@ -259,12 +262,14 @@ struct Options
+ 	{
+ 		std::cout << "Options:" << std::endl;
+ 		std::cout << "    verbose: " << verbose << std::endl;
++		std::cout << "    camera_name: " << camera_name << std::endl;
+ 		if (!config_file.empty())
+ 			std::cout << "    config file: " << config_file << std::endl;
+ 		std::cout << "    info_text:" << info_text << std::endl;
+ 		std::cout << "    timeout: " << timeout << std::endl;
+ 		std::cout << "    width: " << width << std::endl;
+ 		std::cout << "    height: " << height << std::endl;
++		std::cout << "    pixelformat: " << pixelformat << std::endl;
+ 		std::cout << "    output: " << output << std::endl;
+ 		std::cout << "    post_process_file: " << post_process_file << std::endl;
+ 		std::cout << "    rawfull: " << rawfull << std::endl;
+-- 
+2.17.1
+

+ 224 - 0
package/starfive/libcamera-apps/0003-add-FbPreview-support-YUYV-YVYU-display.patch

@@ -0,0 +1,224 @@
+From 4fd0d73ae92f4e0f49c9d0f9f7e559e55e4fe2a8 Mon Sep 17 00:00:00 2001
+From: sw.multimedia <sw.multimedia@starfivetech.com>
+Date: Mon, 22 Nov 2021 15:28:17 +0800
+Subject: [PATCH] add FbPreview support YUYV/YVYU display
+
+---
+ preview/fb_preview.cpp | 123 +++++++++++++++++++++++++++++++++++------
+ preview/preview.cpp    |  15 +++--
+ 2 files changed, 118 insertions(+), 20 deletions(-)
+
+diff --git a/preview/fb_preview.cpp b/preview/fb_preview.cpp
+index 1a88b79..0b42ad1 100644
+--- a/preview/fb_preview.cpp
++++ b/preview/fb_preview.cpp
+@@ -44,6 +44,7 @@ enum COLOR_FORMAT {
+ 	COLOR_RGB888_RGBA,      // 9
+ 	COLOR_RGB888_BGRA,      // 10
+ 	COLOR_RGB565,           // 11
++	COLOR_UNKOWN,           // unkown
+ };
+ 
+ struct pp_video_mode {
+@@ -83,6 +84,10 @@ public:
+ private:
+ 	int NVResize(unsigned char *inBuf,
+ 		unsigned char *outBuf, int imgWidth, int imgHeight);
++	int YUYVResize(unsigned char *inBuf,
++		unsigned char *outBuf, int imgWidth, int imgHeight);
++	void setPixformat(unsigned int format);
++	unsigned int getPixformat(void);
+ 	int g_fb_fd;
+ 	int g_stfbc_fd;
+ 	struct fb_var_screeninfo g_vinfo;
+@@ -93,10 +98,8 @@ private:
+ 	enum COLOR_FORMAT pixformat;
+ };
+ 
+-static enum COLOR_FORMAT v4l2fmt_to_fbfmt(unsigned int format)
++void FbPreview::setPixformat(unsigned int format)
+ {
+-	enum COLOR_FORMAT pixformat = COLOR_RGB565;
+-
+ 	switch (format) {
+ 	case V4L2_PIX_FMT_RGB565:
+ 		pixformat = COLOR_RGB565;
+@@ -120,11 +123,43 @@ static enum COLOR_FORMAT v4l2fmt_to_fbfmt(unsigned int format)
+ 		pixformat = COLOR_YUV422_YVYU;
+ 		break;
+ 	default:
+-		pixformat = COLOR_RGB565;
++		pixformat = COLOR_UNKOWN;
+ 		break;
+ 	}
++}
+ 
+-	return pixformat;
++unsigned int FbPreview::getPixformat(void)
++{
++	unsigned int format;
++
++	switch (pixformat) {
++	case COLOR_RGB565:
++		format = V4L2_PIX_FMT_RGB565;
++		break;
++	case COLOR_RGB888_ARGB:
++		format = V4L2_PIX_FMT_RGB24;
++		break;
++	case COLOR_YUV420P:
++		format = V4L2_PIX_FMT_YUV420;
++		break;
++	case COLOR_YUV422_YUYV:
++		format = V4L2_PIX_FMT_YUYV;
++		break;
++	case COLOR_YUV420_NV21:
++		format = V4L2_PIX_FMT_NV21;
++		break;
++	case COLOR_YUV420_NV12:
++		format = V4L2_PIX_FMT_NV12;
++		break;
++	case COLOR_YUV422_YVYU:
++		format = V4L2_PIX_FMT_YVYU;
++		break;
++	default:
++		format = 0;
++		break;
++	}
++
++	return format;
+ }
+ 
+ FbPreview::FbPreview(Options const *options) : Preview(options)
+@@ -140,10 +175,10 @@ FbPreview::FbPreview(Options const *options) : Preview(options)
+ 	libcamera::PixelFormat pixelFormat =
+ 		libcamera::PixelFormat::fromString(options_->pixelformat);
+ 
+-	if (pixelFormat.fourcc() != V4L2_PIX_FMT_NV12)
+-		throw std::runtime_error("FB onlu support NV12 display");
++	setPixformat(pixelFormat.fourcc());
++	if (pixformat == COLOR_UNKOWN)
++		throw std::runtime_error("FB unsupport format display.");
+ 
+-	pixformat = v4l2fmt_to_fbfmt(pixelFormat.fourcc());
+ 	if (-1 == ioctl(g_stfbc_fd, FBIOPAN_GET_PP_MODE, &pp_info[0]))
+ 		throw std::runtime_error("Error reading variable information.");
+ 
+@@ -217,9 +252,6 @@ int FbPreview::NVResize(unsigned char *inBuf,
+ 	int fb_Ypos, fb_Upos, fb_Vpos;
+ 	int width, height;
+ 	int x_offset, y_offset;
+-	unsigned int start_timems;
+-	unsigned int end_timems;
+-	struct timeval ts_start, ts_end;
+ 
+ 	width = imgWidth > g_vinfo.xres ? g_vinfo.xres : imgWidth;
+ 	height = imgHeight > g_vinfo.yres ? g_vinfo.yres : imgHeight;
+@@ -264,15 +296,74 @@ int FbPreview::NVResize(unsigned char *inBuf,
+ 	return 0;
+ }
+ 
++int FbPreview::YUYVResize(unsigned char *inBuf,
++		unsigned char *outBuf, int imgWidth, int imgHeight)
++{
++	int rows, cols;
++	unsigned char *OutNVdata, *InNVdata;
++	int Ypos, fb_Ypos;
++	int width, height;
++	int x_offset, y_offset;
++
++	width = imgWidth > g_vinfo.xres ? g_vinfo.xres : imgWidth;
++	height = imgHeight > g_vinfo.yres ? g_vinfo.yres : imgHeight;
++	x_offset = (g_vinfo.xres - width) / 2;
++	y_offset = (g_vinfo.yres - height) / 2;
++	x_offset &= ~0x1;
++
++	InNVdata = inBuf;
++	OutNVdata = tmpBuf;
++
++	if (imgWidth == g_vinfo.xres) {
++		fb_Ypos = y_offset * g_vinfo.xres + x_offset;
++		memcpy(&tmpBuf[fb_Ypos], inBuf, imgWidth * height * 2);
++		memcpy(&outBuf[fb_Ypos], &tmpBuf[fb_Ypos], imgWidth * height * 2);
++		return 0;
++	}
++
++	/* two bytes for every pixels */
++	for(rows = 0; rows < height; rows++)
++	{
++		// g_vinfo.xres, g_vinfo.yres g_vinfo.bits_per_pixel
++		fb_Ypos = ((rows + y_offset) * g_vinfo.xres + x_offset) * 2;
++		Ypos = rows * imgWidth * 2;
++		memcpy(&OutNVdata[fb_Ypos], &InNVdata[Ypos], width * 2);
++	}
++
++	memcpy(outBuf, tmpBuf, g_screensize);
++
++	return 0;
++}
++
+ void FbPreview::Show(int fd, libcamera::Span<uint8_t> span, int width, int height, int stride)
+ {
+-	int imgsize = stride * height + stride * height / 2;
+ 	auto startTime = std::chrono::high_resolution_clock::now();
++	int size_valid = 0;
++	int imgsize;
++
++	if (pixformat == COLOR_YUV420_NV21 || pixformat == COLOR_YUV420_NV12) {
++		imgsize = stride * height + stride * height / 2;
++		if (imgsize == span.size()) {
++			size_valid = 1;
++			NVResize(span.data(), g_fb_buf, width, height);
++		}
++	} else if (pixformat == COLOR_YUV422_YVYU || pixformat == COLOR_YUV422_YUYV) {
++		imgsize = stride * height;
++		if (imgsize == span.size()) {
++			size_valid = 1;
++			YUYVResize(span.data(), g_fb_buf, width, height);
++		}
++	}
+ 
+-	if (pixformat == COLOR_YUV420_NV21 || pixformat == COLOR_YUV420_NV12)
+-		NVResize(span.data(), g_fb_buf, width, height);
+-	else
+-		std::cout << "FbPreview unsupport pixformat: " << pixformat << std::endl;
++	if (!size_valid) {
++		std::cout << "FbPreview unsupport pixformat: " << pixformat
++			<< " fb need size: " << imgsize
++			<< " width: " << width
++			<< " height: " << height
++			<< " stride: " << stride
++			<< " size: " << span.size() << std::endl;
++		throw std::runtime_error("Error FbPreview unsupport pixformat.");
++	}
+ 
+ 	auto endTime = std::chrono::high_resolution_clock::now();
+ 
+diff --git a/preview/preview.cpp b/preview/preview.cpp
+index 0086d83..17118d4 100644
+--- a/preview/preview.cpp
++++ b/preview/preview.cpp
+@@ -49,10 +49,17 @@ Preview *make_preview(Options const *options)
+ 			}
+ 			catch (std::exception const &e)
+ 			{
+-				std::cout << "FB Preview window." << std::endl;
+-				return make_fb_preview(options);
+-				// std::cout << "Preview window unavailable" << std::endl;
+-				// return make_null_preview(options);
++				try {
++					std::cout << "FB Preview window." << std::endl;
++					return make_fb_preview(options);
++					if (options->verbose)
++						std::cout << "Made FB Preview window." << std::endl;
++				}
++				catch (std::exception const &e)
++				{
++					std::cout << "Preview window unavailable" << std::endl;
++					return make_null_preview(options);
++				}
+ 			}
+ 		}
+ 	}
+-- 
+2.17.1
+

+ 153 - 0
package/starfive/libcamera-apps/0004-add-DrmPreview-support-NV12-display.patch

@@ -0,0 +1,153 @@
+From 30ce74f6cc34ddb310cee72280d9e1e00aa4e459 Mon Sep 17 00:00:00 2001
+From: sw.multimedia <sw.multimedia@starfivetech.com>
+Date: Thu, 25 Nov 2021 14:57:09 +0800
+Subject: [PATCH] add DrmPreview support NV12 display
+
+---
+ preview/drm_preview.cpp | 84 +++++++++++++++++++++++++++++++++++------
+ 1 file changed, 73 insertions(+), 11 deletions(-)
+
+diff --git a/preview/drm_preview.cpp b/preview/drm_preview.cpp
+index c444918..a969358 100644
+--- a/preview/drm_preview.cpp
++++ b/preview/drm_preview.cpp
+@@ -10,6 +10,8 @@
+ #include <drm_mode.h>
+ #include <xf86drm.h>
+ #include <xf86drmMode.h>
++#include <fcntl.h>
++#include <libcamera/formats.h>
+ 
+ #include "core/options.hpp"
+ 
+@@ -50,6 +52,7 @@ private:
+ 	void findPlane();
+ 	int drmfd_;
+ 	int conId_;
++	drmModeConnector *con_;
+ 	uint32_t crtcId_;
+ 	int crtcIdx_;
+ 	uint32_t planeId_;
+@@ -95,10 +98,19 @@ void DrmPreview::findCrtc()
+ 			if (con->encoder_id)
+ 			{
+ 				enc = drmModeGetEncoder(drmfd_, con->encoder_id);
+-				if (enc->crtc_id)
+-				{
+-					crtc = drmModeGetCrtc(drmfd_, enc->crtc_id);
+-				}
++			}
++			else
++			{
++				enc = drmModeGetEncoder(drmfd_, con->encoders[0]);
++			}
++
++			if (enc->crtc_id)
++			{
++				crtc = drmModeGetCrtc(drmfd_, enc->crtc_id);
++			}
++			else
++			{
++				crtc = drmModeGetCrtc(drmfd_, res->crtcs[0]);
+ 			}
+ 
+ 			if (!conId_ && crtc)
+@@ -161,6 +173,8 @@ void DrmPreview::findCrtc()
+ 		throw std::runtime_error("connector supports no mode");
+ 	}
+ 
++	con_ = c;
++
+ 	if (options_->fullscreen || width_ == 0 || height_ == 0)
+ 	{
+ 		drmModeCrtc *crtc = drmModeGetCrtc(drmfd_, crtcId_);
+@@ -227,10 +241,13 @@ void DrmPreview::findPlane()
+ 
+ DrmPreview::DrmPreview(Options const *options) : Preview(options), last_fd_(-1)
+ {
+-	drmfd_ = drmOpen("vc4", NULL);
++	// drmfd_ = drmOpen("vc4", NULL);
++	drmfd_ = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
+ 	if (drmfd_ < 0)
+ 		throw std::runtime_error("drmOpen failed: " + std::string(ERRSTR));
+ 
++	drmSetClientCap(drmfd_, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
++
+ 	x_ = options_->preview_x;
+ 	y_ = options_->preview_y;
+ 	width_ = options_->preview_width;
+@@ -245,7 +262,12 @@ DrmPreview::DrmPreview(Options const *options) : Preview(options), last_fd_(-1)
+ 
+ 		conId_ = 0;
+ 		findCrtc();
+-		out_fourcc_ = DRM_FORMAT_YUV420;
++		if (options_->pixelformat.length()) {
++			libcamera::PixelFormat pixelFormat =
++				libcamera::PixelFormat::fromString(options_->pixelformat);
++			out_fourcc_ = pixelFormat.fourcc();
++		} else
++			out_fourcc_ = DRM_FORMAT_YUV420;
+ 		findPlane();
+ 	}
+ 	catch (std::exception const &e)
+@@ -281,12 +303,52 @@ void DrmPreview::makeBuffer(int fd, size_t size, unsigned int width, unsigned in
+ 	if (drmPrimeFDToHandle(drmfd_, fd, &buffer.bo_handle))
+ 		throw std::runtime_error("drmPrimeFDToHandle failed for fd " + std::to_string(fd));
+ 
+-	uint32_t offsets[4] = { 0, stride * height, stride * height + (stride / 2) * (height / 2) };
+-	uint32_t pitches[4] = { stride, stride / 2, stride / 2 };
+-	uint32_t bo_handles[4] = { buffer.bo_handle, buffer.bo_handle, buffer.bo_handle };
+-
+-	if (drmModeAddFB2(drmfd_, width, height, out_fourcc_, bo_handles, pitches, offsets, &buffer.fb_handle, 0))
++	if (out_fourcc_ == DRM_FORMAT_YUV420) {
++		uint32_t offsets[4] = { 0, stride * height, stride * height + (stride / 2) * (height / 2) };
++		uint32_t pitches[4] = { stride, stride / 2, stride / 2 };
++		uint32_t bo_handles[4] = { buffer.bo_handle, buffer.bo_handle, buffer.bo_handle };
++
++		if (drmModeAddFB2(drmfd_, width, height, out_fourcc_, bo_handles, pitches, offsets, &buffer.fb_handle, 0))
++			throw std::runtime_error("YUV420 drmModeAddFB2 failed: " + std::string(ERRSTR));
++	} else if (out_fourcc_ == DRM_FORMAT_NV12 || out_fourcc_ == DRM_FORMAT_NV21) {
++		uint32_t offsets[4] = { 0, stride * height};
++		uint32_t pitches[4] = { stride, stride};
++		uint32_t bo_handles[4] = { buffer.bo_handle, buffer.bo_handle};
++
++		if (drmModeAddFB2(drmfd_, width, height, out_fourcc_, bo_handles, pitches, offsets, &buffer.fb_handle, 0))
++			throw std::runtime_error("NV12/21 drmModeAddFB2 failed: " + std::string(ERRSTR));
++	} else
+ 		throw std::runtime_error("drmModeAddFB2 failed: " + std::string(ERRSTR));
++
++	/* find preferred mode */
++	drmModeModeInfo *modeptr = NULL, *preferred = NULL;
++	for (int m = 0; m < con_->count_modes; m++) {
++		modeptr = &con_->modes[m];
++		if (modeptr->hdisplay == width && modeptr->vdisplay == height) {
++			preferred = modeptr;
++			std::cout << "find the matched mode, modes index= "
++				<< m << ", " << width << "x" << height << std::endl;
++			break;
++		}
++		if (modeptr->type & DRM_MODE_TYPE_PREFERRED) {
++			preferred = modeptr;
++			std::cout << "find perferred mode, modes index= " << m << std::endl;
++		}
++	}
++
++	if (!preferred)
++		preferred = &con_->modes[0];
++
++	// set default
++	if (drmModeSetCrtc(drmfd_, crtcId_, buffer.fb_handle, 0, 0,
++	        (uint32_t *)&conId_, 1, preferred)) {
++		throw std::runtime_error("drmModeSetCrtc() failed");
++	}
++
++	screen_width_ = preferred->hdisplay;
++	screen_height_ = preferred->vdisplay;
++	width_ = width;
++	height_ = height;
+ }
+ 
+ void DrmPreview::Show(int fd, libcamera::Span<uint8_t> span, int width, int height, int stride)
+-- 
+2.17.1
+

+ 18 - 0
package/starfive/libcamera-apps/Config.in

@@ -0,0 +1,18 @@
+config BR2_PACKAGE_LIBCAMERA_APPS
+	bool "libcamera-apps"
+	depends on BR2_PACKAGE_LIBCAMERA
+	select BR2_PACKAGE_GNUTLS
+	select BR2_PACKAGE_BOOST
+	select BR2_PACKAGE_BOOST_PROGRAM_OPTIONS
+	select BR2_PACKAGE_BOOST_SYSTEM
+	select BR2_PACKAGE_TIFF
+	select BR2_PACKAGE_LIBEXIF
+	select BR2_PACKAGE_LIBYUV
+	help
+	  libcamera-apps provides a software stack to support
+	  complex devices that need heavy hardware image
+	  processing operations.
+
+comment "libcamera-apps needs a toolchain w/ C++, threads, wchar, dynamic library, gcc >= 7"
+	depends on !BR2_INSTALL_LIBSTDCPP || !BR2_TOOLCHAIN_HAS_THREADS || \
+		!BR2_TOOLCHAIN_GCC_AT_LEAST_7 || BR2_STATIC_LIBS || !BR2_USE_WCHAR

+ 2 - 0
package/starfive/libcamera-apps/libcamera-apps.hash

@@ -0,0 +1,2 @@
+sha256  5600c4289457c03a5062001f5942c77fabe4fcef8877b67cfaeed5bae5672756  libcamera-apps-main.tar.gz
+sha256  3fe6e9e343f268c518652a00260791c55f18cd5902fd5de578005b9c6ec9785b  libcamera-apps-87f807f4eacf7d62021e3b4061348e64b2ecadc3.tar.gz

+ 14 - 0
package/starfive/libcamera-apps/libcamera-apps.mk

@@ -0,0 +1,14 @@
+################################################################################
+#
+# libcamera-apps
+#
+################################################################################
+
+LIBCAMERA_APPS_SITE = https://github.com/raspberrypi/libcamera-apps.git
+LIBCAMERA_APPS_VERSION = 87f807f4eacf7d62021e3b4061348e64b2ecadc3
+LIBCAMERA_APPS_SITE_METHOD = git
+LIBCAMERA_APPS_INSTALL_STAGING = YES
+
+LIBCAMERA_APPS_DEPENDENCIES = libcamera libexif tiff boost host-pkgconf
+
+$(eval $(cmake-package))