raspberrypi / libcamera

Other
220 stars 94 forks source link

API example yields segmentation fault #181

Open ldearquer opened 2 days ago

ldearquer commented 2 days ago

libcamera 0.3.1

When making a simple program following the API tutorial at Documentation/guides/application-developer.rst, I keep getting segmentation fault. Both on x86 with UVC camera, and on raspberry pi with HQ camera. rpicam apps work ok though.

The code I end up with is this:

#include <iomanip>
#include <iostream>
#include <memory>
#include <thread>

#include <libcamera/libcamera.h>

using namespace libcamera;
using namespace std::chrono_literals;

static std::shared_ptr<Camera> camera;

static void requestComplete(Request *request)
{
    if (request->status() == Request::RequestCancelled)
        return;

    const std::map<const Stream *, FrameBuffer *> &buffers = request->buffers();
    for (auto bufferPair : buffers)
    {
        FrameBuffer *buffer = bufferPair.second;
        const FrameMetadata &metadata = buffer->metadata();

        std::cout << " seq: " << std::setw(6) << std::setfill('0') << metadata.sequence << " bytesused: ";

        unsigned int nplane = 0;
        for (const FrameMetadata::Plane &plane : metadata.planes())
        {
            std::cout << plane.bytesused;
            if (++nplane < metadata.planes().size()) std::cout << "/";
        }

        std::cout << std::endl;
    }

    request->reuse(Request::ReuseBuffers);
    camera->queueRequest(request);
}

int main()
{
    std::unique_ptr<CameraManager> cm = std::make_unique<CameraManager>();
    cm->start();

    for (auto const &camera : cm->cameras())
    {
        std::cout << camera->id() << std::endl;
    }

    auto cameras = cm->cameras();
    if (cameras.empty()) {
        std::cout << "No cameras were identified on the system."
                  << std::endl;
        cm->stop();
        return EXIT_FAILURE;
    }

    std::string cameraId = cameras[0]->id();

    auto camera = cm->get(cameraId);
    /*
     * Note that `camera` may not compare equal to `cameras[0]`.
     * In fact, it might simply be a `nullptr`, as the particular
     * device might have disappeared (and reappeared) in the meantime.
     */

    camera->acquire();

    std::unique_ptr<CameraConfiguration> config =
           camera->generateConfiguration( { StreamRole::Viewfinder } );

    StreamConfiguration &streamConfig = config->at(0);
    std::cout << "Default viewfinder configuration is: " << streamConfig.toString() << std::endl;
    streamConfig.size.width = 800;
    streamConfig.size.height = 600;
    config->validate();
    std::cout << "Validated viewfinder configuration is: " << streamConfig.toString() << std::endl;
    camera->configure(config.get());

    FrameBufferAllocator *allocator = new FrameBufferAllocator(camera);

    for (StreamConfiguration &cfg : *config) {
        int ret = allocator->allocate(cfg.stream());
        if (ret < 0) {
            std::cerr << "Can't allocate buffers" << std::endl;
            return -ENOMEM;
        }

        size_t allocated = allocator->buffers(cfg.stream()).size();
        std::cout << "Allocated " << allocated << " buffers for stream" << std::endl;
    }

    Stream *stream = streamConfig.stream();
    const std::vector<std::unique_ptr<FrameBuffer>> &buffers = allocator->buffers(stream);
    std::vector<std::unique_ptr<Request>> requests;

    for (unsigned int i = 0; i < buffers.size(); ++i)
    {
        std::unique_ptr<Request> request = camera->createRequest();
        if (!request)
        {
            std::cerr << "Can't create request" << std::endl;
            return -ENOMEM;
        }

        const std::unique_ptr<FrameBuffer> &buffer = buffers[i];
        int ret = request->addBuffer(stream, buffer.get());
        if (ret < 0)
        {
            std::cerr << "Can't set buffer for request"
                  << std::endl;
            return ret;
        }

        requests.push_back(std::move(request));
    }

    camera->requestCompleted.connect(requestComplete);
    camera->start();
    for (std::unique_ptr<Request> &request : requests)
    {
        camera->queueRequest(request.get());
    }

    std::this_thread::sleep_for(3000ms);

    return 0;
}

After one frame, I get segmentation fault. Is there anything obvious I got wrong here?

Thanks

naushir commented 1 day ago

Are you able to run this under gdb and inspect the callstack when you get the segfault?

ldearquer commented 3 hours ago

Sure, see below:

(gdb) run
Starting program: /home/user/test/simple 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/aarch64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ff6aad5c0 (LWP 2026)]
[New Thread 0x7ff629d5c0 (LWP 2027)]
[0:01:47.246116319] [2024]  INFO Camera camera_manager.cpp:316 libcamera v0.3.1+50-69a894c4
[New Thread 0x7ff5a8d5c0 (LWP 2028)]
[0:01:47.305503296] [2028]  WARN RPiSdn sdn.cpp:40 Using legacy SDN tuning - please consider moving SDN inside rpi.denoise
[New Thread 0x7ff51ce5c0 (LWP 2029)]
[New Thread 0x7ff49be5c0 (LWP 2030)]
[0:01:47.309044710] [2028]  INFO RPI vc4.cpp:447 Registered camera /base/soc/i2c0mux/i2c@1/imx477@1a to Unicam device /dev/media3 and ISP device /dev/media1
/base/soc/i2c0mux/i2c@1/imx477@1a
Default viewfinder configuration is: 800x600-XRGB8888
Validated viewfinder configuration is: 800x600-XRGB8888
[0:01:47.309721840] [2024]  INFO Camera camera.cpp:1191 configuring streams: (0) 800x600-XRGB8888
[0:01:47.310111203] [2028]  INFO RPI vc4.cpp:622 Sensor: /base/soc/i2c0mux/i2c@1/imx477@1a - Selected sensor format: 2028x1520-SBGGR12_1X12 - Selected unicam format: 2028x1520-pBCC
Allocated 4 buffers for stream
[New Thread 0x7fefffe5c0 (LWP 2031)]
 seq: 000007 bytesused: 1920000

Thread 4 "simple" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ff5a8d5c0 (LWP 2028)]
0x0000007ff7e6860c in libcamera::Camera::queueRequest(libcamera::Request*) ()
   from /lib/aarch64-linux-gnu/libcamera.so.0.3
(gdb) backtrace
#0  0x0000007ff7e6860c in libcamera::Camera::queueRequest(libcamera::Request*) ()
   from /lib/aarch64-linux-gnu/libcamera.so.0.3
#1  0x0000005555552554 in requestComplete (request=0x5555585cd0) at simple.cpp:37
#2  0x0000005555555e84 in libcamera::BoundMethodStatic<void, libcamera::Request*>::activate (
    this=0x55555dbcf0, args#0=0x5555585cd0, deleteMethod=false)
    at /usr/include/libcamera/libcamera/base/bound_method.h:212
#3  0x0000007ff7e69b88 in libcamera::Camera::requestComplete(libcamera::Request*) ()
   from /lib/aarch64-linux-gnu/libcamera.so.0.3
#4  0x0000007ff7e9a328 in libcamera::PipelineHandler::completeRequest(libcamera::Request*) ()
   from /lib/aarch64-linux-gnu/libcamera.so.0.3
#5  0x0000007ff7eeddc0 in libcamera::RPi::CameraData::checkRequestCompleted() ()
   from /lib/aarch64-linux-gnu/libcamera.so.0.3
#6  0x0000007ff7eedeec in libcamera::RPi::CameraData::handleState() ()
   from /lib/aarch64-linux-gnu/libcamera.so.0.3
#7  0x0000007ff7e52ab4 in libcamera::ipa::RPi::IPAProxyRPi::processStatsCompleteThread(libcamera::ipa::RPi::BufferIds const&) () from /lib/aarch64-linux-gnu/libcamera.so.0.3
#8  0x0000007ff7d5fa10 in libcamera::Object::message(libcamera::Message*) ()
   from /lib/aarch64-linux-gnu/libcamera-base.so.0.3
#9  0x0000007ff7d6bd48 in libcamera::Thread::dispatchMessages(libcamera::Message::Type) ()
   from /lib/aarch64-linux-gnu/libcamera-base.so.0.3
#10 0x0000007ff7d63ce8 in libcamera::EventDispatcherPoll::processEvents() ()
   from /lib/aarch64-linux-gnu/libcamera-base.so.0.3
#11 0x0000007ff7d6b6e8 in libcamera::Thread::exec() ()
   from /lib/aarch64-linux-gnu/libcamera-base.so.0.3
#12 0x0000007ff7e6d490 in libcamera::CameraManager::Private::run() ()
   from /lib/aarch64-linux-gnu/libcamera.so.0.3
#13 0x0000007ff7bfe9dc in ?? () from /lib/aarch64-linux-gnu/libstdc++.so.6
#14 0x0000007ff79bee30 in start_thread (arg=0x7fffffe957) at ./nptl/pthread_create.c:442
#15 0x0000007ff7a27adc in thread_start () at ../sysdeps/unix/sysv/linux/aarch64/clone.S:79
(gdb) 

Is there an additional requirement with queueRequest() I am not doing? Is it OK to call it from requestComplete callback? Looking at the source of queueRequest(), it should be thread safe...