mirror of
https://git.libcamera.org/libcamera/libcamera.git
synced 2025-07-23 16:45:07 +03:00
qcam: Allow for a second raw stream to be configured
Allow a second stream to be configured for raw capture. This change only adds support for configuring and allocating buffers for the second stream. Later changes are needed to queue the allocated buffers to the camera when the user wishes to capture a raw frame. Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
This commit is contained in:
parent
5888918153
commit
55d5e3e59f
2 changed files with 79 additions and 39 deletions
|
@ -281,17 +281,30 @@ void MainWindow::toggleCapture(bool start)
|
||||||
int MainWindow::startCapture()
|
int MainWindow::startCapture()
|
||||||
{
|
{
|
||||||
StreamRoles roles = StreamKeyValueParser::roles(options_[OptStream]);
|
StreamRoles roles = StreamKeyValueParser::roles(options_[OptStream]);
|
||||||
|
std::vector<Request *> requests;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Verify roles are supported. */
|
/* Verify roles are supported. */
|
||||||
if (roles.size() != 1) {
|
switch (roles.size()) {
|
||||||
qCritical() << "Only one stream supported";
|
case 1:
|
||||||
return -EINVAL;
|
if (roles[0] != StreamRole::Viewfinder) {
|
||||||
}
|
qWarning() << "Only viewfinder supported for single stream";
|
||||||
|
return -EINVAL;
|
||||||
if (roles[0] != StreamRole::Viewfinder) {
|
}
|
||||||
qCritical() << "Only viewfinder supported";
|
break;
|
||||||
return -EINVAL;
|
case 2:
|
||||||
|
if (roles[0] != StreamRole::Viewfinder ||
|
||||||
|
roles[1] != StreamRole::StillCaptureRaw) {
|
||||||
|
qWarning() << "Only viewfinder + raw supported for dual streams";
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (roles.size() != 1) {
|
||||||
|
qWarning() << "Unsuported stream configuration";
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Configure the camera. */
|
/* Configure the camera. */
|
||||||
|
@ -301,17 +314,17 @@ int MainWindow::startCapture()
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamConfiguration &cfg = config_->at(0);
|
StreamConfiguration &vfConfig = config_->at(0);
|
||||||
|
|
||||||
/* Use a format supported by the viewfinder if available. */
|
/* Use a format supported by the viewfinder if available. */
|
||||||
std::vector<PixelFormat> formats = cfg.formats().pixelformats();
|
std::vector<PixelFormat> formats = vfConfig.formats().pixelformats();
|
||||||
for (const PixelFormat &format : viewfinder_->nativeFormats()) {
|
for (const PixelFormat &format : viewfinder_->nativeFormats()) {
|
||||||
auto match = std::find_if(formats.begin(), formats.end(),
|
auto match = std::find_if(formats.begin(), formats.end(),
|
||||||
[&](const PixelFormat &f) {
|
[&](const PixelFormat &f) {
|
||||||
return f == format;
|
return f == format;
|
||||||
});
|
});
|
||||||
if (match != formats.end()) {
|
if (match != formats.end()) {
|
||||||
cfg.pixelFormat = format;
|
vfConfig.pixelFormat = format;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -331,7 +344,7 @@ int MainWindow::startCapture()
|
||||||
|
|
||||||
if (validation == CameraConfiguration::Adjusted)
|
if (validation == CameraConfiguration::Adjusted)
|
||||||
qInfo() << "Stream configuration adjusted to "
|
qInfo() << "Stream configuration adjusted to "
|
||||||
<< cfg.toString().c_str();
|
<< vfConfig.toString().c_str();
|
||||||
|
|
||||||
ret = camera_->configure(config_.get());
|
ret = camera_->configure(config_.get());
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -339,10 +352,16 @@ int MainWindow::startCapture()
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Store stream allocation. */
|
||||||
|
vfStream_ = config_->at(0).stream();
|
||||||
|
if (config_->size() == 2)
|
||||||
|
rawStream_ = config_->at(1).stream();
|
||||||
|
else
|
||||||
|
rawStream_ = nullptr;
|
||||||
|
|
||||||
/* Configure the viewfinder. */
|
/* Configure the viewfinder. */
|
||||||
Stream *stream = cfg.stream();
|
ret = viewfinder_->setFormat(vfConfig.pixelFormat,
|
||||||
ret = viewfinder_->setFormat(cfg.pixelFormat,
|
QSize(vfConfig.size.width, vfConfig.size.height));
|
||||||
QSize(cfg.size.width, cfg.size.height));
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
qInfo() << "Failed to set viewfinder format";
|
qInfo() << "Failed to set viewfinder format";
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -350,16 +369,33 @@ int MainWindow::startCapture()
|
||||||
|
|
||||||
adjustSize();
|
adjustSize();
|
||||||
|
|
||||||
/* Allocate buffers and requests. */
|
/* Allocate and map buffers. */
|
||||||
allocator_ = new FrameBufferAllocator(camera_);
|
allocator_ = new FrameBufferAllocator(camera_);
|
||||||
ret = allocator_->allocate(stream);
|
for (StreamConfiguration &config : *config_) {
|
||||||
if (ret < 0) {
|
Stream *stream = config.stream();
|
||||||
qWarning() << "Failed to allocate capture buffers";
|
|
||||||
return ret;
|
ret = allocator_->allocate(stream);
|
||||||
|
if (ret < 0) {
|
||||||
|
qWarning() << "Failed to allocate capture buffers";
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const std::unique_ptr<FrameBuffer> &buffer : allocator_->buffers(stream)) {
|
||||||
|
/* Map memory buffers and cache the mappings. */
|
||||||
|
const FrameBuffer::Plane &plane = buffer->planes().front();
|
||||||
|
void *memory = mmap(NULL, plane.length, PROT_READ, MAP_SHARED,
|
||||||
|
plane.fd.fd(), 0);
|
||||||
|
mappedBuffers_[buffer.get()] = { memory, plane.length };
|
||||||
|
|
||||||
|
/* Store buffers on the free list. */
|
||||||
|
freeBuffers_[stream].enqueue(buffer.get());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Request *> requests;
|
/* Create requests and fill them with buffers from the viewfinder. */
|
||||||
for (const std::unique_ptr<FrameBuffer> &buffer : allocator_->buffers(stream)) {
|
while (!freeBuffers_[vfStream_].isEmpty()) {
|
||||||
|
FrameBuffer *buffer = freeBuffers_[vfStream_].dequeue();
|
||||||
|
|
||||||
Request *request = camera_->createRequest();
|
Request *request = camera_->createRequest();
|
||||||
if (!request) {
|
if (!request) {
|
||||||
qWarning() << "Can't create request";
|
qWarning() << "Can't create request";
|
||||||
|
@ -367,19 +403,13 @@ int MainWindow::startCapture()
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = request->addBuffer(stream, buffer.get());
|
ret = request->addBuffer(vfStream_, buffer);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
qWarning() << "Can't set buffer for request";
|
qWarning() << "Can't set buffer for request";
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
requests.push_back(request);
|
requests.push_back(request);
|
||||||
|
|
||||||
/* Map memory buffers and cache the mappings. */
|
|
||||||
const FrameBuffer::Plane &plane = buffer->planes().front();
|
|
||||||
void *memory = mmap(NULL, plane.length, PROT_READ, MAP_SHARED,
|
|
||||||
plane.fd.fd(), 0);
|
|
||||||
mappedBuffers_[buffer.get()] = { memory, plane.length };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Start the title timer and the camera. */
|
/* Start the title timer and the camera. */
|
||||||
|
@ -424,6 +454,8 @@ error:
|
||||||
}
|
}
|
||||||
mappedBuffers_.clear();
|
mappedBuffers_.clear();
|
||||||
|
|
||||||
|
freeBuffers_.clear();
|
||||||
|
|
||||||
delete allocator_;
|
delete allocator_;
|
||||||
allocator_ = nullptr;
|
allocator_ = nullptr;
|
||||||
|
|
||||||
|
@ -466,6 +498,7 @@ void MainWindow::stopCapture()
|
||||||
* but not processed yet. Clear the queue of done buffers to avoid
|
* but not processed yet. Clear the queue of done buffers to avoid
|
||||||
* racing with the event handler.
|
* racing with the event handler.
|
||||||
*/
|
*/
|
||||||
|
freeBuffers_.clear();
|
||||||
doneQueue_.clear();
|
doneQueue_.clear();
|
||||||
|
|
||||||
titleTimer_.stop();
|
titleTimer_.stop();
|
||||||
|
@ -505,12 +538,9 @@ void MainWindow::requestComplete(Request *request)
|
||||||
* are not allowed. Add the buffer to the done queue and post a
|
* are not allowed. Add the buffer to the done queue and post a
|
||||||
* CaptureEvent for the application thread to handle.
|
* CaptureEvent for the application thread to handle.
|
||||||
*/
|
*/
|
||||||
const std::map<Stream *, FrameBuffer *> &buffers = request->buffers();
|
|
||||||
FrameBuffer *buffer = buffers.begin()->second;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&mutex_);
|
QMutexLocker locker(&mutex_);
|
||||||
doneQueue_.enqueue(buffer);
|
doneQueue_.enqueue(request->buffers());
|
||||||
}
|
}
|
||||||
|
|
||||||
QCoreApplication::postEvent(this, new CaptureEvent);
|
QCoreApplication::postEvent(this, new CaptureEvent);
|
||||||
|
@ -523,16 +553,23 @@ void MainWindow::processCapture()
|
||||||
* if stopCapture() has been called while a CaptureEvent was posted but
|
* if stopCapture() has been called while a CaptureEvent was posted but
|
||||||
* not processed yet. Return immediately in that case.
|
* not processed yet. Return immediately in that case.
|
||||||
*/
|
*/
|
||||||
FrameBuffer *buffer;
|
std::map<Stream *, FrameBuffer *> buffers;
|
||||||
|
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&mutex_);
|
QMutexLocker locker(&mutex_);
|
||||||
if (doneQueue_.isEmpty())
|
if (doneQueue_.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
buffer = doneQueue_.dequeue();
|
buffers = doneQueue_.dequeue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Process buffers. */
|
||||||
|
if (buffers.count(vfStream_))
|
||||||
|
processViewfinder(buffers[vfStream_]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::processViewfinder(FrameBuffer *buffer)
|
||||||
|
{
|
||||||
framesCaptured_++;
|
framesCaptured_++;
|
||||||
|
|
||||||
const FrameMetadata &metadata = buffer->metadata();
|
const FrameMetadata &metadata = buffer->metadata();
|
||||||
|
@ -559,8 +596,7 @@ void MainWindow::queueRequest(FrameBuffer *buffer)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream *stream = config_->at(0).stream();
|
request->addBuffer(vfStream_, buffer);
|
||||||
request->addBuffer(stream, buffer);
|
|
||||||
|
|
||||||
camera_->queueRequest(request);
|
camera_->queueRequest(request);
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,7 @@ private:
|
||||||
|
|
||||||
void requestComplete(Request *request);
|
void requestComplete(Request *request);
|
||||||
void processCapture();
|
void processCapture();
|
||||||
|
void processViewfinder(FrameBuffer *buffer);
|
||||||
|
|
||||||
/* UI elements */
|
/* UI elements */
|
||||||
QToolBar *toolbar_;
|
QToolBar *toolbar_;
|
||||||
|
@ -95,8 +96,11 @@ private:
|
||||||
|
|
||||||
/* Capture state, buffers queue and statistics */
|
/* Capture state, buffers queue and statistics */
|
||||||
bool isCapturing_;
|
bool isCapturing_;
|
||||||
QQueue<FrameBuffer *> doneQueue_;
|
Stream *vfStream_;
|
||||||
QMutex mutex_; /* Protects doneQueue_ */
|
Stream *rawStream_;
|
||||||
|
std::map<Stream *, QQueue<FrameBuffer *>> freeBuffers_;
|
||||||
|
QQueue<std::map<Stream *, FrameBuffer *>> doneQueue_;
|
||||||
|
QMutex mutex_; /* Protects freeBuffers_ and doneQueue_ */
|
||||||
|
|
||||||
uint64_t lastBufferTime_;
|
uint64_t lastBufferTime_;
|
||||||
QElapsedTimer frameRateInterval_;
|
QElapsedTimer frameRateInterval_;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue