video: decoder: vaapi: port the VAAPI backend to cros-codecs
Port the VAAPI backend to the new cros-codecs crate. This crate now
contains all codec related code and is independent from the rest of the
CrosVM code.
BUG=b:214478588
TEST="cargo test --package devices --lib --features video-decoder --features vaapi -- virtio::video::decoder::backend::vaapi::tests::test_get_capabilities --include-ignored"
Change-Id: Id207c53c0c4200e03ce8793d7c37cb5fbe808829
Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/3875044
Reviewed-by: Keiichi Watanabe <keiichiw@chromium.org>
Reviewed-by: Alexandre Courbot <acourbot@chromium.org>
Commit-Queue: Alexandre Courbot <acourbot@chromium.org>
diff --git a/Cargo.lock b/Cargo.lock
index dd3a744..117ce5b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -637,6 +637,7 @@
"cfg-if",
"chrono",
"crc32fast",
+ "cros-codecs",
"cros_async",
"cros_tracing",
"crosvm_cli",
@@ -690,7 +691,6 @@
"vm_control",
"vm_memory",
"vmm_vhost",
- "vp8",
"win_util",
"winapi",
]
@@ -2087,15 +2087,6 @@
]
[[package]]
-name = "vp8"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "bytes",
- "log",
-]
-
-[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 27c5dd9..33bd102 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -72,7 +72,7 @@
"media/ffmpeg",
"media/libva",
"media/libvda",
- "media/vp8",
+ "media/cros-codecs",
"net_sys",
"net_util",
"power_monitor",
diff --git a/devices/Cargo.toml b/devices/Cargo.toml
index c2bd400..4e83f98 100644
--- a/devices/Cargo.toml
+++ b/devices/Cargo.toml
@@ -15,7 +15,7 @@
libvda-stub = ["libvda/libvda-stub"]
tpm = ["tpm2"]
usb = []
-vaapi = ["libva", "vp8", "downcast-rs", "crc32fast"]
+vaapi = ["libva", "cros-codecs/vaapi", "downcast-rs", "crc32fast"]
video-decoder = []
video-encoder = []
minigbm = ["rutabaga_gfx/minigbm"]
@@ -41,6 +41,7 @@
chrono = "*"
crc32fast = { version = "1.2.1", optional = true }
cros_async = { path = "../cros_async" }
+cros-codecs = { path = "../media/cros-codecs", optional = true }
crosvm_cli = { path = "../crosvm_cli" }
data_model = { path = "../common/data_model" }
dbus = { version = "0.9", optional = true }
@@ -81,7 +82,6 @@
virtio_sys = { path = "../virtio_sys" }
vm_control = { path = "../vm_control" }
vm_memory = { path = "../vm_memory" }
-vp8 = { path = "../media/vp8", optional = true }
[target.'cfg(unix)'.dependencies]
fuse = {path = "../fuse" }
diff --git a/devices/src/virtio/video/decoder/backend/vaapi.rs b/devices/src/virtio/video/decoder/backend/vaapi.rs
index d99830c..52e0ce1 100644
--- a/devices/src/virtio/video/decoder/backend/vaapi.rs
+++ b/devices/src/virtio/video/decoder/backend/vaapi.rs
@@ -4,9 +4,7 @@
#![deny(missing_docs)]
-use std::any::Any;
use std::borrow::Borrow;
-use std::cell::RefCell;
use std::collections::btree_map::Entry;
use std::collections::BTreeMap;
use std::collections::VecDeque;
@@ -17,14 +15,10 @@
use anyhow::Result;
use base::MappedRegion;
use base::MemoryMappingArena;
-use libva::Config;
-use libva::Context;
+use cros_codecs::decoders::DynDecodedHandle;
+use cros_codecs::decoders::VideoDecoder;
+use cros_codecs::DecodedFormat;
use libva::Display;
-use libva::Image;
-use libva::Picture;
-use libva::PictureSync;
-use libva::Surface;
-use libva::UsageHint;
use crate::virtio::video::decoder::Capability;
use crate::virtio::video::decoder::DecoderBackend;
@@ -45,108 +39,6 @@
use crate::virtio::video::utils::EventQueue;
use crate::virtio::video::utils::OutputQueue;
-mod vp8;
-
-/// A surface pool handle to reduce the number of costly Surface allocations.
-#[derive(Clone)]
-pub struct SurfacePoolHandle {
- surfaces: Rc<RefCell<VecDeque<Surface>>>,
- current_resolution: Resolution,
-}
-
-impl SurfacePoolHandle {
- /// Creates a new pool
- pub fn new(surfaces: Vec<Surface>, resolution: Resolution) -> Self {
- Self {
- surfaces: Rc::new(RefCell::new(VecDeque::from(surfaces))),
- current_resolution: resolution,
- }
- }
-
- /// Retrieve the current resolution for the pool
- pub fn current_resolution(&self) -> &Resolution {
- &self.current_resolution
- }
-
- /// Sets the current resolution for the pool
- pub fn set_current_resolution(&mut self, resolution: Resolution) {
- self.current_resolution = resolution;
- }
-
- /// Adds a new surface to the pool
- pub fn add_surface(&mut self, surface: Surface) {
- self.surfaces.borrow_mut().push_back(surface)
- }
-
- /// Gets a free surface from the pool
- pub fn get_surface(&mut self) -> Result<Surface> {
- let mut vec = self.surfaces.borrow_mut();
- vec.pop_front().ok_or(anyhow!("Out of surfaces"))
- }
-
- /// Drops all surfaces from the pool
- pub fn drop_surfaces(&mut self) {
- self.surfaces = Default::default();
- }
-}
-
-/// A decoded frame handle.
-#[derive(Clone)]
-pub struct DecodedFrameHandle {
- /// The actual picture backing the handle.
- picture: Option<Rc<RefCell<Picture<PictureSync>>>>,
- /// The bitstream header parsed for this frame
- header: Rc<dyn Any>,
- /// The decoder resolution when this frame was processed. Not all codecs
- /// send resolution data in every frame header.
- resolution: Resolution,
- /// A handle to the surface pool.
- surface_pool: SurfacePoolHandle,
-}
-
-impl DecodedFrameHandle {
- /// Creates a new handle
- pub fn new(
- picture: Rc<RefCell<Picture<PictureSync>>>,
- header: Rc<dyn Any>,
- resolution: Resolution,
- surface_pool: SurfacePoolHandle,
- ) -> Self {
- Self {
- picture: Some(picture),
- header,
- resolution,
- surface_pool,
- }
- }
-
- /// Retrieves a reference into the picture backing the handle
- pub fn picture(&self) -> Rc<RefCell<Picture<PictureSync>>> {
- Rc::clone(self.picture.as_ref().unwrap())
- }
-
- // Returns the resolution when this frame was processed
- pub fn resolution(&self) -> &Resolution {
- &self.resolution
- }
-}
-
-impl Drop for DecodedFrameHandle {
- fn drop(&mut self) {
- if let Ok(picture) = Rc::try_unwrap(self.picture.take().unwrap()) {
- let pool = &mut self.surface_pool;
-
- // Only retrieve if the resolutions match, otherwise let the stale Surface drop.
- if *pool.current_resolution() == self.resolution {
- // Retrieve if not currently used by another field.
- if let Ok(surface) = picture.into_inner().take_surface() {
- pool.add_surface(surface);
- }
- }
- }
- }
-}
-
/// A set of params returned when a dynamic resolution change is found in the
/// bitstream.
pub struct DrcParams {
@@ -160,6 +52,28 @@
visible_rect: Rect,
}
+impl TryFrom<DecodedFormat> for Format {
+ type Error = anyhow::Error;
+
+ fn try_from(value: DecodedFormat) -> Result<Self, Self::Error> {
+ match value {
+ DecodedFormat::NV12 => Ok(Format::NV12),
+ DecodedFormat::I420 => Err(anyhow!("Unsupported format")),
+ }
+ }
+}
+
+impl TryFrom<Format> for DecodedFormat {
+ type Error = anyhow::Error;
+
+ fn try_from(value: Format) -> Result<Self, Self::Error> {
+ match value {
+ Format::NV12 => Ok(DecodedFormat::NV12),
+ _ => Err(anyhow!("Unsupported format")),
+ }
+ }
+}
+
impl TryFrom<libva::VAProfile::Type> for Profile {
type Error = anyhow::Error;
@@ -185,52 +99,6 @@
}
}
-/// State of the input stream, which can be either unparsed (we don't know the stream properties
-/// yet) or parsed (we know the stream properties and are ready to decode).
-pub enum StreamMetadataState {
- /// The metadata for the current stream has not yet been parsed.
- Unparsed { display: Rc<Display> },
-
- /// The metadata for the current stream has been parsed and a suitable
- /// VAContext has been created to accomodate it.
- Parsed {
- /// A VAContext from which we can decode from.
- context: Rc<Context>,
- /// The VAConfig that created the context.
- config: Config,
- /// A pool of surfaces. We reuse surfaces as they are expensive to allocate.
- surface_pool: SurfacePoolHandle,
- },
-}
-
-impl StreamMetadataState {
- fn display(&self) -> Rc<libva::Display> {
- match self {
- StreamMetadataState::Unparsed { display } => Rc::clone(display),
- StreamMetadataState::Parsed { context, .. } => context.display(),
- }
- }
-
- fn context(&self) -> Result<Rc<libva::Context>> {
- match self {
- StreamMetadataState::Unparsed { .. } => Err(anyhow!("Stream metadata not parsed yet")),
- StreamMetadataState::Parsed { context, .. } => Ok(Rc::clone(context)),
- }
- }
-
- fn surface_pool(&self) -> Result<SurfacePoolHandle> {
- match self {
- StreamMetadataState::Unparsed { .. } => Err(anyhow!("Invalid state")),
- StreamMetadataState::Parsed { surface_pool, .. } => Ok(surface_pool.clone()),
- }
- }
-
- fn get_surface(&mut self) -> Result<Surface> {
- let mut surface_pool = self.surface_pool()?;
- surface_pool.get_surface()
- }
-}
-
/// The state for the output queue containing the buffers that will receive the
/// decoded data.
enum OutputQueueState {
@@ -550,12 +418,12 @@
/// A decoder session for the libva backend
pub struct VaapiDecoderSession {
/// The implementation for the codec specific logic.
- codec: Box<dyn VaapiCodec>,
+ codec: Box<dyn VideoDecoder>,
/// The state for the output queue. Updated when `set_output_buffer_count`
/// is called or when we detect a dynamic resolution change.
output_queue_state: OutputQueueState,
/// Queue containing decoded pictures.
- ready_queue: VecDeque<DecodedFrameHandle>,
+ ready_queue: VecDeque<Box<dyn DynDecodedHandle>>,
/// The event queue we can use to signal new events.
event_queue: EventQueue<DecoderEvent>,
/// Whether the decoder is currently flushing.
@@ -607,25 +475,6 @@
}
impl VaapiDecoderSession {
- /// Create the `num_surfaces` surfaces for a combination of `width`,
- /// `height` and `rt_format`.
- fn create_surfaces(
- display: Rc<Display>,
- width: u32,
- height: u32,
- rt_format: u32,
- num_surfaces: usize,
- ) -> Result<Vec<Surface>> {
- display.create_surfaces(
- rt_format,
- Some(libva::constants::VA_FOURCC_NV12), // NV12 is hardcoded for now
- width,
- height,
- Some(UsageHint::USAGE_HINT_DECODER),
- num_surfaces as u32,
- )
- }
-
fn change_resolution(&mut self, new_params: DrcParams) -> Result<()> {
// Ask the client for new buffers.
self.event_queue
@@ -647,77 +496,8 @@
Ok(())
}
- /// Copies `src` into `dst` as NV12, removing any extra padding.
- fn nv12_copy(
- mut src: &[u8],
- mut dst: &mut [u8],
- width: u32,
- height: u32,
- strides: [u32; 3],
- offsets: [u32; 3],
- ) {
- let width = width.try_into().unwrap();
- let height = height.try_into().unwrap();
- let data = src;
-
- // Copy luma
- for _ in 0..height {
- dst[..width].copy_from_slice(&src[..width]);
- dst = &mut dst[width..];
- src = &src[strides[0] as usize..];
- }
-
- // Advance to the offset of the chroma plane
- let mut src = &data[offsets[1] as usize..];
-
- // Copy chroma
- for _ in 0..height / 2 {
- dst[..width].copy_from_slice(&src[..width]);
- dst = &mut dst[width..];
- src = &src[strides[1_usize] as usize..];
- }
- }
-
- /// Copy the decoded `image` into `output_buffer`, removing any extra
- /// padding. This is hardcoded to NV12 for now.
- fn copy_image_to_output(image: &Image, output_buffer: &mut GuestResource) -> Result<()> {
- let va_img = image.image();
-
- if va_img.format.fourcc != libva::constants::VA_FOURCC_NV12 {
- panic!("Unsupported format")
- }
-
- // Get a mapping from the start of the buffer to the size of the
- // underlying decoded data in the Image.
- let mut output_map: BufferMapping<_, GuestResourceHandle> = BufferMapping::new(
- &mut output_buffer.handle,
- 0,
- usize::from(va_img.width) * usize::from(va_img.height) * 3 / 2,
- )?;
-
- let output_bytes = output_map.as_mut();
-
- let decoded_bytes = image.as_ref();
-
- VaapiDecoderSession::nv12_copy(
- decoded_bytes,
- output_bytes,
- u32::from(va_img.width),
- u32::from(va_img.height),
- va_img.pitches,
- va_img.offsets,
- );
-
- Ok(())
- }
-
/// Copy raw decoded data from `image` into the output buffer
- pub fn output_picture(
- &mut self,
- decoded_frame: DecodedFrameHandle,
- resolution: Resolution,
- image_format: &libva::VAImageFormat,
- ) -> Result<bool> {
+ pub fn output_picture(&mut self, decoded_frame: &dyn DynDecodedHandle) -> Result<bool> {
let output_queue = self.output_queue_state.output_queue_mut()?;
// Output buffer to be used.
@@ -728,27 +508,29 @@
}
};
- let timestamp = RefCell::borrow(&decoded_frame.picture()).timestamp();
- let picture = decoded_frame.picture();
- let mut picture = picture.borrow_mut();
+ let mapped_resolution = decoded_frame
+ .dyn_picture_mut()
+ .dyn_mappable_handle_mut()
+ .mapped_resolution()?;
- // Get the associated VAImage, which will map the
- // VASurface onto our address space.
- let image = Image::new(
- &mut picture,
- *image_format,
- resolution.width,
- resolution.height,
- false,
+ let display_resolution = decoded_frame.display_resolution();
+
+ // Get a mapping from the start of the buffer to the size of the
+ // underlying decoded data in the Image.
+ let mut output_map: BufferMapping<_, GuestResourceHandle> = BufferMapping::new(
+ &mut output_buffer.handle,
+ 0,
+ mapped_resolution.width as usize * mapped_resolution.height as usize * 3 / 2,
)?;
- VaapiDecoderSession::copy_image_to_output(&image, output_buffer)?;
+ let output_bytes = output_map.as_mut();
- // The actual width and height. Note that The width and height fields
- // returned in the VAImage structure may get enlarged for some YUV
- // formats.
- let width = i32::from(image.image().width);
- let height = i32::from(image.image().height);
+ decoded_frame
+ .dyn_picture_mut()
+ .dyn_mappable_handle_mut()
+ .read(output_bytes)?;
+
+ let timestamp = decoded_frame.timestamp();
let picture_buffer_id = picture_buffer_id as i32;
// Say that we are done decoding this picture.
@@ -759,8 +541,8 @@
visible_rect: Rect {
left: 0,
top: 0,
- right: width as i32,
- bottom: height as i32,
+ right: display_resolution.width as i32,
+ bottom: display_resolution.height as i32,
},
})
.map_err(|e| {
@@ -771,17 +553,8 @@
}
fn drain_ready_queue(&mut self) -> Result<()> {
- let image_format = match self.codec.va_image_fmt() {
- Some(image_fmt) => *image_fmt,
- // No image format currently set means we have no output to drain.
- None => return Ok(()),
- };
-
while let Some(decoded_frame) = self.ready_queue.pop_front() {
- let resolution = *decoded_frame.resolution();
-
- let outputted =
- self.output_picture(decoded_frame.clone(), resolution, &image_format)?;
+ let outputted = self.output_picture(decoded_frame.as_ref())?;
if !outputted {
self.ready_queue.push_front(decoded_frame);
break;
@@ -791,45 +564,6 @@
Ok(())
}
- /// Convenience function to get the test NV12 result. This can be used to,
- /// e.g.: dump it to disk or compute a CRC32
- #[cfg(test)]
- #[allow(dead_code)]
- fn get_test_nv12<T: Fn(&[u8]) -> U, U>(
- display: Rc<Display>,
- picture: Rc<RefCell<Picture<PictureSync>>>,
- width: u32,
- height: u32,
- action: T,
- ) -> U {
- let mut picture = picture.borrow_mut();
-
- let image_fmts = display.query_image_formats().unwrap();
- let image_fmt = image_fmts
- .into_iter()
- .find(|f| f.fourcc == libva::constants::VA_FOURCC_NV12)
- .expect("No valid VAImageFormat found for NV12");
-
- // Get the associated VAImage, which will map the
- // VASurface onto our address space.
- let image = Image::new(&mut picture, image_fmt, width, height, false).unwrap();
-
- let va_img = image.image();
- let mut out: Vec<u8> =
- vec![0; usize::from(va_img.width) * usize::from(va_img.height) * 3 / 2];
-
- VaapiDecoderSession::nv12_copy(
- image.as_ref(),
- &mut out,
- u32::from(va_img.width),
- u32::from(va_img.height),
- va_img.pitches,
- va_img.offsets,
- );
-
- action(&out)
- }
-
fn try_emit_flush_completed(&mut self) -> Result<()> {
let num_remaining = self.ready_queue.len();
@@ -848,14 +582,58 @@
}
impl DecoderSession for VaapiDecoderSession {
- fn set_output_parameters(&mut self, buffer_count: usize, format: Format) -> VideoResult<()> {
+ fn set_output_parameters(&mut self, buffer_count: usize, _: Format) -> VideoResult<()> {
let output_queue_state = &mut self.output_queue_state;
+ // This logic can still be improved, in particular it needs better
+ // support at the virtio-video protocol level.
+ //
+ // We must ensure that set_output_parameters is only called after we are
+ // sure that we have processed some stream metadata, which currently is
+ // not the case. In particular, the {SET|GET}_PARAMS logic currently
+ // takes place *before* we had a chance to parse any stream metadata at
+ // all.
+ //
+ // This can lead to a situation where we accept a format (say, NV12),
+ // but then discover we are unable to decode it after processing some
+ // buffers (because the stream indicates that the bit depth is 10, for
+ // example). Note that there is no way to reject said stream as of right
+ // now unless we hardcode NV12 in cros-codecs itself.
+ //
+ // Nevertheless, the support is already in place in cros-codecs: the
+ // decoders will queue buffers until they read some metadata. At this
+ // point, it will allow for the negotiation of the decoded format until
+ // a new call to decode() is made. At the crosvm level, we can use this
+ // window of time to try different decoded formats with .try_format().
+ //
+ // For now, we accept the default format chosen by cros-codecs instead.
+ // In practice, this means NV12 if it the stream can be decoded into
+ // NV12 and if the hardware can do so.
+
match output_queue_state {
OutputQueueState::AwaitingBufferCount | OutputQueueState::Drc => {
- self.codec
- .set_raw_fmt(format)
- .map_err(VideoError::BackendFailure)?;
+ // Accept the default format chosen by cros-codecs instead.
+ //
+ // if let Some(backend_format) = self.backend.backend().format() {
+ // let backend_format = Format::try_from(backend_format);
+
+ // let format_matches = match backend_format {
+ // Ok(backend_format) => backend_format != format,
+ // Err(_) => false,
+ // };
+
+ // if !format_matches {
+ // let format =
+ // DecodedFormat::try_from(format).map_err(VideoError::BackendFailure)?;
+
+ // self.backend.backend().try_format(format).map_err(|e| {
+ // VideoError::BackendFailure(anyhow!(
+ // "Failed to set the codec backend format: {}",
+ // e
+ // ))
+ // })?;
+ // }
+ // }
*output_queue_state = OutputQueueState::Decoding {
output_queue: OutputQueue::new(buffer_count),
@@ -877,12 +655,19 @@
offset: u32,
bytes_used: u32,
) -> VideoResult<()> {
- let frames = self.codec.decode(timestamp, &resource, offset, bytes_used);
+ let bitstream_map: BufferMapping<_, GuestResourceHandle> = BufferMapping::new(
+ resource,
+ offset.try_into().unwrap(),
+ bytes_used.try_into().unwrap(),
+ )
+ .map_err(|e| VideoError::BackendFailure(anyhow!(e)))?;
+
+ let frames = self.codec.decode(timestamp, &bitstream_map);
match frames {
Ok(frames) => {
self.event_queue
- .queue_event(DecoderEvent::NotifyEndOfBitstreamBuffer(resource_id))
+ .queue_event(DecoderEvent::NotifyEndOfBitstreamBuffer(timestamp as u32))
.map_err(|e| {
VideoError::BackendFailure(anyhow!(
"Can't queue the NotifyEndOfBitstream event {}",
@@ -890,8 +675,22 @@
))
})?;
- if let Some(params) = self.codec.drc() {
- self.change_resolution(params)
+ if self.codec.negotiation_possible() {
+ let resolution = self.codec.backend().coded_resolution().unwrap();
+
+ let drc_params = DrcParams {
+ min_num_buffers: self.codec.backend().num_resources_total(),
+ width: resolution.width,
+ height: resolution.height,
+ visible_rect: Rect {
+ left: 0,
+ top: 0,
+ right: resolution.width as i32,
+ bottom: resolution.height as i32,
+ },
+ };
+
+ self.change_resolution(drc_params)
.map_err(VideoError::BackendFailure)?;
}
@@ -919,7 +718,7 @@
))
})?;
- Err(VideoError::BackendFailure(e))
+ Err(VideoError::BackendFailure(anyhow!(e)))
}
}
}
@@ -928,7 +727,11 @@
self.flushing = true;
// Retrieve ready frames from the codec, if any.
- let pics = self.codec.flush().map_err(VideoError::BackendFailure)?;
+ let pics = self
+ .codec
+ .flush()
+ .map_err(|e| VideoError::BackendFailure(anyhow!(e)))?;
+
self.ready_queue.extend(pics);
self.drain_ready_queue()
@@ -952,10 +755,57 @@
})?;
let display = Rc::new(Display::open().map_err(VideoError::BackendFailure)?);
- let codec = match self.coded_format {
- Format::VP8 => Box::new(
- vp8::Vp8Codec::new(display).map_err(|e| VideoError::BackendFailure(anyhow!(e)))?,
- ),
+ let codec: Box<dyn VideoDecoder> = match self.coded_format {
+ Format::VP8 => {
+ let backend = Box::new(
+ cros_codecs::decoders::vp8::backends::stateless::vaapi::Backend::new(
+ Rc::clone(&display),
+ )
+ .unwrap(),
+ );
+
+ Box::new(
+ cros_codecs::decoders::vp8::decoder::Decoder::new(
+ backend,
+ cros_codecs::decoders::BlockingMode::NonBlocking,
+ )
+ .map_err(|e| VideoError::BackendFailure(anyhow!(e)))?,
+ )
+ }
+
+ Format::VP9 => {
+ let backend = Box::new(
+ cros_codecs::decoders::vp9::backends::stateless::vaapi::Backend::new(
+ Rc::clone(&display),
+ )
+ .unwrap(),
+ );
+
+ Box::new(
+ cros_codecs::decoders::vp9::decoder::Decoder::new(
+ backend,
+ cros_codecs::decoders::BlockingMode::NonBlocking,
+ )
+ .map_err(|e| VideoError::BackendFailure(anyhow!(e)))?,
+ )
+ }
+
+ Format::H264 => {
+ let backend = Box::new(
+ cros_codecs::decoders::h264::backends::stateless::vaapi::Backend::new(
+ Rc::clone(&display),
+ )
+ .unwrap(),
+ );
+ Box::new(
+ cros_codecs::decoders::h264::decoder::Decoder::new(
+ backend,
+ cros_codecs::decoders::BlockingMode::NonBlocking,
+ )
+ .map_err(|e| VideoError::BackendFailure(anyhow!(e)))?,
+ )
+ }
+
_ => return Err(VideoError::InvalidFormat),
};
@@ -1062,10 +912,56 @@
fn new_session(&mut self, format: Format) -> VideoResult<Self::Session> {
let display = Rc::new(Display::open().map_err(VideoError::BackendFailure)?);
- let codec = match format {
- Format::VP8 => Box::new(
- vp8::Vp8Codec::new(display).map_err(|e| VideoError::BackendFailure(anyhow!(e)))?,
- ),
+ let codec: Box<dyn VideoDecoder> = match format {
+ Format::VP8 => {
+ let backend = Box::new(
+ cros_codecs::decoders::vp8::backends::stateless::vaapi::Backend::new(
+ Rc::clone(&display),
+ )
+ .unwrap(),
+ );
+
+ Box::new(
+ cros_codecs::decoders::vp8::decoder::Decoder::new(
+ backend,
+ cros_codecs::decoders::BlockingMode::NonBlocking,
+ )
+ .map_err(|e| VideoError::BackendFailure(anyhow!(e)))?,
+ )
+ }
+
+ Format::VP9 => {
+ let backend = Box::new(
+ cros_codecs::decoders::vp9::backends::stateless::vaapi::Backend::new(
+ Rc::clone(&display),
+ )
+ .unwrap(),
+ );
+
+ Box::new(
+ cros_codecs::decoders::vp9::decoder::Decoder::new(
+ backend,
+ cros_codecs::decoders::BlockingMode::NonBlocking,
+ )
+ .map_err(|e| VideoError::BackendFailure(anyhow!(e)))?,
+ )
+ }
+
+ Format::H264 => {
+ let backend = Box::new(
+ cros_codecs::decoders::h264::backends::stateless::vaapi::Backend::new(
+ Rc::clone(&display),
+ )
+ .unwrap(),
+ );
+ Box::new(
+ cros_codecs::decoders::h264::decoder::Decoder::new(
+ backend,
+ cros_codecs::decoders::BlockingMode::NonBlocking,
+ )
+ .map_err(|e| VideoError::BackendFailure(anyhow!(e)))?,
+ )
+ }
_ => return Err(VideoError::InvalidFormat),
};
@@ -1080,74 +976,9 @@
}
}
-pub trait VaapiCodec: downcast_rs::Downcast {
- /// Decode the compressed stream contained in
- /// [`offset`..`offset`+`bytes_used`] of the shared memory in `resource`.
- /// `timestamp` is the timestamp for that part of the stream.
- /// Returns zero or more decoded pictures depending on the compressed
- /// stream, which might also be part of the codec's decoded picture buffer
- /// (DPB).
- fn decode(
- &mut self,
- timestamp: u64,
- resource: &GuestResourceHandle,
- offset: u32,
- bytes_used: u32,
- ) -> Result<Vec<DecodedFrameHandle>>;
-
- /// Flush the decoder i.e. finish processing all queued decode requests and
- /// emit frames for them.
- fn flush(&mut self) -> Result<Vec<DecodedFrameHandle>>;
-
- /// Returns the current VA image format
- fn va_image_fmt(&self) -> &Option<libva::VAImageFormat>;
-
- /// Set the raw picture format.
- fn set_raw_fmt(&mut self, format: Format) -> Result<()>;
-
- /// Whether the codec has encountered a dynamic resolution change, i.e.:
- /// Whether there were changes in coded resolution (OUTPUT width and
- /// height), in the visible resolution (selection rectangles), in the
- /// minimum number of buffers needed for decoding or in bit-depth of the
- /// bitstream. This function will be called at every frame by the session.
- fn drc(&mut self) -> Option<DrcParams>;
-}
-downcast_rs::impl_downcast!(VaapiCodec); // Used in unit tests.
-
#[cfg(test)]
mod tests {
- use base::FromRawDescriptor;
- use base::MemoryMappingBuilder;
- use base::SafeDescriptor;
- use base::SharedMemory;
-
use super::*;
- use crate::virtio::video::resource::GuestMemArea;
- use crate::virtio::video::resource::GuestMemHandle;
-
- pub(crate) fn build_resource(slice: &[u8]) -> GuestResourceHandle {
- let input_shm = SharedMemory::new("", u64::try_from(slice.len()).unwrap()).unwrap();
-
- let input_mapping = MemoryMappingBuilder::new(input_shm.size() as usize)
- .from_shared_memory(&input_shm)
- .build()
- .unwrap();
-
- input_mapping
- .write_slice(slice, 0)
- .expect("Failed to write stream data into input buffer.");
-
- GuestResourceHandle::GuestPages(GuestMemHandle {
- // Safe because we are taking ownership of a just-duplicated FD.
- desc: unsafe {
- SafeDescriptor::from_raw_descriptor(base::clone_descriptor(&input_shm).unwrap())
- },
- mem_areas: vec![GuestMemArea {
- offset: 0,
- length: input_shm.size() as usize,
- }],
- })
- }
#[test]
// Ignore this test by default as it requires libva-compatible hardware.
diff --git a/devices/src/virtio/video/decoder/backend/vaapi/test-25fps-vp8-probability-table-0.bin b/devices/src/virtio/video/decoder/backend/vaapi/test-25fps-vp8-probability-table-0.bin
deleted file mode 100644
index 46ea618..0000000
--- a/devices/src/virtio/video/decoder/backend/vaapi/test-25fps-vp8-probability-table-0.bin
+++ /dev/null
Binary files differ
diff --git a/devices/src/virtio/video/decoder/backend/vaapi/test-25fps-vp8-probability-table-1.bin b/devices/src/virtio/video/decoder/backend/vaapi/test-25fps-vp8-probability-table-1.bin
deleted file mode 100644
index 19b4062..0000000
--- a/devices/src/virtio/video/decoder/backend/vaapi/test-25fps-vp8-probability-table-1.bin
+++ /dev/null
Binary files differ
diff --git a/devices/src/virtio/video/decoder/backend/vaapi/test-25fps-vp8-probability-table-2.bin b/devices/src/virtio/video/decoder/backend/vaapi/test-25fps-vp8-probability-table-2.bin
deleted file mode 100644
index 19b4062..0000000
--- a/devices/src/virtio/video/decoder/backend/vaapi/test-25fps-vp8-probability-table-2.bin
+++ /dev/null
Binary files differ
diff --git a/devices/src/virtio/video/decoder/backend/vaapi/test-25fps-vp8-slice-data-0.bin b/devices/src/virtio/video/decoder/backend/vaapi/test-25fps-vp8-slice-data-0.bin
deleted file mode 100644
index bd6a684..0000000
--- a/devices/src/virtio/video/decoder/backend/vaapi/test-25fps-vp8-slice-data-0.bin
+++ /dev/null
Binary files differ
diff --git a/devices/src/virtio/video/decoder/backend/vaapi/test-25fps-vp8-slice-data-1.bin b/devices/src/virtio/video/decoder/backend/vaapi/test-25fps-vp8-slice-data-1.bin
deleted file mode 100644
index 85ed11a..0000000
--- a/devices/src/virtio/video/decoder/backend/vaapi/test-25fps-vp8-slice-data-1.bin
+++ /dev/null
Binary files differ
diff --git a/devices/src/virtio/video/decoder/backend/vaapi/test-25fps-vp8-slice-data-2.bin b/devices/src/virtio/video/decoder/backend/vaapi/test-25fps-vp8-slice-data-2.bin
deleted file mode 100644
index 6391b98..0000000
--- a/devices/src/virtio/video/decoder/backend/vaapi/test-25fps-vp8-slice-data-2.bin
+++ /dev/null
Binary files differ
diff --git a/devices/src/virtio/video/decoder/backend/vaapi/test-25fps.vp8 b/devices/src/virtio/video/decoder/backend/vaapi/test-25fps.vp8
deleted file mode 100644
index dc22d43..0000000
--- a/devices/src/virtio/video/decoder/backend/vaapi/test-25fps.vp8
+++ /dev/null
Binary files differ
diff --git a/devices/src/virtio/video/decoder/backend/vaapi/test-25fps.vp8.crc b/devices/src/virtio/video/decoder/backend/vaapi/test-25fps.vp8.crc
deleted file mode 100644
index 72f1ff6..0000000
--- a/devices/src/virtio/video/decoder/backend/vaapi/test-25fps.vp8.crc
+++ /dev/null
@@ -1,250 +0,0 @@
-23ad177c
-1b9c8cae
-233b5afd
-e23f451d
-b3e6e470
-e204fec1
-36cf5e86
-fba3f823
-7b86762f
-7de7535c
-0e7a5291
-a8897d66
-3e0c1e91
-d7d29155
-d7316205
-810a2ae9
-92dac65f
-d323a7b8
-bca35249
-1e9ad2a6
-b6fd4f82
-6fe8fe2e
-bf949b74
-3f59a84b
-85e1a39b
-63ac1653
-ef92b037
-a775daa2
-5dc0dd88
-468bd51f
-cbb91ea3
-90b60e7b
-bf1ae070
-1c67ce82
-7ad8c84d
-6ef5edd2
-05f75839
-5ebd62ac
-d35c657f
-a1dac6fd
-0b2f6044
-734d13bb
-5679bd57
-fa199a3e
-a4a4d6d4
-6bd56dbe
-d799a86f
-227bbf58
-1541656d
-c723e6b8
-e07cc71a
-04778c4f
-fdd8b676
-8c681031
-e6847162
-8c7c211d
-5035e333
-4c5c8b3a
-77401793
-8d15d34c
-3587624d
-47307e2b
-4480cb44
-fbe55c59
-6520892e
-39df34bc
-8f8754e0
-3b08b12f
-bff1799a
-0034759b
-68861fbb
-801befe6
-c8101440
-cc5b856d
-96c1b94c
-a87e1845
-18b19080
-63e158c3
-96e5974c
-3faf3f86
-5947f01f
-ee3c1a46
-69f11a00
-2dff59fe
-d427bce6
-6bcee14f
-04a10cc5
-e3e12f43
-873d32ac
-f986a99f
-ac98dfc4
-c08f5c03
-7649d9f5
-68b76170
-7f8c6613
-433f4970
-9ec1ba95
-f7b03143
-37873456
-e2da340c
-c467fd97
-bf68a9c5
-dcece5cb
-5aa3c7d7
-9027434c
-5e4690f9
-79b59499
-3e6c0f04
-42853006
-9b761bc1
-2f2d290b
-12595217
-70482029
-770bf015
-f70adb10
-4189e078
-a4619a87
-5b2044f2
-2d162e58
-e071f345
-f845ff1f
-c41a2258
-c300cd49
-b5209b89
-51490639
-d5356f60
-a232fd4b
-0d98e5bf
-d34b3e3b
-9b951312
-b2cd5ead
-d9bfb278
-6018e793
-e8b2f310
-a8910a6c
-accee1d8
-6e51c606
-9050757f
-aa752e7b
-a4274821
-a0561066
-9258c326
-f6b66d22
-0e2f7048
-84d7bd75
-9a2b13ff
-7db691f1
-e0fd2689
-8fb24bc0
-89b201e9
-041dae30
-c8856390
-468220e4
-1371ec57
-9ddb3769
-76aa8d4c
-2e178d45
-7519b38e
-23da843f
-da24380f
-34e3b146
-42735634
-206269cc
-22ff4630
-8599f9d0
-5353b7b9
-f198b705
-27a62538
-2b6309a1
-bbed8b85
-c8c605ba
-23cc23aa
-a4b7cfab
-e8adab6b
-50308549
-82428aed
-90e1a158
-f6177648
-63f1a6bf
-53dd22fc
-e47289f0
-f0c36a34
-fcad085d
-fd925241
-32a79fc0
-047d4d80
-d31235f8
-5c46386a
-aaa9ee3c
-935a352c
-88df87ae
-e4dd9b22
-605f46fb
-087558c3
-25461a0f
-fe564aec
-1dc99d84
-7c2b1a2b
-08c433ce
-951609cb
-e69a15ee
-0be31b4a
-b0988d03
-c5c0769c
-7807a352
-925e5b37
-7dc3aaf0
-d1621353
-f1c0e356
-49a5f2d8
-2a7f52ef
-f280d5b6
-4f546dc9
-af08b467
-9612c8b7
-360aebde
-2311a9fb
-c175e073
-966537f0
-1cd52932
-cb098673
-11861fe5
-b482ec61
-a3d89823
-96fe22e6
-d28c91b1
-13ff2ba3
-998e52b7
-ae6ebd46
-20381724
-a0c7113d
-de28b72d
-a50f4d0e
-f6ac0144
-71679cf2
-f354c9d1
-6d84c275
-ca0b4570
-95138a3a
-e943fac5
-af611c9c
-d5811d81
-24fd3da7
-90ec3b70
-300659b6
-1aa57f0f
-3192b32f
-34091c37
-b7bc0a9f
-ea1f6fa7
diff --git a/devices/src/virtio/video/decoder/backend/vaapi/vp8.rs b/devices/src/virtio/video/decoder/backend/vaapi/vp8.rs
deleted file mode 100644
index 33a181d..0000000
--- a/devices/src/virtio/video/decoder/backend/vaapi/vp8.rs
+++ /dev/null
@@ -1,1197 +0,0 @@
-// Copyright 2022 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::convert::TryFrom;
-use std::rc::Rc;
-
-use anyhow::anyhow;
-use anyhow::Context;
-use anyhow::Result;
-use libva::BufferType;
-use libva::IQMatrix;
-use libva::IQMatrixBufferVP8;
-use libva::Picture;
-use libva::ProbabilityDataBufferVP8;
-use vp8::parser as vp8;
-
-use crate::virtio::video::decoder::backend::vaapi::BufferMapping;
-use crate::virtio::video::decoder::backend::vaapi::DecodedFrameHandle;
-use crate::virtio::video::decoder::backend::vaapi::DrcParams;
-use crate::virtio::video::decoder::backend::vaapi::Resolution;
-use crate::virtio::video::decoder::backend::vaapi::StreamMetadataState;
-use crate::virtio::video::decoder::backend::vaapi::SurfacePoolHandle;
-use crate::virtio::video::decoder::backend::vaapi::VaapiCodec;
-use crate::virtio::video::decoder::backend::vaapi::VaapiDecoderSession;
-use crate::virtio::video::error::VideoError;
-use crate::virtio::video::format::Format;
-use crate::virtio::video::format::Rect;
-use crate::virtio::video::resource::GuestResourceHandle;
-
-/// The number of surfaces to allocate for this codec. Same as GStreamer's vavp8dec.
-const NUM_SURFACES: usize = 7;
-
-#[cfg(test)]
-#[derive(Default)]
-struct Params {
- pic_param: Option<BufferType>,
- slice_param: Option<BufferType>,
- slice_data: Option<BufferType>,
- iq_matrix: Option<BufferType>,
- probability_table: Option<BufferType>,
- last_pic: Option<DecodedFrameHandle>,
-}
-
-pub struct Vp8Codec {
- /// The metadata state. Updated whenever the decoder reads new data from the stream.
- metadata_state: StreamMetadataState,
-
- /// A parser to extract bitstream data and build frame data in turn
- parser: vp8::Parser,
-
- /// The picture used as the last reference picture.
- last_picture: Option<DecodedFrameHandle>,
- /// The picture used as the golden reference picture.
- golden_ref_picture: Option<DecodedFrameHandle>,
- /// The picture used as the alternate reference picture.
- alt_ref_picture: Option<DecodedFrameHandle>,
-
- /// The VAImageFormat we will decode into
- image_fmt: Option<libva::VAImageFormat>,
- /// The current resolution
- resolution: Resolution,
-
- /// Whether we are still waiting for the leading key frame. We skip all
- /// frames until then, since decoding can only start from key frames.
- wait_keyframe: bool,
-
- /// Whether the initial setup is done after reading some metadata for the
- /// first time.
- open: bool,
-
- /// Whether the codec has identified a dynamic resolution change. Cleared
- /// when this information is relayed to the session.
- drc: bool,
-
- #[cfg(test)]
- params: Params,
-}
-
-impl Vp8Codec {
- /// Create a new codec backend for VP8.
- pub fn new(display: Rc<libva::Display>) -> Result<Self> {
- Ok(Self {
- metadata_state: StreamMetadataState::Unparsed { display },
- parser: Default::default(),
- last_picture: Default::default(),
- golden_ref_picture: Default::default(),
- alt_ref_picture: Default::default(),
- image_fmt: Default::default(),
- resolution: Default::default(),
- wait_keyframe: true,
- open: Default::default(),
- drc: Default::default(),
-
- #[cfg(test)]
- params: Default::default(),
- })
- }
-
- /// A clamp such that min <= x <= max
- fn clamp<T: PartialOrd>(x: T, low: T, high: T) -> T {
- if x > high {
- high
- } else if x < low {
- low
- } else {
- x
- }
- }
-
- fn change_resolution(&mut self, resolution: Resolution) -> Result<()> {
- self.resolution = resolution;
-
- let mut pool = self.metadata_state.surface_pool()?;
-
- // Tell the pool so it can refuse stale surfaces.
- pool.set_current_resolution(resolution);
-
- self.drc = true;
-
- Ok(())
- }
-
- /// Initialize the codec state by reading some metadata from the current
- /// frame.
- fn open(&mut self, frame_hdr: &vp8::Header) -> Result<()> {
- let display = self.metadata_state.display();
-
- let va_profile = libva::VAProfile::VAProfileVP8Version0_3;
- let rt_format = libva::constants::VA_RT_FORMAT_YUV420;
-
- let frame_w = u32::from(frame_hdr.width());
- let frame_h = u32::from(frame_hdr.height());
-
- let attrs = vec![libva::VAConfigAttrib {
- type_: libva::VAConfigAttribType::VAConfigAttribRTFormat,
- value: rt_format,
- }];
-
- let config = display.create_config(
- Some(attrs),
- va_profile,
- libva::VAEntrypoint::VAEntrypointVLD,
- )?;
-
- let surfaces = VaapiDecoderSession::create_surfaces(
- Rc::clone(&display),
- frame_w,
- frame_h,
- rt_format,
- NUM_SURFACES,
- )?;
-
- let context = Rc::new(display.create_context(
- &config,
- i32::try_from(frame_w)?,
- i32::try_from(frame_h)?,
- Some(&surfaces),
- true,
- )?);
-
- let resolution = Resolution {
- width: frame_w,
- height: frame_h,
- };
-
- let surface_pool = SurfacePoolHandle::new(surfaces, resolution);
-
- self.metadata_state = StreamMetadataState::Parsed {
- context,
- config,
- surface_pool,
- };
-
- self.change_resolution(resolution)?;
-
- Ok(())
- }
-
- /// Check for new stream parameters. This might mean that the surfaces
- /// and/or context get recreated. If a new resolution is detected, the
- /// decoder will enter a DRC state and it will ask crosvm for new output
- /// buffers to write to.
- fn check_stream_params(&mut self, frame_hdr: &vp8::Header) -> Result<()> {
- let frame_w = u32::from(frame_hdr.width());
- let frame_h = u32::from(frame_hdr.height());
-
- assert!(frame_hdr.version() <= 3);
-
- let rt_format = libva::constants::VA_RT_FORMAT_YUV420;
-
- let recreate_surfaces =
- self.resolution.width != frame_w || self.resolution.height != frame_h;
-
- if recreate_surfaces {
- let display = self.metadata_state.display();
- let surfaces = VaapiDecoderSession::create_surfaces(
- display,
- frame_w,
- frame_h,
- rt_format,
- NUM_SURFACES,
- )?;
-
- let mut pool = self.metadata_state.surface_pool()?;
-
- // Drop the old surfaces still in the pool
- pool.drop_surfaces();
-
- for surface in surfaces {
- pool.add_surface(surface)
- }
-
- self.change_resolution(Resolution {
- width: frame_w,
- height: frame_h,
- })?;
- }
-
- Ok(())
- }
-
- fn build_iq_matrix(frame_hdr: &vp8::Header, parser: &vp8::Parser) -> Result<libva::BufferType> {
- let mut quantization_index: [[u16; 6]; 4] = Default::default();
- let seg = parser.segmentation();
-
- for (i, quantization_index) in quantization_index.iter_mut().enumerate() {
- let mut qi_base: i16;
-
- if seg.segmentation_enabled {
- qi_base = i16::from(seg.quantizer_update_value[i]);
- if !seg.segment_feature_mode {
- qi_base += i16::from(frame_hdr.quant_indices().y_ac_qi);
- }
- } else {
- qi_base = i16::from(frame_hdr.quant_indices().y_ac_qi);
- }
-
- let mut qi = qi_base;
- quantization_index[0] = Vp8Codec::clamp(u16::try_from(qi)?, 0, 127);
- qi = qi_base + i16::from(frame_hdr.quant_indices().y_dc_delta);
- quantization_index[1] = Vp8Codec::clamp(u16::try_from(qi)?, 0, 127);
- qi = qi_base + i16::from(frame_hdr.quant_indices().y2_dc_delta);
- quantization_index[2] = Vp8Codec::clamp(u16::try_from(qi)?, 0, 127);
- qi = qi_base + i16::from(frame_hdr.quant_indices().y2_ac_delta);
- quantization_index[3] = Vp8Codec::clamp(u16::try_from(qi)?, 0, 127);
- qi = qi_base + i16::from(frame_hdr.quant_indices().uv_dc_delta);
- quantization_index[4] = Vp8Codec::clamp(u16::try_from(qi)?, 0, 127);
- qi = qi_base + i16::from(frame_hdr.quant_indices().uv_ac_delta);
- quantization_index[5] = Vp8Codec::clamp(u16::try_from(qi)?, 0, 127);
- }
-
- Ok(BufferType::IQMatrix(IQMatrix::VP8(IQMatrixBufferVP8::new(
- quantization_index,
- ))))
- }
-
- fn build_probability_table(frame_hdr: &vp8::Header) -> libva::BufferType {
- BufferType::Probability(ProbabilityDataBufferVP8::new(frame_hdr.coeff_prob()))
- }
-
- fn build_pic_param(
- frame_hdr: &vp8::Header,
- resolution: &Resolution,
- seg: &vp8::Segmentation,
- adj: &vp8::MbLfAdjustments,
- last: &Option<DecodedFrameHandle>,
- golden: &Option<DecodedFrameHandle>,
- alt: &Option<DecodedFrameHandle>,
- ) -> Result<libva::BufferType> {
- let mut loop_filter_level: [u8; 4] = Default::default();
- let mut loop_filter_deltas_ref_frame: [i8; 4] = Default::default();
- let mut loop_filter_deltas_mode: [i8; 4] = Default::default();
-
- for i in 0..4 {
- let mut level;
- if seg.segmentation_enabled {
- level = seg.lf_update_value[i];
- if !seg.segment_feature_mode {
- level += i8::try_from(frame_hdr.loop_filter_level())?;
- }
- } else {
- level = i8::try_from(frame_hdr.loop_filter_level())?;
- }
-
- loop_filter_level[i] = Vp8Codec::clamp(u8::try_from(level)?, 0, 63);
- loop_filter_deltas_ref_frame[i] = adj.ref_frame_delta[i];
- loop_filter_deltas_mode[i] = adj.mb_mode_delta[i];
- }
-
- let last_surface = if let Some(last_ref) = last {
- last_ref.picture().borrow().surface().id()
- } else {
- libva::constants::VA_INVALID_SURFACE
- };
-
- let golden_surface = if let Some(golden_ref) = golden {
- golden_ref.picture().borrow().surface().id()
- } else {
- libva::constants::VA_INVALID_SURFACE
- };
-
- let alt_surface = if let Some(alt_ref) = alt {
- alt_ref.picture().borrow().surface().id()
- } else {
- libva::constants::VA_INVALID_SURFACE
- };
-
- let pic_fields = libva::VP8PicFields::new(
- u32::from(!frame_hdr.key_frame()),
- u32::from(frame_hdr.version()),
- u32::from(seg.segmentation_enabled),
- u32::from(seg.update_mb_segmentation_map),
- u32::from(seg.update_segment_feature_data),
- u32::from(frame_hdr.filter_type()),
- u32::from(frame_hdr.sharpness_level()),
- u32::from(adj.loop_filter_adj_enable),
- u32::from(adj.mode_ref_lf_delta_update),
- u32::from(frame_hdr.sign_bias_golden()),
- u32::from(frame_hdr.sign_bias_alternate()),
- u32::from(frame_hdr.mb_no_coeff_skip()),
- u32::from(frame_hdr.loop_filter_level() == 0),
- );
-
- let bool_coder_ctx = libva::BoolCoderContextVPX::new(
- u8::try_from(frame_hdr.bd_range())?,
- u8::try_from(frame_hdr.bd_value())?,
- u8::try_from(frame_hdr.bd_count())?,
- );
-
- let pic_param = libva::PictureParameterBufferVP8::new(
- resolution.width,
- resolution.height,
- last_surface,
- golden_surface,
- alt_surface,
- &pic_fields,
- seg.segment_prob,
- loop_filter_level,
- loop_filter_deltas_ref_frame,
- loop_filter_deltas_mode,
- frame_hdr.prob_skip_false(),
- frame_hdr.prob_intra(),
- frame_hdr.prob_last(),
- frame_hdr.prob_golden(),
- frame_hdr.mode_probs().intra_16x16_prob,
- frame_hdr.mode_probs().intra_chroma_prob,
- frame_hdr.mv_prob(),
- &bool_coder_ctx,
- );
-
- Ok(libva::BufferType::PictureParameter(
- libva::PictureParameter::VP8(pic_param),
- ))
- }
-
- fn build_slice_param(frame_hdr: &vp8::Header, slice_size: usize) -> Result<libva::BufferType> {
- let mut partition_size: [u32; 9] = Default::default();
- let num_of_partitions = (1 << frame_hdr.log2_nbr_of_dct_partitions()) + 1;
-
- partition_size[0] = frame_hdr.first_part_size() - ((frame_hdr.header_size() + 7) >> 3);
-
- partition_size[1..num_of_partitions]
- .clone_from_slice(&frame_hdr.partition_size()[..(num_of_partitions - 1)]);
-
- Ok(libva::BufferType::SliceParameter(
- libva::SliceParameter::VP8(libva::SliceParameterBufferVP8::new(
- u32::try_from(slice_size)?,
- u32::from(frame_hdr.data_chunk_size()),
- 0,
- frame_hdr.header_size(),
- u8::try_from(num_of_partitions)?,
- partition_size,
- )),
- ))
- }
-
- /// Replace a reference frame with `picture`.
- fn replace_reference(reference: &mut Option<DecodedFrameHandle>, picture: &DecodedFrameHandle) {
- *reference = Some(picture.clone());
- }
-
- fn update_references(
- decoded_frame: &DecodedFrameHandle,
- last_picture: &mut Option<DecodedFrameHandle>,
- golden_ref_picture: &mut Option<DecodedFrameHandle>,
- alt_ref_picture: &mut Option<DecodedFrameHandle>,
- ) -> Result<()> {
- let header = decoded_frame
- .header
- .downcast_ref::<vp8::Header>()
- .with_context(|| "decoded frame header is not a VP8 header")?;
-
- if header.key_frame() {
- Vp8Codec::replace_reference(last_picture, decoded_frame);
- Vp8Codec::replace_reference(golden_ref_picture, decoded_frame);
- Vp8Codec::replace_reference(alt_ref_picture, decoded_frame);
- } else {
- if header.refresh_alternate_frame() {
- Vp8Codec::replace_reference(alt_ref_picture, decoded_frame);
- } else {
- match header.copy_buffer_to_alternate() {
- 0 => { /* do nothing */ }
-
- 1 => {
- if let Some(last_picture) = last_picture {
- Vp8Codec::replace_reference(alt_ref_picture, last_picture);
- }
- }
-
- 2 => {
- if let Some(golden_ref) = golden_ref_picture {
- Vp8Codec::replace_reference(alt_ref_picture, golden_ref);
- }
- }
-
- other => panic!("Invalid value: {}", other),
- }
- }
-
- if header.refresh_golden_frame() {
- Vp8Codec::replace_reference(golden_ref_picture, decoded_frame);
- } else {
- match header.copy_buffer_to_golden() {
- 0 => { /* do nothing */ }
-
- 1 => {
- if let Some(last_picture) = last_picture {
- Vp8Codec::replace_reference(golden_ref_picture, last_picture);
- }
- }
-
- 2 => {
- if let Some(alt_ref) = alt_ref_picture {
- Vp8Codec::replace_reference(golden_ref_picture, alt_ref);
- }
- }
-
- other => panic!("Invalid value: {}", other),
- }
- }
-
- if header.refresh_last() {
- Vp8Codec::replace_reference(last_picture, decoded_frame);
- }
- }
-
- Ok(())
- }
-
- #[cfg(test)]
- fn save_params(
- params: &mut Params,
- pic_param: BufferType,
- slice_param: BufferType,
- slice_data: BufferType,
- iq_matrix: BufferType,
- probability_table: BufferType,
- last_pic: DecodedFrameHandle,
- ) {
- params.pic_param = Some(pic_param);
- params.slice_param = Some(slice_param);
- params.slice_data = Some(slice_data);
- params.iq_matrix = Some(iq_matrix);
- params.probability_table = Some(probability_table);
- params.last_pic = Some(last_pic);
- }
-
- /// Decode a single frame, copying the decoded data into the output buffer
- /// as per the format in `self.image_fmt`.
- fn decode_frame<T: AsRef<[u8]>>(
- &mut self,
- frame: vp8::Frame<T>,
- timestamp: u64,
- ) -> Result<DecodedFrameHandle> {
- let context = self.metadata_state.context()?;
-
- let iq_buffer =
- context.create_buffer(Vp8Codec::build_iq_matrix(&frame.header, &self.parser)?)?;
- let probs = context.create_buffer(Vp8Codec::build_probability_table(&frame.header))?;
-
- let pic_param = context.create_buffer(Vp8Codec::build_pic_param(
- &frame.header,
- &self.resolution,
- self.parser.segmentation(),
- self.parser.mb_lf_adjust(),
- &self.last_picture,
- &self.golden_ref_picture,
- &self.alt_ref_picture,
- )?)?;
-
- let slice_param = context.create_buffer(Vp8Codec::build_slice_param(
- &frame.header,
- frame.bitstream.as_ref().len(),
- )?)?;
- let slice_data = context.create_buffer(libva::BufferType::SliceData(Vec::from(
- frame.bitstream.as_ref(),
- )))?;
-
- let context = self.metadata_state.context()?;
- let surface = self.metadata_state.get_surface()?;
-
- let mut picture = Picture::new(timestamp, Rc::clone(&context), surface);
-
- // Add buffers with the parsed data.
- picture.add_buffer(iq_buffer);
- picture.add_buffer(probs);
- picture.add_buffer(pic_param);
- picture.add_buffer(slice_param);
- picture.add_buffer(slice_data);
-
- let picture = picture.begin()?.render()?.end()?.sync()?;
- let picture = Rc::new(RefCell::new(picture));
-
- #[cfg(test)]
- Vp8Codec::save_params(
- &mut self.params,
- Vp8Codec::build_pic_param(
- &frame.header,
- &self.resolution,
- self.parser.segmentation(),
- self.parser.mb_lf_adjust(),
- &self.last_picture,
- &self.golden_ref_picture,
- &self.alt_ref_picture,
- )?,
- Vp8Codec::build_slice_param(&frame.header, frame.bitstream.as_ref().len())?,
- libva::BufferType::SliceData(Vec::from(frame.bitstream.as_ref())),
- Vp8Codec::build_iq_matrix(&frame.header, &self.parser)?,
- Vp8Codec::build_probability_table(&frame.header),
- DecodedFrameHandle::new(
- Rc::clone(&picture),
- Rc::new(frame.header.clone()),
- self.resolution,
- self.metadata_state.surface_pool()?,
- ),
- );
-
- let decoded_frame = DecodedFrameHandle::new(
- picture,
- Rc::new(frame.header),
- self.resolution,
- self.metadata_state.surface_pool()?,
- );
-
- // Do DPB management
- Vp8Codec::update_references(
- &decoded_frame,
- &mut self.last_picture,
- &mut self.golden_ref_picture,
- &mut self.alt_ref_picture,
- )?;
-
- Ok(decoded_frame)
- }
-
- /// Handle a single frame.
- fn handle_frame<T: AsRef<[u8]>>(
- &mut self,
- frame: vp8::Frame<T>,
- timestamp: u64,
- ) -> Result<Vec<DecodedFrameHandle>> {
- if self.wait_keyframe {
- if !frame.header.key_frame() {
- return Ok(Vec::new());
- }
- }
-
- if !self.open {
- self.open(&frame.header)?;
- self.open = true;
- }
-
- self.wait_keyframe = false;
-
- if frame.header.key_frame() {
- // Always check for changes at every key frame
- self.check_stream_params(&frame.header)?;
- }
-
- Ok(vec![(self.decode_frame(frame, timestamp)?)])
- }
-}
-
-impl VaapiCodec for Vp8Codec {
- fn decode(
- &mut self,
- timestamp: u64,
- resource: &GuestResourceHandle,
- offset: u32,
- bytes_used: u32,
- ) -> Result<Vec<DecodedFrameHandle>> {
- let bytes_used =
- usize::try_from(bytes_used).map_err(|e| VideoError::BackendFailure(anyhow!(e)))?;
-
- let offset = usize::try_from(offset).map_err(|e| VideoError::BackendFailure(anyhow!(e)))?;
-
- let bitstream_map: BufferMapping<_, GuestResourceHandle> =
- BufferMapping::new(resource, offset, bytes_used)?;
- let frame = self
- .parser
- .parse_frame(bitstream_map.as_ref())
- .map_err(|e| VideoError::BackendFailure(anyhow!(e)))?;
-
- self.handle_frame(frame, timestamp)
- }
-
- fn flush(&mut self) -> Result<Vec<DecodedFrameHandle>> {
- self.wait_keyframe = true;
-
- // VP8 has no internal picture queue, so simply return nothing here.
- Ok(vec![])
- }
-
- fn va_image_fmt(&self) -> &Option<libva::VAImageFormat> {
- &self.image_fmt
- }
-
- fn set_raw_fmt(&mut self, format: Format) -> Result<()> {
- let image_formats = self.metadata_state.display().query_image_formats()?;
-
- //TODO: this is hardcoded to NV12 for now. Will change when a proper
- //Format is implemented in the libva crate so we can use TryFrom.
- let image_fmt = image_formats
- .into_iter()
- .find(|f| f.fourcc == libva::constants::VA_FOURCC_NV12)
- .with_context(|| format!("unsupported format {}", format))?;
-
- self.image_fmt = Some(image_fmt);
-
- Ok(())
- }
-
- fn drc(&mut self) -> Option<DrcParams> {
- if self.drc {
- self.drc = false;
-
- Some(DrcParams {
- min_num_buffers: NUM_SURFACES,
- width: self.resolution.width,
- height: self.resolution.height,
- visible_rect: Rect {
- left: 0,
- top: 0,
- right: self.resolution.width as i32,
- bottom: self.resolution.height as i32,
- },
- })
- } else {
- None
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use std::io::Cursor;
- use std::io::Read;
- use std::io::Seek;
-
- use base::FromRawDescriptor;
- use base::SafeDescriptor;
- use base::SharedMemory;
- use bytes::Buf;
- use libva::BufferType;
- use libva::IQMatrix;
- use libva::PictureParameter;
- use libva::SliceParameter;
-
- use crate::virtio::video::decoder::backend::vaapi::tests::build_resource;
- use crate::virtio::video::decoder::backend::vaapi::vp8::Vp8Codec;
- use crate::virtio::video::decoder::backend::vaapi::vp8::NUM_SURFACES;
- use crate::virtio::video::decoder::backend::vaapi::VaapiDecoder;
- use crate::virtio::video::decoder::backend::vaapi::VaapiDecoderSession;
- use crate::virtio::video::decoder::backend::DecoderBackend;
- use crate::virtio::video::decoder::backend::DecoderEvent;
- use crate::virtio::video::decoder::backend::DecoderSession;
- use crate::virtio::video::format::Format;
- use crate::virtio::video::format::FramePlane;
- use crate::virtio::video::format::Rect;
- use crate::virtio::video::resource::GuestMemArea;
- use crate::virtio::video::resource::GuestMemHandle;
- use crate::virtio::video::resource::GuestResource;
- use crate::virtio::video::resource::GuestResourceHandle;
-
- fn get_codec(session: &mut VaapiDecoderSession) -> &mut Vp8Codec {
- session.codec.as_mut().downcast_mut::<Vp8Codec>().unwrap()
- }
-
- /// Read and return the data from the next IVF packet. Returns `None` if there is no more data
- /// to read.
- fn read_ivf_packet(cursor: &mut Cursor<&[u8]>) -> Option<Box<[u8]>> {
- if !cursor.has_remaining() {
- return None;
- }
-
- let len = cursor.get_u32_le();
- // Skip PTS.
- let _ = cursor.get_u64_le();
-
- let mut buf = vec![0u8; len as usize];
- cursor.read_exact(&mut buf).unwrap();
-
- Some(buf.into_boxed_slice())
- }
-
- #[test]
- // Ignore this test by default as it requires libva-compatible hardware.
- #[ignore]
- fn test_25fps_vp8() {
- // Test video stream and its properties. Compare the parameters for frames
- // 0,1, 2, and make sure the rest does not fail by unwraping the result
- // of codec.handle_frame()
- const TEST_25_FPS_VP8_STREAM: &[u8] = include_bytes!("test-25fps.vp8");
- const TEST_25_FPS_VP8_STREAM_WIDTH: i32 = 320;
- const TEST_25_FPS_VP8_STREAM_HEIGHT: i32 = 240;
- const NUM_BUFS: u32 = NUM_SURFACES as u32;
-
- // Extracted using "LIBVA_TRACE=libva LIBVA_TRACE_BUFDATA=1 gst-launch-1.0
- // filesrc location=test-25fps.vp8 ! parsebin ! vavp8dec ! fakevideosink"
- // A couple frames worth of data should make it easier spot parser/decoder
- // issues in contrast with using only a crc check
- const TEST_25_FPS_VP8_STREAM_SLICE_DATA_0: &[u8] =
- include_bytes!("test-25fps-vp8-slice-data-0.bin");
- const TEST_25_FPS_VP8_STREAM_SLICE_DATA_1: &[u8] =
- include_bytes!("test-25fps-vp8-slice-data-1.bin");
- const TEST_25_FPS_VP8_STREAM_SLICE_DATA_2: &[u8] =
- include_bytes!("test-25fps-vp8-slice-data-2.bin");
-
- const TEST_25_FPS_VP8_STREAM_PROBABILITY_TABLE_0: &[u8] =
- include_bytes!("test-25fps-vp8-probability-table-0.bin");
- const TEST_25_FPS_VP8_STREAM_PROBABILITY_TABLE_1: &[u8] =
- include_bytes!("test-25fps-vp8-probability-table-1.bin");
- const TEST_25_FPS_VP8_STREAM_PROBABILITY_TABLE_2: &[u8] =
- include_bytes!("test-25fps-vp8-probability-table-2.bin");
-
- const VP8_STREAM_CRCS: &str = include_str!("test-25fps.vp8.crc");
- let mut expected_frames_crcs = VP8_STREAM_CRCS.lines();
-
- let mut cursor = Cursor::new(TEST_25_FPS_VP8_STREAM);
- // Confirm the width and height of the stream.
- cursor.seek(std::io::SeekFrom::Start(12)).unwrap();
- let w = cursor.get_u16_le();
- let h = cursor.get_u16_le();
- assert!(w as i32 == TEST_25_FPS_VP8_STREAM_WIDTH);
- assert!(h as i32 == TEST_25_FPS_VP8_STREAM_HEIGHT);
-
- // Skip the IVH header entirely.
- cursor.seek(std::io::SeekFrom::Start(32)).unwrap();
-
- let mut decoder = VaapiDecoder::new().unwrap();
- let mut session = decoder.new_session(Format::VP8).unwrap();
-
- // Output buffers suitable for receiving NV12 frames for our stream.
- let output_buffers = (0..NUM_BUFS)
- .into_iter()
- .map(|_| {
- SharedMemory::new(
- "",
- (TEST_25_FPS_VP8_STREAM_WIDTH * TEST_25_FPS_VP8_STREAM_HEIGHT * 3 / 2) as u64,
- )
- .unwrap()
- })
- .collect::<Vec<_>>();
-
- let mut frame_num = 0;
- while let Some(packet) = read_ivf_packet(&mut cursor) {
- session
- .decode(
- 0,
- frame_num as u64,
- build_resource(packet.as_ref()),
- 0,
- packet.len() as u32,
- )
- .unwrap();
-
- // After sending the first buffer we should get the initial
- // resolution change event and can provide the frames to decode
- // into.
- if frame_num == 0 {
- assert!(session.event_queue.len() == 2);
-
- // Done with input buffer 0 and Waiting for output buffers.
- assert!(matches!(
- session.read_event().unwrap(),
- DecoderEvent::ProvidePictureBuffers {
- min_num_buffers: NUM_BUFS,
- width: TEST_25_FPS_VP8_STREAM_WIDTH,
- height: TEST_25_FPS_VP8_STREAM_HEIGHT,
- visible_rect: Rect {
- left: 0,
- top: 0,
- right: TEST_25_FPS_VP8_STREAM_WIDTH,
- bottom: TEST_25_FPS_VP8_STREAM_HEIGHT,
- },
- } | DecoderEvent::NotifyEndOfBitstreamBuffer(0)
- ));
-
- assert!(matches!(
- session.read_event().unwrap(),
- DecoderEvent::ProvidePictureBuffers {
- min_num_buffers: NUM_BUFS,
- width: TEST_25_FPS_VP8_STREAM_WIDTH,
- height: TEST_25_FPS_VP8_STREAM_HEIGHT,
- visible_rect: Rect {
- left: 0,
- top: 0,
- right: TEST_25_FPS_VP8_STREAM_WIDTH,
- bottom: TEST_25_FPS_VP8_STREAM_HEIGHT,
- },
- } | DecoderEvent::NotifyEndOfBitstreamBuffer(0)
- ));
-
- session
- .set_output_parameters(NUM_BUFS as usize, Format::NV12)
- .unwrap();
-
- // Pass the buffers we will decode into.
- for (picture_buffer_id, buffer) in output_buffers.iter().enumerate() {
- let handle = GuestResourceHandle::GuestPages(GuestMemHandle {
- // Safe because we are taking ownership of a just-duplicated FD.
- desc: unsafe {
- SafeDescriptor::from_raw_descriptor(
- base::clone_descriptor(buffer).unwrap(),
- )
- },
- mem_areas: vec![GuestMemArea {
- offset: 0,
- length: buffer.size() as usize,
- }],
- });
-
- session
- .use_output_buffer(
- picture_buffer_id as i32,
- GuestResource {
- handle,
- planes: vec![
- FramePlane {
- offset: 0,
- stride: TEST_25_FPS_VP8_STREAM_WIDTH as usize,
- size: (TEST_25_FPS_VP8_STREAM_WIDTH
- * TEST_25_FPS_VP8_STREAM_HEIGHT)
- as usize,
- },
- FramePlane {
- offset: (TEST_25_FPS_VP8_STREAM_WIDTH
- * TEST_25_FPS_VP8_STREAM_HEIGHT)
- as usize,
- stride: TEST_25_FPS_VP8_STREAM_WIDTH as usize,
- size: (TEST_25_FPS_VP8_STREAM_WIDTH
- * TEST_25_FPS_VP8_STREAM_HEIGHT
- / 2)
- as usize,
- },
- ],
- width: TEST_25_FPS_VP8_STREAM_WIDTH as u32,
- height: TEST_25_FPS_VP8_STREAM_HEIGHT as u32,
- format: Format::NV12,
- },
- )
- .unwrap();
- }
- }
-
- let codec = get_codec(&mut session);
- let params = &codec.params;
- let pic_param = match params.pic_param {
- Some(BufferType::PictureParameter(PictureParameter::VP8(ref pic_param))) => {
- pic_param
- }
- _ => panic!(),
- };
-
- let slice_param = match params.slice_param {
- Some(BufferType::SliceParameter(SliceParameter::VP8(ref slice_param))) => {
- slice_param
- }
- _ => panic!(),
- };
-
- let slice_data = match params.slice_data {
- Some(BufferType::SliceData(ref data)) => data,
- _ => panic!(),
- };
-
- let iq_matrix = match params.iq_matrix {
- Some(BufferType::IQMatrix(IQMatrix::VP8(ref iq_matrix))) => iq_matrix,
- _ => panic!(),
- };
-
- let probability_table = match params.probability_table {
- Some(BufferType::Probability(ref probability_table)) => probability_table,
- _ => panic!(),
- };
-
- let last_pic = params.last_pic.as_ref().unwrap();
-
- // Uncomment to dump the YUV output to /tmp
- // VaapiDecoderSession::get_test_nv12(
- // codec.metadata_state.display(),
- // last_pic.picture(),
- // 320,
- // 240,
- // |nv12| {
- // std::fs::write(
- // format!("/tmp/{}_frame{}.yuv", "test_25fps.vp8", frame_num),
- // nv12,
- // )
- // .unwrap();
- // },
- // );
-
- let frame_crc = VaapiDecoderSession::get_test_nv12(
- codec.metadata_state.display(),
- last_pic.picture(),
- 320,
- 240,
- crc32fast::hash,
- );
- assert_eq!(
- format!("{:08x}", frame_crc),
- expected_frames_crcs
- .next()
- .expect("No CRC for decoded frame")
- );
-
- if frame_num == 0 {
- assert_eq!(iq_matrix.inner().quantization_index, [[4; 6]; 4]);
- for i in 0..4 {
- for j in 0..8 {
- for k in 0..3 {
- for l in 0..11 {
- const OFF_I: usize = 8 * 3 * 11;
- const OFF_J: usize = 3 * 11;
- const OFF_K: usize = 11;
- // maybe std::transmute?
- assert_eq!(
- probability_table.inner().dct_coeff_probs[i][j][k][l],
- TEST_25_FPS_VP8_STREAM_PROBABILITY_TABLE_0
- [(i * OFF_I) + (j * OFF_J) + (k * OFF_K) + l]
- );
- }
- }
- }
- }
-
- 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::constants::VA_INVALID_SURFACE
- );
- assert_eq!(
- pic_param.inner().golden_ref_frame,
- libva::constants::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::constants::VA_INVALID_SURFACE
- );
-
- // Safe because this bitfield is initialized by the decoder.
- assert_eq!(unsafe { pic_param.inner().pic_fields.value }, unsafe {
- libva::VP8PicFields::new(0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1)
- .inner()
- .value
- });
-
- assert_eq!(pic_param.inner().mb_segment_tree_probs, [0; 3]);
- assert_eq!(pic_param.inner().loop_filter_level, [0; 4]);
- assert_eq!(
- pic_param.inner().loop_filter_deltas_ref_frame,
- [2, 0, -2, -2]
- );
- assert_eq!(pic_param.inner().loop_filter_deltas_mode, [4, -2, 2, 4]);
- assert_eq!(pic_param.inner().prob_skip_false, 0xbe);
- assert_eq!(pic_param.inner().prob_intra, 0);
- assert_eq!(pic_param.inner().prob_last, 0);
- assert_eq!(pic_param.inner().prob_gf, 0);
-
- assert_eq!(pic_param.inner().y_mode_probs, [0x91, 0x9c, 0xa3, 0x80]);
- assert_eq!(pic_param.inner().uv_mode_probs, [0x8e, 0x72, 0xb7]);
- assert_eq!(
- pic_param.inner().mv_probs[0],
- [
- 0xa2, 0x80, 0xe1, 0x92, 0xac, 0x93, 0xd6, 0x27, 0x9c, 0x80, 0x81, 0x84,
- 0x4b, 0x91, 0xb2, 0xce, 0xef, 0xfe, 0xfe
- ]
- );
- assert_eq!(
- pic_param.inner().mv_probs[1],
- [
- 0xa4, 0x80, 0xcc, 0xaa, 0x77, 0xeb, 0x8c, 0xe6, 0xe4, 0x80, 0x82, 0x82,
- 0x4a, 0x94, 0xb4, 0xcb, 0xec, 0xfe, 0xfe,
- ]
- );
-
- assert_eq!(pic_param.inner().bool_coder_ctx.range, 0xfc);
- assert_eq!(pic_param.inner().bool_coder_ctx.value, 0x39);
- assert_eq!(pic_param.inner().bool_coder_ctx.count, 0x0);
-
- assert_eq!(
- slice_param.inner(),
- libva::SliceParameterBufferVP8::new(
- 14788,
- 10,
- 0,
- 3040,
- 2,
- [926, 13472, 0, 0, 0, 0, 0, 0, 0],
- )
- .inner(),
- );
-
- assert_eq!(&slice_data[..], TEST_25_FPS_VP8_STREAM_SLICE_DATA_0);
- } else if frame_num == 1 {
- assert_eq!(iq_matrix.inner().quantization_index, [[0x7f; 6]; 4]);
- for i in 0..4 {
- for j in 0..8 {
- for k in 0..3 {
- for l in 0..11 {
- const OFF_I: usize = 8 * 3 * 11;
- const OFF_J: usize = 3 * 11;
- const OFF_K: usize = 11;
- // maybe std::transmute?
- assert_eq!(
- probability_table.inner().dct_coeff_probs[i][j][k][l],
- TEST_25_FPS_VP8_STREAM_PROBABILITY_TABLE_1
- [(i * OFF_I) + (j * OFF_J) + (k * OFF_K) + l]
- );
- }
- }
- }
- }
- assert_eq!(pic_param.inner().frame_width, 320);
- assert_eq!(pic_param.inner().frame_height, 240);
- assert_eq!(pic_param.inner().last_ref_frame, 0);
- assert_eq!(pic_param.inner().golden_ref_frame, 0);
- assert_eq!(pic_param.inner().alt_ref_frame, 0);
- assert_eq!(
- pic_param.inner().out_of_loop_frame,
- libva::constants::VA_INVALID_SURFACE
- );
-
- // Safe because this bitfield is initialized by the decoder.
- assert_eq!(unsafe { pic_param.inner().pic_fields.value }, unsafe {
- libva::VP8PicFields::new(1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0)
- .inner()
- .value
- });
-
- assert_eq!(pic_param.inner().mb_segment_tree_probs, [0; 3]);
- assert_eq!(pic_param.inner().loop_filter_level, [44; 4]);
- assert_eq!(
- pic_param.inner().loop_filter_deltas_ref_frame,
- [2, 0, -2, -2]
- );
- assert_eq!(pic_param.inner().loop_filter_deltas_mode, [4, -2, 2, 4]);
- assert_eq!(pic_param.inner().prob_skip_false, 0x11);
- assert_eq!(pic_param.inner().prob_intra, 0x2a);
- assert_eq!(pic_param.inner().prob_last, 0xff);
- assert_eq!(pic_param.inner().prob_gf, 0x80);
-
- assert_eq!(pic_param.inner().y_mode_probs, [0x70, 0x56, 0x8c, 0x25]);
- assert_eq!(pic_param.inner().uv_mode_probs, [0xa2, 0x65, 0xcc]);
- assert_eq!(
- pic_param.inner().mv_probs[0],
- [
- 0xa2, 0x80, 0xe1, 0x92, 0xac, 0x93, 0xd6, 0x27, 0x9c, 0x80, 0x81, 0x84,
- 0x4b, 0x91, 0xb2, 0xce, 0xef, 0xfe, 0xfe,
- ]
- );
- assert_eq!(
- pic_param.inner().mv_probs[1],
- [
- 0xa4, 0x80, 0xcc, 0xaa, 0x77, 0xeb, 0x8c, 0xe6, 0xe4, 0x80, 0x82, 0x82,
- 0x4a, 0x94, 0xb4, 0xcb, 0xec, 0xfe, 0xfe,
- ]
- );
-
- assert_eq!(pic_param.inner().bool_coder_ctx.range, 0xde);
- assert_eq!(pic_param.inner().bool_coder_ctx.value, 0x39);
- assert_eq!(pic_param.inner().bool_coder_ctx.count, 0x7);
-
- assert_eq!(
- slice_param.inner(),
- libva::SliceParameterBufferVP8::new(
- 257,
- 3,
- 0,
- 129,
- 2,
- [143, 94, 0, 0, 0, 0, 0, 0, 0],
- )
- .inner()
- );
-
- assert_eq!(&slice_data[..], TEST_25_FPS_VP8_STREAM_SLICE_DATA_1);
- } else if frame_num == 2 {
- assert_eq!(iq_matrix.inner().quantization_index, [[0x7f; 6]; 4]);
- for i in 0..4 {
- for j in 0..8 {
- for k in 0..3 {
- for l in 0..11 {
- const OFF_I: usize = 8 * 3 * 11;
- const OFF_J: usize = 3 * 11;
- const OFF_K: usize = 11;
- // maybe std::transmute?
- assert_eq!(
- probability_table.inner().dct_coeff_probs[i][j][k][l],
- TEST_25_FPS_VP8_STREAM_PROBABILITY_TABLE_2
- [(i * OFF_I) + (j * OFF_J) + (k * OFF_K) + l]
- );
- }
- }
- }
- }
- assert_eq!(pic_param.inner().frame_width, 320);
- assert_eq!(pic_param.inner().frame_height, 240);
- assert_eq!(pic_param.inner().last_ref_frame, 1);
- assert_eq!(pic_param.inner().golden_ref_frame, 0);
- assert_eq!(pic_param.inner().alt_ref_frame, 0);
- assert_eq!(
- pic_param.inner().out_of_loop_frame,
- libva::constants::VA_INVALID_SURFACE
- );
-
- // Safe because this bitfield is initialized by the decoder.
- assert_eq!(unsafe { pic_param.inner().pic_fields.value }, unsafe {
- libva::VP8PicFields::new(1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0)
- .inner()
- .value
- });
-
- assert_eq!(pic_param.inner().mb_segment_tree_probs, [0; 3]);
- assert_eq!(pic_param.inner().loop_filter_level, [28; 4]);
- assert_eq!(
- pic_param.inner().loop_filter_deltas_ref_frame,
- [2, 0, -2, -2]
- );
- assert_eq!(pic_param.inner().loop_filter_deltas_mode, [4, -2, 2, 4]);
- assert_eq!(pic_param.inner().prob_skip_false, 0x6);
- assert_eq!(pic_param.inner().prob_intra, 0x1);
- assert_eq!(pic_param.inner().prob_last, 0xf8);
- assert_eq!(pic_param.inner().prob_gf, 0xff);
-
- assert_eq!(pic_param.inner().y_mode_probs, [0x70, 0x56, 0x8c, 0x25]);
- assert_eq!(pic_param.inner().uv_mode_probs, [0xa2, 0x65, 0xcc]);
- assert_eq!(
- pic_param.inner().mv_probs[0],
- [
- 0xa2, 0x80, 0xe1, 0x92, 0xac, 0x93, 0xd6, 0x27, 0x9c, 0x80, 0x81, 0x84,
- 0x4b, 0x91, 0xb2, 0xce, 0xef, 0xfe, 0xfe,
- ]
- );
- assert_eq!(
- pic_param.inner().mv_probs[1],
- [
- 0xa4, 0x80, 0xcc, 0xaa, 0x77, 0xeb, 0x8c, 0xe6, 0xe4, 0x80, 0x82, 0x82,
- 0x4a, 0x94, 0xb4, 0xcb, 0xec, 0xfe, 0xfe,
- ]
- );
-
- assert_eq!(pic_param.inner().bool_coder_ctx.range, 0xb1);
- assert_eq!(pic_param.inner().bool_coder_ctx.value, 0xd);
- assert_eq!(pic_param.inner().bool_coder_ctx.count, 0x2);
-
- assert_eq!(
- slice_param.inner(),
- libva::SliceParameterBufferVP8::new(
- 131,
- 3,
- 0,
- 86,
- 2,
- [66, 51, 0, 0, 0, 0, 0, 0, 0],
- )
- .inner()
- );
-
- assert_eq!(&slice_data[..], TEST_25_FPS_VP8_STREAM_SLICE_DATA_2);
- }
-
- // Assume for the purposes of this test that the output buffer can
- // be reused, otherwise we are going to run out
- let _ = session.reuse_output_buffer(frame_num as i32 % NUM_BUFS as i32);
- frame_num += 1;
- }
- }
-}
diff --git a/media/vp8/Cargo.toml b/media/vp8/Cargo.toml
deleted file mode 100644
index efedec1..0000000
--- a/media/vp8/Cargo.toml
+++ /dev/null
@@ -1,10 +0,0 @@
-[package]
-name = "vp8"
-version = "0.1.0"
-authors = ["The Chromium OS Authors"]
-edition = "2021"
-
-[dependencies]
-anyhow = "*"
-bytes = "1.1.0"
-log = { version = "0", features = ["release_max_level_debug"]}
diff --git a/media/vp8/src/bool_decoder.rs b/media/vp8/src/bool_decoder.rs
deleted file mode 100644
index 424a4be..0000000
--- a/media/vp8/src/bool_decoder.rs
+++ /dev/null
@@ -1,270 +0,0 @@
-// Copyright 2022 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/// A VP8 boolean decoder based on the implementation in Chromium and GStreamer.
-use std::convert::TryFrom;
-/// A VP8 boolean decoder based on the implementation in Chromium and GStreamer.
-use std::io::Cursor;
-
-use anyhow::anyhow;
-use anyhow::Result;
-use bytes::Buf;
-
-const LOTS_OF_BITS: u32 = 0x40000000;
-const U8_BITS: usize = u8::BITS as usize;
-const BD_VALUE_SIZE: usize = std::mem::size_of::<usize>() * U8_BITS;
-
-const NORM: [u8; 256] = [
- 0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-];
-
-/// Some bits are "encoded" with a 50/50 probability.
-const DEFAULT_PROBABILITY: u8 = 128;
-
-/// The decoder state.
-#[derive(Default)]
-pub struct BoolDecoder<T> {
- data: Cursor<T>,
- range: usize,
- value: usize,
- count: isize,
-}
-
-impl<T: AsRef<[u8]>> BoolDecoder<T> {
- /// Creates a new instance.
- pub fn new(data: T) -> Self {
- Self {
- data: Cursor::new(data),
- range: 255usize,
- value: 0usize,
- count: -8,
- }
- }
-
- /// Fills more bits from `data` to `value`. We shall keep at least 8 bits of
- /// the current `data` in `value`.
- fn fill(&mut self) -> Result<()> {
- let mut shift =
- (BD_VALUE_SIZE as isize - U8_BITS as isize - (self.count + U8_BITS as isize)) as i32;
- let bits_left = (self.data.remaining() * U8_BITS) as i32;
- let x = shift + U8_BITS as i32 - bits_left;
- let mut loop_end = 0;
-
- if x >= 0 {
- self.count += LOTS_OF_BITS as isize;
- loop_end = x;
- }
-
- if x < 0 || bits_left != 0 {
- while shift >= loop_end {
- self.count += U8_BITS as isize;
- self.value |= (self.data.get_u8() as usize) << shift;
- shift -= U8_BITS as i32;
- }
- Ok(())
- } else {
- Err(anyhow!("Out of bits"))
- }
- }
-
- /// Reads the next bit from the coded stream. The probability of the bit to
- /// be one is probability / 256.
- fn read_bit(&mut self, probability: u8) -> Result<bool> {
- let split = 1 + (((self.range - 1) * probability as usize) >> 8);
-
- if self.count < 0 {
- self.fill()?;
- }
-
- let bigsplit = split << (BD_VALUE_SIZE - U8_BITS);
-
- let bit = if self.value >= bigsplit {
- self.range -= split;
- self.value -= bigsplit;
- true
- } else {
- self.range = split;
- false
- };
-
- let shift = NORM[self.range];
- self.range <<= shift;
- self.value <<= shift;
- self.count -= isize::from(shift);
-
- Ok(bit)
- }
-
- /// Reads a "literal", that is, a "num_bits"-wide unsigned value whose bits
- /// come high- to low-order, with each bit encoded at probability 1/2.
- fn read_literal(&mut self, mut nbits: usize) -> Result<i32> {
- let mut ret = 0;
-
- while nbits > 0 {
- let bit = self.read_bit(DEFAULT_PROBABILITY)?;
- ret = (ret << 1) | bit as i32;
- nbits -= 1;
- }
-
- Ok(ret)
- }
-
- /// Reads a boolean from the coded stream. Returns false if it has reached the
- /// end of data and failed to read the boolean. The probability of out to
- /// be true is probability / 256, e.g., when probability is 0x80, the
- /// chance is 1/2 (i.e., 0x80 / 256).
- pub fn read_bool(&mut self) -> Result<bool> {
- self.read_literal(1).map(|bit| bit != 0)
- }
-
- /// Reads a boolean from the coded stream. Returns false if it has reached the
- /// end of data and failed to read the boolean. The probability of out to
- /// be true is probability / 256, e.g., when probability is 0x80, the
- /// chance is 1/2 (i.e., 0x80 / 256).
- pub fn read_bool_with_prob(&mut self, probability: u8) -> Result<bool> {
- self.read_bit(probability)
- }
-
- /// Reads an unsigned literal from the coded stream.
- pub fn read_uint<U: TryFrom<i32>>(&mut self, nbits: usize) -> Result<U> {
- let value = self.read_literal(nbits)?;
- U::try_from(value).map_err(|_| anyhow!("Conversion failed"))
- }
-
- /// Reads a literal with sign from the coded stream. This is similar to the
- /// read_literal(), it first read a "num_bits"-wide unsigned value, and then
- /// read an extra bit as the sign of the literal.
- pub fn read_sint<U: TryFrom<i32>>(&mut self, nbits: usize) -> Result<U> {
- let mut value = self.read_literal(nbits)?;
- let sign = self.read_bool()?;
-
- if sign {
- value = -value;
- }
-
- U::try_from(value).map_err(|_| anyhow!("Conversion failed"))
- }
-
- /// Returns the current coded value.
- pub fn value(&self) -> usize {
- self.value >> (BD_VALUE_SIZE - U8_BITS)
- }
-
- /// Returns the number of bytes in the `value` buffer.
- pub fn count(&self) -> isize {
- (U8_BITS as isize + self.count) % U8_BITS as isize
- }
-
- /// Returns the range of the current coded value.
- pub fn range(&self) -> usize {
- self.range
- }
-
- /// Returns the current bit position.
- pub fn pos(&self) -> usize {
- let mut bit_count = (self.count + 8) as usize;
-
- if bit_count > BD_VALUE_SIZE {
- bit_count = std::cmp::max(0, bit_count - LOTS_OF_BITS as usize);
- }
-
- let pos = self.data.position() as usize;
- pos * U8_BITS - bit_count
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- const NUM_BITS_TO_TEST: usize = 100;
-
- /// 100 zeros with probability of 0x80.
- const DATA_ZEROS_AND_EVEN_PROBABILITIES: [u8; 14] = [
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- ];
-
- /// 100 ones with probability of 0x80.
- const DATA_ONES_AND_EVEN_PROBABILITIES: [u8; 14] = [
- 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x20,
- ];
-
- /// [0, 1, 0, 1, ..., 1] with probability [0, 1, 2, 3, ..., 99].
- const DATA_PARITIES_AND_INCREASING_PROBABILITIES: [u8; 21] = [
- 0x00, 0x02, 0x08, 0x31, 0x8e, 0xca, 0xab, 0xe2, 0xc8, 0x31, 0x12, 0xb3, 0x2c, 0x19, 0x90,
- 0xc6, 0x6a, 0xeb, 0x17, 0x52, 0x30,
- ];
-
- // All tests adapted from:
- // https://chromium.googlesource.com/chromium/src/+/refs/heads/main/media/parsers/vp8_bool_decoder_unittest.cc
-
- #[test]
- fn decode_bools_with_zeros_and_even_probabilities() {
- let mut bd = BoolDecoder::new(&DATA_ZEROS_AND_EVEN_PROBABILITIES[..]);
- assert!(bd.pos() == 0);
-
- for i in 0..NUM_BITS_TO_TEST {
- assert!(!bd.read_bool_with_prob(0x80).unwrap());
- assert_eq!(i, bd.pos());
- }
- }
-
- #[test]
- fn decode_literals_with_zeros_and_even_probabilities() {
- // Adapted from:
- // https://chromium.googlesource.com/chromium/src/+/refs/heads/main/media/parsers/vp8_bool_decoder_unittest.cc
- let mut bd = BoolDecoder::new(&DATA_ZEROS_AND_EVEN_PROBABILITIES[..]);
- assert!(bd.pos() == 0);
-
- assert!(bd.read_literal(1).unwrap() == 0);
- assert!(bd.read_literal(32).unwrap() == 0);
- assert!(bd.read_sint::<i32>(1).unwrap() == 0);
- assert!(bd.read_sint::<i32>(31).unwrap() == 0);
- }
-
- #[test]
- fn decode_bools_with_ones_and_even_probabilities() {
- let mut bd = BoolDecoder::new(&DATA_ONES_AND_EVEN_PROBABILITIES[..]);
- assert!(bd.pos() == 0);
-
- for i in 0..NUM_BITS_TO_TEST {
- assert!(bd.read_bool_with_prob(0x80).unwrap());
- assert_eq!(i + 1, bd.pos());
- }
- }
-
- #[test]
- fn decode_literals_with_ones_and_even_probabilities() {
- let mut bd = BoolDecoder::new(&DATA_ONES_AND_EVEN_PROBABILITIES[..]);
- assert!(bd.pos() == 0);
-
- assert!(bd.read_literal(1).unwrap() == 1);
- assert!(bd.read_literal(31).unwrap() == 0x7fffffff);
- assert!(bd.read_sint::<i32>(1).unwrap() == -1);
- assert!(bd.read_sint::<i32>(31).unwrap() == -0x7fffffff);
- }
-
- #[test]
- fn decode_bools_with_parities_and_increasing_probabilities() {
- let mut bd = BoolDecoder::new(&DATA_PARITIES_AND_INCREASING_PROBABILITIES[..]);
- assert!(bd.pos() == 0);
-
- for i in 0..NUM_BITS_TO_TEST {
- let bit = bd.read_bool_with_prob(i as u8).unwrap();
-
- if i % 2 == 0 {
- assert!(!bit);
- } else {
- assert!(bit);
- }
- }
- }
-}
diff --git a/media/vp8/src/lib.rs b/media/vp8/src/lib.rs
deleted file mode 100644
index 6f6b7c9..0000000
--- a/media/vp8/src/lib.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-// Copyright 2022 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-mod bool_decoder;
-pub mod parser;
-mod probs;
diff --git a/media/vp8/src/parser.rs b/media/vp8/src/parser.rs
deleted file mode 100644
index 4aa5944..0000000
--- a/media/vp8/src/parser.rs
+++ /dev/null
@@ -1,902 +0,0 @@
-// Copyright 2022 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::convert::TryFrom;
-use std::io::Cursor;
-
-use anyhow::anyhow;
-use anyhow::Result;
-use bytes::Buf;
-use log::debug;
-
-use crate::bool_decoder::BoolDecoder;
-use crate::probs::COEFF_DEFAULT_PROBS;
-use crate::probs::COEFF_UPDATE_PROBS;
-use crate::probs::KF_UV_MODE_PROBS;
-use crate::probs::KF_Y_MODE_PROBS;
-use crate::probs::MV_DEFAULT_PROBS;
-use crate::probs::MV_UPDATE_PROBS;
-use crate::probs::NK_UV_MODE_PROBS;
-use crate::probs::NK_Y_MODE_PROBS;
-
-/// Dequantization indices as parsed from the quant_indices() syntax.
-#[derive(Clone, Default)]
-pub struct QuantIndices {
- /// The dequantization table index used for the luma AC coefficients (and
- /// other coefficient groups if no delta value is present).
- pub y_ac_qi: u8,
- /// Indicates the delta value that is added to the baseline index to obtain
- /// the luma DC coefficient dequantization index.
- pub y_dc_delta: i8,
- /// Indicates the delta value that is added to the baseline index to obtain
- /// the Y2 block DC coefficient dequantization index.
- pub y2_dc_delta: i8,
- /// Indicates the delta value that is added to the baseline index to obtain
- /// the Y2 block AC coefficient dequantization index.
- pub y2_ac_delta: i8,
- /// Indicates the delta value that is added to the baseline index to obtain
- /// the chroma DC coefficient dequantization index.
- pub uv_dc_delta: i8,
- /// Indicates the delta value that is added to the baseline index to obtain
- /// the chroma AC coefficient dequantization index.
- pub uv_ac_delta: i8,
-}
-
-#[derive(Clone, Default)]
-pub struct MbLfAdjustments {
- /// Indicates if the MB-level loop filter adjustment (based on the used
- /// reference frame and coding mode) is on for the current frame.
- pub loop_filter_adj_enable: bool,
- /// Indicates if the delta values used in adjustment are updated in the
- /// current frame.
- pub mode_ref_lf_delta_update: bool,
-
- //if mode_ref_lf_delta_update == 1
- /// Indicates the adjustment delta value corresponding to a certain used
- /// reference frame.
- pub ref_frame_delta: [i8; 4],
- /// Indicates the adjustment delta value corresponding to a certain MB
- /// prediction mode
- pub mb_mode_delta: [i8; 4],
-}
-
-#[derive(Clone, Default)]
-pub struct Segmentation {
- /// Enables the segmentation feature for the current frame.
- pub segmentation_enabled: bool,
- /// Determines if the MB segmentation map is updated in the current frame.
- pub update_mb_segmentation_map: bool,
- /// indicates if the segment feature data is updated in the current frame.
- pub update_segment_feature_data: bool,
-
- // If update_segment_feature_data == 1
- /// Indicates the feature data update mode, O for delta and 1 for the
- /// absolute value.
- pub segment_feature_mode: bool,
- /// Indicates if the quantizer value is updated for the izh segment.
- pub quantizer_update_value: [i8; 4],
- /// Indicates the update value for the loop filter level.
- pub lf_update_value: [i8; 4],
-
- // if update_mb_segmentation_map == 1
- /// The branch probabilities of the segment id decoding tree.
- pub segment_prob: [u8; 3],
-}
-
-#[derive(Default, Copy, Clone)]
-pub struct ModeProbs {
- /// Branch probabilities of the luma intra prediction mode decoding tree,
- /// kept live between frames.
- pub intra_16x16_prob: [u8; 4],
- /// Branch probabilities of the chroma intra prediction mode decoding tree,
- /// kept live between frames.
- pub intra_chroma_prob: [u8; 3],
-}
-
-#[derive(Clone, Default)]
-pub struct Header {
- /// Indicates if the current frame is a key frame or not.
- key_frame: bool,
- /// Determines the bitstream version.
- version: u8,
- /// Indicates if the current frame is meant to be displayed or not.
- show_frame: bool,
- /// The size in bytes of the Uncompressed Data Chunk
- data_chunk_size: u8,
- /// Determines the size of the first partition (control partition) excluding
- /// the size of the Uncompressed Data Chunk
- first_part_size: u32,
-
- /// The frame's width, in pixels.
- width: u16,
- /// The frame's height, in pixels.
- height: u16,
- /// Horizontal scale code value.
- horiz_scale_code: u8,
- /// Vertical scale code value.
- vert_scale_code: u8,
- /// Defines the YUV color space of the sequence.
- color_space: bool,
- /// Specifies if the decoder is required to clamp the reconstructed pixel
- /// values.
- clamping_type: bool,
- /// Determines whether the normal or the simple loop filter is used.
- filter_type: bool,
- /// Controls the deblocking filter.
- loop_filter_level: u8,
- /// Controls the deblocking filter.
- sharpness_level: u8,
- /// Determines the number of separate partitions containing the DCT
- /// coefficients of the macroblocks.
- log2_nbr_of_dct_partitions: u8,
-
- partition_size: [u32; 8],
-
- /// Dequantizer indices.
- quant_indices: QuantIndices,
-
- /// Determines whether updated token probabilities are used only for this
- /// frame or until further update
- refresh_entropy_probs: bool,
- /// Determines if the current decoded frame refreshes the last frame
- /// reference buffer
- refresh_last: bool,
-
- /// Determines if the current decoded frame refreshes the golden frame.
- refresh_golden_frame: bool,
- /// Determines if the current decoded frame refreshes the alternate
- /// reference frame.
- refresh_alternate_frame: bool,
- /// Determines if the golden reference is replaced by another reference.
- copy_buffer_to_golden: u8,
- /// Determines if the alternate reference is replaced by another reference.
- copy_buffer_to_alternate: u8,
- /// Controls the sign of motion vectors when the golden frame is referenced.
- sign_bias_golden: bool,
- /// Controls the sign of motion vectors when the alternate frame is
- /// referenced.
- sign_bias_alternate: bool,
-
- /// The new branch probability for the DCT/WHT tree.
- coeff_prob: [[[[u8; 11]; 3]; 8]; 4],
- /// MV decoding probability.
- mv_prob: [[u8; 19]; 2],
-
- /// Enables or disables the skipping of macroblocks containing no non-zero
- /// coefficients.
- mb_no_coeff_skip: bool,
- /// The probability that the macroblock is not skipped (flag indicating
- /// skipped macroblock is false).
- prob_skip_false: u8,
- /// The probability of an intra macroblock.
- prob_intra: u8,
- /// The probability that the last reference frame is used for inter
- /// prediction.
- prob_last: u8,
- /// The probability that the golden reference frame is used for inter
- /// prediction.
- prob_golden: u8,
- /// Branch probabilities kept live across frames.
- mode_probs: ModeProbs,
-
- /// Boolean decoder `range` for this frame.
- bd_range: usize,
- /// Boolean decoder `value` for this frame.
- bd_value: usize,
- /// Boolean decoder `count` for this frame.
- bd_count: isize,
-
- /// The size in bits of the Frame Header, thus excluding any Uncompressed
- /// Data Chunk bytes.
- header_size: u32,
-}
-
-#[allow(dead_code)]
-impl Header {
- /// Get a reference to the header's key frame.
- pub fn key_frame(&self) -> bool {
- self.key_frame
- }
-
- /// Get a reference to the header's version.
- pub fn version(&self) -> u8 {
- self.version
- }
-
- /// Get a reference to the header's show frame.
- pub fn show_frame(&self) -> bool {
- self.show_frame
- }
-
- /// Get a reference to the header's data chunk size.
- pub fn data_chunk_size(&self) -> u8 {
- self.data_chunk_size
- }
-
- /// Get a reference to the header's first part size.
- pub fn first_part_size(&self) -> u32 {
- self.first_part_size
- }
-
- /// Get a reference to the header's width.
- pub fn width(&self) -> u16 {
- self.width
- }
-
- /// Get a reference to the header's height.
- pub fn height(&self) -> u16 {
- self.height
- }
-
- /// Get a reference to the header's horiz scale code.
- pub fn horiz_scale_code(&self) -> u8 {
- self.horiz_scale_code
- }
-
- /// Get a reference to the header's vert scale code.
- pub fn vert_scale_code(&self) -> u8 {
- self.vert_scale_code
- }
-
- /// Get a reference to the header's color space.
- pub fn color_space(&self) -> bool {
- self.color_space
- }
-
- /// Get a reference to the header's clamping type.
- pub fn clamping_type(&self) -> bool {
- self.clamping_type
- }
-
- /// Get a reference to the header's filter type.
- pub fn filter_type(&self) -> bool {
- self.filter_type
- }
-
- /// Get a reference to the header's loop filter level.
- pub fn loop_filter_level(&self) -> u8 {
- self.loop_filter_level
- }
-
- /// Get a reference to the header's sharpness level.
- pub fn sharpness_level(&self) -> u8 {
- self.sharpness_level
- }
-
- /// Get a reference to the header's log2 nbr of dct partitions.
- pub fn log2_nbr_of_dct_partitions(&self) -> u8 {
- self.log2_nbr_of_dct_partitions
- }
-
- /// Get a reference to the header's partition size.
- pub fn partition_size(&self) -> [u32; 8] {
- self.partition_size
- }
-
- /// Get a reference to the header's quant indices.
- pub fn quant_indices(&self) -> &QuantIndices {
- &self.quant_indices
- }
-
- /// Get a reference to the header's refresh entropy probs.
- pub fn refresh_entropy_probs(&self) -> bool {
- self.refresh_entropy_probs
- }
-
- /// Get a reference to the header's refresh last.
- pub fn refresh_last(&self) -> bool {
- self.refresh_last
- }
-
- /// Get a reference to the header's refresh golden frame.
- pub fn refresh_golden_frame(&self) -> bool {
- self.refresh_golden_frame
- }
-
- /// Get a reference to the header's refresh alternate frame.
- pub fn refresh_alternate_frame(&self) -> bool {
- self.refresh_alternate_frame
- }
-
- /// Get a reference to the header's copy buffer to golden.
- pub fn copy_buffer_to_golden(&self) -> u8 {
- self.copy_buffer_to_golden
- }
-
- /// Get a reference to the header's copy buffer to alternate.
- pub fn copy_buffer_to_alternate(&self) -> u8 {
- self.copy_buffer_to_alternate
- }
-
- /// Get a reference to the header's sign bias golden.
- pub fn sign_bias_golden(&self) -> bool {
- self.sign_bias_golden
- }
-
- /// Get a reference to the header's sign bias alternate.
- pub fn sign_bias_alternate(&self) -> bool {
- self.sign_bias_alternate
- }
-
- /// Get a reference to the header's coeff prob.
- pub fn coeff_prob(&self) -> [[[[u8; 11]; 3]; 8]; 4] {
- self.coeff_prob
- }
-
- /// Get a reference to the header's mv prob.
- pub fn mv_prob(&self) -> [[u8; 19]; 2] {
- self.mv_prob
- }
-
- /// Get a reference to the header's mb no coeff skip.
- pub fn mb_no_coeff_skip(&self) -> bool {
- self.mb_no_coeff_skip
- }
-
- /// Get a reference to the header's prob skip false.
- pub fn prob_skip_false(&self) -> u8 {
- self.prob_skip_false
- }
-
- /// Get a reference to the header's prob intra.
- pub fn prob_intra(&self) -> u8 {
- self.prob_intra
- }
-
- /// Get a reference to the header's prob last.
- pub fn prob_last(&self) -> u8 {
- self.prob_last
- }
-
- /// Get a reference to the header's prob golden.
- pub fn prob_golden(&self) -> u8 {
- self.prob_golden
- }
-
- /// Get a reference to the header's mode probs.
- pub fn mode_probs(&self) -> ModeProbs {
- self.mode_probs
- }
-
- /// Get a reference to the header's bd range.
- pub fn bd_range(&self) -> usize {
- self.bd_range
- }
-
- /// Get a reference to the header's bd value.
- pub fn bd_value(&self) -> usize {
- self.bd_value
- }
-
- /// Get a reference to the header's bd count.
- pub fn bd_count(&self) -> isize {
- self.bd_count
- }
-
- /// Get a reference to the header's header size.
- pub fn header_size(&self) -> u32 {
- self.header_size
- }
-}
-/// A VP8 frame.
-pub struct Frame<T: AsRef<[u8]>> {
- /// The abstraction for the raw memory for this frame.
- pub bitstream: T,
- /// The frame header.
- pub header: Header,
-}
-
-impl<T: AsRef<[u8]>> Frame<T> {
- /// Creates a new frame, using the resource as its backing memory.
- fn new(bitstream: T) -> Self {
- Self {
- bitstream,
- header: Default::default(),
- }
- }
-}
-
-impl<T: AsRef<[u8]>> AsRef<[u8]> for Frame<T> {
- fn as_ref(&self) -> &[u8] {
- self.bitstream.as_ref()
- }
-}
-
-/// A VP8 parser based on GStreamer's vp8parser and Chromium's VP8 parser.
-pub struct Parser {
- /// Segmentation data kept live across frames.
- segmentation: Segmentation,
- /// MbLfAdjustments data kept live across frames.
- mb_lf_adjust: MbLfAdjustments,
- /// Coeff probabilities data kept live across frames.
- coeff_prob: [[[[u8; 11]; 3]; 8]; 4],
- /// Motion vector probabilities data kept live across frames.
- mv_prob: [[u8; 19]; 2],
- /// Branch probabilities kept live across frames.
- mode_probs: ModeProbs,
-}
-
-impl Parser {
- pub fn segmentation(&self) -> &Segmentation {
- &self.segmentation
- }
-
- pub fn mb_lf_adjust(&self) -> &MbLfAdjustments {
- &self.mb_lf_adjust
- }
-
- pub fn reset(&mut self) {
- *self = Default::default();
- }
-
- fn mode_probs_init_defaults(mode_probs: &mut ModeProbs, key_frame: bool) {
- if key_frame {
- mode_probs.intra_16x16_prob = KF_Y_MODE_PROBS;
- mode_probs.intra_chroma_prob = KF_UV_MODE_PROBS;
- } else {
- mode_probs.intra_16x16_prob = NK_Y_MODE_PROBS;
- mode_probs.intra_chroma_prob = NK_UV_MODE_PROBS;
- }
- }
-
- fn parse_uncompressed_data_chunk<T: AsRef<[u8]>>(
- &mut self,
- reader: &mut Cursor<T>,
- frame: &mut Header,
- ) -> Result<()> {
- debug!("Parsing VP8 uncompressed data chunk.");
-
- let frame_tag = reader.get_uint_le(3) as u32;
-
- frame.key_frame = (frame_tag & 0x1) == 0;
- frame.version = ((frame_tag >> 1) & 0x07) as u8;
- frame.show_frame = ((frame_tag >> 4) & 0x1) != 0;
- frame.first_part_size = (frame_tag >> 5) & 0x7ffff;
-
- if frame.key_frame {
- let start_code = reader.get_uint(3) as u32;
-
- if start_code != 0x9d012a {
- return Err(anyhow!("Invalid start code {}", start_code));
- }
-
- let size_code = reader.get_uint_le(2) as u16;
- frame.horiz_scale_code = (size_code >> 14) as u8;
- frame.width = size_code & 0x3fff;
-
- let size_code = reader.get_uint_le(2) as u16;
- frame.vert_scale_code = (size_code >> 14) as u8;
- frame.height = size_code & 0x3fff;
-
- // Reset on every key frame.
- self.reset();
- }
-
- frame.data_chunk_size = reader.position() as u8;
- Ok(())
- }
-
- fn update_segmentation<T: AsRef<[u8]>>(
- bd: &mut BoolDecoder<T>,
- seg: &mut Segmentation,
- ) -> Result<()> {
- seg.update_mb_segmentation_map = false;
- seg.update_segment_feature_data = false;
-
- seg.segmentation_enabled = bd.read_bool()?;
- if !seg.segmentation_enabled {
- return Ok(());
- }
-
- seg.update_mb_segmentation_map = bd.read_bool()?;
- seg.update_segment_feature_data = bd.read_bool()?;
-
- if seg.update_segment_feature_data {
- seg.segment_feature_mode = bd.read_bool()?;
-
- for value in seg.quantizer_update_value.iter_mut() {
- let update = bd.read_bool()?;
- if update {
- *value = bd.read_sint(7)?;
- } else {
- // quantizer_update_value defaults to zero if update flag is
- // zero (Section 9.3, 4.b)
- *value = 0;
- }
- }
-
- for value in seg.lf_update_value.iter_mut() {
- let update = bd.read_bool()?;
- if update {
- *value = bd.read_sint(6)?;
- } else {
- // lf_update_value defaults to zero if update flag is
- // zero (Section 9.3, 4.b)
- *value = 0;
- }
- }
-
- if seg.update_mb_segmentation_map {
- for value in seg.segment_prob.iter_mut() {
- let update = bd.read_bool()?;
- if update {
- *value = bd.read_uint(8)?;
- } else {
- // segment_prob defaults to 255 if update flag is
- // zero (Section 9.3, 5)
- *value = 255;
- }
- }
- }
- }
-
- Ok(())
- }
-
- fn parse_mb_lf_adjustments<T: AsRef<[u8]>>(
- bd: &mut BoolDecoder<T>,
- adj: &mut MbLfAdjustments,
- ) -> Result<()> {
- adj.mode_ref_lf_delta_update = false;
-
- adj.loop_filter_adj_enable = bd.read_bool()?;
- if !adj.loop_filter_adj_enable {
- return Ok(());
- }
-
- adj.mode_ref_lf_delta_update = bd.read_bool()?;
- if !adj.mode_ref_lf_delta_update {
- return Ok(());
- }
-
- for value in adj.ref_frame_delta.iter_mut() {
- let update = bd.read_bool()?;
- if update {
- *value = bd.read_sint(6)?;
- }
- }
-
- for value in adj.mb_mode_delta.iter_mut() {
- let update = bd.read_bool()?;
- if update {
- *value = bd.read_sint(6)?;
- }
- }
-
- Ok(())
- }
-
- fn parse_quant_indices<T: AsRef<[u8]>>(
- bd: &mut BoolDecoder<T>,
- q: &mut QuantIndices,
- ) -> Result<()> {
- q.y_ac_qi = bd.read_uint(7)?;
-
- let y_dc_delta_present = bd.read_bool()?;
-
- if y_dc_delta_present {
- q.y_dc_delta = bd.read_sint(4)?;
- } else {
- q.y_dc_delta = 0;
- }
-
- let y2_dc_delta_present = bd.read_bool()?;
- if y2_dc_delta_present {
- q.y2_dc_delta = bd.read_sint(4)?;
- } else {
- q.y2_dc_delta = 0;
- }
-
- let y2_ac_delta_present = bd.read_bool()?;
- if y2_ac_delta_present {
- q.y2_ac_delta = bd.read_sint(4)?;
- } else {
- q.y2_ac_delta = 0;
- }
-
- let uv_dc_delta_present = bd.read_bool()?;
- if uv_dc_delta_present {
- q.uv_dc_delta = bd.read_sint(4)?;
- } else {
- q.uv_dc_delta = 0;
- }
-
- let uv_ac_delta_present = bd.read_bool()?;
- if uv_ac_delta_present {
- q.uv_ac_delta = bd.read_sint(4)?;
- } else {
- q.uv_ac_delta = 0;
- }
-
- Ok(())
- }
-
- fn parse_token_prob_update<T: AsRef<[u8]>>(
- bd: &mut BoolDecoder<T>,
- coeff_probs: &mut [[[[u8; 11]; 3]; 8]; 4],
- ) -> Result<()> {
- for (i, vi) in coeff_probs.iter_mut().enumerate() {
- for (j, vj) in vi.iter_mut().enumerate() {
- for (k, vk) in vj.iter_mut().enumerate() {
- for (l, prob) in vk.iter_mut().enumerate() {
- let update = bd.read_bool_with_prob(COEFF_UPDATE_PROBS[i][j][k][l])?;
- if update {
- *prob = bd.read_uint(8)?;
- }
- }
- }
- }
- }
-
- Ok(())
- }
-
- fn parse_mv_prob_update<T: AsRef<[u8]>>(
- bd: &mut BoolDecoder<T>,
- mv_probs: &mut [[u8; 19]; 2],
- ) -> Result<()> {
- for (i, vi) in mv_probs.iter_mut().enumerate() {
- for (j, prob) in vi.iter_mut().enumerate() {
- let update = bd.read_bool_with_prob(MV_UPDATE_PROBS[i][j])?;
- if update {
- let mv_prob_update = bd.read_uint::<u8>(7)?;
-
- if mv_prob_update > 0 {
- *prob = mv_prob_update << 1;
- } else {
- *prob = 1;
- }
- }
- }
- }
-
- Ok(())
- }
-
- fn parse_frame_header(&mut self, data: &[u8], frame: &mut Header) -> Result<()> {
- debug!("Parsing VP8 frame header.");
- let mut bd = BoolDecoder::new(data);
-
- if frame.key_frame {
- frame.color_space = bd.read_bool()?;
- frame.clamping_type = bd.read_bool()?;
- }
-
- Parser::update_segmentation(&mut bd, &mut self.segmentation)?;
-
- frame.filter_type = bd.read_bool()?;
- frame.loop_filter_level = bd.read_uint(6)?;
- frame.sharpness_level = bd.read_uint(3)?;
-
- Parser::parse_mb_lf_adjustments(&mut bd, &mut self.mb_lf_adjust)?;
-
- frame.log2_nbr_of_dct_partitions = bd.read_uint(2)?;
-
- Parser::parse_quant_indices(&mut bd, &mut frame.quant_indices)?;
-
- frame.copy_buffer_to_golden = 0;
- frame.copy_buffer_to_alternate = 0;
-
- if frame.key_frame {
- frame.refresh_entropy_probs = bd.read_bool()?;
-
- frame.refresh_last = true;
- frame.refresh_golden_frame = true;
- frame.refresh_alternate_frame = true;
-
- Parser::mode_probs_init_defaults(&mut frame.mode_probs, true);
- } else {
- frame.refresh_golden_frame = bd.read_bool()?;
- frame.refresh_alternate_frame = bd.read_bool()?;
-
- if !frame.refresh_golden_frame {
- frame.copy_buffer_to_golden = bd.read_uint(2)?;
- }
-
- if !frame.refresh_alternate_frame {
- frame.copy_buffer_to_alternate = bd.read_uint(2)?;
- }
-
- frame.sign_bias_golden = bd.read_bool()?;
- frame.sign_bias_alternate = bd.read_bool()?;
- frame.refresh_entropy_probs = bd.read_bool()?;
- frame.refresh_last = bd.read_bool()?;
-
- frame.mode_probs = self.mode_probs;
- }
-
- frame.coeff_prob = self.coeff_prob;
- frame.mv_prob = self.mv_prob;
-
- Parser::parse_token_prob_update(&mut bd, &mut frame.coeff_prob)?;
-
- frame.mb_no_coeff_skip = bd.read_bool()?;
- if frame.mb_no_coeff_skip {
- frame.prob_skip_false = bd.read_uint(8)?;
- }
-
- if !frame.key_frame {
- frame.prob_intra = bd.read_uint(8)?;
- frame.prob_last = bd.read_uint(8)?;
- frame.prob_golden = bd.read_uint(8)?;
-
- let intra_16x16_prob_update_flag = bd.read_bool()?;
- if intra_16x16_prob_update_flag {
- for prob in frame.mode_probs.intra_16x16_prob.iter_mut() {
- *prob = bd.read_uint(8)?;
- }
- }
-
- let intra_chroma_prob_update_flag = bd.read_bool()?;
- if intra_chroma_prob_update_flag {
- for prob in frame.mode_probs.intra_chroma_prob.iter_mut() {
- *prob = bd.read_uint(8)?;
- }
- }
-
- Parser::parse_mv_prob_update(&mut bd, &mut frame.mv_prob)?;
- }
-
- if frame.refresh_entropy_probs {
- self.coeff_prob = frame.coeff_prob;
- self.mv_prob = frame.mv_prob;
-
- if !frame.key_frame {
- self.mode_probs = frame.mode_probs;
- }
- }
-
- frame.header_size = bd.pos() as u32;
- Parser::sync_bd_state(&bd, frame);
-
- Ok(())
- }
-
- fn compute_partition_sizes(frame: &mut Header, data: &[u8]) -> Result<()> {
- let num_partitions = usize::try_from(1 << frame.log2_nbr_of_dct_partitions)?;
- let mut part_size_ofs = usize::try_from(frame.first_part_size)?;
- let mut ofs = part_size_ofs + 3 * (num_partitions - 1);
-
- if ofs > data.len() {
- return Err(anyhow!("Not enough bytes left to parse partition sizes.",));
- }
-
- for i in 0..num_partitions - 1 {
- let b0 = u32::from(data[part_size_ofs + 0]);
- let b1 = u32::from(data[part_size_ofs + 1]) << 8;
- let b2 = u32::from(data[part_size_ofs + 2]) << 16;
-
- let part_size = usize::try_from(b0 | b1 | b2)?;
- part_size_ofs += 3;
-
- frame.partition_size[i] = u32::try_from(part_size)?;
- ofs += part_size;
- }
-
- if ofs > data.len() {
- return Err(anyhow!(
- "Not enough bytes left to determine the last partition size",
- ));
- }
-
- frame.partition_size[num_partitions - 1] = u32::try_from(data.len() - ofs)?;
- Ok(())
- }
-
- fn sync_bd_state<T: AsRef<[u8]>>(bd: &BoolDecoder<T>, frame: &mut Header) {
- frame.bd_range = bd.range();
- frame.bd_value = bd.value();
- frame.bd_count = bd.count();
- }
-
- /// Parse a single frame from the chunk in `data`.
- pub fn parse_frame<T: AsRef<[u8]>>(&mut self, resource: T) -> Result<Frame<T>> {
- let mut frame = Frame::new(resource);
- let mut reader = Cursor::new(frame.bitstream.as_ref());
-
- self.parse_uncompressed_data_chunk(&mut reader, &mut frame.header)?;
-
- let data = frame.bitstream.as_ref();
-
- if usize::from(frame.header.data_chunk_size)
- + usize::try_from(frame.header.first_part_size)?
- > data.len()
- {
- return Err(anyhow!("Broken data"));
- }
-
- let data = &data[frame.header.data_chunk_size as usize..];
-
- self.parse_frame_header(data, &mut frame.header)?;
- Parser::compute_partition_sizes(&mut frame.header, data)?;
-
- Ok(frame)
- }
-}
-
-impl Default for Parser {
- fn default() -> Self {
- Self {
- segmentation: Default::default(),
- mb_lf_adjust: Default::default(),
- coeff_prob: COEFF_DEFAULT_PROBS,
- mv_prob: MV_DEFAULT_PROBS,
- mode_probs: ModeProbs {
- intra_16x16_prob: NK_Y_MODE_PROBS,
- intra_chroma_prob: NK_UV_MODE_PROBS,
- },
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::Parser;
-
- // Test and test data extracted from GStreamer
- // subprojects/gst-plugins-bad/tests/check/libs/vp8parser.c
- const VP8_TEST_0_INTRA: &[u8] = include_bytes!("vp8-parser-test-0-intra.bin");
- const VP8_TEST_0_INTER: &[u8] = include_bytes!("vp8-parser-test-0-inter.bin");
-
- #[test]
- fn gst_vp8parser_test_intra() {
- let mut parser = Parser::default();
- let frame = parser
- .parse_frame(VP8_TEST_0_INTRA)
- .expect("Parsing a intra frame failed");
-
- assert!(frame.header.key_frame);
-
- assert_eq!(frame.header.first_part_size, 234);
- assert_eq!(frame.header.width, 176);
- assert_eq!(frame.header.height, 144);
-
- assert!(parser.mb_lf_adjust.loop_filter_adj_enable);
- assert!(parser.mb_lf_adjust.mode_ref_lf_delta_update);
-
- assert_eq!(parser.mb_lf_adjust.ref_frame_delta[0], 2);
- assert_eq!(parser.mb_lf_adjust.ref_frame_delta[1], 0);
- assert_eq!(parser.mb_lf_adjust.ref_frame_delta[2], -2);
- assert_eq!(parser.mb_lf_adjust.ref_frame_delta[3], -2);
-
- assert_eq!(parser.mb_lf_adjust.mb_mode_delta[0], 4);
- assert_eq!(parser.mb_lf_adjust.mb_mode_delta[1], -2);
- assert_eq!(parser.mb_lf_adjust.mb_mode_delta[2], 2);
- assert_eq!(parser.mb_lf_adjust.mb_mode_delta[3], 4);
-
- assert_eq!(frame.header.quant_indices.y_ac_qi, 4);
- assert!(frame.header.mb_no_coeff_skip);
-
- assert_eq!(frame.header.bd_range, 0xe8);
- assert_eq!(frame.header.bd_value, 0x68);
- assert_eq!(frame.header.bd_count, 1);
- }
-
- #[test]
- fn gst_vp8parser_test_inter() {
- let mut parser = Parser::default();
- let frame = parser
- .parse_frame(VP8_TEST_0_INTER)
- .expect("Parsing a inter frame failed");
-
- assert!(!frame.header.key_frame);
-
- assert_eq!(frame.header.first_part_size, 98);
- assert!(parser.mb_lf_adjust.loop_filter_adj_enable);
- assert_eq!(frame.header.quant_indices.y_ac_qi, 4);
-
- assert!(frame.header.refresh_entropy_probs);
- assert!(frame.header.refresh_last);
- assert!(frame.header.mb_no_coeff_skip);
-
- assert_eq!(frame.header.prob_skip_false, 131);
- assert_eq!(frame.header.prob_intra, 224);
- assert_eq!(frame.header.prob_last, 233);
- assert_eq!(frame.header.prob_golden, 1);
-
- assert_eq!(frame.header.bd_range, 0x8e);
- assert_eq!(frame.header.bd_value, 0x85);
- assert_eq!(frame.header.bd_count, 5);
- }
-}
diff --git a/media/vp8/src/probs.rs b/media/vp8/src/probs.rs
deleted file mode 100644
index d37b18b..0000000
--- a/media/vp8/src/probs.rs
+++ /dev/null
@@ -1,376 +0,0 @@
-// Copyright 2022 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/// VP8 probability tables as per the reference software and specification.
-
-pub const MV_UPDATE_PROBS: [[u8; 19]; 2] = [
- [
- 237, 246, 253, 253, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 250, 250, 252, 254,
- 254,
- ],
- [
- 231, 243, 245, 253, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 251, 251, 254, 254,
- 254,
- ],
-];
-
-pub const MV_DEFAULT_PROBS: [[u8; 19]; 2] = [
- [
- 162, 128, 225, 146, 172, 147, 214, 39, 156, 128, 129, 132, 75, 145, 178, 206, 239, 254, 254,
- ],
- [
- 164, 128, 204, 170, 119, 235, 140, 230, 228, 128, 130, 130, 74, 148, 180, 203, 236, 254,
- 254,
- ],
-];
-
-pub const NK_Y_MODE_PROBS: [u8; 4] = [112, 86, 140, 37];
-
-pub const KF_Y_MODE_PROBS: [u8; 4] = [145, 156, 163, 128];
-
-pub const NK_UV_MODE_PROBS: [u8; 3] = [162, 101, 204];
-
-pub const KF_UV_MODE_PROBS: [u8; 3] = [142, 114, 183];
-
-pub const COEFF_UPDATE_PROBS: [[[[u8; 11]; 3]; 8]; 4] = [
- [
- [
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- ],
- [
- [176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255],
- [249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255],
- ],
- [
- [255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255],
- [234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255],
- [253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- ],
- [
- [255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255],
- [239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255],
- [254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255],
- ],
- [
- [255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255],
- [251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255],
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- ],
- [
- [255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255],
- [251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255],
- [254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255],
- ],
- [
- [255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255],
- [250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255],
- [254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- ],
- [
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- ],
- ],
- [
- [
- [217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255],
- [234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255],
- ],
- [
- [255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255],
- [238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255],
- ],
- [
- [255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255],
- [249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- ],
- [
- [255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- ],
- [
- [255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255],
- [252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- ],
- [
- [255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255],
- [253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- ],
- [
- [255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255],
- [250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- ],
- [
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- ],
- ],
- [
- [
- [186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255],
- [234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255],
- [251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255],
- ],
- [
- [255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255],
- [236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255],
- [251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255],
- ],
- [
- [255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255],
- [254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255],
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- ],
- [
- [255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- ],
- [
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- ],
- [
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- ],
- [
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- ],
- [
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- ],
- ],
- [
- [
- [248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255],
- [248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255],
- ],
- [
- [255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255],
- [246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255],
- [252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255],
- ],
- [
- [255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255],
- [248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255],
- [253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255],
- ],
- [
- [255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255],
- [245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255],
- [253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255],
- ],
- [
- [255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255],
- [252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255],
- [255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- ],
- [
- [255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255],
- [255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255],
- ],
- [
- [255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255],
- [250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- ],
- [
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
- ],
- ],
-];
-
-pub const COEFF_DEFAULT_PROBS: [[[[u8; 11]; 3]; 8]; 4] = [
- [
- [
- [128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128],
- [128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128],
- [128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128],
- ],
- [
- [253, 136, 254, 255, 228, 219, 128, 128, 128, 128, 128],
- [189, 129, 242, 255, 227, 213, 255, 219, 128, 128, 128],
- [106, 126, 227, 252, 214, 209, 255, 255, 128, 128, 128],
- ],
- [
- [1, 98, 248, 255, 236, 226, 255, 255, 128, 128, 128],
- [181, 133, 238, 254, 221, 234, 255, 154, 128, 128, 128],
- [78, 134, 202, 247, 198, 180, 255, 219, 128, 128, 128],
- ],
- [
- [1, 185, 249, 255, 243, 255, 128, 128, 128, 128, 128],
- [184, 150, 247, 255, 236, 224, 128, 128, 128, 128, 128],
- [77, 110, 216, 255, 236, 230, 128, 128, 128, 128, 128],
- ],
- [
- [1, 101, 251, 255, 241, 255, 128, 128, 128, 128, 128],
- [170, 139, 241, 252, 236, 209, 255, 255, 128, 128, 128],
- [37, 116, 196, 243, 228, 255, 255, 255, 128, 128, 128],
- ],
- [
- [1, 204, 254, 255, 245, 255, 128, 128, 128, 128, 128],
- [207, 160, 250, 255, 238, 128, 128, 128, 128, 128, 128],
- [102, 103, 231, 255, 211, 171, 128, 128, 128, 128, 128],
- ],
- [
- [1, 152, 252, 255, 240, 255, 128, 128, 128, 128, 128],
- [177, 135, 243, 255, 234, 225, 128, 128, 128, 128, 128],
- [80, 129, 211, 255, 194, 224, 128, 128, 128, 128, 128],
- ],
- [
- [1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128],
- [246, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128],
- [255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128],
- ],
- ],
- [
- [
- [198, 35, 237, 223, 193, 187, 162, 160, 145, 155, 62],
- [131, 45, 198, 221, 172, 176, 220, 157, 252, 221, 1],
- [68, 47, 146, 208, 149, 167, 221, 162, 255, 223, 128],
- ],
- [
- [1, 149, 241, 255, 221, 224, 255, 255, 128, 128, 128],
- [184, 141, 234, 253, 222, 220, 255, 199, 128, 128, 128],
- [81, 99, 181, 242, 176, 190, 249, 202, 255, 255, 128],
- ],
- [
- [1, 129, 232, 253, 214, 197, 242, 196, 255, 255, 128],
- [99, 121, 210, 250, 201, 198, 255, 202, 128, 128, 128],
- [23, 91, 163, 242, 170, 187, 247, 210, 255, 255, 128],
- ],
- [
- [1, 200, 246, 255, 234, 255, 128, 128, 128, 128, 128],
- [109, 178, 241, 255, 231, 245, 255, 255, 128, 128, 128],
- [44, 130, 201, 253, 205, 192, 255, 255, 128, 128, 128],
- ],
- [
- [1, 132, 239, 251, 219, 209, 255, 165, 128, 128, 128],
- [94, 136, 225, 251, 218, 190, 255, 255, 128, 128, 128],
- [22, 100, 174, 245, 186, 161, 255, 199, 128, 128, 128],
- ],
- [
- [1, 182, 249, 255, 232, 235, 128, 128, 128, 128, 128],
- [124, 143, 241, 255, 227, 234, 128, 128, 128, 128, 128],
- [35, 77, 181, 251, 193, 211, 255, 205, 128, 128, 128],
- ],
- [
- [1, 157, 247, 255, 236, 231, 255, 255, 128, 128, 128],
- [121, 141, 235, 255, 225, 227, 255, 255, 128, 128, 128],
- [45, 99, 188, 251, 195, 217, 255, 224, 128, 128, 128],
- ],
- [
- [1, 1, 251, 255, 213, 255, 128, 128, 128, 128, 128],
- [203, 1, 248, 255, 255, 128, 128, 128, 128, 128, 128],
- [137, 1, 177, 255, 224, 255, 128, 128, 128, 128, 128],
- ],
- ],
- [
- [
- [253, 9, 248, 251, 207, 208, 255, 192, 128, 128, 128],
- [175, 13, 224, 243, 193, 185, 249, 198, 255, 255, 128],
- [73, 17, 171, 221, 161, 179, 236, 167, 255, 234, 128],
- ],
- [
- [1, 95, 247, 253, 212, 183, 255, 255, 128, 128, 128],
- [239, 90, 244, 250, 211, 209, 255, 255, 128, 128, 128],
- [155, 77, 195, 248, 188, 195, 255, 255, 128, 128, 128],
- ],
- [
- [1, 24, 239, 251, 218, 219, 255, 205, 128, 128, 128],
- [201, 51, 219, 255, 196, 186, 128, 128, 128, 128, 128],
- [69, 46, 190, 239, 201, 218, 255, 228, 128, 128, 128],
- ],
- [
- [1, 191, 251, 255, 255, 128, 128, 128, 128, 128, 128],
- [223, 165, 249, 255, 213, 255, 128, 128, 128, 128, 128],
- [141, 124, 248, 255, 255, 128, 128, 128, 128, 128, 128],
- ],
- [
- [1, 16, 248, 255, 255, 128, 128, 128, 128, 128, 128],
- [190, 36, 230, 255, 236, 255, 128, 128, 128, 128, 128],
- [149, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128],
- ],
- [
- [1, 226, 255, 128, 128, 128, 128, 128, 128, 128, 128],
- [247, 192, 255, 128, 128, 128, 128, 128, 128, 128, 128],
- [240, 128, 255, 128, 128, 128, 128, 128, 128, 128, 128],
- ],
- [
- [1, 134, 252, 255, 255, 128, 128, 128, 128, 128, 128],
- [213, 62, 250, 255, 255, 128, 128, 128, 128, 128, 128],
- [55, 93, 255, 128, 128, 128, 128, 128, 128, 128, 128],
- ],
- [
- [128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128],
- [128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128],
- [128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128],
- ],
- ],
- [
- [
- [202, 24, 213, 235, 186, 191, 220, 160, 240, 175, 255],
- [126, 38, 182, 232, 169, 184, 228, 174, 255, 187, 128],
- [61, 46, 138, 219, 151, 178, 240, 170, 255, 216, 128],
- ],
- [
- [1, 112, 230, 250, 199, 191, 247, 159, 255, 255, 128],
- [166, 109, 228, 252, 211, 215, 255, 174, 128, 128, 128],
- [39, 77, 162, 232, 172, 180, 245, 178, 255, 255, 128],
- ],
- [
- [1, 52, 220, 246, 198, 199, 249, 220, 255, 255, 128],
- [124, 74, 191, 243, 183, 193, 250, 221, 255, 255, 128],
- [24, 71, 130, 219, 154, 170, 243, 182, 255, 255, 128],
- ],
- [
- [1, 182, 225, 249, 219, 240, 255, 224, 128, 128, 128],
- [149, 150, 226, 252, 216, 205, 255, 171, 128, 128, 128],
- [28, 108, 170, 242, 183, 194, 254, 223, 255, 255, 128],
- ],
- [
- [1, 81, 230, 252, 204, 203, 255, 192, 128, 128, 128],
- [123, 102, 209, 247, 188, 196, 255, 233, 128, 128, 128],
- [20, 95, 153, 243, 164, 173, 255, 203, 128, 128, 128],
- ],
- [
- [1, 222, 248, 255, 216, 213, 128, 128, 128, 128, 128],
- [168, 175, 246, 252, 235, 205, 255, 255, 128, 128, 128],
- [47, 116, 215, 255, 211, 212, 255, 255, 128, 128, 128],
- ],
- [
- [1, 121, 236, 253, 212, 214, 255, 255, 128, 128, 128],
- [141, 84, 213, 252, 201, 202, 255, 219, 128, 128, 128],
- [42, 80, 160, 240, 162, 185, 255, 205, 128, 128, 128],
- ],
- [
- [1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128],
- [244, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128],
- [238, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128],
- ],
- ],
-];
diff --git a/media/vp8/src/vp8-parser-test-0-inter.bin b/media/vp8/src/vp8-parser-test-0-inter.bin
deleted file mode 100644
index 6fce364..0000000
--- a/media/vp8/src/vp8-parser-test-0-inter.bin
+++ /dev/null
Binary files differ
diff --git a/media/vp8/src/vp8-parser-test-0-intra.bin b/media/vp8/src/vp8-parser-test-0-intra.bin
deleted file mode 100644
index 6bc44d1..0000000
--- a/media/vp8/src/vp8-parser-test-0-intra.bin
+++ /dev/null
Binary files differ