Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Postproc #676

Merged
merged 6 commits into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/rpicam-apps-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
clean: true

- name: Configure meson
run: meson setup ${{github.workspace}}/build --pkg-config-path=${{env.LIBCAMERA_LKG_DIR}}/lib/aarch64-linux-gnu/pkgconfig/ -Dbuildtype=release -Denable_drm=false -Denable_egl=false -Denable_qt=false -Denable_opencv=false -Denable_tflite=false -Denable_libav=false
run: meson setup ${{github.workspace}}/build --pkg-config-path=${{env.LIBCAMERA_LKG_DIR}}/lib/aarch64-linux-gnu/pkgconfig/ -Dbuildtype=release -Denable_drm='disabled' -Denable_egl='disabled' -Denable_qt='disabled' -Denable_opencv='disabled' -Denable_tflite='disabled' -Denable_libav='disabled'
timeout-minutes: 5

- name: Build
Expand Down Expand Up @@ -107,7 +107,7 @@ jobs:
run: ldd ${{github.workspace}}/build/apps/rpicam-hello | grep libcamera

- name: Test
run: ${{github.workspace}}/utils/test.py --exe-dir ${{github.workspace}}/build/apps/ --output-dir ${{github.workspace}}/test_output --json-dir ${{github.workspace}}/assets
run: ${{github.workspace}}/utils/test.py --exe-dir ${{github.workspace}}/build/apps/ --output-dir ${{github.workspace}}/test_output --json-dir ${{github.workspace}}/assets --post-process-libs ${{github.workspace}}/build/post_processing_stages/
timeout-minutes: 8

- name: Upload test output
Expand Down
3 changes: 3 additions & 0 deletions core/options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ Options::Options()
"Set the output file name")
("post-process-file", value<std::string>(&post_process_file),
"Set the file name for configuring the post-processing")
("post-process-libs", value<std::string>(&post_process_libs),
"Set a custom location for the post-processing library .so files")
("nopreview,n", value<bool>(&nopreview)->default_value(false)->implicit_value(true),
"Do not show a preview window")
("preview,p", value<std::string>(&preview)->default_value("0,0,0,0"),
Expand Down Expand Up @@ -631,6 +633,7 @@ void Options::Print() const
std::cerr << " height: " << height << std::endl;
std::cerr << " output: " << output << std::endl;
std::cerr << " post_process_file: " << post_process_file << std::endl;
std::cerr << " post_process_libs: " << post_process_libs << std::endl;
if (nopreview)
std::cerr << " preview: none" << std::endl;
else if (fullscreen)
Expand Down
1 change: 1 addition & 0 deletions core/options.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ struct Options
std::string config_file;
std::string output;
std::string post_process_file;
std::string post_process_libs;
unsigned int width;
unsigned int height;
bool nopreview;
Expand Down
74 changes: 74 additions & 0 deletions core/post_processor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
* post_processor.cpp - Post processor implementation.
*/

#include <dlfcn.h>
#include <filesystem>
#include <iostream>

#include "core/options.hpp"
#include "core/rpicam_app.hpp"
#include "core/post_processor.hpp"

Expand All @@ -15,12 +18,83 @@
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>

#include "post_processing_stages/postproc_lib.h"

namespace fs = std::filesystem;

PostProcessingLib::PostProcessingLib(const std::string &lib)
{
if (!lib.empty())
{
lib_ = dlopen(lib.c_str(), RTLD_LAZY);
if (!lib_)
LOG_ERROR("Unable to open " << lib << " with error: " << dlerror());
}
}

PostProcessingLib::PostProcessingLib(PostProcessingLib &&other)
{
lib_ = other.lib_;
symbol_map_ = std::move(other.symbol_map_);
other.lib_ = nullptr;
}

PostProcessingLib::~PostProcessingLib()
{
if (lib_)
dlclose(lib_);
}

const void *PostProcessingLib::GetSymbol(const std::string &symbol)
{
if (!lib_)
return nullptr;

std::scoped_lock<std::mutex> l(lock_);

const auto it = symbol_map_.find(symbol);
if (it == symbol_map_.end())
{
const void *fn = dlsym(lib_, symbol.c_str());

if (!fn)
{
LOG_ERROR("Unable to find postprocessing symbol " << symbol << " with error: " << dlerror());
return nullptr;
}

symbol_map_[symbol] = fn;
}

return symbol_map_[symbol];
}

PostProcessor::PostProcessor(RPiCamApp *app) : app_(app)
{
}

PostProcessor::~PostProcessor()
{
// Must clear stages_ before dynamic_stages_ as the latter will unload the necessary symbols.
stages_.clear();
dynamic_stages_.clear();
}

void PostProcessor::LoadModules(const std::string &lib_dir)
{
const fs::path path(!lib_dir.empty() ? lib_dir : POSTPROC_LIB_DIR);
const std::string ext(".so");

if (!fs::exists(path))
return;

// Dynamically load all .so files from the system postprocessing lib path.
// This will automatically register the stages with the factory.
for (auto const &p : fs::recursive_directory_iterator(path))
{
if (p.path().extension() == ext)
dynamic_stages_.emplace_back(p.path().string());
}
}

void PostProcessor::Read(std::string const &filename)
Expand Down
21 changes: 21 additions & 0 deletions core/post_processor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,33 @@ using PostProcessorCallback = std::function<void(CompletedRequestPtr &)>;
using StreamConfiguration = libcamera::StreamConfiguration;
typedef std::unique_ptr<PostProcessingStage> StagePtr;

// Dynamic postprocessing library helper.
class PostProcessingLib
{
public:
PostProcessingLib(const std::string &lib);
PostProcessingLib(PostProcessingLib &&other);
PostProcessingLib(const PostProcessingLib &other) = delete;
PostProcessingLib &operator=(const PostProcessingLib &other) = delete;
~PostProcessingLib();

const void *GetSymbol(const std::string &symbol);

private:
void *lib_ = nullptr;
std::map<std::string, const void *> symbol_map_;
std::mutex lock_;
};

class PostProcessor
{
public:
PostProcessor(RPiCamApp *app);

~PostProcessor();

void LoadModules(const std::string &lib_dir);

void Read(std::string const &filename);

void SetCallback(PostProcessorCallback callback);
Expand All @@ -57,6 +77,7 @@ class PostProcessor

RPiCamApp *app_;
std::vector<StagePtr> stages_;
std::vector<PostProcessingLib> dynamic_stages_;
void outputThread();

std::queue<CompletedRequestPtr> requests_;
Expand Down
3 changes: 3 additions & 0 deletions core/rpicam_app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,10 @@ void RPiCamApp::OpenCamera()
LOG(2, "Acquired camera " << cam_id);

if (!options_->post_process_file.empty())
{
post_processor_.LoadModules(options_->post_process_libs);
post_processor_.Read(options_->post_process_file);
}
// The queue takes over ownership from the post-processor.
post_processor_.SetCallback(
[this](CompletedRequestPtr &r) { this->msg_queue_.Post(Msg(MsgType::RequestComplete, std::move(r))); });
Expand Down
20 changes: 9 additions & 11 deletions encoder/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,18 @@ encoder_headers = files([
'null_encoder.hpp',
])

enable_libav = get_option('enable_libav')
libav_dep_names = ['libavcodec', 'libavdevice', 'libavformat', 'libavutil', 'libswresample']
libav_deps = []

if enable_libav
foreach name : libav_dep_names
dep = dependency(name, required : false)
if not dep.found()
enable_libav = false
break
endif
libav_deps += dep
endforeach
endif
enable_libav = true
foreach name : libav_dep_names
dep = dependency(name, required : get_option('enable_libav'))
if not dep.found()
enable_libav = false
break
endif
libav_deps += dep
endforeach

if enable_libav
rpicam_app_src += files('libav_encoder.cpp')
Expand Down
7 changes: 5 additions & 2 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ elif neon == 'armv8-neon'
cpp_arguments += ['-mfpu=neon-fp-armv8', '-ftree-vectorize']
endif

dl_dep = dependency('dl', required : true)
libcamera_dep = dependency('libcamera', required : true)

summary({
Expand All @@ -40,18 +41,20 @@ summary({
section : 'libcamera')

rpicam_app_src = []
rpicam_app_dep = [libcamera_dep]
rpicam_app_dep = [libcamera_dep, dl_dep]

subdir('core')
subdir('encoder')
subdir('image')
subdir('output')
subdir('preview')
subdir('post_processing_stages')
subdir('utils')

add_project_arguments(cpp_arguments, language : 'cpp')

# Must be put after add_project_arguments as it defines shared library targets.
subdir('post_processing_stages')

rpicam_app = library(
'rpicam_app',
rpicam_app_src,
Expand Down
24 changes: 12 additions & 12 deletions meson_options.txt
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
option('enable_libav',
type : 'boolean',
value : true,
type : 'feature',
value : 'auto',
description : 'Enable the libav encoder for video/audio capture')

option('enable_drm',
type : 'boolean',
value : true,
type : 'feature',
value : 'auto',
description : 'Enable DRM preview window support')

option('enable_egl',
type : 'boolean',
value : true,
type : 'feature',
value : 'auto',
description : 'Enable EGL preview window support')

option('enable_qt',
type : 'boolean',
value : true,
type : 'feature',
value : 'auto',
description : 'Enable QT preview window support')

option('enable_opencv',
type : 'boolean',
value : true,
type : 'feature',
value : 'disabled',
description : 'Enable OpenCV postprocessing support')

option('enable_tflite',
type : 'boolean',
value : false,
type : 'feature',
value : 'disabled',
description : 'Enable Tensorflow Lite postprocessing support')

option('neon_flags',
Expand Down
Loading