1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2019-2021, Google Inc. * * camera_request.h - libcamera Android Camera Request Descriptor */ #pragma once #include <map> #include <memory> #include <vector> #include <libcamera/base/class.h> #include <libcamera/base/mutex.h> #include <libcamera/base/unique_fd.h> #include <libcamera/camera.h> #include <libcamera/framebuffer.h> #include <hardware/camera3.h> #include "camera_metadata.h" #include "hal_framebuffer.h" class CameraBuffer; class CameraStream; class Camera3RequestDescriptor { public: enum class Status { Success, Error, }; struct StreamBuffer { StreamBuffer(CameraStream *stream, const camera3_stream_buffer_t &buffer, Camera3RequestDescriptor *request); ~StreamBuffer(); StreamBuffer(StreamBuffer &&); StreamBuffer &operator=(StreamBuffer &&); CameraStream *stream; buffer_handle_t *camera3Buffer; std::unique_ptr<HALFrameBuffer> frameBuffer; libcamera::UniqueFD fence; Status status = Status::Success; libcamera::FrameBuffer *internalBuffer = nullptr; const libcamera::FrameBuffer *srcBuffer = nullptr; std::unique_ptr<CameraBuffer> dstBuffer; Camera3RequestDescriptor *request; private: LIBCAMERA_DISABLE_COPY(StreamBuffer) }; /* Keeps track of streams requiring post-processing. */ std::map<CameraStream *, StreamBuffer *> pendingStreamsToProcess_ LIBCAMERA_TSA_GUARDED_BY(streamsProcessMutex_); libcamera::Mutex streamsProcessMutex_; Camera3RequestDescriptor(libcamera::Camera *camera, const camera3_capture_request_t *camera3Request); ~Camera3RequestDescriptor(); bool isPending() const { return !complete_; } uint32_t frameNumber_ = 0; std::vector<StreamBuffer> buffers_; CameraMetadata settings_; /* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2021, Google Inc. * * Test: * - Multiple reconfigurations of the Camera without stopping the CameraManager * - Validate there are no file descriptor leaks when using IPC */ #include <dirent.h> #include <fstream> #include <iostream> #include <libcamera/base/event_dispatcher.h> #include <libcamera/base/file.h> #include <libcamera/base/thread.h> #include <libcamera/base/timer.h> #include <libcamera/framebuffer_allocator.h> #include "camera_test.h" #include "test.h" using namespace libcamera; using namespace std; using namespace std::chrono_literals; namespace { class CameraReconfigure : public CameraTest, public Test { public: /* Initialize CameraTest with isolated IPA */ CameraReconfigure() : CameraTest(kCamId_, true) { } private: static constexpr const char *kCamId_ = "platform/vimc.0 Sensor B"; static constexpr const char *kIpaProxyName_ = "vimc_ipa_proxy"; static constexpr unsigned int kNumOfReconfigures_ = 10; void requestComplete(Request *request) { if (request->status() != Request::RequestComplete) return; const Request::BufferMap &buffers = request->buffers(); const Stream *stream = buffers.begin()->first; FrameBuffer *buffer = buffers.begin()->second; /* Reuse the request and re-queue it with the same buffers. */ request->reuse(); request->addBuffer(stream, buffer); camera_->queueRequest(request); } int startAndStop() { StreamConfiguration &cfg = config_->at(0); if (camera_->acquire()) { cerr << "Failed to acquire the camera" << endl; return TestFail; } if (camera_->configure(config_.get())) { cerr << "Failed to set default configuration" << endl; return TestFail; } Stream *stream = cfg.stream(); /* * The configuration is consistent so we can re-use the * same buffer allocation for each run. */ if (!allocated_) { int ret = allocator_->allocate(stream); if (ret < 0) { cerr << "Failed to allocate buffers" << endl; return TestFail; } allocated_ = true; } for (const unique_ptr<FrameBuffer> &buffer : allocator_->buffers(stream)) { unique_ptr<Request> request = camera_->createRequest(); if (!request) { cerr << "Failed to create request" << endl; return TestFail; } if (request->addBuffer(stream, buffer.get())) { cerr << "Failed to associate buffer with request" << endl; return TestFail; } requests_.push_back(move(request)); } camera_->requestCompleted.connect(this, &CameraReconfigure::requestComplete); if (camera_->start()) { cerr << "Failed to start camera" << endl; return TestFail; } for (unique_ptr<Request> &request : requests_) { if (camera_->queueRequest(request.get())) { cerr << "Failed to queue request" << endl; return TestFail; } } EventDispatcher *dispatcher = Thread::current()->eventDispatcher(); Timer timer; timer.start(100ms); while (timer.isRunning()) dispatcher->processEvents(); if (camera_->stop()) { cerr << "Failed to stop camera" << endl; return TestFail; } if (camera_->release()) { cerr << "Failed to release camera" << endl; return TestFail; } camera_->requestCompleted.disconnect(this); requests_.clear(); return 0; } int fdsOpen(pid_t pid) { string proxyFdPath = "/proc/" + to_string(pid) + "/fd"; DIR *dir; struct dirent *ptr; unsigned int openFds = 0; dir = opendir(proxyFdPath.c_str()); if (dir == nullptr) { int err = errno; cerr << "Error opening " << proxyFdPath << ": " << strerror(-err) << endl; return 0; } while ((ptr = readdir(dir)) != nullptr) { if ((strcmp(ptr->d_name, ".") == 0) || (strcmp(ptr->d_name, "..") == 0)) continue; openFds++; } closedir(dir); return openFds; } pid_t findProxyPid() { string proxyPid; string proxyName(kIpaProxyName_); DIR *dir; struct dirent *ptr; dir = opendir("/proc"); while ((ptr = readdir(dir)) != nullptr) { if (ptr->d_type != DT_DIR) continue; string pname("/proc/" + string(ptr->d_name) + "/comm"); if (File::exists(pname.c_str())) { ifstream pfile(pname.c_str()); string comm; getline(pfile, comm); pfile.close(); proxyPid = comm == proxyName ? string(ptr->d_name) : ""; } if (!proxyPid.empty()) break; } closedir(dir); if (!proxyPid.empty()) return atoi(proxyPid.c_str()); return -1; } int init() override { if (status_ != TestPass) return status_; config_ = camera_->generateConfiguration({ StreamRole::StillCapture }); if (!config_ || config_->size() != 1) { cerr << "Failed to generate default configuration" << endl; return TestFail; } allocator_ = make_unique<FrameBufferAllocator>(camera_); allocated_ = false; return TestPass; } int run() override { unsigned int openFdsAtStart = 0; unsigned int openFds = 0; pid_t proxyPid = findProxyPid(); if (proxyPid < 0) { cerr << "Cannot find " << kIpaProxyName_ << " pid, exiting" << endl; return TestFail; } openFdsAtStart = fdsOpen(proxyPid); for (unsigned int i = 0; i < kNumOfReconfigures_; i++) { startAndStop(); openFds = fdsOpen(proxyPid); if (openFds == 0) { cerr << "No open fds found whereas " << "open fds at start: " << openFdsAtStart << endl; return TestFail; } if (openFds != openFdsAtStart) { cerr << "Leaking fds for " << kIpaProxyName_ << " - Open fds: " << openFds << " vs " << "Open fds at start: " << openFdsAtStart << endl; return TestFail; } } return TestPass; } bool allocated_; vector<unique_ptr<Request>> requests_; unique_ptr<CameraConfiguration> config_; unique_ptr<FrameBufferAllocator> allocator_; }; } /* namespace */ TEST_REGISTER(CameraReconfigure)