[RFC,v1,23/23] treewide: Use `Request::metadata2()`
diff mbox series

Message ID 20250606164156.1442682-24-barnabas.pocze@ideasonboard.com
State New
Headers show
Series
  • libcamera: Add `MetadataList`
Related show

Commit Message

Barnabás Pőcze June 6, 2025, 4:41 p.m. UTC
Apart from a few exceptions, make everything use the new metadata list
of a request to access metadata items. After this change the only
thing remaining is to remove the exceptional uses, remove `Request::metadata()`
altogether, and finally rename `Request::metadata2()`.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
---
 src/android/camera_device.cpp              | 4 ++--
 src/apps/cam/file_sink.cpp                 | 4 ++--
 src/apps/cam/file_sink.h                   | 4 ++--
 src/apps/common/dng_writer.cpp             | 2 +-
 src/apps/common/dng_writer.h               | 4 ++--
 src/apps/qcam/main_window.cpp              | 4 ++--
 src/apps/qcam/main_window.h                | 4 ++--
 src/gstreamer/gstlibcamera-controls.cpp.in | 4 ++--
 src/gstreamer/gstlibcamerasrc.cpp          | 2 +-
 src/libcamera/pipeline/ipu3/ipu3.cpp       | 2 +-
 10 files changed, 17 insertions(+), 17 deletions(-)

Comments

Jacopo Mondi June 19, 2025, 2:04 p.m. UTC | #1
Hi Barnabás

On Fri, Jun 06, 2025 at 06:41:56PM +0200, Barnabás Pőcze wrote:
> Apart from a few exceptions, make everything use the new metadata list
> of a request to access metadata items. After this change the only
> thing remaining is to remove the exceptional uses, remove `Request::metadata()`
> altogether, and finally rename `Request::metadata2()`.

This is the most intersting part indeed.. replacing

ControlList Request::metadata()
with
MetadataList Request::metadata()

Then we will remove all usages of "ControlList Request::metadata()" in
the library code base and only operate on a MetadataList.

it's an ABI breaking change, so let me cc kieran to make sure he's
aware of this. I wonder if we need a plan to handle an interim period
maybe by keeping "ControlList metadata()" around for one more release
after this series will be merged and notifying to applications that
they should use "MetadataList metadata2()" instead ?

If I'm not mistaken, even after this series, once a Request completes
both metadata() and metadata2() will contain the same metadata list,
right ?

>
> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
> ---
>  src/android/camera_device.cpp              | 4 ++--
>  src/apps/cam/file_sink.cpp                 | 4 ++--
>  src/apps/cam/file_sink.h                   | 4 ++--
>  src/apps/common/dng_writer.cpp             | 2 +-
>  src/apps/common/dng_writer.h               | 4 ++--
>  src/apps/qcam/main_window.cpp              | 4 ++--
>  src/apps/qcam/main_window.h                | 4 ++--
>  src/gstreamer/gstlibcamera-controls.cpp.in | 4 ++--
>  src/gstreamer/gstlibcamerasrc.cpp          | 2 +-
>  src/libcamera/pipeline/ipu3/ipu3.cpp       | 2 +-
>  10 files changed, 17 insertions(+), 17 deletions(-)
>
> diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
> index 80ff248c2..b8c6ddf84 100644
> --- a/src/android/camera_device.cpp
> +++ b/src/android/camera_device.cpp
> @@ -1187,7 +1187,7 @@ void CameraDevice::requestComplete(Request *request)
>  	 * \todo The shutter event notification should be sent to the framework
>  	 * as soon as possible, earlier than request completion time.
>  	 */
> -	uint64_t sensorTimestamp = static_cast<uint64_t>(request->metadata()
> +	uint64_t sensorTimestamp = static_cast<uint64_t>(request->metadata2()
>  								 .get(controls::SensorTimestamp)
>  								 .value_or(0));
>  	notifyShutter(descriptor->frameNumber_, sensorTimestamp);
> @@ -1423,7 +1423,7 @@ void CameraDevice::notifyError(uint32_t frameNumber, camera3_stream_t *stream,
>  std::unique_ptr<CameraMetadata>
>  CameraDevice::getResultMetadata(const Camera3RequestDescriptor &descriptor) const
>  {
> -	const ControlList &metadata = descriptor.request_->metadata();
> +	const MetadataList &metadata = descriptor.request_->metadata2();
>  	const CameraMetadata &settings = descriptor.settings_;
>  	camera_metadata_ro_entry_t entry;
>  	bool found;
> diff --git a/src/apps/cam/file_sink.cpp b/src/apps/cam/file_sink.cpp
> index 65794a2f9..1cc1b2b12 100644
> --- a/src/apps/cam/file_sink.cpp
> +++ b/src/apps/cam/file_sink.cpp
> @@ -96,13 +96,13 @@ void FileSink::mapBuffer(FrameBuffer *buffer)
>  bool FileSink::processRequest(Request *request)
>  {
>  	for (auto [stream, buffer] : request->buffers())
> -		writeBuffer(stream, buffer, request->metadata());
> +		writeBuffer(stream, buffer, request->metadata2());
>
>  	return true;
>  }
>
>  void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer,
> -			   [[maybe_unused]] const ControlList &metadata)
> +			   [[maybe_unused]] const MetadataList &metadata)
>  {
>  	std::string filename = pattern_;
>  	size_t pos;
> diff --git a/src/apps/cam/file_sink.h b/src/apps/cam/file_sink.h
> index 26cd61b36..1f973f2e6 100644
> --- a/src/apps/cam/file_sink.h
> +++ b/src/apps/cam/file_sink.h
> @@ -11,7 +11,7 @@
>  #include <memory>
>  #include <string>
>
> -#include <libcamera/controls.h>
> +#include <libcamera/metadata_list.h>
>  #include <libcamera/stream.h>
>
>  #include "frame_sink.h"
> @@ -44,7 +44,7 @@ private:
>
>  	void writeBuffer(const libcamera::Stream *stream,
>  			 libcamera::FrameBuffer *buffer,
> -			 const libcamera::ControlList &metadata);
> +			 const libcamera::MetadataList &metadata);
>
>  #ifdef HAVE_TIFF
>  	const libcamera::Camera *camera_;
> diff --git a/src/apps/common/dng_writer.cpp b/src/apps/common/dng_writer.cpp
> index ac4619511..d8884548c 100644
> --- a/src/apps/common/dng_writer.cpp
> +++ b/src/apps/common/dng_writer.cpp
> @@ -521,7 +521,7 @@ const std::map<PixelFormat, FormatInfo> formatInfo = {
>
>  int DNGWriter::write(const char *filename, const Camera *camera,
>  		     const StreamConfiguration &config,
> -		     const ControlList &metadata,
> +		     const MetadataList &metadata,
>  		     [[maybe_unused]] const FrameBuffer *buffer,
>  		     const void *data)
>  {
> diff --git a/src/apps/common/dng_writer.h b/src/apps/common/dng_writer.h
> index aaa8a852b..741f78a75 100644
> --- a/src/apps/common/dng_writer.h
> +++ b/src/apps/common/dng_writer.h
> @@ -10,8 +10,8 @@
>  #ifdef HAVE_TIFF
>
>  #include <libcamera/camera.h>
> -#include <libcamera/controls.h>
>  #include <libcamera/framebuffer.h>
> +#include <libcamera/metadata_list.h>
>  #include <libcamera/stream.h>
>
>  class DNGWriter
> @@ -19,7 +19,7 @@ class DNGWriter
>  public:
>  	static int write(const char *filename, const libcamera::Camera *camera,
>  			 const libcamera::StreamConfiguration &config,
> -			 const libcamera::ControlList &metadata,
> +			 const libcamera::MetadataList &metadata,
>  			 const libcamera::FrameBuffer *buffer, const void *data);
>  };
>
> diff --git a/src/apps/qcam/main_window.cpp b/src/apps/qcam/main_window.cpp
> index 7e3f3da60..46dfbb198 100644
> --- a/src/apps/qcam/main_window.cpp
> +++ b/src/apps/qcam/main_window.cpp
> @@ -643,7 +643,7 @@ void MainWindow::captureRaw()
>  }
>
>  void MainWindow::processRaw(FrameBuffer *buffer,
> -			    [[maybe_unused]] const ControlList &metadata)
> +			    [[maybe_unused]] const MetadataList &metadata)
>  {
>  #ifdef HAVE_TIFF
>  	QString defaultPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
> @@ -707,7 +707,7 @@ void MainWindow::processCapture()
>  		processViewfinder(request->buffers().at(vfStream_));
>
>  	if (request->buffers().count(rawStream_))
> -		processRaw(request->buffers().at(rawStream_), request->metadata());
> +		processRaw(request->buffers().at(rawStream_), request->metadata2());
>
>  	request->reuse();
>  	QMutexLocker locker(&mutex_);
> diff --git a/src/apps/qcam/main_window.h b/src/apps/qcam/main_window.h
> index 81fcf915a..eb1e4ce58 100644
> --- a/src/apps/qcam/main_window.h
> +++ b/src/apps/qcam/main_window.h
> @@ -12,9 +12,9 @@
>
>  #include <libcamera/camera.h>
>  #include <libcamera/camera_manager.h>
> -#include <libcamera/controls.h>
>  #include <libcamera/framebuffer.h>
>  #include <libcamera/framebuffer_allocator.h>
> +#include <libcamera/metadata_list.h>
>  #include <libcamera/request.h>
>  #include <libcamera/stream.h>
>
> @@ -66,7 +66,7 @@ private Q_SLOTS:
>  	void saveImageAs();
>  	void captureRaw();
>  	void processRaw(libcamera::FrameBuffer *buffer,
> -			const libcamera::ControlList &metadata);
> +			const libcamera::MetadataList &metadata);
>
>  	void renderComplete(libcamera::FrameBuffer *buffer);
>
> diff --git a/src/gstreamer/gstlibcamera-controls.cpp.in b/src/gstreamer/gstlibcamera-controls.cpp.in
> index 89c530da0..f5bd2885c 100644
> --- a/src/gstreamer/gstlibcamera-controls.cpp.in
> +++ b/src/gstreamer/gstlibcamera-controls.cpp.in
> @@ -322,6 +322,6 @@ void GstCameraControls::applyControls(std::unique_ptr<libcamera::Request> &reque
>
>  void GstCameraControls::readMetadata(libcamera::Request *request)
>  {
> -	controls_acc_.merge(request->metadata(),
> -			    ControlList::MergePolicy::OverwriteExisting);
> +	for (const auto &[k, v] : request->metadata2())
> +		controls_acc_.set(k, ControlValue(v));
>  }
> diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp
> index b34f08977..5b570e86f 100644
> --- a/src/gstreamer/gstlibcamerasrc.cpp
> +++ b/src/gstreamer/gstlibcamerasrc.cpp
> @@ -247,7 +247,7 @@ GstLibcameraSrcState::requestCompleted(Request *request)
>  	}
>
>  	if (GST_ELEMENT_CLOCK(src_)) {
> -		int64_t timestamp = request->metadata().get(controls::SensorTimestamp).value_or(0);
> +		int64_t timestamp = request->metadata2().get(controls::SensorTimestamp).value_or(0);
>
>  		GstClockTime gst_base_time = GST_ELEMENT(src_)->base_time;
>  		GstClockTime gst_now = gst_clock_get_time(GST_ELEMENT_CLOCK(src_));
> diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> index 0f59d0727..5bc8b5ed8 100644
> --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> @@ -1375,7 +1375,7 @@ void IPU3CameraData::statBufferReady(FrameBuffer *buffer)
>  		return;
>  	}
>
> -	ipa_->processStats(info->id, request->metadata().get(controls::SensorTimestamp).value_or(0),
> +	ipa_->processStats(info->id, request->metadata2().get(controls::SensorTimestamp).value_or(0),
>  			   info->statBuffer->cookie(), info->effectiveSensorControls);
>  }
>
> --
> 2.49.0
>
Barnabás Pőcze June 19, 2025, 2:17 p.m. UTC | #2
2025. 06. 19. 16:04 keltezéssel, Jacopo Mondi írta:
> Hi Barnabás
> 
> On Fri, Jun 06, 2025 at 06:41:56PM +0200, Barnabás Pőcze wrote:
>> Apart from a few exceptions, make everything use the new metadata list
>> of a request to access metadata items. After this change the only
>> thing remaining is to remove the exceptional uses, remove `Request::metadata()`
>> altogether, and finally rename `Request::metadata2()`.
> 
> This is the most intersting part indeed.. replacing
> 
> ControlList Request::metadata()
> with
> MetadataList Request::metadata()
> 
> Then we will remove all usages of "ControlList Request::metadata()" in
> the library code base and only operate on a MetadataList.
> 
> it's an ABI breaking change, so let me cc kieran to make sure he's
> aware of this. I wonder if we need a plan to handle an interim period
> maybe by keeping "ControlList metadata()" around for one more release
> after this series will be merged and notifying to applications that
> they should use "MetadataList metadata2()" instead ?
> 
> If I'm not mistaken, even after this series, once a Request completes
> both metadata() and metadata2() will contain the same metadata list,
> right ?

Yes.


> 
>>
>> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
>> ---
>>   src/android/camera_device.cpp              | 4 ++--
>>   src/apps/cam/file_sink.cpp                 | 4 ++--
>>   src/apps/cam/file_sink.h                   | 4 ++--
>>   src/apps/common/dng_writer.cpp             | 2 +-
>>   src/apps/common/dng_writer.h               | 4 ++--
>>   src/apps/qcam/main_window.cpp              | 4 ++--
>>   src/apps/qcam/main_window.h                | 4 ++--
>>   src/gstreamer/gstlibcamera-controls.cpp.in | 4 ++--
>>   src/gstreamer/gstlibcamerasrc.cpp          | 2 +-
>>   src/libcamera/pipeline/ipu3/ipu3.cpp       | 2 +-
>>   10 files changed, 17 insertions(+), 17 deletions(-)
>>
>> diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
>> index 80ff248c2..b8c6ddf84 100644
>> --- a/src/android/camera_device.cpp
>> +++ b/src/android/camera_device.cpp
>> @@ -1187,7 +1187,7 @@ void CameraDevice::requestComplete(Request *request)
>>   	 * \todo The shutter event notification should be sent to the framework
>>   	 * as soon as possible, earlier than request completion time.
>>   	 */
>> -	uint64_t sensorTimestamp = static_cast<uint64_t>(request->metadata()
>> +	uint64_t sensorTimestamp = static_cast<uint64_t>(request->metadata2()
>>   								 .get(controls::SensorTimestamp)
>>   								 .value_or(0));
>>   	notifyShutter(descriptor->frameNumber_, sensorTimestamp);
>> @@ -1423,7 +1423,7 @@ void CameraDevice::notifyError(uint32_t frameNumber, camera3_stream_t *stream,
>>   std::unique_ptr<CameraMetadata>
>>   CameraDevice::getResultMetadata(const Camera3RequestDescriptor &descriptor) const
>>   {
>> -	const ControlList &metadata = descriptor.request_->metadata();
>> +	const MetadataList &metadata = descriptor.request_->metadata2();
>>   	const CameraMetadata &settings = descriptor.settings_;
>>   	camera_metadata_ro_entry_t entry;
>>   	bool found;
>> diff --git a/src/apps/cam/file_sink.cpp b/src/apps/cam/file_sink.cpp
>> index 65794a2f9..1cc1b2b12 100644
>> --- a/src/apps/cam/file_sink.cpp
>> +++ b/src/apps/cam/file_sink.cpp
>> @@ -96,13 +96,13 @@ void FileSink::mapBuffer(FrameBuffer *buffer)
>>   bool FileSink::processRequest(Request *request)
>>   {
>>   	for (auto [stream, buffer] : request->buffers())
>> -		writeBuffer(stream, buffer, request->metadata());
>> +		writeBuffer(stream, buffer, request->metadata2());
>>
>>   	return true;
>>   }
>>
>>   void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer,
>> -			   [[maybe_unused]] const ControlList &metadata)
>> +			   [[maybe_unused]] const MetadataList &metadata)
>>   {
>>   	std::string filename = pattern_;
>>   	size_t pos;
>> diff --git a/src/apps/cam/file_sink.h b/src/apps/cam/file_sink.h
>> index 26cd61b36..1f973f2e6 100644
>> --- a/src/apps/cam/file_sink.h
>> +++ b/src/apps/cam/file_sink.h
>> @@ -11,7 +11,7 @@
>>   #include <memory>
>>   #include <string>
>>
>> -#include <libcamera/controls.h>
>> +#include <libcamera/metadata_list.h>
>>   #include <libcamera/stream.h>
>>
>>   #include "frame_sink.h"
>> @@ -44,7 +44,7 @@ private:
>>
>>   	void writeBuffer(const libcamera::Stream *stream,
>>   			 libcamera::FrameBuffer *buffer,
>> -			 const libcamera::ControlList &metadata);
>> +			 const libcamera::MetadataList &metadata);
>>
>>   #ifdef HAVE_TIFF
>>   	const libcamera::Camera *camera_;
>> diff --git a/src/apps/common/dng_writer.cpp b/src/apps/common/dng_writer.cpp
>> index ac4619511..d8884548c 100644
>> --- a/src/apps/common/dng_writer.cpp
>> +++ b/src/apps/common/dng_writer.cpp
>> @@ -521,7 +521,7 @@ const std::map<PixelFormat, FormatInfo> formatInfo = {
>>
>>   int DNGWriter::write(const char *filename, const Camera *camera,
>>   		     const StreamConfiguration &config,
>> -		     const ControlList &metadata,
>> +		     const MetadataList &metadata,
>>   		     [[maybe_unused]] const FrameBuffer *buffer,
>>   		     const void *data)
>>   {
>> diff --git a/src/apps/common/dng_writer.h b/src/apps/common/dng_writer.h
>> index aaa8a852b..741f78a75 100644
>> --- a/src/apps/common/dng_writer.h
>> +++ b/src/apps/common/dng_writer.h
>> @@ -10,8 +10,8 @@
>>   #ifdef HAVE_TIFF
>>
>>   #include <libcamera/camera.h>
>> -#include <libcamera/controls.h>
>>   #include <libcamera/framebuffer.h>
>> +#include <libcamera/metadata_list.h>
>>   #include <libcamera/stream.h>
>>
>>   class DNGWriter
>> @@ -19,7 +19,7 @@ class DNGWriter
>>   public:
>>   	static int write(const char *filename, const libcamera::Camera *camera,
>>   			 const libcamera::StreamConfiguration &config,
>> -			 const libcamera::ControlList &metadata,
>> +			 const libcamera::MetadataList &metadata,
>>   			 const libcamera::FrameBuffer *buffer, const void *data);
>>   };
>>
>> diff --git a/src/apps/qcam/main_window.cpp b/src/apps/qcam/main_window.cpp
>> index 7e3f3da60..46dfbb198 100644
>> --- a/src/apps/qcam/main_window.cpp
>> +++ b/src/apps/qcam/main_window.cpp
>> @@ -643,7 +643,7 @@ void MainWindow::captureRaw()
>>   }
>>
>>   void MainWindow::processRaw(FrameBuffer *buffer,
>> -			    [[maybe_unused]] const ControlList &metadata)
>> +			    [[maybe_unused]] const MetadataList &metadata)
>>   {
>>   #ifdef HAVE_TIFF
>>   	QString defaultPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
>> @@ -707,7 +707,7 @@ void MainWindow::processCapture()
>>   		processViewfinder(request->buffers().at(vfStream_));
>>
>>   	if (request->buffers().count(rawStream_))
>> -		processRaw(request->buffers().at(rawStream_), request->metadata());
>> +		processRaw(request->buffers().at(rawStream_), request->metadata2());
>>
>>   	request->reuse();
>>   	QMutexLocker locker(&mutex_);
>> diff --git a/src/apps/qcam/main_window.h b/src/apps/qcam/main_window.h
>> index 81fcf915a..eb1e4ce58 100644
>> --- a/src/apps/qcam/main_window.h
>> +++ b/src/apps/qcam/main_window.h
>> @@ -12,9 +12,9 @@
>>
>>   #include <libcamera/camera.h>
>>   #include <libcamera/camera_manager.h>
>> -#include <libcamera/controls.h>
>>   #include <libcamera/framebuffer.h>
>>   #include <libcamera/framebuffer_allocator.h>
>> +#include <libcamera/metadata_list.h>
>>   #include <libcamera/request.h>
>>   #include <libcamera/stream.h>
>>
>> @@ -66,7 +66,7 @@ private Q_SLOTS:
>>   	void saveImageAs();
>>   	void captureRaw();
>>   	void processRaw(libcamera::FrameBuffer *buffer,
>> -			const libcamera::ControlList &metadata);
>> +			const libcamera::MetadataList &metadata);
>>
>>   	void renderComplete(libcamera::FrameBuffer *buffer);
>>
>> diff --git a/src/gstreamer/gstlibcamera-controls.cpp.in b/src/gstreamer/gstlibcamera-controls.cpp.in
>> index 89c530da0..f5bd2885c 100644
>> --- a/src/gstreamer/gstlibcamera-controls.cpp.in
>> +++ b/src/gstreamer/gstlibcamera-controls.cpp.in
>> @@ -322,6 +322,6 @@ void GstCameraControls::applyControls(std::unique_ptr<libcamera::Request> &reque
>>
>>   void GstCameraControls::readMetadata(libcamera::Request *request)
>>   {
>> -	controls_acc_.merge(request->metadata(),
>> -			    ControlList::MergePolicy::OverwriteExisting);
>> +	for (const auto &[k, v] : request->metadata2())
>> +		controls_acc_.set(k, ControlValue(v));
>>   }
>> diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp
>> index b34f08977..5b570e86f 100644
>> --- a/src/gstreamer/gstlibcamerasrc.cpp
>> +++ b/src/gstreamer/gstlibcamerasrc.cpp
>> @@ -247,7 +247,7 @@ GstLibcameraSrcState::requestCompleted(Request *request)
>>   	}
>>
>>   	if (GST_ELEMENT_CLOCK(src_)) {
>> -		int64_t timestamp = request->metadata().get(controls::SensorTimestamp).value_or(0);
>> +		int64_t timestamp = request->metadata2().get(controls::SensorTimestamp).value_or(0);
>>
>>   		GstClockTime gst_base_time = GST_ELEMENT(src_)->base_time;
>>   		GstClockTime gst_now = gst_clock_get_time(GST_ELEMENT_CLOCK(src_));
>> diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
>> index 0f59d0727..5bc8b5ed8 100644
>> --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
>> +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
>> @@ -1375,7 +1375,7 @@ void IPU3CameraData::statBufferReady(FrameBuffer *buffer)
>>   		return;
>>   	}
>>
>> -	ipa_->processStats(info->id, request->metadata().get(controls::SensorTimestamp).value_or(0),
>> +	ipa_->processStats(info->id, request->metadata2().get(controls::SensorTimestamp).value_or(0),
>>   			   info->statBuffer->cookie(), info->effectiveSensorControls);
>>   }
>>
>> --
>> 2.49.0
>>

Patch
diff mbox series

diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
index 80ff248c2..b8c6ddf84 100644
--- a/src/android/camera_device.cpp
+++ b/src/android/camera_device.cpp
@@ -1187,7 +1187,7 @@  void CameraDevice::requestComplete(Request *request)
 	 * \todo The shutter event notification should be sent to the framework
 	 * as soon as possible, earlier than request completion time.
 	 */
-	uint64_t sensorTimestamp = static_cast<uint64_t>(request->metadata()
+	uint64_t sensorTimestamp = static_cast<uint64_t>(request->metadata2()
 								 .get(controls::SensorTimestamp)
 								 .value_or(0));
 	notifyShutter(descriptor->frameNumber_, sensorTimestamp);
@@ -1423,7 +1423,7 @@  void CameraDevice::notifyError(uint32_t frameNumber, camera3_stream_t *stream,
 std::unique_ptr<CameraMetadata>
 CameraDevice::getResultMetadata(const Camera3RequestDescriptor &descriptor) const
 {
-	const ControlList &metadata = descriptor.request_->metadata();
+	const MetadataList &metadata = descriptor.request_->metadata2();
 	const CameraMetadata &settings = descriptor.settings_;
 	camera_metadata_ro_entry_t entry;
 	bool found;
diff --git a/src/apps/cam/file_sink.cpp b/src/apps/cam/file_sink.cpp
index 65794a2f9..1cc1b2b12 100644
--- a/src/apps/cam/file_sink.cpp
+++ b/src/apps/cam/file_sink.cpp
@@ -96,13 +96,13 @@  void FileSink::mapBuffer(FrameBuffer *buffer)
 bool FileSink::processRequest(Request *request)
 {
 	for (auto [stream, buffer] : request->buffers())
-		writeBuffer(stream, buffer, request->metadata());
+		writeBuffer(stream, buffer, request->metadata2());
 
 	return true;
 }
 
 void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer,
-			   [[maybe_unused]] const ControlList &metadata)
+			   [[maybe_unused]] const MetadataList &metadata)
 {
 	std::string filename = pattern_;
 	size_t pos;
diff --git a/src/apps/cam/file_sink.h b/src/apps/cam/file_sink.h
index 26cd61b36..1f973f2e6 100644
--- a/src/apps/cam/file_sink.h
+++ b/src/apps/cam/file_sink.h
@@ -11,7 +11,7 @@ 
 #include <memory>
 #include <string>
 
-#include <libcamera/controls.h>
+#include <libcamera/metadata_list.h>
 #include <libcamera/stream.h>
 
 #include "frame_sink.h"
@@ -44,7 +44,7 @@  private:
 
 	void writeBuffer(const libcamera::Stream *stream,
 			 libcamera::FrameBuffer *buffer,
-			 const libcamera::ControlList &metadata);
+			 const libcamera::MetadataList &metadata);
 
 #ifdef HAVE_TIFF
 	const libcamera::Camera *camera_;
diff --git a/src/apps/common/dng_writer.cpp b/src/apps/common/dng_writer.cpp
index ac4619511..d8884548c 100644
--- a/src/apps/common/dng_writer.cpp
+++ b/src/apps/common/dng_writer.cpp
@@ -521,7 +521,7 @@  const std::map<PixelFormat, FormatInfo> formatInfo = {
 
 int DNGWriter::write(const char *filename, const Camera *camera,
 		     const StreamConfiguration &config,
-		     const ControlList &metadata,
+		     const MetadataList &metadata,
 		     [[maybe_unused]] const FrameBuffer *buffer,
 		     const void *data)
 {
diff --git a/src/apps/common/dng_writer.h b/src/apps/common/dng_writer.h
index aaa8a852b..741f78a75 100644
--- a/src/apps/common/dng_writer.h
+++ b/src/apps/common/dng_writer.h
@@ -10,8 +10,8 @@ 
 #ifdef HAVE_TIFF
 
 #include <libcamera/camera.h>
-#include <libcamera/controls.h>
 #include <libcamera/framebuffer.h>
+#include <libcamera/metadata_list.h>
 #include <libcamera/stream.h>
 
 class DNGWriter
@@ -19,7 +19,7 @@  class DNGWriter
 public:
 	static int write(const char *filename, const libcamera::Camera *camera,
 			 const libcamera::StreamConfiguration &config,
-			 const libcamera::ControlList &metadata,
+			 const libcamera::MetadataList &metadata,
 			 const libcamera::FrameBuffer *buffer, const void *data);
 };
 
diff --git a/src/apps/qcam/main_window.cpp b/src/apps/qcam/main_window.cpp
index 7e3f3da60..46dfbb198 100644
--- a/src/apps/qcam/main_window.cpp
+++ b/src/apps/qcam/main_window.cpp
@@ -643,7 +643,7 @@  void MainWindow::captureRaw()
 }
 
 void MainWindow::processRaw(FrameBuffer *buffer,
-			    [[maybe_unused]] const ControlList &metadata)
+			    [[maybe_unused]] const MetadataList &metadata)
 {
 #ifdef HAVE_TIFF
 	QString defaultPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
@@ -707,7 +707,7 @@  void MainWindow::processCapture()
 		processViewfinder(request->buffers().at(vfStream_));
 
 	if (request->buffers().count(rawStream_))
-		processRaw(request->buffers().at(rawStream_), request->metadata());
+		processRaw(request->buffers().at(rawStream_), request->metadata2());
 
 	request->reuse();
 	QMutexLocker locker(&mutex_);
diff --git a/src/apps/qcam/main_window.h b/src/apps/qcam/main_window.h
index 81fcf915a..eb1e4ce58 100644
--- a/src/apps/qcam/main_window.h
+++ b/src/apps/qcam/main_window.h
@@ -12,9 +12,9 @@ 
 
 #include <libcamera/camera.h>
 #include <libcamera/camera_manager.h>
-#include <libcamera/controls.h>
 #include <libcamera/framebuffer.h>
 #include <libcamera/framebuffer_allocator.h>
+#include <libcamera/metadata_list.h>
 #include <libcamera/request.h>
 #include <libcamera/stream.h>
 
@@ -66,7 +66,7 @@  private Q_SLOTS:
 	void saveImageAs();
 	void captureRaw();
 	void processRaw(libcamera::FrameBuffer *buffer,
-			const libcamera::ControlList &metadata);
+			const libcamera::MetadataList &metadata);
 
 	void renderComplete(libcamera::FrameBuffer *buffer);
 
diff --git a/src/gstreamer/gstlibcamera-controls.cpp.in b/src/gstreamer/gstlibcamera-controls.cpp.in
index 89c530da0..f5bd2885c 100644
--- a/src/gstreamer/gstlibcamera-controls.cpp.in
+++ b/src/gstreamer/gstlibcamera-controls.cpp.in
@@ -322,6 +322,6 @@  void GstCameraControls::applyControls(std::unique_ptr<libcamera::Request> &reque
 
 void GstCameraControls::readMetadata(libcamera::Request *request)
 {
-	controls_acc_.merge(request->metadata(),
-			    ControlList::MergePolicy::OverwriteExisting);
+	for (const auto &[k, v] : request->metadata2())
+		controls_acc_.set(k, ControlValue(v));
 }
diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp
index b34f08977..5b570e86f 100644
--- a/src/gstreamer/gstlibcamerasrc.cpp
+++ b/src/gstreamer/gstlibcamerasrc.cpp
@@ -247,7 +247,7 @@  GstLibcameraSrcState::requestCompleted(Request *request)
 	}
 
 	if (GST_ELEMENT_CLOCK(src_)) {
-		int64_t timestamp = request->metadata().get(controls::SensorTimestamp).value_or(0);
+		int64_t timestamp = request->metadata2().get(controls::SensorTimestamp).value_or(0);
 
 		GstClockTime gst_base_time = GST_ELEMENT(src_)->base_time;
 		GstClockTime gst_now = gst_clock_get_time(GST_ELEMENT_CLOCK(src_));
diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
index 0f59d0727..5bc8b5ed8 100644
--- a/src/libcamera/pipeline/ipu3/ipu3.cpp
+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
@@ -1375,7 +1375,7 @@  void IPU3CameraData::statBufferReady(FrameBuffer *buffer)
 		return;
 	}
 
-	ipa_->processStats(info->id, request->metadata().get(controls::SensorTimestamp).value_or(0),
+	ipa_->processStats(info->id, request->metadata2().get(controls::SensorTimestamp).value_or(0),
 			   info->statBuffer->cookie(), info->effectiveSensorControls);
 }