Skip to content

Commit eeaa7de

Browse files
HarveyCHYangkbingham
authored andcommitted
libcamera: pipeline: Add test pattern for VirtualPipelineHandler
Add a test pattern generator class hierarchy for the Virtual pipeline handler. Implement two types of test patterns: color bars and diagonal lines generator and use them in the Virtual pipeline handler. A shifting mechanism is enabled. For each frame, the image is shifted to the left by 1 pixel. It drops FPS though. Add a dependency for libyuv to the build system to generate images in NV12 format from the test pattern. Signed-off-by: Konami Shu <konamiz@google.com> Co-developed-by: Harvey Yang <chenghaoyang@chromium.org> Signed-off-by: Harvey Yang <chenghaoyang@chromium.org> Co-developed-by: Yunke Cao <yunkec@chromium.org> Signed-off-by: Yunke Cao <yunkec@chromium.org> Co-developed-by: Tomasz Figa <tfiga@chromium.org> Signed-off-by: Tomasz Figa <tfiga@chromium.org> Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
1 parent 3a884eb commit eeaa7de

File tree

8 files changed

+282
-22
lines changed

8 files changed

+282
-22
lines changed

src/android/meson.build

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,6 @@ foreach dep : android_deps
1515
endif
1616
endforeach
1717

18-
libyuv_dep = dependency('libyuv', required : false)
19-
20-
# Fallback to a subproject if libyuv isn't found, as it's typically not
21-
# provided by distributions.
22-
if not libyuv_dep.found()
23-
cmake = import('cmake')
24-
25-
libyuv_vars = cmake.subproject_options()
26-
libyuv_vars.add_cmake_defines({'CMAKE_POSITION_INDEPENDENT_CODE': 'ON'})
27-
libyuv_vars.set_override_option('cpp_std', 'c++17')
28-
libyuv_vars.append_compile_args('cpp',
29-
'-Wno-sign-compare',
30-
'-Wno-unused-variable',
31-
'-Wno-unused-parameter')
32-
libyuv_vars.append_link_args('-ljpeg')
33-
libyuv = cmake.subproject('libyuv', options : libyuv_vars)
34-
libyuv_dep = libyuv.dependency('yuv')
35-
endif
36-
3718
android_deps += [libyuv_dep]
3819

3920
android_hal_sources = files([
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2+
/*
3+
* Copyright (C) 2024, Google Inc.
4+
*
5+
* Virtual cameras helper to generate frames
6+
*/
7+
8+
#pragma once
9+
10+
#include <libcamera/framebuffer.h>
11+
#include <libcamera/geometry.h>
12+
13+
namespace libcamera {
14+
15+
class FrameGenerator
16+
{
17+
public:
18+
virtual ~FrameGenerator() = default;
19+
20+
virtual void configure(const Size &size) = 0;
21+
22+
virtual int generateFrame(const Size &size,
23+
const FrameBuffer *buffer) = 0;
24+
25+
protected:
26+
FrameGenerator() {}
27+
};
28+
29+
} /* namespace libcamera */
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# SPDX-License-Identifier: CC0-1.0
22

33
libcamera_internal_sources += files([
4+
'test_pattern_generator.cpp',
45
'virtual.cpp',
56
])
7+
8+
libcamera_deps += [libyuv_dep]
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2+
/*
3+
* Copyright (C) 2024, Google Inc.
4+
*
5+
* Derived class of FrameGenerator for generating test patterns
6+
*/
7+
8+
#include "test_pattern_generator.h"
9+
10+
#include <libcamera/base/log.h>
11+
12+
#include "libcamera/internal/mapped_framebuffer.h"
13+
14+
#include <libyuv/convert_from_argb.h>
15+
16+
namespace libcamera {
17+
18+
LOG_DECLARE_CATEGORY(Virtual)
19+
20+
static const unsigned int kARGBSize = 4;
21+
22+
int TestPatternGenerator::generateFrame(const Size &size,
23+
const FrameBuffer *buffer)
24+
{
25+
MappedFrameBuffer mappedFrameBuffer(buffer,
26+
MappedFrameBuffer::MapFlag::Write);
27+
28+
auto planes = mappedFrameBuffer.planes();
29+
30+
shiftLeft(size);
31+
32+
/* Convert the template_ to the frame buffer */
33+
int ret = libyuv::ARGBToNV12(template_.get(), size.width * kARGBSize,
34+
planes[0].begin(), size.width,
35+
planes[1].begin(), size.width,
36+
size.width, size.height);
37+
if (ret != 0)
38+
LOG(Virtual, Error) << "ARGBToNV12() failed with " << ret;
39+
40+
return ret;
41+
}
42+
43+
void TestPatternGenerator::shiftLeft(const Size &size)
44+
{
45+
/* Store the first column temporarily */
46+
auto firstColumn = std::make_unique<uint8_t[]>(size.height * kARGBSize);
47+
for (size_t h = 0; h < size.height; h++) {
48+
unsigned int index = h * size.width * kARGBSize;
49+
unsigned int index1 = h * kARGBSize;
50+
firstColumn[index1] = template_[index];
51+
firstColumn[index1 + 1] = template_[index + 1];
52+
firstColumn[index1 + 2] = template_[index + 2];
53+
firstColumn[index1 + 3] = 0x00;
54+
}
55+
56+
/* Overwrite template_ */
57+
uint8_t *buf = template_.get();
58+
for (size_t h = 0; h < size.height; h++) {
59+
for (size_t w = 0; w < size.width - 1; w++) {
60+
/* Overwrite with the pixel on the right */
61+
unsigned int index = (h * size.width + w + 1) * kARGBSize;
62+
*buf++ = template_[index]; /* B */
63+
*buf++ = template_[index + 1]; /* G */
64+
*buf++ = template_[index + 2]; /* R */
65+
*buf++ = 0x00; /* A */
66+
}
67+
/* Overwrite the new last column with the original first column */
68+
unsigned int index1 = h * kARGBSize;
69+
*buf++ = firstColumn[index1]; /* B */
70+
*buf++ = firstColumn[index1 + 1]; /* G */
71+
*buf++ = firstColumn[index1 + 2]; /* R */
72+
*buf++ = 0x00; /* A */
73+
}
74+
}
75+
76+
void ColorBarsGenerator::configure(const Size &size)
77+
{
78+
constexpr uint8_t kColorBar[8][3] = {
79+
/* R, G, B */
80+
{ 0xff, 0xff, 0xff }, /* White */
81+
{ 0xff, 0xff, 0x00 }, /* Yellow */
82+
{ 0x00, 0xff, 0xff }, /* Cyan */
83+
{ 0x00, 0xff, 0x00 }, /* Green */
84+
{ 0xff, 0x00, 0xff }, /* Magenta */
85+
{ 0xff, 0x00, 0x00 }, /* Red */
86+
{ 0x00, 0x00, 0xff }, /* Blue */
87+
{ 0x00, 0x00, 0x00 }, /* Black */
88+
};
89+
90+
template_ = std::make_unique<uint8_t[]>(
91+
size.width * size.height * kARGBSize);
92+
93+
unsigned int colorBarWidth = size.width / std::size(kColorBar);
94+
95+
uint8_t *buf = template_.get();
96+
for (size_t h = 0; h < size.height; h++) {
97+
for (size_t w = 0; w < size.width; w++) {
98+
/* Repeat when the width is exceed */
99+
unsigned int index = (w / colorBarWidth) % std::size(kColorBar);
100+
101+
*buf++ = kColorBar[index][2]; /* B */
102+
*buf++ = kColorBar[index][1]; /* G */
103+
*buf++ = kColorBar[index][0]; /* R */
104+
*buf++ = 0x00; /* A */
105+
}
106+
}
107+
}
108+
109+
void DiagonalLinesGenerator::configure(const Size &size)
110+
{
111+
constexpr uint8_t kColorBar[2][3] = {
112+
/* R, G, B */
113+
{ 0xff, 0xff, 0xff }, /* White */
114+
{ 0x00, 0x00, 0x00 }, /* Black */
115+
};
116+
117+
template_ = std::make_unique<uint8_t[]>(
118+
size.width * size.height * kARGBSize);
119+
120+
unsigned int lineWidth = size.width / 10;
121+
122+
uint8_t *buf = template_.get();
123+
for (size_t h = 0; h < size.height; h++) {
124+
for (size_t w = 0; w < size.width; w++) {
125+
/* Repeat when the width is exceed */
126+
int index = ((w + h) / lineWidth) % 2;
127+
128+
*buf++ = kColorBar[index][2]; /* B */
129+
*buf++ = kColorBar[index][1]; /* G */
130+
*buf++ = kColorBar[index][0]; /* R */
131+
*buf++ = 0x00; /* A */
132+
}
133+
}
134+
}
135+
136+
} /* namespace libcamera */
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2+
/*
3+
* Copyright (C) 2024, Google Inc.
4+
*
5+
* Derived class of FrameGenerator for generating test patterns
6+
*/
7+
8+
#pragma once
9+
10+
#include <memory>
11+
12+
#include <libcamera/framebuffer.h>
13+
#include <libcamera/geometry.h>
14+
15+
#include "frame_generator.h"
16+
17+
namespace libcamera {
18+
19+
enum class TestPattern : char {
20+
ColorBars = 0,
21+
DiagonalLines = 1,
22+
};
23+
24+
class TestPatternGenerator : public FrameGenerator
25+
{
26+
public:
27+
int generateFrame(const Size &size, const FrameBuffer *buffer) override;
28+
29+
protected:
30+
/* Buffer of test pattern template */
31+
std::unique_ptr<uint8_t[]> template_;
32+
33+
private:
34+
/* Shift the buffer by 1 pixel left each frame */
35+
void shiftLeft(const Size &size);
36+
};
37+
38+
class ColorBarsGenerator : public TestPatternGenerator
39+
{
40+
public:
41+
/* Generate a template buffer of the color bar test pattern. */
42+
void configure(const Size &size) override;
43+
};
44+
45+
class DiagonalLinesGenerator : public TestPatternGenerator
46+
{
47+
public:
48+
/* Generate a template buffer of the diagonal lines test pattern. */
49+
void configure(const Size &size) override;
50+
};
51+
52+
} /* namespace libcamera */

src/libcamera/pipeline/virtual/virtual.cpp

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "libcamera/internal/camera.h"
3535
#include "libcamera/internal/dma_buf_allocator.h"
3636
#include "libcamera/internal/formats.h"
37+
#include "libcamera/internal/framebuffer.h"
3738
#include "libcamera/internal/pipeline_handler.h"
3839

3940
namespace libcamera {
@@ -94,6 +95,8 @@ class PipelineHandlerVirtual : public PipelineHandler
9495
return static_cast<VirtualCameraData *>(camera->_d());
9596
}
9697

98+
void initFrameGenerator(Camera *camera);
99+
97100
DmaBufAllocator dmaBufAllocator_;
98101

99102
bool resetCreated_ = false;
@@ -241,8 +244,10 @@ int PipelineHandlerVirtual::configure(Camera *camera,
241244
CameraConfiguration *config)
242245
{
243246
VirtualCameraData *data = cameraData(camera);
244-
for (auto [i, c] : utils::enumerate(*config))
247+
for (auto [i, c] : utils::enumerate(*config)) {
245248
c.setStream(&data->streamConfigs_[i].stream);
249+
data->streamConfigs_[i].frameGenerator->configure(c.size);
250+
}
246251

247252
return 0;
248253
}
@@ -278,8 +283,24 @@ void PipelineHandlerVirtual::stopDevice([[maybe_unused]] Camera *camera)
278283
int PipelineHandlerVirtual::queueRequestDevice([[maybe_unused]] Camera *camera,
279284
Request *request)
280285
{
281-
for (auto it : request->buffers())
282-
completeBuffer(request, it.second);
286+
VirtualCameraData *data = cameraData(camera);
287+
288+
for (auto const &[stream, buffer] : request->buffers()) {
289+
bool found = false;
290+
/* map buffer and fill test patterns */
291+
for (auto &streamConfig : data->streamConfigs_) {
292+
if (stream == &streamConfig.stream) {
293+
found = true;
294+
if (streamConfig.frameGenerator->generateFrame(
295+
stream->configuration().size, buffer))
296+
buffer->_d()->cancel();
297+
298+
completeBuffer(request, buffer);
299+
break;
300+
}
301+
}
302+
ASSERT(found);
303+
}
283304

284305
request->metadata().set(controls::SensorTimestamp, currentTimestamp());
285306
completeRequest(request);
@@ -325,13 +346,27 @@ bool PipelineHandlerVirtual::match([[maybe_unused]] DeviceEnumerator *enumerator
325346

326347
const std::string id = "Virtual0";
327348
std::shared_ptr<Camera> camera = Camera::create(std::move(data), id, streams);
349+
350+
initFrameGenerator(camera.get());
351+
328352
registerCamera(std::move(camera));
329353

330354
resetCreated_ = true;
331355

332356
return true;
333357
}
334358

359+
void PipelineHandlerVirtual::initFrameGenerator(Camera *camera)
360+
{
361+
auto data = cameraData(camera);
362+
for (auto &streamConfig : data->streamConfigs_) {
363+
if (data->testPattern_ == TestPattern::DiagonalLines)
364+
streamConfig.frameGenerator = std::make_unique<DiagonalLinesGenerator>();
365+
else
366+
streamConfig.frameGenerator = std::make_unique<ColorBarsGenerator>();
367+
}
368+
}
369+
335370
REGISTER_PIPELINE_HANDLER(PipelineHandlerVirtual, "virtual")
336371

337372
} /* namespace libcamera */

src/libcamera/pipeline/virtual/virtual.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#include "libcamera/internal/camera.h"
1616
#include "libcamera/internal/pipeline_handler.h"
1717

18+
#include "test_pattern_generator.h"
19+
1820
namespace libcamera {
1921

2022
class VirtualCameraData : public Camera::Private
@@ -28,13 +30,16 @@ class VirtualCameraData : public Camera::Private
2830
};
2931
struct StreamConfig {
3032
Stream stream;
33+
std::unique_ptr<FrameGenerator> frameGenerator;
3134
};
3235

3336
VirtualCameraData(PipelineHandler *pipe,
3437
const std::vector<Resolution> &supportedResolutions);
3538

3639
~VirtualCameraData() = default;
3740

41+
TestPattern testPattern_ = TestPattern::ColorBars;
42+
3843
const std::vector<Resolution> supportedResolutions_;
3944
Size maxResolutionSize_;
4045
Size minResolutionSize_;

src/meson.build

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,25 @@ else
2727
ipa_sign_module = false
2828
endif
2929

30+
libyuv_dep = dependency('libyuv', required : false)
31+
32+
# Fallback to a subproject if libyuv isn't found, as it's typically not
33+
# provided by distributions.
34+
if not libyuv_dep.found()
35+
cmake = import('cmake')
36+
37+
libyuv_vars = cmake.subproject_options()
38+
libyuv_vars.add_cmake_defines({'CMAKE_POSITION_INDEPENDENT_CODE': 'ON'})
39+
libyuv_vars.set_override_option('cpp_std', 'c++17')
40+
libyuv_vars.append_compile_args('cpp',
41+
'-Wno-sign-compare',
42+
'-Wno-unused-variable',
43+
'-Wno-unused-parameter')
44+
libyuv_vars.append_link_args('-ljpeg')
45+
libyuv = cmake.subproject('libyuv', options : libyuv_vars)
46+
libyuv_dep = libyuv.dependency('yuv')
47+
endif
48+
3049
# libcamera must be built first as a dependency to the other components.
3150
subdir('libcamera')
3251

0 commit comments

Comments
 (0)