ioctl: revamp struct V4l2Buffer

Change the way struct V4l2Buffer works in order to (hopefully) make it
safer.

The invariants that this structure is supposed to maintain are now
better defined, and it is possible to obtain a valid pointer to the
inner v4l2_buffer with a valid planes pointer (if the buffer is
multiplanar).

Plane data is not accessed through queue-agnostic accessors, that
operate the same whether the buffer is single-planar or not.

Documentation for V4l2Buffer is better, albeit still incomplete.

There is still some work to be done, but this looks like a step in the
right direction.
diff --git a/ffi/src/decoder.rs b/ffi/src/decoder.rs
index c462b57..7521539 100644
--- a/ffi/src/decoder.rs
+++ b/ffi/src/decoder.rs
@@ -197,14 +197,14 @@
         dqbuf.data.index(),
         dqbuf.data.flags(),
     );
-    let v4l2_data = dqbuf.data.clone();
+    let mut v4l2_data = dqbuf.data.clone();
     // Drop the DQBuffer early so the C callback can reuse the V4L2
     // buffer if it needs to.
     drop(dqbuf);
 
     // Immediately recycle empty frames. We will pass the corresponding
     // event to the client.
-    if v4l2_data.get_first_plane().bytesused() == 0 {
+    if *v4l2_data.get_first_plane().bytesused == 0 {
         debug!(
             "Immediately recycling zero-sized frame {} {}",
             frame.id,
@@ -225,7 +225,7 @@
         event_cb(
             cb_data,
             &mut v4l2r_decoder_event::FrameDecoded(v4l2r_decoder_frame_decoded_event {
-                buffer: v4l2_data.v4l2_buffer() as *const bindings::v4l2_buffer,
+                buffer: v4l2_data.as_mut_ptr() as *const _,
                 frame,
             }),
         );
@@ -328,10 +328,10 @@
         Box::new(
             move |buf: CompletedInputBuffer<Vec<DmaBufHandle<DmaBufFd>>>| {
                 match buf {
-                    CompletedInputBuffer::Dequeued(dqbuf) => {
+                    CompletedInputBuffer::Dequeued(mut dqbuf) => {
                         debug!("Input buffer {} done", dqbuf.data.index());
                         // TODO check return value?
-                        input_done_cb(cb_data.0, dqbuf.data.v4l2_buffer() as *const bindings::v4l2_buffer);
+                        input_done_cb(cb_data.0, dqbuf.data.as_mut_ptr() as *const _);
                     }
                     // Just drop canceled buffers for now - the client will remove
                     // them on its side as well.
diff --git a/lib/examples/fwht_encoder/main.rs b/lib/examples/fwht_encoder/main.rs
index e2aa5a6..77cecb5 100644
--- a/lib/examples/fwht_encoder/main.rs
+++ b/lib/examples/fwht_encoder/main.rs
@@ -215,7 +215,7 @@
     let poll_count_writer = Arc::clone(&poll_count_reader);
     let mut frame_counter = 0usize;
     let output_ready_cb = move |cap_dqbuf: DqBuffer<Capture, Vec<MmapHandle>>| {
-        let bytes_used = cap_dqbuf.data.get_first_plane().bytesused() as usize;
+        let bytes_used = *cap_dqbuf.data.get_first_plane().bytesused as usize;
         // Ignore zero-sized buffers.
         if bytes_used == 0 {
             return;
diff --git a/lib/examples/simple_decoder/main.rs b/lib/examples/simple_decoder/main.rs
index ab39e14..50f6a59 100644
--- a/lib/examples/simple_decoder/main.rs
+++ b/lib/examples/simple_decoder/main.rs
@@ -109,7 +109,7 @@
     let start_time = std::time::Instant::now();
     let mut frame_counter = 0usize;
     let mut output_ready_cb = move |cap_dqbuf: DqBuffer<Capture, Vec<MmapHandle>>| {
-        let bytes_used = cap_dqbuf.data.get_first_plane().bytesused() as usize;
+        let bytes_used = *cap_dqbuf.data.get_first_plane().bytesused as usize;
         // Ignore zero-sized buffers.
         if bytes_used == 0 {
             return;
diff --git a/lib/examples/vicodec_test/device_api.rs b/lib/examples/vicodec_test/device_api.rs
index 24ca5ba..5ede889 100644
--- a/lib/examples/vicodec_test/device_api.rs
+++ b/lib/examples/vicodec_test/device_api.rs
@@ -241,7 +241,7 @@
             .try_dequeue()
             .expect("Failed to dequeue capture buffer");
         let cap_index = cap_dqbuf.data.index() as usize;
-        let bytes_used = cap_dqbuf.data.get_first_plane().bytesused() as usize;
+        let bytes_used = *cap_dqbuf.data.get_first_plane().bytesused as usize;
 
         total_size = total_size.wrapping_add(bytes_used);
         let elapsed = start_time.elapsed();
diff --git a/lib/examples/vicodec_test/ioctl_api.rs b/lib/examples/vicodec_test/ioctl_api.rs
index 97ae435..9a8de2b 100644
--- a/lib/examples/vicodec_test/ioctl_api.rs
+++ b/lib/examples/vicodec_test/ioctl_api.rs
@@ -251,7 +251,7 @@
         // The CAPTURE buffer, on the other hand, we want to examine more closely.
         let cap_dqbuf: V4l2Buffer =
             dqbuf(&fd, capture_queue).expect("Failed to dequeue capture buffer");
-        let bytes_used = cap_dqbuf.get_first_plane().bytesused() as usize;
+        let bytes_used = *cap_dqbuf.get_first_plane().bytesused as usize;
 
         total_size = total_size.wrapping_add(bytes_used);
         let elapsed = start_time.elapsed();
diff --git a/lib/src/device/queue/dqbuf.rs b/lib/src/device/queue/dqbuf.rs
index 01bac86..5db1346 100644
--- a/lib/src/device/queue/dqbuf.rs
+++ b/lib/src/device/queue/dqbuf.rs
@@ -86,12 +86,12 @@
         // We can only obtain a mapping if this buffer has not been deleted.
         let buffer_info = self.buffer_info.upgrade()?;
         let plane = buffer_info.features.planes.get(plane_index)?;
-        let plane_data = self.data.get_plane(plane_index)?;
+        let plane_data = self.data.planes_iter().nth(plane_index)?;
         // If the buffer info was alive, then the device must also be.
         let device = self.device.upgrade()?;
 
-        let start = plane_data.data_offset() as usize;
-        let end = start + plane_data.bytesused() as usize;
+        let start = *plane_data.data_offset.unwrap_or(&0) as usize;
+        let end = start + *plane_data.bytesused as usize;
 
         Some(P::HandleType::map(device.as_ref(), plane)?.restrict(start, end))
     }
diff --git a/lib/src/encoder.rs b/lib/src/encoder.rs
index f322d25..4741296 100644
--- a/lib/src/encoder.rs
+++ b/lib/src/encoder.rs
@@ -553,7 +553,7 @@
                         // TODO Manage errors here, including corrupted buffers!
                         if let Ok(mut cap_buf) = self.capture_queue.try_dequeue() {
                             let is_last = cap_buf.data.is_last();
-                            let is_empty = cap_buf.data.get_first_plane().bytesused() == 0;
+                            let is_empty = *cap_buf.data.get_first_plane().bytesused == 0;
 
                             // Add a drop callback to the dequeued buffer so we
                             // re-queue it as soon as it is dropped.
diff --git a/lib/src/ioctl.rs b/lib/src/ioctl.rs
index ae478ab..769d53d 100644
--- a/lib/src/ioctl.rs
+++ b/lib/src/ioctl.rs
@@ -93,13 +93,19 @@
 use std::ffi::CStr;
 use std::ffi::FromBytesWithNulError;
 use std::fmt::Debug;
+use std::ops::Deref;
+use std::ops::DerefMut;
 
 use bitflags::bitflags;
 use enumn::N;
 use nix::errno::Errno;
 
 use crate::bindings;
+use crate::memory::DmaBuf;
+use crate::memory::Memory;
 use crate::memory::MemoryType;
+use crate::memory::Mmap;
+use crate::memory::UserPtr;
 use crate::QueueType;
 
 /// Utility function for sub-modules.
@@ -178,12 +184,10 @@
 type V4l2BufferPlanes = [bindings::v4l2_plane; bindings::VIDEO_MAX_PLANES as usize];
 
 /// Information about a single plane of a V4L2 buffer.
-pub struct V4l2BufferPlane<'a> {
-    buffer: &'a bindings::v4l2_buffer,
-    plane: &'a bindings::v4l2_plane,
-}
+#[repr(transparent)]
+pub struct V4l2BufferPlane(bindings::v4l2_plane);
 
-impl<'a> Debug for V4l2BufferPlane<'a> {
+impl Debug for V4l2BufferPlane {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         f.debug_struct("V4l2BufferPlane")
             .field("length", &self.length())
@@ -193,30 +197,29 @@
     }
 }
 
-impl<'a> V4l2BufferPlane<'a> {
+impl V4l2BufferPlane {
     pub fn length(&self) -> u32 {
-        self.plane.length
+        self.0.length
+    }
+
+    pub fn set_length(&mut self, length: u32) {
+        self.0.length = length;
     }
 
     pub fn bytesused(&self) -> u32 {
-        self.plane.bytesused
+        self.0.bytesused
     }
 
-    /// Returns the memory offset of this plane if the buffer's type is MMAP.
-    pub fn mem_offset(&self) -> Option<u32> {
-        if MemoryType::n(self.buffer.memory) == Some(MemoryType::Mmap) {
-            // Safe because we are returning a u32 in any case. It may be garbage, but will just
-            // lead to a runtime error down the road.
-            // Additionally we checked that the memory type of the buffer was MMAP, so the value
-            // should be valid.
-            Some(unsafe { self.plane.m.mem_offset })
-        } else {
-            None
-        }
+    pub fn set_bytesused(&mut self, bytesused: u32) {
+        self.0.bytesused = bytesused;
     }
 
     pub fn data_offset(&self) -> u32 {
-        self.plane.data_offset
+        self.0.data_offset
+    }
+
+    pub fn set_data_offset(&mut self, data_offset: u32) {
+        self.0.data_offset = data_offset;
     }
 }
 
@@ -256,11 +259,30 @@
     InterlacedTb = bindings::v4l2_field_V4L2_FIELD_INTERLACED_TB,
     InterlacedBt = bindings::v4l2_field_V4L2_FIELD_INTERLACED_BT,
 }
-/// A completely owned v4l2_buffer, where the pointer to planes is meaningless and fixed up when
-/// needed.
+
+/// Safe-ish representation of a `struct v4l2_buffer`. It owns its own planes array and can only be
+/// constructed from valid data.
 ///
-/// If the buffer is single-planar, it is modified to use `planes` anyway for the information of
-/// its unique plane.
+/// This structure guarantees the following invariants:
+///
+/// * The buffer's queue type is valid and cannot change,
+/// * The buffer's memory type is valid and cannot change,
+/// * The memory backing (MMAP offset/user pointer/DMABUF) can only be read and set according to
+///   the memory type of the buffer. I.e. it is impossible to mistakenly set `fd` unless the
+///   buffer's memory type is `DMABUF`.
+///
+/// Planes management is a bit complicated due to the existence of the single-planar and a
+/// multi-planar buffer representations. There are situations where one wants to access plane
+/// information regardless of the representation used, and others where one wants to access the
+/// actual array of `struct v4l2_plane`, provided it exists.
+///
+/// For the first situation, use the `planes_iter` and `planes_iter_mut` methods. They return an
+/// iterator to an accessor to plane data that is identical whether the buffer is single or multi
+/// planar (or course, for single-planar buffers the length of the iterator will be exactly 1).
+///
+/// For the second situation, the `as_v4l2_planes` method returns an actual slice of `struct
+/// v4l2_plane` with the plane information if the buffer is multi-planar (and an empty slice if the
+/// it is single-planar).
 #[derive(Clone)]
 #[repr(C)]
 pub struct V4l2Buffer {
@@ -283,6 +305,24 @@
 }
 
 impl V4l2Buffer {
+    pub fn new(queue: QueueType, index: u32, memory: MemoryType) -> Self {
+        Self {
+            buffer: bindings::v4l2_buffer {
+                index,
+                type_: queue as u32,
+                memory: memory as u32,
+                // Make sure that a multiplanar buffer always has at least one plane.
+                length: if queue.is_multiplanar() {
+                    1
+                } else {
+                    Default::default()
+                },
+                ..Default::default()
+            },
+            planes: Default::default(),
+        }
+    }
+
     pub fn index(&self) -> u32 {
         self.buffer.index
     }
@@ -299,6 +339,10 @@
         BufferFlags::from_bits_truncate(self.buffer.flags)
     }
 
+    pub fn set_flags(&mut self, flags: BufferFlags) {
+        self.buffer.flags = flags.bits();
+    }
+
     pub fn is_last(&self) -> bool {
         self.flags().contains(BufferFlags::LAST)
     }
@@ -307,10 +351,18 @@
         self.buffer.timestamp
     }
 
+    pub fn set_timestamp(&mut self, timestamp: bindings::timeval) {
+        self.buffer.timestamp = timestamp;
+    }
+
     pub fn sequence(&self) -> u32 {
         self.buffer.sequence
     }
 
+    pub fn set_sequence(&mut self, sequence: u32) {
+        self.buffer.sequence = sequence;
+    }
+
     pub fn num_planes(&self) -> usize {
         if self.queue_type().is_multiplanar() {
             self.buffer.length as usize
@@ -321,35 +373,417 @@
 
     /// Returns the first plane of the buffer. This method is guaranteed to
     /// succeed because every buffer has at least one plane.
-    pub fn get_first_plane(&self) -> V4l2BufferPlane {
-        V4l2BufferPlane {
-            buffer: &self.buffer,
-            plane: &self.planes[0],
-        }
+    pub fn get_first_plane(&self) -> V4l2PlaneAccessor {
+        self.planes_iter().next().unwrap()
     }
 
-    /// Returns plane `index` of the buffer, or `None` if `index` is larger than
-    /// the number of planes in this buffer.
-    pub fn get_plane(&self, index: usize) -> Option<V4l2BufferPlane> {
-        if index < self.num_planes() {
-            Some(V4l2BufferPlane {
-                buffer: &self.buffer,
-                plane: &self.planes[index],
-            })
-        } else {
-            None
-        }
-    }
-
-    /// Returns a reference to the internal `v4l2_buffer`. All pointers in this
-    /// structure are invalid.
-    pub fn v4l2_buffer(&self) -> &bindings::v4l2_buffer {
+    /// Returns a non-mutable reference to the internal `v4l2_buffer`.
+    ///
+    /// The returned value is not suitable for passing to C functions or ioctls (which anyway
+    /// require a mutable pointer), but can be useful to construct other values.
+    ///
+    /// In particular, if the buffer is multi-planar, then the `planes` pointer will be invalid.
+    /// Dereferencing it would require an `unsafe` block anyway.
+    ///
+    /// If you need to access the `v4l2_planes` of this buffer, use `as_v4l2_planes`.
+    ///
+    /// If you need to pass the `v4l2_buffer` to a C function or ioctl and need a valid `planes`
+    /// pointer, use `as_mut_ptr` and read the warning in its documentation.
+    pub fn as_v4l2_buffer(&self) -> &bindings::v4l2_buffer {
         &self.buffer
     }
 
-    /// Returns an iterator to the internal `v4l2_plane`s. All pointers in the
-    /// planes are invalid.
-    pub fn v4l2_plane_iter(&self) -> impl Iterator<Item = &bindings::v4l2_plane> {
-        self.planes.iter()
+    /// Returns a slice of this buffer's `v4l2_plane`s, if the buffer is multi-planar.
+    ///
+    /// If it is single-planar, an empty slice is returned.
+    ///
+    /// This method only exists for the rare case when one needs to access the original plane data.
+    /// For this reason there is no `v4l2_planes_mut` - use of the `planes_iter*_mut` methods
+    /// instead if you need to modify plane information.
+    pub fn as_v4l2_planes(&self) -> &[bindings::v4l2_plane] {
+        let planes_upper = if self.queue_type().is_multiplanar() {
+            self.buffer.length as usize
+        } else {
+            0
+        };
+
+        &self.planes[0..planes_upper]
     }
+
+    /// Returns a pointer to the internal `v4l2_buffer`.
+    ///
+    /// If this buffer is multi-planar then the `planes` pointer will be updated so the returned
+    /// data is valid if passed to a C function or an ioctl.
+    ///
+    /// Beware however that as a consequence the returned pointer is only valid as long as the
+    /// `V4l2Buffer` is not moved anywhere. Use with extreme caution.
+    pub fn as_mut_ptr(&mut self) -> *mut bindings::v4l2_buffer {
+        if self.queue_type().is_multiplanar() && self.buffer.length > 0 {
+            self.buffer.m.planes = self.planes.as_mut_ptr();
+        }
+
+        &mut self.buffer as *mut _
+    }
+
+    /// Returns planar information in a way that is consistent between single-planar and
+    /// multi-planar buffers.
+    pub fn planes_iter(&self) -> impl Iterator<Item = V4l2PlaneAccessor> {
+        let multiplanar = self.queue_type().is_multiplanar();
+        let planes_iter = self.as_v4l2_planes().iter();
+
+        // In order to return a consistent type for both single-planar and multi-planar buffers,
+        // we chain the single-planar iterator to the multi-planar one. If the buffer is
+        // single-planar, then the multi-planar iterator will be empty. If the buffer is
+        // multi-planar, we skip the first entry which is the (invalid) single-planar iterator.
+        std::iter::once(V4l2PlaneAccessor::new_single_planar(&self.buffer))
+            .chain(planes_iter.map(V4l2PlaneAccessor::new_multi_planar))
+            .skip(if multiplanar { 1 } else { 0 })
+    }
+
+    /// Returns planar information in a way that is consistent between single-planar and
+    /// multi-planar buffers.
+    pub fn planes_iter_mut(&mut self) -> impl Iterator<Item = V4l2PlaneMutAccessor> {
+        let multiplanar = self.queue_type().is_multiplanar();
+        let planes_upper = if multiplanar {
+            self.buffer.length as usize
+        } else {
+            0
+        };
+        let planes_iter = self.planes[0..planes_upper].iter_mut();
+
+        // In order to return a consistent type for both single-planar and multi-planar buffers,
+        // we chain the single-planar iterator to the multi-planar one. If the buffer is
+        // single-planar, then the multi-planar iterator will be empty. If the buffer is
+        // multi-planar, we skip the first entry which is the (invalid) single-planar iterator.
+        std::iter::once(V4l2PlaneMutAccessor::new_single_planar(&mut self.buffer))
+            .chain(planes_iter.map(V4l2PlaneMutAccessor::new_multi_planar))
+            .skip(if multiplanar { 1 } else { 0 })
+    }
+
+    /// Build a plane iterator including the memory backings for memory type `M`.
+    ///
+    /// # Safety
+    ///
+    /// The caller must be sure that the buffer's memory type is indeed `M`.
+    unsafe fn planes_iter_with_backing<M: Memory>(
+        &self,
+    ) -> impl Iterator<Item = V4l2PlaneAccessorWithRawBacking<M>> {
+        let is_multiplanar = self.queue_type().is_multiplanar();
+        let planes_length = if is_multiplanar {
+            self.buffer.length as usize
+        } else {
+            0
+        };
+        let planes = &self.planes[0..planes_length];
+        // In order to return a consistent type for both single-planar and multi-planar buffers,
+        // we chain the single-planar iterator to the multi-planar one. If the buffer is
+        // single-planar, then the multi-planar iterator will be empty. If the buffer is
+        // multi-planar, we skip the first entry which is the (invalid) single-planar iterator.
+        std::iter::once(V4l2PlaneAccessorWithRawBacking::new_single_planar(
+            &self.buffer,
+        ))
+        .chain(
+            planes
+                .iter()
+                .map(|p| V4l2PlaneAccessorWithRawBacking::new_multi_planar(p)),
+        )
+        .skip(if self.queue_type().is_multiplanar() {
+            1
+        } else {
+            0
+        })
+    }
+
+    pub fn planes_with_backing_iter(
+        &self,
+    ) -> V4l2PlanesWithBacking<
+        impl Iterator<Item = V4l2PlaneAccessorWithRawBacking<Mmap>>,
+        impl Iterator<Item = V4l2PlaneAccessorWithRawBacking<UserPtr>>,
+        impl Iterator<Item = V4l2PlaneAccessorWithRawBacking<DmaBuf>>,
+    > {
+        match self.memory() {
+            MemoryType::Mmap => {
+                V4l2PlanesWithBacking::Mmap(unsafe { self.planes_iter_with_backing() })
+            }
+            MemoryType::UserPtr => {
+                V4l2PlanesWithBacking::UserPtr(unsafe { self.planes_iter_with_backing() })
+            }
+            MemoryType::DmaBuf => {
+                V4l2PlanesWithBacking::DmaBuf(unsafe { self.planes_iter_with_backing() })
+            }
+            MemoryType::Overlay => V4l2PlanesWithBacking::Overlay,
+        }
+    }
+
+    /// Build a mutable plane iterator including the memory backings for memory type `M`.
+    ///
+    /// # Safety
+    ///
+    /// The caller must be sure that the buffer's memory type is indeed `M`.
+    unsafe fn planes_iter_with_backing_mut<M: Memory>(
+        &mut self,
+    ) -> impl Iterator<Item = V4l2PlaneMutAccessorWithRawBacking<M>> {
+        let is_multiplanar = self.queue_type().is_multiplanar();
+        let planes_length = if is_multiplanar {
+            self.buffer.length as usize
+        } else {
+            0
+        };
+        let planes = &mut self.planes[0..planes_length];
+
+        // In order to return a consistent type for both single-planar and multi-planar buffers,
+        // we chain the single-planar iterator to the multi-planar one. If the buffer is
+        // single-planar, then the multi-planar iterator will be empty. If the buffer is
+        // multi-planar, we skip the first entry which is the (invalid) single-planar iterator.
+        std::iter::once(V4l2PlaneMutAccessorWithRawBacking::new_single_planar(
+            &mut self.buffer,
+        ))
+        .chain(
+            planes
+                .iter_mut()
+                .map(|p| V4l2PlaneMutAccessorWithRawBacking::new_multi_planar(p)),
+        )
+        .skip(if is_multiplanar { 1 } else { 0 })
+    }
+
+    pub fn planes_with_backing_iter_mut(
+        &mut self,
+    ) -> V4l2PlanesWithBackingMut<
+        impl Iterator<Item = V4l2PlaneMutAccessorWithRawBacking<Mmap>>,
+        impl Iterator<Item = V4l2PlaneMutAccessorWithRawBacking<UserPtr>>,
+        impl Iterator<Item = V4l2PlaneMutAccessorWithRawBacking<DmaBuf>>,
+    > {
+        match self.memory() {
+            MemoryType::Mmap => {
+                V4l2PlanesWithBackingMut::Mmap(unsafe { self.planes_iter_with_backing_mut() })
+            }
+            MemoryType::UserPtr => {
+                V4l2PlanesWithBackingMut::UserPtr(unsafe { self.planes_iter_with_backing_mut() })
+            }
+            MemoryType::DmaBuf => {
+                V4l2PlanesWithBackingMut::DmaBuf(unsafe { self.planes_iter_with_backing_mut() })
+            }
+            MemoryType::Overlay => V4l2PlanesWithBackingMut::Overlay,
+        }
+    }
+}
+
+/// Accessor to a buffer's plane information.
+///
+/// This is just a set of references, that are set to point to the right location depending on
+/// whether the buffer is single or multi-planar.
+pub struct V4l2PlaneAccessor<'a> {
+    pub bytesused: &'a u32,
+    pub length: &'a u32,
+    pub data_offset: Option<&'a u32>,
+}
+
+impl<'a> V4l2PlaneAccessor<'a> {
+    fn new_single_planar(buffer: &'a bindings::v4l2_buffer) -> Self {
+        Self {
+            bytesused: &buffer.bytesused,
+            length: &buffer.length,
+            data_offset: None,
+        }
+    }
+
+    fn new_multi_planar(plane: &'a bindings::v4l2_plane) -> Self {
+        Self {
+            bytesused: &plane.bytesused,
+            length: &plane.length,
+            data_offset: Some(&plane.data_offset),
+        }
+    }
+}
+
+/// Mutable accessor to a buffer's plane information.
+///
+/// This is just a set of references, that are set to point to the right location depending on
+/// whether the buffer is single or multi-planar.
+pub struct V4l2PlaneMutAccessor<'a> {
+    pub bytesused: &'a mut u32,
+    pub length: &'a mut u32,
+    pub data_offset: Option<&'a mut u32>,
+}
+
+impl<'a> V4l2PlaneMutAccessor<'a> {
+    fn new_single_planar(buffer: &'a mut bindings::v4l2_buffer) -> Self {
+        Self {
+            bytesused: &mut buffer.bytesused,
+            length: &mut buffer.length,
+            data_offset: None,
+        }
+    }
+
+    fn new_multi_planar(plane: &'a mut bindings::v4l2_plane) -> Self {
+        Self {
+            bytesused: &mut plane.bytesused,
+            length: &mut plane.length,
+            data_offset: Some(&mut plane.data_offset),
+        }
+    }
+}
+
+pub struct V4l2PlaneAccessorWithRawBacking<'a, M: Memory> {
+    data: V4l2PlaneAccessor<'a>,
+    backing: &'a M::RawBacking,
+}
+
+impl<'a, M: Memory> Deref for V4l2PlaneAccessorWithRawBacking<'a, M> {
+    type Target = V4l2PlaneAccessor<'a>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.data
+    }
+}
+
+impl<'a, M: Memory> V4l2PlaneAccessorWithRawBacking<'a, M> {
+    /// Create a new plane accessor for memory type `M`.
+    ///
+    /// # Safety
+    ///
+    /// `v4l2_buffer` must be of a single-planar type and use memory type `M`.
+    pub unsafe fn new_single_planar(buffer: &'a bindings::v4l2_buffer) -> Self {
+        Self {
+            data: V4l2PlaneAccessor::new_single_planar(buffer),
+            backing: M::get_single_planar_buffer_backing(&buffer.m),
+        }
+    }
+
+    /// Create a new plane accessor for memory type `M`.
+    ///
+    /// # Safety
+    ///
+    /// `v4l2_plane` must come from a multi-planar buffer using memory type `M`.
+    pub unsafe fn new_multi_planar(plane: &'a bindings::v4l2_plane) -> Self {
+        Self {
+            data: V4l2PlaneAccessor::new_multi_planar(plane),
+            backing: M::get_plane_buffer_backing(&plane.m),
+        }
+    }
+}
+
+impl<'a> V4l2PlaneAccessorWithRawBacking<'a, Mmap> {
+    pub fn mem_offset(&self) -> <Mmap as Memory>::RawBacking {
+        *self.backing
+    }
+}
+
+impl<'a> V4l2PlaneAccessorWithRawBacking<'a, UserPtr> {
+    pub fn userptr(&self) -> <UserPtr as Memory>::RawBacking {
+        *self.backing
+    }
+}
+
+impl<'a> V4l2PlaneAccessorWithRawBacking<'a, DmaBuf> {
+    pub fn fd(&self) -> <DmaBuf as Memory>::RawBacking {
+        *self.backing
+    }
+}
+
+pub enum V4l2PlanesWithBacking<
+    'a,
+    M: Iterator<Item = V4l2PlaneAccessorWithRawBacking<'a, Mmap>>,
+    U: Iterator<Item = V4l2PlaneAccessorWithRawBacking<'a, UserPtr>>,
+    D: Iterator<Item = V4l2PlaneAccessorWithRawBacking<'a, DmaBuf>>,
+> {
+    Mmap(M),
+    UserPtr(U),
+    DmaBuf(D),
+    Overlay,
+}
+
+pub struct V4l2PlaneMutAccessorWithRawBacking<'a, M: Memory> {
+    data: V4l2PlaneMutAccessor<'a>,
+    backing: &'a mut M::RawBacking,
+}
+
+impl<'a, M: Memory> Deref for V4l2PlaneMutAccessorWithRawBacking<'a, M> {
+    type Target = V4l2PlaneMutAccessor<'a>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.data
+    }
+}
+
+impl<'a, M: Memory> DerefMut for V4l2PlaneMutAccessorWithRawBacking<'a, M> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.data
+    }
+}
+
+impl<'a, M: Memory> V4l2PlaneMutAccessorWithRawBacking<'a, M> {
+    /// Create a new plane accessor for memory type `M`.
+    ///
+    /// # Safety
+    ///
+    /// `v4l2_buffer` must be of a single-planar type and use memory type `M`.
+    pub unsafe fn new_single_planar(buffer: &'a mut bindings::v4l2_buffer) -> Self {
+        Self {
+            data: V4l2PlaneMutAccessor {
+                bytesused: &mut buffer.bytesused,
+                length: &mut buffer.length,
+                data_offset: None,
+            },
+            backing: M::get_single_planar_buffer_backing_mut(&mut buffer.m),
+        }
+    }
+
+    /// Create a new plane accessor for memory type `M`.
+    ///
+    /// # Safety
+    ///
+    /// `v4l2_plane` must come from a multi-planar buffer using memory type `M`.
+    pub unsafe fn new_multi_planar(plane: &'a mut bindings::v4l2_plane) -> Self {
+        Self {
+            data: V4l2PlaneMutAccessor {
+                bytesused: &mut plane.bytesused,
+                length: &mut plane.length,
+                data_offset: Some(&mut plane.data_offset),
+            },
+            backing: M::get_plane_buffer_backing_mut(&mut plane.m),
+        }
+    }
+}
+
+impl<'a> V4l2PlaneMutAccessorWithRawBacking<'a, Mmap> {
+    pub fn mem_offset(&self) -> <Mmap as Memory>::RawBacking {
+        *self.backing
+    }
+
+    pub fn set_mem_offset(&mut self, mem_offset: <Mmap as Memory>::RawBacking) {
+        *self.backing = mem_offset;
+    }
+}
+
+impl<'a> V4l2PlaneMutAccessorWithRawBacking<'a, UserPtr> {
+    pub fn userptr(&self) -> <UserPtr as Memory>::RawBacking {
+        *self.backing
+    }
+
+    pub fn set_userptr(&mut self, userptr: <UserPtr as Memory>::RawBacking) {
+        *self.backing = userptr;
+    }
+}
+
+impl<'a> V4l2PlaneMutAccessorWithRawBacking<'a, DmaBuf> {
+    pub fn fd(&self) -> <DmaBuf as Memory>::RawBacking {
+        *self.backing
+    }
+
+    pub fn set_fd(&mut self, fd: <DmaBuf as Memory>::RawBacking) {
+        *self.backing = fd;
+    }
+}
+
+pub enum V4l2PlanesWithBackingMut<
+    'a,
+    M: Iterator<Item = V4l2PlaneMutAccessorWithRawBacking<'a, Mmap>>,
+    U: Iterator<Item = V4l2PlaneMutAccessorWithRawBacking<'a, UserPtr>>,
+    D: Iterator<Item = V4l2PlaneMutAccessorWithRawBacking<'a, DmaBuf>>,
+> {
+    Mmap(M),
+    UserPtr(U),
+    DmaBuf(D),
+    Overlay,
 }
diff --git a/lib/src/memory.rs b/lib/src/memory.rs
index 6eba192..0a4999c 100644
--- a/lib/src/memory.rs
+++ b/lib/src/memory.rs
@@ -58,6 +58,47 @@
 pub trait Memory: 'static {
     /// The memory type represented.
     const MEMORY_TYPE: MemoryType;
+    /// The final type of the memory backing information in `struct v4l2_buffer` or `struct
+    /// v4l2_plane`.
+    type RawBacking;
+
+    /// Returns a reference to the memory backing information for `m` that is relevant for this
+    /// memory type.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensure that `m` indeed belongs to a buffer of this memory type.
+    unsafe fn get_plane_buffer_backing(m: &bindings::v4l2_plane__bindgen_ty_1)
+        -> &Self::RawBacking;
+
+    /// Returns a reference to the memory backing information for `m` that is relevant for this memory type.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensure that `m` indeed belongs to a buffer of this memory type.
+    unsafe fn get_single_planar_buffer_backing(
+        m: &bindings::v4l2_buffer__bindgen_ty_1,
+    ) -> &Self::RawBacking;
+
+    /// Returns a mutable reference to the memory backing information for `m` that is relevant for
+    /// this memory type.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensure that `m` indeed belongs to a buffer of this memory type.
+    unsafe fn get_plane_buffer_backing_mut(
+        m: &mut bindings::v4l2_plane__bindgen_ty_1,
+    ) -> &mut Self::RawBacking;
+
+    /// Returns a mutable reference to the memory backing information for `m` that is relevant for
+    /// this memory type.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensure that `m` indeed belongs to a buffer of this memory type.
+    unsafe fn get_single_planar_buffer_backing_mut(
+        m: &mut bindings::v4l2_buffer__bindgen_ty_1,
+    ) -> &mut Self::RawBacking;
 }
 
 /// Trait for memory types that provide their own memory, i.e. MMAP.
diff --git a/lib/src/memory/dmabuf.rs b/lib/src/memory/dmabuf.rs
index e2e0faa..315b5c2 100644
--- a/lib/src/memory/dmabuf.rs
+++ b/lib/src/memory/dmabuf.rs
@@ -3,6 +3,7 @@
 
 use super::*;
 use crate::{bindings, ioctl};
+use std::os::fd::RawFd;
 use std::os::unix::io::{AsFd, AsRawFd};
 
 pub struct DmaBuf;
@@ -11,6 +12,31 @@
 
 impl Memory for DmaBuf {
     const MEMORY_TYPE: MemoryType = MemoryType::DmaBuf;
+    type RawBacking = RawFd;
+
+    unsafe fn get_plane_buffer_backing(
+        m: &bindings::v4l2_plane__bindgen_ty_1,
+    ) -> &Self::RawBacking {
+        &m.fd
+    }
+
+    unsafe fn get_single_planar_buffer_backing(
+        m: &bindings::v4l2_buffer__bindgen_ty_1,
+    ) -> &Self::RawBacking {
+        &m.fd
+    }
+
+    unsafe fn get_plane_buffer_backing_mut(
+        m: &mut bindings::v4l2_plane__bindgen_ty_1,
+    ) -> &mut Self::RawBacking {
+        &mut m.fd
+    }
+
+    unsafe fn get_single_planar_buffer_backing_mut(
+        m: &mut bindings::v4l2_buffer__bindgen_ty_1,
+    ) -> &mut Self::RawBacking {
+        &mut m.fd
+    }
 }
 
 impl Imported for DmaBuf {}
diff --git a/lib/src/memory/mmap.rs b/lib/src/memory/mmap.rs
index e2485be..0a24f1e 100644
--- a/lib/src/memory/mmap.rs
+++ b/lib/src/memory/mmap.rs
@@ -9,6 +9,31 @@
 
 impl Memory for Mmap {
     const MEMORY_TYPE: MemoryType = MemoryType::Mmap;
+    type RawBacking = u32;
+
+    unsafe fn get_plane_buffer_backing(
+        m: &bindings::v4l2_plane__bindgen_ty_1,
+    ) -> &Self::RawBacking {
+        &m.mem_offset
+    }
+
+    unsafe fn get_single_planar_buffer_backing(
+        m: &bindings::v4l2_buffer__bindgen_ty_1,
+    ) -> &Self::RawBacking {
+        &m.offset
+    }
+
+    unsafe fn get_plane_buffer_backing_mut(
+        m: &mut bindings::v4l2_plane__bindgen_ty_1,
+    ) -> &mut Self::RawBacking {
+        &mut m.mem_offset
+    }
+
+    unsafe fn get_single_planar_buffer_backing_mut(
+        m: &mut bindings::v4l2_buffer__bindgen_ty_1,
+    ) -> &mut Self::RawBacking {
+        &mut m.offset
+    }
 }
 
 impl SelfBacked for Mmap {}
diff --git a/lib/src/memory/userptr.rs b/lib/src/memory/userptr.rs
index f602f6a..d0958dc 100644
--- a/lib/src/memory/userptr.rs
+++ b/lib/src/memory/userptr.rs
@@ -7,6 +7,31 @@
 
 impl Memory for UserPtr {
     const MEMORY_TYPE: MemoryType = MemoryType::UserPtr;
+    type RawBacking = core::ffi::c_ulong;
+
+    unsafe fn get_plane_buffer_backing(
+        m: &bindings::v4l2_plane__bindgen_ty_1,
+    ) -> &Self::RawBacking {
+        &m.userptr
+    }
+
+    unsafe fn get_single_planar_buffer_backing(
+        m: &bindings::v4l2_buffer__bindgen_ty_1,
+    ) -> &Self::RawBacking {
+        &m.userptr
+    }
+
+    unsafe fn get_plane_buffer_backing_mut(
+        m: &mut bindings::v4l2_plane__bindgen_ty_1,
+    ) -> &mut Self::RawBacking {
+        &mut m.userptr
+    }
+
+    unsafe fn get_single_planar_buffer_backing_mut(
+        m: &mut bindings::v4l2_buffer__bindgen_ty_1,
+    ) -> &mut Self::RawBacking {
+        &mut m.userptr
+    }
 }
 
 impl Imported for UserPtr {}