diff --git a/.gitignore b/.gitignore index 6aac4b29..27a2965f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ **/target **/*.rs.bk **/wget-log +gstreamer/ diff --git a/README.md b/README.md index 7f713140..067fe14f 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ what the NDK itself calls a --install-dir=android-18-arm-toolchain --arch=arm ``` -The final thing for us to do is tell Cargo where to find the Android linker, +After that you need to tell Cargo where to find the Android linker and ar, which is in the standalone NDK toolchain we just created. To do that we configure the `arm-linux-androideabi` target in `.cargo/config` (or in `~/.cargo/config` if you want to apply the setting globaly) with the `linker` @@ -48,6 +48,7 @@ value. ```toml [target.arm-linux-androideabi] linker = "/android-18-toolchain/bin/arm-linux-androideabi-gcc" +ar = "/android-18-toolchain/bin/arm-linux-androideabi-ar" ``` This crate indirectly depends on @@ -58,6 +59,26 @@ GStreamer on Android. If you want to generate your own `libgstreamer_android.so` bundle, check the documentation from that repo and tweak the [build script](https://github.com/servo/media/blob/master/backends/gstreamer/build.rs#L48) accordingly. +The final step requires fetching or generating this dependency and setting the pkg-config to use +`libgstreamer_android.so`. To do that, there's a [helper script](etc/android_bootstrap.sh) +that will fetch the latest version of this dependency generated for +Servo. To run the script do: + +``` +cd etc +./android_bootstrap.sh +``` + +where `target` can be `armeabi-v7` or `x86`. + +After running the script, you will need to add the path to the `pkg-config` +info for all GStreamer dependencies to your `PKG_CONFIG_PATH` environment variable +The script will output the path and a command suggestion. For example: + +``` +export PKG_CONFIG_PATH=/Users/ferjm/dev/mozilla/media/etc/../gstreamer/armeabi-v7a/gst-build-armeabi-v7a/pkgconfig +``` + ## Build For macOS, Windows, and Linux, simply run: ```bash @@ -74,11 +95,5 @@ Make sure that you have [adb](https://developer.android.com/studio/command-line/ installed and you have adb access to your Android device. Go to the `examples/android` folder and run: ```ssh -./setup.sh -``` -This will download and add the required GStreamer code generated by -`libgstreamer_android_gen` required to initialize GStreamer on Android. This -step is only required once. After that you can run: -```ssh source run.sh ``` diff --git a/backends/gstreamer/build.rs b/backends/gstreamer/build.rs deleted file mode 100644 index 10442dcc..00000000 --- a/backends/gstreamer/build.rs +++ /dev/null @@ -1,149 +0,0 @@ -extern crate regex; -extern crate zip; - -use regex::Regex; -use std::env; -use std::fs; -use std::io; -use std::path::{Path, PathBuf}; -use std::process::Command; - -fn main() { - let target = env::var("TARGET").unwrap(); - if target.contains("android") { - android_main(&target); - } -} - -fn android_main(target: &str) { - // Download zip file containing prebuilt libgstreamer_android.so - let lib_file_name = if Regex::new("arm-([a-z])*-androideabi") - .unwrap() - .is_match(target) - { - "gst-build-armeabi" - } else if Regex::new("armv7-([a-z])*-androideabi") - .unwrap() - .is_match(target) - { - "gst-build-armeabi-v7a" - } else if Regex::new("x86_64-([a-z])*-android") - .unwrap() - .is_match(target) - { - "gst-build-x86_64" - } else if Regex::new("i686-([a-z])*-android") - .unwrap() - .is_match(target) - { - "gst-build-x86" - } else { - panic!("Invalid target architecture {}", target); - }; - - let mut lib_dir = env::current_dir().unwrap(); - lib_dir.push("target"); - lib_dir.push(lib_file_name); - let path = Path::new(&lib_dir); - - if path.exists() { - return; - } - - let url = format!( - "https://github.com/servo/libgstreamer_android_gen/blob/master/out/{}.zip?raw=true", - lib_file_name - ); - let status = Command::new("wget") - .args(&[&url, "-O", "lib.zip"]) - .status() - .unwrap(); - if !status.success() { - panic!( - "Could not download required libgstreamer_android.so {}", - status - ); - } - - // Unpack downloaded lib zip - let fname = std::path::Path::new("lib.zip"); - let file = fs::File::open(&fname).unwrap(); - let mut archive = zip::ZipArchive::new(file).unwrap(); - for i in 0..archive.len() { - let mut file = archive.by_index(i).unwrap(); - let mut outpath = PathBuf::from("target"); - outpath.push(sanitize_filename(file.name())); - if (&*file.name()).ends_with('/') { - println!( - "File {} extracted to \"{}\"", - i, - outpath.as_path().display() - ); - fs::create_dir_all(&outpath).unwrap(); - } else { - println!( - "File {} extracted to \"{}\" ({} bytes)", - i, - outpath.as_path().display(), - file.size() - ); - if let Some(p) = outpath.parent() { - if !p.exists() { - fs::create_dir_all(&p).unwrap(); - } - } - let mut outfile = fs::File::create(&outpath).unwrap(); - io::copy(&mut file, &mut outfile).unwrap(); - - if outpath.extension().unwrap() != "pc" { - continue; - } - - // If this is a .pc file, change pkgconfig info to make all GStreamer - // dependencies point to libgstreamer_android.so - let expr = format!("s#libdir=.*#libdir={}#g", &lib_dir.to_str().unwrap()); - let pkg_config_dir_str = outpath.to_str().unwrap(); - let status = Command::new("perl") - .arg("-i") - .arg("-pe") - .arg(&expr) - .arg(&pkg_config_dir_str) - .status() - .unwrap(); - if !status.success() { - panic!("Could not modify pkgconfig data {}", status); - } - } - - // Get and Set permissions - #[cfg(unix)] - { - use std::os::unix::fs::PermissionsExt; - - if let Some(mode) = file.unix_mode() { - fs::set_permissions(&outpath, fs::Permissions::from_mode(mode)).unwrap(); - } - } - } - - // Remove downloaded zip. - fs::remove_file("lib.zip").unwrap(); -} - -fn sanitize_filename(filename: &str) -> std::path::PathBuf { - let no_null_filename = match filename.find('\0') { - Some(index) => &filename[0..index], - None => filename, - }; - - std::path::Path::new(no_null_filename) - .components() - .filter(|component| match *component { - std::path::Component::Normal(..) => true, - _ => false, - }) - .fold(std::path::PathBuf::new(), |mut path, ref cur| { - path.push(cur.as_os_str()); - path - }) -} diff --git a/etc/android_bootstrap.sh b/etc/android_bootstrap.sh new file mode 100755 index 00000000..0291fae3 --- /dev/null +++ b/etc/android_bootstrap.sh @@ -0,0 +1,34 @@ +set -e + +if [ "$#" -ne 1 ]; then + echo "Usage: ./android_bootstrap (supported targets: armeabi-v7a x86)" >&2 + exit 1 +fi + +TARGET=$1 + +if [ $TARGET != "armeabi-v7a" ] && [ $TARGET != "x86" ]; then + echo "Unsupported target (supported targets: armeabi-v7a x86)" >&2 + exit 1 +fi + +GST_DIR="${PWD}/../gstreamer" +if [ ! -d $GST_DIR ]; then + mkdir $GST_DIR +fi + +GST_DIR_TARGET="${GST_DIR}/${TARGET}" +if [ ! -d $GST_DIR_TARGET ]; then + GST_ZIP="../gstreamer-${TARGET}.zip" + # Download the bundle containing all dependencies for Android. + wget http://servo-deps.s3.amazonaws.com/gstreamer/gstreamer-$TARGET-1.14.3-20181004-142930.zip -O $GST_ZIP + unzip $GST_ZIP -d $GST_DIR_TARGET + rm $GST_ZIP +fi + +GST_LIB_DIR="${GST_DIR}/${TARGET}/gst-build-${TARGET}" +# Fix pkg-config info to point to the location of the libgstreamer_android.so lib +perl -i -pe "s#libdir=.*#libdir=${GST_LIB_DIR}#g" $GST_LIB_DIR/pkgconfig/* + +echo "\n\nYou need to add ${GST_LIB_DIR}/pkgconfig to your PKG_CONFIG_PATH.\n\n" \ +"i.e. export PKG_CONFIG_PATH=${GST_LIB_DIR}/pkgconfig" diff --git a/examples/Cargo.toml b/examples/Cargo.toml index a1e8b213..73a57744 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -7,14 +7,16 @@ license = "MPL-2.0" env_logger = "0.5" euclid = "0.19.0" gleam = "0.6.0" -glutin = "0.17.0" rand = "0.5.0" time = "0.1.40" servo-media = { path = "../servo-media" } webrender = { git = "https://github.com/servo/webrender/" } -winit = "0.16.2" ipc-channel = "0.11" +[target.'cfg(not(target_os = "android"))'.dependencies] +winit = "0.16.2" +glutin = "0.17.0" + [[bin]] name = "audio_decoder" path = "audio_decoder.rs" diff --git a/examples/android/.gitignore b/examples/android/.gitignore index 161e5324..e69de29b 100644 --- a/examples/android/.gitignore +++ b/examples/android/.gitignore @@ -1,2 +0,0 @@ -src/app/src/main/assets/ -src/app/src/main/java/org/freedesktop diff --git a/examples/android/lib/Cargo.toml b/examples/android/lib/Cargo.toml index 034ac019..b4e5ec19 100644 --- a/examples/android/lib/Cargo.toml +++ b/examples/android/lib/Cargo.toml @@ -7,8 +7,6 @@ authors = ["Fernando Jiménez Moreno "] [lib] crate-type = ["cdylib"] -[dependencies] -servo-media = { path = "../../../servo-media" } - [target.'cfg(target_os="android")'.dependencies] jni = { version = "0.5", default-features = false } +servo-media = { path = "../../../servo-media" } diff --git a/examples/android/lib/src/lib.rs b/examples/android/lib/src/lib.rs index 4eee2111..fa786a1c 100644 --- a/examples/android/lib/src/lib.rs +++ b/examples/android/lib/src/lib.rs @@ -1,15 +1,23 @@ +#[cfg(target_os = "android")] extern crate servo_media; +#[cfg(target_os = "android")] use servo_media::audio::gain_node::GainNodeOptions; +#[cfg(target_os = "android")] use servo_media::audio::context::AudioContext; +#[cfg(target_os = "android")] use servo_media::audio::node::{AudioNodeInit, AudioNodeMessage}; +#[cfg(target_os = "android")] use servo_media::audio::node::AudioScheduledSourceNodeMessage; +#[cfg(target_os = "android")] use servo_media::{Backend, ServoMedia}; +#[cfg(target_os = "android")] struct AudioStream { context: AudioContext, } +#[cfg(target_os = "android")] impl AudioStream { pub fn new() -> Self { let context = ServoMedia::get().unwrap().create_audio_context(Default::default()); diff --git a/examples/android/run.sh b/examples/android/run.sh index 965ef72f..668e868c 100755 --- a/examples/android/run.sh +++ b/examples/android/run.sh @@ -1,7 +1,9 @@ ROOT=${PWD} PKG_CONFIG_PATH_OLD=$PKG_CONFIG_PATH -cd ../../ -export PKG_CONFIG_PATH=${PWD}/backends/gstreamer/target/gst-build-armeabi/pkgconfig +cd ../../etc +./android_bootstrap.sh armeabi-v7a +cd .. +export PKG_CONFIG_PATH=${PWD}/gstreamer/armeabi-v7a/gst-build-armeabi-v7a/pkgconfig echo "Set PKG_CONFIG_PATH to ${PKG_CONFIG_PATH}" cd servo-media echo "Building servo-media ${PWD}" @@ -17,7 +19,7 @@ rm -rf jniLibs mkdir -p jniLibs/armeabi ln -s ${ROOT}/../../target/arm-linux-androideabi/debug/libservo_media_android.so jniLibs/armeabi/libservo_media_android.so -ln -s ${ROOT}/../../backends/gstreamer/target/gst-build-armeabi/libgstreamer_android.so jniLibs/armeabi/libgstreamer_android.so +ln -s ${ROOT}/../../gstreamer/armeabi-v7a/gst-build-armeabi-v7a/libgstreamer_android.so jniLibs/armeabi/libgstreamer_android.so cd ${ROOT}/src ./gradlew installDebug || return 1 diff --git a/examples/android/setup.sh b/examples/android/setup.sh deleted file mode 100755 index 5422cf21..00000000 --- a/examples/android/setup.sh +++ /dev/null @@ -1,8 +0,0 @@ -wget https://github.com/servo/libgstreamer_android_gen/blob/master/out/src.zip?raw=true -O src.zip -unzip src.zip -d src_ - -cp -v src_/src/org/freedesktop/gstreamer/GStreamer.java src/app/src/main/java/org/freedesktop/gstreamer/ -cp -rv src_/src/main/* src/app/src/main/ - -rm -rf src.zip -rm -rf src_ diff --git a/examples/android/src/app/src/main/java/org/freedesktop/gstreamer/GStreamer.java b/examples/android/src/app/src/main/java/org/freedesktop/gstreamer/GStreamer.java new file mode 100644 index 00000000..30ec3575 --- /dev/null +++ b/examples/android/src/app/src/main/java/org/freedesktop/gstreamer/GStreamer.java @@ -0,0 +1,12 @@ +package org.freedesktop.gstreamer; + +import android.content.Context; + +public class GStreamer { + private static native void nativeInit(Context context) throws Exception; + + public static void init(Context context) throws Exception { + nativeInit(context); + } +} + diff --git a/examples/player/main.rs b/examples/player/main.rs index 70d2e336..06808e1f 100644 --- a/examples/player/main.rs +++ b/examples/player/main.rs @@ -1,13 +1,18 @@ +#![allow(unused_imports)] +#![allow(dead_code)] + // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. extern crate gleam; +#[cfg(not(target_os = "android"))] extern crate glutin; extern crate ipc_channel; extern crate servo_media; extern crate time; extern crate webrender; +#[cfg(not(target_os = "android"))] extern crate winit; use gleam::gl; @@ -24,9 +29,11 @@ use std::path::Path; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use std::thread::Builder; +#[cfg(not(target_os = "android"))] use ui::HandyDandyRectBuilder; use webrender::api::*; +#[cfg(not(target_os = "android"))] #[path = "ui.rs"] mod ui; @@ -150,6 +157,7 @@ impl App { } } +#[cfg(not(target_os = "android"))] impl ui::Example for App { fn render( &mut self, @@ -226,6 +234,12 @@ impl FrameRenderer for App { } } +#[cfg(target_os = "android")] +fn main() { + panic!("Unsupported"); +} + +#[cfg(not(target_os = "android"))] fn main() { let args: Vec<_> = env::args().collect(); let filename: &str = if args.len() == 2 {