[RFC PATCH] libcamera: converter: Replace usage of stream index by Stream pointer

Kieran Bingham kieran.bingham at ideasonboard.com
Mon May 27 12:25:48 CEST 2024


Quoting Umang Jain (2024-05-24 07:54:19)
> The converter interface uses the unsigned int output stream index to map
> to the output frame buffers. This is cumbersome to implement new
> converters because one has to keep around additional book keeping
> to track the streams with their correct indexes.
> 
> This patch (still an RFC) intends to drop stream index usage.
> The index is replaced by Stream pointer. This is convenient since
> Stream pointers are easily accessible at configuration time (from
> StreamConfiguration) and from Request objects.

It's a little awkward that the term Stream is used in multiple places
(not your patch, the existing situation!)

But I think a convertor Stream does / should map to a libcamera::Stream.

> The v4l2_converter_m2m and simple pipeline handler are adapt to
> use the new interface. This work roped in software ISP as well,
> which also seems to use indexes (although it doesn't implement converter
> interface) because of a common conversionQueue_ queue used for
> converter_ and swIsp_.
> 
> Signed-off-by: Umang Jain <umang.jain at ideasonboard.com>
> ---
> 
> The patch is an RFC and needs more work to be finalised. I want to check
> if this direction is decent enough to take since this is going to block
> my DW100 converter rework.

I think it more or less still makes sense! Could do with a check from
the SoftISP team to consider it from that side too.


> 
> - [PATCH v2 0/4] libcamera: rkisp1: Plumb i.MX8MP DW100 dewarper
> 
> - Some \todos still around validation of outputs framebuffers.
> - compile tested with -Dpipelines=rkisp1,simple 
> 
> ---
>  include/libcamera/internal/converter.h        |  5 +-
>  .../internal/converter/converter_v4l2_m2m.h   | 11 +++--
>  .../internal/software_isp/software_isp.h      |  4 +-
>  .../converter/converter_v4l2_m2m.cpp          | 47 +++++++++----------
>  src/libcamera/pipeline/simple/simple.cpp      | 12 ++---
>  src/libcamera/software_isp/software_isp.cpp   | 22 ++++-----
>  6 files changed, 46 insertions(+), 55 deletions(-)
> 
> diff --git a/include/libcamera/internal/converter.h b/include/libcamera/internal/converter.h
> index 5d74db6b..b51563d7 100644
> --- a/include/libcamera/internal/converter.h
> +++ b/include/libcamera/internal/converter.h
> @@ -26,6 +26,7 @@ namespace libcamera {
>  class FrameBuffer;
>  class MediaDevice;
>  class PixelFormat;
> +class Stream;
>  struct StreamConfiguration;
>  
>  class Converter
> @@ -46,14 +47,14 @@ public:
>  
>         virtual int configure(const StreamConfiguration &inputCfg,
>                               const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs) = 0;
> -       virtual int exportBuffers(unsigned int output, unsigned int count,
> +       virtual int exportBuffers(const Stream *stream, unsigned int count,
>                                   std::vector<std::unique_ptr<FrameBuffer>> *buffers) = 0;
>  
>         virtual int start() = 0;
>         virtual void stop() = 0;
>  
>         virtual int queueBuffers(FrameBuffer *input,
> -                                const std::map<unsigned int, FrameBuffer *> &outputs) = 0;
> +                                const std::map<const Stream *, FrameBuffer *> &outputs) = 0;
>  
>         Signal<FrameBuffer *> inputBufferReady;
>         Signal<FrameBuffer *> outputBufferReady;
> diff --git a/include/libcamera/internal/converter/converter_v4l2_m2m.h b/include/libcamera/internal/converter/converter_v4l2_m2m.h
> index 1126050c..711a1a5f 100644
> --- a/include/libcamera/internal/converter/converter_v4l2_m2m.h
> +++ b/include/libcamera/internal/converter/converter_v4l2_m2m.h
> @@ -28,6 +28,7 @@ class FrameBuffer;
>  class MediaDevice;
>  class Size;
>  class SizeRange;
> +class Stream;
>  struct StreamConfiguration;
>  class V4L2M2MDevice;
>  
> @@ -47,20 +48,20 @@ public:
>  
>         int configure(const StreamConfiguration &inputCfg,
>                       const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfg);
> -       int exportBuffers(unsigned int output, unsigned int count,
> +       int exportBuffers(const Stream *stream, unsigned int count,
>                           std::vector<std::unique_ptr<FrameBuffer>> *buffers);
>  
>         int start();
>         void stop();
>  
>         int queueBuffers(FrameBuffer *input,
> -                        const std::map<unsigned int, FrameBuffer *> &outputs);
> +                        const std::map<const Stream *, FrameBuffer *> &outputs);
>  
>  private:
>         class Stream : protected Loggable
>         {
>         public:
> -               Stream(V4L2M2MConverter *converter, unsigned int index);
> +               Stream(V4L2M2MConverter *converter, const libcamera::Stream *stream);
>  
>                 bool isValid() const { return m2m_ != nullptr; }
>  
> @@ -82,7 +83,7 @@ private:
>                 void outputBufferReady(FrameBuffer *buffer);
>  
>                 V4L2M2MConverter *converter_;
> -               unsigned int index_;
> +               const libcamera::Stream *stream_;
>                 std::unique_ptr<V4L2M2MDevice> m2m_;
>  
>                 unsigned int inputBufferCount_;
> @@ -91,7 +92,7 @@ private:
>  
>         std::unique_ptr<V4L2M2MDevice> m2m_;
>  
> -       std::vector<Stream> streams_;
> +       std::map<const libcamera::Stream *, Stream> streams_;

This is where it gets 'funny'. We have to map a stream to a stream ;+)

I guess this is only used for indexing though.

>         std::map<FrameBuffer *, unsigned int> queue_;
>  };
>  
> diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h
> index 7e9fae6a..be3d1b2f 100644
> --- a/include/libcamera/internal/software_isp/software_isp.h
> +++ b/include/libcamera/internal/software_isp/software_isp.h
> @@ -62,7 +62,7 @@ public:
>                       const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,
>                       const ControlInfoMap &sensorControls);
>  
> -       int exportBuffers(unsigned int output, unsigned int count,
> +       int exportBuffers(const Stream *stream, unsigned int count,
>                           std::vector<std::unique_ptr<FrameBuffer>> *buffers);
>  
>         void processStats(const ControlList &sensorControls);
> @@ -71,7 +71,7 @@ public:
>         void stop();
>  
>         int queueBuffers(FrameBuffer *input,
> -                        const std::map<unsigned int, FrameBuffer *> &outputs);
> +                        const std::map<const Stream *, FrameBuffer *> &outputs);
>  
>         void process(FrameBuffer *input, FrameBuffer *output);
>  
> diff --git a/src/libcamera/converter/converter_v4l2_m2m.cpp b/src/libcamera/converter/converter_v4l2_m2m.cpp
> index d8929fc5..e0619689 100644
> --- a/src/libcamera/converter/converter_v4l2_m2m.cpp
> +++ b/src/libcamera/converter/converter_v4l2_m2m.cpp
> @@ -35,8 +35,8 @@ LOG_DECLARE_CATEGORY(Converter)
>   * V4L2M2MConverter::Stream
>   */
>  
> -V4L2M2MConverter::Stream::Stream(V4L2M2MConverter *converter, unsigned int index)
> -       : converter_(converter), index_(index)
> +V4L2M2MConverter::Stream::Stream(V4L2M2MConverter *converter, const libcamera::Stream *stream)
> +       : converter_(converter), stream_(stream)
>  {
>         m2m_ = std::make_unique<V4L2M2MDevice>(converter->deviceNode());
>  
> @@ -157,7 +157,7 @@ int V4L2M2MConverter::Stream::queueBuffers(FrameBuffer *input, FrameBuffer *outp
>  
>  std::string V4L2M2MConverter::Stream::logPrefix() const
>  {
> -       return "stream" + std::to_string(index_);
> +       return stream_->configuration().toString();

What does this produce? I'm not sure if this will be correct. The
V4L2M2MConvertor might simply be only one 'component' of the
libcamera::Stream ?


>  }
>  
>  void V4L2M2MConverter::Stream::outputBufferReady(FrameBuffer *buffer)
> @@ -333,10 +333,13 @@ int V4L2M2MConverter::configure(const StreamConfiguration &inputCfg,
>         int ret = 0;
>  
>         streams_.clear();
> -       streams_.reserve(outputCfgs.size());
>  
>         for (unsigned int i = 0; i < outputCfgs.size(); ++i) {
> -               Stream &stream = streams_.emplace_back(this, i);
> +               const StreamConfiguration &cfg = outputCfgs[i];
> +               streams_.emplace(cfg.stream(),
> +                                Stream(this, cfg.stream()));
> +
> +               Stream &stream = streams_.at(cfg.stream());
>  

Aha, but this clears one bit up for me. The convertor does deal with
multiple libcamera::Streams and they do map to the Stream instance
above.

>                 if (!stream.isValid()) {
>                         LOG(Converter, Error)
> @@ -345,7 +348,7 @@ int V4L2M2MConverter::configure(const StreamConfiguration &inputCfg,
>                         break;
>                 }
>  
> -               ret = stream.configure(inputCfg, outputCfgs[i]);
> +               ret = stream.configure(inputCfg, cfg);
>                 if (ret < 0)
>                         break;
>         }
> @@ -361,13 +364,14 @@ int V4L2M2MConverter::configure(const StreamConfiguration &inputCfg,
>  /**
>   * \copydoc libcamera::Converter::exportBuffers
>   */
> -int V4L2M2MConverter::exportBuffers(unsigned int output, unsigned int count,
> +int V4L2M2MConverter::exportBuffers(const libcamera::Stream *stream, unsigned int count,
>                                     std::vector<std::unique_ptr<FrameBuffer>> *buffers)
>  {
> -       if (output >= streams_.size())
> +       auto iter = streams_.find(stream);
> +       if (iter == streams_.end())
>                 return -EINVAL;
>  
> -       return streams_[output].exportBuffers(count, buffers);
> +       return iter->second.exportBuffers(count, buffers);
>  }
>  
>  /**
> @@ -377,8 +381,8 @@ int V4L2M2MConverter::start()
>  {
>         int ret;
>  
> -       for (Stream &stream : streams_) {
> -               ret = stream.start();
> +       for (auto &iter : streams_) {
> +               ret = iter.second.start();
>                 if (ret < 0) {
>                         stop();
>                         return ret;
> @@ -393,17 +397,16 @@ int V4L2M2MConverter::start()
>   */
>  void V4L2M2MConverter::stop()
>  {
> -       for (Stream &stream : utils::reverse(streams_))
> -               stream.stop();
> +       for (auto &iter : streams_)
> +               iter.second.stop();
>  }
>  
>  /**
>   * \copydoc libcamera::Converter::queueBuffers
>   */
>  int V4L2M2MConverter::queueBuffers(FrameBuffer *input,
> -                                  const std::map<unsigned int, FrameBuffer *> &outputs)
> +                                  const std::map<const libcamera::Stream *, FrameBuffer *> &outputs)
>  {
> -       unsigned int mask = 0;
>         int ret;
>  
>         /*
> @@ -414,20 +417,12 @@ int V4L2M2MConverter::queueBuffers(FrameBuffer *input,
>         if (outputs.empty())
>                 return -EINVAL;
>  
> -       for (auto [index, buffer] : outputs) {
> -               if (!buffer)
> -                       return -EINVAL;
> -               if (index >= streams_.size())
> -                       return -EINVAL;
> -               if (mask & (1 << index))
> -                       return -EINVAL;
>  
> -               mask |= 1 << index;
> -       }
> +       /* \TODO: validate outputs against streams */
>  
>         /* Queue the input and output buffers to all the streams. */
> -       for (auto [index, buffer] : outputs) {
> -               ret = streams_[index].queueBuffers(input, buffer);
> +       for (auto [stream, buffer] : outputs) {
> +               ret = streams_.at(stream).queueBuffers(input, buffer);
>                 if (ret < 0)
>                         return ret;
>         }
> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
> index db3575c3..c22b2f89 100644
> --- a/src/libcamera/pipeline/simple/simple.cpp
> +++ b/src/libcamera/pipeline/simple/simple.cpp
> @@ -278,7 +278,7 @@ public:
>         std::map<PixelFormat, std::vector<const Configuration *>> formats_;
>  
>         std::vector<std::unique_ptr<FrameBuffer>> conversionBuffers_;
> -       std::queue<std::map<unsigned int, FrameBuffer *>> conversionQueue_;
> +       std::queue<std::map<const Stream *, FrameBuffer *>> conversionQueue_;
>         bool useConversion_;
>  
>         std::unique_ptr<Converter> converter_;
> @@ -837,7 +837,7 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
>         Request *request = buffer->request();
>  
>         if (useConversion_ && !conversionQueue_.empty()) {
> -               const std::map<unsigned int, FrameBuffer *> &outputs =
> +               const std::map<const Stream *, FrameBuffer *> &outputs =
>                         conversionQueue_.front();
>                 if (!outputs.empty()) {
>                         FrameBuffer *outputBuffer = outputs.begin()->second;
> @@ -1304,9 +1304,9 @@ int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
>          */
>         if (data->useConversion_)
>                 return data->converter_
> -                              ? data->converter_->exportBuffers(data->streamIndex(stream),
> +                              ? data->converter_->exportBuffers(stream,
>                                                                  count, buffers)
> -                              : data->swIsp_->exportBuffers(data->streamIndex(stream),
> +                              : data->swIsp_->exportBuffers(stream,
>                                                              count, buffers);
>         else
>                 return data->video_->exportBuffers(count, buffers);
> @@ -1399,7 +1399,7 @@ int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request)
>         SimpleCameraData *data = cameraData(camera);
>         int ret;
>  
> -       std::map<unsigned int, FrameBuffer *> buffers;
> +       std::map<const Stream *, FrameBuffer *> buffers;
>  
>         for (auto &[stream, buffer] : request->buffers()) {
>                 /*
> @@ -1408,7 +1408,7 @@ int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request)
>                  * completion handler.
>                  */
>                 if (data->useConversion_) {
> -                       buffers.emplace(data->streamIndex(stream), buffer);
> +                       buffers.emplace(stream, buffer);
>                 } else {
>                         ret = data->video_->queueBuffer(buffer);
>                         if (ret < 0)
> diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
> index c9b6be56..b81b90bd 100644
> --- a/src/libcamera/software_isp/software_isp.cpp
> +++ b/src/libcamera/software_isp/software_isp.cpp
> @@ -227,13 +227,13 @@ int SoftwareIsp::configure(const StreamConfiguration &inputCfg,
>   * \return The number of allocated buffers on success or a negative error code
>   * otherwise
>   */
> -int SoftwareIsp::exportBuffers(unsigned int output, unsigned int count,
> +int SoftwareIsp::exportBuffers(const Stream *stream, unsigned int count,
>                                std::vector<std::unique_ptr<FrameBuffer>> *buffers)
>  {
>         ASSERT(debayer_ != nullptr);
>  
>         /* single output for now */
> -       if (output >= 1)
> +       if (stream != nullptr)
>                 return -EINVAL;

Surely this should be
	if (stream == nullptr)

But can that ever even happen? We can't get here with a null stream can
we ? (We shouldn't!)

Perhaps there needs to be a check/mapping for each stream expected to be
supported - or maybe that will already be limited in
configure()/validate().

>         for (unsigned int i = 0; i < count; i++) {
> @@ -265,9 +265,8 @@ int SoftwareIsp::exportBuffers(unsigned int output, unsigned int count,
>   * \return 0 on success, a negative errno on failure
>   */
>  int SoftwareIsp::queueBuffers(FrameBuffer *input,
> -                             const std::map<unsigned int, FrameBuffer *> &outputs)
> +                             const std::map<const Stream *, FrameBuffer *> &outputs)
>  {
> -       unsigned int mask = 0;
>  
>         /*
>          * Validate the outputs as a sanity check: at least one output is
> @@ -277,18 +276,13 @@ int SoftwareIsp::queueBuffers(FrameBuffer *input,
>         if (outputs.empty())
>                 return -EINVAL;
>  
> -       for (auto [index, buffer] : outputs) {
> -               if (!buffer)
> -                       return -EINVAL;
> -               if (index >= 1) /* only single stream atm */
> -                       return -EINVAL;
> -               if (mask & (1 << index))
> -                       return -EINVAL;
> +       /* \todo validate outputs against streams*/
>  
> -               mask |= 1 << index;
> -       }
> +       if (outputs.size() != 1) /* only single stream atm */
> +               return -EINVAL;
>  
> -       process(input, outputs.at(0));
> +       for (auto iter = outputs.begin(); iter != outputs.end(); iter++)
> +               process(input, iter->second);
>  
>         return 0;
>  }
> -- 
> 2.44.0
>


More information about the libcamera-devel mailing list