diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h
index f8e00003..3602bce8 100644
--- a/include/libcamera/internal/software_isp/software_isp.h
+++ b/include/libcamera/internal/software_isp/software_isp.h
@@ -11,6 +11,7 @@
 #include <initializer_list>
 #include <map>
 #include <memory>
+#include <stdint.h>
 #include <string>
 #include <tuple>
 #include <vector>
@@ -66,7 +67,8 @@ public:
 	int exportBuffers(const Stream *stream, unsigned int count,
 			  std::vector<std::unique_ptr<FrameBuffer>> *buffers);
 
-	void processStats(const ControlList &sensorControls);
+	void processStats(const uint32_t frame, const uint32_t bufferId,
+			  const ControlList &sensorControls);
 
 	int start();
 	void stop();
@@ -78,13 +80,13 @@ public:
 
 	Signal<FrameBuffer *> inputBufferReady;
 	Signal<FrameBuffer *> outputBufferReady;
-	Signal<> ispStatsReady;
+	Signal<uint32_t, uint32_t> ispStatsReady;
 	Signal<const ControlList &> setSensorControls;
 
 private:
 	void saveIspParams();
 	void setSensorCtrls(const ControlList &sensorControls);
-	void statsReady();
+	void statsReady(uint32_t frame, uint32_t bufferId);
 	void inputReady(FrameBuffer *input);
 	void outputReady(FrameBuffer *output);
 
diff --git a/include/libcamera/ipa/soft.mojom b/include/libcamera/ipa/soft.mojom
index 3aa2066e..bd48ece9 100644
--- a/include/libcamera/ipa/soft.mojom
+++ b/include/libcamera/ipa/soft.mojom
@@ -19,7 +19,7 @@ interface IPASoftInterface {
 	configure(libcamera.ControlInfoMap sensorCtrlInfoMap)
 		=> (int32 ret);
 
-	[async] processStats(libcamera.ControlList sensorControls);
+	[async] processStats(uint32 frame, uint32 bufferId, libcamera.ControlList sensorControls);
 };
 
 interface IPASoftEventInterface {
diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp
index b41b24c6..09e3c8f6 100644
--- a/src/ipa/simple/soft_simple.cpp
+++ b/src/ipa/simple/soft_simple.cpp
@@ -74,7 +74,8 @@ public:
 	int start() override;
 	void stop() override;
 
-	void processStats(const ControlList &sensorControls) override;
+	void processStats(const uint32_t frame, const uint32_t bufferId,
+			  const ControlList &sensorControls) override;
 
 protected:
 	std::string logPrefix() const override;
@@ -248,7 +249,10 @@ void IPASoftSimple::stop()
 {
 }
 
-void IPASoftSimple::processStats(const ControlList &sensorControls)
+void IPASoftSimple::processStats(
+	[[maybe_unused]] const uint32_t frame,
+	[[maybe_unused]] const uint32_t bufferId,
+	const ControlList &sensorControls)
 {
 	SwIspStats::Histogram histogram = stats_->yHistogram;
 	if (ignoreUpdates_ > 0)
diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
index 7c24c2c1..e7149909 100644
--- a/src/libcamera/pipeline/simple/simple.cpp
+++ b/src/libcamera/pipeline/simple/simple.cpp
@@ -13,6 +13,7 @@
 #include <memory>
 #include <queue>
 #include <set>
+#include <stdint.h>
 #include <string.h>
 #include <string>
 #include <unordered_map>
@@ -291,7 +292,7 @@ private:
 	void conversionInputDone(FrameBuffer *buffer);
 	void conversionOutputDone(FrameBuffer *buffer);
 
-	void ispStatsReady();
+	void ispStatsReady(uint32_t frame, uint32_t bufferId);
 	void setSensorControls(const ControlList &sensorControls);
 };
 
@@ -891,11 +892,13 @@ void SimpleCameraData::conversionOutputDone(FrameBuffer *buffer)
 		pipe->completeRequest(request);
 }
 
-void SimpleCameraData::ispStatsReady()
+void SimpleCameraData::ispStatsReady(uint32_t frame, uint32_t bufferId)
 {
 	/* \todo Use the DelayedControls class */
-	swIsp_->processStats(sensor_->getControls({ V4L2_CID_ANALOGUE_GAIN,
-						    V4L2_CID_EXPOSURE }));
+	swIsp_->processStats(
+		frame, bufferId,
+		sensor_->getControls({ V4L2_CID_ANALOGUE_GAIN,
+				       V4L2_CID_EXPOSURE }));
 }
 
 void SimpleCameraData::setSensorControls(const ControlList &sensorControls)
diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
index f8d2677d..1575cedb 100644
--- a/src/libcamera/software_isp/debayer_cpu.cpp
+++ b/src/libcamera/software_isp/debayer_cpu.cpp
@@ -784,7 +784,13 @@ void DebayerCpu::process(FrameBuffer *input, FrameBuffer *output, DebayerParams
 		}
 	}
 
-	stats_->finishFrame();
+	/*
+	 * Frame and buffer ids are currently not used, so pass zeros as parameters.
+	 *
+	 * \todo Pass real values once frame is passed here and stats buffer passing
+	 * is changed.
+	 */
+	stats_->finishFrame(0, 0);
 	outputBufferReady.emit(output);
 	inputBufferReady.emit(input);
 }
diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
index a7e9058e..b48c9903 100644
--- a/src/libcamera/software_isp/software_isp.cpp
+++ b/src/libcamera/software_isp/software_isp.cpp
@@ -156,15 +156,18 @@ SoftwareIsp::~SoftwareIsp()
 
 /**
  * \brief Process the statistics gathered
+ * \param[in] frame The frame number
+ * \param[in] bufferId ID of the statistics buffer
  * \param[in] sensorControls The sensor controls
  *
  * Requests the IPA to calculate new parameters for ISP and new control
  * values for the sensor.
  */
-void SoftwareIsp::processStats(const ControlList &sensorControls)
+void SoftwareIsp::processStats(const uint32_t frame, const uint32_t bufferId,
+			       const ControlList &sensorControls)
 {
 	ASSERT(ipa_);
-	ipa_->processStats(sensorControls);
+	ipa_->processStats(frame, bufferId, sensorControls);
 }
 
 /**
@@ -350,9 +353,9 @@ void SoftwareIsp::setSensorCtrls(const ControlList &sensorControls)
 	setSensorControls.emit(sensorControls);
 }
 
-void SoftwareIsp::statsReady()
+void SoftwareIsp::statsReady(uint32_t frame, uint32_t bufferId)
 {
-	ispStatsReady.emit();
+	ispStatsReady.emit(frame, bufferId);
 }
 
 void SoftwareIsp::inputReady(FrameBuffer *input)
diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp
index 815c4d4f..a9b1f35a 100644
--- a/src/libcamera/software_isp/swstats_cpu.cpp
+++ b/src/libcamera/software_isp/swstats_cpu.cpp
@@ -314,10 +314,10 @@ void SwStatsCpu::startFrame(void)
  *
  * This may only be called after a successful setWindow() call.
  */
-void SwStatsCpu::finishFrame(void)
+void SwStatsCpu::finishFrame(uint32_t frame, uint32_t bufferId)
 {
 	*sharedStats_ = stats_;
-	statsReady.emit();
+	statsReady.emit(frame, bufferId);
 }
 
 /**
diff --git a/src/libcamera/software_isp/swstats_cpu.h b/src/libcamera/software_isp/swstats_cpu.h
index 363e326f..26a2f462 100644
--- a/src/libcamera/software_isp/swstats_cpu.h
+++ b/src/libcamera/software_isp/swstats_cpu.h
@@ -41,7 +41,7 @@ public:
 	int configure(const StreamConfiguration &inputCfg);
 	void setWindow(const Rectangle &window);
 	void startFrame();
-	void finishFrame();
+	void finishFrame(uint32_t frame, uint32_t bufferId);
 
 	void processLine0(unsigned int y, const uint8_t *src[])
 	{
@@ -61,7 +61,7 @@ public:
 		(this->*stats2_)(src);
 	}
 
-	Signal<> statsReady;
+	Signal<uint32_t, uint32_t> statsReady;
 
 private:
 	using statsProcessFn = void (SwStatsCpu::*)(const uint8_t *src[]);
