diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
index 7ad66f21..7271573a 100644
--- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
+++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
@@ -205,9 +205,7 @@ public:
 private:
 	void checkRequestCompleted();
 	void tryRunPipeline();
-	void tryFlushQueues();
-	FrameBuffer *updateQueue(std::queue<FrameBuffer *> &q, uint64_t timestamp,
-				 RPi::Stream *stream);
+	bool findMatchingBuffers(FrameBuffer *&bayerBuffer, FrameBuffer *&embeddedBuffer);
 
 	unsigned int ispOutputCount_;
 };
@@ -1537,7 +1535,6 @@ void RPiCameraData::handleState()
 
 	case State::Idle:
 		tryRunPipeline();
-		tryFlushQueues();
 		break;
 	}
 }
@@ -1588,41 +1585,8 @@ void RPiCameraData::tryRunPipeline()
 	    bayerQueue_.empty() || embeddedQueue_.empty())
 		return;
 
-	/* Start with the front of the bayer buffer queue. */
-	bayerBuffer = bayerQueue_.front();
-
-	/*
-	 * Find the embedded data buffer with a matching timestamp to pass to
-	 * the IPA. Any embedded buffers with a timestamp lower than the
-	 * current bayer buffer will be removed and re-queued to the driver.
-	 */
-	embeddedBuffer = updateQueue(embeddedQueue_, bayerBuffer->metadata().timestamp,
-				     &unicam_[Unicam::Embedded]);
-
-	if (!embeddedBuffer) {
-		LOG(RPI, Debug) << "Could not find matching embedded buffer";
-
-		/*
-		 * Look the other way, try to match a bayer buffer with the
-		 * first embedded buffer in the queue. This will also do some
-		 * housekeeping on the bayer image queue - clear out any
-		 * buffers that are older than the first buffer in the embedded
-		 * queue.
-		 *
-		 * But first check if the embedded queue has emptied out.
-		 */
-		if (embeddedQueue_.empty())
-			return;
-
-		embeddedBuffer = embeddedQueue_.front();
-		bayerBuffer = updateQueue(bayerQueue_, embeddedBuffer->metadata().timestamp,
-					  &unicam_[Unicam::Image]);
-
-		if (!bayerBuffer) {
-			LOG(RPI, Debug) << "Could not find matching bayer buffer - ending.";
-			return;
-		}
-	}
+	if (!findMatchingBuffers(bayerBuffer, embeddedBuffer))
+		return;
 
 	/* Take the first request from the queue and action the IPA. */
 	Request *request = requestQueue_.front();
@@ -1665,10 +1629,6 @@ void RPiCameraData::tryRunPipeline()
 	op.controls = { request->controls() };
 	ipa_->processEvent(op);
 
-	/* Ready to use the buffers, pop them off the queue. */
-	bayerQueue_.pop();
-	embeddedQueue_.pop();
-
 	/* Set our state to say the pipeline is active. */
 	state_ = State::Busy;
 
@@ -1685,62 +1645,102 @@ void RPiCameraData::tryRunPipeline()
 	ipa_->processEvent(op);
 }
 
-void RPiCameraData::tryFlushQueues()
+bool RPiCameraData::findMatchingBuffers(FrameBuffer *&bayerBuffer, FrameBuffer *&embeddedBuffer)
 {
-	/*
-	 * It is possible for us to end up in a situation where all available
-	 * Unicam buffers have been dequeued but do not match. This can happen
-	 * when the system is heavily loaded and we get out of lock-step with
-	 * the two channels.
-	 *
-	 * In such cases, the best thing to do is the re-queue all the buffers
-	 * and give a chance for the hardware to return to lock-step. We do have
-	 * to drop all interim frames.
-	 */
-	if (unicam_[Unicam::Image].getBuffers().size() == bayerQueue_.size() &&
-	    unicam_[Unicam::Embedded].getBuffers().size() == embeddedQueue_.size()) {
-		/* This cannot happen when Unicam streams are external. */
-		assert(!unicam_[Unicam::Image].isExternal());
+	unsigned int embeddedRequeueCount = 0, bayerRequeueCount = 0;
 
-		LOG(RPI, Warning) << "Flushing all buffer queues!";
-
-		while (!bayerQueue_.empty()) {
-			unicam_[Unicam::Image].queueBuffer(bayerQueue_.front());
-			bayerQueue_.pop();
-		}
+	/* Loop until we find a matching bayer and embedded data buffer. */
+	while (!bayerQueue_.empty()) {
+		/* Start with the front of the bayer queue. */
+		bayerBuffer = bayerQueue_.front();
 
+		/*
+		 * Find the embedded data buffer with a matching timestamp to pass to
+		 * the IPA. Any embedded buffers with a timestamp lower than the
+		 * current bayer buffer will be removed and re-queued to the driver.
+		 */
+		uint64_t ts = bayerBuffer->metadata().timestamp;
+		embeddedBuffer = nullptr;
 		while (!embeddedQueue_.empty()) {
-			unicam_[Unicam::Embedded].queueBuffer(embeddedQueue_.front());
-			embeddedQueue_.pop();
+			FrameBuffer *b = embeddedQueue_.front();
+			if (!unicam_[Unicam::Embedded].isExternal() && b->metadata().timestamp < ts) {
+				embeddedQueue_.pop();
+				unicam_[Unicam::Embedded].queueBuffer(b);
+				embeddedRequeueCount++;
+				LOG(RPI, Warning) << "Dropping unmatched input frame in stream "
+						  << unicam_[Unicam::Embedded].name();
+			} else if (unicam_[Unicam::Embedded].isExternal() || b->metadata().timestamp == ts) {
+				/* We pop the item from the queue lower down. */
+				embeddedBuffer = b;
+				break;
+			} else {
+				break; /* Only higher timestamps from here. */
+			}
 		}
-	}
-}
 
-FrameBuffer *RPiCameraData::updateQueue(std::queue<FrameBuffer *> &q, uint64_t timestamp,
-					RPi::Stream *stream)
-{
-	/*
-	 * If the unicam streams are external (both have be to the same), then we
-	 * can only return out the top buffer in the queue, and assume they have
-	 * been synced by queuing at the same time. We cannot drop these frames,
-	 * as they may have been provided externally.
-	 */
-	while (!q.empty()) {
-		FrameBuffer *b = q.front();
-		if (!stream->isExternal() && b->metadata().timestamp < timestamp) {
-			q.pop();
-			stream->queueBuffer(b);
+		if (!embeddedBuffer) {
+			LOG(RPI, Debug) << "Could not find matching embedded buffer";
+
+			/*
+			 * If we have requeued all available embedded data buffers in this loop,
+			 * then we are fully out of sync, so might as well requeue all the pending
+			 * bayer buffers.
+			 */
+			if (embeddedRequeueCount == unicam_[Unicam::Embedded].getBuffers().size()) {
+				/* The embedded queue must be empty at this point! */
+				ASSERT(embeddedQueue_.empty());
+
+				LOG(RPI, Warning) << "Flushing bayer stream!";
+				while (!bayerQueue_.empty()) {
+					unicam_[Unicam::Image].queueBuffer(bayerQueue_.front());
+					bayerQueue_.pop();
+				}
+				return false;
+			}
+
+			/*
+			 * Not found a matching embedded buffer for the bayer buffer in
+			 * the front of the queue. This buffer is now orphaned, so requeue
+			 * it back to the device.
+			 */
+			unicam_[Unicam::Image].queueBuffer(bayerQueue_.front());
+			bayerQueue_.pop();
+			bayerRequeueCount++;
 			LOG(RPI, Warning) << "Dropping unmatched input frame in stream "
-					  << stream->name();
-		} else if (stream->isExternal() || b->metadata().timestamp == timestamp) {
-			/* The calling function will pop the item from the queue. */
-			return b;
+					  << unicam_[Unicam::Image].name();
+
+			/*
+			 * Similar to the above, if we have requeued all available bayer buffers in
+			 * the loop, then we are fully out of sync, so might as well requeue all the
+			 * pending embedded data buffers.
+			 */
+			if (bayerRequeueCount == unicam_[Unicam::Image].getBuffers().size()) {
+				/* The embedded queue must be empty at this point! */
+				ASSERT(bayerQueue_.empty());
+
+				LOG(RPI, Warning) << "Flushing embedded data stream!";
+				while (!embeddedQueue_.empty()) {
+					unicam_[Unicam::Embedded].queueBuffer(embeddedQueue_.front());
+					embeddedQueue_.pop();
+				}
+				return false;
+			}
+
+			/* If the embedded queue has become empty, we cannot do any more. */
+			if (embeddedQueue_.empty())
+				return false;
 		} else {
-			break; /* Only higher timestamps from here. */
+			/*
+			 * We have found a matching bayer and embedded data buffer, so
+			 * nothing more to do apart from popping the buffers from the queue.
+			 */
+			bayerQueue_.pop();
+			embeddedQueue_.pop();
+			return true;
 		}
 	}
 
-	return nullptr;
+	return false;
 }
 
 REGISTER_PIPELINE_HANDLER(PipelineHandlerRPi)
