Vendor things

This commit is contained in:
John Doty 2024-03-08 11:03:01 -08:00
parent 5deceec006
commit 977e3c17e5
19434 changed files with 10682014 additions and 0 deletions

View file

@ -0,0 +1 @@
{"files":{"CHANGELOG.md":"fb266dc992ce98787c6afa9f76d7875080f66c6b4fbf5b6127d539bca710f838","Cargo.toml":"dcf8924779fc545761ebbd778ddfef0f41ace80bb50267e4853b5eedfd183a21","README.md":"9adf2ca8412635302cbfa7d9a55537e92b3c1e3e3e2b963f4ecc478d774b4c96","src/asset.rs":"c7c07a9ca655fbd8f1c61cf9039689aa5ac5c35e170d440bef750a430e6b9210","src/audio.rs":"62658a036a49d1b474f0ee426c3bded79e5d06c1a1932329078927911d72b672","src/bitmap.rs":"51d95ef13563e47f245c0c102f2da86f5b2dd868a625a4d5ae1f22c661fafcfc","src/configuration.rs":"25ff6224868b09f89cdff84bc7a80c06de7f7ab71524561fd1b70ab852805c44","src/event.rs":"bfd9f82ca0b058675056264369fc54af77519e41a5a4164e4f51519fbea2881f","src/hardware_buffer.rs":"c42e33c5b06ab692567471e7a345a9e3791cd915352455cc0a0a3edea594a31b","src/hardware_buffer_format.rs":"bc54a2333c248cb3f8160c54ff27bf2fad2f50eb0d6ec7360e5cec59bf48167a","src/input_queue.rs":"d888e2bea21fc9853c35afc06146013604ce466714382ebdc26352ba52bb98bb","src/lib.rs":"e5437e6cac64916c0856569e7b8dcc626718a5766d2ec2c1252cfcb9f0bae77a","src/looper.rs":"c1f48c56d5d02b781dbb751d143eddd7a2689243d654ff9afd210b413e033b34","src/media/error.rs":"caeec11e39cb3433a9afa259677eeadf42f73601b6ff6ee597f9ee914c7fbc5c","src/media/image_reader.rs":"489951386fe71209d174a19dfa9444383337825a4371cf342274182703811646","src/media/media_codec.rs":"fd23d4cdec99bbfbeac8313cca402fd4d06d9cdf7f0302e38cfcb1fd135cddc9","src/media/mod.rs":"b3f79d99d1f4b4dd6f9d70be4c6666765489fcb038767446e9088ca0283e64cf","src/native_activity.rs":"c8d1b4b36944e2f64d74fbaa69cc5da199cfa12749297f7f353368344c78173a","src/native_window.rs":"e563f4634a5a83ad30c64873e3e8ed5e40ffb70f86081ad00a8738b9d7590d4a","src/surface_texture.rs":"ad323d5c2dff6f72c1ed9a86ff5cec88351ec9f3e22289e1499434e66e80213c","src/trace.rs":"f8013db7e2ea18cf60a59487e3028844098c3cc21d935da46509aded1e7bf97f","src/utils.rs":"90baded328bd0cabcbea799649a1ffa6b7f7aaa9009b941902a98245c220b646"},"package":"451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0"}

67
third-party/vendor/ndk/CHANGELOG.md vendored Normal file
View file

@ -0,0 +1,67 @@
# Unreleased
# 0.7.0 (2022-07-24)
- hardware_buffer: Make `HardwareBuffer::as_ptr()` public for interop with Vulkan. (#213)
- **Breaking:** `Configuration::country()` now returns `None` when the country is unset (akin to `Configuration::language()`). (#220)
- Add `MediaCodec` and `MediaFormat` bindings. (#216)
- **Breaking:** Upgrade to [`ndk-sys 0.4.0`](../ndk-sys/CHANGELOG.md#040-2022-07-XXXX) and use new `enum` newtype wrappers. (#245)
- native_window: Use `release`/`acquire` for `Drop` and `Clone` respectively. (#207)
- **Breaking:** audio: Rename from `aaudio` to `audio` and drop `A` prefix. (#273)
- Implement `HasRawWindowHandle` directly on `NativeWindow`. (#274, #319)
- **Breaking:** native_activity: Replace `CStr` return types with `Path`. (#279)
- native_window: Add `format()` getter and `set_buffers_geometry()` setter. (#276)
- native_activity: Add `set_window_format()` setter. (#277)
- native_activity: Add `set_window_flags()` to change window behavior. (#278)
- Add `SurfaceTexture` bindings. (#267)
- Improve library and structure documentation, linking back to the NDK docs more rigorously. (#290)
- **Breaking:** input_queue: `get_event()` now returns a `Result` with `std::io::Error`; `InputQueueError` has been removed. (#292)
- **Breaking:** input_queue: `has_events()` now returns a `bool` directly without being wrapped in `Result`. (#294)
- **Breaking:** hardware_buffer: `HardwareBufferError` has been removed and replaced with `std::io::Error` in return types. (#295)
- Fixed `HardwareBuffer` leak on buffers returned from `AndroidBitmap::get_hardware_buffer()`. (#296)
- **Breaking:** Update `jni` crate (used in public API) from `0.18` to `0.19`. (#300)
- hardware_buffer: Made `HardwareBufferDesc` fields `pub`. (#313)
- **Breaking:** Remove `hardware_buffer` and `trace` features in favour of using `api-level-26` or `api-level-23` directly. (#320)
# 0.6.0 (2022-01-05)
- **Breaking:** Upgrade to [`ndk-sys 0.3.0`](../ndk-sys/CHANGELOG.md#030-2022-01-05) and migrate to `jni-sys` types that it now directly uses in its bindings. (#209 / #214)
# 0.5.0 (2021-11-22)
- **Breaking:** Replace `add_fd_with_callback` `ident` with constant value `ALOOPER_POLL_CALLBACK`,
as per https://developer.android.com/ndk/reference/group/looper#alooper_addfd.
- **Breaking:** Accept unboxed closure in `add_fd_with_callback`.
- aaudio: Replace "Added in" comments with missing `#[cfg(feature)]`.
- aaudio: Add missing `fn get_allowed_capture_policy()`.
- configuration: Add missing `api-level-30` feature to `fn screen_round()`.
# 0.4.0 (2021-08-02)
- **Breaking:** Model looper file descriptor events integer as `bitflags`.
# 0.3.0 (2021-01-30)
- **Breaking:** Looper `ident` not passed in `data` pointer anymore.
`attach_looper` now only sets the `ident` field when attaching an
`InputQueue` to a `ForeignLooper`.
If you are relying on `Poll::Event::data` to tell event fd and
input queue apart, please use `Poll::Event::ident` and the new
constants introduced in `ndk-glue`!
# 0.2.1 (2020-10-15)
- Fix documentation build on docs.rs
# 0.2.0 (2020-09-15)
- **Breaking:** Updated to use [ndk-sys 0.2.0](../ndk-sys/CHANGELOG.md#020-2020-09-15)
- Added `media` bindings
- Added `bitmap` and `hardware_buffer` bindings
- Added `aaudio` bindings
- Fixed assets directory path to be relative to the manifest
- Added `trace` feature for native tracing
# 0.1.0 (2020-04-22)
- Initial release! 🎉

98
third-party/vendor/ndk/Cargo.toml vendored Normal file
View file

@ -0,0 +1,98 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
name = "ndk"
version = "0.7.0"
authors = ["The Rust Windowing contributors"]
description = "Safe Rust bindings to the Android NDK"
homepage = "https://github.com/rust-windowing/android-ndk-rs"
documentation = "https://docs.rs/android-ndk"
readme = "README.md"
keywords = [
"android",
"ndk",
]
license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-windowing/android-ndk-rs"
[package.metadata.docs.rs]
features = [
"jni",
"jni-glue",
"all",
]
rustdoc-args = [
"--cfg",
"docsrs",
]
targets = [
"aarch64-linux-android",
"armv7-linux-androideabi",
"i686-linux-android",
"x86_64-linux-android",
]
[dependencies.bitflags]
version = "1.2.1"
[dependencies.ffi]
version = "0.4.0"
package = "ndk-sys"
[dependencies.jni]
version = "0.19"
optional = true
[dependencies.jni-glue]
version = "0.0.10"
optional = true
[dependencies.jni-sys]
version = "0.3.0"
[dependencies.num_enum]
version = "0.5.1"
[dependencies.raw-window-handle]
version = "0.5"
[dependencies.thiserror]
version = "1.0.23"
[features]
all = [
"audio",
"bitmap",
"media",
"api-level-30",
]
api-level-23 = []
api-level-24 = ["api-level-23"]
api-level-25 = ["api-level-24"]
api-level-26 = ["api-level-25"]
api-level-27 = ["api-level-26"]
api-level-28 = ["api-level-27"]
api-level-29 = ["api-level-28"]
api-level-30 = ["api-level-29"]
audio = [
"ffi/audio",
"api-level-26",
]
bitmap = ["ffi/bitmap"]
media = ["ffi/media"]
test = [
"ffi/test",
"jni",
"jni-glue",
"all",
]

175
third-party/vendor/ndk/README.md vendored Normal file
View file

@ -0,0 +1,175 @@
# Rust on Android
[![Rust](https://github.com/rust-windowing/android-ndk-rs/workflows/Rust/badge.svg)](https://github.com/rust-windowing/android-ndk-rs/actions) ![MIT license](https://img.shields.io/badge/License-MIT-green.svg) ![APACHE2 license](https://img.shields.io/badge/License-APACHE2-green.svg)
Libraries and tools for Rust programming on Android targets:
Name | Description | Badges
--- | --- | ---
[`ndk-sys`](./ndk-sys) | Raw FFI bindings to the NDK | [![crates.io](https://img.shields.io/crates/v/ndk-sys.svg)](https://crates.io/crates/ndk-sys) [![crates.io](https://docs.rs/ndk-sys/badge.svg)](https://docs.rs/ndk-sys)
[`ndk`](./ndk) | Safe abstraction of the bindings | [![crates.io](https://img.shields.io/crates/v/ndk.svg)](https://crates.io/crates/ndk) [![crates.io](https://docs.rs/ndk/badge.svg)](https://docs.rs/ndk)
[`ndk-context`](./ndk-context) | Android handles | [![crates.io](https://img.shields.io/crates/v/ndk-context.svg)](https://crates.io/crates/ndk-context)
[`ndk-glue`](./ndk-glue) | Startup code | [![crates.io](https://img.shields.io/crates/v/ndk-glue.svg)](https://crates.io/crates/ndk-glue) [![crates.io](https://docs.rs/ndk-glue/badge.svg)](https://docs.rs/ndk-glue)
[`ndk-build`](./ndk-build) | Everything for building apk's | [![crates.io](https://img.shields.io/crates/v/ndk-build.svg)](https://crates.io/crates/ndk-build) [![crates.io](https://docs.rs/ndk-build/badge.svg)](https://docs.rs/ndk-build)
[`cargo-apk`](./cargo-apk) | Build tool | [![crates.io](https://img.shields.io/crates/v/cargo-apk.svg)](https://crates.io/crates/cargo-apk) [![crates.io](https://docs.rs/cargo-apk/badge.svg)](https://docs.rs/cargo-apk)
See [`ndk-examples`](./ndk-examples) for examples using the NDK and the README files of the crates for more details.
## Supported NDK versions
`android-ndk-rs` aims to support at least the `LTS` and `Rolling Release` branches of the NDK, as described on [their wiki](https://github.com/android/ndk/wiki#supported-downloads). Additionally the `Beta Release` might be supported to prepare for an upcoming release.
As of writing (2021-07-24) the following NDKs are tested:
Branch | Version | Status | Working
-|-|:-:|:-:
r18 | 18.1.5063045 | _Deprecated_ | :x:
r19 | 19.2.5345600 | _Deprecated_ | :heavy_check_mark:
r20 | 20.1.5948944 | _Deprecated_ | :heavy_check_mark:
r21 | 21.4.7075529 | _Deprecated_ | :heavy_check_mark:
r22 | 22.1.7171670 | _Deprecated_ | :heavy_check_mark:
r23 | beta 1/2 | _Deprecated_ | :heavy_check_mark:
r23 | 23.0.7272597-beta3 | _Deprecated_ | :heavy_check_mark: Workaround in [#189](https://github.com/rust-windowing/android-ndk-rs/pull/189)
r23 | 23.1.7779620 | LTS | :heavy_check_mark: Workaround in [#189](https://github.com/rust-windowing/android-ndk-rs/pull/189)
r24 | 24.0.7856742-beta1 | Rolling Release | :heavy_check_mark: Workaround in [#189](https://github.com/rust-windowing/android-ndk-rs/pull/189)
## Quick start: `Hello World` crate on Android
Quick start setting up a new project with support for Android, using the `ndk-glue` layer for communicating with the Android framework through [`NativeActivity`](https://developer.android.com/reference/android/app/NativeActivity) and `cargo-apk` for packaging a crate in an Android `.apk` file.
This short guide can also be used as a reference for converting existing crates to be runnable on Android.
### 1. Install the Android NDK and SDK
Make sure the Android NDK is installed, together with a target platform (`30` by default), `build-tools` and `platform-tools`, using either the [`sdkmanager`](https://developer.android.com/studio/command-line/sdkmanager) or [Android Studio](https://developer.android.com/studio/projects/install-ndk).
### 2. Create a new library crate
```console
$ cargo new hello_world_android --lib
```
Never name your project `android` as this results in a target binary named `libandroid.so` which is also the name of Android's framework library: this will fail to link.
### 3. Configure crate for use on Android
Add the `ndk-glue` dependency to your crate.
`Cargo.toml`
```toml
# This dependency will only be included when targeting Android
[target.'cfg(target_os = "android")'.dependencies]
ndk-glue = "xxx" # Substitute this with the latest ndk-glue version you wish to use
```
Then configure the library target to be compiled to a Rust `lib` (for use in an executable on desktop) and a `cdylib` to create a native binary that can be bundled in the final `.apk` and loaded by Android.
`Cargo.toml`
```toml
[lib]
crate-type = ["lib", "cdylib"]
```
### 4. Wrap entry point with `ndk-glue`
Create a `main` function holding your code in the library portion of the crate, and wrap it in the `ndk_glue::main` attribute macro when targeting Android.
`src/lib.rs`
```rust
#[cfg_attr(target_os = "android", ndk_glue::main(backtrace = "on"))]
pub fn main() {
println!("Hello World");
}
```
See the [`ndk-macro` documentation](./ndk-macro/README.md) for more options.
Additionally, to make this crate runnable outside of Android, create a binary that calls the main function in the library.
`src/main.rs`
```rust
fn main() {
hello_world_android::main()
}
```
As a sanity check, run this binary to make sure everything is set up correctly:
```console
$ cargo run
```
### 5. Run the crate on your Android device
Install `cargo apk` for building, running and debugging your application:
```console
$ cargo install cargo-apk
```
We can now directly execute our `Hello World` application on a real connected device or an emulator:
```console
$ cargo apk run
```
If the crate includes a runnable binary as suggested above, you will likely be greeted by the following error:
```console
$ cargo apk run
error: extra arguments to `rustc` can only be passed to one target, consider filtering
the package by passing, e.g., `--lib` or `--bin NAME` to specify a single target
Error: Command `cargo rustc --target aarch64-linux-android -- -L hello_world_android/target/cargo-apk-temp-extra-link-libraries` had a non-zero exit code.
```
To solve this, add `--lib` to the run invocation, like so:
```console
$ cargo apk run --lib
```
### 6. Inspect the output
`ndk-glue` redirects stdout and stderr to Android `logcat`, including the `println!("Hello World")` by the example above. See [Logging and stdout](##Logging-and-stdout) below how to access it.
## Rendering to the window
Android native apps have no easy access to [Android's User Interface](https://developer.android.com/guide/topics/ui) functionality (bar [JNI](##jni) interop). Applications can instead draw pixels directly to the window using [`ANativeWindow_lock`](https://developer.android.com/ndk/reference/group/a-native-window#group___a_native_window_1ga0b0e3b7d442dee83e1a1b42e5b0caee6), or use a graphics API like OpenGL or Vulkan for high performance rendering.
## Logging and stdout
Stdout is redirected to the android log api when using `ndk-glue`. Any logger that logs to
stdout, like `println!`, should therefore work.
To filter on this output in `logcat`:
```console
$ adb logcat RustStdoutStderr:D *:S
```
### Android logger
Enable the `"logger"` feature on the `ndk-glue` macro and configure its log tag and debug level through the attribute macro:
`src/lib.rs`
```rust
#[cfg_attr(target_os = "android", ndk_glue::main(logger(level = "debug", tag = "my-tag")))]
pub fn main() {
log!("hello world");
}
```
## App/APK configuration
Android APKs contain a file called `AndroidManifest.xml`, which has things like permission requests and feature declarations, plus configuration of activities, intents, resource folders and more. This file is autogenerated by `cargo-apk`. To control what goes in it through Cargo.toml, refer to [`cargo-apk`'s README](./cargo-apk/README.md).
## Overriding crate paths
The macro `ndk_glue::main` tries to determine crate names from current _Cargo.toml_.
You can override this names with specific paths like so:
```rust
#[ndk_glue::main(
ndk_glue = "path::to::ndk_glue",
)]
fn main() {}
```
## JNI
Java Native Interface (JNI) allows executing Java code in a VM from native applications. To access
the JNI use the `AndroidContext` from the `ndk-context` crate. `ndk-examples` contains a `jni_audio`
example which will print out all output audio devices in the log.
- [`jni`](https://crates.io/crates/jni), JNI bindings for Rust

274
third-party/vendor/ndk/src/asset.rs vendored Normal file
View file

@ -0,0 +1,274 @@
//! Bindings for [`AAsset`], [`AAssetDir`] and [`AAssetManager`]
//!
//! [`AAsset`]: https://developer.android.com/ndk/reference/group/asset#aasset
//! [`AAssetDir`]: https://developer.android.com/ndk/reference/group/asset#aassetdir
//! [`AAssetManager`]: https://developer.android.com/ndk/reference/group/asset#aassetmanager
use std::ffi::{CStr, CString};
use std::io;
use std::ptr::NonNull;
/// A native [`AAssetManager *`]
///
/// [`AAssetManager *`]: https://developer.android.com/ndk/reference/group/asset#aassetmanager
#[derive(Debug)]
pub struct AssetManager {
ptr: NonNull<ffi::AAssetManager>,
}
// AAssetManager is thread safe.
// See https://developer.android.com/ndk/reference/group/asset#aassetmanager
unsafe impl Send for AssetManager {}
unsafe impl Sync for AssetManager {}
impl AssetManager {
/// Create an `AssetManager` from a pointer
///
/// # Safety
/// By calling this function, you assert that the pointer is a valid pointer to a native
/// `AAssetManager`.
pub unsafe fn from_ptr(ptr: NonNull<ffi::AAssetManager>) -> Self {
Self { ptr }
}
/// Returns the pointer to the native `AAssetManager`.
pub fn ptr(&self) -> NonNull<ffi::AAssetManager> {
self.ptr
}
/// Open the asset. Returns `None` if opening the asset fails.
///
/// This currently always opens the asset in the streaming mode.
pub fn open(&self, filename: &CStr) -> Option<Asset> {
unsafe {
let ptr = ffi::AAssetManager_open(
self.ptr.as_ptr(),
filename.as_ptr(),
ffi::AASSET_MODE_STREAMING as i32,
);
Some(Asset::from_ptr(NonNull::new(ptr)?))
}
}
/// Open an asset directory. Returns `None` if opening the directory fails.
pub fn open_dir(&self, filename: &CStr) -> Option<AssetDir> {
unsafe {
let ptr = ffi::AAssetManager_openDir(self.ptr.as_ptr(), filename.as_ptr());
Some(AssetDir::from_ptr(NonNull::new(ptr)?))
}
}
}
/// A native [`AAssetDir *`]
///
/// ```no_run
/// # use std::ffi::CString;
/// # use ndk::asset::AssetManager;
/// # let asset_manager: AssetManager = unimplemented!();
/// use std::io::Read;
///
/// let mut my_dir = asset_manager
/// .open_dir(&CString::new("my_dir").unwrap())
/// .expect("Could not open directory");
///
/// // Use it as an iterator
/// let all_files = my_dir.collect::<Vec<CString>>();
///
/// // Reset the iterator
/// my_dir.rewind();
///
/// // Use .with_next() to iterate without allocating `CString`s
/// while let Some(asset) = my_dir.with_next(|cstr| asset_manager.open(cstr).unwrap()) {
/// let mut text = String::new();
/// asset.read_to_string(&mut text);
/// // ...
/// }
/// ```
///
/// [`AAssetDir *`]: https://developer.android.com/ndk/reference/group/asset#aassetdir
#[derive(Debug)]
pub struct AssetDir {
ptr: NonNull<ffi::AAssetDir>,
}
// It's unclear if AAssetDir is thread safe.
// However, AAsset is not, so there's a good chance that AAssetDir is not either.
impl Drop for AssetDir {
fn drop(&mut self) {
unsafe { ffi::AAssetDir_close(self.ptr.as_ptr()) }
}
}
impl AssetDir {
/// Construct an `AssetDir` from the native `AAssetDir *`. This gives ownership of the
/// `AAssetDir *` to the `AssetDir`, which will handle closing the asset. Avoid using
/// the pointer after calling this function.
///
/// # Safety
/// By calling this function, you assert that it points to a valid native `AAssetDir`.
pub unsafe fn from_ptr(ptr: NonNull<ffi::AAssetDir>) -> Self {
Self { ptr }
}
/// The corresponding native `AAssetDir *`
pub fn ptr(&self) -> NonNull<ffi::AAssetDir> {
self.ptr
}
/// Get the next filename, if any, and process it. Like `.next()`, but performs no additional
/// allocation.
///
/// The filenames are in the correct format to be passed to `AssetManager::open`
pub fn with_next<T>(&mut self, f: impl for<'a> FnOnce(&'a CStr) -> T) -> Option<T> {
unsafe {
let next_name = ffi::AAssetDir_getNextFileName(self.ptr.as_ptr());
if next_name.is_null() {
None
} else {
Some(f(CStr::from_ptr(next_name)))
}
}
}
/// Reset the iteration state
pub fn rewind(&mut self) {
unsafe {
ffi::AAssetDir_rewind(self.ptr.as_ptr());
}
}
}
impl Iterator for AssetDir {
type Item = CString;
fn next(&mut self) -> Option<CString> {
self.with_next(|cstr| cstr.to_owned())
}
}
/// A native [`AAsset *`], opened in streaming mode
///
/// ```no_run
/// # use std::ffi::CString;
/// # use ndk::asset::AssetManager;
/// # let asset_manager: AssetManager = unimplemented!();
/// use std::io::Read;
///
/// let asset = asset_manager
/// .open(&CString::new("path/to/asset").unwrap())
/// .expect("Could not open asset");
///
/// let mut data = vec![];
/// asset.read_to_end(&mut data);
/// // ... use data ...
/// ```
///
/// [`AAsset *`]: https://developer.android.com/ndk/reference/group/asset#aasset
#[derive(Debug)]
pub struct Asset {
ptr: NonNull<ffi::AAsset>,
}
// AAsset is *not* thread safe.
// See https://developer.android.com/ndk/reference/group/asset#aasset
impl Drop for Asset {
fn drop(&mut self) {
unsafe { ffi::AAsset_close(self.ptr.as_ptr()) }
}
}
impl Asset {
/// Construct an `Asset` from the native `AAsset *`. This gives ownership of the `AAsset *` to
/// the `Asset`, which will handle closing the asset. Avoid using the pointer after calling
/// this function.
///
/// # Safety
/// By calling this function, you assert that it points to a valid native `AAsset`, open
/// in the streaming mode.
pub unsafe fn from_ptr(ptr: NonNull<ffi::AAsset>) -> Self {
Self { ptr }
}
/// The corresponding native `AAsset *`
pub fn ptr(&self) -> NonNull<ffi::AAsset> {
self.ptr
}
/// Returns the total length of the asset, in bytes
pub fn get_length(&self) -> usize {
unsafe { ffi::AAsset_getLength64(self.ptr.as_ptr()) as usize }
}
/// Returns the remaining length of the asset, in bytes
pub fn get_remaining_length(&self) -> usize {
unsafe { ffi::AAsset_getRemainingLength64(self.ptr.as_ptr()) as usize }
}
/// Reads all data into a buffer and returns it
pub fn get_buffer(&mut self) -> io::Result<&[u8]> {
unsafe {
let buf_ptr = ffi::AAsset_getBuffer(self.ptr.as_ptr());
if buf_ptr.is_null() {
Err(io::Error::new(
io::ErrorKind::Other,
"Android Asset error creating buffer",
))
} else {
Ok(std::slice::from_raw_parts(
buf_ptr as *const u8,
self.get_remaining_length(),
))
}
}
}
//pub fn open_file_descriptor(&self) -> TODO
}
impl io::Read for Asset {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
unsafe {
let res = ffi::AAsset_read(
self.ptr.as_ptr(),
buf.as_mut_ptr() as *mut _,
buf.len() as ffi::size_t,
);
if res >= 0 {
Ok(res as usize)
} else {
Err(io::Error::new(
io::ErrorKind::Other,
"Android Asset read error",
))
}
}
}
}
impl io::Seek for Asset {
fn seek(&mut self, seek: io::SeekFrom) -> io::Result<u64> {
unsafe {
let res = match seek {
io::SeekFrom::Start(x) => {
ffi::AAsset_seek64(self.ptr.as_ptr(), x as i64, ffi::SEEK_SET as i32)
}
io::SeekFrom::Current(x) => {
ffi::AAsset_seek64(self.ptr.as_ptr(), x, ffi::SEEK_CUR as i32)
}
io::SeekFrom::End(x) => {
ffi::AAsset_seek64(self.ptr.as_ptr(), x, ffi::SEEK_END as i32)
}
};
if res < 0 {
Err(io::Error::new(
io::ErrorKind::Other,
"Android Asset seek error",
))
} else {
Ok(res as u64)
}
}
}
}

1367
third-party/vendor/ndk/src/audio.rs vendored Normal file

File diff suppressed because it is too large Load diff

156
third-party/vendor/ndk/src/bitmap.rs vendored Normal file
View file

@ -0,0 +1,156 @@
//! Bindings for [`AndroidBitmap`] functions
//!
//! These functions operate directly on a JNI [`android.graphics.Bitmap`] instance.
//!
//! [`AndroidBitmap`]: https://developer.android.com/ndk/reference/group/bitmap
//! [`android.graphics.Bitmap`]: https://developer.android.com/reference/android/graphics/Bitmap
#![cfg(feature = "bitmap")]
use jni_sys::{jobject, JNIEnv};
use num_enum::{IntoPrimitive, TryFromPrimitive};
use std::{convert::TryInto, mem::MaybeUninit};
#[cfg(feature = "api-level-30")]
use crate::hardware_buffer::HardwareBufferRef;
#[repr(i32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum BitmapError {
Unknown,
AllocationFailed = ffi::ANDROID_BITMAP_RESULT_ALLOCATION_FAILED,
BadParameter = ffi::ANDROID_BITMAP_RESULT_BAD_PARAMETER,
JniException = ffi::ANDROID_BITMAP_RESULT_JNI_EXCEPTION,
}
pub type BitmapResult<T, E = BitmapError> = std::result::Result<T, E>;
impl BitmapError {
pub(crate) fn from_status(status: i32) -> BitmapResult<()> {
Err(match status {
ffi::ANDROID_BITMAP_RESULT_SUCCESS => return Ok(()),
ffi::ANDROID_BITMAP_RESULT_ALLOCATION_FAILED => BitmapError::AllocationFailed,
ffi::ANDROID_BITMAP_RESULT_BAD_PARAMETER => BitmapError::BadParameter,
ffi::ANDROID_BITMAP_RESULT_JNI_EXCEPTION => BitmapError::JniException,
_ => BitmapError::Unknown,
})
}
}
fn construct<T>(with_ptr: impl FnOnce(*mut T) -> i32) -> BitmapResult<T> {
let mut result = MaybeUninit::uninit();
let status = with_ptr(result.as_mut_ptr());
BitmapError::from_status(status).map(|()| unsafe { result.assume_init() })
}
// IntoPrimitive, TryFromPrimitive use the deprecated `RGBA_4444` member below,
// resulting in deprecation warnings in generated code beyond the `enum`. These
// can only be disabled at the module level, and such warnings seem to be gone on
// at least the Rust 1.56 nightlies.
#[allow(deprecated)]
mod temp_allow_deprecated {
use super::*;
#[repr(u32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, IntoPrimitive, TryFromPrimitive)]
#[allow(non_camel_case_types)]
pub enum BitmapFormat {
NONE = ffi::AndroidBitmapFormat::ANDROID_BITMAP_FORMAT_NONE.0,
RGBA_8888 = ffi::AndroidBitmapFormat::ANDROID_BITMAP_FORMAT_RGBA_8888.0,
RGB_565 = ffi::AndroidBitmapFormat::ANDROID_BITMAP_FORMAT_RGB_565.0,
#[deprecated = "Deprecated in API level 13. Because of the poor quality of this configuration, it is advised to use ARGB_8888 instead."]
RGBA_4444 = ffi::AndroidBitmapFormat::ANDROID_BITMAP_FORMAT_RGBA_4444.0,
A_8 = ffi::AndroidBitmapFormat::ANDROID_BITMAP_FORMAT_A_8.0,
RGBA_F16 = ffi::AndroidBitmapFormat::ANDROID_BITMAP_FORMAT_RGBA_F16.0,
}
}
pub use temp_allow_deprecated::*;
/// An immediate wrapper over [`android.graphics.Bitmap`]
///
/// [`android.graphics.Bitmap`]: https://developer.android.com/reference/android/graphics/Bitmap
#[derive(Debug)]
pub struct AndroidBitmap {
env: *mut JNIEnv,
inner: jobject,
}
impl AndroidBitmap {
/// Create an [`AndroidBitmap`] wrapper from JNI pointers
///
/// # Safety
/// This function should be called with a healthy JVM pointer and with a non-null
/// [`android.graphics.Bitmap`], which must be kept alive on the Java/Kotlin side.
///
/// [`android.graphics.Bitmap`]: https://developer.android.com/reference/android/graphics/Bitmap
pub unsafe fn from_jni(env: *mut JNIEnv, bitmap: jobject) -> Self {
Self { env, inner: bitmap }
}
pub fn get_info(&self) -> BitmapResult<AndroidBitmapInfo> {
let inner =
construct(|res| unsafe { ffi::AndroidBitmap_getInfo(self.env, self.inner, res) })?;
Ok(AndroidBitmapInfo { inner })
}
pub fn lock_pixels(&self) -> BitmapResult<*mut std::os::raw::c_void> {
construct(|res| unsafe { ffi::AndroidBitmap_lockPixels(self.env, self.inner, res) })
}
pub fn unlock_pixels(&self) -> BitmapResult<()> {
let status = unsafe { ffi::AndroidBitmap_unlockPixels(self.env, self.inner) };
BitmapError::from_status(status)
}
/// Retrieve the native object associated with a `HARDWARE` [`AndroidBitmap`].
///
/// Client must not modify it while an [`AndroidBitmap`] is wrapping it.
#[cfg(feature = "api-level-30")]
pub fn get_hardware_buffer(&self) -> BitmapResult<HardwareBufferRef> {
unsafe {
let result =
construct(|res| ffi::AndroidBitmap_getHardwareBuffer(self.env, self.inner, res))?;
let non_null = if cfg!(debug_assertions) {
std::ptr::NonNull::new(result).expect("result should never be null")
} else {
std::ptr::NonNull::new_unchecked(result)
};
Ok(HardwareBufferRef::from_ptr(non_null))
}
}
}
/// A native [`AndroidBitmapInfo`]
///
/// [`AndroidBitmapInfo`]: https://developer.android.com/ndk/reference/struct/android-bitmap-info#struct_android_bitmap_info
#[derive(Copy, Clone, Debug)]
pub struct AndroidBitmapInfo {
inner: ffi::AndroidBitmapInfo,
}
// TODO: flesh out when API 30 is released
#[cfg(feature = "api-level-30")]
pub type AndroidBitmapInfoFlags = u32;
impl AndroidBitmapInfo {
pub fn width(&self) -> u32 {
self.inner.width
}
pub fn height(&self) -> u32 {
self.inner.height
}
pub fn stride(&self) -> u32 {
self.inner.stride
}
pub fn format(&self) -> BitmapFormat {
let format = self.inner.format as u32;
format.try_into().unwrap()
}
#[cfg(feature = "api-level-30")]
pub fn flags(&self) -> AndroidBitmapInfoFlags {
self.inner.flags
}
}

View file

@ -0,0 +1,573 @@
//! Bindings for [`AConfiguration`]
//!
//! See also the [NDK docs](https://developer.android.com/ndk/reference/group/configuration) for
//! [`AConfiguration`], as well as the [docs for providing
//! resources](https://developer.android.com/guide/topics/resources/providing-resources.html),
//! which explain many of the configuration values. The [`android.content.res.Configuration`
//! javadoc](https://developer.android.com/reference/android/content/res/Configuration.html) may
//! also have useful information.
//!
//! [`AConfiguration`]: https://developer.android.com/ndk/reference/group/configuration#aconfiguration
use crate::asset::AssetManager;
use num_enum::{IntoPrimitive, TryFromPrimitive};
use std::convert::TryInto;
use std::fmt;
use std::ptr::NonNull;
/// A native [`AConfiguration *`]
///
/// [`Configuration`] is an opaque type used to get and set various subsystem configurations.
///
/// [`AConfiguration *`]: https://developer.android.com/ndk/reference/group/configuration#aconfiguration
pub struct Configuration {
ptr: NonNull<ffi::AConfiguration>,
}
unsafe impl Send for Configuration {}
unsafe impl Sync for Configuration {}
impl Drop for Configuration {
fn drop(&mut self) {
unsafe { ffi::AConfiguration_delete(self.ptr.as_ptr()) }
}
}
impl Clone for Configuration {
fn clone(&self) -> Self {
let mut new = Self::new();
new.copy(self);
new
}
}
impl PartialEq for Configuration {
fn eq(&self, other: &Self) -> bool {
self.diff(other).0 == 0
}
}
impl Eq for Configuration {}
impl fmt::Debug for Configuration {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Configuration")
.field("mcc", &self.mcc())
.field("mnc", &self.mnc())
.field("lang", &self.language())
.field("country", &self.country())
.field("orientation", &self.orientation())
.field("touchscreen", &self.touchscreen())
.field("density", &self.density())
.field("keyboard", &self.keyboard())
.field("navigation", &self.navigation())
.field("keys_hidden", &self.keys_hidden())
.field("nav_hidden", &self.nav_hidden())
.field("sdk_version", &self.sdk_version())
.field("screen_size", &self.screen_size())
.field("screen_long", &self.screen_long())
.field("ui_mode_type", &self.ui_mode_type())
.field("ui_mode_night", &self.ui_mode_night())
.finish()
}
}
impl Configuration {
/// Construct a `Configuration` from a pointer.
///
/// # Safety
/// By calling this function, you assert that it is a valid pointer to a native
/// `AConfiguration`, and give ownership of it to the `Configuration` instance.
pub unsafe fn from_ptr(ptr: NonNull<ffi::AConfiguration>) -> Self {
Self { ptr }
}
/// Create a new `Configuration`, with the same contents as the `AConfiguration` referenced by
/// the pointer.
///
/// This is useful if you have a pointer, but not ownership of it.
///
/// # Safety
/// By calling this function, you assert that it is a valid pointer to a native
/// `AConfiguration`.
pub unsafe fn clone_from_ptr(ptr: NonNull<ffi::AConfiguration>) -> Self {
let conf = Self::new();
ffi::AConfiguration_copy(conf.ptr.as_ptr(), ptr.as_ptr());
conf
}
/// The pointer to the native `AConfiguration`. Keep in mind that the `Configuration` object
/// still has ownership, and will free it when dropped.
pub fn ptr(&self) -> NonNull<ffi::AConfiguration> {
self.ptr
}
pub fn from_asset_manager(am: &AssetManager) -> Self {
let config = Self::new();
unsafe {
ffi::AConfiguration_fromAssetManager(config.ptr().as_mut(), am.ptr().as_mut());
}
config
}
/// Create a new `Configuration`, with none of the values set.
pub fn new() -> Self {
unsafe {
Self {
ptr: NonNull::new(ffi::AConfiguration_new()).unwrap(),
}
}
}
/// `dest.copy(&src)` copies the contents of `src` to `dest`
pub fn copy(&mut self, other: &Self) {
unsafe { ffi::AConfiguration_copy(self.ptr.as_ptr(), other.ptr.as_ptr()) }
}
/// Information about what fields differ between the two configurations
pub fn diff(&self, other: &Self) -> DiffResult {
unsafe {
DiffResult(ffi::AConfiguration_diff(self.ptr.as_ptr(), other.ptr.as_ptr()) as u32)
}
}
/// Returns false if anything in `self` conflicts with `requested`
pub fn matches(&self, requested: &Self) -> bool {
unsafe { ffi::AConfiguration_match(self.ptr.as_ptr(), requested.ptr.as_ptr()) != 0 }
}
/// Returns the country code, as a [`String`] of two characters, if set
pub fn country(&self) -> Option<String> {
let mut chars = [0u8; 2];
unsafe {
ffi::AConfiguration_getCountry(self.ptr.as_ptr(), chars.as_mut_ptr().cast());
}
if chars[0] == 0 {
None
} else {
Some(std::str::from_utf8(chars.as_slice()).unwrap().to_owned())
}
}
/// Returns the screen density in dpi.
///
/// On some devices it can return values outside of the density enum.
pub fn density(&self) -> Option<u32> {
let density = unsafe { ffi::AConfiguration_getDensity(self.ptr.as_ptr()) as u32 };
match density {
ffi::ACONFIGURATION_DENSITY_DEFAULT => Some(160),
ffi::ACONFIGURATION_DENSITY_ANY => None,
ffi::ACONFIGURATION_DENSITY_NONE => None,
density => Some(density),
}
}
/// Returns the keyboard type.
pub fn keyboard(&self) -> Keyboard {
unsafe {
(ffi::AConfiguration_getKeyboard(self.ptr.as_ptr()) as u32)
.try_into()
.unwrap()
}
}
/// Returns keyboard visibility/availability.
pub fn keys_hidden(&self) -> KeysHidden {
unsafe {
(ffi::AConfiguration_getKeysHidden(self.ptr.as_ptr()) as u32)
.try_into()
.unwrap()
}
}
/// Returns the language, as a [`String`] of two characters, if set
pub fn language(&self) -> Option<String> {
let mut chars = [0u8; 2];
unsafe {
ffi::AConfiguration_getLanguage(self.ptr.as_ptr(), chars.as_mut_ptr().cast());
}
if chars[0] == 0 {
None
} else {
Some(std::str::from_utf8(chars.as_slice()).unwrap().to_owned())
}
}
/// Returns the layout direction
pub fn layout_direction(&self) -> LayoutDir {
unsafe {
(ffi::AConfiguration_getLayoutDirection(self.ptr.as_ptr()) as u32)
.try_into()
.unwrap()
}
}
/// Returns the mobile country code.
pub fn mcc(&self) -> i32 {
unsafe { ffi::AConfiguration_getMcc(self.ptr.as_ptr()) }
}
/// Returns the mobile network code, if one is defined
pub fn mnc(&self) -> Option<i32> {
unsafe {
match ffi::AConfiguration_getMnc(self.ptr.as_ptr()) {
0 => None,
x if x == ffi::ACONFIGURATION_MNC_ZERO as i32 => Some(0),
x => Some(x),
}
}
}
pub fn nav_hidden(&self) -> NavHidden {
unsafe {
(ffi::AConfiguration_getNavHidden(self.ptr.as_ptr()) as u32)
.try_into()
.unwrap()
}
}
pub fn navigation(&self) -> Navigation {
unsafe {
(ffi::AConfiguration_getNavigation(self.ptr.as_ptr()) as u32)
.try_into()
.unwrap()
}
}
pub fn orientation(&self) -> Orientation {
unsafe {
(ffi::AConfiguration_getOrientation(self.ptr.as_ptr()) as u32)
.try_into()
.unwrap()
}
}
pub fn screen_height_dp(&self) -> Option<i32> {
unsafe {
let height = ffi::AConfiguration_getScreenHeightDp(self.ptr.as_ptr());
if height == ffi::ACONFIGURATION_SCREEN_HEIGHT_DP_ANY as i32 {
None
} else {
Some(height)
}
}
}
pub fn screen_width_dp(&self) -> Option<i32> {
unsafe {
let width = ffi::AConfiguration_getScreenWidthDp(self.ptr.as_ptr());
if width == ffi::ACONFIGURATION_SCREEN_WIDTH_DP_ANY as i32 {
None
} else {
Some(width)
}
}
}
pub fn screen_long(&self) -> ScreenLong {
unsafe {
(ffi::AConfiguration_getScreenLong(self.ptr.as_ptr()) as u32)
.try_into()
.unwrap()
}
}
#[cfg(feature = "api-level-30")]
pub fn screen_round(&self) -> ScreenRound {
unsafe {
(ffi::AConfiguration_getScreenRound(self.ptr.as_ptr()) as u32)
.try_into()
.unwrap()
}
}
pub fn screen_size(&self) -> ScreenSize {
unsafe {
(ffi::AConfiguration_getScreenSize(self.ptr.as_ptr()) as u32)
.try_into()
.unwrap()
}
}
pub fn sdk_version(&self) -> i32 {
unsafe { ffi::AConfiguration_getSdkVersion(self.ptr.as_ptr()) }
}
pub fn smallest_screen_width_dp(&self) -> Option<i32> {
unsafe {
let width = ffi::AConfiguration_getSmallestScreenWidthDp(self.ptr.as_ptr());
if width == ffi::ACONFIGURATION_SMALLEST_SCREEN_WIDTH_DP_ANY as i32 {
None
} else {
Some(width)
}
}
}
pub fn touchscreen(&self) -> Touchscreen {
unsafe {
(ffi::AConfiguration_getTouchscreen(self.ptr.as_ptr()) as u32)
.try_into()
.unwrap()
}
}
pub fn ui_mode_night(&self) -> UiModeNight {
unsafe {
(ffi::AConfiguration_getUiModeNight(self.ptr.as_ptr()) as u32)
.try_into()
.unwrap()
}
}
pub fn ui_mode_type(&self) -> UiModeType {
unsafe {
(ffi::AConfiguration_getUiModeType(self.ptr.as_ptr()) as u32)
.try_into()
.unwrap()
}
}
}
/// A bitfield representing the differences between two [`Configuration`]s
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct DiffResult(pub u32);
impl DiffResult {
pub fn mcc(self) -> bool {
self.0 & ffi::ACONFIGURATION_MCC != 0
}
pub fn mnc(self) -> bool {
self.0 & ffi::ACONFIGURATION_MNC != 0
}
pub fn locale(self) -> bool {
self.0 & ffi::ACONFIGURATION_LOCALE != 0
}
pub fn touchscreen(self) -> bool {
self.0 & ffi::ACONFIGURATION_TOUCHSCREEN != 0
}
pub fn keyboard(self) -> bool {
self.0 & ffi::ACONFIGURATION_KEYBOARD != 0
}
pub fn keyboard_hidden(self) -> bool {
self.0 & ffi::ACONFIGURATION_KEYBOARD_HIDDEN != 0
}
pub fn navigation(self) -> bool {
self.0 & ffi::ACONFIGURATION_NAVIGATION != 0
}
pub fn orientation(self) -> bool {
self.0 & ffi::ACONFIGURATION_ORIENTATION != 0
}
pub fn density(self) -> bool {
self.0 & ffi::ACONFIGURATION_DENSITY != 0
}
pub fn screen_size(self) -> bool {
self.0 & ffi::ACONFIGURATION_SCREEN_SIZE != 0
}
pub fn version(self) -> bool {
self.0 & ffi::ACONFIGURATION_VERSION != 0
}
pub fn screen_layout(self) -> bool {
self.0 & ffi::ACONFIGURATION_SCREEN_LAYOUT != 0
}
pub fn ui_mode(self) -> bool {
self.0 & ffi::ACONFIGURATION_UI_MODE != 0
}
pub fn smallest_screen_size(self) -> bool {
self.0 & ffi::ACONFIGURATION_SMALLEST_SCREEN_SIZE != 0
}
pub fn layout_dir(self) -> bool {
self.0 & ffi::ACONFIGURATION_LAYOUTDIR != 0
}
pub fn screen_round(self) -> bool {
self.0 & ffi::ACONFIGURATION_SCREEN_ROUND != 0
}
pub fn color_mode(self) -> bool {
self.0 & ffi::ACONFIGURATION_COLOR_MODE != 0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum Orientation {
Any = ffi::ACONFIGURATION_ORIENTATION_ANY,
Port = ffi::ACONFIGURATION_ORIENTATION_PORT,
Land = ffi::ACONFIGURATION_ORIENTATION_LAND,
Square = ffi::ACONFIGURATION_ORIENTATION_SQUARE,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum Touchscreen {
Any = ffi::ACONFIGURATION_TOUCHSCREEN_ANY,
NoTouch = ffi::ACONFIGURATION_TOUCHSCREEN_NOTOUCH,
Stylus = ffi::ACONFIGURATION_TOUCHSCREEN_STYLUS,
Finger = ffi::ACONFIGURATION_TOUCHSCREEN_FINGER,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum Density {
Default = ffi::ACONFIGURATION_DENSITY_DEFAULT,
Low = ffi::ACONFIGURATION_DENSITY_LOW,
Medium = ffi::ACONFIGURATION_DENSITY_MEDIUM,
TV = ffi::ACONFIGURATION_DENSITY_TV,
High = ffi::ACONFIGURATION_DENSITY_HIGH,
XHigh = ffi::ACONFIGURATION_DENSITY_XHIGH,
XXHigh = ffi::ACONFIGURATION_DENSITY_XXHIGH,
XXXHigh = ffi::ACONFIGURATION_DENSITY_XXXHIGH,
Any = ffi::ACONFIGURATION_DENSITY_ANY,
None = ffi::ACONFIGURATION_DENSITY_NONE,
}
impl Density {
/// The DPI associated with the density class.
/// See [the Android screen density
/// docs](https://developer.android.com/training/multiscreen/screendensities#TaskProvideAltBmp)
///
/// There are some `Density` values that have no associated DPI; these values return `None`.
pub fn dpi(self) -> Option<u32> {
match self {
Self::Default => Some(160), // Or should it be None?
Self::Low => Some(120),
Self::Medium => Some(160),
Self::High => Some(240),
Self::XHigh => Some(320),
Self::XXHigh => Some(480),
Self::XXXHigh => Some(640),
Self::TV => Some(213),
Self::Any => None,
Self::None => None,
}
}
/// The Hi-DPI factor associated with the density class. This is the factor by which an
/// image/resource should be scaled to match its size across devices. The baseline is a 160dpi
/// screen (i.e., Hi-DPI factor = DPI / 160).
/// See [the Android screen density
/// docs](https://developer.android.com/training/multiscreen/screendensities#TaskProvideAltBmp)
///
/// There are some `Density` values that have no associated DPI; these values return `None`.
pub fn approx_hidpi_factor(self) -> Option<f64> {
match self {
Self::Default => Some(1.), // Or should it be None?
Self::Low => Some(0.75),
Self::Medium => Some(1.),
Self::High => Some(1.5),
Self::XHigh => Some(2.),
Self::XXHigh => Some(3.),
Self::XXXHigh => Some(4.),
Self::TV => Some(4. / 3.),
Self::Any => None,
Self::None => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum Keyboard {
Any = ffi::ACONFIGURATION_KEYBOARD_ANY,
NoKeys = ffi::ACONFIGURATION_KEYBOARD_NOKEYS,
Qwerty = ffi::ACONFIGURATION_KEYBOARD_QWERTY,
TwelveKey = ffi::ACONFIGURATION_KEYBOARD_12KEY,
}
// FIXME is it a bitmask?
// FIXME are they all bitmasks?
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum Navigation {
Any = ffi::ACONFIGURATION_NAVIGATION_ANY,
NoNav = ffi::ACONFIGURATION_NAVIGATION_NONAV,
DPad = ffi::ACONFIGURATION_NAVIGATION_DPAD,
Trackball = ffi::ACONFIGURATION_NAVIGATION_TRACKBALL,
Wheel = ffi::ACONFIGURATION_NAVIGATION_WHEEL,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum KeysHidden {
Any = ffi::ACONFIGURATION_KEYSHIDDEN_ANY,
No = ffi::ACONFIGURATION_KEYSHIDDEN_NO,
Yes = ffi::ACONFIGURATION_KEYSHIDDEN_YES,
Soft = ffi::ACONFIGURATION_KEYSHIDDEN_SOFT,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum NavHidden {
Any = ffi::ACONFIGURATION_NAVHIDDEN_ANY,
No = ffi::ACONFIGURATION_NAVHIDDEN_NO,
Yes = ffi::ACONFIGURATION_NAVHIDDEN_YES,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum ScreenSize {
Any = ffi::ACONFIGURATION_SCREENSIZE_ANY,
Small = ffi::ACONFIGURATION_SCREENSIZE_SMALL,
Normal = ffi::ACONFIGURATION_SCREENSIZE_NORMAL,
Large = ffi::ACONFIGURATION_SCREENSIZE_LARGE,
XLarge = ffi::ACONFIGURATION_SCREENSIZE_XLARGE,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum ScreenLong {
Any = ffi::ACONFIGURATION_SCREENLONG_ANY,
No = ffi::ACONFIGURATION_SCREENLONG_NO,
Yes = ffi::ACONFIGURATION_SCREENLONG_YES,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum ScreenRound {
Any = ffi::ACONFIGURATION_SCREENROUND_ANY,
No = ffi::ACONFIGURATION_SCREENROUND_NO,
Yes = ffi::ACONFIGURATION_SCREENROUND_YES,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum WideColorGamut {
Any = ffi::ACONFIGURATION_WIDE_COLOR_GAMUT_ANY,
No = ffi::ACONFIGURATION_WIDE_COLOR_GAMUT_NO,
Yes = ffi::ACONFIGURATION_WIDE_COLOR_GAMUT_YES,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum HDR {
Any = ffi::ACONFIGURATION_HDR_ANY,
No = ffi::ACONFIGURATION_HDR_NO,
Yes = ffi::ACONFIGURATION_HDR_YES,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum LayoutDir {
Any = ffi::ACONFIGURATION_LAYOUTDIR_ANY,
Ltr = ffi::ACONFIGURATION_LAYOUTDIR_LTR,
Rtl = ffi::ACONFIGURATION_LAYOUTDIR_RTL,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum UiModeType {
Any = ffi::ACONFIGURATION_UI_MODE_TYPE_ANY,
Normal = ffi::ACONFIGURATION_UI_MODE_TYPE_NORMAL,
Desk = ffi::ACONFIGURATION_UI_MODE_TYPE_DESK,
Car = ffi::ACONFIGURATION_UI_MODE_TYPE_CAR,
Television = ffi::ACONFIGURATION_UI_MODE_TYPE_TELEVISION,
Applicance = ffi::ACONFIGURATION_UI_MODE_TYPE_APPLIANCE,
Watch = ffi::ACONFIGURATION_UI_MODE_TYPE_WATCH,
VrHeadset = ffi::ACONFIGURATION_UI_MODE_TYPE_VR_HEADSET,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum UiModeNight {
Any = ffi::ACONFIGURATION_UI_MODE_NIGHT_ANY,
No = ffi::ACONFIGURATION_UI_MODE_NIGHT_NO,
Yes = ffi::ACONFIGURATION_UI_MODE_NIGHT_YES,
}

1492
third-party/vendor/ndk/src/event.rs vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,524 @@
//! Bindings for [`AHardwareBuffer`]
//!
//! [`AHardwareBuffer`]: https://developer.android.com/ndk/reference/group/a-hardware-buffer#ahardwarebuffer
#![cfg(feature = "api-level-26")]
use crate::utils::status_to_io_result;
pub use super::hardware_buffer_format::HardwareBufferFormat;
use jni_sys::{jobject, JNIEnv};
use std::{
convert::TryInto, io::Result, mem::MaybeUninit, ops::Deref, os::raw::c_void,
os::unix::io::RawFd, ptr::NonNull,
};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct HardwareBufferUsage(pub ffi::AHardwareBuffer_UsageFlags);
impl HardwareBufferUsage {
pub const CPU_READ_NEVER: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_NEVER);
pub const CPU_READ_RARELY: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_RARELY);
pub const CPU_READ_OFTEN: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN);
pub const CPU_READ_MASK: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_MASK);
pub const CPU_WRITE_NEVER: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER);
pub const CPU_WRITE_RARELY: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY);
pub const CPU_WRITE_OFTEN: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN);
pub const CPU_WRITE_MASK: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK);
pub const GPU_SAMPLED_IMAGE: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE);
pub const GPU_FRAMEBUFFER: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER);
pub const COMPOSER_OVERLAY: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY);
pub const PROTECTED_CONTENT: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT);
pub const VIDEO_ENCODE: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VIDEO_ENCODE);
pub const SENSOR_DIRECT_DATA: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA);
pub const GPU_DATA_BUFFER: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER);
pub const GPU_CUBE_MAP: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP);
pub const GPU_MIPMAP_COMPLETE: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE);
pub const VENDOR_0: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_0);
pub const VENDOR_1: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_1);
pub const VENDOR_2: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_2);
pub const VENDOR_3: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_3);
pub const VENDOR_4: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_4);
pub const VENDOR_5: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_5);
pub const VENDOR_6: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_6);
pub const VENDOR_7: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_7);
pub const VENDOR_8: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_8);
pub const VENDOR_9: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_9);
pub const VENDOR_10: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_10);
pub const VENDOR_11: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_11);
pub const VENDOR_12: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_12);
pub const VENDOR_13: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_13);
pub const VENDOR_14: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_14);
pub const VENDOR_15: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_15);
pub const VENDOR_16: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_16);
pub const VENDOR_17: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_17);
pub const VENDOR_18: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_18);
pub const VENDOR_19: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_19);
}
pub type Rect = ffi::ARect;
fn construct<T>(with_ptr: impl FnOnce(*mut T) -> i32) -> Result<T> {
let mut result = MaybeUninit::uninit();
let status = with_ptr(result.as_mut_ptr());
status_to_io_result(status, unsafe { result.assume_init() })
}
/// A native [`AHardwareBuffer *`]
///
/// [`HardwareBuffer`] objects represent chunks of memory that can be accessed by various hardware
/// components in the system.
///
/// It can be easily converted to the Java counterpart [`android.hardware.HardwareBuffer`] and
/// passed between processes using Binder. All operations involving [`HardwareBuffer`] and
/// [`android.hardware.HardwareBuffer`] are zero-copy, i.e., passing [`HardwareBuffer`] to another
/// process creates a shared view of the same region of memory.
///
/// [`HardwareBuffer`] can be bound to EGL/OpenGL and Vulkan primitives. For EGL, use the extension
/// function [`eglGetNativeClientBufferANDROID`] to obtain an `EGLClientBuffer` and pass it
/// directly to [`eglCreateImageKHR`]. Refer to the EGL extensions
/// [`EGL_ANDROID_get_native_client_buffer`] and [`EGL_ANDROID_image_native_buffer`] for more
/// information. In Vulkan, the contents of the [`HardwareBuffer`] can be accessed as [external
/// memory]. See the [`VK_ANDROID_external_memory_android_hardware_buffer`] extension for details.
///
/// [`AHardwareBuffer *`]: https://developer.android.com/ndk/reference/group/a-hardware-buffer#ahardwarebuffer
/// [`android.hardware.HardwareBuffer`]: https://developer.android.com/reference/android/hardware/HardwareBuffer
/// [`eglGetNativeClientBufferANDROID`]: https://www.khronos.org/registry/EGL/extensions/ANDROID/EGL_ANDROID_get_native_client_buffer.txt
/// [`eglCreateImageKHR`]: https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_image_base.txt
/// [`EGL_ANDROID_get_native_client_buffer`]: https://www.khronos.org/registry/EGL/extensions/ANDROID/EGL_ANDROID_get_native_client_buffer.txt
/// [`EGL_ANDROID_image_native_buffer`]: https://www.khronos.org/registry/EGL/extensions/ANDROID/EGL_ANDROID_image_native_buffer.txt
/// [external memory]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VK_KHR_external_memory.html
/// [`VK_ANDROID_external_memory_android_hardware_buffer`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VK_ANDROID_external_memory_android_hardware_buffer.html
#[derive(Debug)]
pub struct HardwareBuffer {
inner: NonNull<ffi::AHardwareBuffer>,
}
impl HardwareBuffer {
/// Create an _unowned_ [`HardwareBuffer`] from a native pointer
///
/// To wrap a strong reference (that is `release`d on [`Drop`]), call
/// [`HardwareBufferRef::from_ptr()`] instead.
///
/// # Safety
/// By calling this function, you assert that it is a valid pointer to an NDK
/// [`ffi::AHardwareBuffer`] that is kept alive externally, or retrieve a strong reference
/// using [`HardwareBuffer::acquire()`].
pub unsafe fn from_ptr(ptr: NonNull<ffi::AHardwareBuffer>) -> Self {
Self { inner: ptr }
}
/// Returns the underlying [`ffi::AHardwareBuffer`] pointer
///
/// See the top-level [`HardwareBuffer`] struct documentation for (graphics) APIs that accept
/// this pointer.
pub fn as_ptr(&self) -> *mut ffi::AHardwareBuffer {
self.inner.as_ptr()
}
/// Allocates a buffer that matches the passed [`HardwareBufferDesc`].
///
/// If allocation succeeds, the buffer can be used according to the usage flags specified in
/// its description. If a buffer is used in ways not compatible with its usage flags, the
/// results are undefined and may include program termination.
pub fn allocate(desc: HardwareBufferDesc) -> Result<HardwareBufferRef> {
unsafe {
let ptr = construct(|res| ffi::AHardwareBuffer_allocate(&desc.into_native(), res))?;
Ok(HardwareBufferRef::from_ptr(NonNull::new_unchecked(ptr)))
}
}
/// Create a [`HardwareBuffer`] from JNI pointers
///
/// # Safety
/// By calling this function, you assert that these are valid pointers to JNI objects.
///
/// This method does not acquire any additional reference to the AHardwareBuffer that is
/// returned. To keep the [`HardwareBuffer`] alive after the [Java `HardwareBuffer`] object
/// is closed, explicitly or by the garbage collector, be sure to retrieve a strong reference
/// using [`HardwareBuffer::acquire()`].
///
/// [Java `HardwareBuffer`]: https://developer.android.com/reference/android/hardware/HardwareBuffer
pub unsafe fn from_jni(env: *mut JNIEnv, hardware_buffer: jobject) -> Self {
let ptr = ffi::AHardwareBuffer_fromHardwareBuffer(env, hardware_buffer);
Self::from_ptr(NonNull::new_unchecked(ptr))
}
/// # Safety
/// By calling this function, you assert that `env` is a valid pointer to a [`JNIEnv`].
pub unsafe fn to_jni(&self, env: *mut JNIEnv) -> jobject {
ffi::AHardwareBuffer_toHardwareBuffer(env, self.as_ptr())
}
/// Return a description of the [`HardwareBuffer`] in the passed [`HardwareBufferDesc`] struct.
pub fn describe(&self) -> HardwareBufferDesc {
let desc = unsafe {
let mut result = MaybeUninit::uninit();
ffi::AHardwareBuffer_describe(self.as_ptr(), result.as_mut_ptr());
result.assume_init()
};
HardwareBufferDesc {
width: desc.width,
height: desc.height,
layers: desc.layers,
format: desc.format.try_into().unwrap(),
usage: HardwareBufferUsage(ffi::AHardwareBuffer_UsageFlags(desc.usage)),
stride: desc.stride,
}
}
/// Test whether the given format and usage flag combination is allocatable.
///
/// If this function returns [`true`], it means that a buffer with the given description can
/// be allocated on this implementation, unless resource exhaustion occurs. If this function
/// returns [`false`], it means that the allocation of the given description will never
/// succeed.
///
/// The return value of this function may depend on all fields in the description, except
/// [`HardwareBufferDesc::stride`], which is always ignored. For example, some implementations
/// have implementation-defined limits on texture size and layer count.
#[cfg(feature = "api-level-29")]
pub fn is_supported(desc: HardwareBufferDesc) -> bool {
let res = unsafe { ffi::AHardwareBuffer_isSupported(&desc.into_native()) };
res == 1
}
/// Lock the [`HardwareBuffer`] for direct CPU access.
///
/// This function can lock the buffer for either reading or writing. It may block if the
/// hardware needs to finish rendering, if CPU caches need to be synchronized, or possibly for
/// other implementation-specific reasons.
///
/// The [`HardwareBuffer`] must have one layer, otherwise the call will fail.
///
/// If `fence` is not [`None`], it specifies a fence file descriptor on which to wait before
/// locking the buffer. If it's [`None`], the caller is responsible for ensuring that writes
/// to the buffer have completed before calling this function. Using this parameter is more
/// efficient than waiting on the fence and then calling this function.
///
/// The `usage` parameter may only specify `HardwareBufferUsage::CPU_*`. If set, then the
/// address of the buffer in virtual memory is returned. The flags must also be compatible with
/// usage flags specified at buffer creation: if a read flag is passed, the buffer must have
/// been created with [`HardwareBufferUsage::CPU_READ_RARELY`] or
/// [`HardwareBufferUsage::CPU_READ_OFTEN`]. If a write flag is passed, it must have been
/// created with [`HardwareBufferUsage::CPU_WRITE_RARELY`] or
/// [`HardwareBufferUsage::CPU_WRITE_OFTEN`].
///
/// If `rect` is not [`None`], the caller promises to modify only data in the area specified by
/// `rect`. If rect is [`None`], the caller may modify the contents of the entire buffer. The
/// content of the buffer outside of the specified rect is NOT modified by this call.
///
/// It is legal for several different threads to lock a buffer for read access; none of the
/// threads are blocked.
///
/// Locking a buffer simultaneously for write or read/write is undefined, but will neither
/// terminate the process nor block the caller. This function may return an error or leave the
/// buffer's content in an indeterminate state.
///
/// If the buffer has [`HardwareBufferFormat::BLOB`], it is legal lock it for reading and
/// writing in multiple threads and/or processes simultaneously, and the contents of the buffer
/// behave like shared memory.
pub fn lock(
&self,
usage: HardwareBufferUsage,
fence: Option<RawFd>,
rect: Option<Rect>,
) -> Result<*mut c_void> {
let fence = fence.unwrap_or(-1);
let rect = match rect {
Some(rect) => &rect,
None => std::ptr::null(),
};
construct(|res| unsafe {
ffi::AHardwareBuffer_lock(self.as_ptr(), usage.0 .0, fence, rect, res)
})
}
/// Lock a [`HardwareBuffer`] for direct CPU access.
///
/// This function is the same as the above [`lock()`][Self::lock()] function, but passes back
/// additional information about the bytes per pixel and the bytes per stride of the locked
/// buffer. If the bytes per pixel or bytes per stride are unknown or variable, or if the
/// underlying mapper implementation does not support returning additional information, then
/// this call will fail with [`std::io::Error::kind()`] = [`std::io::ErrorKind::Unsupported`].
#[cfg(feature = "api-level-29")]
pub fn lock_and_get_info(
&self,
usage: HardwareBufferUsage,
fence: Option<RawFd>,
rect: Option<Rect>,
) -> Result<LockedPlaneInfo> {
let fence = fence.unwrap_or(-1);
let rect = match rect {
Some(rect) => &rect,
None => std::ptr::null(),
};
let mut virtual_address = MaybeUninit::uninit();
let mut bytes_per_pixel = MaybeUninit::uninit();
let mut bytes_per_stride = MaybeUninit::uninit();
let status = unsafe {
ffi::AHardwareBuffer_lockAndGetInfo(
self.as_ptr(),
usage.0 .0,
fence,
rect,
virtual_address.as_mut_ptr(),
bytes_per_pixel.as_mut_ptr(),
bytes_per_stride.as_mut_ptr(),
)
};
status_to_io_result(status, ()).map(|()| unsafe {
LockedPlaneInfo {
virtual_address: virtual_address.assume_init(),
bytes_per_pixel: bytes_per_pixel.assume_init() as u32,
bytes_per_stride: bytes_per_stride.assume_init() as u32,
}
})
}
/// Lock a potentially multi-planar [`HardwareBuffer`] for direct CPU access.
///
/// This function is similar to [`lock()`][Self::lock()], but can lock multi-planar formats.
/// Note, that multi-planar should not be confused with multi-layer images, which this locking
/// function does not support.
///
/// YUV formats are always represented by three separate planes of data, one for each color
/// plane. The order of planes in the array is guaranteed such that plane #0 is always `Y`,
/// plane #1 is always `U` (`Cb`), and plane #2 is always `V` (`Cr`). All other formats are
/// represented by a single plane.
///
/// Additional information always accompanies the buffers, describing the row stride and the
/// pixel stride for each plane.
///
/// In case the buffer cannot be locked, this will return zero planes.
///
/// See the [`lock()`][Self::lock()] documentation for all other locking semantics.
#[cfg(feature = "api-level-29")]
pub fn lock_planes(
&self,
usage: HardwareBufferUsage,
fence: Option<RawFd>,
rect: Option<Rect>,
) -> Result<HardwareBufferPlanes> {
let fence = fence.unwrap_or(-1);
let rect = match rect {
Some(rect) => &rect,
None => std::ptr::null(),
};
let planes = construct(|res| unsafe {
ffi::AHardwareBuffer_lockPlanes(self.as_ptr(), usage.0 .0, fence, rect, res)
})?;
Ok(HardwareBufferPlanes {
inner: planes,
index: 0,
})
}
/// Unlock the [`HardwareBuffer`] from direct CPU access.
///
/// Must be called after all changes to the buffer are completed by the caller. The function
/// will block until all work is completed. See [`unlock_async()`][Self::unlock_async()] for
/// a non-blocking variant that returns a file descriptor to be signaled on unlocking instead.
pub fn unlock(&self) -> Result<()> {
let status = unsafe { ffi::AHardwareBuffer_unlock(self.as_ptr(), std::ptr::null_mut()) };
status_to_io_result(status, ())
}
/// Unlock the [`HardwareBuffer`] from direct CPU access.
///
/// Returns a fence file descriptor that will become signaled when unlocking is completed, or
/// [`None`] if unlocking is already finished. The caller is responsible for closing the file
/// descriptor once it's no longer needed. See [`unlock()`][Self::unlock()] for a variant that
/// blocks instead.
pub fn unlock_async(&self) -> Result<Option<RawFd>> {
let fence = construct(|res| unsafe { ffi::AHardwareBuffer_unlock(self.as_ptr(), res) })?;
Ok(match fence {
-1 => None,
fence => Some(fence),
})
}
/// Receive a [`HardwareBuffer`] from an `AF_UNIX` socket.
///
/// `AF_UNIX` sockets are wrapped by [`std::os::unix::net::UnixListener`] in Rust.
pub fn recv_handle_from_unix_socket(socket_fd: RawFd) -> Result<Self> {
unsafe {
let ptr =
construct(|res| ffi::AHardwareBuffer_recvHandleFromUnixSocket(socket_fd, res))?;
Ok(Self::from_ptr(NonNull::new_unchecked(ptr)))
}
}
/// Send the [`HardwareBuffer`] to an `AF_UNIX` socket.
///
/// `AF_UNIX` sockets are wrapped by [`std::os::unix::net::UnixListener`] in Rust.
pub fn send_handle_to_unix_socket(&self, socket_fd: RawFd) -> Result<()> {
let status =
unsafe { ffi::AHardwareBuffer_sendHandleToUnixSocket(self.as_ptr(), socket_fd) };
status_to_io_result(status, ())
}
/// Acquire a reference on the given [`HardwareBuffer`] object.
///
/// This prevents the object from being deleted until the last strong reference, represented
/// by [`HardwareBufferRef`], is [`drop()`]ped.
pub fn acquire(&self) -> HardwareBufferRef {
unsafe {
ffi::AHardwareBuffer_acquire(self.as_ptr());
HardwareBufferRef::from_ptr(self.inner)
}
}
}
/// A [`HardwareBuffer`] with an owned reference, that is released when dropped.
/// It behaves much like a strong [`std::rc::Rc`] reference.
#[derive(Debug)]
pub struct HardwareBufferRef {
inner: HardwareBuffer,
}
impl HardwareBufferRef {
/// Create an _owned_ [`HardwareBuffer`] from a native pointer
///
/// To wrap a weak reference (that is **not** `release`d on [`Drop`]), call
/// [`HardwareBuffer::from_ptr()`] instead.
///
/// # Safety
/// By calling this function, you assert that it is a valid pointer to an NDK
/// [`ffi::AHardwareBuffer`].
pub unsafe fn from_ptr(ptr: NonNull<ffi::AHardwareBuffer>) -> Self {
Self {
inner: HardwareBuffer { inner: ptr },
}
}
}
impl Deref for HardwareBufferRef {
type Target = HardwareBuffer;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl Drop for HardwareBufferRef {
fn drop(&mut self) {
unsafe { ffi::AHardwareBuffer_release(self.inner.as_ptr()) }
}
}
impl Clone for HardwareBufferRef {
fn clone(&self) -> Self {
self.acquire()
}
}
/// Buffer description.
///
/// Used for allocating new buffers and querying parameters of existing ones.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct HardwareBufferDesc {
pub width: u32,
pub height: u32,
pub layers: u32,
pub format: HardwareBufferFormat,
pub usage: HardwareBufferUsage,
pub stride: u32,
}
impl HardwareBufferDesc {
fn into_native(self) -> ffi::AHardwareBuffer_Desc {
ffi::AHardwareBuffer_Desc {
width: self.width,
height: self.height,
layers: self.layers,
format: self.format.try_into().unwrap(),
usage: self.usage.0 .0,
stride: self.stride,
rfu0: 0,
rfu1: 0,
}
}
}
/// A native [`AHardwareBuffer_Plane`]
///
/// Contains the same fields as [`ffi::AHardwareBuffer_Plane`].
///
/// [`AHardwareBuffer_Plane`]: https://developer.android.com/ndk/reference/group/a-hardware-buffer#ahardwarebuffer_plane
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct LockedPlaneInfo {
pub virtual_address: *mut c_void,
pub bytes_per_pixel: u32,
pub bytes_per_stride: u32,
}
/// Iterator over [`ffi::AHardwareBuffer_Planes`], containing a list of [`LockedPlaneInfo`].
#[derive(Debug)]
pub struct HardwareBufferPlanes {
inner: ffi::AHardwareBuffer_Planes,
index: u32,
}
impl Iterator for HardwareBufferPlanes {
type Item = LockedPlaneInfo;
fn next(&mut self) -> Option<LockedPlaneInfo> {
if self.index == self.inner.planeCount {
None
} else {
let plane = self.inner.planes[self.index as usize];
self.index += 1;
Some(LockedPlaneInfo {
virtual_address: plane.data,
bytes_per_pixel: plane.pixelStride,
bytes_per_stride: plane.rowStride,
})
}
}
}

View file

@ -0,0 +1,38 @@
//! Bindings for [`AHardwareBuffer_Format`]
//!
//! [`AHardwareBuffer_Format`]: https://developer.android.com/ndk/reference/group/a-hardware-buffer#ahardwarebuffer_format
use num_enum::{IntoPrimitive, TryFromPrimitive};
/// Buffer pixel formats.
#[repr(u32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[allow(non_camel_case_types)]
pub enum HardwareBufferFormat {
/// Matches deprecated [`ffi::ANativeWindow_LegacyFormat::WINDOW_FORMAT_RGBA_8888`].
R8G8B8A8_UNORM = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM.0,
/// Matches deprecated [`ffi::ANativeWindow_LegacyFormat::WINDOW_FORMAT_RGBX_8888`].
R8G8B8X8_UNORM = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM.0,
#[cfg(feature = "api-level-26")]
R8G8B8_UNORM = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM.0,
/// Matches deprecated [`ffi::ANativeWindow_LegacyFormat::WINDOW_FORMAT_RGB_565`].
R5G6B5_UNORM = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM.0,
R16G16B16A16_FLOAT = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT.0,
#[cfg(feature = "api-level-26")]
R10G10B10A2_UNORM = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM.0,
#[cfg(feature = "api-level-26")]
BLOB = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_BLOB.0,
#[cfg(feature = "api-level-26")]
D16_UNORM = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_D16_UNORM.0,
#[cfg(feature = "api-level-26")]
D24_UNORM = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_D24_UNORM.0,
#[cfg(feature = "api-level-26")]
D24_UNORM_S8_UINT = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT.0,
#[cfg(feature = "api-level-26")]
D32_FLOAT = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_D32_FLOAT.0,
#[cfg(feature = "api-level-26")]
D32_FLOAT_S8_UINT = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT.0,
#[cfg(feature = "api-level-26")]
S8_UINT = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_S8_UINT.0,
#[cfg(feature = "api-level-26")]
Y8Cb8Cr8_420 = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420.0,
}

View file

@ -0,0 +1,114 @@
//! Bindings for [`AInputQueue`]
//!
//! [`AInputQueue`]: https://developer.android.com/ndk/reference/group/input#ainputqueue
use std::io::Result;
use std::os::raw::c_int;
use std::ptr::{self, NonNull};
use crate::event::InputEvent;
#[cfg(doc)]
use crate::event::KeyEvent;
use crate::looper::ForeignLooper;
use crate::utils::status_to_io_result;
/// A native [`AInputQueue *`]
///
/// An input queue is the facility through which you retrieve input events.
///
/// [`AInputQueue *`]: https://developer.android.com/ndk/reference/group/input#ainputqueue
#[derive(Debug)]
pub struct InputQueue {
ptr: NonNull<ffi::AInputQueue>,
}
// It gets shared between threads in `ndk-glue`
unsafe impl Send for InputQueue {}
unsafe impl Sync for InputQueue {}
impl InputQueue {
/// Construct an [`InputQueue`] from the native pointer.
///
/// # Safety
/// By calling this function, you assert that the pointer is a valid pointer to an NDK [`ffi::AInputQueue`].
pub unsafe fn from_ptr(ptr: NonNull<ffi::AInputQueue>) -> Self {
Self { ptr }
}
pub fn ptr(&self) -> NonNull<ffi::AInputQueue> {
self.ptr
}
/// Returns the next available [`InputEvent`] from the queue.
///
/// Returns [`None`] if no event is available.
pub fn get_event(&self) -> Result<Option<InputEvent>> {
let mut out_event = ptr::null_mut();
let status = unsafe { ffi::AInputQueue_getEvent(self.ptr.as_ptr(), &mut out_event) };
match status_to_io_result(status, ()) {
Ok(()) => {
debug_assert!(!out_event.is_null());
Ok(Some(unsafe {
InputEvent::from_ptr(NonNull::new_unchecked(out_event))
}))
}
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => Ok(None),
Err(e) => Err(e),
}
}
/// Returns [`true`] if there are one or more events available in the input queue.
pub fn has_events(&self) -> bool {
match unsafe { ffi::AInputQueue_hasEvents(self.ptr.as_ptr()) } {
0 => false,
1 => true,
r => unreachable!("AInputQueue_hasEvents returned non-boolean {}", r),
}
}
/// Sends the key for standard pre-dispatching that is, possibly deliver it to the current IME
/// to be consumed before the app.
///
/// Returns [`Some`] if it was not pre-dispatched, meaning you can process it right now. If
/// [`None`] is returned, you must abandon the current event processing and allow the event to
/// appear again in the event queue (if it does not get consumed during pre-dispatching).
///
/// Also returns [`None`] if `event` is not a [`KeyEvent`].
pub fn pre_dispatch(&self, event: InputEvent) -> Option<InputEvent> {
match unsafe { ffi::AInputQueue_preDispatchEvent(self.ptr.as_ptr(), event.ptr().as_ptr()) }
{
0 => Some(event),
1 => None,
r => unreachable!("AInputQueue_preDispatchEvent returned non-boolean {}", r),
}
}
/// Report that dispatching has finished with the given [`InputEvent`].
///
/// This must be called after receiving an event with [`InputQueue::get_event()`].
pub fn finish_event(&self, event: InputEvent, handled: bool) {
unsafe {
ffi::AInputQueue_finishEvent(self.ptr.as_ptr(), event.ptr().as_ptr(), handled as c_int)
}
}
/// Add this input queue to a [`ForeignLooper`] for processing.
///
/// See [`ForeignLooper::add_fd()`] for information on the `ident`, `callback`, and `data` params.
pub fn attach_looper(&self, looper: &ForeignLooper, id: i32) {
unsafe {
ffi::AInputQueue_attachLooper(
self.ptr.as_ptr(),
looper.ptr().as_ptr(),
id,
None,
std::ptr::null_mut(),
)
}
}
/// Remove this input queue from the [`ForeignLooper`] it is currently attached to.
pub fn detach_looper(&self) {
unsafe { ffi::AInputQueue_detachLooper(self.ptr.as_ptr()) }
}
}

23
third-party/vendor/ndk/src/lib.rs vendored Normal file
View file

@ -0,0 +1,23 @@
//! # Android NDK
//!
//! Bindings to the [Android NDK].
//!
//! [Android NDK]: https://developer.android.com/ndk/reference
#![warn(missing_debug_implementations, trivial_casts)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
pub mod asset;
pub mod audio;
pub mod bitmap;
pub mod configuration;
pub mod event;
pub mod hardware_buffer;
pub mod hardware_buffer_format;
pub mod input_queue;
pub mod looper;
pub mod media;
pub mod native_activity;
pub mod native_window;
pub mod surface_texture;
pub mod trace;
mod utils;

310
third-party/vendor/ndk/src/looper.rs vendored Normal file
View file

@ -0,0 +1,310 @@
//! Bindings for [`ALooper`]
//!
//! In Android, [`ALooper`]s are inherently thread-local. Due to this, there are two different
//! [`ALooper`] interfaces exposed in this module:
//!
//! * [`ThreadLooper`], which has methods for the operations performable with a looper in one's own
//! thread; and
//! * [`ForeignLooper`], which has methods for the operations performable with any thread's looper.
//!
//! [`ALooper`]: https://developer.android.com/ndk/reference/group/looper#alooper
use bitflags::bitflags;
use std::convert::TryInto;
use std::mem::ManuallyDrop;
use std::os::raw::c_void;
use std::os::unix::io::RawFd;
use std::ptr;
use std::ptr::NonNull;
use std::time::Duration;
use thiserror::Error;
/// A thread-local native [`ALooper *`]. This promises that there is a looper associated with the
/// current thread.
///
/// [`ALooper *`]: https://developer.android.com/ndk/reference/group/looper#alooper
#[derive(Debug)]
pub struct ThreadLooper {
_marker: std::marker::PhantomData<*mut ()>, // Not send or sync
foreign: ForeignLooper,
}
bitflags! {
/// Flags for file descriptor events that a looper can monitor.
pub struct FdEvent: u32 {
const INPUT = ffi::ALOOPER_EVENT_INPUT;
const OUTPUT = ffi::ALOOPER_EVENT_OUTPUT;
const ERROR = ffi::ALOOPER_EVENT_ERROR;
const HANGUP = ffi::ALOOPER_EVENT_HANGUP;
const INVALID = ffi::ALOOPER_EVENT_INVALID;
}
}
/// The poll result from a [`ThreadLooper`].
#[derive(Debug)]
pub enum Poll {
/// This looper was woken using [`ForeignLooper::wake()`]
Wake,
/// For [`ThreadLooper::poll_once*()`][ThreadLooper::poll_once()], an event was received and processed using a callback.
Callback,
/// For [`ThreadLooper::poll_*_timeout()`][ThreadLooper::poll_once_timeout()], the requested timeout was reached before any events.
Timeout,
/// An event was received
Event {
ident: i32,
fd: RawFd,
events: FdEvent,
data: *mut c_void,
},
}
#[derive(Debug, Copy, Clone, Error)]
#[error("Android Looper error")]
pub struct LooperError;
impl ThreadLooper {
/// Prepares a looper for the current thread and returns it
pub fn prepare() -> Self {
unsafe {
let ptr = ffi::ALooper_prepare(ffi::ALOOPER_PREPARE_ALLOW_NON_CALLBACKS as _);
let foreign = ForeignLooper::from_ptr(NonNull::new(ptr).expect("looper non null"));
Self {
_marker: std::marker::PhantomData,
foreign,
}
}
}
/// Returns the looper associated with the current thread, if any.
pub fn for_thread() -> Option<Self> {
Some(Self {
_marker: std::marker::PhantomData,
foreign: ForeignLooper::for_thread()?,
})
}
fn poll_once_ms(&self, ms: i32) -> Result<Poll, LooperError> {
let mut fd: RawFd = -1;
let mut events: i32 = -1;
let mut data: *mut c_void = ptr::null_mut();
match unsafe { ffi::ALooper_pollOnce(ms, &mut fd, &mut events, &mut data) } {
ffi::ALOOPER_POLL_WAKE => Ok(Poll::Wake),
ffi::ALOOPER_POLL_CALLBACK => Ok(Poll::Callback),
ffi::ALOOPER_POLL_TIMEOUT => Ok(Poll::Timeout),
ffi::ALOOPER_POLL_ERROR => Err(LooperError),
ident if ident >= 0 => Ok(Poll::Event {
ident,
fd,
events: FdEvent::from_bits(events as u32)
.expect("poll event contains unknown bits"),
data,
}),
_ => unreachable!(),
}
}
/// Polls the looper, blocking on processing an event.
#[inline]
pub fn poll_once(&self) -> Result<Poll, LooperError> {
self.poll_once_ms(-1)
}
/// Polls the looper, blocking on processing an event, but with a timeout. Give a timeout of 0
/// to make this non-blocking.
///
/// It panics if the timeout is larger than expressible as an [`i32`] of milliseconds (roughly 25
/// days).
#[inline]
pub fn poll_once_timeout(&self, timeout: Duration) -> Result<Poll, LooperError> {
self.poll_once_ms(
timeout
.as_millis()
.try_into()
.expect("Supplied timeout is too large"),
)
}
fn poll_all_ms(&self, ms: i32) -> Result<Poll, LooperError> {
let mut fd: RawFd = -1;
let mut events: i32 = -1;
let mut data: *mut c_void = ptr::null_mut();
match unsafe { ffi::ALooper_pollAll(ms, &mut fd, &mut events, &mut data) } {
ffi::ALOOPER_POLL_WAKE => Ok(Poll::Wake),
ffi::ALOOPER_POLL_TIMEOUT => Ok(Poll::Timeout),
ffi::ALOOPER_POLL_ERROR => Err(LooperError),
ident if ident >= 0 => Ok(Poll::Event {
ident,
fd,
events: FdEvent::from_bits(events as u32)
.expect("poll event contains unknown bits"),
data,
}),
_ => unreachable!(),
}
}
/// Repeatedly polls the looper, blocking on processing an event.
///
/// This function will never return [`Poll::Callback`].
#[inline]
pub fn poll_all(&self) -> Result<Poll, LooperError> {
self.poll_all_ms(-1)
}
/// Repeatedly polls the looper, blocking on processing an event, but with a timeout. Give a
/// timeout of 0 to make this non-blocking.
///
/// This function will never return [`Poll::Callback`].
///
/// It panics if the timeout is larger than expressible as an [`i32`] of milliseconds (roughly 25
/// days).
#[inline]
pub fn poll_all_timeout(&self, timeout: Duration) -> Result<Poll, LooperError> {
self.poll_all_ms(
timeout
.as_millis()
.try_into()
.expect("Supplied timeout is too large"),
)
}
/// Returns a reference to the [`ForeignLooper`] that is associated with the current thread.
pub fn as_foreign(&self) -> &ForeignLooper {
&self.foreign
}
pub fn into_foreign(self) -> ForeignLooper {
self.foreign
}
}
/// A native [`ALooper *`], not necessarily allocated with the current thread.
///
/// [`ALooper *`]: https://developer.android.com/ndk/reference/group/looper#alooper
#[derive(Debug)]
pub struct ForeignLooper {
ptr: NonNull<ffi::ALooper>,
}
unsafe impl Send for ForeignLooper {}
unsafe impl Sync for ForeignLooper {}
impl Drop for ForeignLooper {
fn drop(&mut self) {
unsafe { ffi::ALooper_release(self.ptr.as_ptr()) }
}
}
impl Clone for ForeignLooper {
fn clone(&self) -> Self {
unsafe {
ffi::ALooper_acquire(self.ptr.as_ptr());
Self { ptr: self.ptr }
}
}
}
impl ForeignLooper {
/// Returns the looper associated with the current thread, if any.
#[inline]
pub fn for_thread() -> Option<Self> {
NonNull::new(unsafe { ffi::ALooper_forThread() }).map(|ptr| unsafe { Self::from_ptr(ptr) })
}
/// Construct a [`ForeignLooper`] object from the given pointer.
///
/// # Safety
/// By calling this function, you guarantee that the pointer is a valid, non-null pointer to an
/// NDK [`ffi::ALooper`].
#[inline]
pub unsafe fn from_ptr(ptr: NonNull<ffi::ALooper>) -> Self {
ffi::ALooper_acquire(ptr.as_ptr());
Self { ptr }
}
/// Returns a pointer to the NDK `ALooper` object.
#[inline]
pub fn ptr(&self) -> NonNull<ffi::ALooper> {
self.ptr
}
/// Wakes the looper. An event of [`Poll::Wake`] will be sent.
pub fn wake(&self) {
unsafe { ffi::ALooper_wake(self.ptr.as_ptr()) }
}
/// Adds a file descriptor to be polled, without a callback.
///
/// See also [the NDK
/// docs](https://developer.android.com/ndk/reference/group/looper.html#alooper_addfd).
// `ALooper_addFd` won't dereference `data`; it will only pass it on to the event.
// Optionally dereferencing it there already enforces `unsafe` context.
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn add_fd(
&self,
fd: RawFd,
ident: i32,
events: FdEvent,
data: *mut c_void,
) -> Result<(), LooperError> {
match unsafe {
ffi::ALooper_addFd(
self.ptr.as_ptr(),
fd,
ident,
events.bits() as i32,
None,
data,
)
} {
1 => Ok(()),
-1 => Err(LooperError),
_ => unreachable!(),
}
}
/// Adds a file descriptor to be polled, with a callback.
///
/// The callback takes as an argument the file descriptor, and should return true to continue
/// receiving callbacks, or false to have the callback unregistered.
///
/// See also [the NDK
/// docs](https://developer.android.com/ndk/reference/group/looper.html#alooper_addfd).
pub fn add_fd_with_callback<F: FnMut(RawFd) -> bool>(
&self,
fd: RawFd,
events: FdEvent,
callback: F,
) -> Result<(), LooperError> {
extern "C" fn cb_handler<F: FnMut(RawFd) -> bool>(
fd: RawFd,
_events: i32,
data: *mut c_void,
) -> i32 {
unsafe {
let mut cb = ManuallyDrop::new(Box::<F>::from_raw(data as *mut _));
let keep_registered = cb(fd);
if !keep_registered {
ManuallyDrop::into_inner(cb);
}
keep_registered as i32
}
}
let data = Box::into_raw(Box::new(callback)) as *mut _;
match unsafe {
ffi::ALooper_addFd(
self.ptr.as_ptr(),
fd,
ffi::ALOOPER_POLL_CALLBACK,
events.bits() as i32,
Some(cb_handler::<F>),
data,
)
} {
1 => Ok(()),
-1 => Err(LooperError),
_ => unreachable!(),
}
}
}

View file

@ -0,0 +1,82 @@
use super::Result;
use thiserror::Error;
#[repr(i32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum MediaErrorResult {
CodecErrorInsufficientResource = ffi::media_status_t::AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE.0,
CodecErrorReclaimed = ffi::media_status_t::AMEDIACODEC_ERROR_RECLAIMED.0,
ErrorUnknown = ffi::media_status_t::AMEDIA_ERROR_UNKNOWN.0,
ErrorMalformed = ffi::media_status_t::AMEDIA_ERROR_MALFORMED.0,
ErrorUnsupported = ffi::media_status_t::AMEDIA_ERROR_UNSUPPORTED.0,
ErrorInvalidObject = ffi::media_status_t::AMEDIA_ERROR_INVALID_OBJECT.0,
ErrorInvalidParameter = ffi::media_status_t::AMEDIA_ERROR_INVALID_PARAMETER.0,
ErrorInvalidOperation = ffi::media_status_t::AMEDIA_ERROR_INVALID_OPERATION.0,
ErrorEndOfStream = ffi::media_status_t::AMEDIA_ERROR_END_OF_STREAM.0,
ErrorIo = ffi::media_status_t::AMEDIA_ERROR_IO.0,
ErrorWouldBlock = ffi::media_status_t::AMEDIA_ERROR_WOULD_BLOCK.0,
DrmErrorBase = ffi::media_status_t::AMEDIA_DRM_ERROR_BASE.0,
DrmNotProvisioned = ffi::media_status_t::AMEDIA_DRM_NOT_PROVISIONED.0,
DrmResourceBusy = ffi::media_status_t::AMEDIA_DRM_RESOURCE_BUSY.0,
DrmDeviceRevoked = ffi::media_status_t::AMEDIA_DRM_DEVICE_REVOKED.0,
DrmShortBuffer = ffi::media_status_t::AMEDIA_DRM_SHORT_BUFFER.0,
DrmSessionNotOpened = ffi::media_status_t::AMEDIA_DRM_SESSION_NOT_OPENED.0,
DrmTamperDetected = ffi::media_status_t::AMEDIA_DRM_TAMPER_DETECTED.0,
DrmVerifyFailed = ffi::media_status_t::AMEDIA_DRM_VERIFY_FAILED.0,
DrmNeedKey = ffi::media_status_t::AMEDIA_DRM_NEED_KEY.0,
DrmLicenseExpired = ffi::media_status_t::AMEDIA_DRM_LICENSE_EXPIRED.0,
ImgreaderErrorBase = ffi::media_status_t::AMEDIA_IMGREADER_ERROR_BASE.0,
ImgreaderNoBufferAvailable = ffi::media_status_t::AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE.0,
ImgreaderMaxImagesAcquired = ffi::media_status_t::AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED.0,
ImgreaderCannotLockImage = ffi::media_status_t::AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE.0,
ImgreaderCannotUnlockImage = ffi::media_status_t::AMEDIA_IMGREADER_CANNOT_UNLOCK_IMAGE.0,
ImgreaderImageNotLocked = ffi::media_status_t::AMEDIA_IMGREADER_IMAGE_NOT_LOCKED.0,
}
#[derive(Debug, Error)]
pub enum NdkMediaError {
#[error("error Media result ({0:?})")]
ErrorResult(MediaErrorResult),
#[error("unknown Media error result ({0:?})")]
UnknownResult(ffi::media_status_t),
}
impl NdkMediaError {
pub(crate) fn from_status(status: ffi::media_status_t) -> Result<()> {
use MediaErrorResult::*;
let result = match status {
ffi::media_status_t::AMEDIA_OK => return Ok(()),
ffi::media_status_t::AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE => {
CodecErrorInsufficientResource
}
ffi::media_status_t::AMEDIACODEC_ERROR_RECLAIMED => CodecErrorReclaimed,
ffi::media_status_t::AMEDIA_ERROR_UNKNOWN => ErrorUnknown,
ffi::media_status_t::AMEDIA_ERROR_MALFORMED => ErrorMalformed,
ffi::media_status_t::AMEDIA_ERROR_UNSUPPORTED => ErrorUnsupported,
ffi::media_status_t::AMEDIA_ERROR_INVALID_OBJECT => ErrorInvalidObject,
ffi::media_status_t::AMEDIA_ERROR_INVALID_PARAMETER => ErrorInvalidParameter,
ffi::media_status_t::AMEDIA_ERROR_INVALID_OPERATION => ErrorInvalidOperation,
ffi::media_status_t::AMEDIA_ERROR_END_OF_STREAM => ErrorEndOfStream,
ffi::media_status_t::AMEDIA_ERROR_IO => ErrorIo,
ffi::media_status_t::AMEDIA_ERROR_WOULD_BLOCK => ErrorWouldBlock,
ffi::media_status_t::AMEDIA_DRM_ERROR_BASE => DrmErrorBase,
ffi::media_status_t::AMEDIA_DRM_NOT_PROVISIONED => DrmNotProvisioned,
ffi::media_status_t::AMEDIA_DRM_RESOURCE_BUSY => DrmResourceBusy,
ffi::media_status_t::AMEDIA_DRM_DEVICE_REVOKED => DrmDeviceRevoked,
ffi::media_status_t::AMEDIA_DRM_SHORT_BUFFER => DrmShortBuffer,
ffi::media_status_t::AMEDIA_DRM_SESSION_NOT_OPENED => DrmSessionNotOpened,
ffi::media_status_t::AMEDIA_DRM_TAMPER_DETECTED => DrmTamperDetected,
ffi::media_status_t::AMEDIA_DRM_VERIFY_FAILED => DrmVerifyFailed,
ffi::media_status_t::AMEDIA_DRM_NEED_KEY => DrmNeedKey,
ffi::media_status_t::AMEDIA_DRM_LICENSE_EXPIRED => DrmLicenseExpired,
ffi::media_status_t::AMEDIA_IMGREADER_ERROR_BASE => ImgreaderErrorBase,
ffi::media_status_t::AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE => ImgreaderNoBufferAvailable,
ffi::media_status_t::AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED => ImgreaderMaxImagesAcquired,
ffi::media_status_t::AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE => ImgreaderCannotLockImage,
ffi::media_status_t::AMEDIA_IMGREADER_CANNOT_UNLOCK_IMAGE => ImgreaderCannotUnlockImage,
ffi::media_status_t::AMEDIA_IMGREADER_IMAGE_NOT_LOCKED => ImgreaderImageNotLocked,
_ => return Err(NdkMediaError::UnknownResult(status)),
};
Err(NdkMediaError::ErrorResult(result))
}
}

View file

@ -0,0 +1,366 @@
//! Bindings for [`AImageReader`] and [`AImage`]
//!
//! [`AImageReader`]: https://developer.android.com/ndk/reference/group/media#aimagereader
//! [`AImage`]: https://developer.android.com/ndk/reference/group/media#aimage
#![cfg(feature = "api-level-24")]
use super::NdkMediaError;
use super::{construct, construct_never_null, error::MediaErrorResult, Result};
use crate::native_window::NativeWindow;
use num_enum::{IntoPrimitive, TryFromPrimitive};
use std::{
convert::TryInto,
ffi::c_void,
fmt::{self, Debug, Formatter},
mem::MaybeUninit,
ptr::NonNull,
};
#[cfg(feature = "api-level-26")]
use std::os::unix::io::RawFd;
#[cfg(feature = "api-level-26")]
use crate::hardware_buffer::{HardwareBuffer, HardwareBufferUsage};
#[repr(u32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[allow(non_camel_case_types)]
pub enum ImageFormat {
RGBA_8888 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RGBA_8888.0,
RGBX_8888 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RGBX_8888.0,
RGB_888 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RGB_888.0,
RGB_565 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RGB_565.0,
RGBA_FP16 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RGBA_FP16.0,
YUV_420_888 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_YUV_420_888.0,
JPEG = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_JPEG.0,
RAW16 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RAW16.0,
RAW_PRIVATE = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RAW_PRIVATE.0,
RAW10 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RAW10.0,
RAW12 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RAW12.0,
DEPTH16 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_DEPTH16.0,
DEPTH_POINT_CLOUD = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_DEPTH_POINT_CLOUD.0,
PRIVATE = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_PRIVATE.0,
Y8 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_Y8.0,
HEIC = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_HEIC.0,
DEPTH_JPEG = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_DEPTH_JPEG.0,
}
pub type ImageListener = Box<dyn FnMut(&ImageReader)>;
#[cfg(feature = "api-level-26")]
pub type BufferRemovedListener = Box<dyn FnMut(&ImageReader, &HardwareBuffer)>;
/// A native [`AImageReader *`]
///
/// [`AImageReader *`]: https://developer.android.com/ndk/reference/group/media#aimagereader
pub struct ImageReader {
inner: NonNull<ffi::AImageReader>,
image_cb: Option<Box<ImageListener>>,
#[cfg(feature = "api-level-26")]
buffer_removed_cb: Option<Box<BufferRemovedListener>>,
}
impl Debug for ImageReader {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_struct("ImageReader")
.field("inner", &self.inner)
.field(
"image_cb",
match &self.image_cb {
Some(_) => &"Some(_)",
None => &"None",
},
)
.finish()
}
}
impl ImageReader {
fn from_ptr(inner: NonNull<ffi::AImageReader>) -> Self {
Self {
inner,
image_cb: None,
#[cfg(feature = "api-level-26")]
buffer_removed_cb: None,
}
}
fn as_ptr(&self) -> *mut ffi::AImageReader {
self.inner.as_ptr()
}
pub fn new(width: i32, height: i32, format: ImageFormat, max_images: i32) -> Result<Self> {
let inner = construct_never_null(|res| unsafe {
ffi::AImageReader_new(width, height, format as i32, max_images, res)
})?;
Ok(Self::from_ptr(inner))
}
#[cfg(feature = "api-level-26")]
pub fn new_with_usage(
width: i32,
height: i32,
format: ImageFormat,
usage: HardwareBufferUsage,
max_images: i32,
) -> Result<Self> {
let inner = construct_never_null(|res| unsafe {
ffi::AImageReader_newWithUsage(
width,
height,
format as i32,
usage.0 .0,
max_images,
res,
)
})?;
Ok(Self::from_ptr(inner))
}
pub fn set_image_listener(&mut self, listener: ImageListener) -> Result<()> {
let mut boxed = Box::new(listener);
let ptr: *mut ImageListener = &mut *boxed;
// keep listener alive until Drop or new listener is assigned
self.image_cb = Some(boxed);
unsafe extern "C" fn on_image_available(
context: *mut c_void,
reader: *mut ffi::AImageReader,
) {
let reader = ImageReader::from_ptr(NonNull::new_unchecked(reader));
let listener: *mut ImageListener = context as *mut _;
(*listener)(&reader);
std::mem::forget(reader);
}
let mut listener = ffi::AImageReader_ImageListener {
context: ptr as _,
onImageAvailable: Some(on_image_available),
};
let status = unsafe { ffi::AImageReader_setImageListener(self.as_ptr(), &mut listener) };
NdkMediaError::from_status(status)
}
#[cfg(feature = "api-level-26")]
pub fn set_buffer_removed_listener(&mut self, listener: BufferRemovedListener) -> Result<()> {
let mut boxed = Box::new(listener);
let ptr: *mut BufferRemovedListener = &mut *boxed;
// keep listener alive until Drop or new listener is assigned
self.buffer_removed_cb = Some(boxed);
unsafe extern "C" fn on_buffer_removed(
context: *mut c_void,
reader: *mut ffi::AImageReader,
buffer: *mut ffi::AHardwareBuffer,
) {
let reader = ImageReader::from_ptr(NonNull::new_unchecked(reader));
let buffer = HardwareBuffer::from_ptr(NonNull::new_unchecked(buffer));
let listener: *mut BufferRemovedListener = context as *mut _;
(*listener)(&reader, &buffer);
std::mem::forget(reader);
}
let mut listener = ffi::AImageReader_BufferRemovedListener {
context: ptr as _,
onBufferRemoved: Some(on_buffer_removed),
};
let status =
unsafe { ffi::AImageReader_setBufferRemovedListener(self.as_ptr(), &mut listener) };
NdkMediaError::from_status(status)
}
pub fn get_window(&self) -> Result<NativeWindow> {
unsafe {
let ptr = construct_never_null(|res| ffi::AImageReader_getWindow(self.as_ptr(), res))?;
Ok(NativeWindow::from_ptr(ptr))
}
}
pub fn get_width(&self) -> Result<i32> {
construct(|res| unsafe { ffi::AImageReader_getWidth(self.as_ptr(), res) })
}
pub fn get_height(&self) -> Result<i32> {
construct(|res| unsafe { ffi::AImageReader_getHeight(self.as_ptr(), res) })
}
pub fn get_format(&self) -> Result<ImageFormat> {
let format = construct(|res| unsafe { ffi::AImageReader_getFormat(self.as_ptr(), res) })?;
Ok((format as u32).try_into().unwrap())
}
pub fn get_max_images(&self) -> Result<i32> {
construct(|res| unsafe { ffi::AImageReader_getMaxImages(self.as_ptr(), res) })
}
pub fn acquire_next_image(&self) -> Result<Option<Image>> {
let res = construct_never_null(|res| unsafe {
ffi::AImageReader_acquireNextImage(self.as_ptr(), res)
});
match res {
Ok(inner) => Ok(Some(Image { inner })),
Err(NdkMediaError::ErrorResult(MediaErrorResult::ImgreaderNoBufferAvailable)) => {
Ok(None)
}
Err(e) => Err(e),
}
}
/// # Safety
/// If the returned file descriptor is not `None`, it must be awaited before attempting to access the Image returned.
/// <https://developer.android.com/ndk/reference/group/media#aimagereader_acquirenextimageasync>
#[cfg(feature = "api-level-26")]
pub unsafe fn acquire_next_image_async(&self) -> Result<(Image, Option<RawFd>)> {
let mut fence = MaybeUninit::uninit();
let inner = construct_never_null(|res| {
ffi::AImageReader_acquireNextImageAsync(self.as_ptr(), res, fence.as_mut_ptr())
})?;
let image = Image { inner };
Ok(match fence.assume_init() {
-1 => (image, None),
fence => (image, Some(fence)),
})
}
pub fn acquire_latest_image(&self) -> Result<Option<Image>> {
let res = construct_never_null(|res| unsafe {
ffi::AImageReader_acquireLatestImage(self.as_ptr(), res)
});
if let Err(NdkMediaError::ErrorResult(MediaErrorResult::ImgreaderNoBufferAvailable)) = res {
return Ok(None);
}
Ok(Some(Image { inner: res? }))
}
/// # Safety
/// If the returned file descriptor is not `None`, it must be awaited before attempting to access the Image returned.
/// <https://developer.android.com/ndk/reference/group/media#aimagereader_acquirelatestimageasync>
#[cfg(feature = "api-level-26")]
pub fn acquire_latest_image_async(&self) -> Result<(Image, Option<RawFd>)> {
let mut fence = MaybeUninit::uninit();
let inner = construct_never_null(|res| unsafe {
ffi::AImageReader_acquireLatestImageAsync(self.as_ptr(), res, fence.as_mut_ptr())
})?;
let image = Image { inner };
Ok(match unsafe { fence.assume_init() } {
-1 => (image, None),
fence => (image, Some(fence)),
})
}
}
impl Drop for ImageReader {
fn drop(&mut self) {
unsafe { ffi::AImageReader_delete(self.as_ptr()) };
}
}
/// A native [`AImage *`]
///
/// [`AImage *`]: https://developer.android.com/ndk/reference/group/media#aimage
#[derive(Debug)]
pub struct Image {
inner: NonNull<ffi::AImage>,
}
pub type CropRect = ffi::AImageCropRect;
impl Image {
fn as_ptr(&self) -> *mut ffi::AImage {
self.inner.as_ptr()
}
pub fn get_plane_data(&self, plane_idx: i32) -> Result<&[u8]> {
let mut result_ptr = MaybeUninit::uninit();
let mut result_len = MaybeUninit::uninit();
let status = unsafe {
ffi::AImage_getPlaneData(
self.as_ptr(),
plane_idx,
result_ptr.as_mut_ptr(),
result_len.as_mut_ptr(),
)
};
NdkMediaError::from_status(status).map(|()| unsafe {
std::slice::from_raw_parts(result_ptr.assume_init(), result_len.assume_init() as _)
})
}
pub fn get_plane_pixel_stride(&self, plane_idx: i32) -> Result<i32> {
construct(|res| unsafe { ffi::AImage_getPlanePixelStride(self.as_ptr(), plane_idx, res) })
}
pub fn get_plane_row_stride(&self, plane_idx: i32) -> Result<i32> {
construct(|res| unsafe { ffi::AImage_getPlaneRowStride(self.as_ptr(), plane_idx, res) })
}
pub fn get_crop_rect(&self) -> Result<CropRect> {
construct(|res| unsafe { ffi::AImage_getCropRect(self.as_ptr(), res) })
}
pub fn get_width(&self) -> Result<i32> {
construct(|res| unsafe { ffi::AImage_getWidth(self.as_ptr(), res) })
}
pub fn get_height(&self) -> Result<i32> {
construct(|res| unsafe { ffi::AImage_getHeight(self.as_ptr(), res) })
}
pub fn get_format(&self) -> Result<ImageFormat> {
let format = construct(|res| unsafe { ffi::AImage_getFormat(self.as_ptr(), res) })?;
Ok((format as u32).try_into().unwrap())
}
pub fn get_timestamp(&self) -> Result<i64> {
construct(|res| unsafe { ffi::AImage_getTimestamp(self.as_ptr(), res) })
}
pub fn get_number_of_planes(&self) -> Result<i32> {
construct(|res| unsafe { ffi::AImage_getNumberOfPlanes(self.as_ptr(), res) })
}
/// Get the hardware buffer handle of the input image intended for GPU and/or hardware access.
///
/// Note that no reference on the returned [`HardwareBuffer`] handle is acquired automatically.
/// Once the [`Image`] or the parent [`ImageReader`] is deleted, the [`HardwareBuffer`] handle
/// from previous [`Image::get_hardware_buffer()`] becomes invalid.
///
/// If the caller ever needs to hold on a reference to the [`HardwareBuffer`] handle after the
/// [`Image`] or the parent [`ImageReader`] is deleted, it must call
/// [`HardwareBuffer::acquire()`] to acquire an extra reference, and [`drop()`] it when
/// finished using it in order to properly deallocate the underlying memory managed by
/// [`HardwareBuffer`]. If the caller has acquired an extra reference on a [`HardwareBuffer`]
/// returned from this function, it must also register a listener using
/// [`ImageReader::set_buffer_removed_listener()`] to be notified when the buffer is no longer
/// used by [`ImageReader`].
#[cfg(feature = "api-level-26")]
pub fn get_hardware_buffer(&self) -> Result<HardwareBuffer> {
unsafe {
let ptr =
construct_never_null(|res| ffi::AImage_getHardwareBuffer(self.as_ptr(), res))?;
Ok(HardwareBuffer::from_ptr(ptr))
}
}
#[cfg(feature = "api-level-26")]
pub fn delete_async(self, release_fence_fd: RawFd) {
unsafe { ffi::AImage_deleteAsync(self.as_ptr(), release_fence_fd) };
std::mem::forget(self);
}
}
impl Drop for Image {
fn drop(&mut self) {
unsafe { ffi::AImage_delete(self.as_ptr()) };
}
}

View file

@ -0,0 +1,494 @@
//! Bindings for [`AMediaFormat`] and [`AMediaCodec`]
//!
//! [`AMediaFormat`]: https://developer.android.com/ndk/reference/group/media#amediaformat
//! [`AMediaCodec`]: https://developer.android.com/ndk/reference/group/media#amediacodec
use super::{get_unlikely_to_be_null, NdkMediaError, Result};
use crate::native_window::NativeWindow;
use std::{
convert::TryInto,
ffi::{CStr, CString},
fmt::Display,
ptr::{self, NonNull},
slice,
time::Duration,
};
#[derive(Debug, PartialEq, Eq)]
pub enum MediaCodecDirection {
Decoder,
Encoder,
}
/// A native [`AMediaFormat *`]
///
/// [`AMediaFormat *`]: https://developer.android.com/ndk/reference/group/media#amediaformat
#[derive(Debug)]
pub struct MediaFormat {
inner: NonNull<ffi::AMediaFormat>,
}
impl Display for MediaFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let c_str = unsafe { CStr::from_ptr(ffi::AMediaFormat_toString(self.as_ptr())) };
f.write_str(c_str.to_str().unwrap())
}
}
impl Default for MediaFormat {
fn default() -> Self {
Self::new()
}
}
impl MediaFormat {
fn as_ptr(&self) -> *mut ffi::AMediaFormat {
self.inner.as_ptr()
}
pub fn new() -> Self {
Self {
inner: NonNull::new(unsafe { ffi::AMediaFormat_new() }).unwrap(),
}
}
pub fn i32(&self, key: &str) -> Option<i32> {
let name = CString::new(key).unwrap();
let mut out = 0;
unsafe { ffi::AMediaFormat_getInt32(self.as_ptr(), name.as_ptr(), &mut out) }.then(|| out)
}
pub fn i64(&self, key: &str) -> Option<i64> {
let name = CString::new(key).unwrap();
let mut out = 0;
unsafe { ffi::AMediaFormat_getInt64(self.as_ptr(), name.as_ptr(), &mut out) }.then(|| out)
}
pub fn f32(&self, key: &str) -> Option<f32> {
let name = CString::new(key).unwrap();
let mut out = 0.0;
unsafe { ffi::AMediaFormat_getFloat(self.as_ptr(), name.as_ptr(), &mut out) }.then(|| out)
}
pub fn usize(&self, key: &str) -> Option<usize> {
let name = CString::new(key).unwrap();
let mut out = 0;
unsafe { ffi::AMediaFormat_getSize(self.as_ptr(), name.as_ptr(), &mut out) }
.then(|| out as usize)
}
pub fn buffer(&self, key: &str) -> Option<&[u8]> {
let name = CString::new(key).unwrap();
let mut out_buffer = ptr::null_mut();
let mut out_size = 0;
unsafe {
ffi::AMediaFormat_getBuffer(
self.as_ptr(),
name.as_ptr(),
&mut out_buffer,
&mut out_size,
)
}
.then(|| unsafe { slice::from_raw_parts(out_buffer.cast(), out_size as usize) })
}
pub fn str(&self, key: &str) -> Option<&str> {
let name = CString::new(key).unwrap();
let mut out = ptr::null();
unsafe { ffi::AMediaFormat_getString(self.as_ptr(), name.as_ptr(), &mut out) }
.then(|| unsafe { CStr::from_ptr(out) }.to_str().unwrap())
}
pub fn set_i32(&self, key: &str, value: i32) {
let name = CString::new(key).unwrap();
unsafe { ffi::AMediaFormat_setInt32(self.as_ptr(), name.as_ptr(), value) };
}
pub fn set_i64(&self, key: &str, value: i64) {
let name = CString::new(key).unwrap();
unsafe { ffi::AMediaFormat_setInt64(self.as_ptr(), name.as_ptr(), value) };
}
pub fn set_f32(&self, key: &str, value: f32) {
let name = CString::new(key).unwrap();
unsafe { ffi::AMediaFormat_setFloat(self.as_ptr(), name.as_ptr(), value) };
}
pub fn set_str(&self, key: &str, value: &str) {
let name = CString::new(key).unwrap();
let c_string = CString::new(value).unwrap();
unsafe { ffi::AMediaFormat_setString(self.as_ptr(), name.as_ptr(), c_string.as_ptr()) };
}
pub fn set_buffer(&self, key: &str, value: &[u8]) {
let name = CString::new(key).unwrap();
unsafe {
ffi::AMediaFormat_setBuffer(
self.as_ptr(),
name.as_ptr(),
value.as_ptr().cast(),
value.len() as ffi::size_t,
)
};
}
#[cfg(feature = "api-level-28")]
pub fn f64(&self, key: &str) -> Option<f64> {
let name = CString::new(key).unwrap();
let mut out = 0.0;
unsafe { ffi::AMediaFormat_getDouble(self.as_ptr(), name.as_ptr(), &mut out) }.then(|| out)
}
/// Returns (left, top, right, bottom)
#[cfg(feature = "api-level-28")]
pub fn rect(&self, key: &str) -> Option<(i32, i32, i32, i32)> {
let name = CString::new(key).unwrap();
let mut left = 0;
let mut top = 0;
let mut right = 0;
let mut bottom = 0;
unsafe {
ffi::AMediaFormat_getRect(
self.as_ptr(),
name.as_ptr(),
&mut left,
&mut top,
&mut right,
&mut bottom,
)
}
.then(|| (left, top, right, bottom))
}
#[cfg(feature = "api-level-28")]
pub fn set_f64(&self, key: &str, value: f64) {
let name = CString::new(key).unwrap();
unsafe { ffi::AMediaFormat_setDouble(self.as_ptr(), name.as_ptr(), value) };
}
#[cfg(feature = "api-level-28")]
pub fn set_rect(&self, key: &str, left: i32, top: i32, right: i32, bottom: i32) {
let name = CString::new(key).unwrap();
unsafe {
ffi::AMediaFormat_setRect(self.as_ptr(), name.as_ptr(), left, top, right, bottom)
};
}
#[cfg(feature = "api-level-28")]
pub fn set_usize(&self, key: &str, value: usize) {
let name = CString::new(key).unwrap();
unsafe { ffi::AMediaFormat_setSize(self.as_ptr(), name.as_ptr(), value as ffi::size_t) };
}
}
impl Drop for MediaFormat {
fn drop(&mut self) {
let status = unsafe { ffi::AMediaFormat_delete(self.as_ptr()) };
NdkMediaError::from_status(status).unwrap();
}
}
/// A native [`AMediaCodec *`]
///
/// [`AMediaCodec *`]: https://developer.android.com/ndk/reference/group/media#amediacodec
#[derive(Debug)]
pub struct MediaCodec {
inner: NonNull<ffi::AMediaCodec>,
}
impl MediaCodec {
fn as_ptr(&self) -> *mut ffi::AMediaCodec {
self.inner.as_ptr()
}
pub fn from_codec_name(name: &str) -> Option<Self> {
let c_string = CString::new(name).unwrap();
Some(Self {
inner: NonNull::new(unsafe { ffi::AMediaCodec_createCodecByName(c_string.as_ptr()) })?,
})
}
pub fn from_decoder_type(mime_type: &str) -> Option<Self> {
let c_string = CString::new(mime_type).unwrap();
Some(Self {
inner: NonNull::new(unsafe {
ffi::AMediaCodec_createDecoderByType(c_string.as_ptr())
})?,
})
}
pub fn from_encoder_type(mime_type: &str) -> Option<Self> {
let c_string = CString::new(mime_type).unwrap();
Some(Self {
inner: NonNull::new(unsafe {
ffi::AMediaCodec_createEncoderByType(c_string.as_ptr())
})?,
})
}
pub fn configure(
&self,
format: &MediaFormat,
surface: Option<&NativeWindow>,
direction: MediaCodecDirection,
) -> Result<()> {
let status = unsafe {
ffi::AMediaCodec_configure(
self.as_ptr(),
format.as_ptr(),
surface.map_or(ptr::null_mut(), |s| s.ptr().as_ptr()),
ptr::null_mut(),
if direction == MediaCodecDirection::Encoder {
ffi::AMEDIACODEC_CONFIGURE_FLAG_ENCODE as u32
} else {
0
},
)
};
NdkMediaError::from_status(status)
}
#[cfg(feature = "api-level-26")]
pub fn create_input_surface(&self) -> Result<NativeWindow> {
use super::construct_never_null;
unsafe {
let ptr = construct_never_null(|res| {
ffi::AMediaCodec_createInputSurface(self.as_ptr(), res)
})?;
Ok(NativeWindow::from_ptr(ptr))
}
}
#[cfg(feature = "api-level-26")]
pub fn create_persistent_input_surface() -> Result<NativeWindow> {
use super::construct_never_null;
unsafe {
let ptr =
construct_never_null(|res| ffi::AMediaCodec_createPersistentInputSurface(res))?;
Ok(NativeWindow::from_ptr(ptr))
}
}
/// Returns [`None`] if timeout is reached.
pub fn dequeue_input_buffer(&self, timeout: Duration) -> Result<Option<InputBuffer>> {
let result = unsafe {
ffi::AMediaCodec_dequeueInputBuffer(
self.as_ptr(),
timeout
.as_micros()
.try_into()
.expect("Supplied timeout is too large"),
)
};
if result == ffi::AMEDIACODEC_INFO_TRY_AGAIN_LATER as ffi::ssize_t {
Ok(None)
} else if result >= 0 {
Ok(Some(InputBuffer {
codec: self,
index: result as ffi::size_t,
}))
} else {
NdkMediaError::from_status(ffi::media_status_t(result as _)).map(|()| None)
}
}
/// Returns [`None`] if timeout is reached.
pub fn dequeue_output_buffer(&self, timeout: Duration) -> Result<Option<OutputBuffer>> {
let mut info: ffi::AMediaCodecBufferInfo = unsafe { std::mem::zeroed() };
let result = unsafe {
ffi::AMediaCodec_dequeueOutputBuffer(
self.as_ptr(),
&mut info,
timeout
.as_micros()
.try_into()
.expect("Supplied timeout is too large"),
)
};
if result == ffi::AMEDIACODEC_INFO_TRY_AGAIN_LATER as ffi::ssize_t {
Ok(None)
} else if result >= 0 {
Ok(Some(OutputBuffer {
codec: self,
index: result as ffi::size_t,
info,
}))
} else {
NdkMediaError::from_status(ffi::media_status_t(result as _)).map(|()| None)
}
}
pub fn flush(&self) -> Result<()> {
let status = unsafe { ffi::AMediaCodec_flush(self.as_ptr()) };
NdkMediaError::from_status(status)
}
#[cfg(feature = "api-level-28")]
pub fn input_format(&self) -> MediaFormat {
let inner =
get_unlikely_to_be_null(|| unsafe { ffi::AMediaCodec_getInputFormat(self.as_ptr()) });
MediaFormat { inner }
}
pub fn output_format(&self) -> MediaFormat {
let inner =
get_unlikely_to_be_null(|| unsafe { ffi::AMediaCodec_getOutputFormat(self.as_ptr()) });
MediaFormat { inner }
}
#[cfg(feature = "api-level-28")]
pub fn name(&self) -> Result<String> {
use super::construct;
unsafe {
let name_ptr = construct(|name| ffi::AMediaCodec_getName(self.as_ptr(), name))?;
let name = CStr::from_ptr(name_ptr).to_str().unwrap().to_owned();
ffi::AMediaCodec_releaseName(self.as_ptr(), name_ptr);
Ok(name)
}
}
pub fn queue_input_buffer(
&self,
buffer: InputBuffer,
offset: usize,
size: usize,
time: u64,
flags: u32,
) -> Result<()> {
let status = unsafe {
ffi::AMediaCodec_queueInputBuffer(
self.as_ptr(),
buffer.index as ffi::size_t,
offset as ffi::off_t,
size as ffi::size_t,
time,
flags,
)
};
NdkMediaError::from_status(status)
}
pub fn release_output_buffer(&self, buffer: OutputBuffer, render: bool) -> Result<()> {
let status = unsafe {
ffi::AMediaCodec_releaseOutputBuffer(self.as_ptr(), buffer.index as ffi::size_t, render)
};
NdkMediaError::from_status(status)
}
pub fn release_output_buffer_at_time(
&self,
buffer: OutputBuffer,
timestamp_ns: i64,
) -> Result<()> {
let status = unsafe {
ffi::AMediaCodec_releaseOutputBufferAtTime(
self.as_ptr(),
buffer.index as ffi::size_t,
timestamp_ns,
)
};
NdkMediaError::from_status(status)
}
#[cfg(feature = "api-level-26")]
pub fn set_input_surface(&self, surface: &NativeWindow) -> Result<()> {
let status =
unsafe { ffi::AMediaCodec_setInputSurface(self.as_ptr(), surface.ptr().as_ptr()) };
NdkMediaError::from_status(status)
}
pub fn set_output_surface(&self, surface: &NativeWindow) -> Result<()> {
let status =
unsafe { ffi::AMediaCodec_setOutputSurface(self.as_ptr(), surface.ptr().as_ptr()) };
NdkMediaError::from_status(status)
}
#[cfg(feature = "api-level-26")]
pub fn set_parameters(&self, params: MediaFormat) -> Result<()> {
let status = unsafe { ffi::AMediaCodec_setParameters(self.as_ptr(), params.as_ptr()) };
NdkMediaError::from_status(status)
}
#[cfg(feature = "api-level-26")]
pub fn set_signal_end_of_input_stream(&self) -> Result<()> {
let status = unsafe { ffi::AMediaCodec_signalEndOfInputStream(self.as_ptr()) };
NdkMediaError::from_status(status)
}
pub fn start(&self) -> Result<()> {
let status = unsafe { ffi::AMediaCodec_start(self.as_ptr()) };
NdkMediaError::from_status(status)
}
pub fn stop(&self) -> Result<()> {
let status = unsafe { ffi::AMediaCodec_stop(self.as_ptr()) };
NdkMediaError::from_status(status)
}
}
impl Drop for MediaCodec {
fn drop(&mut self) {
let status = unsafe { ffi::AMediaCodec_delete(self.as_ptr()) };
NdkMediaError::from_status(status).unwrap();
}
}
#[derive(Debug)]
pub struct InputBuffer<'a> {
codec: &'a MediaCodec,
index: ffi::size_t,
}
impl InputBuffer<'_> {
pub fn buffer_mut(&mut self) -> &mut [u8] {
unsafe {
let mut out_size = 0;
let buffer_ptr =
ffi::AMediaCodec_getInputBuffer(self.codec.as_ptr(), self.index, &mut out_size);
assert!(!buffer_ptr.is_null());
slice::from_raw_parts_mut(buffer_ptr, out_size as usize)
}
}
}
#[derive(Debug)]
pub struct OutputBuffer<'a> {
codec: &'a MediaCodec,
index: ffi::size_t,
info: ffi::AMediaCodecBufferInfo,
}
impl OutputBuffer<'_> {
pub fn buffer(&self) -> &[u8] {
unsafe {
let mut _out_size = 0;
let buffer_ptr =
ffi::AMediaCodec_getOutputBuffer(self.codec.as_ptr(), self.index, &mut _out_size);
assert!(!buffer_ptr.is_null());
slice::from_raw_parts(
buffer_ptr.add(self.info.offset as usize),
self.info.size as usize,
)
}
}
#[cfg(feature = "api-level-28")]
pub fn format(&self) -> MediaFormat {
let inner = get_unlikely_to_be_null(|| unsafe {
ffi::AMediaCodec_getBufferFormat(self.codec.as_ptr(), self.index)
});
MediaFormat { inner }
}
pub fn flags(&self) -> u32 {
self.info.flags
}
pub fn presentation_time_us(&self) -> i64 {
self.info.presentationTimeUs
}
}

41
third-party/vendor/ndk/src/media/mod.rs vendored Normal file
View file

@ -0,0 +1,41 @@
//! Bindings for the NDK media classes.
//!
//! See also [the NDK docs](https://developer.android.com/ndk/reference/group/media)
#![cfg(feature = "media")]
mod error;
pub mod image_reader;
pub mod media_codec;
pub use error::NdkMediaError;
use std::{mem::MaybeUninit, ptr::NonNull};
pub type Result<T, E = NdkMediaError> = std::result::Result<T, E>;
fn construct<T>(with_ptr: impl FnOnce(*mut T) -> ffi::media_status_t) -> Result<T> {
let mut result = MaybeUninit::uninit();
let status = with_ptr(result.as_mut_ptr());
NdkMediaError::from_status(status).map(|()| unsafe { result.assume_init() })
}
fn construct_never_null<T>(
with_ptr: impl FnOnce(*mut *mut T) -> ffi::media_status_t,
) -> Result<NonNull<T>> {
let result = construct(with_ptr)?;
let non_null = if cfg!(debug_assertions) {
NonNull::new(result).expect("result should never be null")
} else {
unsafe { NonNull::new_unchecked(result) }
};
Ok(non_null)
}
/// Function is not expected to ever return `null`, but this
/// cannot be validated through the Android documentation.
///
/// As such this function always asserts on `null` values,
/// even when `cfg!(debug_assertions)` is disabled.
fn get_unlikely_to_be_null<T>(get_ptr: impl FnOnce() -> *mut T) -> NonNull<T> {
let result = get_ptr();
NonNull::new(result).expect("result should never be null")
}

View file

@ -0,0 +1,250 @@
//! Bindings for [`ANativeActivity`]
//!
//! [`ANativeActivity`]: https://developer.android.com/ndk/reference/group/native-activity#anativeactivity
use super::hardware_buffer_format::HardwareBufferFormat;
use bitflags::bitflags;
use std::{
ffi::{CStr, OsStr},
os::{raw::c_void, unix::prelude::OsStrExt},
path::Path,
ptr::NonNull,
};
bitflags! {
/// Window flags, as per the Java API at [`android.view.WindowManager.LayoutParams`].
///
/// <https://developer.android.com/ndk/reference/group/native-activity#group___native_activity_1ga2f1398dba5e4a5616b83437528bdb28e>
///
/// [`android.view.WindowManager.LayoutParams`]: https://developer.android.com/reference/android/view/WindowManager.LayoutParams
pub struct WindowFlags: u32 {
const ALLOW_LOCK_WHILE_SCREEN_ON = ffi::AWINDOW_FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
const DIM_BEHIND = ffi::AWINDOW_FLAG_DIM_BEHIND;
#[deprecated = "Deprecated. Blurring is no longer supported."]
const BLUR_BEHIND = ffi::AWINDOW_FLAG_BLUR_BEHIND;
const NOT_FOCUSABLE = ffi::AWINDOW_FLAG_NOT_FOCUSABLE;
const NOT_TOUCHABLE = ffi::AWINDOW_FLAG_NOT_TOUCHABLE;
const NOT_TOUCH_MODAL = ffi::AWINDOW_FLAG_NOT_TOUCH_MODAL;
#[deprecated = "This constant was deprecated in API level 20. This flag has no effect."]
const TOUCHABLE_WHEN_WAKING = ffi::AWINDOW_FLAG_TOUCHABLE_WHEN_WAKING;
const KEEP_SCREEN_ON = ffi::AWINDOW_FLAG_KEEP_SCREEN_ON;
const LAYOUT_IN_SCREEN = ffi::AWINDOW_FLAG_LAYOUT_IN_SCREEN;
const LAYOUT_NO_LIMITS = ffi::AWINDOW_FLAG_LAYOUT_NO_LIMITS;
const FULLSCREEN = ffi::AWINDOW_FLAG_FULLSCREEN;
#[cfg_attr(feature = "api-level-30", deprecated = "This constant was deprecated in API level 30. This value became API \"by accident\", and shouldn't be used by 3rd party applications.")]
const FORCE_NOT_FULLSCREEN = ffi::AWINDOW_FLAG_FORCE_NOT_FULLSCREEN;
#[deprecated = "This constant was deprecated in API level 17. This flag is no longer used."]
const DITHER = ffi::AWINDOW_FLAG_DITHER;
const SECURE = ffi::AWINDOW_FLAG_SECURE;
const SCALED = ffi::AWINDOW_FLAG_SCALED;
const IGNORE_CHEEK_PRESSES = ffi::AWINDOW_FLAG_IGNORE_CHEEK_PRESSES;
const LAYOUT_INSET_DECOR = ffi::AWINDOW_FLAG_LAYOUT_INSET_DECOR;
const ALT_FOCUSABLE_IM = ffi::AWINDOW_FLAG_ALT_FOCUSABLE_IM;
const WATCH_OUTSIDE_TOUCH = ffi::AWINDOW_FLAG_WATCH_OUTSIDE_TOUCH;
const SHOW_WHEN_LOCKED = ffi::AWINDOW_FLAG_SHOW_WHEN_LOCKED;
const SHOW_WALLPAPER = ffi::AWINDOW_FLAG_SHOW_WALLPAPER;
const TURN_SCREEN_ON = ffi::AWINDOW_FLAG_TURN_SCREEN_ON;
#[cfg_attr(feature = "api-level-26", deprecated = "This constant was deprecated in API level 26. Use `SHOW_WHEN_LOCKED` instead.")]
const DISMISS_KEYGUARD = ffi::AWINDOW_FLAG_DISMISS_KEYGUARD;
const ATTACHED_IN_DECOR = 0x40000000;
}
}
/// A native [`ANativeActivity *`]
///
/// This is either provided in [`ffi::ANativeActivity_onCreate()`], or accessible through
/// `ndk_glue::native_activity()`.
///
/// [`ANativeActivity *`]: https://developer.android.com/ndk/reference/struct/a-native-activity
#[derive(Debug)]
pub struct NativeActivity {
ptr: NonNull<ffi::ANativeActivity>,
}
// It gets shared between threads in `ndk-glue`
unsafe impl Send for NativeActivity {}
unsafe impl Sync for NativeActivity {}
impl NativeActivity {
/// Create a `NativeActivity` from a pointer
///
/// # Safety
/// By calling this function, you assert that it is a valid pointer to a native
/// `ANativeActivity`.
pub unsafe fn from_ptr(ptr: NonNull<ffi::ANativeActivity>) -> Self {
Self { ptr }
}
/// The pointer to the native `ANativeActivity`
pub fn ptr(&self) -> NonNull<ffi::ANativeActivity> {
self.ptr
}
}
/// Methods that relate to fields of the struct itself
///
/// The relevant NDK docs can be found
/// [here](https://developer.android.com/ndk/reference/struct/a-native-activity).
impl NativeActivity {
/// The platform's SDK version code
pub fn sdk_version(&self) -> i32 {
unsafe { self.ptr.as_ref().sdkVersion }
}
/// Path to this application's internal data directory
pub fn internal_data_path(&self) -> &Path {
OsStr::from_bytes(unsafe { CStr::from_ptr(self.ptr.as_ref().internalDataPath) }.to_bytes())
.as_ref()
}
/// Path to this application's external (removable, mountable) data directory
pub fn external_data_path(&self) -> &Path {
OsStr::from_bytes(unsafe { CStr::from_ptr(self.ptr.as_ref().externalDataPath) }.to_bytes())
.as_ref()
}
/// This app's asset manager, which can be used to access assets from the `.apk` file.
pub fn asset_manager(&self) -> crate::asset::AssetManager {
unsafe {
crate::asset::AssetManager::from_ptr(
NonNull::new(self.ptr.as_ref().assetManager).unwrap(),
)
}
}
/// Instance data associated with the activity
pub fn instance(&self) -> *mut c_void {
unsafe { self.ptr.as_ref().instance }
}
/// Set the instance data associated with the activity
///
/// # Safety
/// This can invalidate assumptions held by `ndk-glue`, as well as cause data
/// races with concurrent access to the instance data.
pub unsafe fn set_instance(&mut self, data: *mut c_void) {
// FIXME Does this create undefined behavior by creating a mutable reference to what could
// also be accessed immutably at the same time?
//
// I think that as long as we warn the users to avoid concurrent access, and we pass along
// the `unsafe` burden, it's OK.
self.ptr.as_mut().instance = data;
}
/// This process's `JavaVM` object.
///
/// Usage with [__jni__](https://crates.io/crates/jni) crate:
/// ```no_run
/// # use ndk::native_activity::NativeActivity;
/// # let native_activity: NativeActivity = unimplemented!();
/// let vm_ptr = native_activity.vm();
/// let vm = unsafe { jni::JavaVM::from_raw(vm_ptr) }.unwrap();
/// let env = vm.attach_current_thread();
/// // Do JNI with env ...
/// ```
///
/// Usage with [__jni-glue__](https://crates.io/crates/jni-glue) crate:
/// ```no_run
/// # use ndk::native_activity::NativeActivity;
/// # let native_activity: NativeActivity = unimplemented!();
/// let vm_ptr = native_activity.vm();
/// let vm = unsafe { jni_glue::VM::from_jni_local(&*vm_ptr) };
/// vm.with_env(|env| {
/// // Do JNI with env ...
/// });
/// ```
pub fn vm(&self) -> *mut jni_sys::JavaVM {
unsafe { self.ptr.as_ref() }.vm
}
/// The [`android.app.NativeActivity`] instance
///
/// In the JNI, this is named `clazz`; however, as the docs say, "it should really be named
/// 'activity' instead of 'clazz', since it's a reference to the NativeActivity instance".
///
/// [`android.app.NativeActivity`]: https://developer.android.com/reference/android/app/NativeActivity
pub fn activity(&self) -> jni_sys::jobject {
unsafe { self.ptr.as_ref() }.clazz
}
/// Path to the directory with the application's OBB files.
///
/// # Safety
/// Only available as of Honeycomb (Android 3.0+, API level 11+)
pub unsafe fn obb_path(&self) -> &Path {
OsStr::from_bytes(CStr::from_ptr(self.ptr.as_ref().obbPath).to_bytes()).as_ref()
}
}
/// Methods that relate to `ANativeActivity_*` functions.
///
/// The relevant NDK docs can be found
/// [here](https://developer.android.com/ndk/reference/group/native-activity).
impl NativeActivity {
/// Sends a destroy event to the activity and stops it.
pub fn finish(&self) {
unsafe { ffi::ANativeActivity_finish(self.ptr.as_ptr()) }
}
/// Shows the IME (the on-screen keyboard).
///
/// If `force` is true, the `SHOW_FORCED` flag is used; otherwise, the `SHOW_IMPLICIT` flag is
/// used. Depending on the value of this flag, the `hide_soft_input` method with behave
/// differently. See [the relevant
/// javadoc](https://developer.android.com/reference/android/view/inputmethod/InputMethodManager#constants_2)
/// for more information.
pub fn show_soft_input(&self, force: bool) {
let flag = if force {
ffi::ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED
} else {
ffi::ANATIVEACTIVITY_SHOW_SOFT_INPUT_IMPLICIT
};
unsafe { ffi::ANativeActivity_showSoftInput(self.ptr.as_ptr(), flag) }
}
/// Hides the IME (the on-screen keyboard).
///
/// If `not_always` is true, the `HIDE_NOT_ALWAYS` flag is used; otherwise, the
/// `HIDE_IMPLICIT_ONLY` flag is used. Depending on the value of this flag and the way the IME
/// was shown, it may or may not be hidden. See [the relevant
/// javadoc](https://developer.android.com/reference/android/view/inputmethod/InputMethodManager#constants_2)
/// for more information.
pub fn hide_soft_input(&self, not_always: bool) {
let flag = if not_always {
ffi::ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS
} else {
ffi::ANATIVEACTIVITY_HIDE_SOFT_INPUT_IMPLICIT_ONLY
};
unsafe { ffi::ANativeActivity_hideSoftInput(self.ptr.as_ptr(), flag) }
}
/// Change the window format of the given activity.
///
/// Calls [`getWindow().setFormat()`] of the given activity. Note that this method can be
/// called from any thread; it will send a message to the main thread of the process where the
/// Java finish call will take place.
///
/// [`getWindow().setFormat()`]: https://developer.android.com/reference/android/view/Window#setFormat(int)
pub fn set_window_format(&self, format: HardwareBufferFormat) {
let format: u32 = format.into();
unsafe { ffi::ANativeActivity_setWindowFormat(self.ptr.as_ptr(), format as i32) }
}
/// Change the window flags of the given activity.
///
/// Calls [`getWindow().setFlags()`] of the given activity.
///
/// Note that this method can be called from any thread; it will send a message to the main
/// thread of the process where the Java finish call will take place.
///
/// [`getWindow().setFlags()`]: https://developer.android.com/reference/android/view/Window#setFlags(int,%20int)
pub fn set_window_flags(&self, add_flags: WindowFlags, remove_flags: WindowFlags) {
unsafe {
ffi::ANativeActivity_setWindowFlags(
self.ptr.as_ptr(),
add_flags.bits(),
remove_flags.bits(),
)
}
}
}

View file

@ -0,0 +1,129 @@
//! Bindings for [`ANativeWindow`]
//!
//! [`ANativeWindow`]: https://developer.android.com/ndk/reference/group/a-native-window#anativewindow
use crate::utils::status_to_io_result;
pub use super::hardware_buffer_format::HardwareBufferFormat;
use jni_sys::{jobject, JNIEnv};
use raw_window_handle::{AndroidNdkWindowHandle, HasRawWindowHandle, RawWindowHandle};
use std::{convert::TryFrom, ffi::c_void, io::Result, ptr::NonNull};
// [`NativeWindow`] represents the producer end of an image queue
///
/// It is the C counterpart of the [`android.view.Surface`] object in Java, and can be converted
/// both ways. Depending on the consumer, images submitted to [`NativeWindow`] can be shown on the
/// display or sent to other consumers, such as video encoders.
///
/// [`android.view.Surface`]: https://developer.android.com/reference/android/view/Surface
#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct NativeWindow {
ptr: NonNull<ffi::ANativeWindow>,
}
unsafe impl Send for NativeWindow {}
unsafe impl Sync for NativeWindow {}
impl Drop for NativeWindow {
fn drop(&mut self) {
unsafe { ffi::ANativeWindow_release(self.ptr.as_ptr()) }
}
}
impl Clone for NativeWindow {
fn clone(&self) -> Self {
unsafe { ffi::ANativeWindow_acquire(self.ptr.as_ptr()) }
Self { ptr: self.ptr }
}
}
unsafe impl HasRawWindowHandle for NativeWindow {
fn raw_window_handle(&self) -> RawWindowHandle {
let mut handle = AndroidNdkWindowHandle::empty();
handle.a_native_window = self.ptr.as_ptr() as *mut c_void;
RawWindowHandle::AndroidNdk(handle)
}
}
impl NativeWindow {
/// Assumes ownership of `ptr`
///
/// # Safety
/// `ptr` must be a valid pointer to an Android [`ffi::ANativeWindow`].
pub unsafe fn from_ptr(ptr: NonNull<ffi::ANativeWindow>) -> Self {
Self { ptr }
}
/// Acquires ownership of `ptr`
///
/// # Safety
/// `ptr` must be a valid pointer to an Android [`ffi::ANativeWindow`].
pub unsafe fn clone_from_ptr(ptr: NonNull<ffi::ANativeWindow>) -> Self {
ffi::ANativeWindow_acquire(ptr.as_ptr());
Self::from_ptr(ptr)
}
pub fn ptr(&self) -> NonNull<ffi::ANativeWindow> {
self.ptr
}
pub fn height(&self) -> i32 {
unsafe { ffi::ANativeWindow_getHeight(self.ptr.as_ptr()) }
}
pub fn width(&self) -> i32 {
unsafe { ffi::ANativeWindow_getWidth(self.ptr.as_ptr()) }
}
/// Return the current pixel format ([`HardwareBufferFormat`]) of the window surface.
pub fn format(&self) -> HardwareBufferFormat {
let value = unsafe { ffi::ANativeWindow_getFormat(self.ptr.as_ptr()) };
let value = u32::try_from(value).unwrap();
HardwareBufferFormat::try_from(value).unwrap()
}
/// Change the format and size of the window buffers.
///
/// The width and height control the number of pixels in the buffers, not the dimensions of the
/// window on screen. If these are different than the window's physical size, then its buffer
/// will be scaled to match that size when compositing it to the screen. The width and height
/// must be either both zero or both non-zero.
///
/// For all of these parameters, if `0` or [`None`] is supplied then the window's base value
/// will come back in force.
pub fn set_buffers_geometry(
&self,
width: i32,
height: i32,
format: Option<HardwareBufferFormat>,
) -> Result<()> {
let format: u32 = format.map_or(0, |f| f.into());
let status = unsafe {
ffi::ANativeWindow_setBuffersGeometry(self.ptr.as_ptr(), width, height, format as i32)
};
status_to_io_result(status, ())
}
/// Return the [`NativeWindow`] associated with a JNI [`android.view.Surface`] pointer.
///
/// # Safety
/// By calling this function, you assert that `env` is a valid pointer to a [`JNIEnv`] and
/// `surface` is a valid pointer to an [`android.view.Surface`].
///
/// [`android.view.Surface`]: https://developer.android.com/reference/android/view/Surface
pub unsafe fn from_surface(env: *mut JNIEnv, surface: jobject) -> Option<Self> {
let ptr = ffi::ANativeWindow_fromSurface(env, surface);
Some(Self::from_ptr(NonNull::new(ptr)?))
}
/// Return a JNI [`android.view.Surface`] pointer derived from this [`NativeWindow`].
///
/// # Safety
/// By calling this function, you assert that `env` is a valid pointer to a [`JNIEnv`].
///
/// [`android.view.Surface`]: https://developer.android.com/reference/android/view/Surface
#[cfg(feature = "api-level-26")]
pub unsafe fn to_surface(&self, env: *mut JNIEnv) -> jobject {
ffi::ANativeWindow_toSurface(env, self.ptr().as_ptr())
}
}

View file

@ -0,0 +1,159 @@
//! Bindings for [`ASurfaceTexture`]
//!
//! See <https://source.android.com/devices/graphics/arch-st> for an architectural overview of
//! [`SurfaceTexture`] internals.
//!
//! [`ASurfaceTexture`]: https://developer.android.com/ndk/reference/group/surface-texture
#![cfg(feature = "api-level-28")]
use crate::{native_window::NativeWindow, utils::status_to_io_result};
use jni_sys::{jobject, JNIEnv};
use std::{convert::TryInto, io::Result, ptr::NonNull, time::Duration};
/// An opaque type to manage [`android.graphics.SurfaceTexture`] from native code
///
/// [`android.graphics.SurfaceTexture`]: https://developer.android.com/reference/android/graphics/SurfaceTexture
#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct SurfaceTexture {
ptr: NonNull<ffi::ASurfaceTexture>,
}
unsafe impl Send for SurfaceTexture {}
impl Drop for SurfaceTexture {
fn drop(&mut self) {
unsafe { ffi::ASurfaceTexture_release(self.ptr.as_ptr()) }
}
}
impl SurfaceTexture {
/// Assumes ownership of `ptr`
///
/// # Safety
/// `ptr` must be a valid pointer to an Android [`ffi::ASurfaceTexture`].
pub unsafe fn from_ptr(ptr: NonNull<ffi::ASurfaceTexture>) -> Self {
Self { ptr }
}
/// Get a reference to the native [`SurfaceTexture`] from the corresponding Java object.
///
/// # Safety
///
/// This function should be called with a healthy JVM pointer and with a non-null
/// [`android.graphics.SurfaceTexture`], which must be kept alive on the Java/Kotlin side.
///
/// The caller must keep a reference to the Java [`android.graphics.SurfaceTexture`] during the
/// lifetime of the returned [`SurfaceTexture`]. Failing to do so could result in the
/// [`SurfaceTexture`] to stop functioning properly once the Java object gets finalized.
/// However, this will not result in program termination.
///
/// [`android.graphics.SurfaceTexture`]: https://developer.android.com/reference/android/graphics/SurfaceTexture
pub unsafe fn from_surface_texture(env: *mut JNIEnv, surface_texture: jobject) -> Option<Self> {
let a_surface_texture_ptr = ffi::ASurfaceTexture_fromSurfaceTexture(env, surface_texture);
let s = NonNull::new(a_surface_texture_ptr)?;
Some(SurfaceTexture::from_ptr(s))
}
/// Returns a pointer to the native [`ffi::ASurfaceTexture`].
pub fn ptr(&self) -> NonNull<ffi::ASurfaceTexture> {
self.ptr
}
/// Returns a reference to a [`NativeWindow`] (i.e. the Producer) for this [`SurfaceTexture`].
///
/// This is equivalent to Java's:
/// ```java
/// Surface sur = new Surface(surfaceTexture);
/// ```
pub fn acquire_native_window(&self) -> Option<NativeWindow> {
let native_window = unsafe { ffi::ASurfaceTexture_acquireANativeWindow(self.ptr.as_ptr()) };
let n = NonNull::new(native_window)?;
Some(unsafe { NativeWindow::from_ptr(n) })
}
/// Attach the [`SurfaceTexture`] to the OpenGL ES context that is current on the calling
/// thread.
///
/// A new OpenGL ES texture object is created and populated with the [`SurfaceTexture`] image
/// frame that was current at the time of the last call to
/// [`detach_from_gl_context()`][Self::detach_from_gl_context()]. This new texture is bound to
/// the `GL_TEXTURE_EXTERNAL_OES` texture target.
///
/// This can be used to access the [`SurfaceTexture`] image contents from multiple OpenGL ES
/// contexts. Note, however, that the image contents are only accessible from one OpenGL ES
/// context at a time.
pub fn attach_to_gl_context(&self, tex_name: u32) -> Result<()> {
let status = unsafe { ffi::ASurfaceTexture_attachToGLContext(self.ptr.as_ptr(), tex_name) };
status_to_io_result(status, ())
}
/// Detach the [`SurfaceTexture`] from the OpenGL ES context that owns the OpenGL ES texture
/// object.
///
/// This call must be made with the OpenGL ES context current on the calling thread. The OpenGL
/// ES texture object will be deleted as a result of this call. After calling this method all
/// calls to [`update_tex_image()`][Self::update_tex_image()] will fail until a successful call
/// to [`attach_to_gl_context()`][Self::attach_to_gl_context()] is made.
///
/// This can be used to access the [`SurfaceTexture`] image contents from multiple OpenGL ES
/// contexts. Note, however, that the image contents are only accessible from one OpenGL ES
/// context at a time.
pub fn detach_from_gl_context(&self) -> Result<()> {
let status = unsafe { ffi::ASurfaceTexture_detachFromGLContext(self.ptr.as_ptr()) };
status_to_io_result(status, ())
}
/// Retrieve the 4x4 texture coordinate transform matrix associated with the texture image set
/// by the most recent call to [`update_tex_image()`][Self::update_tex_image()].
///
/// This transform matrix maps 2D homogeneous texture coordinates of the form `(s, t, 0, 1)`
/// with `s` and `t` in the inclusive range `[0, 1]` to the texture coordinate that should be
/// used to sample that location from the texture. Sampling the texture outside of the range of
/// this transform is undefined.
///
/// The matrix is stored in column-major order so that it may be passed directly to OpenGL ES
/// via the [`glLoadMatrixf()`] or [`glUniformMatrix4fv()`] functions.
///
/// [`glLoadMatrixf()`]: https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glLoadMatrix.xml
/// [`gluniformmatrix4fv()`]: https://www.khronos.org/registry/OpenGL-Refpages/es3.1/html/glUniform.xhtml
pub fn transform_matrix(&self) -> [f32; 16] {
let mut r = [0f32; 16];
unsafe { ffi::ASurfaceTexture_getTransformMatrix(self.ptr.as_ptr(), r.as_mut_ptr()) };
r
}
/// Retrieve the timestamp associated with the texture image set by the most recent call to
/// [`update_tex_image()`][Self::update_tex_image()].
///
/// This timestamp is in nanoseconds, and is normally monotonically increasing. The timestamp
/// should be unaffected by time-of-day adjustments, and for a camera should be strictly
/// monotonic but for a [`MediaPlayer`] may be reset when the position is set. The specific
/// meaning and zero point of the timestamp depends on the source providing images to the
/// [`SurfaceTexture`]. Unless otherwise specified by the image source, timestamps cannot
/// generally be compared across [`SurfaceTexture`] instances, or across multiple program
/// invocations. It is mostly useful for determining time offsets between subsequent frames.
///
/// For EGL/Vulkan producers, this timestamp is the desired present time set with the
/// [`EGL_ANDROID_presentation_time`] or [`VK_GOOGLE_display_timing`] extensions.
///
/// [`MediaPlayer`]: https://developer.android.com/reference/android/media/MediaPlayer
/// [`EGL_ANDROID_presentation_time`]: https://www.khronos.org/registry/EGL/extensions/ANDROID/EGL_ANDROID_presentation_time.txt
/// [`VK_GOOGLE_display_timing`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VK_GOOGLE_display_timing.html
pub fn timestamp(&self) -> Duration {
Duration::from_nanos(
unsafe { ffi::ASurfaceTexture_getTimestamp(self.ptr.as_ptr()) }
.try_into()
.unwrap(),
)
}
/// Update the texture image to the most recent frame from the image stream.
///
/// This may only be called while the OpenGL ES context that owns the texture is current on the
/// calling thread. It will implicitly bind its texture to the `GL_TEXTURE_EXTERNAL_OES`
/// texture target.
pub fn update_tex_image(&self) -> Result<()> {
let status = unsafe { ffi::ASurfaceTexture_updateTexImage(self.ptr.as_ptr()) };
status_to_io_result(status, ())
}
}

92
third-party/vendor/ndk/src/trace.rs vendored Normal file
View file

@ -0,0 +1,92 @@
//! Bindings for the NDK tracing API.
//!
//! See also [the NDK docs](https://developer.android.com/ndk/reference/group/tracing)
#![cfg(feature = "api-level-23")]
use std::ffi::{CString, NulError};
use std::marker::PhantomData;
pub fn is_trace_enabled() -> bool {
unsafe { ffi::ATrace_isEnabled() }
}
#[derive(Debug)]
pub struct Section {
// Section is !Sync and !Send
_pd: PhantomData<*mut ()>,
}
impl Section {
pub fn new(name: &str) -> Result<Self, NulError> {
let section_name = CString::new(name)?;
unsafe { ffi::ATrace_beginSection(section_name.as_ptr()) };
Ok(Self { _pd: PhantomData })
}
pub fn end(self) {
std::mem::drop(self)
}
}
impl Drop for Section {
fn drop(&mut self) {
unsafe { ffi::ATrace_endSection() };
}
}
/// Unique identifier for distinguishing simultaneous events
#[derive(Debug)]
#[cfg(feature = "api-level-29")]
pub struct Cookie(pub i32);
#[derive(Debug)]
#[cfg(feature = "api-level-29")]
pub struct AsyncSection {
section_name: CString,
cookie: Cookie,
// AsyncSection is !Sync
_pd: PhantomData<&'static ()>,
}
#[cfg(feature = "api-level-29")]
impl AsyncSection {
pub fn new(name: &str, cookie: Cookie) -> Result<Self, NulError> {
let section_name = CString::new(name)?;
unsafe { ffi::ATrace_beginAsyncSection(section_name.as_ptr(), cookie.0) };
Ok(Self {
section_name,
cookie,
_pd: PhantomData,
})
}
pub fn end(self) {
std::mem::drop(self)
}
}
#[cfg(feature = "api-level-29")]
impl Drop for AsyncSection {
fn drop(&mut self) {
unsafe { ffi::ATrace_endAsyncSection(self.section_name.as_ptr(), self.cookie.0) };
}
}
#[cfg(feature = "api-level-29")]
#[derive(Debug)]
pub struct Counter {
name: CString,
}
#[cfg(feature = "api-level-29")]
impl Counter {
pub fn new(name: &str) -> Result<Self, NulError> {
let name = CString::new(name)?;
Ok(Self { name })
}
pub fn set_value(&self, value: i64) {
unsafe { ffi::ATrace_setCounter(self.name.as_ptr(), value) }
}
}

14
third-party/vendor/ndk/src/utils.rs vendored Normal file
View file

@ -0,0 +1,14 @@
//! Internal utilities
use std::io::{Error, Result};
/// Turns standard `<errno.h>` status codes - typically rewrapped by Android's [`Errors.h`] - into
/// Rust's [`std::io::Error`].
///
/// [`Errors.h`]: https://cs.android.com/android/platform/superproject/+/master:system/core/libutils/include/utils/Errors.h
pub(crate) fn status_to_io_result<T>(status: i32, value: T) -> Result<T> {
match status {
0 => Ok(value),
r if r < 0 => Err(Error::from_raw_os_error(-r)),
r => unreachable!("Status is positive integer {}", r),
}
}