[libcamera-devel,v3,3/8] cam: Turn BufferWriter into a FrameSink
diff mbox series

Message ID 20210804124314.8044-4-laurent.pinchart@ideasonboard.com
State Accepted
Headers show
Series
  • libcamera: Add DRM/KMS viewfinder display to cam
Related show

Commit Message

Laurent Pinchart Aug. 4, 2021, 12:43 p.m. UTC
Make the BufferWriter class inherit from FrameSink, and use the
FrameSink API to manage it. This makes the code more generic, and will
allow usage of other sinks.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
---
Changes since v2:

- Write all buffers
- Fix errno printing in writeBuffer()

Changes since v1:

- Print message if the file can't be opened
---
 src/cam/buffer_writer.cpp  | 39 +++++++++++++++++++----
 src/cam/buffer_writer.h    | 17 +++++++---
 src/cam/camera_session.cpp | 64 ++++++++++++++++++++++++++++++++------
 src/cam/camera_session.h   |  6 ++--
 4 files changed, 104 insertions(+), 22 deletions(-)

Comments

Paul Elder Aug. 5, 2021, 4:04 a.m. UTC | #1
Hi Laurent,

On Wed, Aug 04, 2021 at 03:43:09PM +0300, Laurent Pinchart wrote:
> Make the BufferWriter class inherit from FrameSink, and use the
> FrameSink API to manage it. This makes the code more generic, and will
> allow usage of other sinks.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>

Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>

> ---
> Changes since v2:
> 
> - Write all buffers
> - Fix errno printing in writeBuffer()
> 
> Changes since v1:
> 
> - Print message if the file can't be opened
> ---
>  src/cam/buffer_writer.cpp  | 39 +++++++++++++++++++----
>  src/cam/buffer_writer.h    | 17 +++++++---
>  src/cam/camera_session.cpp | 64 ++++++++++++++++++++++++++++++++------
>  src/cam/camera_session.h   |  6 ++--
>  4 files changed, 104 insertions(+), 22 deletions(-)
> 
> diff --git a/src/cam/buffer_writer.cpp b/src/cam/buffer_writer.cpp
> index a7648a92fc92..2f4b2b02d3cb 100644
> --- a/src/cam/buffer_writer.cpp
> +++ b/src/cam/buffer_writer.cpp
> @@ -13,6 +13,8 @@
>  #include <sys/mman.h>
>  #include <unistd.h>
>  
> +#include <libcamera/camera.h>
> +
>  #include "buffer_writer.h"
>  
>  using namespace libcamera;
> @@ -32,6 +34,21 @@ BufferWriter::~BufferWriter()
>  	mappedBuffers_.clear();
>  }
>  
> +int BufferWriter::configure(const libcamera::CameraConfiguration &config)
> +{
> +	int ret = FrameSink::configure(config);
> +	if (ret < 0)
> +		return ret;
> +
> +	streamNames_.clear();
> +	for (unsigned int index = 0; index < config.size(); ++index) {
> +		const StreamConfiguration &cfg = config.at(index);
> +		streamNames_[cfg.stream()] = "stream" + std::to_string(index);
> +	}
> +
> +	return 0;
> +}
> +
>  void BufferWriter::mapBuffer(FrameBuffer *buffer)
>  {
>  	for (const FrameBuffer::Plane &plane : buffer->planes()) {
> @@ -43,7 +60,15 @@ void BufferWriter::mapBuffer(FrameBuffer *buffer)
>  	}
>  }
>  
> -int BufferWriter::write(FrameBuffer *buffer, const std::string &streamName)
> +bool BufferWriter::processRequest(Request *request)
> +{
> +	for (auto [stream, buffer] : request->buffers())
> +		writeBuffer(stream, buffer);
> +
> +	return true;
> +}
> +
> +void BufferWriter::writeBuffer(const Stream *stream, FrameBuffer *buffer)
>  {
>  	std::string filename;
>  	size_t pos;
> @@ -58,7 +83,7 @@ int BufferWriter::write(FrameBuffer *buffer, const std::string &streamName)
>  	pos = filename.find_first_of('#');
>  	if (pos != std::string::npos) {
>  		std::stringstream ss;
> -		ss << streamName << "-" << std::setw(6)
> +		ss << streamNames_[stream] << "-" << std::setw(6)
>  		   << std::setfill('0') << buffer->metadata().sequence;
>  		filename.replace(pos, 1, ss.str());
>  	}
> @@ -66,8 +91,12 @@ int BufferWriter::write(FrameBuffer *buffer, const std::string &streamName)
>  	fd = open(filename.c_str(), O_CREAT | O_WRONLY |
>  		  (pos == std::string::npos ? O_APPEND : O_TRUNC),
>  		  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
> -	if (fd == -1)
> -		return -errno;
> +	if (fd == -1) {
> +		ret = -errno;
> +		std::cerr << "failed to open file " << filename << ": "
> +			  << strerror(-ret) << std::endl;
> +		return;
> +	}
>  
>  	for (unsigned int i = 0; i < buffer->planes().size(); ++i) {
>  		const FrameBuffer::Plane &plane = buffer->planes()[i];
> @@ -96,6 +125,4 @@ int BufferWriter::write(FrameBuffer *buffer, const std::string &streamName)
>  	}
>  
>  	close(fd);
> -
> -	return ret;
>  }
> diff --git a/src/cam/buffer_writer.h b/src/cam/buffer_writer.h
> index 7626de42d369..32bb6ed5e045 100644
> --- a/src/cam/buffer_writer.h
> +++ b/src/cam/buffer_writer.h
> @@ -10,20 +10,27 @@
>  #include <map>
>  #include <string>
>  
> -#include <libcamera/framebuffer.h>
> +#include <libcamera/stream.h>
>  
> -class BufferWriter
> +#include "frame_sink.h"
> +
> +class BufferWriter : public FrameSink
>  {
>  public:
>  	BufferWriter(const std::string &pattern = "");
>  	~BufferWriter();
>  
> -	void mapBuffer(libcamera::FrameBuffer *buffer);
> +	int configure(const libcamera::CameraConfiguration &config) override;
>  
> -	int write(libcamera::FrameBuffer *buffer,
> -		  const std::string &streamName);
> +	void mapBuffer(libcamera::FrameBuffer *buffer) override;
> +
> +	bool processRequest(libcamera::Request *request) override;
>  
>  private:
> +	void writeBuffer(const libcamera::Stream *stream,
> +			 libcamera::FrameBuffer *buffer);
> +
> +	std::map<const libcamera::Stream *, std::string> streamNames_;
>  	std::string pattern_;
>  	std::map<int, std::pair<void *, unsigned int>> mappedBuffers_;
>  };
> diff --git a/src/cam/camera_session.cpp b/src/cam/camera_session.cpp
> index 0d49fc1ade83..f91b5234a082 100644
> --- a/src/cam/camera_session.cpp
> +++ b/src/cam/camera_session.cpp
> @@ -13,6 +13,7 @@
>  #include <libcamera/control_ids.h>
>  #include <libcamera/property_ids.h>
>  
> +#include "buffer_writer.h"
>  #include "camera_session.h"
>  #include "event_loop.h"
>  #include "main.h"
> @@ -162,9 +163,20 @@ int CameraSession::start()
>  
>  	if (options_.isSet(OptFile)) {
>  		if (!options_[OptFile].toString().empty())
> -			writer_ = std::make_unique<BufferWriter>(options_[OptFile]);
> +			sink_ = std::make_unique<BufferWriter>(options_[OptFile]);
>  		else
> -			writer_ = std::make_unique<BufferWriter>();
> +			sink_ = std::make_unique<BufferWriter>();
> +	}
> +
> +	if (sink_) {
> +		ret = sink_->configure(*config_);
> +		if (ret < 0) {
> +			std::cout << "Failed to configure frame sink"
> +				  << std::endl;
> +			return ret;
> +		}
> +
> +		sink_->requestProcessed.connect(this, &CameraSession::sinkRelease);
>  	}
>  
>  	allocator_ = std::make_unique<FrameBufferAllocator>(camera_);
> @@ -178,7 +190,13 @@ void CameraSession::stop()
>  	if (ret)
>  		std::cout << "Failed to stop capture" << std::endl;
>  
> -	writer_.reset();
> +	if (sink_) {
> +		ret = sink_->stop();
> +		if (ret)
> +			std::cout << "Failed to stop frame sink" << std::endl;
> +	}
> +
> +	sink_.reset();
>  
>  	requests_.clear();
>  
> @@ -227,16 +245,26 @@ int CameraSession::startCapture()
>  				return ret;
>  			}
>  
> -			if (writer_)
> -				writer_->mapBuffer(buffer.get());
> +			if (sink_)
> +				sink_->mapBuffer(buffer.get());
>  		}
>  
>  		requests_.push_back(std::move(request));
>  	}
>  
> +	if (sink_) {
> +		ret = sink_->start();
> +		if (ret) {
> +			std::cout << "Failed to start frame sink" << std::endl;
> +			return ret;
> +		}
> +	}
> +
>  	ret = camera_->start();
>  	if (ret) {
>  		std::cout << "Failed to start capture" << std::endl;
> +		if (sink_)
> +			sink_->stop();
>  		return ret;
>  	}
>  
> @@ -245,6 +273,8 @@ int CameraSession::startCapture()
>  		if (ret < 0) {
>  			std::cerr << "Can't queue request" << std::endl;
>  			camera_->stop();
> +			if (sink_)
> +				sink_->stop();
>  			return ret;
>  		}
>  	}
> @@ -296,6 +326,8 @@ void CameraSession::processRequest(Request *request)
>  	fps = last_ != 0 && fps ? 1000000000.0 / fps : 0.0;
>  	last_ = ts;
>  
> +	bool requeue = true;
> +
>  	std::stringstream info;
>  	info << ts / 1000000000 << "."
>  	     << std::setw(6) << std::setfill('0') << ts / 1000 % 1000000
> @@ -304,11 +336,10 @@ void CameraSession::processRequest(Request *request)
>  	for (auto it = buffers.begin(); it != buffers.end(); ++it) {
>  		const Stream *stream = it->first;
>  		FrameBuffer *buffer = it->second;
> -		const std::string &name = streamName_[stream];
>  
>  		const FrameMetadata &metadata = buffer->metadata();
>  
> -		info << " " << name
> +		info << " " << streamName_[stream]
>  		     << " seq: " << std::setw(6) << std::setfill('0') << metadata.sequence
>  		     << " bytesused: ";
>  
> @@ -318,9 +349,11 @@ void CameraSession::processRequest(Request *request)
>  			if (++nplane < metadata.planes.size())
>  				info << "/";
>  		}
> +	}
>  
> -		if (writer_)
> -			writer_->write(buffer, name);
> +	if (sink_) {
> +		if (!sink_->processRequest(request))
> +			requeue = false;
>  	}
>  
>  	std::cout << info.str() << std::endl;
> @@ -340,6 +373,19 @@ void CameraSession::processRequest(Request *request)
>  		return;
>  	}
>  
> +	/*
> +	 * If the frame sink holds on the request, we'll requeue it later in the
> +	 * complete handler.
> +	 */
> +	if (!requeue)
> +		return;
> +
> +	request->reuse(Request::ReuseBuffers);
> +	camera_->queueRequest(request);
> +}
> +
> +void CameraSession::sinkRelease(Request *request)
> +{
>  	request->reuse(Request::ReuseBuffers);
>  	queueRequest(request);
>  }
> diff --git a/src/cam/camera_session.h b/src/cam/camera_session.h
> index b0f50e7f998e..2ccc71977a99 100644
> --- a/src/cam/camera_session.h
> +++ b/src/cam/camera_session.h
> @@ -21,9 +21,10 @@
>  #include <libcamera/request.h>
>  #include <libcamera/stream.h>
>  
> -#include "buffer_writer.h"
>  #include "options.h"
>  
> +class FrameSink;
> +
>  class CameraSession
>  {
>  public:
> @@ -53,13 +54,14 @@ private:
>  	int queueRequest(libcamera::Request *request);
>  	void requestComplete(libcamera::Request *request);
>  	void processRequest(libcamera::Request *request);
> +	void sinkRelease(libcamera::Request *request);
>  
>  	const OptionsParser::Options &options_;
>  	std::shared_ptr<libcamera::Camera> camera_;
>  	std::unique_ptr<libcamera::CameraConfiguration> config_;
>  
>  	std::map<const libcamera::Stream *, std::string> streamName_;
> -	std::unique_ptr<BufferWriter> writer_;
> +	std::unique_ptr<FrameSink> sink_;
>  	unsigned int cameraIndex_;
>  
>  	uint64_t last_;
> -- 
> Regards,
> 
> Laurent Pinchart
>

Patch
diff mbox series

diff --git a/src/cam/buffer_writer.cpp b/src/cam/buffer_writer.cpp
index a7648a92fc92..2f4b2b02d3cb 100644
--- a/src/cam/buffer_writer.cpp
+++ b/src/cam/buffer_writer.cpp
@@ -13,6 +13,8 @@ 
 #include <sys/mman.h>
 #include <unistd.h>
 
+#include <libcamera/camera.h>
+
 #include "buffer_writer.h"
 
 using namespace libcamera;
@@ -32,6 +34,21 @@  BufferWriter::~BufferWriter()
 	mappedBuffers_.clear();
 }
 
+int BufferWriter::configure(const libcamera::CameraConfiguration &config)
+{
+	int ret = FrameSink::configure(config);
+	if (ret < 0)
+		return ret;
+
+	streamNames_.clear();
+	for (unsigned int index = 0; index < config.size(); ++index) {
+		const StreamConfiguration &cfg = config.at(index);
+		streamNames_[cfg.stream()] = "stream" + std::to_string(index);
+	}
+
+	return 0;
+}
+
 void BufferWriter::mapBuffer(FrameBuffer *buffer)
 {
 	for (const FrameBuffer::Plane &plane : buffer->planes()) {
@@ -43,7 +60,15 @@  void BufferWriter::mapBuffer(FrameBuffer *buffer)
 	}
 }
 
-int BufferWriter::write(FrameBuffer *buffer, const std::string &streamName)
+bool BufferWriter::processRequest(Request *request)
+{
+	for (auto [stream, buffer] : request->buffers())
+		writeBuffer(stream, buffer);
+
+	return true;
+}
+
+void BufferWriter::writeBuffer(const Stream *stream, FrameBuffer *buffer)
 {
 	std::string filename;
 	size_t pos;
@@ -58,7 +83,7 @@  int BufferWriter::write(FrameBuffer *buffer, const std::string &streamName)
 	pos = filename.find_first_of('#');
 	if (pos != std::string::npos) {
 		std::stringstream ss;
-		ss << streamName << "-" << std::setw(6)
+		ss << streamNames_[stream] << "-" << std::setw(6)
 		   << std::setfill('0') << buffer->metadata().sequence;
 		filename.replace(pos, 1, ss.str());
 	}
@@ -66,8 +91,12 @@  int BufferWriter::write(FrameBuffer *buffer, const std::string &streamName)
 	fd = open(filename.c_str(), O_CREAT | O_WRONLY |
 		  (pos == std::string::npos ? O_APPEND : O_TRUNC),
 		  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
-	if (fd == -1)
-		return -errno;
+	if (fd == -1) {
+		ret = -errno;
+		std::cerr << "failed to open file " << filename << ": "
+			  << strerror(-ret) << std::endl;
+		return;
+	}
 
 	for (unsigned int i = 0; i < buffer->planes().size(); ++i) {
 		const FrameBuffer::Plane &plane = buffer->planes()[i];
@@ -96,6 +125,4 @@  int BufferWriter::write(FrameBuffer *buffer, const std::string &streamName)
 	}
 
 	close(fd);
-
-	return ret;
 }
diff --git a/src/cam/buffer_writer.h b/src/cam/buffer_writer.h
index 7626de42d369..32bb6ed5e045 100644
--- a/src/cam/buffer_writer.h
+++ b/src/cam/buffer_writer.h
@@ -10,20 +10,27 @@ 
 #include <map>
 #include <string>
 
-#include <libcamera/framebuffer.h>
+#include <libcamera/stream.h>
 
-class BufferWriter
+#include "frame_sink.h"
+
+class BufferWriter : public FrameSink
 {
 public:
 	BufferWriter(const std::string &pattern = "");
 	~BufferWriter();
 
-	void mapBuffer(libcamera::FrameBuffer *buffer);
+	int configure(const libcamera::CameraConfiguration &config) override;
 
-	int write(libcamera::FrameBuffer *buffer,
-		  const std::string &streamName);
+	void mapBuffer(libcamera::FrameBuffer *buffer) override;
+
+	bool processRequest(libcamera::Request *request) override;
 
 private:
+	void writeBuffer(const libcamera::Stream *stream,
+			 libcamera::FrameBuffer *buffer);
+
+	std::map<const libcamera::Stream *, std::string> streamNames_;
 	std::string pattern_;
 	std::map<int, std::pair<void *, unsigned int>> mappedBuffers_;
 };
diff --git a/src/cam/camera_session.cpp b/src/cam/camera_session.cpp
index 0d49fc1ade83..f91b5234a082 100644
--- a/src/cam/camera_session.cpp
+++ b/src/cam/camera_session.cpp
@@ -13,6 +13,7 @@ 
 #include <libcamera/control_ids.h>
 #include <libcamera/property_ids.h>
 
+#include "buffer_writer.h"
 #include "camera_session.h"
 #include "event_loop.h"
 #include "main.h"
@@ -162,9 +163,20 @@  int CameraSession::start()
 
 	if (options_.isSet(OptFile)) {
 		if (!options_[OptFile].toString().empty())
-			writer_ = std::make_unique<BufferWriter>(options_[OptFile]);
+			sink_ = std::make_unique<BufferWriter>(options_[OptFile]);
 		else
-			writer_ = std::make_unique<BufferWriter>();
+			sink_ = std::make_unique<BufferWriter>();
+	}
+
+	if (sink_) {
+		ret = sink_->configure(*config_);
+		if (ret < 0) {
+			std::cout << "Failed to configure frame sink"
+				  << std::endl;
+			return ret;
+		}
+
+		sink_->requestProcessed.connect(this, &CameraSession::sinkRelease);
 	}
 
 	allocator_ = std::make_unique<FrameBufferAllocator>(camera_);
@@ -178,7 +190,13 @@  void CameraSession::stop()
 	if (ret)
 		std::cout << "Failed to stop capture" << std::endl;
 
-	writer_.reset();
+	if (sink_) {
+		ret = sink_->stop();
+		if (ret)
+			std::cout << "Failed to stop frame sink" << std::endl;
+	}
+
+	sink_.reset();
 
 	requests_.clear();
 
@@ -227,16 +245,26 @@  int CameraSession::startCapture()
 				return ret;
 			}
 
-			if (writer_)
-				writer_->mapBuffer(buffer.get());
+			if (sink_)
+				sink_->mapBuffer(buffer.get());
 		}
 
 		requests_.push_back(std::move(request));
 	}
 
+	if (sink_) {
+		ret = sink_->start();
+		if (ret) {
+			std::cout << "Failed to start frame sink" << std::endl;
+			return ret;
+		}
+	}
+
 	ret = camera_->start();
 	if (ret) {
 		std::cout << "Failed to start capture" << std::endl;
+		if (sink_)
+			sink_->stop();
 		return ret;
 	}
 
@@ -245,6 +273,8 @@  int CameraSession::startCapture()
 		if (ret < 0) {
 			std::cerr << "Can't queue request" << std::endl;
 			camera_->stop();
+			if (sink_)
+				sink_->stop();
 			return ret;
 		}
 	}
@@ -296,6 +326,8 @@  void CameraSession::processRequest(Request *request)
 	fps = last_ != 0 && fps ? 1000000000.0 / fps : 0.0;
 	last_ = ts;
 
+	bool requeue = true;
+
 	std::stringstream info;
 	info << ts / 1000000000 << "."
 	     << std::setw(6) << std::setfill('0') << ts / 1000 % 1000000
@@ -304,11 +336,10 @@  void CameraSession::processRequest(Request *request)
 	for (auto it = buffers.begin(); it != buffers.end(); ++it) {
 		const Stream *stream = it->first;
 		FrameBuffer *buffer = it->second;
-		const std::string &name = streamName_[stream];
 
 		const FrameMetadata &metadata = buffer->metadata();
 
-		info << " " << name
+		info << " " << streamName_[stream]
 		     << " seq: " << std::setw(6) << std::setfill('0') << metadata.sequence
 		     << " bytesused: ";
 
@@ -318,9 +349,11 @@  void CameraSession::processRequest(Request *request)
 			if (++nplane < metadata.planes.size())
 				info << "/";
 		}
+	}
 
-		if (writer_)
-			writer_->write(buffer, name);
+	if (sink_) {
+		if (!sink_->processRequest(request))
+			requeue = false;
 	}
 
 	std::cout << info.str() << std::endl;
@@ -340,6 +373,19 @@  void CameraSession::processRequest(Request *request)
 		return;
 	}
 
+	/*
+	 * If the frame sink holds on the request, we'll requeue it later in the
+	 * complete handler.
+	 */
+	if (!requeue)
+		return;
+
+	request->reuse(Request::ReuseBuffers);
+	camera_->queueRequest(request);
+}
+
+void CameraSession::sinkRelease(Request *request)
+{
 	request->reuse(Request::ReuseBuffers);
 	queueRequest(request);
 }
diff --git a/src/cam/camera_session.h b/src/cam/camera_session.h
index b0f50e7f998e..2ccc71977a99 100644
--- a/src/cam/camera_session.h
+++ b/src/cam/camera_session.h
@@ -21,9 +21,10 @@ 
 #include <libcamera/request.h>
 #include <libcamera/stream.h>
 
-#include "buffer_writer.h"
 #include "options.h"
 
+class FrameSink;
+
 class CameraSession
 {
 public:
@@ -53,13 +54,14 @@  private:
 	int queueRequest(libcamera::Request *request);
 	void requestComplete(libcamera::Request *request);
 	void processRequest(libcamera::Request *request);
+	void sinkRelease(libcamera::Request *request);
 
 	const OptionsParser::Options &options_;
 	std::shared_ptr<libcamera::Camera> camera_;
 	std::unique_ptr<libcamera::CameraConfiguration> config_;
 
 	std::map<const libcamera::Stream *, std::string> streamName_;
-	std::unique_ptr<BufferWriter> writer_;
+	std::unique_ptr<FrameSink> sink_;
 	unsigned int cameraIndex_;
 
 	uint64_t last_;