Revert "Sync cros-codecs code with ChromeOS"

This reverts commit 60b5c274eab10b0c079991a6f384e7e7a0c3d699.

Reason for revert: <Potential culprit for b/384872664  - verifying through ABTD before revert submission. This is part of the standard investigation process, and does not mean your CL will be reverted.E>

Change-Id: Iff11d38551e4300e4711a021aad939c9ed58afb2
diff --git a/Cargo.toml b/Cargo.toml
index ba80813..4c68ab5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,14 +9,13 @@
 
 [features]
 default = []
-backend = []
-vaapi = ["libva", "anyhow", "byteorder", "thiserror", "crc32fast", "nix", "backend"]
-v4l2 = ["v4l2r", "anyhow", "byteorder", "thiserror", "crc32fast", "nix", "backend"]
+vaapi = ["libva", "anyhow", "byteorder", "thiserror", "crc32fast", "nix"]
+v4l2 = ["v4l2r", "anyhow", "byteorder", "thiserror", "crc32fast", "nix"]
 
 [dependencies]
 anyhow = { version = "1.0.75", optional = true }
 byteorder = { version = "1.4.3", optional = true }
-libva = { version = "0.0.12", package = "cros-libva", optional = true }
+libva = { version = "0.0.7", package = "cros-libva", optional = true }
 v4l2r = { version = "0.0.5", package = "v4l2r", optional = true }
 log = { version = "0", features = ["release_max_level_debug"] }
 thiserror = { version = "1.0.58", optional = true }
@@ -37,8 +36,8 @@
 
 [[example]]
 name = "ccdec"
-required-features = ["backend"]
+required-features = ["vaapi"]
 
 [[example]]
 name = "ccenc"
-required-features = ["backend"]
+required-features = ["vaapi"]
diff --git a/examples/ccdec-v4l2-stateless.rs b/examples/ccdec-v4l2-stateless.rs
deleted file mode 100644
index fdee24d..0000000
--- a/examples/ccdec-v4l2-stateless.rs
+++ /dev/null
@@ -1,187 +0,0 @@
-// Copyright 2024 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-use std::borrow::Cow;
-use std::fs::File;
-use std::io::Read;
-use std::io::Write;
-use std::path::PathBuf;
-use std::str::FromStr;
-
-use argh::FromArgs;
-use cros_codecs::backend::v4l2::decoder::stateless::V4l2StatelessDecoderHandle;
-use cros_codecs::bitstream_utils::NalIterator;
-use cros_codecs::codec::h264::parser::Nalu as H264Nalu;
-use cros_codecs::decoder::stateless::h264::H264;
-use cros_codecs::decoder::stateless::StatelessDecoder;
-use cros_codecs::decoder::stateless::StatelessVideoDecoder;
-use cros_codecs::decoder::BlockingMode;
-use cros_codecs::decoder::DecodedHandle;
-use cros_codecs::decoder::DynDecodedHandle;
-use cros_codecs::multiple_desc_type;
-use cros_codecs::utils::simple_playback_loop;
-use cros_codecs::utils::simple_playback_loop_owned_frames;
-use cros_codecs::utils::DmabufFrame;
-use cros_codecs::utils::UserPtrFrame;
-use cros_codecs::DecodedFormat;
-
-multiple_desc_type! {
-    enum BufferDescriptor {
-        Managed(()),
-        Dmabuf(DmabufFrame),
-        User(UserPtrFrame),
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Copy, Clone)]
-enum EncodedFormat {
-    H264,
-    H265,
-    VP8,
-    VP9,
-    AV1,
-}
-
-impl FromStr for EncodedFormat {
-    type Err = &'static str;
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        match s {
-            "h264" | "H264" => Ok(EncodedFormat::H264),
-            "h265" | "H265" => Ok(EncodedFormat::H265),
-            "vp8" | "VP8" => Ok(EncodedFormat::VP8),
-            "vp9" | "VP9" => Ok(EncodedFormat::VP9),
-            "av1" | "AV1" => Ok(EncodedFormat::AV1),
-            _ => Err("unrecognized input format. Valid values: h264, h265, vp8, vp9, av1"),
-        }
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Copy, Clone)]
-enum FrameMemoryType {
-    Managed,
-    Prime,
-    User,
-}
-
-impl FromStr for FrameMemoryType {
-    type Err = &'static str;
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        match s {
-            "managed" => Ok(FrameMemoryType::Managed),
-            "prime" => Ok(FrameMemoryType::Prime),
-            "user" => Ok(FrameMemoryType::User),
-            _ => Err("unrecognized memory type. Valid values: managed, prime, user"),
-        }
-    }
-}
-
-/// Simple player using cros-codecs
-#[derive(Debug, FromArgs)]
-struct Args {
-    /// input file
-    #[argh(positional)]
-    input: PathBuf,
-
-    /// output file to write the decoded frames to
-    #[argh(option)]
-    output: Option<PathBuf>,
-
-    /// input format to decode from.
-    #[argh(option)]
-    input_format: EncodedFormat,
-
-    //TODO    /// pixel format to decode into. Default: i420
-    //TODO    #[argh(option, default = "DecodedFormat::I420")]
-    //TODO    output_format: DecodedFormat,
-    /// origin of the memory for decoded buffers (managed, prime or user). Default: managed.
-    #[argh(option, default = "FrameMemoryType::Managed")]
-    frame_memory: FrameMemoryType,
-
-    //TODO    /// path to the GBM device to use if frame-memory=prime
-    //TODO    #[argh(option)]
-    //TODO    gbm_device: Option<PathBuf>,
-    /// whether to decode frames synchronously
-    #[argh(switch)]
-    synchronous: bool,
-    //TODO    /// whether to display the MD5 of the decoded stream, and at which granularity
-    //TODO    /// (stream or frame)
-    //TODO    #[argh(option)]
-    //TODO    compute_md5: Option<Md5Computation>,
-}
-
-fn main() {
-    env_logger::init();
-
-    let args: Args = argh::from_env();
-
-    let input = {
-        let mut buf = Vec::new();
-        File::open(args.input)
-            .expect("error opening input file")
-            .read_to_end(&mut buf)
-            .expect("error reading input file");
-        buf
-    };
-
-    let mut output = args
-        .output
-        .as_ref()
-        .map(|p| File::create(p).expect("Failed to create output file"));
-
-    let blocking_mode = if args.synchronous {
-        todo!() // BlockingMode::Blocking
-    } else {
-        BlockingMode::NonBlocking
-    };
-
-    let (mut decoder, frame_iter) = match args.input_format {
-        EncodedFormat::H264 => {
-            let frame_iter = Box::new(NalIterator::<H264Nalu>::new(&input))
-                as Box<dyn Iterator<Item = Cow<[u8]>>>;
-
-            let decoder = StatelessDecoder::<H264, _>::new_v4l2(blocking_mode).into_trait_object();
-
-            (decoder, frame_iter)
-        }
-        EncodedFormat::VP8 => todo!(),
-        EncodedFormat::VP9 => todo!(),
-        EncodedFormat::H265 => todo!(),
-        EncodedFormat::AV1 => todo!(),
-    };
-
-    let mut on_new_frame = |handle: DynDecodedHandle<()>| {
-        let picture = handle.dyn_picture();
-        let mut handle = picture.dyn_mappable_handle().unwrap();
-        let buffer_size = handle.image_size();
-        let mut frame_data = vec![0; buffer_size];
-        handle.read(&mut frame_data).unwrap();
-        if let Some(output) = &mut output {
-            output
-                .write_all(&frame_data)
-                .expect("Failed to write output file");
-        }
-    };
-
-    simple_playback_loop(
-        decoder.as_mut(),
-        frame_iter,
-        &mut on_new_frame,
-        &mut |stream_info, nb_frames| {
-            Ok(match args.frame_memory {
-                FrameMemoryType::Managed => {
-                    simple_playback_loop_owned_frames(stream_info, nb_frames)?
-                        .into_iter()
-                        .collect()
-                }
-                FrameMemoryType::Prime => todo!(),
-                FrameMemoryType::User => todo!(),
-            })
-        },
-        DecodedFormat::NV12,
-        blocking_mode,
-    )
-    .expect("error during playback loop");
-}
diff --git a/examples/ccdec/main.rs b/examples/ccdec/main.rs
index 2547343..81ad007 100644
--- a/examples/ccdec/main.rs
+++ b/examples/ccdec/main.rs
@@ -1,29 +1,517 @@
-// Copyright 2024 The ChromiumOS Authors
+// Copyright 2023 The ChromiumOS Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+//! ccdec, a simple decoder program using cros-codecs. Capable of computing MD5 checksums from the
+//! input and writing the raw decoded frames to a file.
+
+use std::borrow::Cow;
+use std::ffi::OsStr;
 use std::fs::File;
+use std::io::Cursor;
+use std::io::Read;
+use std::io::Write;
+use std::os::fd::AsFd;
+use std::os::fd::BorrowedFd;
+use std::path::Path;
+use std::path::PathBuf;
+use std::str::FromStr;
 
 mod md5;
-mod util;
-use util::Args;
 
-#[cfg(feature = "vaapi")]
-mod vaapi_decoder;
-#[cfg(feature = "vaapi")]
-use vaapi_decoder::do_decode;
+use argh::FromArgs;
+use cros_codecs::bitstream_utils::IvfIterator;
+use cros_codecs::bitstream_utils::NalIterator;
+use cros_codecs::codec::h264::parser::Nalu as H264Nalu;
+use cros_codecs::codec::h265::parser::Nalu as H265Nalu;
+use cros_codecs::decoder::stateless::av1::Av1;
+use cros_codecs::decoder::stateless::h264::H264;
+use cros_codecs::decoder::stateless::h265::H265;
+use cros_codecs::decoder::stateless::vp8::Vp8;
+use cros_codecs::decoder::stateless::vp9::Vp9;
+use cros_codecs::decoder::stateless::StatelessDecoder;
+use cros_codecs::decoder::stateless::StatelessVideoDecoder;
+use cros_codecs::decoder::BlockingMode;
+use cros_codecs::decoder::DecodedHandle;
+use cros_codecs::decoder::DynDecodedHandle;
+use cros_codecs::decoder::StreamInfo;
+use cros_codecs::multiple_desc_type;
+use cros_codecs::utils::simple_playback_loop;
+use cros_codecs::utils::simple_playback_loop_owned_frames;
+use cros_codecs::utils::simple_playback_loop_userptr_frames;
+use cros_codecs::utils::DmabufFrame;
+use cros_codecs::utils::UserPtrFrame;
+use cros_codecs::DecodedFormat;
+use cros_codecs::Fourcc;
+use cros_codecs::FrameLayout;
+use cros_codecs::PlaneLayout;
+use cros_codecs::Resolution;
+use matroska_demuxer::Frame;
+use matroska_demuxer::MatroskaFile;
+use md5::md5_digest;
+use md5::MD5Context;
 
-#[cfg(feature = "v4l2")]
-mod v4l2_stateless_decoder;
-#[cfg(feature = "v4l2")]
-use v4l2_stateless_decoder::do_decode;
+// Our buffer descriptor type.
+//
+// We support buffers which memory is managed by the backend, or imported from user memory or a
+// PRIME buffer.
+multiple_desc_type! {
+    enum BufferDescriptor {
+        Managed(()),
+        Dmabuf(DmabufFrame),
+        User(UserPtrFrame),
+    }
+}
+
+/// Export a file descriptor from a GBM `BufferObject` and turn it into a `DmabufFrame` suitable
+/// for using as the target of a decoder.
+fn export_gbm_bo<T>(obj: &gbm::BufferObject<T>) -> anyhow::Result<DmabufFrame> {
+    let fd = obj.fd()?;
+    let modifier = obj.modifier()?;
+    let format = obj.format()?;
+    let planes = (0..obj.plane_count()? as i32)
+        .map(|i| PlaneLayout {
+            buffer_index: 0,
+            offset: obj.offset(i).unwrap() as usize,
+            stride: obj.stride_for_plane(i).unwrap() as usize,
+        })
+        .collect();
+    let size = Resolution::from((obj.width().unwrap(), obj.height().unwrap()));
+
+    Ok(DmabufFrame {
+        fds: vec![fd],
+        layout: FrameLayout {
+            format: (Fourcc::from(format as u32), modifier.into()),
+            size,
+            planes,
+        },
+    })
+}
+
+/// Buffer allocation callback for `simple_playback_loop` to allocate and export buffers from a GBM
+/// device.
+fn simple_playback_loop_prime_frames<D: AsFd>(
+    device: &gbm::Device<D>,
+    stream_info: &StreamInfo,
+    nb_frames: usize,
+) -> anyhow::Result<Vec<DmabufFrame>> {
+    let gbm_fourcc = match stream_info.format {
+        DecodedFormat::I420 | DecodedFormat::NV12 => gbm::Format::Nv12,
+        _ => anyhow::bail!(
+            "{:?} format is unsupported with GBM memory",
+            stream_info.format
+        ),
+    };
+
+    (0..nb_frames)
+        .map(|_| {
+            device
+                .create_buffer_object::<()>(
+                    stream_info.coded_resolution.width,
+                    stream_info.coded_resolution.height,
+                    gbm_fourcc,
+                    gbm::BufferObjectFlags::SCANOUT,
+                )
+                .map_err(|e| anyhow::anyhow!(e))
+                .and_then(|o| export_gbm_bo(&o))
+        })
+        .collect()
+}
+
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+enum EncodedFormat {
+    H264,
+    H265,
+    VP8,
+    VP9,
+    AV1,
+}
+
+impl FromStr for EncodedFormat {
+    type Err = &'static str;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "h264" | "H264" => Ok(EncodedFormat::H264),
+            "h265" | "H265" => Ok(EncodedFormat::H265),
+            "vp8" | "VP8" => Ok(EncodedFormat::VP8),
+            "vp9" | "VP9" => Ok(EncodedFormat::VP9),
+            "av1" | "AV1" => Ok(EncodedFormat::AV1),
+            _ => Err("unrecognized input format. Valid values: h264, h265, vp8, vp9, av1"),
+        }
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+enum FrameMemoryType {
+    Managed,
+    Prime,
+    User,
+}
+
+impl FromStr for FrameMemoryType {
+    type Err = &'static str;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "managed" => Ok(FrameMemoryType::Managed),
+            "prime" => Ok(FrameMemoryType::Prime),
+            "user" => Ok(FrameMemoryType::User),
+            _ => Err("unrecognized memory type. Valid values: managed, prime, user"),
+        }
+    }
+}
+
+struct MkvFrameIterator<T: AsRef<[u8]>> {
+    input: MatroskaFile<Cursor<T>>,
+    video_track: u64,
+}
+
+impl<T: AsRef<[u8]>> MkvFrameIterator<T> {
+    fn new(input: T) -> anyhow::Result<Self> {
+        let input = MatroskaFile::open(Cursor::new(input))?;
+        let video_track = input
+            .tracks()
+            .iter()
+            .find(|t| t.track_type() == matroska_demuxer::TrackType::Video)
+            .map(|t| t.track_number().get())
+            .ok_or_else(|| anyhow::anyhow!("no video track in input file"))?;
+
+        Ok(Self { input, video_track })
+    }
+}
+
+impl<T: AsRef<[u8]>> Iterator for MkvFrameIterator<T> {
+    type Item = Vec<u8>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let mut frame = Frame::default();
+        while self.input.next_frame(&mut frame).unwrap() {
+            if frame.track == self.video_track {
+                return Some(frame.data);
+            }
+        }
+
+        None
+    }
+}
+
+#[derive(Debug)]
+enum Md5Computation {
+    Stream,
+    Frame,
+}
+
+impl FromStr for Md5Computation {
+    type Err = &'static str;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "stream" => Ok(Md5Computation::Stream),
+            "frame" => Ok(Md5Computation::Frame),
+            _ => Err("unrecognized MD5 computation option. Valid values: stream, frame"),
+        }
+    }
+}
+
+/// Simple player using cros-codecs
+#[derive(Debug, FromArgs)]
+struct Args {
+    /// input file
+    #[argh(positional)]
+    input: PathBuf,
+
+    /// output file to write the decoded frames to
+    #[argh(option)]
+    output: Option<PathBuf>,
+
+    /// whether to decode a frame per file. Requires "output" to be set.
+    #[argh(switch)]
+    multiple_output_files: bool,
+
+    /// input format to decode from.
+    #[argh(option)]
+    input_format: EncodedFormat,
+
+    /// pixel format to decode into. Default: i420
+    #[argh(option, default = "DecodedFormat::I420")]
+    output_format: DecodedFormat,
+
+    /// origin of the memory for decoded buffers (managed, prime or user). Default: managed.
+    #[argh(option, default = "FrameMemoryType::Managed")]
+    frame_memory: FrameMemoryType,
+
+    /// path to the GBM device to use if frame-memory=prime
+    #[argh(option)]
+    gbm_device: Option<PathBuf>,
+
+    /// whether to decode frames synchronously
+    #[argh(switch)]
+    synchronous: bool,
+
+    /// whether to display the MD5 of the decoded stream, and at which granularity (stream or
+    /// frame)
+    #[argh(option)]
+    compute_md5: Option<Md5Computation>,
+
+    /// path to JSON file containing golden MD5 sums of each frame.
+    #[argh(option)]
+    golden: Option<PathBuf>,
+}
+
+/// Detects the container type (IVF or MKV) and returns the corresponding frame iterator.
+fn create_vpx_frame_iterator(input: &[u8]) -> Box<dyn Iterator<Item = Cow<[u8]>> + '_> {
+    if input.starts_with(&[0x1a, 0x45, 0xdf, 0xa3]) {
+        Box::new(MkvFrameIterator::new(input).unwrap().map(Cow::Owned))
+    } else {
+        Box::new(IvfIterator::new(input).map(Cow::Borrowed))
+    }
+}
+
+/// Decide the output file name when multiple_output_files is set
+fn decide_output_file_name<'a>(output: &'a Path, index: i32) -> PathBuf {
+    let extract_str = |s: Option<&'a OsStr>| s.and_then(|s| s.to_str()).expect("malformed file");
+
+    let [file_name, stem] = [output.file_name(), output.file_stem()].map(extract_str);
+
+    if output.extension().is_some() {
+        let [extension] = [output.extension()].map(extract_str);
+        let new_file_name = format!("{}_{}.{}", stem, index, extension);
+        PathBuf::from(String::from(output.to_str().unwrap()).replace(file_name, &new_file_name))
+    } else {
+        let new_file_name = format!("{}_{}", stem, index);
+        PathBuf::from(String::from(output.to_str().unwrap()).replace(file_name, &new_file_name))
+    }
+}
 
 fn main() {
     env_logger::init();
 
     let args: Args = argh::from_env();
 
-    let input = File::open(&args.input).expect("error opening input file");
+    let input = {
+        let mut buf = Vec::new();
+        File::open(args.input)
+            .expect("error opening input file")
+            .read_to_end(&mut buf)
+            .expect("error reading input file");
+        buf
+    };
 
-    do_decode(input, args);
+    let mut output = if !args.multiple_output_files {
+        args.output
+            .as_ref()
+            .map(|p| File::create(p).expect("error creating output file"))
+    } else {
+        None
+    };
+
+    let blocking_mode = if args.synchronous {
+        BlockingMode::Blocking
+    } else {
+        BlockingMode::NonBlocking
+    };
+
+    let golden_md5s: Vec<String> = match args.golden {
+        None => vec![],
+        Some(ref path) => {
+            let mut golden_file_content = String::new();
+            File::open(&path)
+                .expect("error opening golden file")
+                .read_to_string(&mut golden_file_content)
+                .expect("error reading golden file");
+            let parsed_json: serde_json::Value =
+                serde_json::from_str(&golden_file_content).expect("error parsing golden file");
+            match &parsed_json["md5_checksums"] {
+                serde_json::Value::Array(checksums) => checksums
+                    .iter()
+                    .map(|x| match x {
+                        serde_json::Value::String(checksum) => String::from(checksum),
+                        _ => panic!("error parsing golden file"),
+                    })
+                    .collect(),
+                _ => panic!("error parsing golden file"),
+            }
+        }
+    };
+    let mut golden_iter = golden_md5s.iter();
+
+    let gbm = match args.frame_memory {
+        FrameMemoryType::Managed | FrameMemoryType::User => None,
+        FrameMemoryType::Prime => {
+            /// A simple wrapper for a GBM device node.
+            pub struct GbmDevice(std::fs::File);
+
+            impl AsFd for GbmDevice {
+                fn as_fd(&self) -> BorrowedFd<'_> {
+                    self.0.as_fd()
+                }
+            }
+            impl drm::Device for GbmDevice {}
+
+            /// Simple helper methods for opening a `Card`.
+            impl GbmDevice {
+                pub fn open<P: AsRef<Path>>(path: P) -> std::io::Result<Self> {
+                    std::fs::OpenOptions::new()
+                        .read(true)
+                        .write(true)
+                        .open(path)
+                        .map(GbmDevice)
+                }
+            }
+
+            let gbm_path = args
+                .gbm_device
+                .unwrap_or(PathBuf::from("/dev/dri/renderD128"));
+            let gbm = GbmDevice::open(gbm_path)
+                .and_then(gbm::Device::new)
+                .expect("failed to create GBM device");
+
+            Some(gbm)
+        }
+    };
+
+    let display = libva::Display::open().expect("failed to open libva display");
+
+    // The created `decoder` is turned into a `DynStatelessVideoDecoder` trait object. This allows
+    // the same code to control the decoder no matter what codec or backend we are using.
+    let (mut decoder, frame_iter) = match args.input_format {
+        EncodedFormat::H264 => {
+            let frame_iter = Box::new(NalIterator::<H264Nalu>::new(&input))
+                as Box<dyn Iterator<Item = Cow<[u8]>>>;
+
+            let decoder = StatelessDecoder::<H264, _>::new_vaapi(display, blocking_mode)
+                .unwrap()
+                .into_trait_object();
+
+            (decoder, frame_iter)
+        }
+        EncodedFormat::VP8 => {
+            let frame_iter = create_vpx_frame_iterator(&input);
+
+            let decoder = StatelessDecoder::<Vp8, _>::new_vaapi(display, blocking_mode)
+                .unwrap()
+                .into_trait_object();
+
+            (decoder, frame_iter)
+        }
+        EncodedFormat::VP9 => {
+            let frame_iter = create_vpx_frame_iterator(&input);
+
+            let decoder = StatelessDecoder::<Vp9, _>::new_vaapi(display, blocking_mode)
+                .unwrap()
+                .into_trait_object();
+
+            (decoder, frame_iter)
+        }
+        EncodedFormat::H265 => {
+            let frame_iter = Box::new(NalIterator::<H265Nalu>::new(&input))
+                as Box<dyn Iterator<Item = Cow<[u8]>>>;
+
+            let decoder = StatelessDecoder::<H265, _>::new_vaapi(display, blocking_mode)
+                .unwrap()
+                .into_trait_object();
+
+            (decoder, frame_iter)
+        }
+        EncodedFormat::AV1 => {
+            let frame_iter = create_vpx_frame_iterator(&input);
+
+            let decoder = StatelessDecoder::<Av1, _>::new_vaapi(display, blocking_mode)
+                .unwrap()
+                .into_trait_object();
+
+            (decoder, frame_iter)
+        }
+    };
+
+    let mut md5_context = MD5Context::new();
+    let mut output_filename_idx = 0;
+    let need_per_frame_md5 = match args.compute_md5 {
+        Some(Md5Computation::Frame) => true,
+        _ => args.golden.is_some(),
+    };
+
+    let mut on_new_frame = |handle: DynDecodedHandle<BufferDescriptor>| {
+        if args.output.is_some() || args.compute_md5.is_some() || args.golden.is_some() {
+            handle.sync().unwrap();
+            let picture = handle.dyn_picture();
+            let mut handle = picture.dyn_mappable_handle().unwrap();
+            let buffer_size = handle.image_size();
+            let mut frame_data = vec![0; buffer_size];
+            handle.read(&mut frame_data).unwrap();
+
+            if args.multiple_output_files {
+                let file_name = decide_output_file_name(
+                    args.output
+                        .as_ref()
+                        .expect("multiple_output_files need output to be set"),
+                    output_filename_idx,
+                );
+
+                let mut output = File::create(file_name).expect("error creating output file");
+                output_filename_idx += 1;
+                output
+                    .write_all(&frame_data)
+                    .expect("failed to write to output file");
+            } else if let Some(output) = &mut output {
+                output
+                    .write_all(&frame_data)
+                    .expect("failed to write to output file");
+            }
+
+            let frame_md5: String = if need_per_frame_md5 {
+                md5_digest(&frame_data)
+            } else {
+                "".to_string()
+            };
+
+            match args.compute_md5 {
+                None => (),
+                Some(Md5Computation::Frame) => println!("{}", frame_md5),
+                Some(Md5Computation::Stream) => md5_context.consume(&frame_data),
+            }
+
+            if args.golden.is_some() {
+                assert_eq!(&frame_md5, golden_iter.next().unwrap());
+            }
+        }
+    };
+
+    simple_playback_loop(
+        decoder.as_mut(),
+        frame_iter,
+        &mut on_new_frame,
+        &mut |stream_info, nb_frames| {
+            Ok(match args.frame_memory {
+                FrameMemoryType::Managed => {
+                    simple_playback_loop_owned_frames(stream_info, nb_frames)?
+                        .into_iter()
+                        .map(BufferDescriptor::Managed)
+                        .collect()
+                }
+                FrameMemoryType::Prime => simple_playback_loop_prime_frames(
+                    gbm.as_ref().unwrap(),
+                    stream_info,
+                    nb_frames,
+                )?
+                .into_iter()
+                .map(BufferDescriptor::Dmabuf)
+                .collect(),
+                FrameMemoryType::User => {
+                    simple_playback_loop_userptr_frames(stream_info, nb_frames)?
+                        .into_iter()
+                        .map(BufferDescriptor::User)
+                        .collect()
+                }
+            })
+        },
+        args.output_format,
+        blocking_mode,
+    )
+    .expect("error during playback loop");
+
+    if let Some(Md5Computation::Stream) = args.compute_md5 {
+        println!("{}", md5_context.flush());
+    }
 }
diff --git a/examples/ccdec/util.rs b/examples/ccdec/util.rs
deleted file mode 100644
index 1f11a56..0000000
--- a/examples/ccdec/util.rs
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright 2024 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-use std::ffi::OsStr;
-use std::path::Path;
-use std::path::PathBuf;
-use std::str::FromStr;
-
-use argh::FromArgs;
-
-use cros_codecs::DecodedFormat;
-
-#[derive(Debug, PartialEq, Eq, Copy, Clone)]
-pub enum EncodedFormat {
-    H264,
-    H265,
-    VP8,
-    VP9,
-    AV1,
-}
-
-impl FromStr for EncodedFormat {
-    type Err = &'static str;
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        match s {
-            "h264" | "H264" => Ok(EncodedFormat::H264),
-            "h265" | "H265" => Ok(EncodedFormat::H265),
-            "vp8" | "VP8" => Ok(EncodedFormat::VP8),
-            "vp9" | "VP9" => Ok(EncodedFormat::VP9),
-            "av1" | "AV1" => Ok(EncodedFormat::AV1),
-            _ => Err("unrecognized input format. Valid values: h264, h265, vp8, vp9, av1"),
-        }
-    }
-}
-
-#[derive(Debug)]
-pub enum Md5Computation {
-    Stream,
-    Frame,
-}
-
-impl FromStr for Md5Computation {
-    type Err = &'static str;
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        match s {
-            "stream" => Ok(Md5Computation::Stream),
-            "frame" => Ok(Md5Computation::Frame),
-            _ => Err("unrecognized MD5 computation option. Valid values: stream, frame"),
-        }
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Copy, Clone)]
-pub enum FrameMemoryType {
-    Managed,
-    Prime,
-    User,
-}
-
-impl FromStr for FrameMemoryType {
-    type Err = &'static str;
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        match s {
-            "managed" => Ok(FrameMemoryType::Managed),
-            "prime" => Ok(FrameMemoryType::Prime),
-            "user" => Ok(FrameMemoryType::User),
-            _ => Err("unrecognized memory type. Valid values: managed, prime, user"),
-        }
-    }
-}
-
-/// Simple player using cros-codecs
-#[derive(Debug, FromArgs)]
-pub struct Args {
-    /// input file
-    #[argh(positional)]
-    pub input: PathBuf,
-
-    /// output file to write the decoded frames to
-    #[argh(option)]
-    pub output: Option<PathBuf>,
-
-    /// whether to decode a frame per file. Requires "output" to be set.
-    #[argh(switch)]
-    pub multiple_output_files: bool,
-
-    /// input format to decode from.
-    #[argh(option)]
-    pub input_format: EncodedFormat,
-
-    /// pixel format to decode into. Default: i420
-    #[argh(option, default = "DecodedFormat::I420")]
-    pub output_format: DecodedFormat,
-
-    /// origin of the memory for decoded buffers (managed, prime or user). Default: managed.
-    #[argh(option, default = "FrameMemoryType::Managed")]
-    pub frame_memory: FrameMemoryType,
-
-    /// path to the GBM device to use if frame-memory=prime
-    #[allow(dead_code)]
-    #[argh(option)]
-    pub gbm_device: Option<PathBuf>,
-
-    /// path to VA-API device. This option is ignored on V4L2 systems.
-    #[argh(option)]
-    #[allow(dead_code)]
-    pub libva_device: Option<PathBuf>,
-
-    /// whether to decode frames synchronously
-    #[argh(switch)]
-    pub synchronous: bool,
-
-    /// whether to display the MD5 of the decoded stream, and at which granularity (stream or
-    /// frame)
-    #[argh(option)]
-    pub compute_md5: Option<Md5Computation>,
-
-    /// path to JSON file containing golden MD5 sums of each frame.
-    #[argh(option)]
-    pub golden: Option<PathBuf>,
-}
-
-/// Decide the output file name when multiple_output_files is set
-pub fn decide_output_file_name<'a>(output: &'a Path, index: i32) -> PathBuf {
-    let extract_str = |s: Option<&'a OsStr>| s.and_then(|s| s.to_str()).expect("malformed file");
-
-    let [file_name, stem] = [output.file_name(), output.file_stem()].map(extract_str);
-
-    if output.extension().is_some() {
-        let [extension] = [output.extension()].map(extract_str);
-        let new_file_name = format!("{}_{}.{}", stem, index, extension);
-        PathBuf::from(String::from(output.to_str().unwrap()).replace(file_name, &new_file_name))
-    } else {
-        let new_file_name = format!("{}_{}", stem, index);
-        PathBuf::from(String::from(output.to_str().unwrap()).replace(file_name, &new_file_name))
-    }
-}
diff --git a/examples/ccdec/v4l2_stateless_decoder.rs b/examples/ccdec/v4l2_stateless_decoder.rs
deleted file mode 100644
index 6bf131b..0000000
--- a/examples/ccdec/v4l2_stateless_decoder.rs
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright 2024 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-use std::borrow::Cow;
-use std::fs::File;
-use std::io::Read;
-use std::io::Write;
-
-use cros_codecs::backend::v4l2::decoder::stateless::V4l2StatelessDecoderHandle;
-use cros_codecs::bitstream_utils::NalIterator;
-use cros_codecs::codec::h264::parser::Nalu as H264Nalu;
-use cros_codecs::decoder::stateless::h264::H264;
-use cros_codecs::decoder::stateless::StatelessDecoder;
-use cros_codecs::decoder::stateless::StatelessVideoDecoder;
-use cros_codecs::decoder::BlockingMode;
-use cros_codecs::decoder::DecodedHandle;
-use cros_codecs::decoder::DynDecodedHandle;
-use cros_codecs::multiple_desc_type;
-use cros_codecs::utils::simple_playback_loop;
-use cros_codecs::utils::simple_playback_loop_owned_frames;
-use cros_codecs::utils::DmabufFrame;
-use cros_codecs::utils::UserPtrFrame;
-use cros_codecs::DecodedFormat;
-
-use crate::md5::md5_digest;
-use crate::md5::MD5Context;
-use crate::util::decide_output_file_name;
-use crate::util::Args;
-use crate::util::EncodedFormat;
-use crate::util::FrameMemoryType;
-use crate::util::Md5Computation;
-
-multiple_desc_type! {
-    enum BufferDescriptor {
-        Managed(()),
-        Dmabuf(DmabufFrame),
-        User(UserPtrFrame),
-    }
-}
-
-pub fn do_decode(mut input: File, args: Args) -> () {
-    let input = {
-        let mut buf = Vec::new();
-        input
-            .read_to_end(&mut buf)
-            .expect("error reading input file");
-        buf
-    };
-
-    let mut output = args
-        .output
-        .as_ref()
-        .map(|p| File::create(p).expect("Failed to create output file"));
-
-    let blocking_mode = if args.synchronous {
-        todo!() // BlockingMode::Blocking
-    } else {
-        BlockingMode::NonBlocking
-    };
-
-    let (mut decoder, frame_iter) = match args.input_format {
-        EncodedFormat::H264 => {
-            let frame_iter = Box::new(NalIterator::<H264Nalu>::new(&input))
-                as Box<dyn Iterator<Item = Cow<[u8]>>>;
-
-            let decoder = StatelessDecoder::<H264, _>::new_v4l2(blocking_mode).into_trait_object();
-
-            (decoder, frame_iter)
-        }
-        EncodedFormat::VP8 => todo!(),
-        EncodedFormat::VP9 => todo!(),
-        EncodedFormat::H265 => todo!(),
-        EncodedFormat::AV1 => todo!(),
-    };
-
-    let mut md5_context = MD5Context::new();
-    let mut output_filename_idx = 0;
-    let need_per_frame_md5 = match args.compute_md5 {
-        Some(Md5Computation::Frame) => true,
-        _ => args.golden.is_some(),
-    };
-
-    let mut on_new_frame = |handle: DynDecodedHandle<()>| {
-        let timestamp = handle.timestamp(); //handle.handle.borrow().timestamp;
-        log::debug!("{:<20} {:?}\n", "on_new_frame", timestamp);
-
-        let picture = handle.dyn_picture();
-        let mut handle = picture.dyn_mappable_handle().unwrap();
-        let buffer_size = handle.image_size();
-        let mut frame_data = vec![0; buffer_size];
-
-        handle.read(&mut frame_data).unwrap();
-        log::debug!(
-            "{:<20} {:?}, {} bytes\n",
-            "on_new_frame",
-            timestamp,
-            buffer_size
-        );
-
-        if args.multiple_output_files {
-            let file_name = decide_output_file_name(
-                args.output
-                    .as_ref()
-                    .expect("multiple_output_files need output to be set"),
-                output_filename_idx,
-            );
-
-            let mut output = File::create(file_name).expect("error creating output file");
-            output_filename_idx += 1;
-            output
-                .write_all(&frame_data)
-                .expect("failed to write to output file");
-        } else if let Some(output) = &mut output {
-            output
-                .write_all(&frame_data)
-                .expect("failed to write to output file");
-        }
-
-        let frame_md5: String = if need_per_frame_md5 {
-            md5_digest(&frame_data)
-        } else {
-            "".to_string()
-        };
-
-        match args.compute_md5 {
-            None => (),
-            Some(Md5Computation::Frame) => println!("{}", frame_md5),
-            Some(Md5Computation::Stream) => md5_context.consume(&frame_data),
-        }
-    };
-
-    simple_playback_loop(
-        decoder.as_mut(),
-        frame_iter,
-        &mut on_new_frame,
-        &mut |stream_info, nb_frames| {
-            Ok(match args.frame_memory {
-                FrameMemoryType::Managed => {
-                    simple_playback_loop_owned_frames(stream_info, nb_frames)?
-                        .into_iter()
-                        .collect()
-                }
-                FrameMemoryType::Prime => todo!(),
-                FrameMemoryType::User => todo!(),
-            })
-        },
-        DecodedFormat::NV12,
-        blocking_mode,
-    )
-    .expect("error during playback loop");
-
-    if let Some(Md5Computation::Stream) = args.compute_md5 {
-        println!("{}", md5_context.flush());
-    }
-}
diff --git a/examples/ccdec/vaapi_decoder.rs b/examples/ccdec/vaapi_decoder.rs
deleted file mode 100644
index 6037edb..0000000
--- a/examples/ccdec/vaapi_decoder.rs
+++ /dev/null
@@ -1,396 +0,0 @@
-// Copyright 2023 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-//! ccdec, a simple decoder program using cros-codecs. Capable of computing MD5 checksums from the
-//! input and writing the raw decoded frames to a file.
-
-use std::borrow::Cow;
-use std::fs::File;
-use std::io::Cursor;
-use std::io::Read;
-use std::io::Write;
-use std::os::fd::AsFd;
-use std::os::fd::BorrowedFd;
-use std::path::Path;
-use std::path::PathBuf;
-
-use cros_codecs::bitstream_utils::IvfIterator;
-use cros_codecs::bitstream_utils::NalIterator;
-use cros_codecs::codec::h264::parser::Nalu as H264Nalu;
-use cros_codecs::codec::h265::parser::Nalu as H265Nalu;
-use cros_codecs::decoder::stateless::av1::Av1;
-use cros_codecs::decoder::stateless::h264::H264;
-use cros_codecs::decoder::stateless::h265::H265;
-use cros_codecs::decoder::stateless::vp8::Vp8;
-use cros_codecs::decoder::stateless::vp9::Vp9;
-use cros_codecs::decoder::stateless::StatelessDecoder;
-use cros_codecs::decoder::stateless::StatelessVideoDecoder;
-use cros_codecs::decoder::BlockingMode;
-use cros_codecs::decoder::DecodedHandle;
-use cros_codecs::decoder::DynDecodedHandle;
-use cros_codecs::decoder::StreamInfo;
-use cros_codecs::multiple_desc_type;
-use cros_codecs::utils::simple_playback_loop;
-use cros_codecs::utils::simple_playback_loop_owned_frames;
-use cros_codecs::utils::simple_playback_loop_userptr_frames;
-use cros_codecs::utils::DmabufFrame;
-use cros_codecs::utils::UserPtrFrame;
-use cros_codecs::DecodedFormat;
-use cros_codecs::Fourcc;
-use cros_codecs::FrameLayout;
-use cros_codecs::PlaneLayout;
-use cros_codecs::Resolution;
-use matroska_demuxer::Frame;
-use matroska_demuxer::MatroskaFile;
-
-use crate::md5::md5_digest;
-use crate::md5::MD5Context;
-use crate::util::decide_output_file_name;
-use crate::util::Args;
-use crate::util::EncodedFormat;
-use crate::util::FrameMemoryType;
-use crate::util::Md5Computation;
-
-// Our buffer descriptor type.
-//
-// We support buffers which memory is managed by the backend, or imported from user memory or a
-// PRIME buffer.
-multiple_desc_type! {
-    enum BufferDescriptor {
-        Managed(()),
-        Dmabuf(DmabufFrame),
-        User(UserPtrFrame),
-    }
-}
-
-/// Export a file descriptor from a GBM `BufferObject` and turn it into a `DmabufFrame` suitable
-/// for using as the target of a decoder.
-fn export_gbm_bo<T>(obj: &gbm::BufferObject<T>) -> anyhow::Result<DmabufFrame> {
-    let fd = obj.fd()?;
-    let modifier = obj.modifier()?;
-    let format = obj.format()?;
-    let planes = (0..obj.plane_count()? as i32)
-        .map(|i| PlaneLayout {
-            buffer_index: 0,
-            offset: obj.offset(i).unwrap() as usize,
-            stride: obj.stride_for_plane(i).unwrap() as usize,
-        })
-        .collect();
-    let size = Resolution::from((obj.width().unwrap(), obj.height().unwrap()));
-
-    Ok(DmabufFrame {
-        fds: vec![fd],
-        layout: FrameLayout {
-            format: (Fourcc::from(format as u32), modifier.into()),
-            size,
-            planes,
-        },
-    })
-}
-
-/// Buffer allocation callback for `simple_playback_loop` to allocate and export buffers from a GBM
-/// device.
-fn simple_playback_loop_prime_frames<D: AsFd>(
-    device: &gbm::Device<D>,
-    stream_info: &StreamInfo,
-    nb_frames: usize,
-) -> anyhow::Result<Vec<DmabufFrame>> {
-    let gbm_fourcc = match stream_info.format {
-        DecodedFormat::I420 | DecodedFormat::NV12 => gbm::Format::Nv12,
-        _ => anyhow::bail!(
-            "{:?} format is unsupported with GBM memory",
-            stream_info.format
-        ),
-    };
-
-    (0..nb_frames)
-        .map(|_| {
-            device
-                .create_buffer_object::<()>(
-                    stream_info.coded_resolution.width,
-                    stream_info.coded_resolution.height,
-                    gbm_fourcc,
-                    gbm::BufferObjectFlags::SCANOUT,
-                )
-                .map_err(|e| anyhow::anyhow!(e))
-                .and_then(|o| export_gbm_bo(&o))
-        })
-        .collect()
-}
-struct MkvFrameIterator<T: AsRef<[u8]>> {
-    input: MatroskaFile<Cursor<T>>,
-    video_track: u64,
-}
-
-impl<T: AsRef<[u8]>> MkvFrameIterator<T> {
-    fn new(input: T) -> anyhow::Result<Self> {
-        let input = MatroskaFile::open(Cursor::new(input))?;
-        let video_track = input
-            .tracks()
-            .iter()
-            .find(|t| t.track_type() == matroska_demuxer::TrackType::Video)
-            .map(|t| t.track_number().get())
-            .ok_or_else(|| anyhow::anyhow!("no video track in input file"))?;
-
-        Ok(Self { input, video_track })
-    }
-}
-
-impl<T: AsRef<[u8]>> Iterator for MkvFrameIterator<T> {
-    type Item = Vec<u8>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        let mut frame = Frame::default();
-        while self.input.next_frame(&mut frame).unwrap() {
-            if frame.track == self.video_track {
-                return Some(frame.data);
-            }
-        }
-
-        None
-    }
-}
-
-/// Detects the container type (IVF or MKV) and returns the corresponding frame iterator.
-fn create_vpx_frame_iterator(input: &[u8]) -> Box<dyn Iterator<Item = Cow<[u8]>> + '_> {
-    if input.starts_with(&[0x1a, 0x45, 0xdf, 0xa3]) {
-        Box::new(MkvFrameIterator::new(input).unwrap().map(Cow::Owned))
-    } else {
-        Box::new(IvfIterator::new(input).map(Cow::Borrowed))
-    }
-}
-
-pub fn do_decode(mut input: File, args: Args) -> () {
-    let input = {
-        let mut buf = Vec::new();
-        input
-            .read_to_end(&mut buf)
-            .expect("error reading input file");
-        buf
-    };
-
-    let mut output = if !args.multiple_output_files {
-        args.output
-            .as_ref()
-            .map(|p| File::create(p).expect("error creating output file"))
-    } else {
-        None
-    };
-
-    let blocking_mode = if args.synchronous {
-        BlockingMode::Blocking
-    } else {
-        BlockingMode::NonBlocking
-    };
-
-    let golden_md5s: Vec<String> = match args.golden {
-        None => vec![],
-        Some(ref path) => {
-            let mut golden_file_content = String::new();
-            File::open(&path)
-                .expect("error opening golden file")
-                .read_to_string(&mut golden_file_content)
-                .expect("error reading golden file");
-            let parsed_json: serde_json::Value =
-                serde_json::from_str(&golden_file_content).expect("error parsing golden file");
-            match &parsed_json["md5_checksums"] {
-                serde_json::Value::Array(checksums) => checksums
-                    .iter()
-                    .map(|x| match x {
-                        serde_json::Value::String(checksum) => String::from(checksum),
-                        _ => panic!("error parsing golden file"),
-                    })
-                    .collect(),
-                _ => panic!("error parsing golden file"),
-            }
-        }
-    };
-    let mut golden_iter = golden_md5s.iter();
-
-    let gbm = match args.frame_memory {
-        FrameMemoryType::Managed | FrameMemoryType::User => None,
-        FrameMemoryType::Prime => {
-            /// A simple wrapper for a GBM device node.
-            pub struct GbmDevice(std::fs::File);
-
-            impl AsFd for GbmDevice {
-                fn as_fd(&self) -> BorrowedFd<'_> {
-                    self.0.as_fd()
-                }
-            }
-            impl drm::Device for GbmDevice {}
-
-            /// Simple helper methods for opening a `Card`.
-            impl GbmDevice {
-                pub fn open<P: AsRef<Path>>(path: P) -> std::io::Result<Self> {
-                    std::fs::OpenOptions::new()
-                        .read(true)
-                        .write(true)
-                        .open(path)
-                        .map(GbmDevice)
-                }
-            }
-
-            let gbm_path = args
-                .gbm_device
-                .unwrap_or(PathBuf::from("/dev/dri/renderD128"));
-            let gbm = GbmDevice::open(gbm_path)
-                .and_then(gbm::Device::new)
-                .expect("failed to create GBM device");
-
-            Some(gbm)
-        }
-    };
-
-    let display = match args.libva_device {
-        Some(libva_device_path) => libva::Display::open_drm_display(libva_device_path.clone())
-            .expect(&format!(
-                "failed to open libva display {}",
-                libva_device_path.display()
-            )),
-        None => libva::Display::open().expect("failed to open libva display"),
-    };
-
-    // The created `decoder` is turned into a `DynStatelessVideoDecoder` trait object. This allows
-    // the same code to control the decoder no matter what codec or backend we are using.
-    let (mut decoder, frame_iter) = match args.input_format {
-        EncodedFormat::H264 => {
-            let frame_iter = Box::new(NalIterator::<H264Nalu>::new(&input))
-                as Box<dyn Iterator<Item = Cow<[u8]>>>;
-
-            let decoder = StatelessDecoder::<H264, _>::new_vaapi(display, blocking_mode)
-                .unwrap()
-                .into_trait_object();
-
-            (decoder, frame_iter)
-        }
-        EncodedFormat::VP8 => {
-            let frame_iter = create_vpx_frame_iterator(&input);
-
-            let decoder = StatelessDecoder::<Vp8, _>::new_vaapi(display, blocking_mode)
-                .unwrap()
-                .into_trait_object();
-
-            (decoder, frame_iter)
-        }
-        EncodedFormat::VP9 => {
-            let frame_iter = create_vpx_frame_iterator(&input);
-
-            let decoder = StatelessDecoder::<Vp9, _>::new_vaapi(display, blocking_mode)
-                .unwrap()
-                .into_trait_object();
-
-            (decoder, frame_iter)
-        }
-        EncodedFormat::H265 => {
-            let frame_iter = Box::new(NalIterator::<H265Nalu>::new(&input))
-                as Box<dyn Iterator<Item = Cow<[u8]>>>;
-
-            let decoder = StatelessDecoder::<H265, _>::new_vaapi(display, blocking_mode)
-                .unwrap()
-                .into_trait_object();
-
-            (decoder, frame_iter)
-        }
-        EncodedFormat::AV1 => {
-            let frame_iter = create_vpx_frame_iterator(&input);
-
-            let decoder = StatelessDecoder::<Av1, _>::new_vaapi(display, blocking_mode)
-                .unwrap()
-                .into_trait_object();
-
-            (decoder, frame_iter)
-        }
-    };
-
-    let mut md5_context = MD5Context::new();
-    let mut output_filename_idx = 0;
-    let need_per_frame_md5 = match args.compute_md5 {
-        Some(Md5Computation::Frame) => true,
-        _ => args.golden.is_some(),
-    };
-
-    let mut on_new_frame = |handle: DynDecodedHandle<BufferDescriptor>| {
-        if args.output.is_some() || args.compute_md5.is_some() || args.golden.is_some() {
-            handle.sync().unwrap();
-            let picture = handle.dyn_picture();
-            let mut handle = picture.dyn_mappable_handle().unwrap();
-            let buffer_size = handle.image_size();
-            let mut frame_data = vec![0; buffer_size];
-            handle.read(&mut frame_data).unwrap();
-
-            if args.multiple_output_files {
-                let file_name = decide_output_file_name(
-                    args.output
-                        .as_ref()
-                        .expect("multiple_output_files need output to be set"),
-                    output_filename_idx,
-                );
-
-                let mut output = File::create(file_name).expect("error creating output file");
-                output_filename_idx += 1;
-                output
-                    .write_all(&frame_data)
-                    .expect("failed to write to output file");
-            } else if let Some(output) = &mut output {
-                output
-                    .write_all(&frame_data)
-                    .expect("failed to write to output file");
-            }
-
-            let frame_md5: String = if need_per_frame_md5 {
-                md5_digest(&frame_data)
-            } else {
-                "".to_string()
-            };
-
-            match args.compute_md5 {
-                None => (),
-                Some(Md5Computation::Frame) => println!("{}", frame_md5),
-                Some(Md5Computation::Stream) => md5_context.consume(&frame_data),
-            }
-
-            if args.golden.is_some() {
-                assert_eq!(&frame_md5, golden_iter.next().unwrap());
-            }
-        }
-    };
-
-    simple_playback_loop(
-        decoder.as_mut(),
-        frame_iter,
-        &mut on_new_frame,
-        &mut |stream_info, nb_frames| {
-            Ok(match args.frame_memory {
-                FrameMemoryType::Managed => {
-                    simple_playback_loop_owned_frames(stream_info, nb_frames)?
-                        .into_iter()
-                        .map(BufferDescriptor::Managed)
-                        .collect()
-                }
-                FrameMemoryType::Prime => simple_playback_loop_prime_frames(
-                    gbm.as_ref().unwrap(),
-                    stream_info,
-                    nb_frames,
-                )?
-                .into_iter()
-                .map(BufferDescriptor::Dmabuf)
-                .collect(),
-                FrameMemoryType::User => {
-                    simple_playback_loop_userptr_frames(stream_info, nb_frames)?
-                        .into_iter()
-                        .map(BufferDescriptor::User)
-                        .collect()
-                }
-            })
-        },
-        args.output_format,
-        blocking_mode,
-    )
-    .expect("error during playback loop");
-
-    if let Some(Md5Computation::Stream) = args.compute_md5 {
-        println!("{}", md5_context.flush());
-    }
-}
diff --git a/examples/ccenc/main.rs b/examples/ccenc/main.rs
deleted file mode 100644
index 8563827..0000000
--- a/examples/ccenc/main.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2024 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-use std::fs::File;
-
-mod util;
-use util::Args;
-
-#[cfg(feature = "vaapi")]
-mod vaapi_encoder;
-#[cfg(feature = "vaapi")]
-use vaapi_encoder::do_encode;
-
-#[cfg(feature = "v4l2")]
-mod v4l2_stateful_encoder;
-#[cfg(feature = "v4l2")]
-use v4l2_stateful_encoder::do_encode;
-
-fn main() {
-    env_logger::init();
-
-    let args: Args = argh::from_env();
-
-    let input = File::open(&args.input).expect("error opening input file");
-
-    do_encode(input, args);
-}
diff --git a/examples/ccenc/util.rs b/examples/ccenc/util.rs
deleted file mode 100644
index ab7558a..0000000
--- a/examples/ccenc/util.rs
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2024 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-use std::path::PathBuf;
-use std::str::FromStr;
-
-use argh::FromArgs;
-
-use cros_codecs::DecodedFormat;
-
-#[derive(Debug, PartialEq, Eq, Copy, Clone, Default)]
-pub enum Codec {
-    #[default]
-    H264,
-    H265,
-    VP8,
-    VP9,
-    AV1,
-}
-
-impl FromStr for Codec {
-    type Err = &'static str;
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        match s {
-            "h264" | "H264" => Ok(Self::H264),
-            "h265" | "H265" => Ok(Self::H265),
-            "vp8" | "VP8" => Ok(Self::VP8),
-            "vp9" | "VP9" => Ok(Self::VP9),
-            "av1" | "AV1" => Ok(Self::AV1),
-            _ => Err("unrecognized codec. Valid values: h264, h265, vp8, vp9, av1"),
-        }
-    }
-}
-
-/// Simple encoder
-#[derive(Debug, FromArgs)]
-pub struct Args {
-    /// input file
-    #[argh(positional)]
-    pub input: PathBuf,
-
-    /// input frames width
-    #[argh(option)]
-    pub width: u32,
-
-    /// input frames height
-    #[argh(option)]
-    pub height: u32,
-
-    /// input frame coded width
-    #[argh(option)]
-    pub coded_width: Option<u32>,
-
-    /// input frame coded height
-    #[argh(option)]
-    pub coded_height: Option<u32>,
-
-    /// input frames count
-    #[argh(option)]
-    pub count: usize,
-
-    /// input fourcc
-    #[argh(option)]
-    pub fourcc: DecodedFormat,
-
-    /// codec
-    #[argh(option)]
-    pub codec: Option<Codec>,
-
-    /// framerate
-    #[argh(option, default = "30")]
-    pub framerate: u32,
-
-    /// bitrate
-    #[argh(option, default = "200000")]
-    pub bitrate: u64,
-
-    /// output file to write the decoded frames to
-    #[argh(option)]
-    pub output: Option<PathBuf>,
-
-    /// set to true if low power version of the API shall be used
-    #[argh(switch)]
-    pub low_power: bool,
-}
diff --git a/examples/ccenc/v4l2_stateful_encoder.rs b/examples/ccenc/v4l2_stateful_encoder.rs
deleted file mode 100644
index e261a0c..0000000
--- a/examples/ccenc/v4l2_stateful_encoder.rs
+++ /dev/null
@@ -1,396 +0,0 @@
-// Copyright 2024 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-use std::fs::File;
-use std::io::Write;
-use std::os::unix::prelude::FileExt;
-use std::sync::Arc;
-
-use cros_codecs::backend::v4l2::encoder::find_device_with_capture;
-use cros_codecs::backend::v4l2::encoder::v4l2_format_to_frame_layout;
-use cros_codecs::backend::v4l2::encoder::EncoderCodec;
-use cros_codecs::backend::v4l2::encoder::MmapingCapture;
-use cros_codecs::backend::v4l2::encoder::OutputBuffer;
-use cros_codecs::backend::v4l2::encoder::OutputBufferHandle;
-use cros_codecs::backend::v4l2::encoder::V4L2Backend;
-use cros_codecs::bitstream_utils::IvfFileHeader;
-use cros_codecs::bitstream_utils::IvfFrameHeader;
-use cros_codecs::encoder::simple_encode_loop;
-use cros_codecs::encoder::stateful::h264::v4l2::V4L2StatefulH264Encoder;
-use cros_codecs::encoder::stateful::h265::v4l2::V4L2StatefulH265Encoder;
-use cros_codecs::encoder::stateful::vp8::v4l2::V4L2StatefulVP8Encoder;
-use cros_codecs::encoder::stateful::vp9::v4l2::V4L2StatefulVP9Encoder;
-use cros_codecs::encoder::stateful::StatefulEncoder;
-use cros_codecs::encoder::CodedBitstreamBuffer;
-use cros_codecs::encoder::FrameMetadata;
-use cros_codecs::encoder::RateControl;
-use cros_codecs::encoder::Tunings;
-use cros_codecs::image_processing::extend_border_nv12;
-use cros_codecs::image_processing::i420_to_nv12_chroma;
-use cros_codecs::image_processing::nv12_copy;
-use cros_codecs::DecodedFormat;
-use cros_codecs::Fourcc;
-use cros_codecs::FrameLayout;
-use cros_codecs::Resolution;
-
-use v4l2r::device::Device;
-use v4l2r::device::DeviceConfig;
-use v4l2r::memory::MmapHandle;
-
-use crate::util::Args;
-use crate::util::Codec;
-
-// "Handle" abstraction for this particular use case. All Encoders take a "Handle" generic type
-// that implements the OutputBufferHandle trait, which basically just tells the Encoder how the
-// frame data is going to be loaded into the V4L2 output buffers. This is where we add the code to
-// load the frames from the disk. Technically we could do the disk load in DiskFrameReader and
-// then pass regular u8 buffers to |queue()|, but this way avoids a copy.
-struct MmapNM12Frame<'a> {
-    resolution: Resolution,
-    file: &'a File,
-    input_fourcc: DecodedFormat,
-    pos: u64,
-    input_coded_resolution: Resolution,
-    queue_layout: FrameLayout,
-}
-
-impl OutputBufferHandle for MmapNM12Frame<'_> {
-    type PrimitiveBufferHandles = Vec<MmapHandle>;
-
-    fn queue(self, buffer: OutputBuffer<'_, Self::PrimitiveBufferHandles>) -> anyhow::Result<()> {
-        let mut input_y = vec![0u8; self.input_coded_resolution.get_area()];
-        let mut input_uv = vec![0u8; self.input_coded_resolution.get_area() / 2];
-
-        // Use |read_at()| instead of |read()| so we don't need to take a mutable reference to the
-        // File. We don't know how many in flight OutputBufferHandles will be created in advance,
-        // and we can only mutably borrow once. We could get around this with an Rc RefCell or an
-        // Arc if we decide we need to use |read()| because we want to support non-POSIX platforms.
-        assert_eq!(
-            self.file
-                .read_at(input_y.as_mut_slice(), self.pos)
-                .expect("Unexpected EOF!"),
-            self.input_coded_resolution.get_area()
-        );
-
-        match self.input_fourcc {
-            DecodedFormat::NV12 => {
-                assert_eq!(
-                    self.file
-                        .read_at(
-                            input_uv.as_mut_slice(),
-                            self.pos + self.input_coded_resolution.get_area() as u64
-                        )
-                        .expect("Unexpected EOF!"),
-                    self.input_coded_resolution.get_area() / 2
-                );
-            }
-            DecodedFormat::I420 => {
-                let mut input_u = vec![0u8; self.input_coded_resolution.get_area() / 4];
-                let mut input_v = vec![0u8; self.input_coded_resolution.get_area() / 4];
-                assert_eq!(
-                    self.file
-                        .read_at(
-                            input_u.as_mut_slice(),
-                            self.pos + self.input_coded_resolution.get_area() as u64
-                        )
-                        .expect("Unexpected EOF!"),
-                    self.input_coded_resolution.get_area() / 4
-                );
-                assert_eq!(
-                    self.file
-                        .read_at(
-                            input_v.as_mut_slice(),
-                            self.pos + self.input_coded_resolution.get_area() as u64 * 5 / 4
-                        )
-                        .expect("Unexpected EOF!"),
-                    self.input_coded_resolution.get_area() / 4
-                );
-                i420_to_nv12_chroma(
-                    input_u.as_slice(),
-                    input_v.as_slice(),
-                    input_uv.as_mut_slice(),
-                );
-            }
-            _ => panic!("Unsupported input format!"),
-        };
-
-        let mut y_plane = buffer.get_plane_mapping(0).unwrap();
-        let mut uv_plane = buffer.get_plane_mapping(1).unwrap();
-        nv12_copy(
-            input_y.as_slice(),
-            self.input_coded_resolution.width as usize,
-            y_plane.as_mut(),
-            self.queue_layout.planes[0].stride,
-            input_uv.as_slice(),
-            self.input_coded_resolution.width as usize,
-            uv_plane.as_mut(),
-            self.queue_layout.planes[1].stride,
-            self.resolution.width as usize,
-            self.resolution.height as usize,
-        );
-        extend_border_nv12(
-            y_plane.as_mut(),
-            uv_plane.as_mut(),
-            self.resolution.width as usize,
-            self.resolution.height as usize,
-            self.queue_layout.planes[0].stride as usize,
-            self.queue_layout.size.height as usize,
-        );
-
-        buffer.queue(&[y_plane.len(), uv_plane.len()])?;
-        Ok(())
-    }
-}
-
-// Generator for MmapNM12Frames. Note that we do the actual loading from disk in |queue()|, this
-// just basically just keeps track of offsets.
-struct DiskFrameReader<'a> {
-    file: &'a File,
-    input_fourcc: DecodedFormat,
-    visible_size: Resolution,
-    input_coded_size: Resolution,
-    layout: FrameLayout,
-    pos: u64,
-    frame_num: usize,
-    total_frames: usize,
-}
-
-impl<'a> Iterator for DiskFrameReader<'a> {
-    type Item = (FrameMetadata, MmapNM12Frame<'a>);
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if self.frame_num >= self.total_frames {
-            return None;
-        }
-
-        let meta = FrameMetadata {
-            timestamp: self.frame_num as u64,
-            layout: self.layout.clone(),
-            force_keyframe: false,
-        };
-
-        let handle = MmapNM12Frame {
-            resolution: self.visible_size,
-            file: &self.file,
-            input_fourcc: self.input_fourcc.clone(),
-            pos: self.pos,
-            input_coded_resolution: self.input_coded_size,
-            queue_layout: self.layout.clone(),
-        };
-
-        self.frame_num += 1;
-        // The 3/2 is an implicit assumption about 4:2:0 subsampling. We probably don't need to
-        // support other subsampling methods, but if we do, make sure to change this line!
-        self.pos += self.input_coded_size.get_area() as u64 * 3 / 2;
-
-        Some((meta, handle))
-    }
-}
-
-impl<'a> DiskFrameReader<'_> {
-    pub fn new(
-        file: &'a File,
-        input_fourcc: DecodedFormat,
-        visible_size: Resolution,
-        input_coded_size: Resolution,
-        layout: FrameLayout,
-        total_frames: usize,
-    ) -> DiskFrameReader<'a> {
-        DiskFrameReader {
-            file: file,
-            input_fourcc: input_fourcc,
-            visible_size: visible_size,
-            input_coded_size: input_coded_size,
-            layout: layout,
-            pos: 0,
-            frame_num: 0,
-            total_frames: total_frames,
-        }
-    }
-}
-
-// V4L2 stateful decoders are all of the form "StatefulEncoder<Handle, V4L2Backend<Handle,
-// CaptureBufferz, Codec>>". Since we know that all the encoders in this file are going to be V4L2
-// stateful and we know the Handle type is going to be MmapNM12Frame, we can alias this type a
-// little bit to make the signature smaller.
-type MmapEncoder<'a, Codec> =
-    StatefulEncoder<MmapNM12Frame<'a>, V4L2Backend<MmapNM12Frame<'a>, MmapingCapture, Codec>>;
-
-fn codec_to_pixelformat(codec: Codec) -> v4l2r::PixelFormat {
-    match codec {
-        Codec::H264 => v4l2r::PixelFormat::from_fourcc(b"H264"),
-        Codec::H265 => v4l2r::PixelFormat::from_fourcc(b"HEVC"),
-        Codec::VP9 => v4l2r::PixelFormat::from_fourcc(b"VP90"),
-        Codec::VP8 => v4l2r::PixelFormat::from_fourcc(b"VP80"),
-        _ => panic!("Unsupported format!"),
-    }
-}
-
-fn codec_to_ivf_magic(codec: Codec) -> [u8; 4] {
-    match codec {
-        // Note that H264 does not generally use IVF containers.
-        Codec::VP8 => IvfFileHeader::CODEC_VP8,
-        Codec::VP9 => IvfFileHeader::CODEC_VP9,
-        _ => panic!("Unsupported format!"),
-    }
-}
-
-fn do_encode_loop<'a, Codecz>(
-    mut encoder: MmapEncoder<'a, Codecz>,
-    input: &'a File,
-    args: Args,
-) -> ()
-where
-    V4L2Backend<MmapNM12Frame<'a>, MmapingCapture, Codecz>: EncoderCodec,
-{
-    // This is the part where we G_FMT to get the actual dimensions of the queue buffers.
-    let layout = v4l2_format_to_frame_layout(&encoder.backend().output_format().unwrap());
-
-    let mut frame_reader = DiskFrameReader::new(
-        &input,
-        args.fourcc,
-        (args.width, args.height).into(),
-        (
-            args.coded_width.unwrap_or(args.width),
-            args.coded_height.unwrap_or(args.height),
-        )
-            .into(),
-        layout,
-        args.count,
-    );
-
-    let codec = args.codec.unwrap_or_default();
-    let output_file = args.output.map(|path| {
-        let mut output = File::create(path).expect("Error opening output file!");
-
-        if codec != Codec::H264 {
-            let hdr = IvfFileHeader::new(
-                codec_to_ivf_magic(codec),
-                args.width as u16,
-                args.height as u16,
-                args.framerate,
-                args.count as u32,
-            );
-            hdr.writo_into(&mut output)
-                .expect("Error writing IVF file header!");
-        }
-
-        output
-    });
-
-    // Unwrapping an optional takes ownership of it, so we do that outside of the lambda so we
-    // don't violate FnMut's lifetime requirements.
-    match output_file {
-        Some(mut output_file) => {
-            let frame_consumer = |coded_chunk: CodedBitstreamBuffer| {
-                if codec != Codec::H264 {
-                    let hdr = IvfFrameHeader {
-                        timestamp: coded_chunk.metadata.timestamp,
-                        frame_size: coded_chunk.bitstream.len() as u32,
-                    };
-                    hdr.writo_into(&mut output_file)
-                        .expect("Error writing IVF frame header!");
-                }
-
-                let _ = output_file
-                    .write(&coded_chunk.bitstream[..])
-                    .expect("Error writing output file!");
-            };
-            simple_encode_loop(&mut encoder, &mut frame_reader, frame_consumer)
-                .expect("Failed to encode!");
-        }
-        None => {
-            simple_encode_loop(&mut encoder, &mut frame_reader, |_| ()).expect("Failed to encode!")
-        }
-    };
-}
-
-pub fn do_encode(input: File, args: Args) -> () {
-    let codec = args.codec.unwrap_or_default();
-    let device = find_device_with_capture(codec_to_pixelformat(codec))
-        .expect("Could not find an encoder for codec");
-    let device = Device::open(&device, DeviceConfig::new().non_blocking_dqbuf()).expect("open");
-    let device = Arc::new(device);
-
-    let resolution = Resolution {
-        width: args.width,
-        height: args.height,
-    };
-    let queue_fourcc = Fourcc::from(b"NM12");
-    let tunings: Tunings = Tunings {
-        rate_control: RateControl::ConstantBitrate(args.bitrate),
-        framerate: args.framerate,
-        ..Default::default()
-    };
-
-    match codec {
-        Codec::H264 => do_encode_loop(
-            V4L2StatefulH264Encoder::new(
-                device,
-                MmapingCapture,
-                cros_codecs::encoder::h264::EncoderConfig {
-                    resolution: resolution.clone(),
-                    initial_tunings: tunings.clone(),
-                    ..Default::default()
-                },
-                queue_fourcc,
-                resolution,
-                tunings,
-            )
-            .expect("Failed to create encoder"),
-            &input,
-            args,
-        ),
-        Codec::H265 => do_encode_loop(
-            V4L2StatefulH265Encoder::new(
-                device,
-                MmapingCapture,
-                cros_codecs::encoder::h265::EncoderConfig {
-                    resolution: resolution.clone(),
-                    ..Default::default()
-                },
-                queue_fourcc,
-                resolution,
-                tunings,
-            )
-            .expect("Failed to create encoder"),
-            &input,
-            args,
-        ),
-        Codec::VP8 => do_encode_loop(
-            V4L2StatefulVP8Encoder::new(
-                device,
-                MmapingCapture,
-                cros_codecs::encoder::vp8::EncoderConfig {
-                    resolution: resolution.clone(),
-                    ..Default::default()
-                },
-                queue_fourcc,
-                resolution,
-                tunings,
-            )
-            .expect("Failed to create encoder"),
-            &input,
-            args,
-        ),
-        Codec::VP9 => do_encode_loop(
-            V4L2StatefulVP9Encoder::new(
-                device,
-                MmapingCapture,
-                cros_codecs::encoder::vp9::EncoderConfig {
-                    resolution: resolution.clone(),
-                    initial_tunings: tunings.clone(),
-                    ..Default::default()
-                },
-                queue_fourcc,
-                resolution,
-                tunings,
-            )
-            .expect("Failed to create encoder"),
-            &input,
-            args,
-        ),
-        _ => panic!("Unsupported format!"),
-    };
-}
diff --git a/examples/ccenc/vaapi_encoder.rs b/examples/ccenc/vaapi_encoder.rs
deleted file mode 100644
index f3596bd..0000000
--- a/examples/ccenc/vaapi_encoder.rs
+++ /dev/null
@@ -1,318 +0,0 @@
-// Copyright 2024 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-use std::borrow::Borrow;
-use std::fs::File;
-use std::io::Read;
-use std::io::Write;
-use std::rc::Rc;
-
-use cros_codecs::backend::vaapi::surface_pool::PooledVaSurface;
-use cros_codecs::backend::vaapi::surface_pool::VaSurfacePool;
-use cros_codecs::bitstream_utils::IvfFileHeader;
-use cros_codecs::bitstream_utils::IvfFrameHeader;
-use cros_codecs::decoder::FramePool;
-use cros_codecs::encoder::av1::EncoderConfig as AV1EncoderConfig;
-use cros_codecs::encoder::h264::EncoderConfig as H264EncoderConfig;
-use cros_codecs::encoder::stateless::av1;
-use cros_codecs::encoder::stateless::h264;
-use cros_codecs::encoder::stateless::vp9;
-use cros_codecs::encoder::vp9::EncoderConfig as VP9EncoderConfig;
-use cros_codecs::encoder::FrameMetadata;
-use cros_codecs::encoder::RateControl;
-use cros_codecs::encoder::Tunings;
-use cros_codecs::encoder::VideoEncoder;
-use cros_codecs::image_processing::extend_border_nv12;
-use cros_codecs::image_processing::i420_to_nv12_chroma;
-use cros_codecs::image_processing::nv12_copy;
-use cros_codecs::BlockingMode;
-use cros_codecs::DecodedFormat;
-use cros_codecs::Fourcc;
-use cros_codecs::FrameLayout;
-use cros_codecs::PlaneLayout;
-use cros_codecs::Resolution;
-
-use crate::util::Args;
-use crate::util::Codec;
-
-fn upload_img<M: libva::SurfaceMemoryDescriptor>(
-    display: &Rc<libva::Display>,
-    surface: &libva::Surface<M>,
-    resolution: Resolution,
-    input_coded_resolution: Resolution,
-    data: &[u8],
-    input_fourcc: DecodedFormat,
-) -> FrameLayout {
-    let input_y = &data[0..input_coded_resolution.get_area()];
-    let mut tmp_input_uv: Vec<u8> = Vec::new();
-    let input_uv = match input_fourcc {
-        DecodedFormat::NV12 => {
-            &data[input_coded_resolution.get_area()..(input_coded_resolution.get_area() * 3 / 2)]
-        }
-        DecodedFormat::I420 => {
-            tmp_input_uv.resize(input_coded_resolution.get_area() / 2, 0);
-            let input_u = &data
-                [input_coded_resolution.get_area()..(input_coded_resolution.get_area() * 5 / 4)];
-            let input_v = &data[(input_coded_resolution.get_area() * 5 / 4)
-                ..(input_coded_resolution.get_area() * 3 / 2)];
-            i420_to_nv12_chroma(input_u, input_v, tmp_input_uv.as_mut_slice());
-            tmp_input_uv.as_slice()
-        }
-        _ => panic!("Unsupported input format!"),
-    };
-
-    let image_fmts = display.query_image_formats().unwrap();
-    let image_fmt = image_fmts
-        .into_iter()
-        .find(|f| f.fourcc == libva::VA_FOURCC_NV12)
-        .unwrap();
-    let mut image = libva::Image::create_from(
-        surface,
-        image_fmt,
-        (resolution.width, resolution.height),
-        (resolution.width, resolution.height),
-    )
-    .unwrap();
-    let va_image = *image.image();
-    let dst = image.as_mut();
-    let (dst_y, dst_uv) =
-        (&mut dst[va_image.offsets[0] as usize..]).split_at_mut(va_image.offsets[1] as usize);
-
-    nv12_copy(
-        input_y,
-        input_coded_resolution.width as usize,
-        dst_y,
-        va_image.pitches[0] as usize,
-        input_uv,
-        input_coded_resolution.width as usize,
-        dst_uv,
-        va_image.pitches[1] as usize,
-        resolution.width as usize,
-        resolution.height as usize,
-    );
-    extend_border_nv12(
-        dst_y,
-        dst_uv,
-        resolution.width as usize,
-        resolution.height as usize,
-        va_image.pitches[0] as usize,
-        va_image.height as usize,
-    );
-
-    drop(image);
-
-    surface.sync().unwrap();
-
-    FrameLayout {
-        format: (Fourcc::from(b"NV12"), 0),
-        size: resolution,
-        planes: vec![
-            PlaneLayout {
-                buffer_index: 0,
-                offset: va_image.offsets[0] as usize,
-                stride: va_image.pitches[0] as usize,
-            },
-            PlaneLayout {
-                buffer_index: 0,
-                offset: va_image.offsets[1] as usize,
-                stride: va_image.pitches[1] as usize,
-            },
-        ],
-    }
-}
-
-fn new_h264_vaapi_encoder(
-    args: &Args,
-    display: &Rc<libva::Display>,
-) -> Box<dyn VideoEncoder<PooledVaSurface<()>>> {
-    let resolution = Resolution {
-        width: args.width,
-        height: args.height,
-    };
-
-    let config = H264EncoderConfig {
-        resolution,
-        initial_tunings: Tunings {
-            framerate: args.framerate,
-            rate_control: RateControl::ConstantBitrate(args.bitrate),
-            ..Default::default()
-        },
-        ..Default::default()
-    };
-
-    let fourcc = b"NV12".into();
-    let encoder = h264::StatelessEncoder::new_vaapi(
-        Rc::clone(display),
-        config,
-        fourcc,
-        resolution,
-        args.low_power,
-        BlockingMode::Blocking,
-    )
-    .expect("Unable to create encoder");
-
-    Box::new(encoder)
-}
-
-fn new_vp9_vaapi_encoder(
-    args: &Args,
-    display: &Rc<libva::Display>,
-) -> Box<dyn VideoEncoder<PooledVaSurface<()>>> {
-    let resolution = Resolution {
-        width: args.width,
-        height: args.height,
-    };
-
-    let config = VP9EncoderConfig {
-        resolution,
-        initial_tunings: Tunings {
-            framerate: args.framerate,
-            rate_control: RateControl::ConstantBitrate(args.bitrate),
-            ..Default::default()
-        },
-        ..Default::default()
-    };
-
-    let fourcc = b"NV12".into();
-    let encoder = vp9::StatelessEncoder::new_vaapi(
-        Rc::clone(display),
-        config,
-        fourcc,
-        resolution,
-        args.low_power,
-        BlockingMode::Blocking,
-    )
-    .expect("Unable to create encoder");
-
-    Box::new(encoder)
-}
-
-fn new_av1_vaapi_encoder(
-    args: &Args,
-    display: &Rc<libva::Display>,
-) -> Box<dyn VideoEncoder<PooledVaSurface<()>>> {
-    let resolution = Resolution {
-        width: args.width,
-        height: args.height,
-    };
-
-    let config = AV1EncoderConfig {
-        resolution,
-        initial_tunings: Tunings {
-            framerate: args.framerate,
-            rate_control: RateControl::ConstantBitrate(args.bitrate),
-            ..Default::default()
-        },
-        ..Default::default()
-    };
-
-    let fourcc = b"NV12".into();
-    let encoder = av1::StatelessEncoder::new_vaapi(
-        Rc::clone(display),
-        config,
-        fourcc,
-        resolution,
-        args.low_power,
-        BlockingMode::Blocking,
-    )
-    .expect("Unable to create encoder");
-
-    Box::new(encoder)
-}
-
-pub fn do_encode(mut input: File, args: Args) -> () {
-    let display = libva::Display::open().unwrap();
-
-    let codec = args.codec.unwrap_or_default();
-
-    let mut encoder = match codec {
-        Codec::H264 => new_h264_vaapi_encoder(&args, &display),
-        Codec::VP9 => new_vp9_vaapi_encoder(&args, &display),
-        Codec::AV1 => new_av1_vaapi_encoder(&args, &display),
-        _ => panic!("Unsupported format!"),
-    };
-
-    let mut pool = VaSurfacePool::new(
-        Rc::clone(&display),
-        libva::VA_RT_FORMAT_YUV420,
-        Some(libva::UsageHint::USAGE_HINT_ENCODER),
-        Resolution {
-            width: args.width,
-            height: args.height,
-        },
-    );
-
-    pool.add_frames(vec![(); 16]).unwrap();
-
-    let coded_width = args.coded_width.unwrap_or(args.width);
-    let coded_height = args.coded_height.unwrap_or(args.height);
-    let coded_frame_size: usize = (coded_width * coded_height * 3 / 2) as usize;
-
-    let mut output = args.output.map(|output| File::create(output).unwrap());
-
-    if let Some(ref mut output) = output {
-        if codec == Codec::VP9 {
-            let hdr = IvfFileHeader::new(
-                IvfFileHeader::CODEC_VP9,
-                args.width as u16,
-                args.height as u16,
-                args.framerate,
-                args.count as u32,
-            );
-            hdr.writo_into(output).unwrap();
-        }
-    }
-
-    let mut buf = vec![0u8; coded_frame_size];
-    for i in 0..args.count {
-        input.read_exact(&mut buf[..]).unwrap();
-        let handle = pool.get_surface().unwrap();
-        let layout = upload_img(
-            &display,
-            handle.borrow(),
-            (args.width, args.height).into(),
-            (coded_width, coded_height).into(),
-            &buf[..],
-            args.fourcc,
-        );
-
-        let input_frame = FrameMetadata {
-            layout,
-            timestamp: i as u64,
-            force_keyframe: false,
-        };
-
-        encoder.encode(input_frame, handle).unwrap();
-        while let Some(coded) = encoder.poll().unwrap() {
-            if let Some(ref mut output) = output {
-                if codec == Codec::VP9 {
-                    let hdr = IvfFrameHeader {
-                        timestamp: coded.metadata.timestamp,
-                        frame_size: coded.bitstream.len() as u32,
-                    };
-
-                    hdr.writo_into(output).unwrap();
-                }
-
-                output.write_all(&coded.bitstream).unwrap();
-            }
-        }
-    }
-
-    encoder.drain().unwrap();
-    while let Some(coded) = encoder.poll().unwrap() {
-        if let Some(ref mut output) = output {
-            if codec == Codec::VP9 {
-                let hdr = IvfFrameHeader {
-                    timestamp: coded.metadata.timestamp,
-                    frame_size: coded.bitstream.len() as u32,
-                };
-
-                hdr.writo_into(output).unwrap();
-            }
-
-            output.write_all(&coded.bitstream).unwrap();
-        }
-    }
-}
diff --git a/src/backend.rs b/src/backend.rs
index a3eec8f..d54fb92 100644
--- a/src/backend.rs
+++ b/src/backend.rs
@@ -10,7 +10,7 @@
 
 #[cfg(any(test, fuzzing))]
 pub(crate) mod dummy;
-#[cfg(any(feature = "v4l2"))]
+#[cfg(feature = "v4l2")]
 pub mod v4l2;
 #[cfg(feature = "vaapi")]
 pub mod vaapi;
diff --git a/src/backend/v4l2.rs b/src/backend/v4l2.rs
index 5de8778..c607540 100644
--- a/src/backend/v4l2.rs
+++ b/src/backend/v4l2.rs
@@ -2,10 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-//! V4L2 backend
+//! V4L2 backend for stateful encoders.
 
-pub mod decoder;
-#[cfg(feature = "v4l2")]
 pub mod encoder;
 
 impl From<v4l2r::PixelFormat> for crate::Fourcc {
diff --git a/src/backend/v4l2/decoder.rs b/src/backend/v4l2/decoder.rs
deleted file mode 100644
index a520dd7..0000000
--- a/src/backend/v4l2/decoder.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2024 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#[cfg(feature = "v4l2")]
-use crate::Resolution;
-
-pub mod stateless;
-
-pub trait V4l2StreamInfo {
-    /// Returns the minimum number of surfaces required to decode the stream.
-    // name was chosen to match vaapi
-    fn min_num_frames(&self) -> usize;
-    /// Returns the coded size of the surfaces required to decode the stream.
-    fn coded_size(&self) -> Resolution;
-    /// Returns the visible rectangle within the coded size for the stream.
-    fn visible_rect(&self) -> ((u32, u32), (u32, u32));
-}
diff --git a/src/backend/v4l2/decoder/stateless.rs b/src/backend/v4l2/decoder/stateless.rs
deleted file mode 100644
index 6e3e940..0000000
--- a/src/backend/v4l2/decoder/stateless.rs
+++ /dev/null
@@ -1,214 +0,0 @@
-// Copyright 2024 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-use std::cell::RefCell;
-use std::rc::Rc;
-
-use crate::backend::v4l2::decoder::V4l2StreamInfo;
-use crate::decoder::stateless::PoolLayer;
-use crate::decoder::stateless::StatelessCodec;
-use crate::decoder::stateless::StatelessDecoderBackend;
-use crate::decoder::stateless::TryFormat;
-use crate::decoder::DecodedHandle;
-use crate::decoder::DynHandle;
-use crate::decoder::FramePool;
-use crate::decoder::MappableHandle;
-use crate::decoder::StreamInfo;
-use crate::DecodedFormat;
-use crate::Resolution;
-
-use crate::device::v4l2::stateless::device::V4l2Device;
-use crate::device::v4l2::stateless::request::V4l2Request;
-
-pub struct V4l2Picture {
-    request: V4l2Request,
-    // To properly decode stream while output and capture queues
-    // are processed independently it's required for v4l2 backend
-    // to maintain DPB buffer recycling. The following vector
-    // is used to prevent reference pictures to be reused while
-    // current picture is still being decoded.
-    // TODO: handle ref list inernally by V4l2Request.
-    ref_pictures: Option<Vec<Rc<RefCell<V4l2Picture>>>>,
-}
-
-impl V4l2Picture {
-    pub fn new(request: V4l2Request) -> Self {
-        Self {
-            request,
-            ref_pictures: None,
-        }
-    }
-    pub fn timestamp(&self) -> u64 {
-        self.request.timestamp()
-    }
-    pub fn set_ref_pictures(&mut self, ref_pictures: Vec<Rc<RefCell<V4l2Picture>>>) -> &mut Self {
-        self.ref_pictures = Some(ref_pictures);
-        self
-    }
-    pub fn sync(&mut self) -> &mut Self {
-        self.request.sync();
-        self.ref_pictures = None;
-        self
-    }
-    pub fn request(&mut self) -> &mut V4l2Request {
-        &mut self.request
-    }
-}
-
-impl<'a> MappableHandle for std::cell::Ref<'a, V4l2Picture> {
-    fn read(&mut self, data: &mut [u8]) -> anyhow::Result<()> {
-        self.request.result().read(data);
-        Ok(())
-    }
-    fn image_size(&mut self) -> usize {
-        self.request.result().length()
-    }
-}
-
-pub struct BackendHandle {
-    pub picture: Rc<RefCell<V4l2Picture>>,
-}
-
-impl<'a> DynHandle for std::cell::Ref<'a, BackendHandle> {
-    fn dyn_mappable_handle<'b>(&'b self) -> anyhow::Result<Box<dyn MappableHandle + 'b>> {
-        self.picture.borrow_mut().sync();
-        Ok(Box::new(self.picture.borrow()))
-    }
-}
-
-pub struct V4l2StatelessDecoderHandle {
-    pub handle: Rc<RefCell<BackendHandle>>,
-}
-
-impl Clone for V4l2StatelessDecoderHandle {
-    fn clone(&self) -> Self {
-        Self {
-            handle: Rc::clone(&self.handle),
-        }
-    }
-}
-
-impl DecodedHandle for V4l2StatelessDecoderHandle {
-    type Descriptor = ();
-
-    fn coded_resolution(&self) -> Resolution {
-        todo!();
-    }
-
-    fn display_resolution(&self) -> Resolution {
-        todo!();
-    }
-
-    fn timestamp(&self) -> u64 {
-        self.handle.borrow().picture.borrow().timestamp()
-    }
-
-    fn dyn_picture<'a>(&'a self) -> Box<dyn DynHandle + 'a> {
-        Box::new(self.handle.borrow())
-    }
-
-    fn sync(&self) -> anyhow::Result<()> {
-        Ok(())
-    }
-
-    fn is_ready(&self) -> bool {
-        todo!();
-    }
-
-    fn resource(&self) -> std::cell::Ref<()> {
-        todo!();
-    }
-}
-
-pub struct V4l2StatelessDecoderBackend {
-    pub device: V4l2Device,
-    stream_info: StreamInfo,
-}
-
-impl V4l2StatelessDecoderBackend {
-    pub fn new() -> Self {
-        Self {
-            device: V4l2Device::new(),
-            stream_info: StreamInfo {
-                format: DecodedFormat::I420,
-                min_num_frames: 0,
-                coded_resolution: Resolution::from((0, 0)),
-                display_resolution: Resolution::from((0, 0)),
-            },
-        }
-    }
-}
-
-impl FramePool for V4l2StatelessDecoderBackend {
-    type Descriptor = ();
-
-    fn coded_resolution(&self) -> Resolution {
-        todo!();
-    }
-
-    fn set_coded_resolution(&mut self, _resolution: Resolution) {
-        todo!();
-    }
-
-    fn add_frames(&mut self, _descriptors: Vec<Self::Descriptor>) -> Result<(), anyhow::Error> {
-        todo!();
-    }
-
-    fn num_free_frames(&self) -> usize {
-        self.device.num_free_buffers()
-    }
-
-    fn num_managed_frames(&self) -> usize {
-        self.device.num_buffers()
-    }
-
-    fn clear(&mut self) {
-        todo!();
-    }
-}
-
-impl<Codec: StatelessCodec> TryFormat<Codec> for V4l2StatelessDecoderBackend
-where
-    for<'a> &'a Codec::FormatInfo: V4l2StreamInfo,
-{
-    fn try_format(
-        &mut self,
-        format_info: &Codec::FormatInfo,
-        format: DecodedFormat,
-    ) -> anyhow::Result<()> {
-        // TODO
-        // VIDIOC_S/G_FMT has been called on both output and capture buffers.
-        // The VAAPI implementation looks to do actual format checking here.
-        // The values provided here are directly from the codec (modulo format).
-        // Hardware may handle this differently, i.e. buffer padding.
-        self.stream_info.format = format;
-        let visible_rect = format_info.visible_rect();
-
-        let display_resolution = Resolution {
-            width: visible_rect.1 .0 - visible_rect.0 .0,
-            height: visible_rect.1 .1 - visible_rect.0 .1,
-        };
-
-        self.stream_info.min_num_frames = format_info.min_num_frames();
-        self.stream_info.coded_resolution = format_info.coded_size();
-        self.stream_info.display_resolution = display_resolution;
-        Ok(())
-    }
-}
-
-impl StatelessDecoderBackend for V4l2StatelessDecoderBackend {
-    type Handle = V4l2StatelessDecoderHandle;
-
-    type FramePool = Self;
-
-    fn stream_info(&self) -> Option<&StreamInfo> {
-        // TODO
-        Some(&self.stream_info)
-    }
-
-    fn frame_pool(&mut self, _: PoolLayer) -> Vec<&mut Self::FramePool> {
-        self.device.recycle_buffers();
-        vec![self]
-    }
-}
diff --git a/src/backend/v4l2/encoder.rs b/src/backend/v4l2/encoder.rs
index aeb900f..d4cd0e0 100644
--- a/src/backend/v4l2/encoder.rs
+++ b/src/backend/v4l2/encoder.rs
@@ -1,12 +1,10 @@
 // Copyright 2024 The ChromiumOS Authors
 // Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
 
 use std::collections::BTreeMap;
 use std::fmt::Debug;
 use std::marker::PhantomData;
 use std::os::fd::AsRawFd;
-use std::path::PathBuf;
 use std::sync::Arc;
 
 use nix::sys::stat::fstat;
@@ -25,19 +23,18 @@
 use v4l2r::device::queue::direction::Capture;
 use v4l2r::device::queue::direction::Output;
 use v4l2r::device::queue::dqbuf::DqBuffer;
+use v4l2r::device::queue::qbuf::get_free::GetFreeBufferError;
+use v4l2r::device::queue::qbuf::get_free::GetFreeCaptureBuffer;
+use v4l2r::device::queue::qbuf::get_free::GetFreeOutputBuffer;
+use v4l2r::device::queue::qbuf::OutputQueueable;
+use v4l2r::device::queue::qbuf::OutputQueueableProvider;
 use v4l2r::device::queue::qbuf::QBuffer;
 use v4l2r::device::queue::BuffersAllocated;
 use v4l2r::device::queue::CreateQueueError;
-use v4l2r::device::queue::GetFreeBufferError;
-use v4l2r::device::queue::GetFreeCaptureBuffer;
-use v4l2r::device::queue::GetFreeOutputBuffer;
-use v4l2r::device::queue::OutputQueueable;
-use v4l2r::device::queue::OutputQueueableProvider;
 use v4l2r::device::queue::Queue;
 use v4l2r::device::queue::RequestBuffersError;
 use v4l2r::device::AllocatedQueue;
 use v4l2r::device::Device;
-use v4l2r::device::DeviceConfig;
 use v4l2r::device::Stream;
 use v4l2r::device::TryDequeue;
 use v4l2r::ioctl;
@@ -72,7 +69,6 @@
 use crate::utils::DmabufFrame;
 use crate::utils::UserPtrFrame;
 use crate::Fourcc;
-use crate::FrameLayout;
 use crate::Resolution;
 
 #[derive(Debug, Error)]
@@ -324,12 +320,7 @@
     /// otherwise if the buffer may not be queue returns false.
     fn queue(
         &mut self,
-        buffer: QBuffer<
-            Capture,
-            Vec<Self::PlaneHandle>,
-            Vec<Self::PlaneHandle>,
-            &Queue<Capture, BuffersAllocated<Vec<Self::PlaneHandle>>>,
-        >,
+        buffer: QBuffer<'_, Capture, Vec<Self::PlaneHandle>, Vec<Self::PlaneHandle>>,
     ) -> anyhow::Result<bool>;
 
     /// Maps the the buffer and returns its contents in form of [`Vec<u8>`]
@@ -344,12 +335,7 @@
 
     fn queue(
         &mut self,
-        buffer: QBuffer<
-            Capture,
-            Vec<Self::PlaneHandle>,
-            Vec<Self::PlaneHandle>,
-            &Queue<Capture, BuffersAllocated<Vec<Self::PlaneHandle>>>,
-        >,
+        buffer: QBuffer<'_, Capture, Vec<Self::PlaneHandle>, Vec<Self::PlaneHandle>>,
     ) -> anyhow::Result<bool> {
         buffer.queue()?;
         Ok(true)
@@ -643,8 +629,8 @@
         let output_pixfmt: PixelFormat = fourcc.0.into();
 
         let output_format = Format {
-            width: coded_size.width,
-            height: coded_size.height,
+            width: visible_size.width,
+            height: visible_size.height,
             pixelformat: output_pixfmt,
             // Let the driver pick
             plane_fmt: vec![],
@@ -662,13 +648,11 @@
 
         Self::apply_ctrl(&device, "header mode", VideoHeaderMode::JoinedWith1stFrame)?;
 
-        if visible_size.width > output_format.width || visible_size.height > output_format.height {
+        if visible_size.width > coded_size.width || visible_size.height > coded_size.height {
             return Err(InitializationError::Unsupported(
                 UnsupportedError::FrameUpscaling,
             ));
-        } else if visible_size.width != output_format.width
-            || visible_size.height != output_format.height
-        {
+        } else if visible_size != coded_size {
             log::info!("The frame visible size is not aligned to coded size, applying selection");
             if let Err(err) = Self::apply_selection(&device, visible_size) {
                 log::error!("Failed to set selection: {err:?}");
@@ -974,60 +958,6 @@
     }
 }
 
-pub fn find_device_with_capture(pixfmt: v4l2r::PixelFormat) -> Option<PathBuf> {
-    const MAX_DEVICE_NO: usize = 128;
-    for dev_no in 0..MAX_DEVICE_NO {
-        let device_path = PathBuf::from(format!("/dev/video{dev_no}"));
-        let Ok(device) = Device::open(&device_path, DeviceConfig::new()) else {
-            continue;
-        };
-
-        let device = Arc::new(device);
-
-        let Ok(queue) = Queue::get_capture_mplane_queue(device) else {
-            continue;
-        };
-
-        for fmt in queue.format_iter() {
-            if fmt.pixelformat == pixfmt {
-                return Some(device_path);
-            }
-        }
-    }
-
-    None
-}
-
-pub fn v4l2_format_to_frame_layout(format: &v4l2r::Format) -> FrameLayout {
-    let mut layout = FrameLayout {
-        format: (Fourcc::from(format.pixelformat.to_u32()), 0),
-        size: Resolution {
-            width: format.width,
-            height: format.height,
-        },
-        planes: format
-            .plane_fmt
-            .iter()
-            .map(|plane| crate::PlaneLayout {
-                buffer_index: 0,
-                offset: 0,
-                stride: plane.bytesperline as usize,
-            })
-            .collect(),
-    };
-
-    // Patch FrameLayout
-    match &format.pixelformat.to_fourcc() {
-        b"NM12" if layout.planes.len() == 2 => {
-            layout.planes[1].buffer_index = 1;
-        }
-        b"NV12" if layout.planes.len() == 1 => {}
-        _ => panic!("Unknown format"),
-    };
-
-    layout
-}
-
 #[cfg(test)]
 pub(crate) mod tests {
     use std::os::fd::AsFd;
@@ -1049,6 +979,31 @@
     use crate::encoder::tests::fill_test_frame_nm12;
     use crate::encoder::tests::fill_test_frame_nv12;
     use crate::encoder::tests::get_test_frame_t;
+    use crate::FrameLayout;
+
+    pub fn find_device_with_capture(pixfmt: v4l2r::PixelFormat) -> Option<PathBuf> {
+        const MAX_DEVICE_NO: usize = 128;
+        for dev_no in 0..MAX_DEVICE_NO {
+            let device_path = PathBuf::from(format!("/dev/video{dev_no}"));
+            let Ok(device) = Device::open(&device_path, DeviceConfig::new()) else {
+                continue;
+            };
+
+            let device = Arc::new(device);
+
+            let Ok(queue) = Queue::get_capture_mplane_queue(device) else {
+                continue;
+            };
+
+            for fmt in queue.format_iter() {
+                if fmt.pixelformat == pixfmt {
+                    return Some(device_path);
+                }
+            }
+        }
+
+        None
+    }
 
     /// A simple wrapper for a GBM device node.
     pub struct GbmDevice(std::fs::File);
@@ -1182,6 +1137,36 @@
         }
     }
 
+    pub fn v4l2_format_to_frame_layout(format: &v4l2r::Format) -> FrameLayout {
+        let mut layout = FrameLayout {
+            format: (Fourcc::from(format.pixelformat.to_u32()), 0),
+            size: Resolution {
+                width: format.width,
+                height: format.height,
+            },
+            planes: format
+                .plane_fmt
+                .iter()
+                .map(|plane| crate::PlaneLayout {
+                    buffer_index: 0,
+                    offset: 0,
+                    stride: plane.bytesperline as usize,
+                })
+                .collect(),
+        };
+
+        // Patch FrameLayout
+        match &format.pixelformat.to_fourcc() {
+            b"NM12" if layout.planes.len() == 2 => {
+                layout.planes[1].buffer_index = 1;
+            }
+            b"NV12" if layout.planes.len() == 1 => {}
+            _ => panic!("Unknown format"),
+        };
+
+        layout
+    }
+
     pub struct TestMmapFrame {
         meta: FrameMetadata,
         frame_count: u64,
diff --git a/src/backend/vaapi.rs b/src/backend/vaapi.rs
index 243fe0d..40cb68c 100644
--- a/src/backend/vaapi.rs
+++ b/src/backend/vaapi.rs
@@ -25,15 +25,15 @@
 
 fn va_rt_format_to_string(va_rt_format: u32) -> String {
     String::from(match va_rt_format {
-        libva::VA_RT_FORMAT_YUV420 => "YUV420",
-        libva::VA_RT_FORMAT_YUV422 => "YUV422",
-        libva::VA_RT_FORMAT_YUV444 => "YUV444",
-        libva::VA_RT_FORMAT_YUV420_10 => "YUV420_10",
-        libva::VA_RT_FORMAT_YUV420_12 => "YUV420_12",
-        libva::VA_RT_FORMAT_YUV422_10 => "YUV422_10",
-        libva::VA_RT_FORMAT_YUV422_12 => "YUV422_12",
-        libva::VA_RT_FORMAT_YUV444_10 => "YUV444_10",
-        libva::VA_RT_FORMAT_YUV444_12 => "YUV444_12",
+        libva::constants::VA_RT_FORMAT_YUV420 => "YUV420",
+        libva::constants::VA_RT_FORMAT_YUV422 => "YUV422",
+        libva::constants::VA_RT_FORMAT_YUV444 => "YUV444",
+        libva::constants::VA_RT_FORMAT_YUV420_10 => "YUV420_10",
+        libva::constants::VA_RT_FORMAT_YUV420_12 => "YUV420_12",
+        libva::constants::VA_RT_FORMAT_YUV422_10 => "YUV422_10",
+        libva::constants::VA_RT_FORMAT_YUV422_12 => "YUV422_12",
+        libva::constants::VA_RT_FORMAT_YUV444_10 => "YUV444_10",
+        libva::constants::VA_RT_FORMAT_YUV444_12 => "YUV444_12",
         other => return format!("unknown VA rt_format {}", other),
     })
 }
@@ -49,53 +49,53 @@
 /// preferred order.
 const FORMAT_MAP: [FormatMap; 10] = [
     FormatMap {
-        rt_format: libva::VA_RT_FORMAT_YUV420,
-        va_fourcc: libva::VA_FOURCC_NV12,
+        rt_format: libva::constants::VA_RT_FORMAT_YUV420,
+        va_fourcc: libva::constants::VA_FOURCC_NV12,
         decoded_format: DecodedFormat::NV12,
     },
     FormatMap {
-        rt_format: libva::VA_RT_FORMAT_YUV420,
-        va_fourcc: libva::VA_FOURCC_I420,
+        rt_format: libva::constants::VA_RT_FORMAT_YUV420,
+        va_fourcc: libva::constants::VA_FOURCC_I420,
         decoded_format: DecodedFormat::I420,
     },
     FormatMap {
-        rt_format: libva::VA_RT_FORMAT_YUV422,
-        va_fourcc: libva::VA_FOURCC_422H,
+        rt_format: libva::constants::VA_RT_FORMAT_YUV422,
+        va_fourcc: libva::constants::VA_FOURCC_422H,
         decoded_format: DecodedFormat::I422,
     },
     FormatMap {
-        rt_format: libva::VA_RT_FORMAT_YUV444,
-        va_fourcc: libva::VA_FOURCC_444P,
+        rt_format: libva::constants::VA_RT_FORMAT_YUV444,
+        va_fourcc: libva::constants::VA_FOURCC_444P,
         decoded_format: DecodedFormat::I444,
     },
     FormatMap {
-        rt_format: libva::VA_RT_FORMAT_YUV420_10,
-        va_fourcc: libva::VA_FOURCC_P010,
+        rt_format: libva::constants::VA_RT_FORMAT_YUV420_10,
+        va_fourcc: libva::constants::VA_FOURCC_P010,
         decoded_format: DecodedFormat::I010,
     },
     FormatMap {
-        rt_format: libva::VA_RT_FORMAT_YUV420_12,
-        va_fourcc: libva::VA_FOURCC_P012,
+        rt_format: libva::constants::VA_RT_FORMAT_YUV420_12,
+        va_fourcc: libva::constants::VA_FOURCC_P012,
         decoded_format: DecodedFormat::I012,
     },
     FormatMap {
-        rt_format: libva::VA_RT_FORMAT_YUV422_10,
-        va_fourcc: libva::VA_FOURCC_Y210,
+        rt_format: libva::constants::VA_RT_FORMAT_YUV422_10,
+        va_fourcc: libva::constants::VA_FOURCC_Y210,
         decoded_format: DecodedFormat::I210,
     },
     FormatMap {
-        rt_format: libva::VA_RT_FORMAT_YUV422_12,
-        va_fourcc: libva::VA_FOURCC_Y212,
+        rt_format: libva::constants::VA_RT_FORMAT_YUV422_12,
+        va_fourcc: libva::constants::VA_FOURCC_Y212,
         decoded_format: DecodedFormat::I212,
     },
     FormatMap {
-        rt_format: libva::VA_RT_FORMAT_YUV444_10,
-        va_fourcc: libva::VA_FOURCC_Y410,
+        rt_format: libva::constants::VA_RT_FORMAT_YUV444_10,
+        va_fourcc: libva::constants::VA_FOURCC_Y410,
         decoded_format: DecodedFormat::I410,
     },
     FormatMap {
-        rt_format: libva::VA_RT_FORMAT_YUV444_12,
-        va_fourcc: libva::VA_FOURCC_Y412,
+        rt_format: libva::constants::VA_RT_FORMAT_YUV444_12,
+        va_fourcc: libva::constants::VA_FOURCC_Y412,
         decoded_format: DecodedFormat::I412,
     },
 ];
@@ -117,7 +117,9 @@
 
     // See whether this RT_FORMAT is supported by the given VAProfile and
     // VAEntrypoint pair.
-    if attrs[0].value == libva::VA_ATTRIB_NOT_SUPPORTED || attrs[0].value & rt_format == 0 {
+    if attrs[0].value == libva::constants::VA_ATTRIB_NOT_SUPPORTED
+        || attrs[0].value & rt_format == 0
+    {
         return Err(anyhow!(
             "rt_format {:?} not supported for profile {:?} and entrypoint {:?}",
             rt_format,
@@ -149,14 +151,14 @@
 
     fn try_from(value: &libva::VAImageFormat) -> Result<Self, Self::Error> {
         match value.fourcc {
-            libva::VA_FOURCC_I420 => Ok(DecodedFormat::I420),
-            libva::VA_FOURCC_NV12 => Ok(DecodedFormat::NV12),
-            libva::VA_FOURCC_P010 => Ok(DecodedFormat::I010),
-            libva::VA_FOURCC_P012 => Ok(DecodedFormat::I012),
-            libva::VA_FOURCC_Y210 => Ok(DecodedFormat::I210),
-            libva::VA_FOURCC_Y212 => Ok(DecodedFormat::I212),
-            libva::VA_FOURCC_Y410 => Ok(DecodedFormat::I410),
-            libva::VA_FOURCC_Y412 => Ok(DecodedFormat::I412),
+            libva::constants::VA_FOURCC_I420 => Ok(DecodedFormat::I420),
+            libva::constants::VA_FOURCC_NV12 => Ok(DecodedFormat::NV12),
+            libva::constants::VA_FOURCC_P010 => Ok(DecodedFormat::I010),
+            libva::constants::VA_FOURCC_P012 => Ok(DecodedFormat::I012),
+            libva::constants::VA_FOURCC_Y210 => Ok(DecodedFormat::I210),
+            libva::constants::VA_FOURCC_Y212 => Ok(DecodedFormat::I212),
+            libva::constants::VA_FOURCC_Y410 => Ok(DecodedFormat::I410),
+            libva::constants::VA_FOURCC_Y412 => Ok(DecodedFormat::I412),
             _ => Err(anyhow!("Unsupported format")),
         }
     }
diff --git a/src/backend/vaapi/decoder.rs b/src/backend/vaapi/decoder.rs
index b91ecf5..940d500 100644
--- a/src/backend/vaapi/decoder.rs
+++ b/src/backend/vaapi/decoder.rs
@@ -55,7 +55,7 @@
     handle: &Option<DecodedHandle<M>>,
 ) -> libva::VASurfaceID {
     match handle {
-        None => libva::VA_INVALID_SURFACE,
+        None => libva::constants::VA_INVALID_SURFACE,
         Some(handle) => handle.borrow().surface().id(),
     }
 }
@@ -107,7 +107,7 @@
     /// Returns the minimum number of surfaces required to decode the stream.
     fn min_num_surfaces(&self) -> usize;
     /// Returns the coded size of the surfaces required to decode the stream.
-    fn coded_size(&self) -> Resolution;
+    fn coded_size(&self) -> (u32, u32);
     /// Returns the visible rectangle within the coded size for the stream.
     fn visible_rect(&self) -> ((u32, u32), (u32, u32));
 }
@@ -177,7 +177,8 @@
         let va_profile = hdr.va_profile()?;
         let rt_format = hdr.rt_format()?;
 
-        let coded_resolution = hdr.coded_size().round(crate::ResolutionRoundMode::Even);
+        let coded_resolution =
+            Resolution::from(hdr.coded_size()).round(crate::ResolutionRoundMode::Even);
 
         let format_map = if let Some(format_map) = format_map {
             format_map
@@ -290,15 +291,15 @@
                 config,
                 stream_info: StreamInfo {
                     format: match rt_format {
-                        libva::VA_RT_FORMAT_YUV420 => DecodedFormat::I420,
-                        libva::VA_RT_FORMAT_YUV422 => DecodedFormat::I422,
-                        libva::VA_RT_FORMAT_YUV444 => DecodedFormat::I444,
-                        libva::VA_RT_FORMAT_YUV420_10 => DecodedFormat::I010,
-                        libva::VA_RT_FORMAT_YUV420_12 => DecodedFormat::I012,
-                        libva::VA_RT_FORMAT_YUV422_10 => DecodedFormat::I210,
-                        libva::VA_RT_FORMAT_YUV422_12 => DecodedFormat::I212,
-                        libva::VA_RT_FORMAT_YUV444_10 => DecodedFormat::I410,
-                        libva::VA_RT_FORMAT_YUV444_12 => DecodedFormat::I412,
+                        libva::constants::VA_RT_FORMAT_YUV420 => DecodedFormat::I420,
+                        libva::constants::VA_RT_FORMAT_YUV422 => DecodedFormat::I422,
+                        libva::constants::VA_RT_FORMAT_YUV444 => DecodedFormat::I444,
+                        libva::constants::VA_RT_FORMAT_YUV420_10 => DecodedFormat::I010,
+                        libva::constants::VA_RT_FORMAT_YUV420_12 => DecodedFormat::I012,
+                        libva::constants::VA_RT_FORMAT_YUV422_10 => DecodedFormat::I210,
+                        libva::constants::VA_RT_FORMAT_YUV422_12 => DecodedFormat::I212,
+                        libva::constants::VA_RT_FORMAT_YUV444_10 => DecodedFormat::I410,
+                        libva::constants::VA_RT_FORMAT_YUV444_12 => DecodedFormat::I412,
                         _ => panic!("unrecognized RT format {}", rt_format),
                     },
                     coded_resolution,
@@ -475,15 +476,10 @@
         let offsets = image_inner.offsets.map(|x| x as usize);
 
         match image_inner.format.fourcc {
-            libva::VA_FOURCC_NV12 => {
-                let (src_y, src_uv) = self.as_ref().split_at(offsets[1]);
-                let (dst_y, dst_uv) = buffer.split_at_mut(width * height);
-                nv12_copy(
-                    src_y, pitches[0], dst_y, width, src_uv, pitches[1], dst_uv, width, width,
-                    height,
-                );
+            libva::constants::VA_FOURCC_NV12 => {
+                nv12_copy(self.as_ref(), buffer, width, height, pitches, offsets);
             }
-            libva::VA_FOURCC_I420 => {
+            libva::constants::VA_FOURCC_I420 => {
                 i4xx_copy(
                     self.as_ref(),
                     buffer,
@@ -494,7 +490,7 @@
                     (true, true),
                 );
             }
-            libva::VA_FOURCC_422H => {
+            libva::constants::VA_FOURCC_422H => {
                 i4xx_copy(
                     self.as_ref(),
                     buffer,
@@ -505,7 +501,7 @@
                     (true, false),
                 );
             }
-            libva::VA_FOURCC_444P => {
+            libva::constants::VA_FOURCC_444P => {
                 i4xx_copy(
                     self.as_ref(),
                     buffer,
@@ -516,22 +512,22 @@
                     (false, false),
                 );
             }
-            libva::VA_FOURCC_P010 => {
+            libva::constants::VA_FOURCC_P010 => {
                 p01x_to_i01x(self.as_ref(), buffer, 10, width, height, pitches, offsets);
             }
-            libva::VA_FOURCC_P012 => {
+            libva::constants::VA_FOURCC_P012 => {
                 p01x_to_i01x(self.as_ref(), buffer, 12, width, height, pitches, offsets);
             }
-            libva::VA_FOURCC_Y210 => {
+            libva::constants::VA_FOURCC_Y210 => {
                 y21x_to_i21x(self.as_ref(), buffer, 10, width, height, pitches, offsets);
             }
-            libva::VA_FOURCC_Y212 => {
+            libva::constants::VA_FOURCC_Y212 => {
                 y21x_to_i21x(self.as_ref(), buffer, 12, width, height, pitches, offsets);
             }
-            libva::VA_FOURCC_Y410 => {
+            libva::constants::VA_FOURCC_Y410 => {
                 y410_to_i410(self.as_ref(), buffer, width, height, pitches, offsets);
             }
-            libva::VA_FOURCC_Y412 => {
+            libva::constants::VA_FOURCC_Y412 => {
                 y412_to_i412(self.as_ref(), buffer, width, height, pitches, offsets);
             }
             _ => {
@@ -583,7 +579,7 @@
         // Create a pool with reasonable defaults, as we don't know the format of the stream yet.
         let surface_pools = vec![VaSurfacePool::new(
             Rc::clone(&display),
-            libva::VA_RT_FORMAT_YUV420,
+            libva::constants::VA_RT_FORMAT_YUV420,
             Some(libva::UsageHint::USAGE_HINT_DECODER),
             Resolution::from((16, 16)),
         )];
diff --git a/src/backend/vaapi/encoder.rs b/src/backend/vaapi/encoder.rs
index 256161d..5558ad6 100644
--- a/src/backend/vaapi/encoder.rs
+++ b/src/backend/vaapi/encoder.rs
@@ -414,8 +414,8 @@
 pub(crate) mod tests {
     use std::borrow::Borrow;
 
-    use libva::VA_FOURCC_NV12;
-    use libva::VA_FOURCC_P010;
+    use libva::constants::VA_FOURCC_NV12;
+    use libva::constants::VA_FOURCC_P010;
 
     use super::*;
     use crate::encoder::tests::fill_test_frame_nv12;
diff --git a/src/codec/h264/parser.rs b/src/codec/h264/parser.rs
index 5cc52b5..4557830 100644
--- a/src/codec/h264/parser.rs
+++ b/src/codec/h264/parser.rs
@@ -294,10 +294,6 @@
     /// the bottom field of a coded frame specified in clause 8.2.1.
     pub delta_pic_order_cnt: [i32; 2],
 
-    /// This value is required by V4L2 stateless decode params so it is calculated
-    /// by parser while processing slice header.
-    pub pic_order_cnt_bit_size: usize,
-
     /// Shall be equal to 0 for slices and slice data partitions belonging to
     /// the primary coded picture. The value of `redundant_pic_cnt shall` be
     /// greater than 0 for coded slices or coded slice data partitions of a
@@ -347,10 +343,6 @@
     /// Decoded reference picture marking parsed using 7.3.3.3
     pub dec_ref_pic_marking: RefPicMarking,
 
-    /// This value is required by V4L2 stateless decode params so it is calculated
-    /// by parser while processing slice header.
-    pub dec_ref_pic_marking_bit_size: usize,
-
     /// Specifies the index for determining the initialization table used in the
     /// initialization process for context variables.
     pub cabac_init_idc: u8,
@@ -1877,7 +1869,7 @@
                     Parser::parse_scaling_list(r, &mut scaling_lists8x8[i], &mut use_default)?;
 
                     if use_default {
-                        Parser::fill_default_scaling_list_8x8(&mut scaling_lists8x8[i], i);
+                        Parser::fill_default_scaling_list_4x4(&mut scaling_lists4x4[i], i);
                     }
                 } else if !sps.seq_scaling_matrix_present_flag {
                     // Table 7-2: Fallback rule A
@@ -2415,7 +2407,6 @@
     ) -> Result<(), String> {
         let rpm = &mut header.dec_ref_pic_marking;
 
-        let num_bits_left = r.num_bits_left();
         if nalu.header.idr_pic_flag {
             rpm.no_output_of_prior_pics_flag = r.read_bit()?;
             rpm.long_term_reference_flag = r.read_bit()?;
@@ -2454,7 +2445,6 @@
                 }
             }
         }
-        header.dec_ref_pic_marking_bit_size = num_bits_left - r.num_bits_left();
 
         Ok(())
     }
@@ -2521,7 +2511,6 @@
             header.idr_pic_id = r.read_ue_max(0xffff)?;
         }
 
-        let num_bits_left = r.num_bits_left();
         if sps.pic_order_cnt_type == 0 {
             header.pic_order_cnt_lsb =
                 r.read_bits(usize::from(sps.log2_max_pic_order_cnt_lsb_minus4) + 4)?;
@@ -2537,7 +2526,6 @@
                 header.delta_pic_order_cnt[1] = r.read_se()?;
             }
         }
-        header.pic_order_cnt_bit_size = num_bits_left - r.num_bits_left();
 
         if pps.redundant_pic_cnt_present_flag {
             header.redundant_pic_cnt = r.read_ue_max(127)?;
diff --git a/src/decoder/stateless/av1/vaapi.rs b/src/decoder/stateless/av1/vaapi.rs
index d91f645..6e566bd 100644
--- a/src/decoder/stateless/av1/vaapi.rs
+++ b/src/decoder/stateless/av1/vaapi.rs
@@ -58,9 +58,9 @@
         match self.seq_profile {
             Profile::Profile0 => {
                 if self.bit_depth == BitDepth::Depth8 {
-                    Ok(libva::VA_RT_FORMAT_YUV420)
+                    Ok(libva::constants::VA_RT_FORMAT_YUV420)
                 } else if self.bit_depth == BitDepth::Depth10 {
-                    Ok(libva::VA_RT_FORMAT_YUV420_10)
+                    Ok(libva::constants::VA_RT_FORMAT_YUV420_10)
                 } else {
                     Err(anyhow!(
                         "Unsupported bit depth {:?} for profile {:?}",
@@ -71,9 +71,9 @@
             }
             Profile::Profile1 => {
                 if self.bit_depth == BitDepth::Depth8 {
-                    Ok(libva::VA_RT_FORMAT_YUV444)
+                    Ok(libva::constants::VA_RT_FORMAT_YUV444)
                 } else if self.bit_depth == BitDepth::Depth10 {
-                    Ok(libva::VA_RT_FORMAT_YUV444_10)
+                    Ok(libva::constants::VA_RT_FORMAT_YUV444_10)
                 } else {
                     Err(anyhow!(
                         "Unsupported bit depth {:?} for profile {:?}",
@@ -93,15 +93,15 @@
         NUM_SURFACES
     }
 
-    fn coded_size(&self) -> Resolution {
-        Resolution::from((
+    fn coded_size(&self) -> (u32, u32) {
+        (
             self.max_frame_width_minus_1 as u32 + 1,
             self.max_frame_height_minus_1 as u32 + 1,
-        ))
+        )
     }
 
     fn visible_rect(&self) -> ((u32, u32), (u32, u32)) {
-        ((0, 0), self.coded_size().into())
+        ((0, 0), self.coded_size())
     }
 }
 
@@ -423,8 +423,8 @@
             .context("Invalid matrix_coefficients")?,
         &seq_info_fields,
         current_frame,
-        libva::VA_INVALID_SURFACE, /* film grain is unsupported for now */
-        vec![],                    /* anchor_frames_list */
+        libva::constants::VA_INVALID_SURFACE, /* film grain is unsupported for now */
+        vec![],                               /* anchor_frames_list */
         u16::try_from(hdr.upscaled_width - 1).context("Invalid frame width")?,
         u16::try_from(hdr.frame_height - 1).context("Invalid frame height")?,
         0, /* output_frame_width_in_tiles_minus_1 */
diff --git a/src/decoder/stateless/h264.rs b/src/decoder/stateless/h264.rs
index b6d7841..c9976be 100644
--- a/src/decoder/stateless/h264.rs
+++ b/src/decoder/stateless/h264.rs
@@ -4,8 +4,6 @@
 
 #[cfg(any(test, fuzzing))]
 mod dummy;
-#[cfg(feature = "v4l2")]
-mod v4l2;
 #[cfg(feature = "vaapi")]
 mod vaapi;
 
diff --git a/src/decoder/stateless/h264/v4l2.rs b/src/decoder/stateless/h264/v4l2.rs
deleted file mode 100644
index 2bdd7fd..0000000
--- a/src/decoder/stateless/h264/v4l2.rs
+++ /dev/null
@@ -1,180 +0,0 @@
-// Copyright 2024 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-use std::cell::RefCell;
-use std::rc::Rc;
-
-use v4l2r::bindings::v4l2_ctrl_h264_pps;
-use v4l2r::bindings::v4l2_ctrl_h264_sps;
-use v4l2r::controls::codec::H264Pps;
-use v4l2r::controls::codec::H264Sps;
-use v4l2r::controls::SafeExtControl;
-
-use crate::backend::v4l2::decoder::stateless::BackendHandle;
-use crate::backend::v4l2::decoder::stateless::V4l2Picture;
-use crate::backend::v4l2::decoder::stateless::V4l2StatelessDecoderBackend;
-use crate::backend::v4l2::decoder::stateless::V4l2StatelessDecoderHandle;
-use crate::backend::v4l2::decoder::V4l2StreamInfo;
-use crate::codec::h264::dpb::Dpb;
-use crate::codec::h264::dpb::DpbEntry;
-use crate::codec::h264::parser::Pps;
-use crate::codec::h264::parser::Slice;
-use crate::codec::h264::parser::SliceHeader;
-use crate::codec::h264::parser::Sps;
-use crate::codec::h264::picture::PictureData;
-use crate::decoder::stateless::h264::StatelessH264DecoderBackend;
-use crate::decoder::stateless::h264::H264;
-use crate::decoder::stateless::NewPictureError;
-use crate::decoder::stateless::NewPictureResult;
-use crate::decoder::stateless::StatelessBackendResult;
-use crate::decoder::stateless::StatelessDecoder;
-use crate::decoder::stateless::StatelessDecoderBackendPicture;
-use crate::decoder::BlockingMode;
-use crate::device::v4l2::stateless::controls::h264::V4l2CtrlH264DecodeMode;
-use crate::device::v4l2::stateless::controls::h264::V4l2CtrlH264DecodeParams;
-use crate::device::v4l2::stateless::controls::h264::V4l2CtrlH264DpbEntry;
-//TODO use crate::device::v4l2::stateless::controls::h264::V4l2CtrlH264ScalingMatrix;
-use crate::Resolution;
-
-impl V4l2StreamInfo for &Rc<Sps> {
-    fn min_num_frames(&self) -> usize {
-        self.max_dpb_frames() + 4
-    }
-
-    fn coded_size(&self) -> Resolution {
-        Resolution::from((self.width(), self.height()))
-    }
-
-    fn visible_rect(&self) -> ((u32, u32), (u32, u32)) {
-        let rect = self.visible_rectangle();
-
-        ((rect.min.x, rect.min.y), (rect.max.x, rect.max.y))
-    }
-}
-
-impl StatelessDecoderBackendPicture<H264> for V4l2StatelessDecoderBackend {
-    type Picture = Rc<RefCell<V4l2Picture>>;
-}
-
-impl StatelessH264DecoderBackend for V4l2StatelessDecoderBackend {
-    fn new_sequence(&mut self, sps: &Rc<Sps>) -> StatelessBackendResult<()> {
-        let mb_unit = 16;
-        let map_unit = 16;
-        let resolution = Resolution::from((
-            (sps.pic_width_in_mbs_minus1 + 1) as u32 * mb_unit,
-            (sps.pic_height_in_map_units_minus1 + 1) as u32 * map_unit,
-        ));
-        self.device.set_resolution(resolution);
-        Ok(())
-    }
-
-    fn new_picture(&mut self, timestamp: u64) -> NewPictureResult<Self::Picture> {
-        let request_buffer = match self.device.alloc_request(timestamp) {
-            Ok(buffer) => buffer,
-            _ => return Err(NewPictureError::OutOfOutputBuffers),
-        };
-        Ok(Rc::new(RefCell::new(V4l2Picture::new(request_buffer))))
-    }
-
-    fn new_field_picture(&mut self, _: u64, _: &Self::Handle) -> NewPictureResult<Self::Picture> {
-        todo!()
-    }
-
-    fn start_picture(
-        &mut self,
-        picture: &mut Self::Picture,
-        picture_data: &PictureData,
-        sps: &Sps,
-        pps: &Pps,
-        dpb: &Dpb<Self::Handle>,
-        slice_header: &SliceHeader,
-    ) -> StatelessBackendResult<()> {
-        let mut dpb_entries = Vec::<V4l2CtrlH264DpbEntry>::new();
-        let mut ref_pictures = Vec::<Rc<RefCell<V4l2Picture>>>::new();
-        for entry in dpb.entries() {
-            let ref_picture = match &entry.reference {
-                Some(handle) => handle.handle.borrow().picture.clone(),
-                None => todo!(),
-            };
-            dpb_entries.push(V4l2CtrlH264DpbEntry {
-                timestamp: ref_picture.borrow().timestamp(),
-                pic: entry.pic.clone(),
-            });
-            ref_pictures.push(ref_picture);
-        }
-        //TODO let mut h264_scaling_matrix = V4l2CtrlH264ScalingMatrix::new();
-        let mut h264_decode_params = V4l2CtrlH264DecodeParams::new();
-        let h264_sps = SafeExtControl::<H264Sps>::from(v4l2_ctrl_h264_sps::from(sps));
-        let h264_pps = SafeExtControl::<H264Pps>::from(v4l2_ctrl_h264_pps::from(pps));
-        h264_decode_params
-            .set_picture_data(picture_data)
-            .set_dpb_entries(dpb_entries)
-            .set_slice_header(slice_header);
-        let mut picture = picture.borrow_mut();
-        picture
-            .request()
-            .ioctl(h264_sps)
-            .ioctl(h264_pps)
-            //TODO.ioctl(&h264_scaling_matrix)
-            .ioctl(&h264_decode_params)
-            .ioctl(V4l2CtrlH264DecodeMode::FrameBased);
-        picture.set_ref_pictures(ref_pictures);
-        ////////////////////////////////////////////////////////////////////////
-        // DEBUG
-        ////////////////////////////////////////////////////////////////////////
-        {
-            let mut dpb_timestamps = Vec::<u64>::new();
-            for entry in dpb.entries() {
-                match &entry.reference {
-                    Some(handle) => {
-                        dpb_timestamps.push(handle.handle.borrow().picture.borrow().timestamp())
-                    }
-                    None => todo!(),
-                };
-            }
-            log::debug!(
-                "{:<20} {:?} {:?}\n",
-                "start_picture",
-                picture.timestamp(),
-                dpb_timestamps
-            );
-        }
-        ////////////////////////////////////////////////////////////////////////
-        Ok(())
-    }
-
-    fn decode_slice(
-        &mut self,
-        picture: &mut Self::Picture,
-        slice: &Slice,
-        _: &Sps,
-        _: &Pps,
-        _: &[&DpbEntry<Self::Handle>],
-        _: &[&DpbEntry<Self::Handle>],
-    ) -> StatelessBackendResult<()> {
-        picture.borrow_mut().request().write(slice.nalu.as_ref());
-        Ok(())
-    }
-
-    fn submit_picture(&mut self, picture: Self::Picture) -> StatelessBackendResult<Self::Handle> {
-        let handle = Rc::new(RefCell::new(BackendHandle {
-            picture: picture.clone(),
-        }));
-        log::debug!(
-            "{:<20} {:?}\n",
-            "submit_picture",
-            picture.borrow().timestamp()
-        );
-        picture.borrow_mut().request().submit();
-        Ok(V4l2StatelessDecoderHandle { handle })
-    }
-}
-
-impl StatelessDecoder<H264, V4l2StatelessDecoderBackend> {
-    // Creates a new instance of the decoder using the v4l2 backend.
-    pub fn new_v4l2(blocking_mode: BlockingMode) -> Self {
-        Self::new(V4l2StatelessDecoderBackend::new(), blocking_mode)
-            .expect("Failed to create v4l2 stateless decoder backend")
-    }
-}
diff --git a/src/decoder/stateless/h264/vaapi.rs b/src/decoder/stateless/h264/vaapi.rs
index 4f5cee6..bdbac4d 100644
--- a/src/decoder/stateless/h264/vaapi.rs
+++ b/src/decoder/stateless/h264/vaapi.rs
@@ -42,7 +42,6 @@
 use crate::decoder::stateless::StatelessDecoder;
 use crate::decoder::stateless::StatelessDecoderBackendPicture;
 use crate::decoder::BlockingMode;
-use crate::Resolution;
 
 impl VaStreamInfo for &Rc<Sps> {
     fn va_profile(&self) -> anyhow::Result<i32> {
@@ -82,15 +81,15 @@
         let chroma_format_idc = self.chroma_format_idc;
 
         match (bit_depth_luma, chroma_format_idc) {
-            (8, 0) | (8, 1) => Ok(libva::VA_RT_FORMAT_YUV420),
-            (8, 2) => Ok(libva::VA_RT_FORMAT_YUV422),
-            (8, 3) => Ok(libva::VA_RT_FORMAT_YUV444),
-            (10, 0) | (10, 1) => Ok(libva::VA_RT_FORMAT_YUV420_10),
-            (10, 2) => Ok(libva::VA_RT_FORMAT_YUV422_10),
-            (10, 3) => Ok(libva::VA_RT_FORMAT_YUV444_10),
-            (12, 0) | (12, 1) => Ok(libva::VA_RT_FORMAT_YUV420_12),
-            (12, 2) => Ok(libva::VA_RT_FORMAT_YUV422_12),
-            (12, 3) => Ok(libva::VA_RT_FORMAT_YUV444_12),
+            (8, 0) | (8, 1) => Ok(libva::constants::VA_RT_FORMAT_YUV420),
+            (8, 2) => Ok(libva::constants::VA_RT_FORMAT_YUV422),
+            (8, 3) => Ok(libva::constants::VA_RT_FORMAT_YUV444),
+            (10, 0) | (10, 1) => Ok(libva::constants::VA_RT_FORMAT_YUV420_10),
+            (10, 2) => Ok(libva::constants::VA_RT_FORMAT_YUV422_10),
+            (10, 3) => Ok(libva::constants::VA_RT_FORMAT_YUV444_10),
+            (12, 0) | (12, 1) => Ok(libva::constants::VA_RT_FORMAT_YUV420_12),
+            (12, 2) => Ok(libva::constants::VA_RT_FORMAT_YUV422_12),
+            (12, 3) => Ok(libva::constants::VA_RT_FORMAT_YUV444_12),
             _ => Err(anyhow!(
                 "unsupported bit depth/chroma format pair {}, {}",
                 bit_depth_luma,
@@ -103,8 +102,8 @@
         self.max_dpb_frames() + 4
     }
 
-    fn coded_size(&self) -> Resolution {
-        Resolution::from((self.width(), self.height()))
+    fn coded_size(&self) -> (u32, u32) {
+        (self.width(), self.height())
     }
 
     fn visible_rect(&self) -> ((u32, u32), (u32, u32)) {
@@ -122,11 +121,11 @@
 ) -> libva::PictureH264 {
     let mut flags = 0;
     let frame_idx = if matches!(h264_pic.reference(), Reference::LongTerm) {
-        flags |= libva::VA_PICTURE_H264_LONG_TERM_REFERENCE;
+        flags |= libva::constants::VA_PICTURE_H264_LONG_TERM_REFERENCE;
         h264_pic.long_term_frame_idx
     } else {
         if matches!(h264_pic.reference(), Reference::ShortTerm { .. }) {
-            flags |= libva::VA_PICTURE_H264_SHORT_TERM_REFERENCE;
+            flags |= libva::constants::VA_PICTURE_H264_SHORT_TERM_REFERENCE;
         }
 
         h264_pic.frame_num
@@ -146,7 +145,7 @@
                     bottom_field_order_cnt = other_field.borrow().bottom_field_order_cnt
                 }
                 (_, _) => {
-                    flags |= libva::VA_PICTURE_H264_TOP_FIELD;
+                    flags |= libva::constants::VA_PICTURE_H264_TOP_FIELD;
                     bottom_field_order_cnt = 0;
                 }
             }
@@ -159,7 +158,7 @@
                     top_field_order_cnt = other_field.borrow().top_field_order_cnt
                 }
                 (_, _) => {
-                    flags |= libva::VA_PICTURE_H264_BOTTOM_FIELD;
+                    flags |= libva::constants::VA_PICTURE_H264_BOTTOM_FIELD;
                     top_field_order_cnt = 0;
                 }
             }
@@ -181,9 +180,9 @@
 /// array slots there is no data to fill them with.
 fn build_invalid_va_h264_pic() -> libva::PictureH264 {
     libva::PictureH264::new(
-        libva::VA_INVALID_ID,
+        libva::constants::VA_INVALID_ID,
         0,
-        libva::VA_PICTURE_H264_INVALID,
+        libva::constants::VA_PICTURE_H264_INVALID,
         0,
         0,
     )
@@ -429,7 +428,7 @@
     let slice_param = libva::SliceParameterBufferH264::new(
         slice_size as u32,
         0,
-        libva::VA_SLICE_DATA_FLAG_ALL,
+        libva::constants::VA_SLICE_DATA_FLAG_ALL,
         hdr.header_bit_size as u16,
         hdr.first_mb_in_slice as u16,
         hdr.slice_type as u8,
diff --git a/src/decoder/stateless/h265/vaapi.rs b/src/decoder/stateless/h265/vaapi.rs
index a899a37..b02d87b 100644
--- a/src/decoder/stateless/h265/vaapi.rs
+++ b/src/decoder/stateless/h265/vaapi.rs
@@ -109,15 +109,15 @@
         let chroma_format_idc = self.chroma_format_idc;
 
         match (bit_depth, chroma_format_idc) {
-            (8, 0) | (8, 1) => Ok(libva::VA_RT_FORMAT_YUV420),
-            (8, 2) => Ok(libva::VA_RT_FORMAT_YUV422),
-            (8, 3) => Ok(libva::VA_RT_FORMAT_YUV444),
-            (9, 0) | (9, 1) | (10, 0) | (10, 1) => Ok(libva::VA_RT_FORMAT_YUV420_10),
-            (9, 2) | (10, 2) => Ok(libva::VA_RT_FORMAT_YUV422_10),
-            (9, 3) | (10, 3) => Ok(libva::VA_RT_FORMAT_YUV444_10),
-            (11, 0) | (11, 1) | (12, 0) | (12, 1) => Ok(libva::VA_RT_FORMAT_YUV420_12),
-            (11, 2) | (12, 2) => Ok(libva::VA_RT_FORMAT_YUV422_12),
-            (11, 3) | (12, 3) => Ok(libva::VA_RT_FORMAT_YUV444_12),
+            (8, 0) | (8, 1) => Ok(libva::constants::VA_RT_FORMAT_YUV420),
+            (8, 2) => Ok(libva::constants::VA_RT_FORMAT_YUV422),
+            (8, 3) => Ok(libva::constants::VA_RT_FORMAT_YUV444),
+            (9, 0) | (9, 1) | (10, 0) | (10, 1) => Ok(libva::constants::VA_RT_FORMAT_YUV420_10),
+            (9, 2) | (10, 2) => Ok(libva::constants::VA_RT_FORMAT_YUV422_10),
+            (9, 3) | (10, 3) => Ok(libva::constants::VA_RT_FORMAT_YUV444_10),
+            (11, 0) | (11, 1) | (12, 0) | (12, 1) => Ok(libva::constants::VA_RT_FORMAT_YUV420_12),
+            (11, 2) | (12, 2) => Ok(libva::constants::VA_RT_FORMAT_YUV422_12),
+            (11, 3) | (12, 3) => Ok(libva::constants::VA_RT_FORMAT_YUV444_12),
             _ => Err(anyhow!(
                 "unsupported bit depth/chroma format pair {}, {}",
                 bit_depth,
@@ -130,8 +130,8 @@
         self.max_dpb_size() + 4
     }
 
-    fn coded_size(&self) -> Resolution {
-        Resolution::from((self.width().into(), self.height().into()))
+    fn coded_size(&self) -> (u32, u32) {
+        (self.width().into(), self.height().into())
     }
 
     fn visible_rect(&self) -> ((u32, u32), (u32, u32)) {
@@ -154,7 +154,7 @@
 
         if let Some(ref_pic_list_entry) = ref_pic_list_entry {
             for (va_ref_idx, va_ref) in va_references.iter().enumerate() {
-                if va_ref.picture_id() == libva::VA_INVALID_ID {
+                if va_ref.picture_id() == libva::constants::VA_INVALID_ID {
                     break;
                 }
 
@@ -183,21 +183,21 @@
         .flatten()
         .any(|dpb_entry| *dpb_entry.0.borrow() == *hevc_pic)
     {
-        libva::VA_PICTURE_HEVC_RPS_ST_CURR_BEFORE
+        libva::constants::VA_PICTURE_HEVC_RPS_ST_CURR_BEFORE
     } else if rps
         .ref_pic_set_st_curr_after
         .iter()
         .flatten()
         .any(|dpb_entry| *dpb_entry.0.borrow() == *hevc_pic)
     {
-        libva::VA_PICTURE_HEVC_RPS_ST_CURR_AFTER
+        libva::constants::VA_PICTURE_HEVC_RPS_ST_CURR_AFTER
     } else if rps
         .ref_pic_set_lt_curr
         .iter()
         .flatten()
         .any(|dpb_entry| *dpb_entry.0.borrow() == *hevc_pic)
     {
-        libva::VA_PICTURE_HEVC_RPS_LT_CURR
+        libva::constants::VA_PICTURE_HEVC_RPS_LT_CURR
     } else {
         0
     }
@@ -206,7 +206,11 @@
 /// Builds an invalid VaPictureHEVC. These pictures are used to fill empty
 /// array slots there is no data to fill them with.
 fn build_invalid_va_hevc_pic() -> libva::PictureHEVC {
-    libva::PictureHEVC::new(libva::VA_INVALID_ID, 0, libva::VA_PICTURE_HEVC_INVALID)
+    libva::PictureHEVC::new(
+        libva::constants::VA_INVALID_ID,
+        0,
+        libva::constants::VA_PICTURE_HEVC_INVALID,
+    )
 }
 
 fn fill_va_hevc_pic<M: SurfaceMemoryDescriptor>(
@@ -217,7 +221,7 @@
     let mut flags = 0;
 
     if matches!(hevc_pic.reference(), Reference::LongTerm) {
-        flags |= libva::VA_PICTURE_HEVC_LONG_TERM_REFERENCE;
+        flags |= libva::constants::VA_PICTURE_HEVC_LONG_TERM_REFERENCE;
     }
 
     flags |= va_rps_flag(hevc_pic, rps);
@@ -771,7 +775,7 @@
         let slice_param = SliceParameterBufferHEVC::new(
             slice.nalu.size as u32,
             0,
-            libva::VA_SLICE_DATA_FLAG_ALL,
+            libva::constants::VA_SLICE_DATA_FLAG_ALL,
             (hdr.header_bit_size / 8) as _,
             hdr.segment_address,
             [ref_pic_list0, ref_pic_list1],
diff --git a/src/decoder/stateless/vp8.rs b/src/decoder/stateless/vp8.rs
index 1a88ea7..2a2998c 100644
--- a/src/decoder/stateless/vp8.rs
+++ b/src/decoder/stateless/vp8.rs
@@ -4,8 +4,6 @@
 
 #[cfg(any(test, fuzzing))]
 mod dummy;
-#[cfg(feature = "v4l2")]
-mod v4l2;
 #[cfg(feature = "vaapi")]
 mod vaapi;
 
diff --git a/src/decoder/stateless/vp8/v4l2.rs b/src/decoder/stateless/vp8/v4l2.rs
deleted file mode 100644
index 6484c1d..0000000
--- a/src/decoder/stateless/vp8/v4l2.rs
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2024 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-use std::cell::RefCell;
-use std::rc::Rc;
-
-use v4l2r::bindings::v4l2_ctrl_vp8_frame;
-use v4l2r::controls::SafeExtControl;
-
-use crate::backend::v4l2::decoder::stateless::BackendHandle;
-use crate::backend::v4l2::decoder::stateless::V4l2Picture;
-use crate::backend::v4l2::decoder::stateless::V4l2StatelessDecoderBackend;
-use crate::backend::v4l2::decoder::stateless::V4l2StatelessDecoderHandle;
-use crate::backend::v4l2::decoder::V4l2StreamInfo;
-
-use crate::codec::vp8::parser::Header;
-use crate::codec::vp8::parser::MbLfAdjustments;
-use crate::codec::vp8::parser::Segmentation;
-
-use crate::decoder::stateless::vp8::StatelessVp8DecoderBackend;
-use crate::decoder::stateless::vp8::Vp8;
-
-use crate::decoder::stateless::NewPictureError;
-use crate::decoder::stateless::NewPictureResult;
-use crate::decoder::stateless::StatelessBackendResult;
-use crate::decoder::stateless::StatelessDecoder;
-use crate::decoder::stateless::StatelessDecoderBackendPicture;
-use crate::decoder::BlockingMode;
-
-use crate::device::v4l2::stateless::controls::vp8::V4l2CtrlVp8FrameParams;
-
-use crate::Resolution;
-
-/// The number of frames to allocate for this codec. Same as GStreamer's vavp8dec.
-const NUM_FRAMES: usize = 7;
-
-impl V4l2StreamInfo for &Header {
-    fn min_num_frames(&self) -> usize {
-        NUM_FRAMES
-    }
-
-    fn coded_size(&self) -> Resolution {
-        Resolution::from((self.width as u32, self.height as u32))
-    }
-
-    fn visible_rect(&self) -> ((u32, u32), (u32, u32)) {
-        ((0, 0), self.coded_size().into())
-    }
-}
-
-impl StatelessDecoderBackendPicture<Vp8> for V4l2StatelessDecoderBackend {
-    type Picture = Rc<RefCell<V4l2Picture>>;
-}
-
-impl StatelessVp8DecoderBackend for V4l2StatelessDecoderBackend {
-    fn new_sequence(&mut self, _: &Header) -> StatelessBackendResult<()> {
-        Ok(())
-    }
-
-    fn new_picture(&mut self, timestamp: u64) -> NewPictureResult<Self::Picture> {
-        let request_buffer = match self.device.alloc_request(timestamp) {
-            Ok(buffer) => buffer,
-            _ => return Err(NewPictureError::OutOfOutputBuffers),
-        };
-        Ok(Rc::new(RefCell::new(V4l2Picture::new(request_buffer))))
-    }
-
-    fn submit_picture(
-        &mut self,
-        picture: Self::Picture,
-        hdr: &Header,
-        _: &Option<Self::Handle>,
-        _: &Option<Self::Handle>,
-        _: &Option<Self::Handle>,
-        _: &[u8],
-        segmentation: &Segmentation,
-        mb_lf_adjust: &MbLfAdjustments,
-    ) -> StatelessBackendResult<Self::Handle> {
-        let mut vp8_frame_params = V4l2CtrlVp8FrameParams::new();
-
-        vp8_frame_params
-            .set_loop_filter_params(hdr, mb_lf_adjust)
-            .set_quantization_params(hdr)
-            .set_segmentation_params(segmentation)
-            .set_entropy_params(hdr);
-
-        let handle = Rc::new(RefCell::new(BackendHandle {
-            picture: picture.clone(),
-        }));
-        println!(
-            "{:<20} {:?}\n",
-            "submit_picture",
-            picture.borrow().timestamp()
-        );
-        picture.borrow_mut().request().submit();
-        Ok(V4l2StatelessDecoderHandle { handle })
-    }
-}
-
-impl StatelessDecoder<Vp8, V4l2StatelessDecoderBackend> {
-    // Creates a new instance of the decoder using the v4l2 backend.
-    pub fn new_v4l2(blocking_mode: BlockingMode) -> Self {
-        Self::new(V4l2StatelessDecoderBackend::new(), blocking_mode)
-            .expect("Failed to create v4l2 stateless decoder backend")
-    }
-}
diff --git a/src/decoder/stateless/vp8/vaapi.rs b/src/decoder/stateless/vp8/vaapi.rs
index 3e59a44..e4aa389 100644
--- a/src/decoder/stateless/vp8/vaapi.rs
+++ b/src/decoder/stateless/vp8/vaapi.rs
@@ -43,19 +43,19 @@
     }
 
     fn rt_format(&self) -> anyhow::Result<u32> {
-        Ok(libva::VA_RT_FORMAT_YUV420)
+        Ok(libva::constants::VA_RT_FORMAT_YUV420)
     }
 
     fn min_num_surfaces(&self) -> usize {
         NUM_SURFACES
     }
 
-    fn coded_size(&self) -> Resolution {
-        Resolution::from((self.width as u32, self.height as u32))
+    fn coded_size(&self) -> (u32, u32) {
+        (self.width as u32, self.height as u32)
     }
 
     fn visible_rect(&self) -> ((u32, u32), (u32, u32)) {
-        ((0, 0), self.coded_size().into())
+        ((0, 0), self.coded_size())
     }
 }
 
@@ -409,9 +409,9 @@
             &resolution,
             parser.segmentation(),
             parser.mb_lf_adjust(),
-            libva::VA_INVALID_SURFACE,
-            libva::VA_INVALID_SURFACE,
-            libva::VA_INVALID_SURFACE,
+            libva::constants::VA_INVALID_SURFACE,
+            libva::constants::VA_INVALID_SURFACE,
+            libva::constants::VA_INVALID_SURFACE,
         )
         .unwrap();
         let pic_param = match pic_param {
@@ -458,15 +458,21 @@
 
         assert_eq!(pic_param.inner().frame_width, 320);
         assert_eq!(pic_param.inner().frame_height, 240);
-        assert_eq!(pic_param.inner().last_ref_frame, libva::VA_INVALID_SURFACE);
+        assert_eq!(
+            pic_param.inner().last_ref_frame,
+            libva::constants::VA_INVALID_SURFACE
+        );
         assert_eq!(
             pic_param.inner().golden_ref_frame,
-            libva::VA_INVALID_SURFACE
+            libva::constants::VA_INVALID_SURFACE
         );
-        assert_eq!(pic_param.inner().alt_ref_frame, libva::VA_INVALID_SURFACE);
+        assert_eq!(
+            pic_param.inner().alt_ref_frame,
+            libva::constants::VA_INVALID_SURFACE
+        );
         assert_eq!(
             pic_param.inner().out_of_loop_frame,
-            libva::VA_INVALID_SURFACE
+            libva::constants::VA_INVALID_SURFACE
         );
 
         // Safe because this bitfield is initialized by the decoder.
@@ -587,7 +593,7 @@
         assert_eq!(pic_param.inner().alt_ref_frame, 0);
         assert_eq!(
             pic_param.inner().out_of_loop_frame,
-            libva::VA_INVALID_SURFACE
+            libva::constants::VA_INVALID_SURFACE
         );
 
         // Safe because this bitfield is initialized by the decoder.
@@ -701,7 +707,7 @@
         assert_eq!(pic_param.inner().alt_ref_frame, 0);
         assert_eq!(
             pic_param.inner().out_of_loop_frame,
-            libva::VA_INVALID_SURFACE
+            libva::constants::VA_INVALID_SURFACE
         );
 
         // Safe because this bitfield is initialized by the decoder.
diff --git a/src/decoder/stateless/vp9/vaapi.rs b/src/decoder/stateless/vp9/vaapi.rs
index bd94079..1212ca7 100644
--- a/src/decoder/stateless/vp9/vaapi.rs
+++ b/src/decoder/stateless/vp9/vaapi.rs
@@ -34,7 +34,6 @@
 use crate::decoder::stateless::StatelessDecoder;
 use crate::decoder::stateless::StatelessDecoderBackendPicture;
 use crate::decoder::BlockingMode;
-use crate::Resolution;
 
 /// The number of surfaces to allocate for this codec.
 const NUM_SURFACES: usize = 12;
@@ -47,12 +46,12 @@
     subsampling_y: bool,
 ) -> anyhow::Result<u32> {
     match profile {
-        Profile::Profile0 => Ok(libva::VA_RT_FORMAT_YUV420),
+        Profile::Profile0 => Ok(libva::constants::VA_RT_FORMAT_YUV420),
         Profile::Profile1 => {
             if subsampling_x && !subsampling_y {
-                Ok(libva::VA_RT_FORMAT_YUV422)
+                Ok(libva::constants::VA_RT_FORMAT_YUV422)
             } else if !subsampling_x && !subsampling_y {
-                Ok(libva::VA_RT_FORMAT_YUV444)
+                Ok(libva::constants::VA_RT_FORMAT_YUV444)
             } else {
                 Err(anyhow!(
                     "Unsupported subsampling for profile 1: X: {:?} Y: {:?}",
@@ -66,8 +65,8 @@
                 "Unsupported bit depth for profile 2: {:?}",
                 bit_depth
             )),
-            BitDepth::Depth10 => Ok(libva::VA_RT_FORMAT_YUV420_10),
-            BitDepth::Depth12 => Ok(libva::VA_RT_FORMAT_YUV420_12),
+            BitDepth::Depth10 => Ok(libva::constants::VA_RT_FORMAT_YUV420_10),
+            BitDepth::Depth12 => Ok(libva::constants::VA_RT_FORMAT_YUV420_12),
         },
         Profile::Profile3 => {
             if subsampling_x && !subsampling_y {
@@ -78,8 +77,8 @@
                             subsampling_y,
                             bit_depth
                         )),
-                        BitDepth::Depth10 => Ok(libva::VA_RT_FORMAT_YUV422_10),
-                        BitDepth::Depth12 => Ok(libva::VA_RT_FORMAT_YUV422_12),
+                        BitDepth::Depth10 => Ok(libva::constants::VA_RT_FORMAT_YUV422_10),
+                        BitDepth::Depth12 => Ok(libva::constants::VA_RT_FORMAT_YUV422_12),
                     }
             } else if !subsampling_x && !subsampling_y {
                 match bit_depth {
@@ -89,8 +88,8 @@
                             subsampling_y,
                             bit_depth
                         )),
-                        BitDepth::Depth10 => Ok(libva::VA_RT_FORMAT_YUV444_10),
-                        BitDepth::Depth12 => Ok(libva::VA_RT_FORMAT_YUV444_12),
+                        BitDepth::Depth10 => Ok(libva::constants::VA_RT_FORMAT_YUV444_10),
+                        BitDepth::Depth12 => Ok(libva::constants::VA_RT_FORMAT_YUV444_12),
                     }
             } else {
                 Err(anyhow!(
@@ -127,12 +126,12 @@
         NUM_SURFACES
     }
 
-    fn coded_size(&self) -> Resolution {
-        Resolution::from((self.width, self.height))
+    fn coded_size(&self) -> (u32, u32) {
+        (self.width, self.height)
     }
 
     fn visible_rect(&self) -> ((u32, u32), (u32, u32)) {
-        ((0, 0), self.coded_size().into())
+        ((0, 0), self.coded_size())
     }
 }
 
@@ -233,7 +232,7 @@
         libva::SliceParameter::VP9(libva::SliceParameterBufferVP9::new(
             slice_size as u32,
             0,
-            libva::VA_SLICE_DATA_FLAG_ALL,
+            libva::constants::VA_SLICE_DATA_FLAG_ALL,
             seg_params,
         )),
     ))
@@ -505,8 +504,11 @@
 
         assert_eq!(frame.as_ref().len(), 10674);
 
-        let pic_param =
-            build_pic_param(&frame.header, [libva::VA_INVALID_SURFACE; NUM_REF_FRAMES]).unwrap();
+        let pic_param = build_pic_param(
+            &frame.header,
+            [libva::constants::VA_INVALID_SURFACE; NUM_REF_FRAMES],
+        )
+        .unwrap();
         let pic_param = match pic_param {
             BufferType::PictureParameter(PictureParameter::VP9(pic_param)) => pic_param,
             _ => panic!(),
@@ -523,7 +525,7 @@
         assert_eq!(pic_param.inner().frame_height, 240);
         assert_eq!(
             pic_param.inner().reference_frames,
-            [libva::VA_INVALID_SURFACE; NUM_REF_FRAMES]
+            [libva::constants::VA_INVALID_SURFACE; NUM_REF_FRAMES]
         );
 
         // Safe because this bitfield is initialized by the decoder.
@@ -550,7 +552,7 @@
         assert_eq!(slice_param.inner().slice_data_offset, 0);
         assert_eq!(
             slice_param.inner().slice_data_flag,
-            libva::VA_SLICE_DATA_FLAG_ALL
+            libva::constants::VA_SLICE_DATA_FLAG_ALL
         );
 
         for seg_param in &slice_param.inner().seg_param {
@@ -611,7 +613,7 @@
         assert_eq!(slice_param.inner().slice_data_offset, 0);
         assert_eq!(
             slice_param.inner().slice_data_flag,
-            libva::VA_SLICE_DATA_FLAG_ALL
+            libva::constants::VA_SLICE_DATA_FLAG_ALL
         );
 
         for seg_param in &slice_param.inner().seg_param {
@@ -673,7 +675,7 @@
         assert_eq!(slice_param.inner().slice_data_offset, 0);
         assert_eq!(
             slice_param.inner().slice_data_flag,
-            libva::VA_SLICE_DATA_FLAG_ALL
+            libva::constants::VA_SLICE_DATA_FLAG_ALL
         );
 
         for seg_param in &slice_param.inner().seg_param {
diff --git a/src/device.rs b/src/device.rs
deleted file mode 100644
index e82a330..0000000
--- a/src/device.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-// Copyright 2024 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#[cfg(feature = "v4l2")]
-pub mod v4l2;
diff --git a/src/device/v4l2.rs b/src/device/v4l2.rs
deleted file mode 100644
index 9ce03c2..0000000
--- a/src/device/v4l2.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright 2024 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-pub mod stateless;
diff --git a/src/device/v4l2/stateless.rs b/src/device/v4l2/stateless.rs
deleted file mode 100644
index 0a6a11b..0000000
--- a/src/device/v4l2/stateless.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2024 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-pub mod controls;
-pub mod device;
-pub mod queue;
-pub mod request;
diff --git a/src/device/v4l2/stateless/controls.rs b/src/device/v4l2/stateless/controls.rs
deleted file mode 100644
index 2aec394..0000000
--- a/src/device/v4l2/stateless/controls.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-// Copyright 2024 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-pub mod h264;
-pub mod vp8;
diff --git a/src/device/v4l2/stateless/controls/h264.rs b/src/device/v4l2/stateless/controls/h264.rs
deleted file mode 100644
index f29f7ab..0000000
--- a/src/device/v4l2/stateless/controls/h264.rs
+++ /dev/null
@@ -1,282 +0,0 @@
-// Copyright 2024 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-use crate::codec::h264::parser::Pps;
-use crate::codec::h264::parser::SliceHeader;
-use crate::codec::h264::parser::Sps;
-use crate::codec::h264::picture::Field;
-use crate::codec::h264::picture::IsIdr;
-use crate::codec::h264::picture::PictureData;
-use crate::codec::h264::picture::RcPictureData;
-use crate::codec::h264::picture::Reference;
-
-use v4l2r::bindings::v4l2_ctrl_h264_decode_params;
-use v4l2r::bindings::v4l2_ctrl_h264_pps;
-use v4l2r::bindings::v4l2_ctrl_h264_scaling_matrix;
-use v4l2r::bindings::v4l2_ctrl_h264_sps;
-use v4l2r::bindings::v4l2_h264_dpb_entry;
-use v4l2r::bindings::v4l2_stateless_h264_decode_mode_V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED as V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED;
-use v4l2r::bindings::v4l2_stateless_h264_decode_mode_V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED as V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED;
-use v4l2r::bindings::V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD;
-use v4l2r::bindings::V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC;
-use v4l2r::bindings::V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC;
-use v4l2r::bindings::V4L2_H264_DPB_ENTRY_FLAG_ACTIVE;
-use v4l2r::bindings::V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM;
-use v4l2r::bindings::V4L2_H264_DPB_ENTRY_FLAG_VALID;
-use v4l2r::bindings::V4L2_H264_FRAME_REF;
-use v4l2r::bindings::V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT;
-use v4l2r::bindings::V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED;
-use v4l2r::bindings::V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT;
-use v4l2r::bindings::V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE;
-use v4l2r::bindings::V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT;
-use v4l2r::bindings::V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT;
-use v4l2r::bindings::V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE;
-use v4l2r::bindings::V4L2_H264_PPS_FLAG_WEIGHTED_PRED;
-use v4l2r::bindings::V4L2_H264_SPS_CONSTRAINT_SET0_FLAG;
-use v4l2r::bindings::V4L2_H264_SPS_CONSTRAINT_SET1_FLAG;
-use v4l2r::bindings::V4L2_H264_SPS_CONSTRAINT_SET2_FLAG;
-use v4l2r::bindings::V4L2_H264_SPS_CONSTRAINT_SET3_FLAG;
-use v4l2r::bindings::V4L2_H264_SPS_CONSTRAINT_SET4_FLAG;
-use v4l2r::bindings::V4L2_H264_SPS_CONSTRAINT_SET5_FLAG;
-use v4l2r::bindings::V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO;
-use v4l2r::bindings::V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE;
-use v4l2r::bindings::V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY;
-use v4l2r::bindings::V4L2_H264_SPS_FLAG_GAPS_IN_FRAME_NUM_VALUE_ALLOWED;
-use v4l2r::bindings::V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD;
-use v4l2r::bindings::V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS;
-use v4l2r::bindings::V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE;
-use v4l2r::controls::codec::H264DecodeMode;
-use v4l2r::controls::codec::H264DecodeParams;
-use v4l2r::controls::codec::H264ScalingMatrix;
-use v4l2r::controls::SafeExtControl;
-
-impl From<&Sps> for v4l2_ctrl_h264_sps {
-    fn from(sps: &Sps) -> Self {
-        let mut constraint_set_flags: u32 = 0;
-        if sps.constraint_set0_flag {
-            constraint_set_flags |= V4L2_H264_SPS_CONSTRAINT_SET0_FLAG;
-        }
-        if sps.constraint_set1_flag {
-            constraint_set_flags |= V4L2_H264_SPS_CONSTRAINT_SET1_FLAG;
-        }
-        if sps.constraint_set2_flag {
-            constraint_set_flags |= V4L2_H264_SPS_CONSTRAINT_SET2_FLAG;
-        }
-        if sps.constraint_set3_flag {
-            constraint_set_flags |= V4L2_H264_SPS_CONSTRAINT_SET3_FLAG;
-        }
-        if sps.constraint_set4_flag {
-            constraint_set_flags |= V4L2_H264_SPS_CONSTRAINT_SET4_FLAG;
-        }
-        if sps.constraint_set5_flag {
-            constraint_set_flags |= V4L2_H264_SPS_CONSTRAINT_SET5_FLAG;
-        }
-        let mut flags: u32 = 0;
-        if sps.separate_colour_plane_flag {
-            flags |= V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE;
-        }
-        if sps.qpprime_y_zero_transform_bypass_flag {
-            flags |= V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS;
-        }
-        if sps.delta_pic_order_always_zero_flag {
-            flags |= V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO;
-        }
-        if sps.gaps_in_frame_num_value_allowed_flag {
-            flags |= V4L2_H264_SPS_FLAG_GAPS_IN_FRAME_NUM_VALUE_ALLOWED;
-        }
-        if sps.frame_mbs_only_flag {
-            flags |= V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY;
-        }
-        if sps.mb_adaptive_frame_field_flag {
-            flags |= V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD;
-        }
-        if sps.direct_8x8_inference_flag {
-            flags |= V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE;
-        }
-        Self {
-            profile_idc: sps.profile_idc,
-            constraint_set_flags: constraint_set_flags as u8,
-            level_idc: sps.level_idc as u8,
-            seq_parameter_set_id: sps.seq_parameter_set_id,
-            chroma_format_idc: sps.chroma_format_idc,
-            bit_depth_luma_minus8: sps.bit_depth_luma_minus8,
-            bit_depth_chroma_minus8: sps.bit_depth_chroma_minus8,
-            log2_max_frame_num_minus4: sps.log2_max_frame_num_minus4,
-            pic_order_cnt_type: sps.pic_order_cnt_type,
-            log2_max_pic_order_cnt_lsb_minus4: sps.log2_max_pic_order_cnt_lsb_minus4,
-            max_num_ref_frames: sps.max_num_ref_frames as u8,
-            num_ref_frames_in_pic_order_cnt_cycle: sps.num_ref_frames_in_pic_order_cnt_cycle,
-            offset_for_ref_frame: sps.offset_for_ref_frame,
-            offset_for_non_ref_pic: sps.offset_for_non_ref_pic,
-            offset_for_top_to_bottom_field: sps.offset_for_top_to_bottom_field,
-            pic_width_in_mbs_minus1: sps.pic_width_in_mbs_minus1 as u16,
-            pic_height_in_map_units_minus1: sps.pic_height_in_map_units_minus1 as u16,
-            flags,
-            ..Default::default()
-        }
-    }
-}
-
-impl From<&Pps> for v4l2_ctrl_h264_pps {
-    fn from(pps: &Pps) -> Self {
-        let mut flags: u32 = 0;
-        if pps.entropy_coding_mode_flag {
-            flags |= V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE;
-        }
-        if pps.bottom_field_pic_order_in_frame_present_flag {
-            flags |= V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT;
-        }
-        if pps.weighted_pred_flag {
-            flags |= V4L2_H264_PPS_FLAG_WEIGHTED_PRED;
-        }
-        if pps.deblocking_filter_control_present_flag {
-            flags |= V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT;
-        }
-        if pps.constrained_intra_pred_flag {
-            flags |= V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED;
-        }
-        if pps.redundant_pic_cnt_present_flag {
-            flags |= V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT;
-        }
-        if pps.transform_8x8_mode_flag {
-            flags |= V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE;
-        }
-        if pps.pic_scaling_matrix_present_flag {
-            flags |= V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT;
-        }
-        Self {
-            pic_parameter_set_id: pps.pic_parameter_set_id,
-            seq_parameter_set_id: pps.seq_parameter_set_id,
-            num_slice_groups_minus1: pps.num_slice_groups_minus1 as u8,
-            num_ref_idx_l0_default_active_minus1: pps.num_ref_idx_l0_default_active_minus1,
-            num_ref_idx_l1_default_active_minus1: pps.num_ref_idx_l1_default_active_minus1,
-            weighted_bipred_idc: pps.weighted_bipred_idc,
-            pic_init_qp_minus26: pps.pic_init_qp_minus26,
-            pic_init_qs_minus26: pps.pic_init_qs_minus26,
-            chroma_qp_index_offset: pps.chroma_qp_index_offset,
-            second_chroma_qp_index_offset: pps.second_chroma_qp_index_offset,
-            flags: flags as u16,
-            ..Default::default()
-        }
-    }
-}
-
-pub struct V4l2CtrlH264DpbEntry {
-    pub timestamp: u64,
-    pub pic: RcPictureData,
-}
-
-impl From<&V4l2CtrlH264DpbEntry> for v4l2_h264_dpb_entry {
-    fn from(dpb: &V4l2CtrlH264DpbEntry) -> Self {
-        let pic: &PictureData = &dpb.pic.borrow();
-        // TODO     DCHECK_EQ(pic->field, H264Picture::FIELD_NONE)
-        // TODO         << "Interlacing not supported";
-
-        let (frame_num, pic_num): (u16, u32) = match pic.reference() {
-            Reference::LongTerm => (pic.long_term_pic_num as u16, pic.long_term_frame_idx),
-            _ => (pic.frame_num as u16, pic.pic_num as u32),
-        };
-
-        let mut flags: u32 = V4L2_H264_DPB_ENTRY_FLAG_VALID;
-        if pic.nal_ref_idc != 0 {
-            flags |= V4L2_H264_DPB_ENTRY_FLAG_ACTIVE;
-        }
-        if matches!(pic.reference(), Reference::LongTerm) {
-            flags |= V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM;
-        }
-
-        Self {
-            reference_ts: dpb.timestamp * 1000, // usec to nsec
-            frame_num,
-            pic_num,
-            fields: V4L2_H264_FRAME_REF as u8,
-            top_field_order_cnt: pic.top_field_order_cnt,
-            bottom_field_order_cnt: pic.bottom_field_order_cnt,
-            flags,
-            ..Default::default()
-        }
-    }
-}
-
-#[derive(Default)]
-pub struct V4l2CtrlH264ScalingMatrix {
-    handle: v4l2_ctrl_h264_scaling_matrix,
-}
-
-impl V4l2CtrlH264ScalingMatrix {
-    pub fn new() -> Self {
-        Default::default()
-    }
-    pub fn set(&mut self) -> &mut Self {
-        todo!()
-    }
-}
-
-impl From<&V4l2CtrlH264ScalingMatrix> for SafeExtControl<H264ScalingMatrix> {
-    fn from(scaling_matrix: &V4l2CtrlH264ScalingMatrix) -> Self {
-        SafeExtControl::<H264ScalingMatrix>::from(scaling_matrix.handle)
-    }
-}
-
-#[derive(Default)]
-pub struct V4l2CtrlH264DecodeParams {
-    handle: v4l2_ctrl_h264_decode_params,
-}
-
-impl V4l2CtrlH264DecodeParams {
-    pub fn new() -> Self {
-        Default::default()
-    }
-    pub fn set_picture_data(&mut self, pic: &PictureData) -> &mut Self {
-        self.handle.top_field_order_cnt = pic.top_field_order_cnt;
-        self.handle.bottom_field_order_cnt = pic.bottom_field_order_cnt;
-        self.handle.flags |= match pic.field {
-            Field::Top => V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC,
-            Field::Bottom => {
-                V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC | V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD
-            }
-            _ => 0,
-        };
-        self.handle.flags |= match pic.is_idr {
-            IsIdr::Yes { idr_pic_id: _ } => V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC,
-            _ => 0,
-        };
-        self.handle.nal_ref_idc = pic.nal_ref_idc as u16;
-        self
-    }
-    pub fn set_dpb_entries(&mut self, dpb: Vec<V4l2CtrlH264DpbEntry>) -> &mut Self {
-        for i in 0..dpb.len() {
-            self.handle.dpb[i] = v4l2_h264_dpb_entry::from(&dpb[i]);
-        }
-        self
-    }
-    pub fn set_slice_header(&mut self, slice_header: &SliceHeader) -> &mut Self {
-        self.handle.frame_num = slice_header.frame_num;
-        self.handle.idr_pic_id = slice_header.idr_pic_id;
-        self.handle.pic_order_cnt_lsb = slice_header.pic_order_cnt_lsb;
-        self.handle.delta_pic_order_cnt_bottom = slice_header.delta_pic_order_cnt_bottom;
-        self.handle.delta_pic_order_cnt0 = slice_header.delta_pic_order_cnt[0];
-        self.handle.delta_pic_order_cnt1 = slice_header.delta_pic_order_cnt[1];
-        self.handle.dec_ref_pic_marking_bit_size = slice_header.dec_ref_pic_marking_bit_size as u32;
-        self.handle.pic_order_cnt_bit_size = slice_header.pic_order_cnt_bit_size as u32;
-        self
-    }
-}
-
-impl From<&V4l2CtrlH264DecodeParams> for SafeExtControl<H264DecodeParams> {
-    fn from(decode_params: &V4l2CtrlH264DecodeParams) -> Self {
-        SafeExtControl::<H264DecodeParams>::from(decode_params.handle)
-    }
-}
-
-pub enum V4l2CtrlH264DecodeMode {
-    SliceBased = V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED as isize,
-    FrameBased = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED as isize,
-}
-
-impl From<V4l2CtrlH264DecodeMode> for SafeExtControl<H264DecodeMode> {
-    fn from(decode_mode: V4l2CtrlH264DecodeMode) -> Self {
-        SafeExtControl::<H264DecodeMode>::from_value(decode_mode as i32)
-    }
-}
diff --git a/src/device/v4l2/stateless/controls/vp8.rs b/src/device/v4l2/stateless/controls/vp8.rs
deleted file mode 100644
index 44e9523..0000000
--- a/src/device/v4l2/stateless/controls/vp8.rs
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright 2024 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-use crate::codec::vp8::parser::Header;
-use crate::codec::vp8::parser::MbLfAdjustments;
-use crate::codec::vp8::parser::Segmentation;
-
-use v4l2r::bindings::v4l2_ctrl_vp8_frame;
-use v4l2r::bindings::V4L2_VP8_COEFF_PROB_CNT;
-use v4l2r::bindings::V4L2_VP8_FRAME_FLAG_EXPERIMENTAL;
-use v4l2r::bindings::V4L2_VP8_FRAME_FLAG_KEY_FRAME;
-use v4l2r::bindings::V4L2_VP8_FRAME_FLAG_MB_NO_SKIP_COEFF;
-use v4l2r::bindings::V4L2_VP8_FRAME_FLAG_SHOW_FRAME;
-use v4l2r::bindings::V4L2_VP8_FRAME_FLAG_SIGN_BIAS_ALT;
-use v4l2r::bindings::V4L2_VP8_FRAME_FLAG_SIGN_BIAS_GOLDEN;
-use v4l2r::bindings::V4L2_VP8_LF_ADJ_ENABLE;
-use v4l2r::bindings::V4L2_VP8_LF_DELTA_UPDATE;
-use v4l2r::bindings::V4L2_VP8_LF_FILTER_TYPE_SIMPLE;
-use v4l2r::bindings::V4L2_VP8_MV_PROB_CNT;
-use v4l2r::bindings::V4L2_VP8_SEGMENT_FLAG_DELTA_VALUE_MODE;
-use v4l2r::bindings::V4L2_VP8_SEGMENT_FLAG_ENABLED;
-use v4l2r::bindings::V4L2_VP8_SEGMENT_FLAG_UPDATE_FEATURE_DATA;
-use v4l2r::bindings::V4L2_VP8_SEGMENT_FLAG_UPDATE_MAP;
-
-use v4l2r::controls::codec::Vp8Frame;
-use v4l2r::controls::SafeExtControl;
-
-#[derive(Default)]
-pub struct V4l2CtrlVp8FrameParams {
-    handle: v4l2_ctrl_vp8_frame,
-}
-
-impl V4l2CtrlVp8FrameParams {
-    pub fn new() -> Self {
-        Default::default()
-    }
-
-    pub fn set_loop_filter_params(
-        &mut self,
-        hdr: &Header,
-        mb_lf_adjust: &MbLfAdjustments,
-    ) -> &mut Self {
-        self.handle.lf.sharpness_level = hdr.sharpness_level;
-        self.handle.lf.level = hdr.loop_filter_level;
-
-        let mut flags: u32 = 0;
-        if hdr.filter_type {
-            flags |= V4L2_VP8_LF_FILTER_TYPE_SIMPLE;
-        }
-        if mb_lf_adjust.loop_filter_adj_enable {
-            flags |= V4L2_VP8_LF_ADJ_ENABLE;
-        }
-        if mb_lf_adjust.mode_ref_lf_delta_update {
-            flags |= V4L2_VP8_LF_DELTA_UPDATE;
-        }
-        self.handle.lf.flags = flags;
-
-        for i in 0..4 {
-            self.handle.lf.ref_frm_delta[i] = mb_lf_adjust.ref_frame_delta[i];
-            self.handle.lf.mb_mode_delta[i] = mb_lf_adjust.mb_mode_delta[i];
-        }
-
-        self
-    }
-
-    pub fn set_quantization_params(&mut self, hdr: &Header) -> &mut Self {
-        self.handle.quant.y_ac_qi =
-            u8::try_from(hdr.quant_indices.y_ac_qi).expect("Value out of range for u8");
-
-        self.handle.quant.y_dc_delta =
-            i8::try_from(hdr.quant_indices.y_dc_delta).expect("Value out of range for u8");
-        self.handle.quant.y2_dc_delta =
-            i8::try_from(hdr.quant_indices.y2_dc_delta).expect("Value out of range for u8");
-        self.handle.quant.y2_ac_delta =
-            i8::try_from(hdr.quant_indices.y2_ac_delta).expect("Value out of range for u8");
-        self.handle.quant.uv_dc_delta =
-            i8::try_from(hdr.quant_indices.uv_dc_delta).expect("Value out of range for u8");
-        self.handle.quant.uv_ac_delta =
-            i8::try_from(hdr.quant_indices.uv_ac_delta).expect("Value out of range for u8");
-        self
-    }
-
-    pub fn set_segmentation_params(&mut self, segmentation: &Segmentation) -> &mut Self {
-        let mut flags: u32 = 0;
-
-        if segmentation.segmentation_enabled {
-            flags |= V4L2_VP8_SEGMENT_FLAG_ENABLED;
-        }
-        if segmentation.update_mb_segmentation_map {
-            flags |= V4L2_VP8_SEGMENT_FLAG_UPDATE_MAP;
-        }
-        if segmentation.update_segment_feature_data {
-            flags |= V4L2_VP8_SEGMENT_FLAG_UPDATE_FEATURE_DATA;
-        }
-        if segmentation.segment_feature_mode == false {
-            flags |= V4L2_VP8_SEGMENT_FLAG_DELTA_VALUE_MODE;
-        }
-        self.handle.segment.flags = flags;
-
-        for i in 0..4 {
-            self.handle.segment.quant_update[i] = segmentation.quantizer_update_value[i];
-            self.handle.segment.lf_update[i] = segmentation.lf_update_value[i];
-        }
-
-        for i in 0..3 {
-            self.handle.segment.segment_probs[i] = segmentation.segment_prob[i];
-        }
-
-        self.handle.segment.padding = 0;
-
-        self
-    }
-
-    pub fn set_entropy_params(&mut self, hdr: &Header) -> &mut Self {
-        self.handle.entropy.coeff_probs = hdr.coeff_prob;
-        self.handle.entropy.y_mode_probs = hdr.mode_probs.intra_16x16_prob;
-        self.handle.entropy.uv_mode_probs = hdr.mode_probs.intra_chroma_prob;
-        self.handle.entropy.mv_probs = hdr.mv_prob;
-        self
-    }
-}
-
-impl From<&V4l2CtrlVp8FrameParams> for SafeExtControl<Vp8Frame> {
-    fn from(decode_params: &V4l2CtrlVp8FrameParams) -> Self {
-        SafeExtControl::<Vp8Frame>::from(decode_params.handle)
-    }
-}
diff --git a/src/device/v4l2/stateless/device.rs b/src/device/v4l2/stateless/device.rs
deleted file mode 100644
index 4d5661a..0000000
--- a/src/device/v4l2/stateless/device.rs
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright 2024 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-use crate::decoder::stateless::DecodeError;
-use crate::device::v4l2::stateless::queue::V4l2CaptureBuffer;
-use crate::device::v4l2::stateless::queue::V4l2CaptureQueue;
-use crate::device::v4l2::stateless::queue::V4l2OutputBuffer;
-use crate::device::v4l2::stateless::queue::V4l2OutputQueue;
-use crate::device::v4l2::stateless::request::V4l2Request;
-use crate::Resolution;
-
-use std::cell::RefCell;
-use std::collections::HashMap;
-use std::os::fd::AsRawFd;
-use std::os::fd::RawFd;
-use std::path::Path;
-use std::rc::Rc;
-use std::sync::Arc;
-
-use v4l2r::device::Device as VideoDevice;
-use v4l2r::device::DeviceConfig;
-use v4l2r::ioctl;
-use v4l2r::nix::fcntl::open;
-use v4l2r::nix::fcntl::OFlag;
-use v4l2r::nix::sys::stat::Mode;
-
-//TODO: handle memory backends other than mmap
-//TODO: handle video formats other than h264
-//TODO: handle queue start/stop at runtime
-//TODO: handle DRC at runtime
-struct DeviceHandle {
-    video_device: Arc<VideoDevice>,
-    media_device: RawFd,
-    output_queue: V4l2OutputQueue,
-    capture_queue: V4l2CaptureQueue,
-    capture_buffers: HashMap<u64, V4l2CaptureBuffer>,
-}
-
-impl DeviceHandle {
-    fn new() -> Self {
-        // TODO: pass video device path and config via function arguments
-        let video_device_path = Path::new("/dev/video-dec0");
-        let video_device_config = DeviceConfig::new().non_blocking_dqbuf();
-        let video_device = Arc::new(
-            VideoDevice::open(video_device_path, video_device_config)
-                .expect("Failed to open video device"),
-        );
-        // TODO: probe capabilties to find releted media device path
-        let media_device_path = Path::new("/dev/media-dec0");
-        let media_device = open(
-            media_device_path,
-            OFlag::O_RDWR | OFlag::O_CLOEXEC,
-            Mode::empty(),
-        )
-        .unwrap_or_else(|_| panic!("Cannot open {}", media_device_path.display()));
-        // TODO: handle custom configuration
-        const NUM_OUTPUT_BUFFERS: u32 = 8;
-        const NUM_CAPTURE_BUFFERS: u32 = 8;
-        let output_queue = V4l2OutputQueue::new(video_device.clone(), NUM_OUTPUT_BUFFERS);
-        let capture_queue = V4l2CaptureQueue::new(video_device.clone(), NUM_CAPTURE_BUFFERS);
-        Self {
-            video_device,
-            media_device,
-            output_queue,
-            capture_queue,
-            capture_buffers: HashMap::<u64, V4l2CaptureBuffer>::new(),
-        }
-    }
-    fn alloc_request(&self) -> ioctl::Request {
-        ioctl::Request::alloc(&self.media_device).expect("Failed to alloc request handle")
-    }
-    fn alloc_buffer(&self) -> Result<V4l2OutputBuffer, DecodeError> {
-        self.output_queue.alloc_buffer()
-    }
-    fn sync(&mut self, timestamp: u64) -> V4l2CaptureBuffer {
-        // TODO: handle synced buffers internally by capture queue
-        loop {
-            match self.capture_buffers.remove(&timestamp) {
-                Some(buffer) => return buffer,
-                _ => self.recycle_buffers(), // TODO: poll/select
-            };
-        }
-    }
-    fn recycle_buffers(&mut self) {
-        self.output_queue.drain();
-        // TODO: handle synced buffers internally by capture queue
-        loop {
-            match self.capture_queue.dequeue_buffer() {
-                Some(buffer) => {
-                    self.capture_buffers.insert(buffer.timestamp(), buffer);
-                }
-                _ => break,
-            }
-        }
-        self.capture_queue.refill();
-    }
-}
-
-#[derive(Clone)]
-pub struct V4l2Device {
-    handle: Rc<RefCell<DeviceHandle>>,
-}
-
-impl V4l2Device {
-    pub fn new() -> Self {
-        Self {
-            handle: Rc::new(RefCell::new(DeviceHandle::new())),
-        }
-    }
-    pub fn num_free_buffers(&self) -> usize {
-        self.handle.borrow().output_queue.num_free_buffers()
-    }
-    pub fn num_buffers(&self) -> usize {
-        self.handle.borrow().output_queue.num_buffers()
-    }
-    pub fn set_resolution(&mut self, resolution: Resolution) -> &mut Self {
-        self.handle
-            .borrow_mut()
-            .output_queue
-            .set_resolution(resolution);
-        self.handle
-            .borrow_mut()
-            .capture_queue
-            .set_resolution(resolution);
-        self
-    }
-    pub fn alloc_request(&self, timestamp: u64) -> Result<V4l2Request, DecodeError> {
-        let output_buffer = self.handle.borrow().alloc_buffer()?;
-
-        Ok(V4l2Request::new(
-            self.clone(),
-            timestamp,
-            self.handle.borrow().alloc_request(),
-            output_buffer,
-        ))
-    }
-    pub fn sync(&self, timestamp: u64) -> V4l2CaptureBuffer {
-        self.handle.borrow_mut().sync(timestamp)
-    }
-    pub fn recycle_buffers(&self) {
-        self.handle.borrow_mut().recycle_buffers()
-    }
-}
-
-impl AsRawFd for V4l2Device {
-    fn as_raw_fd(&self) -> i32 {
-        self.handle.borrow().video_device.as_raw_fd()
-    }
-}
diff --git a/src/device/v4l2/stateless/queue.rs b/src/device/v4l2/stateless/queue.rs
deleted file mode 100644
index 966700c..0000000
--- a/src/device/v4l2/stateless/queue.rs
+++ /dev/null
@@ -1,338 +0,0 @@
-// Copyright 2024 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-use anyhow::anyhow;
-use std::cell::RefCell;
-use std::rc::Rc;
-use std::sync::Arc;
-
-use v4l2r::bindings::v4l2_format;
-use v4l2r::device::queue::direction::Capture;
-use v4l2r::device::queue::direction::Output;
-use v4l2r::device::queue::dqbuf::DqBuffer;
-use v4l2r::device::queue::qbuf::QBuffer;
-use v4l2r::device::queue::BuffersAllocated;
-use v4l2r::device::queue::GetFreeCaptureBuffer;
-use v4l2r::device::queue::GetFreeOutputBuffer;
-use v4l2r::device::queue::Queue;
-use v4l2r::device::queue::QueueInit;
-use v4l2r::device::AllocatedQueue;
-use v4l2r::device::Device;
-use v4l2r::device::Stream;
-use v4l2r::device::TryDequeue;
-use v4l2r::memory::MemoryType;
-use v4l2r::memory::MmapHandle;
-use v4l2r::nix::sys::time::TimeVal;
-use v4l2r::Format;
-use v4l2r::PixelFormat;
-use v4l2r::PlaneLayout;
-
-use crate::decoder::stateless::DecodeError;
-use crate::Resolution;
-
-//TODO: handle memory backends other than mmap
-pub struct V4l2OutputBuffer {
-    queue: V4l2OutputQueue,
-    handle: QBuffer<
-        Output,
-        Vec<MmapHandle>,
-        Vec<MmapHandle>,
-        Rc<Queue<Output, BuffersAllocated<Vec<MmapHandle>>>>,
-    >,
-    length: usize,
-}
-
-impl V4l2OutputBuffer {
-    fn new(
-        queue: V4l2OutputQueue,
-        handle: QBuffer<
-            Output,
-            Vec<MmapHandle>,
-            Vec<MmapHandle>,
-            Rc<Queue<Output, BuffersAllocated<Vec<MmapHandle>>>>,
-        >,
-    ) -> Self {
-        Self {
-            queue,
-            handle,
-            length: 0,
-        }
-    }
-    pub fn index(&self) -> usize {
-        self.handle.index()
-    }
-    pub fn length(&self) -> usize {
-        self.length
-    }
-    pub fn write(&mut self, data: &[u8]) -> &mut Self {
-        let mut mapping = self
-            .handle
-            .get_plane_mapping(0)
-            .expect("Failed to mmap output buffer");
-
-        mapping.as_mut()[self.length..self.length + 3].copy_from_slice(&[0, 0, 1]);
-        self.length += 3;
-
-        mapping.as_mut()[self.length..self.length + data.len()].copy_from_slice(data);
-        self.length += data.len();
-
-        drop(mapping);
-        self
-    }
-    pub fn submit(self, timestamp: u64, request_fd: i32) {
-        let handle = &*self.queue.handle.borrow();
-        let queue = match handle {
-            V4l2OutputQueueHandle::Streaming(queue) => queue,
-            _ => panic!("ERROR"),
-        };
-        self.handle
-            .set_timestamp(TimeVal::new(/* FIXME: sec */ 0, timestamp as i64))
-            .set_request(request_fd)
-            .queue(&[self.length])
-            .expect("Failed to queue output buffer");
-    }
-}
-
-//TODO: handle memory backends other than mmap
-//TODO: handle video formats other than h264
-//TODO: handle queue start/stop at runtime
-//TODO: handle DRC at runtime
-#[derive(Default)]
-enum V4l2OutputQueueHandle {
-    Init(Queue<Output, QueueInit>),
-    Streaming(Rc<Queue<Output, BuffersAllocated<Vec<MmapHandle>>>>),
-    #[default]
-    Unknown,
-}
-
-#[derive(Clone)]
-pub struct V4l2OutputQueue {
-    handle: Rc<RefCell<V4l2OutputQueueHandle>>,
-    num_buffers: u32,
-}
-
-impl V4l2OutputQueue {
-    pub fn new(device: Arc<Device>, num_buffers: u32) -> Self {
-        let handle = Queue::get_output_mplane_queue(device).expect("Failed to get output queue");
-        log::debug!("Output queue:\n\tstate: None -> Init\n");
-        let handle = Rc::new(RefCell::new(V4l2OutputQueueHandle::Init(handle)));
-        Self {
-            handle,
-            num_buffers,
-        }
-    }
-    pub fn set_resolution(&mut self, res: Resolution) -> &mut Self {
-        self.handle.replace(match self.handle.take() {
-            V4l2OutputQueueHandle::Init(mut handle) => {
-                let (width, height) = res.into();
-
-                handle
-                    .change_format()
-                    .expect("Failed to change output format")
-                    .set_size(width as usize, height as usize)
-                    .set_pixelformat(PixelFormat::from_fourcc(b"S264"))
-                    // 1 MB per decoding unit should be enough for most streams.
-                    .set_planes_layout(vec![PlaneLayout {
-                        sizeimage: 1024 * 1024,
-                        ..Default::default()
-                    }])
-                    .apply::<v4l2_format>()
-                    .expect("Failed to apply output format");
-
-                let format: Format = handle.get_format().expect("Failed to get output format");
-                log::debug!("Output format:\n\t{:?}\n", format);
-
-                let handle = handle
-                    .request_buffers_generic::<Vec<MmapHandle>>(MemoryType::Mmap, self.num_buffers)
-                    .expect("Failed to request output buffers");
-                log::debug!(
-                    "Output queue:\n\t
-                    num_buffers: {}\n\t
-                    num_queued_buffers: {}\n\t
-                    num_free_buffers: {}\n",
-                    handle.num_buffers(),
-                    handle.num_queued_buffers(),
-                    handle.num_free_buffers()
-                );
-
-                // TODO: handle start/stop at runtime
-                handle.stream_on().expect("Failed to start output queue");
-
-                log::debug!("Output queue:\n\tstate: Init -> Streaming\n");
-                V4l2OutputQueueHandle::Streaming(handle.into())
-            }
-            _ => {
-                /* TODO: handle DRC */
-                todo!()
-            }
-        });
-        self
-    }
-    pub fn num_buffers(&self) -> usize {
-        let handle = &*self.handle.borrow();
-        match handle {
-            V4l2OutputQueueHandle::Streaming(handle) => handle.num_buffers(),
-            _ => 0,
-        }
-    }
-    pub fn num_free_buffers(&self) -> usize {
-        let handle = &*self.handle.borrow();
-        match handle {
-            V4l2OutputQueueHandle::Streaming(handle) => handle.num_free_buffers(),
-            _ => 0,
-        }
-    }
-    pub fn alloc_buffer(&self) -> Result<V4l2OutputBuffer, DecodeError> {
-        let handle = &*self.handle.borrow();
-        match handle {
-            V4l2OutputQueueHandle::Streaming(handle) => match handle.try_get_free_buffer() {
-                Ok(buffer) => Ok(V4l2OutputBuffer::new(self.clone(), buffer)),
-                Err(_) => Err(DecodeError::NotEnoughOutputBuffers(1)),
-            },
-            _ => Err(DecodeError::DecoderError(anyhow!(
-                "Invalid hardware handle"
-            ))),
-        }
-    }
-    pub fn drain(&self) {
-        let handle = &*self.handle.borrow();
-        match handle {
-            V4l2OutputQueueHandle::Streaming(handle) => loop {
-                match handle.try_dequeue() {
-                    Ok(buffer) => continue,
-                    _ => break,
-                }
-            },
-            _ => panic!("ERROR"),
-        }
-    }
-}
-
-// TODO: handle other memory backends
-pub struct V4l2CaptureBuffer {
-    handle: DqBuffer<Capture, Vec<MmapHandle>>,
-}
-
-impl V4l2CaptureBuffer {
-    fn new(handle: DqBuffer<Capture, Vec<MmapHandle>>) -> Self {
-        Self { handle }
-    }
-    pub fn index(&self) -> usize {
-        self.handle.data.index() as usize
-    }
-    pub fn timestamp(&self) -> u64 {
-        self.handle.data.timestamp().tv_usec as u64
-    }
-    pub fn length(&self) -> usize {
-        let mut length = 0;
-        for i in 0..self.handle.data.num_planes() {
-            let mapping = self
-                .handle
-                .get_plane_mapping(i)
-                .expect("Failed to mmap capture buffer");
-            length += mapping.size();
-            drop(mapping);
-        }
-        length
-    }
-    pub fn read(&self, data: &mut [u8]) {
-        let mut offset = 0;
-        for i in 0..self.handle.data.num_planes() {
-            let mapping = self
-                .handle
-                .get_plane_mapping(i)
-                .expect("Failed to mmap capture buffer");
-            data[offset..offset + mapping.size()].copy_from_slice(&mapping);
-            offset += mapping.size();
-            drop(mapping);
-        }
-    }
-}
-
-//TODO: handle memory backends other than mmap
-//TODO: handle video formats other than h264
-//TODO: handle queue start/stop at runtime
-//TODO: handle DRC at runtime
-//TODO: handle synced buffers in Streaming state
-#[derive(Default)]
-enum V4l2CaptureQueueHandle {
-    Init(Queue<Capture, QueueInit>),
-    Streaming(Queue<Capture, BuffersAllocated<Vec<MmapHandle>>>),
-    #[default]
-    Unknown,
-}
-
-pub struct V4l2CaptureQueue {
-    handle: RefCell<V4l2CaptureQueueHandle>,
-    num_buffers: u32,
-}
-
-impl V4l2CaptureQueue {
-    pub fn new(device: Arc<Device>, num_buffers: u32) -> Self {
-        let handle = Queue::get_capture_mplane_queue(device).expect("Failed to get capture queue");
-        log::debug!("Capture queue:\n\tstate: None -> Init\n");
-        let handle = RefCell::new(V4l2CaptureQueueHandle::Init(handle));
-        Self {
-            handle,
-            num_buffers,
-        }
-    }
-    pub fn set_resolution(&mut self, _: Resolution) -> &mut Self {
-        self.handle.replace(match self.handle.take() {
-            V4l2CaptureQueueHandle::Init(handle) => {
-                let format: Format = handle.get_format().expect("Failed to get capture format");
-                log::debug!("Capture format:\n\t{:?}\n", format);
-
-                let handle = handle
-                    .request_buffers_generic::<Vec<MmapHandle>>(MemoryType::Mmap, self.num_buffers)
-                    .expect("Failed to request capture buffers");
-                log::debug!(
-                    "Capture queue:\n\t
-                    num_buffers: {}\n\t
-                    num_queued_buffers: {}\n\t
-                    num_free_buffers: {}\n",
-                    handle.num_buffers(),
-                    handle.num_queued_buffers(),
-                    handle.num_free_buffers()
-                );
-
-                // TODO: handle start/stop at runtime
-                handle.stream_on().expect("Failed to start capture queue");
-
-                log::debug!("Capture queue:\n\tstate: Init -> Streaming\n");
-                V4l2CaptureQueueHandle::Streaming(handle)
-            }
-            _ => {
-                /* TODO: handle DRC */
-                todo!()
-            }
-        });
-        self
-    }
-    pub fn dequeue_buffer(&self) -> Option<V4l2CaptureBuffer> {
-        let handle = &*self.handle.borrow();
-        match handle {
-            V4l2CaptureQueueHandle::Streaming(handle) => match handle.try_dequeue() {
-                Ok(buffer) => Some(V4l2CaptureBuffer::new(buffer)),
-                _ => None,
-            },
-            _ => panic!("ERROR"),
-        }
-    }
-    pub fn refill(&self) {
-        let handle = &*self.handle.borrow();
-        match handle {
-            V4l2CaptureQueueHandle::Streaming(handle) => {
-                while handle.num_free_buffers() != 0 {
-                    let buffer = handle
-                        .try_get_free_buffer()
-                        .expect("Failed to alloc capture buffer");
-                    log::debug!("capture >> index: {}\n", buffer.index());
-                    buffer.queue().expect("Failed to queue capture buffer");
-                }
-            }
-            _ => panic!("ERROR"),
-        }
-    }
-}
diff --git a/src/device/v4l2/stateless/request.rs b/src/device/v4l2/stateless/request.rs
deleted file mode 100644
index c831eff..0000000
--- a/src/device/v4l2/stateless/request.rs
+++ /dev/null
@@ -1,203 +0,0 @@
-// Copyright 2024 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-use std::cell::RefCell;
-use std::os::fd::AsRawFd;
-use std::rc::Rc;
-
-use v4l2r::controls::ExtControlTrait;
-use v4l2r::controls::SafeExtControl;
-use v4l2r::ioctl;
-
-use crate::device::v4l2::stateless::device::V4l2Device;
-use crate::device::v4l2::stateless::queue::V4l2CaptureBuffer;
-use crate::device::v4l2::stateless::queue::V4l2OutputBuffer;
-
-struct InitRequestHandle {
-    device: V4l2Device,
-    timestamp: u64,
-    handle: ioctl::Request,
-    buffer: V4l2OutputBuffer,
-}
-
-impl InitRequestHandle {
-    fn new(
-        device: V4l2Device,
-        timestamp: u64,
-        handle: ioctl::Request,
-        buffer: V4l2OutputBuffer,
-    ) -> Self {
-        Self {
-            device,
-            timestamp,
-            handle,
-            buffer,
-        }
-    }
-    fn ioctl<C, T>(&mut self, ctrl: C) -> &mut Self
-    where
-        C: Into<SafeExtControl<T>>,
-        T: ExtControlTrait,
-    {
-        let which = ioctl::CtrlWhich::Request(self.handle.as_raw_fd());
-        let mut ctrl: SafeExtControl<T> = ctrl.into();
-        ioctl::s_ext_ctrls(&self.device, which, &mut ctrl).expect("Failed to set output control");
-        self
-    }
-    fn write(&mut self, data: &[u8]) -> &mut Self {
-        self.buffer.write(data);
-        self
-    }
-    fn submit(self) -> PendingRequestHandle {
-        self.buffer.submit(self.timestamp, self.handle.as_raw_fd());
-        self.handle.queue().expect("Failed to queue request handle");
-        PendingRequestHandle {
-            device: self.device.clone(),
-            timestamp: self.timestamp,
-        }
-    }
-}
-
-struct PendingRequestHandle {
-    device: V4l2Device,
-    timestamp: u64,
-}
-
-impl PendingRequestHandle {
-    fn sync(self) -> DoneRequestHandle {
-        DoneRequestHandle {
-            buffer: Rc::new(RefCell::new(self.device.sync(self.timestamp))),
-        }
-    }
-}
-
-struct DoneRequestHandle {
-    buffer: Rc<RefCell<V4l2CaptureBuffer>>,
-}
-
-impl DoneRequestHandle {
-    fn result(&self) -> V4l2Result {
-        V4l2Result {
-            buffer: self.buffer.clone(),
-        }
-    }
-}
-
-#[derive(Default)]
-enum RequestHandle {
-    Init(InitRequestHandle),
-    Pending(PendingRequestHandle),
-    Done(DoneRequestHandle),
-    #[default]
-    Unknown,
-}
-
-impl RequestHandle {
-    fn new(
-        device: V4l2Device,
-        timestamp: u64,
-        handle: ioctl::Request,
-        buffer: V4l2OutputBuffer,
-    ) -> Self {
-        Self::Init(InitRequestHandle::new(device, timestamp, handle, buffer))
-    }
-    fn timestamp(&self) -> u64 {
-        match self {
-            Self::Init(handle) => handle.timestamp,
-            Self::Pending(handle) => handle.timestamp,
-            Self::Done(handle) => handle.buffer.borrow().timestamp(),
-            _ => panic!("ERROR"),
-        }
-    }
-    fn ioctl<C, T>(&mut self, ctrl: C) -> &mut Self
-    where
-        C: Into<SafeExtControl<T>>,
-        T: ExtControlTrait,
-    {
-        match self {
-            Self::Init(handle) => handle.ioctl(ctrl),
-            _ => panic!("ERROR"),
-        };
-        self
-    }
-    fn write(&mut self, data: &[u8]) -> &mut Self {
-        match self {
-            Self::Init(handle) => handle.write(data),
-            _ => panic!("ERROR"),
-        };
-        self
-    }
-
-    // This method can modify in-place instead of returning a new value. This removes the need for
-    // a RefCell in V4l2Request.
-    fn submit(&mut self) {
-        match std::mem::take(self) {
-            Self::Init(handle) => *self = Self::Pending(handle.submit()),
-            _ => panic!("ERROR"),
-        }
-    }
-    fn sync(&mut self) {
-        match std::mem::take(self) {
-            Self::Pending(handle) => *self = Self::Done(handle.sync()),
-            s @ Self::Done(_) => *self = s,
-            _ => panic!("ERROR"),
-        }
-    }
-    fn result(&self) -> V4l2Result {
-        match self {
-            Self::Done(handle) => handle.result(),
-            _ => panic!("ERROR"),
-        }
-    }
-}
-
-pub struct V4l2Request(RequestHandle);
-
-impl V4l2Request {
-    pub fn new(
-        device: V4l2Device,
-        timestamp: u64,
-        handle: ioctl::Request,
-        buffer: V4l2OutputBuffer,
-    ) -> Self {
-        Self(RequestHandle::new(device, timestamp, handle, buffer))
-    }
-    pub fn timestamp(&self) -> u64 {
-        self.0.timestamp()
-    }
-    pub fn ioctl<C, T>(&mut self, ctrl: C) -> &mut Self
-    where
-        C: Into<SafeExtControl<T>>,
-        T: ExtControlTrait,
-    {
-        self.0.ioctl(ctrl);
-        self
-    }
-    pub fn write(&mut self, data: &[u8]) -> &mut Self {
-        self.0.write(data);
-        self
-    }
-    pub fn submit(&mut self) {
-        self.0.submit();
-    }
-    pub fn sync(&mut self) {
-        self.0.sync();
-    }
-    pub fn result(&self) -> V4l2Result {
-        self.0.result()
-    }
-}
-
-pub struct V4l2Result {
-    buffer: Rc<RefCell<V4l2CaptureBuffer>>,
-}
-
-impl V4l2Result {
-    pub fn length(&self) -> usize {
-        self.buffer.borrow().length()
-    }
-    pub fn read(&self, data: &mut [u8]) {
-        self.buffer.borrow().read(data)
-    }
-}
diff --git a/src/encoder/stateful/h264/v4l2.rs b/src/encoder/stateful/h264/v4l2.rs
index eb30523..8bd3704 100644
--- a/src/encoder/stateful/h264/v4l2.rs
+++ b/src/encoder/stateful/h264/v4l2.rs
@@ -168,12 +168,12 @@
     use v4l2r::device::Device;
     use v4l2r::device::DeviceConfig;
 
-    use crate::backend::v4l2::encoder::find_device_with_capture;
+    use crate::backend::v4l2::encoder::tests::find_device_with_capture;
     use crate::backend::v4l2::encoder::tests::perform_v4l2_encoder_dmabuf_test;
     use crate::backend::v4l2::encoder::tests::perform_v4l2_encoder_mmap_test;
+    use crate::backend::v4l2::encoder::tests::v4l2_format_to_frame_layout;
     use crate::backend::v4l2::encoder::tests::BoPoolAllocator;
     use crate::backend::v4l2::encoder::tests::GbmDevice;
-    use crate::backend::v4l2::encoder::v4l2_format_to_frame_layout;
     use crate::backend::v4l2::encoder::MmapingCapture;
     use crate::encoder::simple_encode_loop;
     use crate::encoder::tests::userptr_test_frame_generator;
diff --git a/src/encoder/stateful/h265/v4l2.rs b/src/encoder/stateful/h265/v4l2.rs
index 0ce10b7..6e34ef1 100644
--- a/src/encoder/stateful/h265/v4l2.rs
+++ b/src/encoder/stateful/h265/v4l2.rs
@@ -159,12 +159,12 @@
     use v4l2r::device::Device;
     use v4l2r::device::DeviceConfig;
 
-    use crate::backend::v4l2::encoder::find_device_with_capture;
+    use crate::backend::v4l2::encoder::tests::find_device_with_capture;
     use crate::backend::v4l2::encoder::tests::perform_v4l2_encoder_dmabuf_test;
     use crate::backend::v4l2::encoder::tests::perform_v4l2_encoder_mmap_test;
+    use crate::backend::v4l2::encoder::tests::v4l2_format_to_frame_layout;
     use crate::backend::v4l2::encoder::tests::BoPoolAllocator;
     use crate::backend::v4l2::encoder::tests::GbmDevice;
-    use crate::backend::v4l2::encoder::v4l2_format_to_frame_layout;
     use crate::backend::v4l2::encoder::MmapingCapture;
     use crate::encoder::simple_encode_loop;
     use crate::encoder::tests::userptr_test_frame_generator;
diff --git a/src/encoder/stateful/vp8/v4l2.rs b/src/encoder/stateful/vp8/v4l2.rs
index ea21930..626cfd2 100644
--- a/src/encoder/stateful/vp8/v4l2.rs
+++ b/src/encoder/stateful/vp8/v4l2.rs
@@ -113,12 +113,12 @@
     use v4l2r::device::Device;
     use v4l2r::device::DeviceConfig;
 
-    use crate::backend::v4l2::encoder::find_device_with_capture;
+    use crate::backend::v4l2::encoder::tests::find_device_with_capture;
     use crate::backend::v4l2::encoder::tests::perform_v4l2_encoder_dmabuf_test;
     use crate::backend::v4l2::encoder::tests::perform_v4l2_encoder_mmap_test;
+    use crate::backend::v4l2::encoder::tests::v4l2_format_to_frame_layout;
     use crate::backend::v4l2::encoder::tests::BoPoolAllocator;
     use crate::backend::v4l2::encoder::tests::GbmDevice;
-    use crate::backend::v4l2::encoder::v4l2_format_to_frame_layout;
     use crate::backend::v4l2::encoder::MmapingCapture;
     use crate::encoder::simple_encode_loop;
     use crate::encoder::tests::userptr_test_frame_generator;
diff --git a/src/encoder/stateful/vp9/v4l2.rs b/src/encoder/stateful/vp9/v4l2.rs
index 3b1d98b..2543e8b 100644
--- a/src/encoder/stateful/vp9/v4l2.rs
+++ b/src/encoder/stateful/vp9/v4l2.rs
@@ -130,12 +130,12 @@
     use v4l2r::device::Device;
     use v4l2r::device::DeviceConfig;
 
-    use crate::backend::v4l2::encoder::find_device_with_capture;
+    use crate::backend::v4l2::encoder::tests::find_device_with_capture;
     use crate::backend::v4l2::encoder::tests::perform_v4l2_encoder_dmabuf_test;
     use crate::backend::v4l2::encoder::tests::perform_v4l2_encoder_mmap_test;
+    use crate::backend::v4l2::encoder::tests::v4l2_format_to_frame_layout;
     use crate::backend::v4l2::encoder::tests::BoPoolAllocator;
     use crate::backend::v4l2::encoder::tests::GbmDevice;
-    use crate::backend::v4l2::encoder::v4l2_format_to_frame_layout;
     use crate::backend::v4l2::encoder::MmapingCapture;
     use crate::encoder::simple_encode_loop;
     use crate::encoder::tests::userptr_test_frame_generator;
diff --git a/src/encoder/stateless/av1/vaapi.rs b/src/encoder/stateless/av1/vaapi.rs
index 64b461e..0652fee 100644
--- a/src/encoder/stateless/av1/vaapi.rs
+++ b/src/encoder/stateless/av1/vaapi.rs
@@ -5,6 +5,7 @@
 use std::num::TryFromIntError;
 use std::rc::Rc;
 
+use libva::constants::VA_INVALID_ID;
 use libva::AV1EncLoopFilterFlags;
 use libva::AV1EncLoopRestorationFlags;
 use libva::AV1EncModeControlFlags;
@@ -25,7 +26,6 @@
 use libva::VAProfile::VAProfileAV1Profile0;
 use libva::VAProfile::VAProfileAV1Profile1;
 use libva::VaError;
-use libva::VA_INVALID_ID;
 
 use crate::backend::vaapi::encoder::CodedOutputPromise;
 use crate::backend::vaapi::encoder::Reconstructed;
@@ -230,11 +230,8 @@
         // Current we always expect the reconstructed frame.
         const DISABLE_FRAME_RECON: bool = false;
 
-        // Palette mode is not used. This also implies force_integer_mv and
-        // allow_screen_content_tools should be false.
+        // Palette mode is not used.
         const PALETTE_MODE_ENABLE: bool = false;
-        const FORCE_INTEGER_MV: bool = false;
-        const ALLOW_SCREEN_CONTENT_TOOLS: bool = false;
 
         // Use 16x16 block size for now.
         // TODO: Use maximum available
@@ -395,8 +392,6 @@
                 DISABLE_FRAME_RECON,
                 request.frame.allow_intrabc,
                 PALETTE_MODE_ENABLE,
-                ALLOW_SCREEN_CONTENT_TOOLS,
-                FORCE_INTEGER_MV,
             ),
             SEG_ID_BLOCK_SIZE,
             NUM_TILE_GROUPS_MINUS1,
@@ -579,7 +574,7 @@
             va_profile,
             fourcc,
             coded_size,
-            libva::VA_RC_CQP,
+            libva::constants::VA_RC_CQP,
             low_power,
         )?;
 
@@ -589,12 +584,12 @@
 
 #[cfg(test)]
 mod tests {
+    use libva::constants::VA_RT_FORMAT_YUV420;
+    use libva::constants::VA_RT_FORMAT_YUV420_10;
     use libva::Display;
     use libva::UsageHint;
     use libva::VAEntrypoint::VAEntrypointEncSliceLP;
     use libva::VAProfile::VAProfileAV1Profile0;
-    use libva::VA_RT_FORMAT_YUV420;
-    use libva::VA_RT_FORMAT_YUV420_10;
 
     use super::*;
     use crate::backend::vaapi::encoder::tests::upload_test_frame_nv12;
@@ -678,7 +673,7 @@
                 width: WIDTH,
                 height: HEIGHT,
             },
-            libva::VA_RC_CQP,
+            libva::constants::VA_RC_CQP,
             low_power,
         )
         .unwrap();
diff --git a/src/encoder/stateless/h264/vaapi.rs b/src/encoder/stateless/h264/vaapi.rs
index 7d7ab1d..47c3165 100644
--- a/src/encoder/stateless/h264/vaapi.rs
+++ b/src/encoder/stateless/h264/vaapi.rs
@@ -7,6 +7,9 @@
 use std::rc::Rc;
 
 use anyhow::Context;
+use libva::constants::VA_INVALID_ID;
+use libva::constants::VA_PICTURE_H264_LONG_TERM_REFERENCE;
+use libva::constants::VA_PICTURE_H264_SHORT_TERM_REFERENCE;
 use libva::BufferType;
 use libva::Display;
 use libva::EncCodedBuffer;
@@ -25,9 +28,6 @@
 use libva::Surface;
 use libva::SurfaceMemoryDescriptor;
 use libva::VAProfile;
-use libva::VA_INVALID_ID;
-use libva::VA_PICTURE_H264_LONG_TERM_REFERENCE;
-use libva::VA_PICTURE_H264_SHORT_TERM_REFERENCE;
 
 use crate::backend::vaapi::encoder::tunings_to_libva_rc;
 use crate::backend::vaapi::encoder::CodedOutputPromise;
@@ -79,9 +79,9 @@
     /// holder to fill staticly sized array.
     fn build_invalid_va_h264_pic_enc() -> libva::PictureH264 {
         libva::PictureH264::new(
-            libva::VA_INVALID_ID,
+            libva::constants::VA_INVALID_ID,
             0,
-            libva::VA_PICTURE_H264_INVALID,
+            libva::constants::VA_PICTURE_H264_INVALID,
             0,
             0,
         )
@@ -414,15 +414,10 @@
             tunings_to_libva_rc::<{ MIN_QP as u32 }, { MAX_QP as u32 }>(&request.tunings)?;
         let rc_param = BufferType::EncMiscParameter(libva::EncMiscParameter::RateControl(rc_param));
 
-        let framerate_param = BufferType::EncMiscParameter(libva::EncMiscParameter::FrameRate(
-            libva::EncMiscParameterFrameRate::new(request.tunings.framerate, 0),
-        ));
-
         picture.add_buffer(self.context().create_buffer(seq_param)?);
         picture.add_buffer(self.context().create_buffer(pic_param)?);
         picture.add_buffer(self.context().create_buffer(slice_param)?);
         picture.add_buffer(self.context().create_buffer(rc_param)?);
-        picture.add_buffer(self.context().create_buffer(framerate_param)?);
 
         // Start processing the picture encoding
         let picture = picture.begin().context("picture begin")?;
@@ -467,8 +462,8 @@
         };
 
         let bitrate_control = match config.initial_tunings.rate_control {
-            RateControl::ConstantBitrate(_) => libva::VA_RC_CBR,
-            RateControl::ConstantQuality(_) => libva::VA_RC_CQP,
+            RateControl::ConstantBitrate(_) => libva::constants::VA_RC_CBR,
+            RateControl::ConstantQuality(_) => libva::constants::VA_RC_CQP,
         };
 
         let backend = VaapiBackend::new(
@@ -486,11 +481,11 @@
 
 #[cfg(test)]
 pub(super) mod tests {
+    use libva::constants::VA_RT_FORMAT_YUV420;
     use libva::Display;
     use libva::UsageHint;
     use libva::VAEntrypoint::VAEntrypointEncSliceLP;
     use libva::VAProfile::VAProfileH264Main;
-    use libva::VA_RT_FORMAT_YUV420;
 
     use super::*;
     use crate::backend::vaapi::encoder::tests::upload_test_frame_nv12;
@@ -558,7 +553,7 @@
                 width: WIDTH,
                 height: HEIGHT,
             },
-            libva::VA_RC_CBR,
+            libva::constants::VA_RC_CBR,
             low_power,
         )
         .unwrap();
diff --git a/src/encoder/stateless/vp9/vaapi.rs b/src/encoder/stateless/vp9/vaapi.rs
index 580dcad..e71be45 100644
--- a/src/encoder/stateless/vp9/vaapi.rs
+++ b/src/encoder/stateless/vp9/vaapi.rs
@@ -7,6 +7,7 @@
 use std::rc::Rc;
 
 use anyhow::Context;
+use libva::constants::VA_INVALID_SURFACE;
 use libva::BufferType;
 use libva::Display;
 use libva::EncPictureParameter;
@@ -20,7 +21,6 @@
 use libva::VAProfile::VAProfileVP9Profile2;
 use libva::VP9EncPicFlags;
 use libva::VP9EncRefFlags;
-use libva::VA_INVALID_SURFACE;
 
 use crate::backend::vaapi::encoder::tunings_to_libva_rc;
 use crate::backend::vaapi::encoder::CodedOutputPromise;
@@ -240,14 +240,9 @@
             request.input,
         );
 
-        let framerate_param = BufferType::EncMiscParameter(libva::EncMiscParameter::FrameRate(
-            libva::EncMiscParameterFrameRate::new(request.tunings.framerate, 0),
-        ));
-
         picture.add_buffer(self.context().create_buffer(seq_param)?);
         picture.add_buffer(self.context().create_buffer(pic_param)?);
         picture.add_buffer(self.context().create_buffer(rc_param)?);
-        picture.add_buffer(self.context().create_buffer(framerate_param)?);
 
         // Start processing the picture encoding
         let picture = picture.begin().context("picture begin")?;
@@ -279,8 +274,8 @@
         blocking_mode: BlockingMode,
     ) -> EncodeResult<Self> {
         let bitrate_control = match config.initial_tunings.rate_control {
-            RateControl::ConstantBitrate(_) => libva::VA_RC_CBR,
-            RateControl::ConstantQuality(_) => libva::VA_RC_CQP,
+            RateControl::ConstantBitrate(_) => libva::constants::VA_RC_CBR,
+            RateControl::ConstantQuality(_) => libva::constants::VA_RC_CQP,
         };
 
         let va_profile = match config.bit_depth {
@@ -304,11 +299,11 @@
 pub(super) mod tests {
     use std::rc::Rc;
 
+    use libva::constants::VA_RT_FORMAT_YUV420;
+    use libva::constants::VA_RT_FORMAT_YUV420_10;
     use libva::Display;
     use libva::UsageHint;
     use libva::VAEntrypoint::VAEntrypointEncSliceLP;
-    use libva::VA_RT_FORMAT_YUV420;
-    use libva::VA_RT_FORMAT_YUV420_10;
 
     use super::*;
     use crate::backend::vaapi::encoder::tests::upload_test_frame_nv12;
@@ -376,7 +371,7 @@
                 width: WIDTH,
                 height: HEIGHT,
             },
-            libva::VA_RC_CBR,
+            libva::constants::VA_RC_CBR,
             low_power,
         )
         .unwrap();
diff --git a/src/image_processing.rs b/src/image_processing.rs
index 2786ee7..7094b5e 100644
--- a/src/image_processing.rs
+++ b/src/image_processing.rs
@@ -6,61 +6,39 @@
 use byteorder::ByteOrder;
 use byteorder::LittleEndian;
 
-/// Copies `src` into `dst` as NV12, handling padding.
+/// Copies `src` into `dst` as NV12, removing any extra padding.
 pub fn nv12_copy(
-    src_y: &[u8],
-    src_y_stride: usize,
-    dst_y: &mut [u8],
-    dst_y_stride: usize,
-    src_uv: &[u8],
-    src_uv_stride: usize,
-    dst_uv: &mut [u8],
-    dst_uv_stride: usize,
+    src: &[u8],
+    dst: &mut [u8],
     width: usize,
     height: usize,
+    strides: [usize; 3],
+    offsets: [usize; 3],
 ) {
-    for y in 0..height {
-        dst_y[(y * dst_y_stride)..(y * dst_y_stride + width)]
-            .copy_from_slice(&src_y[(y * src_y_stride)..(y * src_y_stride + width)]);
-    }
-    for y in 0..(height / 2) {
-        dst_uv[(y * dst_uv_stride)..(y * dst_uv_stride + width)]
-            .copy_from_slice(&src_uv[(y * src_uv_stride)..(y * src_uv_stride + width)]);
-    }
-}
+    // Copy Y.
+    let src_y_lines = src[offsets[0]..]
+        .chunks(strides[0])
+        .map(|line| &line[..width]);
+    let dst_y_lines = dst.chunks_mut(width);
 
-/// Replace 0 padding with the last pixels of the real image. This helps reduce compression
-/// artifacts caused by the sharp transition between real image data and 0.
-pub fn extend_border_nv12(
-    y_plane: &mut [u8],
-    uv_plane: &mut [u8],
-    visible_width: usize,
-    visible_height: usize,
-    coded_width: usize,
-    coded_height: usize,
-) {
-    assert!(visible_width > 1);
-    assert!(visible_height > 1);
-    for y in 0..visible_height {
-        let row_start = y * coded_width;
-        for x in visible_width..coded_width {
-            y_plane[row_start + x] = y_plane[row_start + x - 1]
-        }
+    for (src_line, dst_line) in src_y_lines.zip(dst_y_lines).take(height) {
+        dst_line.copy_from_slice(src_line);
     }
-    for y in visible_height..coded_height {
-        let (src, dst) = y_plane.split_at_mut(y * coded_width);
-        dst[0..coded_width].copy_from_slice(&src[((y - 1) * coded_width)..(y * coded_width)]);
-    }
-    for y in 0..(visible_height / 2) {
-        let row_start = y * coded_width;
-        for x in visible_width..coded_width {
-            // We use minus 2 here because we want to actually repeat the last 2 UV values.
-            uv_plane[row_start + x] = uv_plane[row_start + x - 2]
-        }
-    }
-    for y in (visible_height / 2)..(coded_height / 2) {
-        let (src, dst) = uv_plane.split_at_mut(y * coded_width);
-        dst[0..coded_width].copy_from_slice(&src[((y - 1) * coded_width)..(y * coded_width)]);
+
+    let dst_u_offset = width * height;
+
+    // Align width and height to 2 for UV plane.
+    // 1 sample per 4 pixels, but we have two components per line so width can remain as-is.
+    let uv_width = if width % 2 == 1 { width + 1 } else { width };
+    let uv_height = if height % 2 == 1 { height + 1 } else { height } / 2;
+
+    // Copy UV.
+    let src_uv_lines = src[offsets[1]..]
+        .chunks(strides[1])
+        .map(|line| &line[..uv_width]);
+    let dst_uv_lines = dst[dst_u_offset..].chunks_mut(uv_width);
+    for (src_line, dst_line) in src_uv_lines.zip(dst_uv_lines).take(uv_height) {
+        dst_line.copy_from_slice(src_line);
     }
 }
 
@@ -162,154 +140,3 @@
         }
     }
 }
-
-/// Simple implementation of MM21 to NV12 detiling. Note that this Rust-only implementation is
-/// unlikely to be fast enough for production code, and is for testing purposes only.
-/// TODO(b:380280455): We will want to speed this up and also add MT2T support.
-pub fn detile_plane(
-    src: &[u8],
-    dst: &mut [u8],
-    width: usize,
-    height: usize,
-    tile_width: usize,
-    tile_height: usize,
-) -> Result<(), String> {
-    if width % tile_width != 0 || height % tile_height != 0 {
-        return Err("Buffers must be aligned to tile dimensions for detiling".to_owned());
-    }
-
-    let tile_size = tile_width * tile_height;
-    let mut output_idx = 0;
-    for y_start in (0..height).step_by(tile_height) {
-        let tile_row_start = y_start * width;
-        for y in 0..tile_height {
-            let row_start = tile_row_start + y * tile_width;
-            for x in (0..width).step_by(tile_width) {
-                let input_idx = row_start + x / tile_width * tile_size;
-                dst[output_idx..(output_idx + tile_width)]
-                    .copy_from_slice(&src[input_idx..(input_idx + tile_width)]);
-                output_idx += tile_width;
-            }
-        }
-    }
-
-    Ok(())
-}
-
-pub fn mm21_to_nv12(
-    src_y: &[u8],
-    dst_y: &mut [u8],
-    src_uv: &[u8],
-    dst_uv: &mut [u8],
-    width: usize,
-    height: usize,
-) -> Result<(), String> {
-    let y_tile_width = 16;
-    let y_tile_height = 32;
-    detile_plane(src_y, dst_y, width, height, y_tile_width, y_tile_height)?;
-    detile_plane(
-        src_uv,
-        dst_uv,
-        width,
-        height / 2,
-        y_tile_width,
-        y_tile_height / 2,
-    )
-}
-
-/// Simple implementation of NV12 to I420. Again, probably not fast enough for production, should
-/// TODO(b:380280455): We may want to speed this up.
-pub fn nv12_to_i420_chroma(src_uv: &[u8], dst_u: &mut [u8], dst_v: &mut [u8]) {
-    for i in 0..src_uv.len() {
-        if i % 2 == 0 {
-            dst_u[i / 2] = src_uv[i];
-        } else {
-            dst_v[i / 2] = src_uv[i];
-        }
-    }
-}
-
-pub fn nv12_to_i420(
-    src_y: &[u8],
-    dst_y: &mut [u8],
-    src_uv: &[u8],
-    dst_u: &mut [u8],
-    dst_v: &mut [u8],
-) {
-    dst_y.copy_from_slice(src_y);
-    nv12_to_i420_chroma(src_uv, dst_u, dst_v);
-}
-
-pub fn i420_to_nv12_chroma(src_u: &[u8], src_v: &[u8], dst_uv: &mut [u8]) {
-    for i in 0..dst_uv.len() {
-        if i % 2 == 0 {
-            dst_uv[i] = src_u[i / 2];
-        } else {
-            dst_uv[i] = src_v[i / 2];
-        }
-    }
-}
-
-pub fn i420_to_nv12(src_y: &[u8], dst_y: &mut [u8], src_u: &[u8], src_v: &[u8], dst_uv: &mut [u8]) {
-    dst_y.copy_from_slice(src_y);
-    i420_to_nv12_chroma(src_u, src_v, dst_uv);
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn test_mm21_to_nv12() {
-        let test_input = include_bytes!("test_data/puppets-480x270_20230825.mm21.yuv");
-        let test_expected_output = include_bytes!("test_data/puppets-480x270_20230825.nv12.yuv");
-
-        let mut test_output = [0u8; 480 * 288 * 3 / 2];
-        let (test_y_output, test_uv_output) = test_output.split_at_mut(480 * 288);
-        mm21_to_nv12(
-            &test_input[0..480 * 288],
-            test_y_output,
-            &test_input[480 * 288..480 * 288 * 3 / 2],
-            test_uv_output,
-            480,
-            288,
-        )
-        .expect("Failed to detile!");
-        assert_eq!(test_output, *test_expected_output);
-    }
-
-    #[test]
-    fn test_nv12_to_i420() {
-        let test_input = include_bytes!("test_data/puppets-480x270_20230825.nv12.yuv");
-        let test_expected_output = include_bytes!("test_data/puppets-480x270_20230825.i420.yuv");
-
-        let mut test_output = [0u8; 480 * 288 * 3 / 2];
-        let (test_y_output, test_uv_output) = test_output.split_at_mut(480 * 288);
-        let (test_u_output, test_v_output) = test_uv_output.split_at_mut(480 * 288 / 4);
-        nv12_to_i420(
-            &test_input[0..480 * 288],
-            test_y_output,
-            &test_input[480 * 288..480 * 288 * 3 / 2],
-            test_u_output,
-            test_v_output,
-        );
-        assert_eq!(test_output, *test_expected_output);
-    }
-
-    #[test]
-    fn test_i420_to_nv12() {
-        let test_input = include_bytes!("test_data/puppets-480x270_20230825.i420.yuv");
-        let test_expected_output = include_bytes!("test_data/puppets-480x270_20230825.nv12.yuv");
-
-        let mut test_output = [0u8; 480 * 288 * 3 / 2];
-        let (test_y_output, test_uv_output) = test_output.split_at_mut(480 * 288);
-        i420_to_nv12(
-            &test_input[0..(480 * 288)],
-            test_y_output,
-            &test_input[(480 * 288)..(480 * 288 * 5 / 4)],
-            &test_input[(480 * 288 * 5 / 4)..(480 * 288 * 3 / 2)],
-            test_uv_output,
-        );
-        assert_eq!(test_output, *test_expected_output);
-    }
-}
diff --git a/src/lib.rs b/src/lib.rs
index 9cbd6af..35c9905 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -27,8 +27,6 @@
 pub mod backend;
 #[cfg(any(feature = "vaapi", feature = "v4l2"))]
 pub mod decoder;
-#[cfg(feature = "v4l2")]
-pub mod device;
 #[cfg(any(feature = "vaapi", feature = "v4l2"))]
 pub mod encoder;
 #[cfg(any(feature = "vaapi", feature = "v4l2"))]
@@ -79,10 +77,6 @@
 
         self
     }
-
-    pub fn get_area(&self) -> usize {
-        (self.width as usize) * (self.height as usize)
-    }
 }
 
 impl From<(u32, u32)> for Resolution {
diff --git a/src/test_data/puppets-480x270_20230825.i420.yuv b/src/test_data/puppets-480x270_20230825.i420.yuv
deleted file mode 100644
index c0822eb..0000000
--- a/src/test_data/puppets-480x270_20230825.i420.yuv
+++ /dev/null
Binary files differ
diff --git a/src/test_data/puppets-480x270_20230825.mm21.yuv b/src/test_data/puppets-480x270_20230825.mm21.yuv
deleted file mode 100644
index 7c3153c..0000000
--- a/src/test_data/puppets-480x270_20230825.mm21.yuv
+++ /dev/null
Binary files differ
diff --git a/src/test_data/puppets-480x270_20230825.nv12.yuv b/src/test_data/puppets-480x270_20230825.nv12.yuv
deleted file mode 100644
index 1a32af3..0000000
--- a/src/test_data/puppets-480x270_20230825.nv12.yuv
+++ /dev/null
Binary files differ