Reland "Add a VAAPI wrapper crate"

This is a reland of commit 213f9fe8a7.

In light of the upcoming VAAPI video decoder backend, add a VAAPI
wrapper crate that exposes a safe Rust API for a subset of the VAAPI C
code. This crate will be called from the VAAPI video decoder backend in
order to decode frames.

BUG=b:214478588
TEST=cargo build --features "video-decoder,vaapi"
TEST=`cargo test -- --include-ignored` in `media/libva` passes on a
device with Intel GPU and libva installed.

Change-Id: I586a160e477e466985c5cfa65a527542ddc03226
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3752274
Reviewed-by: Keiichi Watanabe <keiichiw@chromium.org>
Commit-Queue: Alexandre Courbot <acourbot@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
Daniel Almeida 2022-04-09 16:17:34 -03:00 committed by Chromeos LUCI
parent 155f7ad98a
commit 9dbb169557
23 changed files with 11147 additions and 0 deletions

View file

@ -65,6 +65,7 @@ members = [
"kvm_sys",
"linux_input_sys",
"media/ffmpeg",
"media/libva",
"media/libvda",
"net_sys",
"net_util",

15
media/libva/Cargo.toml Normal file
View file

@ -0,0 +1,15 @@
[package]
name = "libva"
version = "0.1.0"
authors = ["The Chromium OS Authors"]
edition = "2021"
[dependencies]
anyhow = "*"
bitflags = "1"
base = { path = "../../base" }
libudev = "0.2.0"
crc32fast = "1.2.1"
[build-dependencies]
pkg-config = "*"

52
media/libva/README.md Normal file
View file

@ -0,0 +1,52 @@
# Libva Rust wrapper
Rust wrapper for libva. Provides safe libva abstractions for use within Rust code using its own
bindgen-generated bindings.
This crate is used as part of the VirtIO Video VA-API backend. VA-API uses the GPU to perform the
actual decoding/encoding of frames. In doing so, it frees the CPU for other uses and reduces power
usage in the system. Hardware accelerated media workflows also tend to be faster than their software
counterparts.
Note: This create requires the native [libva](https://github.com/intel/libva) library at link time.
It also requires a VA-API driver to be installed on the system. The VA-API driver to use depends on
the underlying hardware, e.g.: the implementation for Intel hardware is in
[intel-media-driver](https://github.com/intel/media-driver), whereas AMD hardware will depend on
[Mesa](https://gitlab.freedesktop.org/mesa/mesa).
An easy way to see whether everything is in order is to run the `vainfo` utility. This is usually
packaged with `libva-utils` or as a standalone package in some distributions. `vainfo` will print
the VA-API version, the driver string, and a list of supported profiles and endpoints, i.e.:
```
vainfo: VA-API version: 1.13 (libva 2.13.0)
vainfo: Driver version: Intel iHD driver for Intel(R) Gen Graphics - 22.2.2 ()
vainfo: Supported profile and entrypoints
VAProfileNone : VAEntrypointVideoProc
VAProfileNone : VAEntrypointStats
VAProfileMPEG2Simple : VAEntrypointVLD
VAProfileMPEG2Simple : VAEntrypointEncSlice
VAProfileMPEG2Main : VAEntrypointVLD
VAProfileMPEG2Main : VAEntrypointEncSlice
VAProfileH264Main : VAEntrypointVLD
etc
```
For decoding, the desired profile must be supported under `VAEntrypointVLD`. For example, in order
to decode VP8 media, this line must be present in the output of `vainfo`:
```
VAProfileVP8Version0_3 : VAEntrypointVLD
```
Whereas to decode H264 Main profile media, this line must be present:
```
VAProfileH264Main : VAEntrypointVLD
```
For more information on VA-API and its usage within ChromeOS, see
[this guide](https://chromium.googlesource.com/chromium/src/+/master/docs/gpu/vaapi.md).
For a brief introduction on how to use this crate, see the `libva_utils_mpeg2vldemo` test under
src/lib.rs.

22
media/libva/bindgen.sh Executable file
View file

@ -0,0 +1,22 @@
#!/usr/bin/env bash
# Copyright 2022 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# Regenerate libva bindgen bindings.
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")/../.."
source tools/impl/bindgen-common.sh
bindgen_generate \
--raw-line "pub mod constants;" \
--with-derive-eq \
--constified-enum-module "VA.*" \
--allowlist-function "va.*" \
--allowlist-type ".*MPEG2.*|.*VP8.*|.*VP9.*|.*H264.*" \
"media/libva/libva-wrapper.h" \
> media/libva/src/bindings/va.rs
bindgen_generate \
--allowlist-var "VA.*" \
"media/libva/libva-wrapper.h" \
> media/libva/src/bindings/va/constants.rs

18
media/libva/build.rs Normal file
View file

@ -0,0 +1,18 @@
// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
fn main() {
// Skip installing dependencies when generating documents.
if std::env::var("CARGO_DOC").is_ok() {
return;
}
match pkg_config::probe_library("libva") {
Ok(_) => (),
Err(e) => panic!("Libva not found: {}", e),
};
println!("cargo:rustc-link-lib=dylib=va");
println!("cargo:rustc-link-lib=dylib=va-drm"); // for the vaGetDisplayDRM entrypoint
}

View file

@ -0,0 +1,6 @@
// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <va/va.h>
#include <va/va_drm.h>

View file

@ -0,0 +1,10 @@
// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! This module implements the bindgen C FFI bindings for use within this crate
#[allow(missing_docs)]
pub mod va;
pub use va::*;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,345 @@
/* automatically generated by tools/bindgen-all-the-things */
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(dead_code)]
pub const VA_MAJOR_VERSION: u32 = 1;
pub const VA_MINOR_VERSION: u32 = 14;
pub const VA_MICRO_VERSION: u32 = 0;
pub const VA_VERSION_S: &[u8; 7usize] = b"1.14.0\0";
pub const VA_VERSION_HEX: u32 = 17694720;
pub const VA_STATUS_SUCCESS: u32 = 0;
pub const VA_STATUS_ERROR_OPERATION_FAILED: u32 = 1;
pub const VA_STATUS_ERROR_ALLOCATION_FAILED: u32 = 2;
pub const VA_STATUS_ERROR_INVALID_DISPLAY: u32 = 3;
pub const VA_STATUS_ERROR_INVALID_CONFIG: u32 = 4;
pub const VA_STATUS_ERROR_INVALID_CONTEXT: u32 = 5;
pub const VA_STATUS_ERROR_INVALID_SURFACE: u32 = 6;
pub const VA_STATUS_ERROR_INVALID_BUFFER: u32 = 7;
pub const VA_STATUS_ERROR_INVALID_IMAGE: u32 = 8;
pub const VA_STATUS_ERROR_INVALID_SUBPICTURE: u32 = 9;
pub const VA_STATUS_ERROR_ATTR_NOT_SUPPORTED: u32 = 10;
pub const VA_STATUS_ERROR_MAX_NUM_EXCEEDED: u32 = 11;
pub const VA_STATUS_ERROR_UNSUPPORTED_PROFILE: u32 = 12;
pub const VA_STATUS_ERROR_UNSUPPORTED_ENTRYPOINT: u32 = 13;
pub const VA_STATUS_ERROR_UNSUPPORTED_RT_FORMAT: u32 = 14;
pub const VA_STATUS_ERROR_UNSUPPORTED_BUFFERTYPE: u32 = 15;
pub const VA_STATUS_ERROR_SURFACE_BUSY: u32 = 16;
pub const VA_STATUS_ERROR_FLAG_NOT_SUPPORTED: u32 = 17;
pub const VA_STATUS_ERROR_INVALID_PARAMETER: u32 = 18;
pub const VA_STATUS_ERROR_RESOLUTION_NOT_SUPPORTED: u32 = 19;
pub const VA_STATUS_ERROR_UNIMPLEMENTED: u32 = 20;
pub const VA_STATUS_ERROR_SURFACE_IN_DISPLAYING: u32 = 21;
pub const VA_STATUS_ERROR_INVALID_IMAGE_FORMAT: u32 = 22;
pub const VA_STATUS_ERROR_DECODING_ERROR: u32 = 23;
pub const VA_STATUS_ERROR_ENCODING_ERROR: u32 = 24;
pub const VA_STATUS_ERROR_INVALID_VALUE: u32 = 25;
pub const VA_STATUS_ERROR_UNSUPPORTED_FILTER: u32 = 32;
pub const VA_STATUS_ERROR_INVALID_FILTER_CHAIN: u32 = 33;
pub const VA_STATUS_ERROR_HW_BUSY: u32 = 34;
pub const VA_STATUS_ERROR_UNSUPPORTED_MEMORY_TYPE: u32 = 36;
pub const VA_STATUS_ERROR_NOT_ENOUGH_BUFFER: u32 = 37;
pub const VA_STATUS_ERROR_TIMEDOUT: u32 = 38;
pub const VA_STATUS_ERROR_UNKNOWN: u32 = 4294967295;
pub const VA_FRAME_PICTURE: u32 = 0;
pub const VA_TOP_FIELD: u32 = 1;
pub const VA_BOTTOM_FIELD: u32 = 2;
pub const VA_TOP_FIELD_FIRST: u32 = 4;
pub const VA_BOTTOM_FIELD_FIRST: u32 = 8;
pub const VA_ENABLE_BLEND: u32 = 4;
pub const VA_CLEAR_DRAWABLE: u32 = 8;
pub const VA_SRC_COLOR_MASK: u32 = 240;
pub const VA_SRC_BT601: u32 = 16;
pub const VA_SRC_BT709: u32 = 32;
pub const VA_SRC_SMPTE_240: u32 = 64;
pub const VA_FILTER_SCALING_DEFAULT: u32 = 0;
pub const VA_FILTER_SCALING_FAST: u32 = 256;
pub const VA_FILTER_SCALING_HQ: u32 = 512;
pub const VA_FILTER_SCALING_NL_ANAMORPHIC: u32 = 768;
pub const VA_FILTER_SCALING_MASK: u32 = 3840;
pub const VA_FILTER_INTERPOLATION_DEFAULT: u32 = 0;
pub const VA_FILTER_INTERPOLATION_NEAREST_NEIGHBOR: u32 = 4096;
pub const VA_FILTER_INTERPOLATION_BILINEAR: u32 = 8192;
pub const VA_FILTER_INTERPOLATION_ADVANCED: u32 = 12288;
pub const VA_FILTER_INTERPOLATION_MASK: u32 = 61440;
pub const VA_PADDING_LOW: u32 = 4;
pub const VA_PADDING_MEDIUM: u32 = 8;
pub const VA_PADDING_HIGH: u32 = 16;
pub const VA_PADDING_LARGE: u32 = 32;
pub const VA_EXEC_SYNC: u32 = 0;
pub const VA_EXEC_ASYNC: u32 = 1;
pub const VA_EXEC_MODE_DEFAULT: u32 = 0;
pub const VA_EXEC_MODE_POWER_SAVING: u32 = 1;
pub const VA_EXEC_MODE_PERFORMANCE: u32 = 2;
pub const VA_FEATURE_NOT_SUPPORTED: u32 = 0;
pub const VA_FEATURE_SUPPORTED: u32 = 1;
pub const VA_FEATURE_REQUIRED: u32 = 2;
pub const VA_RT_FORMAT_YUV420: u32 = 1;
pub const VA_RT_FORMAT_YUV422: u32 = 2;
pub const VA_RT_FORMAT_YUV444: u32 = 4;
pub const VA_RT_FORMAT_YUV411: u32 = 8;
pub const VA_RT_FORMAT_YUV400: u32 = 16;
pub const VA_RT_FORMAT_YUV420_10: u32 = 256;
pub const VA_RT_FORMAT_YUV422_10: u32 = 512;
pub const VA_RT_FORMAT_YUV444_10: u32 = 1024;
pub const VA_RT_FORMAT_YUV420_12: u32 = 4096;
pub const VA_RT_FORMAT_YUV422_12: u32 = 8192;
pub const VA_RT_FORMAT_YUV444_12: u32 = 16384;
pub const VA_RT_FORMAT_RGB16: u32 = 65536;
pub const VA_RT_FORMAT_RGB32: u32 = 131072;
pub const VA_RT_FORMAT_RGBP: u32 = 1048576;
pub const VA_RT_FORMAT_RGB32_10: u32 = 2097152;
pub const VA_RT_FORMAT_PROTECTED: u32 = 2147483648;
pub const VA_RT_FORMAT_RGB32_10BPP: u32 = 2097152;
pub const VA_RT_FORMAT_YUV420_10BPP: u32 = 256;
pub const VA_RC_NONE: u32 = 1;
pub const VA_RC_CBR: u32 = 2;
pub const VA_RC_VBR: u32 = 4;
pub const VA_RC_VCM: u32 = 8;
pub const VA_RC_CQP: u32 = 16;
pub const VA_RC_VBR_CONSTRAINED: u32 = 32;
pub const VA_RC_ICQ: u32 = 64;
pub const VA_RC_MB: u32 = 128;
pub const VA_RC_CFS: u32 = 256;
pub const VA_RC_PARALLEL: u32 = 512;
pub const VA_RC_QVBR: u32 = 1024;
pub const VA_RC_AVBR: u32 = 2048;
pub const VA_RC_TCBRC: u32 = 4096;
pub const VA_DEC_SLICE_MODE_NORMAL: u32 = 1;
pub const VA_DEC_SLICE_MODE_BASE: u32 = 2;
pub const VA_DEC_PROCESSING_NONE: u32 = 0;
pub const VA_DEC_PROCESSING: u32 = 1;
pub const VA_ENC_PACKED_HEADER_NONE: u32 = 0;
pub const VA_ENC_PACKED_HEADER_SEQUENCE: u32 = 1;
pub const VA_ENC_PACKED_HEADER_PICTURE: u32 = 2;
pub const VA_ENC_PACKED_HEADER_SLICE: u32 = 4;
pub const VA_ENC_PACKED_HEADER_MISC: u32 = 8;
pub const VA_ENC_PACKED_HEADER_RAW_DATA: u32 = 16;
pub const VA_ENC_INTERLACED_NONE: u32 = 0;
pub const VA_ENC_INTERLACED_FRAME: u32 = 1;
pub const VA_ENC_INTERLACED_FIELD: u32 = 2;
pub const VA_ENC_INTERLACED_MBAFF: u32 = 4;
pub const VA_ENC_INTERLACED_PAFF: u32 = 8;
pub const VA_ENC_SLICE_STRUCTURE_POWER_OF_TWO_ROWS: u32 = 1;
pub const VA_ENC_SLICE_STRUCTURE_ARBITRARY_MACROBLOCKS: u32 = 2;
pub const VA_ENC_SLICE_STRUCTURE_EQUAL_ROWS: u32 = 4;
pub const VA_ENC_SLICE_STRUCTURE_MAX_SLICE_SIZE: u32 = 8;
pub const VA_ENC_SLICE_STRUCTURE_ARBITRARY_ROWS: u32 = 16;
pub const VA_ENC_SLICE_STRUCTURE_EQUAL_MULTI_ROWS: u32 = 32;
pub const VA_ENC_QUANTIZATION_NONE: u32 = 0;
pub const VA_ENC_QUANTIZATION_TRELLIS_SUPPORTED: u32 = 1;
pub const VA_PREDICTION_DIRECTION_PREVIOUS: u32 = 1;
pub const VA_PREDICTION_DIRECTION_FUTURE: u32 = 2;
pub const VA_PREDICTION_DIRECTION_BI_NOT_EMPTY: u32 = 4;
pub const VA_ENC_INTRA_REFRESH_NONE: u32 = 0;
pub const VA_ENC_INTRA_REFRESH_ROLLING_COLUMN: u32 = 1;
pub const VA_ENC_INTRA_REFRESH_ROLLING_ROW: u32 = 2;
pub const VA_ENC_INTRA_REFRESH_ADAPTIVE: u32 = 16;
pub const VA_ENC_INTRA_REFRESH_CYCLIC: u32 = 32;
pub const VA_ENC_INTRA_REFRESH_P_FRAME: u32 = 65536;
pub const VA_ENC_INTRA_REFRESH_B_FRAME: u32 = 131072;
pub const VA_ENC_INTRA_REFRESH_MULTI_REF: u32 = 262144;
pub const VA_PC_CIPHER_AES: u32 = 1;
pub const VA_PC_BLOCK_SIZE_128: u32 = 1;
pub const VA_PC_BLOCK_SIZE_192: u32 = 2;
pub const VA_PC_BLOCK_SIZE_256: u32 = 4;
pub const VA_PC_CIPHER_MODE_ECB: u32 = 1;
pub const VA_PC_CIPHER_MODE_CBC: u32 = 2;
pub const VA_PC_CIPHER_MODE_CTR: u32 = 4;
pub const VA_PC_SAMPLE_TYPE_FULLSAMPLE: u32 = 1;
pub const VA_PC_SAMPLE_TYPE_SUBSAMPLE: u32 = 2;
pub const VA_PC_USAGE_DEFAULT: u32 = 0;
pub const VA_PC_USAGE_WIDEVINE: u32 = 1;
pub const VA_PROCESSING_RATE_NONE: u32 = 0;
pub const VA_PROCESSING_RATE_ENCODE: u32 = 1;
pub const VA_PROCESSING_RATE_DECODE: u32 = 2;
pub const VA_ATTRIB_NOT_SUPPORTED: u32 = 2147483648;
pub const VA_INVALID_ID: u32 = 4294967295;
pub const VA_INVALID_SURFACE: u32 = 4294967295;
pub const VA_SURFACE_ATTRIB_NOT_SUPPORTED: u32 = 0;
pub const VA_SURFACE_ATTRIB_GETTABLE: u32 = 1;
pub const VA_SURFACE_ATTRIB_SETTABLE: u32 = 2;
pub const VA_SURFACE_ATTRIB_MEM_TYPE_VA: u32 = 1;
pub const VA_SURFACE_ATTRIB_MEM_TYPE_V4L2: u32 = 2;
pub const VA_SURFACE_ATTRIB_MEM_TYPE_USER_PTR: u32 = 4;
pub const VA_SURFACE_EXTBUF_DESC_ENABLE_TILING: u32 = 1;
pub const VA_SURFACE_EXTBUF_DESC_CACHED: u32 = 2;
pub const VA_SURFACE_EXTBUF_DESC_UNCACHED: u32 = 4;
pub const VA_SURFACE_EXTBUF_DESC_WC: u32 = 8;
pub const VA_SURFACE_EXTBUF_DESC_PROTECTED: u32 = 2147483648;
pub const VA_SURFACE_ATTRIB_USAGE_HINT_GENERIC: u32 = 0;
pub const VA_SURFACE_ATTRIB_USAGE_HINT_DECODER: u32 = 1;
pub const VA_SURFACE_ATTRIB_USAGE_HINT_ENCODER: u32 = 2;
pub const VA_SURFACE_ATTRIB_USAGE_HINT_VPP_READ: u32 = 4;
pub const VA_SURFACE_ATTRIB_USAGE_HINT_VPP_WRITE: u32 = 8;
pub const VA_SURFACE_ATTRIB_USAGE_HINT_DISPLAY: u32 = 16;
pub const VA_SURFACE_ATTRIB_USAGE_HINT_EXPORT: u32 = 32;
pub const VA_PROGRESSIVE: u32 = 1;
pub const VA_ENCRYPTION_TYPE_FULLSAMPLE_CTR: u32 = 1;
pub const VA_ENCRYPTION_TYPE_FULLSAMPLE_CBC: u32 = 2;
pub const VA_ENCRYPTION_TYPE_SUBSAMPLE_CTR: u32 = 4;
pub const VA_ENCRYPTION_TYPE_SUBSAMPLE_CBC: u32 = 8;
pub const VA_SLICE_DATA_FLAG_ALL: u32 = 0;
pub const VA_SLICE_DATA_FLAG_BEGIN: u32 = 1;
pub const VA_SLICE_DATA_FLAG_MIDDLE: u32 = 2;
pub const VA_SLICE_DATA_FLAG_END: u32 = 4;
pub const VA_MB_TYPE_MOTION_FORWARD: u32 = 2;
pub const VA_MB_TYPE_MOTION_BACKWARD: u32 = 4;
pub const VA_MB_TYPE_MOTION_PATTERN: u32 = 8;
pub const VA_MB_TYPE_MOTION_INTRA: u32 = 16;
pub const VA_PICTURE_H264_INVALID: u32 = 1;
pub const VA_PICTURE_H264_TOP_FIELD: u32 = 2;
pub const VA_PICTURE_H264_BOTTOM_FIELD: u32 = 4;
pub const VA_PICTURE_H264_SHORT_TERM_REFERENCE: u32 = 8;
pub const VA_PICTURE_H264_LONG_TERM_REFERENCE: u32 = 16;
pub const VA_CODED_BUF_STATUS_PICTURE_AVE_QP_MASK: u32 = 255;
pub const VA_CODED_BUF_STATUS_LARGE_SLICE_MASK: u32 = 256;
pub const VA_CODED_BUF_STATUS_SLICE_OVERFLOW_MASK: u32 = 512;
pub const VA_CODED_BUF_STATUS_BITRATE_OVERFLOW: u32 = 1024;
pub const VA_CODED_BUF_STATUS_BITRATE_HIGH: u32 = 2048;
pub const VA_CODED_BUF_STATUS_FRAME_SIZE_OVERFLOW: u32 = 4096;
pub const VA_CODED_BUF_STATUS_BAD_BITSTREAM: u32 = 32768;
pub const VA_CODED_BUF_STATUS_AIR_MB_OVER_THRESHOLD: u32 = 16711680;
pub const VA_CODED_BUF_STATUS_NUMBER_PASSES_MASK: u32 = 251658240;
pub const VA_CODED_BUF_STATUS_SINGLE_NALU: u32 = 268435456;
pub const VA_EXPORT_SURFACE_READ_ONLY: u32 = 1;
pub const VA_EXPORT_SURFACE_WRITE_ONLY: u32 = 2;
pub const VA_EXPORT_SURFACE_READ_WRITE: u32 = 3;
pub const VA_EXPORT_SURFACE_SEPARATE_LAYERS: u32 = 4;
pub const VA_EXPORT_SURFACE_COMPOSED_LAYERS: u32 = 8;
pub const VA_TIMEOUT_INFINITE: i32 = -1;
pub const VA_FOURCC_NV12: u32 = 842094158;
pub const VA_FOURCC_NV21: u32 = 825382478;
pub const VA_FOURCC_AI44: u32 = 875839817;
pub const VA_FOURCC_RGBA: u32 = 1094862674;
pub const VA_FOURCC_RGBX: u32 = 1480738642;
pub const VA_FOURCC_BGRA: u32 = 1095911234;
pub const VA_FOURCC_BGRX: u32 = 1481787202;
pub const VA_FOURCC_ARGB: u32 = 1111970369;
pub const VA_FOURCC_XRGB: u32 = 1111970392;
pub const VA_FOURCC_ABGR: u32 = 1380401729;
pub const VA_FOURCC_XBGR: u32 = 1380401752;
pub const VA_FOURCC_UYVY: u32 = 1498831189;
pub const VA_FOURCC_YUY2: u32 = 844715353;
pub const VA_FOURCC_AYUV: u32 = 1448433985;
pub const VA_FOURCC_NV11: u32 = 825316942;
pub const VA_FOURCC_YV12: u32 = 842094169;
pub const VA_FOURCC_P208: u32 = 942682704;
pub const VA_FOURCC_I420: u32 = 808596553;
pub const VA_FOURCC_YV24: u32 = 875714137;
pub const VA_FOURCC_YV32: u32 = 842225241;
pub const VA_FOURCC_Y800: u32 = 808466521;
pub const VA_FOURCC_IMC3: u32 = 860048713;
pub const VA_FOURCC_411P: u32 = 1345401140;
pub const VA_FOURCC_411R: u32 = 1378955572;
pub const VA_FOURCC_422H: u32 = 1211249204;
pub const VA_FOURCC_422V: u32 = 1446130228;
pub const VA_FOURCC_444P: u32 = 1345598516;
pub const VA_FOURCC_RGBP: u32 = 1346520914;
pub const VA_FOURCC_BGRP: u32 = 1347569474;
pub const VA_FOURCC_RGB565: u32 = 909199186;
pub const VA_FOURCC_BGR565: u32 = 909199170;
pub const VA_FOURCC_Y210: u32 = 808530521;
pub const VA_FOURCC_Y212: u32 = 842084953;
pub const VA_FOURCC_Y216: u32 = 909193817;
pub const VA_FOURCC_Y410: u32 = 808531033;
pub const VA_FOURCC_Y412: u32 = 842085465;
pub const VA_FOURCC_Y416: u32 = 909194329;
pub const VA_FOURCC_YV16: u32 = 909203033;
pub const VA_FOURCC_P010: u32 = 808530000;
pub const VA_FOURCC_P012: u32 = 842084432;
pub const VA_FOURCC_P016: u32 = 909193296;
pub const VA_FOURCC_I010: u32 = 808529993;
pub const VA_FOURCC_IYUV: u32 = 1448433993;
pub const VA_FOURCC_A2R10G10B10: u32 = 808669761;
pub const VA_FOURCC_A2B10G10R10: u32 = 808665665;
pub const VA_FOURCC_X2R10G10B10: u32 = 808669784;
pub const VA_FOURCC_X2B10G10R10: u32 = 808665688;
pub const VA_FOURCC_Y8: u32 = 538982489;
pub const VA_FOURCC_Y16: u32 = 540422489;
pub const VA_FOURCC_VYUY: u32 = 1498765654;
pub const VA_FOURCC_YVYU: u32 = 1431918169;
pub const VA_FOURCC_ARGB64: u32 = 877089345;
pub const VA_FOURCC_ABGR64: u32 = 877085249;
pub const VA_FOURCC_XYUV: u32 = 1448434008;
pub const VA_LSB_FIRST: u32 = 1;
pub const VA_MSB_FIRST: u32 = 2;
pub const VA_SUBPICTURE_CHROMA_KEYING: u32 = 1;
pub const VA_SUBPICTURE_GLOBAL_ALPHA: u32 = 2;
pub const VA_SUBPICTURE_DESTINATION_IS_SCREEN_COORD: u32 = 4;
pub const VA_ROTATION_NONE: u32 = 0;
pub const VA_ROTATION_90: u32 = 1;
pub const VA_ROTATION_180: u32 = 2;
pub const VA_ROTATION_270: u32 = 3;
pub const VA_MIRROR_NONE: u32 = 0;
pub const VA_MIRROR_HORIZONTAL: u32 = 1;
pub const VA_MIRROR_VERTICAL: u32 = 2;
pub const VA_OOL_DEBLOCKING_FALSE: u32 = 0;
pub const VA_OOL_DEBLOCKING_TRUE: u32 = 1;
pub const VA_RENDER_MODE_UNDEFINED: u32 = 0;
pub const VA_RENDER_MODE_LOCAL_OVERLAY: u32 = 1;
pub const VA_RENDER_MODE_LOCAL_GPU: u32 = 2;
pub const VA_RENDER_MODE_EXTERNAL_OVERLAY: u32 = 4;
pub const VA_RENDER_MODE_EXTERNAL_GPU: u32 = 8;
pub const VA_RENDER_DEVICE_UNDEFINED: u32 = 0;
pub const VA_RENDER_DEVICE_LOCAL: u32 = 1;
pub const VA_RENDER_DEVICE_EXTERNAL: u32 = 2;
pub const VA_DISPLAY_ATTRIB_NOT_SUPPORTED: u32 = 0;
pub const VA_DISPLAY_ATTRIB_GETTABLE: u32 = 1;
pub const VA_DISPLAY_ATTRIB_SETTABLE: u32 = 2;
pub const VA_PICTURE_HEVC_INVALID: u32 = 1;
pub const VA_PICTURE_HEVC_FIELD_PIC: u32 = 2;
pub const VA_PICTURE_HEVC_BOTTOM_FIELD: u32 = 4;
pub const VA_PICTURE_HEVC_LONG_TERM_REFERENCE: u32 = 8;
pub const VA_PICTURE_HEVC_RPS_ST_CURR_BEFORE: u32 = 16;
pub const VA_PICTURE_HEVC_RPS_ST_CURR_AFTER: u32 = 32;
pub const VA_PICTURE_HEVC_RPS_LT_CURR: u32 = 64;
pub const VA_FEI_FUNCTION_ENC: u32 = 1;
pub const VA_FEI_FUNCTION_PAK: u32 = 2;
pub const VA_FEI_FUNCTION_ENC_PAK: u32 = 4;
pub const VA_PICTURE_STATS_INVALID: u32 = 1;
pub const VA_PICTURE_STATS_PROGRESSIVE: u32 = 0;
pub const VA_PICTURE_STATS_TOP_FIELD: u32 = 2;
pub const VA_PICTURE_STATS_BOTTOM_FIELD: u32 = 4;
pub const VA_PICTURE_STATS_CONTENT_UPDATED: u32 = 16;
pub const VA_MB_PRED_AVAIL_TOP_LEFT: u32 = 4;
pub const VA_MB_PRED_AVAIL_TOP: u32 = 16;
pub const VA_MB_PRED_AVAIL_TOP_RIGHT: u32 = 8;
pub const VA_MB_PRED_AVAIL_LEFT: u32 = 64;
pub const VA_AV1_MAX_SEGMENTS: u32 = 8;
pub const VA_AV1_SEG_LVL_MAX: u32 = 8;
pub const VA_BLEND_GLOBAL_ALPHA: u32 = 1;
pub const VA_BLEND_PREMULTIPLIED_ALPHA: u32 = 2;
pub const VA_BLEND_LUMA_KEY: u32 = 16;
pub const VA_PROC_PIPELINE_SUBPICTURES: u32 = 1;
pub const VA_PROC_PIPELINE_FAST: u32 = 2;
pub const VA_PROC_FILTER_MANDATORY: u32 = 1;
pub const VA_PIPELINE_FLAG_END: u32 = 4;
pub const VA_CHROMA_SITING_UNKNOWN: u32 = 0;
pub const VA_CHROMA_SITING_VERTICAL_TOP: u32 = 1;
pub const VA_CHROMA_SITING_VERTICAL_CENTER: u32 = 2;
pub const VA_CHROMA_SITING_VERTICAL_BOTTOM: u32 = 3;
pub const VA_CHROMA_SITING_HORIZONTAL_LEFT: u32 = 4;
pub const VA_CHROMA_SITING_HORIZONTAL_CENTER: u32 = 8;
pub const VA_SOURCE_RANGE_UNKNOWN: u32 = 0;
pub const VA_SOURCE_RANGE_REDUCED: u32 = 1;
pub const VA_SOURCE_RANGE_FULL: u32 = 2;
pub const VA_TONE_MAPPING_HDR_TO_HDR: u32 = 1;
pub const VA_TONE_MAPPING_HDR_TO_SDR: u32 = 2;
pub const VA_TONE_MAPPING_HDR_TO_EDR: u32 = 4;
pub const VA_TONE_MAPPING_SDR_TO_HDR: u32 = 8;
pub const VA_DEINTERLACING_BOTTOM_FIELD_FIRST: u32 = 1;
pub const VA_DEINTERLACING_BOTTOM_FIELD: u32 = 2;
pub const VA_DEINTERLACING_ONE_FIELD: u32 = 4;
pub const VA_DEINTERLACING_FMD_ENABLE: u32 = 8;
pub const VA_DEINTERLACING_SCD_ENABLE: u32 = 16;
pub const VA_PROC_HVS_DENOISE_DEFAULT: u32 = 0;
pub const VA_PROC_HVS_DENOISE_AUTO_BDRATE: u32 = 1;
pub const VA_PROC_HVS_DENOISE_AUTO_SUBJECTIVE: u32 = 2;
pub const VA_PROC_HVS_DENOISE_MANUAL: u32 = 3;
pub const VA_3DLUT_CHANNEL_UNKNOWN: u32 = 0;
pub const VA_3DLUT_CHANNEL_RGB_RGB: u32 = 1;
pub const VA_3DLUT_CHANNEL_YUV_RGB: u32 = 2;
pub const VA_3DLUT_CHANNEL_VUY_RGB: u32 = 4;

131
media/libva/src/buffer.rs Normal file
View file

@ -0,0 +1,131 @@
// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::rc::Rc;
use anyhow::Result;
use base::error;
use crate::{
bindings, buffer_type::BufferType, status::Status, Context, IQMatrix, PictureParameter,
SliceParameter,
};
/// A wrapper type representing a buffer created with vaCreateBuffer
pub struct Buffer {
context: Rc<Context>,
id: bindings::VABufferID,
}
impl Buffer {
/// Creates a new Buffer
pub(crate) fn new(context: Rc<Context>, mut type_: BufferType) -> Result<Self> {
let mut buffer_id = 0;
let (ptr, size) = match type_ {
BufferType::PictureParameter(ref mut picture_param) => match picture_param {
PictureParameter::MPEG2(ref mut wrapper) => (
wrapper.inner_mut() as *mut _ as *mut std::ffi::c_void,
std::mem::size_of_val(wrapper.inner_mut()),
),
PictureParameter::VP8(ref mut wrapper) => (
wrapper.inner_mut() as *mut _ as *mut std::ffi::c_void,
std::mem::size_of_val(wrapper.inner_mut()),
),
PictureParameter::VP9(ref mut wrapper) => (
wrapper.inner_mut() as *mut _ as *mut std::ffi::c_void,
std::mem::size_of_val(wrapper.inner_mut()),
),
PictureParameter::H264(ref mut wrapper) => (
wrapper.inner_mut() as *mut _ as *mut std::ffi::c_void,
std::mem::size_of_val(wrapper.inner_mut()),
),
},
BufferType::SliceParameter(ref mut slice_param) => match slice_param {
SliceParameter::MPEG2(ref mut wrapper) => (
wrapper.inner_mut() as *mut _ as *mut std::ffi::c_void,
std::mem::size_of_val(wrapper.inner_mut()),
),
SliceParameter::VP8(ref mut wrapper) => (
wrapper.inner_mut() as *mut _ as *mut std::ffi::c_void,
std::mem::size_of_val(wrapper.inner_mut()),
),
SliceParameter::VP9(ref mut wrapper) => (
wrapper.inner_mut() as *mut _ as *mut std::ffi::c_void,
std::mem::size_of_val(wrapper.inner_mut()),
),
SliceParameter::H264(ref mut wrapper) => (
wrapper.inner_mut() as *mut _ as *mut std::ffi::c_void,
std::mem::size_of_val(wrapper.inner_mut()),
),
},
BufferType::IQMatrix(ref mut iq_matrix) => match iq_matrix {
IQMatrix::MPEG2(ref mut wrapper) => (
wrapper.inner_mut() as *mut _ as *mut std::ffi::c_void,
std::mem::size_of_val(wrapper.inner_mut()),
),
IQMatrix::VP8(ref mut wrapper) => (
wrapper.inner_mut() as *mut _ as *mut std::ffi::c_void,
std::mem::size_of_val(wrapper.inner_mut()),
),
IQMatrix::H264(ref mut wrapper) => (
wrapper.inner_mut() as *mut _ as *mut std::ffi::c_void,
std::mem::size_of_val(wrapper.inner_mut()),
),
},
BufferType::Probability(ref mut wrapper) => (
wrapper.inner_mut() as *mut _ as *mut std::ffi::c_void,
std::mem::size_of_val(wrapper.inner_mut()),
),
BufferType::SliceData(ref mut data) => {
(data.as_mut_ptr() as *mut std::ffi::c_void, data.len())
}
};
// Safe because `self` represents a valid VAContext. `ptr` and `size`
// are also ensured to be correct, as `ptr` is just a cast to `*c_void`
// from a Rust struct, and `size` is computed from
// `std::mem::size_of_val`
Status(unsafe {
bindings::vaCreateBuffer(
context.display().handle(),
context.id(),
type_.inner(),
size as u32,
1,
ptr,
&mut buffer_id,
)
})
.check()?;
Ok(Self {
context,
id: buffer_id,
})
}
/// Convenience function to return a VABufferID vector. Useful to interface
/// with the C API where a buffer array might be needed.
pub fn as_id_vec(buffers: &[Self]) -> Vec<bindings::VABufferID> {
buffers.iter().map(|buffer| buffer.id).collect()
}
}
impl Drop for Buffer {
fn drop(&mut self) {
// Safe because `self` represents a valid buffer, created with
// vaCreateBuffers.
let status =
Status(unsafe { bindings::vaDestroyBuffer(self.context.display().handle(), self.id) })
.check();
if status.is_err() {
error!("vaDestroyBuffer failed: {}", status.unwrap_err());
}
}
}

View file

@ -0,0 +1,941 @@
// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::bindings;
/// An abstraction over VA BufferTypes
pub enum BufferType {
/// An abstraction over VAPictureParameterBufferType. Needed for MPEG2, VP8, VP9, H264
PictureParameter(PictureParameter),
/// An abstraction over VASliceParameterBufferType. Needed for MPEG2, VP8, VP9, H264
SliceParameter(SliceParameter),
/// An abstraction over VAIQMatrixBufferType. Needed for VP8, H264
IQMatrix(IQMatrix),
/// An abstraction over VAProbabilityDataBuffeTyper. Needed for VP8
Probability(ProbabilityDataBufferVP8),
/// An abstraction over VASliceDataBufferType. Needed for VP9, H264
SliceData(Vec<u8>),
}
impl BufferType {
/// Returns the inner FFI buffer type.
pub(crate) fn inner(&self) -> bindings::VABufferType::Type {
match self {
BufferType::PictureParameter(_) => bindings::VABufferType::VAPictureParameterBufferType,
BufferType::SliceParameter(_) => bindings::VABufferType::VASliceParameterBufferType,
BufferType::IQMatrix(_) => bindings::VABufferType::VAIQMatrixBufferType,
BufferType::Probability(_) => bindings::VABufferType::VAProbabilityBufferType,
BufferType::SliceData { .. } => bindings::VABufferType::VASliceDataBufferType,
}
}
}
/// Wrapper over the `picture_coding_extension` bindgen field in VAPictureParameterBufferMPEG2
pub struct MPEG2PictureCodingExtension(bindings::_VAPictureParameterBufferMPEG2__bindgen_ty_1);
impl MPEG2PictureCodingExtension {
/// Creates the bindgen field
pub fn new(
intra_dc_precision: u32,
picture_structure: u32,
top_field_first: u32,
frame_pred_frame_dct: u32,
concealment_motion_vectors: u32,
q_scale_type: u32,
intra_vlc_format: u32,
alternate_scan: u32,
repeat_first_field: u32,
progressive_frame: u32,
is_first_field: u32,
) -> Self {
let _bitfield_1 =
bindings::_VAPictureParameterBufferMPEG2__bindgen_ty_1__bindgen_ty_1::new_bitfield_1(
intra_dc_precision,
picture_structure,
top_field_first,
frame_pred_frame_dct,
concealment_motion_vectors,
q_scale_type,
intra_vlc_format,
alternate_scan,
repeat_first_field,
progressive_frame,
is_first_field,
);
Self(bindings::_VAPictureParameterBufferMPEG2__bindgen_ty_1 {
bits: bindings::_VAPictureParameterBufferMPEG2__bindgen_ty_1__bindgen_ty_1 {
_bitfield_align_1: Default::default(),
_bitfield_1,
__bindgen_padding_0: Default::default(),
},
})
}
/// Returns the inner FFI type. Useful for testing purposes.
pub fn inner(&mut self) -> &bindings::_VAPictureParameterBufferMPEG2__bindgen_ty_1 {
&self.0
}
}
/// Wrapper over the PictureParameterBufferMPEG2 FFI type.
pub struct PictureParameterBufferMPEG2(Box<bindings::VAPictureParameterBufferMPEG2>);
impl PictureParameterBufferMPEG2 {
/// Creates the wrapper
pub fn new(
horizontal_size: u16,
vertical_size: u16,
forward_reference_picture: bindings::VASurfaceID,
backward_reference_picture: bindings::VASurfaceID,
picture_coding_type: i32,
f_code: i32,
picture_coding_extension: &MPEG2PictureCodingExtension,
) -> Self {
let picture_coding_extension = picture_coding_extension.0;
Self(Box::new(bindings::VAPictureParameterBufferMPEG2 {
horizontal_size,
vertical_size,
forward_reference_picture,
backward_reference_picture,
picture_coding_type,
f_code,
picture_coding_extension,
va_reserved: Default::default(),
}))
}
pub(crate) fn inner_mut(&mut self) -> &mut bindings::VAPictureParameterBufferMPEG2 {
self.0.as_mut()
}
/// Returns the inner FFI type. Useful for testing purposes.
pub fn inner(&mut self) -> &bindings::VAPictureParameterBufferMPEG2 {
self.0.as_ref()
}
}
/// Wrapper over the `pic_fields` bindgen field in VAPictureParameterBufferVP8
pub struct VP8PicFields(bindings::_VAPictureParameterBufferVP8__bindgen_ty_1);
impl VP8PicFields {
/// Creates the bindgen field
pub fn new(
key_frame: u32,
version: u32,
segmentation_enabled: u32,
update_mb_segmentation_map: u32,
update_segment_feature_data: u32,
filter_type: u32,
sharpness_level: u32,
loop_filter_adj_enable: u32,
mode_ref_lf_delta_update: u32,
sign_bias_golden: u32,
sign_bias_alternate: u32,
mb_no_coeff_skip: u32,
loop_filter_disable: u32,
) -> Self {
let _bitfield_1 =
bindings::_VAPictureParameterBufferVP8__bindgen_ty_1__bindgen_ty_1::new_bitfield_1(
key_frame,
version,
segmentation_enabled,
update_mb_segmentation_map,
update_segment_feature_data,
filter_type,
sharpness_level,
loop_filter_adj_enable,
mode_ref_lf_delta_update,
sign_bias_golden,
sign_bias_alternate,
mb_no_coeff_skip,
loop_filter_disable,
);
Self(bindings::_VAPictureParameterBufferVP8__bindgen_ty_1 {
bits: bindings::_VAPictureParameterBufferVP8__bindgen_ty_1__bindgen_ty_1 {
_bitfield_align_1: Default::default(),
_bitfield_1,
__bindgen_padding_0: Default::default(),
},
})
}
/// Returns the inner FFI type. Useful for testing purposes.
pub fn inner(&self) -> &bindings::_VAPictureParameterBufferVP8__bindgen_ty_1 {
&self.0
}
}
/// Wrapper over the VABoolCoderContextVPX FFI type
pub struct BoolCoderContextVPX(bindings::VABoolCoderContextVPX);
impl BoolCoderContextVPX {
/// Creates the wrapper
pub fn new(range: u8, value: u8, count: u8) -> Self {
Self(bindings::VABoolCoderContextVPX {
range,
value,
count,
})
}
}
/// Wrapper over the PictureParameterBufferVP8 FFI type
pub struct PictureParameterBufferVP8(Box<bindings::VAPictureParameterBufferVP8>);
impl PictureParameterBufferVP8 {
/// Creates the wrapper
pub fn new(
frame_width: u32,
frame_height: u32,
last_ref_frame: bindings::VASurfaceID,
golden_ref_frame: bindings::VASurfaceID,
alt_ref_frame: bindings::VASurfaceID,
pic_fields: &VP8PicFields,
mb_segment_tree_probs: [u8; 3usize],
loop_filter_level: [u8; 4usize],
loop_filter_deltas_ref_frame: [i8; 4usize],
loop_filter_deltas_mode: [i8; 4usize],
prob_skip_false: u8,
prob_intra: u8,
prob_last: u8,
prob_gf: u8,
y_mode_probs: [u8; 4usize],
uv_mode_probs: [u8; 3usize],
mv_probs: [[u8; 19usize]; 2usize],
bool_coder_ctx: &BoolCoderContextVPX,
) -> Self {
let pic_fields = pic_fields.0;
let bool_coder_ctx = bool_coder_ctx.0;
Self(Box::new(bindings::VAPictureParameterBufferVP8 {
frame_width,
frame_height,
last_ref_frame,
golden_ref_frame,
alt_ref_frame,
out_of_loop_frame: bindings::constants::VA_INVALID_SURFACE,
pic_fields,
mb_segment_tree_probs,
loop_filter_level,
loop_filter_deltas_ref_frame,
loop_filter_deltas_mode,
prob_skip_false,
prob_intra,
prob_last,
prob_gf,
y_mode_probs,
uv_mode_probs,
mv_probs,
bool_coder_ctx,
va_reserved: Default::default(),
}))
}
pub(crate) fn inner_mut(&mut self) -> &mut bindings::VAPictureParameterBufferVP8 {
self.0.as_mut()
}
/// Returns the inner FFI type. Useful for testing purposes.
pub fn inner(&self) -> &bindings::VAPictureParameterBufferVP8 {
self.0.as_ref()
}
}
/// Wrapper over the `pic_fields` bindgen field in VAPictureParameterBufferVP9
pub struct VP9PicFields(bindings::_VADecPictureParameterBufferVP9__bindgen_ty_1);
impl VP9PicFields {
/// Creates the bindgen field
pub fn new(
subsampling_x: u32,
subsampling_y: u32,
frame_type: u32,
show_frame: u32,
error_resilient_mode: u32,
intra_only: u32,
allow_high_precision_mv: u32,
mcomp_filter_type: u32,
frame_parallel_decoding_mode: u32,
reset_frame_context: u32,
refresh_frame_context: u32,
frame_context_idx: u32,
segmentation_enabled: u32,
segmentation_temporal_update: u32,
segmentation_update_map: u32,
last_ref_frame: u32,
last_ref_frame_sign_bias: u32,
golden_ref_frame: u32,
golden_ref_frame_sign_bias: u32,
alt_ref_frame: u32,
alt_ref_frame_sign_bias: u32,
lossless_flag: u32,
) -> Self {
let _bitfield_1 =
bindings::_VADecPictureParameterBufferVP9__bindgen_ty_1__bindgen_ty_1::new_bitfield_1(
subsampling_x,
subsampling_y,
frame_type,
show_frame,
error_resilient_mode,
intra_only,
allow_high_precision_mv,
mcomp_filter_type,
frame_parallel_decoding_mode,
reset_frame_context,
refresh_frame_context,
frame_context_idx,
segmentation_enabled,
segmentation_temporal_update,
segmentation_update_map,
last_ref_frame,
last_ref_frame_sign_bias,
golden_ref_frame,
golden_ref_frame_sign_bias,
alt_ref_frame,
alt_ref_frame_sign_bias,
lossless_flag,
);
Self(bindings::_VADecPictureParameterBufferVP9__bindgen_ty_1 {
bits: bindings::_VADecPictureParameterBufferVP9__bindgen_ty_1__bindgen_ty_1 {
_bitfield_align_1: Default::default(),
_bitfield_1,
},
})
}
/// Returns the inner FFI type. Useful for testing purposes.
pub fn inner(&mut self) -> &bindings::_VADecPictureParameterBufferVP9__bindgen_ty_1 {
&self.0
}
}
/// Wrapper over the PictureParameterBufferVP9 FFI type
pub struct PictureParameterBufferVP9(Box<bindings::VADecPictureParameterBufferVP9>);
impl PictureParameterBufferVP9 {
/// Creates the wrapper
pub fn new(
frame_width: u16,
frame_height: u16,
reference_frames: [bindings::VASurfaceID; 8],
pic_fields: &VP9PicFields,
filter_level: u8,
sharpness_level: u8,
log2_tile_rows: u8,
log2_tile_columns: u8,
frame_header_length_in_bytes: u8,
first_partition_size: u16,
mb_segment_tree_probs: [u8; 7usize],
segment_pred_probs: [u8; 3usize],
profile: u8,
bit_depth: u8,
) -> Self {
let pic_fields = pic_fields.0;
Self(Box::new(bindings::VADecPictureParameterBufferVP9 {
frame_width,
frame_height,
reference_frames,
pic_fields,
filter_level,
sharpness_level,
log2_tile_rows,
log2_tile_columns,
frame_header_length_in_bytes,
first_partition_size,
mb_segment_tree_probs,
segment_pred_probs,
profile,
bit_depth,
va_reserved: Default::default(),
}))
}
pub(crate) fn inner_mut(&mut self) -> &mut bindings::VADecPictureParameterBufferVP9 {
self.0.as_mut()
}
/// Returns the inner FFI type. Useful for testing purposes.
pub fn inner(&self) -> &bindings::VADecPictureParameterBufferVP9 {
self.0.as_ref()
}
}
/// Wrapper over the VAPictureH264 FFI type
pub struct PictureH264(bindings::VAPictureH264);
impl PictureH264 {
/// Creates the wrapper
pub fn new(
picture_id: bindings::VASurfaceID,
frame_idx: u32,
flags: u32,
top_field_order_cnt: i32,
bottom_field_order_cnt: i32,
) -> Self {
Self(bindings::VAPictureH264 {
picture_id,
frame_idx,
flags,
TopFieldOrderCnt: top_field_order_cnt,
BottomFieldOrderCnt: bottom_field_order_cnt,
va_reserved: Default::default(),
})
}
}
/// Wrapper over the `seq_fields` bindgen field in VAPictureParameterBufferH264
pub struct H264SeqFields(bindings::_VAPictureParameterBufferH264__bindgen_ty_1);
impl H264SeqFields {
/// Creates the bindgen field
pub fn new(
chroma_format_idc: u32,
residual_colour_transform_flag: u32,
gaps_in_frame_num_value_allowed_flag: u32,
frame_mbs_only_flag: u32,
mb_adaptive_frame_field_flag: u32,
direct_8x8_inference_flag: u32,
min_luma_bi_pred_size8x8: u32,
log2_max_frame_num_minus4: u32,
pic_order_cnt_type: u32,
log2_max_pic_order_cnt_lsb_minus4: u32,
delta_pic_order_always_zero_flag: u32,
) -> Self {
let _bitfield_1 =
bindings::_VAPictureParameterBufferH264__bindgen_ty_1__bindgen_ty_1::new_bitfield_1(
chroma_format_idc,
residual_colour_transform_flag,
gaps_in_frame_num_value_allowed_flag,
frame_mbs_only_flag,
mb_adaptive_frame_field_flag,
direct_8x8_inference_flag,
min_luma_bi_pred_size8x8,
log2_max_frame_num_minus4,
pic_order_cnt_type,
log2_max_pic_order_cnt_lsb_minus4,
delta_pic_order_always_zero_flag,
);
Self(bindings::_VAPictureParameterBufferH264__bindgen_ty_1 {
bits: bindings::_VAPictureParameterBufferH264__bindgen_ty_1__bindgen_ty_1 {
_bitfield_align_1: Default::default(),
_bitfield_1,
__bindgen_padding_0: Default::default(),
},
})
}
/// Returns the inner FFI type. Useful for testing purposes.
pub fn inner(&mut self) -> &bindings::_VAPictureParameterBufferH264__bindgen_ty_1 {
&self.0
}
}
/// Wrapper over the `pic_fields` bindgen field in VAPictureParameterBufferH264
pub struct H264PicFields(bindings::_VAPictureParameterBufferH264__bindgen_ty_2);
impl H264PicFields {
/// Creates the bindgen field
pub fn new(
entropy_coding_mode_flag: u32,
weighted_pred_flag: u32,
weighted_bipred_idc: u32,
transform_8x8_mode_flag: u32,
field_pic_flag: u32,
constrained_intra_pred_flag: u32,
pic_order_present_flag: u32,
deblocking_filter_control_present_flag: u32,
redundant_pic_cnt_present_flag: u32,
reference_pic_flag: u32,
) -> Self {
let _bitfield_1 =
bindings::_VAPictureParameterBufferH264__bindgen_ty_2__bindgen_ty_1::new_bitfield_1(
entropy_coding_mode_flag,
weighted_pred_flag,
weighted_bipred_idc,
transform_8x8_mode_flag,
field_pic_flag,
constrained_intra_pred_flag,
pic_order_present_flag,
deblocking_filter_control_present_flag,
redundant_pic_cnt_present_flag,
reference_pic_flag,
);
Self(bindings::_VAPictureParameterBufferH264__bindgen_ty_2 {
bits: bindings::_VAPictureParameterBufferH264__bindgen_ty_2__bindgen_ty_1 {
_bitfield_align_1: Default::default(),
_bitfield_1,
__bindgen_padding_0: Default::default(),
},
})
}
/// Returns the inner FFI type. Useful for testing purposes.
pub fn inner(&mut self) -> &bindings::_VAPictureParameterBufferH264__bindgen_ty_2 {
&self.0
}
}
/// A wrapper over VAPictureParameterBufferH264 FFI type
pub struct PictureParameterBufferH264(Box<bindings::VAPictureParameterBufferH264>);
impl PictureParameterBufferH264 {
/// Creates the wrapper
pub fn new(
curr_pic: PictureH264,
reference_frames: [PictureH264; 16],
picture_width_in_mbs_minus1: u16,
picture_height_in_mbs_minus1: u16,
bit_depth_luma_minus8: u8,
bit_depth_chroma_minus8: u8,
num_ref_frames: u8,
seq_fields: &H264SeqFields,
num_slice_groups_minus1: u8,
slice_group_map_type: u8,
slice_group_change_rate_minus1: u16,
pic_init_qp_minus26: i8,
pic_init_qs_minus26: i8,
chroma_qp_index_offset: i8,
second_chroma_qp_index_offset: i8,
pic_fields: &H264PicFields,
frame_num: u16,
) -> Self {
let reference_frames = (0..16usize)
.map(|i| reference_frames[i].0)
.collect::<Vec<_>>()
.try_into()
// try_into is guaranteed to work because the iterator and target array have the same
// size.
.unwrap();
let seq_fields = seq_fields.0;
let pic_fields = pic_fields.0;
Self(Box::new(bindings::VAPictureParameterBufferH264 {
CurrPic: curr_pic.0,
ReferenceFrames: reference_frames,
picture_width_in_mbs_minus1,
picture_height_in_mbs_minus1,
bit_depth_luma_minus8,
bit_depth_chroma_minus8,
num_ref_frames,
seq_fields,
num_slice_groups_minus1,
slice_group_map_type,
slice_group_change_rate_minus1,
pic_init_qp_minus26,
pic_init_qs_minus26,
chroma_qp_index_offset,
second_chroma_qp_index_offset,
pic_fields,
frame_num,
va_reserved: Default::default(),
}))
}
pub(crate) fn inner_mut(&mut self) -> &mut bindings::VAPictureParameterBufferH264 {
self.0.as_mut()
}
/// Returns the inner FFI type. Useful for testing purposes.
pub fn inner(&self) -> &bindings::VAPictureParameterBufferH264 {
self.0.as_ref()
}
}
/// An abstraction over the PictureParameterBuffer types we support
pub enum PictureParameter {
/// A wrapper over VAPictureParameterBufferMPEG2
MPEG2(PictureParameterBufferMPEG2),
/// A wrapper over VAPictureParameterBufferVP8
VP8(PictureParameterBufferVP8),
/// A wrapper over VAPictureParameterBufferVP9
VP9(PictureParameterBufferVP9),
/// A wrapper over VAPictureParameterBufferH264
H264(PictureParameterBufferH264),
}
/// Wrapper over the VASliceParameterBufferMPEG2 FFI type
pub struct SliceParameterBufferMPEG2(Box<bindings::VASliceParameterBufferMPEG2>);
impl SliceParameterBufferMPEG2 {
/// Creates the wrapper
pub fn new(
slice_data_size: u32,
slice_data_offset: u32,
slice_data_flag: u32,
macroblock_offset: u32,
slice_horizontal_position: u32,
slice_vertical_position: u32,
quantiser_scale_code: i32,
intra_slice_flag: i32,
) -> Self {
Self(Box::new(bindings::VASliceParameterBufferMPEG2 {
slice_data_size,
slice_data_offset,
slice_data_flag,
macroblock_offset,
slice_horizontal_position,
slice_vertical_position,
quantiser_scale_code,
intra_slice_flag,
va_reserved: Default::default(),
}))
}
pub(crate) fn inner_mut(&mut self) -> &mut bindings::VASliceParameterBufferMPEG2 {
self.0.as_mut()
}
/// Returns the inner FFI type. Useful for testing purposes.
pub fn inner(&self) -> &bindings::VASliceParameterBufferMPEG2 {
self.0.as_ref()
}
}
/// An abstraction over the SliceParameterBuffer types we support
pub enum SliceParameter {
/// A wrapper over VASliceParameterBufferMPEG2
MPEG2(SliceParameterBufferMPEG2),
/// A wrapper over VASliceParameterBufferVP8
VP8(SliceParameterBufferVP8),
/// A wrapper over VASliceParameterBufferVP9
VP9(SliceParameterBufferVP9),
/// A wrapper over VASliceParameterBufferH264
H264(SliceParameterBufferH264),
}
/// Wrapper over the VASliceParameterBufferVP8 FFI type
pub struct SliceParameterBufferVP8(Box<bindings::VASliceParameterBufferVP8>);
impl SliceParameterBufferVP8 {
/// Creates the wrapper
pub fn new(
slice_data_size: u32,
slice_data_offset: u32,
slice_data_flag: u32,
macroblock_offset: u32,
num_of_partitions: u8,
partition_size: [u32; 9usize],
) -> Self {
Self(Box::new(bindings::VASliceParameterBufferVP8 {
slice_data_size,
slice_data_offset,
slice_data_flag,
macroblock_offset,
num_of_partitions,
partition_size,
va_reserved: Default::default(),
}))
}
pub(crate) fn inner_mut(&mut self) -> &mut bindings::VASliceParameterBufferVP8 {
self.0.as_mut()
}
/// Returns the inner FFI type. Useful for testing purposes.
pub fn inner(&self) -> &bindings::VASliceParameterBufferVP8 {
self.0.as_ref()
}
}
/// Wrapper over the `segment_flags` bindgen field in VASegmentParameterVP9
pub struct VP9SegmentFlags(bindings::_VASegmentParameterVP9__bindgen_ty_1);
impl VP9SegmentFlags {
/// Creates the wrapper
pub fn new(
segment_reference_enabled: u16,
segment_reference: u16,
segment_reference_skipped: u16,
) -> Self {
let _bitfield_1 =
bindings::_VASegmentParameterVP9__bindgen_ty_1__bindgen_ty_1::new_bitfield_1(
segment_reference_enabled,
segment_reference,
segment_reference_skipped,
);
Self(bindings::_VASegmentParameterVP9__bindgen_ty_1 {
fields: bindings::_VASegmentParameterVP9__bindgen_ty_1__bindgen_ty_1 {
_bitfield_align_1: Default::default(),
_bitfield_1,
__bindgen_padding_0: Default::default(),
},
})
}
/// Returns the inner FFI type. Useful for testing purposes.
pub fn inner(&mut self) -> &bindings::_VASegmentParameterVP9__bindgen_ty_1 {
&self.0
}
}
/// Wrapper over the VASegmentParameterVP9 FFI type
pub struct SegmentParameterVP9(bindings::VASegmentParameterVP9);
impl SegmentParameterVP9 {
/// Creates the wrapper
pub fn new(
segment_flags: &VP9SegmentFlags,
filter_level: [[u8; 2usize]; 4usize],
luma_ac_quant_scale: i16,
luma_dc_quant_scale: i16,
chroma_ac_quant_scale: i16,
chroma_dc_quant_scale: i16,
) -> Self {
let segment_flags = segment_flags.0;
Self(bindings::VASegmentParameterVP9 {
segment_flags,
filter_level,
luma_ac_quant_scale,
luma_dc_quant_scale,
chroma_ac_quant_scale,
chroma_dc_quant_scale,
va_reserved: Default::default(),
})
}
}
/// Wrapper over the VASliceParameterBufferVP9 FFI type
pub struct SliceParameterBufferVP9(Box<bindings::VASliceParameterBufferVP9>);
impl SliceParameterBufferVP9 {
/// Creates the wrapper
pub fn new(
slice_data_size: u32,
slice_data_offset: u32,
slice_data_flag: u32,
seg_param: [&SegmentParameterVP9; 8usize],
) -> Self {
let seg_param = seg_param.map(|param| param.0);
Self(Box::new(bindings::VASliceParameterBufferVP9 {
slice_data_size,
slice_data_offset,
slice_data_flag,
seg_param,
va_reserved: Default::default(),
}))
}
pub(crate) fn inner_mut(&mut self) -> &mut bindings::VASliceParameterBufferVP9 {
self.0.as_mut()
}
/// Returns the inner FFI type. Useful for testing purposes.
pub fn inner(&self) -> &bindings::VASliceParameterBufferVP9 {
self.0.as_ref()
}
}
/// Wrapper over the VASliceParameterBufferH264 FFI type
pub struct SliceParameterBufferH264(Box<bindings::VASliceParameterBufferH264>);
impl SliceParameterBufferH264 {
/// Creates the wrapper
pub fn new(
slice_data_size: u32,
slice_data_offset: u32,
slice_data_flag: u32,
slice_data_bit_offset: u16,
first_mb_in_slice: u16,
slice_type: u8,
direct_spatial_mv_pred_flag: u8,
num_ref_idx_l0_active_minus1: u8,
num_ref_idx_l1_active_minus1: u8,
cabac_init_idc: u8,
slice_qp_delta: i8,
disable_deblocking_filter_idc: u8,
slice_alpha_c0_offset_div2: i8,
slice_beta_offset_div2: i8,
ref_pic_list_0: [&PictureH264; 32usize],
ref_pic_list_1: [&PictureH264; 32usize],
luma_log2_weight_denom: u8,
chroma_log2_weight_denom: u8,
luma_weight_l0_flag: u8,
luma_weight_l0: [i16; 32usize],
luma_offset_l0: [i16; 32usize],
chroma_weight_l0_flag: u8,
chroma_weight_l0: [[i16; 2usize]; 32usize],
chroma_offset_l0: [[i16; 2usize]; 32usize],
luma_weight_l1_flag: u8,
luma_weight_l1: [i16; 32usize],
luma_offset_l1: [i16; 32usize],
chroma_weight_l1_flag: u8,
chroma_weight_l1: [[i16; 2usize]; 32usize],
chroma_offset_l1: [[i16; 2usize]; 32usize],
) -> Self {
let ref_pic_list_0 = ref_pic_list_0.map(|pic| pic.0);
let ref_pic_list_1 = ref_pic_list_1.map(|pic| pic.0);
Self(Box::new(bindings::VASliceParameterBufferH264 {
slice_data_size,
slice_data_offset,
slice_data_flag,
slice_data_bit_offset,
first_mb_in_slice,
slice_type,
direct_spatial_mv_pred_flag,
num_ref_idx_l0_active_minus1,
num_ref_idx_l1_active_minus1,
cabac_init_idc,
slice_qp_delta,
disable_deblocking_filter_idc,
slice_alpha_c0_offset_div2,
slice_beta_offset_div2,
RefPicList0: ref_pic_list_0,
RefPicList1: ref_pic_list_1,
luma_log2_weight_denom,
chroma_log2_weight_denom,
luma_weight_l0_flag,
luma_weight_l0,
luma_offset_l0,
chroma_weight_l0_flag,
chroma_weight_l0,
chroma_offset_l0,
luma_weight_l1_flag,
luma_weight_l1,
luma_offset_l1,
chroma_weight_l1_flag,
chroma_weight_l1,
chroma_offset_l1,
va_reserved: Default::default(),
}))
}
pub(crate) fn inner_mut(&mut self) -> &mut bindings::VASliceParameterBufferH264 {
self.0.as_mut()
}
/// Returns the inner FFI type. Useful for testing purposes.
pub fn inner(&self) -> &bindings::VASliceParameterBufferH264 {
self.0.as_ref()
}
}
/// Wrapper over the VAIQMatrixBufferMPEG2 FFI type
pub struct IQMatrixBufferMPEG2(Box<bindings::VAIQMatrixBufferMPEG2>);
impl IQMatrixBufferMPEG2 {
/// Creates the wrapper
pub fn new(
load_intra_quantiser_matrix: i32,
load_non_intra_quantiser_matrix: i32,
load_chroma_intra_quantiser_matrix: i32,
load_chroma_non_intra_quantiser_matrix: i32,
intra_quantiser_matrix: [u8; 64usize],
non_intra_quantiser_matrix: [u8; 64usize],
chroma_intra_quantiser_matrix: [u8; 64usize],
chroma_non_intra_quantiser_matrix: [u8; 64usize],
) -> Self {
Self(Box::new(bindings::VAIQMatrixBufferMPEG2 {
load_intra_quantiser_matrix,
load_non_intra_quantiser_matrix,
load_chroma_intra_quantiser_matrix,
load_chroma_non_intra_quantiser_matrix,
intra_quantiser_matrix,
non_intra_quantiser_matrix,
chroma_intra_quantiser_matrix,
chroma_non_intra_quantiser_matrix,
va_reserved: Default::default(),
}))
}
pub(crate) fn inner_mut(&mut self) -> &mut bindings::VAIQMatrixBufferMPEG2 {
self.0.as_mut()
}
/// Returns the inner FFI type. Useful for testing purposes.
pub fn inner(&self) -> &bindings::VAIQMatrixBufferMPEG2 {
self.0.as_ref()
}
}
/// Wrapper over the VAIQMatrixBufferVP8 FFI type
pub struct IQMatrixBufferVP8(Box<bindings::VAIQMatrixBufferVP8>);
impl IQMatrixBufferVP8 {
/// Creates the wrapper
pub fn new(quantization_index: [[u16; 6usize]; 4usize]) -> Self {
Self(Box::new(bindings::VAIQMatrixBufferVP8 {
quantization_index,
va_reserved: Default::default(),
}))
}
pub(crate) fn inner_mut(&mut self) -> &mut bindings::VAIQMatrixBufferVP8 {
self.0.as_mut()
}
/// Returns the inner FFI type. Useful for testing purposes.
pub fn inner(&self) -> &bindings::VAIQMatrixBufferVP8 {
self.0.as_ref()
}
}
/// Wrapper over the VAIQMatrixBufferH264 FFI type
pub struct IQMatrixBufferH264(Box<bindings::VAIQMatrixBufferH264>);
impl IQMatrixBufferH264 {
/// Creates the wrapper
pub fn new(
scaling_list4x4: [[u8; 16usize]; 6usize],
scaling_list8x8: [[u8; 64usize]; 2usize],
) -> Self {
Self(Box::new(bindings::VAIQMatrixBufferH264 {
ScalingList4x4: scaling_list4x4,
ScalingList8x8: scaling_list8x8,
va_reserved: Default::default(),
}))
}
pub(crate) fn inner_mut(&mut self) -> &mut bindings::VAIQMatrixBufferH264 {
self.0.as_mut()
}
/// Returns the inner FFI type. Useful for testing purposes.
pub fn inner(&self) -> &bindings::VAIQMatrixBufferH264 {
self.0.as_ref()
}
}
/// An abstraction over the IQMatrixBuffer types we support
pub enum IQMatrix {
/// An abstraction over VAIQMatrixBufferMPEG2
MPEG2(IQMatrixBufferMPEG2),
/// An abstraction over VAIQMatrixBufferVP8
VP8(IQMatrixBufferVP8),
/// An abstraction over VAIQMatrixBufferH264
H264(IQMatrixBufferH264),
}
/// Wrapper over the VAProbabilityDataBufferVP8 FFI type
pub struct ProbabilityDataBufferVP8(Box<bindings::VAProbabilityDataBufferVP8>);
impl ProbabilityDataBufferVP8 {
/// Creates the wrapper
pub fn new(dct_coeff_probs: [[[[u8; 11usize]; 3usize]; 8usize]; 4usize]) -> Self {
Self(Box::new(bindings::VAProbabilityDataBufferVP8 {
dct_coeff_probs,
va_reserved: Default::default(),
}))
}
pub(crate) fn inner_mut(&mut self) -> &mut bindings::VAProbabilityDataBufferVP8 {
self.0.as_mut()
}
/// Returns the inner FFI type. Useful for testing purposes.
pub fn inner(&self) -> &bindings::VAProbabilityDataBufferVP8 {
self.0.as_ref()
}
}

129
media/libva/src/config.rs Normal file
View file

@ -0,0 +1,129 @@
// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::rc::Rc;
use anyhow::Result;
use base::error;
use crate::{bindings, display::Display, generic_value::GenericValue, status::Status};
/// An owned VAConfig that is tied to the lifetime of a particular VADisplay
pub struct Config {
display: Rc<Display>,
id: bindings::VAConfigID,
}
impl Config {
/// Create a VAConfig by wrapping around the vaCreateConfig call. `attrs`
/// describe the attributes to set for this config. A list of the supported
/// attributes for a given profile/entrypoint pair can be retrieved using
/// Display::get_config_attributes. Other attributes will take their default
/// values
pub(crate) fn new(
display: Rc<Display>,
attrs: Option<Vec<bindings::VAConfigAttrib>>,
profile: bindings::VAProfile::Type,
entrypoint: bindings::VAEntrypoint::Type,
) -> Result<Self> {
let mut config_id = 0u32;
let mut attrs = attrs.unwrap_or_default();
// Safe because `self` represents a valid VADisplay The "attrs" vectors
// is properly initialized and a valid size is passed to the C function,
// so it is impossible to write past the end of the vector's storage by
// mistake
Status(unsafe {
bindings::vaCreateConfig(
display.handle(),
profile,
entrypoint,
attrs.as_mut_ptr(),
attrs.len() as i32,
&mut config_id,
)
})
.check()?;
Ok(Self {
display,
id: config_id,
})
}
/// Returns the associated VAConfigID
pub(crate) fn id(&self) -> bindings::VAConfigID {
self.id
}
// Queries surface attributes for the supplied config. This function
// queries for all supported attributes for the supplied VA config. In
// particular, if the underlying hardware supports the creation of VA
// surfaces in various formats, then this function will enumerate all pixel
// formats that are supported.
fn query_surface_attributes(&mut self) -> Result<Vec<bindings::VASurfaceAttrib>> {
// Safe because `self` represents a valid VAConfig. We first query how
// much space is needed by the C API by passing in NULL in the first
// call to `vaQuerySurfaceAttributes`.
let attrs_len: std::os::raw::c_uint = 0;
Status(unsafe {
bindings::vaQuerySurfaceAttributes(
self.display.handle(),
self.id,
std::ptr::null_mut(),
&attrs_len as *const _ as *mut std::os::raw::c_uint,
)
})
.check()?;
let mut attrs = Vec::with_capacity(attrs_len as usize);
// Safe because we allocate a vector with the required capacity as
// returned by the initial call to vaQuerySurfaceAttributes. We then
// pass a valid pointer to it.
Status(unsafe {
bindings::vaQuerySurfaceAttributes(
self.display.handle(),
self.id,
attrs.as_mut_ptr(),
&attrs_len as *const _ as *mut std::os::raw::c_uint,
)
})
.check()?;
// Safe because vaQuerySurfaceAttributes will have written to
// exactly attrs_len entries in the vector.
unsafe {
attrs.set_len(attrs_len as usize);
}
Ok(attrs)
}
/// Query the surface attribute of type `attr_type`. The attribute may or
/// may not be defined by the driver.
pub fn query_surface_attribute(
&mut self,
attr_type: bindings::VASurfaceAttribType::Type,
) -> Result<Option<GenericValue>> {
let surface_attributes = self.query_surface_attributes()?;
surface_attributes
.into_iter()
.find(|attr| attr.type_ == attr_type)
.map_or(Ok(None), |attrib| {
Ok(Some(GenericValue::try_from(attrib.value)?))
})
}
}
impl Drop for Config {
fn drop(&mut self) {
// Safe because `self` represents a valid Config.
let status =
Status(unsafe { bindings::vaDestroyConfig(self.display.handle(), self.id) }).check();
if status.is_err() {
error!("vaDestroyConfig failed: {}", status.unwrap_err());
}
}
}

View file

@ -0,0 +1,97 @@
// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::rc::Rc;
use anyhow::Result;
use base::error;
use crate::{
bindings, buffer::Buffer, buffer_type::BufferType, display::Display, status::Status, Config,
Surface,
};
/// An owned Context that is tied to the lifetime of a particular Display
pub struct Context {
display: Rc<Display>,
id: bindings::VAContextID,
}
impl Context {
/// Create a Context by wrapping around a vaCreateContext call.
/// `config` is the configuration for the context
/// `coded_width` is the coded picture width
/// `coded_height` is the coded picture height
/// `progressive` is whether only progressive frame pictures are present in the sequence
/// `surfaces` are a hint for the amount of surfaces tied to the context
pub(crate) fn new(
display: Rc<Display>,
config: &Config,
coded_width: i32,
coded_height: i32,
surfaces: Option<&Vec<Surface>>,
progressive: bool,
) -> Result<Self> {
let mut context_id = 0;
let flags = if progressive {
bindings::constants::VA_PROGRESSIVE as i32
} else {
0
};
let mut render_targets = match surfaces {
Some(surfaces) => Surface::as_id_vec(surfaces),
None => Default::default(),
};
// Safe because `self` represents a valid VADisplay and render_targets
// and ntargets are properly initialized. Note that render_targets==NULL
// is valid so long as ntargets==0.
Status(unsafe {
bindings::vaCreateContext(
display.handle(),
config.id(),
coded_width,
coded_height,
flags,
render_targets.as_mut_ptr(),
render_targets.len() as i32,
&mut context_id,
)
})
.check()?;
Ok(Self {
display,
id: context_id,
})
}
/// Returns the inner VADisplay
pub fn display(&self) -> Rc<Display> {
Rc::clone(&self.display)
}
/// Returns the VAContextID for this Context
pub(crate) fn id(&self) -> bindings::VAContextID {
self.id
}
/// Create a buffer by wrapping a vaCreateBuffer call. `type_` describes the
/// underlying data to libva.
pub fn create_buffer(self: &Rc<Self>, type_: BufferType) -> Result<Buffer> {
Buffer::new(Rc::clone(self), type_)
}
}
impl Drop for Context {
fn drop(&mut self) {
// Safe because `self` represents a valid VAContext.
let status =
Status(unsafe { bindings::vaDestroyContext(self.display.handle(), self.id) }).check();
if status.is_err() {
error!("vaDestroyContext failed: {}", status.unwrap_err());
}
}
}

299
media/libva/src/display.rs Normal file
View file

@ -0,0 +1,299 @@
// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::{ffi::CStr, fs::File, rc::Rc};
use anyhow::Context as AnyhowContext;
use anyhow::{anyhow, Result};
use base::AsRawDescriptor;
use crate::{
bindings, config::Config, context::Context, status::Status, surface::Surface, UsageHint,
};
/// An owned VADisplay
pub struct Display {
/// The handle to interact with the underlying VADisplay.
handle: bindings::VADisplay,
/// The DRM file that must be kept open while the VADisplay is in use.
#[allow(dead_code)]
drm_file: File,
}
impl Display {
/// Opens and initializes an owned Display
pub fn open() -> Result<Self> {
let (display, drm_file) = match Display::drm_open()? {
Some(x) => x,
None => return Err(anyhow!("Couldn't open a suitable DRM file descriptor")),
};
let mut major = 0i32;
let mut minor = 0i32;
// Safe because we ensure that the display is valid (i.e not NULL)
// before calling vaInitialize
let result =
Status(unsafe { bindings::vaInitialize(display, &mut major, &mut minor) }).check();
if let Err(error) = result {
// The File will close the DRM fd on drop.
return Err(error);
}
Ok(Self {
handle: display,
drm_file,
})
}
fn drm_open() -> Result<Option<(bindings::VADisplay, File)>> {
let udev_context = libudev::Context::new()?;
let mut enumerator = libudev::Enumerator::new(&udev_context)?;
enumerator
.match_subsystem("drm")
.context("udev error when matching DRM devices")?;
for device in enumerator.scan_devices()? {
let path = device
.devnode()
.and_then(|f| f.file_name())
.and_then(|f| f.to_str());
match path {
Some(name) => {
if !name.contains("renderD") {
continue;
}
}
None => continue,
}
let file = std::fs::File::open(device.devnode().unwrap())?;
let fd = file.as_raw_descriptor();
// Safe because fd represents a valid file descriptor and
// the pointer is checked for NULL afterwards.
let display = unsafe { bindings::vaGetDisplayDRM(fd) };
if display.is_null() {
// The File will close the DRM fd on drop.
return Err(anyhow!("va_open_display() failed"));
}
return Ok(Some((display, file)));
}
Ok(None)
}
/// Returns the associated handle
pub(crate) fn handle(&self) -> bindings::VADisplay {
self.handle
}
/// Queries supported profiles
pub fn query_config_profiles(&self) -> Result<Vec<bindings::VAProfile::Type>> {
// Safe because `self` represents a valid VADisplay.
let mut max_num_profiles = unsafe { bindings::vaMaxNumProfiles(self.handle) };
let mut profiles = Vec::with_capacity(max_num_profiles as usize);
// Safe because `self` represents a valid VADisplay and the vector has
// "max_num_profiles" as capacity.
Status(unsafe {
bindings::vaQueryConfigProfiles(
self.handle,
profiles.as_mut_ptr(),
&mut max_num_profiles,
)
})
.check()?;
// Safe because "profiles" is allocated with a "max_num_profiles"
// capacity. Additionally, vaQueryConfigProfiles will write to
// "max_num_entrypoints" entries in the vector.
unsafe {
profiles.set_len(max_num_profiles as usize);
};
Ok(profiles)
}
/// Returns a String describing some aspects of the VA implemenation on a
/// specific hardware accelerator. The format of the returned string is
/// vendor specific and at the discretion of the implementer. e.g. for the
/// Intel GMA500 implementation, an example would be: "Intel GMA500 -
/// 2.0.0.32L.0005"
pub fn query_vendor_string(&self) -> std::result::Result<String, &'static str> {
// Safe because `self` represents a valid VADisplay.
let vendor_string = unsafe { bindings::vaQueryVendorString(self.handle) };
if vendor_string.is_null() {
return Err("vaQueryVendorString() returned NULL");
}
// Safe because we check the whether the vendor_String pointer is NULL
Ok(unsafe { CStr::from_ptr(vendor_string) }
.to_string_lossy()
.to_string())
}
/// Query supported entrypoints for a given profile
pub fn query_config_entrypoints(
&self,
profile: bindings::VAProfile::Type,
) -> Result<Vec<bindings::VAEntrypoint::Type>> {
// Safe because `self` represents a valid VADisplay.
let mut max_num_entrypoints = unsafe { bindings::vaMaxNumEntrypoints(self.handle) };
let mut entrypoints = Vec::with_capacity(max_num_entrypoints as usize);
// Safe because `self` represents a valid VADisplay and the vector has
// "max_num_entrypoints" as capacity.
Status(unsafe {
bindings::vaQueryConfigEntrypoints(
self.handle,
profile,
entrypoints.as_mut_ptr(),
&mut max_num_entrypoints,
)
})
.check()?;
// Safe because "entrypoints" is allocated with a
// "max_num_entrypoints" capacity. Safe because
// vaQueryConfigEntrypoints will write to "max_num_entrypoints"
// entries in the vector.
unsafe {
entrypoints.set_len(max_num_entrypoints as usize);
}
Ok(entrypoints)
}
/// Get attributes for a given profile/entrypoint pair
pub fn get_config_attributes(
&self,
profile: bindings::VAProfile::Type,
entrypoint: bindings::VAEntrypoint::Type,
attributes: &mut Vec<bindings::VAConfigAttrib>,
) -> Result<()> {
// Safe because `self` represents a valid VADisplay. The vector length
// is passed to the C function, so it is impossible to write past the
// end of the vector's storage by mistake.
Status(unsafe {
bindings::vaGetConfigAttributes(
self.handle,
profile,
entrypoint,
attributes.as_mut_ptr(),
attributes.len() as i32,
)
})
.check()
}
/// Create VASurfaces by wrapping around a vaCreateSurfaces call
/// `rt_format` is the desired surface format. See VA_RT_FORMAT_*
/// `va_fourcc` is the desired pixel format. See VA_FOURCC_*
/// `width` is the surface width
/// `height` is the surface height
/// `usage_hint` gives the driver a hint of intended usage to optimize allocation (e.g. tiling)
/// `num_surfaces` is the number of surfaces to create
pub fn create_surfaces(
self: &Rc<Self>,
rt_format: u32,
va_fourcc: Option<u32>,
width: u32,
height: u32,
usage_hint: Option<UsageHint>,
num_surfaces: u32,
) -> Result<Vec<Surface>> {
Surface::new(
Rc::clone(self),
rt_format,
va_fourcc,
width,
height,
usage_hint,
num_surfaces,
)
}
/// Create a Context by wrapping around a vaCreateContext call.
/// `config` is the configuration for the context
/// `coded_width` is the coded picture width
/// `coded_height` is the coded picture height
/// `progressive` is whether only progressive frame pictures are present in the sequence
/// `surfaces` are a hint for the amount of surfaces tied to the context
pub fn create_context(
self: &Rc<Self>,
config: &Config,
coded_width: i32,
coded_height: i32,
surfaces: Option<&Vec<Surface>>,
progressive: bool,
) -> Result<Context> {
Context::new(
Rc::clone(self),
config,
coded_width,
coded_height,
surfaces,
progressive,
)
}
/// Create a VAConfig by wrapping around the vaCreateConfig call. `attrs`
/// describe the attributes to set for this config. A list of the supported
/// attributes for a given profile/entrypoint pair can be retrieved using
/// Display::get_config_attributes. Other attributes will take their default
/// values
pub fn create_config(
self: &Rc<Self>,
attrs: Option<Vec<bindings::VAConfigAttrib>>,
profile: bindings::VAProfile::Type,
entrypoint: bindings::VAEntrypoint::Type,
) -> Result<Config> {
Config::new(Rc::clone(self), attrs, profile, entrypoint)
}
/// A wrapper over vaQueryImageFormats.
pub fn query_image_formats(self: &Rc<Self>) -> Result<Vec<bindings::VAImageFormat>> {
// Safe because `self` represents a valid VADisplay
let mut num_image_formats = unsafe { bindings::vaMaxNumImageFormats(self.handle) };
let mut image_formats = Vec::with_capacity(num_image_formats as usize);
// Safe because `self` represents a valid VADisplay. The "image_formats"
// vector is properly initialized and a valid size is passed to the C
// function, so it is impossible to write past the end of their storage
// by mistake
Status(unsafe {
bindings::vaQueryImageFormats(
self.handle,
image_formats.as_mut_ptr(),
&mut num_image_formats,
)
})
.check()?;
// Safe because the C function will have writter to exactly
// "num_image_format" entries, which is known to be within the vector's
// capacity.
unsafe {
image_formats.set_len(num_image_formats as usize);
}
Ok(image_formats)
}
}
impl Drop for Display {
fn drop(&mut self) {
// Safe because `self` represents a valid VADisplay.
unsafe {
bindings::vaTerminate(self.handle);
// The File will close the DRM fd on drop.
}
}
}

View file

@ -0,0 +1,50 @@
// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use anyhow::{anyhow, Result};
use crate::bindings;
/// A wrapper over VAGenericValue so we can safely access the underlying union
/// members
#[derive(Debug)]
pub enum GenericValue {
/// A wrapper over VAGenericValueTypeInteger
Integer(i32),
/// A wrapper over VAGenericValueTypeFloat
Float(f32),
/// A wrapper over VAGenericValueTypePointer
Pointer(*mut std::os::raw::c_void),
/// A wrapper over VAGenericValueTypeFunc
Func(bindings::VAGenericFunc),
}
impl TryFrom<bindings::VAGenericValue> for GenericValue {
type Error = anyhow::Error;
fn try_from(value: bindings::VAGenericValue) -> Result<Self, Self::Error> {
// Safe because we check the type before accessing the union.
match value.type_ {
// Safe because we check the type before accessing the union.
bindings::VAGenericValueType::VAGenericValueTypeInteger => {
Ok(Self::Integer(unsafe { value.value.i }))
}
bindings::VAGenericValueType::VAGenericValueTypeFloat => {
Ok(Self::Float(unsafe { value.value.f }))
}
bindings::VAGenericValueType::VAGenericValueTypePointer => {
Ok(Self::Pointer(unsafe { value.value.p }))
}
bindings::VAGenericValueType::VAGenericValueTypeFunc => {
Ok(Self::Func(unsafe { value.value.fn_ }))
}
other => {
return Err(anyhow!(
"Conversion failed for unexpected VAGenericValueType: {}",
other
))
}
}
}
}

232
media/libva/src/image.rs Normal file
View file

@ -0,0 +1,232 @@
// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use anyhow::Result;
use crate::{
bindings,
picture::{Picture, PictureSync},
status::Status,
};
/// An owned VAImage that is tied to the lifetime of a given Picture.
/// A VAImage is used to either get the surface data to client memory, or
/// to copy image data in client memory to a surface.
pub struct Image<'a> {
/// The picture whose Surface we use in the vaGetImage call.
picture: &'a Picture<PictureSync>,
/// The VAImage returned by libva.
image: bindings::VAImage,
/// The mapped surface data.
data: &'a mut [u8],
/// Whether the image was derived using the `vaDeriveImage` API or created
/// using the `vaCreateImage` API.
derived: bool,
/// Tracks whether the underlying data has possibly been written to, i.e. an
/// encoder will create an Image and map its buffer in order to write to it,
/// so we must writeback later.
dirty: bool,
}
impl<'a> Image<'a> {
/// Creates a new `Image` either by calling `vaCreateImage` or
/// `vaDeriveImage`. Creating an Image depends on acquiring a ready Surface
/// from an underlying Picture. Note that Image has a borrowed Picture, so
/// it will be dropped before the underlying Surface is dropped, as mandated
/// by VAAPI.
///
/// # Arguments
/// * `picture` is the Picture that owns the Surface this Image will be created from.
/// * `format` is a VAImageFormat returned by the vaQueryImageFormats wrapper.
/// * `width` is the Image's desired width.
/// * `height` is the Image's desired height.
/// * `derive` whether to try deriving the image. Deriving may fail, in
/// which case vaCreateImage will be used instead.
pub fn new(
picture: &'a mut Picture<PictureSync>,
mut format: bindings::VAImageFormat,
width: u32,
height: u32,
derive: bool,
) -> Result<Self> {
// Safe because an all-zero byte-pattern represent a valid value for
// bindings::VAImage. Note that this is a FFI type and that it does not have
// any references in it.
let mut image: bindings::VAImage = Default::default();
let mut addr = std::ptr::null_mut();
let mut derived = false;
if derive {
derived = Image::derive_image(picture, &mut image)?;
}
if !derived {
Image::create_image(picture, &mut image, &mut format, width, height)?;
}
// Safe since `picture.inner.context` represents a valid VAContext.
// Image creation is ensured by either the vaDeriveImage or
// vaCreateImage APIs and vaGetImage is called if the VAImage was not
// derived, as mandated by VAAPI.
match Status(unsafe {
bindings::vaMapBuffer(
picture.inner().context().display().handle(),
image.buf,
&mut addr,
)
})
.check()
{
Ok(_) => {
// Safe since addr will point to data mapped onto our address
// space since we call vaGetImage above, which also guarantees
// that the data is valid for len * mem::size_of<u8>().
// Furthermore, we can only access the underlying memory using
// the slice below.
let data =
unsafe { std::slice::from_raw_parts_mut(addr as _, image.data_size as usize) };
Ok(Self {
picture,
image,
data,
derived,
dirty: false,
})
}
Err(e) => {
// Safe because `picture.inner.context` represents a valid
// VAContext and `image` represents a valid VAImage.
unsafe {
bindings::vaDestroyImage(
picture.inner().context().display().handle(),
image.image_id,
);
}
Err(e)
}
}
}
fn create_image(
picture: &'a mut Picture<PictureSync>,
image: &mut bindings::VAImage,
format: &mut bindings::VAImageFormat,
width: u32,
height: u32,
) -> Result<()> {
let dpy = picture.inner().context().display().handle();
// Safe because `picture.inner.context` represents a valid
// VAContext.
Status(unsafe { bindings::vaCreateImage(dpy, format, width as i32, height as i32, image) })
.check()?;
// Safe because `picture.inner.context` represents a valid VAContext,
// `picture.surface` represents a valid VASurface and `image` represents
// a valid `VAImage`.
if let Err(e) = Status(unsafe {
bindings::vaGetImage(
dpy,
picture.surface_mut().id(),
0,
0,
width,
height,
image.image_id,
)
})
.check()
{
// Safe since `image` represents a valid `VAImage`.
unsafe {
bindings::vaDestroyImage(dpy, image.image_id);
}
return Err(e);
}
Ok(())
}
fn derive_image(
picture: &'a mut Picture<PictureSync>,
image: &mut bindings::VAImage,
) -> Result<bool> {
let status = Status(unsafe {
bindings::vaDeriveImage(
picture.inner().context().display().handle(),
picture.surface_mut().id(),
image,
)
});
if status.0 == bindings::constants::VA_STATUS_ERROR_OPERATION_FAILED as i32 {
// The implementation can't derive, try the create API instead.
return Ok(false);
} else {
status.check()?;
Ok(true)
}
}
/// Get a reference to the underlying VAImage that describes this Image.
pub fn image(&self) -> &bindings::VAImage {
&self.image
}
}
impl<'a> AsRef<[u8]> for Image<'a> {
fn as_ref(&self) -> &[u8] {
self.data
}
}
impl<'a> AsMut<[u8]> for Image<'a> {
fn as_mut(&mut self) -> &mut [u8] {
self.dirty = true;
self.data
}
}
impl<'a> Drop for Image<'a> {
fn drop(&mut self) {
// Safe because `picture.inner.context` represents a valid VAContext,
// `picture.surface` represents a valid VASurface and `image` represents
// a valid `VAImage`. Lastly, the buffer is mapped in Image::new, so
// self.image.buf points to a valid VABufferID.
let surface = self.picture.surface();
if !self.derived && self.dirty {
unsafe {
bindings::vaPutImage(
self.picture.inner().context().display().handle(),
surface.id(),
self.image.image_id,
0,
0,
self.image.width as u32,
self.image.height as u32,
0,
0,
self.image.width as u32,
self.image.height as u32,
);
}
}
unsafe {
// Safe since the buffer is mapped in Image::new, so self.image.buf
// points to a valid VABufferID.
bindings::vaUnmapBuffer(
self.picture.inner().context().display().handle(),
self.image.buf,
);
}
unsafe {
// Safe since `self.image` represents a valid VAImage.
bindings::vaDestroyImage(
self.picture.inner().context().display().handle(),
self.image.image_id,
);
}
}
}

236
media/libva/src/lib.rs Normal file
View file

@ -0,0 +1,236 @@
// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! This module implements a lightweight and safe decoder interface over
//! `libva`. It is designed to concentrate all calls to unsafe methods in one
//! place
#![deny(missing_docs)]
mod bindings;
mod buffer;
mod buffer_type;
mod config;
mod context;
mod display;
mod generic_value;
mod image;
mod picture;
mod status;
mod surface;
mod usage_hint;
pub use bindings::constants;
pub use bindings::VAConfigAttrib;
pub use bindings::VAConfigAttribType;
pub use bindings::VAEntrypoint;
pub use bindings::VAImageFormat;
pub use bindings::VAProfile;
pub use bindings::VASurfaceAttribType;
pub use buffer::*;
pub use buffer_type::*;
pub use config::*;
pub use context::*;
pub use display::*;
pub use generic_value::*;
pub use image::*;
pub use picture::*;
pub use surface::*;
pub use usage_hint::*;
#[cfg(test)]
mod tests {
use std::rc::Rc;
use super::*;
/// Returns a 32-bit CRC for the visible part of `image`, which must be in NV12 format.
fn crc_nv12_image(image: &Image) -> u32 {
let data = image.as_ref();
let va_image = image.image();
let offsets = &va_image.offsets;
let pitches = &va_image.pitches;
let width = va_image.width as usize;
let height = va_image.height as usize;
// We only support NV12 images
assert_eq!(va_image.format.fourcc, u32::from_ne_bytes(*b"NV12"));
// Consistency check
assert_eq!(va_image.num_planes, 2);
let mut hasher = crc32fast::Hasher::new();
let offset = offsets[0] as usize;
let pitch = pitches[0] as usize;
let y_plane = data[offset..(offset + pitch * height)]
.chunks(pitch)
.map(|line| &line[0..width]);
let offset = offsets[1] as usize;
let pitch = pitches[1] as usize;
let uv_plane = data[offset..(offset + pitch * ((height + 1) / 2))]
.chunks(pitch)
.map(|line| &line[0..width]);
for line in y_plane.chain(uv_plane) {
hasher.update(line);
}
hasher.finalize()
}
#[test]
// Ignore this test by default as it requires libva-compatible hardware.
#[ignore]
fn libva_utils_mpeg2vldemo() {
// Adapted from <https://github.com/intel/libva-utils/blob/master/decode/mpeg2vldemo.cpp>
let display = Rc::new(Display::open().unwrap());
assert!(!display.query_vendor_string().unwrap().is_empty());
let profiles = display.query_config_profiles().unwrap();
assert!(!profiles.is_empty());
let profile = bindings::VAProfile::VAProfileMPEG2Main;
let entrypoints = display.query_config_entrypoints(profile).unwrap();
assert!(!entrypoints.is_empty());
assert!(entrypoints
.iter()
.any(|e| *e == bindings::VAEntrypoint::VAEntrypointVLD));
let format = bindings::constants::VA_RT_FORMAT_YUV420;
let width = 16;
let height = 16;
let mut attrs = vec![bindings::VAConfigAttrib {
type_: bindings::VAConfigAttribType::VAConfigAttribRTFormat,
value: 0,
}];
let entrypoint = bindings::VAEntrypoint::VAEntrypointVLD;
display
.get_config_attributes(profile, entrypoint, &mut attrs)
.unwrap();
assert!(attrs[0].value != bindings::constants::VA_ATTRIB_NOT_SUPPORTED);
assert!(attrs[0].value & bindings::constants::VA_RT_FORMAT_YUV420 != 0);
let config = display
.create_config(Some(attrs), profile, entrypoint)
.unwrap();
let mut surfaces = display
.create_surfaces(
format,
None,
width,
height,
Some(UsageHint::USAGE_HINT_DECODER),
1,
)
.unwrap();
let context = Rc::new(
display
.create_context(
&config,
width as i32,
(((height + 15) / 16) * 16) as i32,
Some(&surfaces),
true,
)
.unwrap(),
);
// The picture data is adapted from libva-utils at decode/mpeg2vldemo.cpp
// Data dump of a 16x16 MPEG2 video clip,it has one I frame
let mut mpeg2_clip: Vec<u8> = vec![
0x00, 0x00, 0x01, 0xb3, 0x01, 0x00, 0x10, 0x13, 0xff, 0xff, 0xe0, 0x18, 0x00, 0x00,
0x01, 0xb5, 0x14, 0x8a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0xb8, 0x00, 0x08,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0f, 0xff, 0xf8, 0x00, 0x00, 0x01, 0xb5,
0x8f, 0xff, 0xf3, 0x41, 0x80, 0x00, 0x00, 0x01, 0x01, 0x13, 0xe1, 0x00, 0x15, 0x81,
0x54, 0xe0, 0x2a, 0x05, 0x43, 0x00, 0x2d, 0x60, 0x18, 0x01, 0x4e, 0x82, 0xb9, 0x58,
0xb1, 0x83, 0x49, 0xa4, 0xa0, 0x2e, 0x05, 0x80, 0x4b, 0x7a, 0x00, 0x01, 0x38, 0x20,
0x80, 0xe8, 0x05, 0xff, 0x60, 0x18, 0xe0, 0x1d, 0x80, 0x98, 0x01, 0xf8, 0x06, 0x00,
0x54, 0x02, 0xc0, 0x18, 0x14, 0x03, 0xb2, 0x92, 0x80, 0xc0, 0x18, 0x94, 0x42, 0x2c,
0xb2, 0x11, 0x64, 0xa0, 0x12, 0x5e, 0x78, 0x03, 0x3c, 0x01, 0x80, 0x0e, 0x80, 0x18,
0x80, 0x6b, 0xca, 0x4e, 0x01, 0x0f, 0xe4, 0x32, 0xc9, 0xbf, 0x01, 0x42, 0x69, 0x43,
0x50, 0x4b, 0x01, 0xc9, 0x45, 0x80, 0x50, 0x01, 0x38, 0x65, 0xe8, 0x01, 0x03, 0xf3,
0xc0, 0x76, 0x00, 0xe0, 0x03, 0x20, 0x28, 0x18, 0x01, 0xa9, 0x34, 0x04, 0xc5, 0xe0,
0x0b, 0x0b, 0x04, 0x20, 0x06, 0xc0, 0x89, 0xff, 0x60, 0x12, 0x12, 0x8a, 0x2c, 0x34,
0x11, 0xff, 0xf6, 0xe2, 0x40, 0xc0, 0x30, 0x1b, 0x7a, 0x01, 0xa9, 0x0d, 0x00, 0xac,
0x64,
];
let picture_coding_extension =
MPEG2PictureCodingExtension::new(0, 3, 0, 1, 0, 0, 0, 0, 0, 1, 1);
let pic_param = PictureParameterBufferMPEG2::new(
16,
16,
0xffffffff,
0xffffffff,
1,
0xffff,
&picture_coding_extension,
);
let pic_param = BufferType::PictureParameter(PictureParameter::MPEG2(pic_param));
let iq_matrix = IQMatrixBufferMPEG2::new(
1,
1,
0,
0,
[
8, 16, 16, 19, 16, 19, 22, 22, 22, 22, 22, 22, 26, 24, 26, 27, 27, 27, 26, 26, 26,
26, 27, 27, 27, 29, 29, 29, 34, 34, 34, 29, 29, 29, 27, 27, 29, 29, 32, 32, 34, 34,
37, 38, 37, 35, 35, 34, 35, 38, 38, 40, 40, 40, 48, 48, 46, 46, 56, 56, 58, 69, 69,
83,
],
[
16, 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; 64],
[0; 64],
);
let iq_matrix = BufferType::IQMatrix(IQMatrix::MPEG2(iq_matrix));
let slice_param = SliceParameterBufferMPEG2::new(150, 0, 0, 38, 0, 0, 2, 0);
let slice_param = BufferType::SliceParameter(SliceParameter::MPEG2(slice_param));
let test_data_offset = 47;
let slice_data = BufferType::SliceData(mpeg2_clip.drain(test_data_offset..).collect());
let buffers = vec![
context.create_buffer(pic_param).unwrap(),
context.create_buffer(slice_param).unwrap(),
context.create_buffer(iq_matrix).unwrap(),
context.create_buffer(slice_data).unwrap(),
];
let mut picture = Picture::new(0, Rc::clone(&context), surfaces.remove(0));
for buffer in buffers {
picture.add_buffer(buffer);
}
// Actual client code can just chain the calls.
let picture = picture.begin().unwrap();
let picture = picture.render().unwrap();
let picture = picture.end().unwrap();
let mut picture = picture.sync().unwrap();
// Test whether we can map the resulting surface to obtain the raw yuv
// data
let image_fmts = display.query_image_formats().unwrap();
let image_fmt = image_fmts
.into_iter()
.find(|f| f.fourcc == bindings::constants::VA_FOURCC_NV12)
.expect("No valid VAImageFormat found for NV12");
let image = Image::new(&mut picture, image_fmt, width, height, false).unwrap();
assert_eq!(crc_nv12_image(&image), 0xa5713e52);
}
}

229
media/libva/src/picture.rs Normal file
View file

@ -0,0 +1,229 @@
// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::{marker::PhantomData, rc::Rc};
use anyhow::Result;
use crate::{bindings, buffer::Buffer, context::Context, status::Status, surface::Surface};
// Use the sealed trait pattern to make sure that new states are not created in
// caller code. More information about the sealed trait pattern can be found at
// <https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed>
mod private {
pub trait Sealed {}
}
/// A `Picture` will only have valid YUV data after a sequence of operations are
/// performed in a particular order. This order correspond to the following
/// VA-API calls: vaBeginPicture, vaRenderPicture, vaEndPicture and
/// vaSyncSurface. This trait enforces this ordering by implementing the
/// Typestate pattern to constrain what operations are available in what
/// particular states.
///
/// The states for the state machine are:
/// PictureNew -> PictureBegin
/// PictureBegin -> PictureRender
/// PictureRender ->PictureEnd
/// PictureEnd -> PictureSync
///
/// Where the surface can be reclaimed in both PictureNew and PictureSync, as
/// either no operation took place (as in PictureNew), or it is guaranteed that
/// the operation has already completed (as in PictureSync)
///
/// More information about the Typestate pattern can be found
/// at <http://cliffle.com/blog/rust-typestate/>
pub trait PictureState: private::Sealed {}
/// Represents a `Picture` that has just been created.
pub enum PictureNew {}
impl PictureState for PictureNew {}
impl private::Sealed for PictureNew {}
/// Represents a `Picture` after `vaBeginPicture` has been called.
pub enum PictureBegin {}
impl PictureState for PictureBegin {}
impl private::Sealed for PictureBegin {}
/// Represents a `Picture` after `vaRenderPicture` has been called.
pub enum PictureRender {}
impl PictureState for PictureRender {}
impl private::Sealed for PictureRender {}
/// Represents a `Picture` after `vaEndPicture` has been called.
pub enum PictureEnd {}
impl PictureState for PictureEnd {}
impl private::Sealed for PictureEnd {}
/// Represents a `Picture` after `vaSyncSurface` has been called on the
/// underlying surface.
pub enum PictureSync {}
impl PictureState for PictureSync {}
impl private::Sealed for PictureSync {}
/// Represents a state where one can reclaim the underlying `Surface` for this
/// `Picture`. This is true when either no decoding has been initiated or,
/// alternatively, when the decoding operation has completed for the underlying
/// `vaSurface`
pub trait PictureReclaimableSurface: PictureState + private::Sealed {}
impl PictureReclaimableSurface for PictureNew {}
impl PictureReclaimableSurface for PictureSync {}
pub(crate) struct PictureInner {
/// Identifies this picture
frame_number: u32,
/// A context associated with this picture
context: Rc<Context>,
/// Contains the buffers used to decode the data
buffers: Vec<Buffer>,
/// Contains the actual decoded data.
surface: Surface,
}
impl PictureInner {
/// Returns a reference to the Context used by the Picture
pub(crate) fn context(&self) -> Rc<Context> {
Rc::clone(&self.context)
}
}
/// An abstraction over VABuffers and a VASurface suitable for decoding with
/// vaBeginPicture, vaRenderPicture, vaEndPicture "surface" will have valid raw
/// picture data after "begin", "render", "end" and "sync" are called, in this
/// order.
pub struct Picture<S: PictureState> {
inner: Box<PictureInner>,
phantom: std::marker::PhantomData<S>,
}
impl Picture<PictureNew> {
/// Creates a new Picture with a given `frame_number` to identify it.
/// `surface` is the underlying surface that libva will render to.
pub fn new(frame_number: u32, context: Rc<Context>, surface: Surface) -> Self {
Self {
inner: Box::new(PictureInner {
frame_number,
context,
buffers: Default::default(),
surface,
}),
phantom: PhantomData,
}
}
/// Add buffers to a picture
pub fn add_buffer(&mut self, buffer: Buffer) {
self.inner.buffers.push(buffer);
}
/// A wrapper around vaBeginPicture
pub fn begin(self) -> Result<Picture<PictureBegin>> {
// Safe because `self.inner.context` represents a valid VAContext and
// `self.inner.surface` represents a valid VASurface.
Status(unsafe {
bindings::vaBeginPicture(
self.inner.context.display().handle(),
self.inner.context.id(),
self.inner.surface.id(),
)
})
.check()?;
Ok(Picture {
inner: self.inner,
phantom: PhantomData,
})
}
}
impl Picture<PictureBegin> {
/// A wrapper around vaRenderPicture
pub fn render(self) -> Result<Picture<PictureRender>> {
// Safe because `self.inner.context` represents a valid VAContext and
// `self.inner.surface` represents a valid VASurface. `buffers` point to
// a Rust struct and the vector length is passed to the C function, so
// it is impossible to write past the end of the vector's storage by
// mistake.
Status(unsafe {
bindings::vaRenderPicture(
self.inner.context.display().handle(),
self.inner.context.id(),
Buffer::as_id_vec(&self.inner.buffers).as_mut_ptr(),
self.inner.buffers.len() as i32,
)
})
.check()?;
Ok(Picture {
inner: self.inner,
phantom: PhantomData,
})
}
}
impl Picture<PictureRender> {
/// A wrapper around vaEndPicture
pub fn end(self) -> Result<Picture<PictureEnd>> {
// Safe because `self.inner.context` represents a valid VAContext.
Status(unsafe {
bindings::vaEndPicture(
self.inner.context.display().handle(),
self.inner.context.id(),
)
})
.check()?;
Ok(Picture {
inner: self.inner,
phantom: PhantomData,
})
}
}
impl Picture<PictureEnd> {
/// Syncs the picture, ensuring that any pending decode operations are
/// complete when this call returns
pub fn sync(self) -> Result<Picture<PictureSync>> {
self.inner.surface.sync()?;
Ok(Picture {
inner: self.inner,
phantom: PhantomData,
})
}
}
impl Picture<PictureSync> {
/// Returns a reference to the underlying `Surface` for this
/// `Picture`
pub fn surface(&self) -> &Surface {
&self.inner.surface
}
/// Returns a mutable reference to the underlying `Surface` for this
/// `Picture`
pub fn surface_mut(&mut self) -> &mut Surface {
&mut self.inner.surface
}
}
impl<S: PictureState> Picture<S> {
/// Get the frame number for this picture.
pub fn frame_number(&self) -> u32 {
self.inner.frame_number
}
/// Returns a reference to the `inner` struct
pub(crate) fn inner(&self) -> &PictureInner {
self.inner.as_ref()
}
}
impl<S: PictureReclaimableSurface> Picture<S> {
/// Reclaim ownership of the Surface, consuming the picture in the process.
/// Useful if the Surface is part of a pool.
pub fn take_surface(self) -> Surface {
self.inner.surface
}
}

31
media/libva/src/status.rs Normal file
View file

@ -0,0 +1,31 @@
// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::ffi::CStr;
use anyhow::{anyhow, Result};
use crate::bindings;
/// Wrapper over VAStatus, calling check() returns a Error if the status is
/// not VA_STATUS_SUCCESS
#[must_use = "VAStatus might not be VA_STATUS_SUCCESS."]
pub(crate) struct Status(pub bindings::VAStatus);
impl Status {
/// Convenience function to convert from Status to Result
pub(crate) fn check(&self) -> Result<()> {
if self.0 == bindings::constants::VA_STATUS_SUCCESS as i32 {
Ok(())
} else {
// Safe because vaErrorStr will return a pointer to a statically
// allocated, null terminated C String. The pointer is guaranteed to
// never be null
let err_str = unsafe { CStr::from_ptr(bindings::vaErrorStr(self.0)) }
.to_str()
.unwrap();
Err(anyhow!("VA-API error: {}: {}", self.0, err_str))
}
}
}

139
media/libva/src/surface.rs Normal file
View file

@ -0,0 +1,139 @@
// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::rc::Rc;
use anyhow::Result;
use crate::{bindings, display::Display, status::Status, UsageHint};
/// An owned VASurface that is tied to the lifetime of a particular VADisplay
pub struct Surface {
display: Rc<Display>,
id: bindings::VASurfaceID,
}
impl Surface {
/// Create VASurfaces by wrapping around a vaCreateSurfaces call
/// `rt_format` is the desired surface format. See VA_RT_FORMAT_*
/// `va_fourcc` is the desired pixel format. See VA_FOURCC_*
/// `width` is the surface width
/// `height` is the surface height
/// `usage_hint` gives the driver a hint of intended usage to optimize allocation (e.g. tiling)
/// `num_surfaces` is the number of surfaces to create
pub(crate) fn new(
display: Rc<Display>,
rt_format: u32,
va_fourcc: Option<u32>,
width: u32,
height: u32,
usage_hint: Option<UsageHint>,
num_surfaces: u32,
) -> Result<Vec<Self>> {
let mut attrs = vec![];
if let Some(usage_hint) = usage_hint {
let attr = bindings::VASurfaceAttrib {
type_: bindings::VASurfaceAttribType::VASurfaceAttribUsageHint,
flags: bindings::constants::VA_SURFACE_ATTRIB_SETTABLE,
value: bindings::VAGenericValue {
type_: bindings::VAGenericValueType::VAGenericValueTypeInteger,
value: bindings::_VAGenericValue__bindgen_ty_1 {
i: usage_hint.bits() as i32,
},
},
};
attrs.push(attr);
}
if let Some(fourcc) = va_fourcc {
let attr = bindings::VASurfaceAttrib {
type_: bindings::VASurfaceAttribType::VASurfaceAttribPixelFormat,
flags: bindings::constants::VA_DISPLAY_ATTRIB_SETTABLE,
value: bindings::VAGenericValue {
type_: bindings::VAGenericValueType::VAGenericValueTypeInteger,
value: bindings::_VAGenericValue__bindgen_ty_1 { i: fourcc as i32 },
},
};
attrs.push(attr);
}
let mut surfaces = Vec::with_capacity(num_surfaces as usize);
// Safe because `self` represents a valid VADisplay. The "surface" and
// "attrs" vectors are properly initialized and valid sizes are passed
// to the C function, so it is impossible to write past the end of their
// storage by mistake
Status(unsafe {
bindings::vaCreateSurfaces(
display.handle(),
rt_format,
width,
height,
surfaces.as_mut_ptr(),
num_surfaces,
attrs.as_mut_ptr(),
attrs.len() as u32,
)
})
.check()?;
// Safe because the C function will have written to exactly
// "num_surface" entries, which is known to be within the vector's
// capacity.
unsafe {
surfaces.set_len(num_surfaces as usize);
}
let va_surfaces = surfaces
.iter()
.map(|&id| Self {
display: Rc::clone(&display),
id,
})
.collect();
Ok(va_surfaces)
}
/// This function blocks until all pending operations on the render target
/// have been completed. Upon return it is safe to use the render target
/// for a different picture.
pub fn sync(&self) -> Result<()> {
// Safe because `self` represents a valid VASurface.
Status(unsafe { bindings::vaSyncSurface(self.display.handle(), self.id) }).check()
}
/// Convenience function to return a VASurfaceID vector. Useful to interface
/// with the C API where a surface array might be needed.
pub fn as_id_vec(surfaces: &[Self]) -> Vec<bindings::VASurfaceID> {
surfaces.iter().map(|surface| surface.id).collect()
}
/// A wrapper over vaQuerySurfaceStatus. Finds out any pending ops on the
/// render target.
pub fn query_status(&self) -> Result<bindings::VASurfaceStatus::Type> {
let mut status: bindings::VASurfaceStatus::Type = 0;
// Safe because `self` represents a valid VASurface.
Status(unsafe {
bindings::vaQuerySurfaceStatus(self.display.handle(), self.id, &mut status)
})
.check()?;
Ok(status)
}
/// Return the VASurfaceID for this surface
pub fn id(&self) -> bindings::VASurfaceID {
self.id
}
}
impl Drop for Surface {
fn drop(&mut self) {
// Safe because `self` represents a valid VASurface.
unsafe { bindings::vaDestroySurfaces(self.display.handle(), &mut self.id, 1) };
}
}

View file

@ -0,0 +1,27 @@
// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use bitflags::bitflags;
use crate::constants;
bitflags! {
/// Gives the driver a hint of intended usage to optimize allocation (e.g. tiling)
pub struct UsageHint: u32 {
/// Surface usage not indicated
const USAGE_HINT_GENERIC = constants::VA_SURFACE_ATTRIB_USAGE_HINT_GENERIC;
/// Surface used by video decoder
const USAGE_HINT_DECODER = constants::VA_SURFACE_ATTRIB_USAGE_HINT_DECODER;
/// Surface used by video encoder
const USAGE_HINT_ENCODER = constants::VA_SURFACE_ATTRIB_USAGE_HINT_ENCODER;
/// Surface read by video post-processing
const USAGE_HINT_VPP_READ = constants::VA_SURFACE_ATTRIB_USAGE_HINT_VPP_READ;
/// Surface written by video post-processing
const USAGE_HINT_VPP_WRITE = constants::VA_SURFACE_ATTRIB_USAGE_HINT_VPP_WRITE;
/// Surface used for display
const USAGE_HINT_DISPLAY = constants::VA_SURFACE_ATTRIB_USAGE_HINT_DISPLAY;
/// Surface used for export to third-party APIs, e.g. via vaExportSurfaceHandle()
const USAGE_HINT_EXPORT = constants::VA_SURFACE_ATTRIB_USAGE_HINT_EXPORT;
}
}

View file

@ -23,6 +23,7 @@ dirs=(
kernel_loader
kvm_sys
media/ffmpeg
media/libva
media/libvda
net_sys
vfio_sys

View file

@ -60,6 +60,7 @@ WIN64_DISABLED_CRATES = [
"io_uring",
"kvm",
"libcras_stub",
"libva",
"libvda",
"minijail-sys",
"minijail",
@ -112,6 +113,15 @@ CRATE_OPTIONS: Dict[str, List[TestOption]] = {
TestOption.DO_NOT_RUN_ON_FOREIGN_KERNEL,
], # b/181674144
"libcrosvm_control": [TestOption.DO_NOT_BUILD_ARMHF], # b/210015864
"libva": [
# Libva only makes sense for x86 Linux platforms, disable building on others.
TestOption.DO_NOT_BUILD_AARCH64,
TestOption.DO_NOT_BUILD_ARMHF,
TestOption.DO_NOT_BUILD_WIN64,
# Only run libva on Linux x86. Note that all tests are not enabled, see b/238047780.
TestOption.DO_NOT_RUN_AARCH64,
TestOption.DO_NOT_RUN_ARMHF,
],
"libvda": [TestOption.DO_NOT_BUILD], # b/202293971
"rutabaga_gfx": [TestOption.DO_NOT_BUILD_ARMHF], # b/210015864
"vhost": [TestOption.DO_NOT_RUN_ON_FOREIGN_KERNEL],