diff --git a/src/libcamera/pipeline/meson.build b/src/libcamera/pipeline/meson.build
index 615ecd20f1a21141..4da5d8cf9c4384bb 100644
--- a/src/libcamera/pipeline/meson.build
+++ b/src/libcamera/pipeline/meson.build
@@ -1,3 +1,4 @@
 libcamera_sources += files([
+    'uvcvideo.cpp',
     'vimc.cpp',
 ])
diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp
new file mode 100644
index 0000000000000000..2579782e90caec5a
--- /dev/null
+++ b/src/libcamera/pipeline/uvcvideo.cpp
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * uvcvideo.cpp - Pipeline handler for uvcvideo devices
+ */
+
+#include <libcamera/camera.h>
+#include <libcamera/camera_manager.h>
+
+#include "device_enumerator.h"
+#include "media_device.h"
+#include "pipeline_handler.h"
+
+namespace libcamera {
+
+class PipeHandlerUvcvideo : public PipelineHandler
+{
+public:
+	PipeHandlerUvcvideo();
+	~PipeHandlerUvcvideo();
+
+	bool match(CameraManager *manager, DeviceEnumerator *enumerator);
+
+private:
+	MediaDevice *dev_;
+};
+
+PipeHandlerUvcvideo::PipeHandlerUvcvideo()
+	: dev_(nullptr)
+{
+}
+
+PipeHandlerUvcvideo::~PipeHandlerUvcvideo()
+{
+	if (dev_)
+		dev_->release();
+}
+
+bool PipeHandlerUvcvideo::match(CameraManager *manager, DeviceEnumerator *enumerator)
+{
+	DeviceMatch dm("uvcvideo");
+
+	dev_ = enumerator->search(dm);
+
+	if (!dev_)
+		return false;
+
+	dev_->acquire();
+
+	std::shared_ptr<Camera> camera = Camera::create(dev_->model());
+	manager->addCamera(std::move(camera));
+
+	return true;
+}
+
+REGISTER_PIPELINE_HANDLER(PipeHandlerUvcvideo);
+
+} /* namespace libcamera */
