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>
This commit is contained in:
Daniel Almeida 2022-09-05 09:21:09 -03:00 committed by crosvm LUCI
parent 42bdf1de57
commit 3399026503
20 changed files with 234 additions and 3430 deletions

11
Cargo.lock generated
View file

@ -637,6 +637,7 @@ dependencies = [
"cfg-if",
"chrono",
"crc32fast",
"cros-codecs",
"cros_async",
"cros_tracing",
"crosvm_cli",
@ -690,7 +691,6 @@ dependencies = [
"vm_control",
"vm_memory",
"vmm_vhost",
"vp8",
"win_util",
"winapi",
]
@ -2086,15 +2086,6 @@ dependencies = [
"tube_transporter",
]
[[package]]
name = "vp8"
version = "0.1.0"
dependencies = [
"anyhow",
"bytes",
"log",
]
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"

View file

@ -72,7 +72,7 @@ members = [
"media/ffmpeg",
"media/libva",
"media/libvda",
"media/vp8",
"media/cros-codecs",
"net_sys",
"net_util",
"power_monitor",

View file

@ -15,7 +15,7 @@ gpu = ["gpu_display"]
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 @@ cfg-if = "1.0.0"
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 @@ vmm_vhost = { path = "../third_party/vmm_vhost", features = ["vmm", "device", "v
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" }

View file

@ -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::anyhow;
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::resource::GuestResourceHandle;
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 @@ pub struct DrcParams {
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 @@ impl TryFrom<libva::VAProfile::Type> for Profile {
}
}
/// 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 @@ pub struct Resolution {
/// 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<T: Borrow<U>, U: BufferHandle> AsMut<[u8]> for BufferMapping<T, U> {
}
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 @@ impl VaapiDecoderSession {
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 @@ impl VaapiDecoderSession {
}
};
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 @@ impl VaapiDecoderSession {
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 @@ impl VaapiDecoderSession {
}
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 @@ impl VaapiDecoderSession {
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 VaapiDecoderSession {
}
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 @@ impl DecoderSession for VaapiDecoderSession {
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 @@ impl DecoderSession for VaapiDecoderSession {
))
})?;
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 @@ impl DecoderSession for VaapiDecoderSession {
))
})?;
Err(VideoError::BackendFailure(e))
Err(VideoError::BackendFailure(anyhow!(e)))
}
}
}
@ -928,7 +727,11 @@ impl DecoderSession for VaapiDecoderSession {
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 @@ impl DecoderSession for VaapiDecoderSession {
})?;
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 @@ impl DecoderBackend for VaapiDecoder {
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 @@ impl DecoderBackend for VaapiDecoder {
}
}
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.

View file

@ -1,2 +0,0 @@
€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€עˆ<EFBFBD>ה<EFBFBD>€€€€€½<EFBFBD>ע<EFBFBD>דױ<EFBFBD><EFBFBD>€€€j~I3ײׁ<01>€€€<62>לג<D79C><D792>€€€µ…מ<D79E>ך<EFBFBD><D79A>€€€N†ֺ ֶ´I<C2B4>€€€¹ש<C2B9>ף<EFBFBD>€€€€€¸ק<E28093>לא€€€€€Mn״לז€€€€€e<><65>ס<EFBFBD>€€€€€×ס<E280B9>לׁ<D79C><D781>€€€%tִףה<D7A3><01>€€€ּ<D6BC>ץ<EFBFBD>€€€€€ ת<C2A0>מ€€€€€€fg`׃«€€€€€˜ֻנ<E28099>€€€€€8‡«£ךב€€€€€.Fֲא€ל€€<01>€€€€€€€€f€€€€€€€€%€€€€€€€€€€ֶ#ם<>ֱ»¢ >ƒ-ֶ<>¬°<C2AC><C2B0><EFBFBD><EFBFBD>D/׀•§<E280A2>¢<EFBFBD><C2A2>ס<E280A2><D7A1>א<EFBFBD><D790>€€€¸<E282AC>ך<D79A><E2808E><EFBFBD>ַ€€€Qcµע°¾שֺ<D7A9><D6BA><01>טײֵעִ<D7A2><D6B4>€cyׂתֶֹ<D6B9>ֺ€€€[£ע×»קׂ<D7A7><D782>ָצ<D6B8>ך<EFBFBD>€€€€€m²ס<C2B2>חץ<D797><D7A5>€€€,ְֹֽ<D6BD><D6B0>€€€ן<E2809E><D79F>ׁ<EFBFBD>¥€€€^ˆב<CB86><D791>¾<EFBFBD><C2BE>€€€d®ץ÷¡<C3B7>ַ€€€¶ש<C2B6>טכ€€€€€|<7C>ס<EFBFBD>דך€€€€€#Mµ<4D>ֱ׃<D6B1>ֽ€€€<01>ק<EFBFBD>לח<D79C><D797>€€€y<E282AC>כ<EFBFBD>בד<D791><D793>€€€-c¼Uֳ<55>א€€€<01><>ױ<EFBFBD>€€€€€ֻר<><D7A8>€€€€€€‰±<>א<EFBFBD>€€€€€ ר<>ֿ׀<D6BF>ְ€€€¯ ^+ֱ¹€ֶ3<D6B6>€I« ¡³=§ך€װ·<D7B0><C2B7>€€€OZפת׃ׁ<D783><D781>€€€M`¼ֳ<01>ן<><D79F><EFBFBD><EFBFBD>ֽ€€€%3<><33>ִ÷€€€€€E.¾ןֹ<D79F><D6B9>ה€€€¿<><C2BF><EFBFBD>€€€€€€/¥ש<C2A5>ױ<EFBFBD>€€€€€<E282AC><><D7A8>€€€€€€n<01>€€€€¾$זל<>€€€€€•<01>€€€€€€€€ג<>€€€€€€€€קְ<D7A7>€€€€€€€€€€€€€€€€<><E280A0><EFBFBD>€€€€€€[‰ת<E280B0><D7AA>€€€€€€7]<5D>€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€וױ÷¿ נ¯]&_(©¸8®<»€:F—²4ר€€p‰Kַ¿E<C2BF>³€Qmb׃׳D®€€€
21:¬´$²<>ֳ4<>Aֶַ.<2E>Br€AJ<41>Y·ֱ$<24>¨<EFBFBD>G9><3E>×0s־sגױ£X<C2A3>נא€€€HF״ֽ(«€€€52Fuz*„צ<C397>Q\#ְֻּ€€ז>R2¼ִP€€װ$$8₪­;b®€ס<>t״ױ,€ז€€Gz<47>כֽ39€€€'=ˆװ<1B>´y”g®ײ†ץ€<D7A5>6T}{<7B>ֺ.fס<D7A1>/>Tƒ¹>{ן€‎—€€€€€€€€E €€€€ק€€:L€€€€‎€€

View file

@ -1,2 +0,0 @@
€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€עˆ<EFBFBD>ה<EFBFBD>€€€€€½<EFBFBD>ע<EFBFBD>דױ<EFBFBD><EFBFBD>€€€j~I3ײׁ<01>€€€<62>לג<D79C><D792>€€€µ…מ<D79E>ך<EFBFBD><D79A>€€€N†ֺ ֶ´I<C2B4>€€€¹ש<C2B9>ף<EFBFBD>€€€€€¸ק<E28093>לא€€€€€Mn״לז€€€€€e<><65>ס<EFBFBD>€€€€€×ס<E280B9>לׁ<D79C><D781>€€€%tִףה<D7A3><01>€€€ּ<D6BC>ץ<EFBFBD>€€€€€ ת<C2A0>מ€€€€€€fg`׃«€€€€€˜ֻנ<E28099>€€€€€8‡«£ךב€€€€€.Fֲא€ל€€<01>€€€€€€€€f€€€€€€€€%€€€€€€€€€€ֶ#ם<>ֱ»¢ >ƒ-ֶ<>¬°<C2AC><C2B0><EFBFBD><EFBFBD>D/׀•§<E280A2>¢<EFBFBD><C2A2>ס<E280A2><D7A1>א<EFBFBD><D790>€€€¸<E282AC>ך<D79A><E2808E><EFBFBD>ַ€€€Qcµע°¾שֺ<D7A9><D6BA><01>טײֵעִ<D7A2><D6B4>€cyׂתֶֹ<D6B9>ֺ€€€[£ע×»קׂ<D7A7><D782>ָצ<D6B8>ך<EFBFBD>€€€€€m²ס<C2B2>חץ<D797><D7A5>€€€,ְֹֽ<D6BD><D6B0>€€€ן<E2809E><D79F>ׁ<EFBFBD>¥€€€^ˆב<CB86><D791>¾<EFBFBD><C2BE>€€€d®ץ÷¡<C3B7>ַ€€€¶ש<C2B6>טכ€€€€€|<7C>ס<EFBFBD>דך€€€€€#Mµ<4D>ֱ׃<D6B1>ֽ€€€<01>ק<EFBFBD>לח<D79C><D797>€€€y<E282AC>כ<EFBFBD>בד<D791><D793>€€€-c¼Uֳ<55>א€€€<01><>ױ<EFBFBD>€€€€€ֻר<><D7A8>€€€€€€‰±<>א<EFBFBD>€€€€€ ר<>ֿ׀<D6BF>ְ€€€¯ ^+ֱ¹€ֶ3<D6B6>€I« ¡³=§ך€װ·<D7B0><C2B7>€€€OZפת׃ׁ<D783><D781>€€€M`¼ֳ<01>ן<><D79F><EFBFBD><EFBFBD>ֽ€€€%3<><33>ִ÷€€€€€E.¾ןֹ<D79F><D6B9>ה€€€¿<><C2BF><EFBFBD>€€€€€€/¥ש<C2A5>ױ<EFBFBD>€€€€€<E282AC><><D7A8>€€€€€€n<01>€€€€¾$זל<>€€€€€•<01>€€€€€€€€ג<>€€€€€€€€קְ<D7A7>€€€€€€€€€€€€€€€€<><E280A0><EFBFBD>€€€€€€[‰ת<E280B0><D7AA>€€€€€€7]<5D>€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€וױ<>÷¿ נ¯]&_(©¸8®<»€:F—²4ר€€p‰Kַ¿E<C2BF>³€Qmb׃׳D®€€€
21:¬´$²<>ֳ4<>Aֶַ.<2E>Br€AJ<41>Y·ֱ$<24>¨<EFBFBD>G9><3E>×0s־sגױ£X<C2A3>נא€€€HF״ֽ(«€€€52Fuz*„צ<C397>Q\#ְֻּ€€ז>R2¼ִP€€װ$$8₪­;b®€ס<>t״ױ,€ז€€Gz<47>כֽ39€€€'=ˆװ<1B>´y<>g®ײ†ץ€<D7A5>·T}{<7B>ֺ.fס<D7A1>/>Tƒ¹>{ן€‎—€€€€€€€€E €€€€ק€€:L€€€€‎€€

View file

@ -1,2 +0,0 @@
€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€עˆ<EFBFBD>ה<EFBFBD>€€€€€½<EFBFBD>ע<EFBFBD>דױ<EFBFBD><EFBFBD>€€€j~I3ײׁ<01>€€€<62>לג<D79C><D792>€€€µ…מ<D79E>ך<EFBFBD><D79A>€€€N†ֺ ֶ´I<C2B4>€€€¹ש<C2B9>ף<EFBFBD>€€€€€¸ק<E28093>לא€€€€€Mn״לז€€€€€e<><65>ס<EFBFBD>€€€€€×ס<E280B9>לׁ<D79C><D781>€€€%tִףה<D7A3><01>€€€ּ<D6BC>ץ<EFBFBD>€€€€€ ת<C2A0>מ€€€€€€fg`׃«€€€€€˜ֻנ<E28099>€€€€€8‡«£ךב€€€€€.Fֲא€ל€€<01>€€€€€€€€f€€€€€€€€%€€€€€€€€€€ֶ#ם<>ֱ»¢ >ƒ-ֶ<>¬°<C2AC><C2B0><EFBFBD><EFBFBD>D/׀•§<E280A2>¢<EFBFBD><C2A2>ס<E280A2><D7A1>א<EFBFBD><D790>€€€¸<E282AC>ך<D79A><E2808E><EFBFBD>ַ€€€Qcµע°¾שֺ<D7A9><D6BA><01>טײֵעִ<D7A2><D6B4>€cyׂתֶֹ<D6B9>ֺ€€€[£ע×»קׂ<D7A7><D782>ָצ<D6B8>ך<EFBFBD>€€€€€m²ס<C2B2>חץ<D797><D7A5>€€€,ְֹֽ<D6BD><D6B0>€€€ן<E2809E><D79F>ׁ<EFBFBD>¥€€€^ˆב<CB86><D791>¾<EFBFBD><C2BE>€€€d®ץ÷¡<C3B7>ַ€€€¶ש<C2B6>טכ€€€€€|<7C>ס<EFBFBD>דך€€€€€#Mµ<4D>ֱ׃<D6B1>ֽ€€€<01>ק<EFBFBD>לח<D79C><D797>€€€y<E282AC>כ<EFBFBD>בד<D791><D793>€€€-c¼Uֳ<55>א€€€<01><>ױ<EFBFBD>€€€€€ֻר<><D7A8>€€€€€€‰±<>א<EFBFBD>€€€€€ ר<>ֿ׀<D6BF>ְ€€€¯ ^+ֱ¹€ֶ3<D6B6>€I« ¡³=§ך€װ·<D7B0><C2B7>€€€OZפת׃ׁ<D783><D781>€€€M`¼ֳ<01>ן<><D79F><EFBFBD><EFBFBD>ֽ€€€%3<><33>ִ÷€€€€€E.¾ןֹ<D79F><D6B9>ה€€€¿<><C2BF><EFBFBD>€€€€€€/¥ש<C2A5>ױ<EFBFBD>€€€€€<E282AC><><D7A8>€€€€€€n<01>€€€€¾$זל<>€€€€€•<01>€€€€€€€€ג<>€€€€€€€€קְ<D7A7>€€€€€€€€€€€€€€€€<><E280A0><EFBFBD>€€€€€€[‰ת<E280B0><D7AA>€€€€€€7]<5D>€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€וױ<>÷¿ נ¯]&_(©¸8®<»€:F—²4ר€€p‰Kַ¿E<C2BF>³€Qmb׃׳D®€€€
21:¬´$²<>ֳ4<>Aֶַ.<2E>Br€AJ<41>Y·ֱ$<24>¨<EFBFBD>G9><3E>×0s־sגױ£X<C2A3>נא€€€HF״ֽ(«€€€52Fuz*„צ<C397>Q\#ְֻּ€€ז>R2¼ִP€€װ$$8₪­;b®€ס<>t״ױ,€ז€€Gz<47>כֽ39€€€'=ˆװ<1B>´y<>g®ײ†ץ€<D7A5>·T}{<7B>ֺ.fס<D7A1>/>Tƒ¹>{ן€‎—€€€€€€€€E €€€€ק€€:L€€€€‎€€

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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"]}

View file

@ -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);
}
}
}
}

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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],
],
],
];