<div dir="ltr">Thanks for the catch Kieran!<div><br></div><div>It's due to a bug in VirtualPipelineHandler::configure [1]:</div><div>I used `<span style="color:rgb(0,127,0);font-family:Menlo,Monaco,Consolas,"Courier New",monospace;font-size:13px">data->streamConfigs_[i].stream.configuration()</span>`</div><div>before it's set in `Camera::configure()`. Should be using</div><div>`CameraConfiguration[i].configuration()` directly.</div><div>Will be fixed in v12.</div><div><br></div><div>[1]: <a href="https://patchwork.libcamera.org/patch/21196/">https://patchwork.libcamera.org/patch/21196/</a></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Mon, Sep 9, 2024 at 8:45 PM Kieran Bingham <<a href="mailto:kieran.bingham@ideasonboard.com">kieran.bingham@ideasonboard.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Hi Harvey,<br>
<br>
Quoting Harvey Yang (2024-09-07 15:28:25)<br>
> Hi all,<br>
> <br>
> This series adds virtual pipeline handler, which doesn't depend on any<br>
> hardware, like camera sensor or ISP. Currently the configuration<br>
> supports test patterns and images.<br>
> <br>
> It passed the gitlab pipeline:<br>
> <a href="https://gitlab.freedesktop.org/chenghaoyang/libcamera/-/pipelines/1266568" rel="noreferrer" target="_blank">https://gitlab.freedesktop.org/chenghaoyang/libcamera/-/pipelines/1266568</a><br>
> <br>
> I failed to pass multi_stream_test when trying to enable multiple<br>
> streams though. Please give me some hints what it tests and what I<br>
> missed:<br>
<br>
I tried to run this but it crashed.<br>
<br>
I ran gdb --args ./build/gcc/src/apps/qcam/qcam and then chose virtual0:<br>
<br>
<br>
And here's the backtraces:<br>
<br>
(gdb) thread apply all bt<br>
<br>
Thread 9 (Thread 0xffffda00eec0 (LWP 31018) "qcam"):<br>
#0 0x0000fffff7c92ff0 in libcamera::TestPatternGenerator::shiftLeft (this=0xffffb8060d40, size=...) at ../../src/libcamera/pipeline/virtual/test_pattern_generator.cpp:50<br>
#1 0x0000fffff7c92e2c in libcamera::TestPatternGenerator::generateFrame (this=0xffffb8060d40, size=..., buffer=0xffffb806fa00) at ../../src/libcamera/pipeline/virtual/test_pattern_generator.cpp:31<br>
#2 0x0000fffff7c94870 in libcamera::PipelineHandlerVirtual::queueRequestDevice (this=0xffffb80464a0, camera=0xffffb808a4d0, request=0xffffdc015e20) at ../../src/libcamera/pipeline/virtual/virtual.cpp:234<br>
#3 0x0000fffff7b76a30 in libcamera::PipelineHandler::doQueueRequest (this=0xffffb80464a0, request=0xffffdc015e20) at ../../src/libcamera/pipeline_handler.cpp:472<br>
#4 0x0000fffff7b76ab8 in libcamera::PipelineHandler::doQueueRequests (this=0xffffb80464a0) at ../../src/libcamera/pipeline_handler.cpp:492<br>
#5 0x0000fffff7b7dcac in libcamera::BoundMethodMember<libcamera::PipelineHandler, void>::invoke (this=0x8fdb40) at ../../include/libcamera/base/bound_method.h:191<br>
#6 0x0000fffff7ad7368 in libcamera::BoundMethodArgs<void>::invokePack<, void>(libcamera::BoundMethodPackBase*, std::integer_sequence<unsigned long>) (this=0x8fdb40, pack=0xffffb806e100) at ../../include/libcamera/base/bound_method.h:115<br>
#7 0x0000fffff7ad6728 in libcamera::BoundMethodArgs<void>::invokePack (this=0x8fdb40, pack=0xffffb806e100) at ../../include/libcamera/base/bound_method.h:124<br>
#8 0x0000fffff7f46ed4 in libcamera::BoundMethodBase::activatePack (this=0x8fdb40, pack=std::shared_ptr<libcamera::BoundMethodPackBase> (use count 2, weak count 0) = {...}, deleteMethod=false) at ../../src/libcamera/base/bound_method.cpp:85<br>
#9 0x0000fffff7b7dbc8 in libcamera::BoundMethodMember<libcamera::PipelineHandler, void>::activate (this=0x8fdb40, deleteMethod=false) at ../../include/libcamera/base/bound_method.h:184<br>
#10 0x0000fffff7af33e4 in libcamera::Signal<>::emit() (this=0x8fda90) at ../../include/libcamera/base/signal.h:153<br>
#11 0x0000fffff7b29b74 in libcamera::Request::Private::emitPrepareCompleted (this=0x8fda80) at ../../src/libcamera/request.cpp:190<br>
#12 0x0000fffff7b29cf0 in libcamera::Request::Private::prepare (this=0x8fda80, timeout=std::chrono::duration = { 300ms }) at ../../src/libcamera/request.cpp:243<br>
#13 0x0000fffff7b76968 in libcamera::PipelineHandler::queueRequest (this=0xffffb80464a0, request=0xffffdc015e20) at ../../src/libcamera/pipeline_handler.cpp:451<br>
#14 0x0000fffff7b0b768 in libcamera::BoundMethodMember<libcamera::PipelineHandler, void, libcamera::Request*>::invoke (this=0x86ad50, args#0=0xffffdc015e20) at ../../include/libcamera/base/bound_method.h:191<br>
#15 0x000000000044f6f4 in libcamera::BoundMethodArgs<void, libcamera::Request*>::invokePack<0ul, void> (this=0x86ad50, pack=0x86ada0) at ../../include/libcamera/base/bound_method.h:115<br>
#16 0x000000000044f1a0 in libcamera::BoundMethodArgs<void, libcamera::Request*>::invokePack (this=0x86ad50, pack=0x86ada0) at ../../include/libcamera/base/bound_method.h:124<br>
#17 0x0000fffff7f60fa8 in libcamera::InvokeMessage::invoke (this=0x86adc0) at ../../src/libcamera/base/message.cpp:153<br>
#18 0x0000fffff7f489c8 in libcamera::Object::message (this=0xffffb80464a0, msg=0x86adc0) at ../../src/libcamera/base/object.cpp:211<br>
#19 0x0000fffff7f62394 in libcamera::Thread::dispatchMessages (this=0x6993a0, type=libcamera::Message::None) at ../../src/libcamera/base/thread.cpp:602<br>
#20 0x0000fffff7f51e08 in libcamera::EventDispatcherPoll::processEvents (this=0xffffb8074d40) at ../../src/libcamera/base/event_dispatcher_poll.cpp:146<br>
#21 0x0000fffff7f618f4 in libcamera::Thread::exec (this=0x6993a0) at ../../src/libcamera/base/thread.cpp:306<br>
#22 0x0000fffff7b0c5ec in libcamera::CameraManager::Private::run (this=0x699390) at ../../src/libcamera/camera_manager.cpp:87<br>
#23 0x0000fffff7f61894 in libcamera::Thread::startThread (this=0x6993a0) at ../../src/libcamera/base/thread.cpp:284<br>
#24 0x0000fffff7f65cf4 in std::__invoke_impl<void, void (libcamera::Thread::*)(), libcamera::Thread*> (__f=@0x707f90: (void (libcamera::Thread::*)(libcamera::Thread * const)) 0xfffff7f61794 <libcamera::Thread::startThread()>, __t=@0x707f88: 0x6993a0) at /usr/include/c++/14/bits/invoke.h:74<br>
#25 0x0000fffff7f65c34 in std::__invoke<void (libcamera::Thread::*)(), libcamera::Thread*> (__fn=@0x707f90: (void (libcamera::Thread::*)(libcamera::Thread * const)) 0xfffff7f61794 <libcamera::Thread::startThread()>) at /usr/include/c++/14/bits/invoke.h:96<br>
#26 0x0000fffff7f65b98 in std::thread::_Invoker<std::tuple<void (libcamera::Thread::*)(), libcamera::Thread*> >::_M_invoke<0ul, 1ul> (this=0x707f88) at /usr/include/c++/14/bits/std_thread.h:301<br>
#27 0x0000fffff7f65b50 in std::thread::_Invoker<std::tuple<void (libcamera::Thread::*)(), libcamera::Thread*> >::operator() (this=0x707f88) at /usr/include/c++/14/bits/std_thread.h:308<br>
#28 0x0000fffff7f65b30 in std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (libcamera::Thread::*)(), libcamera::Thread*> > >::_M_run (this=0x707f80) at /usr/include/c++/14/bits/std_thread.h:253<br>
#29 0x0000fffff58e0200 in execute_native_thread_routine () at /lib64/libstdc++.so.6<br>
#30 0x0000fffff56b6998 in start_thread () at /lib64/libc.so.6<br>
#31 0x0000fffff572268c in thread_start () at /lib64/libc.so.6<br>
<br>
Thread 8 (Thread 0xffffd960eec0 (LWP 31016) "qcam"):<br>
#0 0x0000fffff57200a4 in syscall () at /lib64/libc.so.6<br>
#1 0x0000fffff4c4c3d4 in g_cond_wait () at /lib64/libglib-2.0.so.0<br>
#2 0x0000fffff4be985c in g_async_queue_pop_intern_unlocked () at /lib64/libglib-2.0.so.0<br>
#3 0x0000fffff4be98ec in g_async_queue_pop () at /lib64/libglib-2.0.so.0<br>
#4 0x0000ffffe1ce3414 in fc_thread_func () at /lib64/libpangoft2-1.0.so.0<br>
#5 0x0000fffff4c54a20 in g_thread_proxy () at /lib64/libglib-2.0.so.0<br>
#6 0x0000fffff56b6998 in start_thread () at /lib64/libc.so.6<br>
#7 0x0000fffff572268c in thread_start () at /lib64/libc.so.6<br>
<br>
Thread 6 (Thread 0xffffdaa0eec0 (LWP 31014) "gdbus"):<br>
#0 0x0000fffff57170c4 in ppoll () at /lib64/libc.so.6<br>
#1 0x0000fffff4c84284 in g_main_context_iterate_unlocked.isra () at /lib64/libglib-2.0.so.0<br>
#2 0x0000fffff4c26fb0 in g_main_loop_run () at /lib64/libglib-2.0.so.0<br>
#3 0x0000ffffe1133f64 in gdbus_shared_thread_func.lto_priv () at /lib64/libgio-2.0.so.0<br>
#4 0x0000fffff4c54a20 in g_thread_proxy () at /lib64/libglib-2.0.so.0<br>
#5 0x0000fffff56b6998 in start_thread () at /lib64/libc.so.6<br>
#6 0x0000fffff572268c in thread_start () at /lib64/libc.so.6<br>
<br>
Thread 5 (Thread 0xffffdb40eec0 (LWP 31013) "gmain"):<br>
--Type <RET> for more, q to quit, c to continue without paging--<br>
#0 0x0000fffff57170c4 in ppoll () at /lib64/libc.so.6<br>
#1 0x0000fffff4c84284 in g_main_context_iterate_unlocked.isra () at /lib64/libglib-2.0.so.0<br>
#2 0x0000fffff4c22264 in g_main_context_iteration () at /lib64/libglib-2.0.so.0<br>
#3 0x0000fffff4c222cc in glib_worker_main () at /lib64/libglib-2.0.so.0<br>
#4 0x0000fffff4c54a20 in g_thread_proxy () at /lib64/libglib-2.0.so.0<br>
#5 0x0000fffff56b6998 in start_thread () at /lib64/libc.so.6<br>
#6 0x0000fffff572268c in thread_start () at /lib64/libc.so.6<br>
<br>
Thread 4 (Thread 0xffffdbe0eec0 (LWP 31012) "pool-spawner"):<br>
#0 0x0000fffff57200a4 in syscall () at /lib64/libc.so.6<br>
#1 0x0000fffff4c4c3d4 in g_cond_wait () at /lib64/libglib-2.0.so.0<br>
#2 0x0000fffff4be985c in g_async_queue_pop_intern_unlocked () at /lib64/libglib-2.0.so.0<br>
#3 0x0000fffff4c557dc in g_thread_pool_spawn_thread () at /lib64/libglib-2.0.so.0<br>
#4 0x0000fffff4c54a20 in g_thread_proxy () at /lib64/libglib-2.0.so.0<br>
#5 0x0000fffff56b6998 in start_thread () at /lib64/libc.so.6<br>
#6 0x0000fffff572268c in thread_start () at /lib64/libc.so.6<br>
<br>
Thread 3 (Thread 0xffffe260eec0 (LWP 31011) "QXcbEventQueue"):<br>
#0 0x0000fffff5716ad4 in poll () at /lib64/libc.so.6<br>
#1 0x0000fffff1821814 in _xcb_conn_wait.part.0 () at /lib64/libxcb.so.1<br>
#2 0x0000fffff18234d8 in xcb_wait_for_event () at /lib64/libxcb.so.1<br>
#3 0x0000ffffe3720d6c in QXcbEventQueue::run() () at /lib64/libQt6XcbQpa.so.6<br>
#4 0x0000fffff71384f4 in QThreadPrivate::start(void*) () at /lib64/libQt6Core.so.6<br>
#5 0x0000fffff56b6998 in start_thread () at /lib64/libc.so.6<br>
#6 0x0000fffff572268c in thread_start () at /lib64/libc.so.6<br>
<br>
Thread 2 (Thread 0xffffe300eec0 (LWP 31010) "QDBusConnection"):<br>
#0 0x0000fffff57170c4 in ppoll () at /lib64/libc.so.6<br>
#1 0x0000fffff4c84284 in g_main_context_iterate_unlocked.isra () at /lib64/libglib-2.0.so.0<br>
#2 0x0000fffff4c22264 in g_main_context_iteration () at /lib64/libglib-2.0.so.0<br>
#3 0x0000fffff7272658 in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () at /lib64/libQt6Core.so.6<br>
#4 0x0000fffff6f7f694 in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () at /lib64/libQt6Core.so.6<br>
#5 0x0000fffff70a4c4c in QThread::exec() () at /lib64/libQt6Core.so.6<br>
#6 0x0000fffff408d790 in QDBusConnectionManager::run() () at /lib64/libQt6DBus.so.6<br>
#7 0x0000fffff71384f4 in QThreadPrivate::start(void*) () at /lib64/libQt6Core.so.6<br>
#8 0x0000fffff56b6998 in start_thread () at /lib64/libc.so.6<br>
#9 0x0000fffff572268c in thread_start () at /lib64/libc.so.6<br>
<br>
Thread 1 (Thread 0xfffff7fb1020 (LWP 31007) "qcam"):<br>
#0 0x0000fffff5722668 in __clone3 () at /lib64/libc.so.6<br>
#1 0x0000fffff5722474 in __clone3_internal () at /lib64/libc.so.6<br>
#2 0x0000fffff5722508 in __clone_internal () at /lib64/libc.so.6<br>
#3 0x0000fffff56b6508 in create_thread () at /lib64/libc.so.6<br>
#4 0x0000fffff56b6fec in pthread_create@GLIBC_2.17 () at /lib64/libc.so.6<br>
#5 0x0000fffff7137d54 in QThread::start(QThread::Priority) () at /lib64/libQt6Core.so.6<br>
#6 0x0000fffff71414f0 in QThreadPoolPrivate::startThread(QRunnable*) () at /lib64/libQt6Core.so.6<br>
#7 0x0000fffff71428cc in QThreadPoolPrivate::tryStart(QRunnable*) () at /lib64/libQt6Core.so.6<br>
#8 0x0000fffff7142e08 in QThreadPool::start(QRunnable*, int) () at /lib64/libQt6Core.so.6<br>
#9 0x0000fffff6b2261c in blend_color_argb(int, QT_FT_Span_ const*, void*) () at /lib64/libQt6Gui.so.6<br>
#10 0x0000fffff66b3628 in bool drawLine<&(drawPixel(QCosmeticStroker*, int, int, int)), (anonymous namespace)::NoDasher>(QCosmeticStroker*, double, double, double, double, int) () at /lib64/libQt6Gui.so.6<br>
#11 0x0000fffff66b3ffc in QCosmeticStroker::drawLine(QPointF const&, QPointF const&) () at /lib64/libQt6Gui.so.6<br>
#12 0x0000fffff66ec12c in QRasterPaintEngine::drawLines(QLine const*, int) () at /lib64/libQt6Gui.so.6<br>
#13 0x0000fffff5e906f0 in QFusionStyle::drawControl(QStyle::ControlElement, QStyleOption const*, QPainter*, QWidget const*) const () at /lib64/libQt6Widgets.so.6<br>
#14 0x0000fffff5fb2198 in QToolBar::paintEvent(QPaintEvent*) () at /lib64/libQt6Widgets.so.6<br>
#15 0x0000fffff5dceecc in QWidget::event(QEvent*) () at /lib64/libQt6Widgets.so.6<br>
#16 0x0000fffff5d6ea18 in QApplicationPrivate::notify_helper(QObject*, QEvent*) () at /lib64/libQt6Widgets.so.6<br>
#17 0x0000fffff6f71d58 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () at /lib64/libQt6Core.so.6<br>
#18 0x0000fffff5dc598c in QWidgetPrivate::sendPaintEvent(QRegion const&) () at /lib64/libQt6Widgets.so.6<br>
--Type <RET> for more, q to quit, c to continue without paging--<br>
#19 0x0000fffff5dc5e48 in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, QFlags<QWidgetPrivate::DrawWidgetFlag>, QPainter*, QWidgetRepaintManager*) () at /lib64/libQt6Widgets.so.6<br>
#20 0x0000fffff5dc714c in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, QFlags<QWidgetPrivate::DrawWidgetFlag>, QPainter*, QWidgetRepaintManager*) () at /lib64/libQt6Widgets.so.6<br>
#21 0x0000fffff5dc7040 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, QFlags<QWidgetPrivate::DrawWidgetFlag>, QPainter*, QWidgetRepaintManager*) () at /lib64/libQt6Widgets.so.6<br>
#22 0x0000fffff5dc5ccc in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, QFlags<QWidgetPrivate::DrawWidgetFlag>, QPainter*, QWidgetRepaintManager*) () at /lib64/libQt6Widgets.so.6<br>
#23 0x0000fffff5ddb794 in QWidgetRepaintManager::paintAndFlush() () at /lib64/libQt6Widgets.so.6<br>
#24 0x0000fffff5dcf64c in QWidget::event(QEvent*) () at /lib64/libQt6Widgets.so.6<br>
#25 0x000000000043aff0 in MainWindow::event (this=0x7c3c20, e=0x7662e0) at ../../src/apps/qcam/main_window.cpp:177<br>
#26 0x0000fffff5d6ea18 in QApplicationPrivate::notify_helper(QObject*, QEvent*) () at /lib64/libQt6Widgets.so.6<br>
#27 0x0000fffff6f71d58 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () at /lib64/libQt6Core.so.6<br>
#28 0x0000fffff6f76068 in QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) () at /lib64/libQt6Core.so.6<br>
#29 0x0000fffff7272fd4 in postEventSourceDispatch(_GSource*, int (*)(void*), void*) () at /lib64/libQt6Core.so.6<br>
#30 0x0000fffff4c20b94 in g_main_context_dispatch_unlocked.lto_priv () at /lib64/libglib-2.0.so.0<br>
#31 0x0000fffff4c841d0 in g_main_context_iterate_unlocked.isra () at /lib64/libglib-2.0.so.0<br>
#32 0x0000fffff4c22264 in g_main_context_iteration () at /lib64/libglib-2.0.so.0<br>
#33 0x0000fffff7272658 in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () at /lib64/libQt6Core.so.6<br>
#34 0x0000fffff6f7f694 in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () at /lib64/libQt6Core.so.6<br>
#35 0x0000fffff6f7ac3c in QCoreApplication::exec() () at /lib64/libQt6Core.so.6<br>
#36 0x0000000000439ae4 in main (argc=1, argv=0xffffffffeed8) at ../../src/apps/qcam/main.cpp:87<br>
<br>
--<br>
Kieran<br>
<br>
<br>
<br>
> ```<br>
> [295:43:43.910237144] [1441841] INFO IPAManager ipa_manager.cpp:137 libcamera is not installed. Adding '/usr/local/google/home/chenghaoyang/Workspace/libca<br>
> mera/build/src/ipa' to the IPA search path<br>
> [295:43:43.914030564] [1441841] INFO Camera camera_manager.cpp:325 libcamera v0.3.1+79-8ca4f033-dirty (2024-08-29T19:18:51UTC)<br>
> [295:43:43.915493754] [1441844] ERROR DmaBufAllocator dma_buf_allocator.cpp:120 Could not open any dma-buf provider<br>
> [295:43:43.919118835] [1441841] INFO IPAManager ipa_manager.cpp:137 libcamera is not installed. Adding '/usr/local/google/home/chenghaoyang/Workspace/libca<br>
> mera/build/src/ipa' to the IPA search path<br>
> [295:43:43.922245825] [1441841] INFO Camera camera_manager.cpp:325 libcamera v0.3.1+79-8ca4f033-dirty (2024-08-29T19:18:51UTC)<br>
> [295:43:43.922820175] [1441846] ERROR DmaBufAllocator dma_buf_allocator.cpp:120 Could not open any dma-buf provider<br>
> Unable to set the pipeline to the playing state.<br>
> ```<br>
> <br>
> Gitlab pipeline failure:<br>
> <a href="https://gitlab.freedesktop.org/chenghaoyang/libcamera/-/pipelines/1260412" rel="noreferrer" target="_blank">https://gitlab.freedesktop.org/chenghaoyang/libcamera/-/pipelines/1260412</a><br>
> <br>
> Update in v11:<br>
> - Allowed a single value in the config file's frame_rates field.<br>
> <br>
> Update in v10:<br>
> Apply fixes according to Jacopo's and Barnabás' comments.<br>
> - Split test_pattern and path fields in the yaml format.<br>
> - Let FrameGenerators control frameCount_.<br>
> - Fixed match() returning values.<br>
> <br>
> Update in v9: Allocate contiguous memory for planes in the same<br>
> FrameBuffer.<br>
> <br>
> BR,<br>
> Harvey<br>
> <br>
> Harvey Yang (4):<br>
> libcamera: add DmaBufAllocator::exportBuffers()<br>
> libcamera: Remove PipelineHandler Fatal check of non-empty<br>
> MediaDevices<br>
> libcamera: virtual: Add VirtualPipelineHandler<br>
> libcamera: software_isp: Refactor SoftwareIsp to use<br>
> DmaBufAllocator::exportBuffers<br>
> <br>
> Konami Shu (3):<br>
> libcamera: pipeline: Add test pattern for VirtualPipelineHandler<br>
> libcamera: virtual: Read config and register cameras based on the<br>
> config<br>
> libcamera: virtual: Add ImageFrameGenerator<br>
> <br>
> .../libcamera/internal/dma_buf_allocator.h | 13 +<br>
> meson.build | 1 +<br>
> meson_options.txt | 3 +-<br>
> src/android/meson.build | 19 --<br>
> src/libcamera/dma_buf_allocator.cpp | 55 +++<br>
> src/libcamera/pipeline/virtual/README.md | 50 +++<br>
> .../pipeline/virtual/data/virtual.yaml | 36 ++<br>
> .../pipeline/virtual/frame_generator.h | 28 ++<br>
> .../virtual/image_frame_generator.cpp | 178 ++++++++++<br>
> .../pipeline/virtual/image_frame_generator.h | 54 +++<br>
> src/libcamera/pipeline/virtual/meson.build | 13 +<br>
> src/libcamera/pipeline/virtual/parser.cpp | 257 ++++++++++++++<br>
> src/libcamera/pipeline/virtual/parser.h | 44 +++<br>
> .../virtual/test_pattern_generator.cpp | 140 ++++++++<br>
> .../pipeline/virtual/test_pattern_generator.h | 57 ++++<br>
> src/libcamera/pipeline/virtual/utils.h | 17 +<br>
> src/libcamera/pipeline/virtual/virtual.cpp | 323 ++++++++++++++++++<br>
> src/libcamera/pipeline/virtual/virtual.h | 107 ++++++<br>
> src/libcamera/pipeline_handler.cpp | 11 +-<br>
> src/libcamera/software_isp/software_isp.cpp | 20 +-<br>
> src/meson.build | 19 ++<br>
> 21 files changed, 1403 insertions(+), 42 deletions(-)<br>
> create mode 100644 src/libcamera/pipeline/virtual/README.md<br>
> create mode 100644 src/libcamera/pipeline/virtual/data/virtual.yaml<br>
> create mode 100644 src/libcamera/pipeline/virtual/frame_generator.h<br>
> create mode 100644 src/libcamera/pipeline/virtual/image_frame_generator.cpp<br>
> create mode 100644 src/libcamera/pipeline/virtual/image_frame_generator.h<br>
> create mode 100644 src/libcamera/pipeline/virtual/meson.build<br>
> create mode 100644 src/libcamera/pipeline/virtual/parser.cpp<br>
> create mode 100644 src/libcamera/pipeline/virtual/parser.h<br>
> create mode 100644 src/libcamera/pipeline/virtual/test_pattern_generator.cpp<br>
> create mode 100644 src/libcamera/pipeline/virtual/test_pattern_generator.h<br>
> create mode 100644 src/libcamera/pipeline/virtual/utils.h<br>
> create mode 100644 src/libcamera/pipeline/virtual/virtual.cpp<br>
> create mode 100644 src/libcamera/pipeline/virtual/virtual.h<br>
> <br>
> -- <br>
> 2.46.0.469.g59c65b2a67-goog<br>
><br>
</blockquote></div>