HDMI输入直至Yolov5识别全流程代码
This commit is contained in:
226
hdmiIn/rk3588_hdmi_in_out-in_and_out/.clang-format
Normal file
226
hdmiIn/rk3588_hdmi_in_out-in_and_out/.clang-format
Normal file
@@ -0,0 +1,226 @@
|
||||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: Google
|
||||
AccessModifierOffset: -1
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignArrayOfStructures: None
|
||||
AlignConsecutiveMacros: None
|
||||
AlignConsecutiveAssignments: None
|
||||
AlignConsecutiveBitFields: None
|
||||
AlignConsecutiveDeclarations: None
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: Align
|
||||
AlignTrailingComments: true
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortEnumsOnASingleLine: true
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: true
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
AttributeMacros:
|
||||
- __capability
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: false
|
||||
AfterControlStatement: Never
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
BeforeLambdaBody: false
|
||||
BeforeWhile: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeConceptDeclarations: true
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 80
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
QualifierAlignment: Leave
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DeriveLineEnding: true
|
||||
DerivePointerAlignment: true
|
||||
DisableFormat: false
|
||||
EmptyLineAfterAccessModifier: Never
|
||||
EmptyLineBeforeAccessModifier: LogicalBlock
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
PackConstructorInitializers: NextLine
|
||||
BasedOnStyle: ''
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
AllowAllConstructorInitializersOnNextLine: true
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IfMacros:
|
||||
- KJ_IF_MAYBE
|
||||
IncludeBlocks: Regroup
|
||||
IncludeCategories:
|
||||
- Regex: '^<ext/.*\.h>'
|
||||
Priority: 2
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '^<.*\.h>'
|
||||
Priority: 1
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '^<.*'
|
||||
Priority: 2
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '.*'
|
||||
Priority: 3
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
IncludeIsMainRegex: '([-_](test|unittest))?$'
|
||||
IncludeIsMainSourceRegex: ''
|
||||
IndentAccessModifiers: false
|
||||
IndentCaseLabels: true
|
||||
IndentCaseBlocks: false
|
||||
IndentGotoLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentExternBlock: AfterExternBlock
|
||||
IndentRequires: false
|
||||
IndentWidth: 2
|
||||
IndentWrappedFunctionNames: false
|
||||
InsertTrailingCommas: None
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
LambdaBodyIndentation: Signature
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBinPackProtocolList: Never
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCBreakBeforeNestedBlockParam: true
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 1
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakOpenParenthesis: 0
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 200
|
||||
PenaltyIndentedWhitespace: 0
|
||||
PointerAlignment: Left
|
||||
PPIndentWidth: -1
|
||||
RawStringFormats:
|
||||
- Language: Cpp
|
||||
Delimiters:
|
||||
- cc
|
||||
- CC
|
||||
- cpp
|
||||
- Cpp
|
||||
- CPP
|
||||
- 'c++'
|
||||
- 'C++'
|
||||
CanonicalDelimiter: ''
|
||||
BasedOnStyle: google
|
||||
- Language: TextProto
|
||||
Delimiters:
|
||||
- pb
|
||||
- PB
|
||||
- proto
|
||||
- PROTO
|
||||
EnclosingFunctions:
|
||||
- EqualsProto
|
||||
- EquivToProto
|
||||
- PARSE_PARTIAL_TEXT_PROTO
|
||||
- PARSE_TEST_PROTO
|
||||
- PARSE_TEXT_PROTO
|
||||
- ParseTextOrDie
|
||||
- ParseTextProtoOrDie
|
||||
- ParseTestProto
|
||||
- ParsePartialTestProto
|
||||
CanonicalDelimiter: pb
|
||||
BasedOnStyle: google
|
||||
ReferenceAlignment: Pointer
|
||||
ReflowComments: true
|
||||
RemoveBracesLLVM: false
|
||||
SeparateDefinitionBlocks: Leave
|
||||
ShortNamespaceLines: 1
|
||||
SortIncludes: CaseSensitive
|
||||
SortJavaStaticImport: Before
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCaseColon: false
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeParensOptions:
|
||||
AfterControlStatements: true
|
||||
AfterForeachMacros: true
|
||||
AfterFunctionDefinitionName: false
|
||||
AfterFunctionDeclarationName: false
|
||||
AfterIfMacros: true
|
||||
AfterOverloadedOperator: false
|
||||
BeforeNonEmptyParentheses: false
|
||||
SpaceAroundPointerQualifiers: Default
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyBlock: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 2
|
||||
SpacesInAngles: Never
|
||||
SpacesInConditionalStatement: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInLineCommentPrefix:
|
||||
Minimum: 1
|
||||
Maximum: -1
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
SpaceBeforeSquareBrackets: false
|
||||
BitFieldColonSpacing: Both
|
||||
Standard: Auto
|
||||
StatementAttributeLikeMacros:
|
||||
- Q_EMIT
|
||||
StatementMacros:
|
||||
- Q_UNUSED
|
||||
- QT_REQUIRE_VERSION
|
||||
TabWidth: 8
|
||||
UseCRLF: false
|
||||
UseTab: Never
|
||||
WhitespaceSensitiveMacros:
|
||||
- STRINGIZE
|
||||
- PP_STRINGIZE
|
||||
- BOOST_PP_STRINGIZE
|
||||
- NS_SWIFT_NAME
|
||||
- CF_SWIFT_NAME
|
||||
...
|
||||
|
||||
3
hdmiIn/rk3588_hdmi_in_out-in_and_out/.gitignore
vendored
Normal file
3
hdmiIn/rk3588_hdmi_in_out-in_and_out/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
cmake-build-*/
|
||||
.idea
|
||||
build
|
||||
33
hdmiIn/rk3588_hdmi_in_out-in_and_out/CMakeLists.txt
Normal file
33
hdmiIn/rk3588_hdmi_in_out-in_and_out/CMakeLists.txt
Normal file
@@ -0,0 +1,33 @@
|
||||
cmake_minimum_required(VERSION 3.21)
|
||||
project(rk3588_hdmi_in)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
# 查找所需的库
|
||||
find_package(OpenCV REQUIRED)
|
||||
find_package(kaylordut REQUIRED)
|
||||
|
||||
# 明确列出所有源文件
|
||||
set(SOURCES
|
||||
main.cpp
|
||||
src/fps_counter.cpp
|
||||
src/hdmi_in.cpp
|
||||
src/framebuffer.cpp
|
||||
src/video_saver.cpp
|
||||
src/virtual_device.cpp
|
||||
)
|
||||
|
||||
# 创建可执行文件
|
||||
add_executable(rk3588_hdmi_in ${SOURCES})
|
||||
|
||||
# 包含头文件
|
||||
target_include_directories(rk3588_hdmi_in PRIVATE
|
||||
include
|
||||
${OpenCV_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
# 链接库
|
||||
target_link_libraries(rk3588_hdmi_in PRIVATE
|
||||
${OpenCV_LIBS}
|
||||
${kaylordut_LIBS}
|
||||
)
|
||||
18
hdmiIn/rk3588_hdmi_in_out-in_and_out/include/fps_counter.h
Normal file
18
hdmiIn/rk3588_hdmi_in_out-in_and_out/include/fps_counter.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef FPS_COUNTER_H
|
||||
#define FPS_COUNTER_H
|
||||
|
||||
#include <chrono>
|
||||
#include <deque>
|
||||
|
||||
class FPSCounter {
|
||||
public:
|
||||
FPSCounter(size_t window_size = 10);
|
||||
double update();
|
||||
|
||||
private:
|
||||
size_t window_size_;
|
||||
std::deque<std::chrono::steady_clock::time_point> time_points_;
|
||||
std::deque<double> fps_values_;
|
||||
};
|
||||
|
||||
#endif // FPS_COUNTER_H
|
||||
25
hdmiIn/rk3588_hdmi_in_out-in_and_out/include/framebuffer.h
Normal file
25
hdmiIn/rk3588_hdmi_in_out-in_and_out/include/framebuffer.h
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Created by kaylor on 1/9/25.
|
||||
//
|
||||
|
||||
#ifndef FRAMEBUFFER_H
|
||||
#define FRAMEBUFFER_H
|
||||
#include "linux/fb.h"
|
||||
#include "opencv2/opencv.hpp"
|
||||
#include "string"
|
||||
|
||||
class FrameBuffer {
|
||||
public:
|
||||
FrameBuffer(std::string device);
|
||||
~FrameBuffer();
|
||||
bool WriteFrameBuffer(const cv::Mat image);
|
||||
|
||||
private:
|
||||
std::string device_;
|
||||
int fd_;
|
||||
char *fb_ptr_{nullptr};
|
||||
fb_fix_screeninfo fix_screeninfo_;
|
||||
fb_var_screeninfo var_screeninfo_;
|
||||
};
|
||||
|
||||
#endif // FRAMEBUFFER_H
|
||||
21
hdmiIn/rk3588_hdmi_in_out-in_and_out/include/hdmi_in.h
Normal file
21
hdmiIn/rk3588_hdmi_in_out-in_and_out/include/hdmi_in.h
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// Created by kaylor on 1/8/25.
|
||||
//
|
||||
|
||||
#ifndef HDMI_IN_H
|
||||
#define HDMI_IN_H
|
||||
|
||||
#include "opencv2/opencv.hpp"
|
||||
|
||||
class HdmiIn {
|
||||
public:
|
||||
HdmiIn(std::string device_name);
|
||||
~HdmiIn();
|
||||
cv::Mat get_next_frame();
|
||||
|
||||
private:
|
||||
cv::VideoCapture capture_;
|
||||
std::string device_name_;
|
||||
};
|
||||
|
||||
#endif // HDMI_IN_H
|
||||
39
hdmiIn/rk3588_hdmi_in_out-in_and_out/include/video_saver.h
Normal file
39
hdmiIn/rk3588_hdmi_in_out-in_and_out/include/video_saver.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef VIDEO_SAVER_H
|
||||
#define VIDEO_SAVER_H
|
||||
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <filesystem>
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
class VideoSaver {
|
||||
public:
|
||||
VideoSaver(int fps, int width, int height, size_t max_total_size_gb = 1);
|
||||
~VideoSaver();
|
||||
|
||||
void write_frame(const cv::Mat& frame);
|
||||
static void file_deleter();
|
||||
static void stop_file_deleter();
|
||||
|
||||
private:
|
||||
void start_new_file();
|
||||
void check_and_cleanup_files();
|
||||
|
||||
int fps_;
|
||||
int width_;
|
||||
int height_;
|
||||
int frame_count_;
|
||||
size_t max_total_size_bytes_;
|
||||
cv::VideoWriter writer_;
|
||||
|
||||
static std::mutex delete_mutex;
|
||||
static std::condition_variable delete_cv;
|
||||
static std::queue<std::string> files_to_delete_queue;
|
||||
static bool stop_delete_thread;
|
||||
};
|
||||
|
||||
#endif // VIDEO_SAVER_H
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef VIRTUAL_DEVICE_H
|
||||
#define VIRTUAL_DEVICE_H
|
||||
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
// 声明全局变量为 extern
|
||||
extern std::queue<cv::Mat> frame_queue;
|
||||
extern std::mutex queue_mutex;
|
||||
extern std::condition_variable queue_cv;
|
||||
extern bool stop_thread;
|
||||
|
||||
int init_virtual_device(const std::string& device_path, int width, int height);
|
||||
void write_to_virtual_device(int fd, const cv::Mat& frame);
|
||||
void virtual_device_writer(int fd);
|
||||
|
||||
#endif // VIRTUAL_DEVICE_H
|
||||
111
hdmiIn/rk3588_hdmi_in_out-in_and_out/main.cpp
Normal file
111
hdmiIn/rk3588_hdmi_in_out-in_and_out/main.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
#include "fps_counter.h"
|
||||
#include "video_saver.h"
|
||||
#include "virtual_device.h"
|
||||
#include "framebuffer.h"
|
||||
#include "hdmi_in.h"
|
||||
#include "kaylordut/log/logger.h"
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
#include <cstdlib> // 为了使用系统函数
|
||||
#include <sys/stat.h> // 为了使用 access 函数
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
// 执行 framebuffer 设置命令
|
||||
int result = system("sudo fbset -xres 1280 -yres 720");
|
||||
if (result != 0) {
|
||||
KAYLORDUT_LOG_ERROR("Failed to set framebuffer resolution");
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string virtual_device = "/dev/video10";
|
||||
|
||||
// 检查虚拟设备是否存在
|
||||
if (access(virtual_device.c_str(), F_OK) == -1) {
|
||||
// 如果不存在,创建虚拟设备
|
||||
result = system("sudo modprobe v4l2loopback video_nr=10 card_label=\"HDMI_Capture\" exclusive_caps=1");
|
||||
if (result != 0) {
|
||||
KAYLORDUT_LOG_ERROR("Failed to load v4l2loopback module");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
ss << argv[i] << " ";
|
||||
}
|
||||
KAYLORDUT_LOG_ERROR("Command: {}", ss.str());
|
||||
|
||||
std::string device = "/dev/video0";
|
||||
HdmiIn hdmi_in(device);
|
||||
std::string fb = "/dev/fb0";
|
||||
FrameBuffer frame_buffer(fb);
|
||||
|
||||
int vd_fd = init_virtual_device(virtual_device, 1280, 720);
|
||||
if (vd_fd == -1) {
|
||||
KAYLORDUT_LOG_ERROR("Failed to initialize virtual device");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cv::Mat frame;
|
||||
FPSCounter fps_counter;
|
||||
|
||||
const std::chrono::milliseconds frame_duration(1000 / 25);
|
||||
auto next_frame_time = std::chrono::steady_clock::now();
|
||||
|
||||
frame = hdmi_in.get_next_frame();
|
||||
if (frame.empty()) {
|
||||
KAYLORDUT_LOG_ERROR("Failed to get initial frame to determine video size");
|
||||
return -1;
|
||||
}
|
||||
|
||||
VideoSaver video_saver(25, frame.cols, frame.rows);
|
||||
|
||||
std::thread writer_thread(virtual_device_writer, vd_fd);
|
||||
std::thread deleter_thread(VideoSaver::file_deleter);
|
||||
|
||||
while (true) {
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
|
||||
frame = hdmi_in.get_next_frame();
|
||||
|
||||
if (!frame.empty()) {
|
||||
double current_fps = fps_counter.update();
|
||||
std::string fps_text = cv::format("FPS: %.1f", current_fps);
|
||||
cv::putText(frame, fps_text, cv::Point(20, 40), cv::FONT_HERSHEY_SIMPLEX, 1.2, CV_RGB(0, 255, 0), 2);
|
||||
|
||||
frame_buffer.WriteFrameBuffer(frame);
|
||||
video_saver.write_frame(frame);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(queue_mutex);
|
||||
frame_queue.push(frame.clone());
|
||||
queue_cv.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
auto process_time = std::chrono::steady_clock::now() - start_time;
|
||||
next_frame_time += frame_duration;
|
||||
std::this_thread::sleep_until(next_frame_time);
|
||||
|
||||
if (process_time > frame_duration) {
|
||||
// KAYLORDUT_LOG_ERROR("Frame processing took too long: {}ms", std::chrono::duration_cast<std::chrono::milliseconds>(process_time).count());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(queue_mutex);
|
||||
stop_thread = true;
|
||||
queue_cv.notify_one();
|
||||
}
|
||||
writer_thread.join();
|
||||
|
||||
VideoSaver::stop_file_deleter();
|
||||
deleter_thread.join();
|
||||
|
||||
if (vd_fd != -1) {
|
||||
close(vd_fd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
9
hdmiIn/rk3588_hdmi_in_out-in_and_out/readme.md
Normal file
9
hdmiIn/rk3588_hdmi_in_out-in_and_out/readme.md
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
```bash
|
||||
> v4l2-ctl -d /dev/video11 -V -D
|
||||
|
||||
#设置framebuffer缓冲区的显示尺寸和输入视频流的尺寸一致,可以降低CPU占用
|
||||
sudo fbset -xres 1280 -yres 720
|
||||
|
||||
#虚拟一个video10设备
|
||||
sudo modprobe v4l2loopback video_nr=10 card_label="HDMI_Capture" exclusive_caps=1
|
||||
25
hdmiIn/rk3588_hdmi_in_out-in_and_out/src/fps_counter.cpp
Normal file
25
hdmiIn/rk3588_hdmi_in_out-in_and_out/src/fps_counter.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include "fps_counter.h"
|
||||
|
||||
FPSCounter::FPSCounter(size_t window_size) : window_size_(window_size) {}
|
||||
|
||||
double FPSCounter::update() {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
if (!time_points_.empty()) {
|
||||
double fps = 1.0 / std::chrono::duration<double>(now - time_points_.back()).count();
|
||||
fps_values_.push_back(fps);
|
||||
if (fps_values_.size() > window_size_) {
|
||||
fps_values_.pop_front();
|
||||
}
|
||||
}
|
||||
time_points_.push_back(now);
|
||||
if (time_points_.size() > window_size_) {
|
||||
time_points_.pop_front();
|
||||
}
|
||||
|
||||
if (fps_values_.empty()) return 0.0;
|
||||
double sum = 0.0;
|
||||
for (double fps : fps_values_) {
|
||||
sum += fps;
|
||||
}
|
||||
return sum / fps_values_.size();
|
||||
}
|
||||
81
hdmiIn/rk3588_hdmi_in_out-in_and_out/src/framebuffer.cpp
Normal file
81
hdmiIn/rk3588_hdmi_in_out-in_and_out/src/framebuffer.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
//
|
||||
// Created by kaylor on 1/9/25.
|
||||
//
|
||||
|
||||
#include "framebuffer.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "kaylordut/log/logger.h"
|
||||
#include "sys/file.h"
|
||||
#include "sys/ioctl.h"
|
||||
#include "sys/mman.h"
|
||||
|
||||
FrameBuffer::FrameBuffer(std::string device) {
|
||||
device_ = device;
|
||||
fd_ = open(device_.c_str(), O_RDWR);
|
||||
if (fd_ == -1) {
|
||||
KAYLORDUT_LOG_ERROR("cannot open {}", device_);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
// 尝试独占锁定帧缓冲设备
|
||||
if (flock(fd_, LOCK_EX | LOCK_NB) == -1) {
|
||||
KAYLORDUT_LOG_ERROR("Error locking framebuffer device");
|
||||
close(fd_);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (ioctl(fd_, FBIOGET_FSCREENINFO, &fix_screeninfo_)) {
|
||||
KAYLORDUT_LOG_ERROR("cannot get fix screen info");
|
||||
flock(fd_, LOCK_UN);
|
||||
close(fd_);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
KAYLORDUT_LOG_ERROR("fix screen info: smem_len: {}, line_len: {}",
|
||||
fix_screeninfo_.smem_len, fix_screeninfo_.line_length);
|
||||
if (ioctl(fd_, FBIOGET_VSCREENINFO, &var_screeninfo_)) {
|
||||
KAYLORDUT_LOG_ERROR("cannot var fix screen info");
|
||||
flock(fd_, LOCK_UN);
|
||||
close(fd_);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
KAYLORDUT_LOG_INFO("var screen info: {}x{}", var_screeninfo_.xres,
|
||||
var_screeninfo_.yres);
|
||||
fb_ptr_ = (char *)mmap(0, fix_screeninfo_.smem_len, PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED, fd_, 0);
|
||||
if ((long)fb_ptr_ == -1) {
|
||||
KAYLORDUT_LOG_ERROR("map mem error");
|
||||
flock(fd_, LOCK_UN);
|
||||
close(fd_);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
FrameBuffer::~FrameBuffer() {
|
||||
if (fb_ptr_ != nullptr) {
|
||||
munmap(fb_ptr_, fix_screeninfo_.smem_len);
|
||||
if (fd_) {
|
||||
flock(fd_, LOCK_UN);
|
||||
close(fd_);
|
||||
}
|
||||
fb_ptr_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool FrameBuffer::WriteFrameBuffer(const cv::Mat image) {
|
||||
cv::Mat result;
|
||||
cv::resize(image, result,
|
||||
cv::Size(var_screeninfo_.xres, var_screeninfo_.yres));
|
||||
if (image.channels() != 4) {
|
||||
// KAYLORDUT_TIME_COST_INFO("BGR2BGRA",
|
||||
// cv::cvtColor(result, result, cv::COLOR_BGR2BGRA));
|
||||
}
|
||||
auto dst = fb_ptr_;
|
||||
auto src = result.data;
|
||||
for (int i = 0; i < result.rows; ++i) {
|
||||
std::memcpy(dst, src, result.cols * result.elemSize());
|
||||
dst += fix_screeninfo_.line_length;
|
||||
src += result.cols * result.elemSize();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
35
hdmiIn/rk3588_hdmi_in_out-in_and_out/src/hdmi_in.cpp
Normal file
35
hdmiIn/rk3588_hdmi_in_out-in_and_out/src/hdmi_in.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// Created by kaylor on 1/8/25.
|
||||
//
|
||||
|
||||
#include "hdmi_in.h"
|
||||
|
||||
#include "kaylordut/log/logger.h"
|
||||
|
||||
HdmiIn::HdmiIn(std::string device_name) {
|
||||
device_name_ = device_name;
|
||||
capture_ = cv::VideoCapture(
|
||||
"v4l2src device=" + device_name_ +
|
||||
" ! videoconvert ! video/x-raw, format=BGR ! appsink",
|
||||
cv::CAP_GSTREAMER);
|
||||
if (!capture_.isOpened()) {
|
||||
KAYLORDUT_LOG_ERROR("cannot open {}", device_name_);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
HdmiIn::~HdmiIn() {
|
||||
if (capture_.isOpened()) {
|
||||
capture_.release();
|
||||
}
|
||||
}
|
||||
|
||||
cv::Mat HdmiIn::get_next_frame() {
|
||||
cv::Mat frame;
|
||||
capture_ >> frame;
|
||||
if (frame.empty()) {
|
||||
KAYLORDUT_LOG_WARN("cannot read a new frame");
|
||||
// return cv::Mat();
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
109
hdmiIn/rk3588_hdmi_in_out-in_and_out/src/video_saver.cpp
Normal file
109
hdmiIn/rk3588_hdmi_in_out-in_and_out/src/video_saver.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
#include "video_saver.h"
|
||||
#include "kaylordut/log/logger.h"
|
||||
|
||||
std::mutex VideoSaver::delete_mutex;
|
||||
std::condition_variable VideoSaver::delete_cv;
|
||||
std::queue<std::string> VideoSaver::files_to_delete_queue;
|
||||
bool VideoSaver::stop_delete_thread = false;
|
||||
|
||||
VideoSaver::VideoSaver(int fps, int width, int height, size_t max_total_size_gb)
|
||||
: fps_(fps), width_(width), height_(height), frame_count_(0), max_total_size_bytes_(max_total_size_gb * 1024 * 1024 * 1024) {
|
||||
fs::create_directory("./video_output");
|
||||
start_new_file();
|
||||
}
|
||||
|
||||
VideoSaver::~VideoSaver() {
|
||||
if (writer_.isOpened()) {
|
||||
writer_.release();
|
||||
}
|
||||
}
|
||||
|
||||
void VideoSaver::write_frame(const cv::Mat& frame) {
|
||||
if (!writer_.isOpened()) {
|
||||
KAYLORDUT_LOG_ERROR("Video writer is not opened!");
|
||||
return;
|
||||
}
|
||||
|
||||
writer_.write(frame);
|
||||
frame_count_++;
|
||||
|
||||
if (frame_count_ >= fps_) {
|
||||
writer_.release();
|
||||
frame_count_ = 0;
|
||||
start_new_file();
|
||||
}
|
||||
}
|
||||
|
||||
void VideoSaver::file_deleter() {
|
||||
while (true) {
|
||||
std::string file_path;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(delete_mutex);
|
||||
delete_cv.wait(lock, [] { return !files_to_delete_queue.empty() || stop_delete_thread; });
|
||||
if (stop_delete_thread && files_to_delete_queue.empty()) break;
|
||||
file_path = files_to_delete_queue.front();
|
||||
files_to_delete_queue.pop();
|
||||
}
|
||||
fs::remove(file_path);
|
||||
KAYLORDUT_LOG_INFO("Deleted old file: {}", file_path);
|
||||
}
|
||||
}
|
||||
|
||||
void VideoSaver::stop_file_deleter() {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(delete_mutex);
|
||||
stop_delete_thread = true;
|
||||
delete_cv.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void VideoSaver::start_new_file() {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto in_time_t = std::chrono::system_clock::to_time_t(now);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << std::put_time(std::localtime(&in_time_t), "%Y%m%d_%H%M%S");
|
||||
std::string timestamp = ss.str();
|
||||
|
||||
std::string filename = "./video_output/video_" + timestamp + ".mp4";
|
||||
int fourcc = cv::VideoWriter::fourcc('m', 'p', '4', 'v');
|
||||
writer_.open(filename, fourcc, fps_, cv::Size(width_, height_));
|
||||
|
||||
if (!writer_.isOpened()) {
|
||||
KAYLORDUT_LOG_ERROR("Could not open video file for writing: {}", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
check_and_cleanup_files();
|
||||
}
|
||||
|
||||
void VideoSaver::check_and_cleanup_files() {
|
||||
std::vector<fs::path> files;
|
||||
size_t total_size = 0;
|
||||
|
||||
for (const auto& entry : fs::directory_iterator("./video_output")) {
|
||||
if (entry.is_regular_file() && entry.path().extension() == ".mp4") {
|
||||
files.push_back(entry.path());
|
||||
total_size += fs::file_size(entry.path());
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(files.begin(), files.end(), [](const fs::path& a, const fs::path& b) {
|
||||
return fs::last_write_time(a) < fs::last_write_time(b);
|
||||
});
|
||||
|
||||
size_t files_to_delete = 0;
|
||||
while (total_size > max_total_size_bytes_ && !files.empty() && files_to_delete < 5) {
|
||||
total_size -= fs::file_size(files.front());
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(delete_mutex);
|
||||
files_to_delete_queue.push(files.front().string());
|
||||
}
|
||||
files.erase(files.begin());
|
||||
files_to_delete++;
|
||||
}
|
||||
|
||||
if (files_to_delete > 0) {
|
||||
delete_cv.notify_one();
|
||||
}
|
||||
}
|
||||
65
hdmiIn/rk3588_hdmi_in_out-in_and_out/src/virtual_device.cpp
Normal file
65
hdmiIn/rk3588_hdmi_in_out-in_and_out/src/virtual_device.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "virtual_device.h"
|
||||
#include <fcntl.h> // 包含 open、O_RDWR 等
|
||||
#include <unistd.h> // 包含 close、write 等
|
||||
#include <linux/videodev2.h> // 包含 Video4Linux2 相关定义
|
||||
#include "kaylordut/log/logger.h" // 包含日志宏
|
||||
#include <sys/ioctl.h> // 包含 ioctl 函数
|
||||
|
||||
// 定义全局变量
|
||||
std::queue<cv::Mat> frame_queue;
|
||||
std::mutex queue_mutex;
|
||||
std::condition_variable queue_cv;
|
||||
bool stop_thread = false;
|
||||
|
||||
int init_virtual_device(const std::string& device_path, int width, int height) {
|
||||
int fd = open(device_path.c_str(), O_RDWR);
|
||||
if (fd == -1) {
|
||||
KAYLORDUT_LOG_ERROR("Failed to open virtual video device: {}", device_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct v4l2_format fmt = {};
|
||||
fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
|
||||
fmt.fmt.pix.width = width;
|
||||
fmt.fmt.pix.height = height;
|
||||
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24;
|
||||
fmt.fmt.pix.field = V4L2_FIELD_NONE;
|
||||
|
||||
if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {
|
||||
KAYLORDUT_LOG_ERROR("Failed to set video format");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
void write_to_virtual_device(int fd, const cv::Mat& frame) {
|
||||
if (fd == -1 || frame.empty()) return;
|
||||
|
||||
cv::Mat continuous_frame;
|
||||
if (!frame.isContinuous()) {
|
||||
continuous_frame = frame.clone();
|
||||
} else {
|
||||
continuous_frame = frame;
|
||||
}
|
||||
|
||||
ssize_t written = write(fd, continuous_frame.data, continuous_frame.total() * continuous_frame.elemSize());
|
||||
if (written == -1) {
|
||||
KAYLORDUT_LOG_ERROR("Failed to write to virtual device");
|
||||
}
|
||||
}
|
||||
|
||||
void virtual_device_writer(int fd) {
|
||||
while (true) {
|
||||
cv::Mat frame;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queue_mutex);
|
||||
queue_cv.wait(lock, [] { return !frame_queue.empty() || stop_thread; });
|
||||
if (stop_thread && frame_queue.empty()) break;
|
||||
frame = frame_queue.front();
|
||||
frame_queue.pop();
|
||||
}
|
||||
write_to_virtual_device(fd, frame);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user