diff --git a/src/libcamera/include/pipeline_handler.h b/src/libcamera/include/pipeline_handler.h
index 5830e53108faa105..4c13496246c2251c 100644
--- a/src/libcamera/include/pipeline_handler.h
+++ b/src/libcamera/include/pipeline_handler.h
@@ -22,6 +22,7 @@ class Camera;
 class CameraConfiguration;
 class CameraManager;
 class DeviceEnumerator;
+class DeviceMatch;
 class MediaDevice;
 class PipelineHandler;
 class Request;
@@ -53,6 +54,8 @@ public:
 	virtual ~PipelineHandler();
 
 	virtual bool match(DeviceEnumerator *enumerator) = 0;
+	MediaDevice *tryAcquire(DeviceEnumerator *enumerator,
+				const DeviceMatch &dm);
 
 	virtual CameraConfiguration
 	streamConfiguration(Camera *camera, const std::vector<StreamUsage> &usages) = 0;
@@ -84,6 +87,7 @@ private:
 	void mediaDeviceDisconnected(MediaDevice *media);
 	virtual void disconnect();
 
+	std::vector<std::shared_ptr<MediaDevice>> mediaDevices_;
 	std::vector<std::weak_ptr<Camera>> cameras_;
 	std::map<const Camera *, std::unique_ptr<CameraData>> cameraData_;
 };
diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
index c18bb0ec9fa6ba8a..4e3b210530ebd92d 100644
--- a/src/libcamera/pipeline/ipu3/ipu3.cpp
+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
@@ -149,7 +149,6 @@ class PipelineHandlerIPU3 : public PipelineHandler
 {
 public:
 	PipelineHandlerIPU3(CameraManager *manager);
-	~PipelineHandlerIPU3();
 
 	CameraConfiguration
 	streamConfiguration(Camera *camera,
@@ -201,8 +200,8 @@ private:
 
 	ImgUDevice imgu0_;
 	ImgUDevice imgu1_;
-	std::shared_ptr<MediaDevice> cio2MediaDev_;
-	std::shared_ptr<MediaDevice> imguMediaDev_;
+	MediaDevice *cio2MediaDev_;
+	MediaDevice *imguMediaDev_;
 };
 
 PipelineHandlerIPU3::PipelineHandlerIPU3(CameraManager *manager)
@@ -210,15 +209,6 @@ PipelineHandlerIPU3::PipelineHandlerIPU3(CameraManager *manager)
 {
 }
 
-PipelineHandlerIPU3::~PipelineHandlerIPU3()
-{
-	if (cio2MediaDev_)
-		cio2MediaDev_->release();
-
-	if (imguMediaDev_)
-		imguMediaDev_->release();
-}
-
 CameraConfiguration
 PipelineHandlerIPU3::streamConfiguration(Camera *camera,
 					 const std::vector<StreamUsage> &usages)
@@ -617,20 +607,14 @@ bool PipelineHandlerIPU3::match(DeviceEnumerator *enumerator)
 	imgu_dm.add("ipu3-imgu 1 viewfinder");
 	imgu_dm.add("ipu3-imgu 1 3a stat");
 
-	cio2MediaDev_ = enumerator->search(cio2_dm);
+	cio2MediaDev_ = tryAcquire(enumerator, cio2_dm);
 	if (!cio2MediaDev_)
 		return false;
 
-	if (!cio2MediaDev_->acquire())
-		return false;
-
-	imguMediaDev_ = enumerator->search(imgu_dm);
+	imguMediaDev_ = tryAcquire(enumerator, imgu_dm);
 	if (!imguMediaDev_)
 		return false;
 
-	if (!imguMediaDev_->acquire())
-		return false;
-
 	/*
 	 * Disable all links that are enabled by default on CIO2, as camera
 	 * creation enables all valid links it finds.
@@ -685,11 +669,11 @@ int PipelineHandlerIPU3::registerCameras()
 {
 	int ret;
 
-	ret = imgu0_.init(imguMediaDev_.get(), 0);
+	ret = imgu0_.init(imguMediaDev_, 0);
 	if (ret)
 		return ret;
 
-	ret = imgu1_.init(imguMediaDev_.get(), 1);
+	ret = imgu1_.init(imguMediaDev_, 1);
 	if (ret)
 		return ret;
 
@@ -708,7 +692,7 @@ int PipelineHandlerIPU3::registerCameras()
 		};
 		CIO2Device *cio2 = &data->cio2_;
 
-		ret = cio2->init(cio2MediaDev_.get(), id);
+		ret = cio2->init(cio2MediaDev_, id);
 		if (ret)
 			continue;
 
diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
index b720abaa6043a95c..64cbe5d2eb594672 100644
--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
@@ -81,7 +81,7 @@ private:
 	int createCamera(MediaEntity *sensor);
 	void bufferReady(Buffer *buffer);
 
-	std::shared_ptr<MediaDevice> media_;
+	MediaDevice *media_;
 	V4L2Subdevice *dphy_;
 	V4L2Subdevice *isp_;
 	V4L2Device *video_;
@@ -100,9 +100,6 @@ PipelineHandlerRkISP1::~PipelineHandlerRkISP1()
 	delete video_;
 	delete isp_;
 	delete dphy_;
-
-	if (media_)
-		media_->release();
 }
 
 /* -----------------------------------------------------------------------------
@@ -359,24 +356,22 @@ bool PipelineHandlerRkISP1::match(DeviceEnumerator *enumerator)
 	dm.add("rkisp1-input-params");
 	dm.add("rockchip-sy-mipi-dphy");
 
-	media_ = enumerator->search(dm);
+	media_ = tryAcquire(enumerator, dm);
 	if (!media_)
 		return false;
 
-	media_->acquire();
-
 	/* Create the V4L2 subdevices we will need. */
-	dphy_ = V4L2Subdevice::fromEntityName(media_.get(),
+	dphy_ = V4L2Subdevice::fromEntityName(media_,
 					      "rockchip-sy-mipi-dphy");
 	if (dphy_->open() < 0)
 		return false;
 
-	isp_ = V4L2Subdevice::fromEntityName(media_.get(), "rkisp1-isp-subdev");
+	isp_ = V4L2Subdevice::fromEntityName(media_, "rkisp1-isp-subdev");
 	if (isp_->open() < 0)
 		return false;
 
 	/* Locate and open the capture video node. */
-	video_ = V4L2Device::fromEntityName(media_.get(), "rkisp1_mainpath");
+	video_ = V4L2Device::fromEntityName(media_, "rkisp1_mainpath");
 	if (video_->open() < 0)
 		return false;
 
diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp
index d7ef37a8171f0eac..c6ffa0e6b2aa9c77 100644
--- a/src/libcamera/pipeline/uvcvideo.cpp
+++ b/src/libcamera/pipeline/uvcvideo.cpp
@@ -24,7 +24,6 @@ class PipelineHandlerUVC : public PipelineHandler
 {
 public:
 	PipelineHandlerUVC(CameraManager *manager);
-	~PipelineHandlerUVC();
 
 	CameraConfiguration
 	streamConfiguration(Camera *camera,
@@ -70,20 +69,13 @@ private:
 			PipelineHandler::cameraData(camera));
 	}
 
-	std::shared_ptr<MediaDevice> media_;
 };
 
 PipelineHandlerUVC::PipelineHandlerUVC(CameraManager *manager)
-	: PipelineHandler(manager), media_(nullptr)
+	: PipelineHandler(manager)
 {
 }
 
-PipelineHandlerUVC::~PipelineHandlerUVC()
-{
-	if (media_)
-		media_->release();
-}
-
 CameraConfiguration
 PipelineHandlerUVC::streamConfiguration(Camera *camera,
 					const std::vector<StreamUsage> &usages)
@@ -183,19 +175,17 @@ int PipelineHandlerUVC::queueRequest(Camera *camera, Request *request)
 
 bool PipelineHandlerUVC::match(DeviceEnumerator *enumerator)
 {
+	MediaDevice *media;
 	DeviceMatch dm("uvcvideo");
 
-	media_ = enumerator->search(dm);
-	if (!media_)
-		return false;
-
-	if (!media_->acquire())
+	media = tryAcquire(enumerator, dm);
+	if (!media)
 		return false;
 
 	std::unique_ptr<UVCCameraData> data = utils::make_unique<UVCCameraData>(this);
 
 	/* Locate and open the default video node. */
-	for (MediaEntity *entity : media_->entities()) {
+	for (MediaEntity *entity : media->entities()) {
 		if (entity->flags() & MEDIA_ENT_FL_DEFAULT) {
 			data->video_ = new V4L2Device(entity);
 			break;
@@ -214,11 +204,11 @@ bool PipelineHandlerUVC::match(DeviceEnumerator *enumerator)
 
 	/* Create and register the camera. */
 	std::set<Stream *> streams{ &data->stream_ };
-	std::shared_ptr<Camera> camera = Camera::create(this, media_->model(), streams);
+	std::shared_ptr<Camera> camera = Camera::create(this, media->model(), streams);
 	registerCamera(std::move(camera), std::move(data));
 
 	/* Enable hot-unplug notifications. */
-	hotplugMediaDevice(media_.get());
+	hotplugMediaDevice(media);
 
 	return true;
 }
diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp
index 25f0802c30d3d07b..03898f935925b006 100644
--- a/src/libcamera/pipeline/vimc.cpp
+++ b/src/libcamera/pipeline/vimc.cpp
@@ -24,7 +24,6 @@ class PipelineHandlerVimc : public PipelineHandler
 {
 public:
 	PipelineHandlerVimc(CameraManager *manager);
-	~PipelineHandlerVimc();
 
 	CameraConfiguration
 	streamConfiguration(Camera *camera,
@@ -69,21 +68,13 @@ private:
 		return static_cast<VimcCameraData *>(
 			PipelineHandler::cameraData(camera));
 	}
-
-	std::shared_ptr<MediaDevice> media_;
 };
 
 PipelineHandlerVimc::PipelineHandlerVimc(CameraManager *manager)
-	: PipelineHandler(manager), media_(nullptr)
+	: PipelineHandler(manager)
 {
 }
 
-PipelineHandlerVimc::~PipelineHandlerVimc()
-{
-	if (media_)
-		media_->release();
-}
-
 CameraConfiguration
 PipelineHandlerVimc::streamConfiguration(Camera *camera,
 					 const std::vector<StreamUsage> &usages)
@@ -183,6 +174,8 @@ int PipelineHandlerVimc::queueRequest(Camera *camera, Request *request)
 
 bool PipelineHandlerVimc::match(DeviceEnumerator *enumerator)
 {
+	MediaDevice *media;
+
 	DeviceMatch dm("vimc");
 
 	dm.add("Raw Capture 0");
@@ -195,17 +188,14 @@ bool PipelineHandlerVimc::match(DeviceEnumerator *enumerator)
 	dm.add("RGB/YUV Input");
 	dm.add("Scaler");
 
-	media_ = enumerator->search(dm);
-	if (!media_)
-		return false;
-
-	if (!media_->acquire())
+	media = tryAcquire(enumerator, dm);
+	if (!media)
 		return false;
 
 	std::unique_ptr<VimcCameraData> data = utils::make_unique<VimcCameraData>(this);
 
 	/* Locate and open the capture video node. */
-	data->video_ = new V4L2Device(media_->getEntityByName("Raw Capture 1"));
+	data->video_ = new V4L2Device(media->getEntityByName("Raw Capture 1"));
 	if (data->video_->open())
 		return false;
 
diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
index 345abca89ab83590..702b398a50a18085 100644
--- a/src/libcamera/pipeline_handler.cpp
+++ b/src/libcamera/pipeline_handler.cpp
@@ -11,6 +11,7 @@
 #include <libcamera/camera.h>
 #include <libcamera/camera_manager.h>
 
+#include "device_enumerator.h"
 #include "log.h"
 #include "media_device.h"
 #include "utils.h"
@@ -116,6 +117,8 @@ PipelineHandler::PipelineHandler(CameraManager *manager)
 
 PipelineHandler::~PipelineHandler()
 {
+	for (std::shared_ptr<MediaDevice> media : mediaDevices_)
+		media->release();
 };
 
 /**
@@ -149,6 +152,36 @@ PipelineHandler::~PipelineHandler()
  * created, or false otherwise
  */
 
+/**
+ * \brief Try to acquire a MediaDevice from a device pattern
+ * \param[in] enumerator Enumerator containing all media devices in the system
+ * \param[in] dm Device match pattern
+ *
+ * Search in the enumerated media devices that are not already in use for a
+ * match described in \a dm. If a match is found acquire it and store it for
+ * release when the pipeline handler is destroyed.
+ *
+ * \return pointer to the matching MediaDevice, or nullptr if no match is found
+ */
+MediaDevice *PipelineHandler::tryAcquire(DeviceEnumerator *enumerator,
+					 const DeviceMatch &dm)
+{
+	std::shared_ptr<MediaDevice> media;
+
+	media = enumerator->search(dm);
+	if (!media)
+		goto out;
+
+	if (!media->acquire()) {
+		media.reset();
+		goto out;
+	}
+
+	mediaDevices_.push_back(media);
+out:
+	return media.get();
+}
+
 /**
  * \fn PipelineHandler::streamConfiguration()
  * \brief Retrieve a group of stream configurations for a specified camera
