Merge changes I55eee605,Ibd4a0dd7 into main

* changes:
  Merge tag 'v0.0.5' into aosp/main
  Merge tag 'v0.0.4' into aosp/main
diff --git a/Cargo.lock b/Cargo.lock
index ba67ef2..32f8cf8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -599,7 +599,7 @@
 
 [[package]]
 name = "v4l2r"
-version = "0.0.4"
+version = "0.0.5"
 dependencies = [
  "anyhow",
  "bindgen",
diff --git a/README.md b/README.md
index c29aca6..c34cad2 100644
--- a/README.md
+++ b/README.md
@@ -49,6 +49,14 @@
 library other projects can link against. A `v4l2r.h` header file with the public
 API is generated upon build.
 
+## Build options
+
+`cargo build` will attempt to generate the V4L2 bindings from
+`/usr/include/linux/videodev2.h` by default. The `V4L2R_VIDEODEV2_H_PATH`
+environment variable can be set to a different location that contains a
+`videodev2.h` file if you need to generate the bindings from a different
+location.
+
 ## How to use
 
 Check `lib/examples/vicodec_test/device_api.rs` for a short example of how to
diff --git a/ffi/src/decoder.rs b/ffi/src/decoder.rs
index a2720d5..29dbb63 100644
--- a/ffi/src/decoder.rs
+++ b/ffi/src/decoder.rs
@@ -28,7 +28,7 @@
         CompletedInputBuffer, DecoderEvent, DecoderEventCallback, FormatChangedCallback,
         FormatChangedReply, InputDoneCallback,
     },
-    device::queue::{direction::Capture, dqbuf::DqBuffer, qbuf::OutputQueueable, FormatBuilder},
+    device::queue::{direction::Capture, dqbuf::DqBuffer, FormatBuilder, OutputQueueable},
     memory::DmaBufHandle,
     PixelFormat, PlaneLayout, Rect,
 };
@@ -139,13 +139,10 @@
     desired_pixel_format: Option<PixelFormat>,
     visible_rect: Rect,
     min_num_buffers: usize,
-    decoder: *mut v4l2r_decoder,
+    decoder: &mut v4l2r_decoder,
     event_cb: v4l2r_decoder_event_cb,
     cb_data: *mut c_void,
 ) -> anyhow::Result<FormatChangedReply<Arc<v4l2r_video_frame_provider>>> {
-    // Safe unless the C part did something funny with the decoder returned by
-    // `v4l2r_decoder_new`.
-    let decoder = unsafe { decoder.as_mut().unwrap() };
     let mut v4l2_format: bindings::v4l2_format = match desired_pixel_format {
         Some(format) => f.set_pixelformat(format).apply()?,
         None => f.apply()?,
@@ -213,6 +210,7 @@
         // Should be safe as `provider` is initialized in the format
         // change callback and is thus valid, as well as `frame`.
         match &decoder.provider {
+            // SAFETY: `provider` is a valid pointer to a frame provider.
             Some(provider) => unsafe {
                 v4l2r_video_frame_provider_queue_frame(provider.as_ref(), frame);
             },
@@ -330,6 +328,8 @@
         let decoder_ptr = decoder_ptr;
         let cb_data = cb_data;
 
+        // SAFETY: `decoder_ptr` will be initialized with a valid decoder by the time this
+        // callback is called.
         let decoder = unsafe { decoder_ptr.0.as_mut().unwrap() };
 
         match event {
@@ -367,12 +367,16 @@
                 let decoder_ptr = decoder_ptr;
                 let cb_data = cb_data;
 
+                // SAFETY: `decoder_ptr` will be initialized with a valid decoder by the time this
+                // callback is called.
+                let decoder = unsafe { decoder_ptr.0.as_mut().unwrap() };
+
                 set_capture_format_cb(
                     f,
                     output_format,
                     visible_rect,
                     min_num_buffers,
-                    decoder_ptr.0,
+                    decoder,
                     event_cb,
                     cb_data.0,
                 )
@@ -396,6 +400,7 @@
         input_buf_size: input_format.plane_fmt[0].sizeimage as u64,
     };
 
+    // SAFETY: `decoder` is a `v4l2r_decoder`, the same type expected by `decoder_box`.
     let decoder_box = unsafe {
         // Replace our uninitialized heap memory with our valid decoder.
         decoder_box.as_mut_ptr().write(decoder);
diff --git a/ffi/src/memory.rs b/ffi/src/memory.rs
index c0c7491..48cb0cd 100644
--- a/ffi/src/memory.rs
+++ b/ffi/src/memory.rs
@@ -17,10 +17,7 @@
         poller::Waker,
         queue::{
             handles_provider::{GetSuitableBufferError, HandlesProvider},
-            qbuf::{
-                get_free::GetFreeCaptureBuffer, get_indexed::GetCaptureBufferByIndex,
-                CaptureQueueableProvider,
-            },
+            GetCaptureBufferByIndex, GetFreeCaptureBuffer,
         },
     },
     memory::{BufferHandles, DmaBufHandle, DmaBufSource, MemoryType, PrimitiveBufferHandles},
@@ -164,10 +161,7 @@
         &self,
         handles: &Self::HandleType,
         queue: &'a Q,
-    ) -> Result<
-        <Q as CaptureQueueableProvider<'a, Self::HandleType>>::Queueable,
-        GetSuitableBufferError,
-    >
+    ) -> Result<Q::Queueable, GetSuitableBufferError>
     where
         Q: GetCaptureBufferByIndex<'a, Self::HandleType>
             + GetFreeCaptureBuffer<'a, Self::HandleType>,
diff --git a/lib/Android.bp b/lib/Android.bp
index 8bc9b59..2173cca 100644
--- a/lib/Android.bp
+++ b/lib/Android.bp
@@ -10,7 +10,7 @@
     host_supported: true,
     crate_name: "v4l2r",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.0.4",
+    cargo_pkg_version: "0.0.5",
     crate_root: "src/lib.rs",
     edition: "2021",
     rustlibs: [
@@ -37,7 +37,7 @@
     host_supported: true,
     crate_name: "v4l2r",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.0.4",
+    cargo_pkg_version: "0.0.5",
     crate_root: "src/lib.rs",
     test_suites: ["general-tests"],
     auto_gen_config: true,
diff --git a/lib/Cargo.toml b/lib/Cargo.toml
index f48bb59..ae5c411 100644
--- a/lib/Cargo.toml
+++ b/lib/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "v4l2r"
-version = "0.0.4"
+version = "0.0.5"
 authors = ["Alexandre Courbot <[email protected]>"]
 edition = "2021"
 description = "Safe and flexible abstraction over V4L2"
@@ -11,6 +11,12 @@
 
 readme.workspace = true
 
+[features]
+# Generate the bindings for 64-bit even if the host is 32-bit.
+arch64 = []
+# Generate the bindings for 32-bit even if the host is 64-bit.
+arch32 = []
+
 [dependencies]
 nix = { version = "0.28", features = ["ioctl", "mman", "poll", "fs", "event"] }
 bitflags = "2.4"
diff --git a/lib/build.rs b/lib/build.rs
index 608ec6d..a3b7f69 100644
--- a/lib/build.rs
+++ b/lib/build.rs
@@ -30,9 +30,17 @@
     println!("cargo::rerun-if-changed={}", videodev2_h.display());
     println!("cargo::rerun-if-changed={}", WRAPPER_H);
 
+    let clang_args = [
+        format!("-I{}", videodev2_h_path),
+        #[cfg(all(feature = "arch64", not(feature = "arch32")))]
+        "--target=x86_64-linux-gnu".into(),
+        #[cfg(all(feature = "arch32", not(feature = "arch64")))]
+        "--target=i686-linux-gnu".into(),
+    ];
+
     let bindings = v4l2r_bindgen_builder(bindgen::Builder::default())
         .header(WRAPPER_H)
-        .clang_args([format!("-I{}", videodev2_h_path)])
+        .clang_args(clang_args)
         .generate()
         .expect("unable to generate bindings");
 
diff --git a/lib/examples/fwht_encoder/main.rs b/lib/examples/fwht_encoder/main.rs
index e5e786e..82b5516 100644
--- a/lib/examples/fwht_encoder/main.rs
+++ b/lib/examples/fwht_encoder/main.rs
@@ -13,7 +13,7 @@
             dqbuf::DqBuffer,
             generic::{GenericBufferHandles, GenericQBuffer, GenericSupportedMemoryType},
             handles_provider::MmapProvider,
-            qbuf::OutputQueueable,
+            OutputQueueable,
         },
     },
     encoder::*,
diff --git a/lib/examples/vicodec_test/device_api.rs b/lib/examples/vicodec_test/device_api.rs
index aa1af2e..a9c9fff 100644
--- a/lib/examples/vicodec_test/device_api.rs
+++ b/lib/examples/vicodec_test/device_api.rs
@@ -5,8 +5,7 @@
 use std::time::Instant;
 use v4l2r_utils::framegen::FrameGenerator;
 
-use qbuf::{get_free::GetFreeCaptureBuffer, get_indexed::GetOutputBufferByIndex};
-use v4l2r::{device::queue::qbuf::OutputQueueable, memory::MemoryType, Format};
+use v4l2r::{device::queue::OutputQueueable, memory::MemoryType, Format};
 use v4l2r::{device::queue::*, memory::MmapHandle};
 use v4l2r::{
     device::{
diff --git a/lib/src/decoder/stateful.rs b/lib/src/decoder/stateful.rs
index 392180a..a6b18ca 100644
--- a/lib/src/decoder/stateful.rs
+++ b/lib/src/decoder/stateful.rs
@@ -7,13 +7,9 @@
         queue::{
             direction::{Capture, Output},
             handles_provider::HandlesProvider,
-            qbuf::{
-                get_free::{GetFreeBufferError, GetFreeCaptureBuffer, GetFreeOutputBuffer},
-                get_indexed::GetCaptureBufferByIndex,
-                OutputQueueableProvider,
-            },
-            BuffersAllocated, CreateQueueError, FormatBuilder, Queue, QueueInit,
-            RequestBuffersError,
+            BuffersAllocated, CreateQueueError, FormatBuilder, GetCaptureBufferByIndex,
+            GetFreeBufferError, GetFreeCaptureBuffer, GetFreeOutputBuffer, OutputQueueableProvider,
+            Queue, QueueInit, RequestBuffersError,
         },
         AllocatedQueue, Device, DeviceConfig, DeviceOpenError, Stream, TryDequeue,
     },
diff --git a/lib/src/decoder/stateful/capture_thread.rs b/lib/src/decoder/stateful/capture_thread.rs
index 8dc849e..67e3268 100644
--- a/lib/src/decoder/stateful/capture_thread.rs
+++ b/lib/src/decoder/stateful/capture_thread.rs
@@ -6,14 +6,8 @@
     device::{
         poller::{DeviceEvent, PollEvent, Poller, Waker},
         queue::{
-            self,
-            direction::Capture,
-            handles_provider::HandlesProvider,
-            qbuf::{
-                get_free::GetFreeCaptureBuffer, get_indexed::GetCaptureBufferByIndex,
-                CaptureQueueable,
-            },
-            BuffersAllocated, Queue, QueueInit,
+            self, direction::Capture, handles_provider::HandlesProvider, BuffersAllocated,
+            CaptureQueueable, GetCaptureBufferByIndex, GetFreeCaptureBuffer, Queue, QueueInit,
         },
         AllocatedQueue, Device, Stream, TryDequeue,
     },
@@ -255,7 +249,7 @@
                 // an infinite number of handles. Break out of the loop when this happens - we will
                 // be called again the next time a CAPTURE buffer becomes available.
                 Err(queue::handles_provider::GetSuitableBufferError::TryGetFree(
-                    queue::qbuf::get_free::GetFreeBufferError::NoFreeBuffer,
+                    queue::GetFreeBufferError::NoFreeBuffer,
                 )) => {
                     break 'enqueue;
                 }
diff --git a/lib/src/device/queue.rs b/lib/src/device/queue.rs
index e2a5e42..2cc15ec 100644
--- a/lib/src/device/queue.rs
+++ b/lib/src/device/queue.rs
@@ -5,8 +5,6 @@
 pub mod handles_provider;
 pub mod qbuf;
 
-use self::qbuf::{get_free::GetFreeOutputBuffer, get_indexed::GetOutputBufferByIndex};
-
 use super::{AllocatedQueue, Device, FreeBuffersResult, Stream, TryDequeue};
 use crate::ioctl::{DqBufResult, QueryBufError, V4l2BufferFromError};
 use crate::{bindings, memory::*};
@@ -21,13 +19,8 @@
 use buffer::*;
 use direction::*;
 use dqbuf::*;
-use generic::{GenericBufferHandles, GenericQBuffer, GenericSupportedMemoryType};
 use log::debug;
-use qbuf::{
-    get_free::{GetFreeBufferError, GetFreeCaptureBuffer},
-    get_indexed::{GetCaptureBufferByIndex, TryGetBufferError},
-    *,
-};
+use qbuf::*;
 
 use std::convert::{Infallible, TryFrom};
 use std::os::unix::io::{AsRawFd, RawFd};
@@ -37,7 +30,7 @@
 /// Base values of a queue, that are always value no matter the state the queue
 /// is in. This base object remains alive as long as the queue is borrowed from
 /// the `Device`.
-pub struct QueueBase {
+struct QueueBase {
     // Reference to the device, so we can perform operations on its `fd` and to let us mark the
     // queue as free again upon destruction.
     device: Arc<Device>,
@@ -116,8 +109,8 @@
     }
 
     /// Returns an iterator over all the formats currently supported by this queue.
-    pub fn format_iter(&self) -> ioctl::FormatIterator<QueueBase> {
-        ioctl::FormatIterator::new(&self.inner, self.inner.type_)
+    pub fn format_iter(&self) -> ioctl::FormatIterator<Device> {
+        ioctl::FormatIterator::new(self.inner.device.as_ref(), self.inner.type_)
     }
 
     pub fn get_selection(&self, target: SelectionTarget) -> Result<Rect, ioctl::GSelectionError> {
@@ -516,51 +509,89 @@
     }
 }
 
+#[derive(Debug, Error)]
+pub enum TryGetBufferError {
+    #[error("buffer with provided index {0} does not exist")]
+    InvalidIndex(usize),
+    #[error("buffer is already in use")]
+    AlreadyUsed,
+}
+
+#[derive(Debug, Error)]
+pub enum GetFreeBufferError {
+    #[error("all buffers are currently being used")]
+    NoFreeBuffer,
+}
+
 mod private {
+    use std::ops::Deref;
+
     use super::*;
 
-    /// Private trait for providing a Queuable regardless of the queue's
-    /// direction. Avoids duplicating the same code in
-    /// Capture/OutputQueueableProvider's implementations.
-    pub trait GetBufferByIndex<'a> {
-        type Queueable: 'a;
+    /// The lifetime `'a` is here to allow implementations to attach the lifetime of their return
+    /// value to `self`. This is useful when we want the buffer to hold a reference to the queue
+    /// that prevents the latter from mutating as long as the buffer is not consumed.
+    pub trait QueueableProvider<'a> {
+        type Queueable;
+    }
 
+    /// Private trait for providing a Queuable regardless of the queue's direction.
+    ///
+    /// This avoids duplicating the same code in Capture/OutputQueueableProvider's implementations.
+    pub trait GetBufferByIndex<'a>: QueueableProvider<'a> {
         fn try_get_buffer(&'a self, index: usize) -> Result<Self::Queueable, TryGetBufferError>;
     }
 
     /// Same as `GetBufferByIndex` but for providing any free buffer.
-    pub trait GetFreeBuffer<'a, ErrorType = GetFreeBufferError>: GetBufferByIndex<'a> {
-        fn try_get_free_buffer(&'a self) -> Result<Self::Queueable, ErrorType>;
+    pub trait GetFreeBuffer<'a>: QueueableProvider<'a> {
+        fn try_get_free_buffer(&'a self) -> Result<Self::Queueable, GetFreeBufferError>;
+    }
+
+    impl<'a, D: Direction, P: PrimitiveBufferHandles> QueueableProvider<'a>
+        for Queue<D, BuffersAllocated<P>>
+    {
+        type Queueable = QBuffer<D, P, P, &'a Queue<D, BuffersAllocated<P>>>;
     }
 
     impl<'a, D: Direction, P: PrimitiveBufferHandles> GetBufferByIndex<'a>
         for Queue<D, BuffersAllocated<P>>
     {
-        type Queueable = QBuffer<'a, D, P, P>;
-
         // Take buffer `id` in order to prepare it for queueing, provided it is available.
         fn try_get_buffer(&'a self, index: usize) -> Result<Self::Queueable, TryGetBufferError> {
             Ok(QBuffer::new(self, self.try_obtain_buffer(index)?))
         }
     }
 
-    impl<'a, D: Direction> GetBufferByIndex<'a> for Queue<D, BuffersAllocated<GenericBufferHandles>> {
-        type Queueable = GenericQBuffer<'a, D>;
+    impl<'a, D, P, Q> QueueableProvider<'a> for Q
+    where
+        D: Direction,
+        P: PrimitiveBufferHandles,
+        Q: Deref<Target = Queue<D, BuffersAllocated<P>>> + Clone,
+    {
+        type Queueable = QBuffer<D, P, P, Q>;
+    }
 
+    /// Allows to obtain a [`QBuffer`] with a `'static` lifetime from e.g. an `Arc<Queue>`.
+    ///
+    /// [`QBuffer`]s obtained directly from a [`Queue`] maintain consistency by holding a reference
+    /// to the [`Queue`], which can be inconvenient if we need to keep the [`QBuffer`] aside for
+    /// some time. This implementation allows [`QBuffer`]s to be created with a static lifetime
+    /// from a queue behind a cloneable and dereferencable type (typically [`std::rc::Rc`] or
+    /// [`std::sync::Arc`]).
+    ///
+    /// This added flexibility comes with the counterpart that the user must unwrap the [`Queue`]
+    /// from its container reference before applying mutable operations to it like
+    /// [`Queue::request_buffers`]. Doing so requires calling methods like
+    /// [`std::sync::Arc::into_inner`], which only succeed if there is no other reference to the
+    /// queue, preserving consistency explicitly at runtime instead of implicitly at compile-time.
+    impl<'a, D, P, Q> GetBufferByIndex<'a> for Q
+    where
+        D: Direction,
+        P: PrimitiveBufferHandles,
+        Q: Deref<Target = Queue<D, BuffersAllocated<P>>> + Clone,
+    {
         fn try_get_buffer(&'a self, index: usize) -> Result<Self::Queueable, TryGetBufferError> {
-            let buffer_info = self.try_obtain_buffer(index)?;
-
-            Ok(match self.state.memory_type {
-                GenericSupportedMemoryType::Mmap => {
-                    GenericQBuffer::Mmap(QBuffer::new(self, buffer_info))
-                }
-                GenericSupportedMemoryType::UserPtr => {
-                    GenericQBuffer::User(QBuffer::new(self, buffer_info))
-                }
-                GenericSupportedMemoryType::DmaBuf => {
-                    GenericQBuffer::DmaBuf(QBuffer::new(self, buffer_info))
-                }
-            })
+            Ok(QBuffer::new(self.clone(), self.try_obtain_buffer(index)?))
         }
     }
 
@@ -571,43 +602,111 @@
         Self: GetBufferByIndex<'a>,
     {
         fn try_get_free_buffer(&'a self) -> Result<Self::Queueable, GetFreeBufferError> {
-            let res = self
-                .state
+            self.state
                 .buffer_info
                 .iter()
                 .enumerate()
-                .find(|(_, s)| s.do_with_state(|s| matches!(s, BufferState::Free)));
+                .find(|(_, s)| s.do_with_state(|s| matches!(s, BufferState::Free)))
+                .ok_or(GetFreeBufferError::NoFreeBuffer)
+                // We found a buffer with a `Free` state, so calling `try_get_buffer` on it is
+                // guaranteed to succeed.
+                .map(|(i, _)| self.try_get_buffer(i).unwrap())
+        }
+    }
 
-            match res {
-                None => Err(GetFreeBufferError::NoFreeBuffer),
-                Some((i, _)) => Ok(self.try_get_buffer(i).unwrap()),
-            }
+    /// Allows to obtain a [`QBuffer`] with a `'static` lifetime from e.g. an `Arc<Queue>`.
+    ///
+    /// [`QBuffer`]s obtained directly from a [`Queue`] maintain consistency by holding a reference
+    /// to the [`Queue`], which can be inconvenient if we need to keep the [`QBuffer`] aside for
+    /// some time. This implementation allows [`QBuffer`]s to be created with a static lifetime
+    /// from a queue behind a cloneable and dereferencable type (typically [`std::rc::Rc`] or
+    /// [`std::sync::Arc`]).
+    ///
+    /// This added flexibility comes with the counterpart that the user must unwrap the [`Queue`]
+    /// from its container reference before applying mutable operations to it like
+    /// [`Queue::request_buffers`]. Doing so requires calling methods like
+    /// [`std::sync::Arc::into_inner`], which only succeed if there is no other reference to the
+    /// queue, preserving consistency explicitly at runtime instead of implicitly at compile-time.
+    impl<'a, D, P, Q> GetFreeBuffer<'a> for Q
+    where
+        D: Direction,
+        P: PrimitiveBufferHandles,
+        Q: Deref<Target = Queue<D, BuffersAllocated<P>>> + Clone,
+    {
+        fn try_get_free_buffer(&'a self) -> Result<Self::Queueable, GetFreeBufferError> {
+            self.state
+                .buffer_info
+                .iter()
+                .enumerate()
+                .find(|(_, s)| s.do_with_state(|s| matches!(s, BufferState::Free)))
+                .ok_or(GetFreeBufferError::NoFreeBuffer)
+                // We found a buffer with a `Free` state, so calling `try_get_buffer` on it is
+                // guaranteed to succeed.
+                .map(|(i, _)| self.try_get_buffer(i).unwrap())
         }
     }
 }
 
-impl<'a, P: PrimitiveBufferHandles> CaptureQueueableProvider<'a, P>
-    for Queue<Capture, BuffersAllocated<P>>
-where
-    Self: private::GetBufferByIndex<'a>,
-    <Self as private::GetBufferByIndex<'a>>::Queueable: CaptureQueueable<P>,
-{
-    type Queueable = <Self as private::GetBufferByIndex<'a>>::Queueable;
+/// Trait for queueable CAPTURE buffers. These buffers only require handles to
+/// be queued.
+pub trait CaptureQueueable<B: BufferHandles> {
+    /// Queue the buffer after binding `handles`, consuming the object.
+    /// The number of handles must match the buffer's expected number of planes.
+    fn queue_with_handles(self, handles: B) -> QueueResult<(), B>;
 }
 
-impl<'a, P: PrimitiveBufferHandles> OutputQueueableProvider<'a, P>
-    for Queue<Output, BuffersAllocated<P>>
-where
-    Self: private::GetBufferByIndex<'a>,
-    <Self as private::GetBufferByIndex<'a>>::Queueable: OutputQueueable<P>,
-{
-    type Queueable = <Self as private::GetBufferByIndex<'a>>::Queueable;
+/// Trait for queueable OUTPUT buffers. The number of bytes used must be
+/// specified for each plane.
+pub trait OutputQueueable<B: BufferHandles> {
+    /// Queue the buffer after binding `handles`, consuming the object.
+    /// The number of handles must match the buffer's expected number of planes.
+    /// `bytes_used` must be a slice with as many slices as there are handles,
+    /// describing the amount of useful data in each of them.
+    fn queue_with_handles(self, handles: B, bytes_used: &[usize]) -> QueueResult<(), B>;
 }
 
-impl<'a, P: BufferHandles, R> GetOutputBufferByIndex<'a, P> for Queue<Output, BuffersAllocated<P>>
+/// Trait for all objects that are capable of providing objects that can be
+/// queued to the CAPTURE queue.
+pub trait CaptureQueueableProvider<'a, B: BufferHandles> {
+    type Queueable: CaptureQueueable<B>;
+}
+
+impl<'a, B, Q> CaptureQueueableProvider<'a, B> for Q
 where
-    Self: private::GetBufferByIndex<'a, Queueable = R>,
-    Self: OutputQueueableProvider<'a, P, Queueable = R>,
+    B: BufferHandles,
+    Q: private::QueueableProvider<'a>,
+    Q::Queueable: CaptureQueueable<B>,
+{
+    type Queueable = <Self as private::QueueableProvider<'a>>::Queueable;
+}
+
+/// Trait for all objects that are capable of providing objects that can be
+/// queued to the CAPTURE queue.
+pub trait OutputQueueableProvider<'a, B: BufferHandles> {
+    type Queueable: OutputQueueable<B>;
+}
+
+impl<'a, B, Q> OutputQueueableProvider<'a, B> for Q
+where
+    B: BufferHandles,
+    Q: private::QueueableProvider<'a>,
+    Q::Queueable: OutputQueueable<B>,
+{
+    type Queueable = <Self as private::QueueableProvider<'a>>::Queueable;
+}
+
+pub trait GetOutputBufferByIndex<'a, B, ErrorType = TryGetBufferError>
+where
+    B: BufferHandles,
+    Self: OutputQueueableProvider<'a, B>,
+{
+    fn try_get_buffer(&'a self, index: usize) -> Result<Self::Queueable, ErrorType>;
+}
+
+impl<'a, B: BufferHandles> GetOutputBufferByIndex<'a, B> for Queue<Output, BuffersAllocated<B>>
+where
+    Self: private::GetBufferByIndex<'a>,
+    <Self as private::QueueableProvider<'a>>::Queueable: OutputQueueable<B>,
 {
     // Take buffer `id` in order to prepare it for queueing, provided it is available.
     fn try_get_buffer(&'a self, index: usize) -> Result<Self::Queueable, TryGetBufferError> {
@@ -615,10 +714,17 @@
     }
 }
 
-impl<'a, P: BufferHandles, R> GetCaptureBufferByIndex<'a, P> for Queue<Capture, BuffersAllocated<P>>
+pub trait GetCaptureBufferByIndex<'a, P: BufferHandles, ErrorType = TryGetBufferError>
 where
-    Self: private::GetBufferByIndex<'a, Queueable = R>,
-    Self: CaptureQueueableProvider<'a, P, Queueable = R>,
+    Self: CaptureQueueableProvider<'a, P>,
+{
+    fn try_get_buffer(&'a self, index: usize) -> Result<Self::Queueable, ErrorType>;
+}
+
+impl<'a, P: BufferHandles> GetCaptureBufferByIndex<'a, P> for Queue<Capture, BuffersAllocated<P>>
+where
+    Self: private::GetBufferByIndex<'a>,
+    <Self as private::QueueableProvider<'a>>::Queueable: CaptureQueueable<P>,
 {
     // Take buffer `id` in order to prepare it for queueing, provided it is available.
     fn try_get_buffer(&'a self, index: usize) -> Result<Self::Queueable, TryGetBufferError> {
@@ -626,20 +732,34 @@
     }
 }
 
-impl<'a, P: BufferHandles, R> GetFreeOutputBuffer<'a, P> for Queue<Output, BuffersAllocated<P>>
+pub trait GetFreeOutputBuffer<'a, P: BufferHandles, ErrorType = GetFreeBufferError>
 where
-    Self: private::GetFreeBuffer<'a, Queueable = R>,
-    Self: OutputQueueableProvider<'a, P, Queueable = R>,
+    Self: OutputQueueableProvider<'a, P>,
+{
+    fn try_get_free_buffer(&'a self) -> Result<Self::Queueable, ErrorType>;
+}
+
+impl<'a, P: BufferHandles, Q> GetFreeOutputBuffer<'a, P> for Q
+where
+    Self: private::GetFreeBuffer<'a>,
+    <Self as private::QueueableProvider<'a>>::Queueable: OutputQueueable<P>,
 {
     fn try_get_free_buffer(&'a self) -> Result<Self::Queueable, GetFreeBufferError> {
         <Self as private::GetFreeBuffer<'a>>::try_get_free_buffer(self)
     }
 }
 
-impl<'a, P: BufferHandles, R> GetFreeCaptureBuffer<'a, P> for Queue<Capture, BuffersAllocated<P>>
+pub trait GetFreeCaptureBuffer<'a, P: BufferHandles, ErrorType = GetFreeBufferError>
 where
-    Self: private::GetFreeBuffer<'a, Queueable = R>,
-    Self: CaptureQueueableProvider<'a, P, Queueable = R>,
+    Self: CaptureQueueableProvider<'a, P>,
+{
+    fn try_get_free_buffer(&'a self) -> Result<Self::Queueable, ErrorType>;
+}
+
+impl<'a, P: BufferHandles, Q> GetFreeCaptureBuffer<'a, P> for Q
+where
+    Self: private::GetFreeBuffer<'a>,
+    <Self as private::QueueableProvider<'a>>::Queueable: CaptureQueueable<P>,
 {
     fn try_get_free_buffer(&'a self) -> Result<Self::Queueable, GetFreeBufferError> {
         <Self as private::GetFreeBuffer<'a>>::try_get_free_buffer(self)
diff --git a/lib/src/device/queue/generic.rs b/lib/src/device/queue/generic.rs
index cf857c3..a590fc9 100644
--- a/lib/src/device/queue/generic.rs
+++ b/lib/src/device/queue/generic.rs
@@ -1,11 +1,9 @@
 use crate::{
     device::queue::{
         direction::{Capture, Direction, Output},
-        qbuf::{
-            CaptureQueueable, CaptureQueueableProvider, OutputQueueable, OutputQueueableProvider,
-            QBuffer, QueueResult,
-        },
-        BuffersAllocated, Queue,
+        private,
+        qbuf::{QBuffer, QueueResult},
+        BuffersAllocated, CaptureQueueable, OutputQueueable, Queue, TryGetBufferError,
     },
     memory::DmaBufHandle,
 };
@@ -13,7 +11,7 @@
     memory::MmapHandle,
     memory::{BufferHandles, MemoryType, UserPtrHandle},
 };
-use std::{fmt::Debug, fs::File};
+use std::{fmt::Debug, fs::File, ops::Deref};
 
 /// Supported memory types for `GenericBufferHandles`.
 /// TODO: This should be renamed to "DynamicBufferHandles", and be constructed
@@ -84,38 +82,78 @@
 
 /// A QBuffer that holds either MMAP or UserPtr handles, depending on which
 /// memory type has been selected for the queue at runtime.
-pub enum GenericQBuffer<'a, D: Direction> {
-    Mmap(QBuffer<'a, D, Vec<MmapHandle>, GenericBufferHandles>),
-    User(QBuffer<'a, D, Vec<UserPtrHandle<Vec<u8>>>, GenericBufferHandles>),
-    DmaBuf(QBuffer<'a, D, Vec<DmaBufHandle<File>>, GenericBufferHandles>),
+pub enum GenericQBuffer<
+    D: Direction,
+    Q: Deref<Target = Queue<D, BuffersAllocated<GenericBufferHandles>>>,
+> {
+    Mmap(QBuffer<D, Vec<MmapHandle>, GenericBufferHandles, Q>),
+    User(QBuffer<D, Vec<UserPtrHandle<Vec<u8>>>, GenericBufferHandles, Q>),
+    DmaBuf(QBuffer<D, Vec<DmaBufHandle<File>>, GenericBufferHandles, Q>),
 }
 
-impl<'a, D: Direction> From<QBuffer<'a, D, Vec<MmapHandle>, GenericBufferHandles>>
-    for GenericQBuffer<'a, D>
+impl<D, Q> From<QBuffer<D, Vec<MmapHandle>, GenericBufferHandles, Q>> for GenericQBuffer<D, Q>
+where
+    D: Direction,
+    Q: Deref<Target = Queue<D, BuffersAllocated<GenericBufferHandles>>>,
 {
-    fn from(qb: QBuffer<'a, D, Vec<MmapHandle>, GenericBufferHandles>) -> Self {
+    fn from(qb: QBuffer<D, Vec<MmapHandle>, GenericBufferHandles, Q>) -> Self {
         GenericQBuffer::Mmap(qb)
     }
 }
 
-impl<'a, D: Direction> From<QBuffer<'a, D, Vec<UserPtrHandle<Vec<u8>>>, GenericBufferHandles>>
-    for GenericQBuffer<'a, D>
+impl<D, Q> From<QBuffer<D, Vec<UserPtrHandle<Vec<u8>>>, GenericBufferHandles, Q>>
+    for GenericQBuffer<D, Q>
+where
+    D: Direction,
+    Q: Deref<Target = Queue<D, BuffersAllocated<GenericBufferHandles>>>,
 {
-    fn from(qb: QBuffer<'a, D, Vec<UserPtrHandle<Vec<u8>>>, GenericBufferHandles>) -> Self {
+    fn from(qb: QBuffer<D, Vec<UserPtrHandle<Vec<u8>>>, GenericBufferHandles, Q>) -> Self {
         GenericQBuffer::User(qb)
     }
 }
 
-impl<'a, D: Direction> From<QBuffer<'a, D, Vec<DmaBufHandle<File>>, GenericBufferHandles>>
-    for GenericQBuffer<'a, D>
+impl<D, Q> From<QBuffer<D, Vec<DmaBufHandle<File>>, GenericBufferHandles, Q>>
+    for GenericQBuffer<D, Q>
+where
+    D: Direction,
+    Q: Deref<Target = Queue<D, BuffersAllocated<GenericBufferHandles>>>,
 {
-    fn from(qb: QBuffer<'a, D, Vec<DmaBufHandle<File>>, GenericBufferHandles>) -> Self {
+    fn from(qb: QBuffer<D, Vec<DmaBufHandle<File>>, GenericBufferHandles, Q>) -> Self {
         GenericQBuffer::DmaBuf(qb)
     }
 }
 
+impl<'a, D: Direction> private::QueueableProvider<'a>
+    for Queue<D, BuffersAllocated<GenericBufferHandles>>
+{
+    type Queueable = GenericQBuffer<D, &'a Self>;
+}
+
+impl<'a, D: Direction> private::GetBufferByIndex<'a>
+    for Queue<D, BuffersAllocated<GenericBufferHandles>>
+{
+    fn try_get_buffer(&'a self, index: usize) -> Result<Self::Queueable, TryGetBufferError> {
+        let buffer_info = self.try_obtain_buffer(index)?;
+
+        Ok(match self.state.memory_type {
+            GenericSupportedMemoryType::Mmap => {
+                GenericQBuffer::Mmap(QBuffer::new(self, buffer_info))
+            }
+            GenericSupportedMemoryType::UserPtr => {
+                GenericQBuffer::User(QBuffer::new(self, buffer_info))
+            }
+            GenericSupportedMemoryType::DmaBuf => {
+                GenericQBuffer::DmaBuf(QBuffer::new(self, buffer_info))
+            }
+        })
+    }
+}
+
 /// Any CAPTURE GenericQBuffer implements CaptureQueueable.
-impl CaptureQueueable<GenericBufferHandles> for GenericQBuffer<'_, Capture> {
+impl<Q> CaptureQueueable<GenericBufferHandles> for GenericQBuffer<Capture, Q>
+where
+    Q: Deref<Target = Queue<Capture, BuffersAllocated<GenericBufferHandles>>>,
+{
     fn queue_with_handles(
         self,
         handles: GenericBufferHandles,
@@ -129,7 +167,10 @@
 }
 
 /// Any OUTPUT GenericQBuffer implements OutputQueueable.
-impl OutputQueueable<GenericBufferHandles> for GenericQBuffer<'_, Output> {
+impl<Q> OutputQueueable<GenericBufferHandles> for GenericQBuffer<Output, Q>
+where
+    Q: Deref<Target = Queue<Output, BuffersAllocated<GenericBufferHandles>>>,
+{
     fn queue_with_handles(
         self,
         handles: GenericBufferHandles,
@@ -142,15 +183,3 @@
         }
     }
 }
-
-impl<'a> CaptureQueueableProvider<'a, GenericBufferHandles>
-    for Queue<Capture, BuffersAllocated<GenericBufferHandles>>
-{
-    type Queueable = GenericQBuffer<'a, Capture>;
-}
-
-impl<'a> OutputQueueableProvider<'a, GenericBufferHandles>
-    for Queue<Output, BuffersAllocated<GenericBufferHandles>>
-{
-    type Queueable = GenericQBuffer<'a, Output>;
-}
diff --git a/lib/src/device/queue/handles_provider.rs b/lib/src/device/queue/handles_provider.rs
index f796884..148864d 100644
--- a/lib/src/device/queue/handles_provider.rs
+++ b/lib/src/device/queue/handles_provider.rs
@@ -7,6 +7,9 @@
 
 use log::error;
 
+use crate::device::queue::{
+    GetCaptureBufferByIndex, GetFreeBufferError, GetFreeCaptureBuffer, TryGetBufferError,
+};
 use crate::{
     bindings,
     device::poller::Waker,
@@ -16,12 +19,6 @@
 
 use thiserror::Error;
 
-use super::qbuf::{
-    get_free::{GetFreeBufferError, GetFreeCaptureBuffer},
-    get_indexed::{GetCaptureBufferByIndex, TryGetBufferError},
-    CaptureQueueableProvider,
-};
-
 #[derive(Debug, Error)]
 pub enum GetSuitableBufferError {
     #[error("error while calling try_get_free_buffer(): {0}")]
@@ -42,10 +39,7 @@
         &self,
         _handles: &Self::HandleType,
         queue: &'a Q,
-    ) -> Result<
-        <Q as CaptureQueueableProvider<'a, Self::HandleType>>::Queueable,
-        GetSuitableBufferError,
-    >
+    ) -> Result<Q::Queueable, GetSuitableBufferError>
     where
         Q: GetCaptureBufferByIndex<'a, Self::HandleType>
             + GetFreeCaptureBuffer<'a, Self::HandleType>,
@@ -66,10 +60,7 @@
         &self,
         handles: &Self::HandleType,
         queue: &'a Q,
-    ) -> Result<
-        <Q as CaptureQueueableProvider<'a, Self::HandleType>>::Queueable,
-        GetSuitableBufferError,
-    >
+    ) -> Result<Q::Queueable, GetSuitableBufferError>
     where
         Q: GetCaptureBufferByIndex<'a, Self::HandleType>
             + GetFreeCaptureBuffer<'a, Self::HandleType>,
@@ -89,10 +80,7 @@
         &self,
         handles: &Self::HandleType,
         queue: &'a Q,
-    ) -> Result<
-        <Q as CaptureQueueableProvider<'a, Self::HandleType>>::Queueable,
-        GetSuitableBufferError,
-    >
+    ) -> Result<Q::Queueable, GetSuitableBufferError>
     where
         Q: GetCaptureBufferByIndex<'a, Self::HandleType>
             + GetFreeCaptureBuffer<'a, Self::HandleType>,
diff --git a/lib/src/device/queue/qbuf.rs b/lib/src/device/queue/qbuf.rs
index cea7c37..58295ff 100644
--- a/lib/src/device/queue/qbuf.rs
+++ b/lib/src/device/queue/qbuf.rs
@@ -1,9 +1,12 @@
 //! Provides types related to queuing buffers on a `Queue` object.
-use super::{buffer::BufferInfo, Capture, Direction, Output};
-use super::{BufferState, BufferStateFuse, BuffersAllocated, Queue};
+use crate::device::queue::{
+    buffer::BufferInfo, BufferState, BufferStateFuse, BuffersAllocated, Capture, CaptureQueueable,
+    Direction, Output, OutputQueueable, Queue,
+};
 use crate::ioctl::{self, QBufIoctlError, QBufResult};
 use crate::memory::*;
 use std::convert::Infallible;
+use std::ops::Deref;
 use std::{
     fmt::{self, Debug},
     os::fd::RawFd,
@@ -13,26 +16,23 @@
 use nix::sys::time::{TimeVal, TimeValLike};
 use thiserror::Error;
 
-pub mod get_free;
-pub mod get_indexed;
-
 /// Error that can occur when queuing a buffer. It wraps a regular error and also
 /// returns the plane handles back to the user.
 #[derive(Error)]
 #[error("{}", self.error)]
-pub struct QueueError<P: BufferHandles> {
+pub struct QueueError<B: BufferHandles> {
     pub error: ioctl::QBufError<Infallible>,
-    pub plane_handles: P,
+    pub plane_handles: B,
 }
 
-impl<P: BufferHandles> Debug for QueueError<P> {
+impl<B: BufferHandles> Debug for QueueError<B> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         Debug::fmt(&self.error, f)
     }
 }
 
 #[allow(type_alias_bounds)]
-pub type QueueResult<R, P: BufferHandles> = std::result::Result<R, QueueError<P>>;
+pub type QueueResult<R, B: BufferHandles> = std::result::Result<R, QueueError<B>>;
 
 /// A free buffer that has just been obtained from `Queue::get_buffer()` and
 /// which is being prepared to the queued.
@@ -62,21 +62,29 @@
 /// queue or device cannot be changed while it is being used. Contrary to
 /// DQBuffer which can be freely duplicated and passed around, instances of this
 /// struct are supposed to be short-lived.
-pub struct QBuffer<'a, D: Direction, P: PrimitiveBufferHandles, Q: BufferHandles + From<P>> {
-    queue: &'a Queue<D, BuffersAllocated<Q>>,
+pub struct QBuffer<
+    D: Direction,
+    P: PrimitiveBufferHandles,
+    B: BufferHandles + From<P>,
+    Q: Deref<Target = Queue<D, BuffersAllocated<B>>>,
+> {
+    queue: Q,
     index: usize,
     num_planes: usize,
     timestamp: TimeVal,
     request: Option<RawFd>,
-    fuse: BufferStateFuse<Q>,
+    fuse: BufferStateFuse<B>,
     _p: std::marker::PhantomData<P>,
 }
 
-impl<'a, D: Direction, P: PrimitiveBufferHandles, Q: BufferHandles + From<P>> QBuffer<'a, D, P, Q> {
-    pub(super) fn new(
-        queue: &'a Queue<D, BuffersAllocated<Q>>,
-        buffer_info: &Arc<BufferInfo<Q>>,
-    ) -> Self {
+impl<D, P, B, Q> QBuffer<D, P, B, Q>
+where
+    D: Direction,
+    P: PrimitiveBufferHandles,
+    B: BufferHandles + From<P>,
+    Q: Deref<Target = Queue<D, BuffersAllocated<B>>>,
+{
+    pub(super) fn new(queue: Q, buffer_info: &Arc<BufferInfo<B>>) -> Self {
         let buffer = &buffer_info.features;
         let fuse = BufferStateFuse::new(Arc::downgrade(buffer_info));
 
@@ -116,7 +124,7 @@
     // Caller is responsible for making sure that the number of planes and
     // plane_handles is the same as the number of expected planes for this
     // buffer.
-    fn queue_bound_planes<R: BufferHandles + Into<Q>>(
+    fn queue_bound_planes<R: BufferHandles + Into<B>>(
         mut self,
         planes: Vec<ioctl::QBufPlane>,
         plane_handles: R,
@@ -155,11 +163,12 @@
     }
 }
 
-impl<'a, P, Q> QBuffer<'a, Output, P, Q>
+impl<P, B, Q> QBuffer<Output, P, B, Q>
 where
     P: PrimitiveBufferHandles,
     P::HandleType: Mappable,
-    Q: BufferHandles + From<P>,
+    B: BufferHandles + From<P>,
+    Q: Deref<Target = Queue<Output, BuffersAllocated<B>>>,
 {
     pub fn get_plane_mapping(&self, plane: usize) -> Option<ioctl::PlaneMapping> {
         let buffer_info = self.queue.state.buffer_info.get(self.index)?;
@@ -168,41 +177,14 @@
     }
 }
 
-/// Trait for queueable CAPTURE buffers. These buffers only require handles to
-/// be queued.
-pub trait CaptureQueueable<Q: BufferHandles> {
-    /// Queue the buffer after binding `handles`, consuming the object.
-    /// The number of handles must match the buffer's expected number of planes.
-    fn queue_with_handles(self, handles: Q) -> QueueResult<(), Q>;
-}
-
-/// Trait for queueable OUTPUT buffers. The number of bytes used must be
-/// specified for each plane.
-pub trait OutputQueueable<Q: BufferHandles> {
-    /// Queue the buffer after binding `handles`, consuming the object.
-    /// The number of handles must match the buffer's expected number of planes.
-    /// `bytes_used` must be a slice with as many slices as there are handles,
-    /// describing the amount of useful data in each of them.
-    fn queue_with_handles(self, handles: Q, bytes_used: &[usize]) -> QueueResult<(), Q>;
-}
-
-/// Trait for all objects that are capable of providing objects that can be
-/// queued to the CAPTURE queue.
-pub trait CaptureQueueableProvider<'a, Q: BufferHandles> {
-    type Queueable: 'a + CaptureQueueable<Q>;
-}
-
-/// Trait for all objects that are capable of providing objects that can be
-/// queued to the CAPTURE queue.
-pub trait OutputQueueableProvider<'a, Q: BufferHandles> {
-    type Queueable: 'a + OutputQueueable<Q>;
-}
-
 /// Any CAPTURE QBuffer implements CaptureQueueable.
-impl<P: PrimitiveBufferHandles, Q: BufferHandles + From<P>> CaptureQueueable<Q>
-    for QBuffer<'_, Capture, P, Q>
+impl<P, B, Q> CaptureQueueable<B> for QBuffer<Capture, P, B, Q>
+where
+    P: PrimitiveBufferHandles,
+    B: BufferHandles + From<P>,
+    Q: Deref<Target = Queue<Capture, BuffersAllocated<B>>>,
 {
-    fn queue_with_handles(self, handles: Q) -> QueueResult<(), Q> {
+    fn queue_with_handles(self, handles: B) -> QueueResult<(), B> {
         if handles.len() != self.num_expected_planes() {
             return Err(QueueError {
                 error: QBufIoctlError::NumPlanesMismatch(handles.len(), self.num_expected_planes())
@@ -211,8 +193,8 @@
             });
         }
 
-        // TODO BufferHandles should have a method returning the actual MEMORY_TYPE implemented? So we can check
-        // that it matches with P.
+        // TODO: BufferHandles should have a method returning the actual MEMORY_TYPE implemented?
+        // So we can check that it matches with P.
 
         let planes: Vec<_> = (0..self.num_expected_planes())
             .map(|i| {
@@ -227,10 +209,13 @@
 }
 
 /// Any OUTPUT QBuffer implements OutputQueueable.
-impl<P: PrimitiveBufferHandles, Q: BufferHandles + From<P>> OutputQueueable<Q>
-    for QBuffer<'_, Output, P, Q>
+impl<P, B, Q> OutputQueueable<B> for QBuffer<Output, P, B, Q>
+where
+    P: PrimitiveBufferHandles,
+    B: BufferHandles + From<P>,
+    Q: Deref<Target = Queue<Output, BuffersAllocated<B>>>,
 {
-    fn queue_with_handles(self, handles: Q, bytes_used: &[usize]) -> QueueResult<(), Q> {
+    fn queue_with_handles(self, handles: B, bytes_used: &[usize]) -> QueueResult<(), B> {
         if handles.len() != self.num_expected_planes() {
             return Err(QueueError {
                 error: QBufIoctlError::NumPlanesMismatch(handles.len(), self.num_expected_planes())
@@ -251,8 +236,8 @@
             });
         }
 
-        // TODO BufferHandles should have a method returning the actual MEMORY_TYPE implemented? So we can check
-        // that it matches with P.
+        // TODO: BufferHandles should have a method returning the actual MEMORY_TYPE implemented?
+        // So we can check that it matches with P.
 
         let planes: Vec<_> = bytes_used
             .iter()
@@ -272,9 +257,12 @@
 /// empty handles.
 /// Since we don't receive plane handles, we also don't need to return any, so
 /// the returned error can be simplified.
-impl<P: PrimitiveBufferHandles + Default, Q: BufferHandles + From<P>> QBuffer<'_, Capture, P, Q>
+impl<P, B, Q> QBuffer<Capture, P, B, Q>
 where
+    P: PrimitiveBufferHandles + Default,
     <P::HandleType as PlaneHandle>::Memory: SelfBacked,
+    B: BufferHandles + From<P>,
+    Q: Deref<Target = Queue<Capture, BuffersAllocated<B>>>,
 {
     pub fn queue(self) -> QBufResult<(), Infallible> {
         let planes: Vec<_> = (0..self.num_expected_planes())
@@ -290,9 +278,12 @@
 /// empty handles.
 /// Since we don't receive plane handles, we also don't need to return any, so
 /// the returned error can be simplified.
-impl<P: PrimitiveBufferHandles + Default, Q: BufferHandles + From<P>> QBuffer<'_, Output, P, Q>
+impl<P, B, Q> QBuffer<Output, P, B, Q>
 where
     <P::HandleType as PlaneHandle>::Memory: SelfBacked,
+    P: PrimitiveBufferHandles + Default,
+    B: BufferHandles + From<P>,
+    Q: Deref<Target = Queue<Output, BuffersAllocated<B>>>,
 {
     pub fn queue(self, bytes_used: &[usize]) -> QBufResult<(), Infallible> {
         // TODO make specific error for bytes_used?
diff --git a/lib/src/device/queue/qbuf/get_free.rs b/lib/src/device/queue/qbuf/get_free.rs
deleted file mode 100644
index f037912..0000000
--- a/lib/src/device/queue/qbuf/get_free.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-//! Traits for buffers providers with their own allocation policy. Users of this
-//! interface leave the choice of which buffer to return to the implementor,
-//! which must define its own allocation policy.
-//!
-//! The returned buffer shall not outlive the object that produced it.
-
-use thiserror::Error;
-
-use crate::memory::BufferHandles;
-
-use super::{CaptureQueueableProvider, OutputQueueableProvider};
-
-#[derive(Debug, Error)]
-pub enum GetFreeBufferError {
-    #[error("all buffers are currently being used")]
-    NoFreeBuffer,
-}
-
-pub trait GetFreeOutputBuffer<'a, P: BufferHandles, ErrorType = GetFreeBufferError>
-where
-    Self: OutputQueueableProvider<'a, P>,
-{
-    fn try_get_free_buffer(&'a self) -> Result<Self::Queueable, ErrorType>;
-}
-
-pub trait GetFreeCaptureBuffer<'a, P: BufferHandles, ErrorType = GetFreeBufferError>
-where
-    Self: CaptureQueueableProvider<'a, P>,
-{
-    fn try_get_free_buffer(&'a self) -> Result<Self::Queueable, ErrorType>;
-}
diff --git a/lib/src/device/queue/qbuf/get_indexed.rs b/lib/src/device/queue/qbuf/get_indexed.rs
deleted file mode 100644
index 073f511..0000000
--- a/lib/src/device/queue/qbuf/get_indexed.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-//! Traits for trying to obtain a queueable, writable buffer from its index.
-//!
-//! `try_get_buffer()` returns the buffer with specified `index`, provided that
-//! this buffer is currently available for use.
-//!
-//! The returned buffer shall not outlive the object that produced it.
-
-use thiserror::Error;
-
-use crate::memory::BufferHandles;
-
-use super::{CaptureQueueableProvider, OutputQueueableProvider};
-
-#[derive(Debug, Error)]
-pub enum TryGetBufferError {
-    #[error("buffer with provided index {0} does not exist")]
-    InvalidIndex(usize),
-    #[error("buffer is already in use")]
-    AlreadyUsed,
-}
-
-pub trait GetOutputBufferByIndex<'a, P: BufferHandles, ErrorType = TryGetBufferError>
-where
-    Self: OutputQueueableProvider<'a, P>,
-{
-    fn try_get_buffer(&'a self, index: usize) -> Result<Self::Queueable, ErrorType>;
-}
-
-pub trait GetCaptureBufferByIndex<'a, P: BufferHandles, ErrorType = TryGetBufferError>
-where
-    Self: CaptureQueueableProvider<'a, P>,
-{
-    fn try_get_buffer(&'a self, index: usize) -> Result<Self::Queueable, ErrorType>;
-}
diff --git a/lib/src/encoder.rs b/lib/src/encoder.rs
index 86c6be3..0286f2f 100644
--- a/lib/src/encoder.rs
+++ b/lib/src/encoder.rs
@@ -7,13 +7,9 @@
             direction::{Capture, Output},
             dqbuf::DqBuffer,
             handles_provider::HandlesProvider,
-            qbuf::{
-                get_free::{GetFreeBufferError, GetFreeCaptureBuffer, GetFreeOutputBuffer},
-                get_indexed::GetCaptureBufferByIndex,
-                CaptureQueueable, OutputQueueableProvider,
-            },
-            BuffersAllocated, CanceledBuffer, CreateQueueError, FormatBuilder, Queue, QueueInit,
-            RequestBuffersError,
+            BuffersAllocated, CanceledBuffer, CaptureQueueable, CreateQueueError, FormatBuilder,
+            GetCaptureBufferByIndex, GetFreeBufferError, GetFreeCaptureBuffer, GetFreeOutputBuffer,
+            OutputQueueableProvider, Queue, QueueInit, RequestBuffersError,
         },
         AllocatedQueue, Device, DeviceConfig, DeviceOpenError, Stream, TryDequeue,
     },
@@ -185,11 +181,7 @@
         memory_type: <P::HandleType as BufferHandles>::SupportedMemoryType,
         num_capture: usize,
         capture_memory_provider: P,
-    ) -> Result<Encoder<ReadyToEncode<OP, P>>, RequestBuffersError>
-    where
-        for<'a> Queue<Capture, BuffersAllocated<P::HandleType>>:
-            GetFreeCaptureBuffer<'a, P::HandleType>,
-    {
+    ) -> Result<Encoder<ReadyToEncode<OP, P>>, RequestBuffersError> {
         Ok(Encoder {
             device: self.device,
             state: ReadyToEncode {
@@ -211,8 +203,6 @@
     ) -> Result<Encoder<ReadyToEncode<OP, P>>, RequestBuffersError>
     where
         P::HandleType: PrimitiveBufferHandles,
-        for<'a> Queue<Capture, BuffersAllocated<P::HandleType>>:
-            GetFreeCaptureBuffer<'a, P::HandleType>,
     {
         self.allocate_capture_buffers_generic(
             P::HandleType::MEMORY_TYPE,
diff --git a/lib/src/ioctl.rs b/lib/src/ioctl.rs
index 7ab697f..c76b925 100644
--- a/lib/src/ioctl.rs
+++ b/lib/src/ioctl.rs
@@ -916,6 +916,12 @@
     UnknownQueueType(u32),
     #[error("unknown memory type {0}")]
     UnknownMemoryType(u32),
+    #[error("invalid number of planes {0}")]
+    InvalidNumberOfPlanes(u32),
+    #[error("plane {0} has bytesused field larger than its length ({1} > {2})")]
+    PlaneSizeOverflow(usize, u32, u32),
+    #[error("plane {0} has data_offset field larger or equal to its bytesused ({1} >= {2})")]
+    InvalidDataOffset(usize, u32, u32),
 }
 
 impl TryFrom<UncheckedV4l2Buffer> for V4l2Buffer {
@@ -925,15 +931,53 @@
     /// fail.
     fn try_from(buffer: UncheckedV4l2Buffer) -> Result<Self, Self::Error> {
         let v4l2_buf = buffer.0;
-        let v4l2_planes = buffer.1;
-        QueueType::n(v4l2_buf.type_)
+        let queue = QueueType::n(v4l2_buf.type_)
             .ok_or(V4l2BufferFromError::UnknownQueueType(v4l2_buf.type_))?;
         MemoryType::n(v4l2_buf.memory)
             .ok_or(V4l2BufferFromError::UnknownMemoryType(v4l2_buf.memory))?;
 
+        let v4l2_planes = buffer.1.unwrap_or_default();
+
+        // Validate plane information
+        if queue.is_multiplanar() {
+            if v4l2_buf.length >= bindings::VIDEO_MAX_PLANES {
+                return Err(V4l2BufferFromError::InvalidNumberOfPlanes(v4l2_buf.length));
+            }
+
+            for (i, plane) in v4l2_planes[0..v4l2_buf.length as usize].iter().enumerate() {
+                if plane.bytesused > plane.length {
+                    return Err(V4l2BufferFromError::PlaneSizeOverflow(
+                        i,
+                        plane.bytesused,
+                        plane.length,
+                    ));
+                }
+
+                let bytesused = if plane.bytesused != 0 {
+                    plane.bytesused
+                } else {
+                    plane.length
+                };
+
+                if plane.data_offset != 0 && plane.data_offset >= bytesused {
+                    return Err(V4l2BufferFromError::InvalidDataOffset(
+                        i,
+                        plane.data_offset,
+                        bytesused,
+                    ));
+                }
+            }
+        } else if v4l2_buf.bytesused > v4l2_buf.length {
+            return Err(V4l2BufferFromError::PlaneSizeOverflow(
+                0,
+                v4l2_buf.bytesused,
+                v4l2_buf.length,
+            ));
+        }
+
         Ok(Self {
             buffer: v4l2_buf,
-            planes: v4l2_planes.unwrap_or_default(),
+            planes: v4l2_planes,
         })
     }
 }
diff --git a/lib/src/memory.rs b/lib/src/memory.rs
index 0a4999c..de15657 100644
--- a/lib/src/memory.rs
+++ b/lib/src/memory.rs
@@ -41,8 +41,8 @@
     ioctl::{PlaneMapping, QueryBufPlane},
 };
 use enumn::N;
-use std::fmt::Debug;
 use std::os::unix::io::AsFd;
+use std::{fmt::Debug, ops::Deref};
 
 /// All the supported V4L2 memory types.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, N)]
@@ -142,17 +142,22 @@
     }
 }
 
-/// Implementation of `BufferHandles` for all vectors of `PlaneHandle`. This is
-/// The simplest way to use primitive handles.
-impl<P: PlaneHandle> BufferHandles for Vec<P> {
+/// Implementation of `BufferHandles` for all indexables of `PlaneHandle` (e.g. [`std::vec::Vec`]).
+///
+/// This is The simplest way to use primitive handles.
+impl<P, Q> BufferHandles for Q
+where
+    P: PlaneHandle,
+    Q: Send + Debug + 'static + Deref<Target = [P]>,
+{
     type SupportedMemoryType = MemoryType;
 
     fn len(&self) -> usize {
-        self.len()
+        self.deref().len()
     }
 
     fn fill_v4l2_plane(&self, index: usize, plane: &mut bindings::v4l2_plane) {
-        self[index].fill_v4l2_plane(plane);
+        self.deref()[index].fill_v4l2_plane(plane);
     }
 }
 
@@ -163,8 +168,13 @@
     const MEMORY_TYPE: Self::SupportedMemoryType;
 }
 
-/// Implementation of `PrimitiveBufferHandles` for all vectors of `PlaneHandle`.
-impl<P: PlaneHandle> PrimitiveBufferHandles for Vec<P> {
+/// Implementation of `PrimitiveBufferHandles` for all indexables of `PlaneHandle` (e.g.
+/// [`std::vec::Vec`]).
+impl<P, Q> PrimitiveBufferHandles for Q
+where
+    P: PlaneHandle,
+    Q: Send + Debug + 'static + Deref<Target = [P]>,
+{
     type HandleType = P;
     const MEMORY_TYPE: Self::SupportedMemoryType = P::Memory::MEMORY_TYPE;
 }