From patchwork Fri Mar 14 20:29:32 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 22964 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id BD2C9C32F7 for ; Fri, 14 Mar 2025 20:30:17 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 4D52768956; Fri, 14 Mar 2025 21:30:17 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="QlWpuQ4T"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 112576894E for ; Fri, 14 Mar 2025 21:30:10 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1741984210; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=mTa4WA1lPtOXS3wfmXBW4JQBLLvsUIU3xGZw/Ji+tP0=; b=QlWpuQ4T/gZ7EJD5pWLuP4csjDe718A3Sd9qikJEWwb+oTwNAY6H+tUe8YhpSbAxZME3ro YaYWUIdpgT9TohJDHs2HBWp+oEhapPIERPrhqcZLryK2TewMgCknzcKmrAqWLMZ4tzqQOP 7FKYYpCLwiu4yZ0CZgDzbEkyLoIKeyg= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-142-a5RqhAKANmipHIMP1M6xxw-1; Fri, 14 Mar 2025 16:30:06 -0400 X-MC-Unique: a5RqhAKANmipHIMP1M6xxw-1 X-Mimecast-MFC-AGG-ID: a5RqhAKANmipHIMP1M6xxw_1741984205 Received: from mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.40]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 885C819560AF; Fri, 14 Mar 2025 20:30:05 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.32.19]) by mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 02C001954B32; Fri, 14 Mar 2025 20:30:03 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Laurent Pinchart Subject: [RFC PATCH 4/7] apps: cam: Add support for multiple sinks Date: Fri, 14 Mar 2025 21:29:32 +0100 Message-ID: <20250314202943.112109-5-mzamazal@redhat.com> In-Reply-To: <20250314202943.112109-1-mzamazal@redhat.com> References: <20250314202943.112109-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.40 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: 1nUbPO54ro-I-hizt4eCRgpWSZ63TXjSRk-irIx56SI_1741984205 X-Mimecast-Originator: redhat.com content-type: text/plain; charset="US-ASCII"; x-default=true X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" It would be useful if cam supported output to different sinks when run with multiple streams. For example, a processed stream could be displayed while a raw stream would be sent to files. Or a processed stream could be output to PPM files while a raw stream to raw files. Let's start with replacing the single CameraSession::sink_ with a vector of sinks_. We put just the default sink there for now, more functionality will be added in followup patches. Signed-off-by: Milan Zamazal --- src/apps/cam/camera_session.cpp | 51 +++++++++++++++++++++------------ src/apps/cam/camera_session.h | 2 +- 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/src/apps/cam/camera_session.cpp b/src/apps/cam/camera_session.cpp index 937a56b5..54073cdf 100644 --- a/src/apps/cam/camera_session.cpp +++ b/src/apps/cam/camera_session.cpp @@ -20,6 +20,7 @@ #include "capture_script.h" #include "file_sink.h" +#include "frame_sink.h" #ifdef HAVE_KMS #include "kms_sink.h" #endif @@ -255,14 +256,17 @@ int CameraSession::start() camera_->requestCompleted.connect(this, &CameraSession::requestComplete); + sinks_.clear(); + std::unique_ptr defaultSink; #ifdef HAVE_KMS if (options_.isSet(OptDisplay)) - sink_ = std::make_unique(options_[OptDisplay].toString()); + defaultSink = + std::make_unique(options_[OptDisplay].toString()); #endif #ifdef HAVE_SDL if (options_.isSet(OptSDL)) - sink_ = std::make_unique(); + defaultSink = std::make_unique(); #endif if (options_.isSet(OptFile)) { @@ -275,18 +279,21 @@ int CameraSession::start() return ret; } - sink_ = std::move(sink); + defaultSink = std::move(sink); } - if (sink_) { - ret = sink_->configure(*config_); + if (defaultSink) + sinks_.push_back(std::move(defaultSink)); + + for (auto &sink : sinks_) { + ret = sink->configure(*config_); if (ret < 0) { std::cout << "Failed to configure frame sink" << std::endl; return ret; } - sink_->requestProcessed.connect(this, &CameraSession::sinkRelease); + sink->requestProcessed.connect(this, &CameraSession::sinkRelease); } allocator_ = std::make_unique(camera_); @@ -300,13 +307,14 @@ void CameraSession::stop() if (ret) std::cout << "Failed to stop capture" << std::endl; - if (sink_) { - ret = sink_->stop(); + for (auto &sink : sinks_) { + ret = sink->stop(); if (ret) std::cout << "Failed to stop frame sink" << std::endl; } - sink_.reset(); + for (auto &sink : sinks_) + sink.reset(); requests_.clear(); @@ -355,15 +363,15 @@ int CameraSession::startCapture() return ret; } - if (sink_) - sink_->mapBuffer(buffer.get()); + for (auto &sink : sinks_) + sink->mapBuffer(buffer.get()); } requests_.push_back(std::move(request)); } - if (sink_) { - ret = sink_->start(); + for (auto &sink : sinks_) { + ret = sink->start(); if (ret) { std::cout << "Failed to start frame sink" << std::endl; return ret; @@ -373,8 +381,8 @@ int CameraSession::startCapture() ret = camera_->start(); if (ret) { std::cout << "Failed to start capture" << std::endl; - if (sink_) - sink_->stop(); + for (auto &sink : sinks_) + sink->stop(); return ret; } @@ -383,8 +391,8 @@ int CameraSession::startCapture() if (ret < 0) { std::cerr << "Can't queue request" << std::endl; camera_->stop(); - if (sink_) - sink_->stop(); + for (auto &sink : sinks_) + sink->stop(); return ret; } } @@ -471,9 +479,14 @@ void CameraSession::processRequest(Request *request) } } - if (sink_) { - if (!sink_->processRequest(request)) + for (auto &sink : sinks_) { + if (!sink->processRequest(request)) { + /* + * \todo What happens with the other sinks when the whole request + * gets requeued? + */ requeue = false; + } } std::cout << info.str() << std::endl; diff --git a/src/apps/cam/camera_session.h b/src/apps/cam/camera_session.h index 4442fd9b..056091e5 100644 --- a/src/apps/cam/camera_session.h +++ b/src/apps/cam/camera_session.h @@ -64,7 +64,7 @@ private: std::unique_ptr script_; std::map streamNames_; - std::unique_ptr sink_; + std::vector> sinks_; unsigned int cameraIndex_; uint64_t last_;