diff --git a/README.md b/README.md index cbac71f8..71b7a78d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Minicap provides a socket interface for streaming realtime screen capture data out of Android devices. It is meant to be used as a component in a larger program and is therefore not immensely useful just by itself. For example, it is being used in [STF](https://github.com/openstf/stf) for remote control. -Minicap works without root if started via [ADB](http://developer.android.com/tools/help/adb.html) on SDK 22 and lower, as well as on Android M Developer Preview 3. The lowest SDK level we build for is 9 (i.e. Android 2.3). Minicap also works on Android Wear. Emulators, however, are not supported. +Minicap works without root if started via [ADB](http://developer.android.com/tools/help/adb.html) on SDK 25 (Android 7.1) and lower. The lowest SDK level we build for is 9 (i.e. Android 2.3). Minicap also works on Android Wear. Emulators, however, are not supported. Note that Android 3.x is not supported since those versions were never open sourced. To capture the screen we currently use two methods. For older Android versions we use the ScreenshotClient, a private API in AOSP. For newer versions we use a virtual display, which also requires access to private APIs. The frames are then encoded using SIMD-enabled [libjpeg-turbo](http://libjpeg-turbo.virtualgl.org/) and sent over a socket interface. A planned future improvement to allow for even higher FPS is to use MediaRecorder and friends to take advantage of hardware encoding. diff --git a/build-remote.sh b/build-remote.sh index e4a93c92..f0302ba1 100755 --- a/build-remote.sh +++ b/build-remote.sh @@ -2,28 +2,20 @@ set -xueo pipefail -TARGET=/tmp/minicap +builder=$1 rsync \ - --rsync-path='nice rsync' \ --recursive \ --copy-links \ --perms \ --times \ - -FF ./jni/minicap-shared/aosp/ "$BUILD_HOST":$TARGET + -FF ./jni/minicap-shared/aosp/ "$builder":minicap/ -ssh -T "$BUILD_HOST" "docker run --rm \ - -a stdout -a stderr \ - -v $TARGET:$TARGET \ - -v \$(which docker):\$(which docker) \ - -v /usr/lib:/usr/lib \ - -v /var/run/docker.sock:/var/run/docker.sock \ - openstf/aosp:jdk7 bash -c 'cd $TARGET && make -j 1'" +ssh -T "$builder" "cd minicap && make -j 1" rsync \ - --rsync-path='nice rsync' \ --recursive \ --copy-links \ --perms \ --times \ - "$BUILD_HOST":$TARGET/libs/ ./jni/minicap-shared/aosp/libs/ + "$builder":minicap/libs/ ./jni/minicap-shared/aosp/libs/ diff --git a/jni/minicap-shared/README.md b/jni/minicap-shared/README.md index 712c1af0..c0745dfa 100644 --- a/jni/minicap-shared/README.md +++ b/jni/minicap-shared/README.md @@ -17,7 +17,7 @@ Using this setup, the minimum server-side machine requirements are: * [docker](https://www.docker.com/) * [SSH](http://www.openssh.com/) * A user account with SSH public key authentication and sudo-less access to docker -* Approximately 20GB of disk space per checked out branch, and approximately 60GB for a full mirror. We'll get to the branches later. We recommend a 512GB SSD, possibly more if you wish to compile other things as well (i.e. not just minicap). +* Depending on the SDK version, 25-60GB of disk space per built branch, and approximately 120GB for a full mirror. We'll get to the branches later. We recommend a 1TB SSD, possibly more if you wish to compile other things as well (i.e. not just minicap). With branch and mirror sizes ever growing, 512GB will soon not be enough, so don't bother if you want to keep everything on one disk. You'll also need [rsync](https://rsync.samba.org/) and [SSH](http://www.openssh.com/) properly set up on your development machine. @@ -50,11 +50,13 @@ Currently the following branches are required to build the libraries for all sup | android-4.4_r1 | 19 | openstf/aosp:jdk6 | | android-5.0.1_r1 | 21 | openstf/aosp:jdk7 | | android-5.1.0_r1 | 22 | openstf/aosp:jdk7 | -| android-m-preview-2 | 23 | openstf/aosp:jdk7 | +| android-6.0.0_r1 | 23 | openstf/aosp:jdk7 | +| android-7.0.0_r1 | 24 | openstf/aosp:jdk8 | +| android-7.1.0_r1 | 25 | openstf/aosp:jdk8 | -Furthermore, to make use of our provided Makefile, you should check out the branches to `/srv/aosp` for maximum ease of use. +Furthermore, to make use of our provided Makefile, you should check out the branches to `/media/aosp` for maximum ease of use. -To check out the branches, you have two options. To reduce download time and avoid bandwidth caps (on the server side), it would be advisable to fetch a full local mirror and then checkout out the individual branches from there. What tends to happen, though, is that the mirror manifest does not get updated quickly enough for new branches, and may be missing a repository or two, making it practically impossible to check out the branch you want. Additionally, the mirror takes over 50GB of disk space in addition to the checkouts. +To check out the branches, you have two options. To reduce download time and avoid bandwidth caps (on the server side), it would be advisable to fetch a full local mirror and then checkout out the individual branches from there. What tends to happen, though, is that the mirror manifest does not get updated quickly enough for new branches, and may be missing a repository or two, making it practically impossible to check out the branch you want. Additionally, the mirror takes over 120GB of disk space in addition to the checkouts. You can also skip the mirror and download each branch directly, but that will stress the AOSP server unnecessarily. @@ -89,7 +91,7 @@ Now that we're a bit more familiar with the helper script, let's start fetching ```bash docker run -ti --rm \ - -v /srv/aosp/mirror:/mirror \ + -v /media/aosp/mirror:/mirror \ -v $PWD/.gitcookies:/root/.gitcookies:ro \ openstf/aosp:jdk7 \ /aosp.sh create-mirror @@ -97,7 +99,7 @@ docker run -ti --rm \ This will take a LONG time, easily several hours. You may wish to leave it running overnight. If an error occurs (it will tell you), run the same command again and again until it finishes without errors. -When the command is done, you should have a copy of the latest mirror in `/srv/aosp/mirror`. We will mount this mirror when checking out individual branches. +When the command is done, you should have a copy of the latest mirror in `/media/aosp/mirror`. We will mount this mirror when checking out individual branches. You should rerun the command whenever a new branch you're interested in gets added to AOSP to sync the mirrored repos. @@ -109,8 +111,8 @@ For each branch in the table, run the following command: ```bash docker run -ti --rm \ - -v /srv/aosp/mirror:/mirror \ - -v /srv/aosp/android-5.1.0_r1:/aosp \ + -v /media/aosp/mirror:/mirror \ + -v /media/aosp/android-5.1.0_r1:/aosp \ -v $PWD/.gitcookies:/root/.gitcookies:ro \ openstf/aosp:jdk7 \ /aosp.sh checkout-branch android-5.1.0_r1 @@ -128,7 +130,7 @@ For each branch you wish to download directly from the AOSP servers, run the fol ```bash docker run -ti --rm \ - -v /srv/aosp/android-5.1.0_r1:/aosp \ + -v /media/aosp/android-5.1.0_r1:/aosp \ -v $PWD/.gitcookies:/root/.gitcookies:ro \ openstf/aosp:jdk7 \ /aosp.sh checkout-branch --no-mirror android-5.1.0_r1 @@ -175,3 +177,25 @@ Our advice? Be damn sure your code compiles before actually compiling :) Alternatively, you may wish to temporarily remove other targets from the `Makefile` all target when working on bigger changes but focusing on a single branch. In any case, congratulations, you're done! + +### Tips + +Here's a systemd unit to keep an external disk mounted in `/media/aosp`. It assumes you have a btrfs-formatted partition labeled `AOSP` on a disk somewhere, mine's on an external SSD. Example of how to create one: + +```sh +mkfs.btrfs -L AOSP /dev/sdc1 +``` + +And here's the corresponding unit: + +```systemd +[Mount] +What=/dev/disk/by-label/AOSP +Where=/media/aosp +Type=btrfs + +[Install] +WantedBy=multi-user.target +``` + +Now just run `systemctl enable media-aosp.mount && systemctl start media-aosp.mount`. diff --git a/jni/minicap-shared/aosp/Android.mk b/jni/minicap-shared/aosp/Android.mk index d30a572c..3fab13d6 100644 --- a/jni/minicap-shared/aosp/Android.mk +++ b/jni/minicap-shared/aosp/Android.mk @@ -5,8 +5,10 @@ LOCAL_MODULE := minicap LOCAL_MODULE_TAGS := optional -ifeq ($(OVERRIDE_PLATFORM_SDK_VERSION),23) -LOCAL_SRC_FILES += src/minicap_23.cpp +ifeq ($(PLATFORM_SDK_VERSION),25) +LOCAL_SRC_FILES += src/minicap_25.cpp +else ifeq ($(PLATFORM_SDK_VERSION),24) +LOCAL_SRC_FILES += src/minicap_24.cpp else ifeq ($(PLATFORM_SDK_VERSION),23) LOCAL_SRC_FILES += src/minicap_23.cpp else ifeq ($(PLATFORM_SDK_VERSION),22) diff --git a/jni/minicap-shared/aosp/Makefile b/jni/minicap-shared/aosp/Makefile index 1a05ae15..63adc5b9 100644 --- a/jni/minicap-shared/aosp/Makefile +++ b/jni/minicap-shared/aosp/Makefile @@ -28,12 +28,20 @@ all: \ libs/android-23/arm64-v8a/minicap.so \ libs/android-23/x86/minicap.so \ libs/android-23/x86_64/minicap.so \ + libs/android-24/armeabi-v7a/minicap.so \ + libs/android-24/arm64-v8a/minicap.so \ + libs/android-24/x86/minicap.so \ + libs/android-24/x86_64/minicap.so \ + libs/android-25/armeabi-v7a/minicap.so \ + libs/android-25/arm64-v8a/minicap.so \ + libs/android-25/x86/minicap.so \ + libs/android-25/x86_64/minicap.so \ libs/android-9/armeabi-v7a/minicap.so: $(SOURCES) src/minicap_9.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-2.3_r1:/aosp \ + -v /media/aosp/android-2.3_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk6 /aosp.sh build generic-eng minicap @@ -42,7 +50,7 @@ libs/android-10/armeabi-v7a/minicap.so: $(SOURCES) src/minicap_9.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-2.3.3_r1:/aosp \ + -v /media/aosp/android-2.3.3_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk6 /aosp.sh build generic-eng minicap @@ -51,7 +59,7 @@ libs/android-14/armeabi-v7a/minicap.so: $(SOURCES) src/minicap_14.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-4.0.1_r1:/aosp \ + -v /media/aosp/android-4.0.1_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk6 /aosp.sh build full-eng minicap @@ -60,7 +68,7 @@ libs/android-14/x86/minicap.so: $(SOURCES) src/minicap_14.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-4.0.1_r1:/aosp \ + -v /media/aosp/android-4.0.1_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk6 /aosp.sh build full_x86-eng minicap @@ -69,7 +77,7 @@ libs/android-15/armeabi-v7a/minicap.so: $(SOURCES) src/minicap_14.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-4.0.3_r1:/aosp \ + -v /media/aosp/android-4.0.3_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk6 /aosp.sh build full-eng minicap @@ -78,7 +86,7 @@ libs/android-15/x86/minicap.so: $(SOURCES) src/minicap_14.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-4.0.3_r1:/aosp \ + -v /media/aosp/android-4.0.3_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk6 /aosp.sh build full_x86-eng minicap @@ -87,7 +95,7 @@ libs/android-16/armeabi-v7a/minicap.so: $(SOURCES) src/minicap_16.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-4.1.1_r1:/aosp \ + -v /media/aosp/android-4.1.1_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk6 /aosp.sh build full-eng minicap @@ -96,7 +104,7 @@ libs/android-16/x86/minicap.so: $(SOURCES) src/minicap_16.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-4.1.1_r1:/aosp \ + -v /media/aosp/android-4.1.1_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk6 /aosp.sh build full_x86-eng minicap @@ -105,7 +113,7 @@ libs/android-17/armeabi-v7a/minicap.so: $(SOURCES) src/minicap_17.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-4.2_r1:/aosp \ + -v /media/aosp/android-4.2_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk6 /aosp.sh build full-eng minicap @@ -114,7 +122,7 @@ libs/android-17/x86/minicap.so: $(SOURCES) src/minicap_17.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-4.2_r1:/aosp \ + -v /media/aosp/android-4.2_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk6 /aosp.sh build full_x86-eng minicap @@ -123,7 +131,7 @@ libs/android-18/armeabi-v7a/minicap.so: $(SOURCES) src/minicap_18.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-4.3_r1:/aosp \ + -v /media/aosp/android-4.3_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk6 /aosp.sh build aosp_arm-eng minicap @@ -132,7 +140,7 @@ libs/android-18/x86/minicap.so: $(SOURCES) src/minicap_18.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-4.3_r1:/aosp \ + -v /media/aosp/android-4.3_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk6 /aosp.sh build aosp_x86-eng minicap @@ -141,7 +149,7 @@ libs/android-19/armeabi-v7a/minicap.so: $(SOURCES) src/minicap_19.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-4.4_r1:/aosp \ + -v /media/aosp/android-4.4_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk6 /aosp.sh build aosp_arm-eng minicap @@ -150,7 +158,7 @@ libs/android-19/x86/minicap.so: $(SOURCES) src/minicap_19.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-4.4_r1:/aosp \ + -v /media/aosp/android-4.4_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk6 /aosp.sh build aosp_x86-eng minicap @@ -159,7 +167,7 @@ libs/android-21/armeabi-v7a/minicap.so: $(SOURCES) src/minicap_21.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-5.0.1_r1:/aosp \ + -v /media/aosp/android-5.0.1_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk7 /aosp.sh build aosp_arm-eng minicap @@ -168,7 +176,7 @@ libs/android-21/arm64-v8a/minicap.so: $(SOURCES) src/minicap_21.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-5.0.1_r1:/aosp \ + -v /media/aosp/android-5.0.1_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk7 /aosp.sh build aosp_arm64-eng minicap @@ -177,7 +185,7 @@ libs/android-21/x86/minicap.so: $(SOURCES) src/minicap_21.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-5.0.1_r1:/aosp \ + -v /media/aosp/android-5.0.1_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk7 /aosp.sh build aosp_x86-eng minicap @@ -186,7 +194,7 @@ libs/android-21/x86_64/minicap.so: $(SOURCES) src/minicap_21.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-5.0.1_r1:/aosp \ + -v /media/aosp/android-5.0.1_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk7 /aosp.sh build aosp_x86_64-eng minicap @@ -195,7 +203,7 @@ libs/android-21/mips/minicap.so: $(SOURCES) src/minicap_21.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-5.0.1_r1:/aosp \ + -v /media/aosp/android-5.0.1_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk7 /aosp.sh build aosp_mips-eng minicap @@ -204,7 +212,7 @@ libs/android-21/mips64/minicap.so: $(SOURCES) src/minicap_21.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-5.0.1_r1:/aosp \ + -v /media/aosp/android-5.0.1_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk7 /aosp.sh build aosp_mips64-eng minicap @@ -213,7 +221,7 @@ libs/android-22/armeabi-v7a/minicap.so: $(SOURCES) src/minicap_22.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-5.1.0_r1:/aosp \ + -v /media/aosp/android-5.1.0_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk7 /aosp.sh build aosp_arm-eng minicap @@ -222,7 +230,7 @@ libs/android-22/arm64-v8a/minicap.so: $(SOURCES) src/minicap_22.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-5.1.0_r1:/aosp \ + -v /media/aosp/android-5.1.0_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk7 /aosp.sh build aosp_arm64-eng minicap @@ -231,7 +239,7 @@ libs/android-22/x86/minicap.so: $(SOURCES) src/minicap_22.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-5.1.0_r1:/aosp \ + -v /media/aosp/android-5.1.0_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk7 /aosp.sh build aosp_x86-eng minicap @@ -240,7 +248,7 @@ libs/android-22/x86_64/minicap.so: $(SOURCES) src/minicap_22.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-5.1.0_r1:/aosp \ + -v /media/aosp/android-5.1.0_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk7 /aosp.sh build aosp_x86_64-eng minicap @@ -249,7 +257,7 @@ libs/android-22/mips/minicap.so: $(SOURCES) src/minicap_22.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-5.1.0_r1:/aosp \ + -v /media/aosp/android-5.1.0_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk7 /aosp.sh build aosp_mips-eng minicap @@ -258,7 +266,7 @@ libs/android-22/mips64/minicap.so: $(SOURCES) src/minicap_22.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-5.1.0_r1:/aosp \ + -v /media/aosp/android-5.1.0_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ openstf/aosp:jdk7 /aosp.sh build aosp_mips64-eng minicap @@ -267,58 +275,160 @@ libs/android-23/armeabi-v7a/minicap.so: $(SOURCES) src/minicap_23.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-m-preview-2:/aosp \ + -v /media/aosp/android-6.0.0_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ - -e OVERRIDE_PLATFORM_SDK_VERSION=23 \ openstf/aosp:jdk7 /aosp.sh build aosp_arm-eng minicap libs/android-23/arm64-v8a/minicap.so: $(SOURCES) src/minicap_23.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-m-preview-2:/aosp \ + -v /media/aosp/android-6.0.0_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ - -e OVERRIDE_PLATFORM_SDK_VERSION=23 \ openstf/aosp:jdk7 /aosp.sh build aosp_arm64-eng minicap libs/android-23/x86/minicap.so: $(SOURCES) src/minicap_23.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-m-preview-2:/aosp \ + -v /media/aosp/android-6.0.0_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ - -e OVERRIDE_PLATFORM_SDK_VERSION=23 \ openstf/aosp:jdk7 /aosp.sh build aosp_x86-eng minicap libs/android-23/x86_64/minicap.so: $(SOURCES) src/minicap_23.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-m-preview-2:/aosp \ + -v /media/aosp/android-6.0.0_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ - -e OVERRIDE_PLATFORM_SDK_VERSION=23 \ openstf/aosp:jdk7 /aosp.sh build aosp_x86_64-eng minicap libs/android-23/mips/minicap.so: $(SOURCES) src/minicap_23.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-m-preview-2:/aosp \ + -v /media/aosp/android-6.0.0_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ - -e OVERRIDE_PLATFORM_SDK_VERSION=23 \ openstf/aosp:jdk7 /aosp.sh build aosp_mips-eng minicap libs/android-23/mips64/minicap.so: $(SOURCES) src/minicap_23.cpp mkdir -p $(@D) docker run --rm \ -a stdout -a stderr \ - -v /srv/aosp/android-m-preview-2:/aosp \ + -v /media/aosp/android-6.0.0_r1:/aosp \ -v $(this_dir):/app \ -v $(this_dir)$(@D):/artifacts \ - -e OVERRIDE_PLATFORM_SDK_VERSION=23 \ openstf/aosp:jdk7 /aosp.sh build aosp_mips64-eng minicap + +libs/android-24/armeabi-v7a/minicap.so: $(SOURCES) src/minicap_24.cpp + mkdir -p $(@D) + docker run --rm \ + -a stdout -a stderr \ + -v /media/aosp/android-7.0.0_r1:/aosp \ + -v $(this_dir):/app \ + -v $(this_dir)$(@D):/artifacts \ + openstf/aosp:jdk8 /aosp.sh build aosp_arm-eng minicap + +libs/android-24/arm64-v8a/minicap.so: $(SOURCES) src/minicap_24.cpp + mkdir -p $(@D) + docker run --rm \ + -a stdout -a stderr \ + -v /media/aosp/android-7.0.0_r1:/aosp \ + -v $(this_dir):/app \ + -v $(this_dir)$(@D):/artifacts \ + openstf/aosp:jdk8 /aosp.sh build aosp_arm64-eng minicap + +libs/android-24/x86/minicap.so: $(SOURCES) src/minicap_24.cpp + mkdir -p $(@D) + docker run --rm \ + -a stdout -a stderr \ + -v /media/aosp/android-7.0.0_r1:/aosp \ + -v $(this_dir):/app \ + -v $(this_dir)$(@D):/artifacts \ + openstf/aosp:jdk8 /aosp.sh build aosp_x86-eng minicap + +libs/android-24/x86_64/minicap.so: $(SOURCES) src/minicap_24.cpp + mkdir -p $(@D) + docker run --rm \ + -a stdout -a stderr \ + -v /media/aosp/android-7.0.0_r1:/aosp \ + -v $(this_dir):/app \ + -v $(this_dir)$(@D):/artifacts \ + openstf/aosp:jdk8 /aosp.sh build aosp_x86_64-eng minicap + +libs/android-24/mips/minicap.so: $(SOURCES) src/minicap_24.cpp + mkdir -p $(@D) + docker run --rm \ + -a stdout -a stderr \ + -v /media/aosp/android-7.0.0_r1:/aosp \ + -v $(this_dir):/app \ + -v $(this_dir)$(@D):/artifacts \ + openstf/aosp:jdk8 /aosp.sh build aosp_mips-eng minicap + +libs/android-24/mips64/minicap.so: $(SOURCES) src/minicap_24.cpp + mkdir -p $(@D) + docker run --rm \ + -a stdout -a stderr \ + -v /media/aosp/android-7.0.0_r1:/aosp \ + -v $(this_dir):/app \ + -v $(this_dir)$(@D):/artifacts \ + openstf/aosp:jdk8 /aosp.sh build aosp_mips64-eng minicap + +libs/android-25/armeabi-v7a/minicap.so: $(SOURCES) src/minicap_25.cpp + mkdir -p $(@D) + docker run --rm \ + -a stdout -a stderr \ + -v /media/aosp/android-7.1.0_r1:/aosp \ + -v $(this_dir):/app \ + -v $(this_dir)$(@D):/artifacts \ + openstf/aosp:jdk8 /aosp.sh build aosp_arm-eng minicap + +libs/android-25/arm64-v8a/minicap.so: $(SOURCES) src/minicap_25.cpp + mkdir -p $(@D) + docker run --rm \ + -a stdout -a stderr \ + -v /media/aosp/android-7.1.0_r1:/aosp \ + -v $(this_dir):/app \ + -v $(this_dir)$(@D):/artifacts \ + openstf/aosp:jdk8 /aosp.sh build aosp_arm64-eng minicap + +libs/android-25/x86/minicap.so: $(SOURCES) src/minicap_25.cpp + mkdir -p $(@D) + docker run --rm \ + -a stdout -a stderr \ + -v /media/aosp/android-7.1.0_r1:/aosp \ + -v $(this_dir):/app \ + -v $(this_dir)$(@D):/artifacts \ + openstf/aosp:jdk8 /aosp.sh build aosp_x86-eng minicap + +libs/android-25/x86_64/minicap.so: $(SOURCES) src/minicap_25.cpp + mkdir -p $(@D) + docker run --rm \ + -a stdout -a stderr \ + -v /media/aosp/android-7.1.0_r1:/aosp \ + -v $(this_dir):/app \ + -v $(this_dir)$(@D):/artifacts \ + openstf/aosp:jdk8 /aosp.sh build aosp_x86_64-eng minicap + +libs/android-25/mips/minicap.so: $(SOURCES) src/minicap_25.cpp + mkdir -p $(@D) + docker run --rm \ + -a stdout -a stderr \ + -v /media/aosp/android-7.1.0_r1:/aosp \ + -v $(this_dir):/app \ + -v $(this_dir)$(@D):/artifacts \ + openstf/aosp:jdk8 /aosp.sh build aosp_mips-eng minicap + +libs/android-25/mips64/minicap.so: $(SOURCES) src/minicap_25.cpp + mkdir -p $(@D) + docker run --rm \ + -a stdout -a stderr \ + -v /media/aosp/android-7.1.0_r1:/aosp \ + -v $(this_dir):/app \ + -v $(this_dir)$(@D):/artifacts \ + openstf/aosp:jdk8 /aosp.sh build aosp_mips64-eng minicap diff --git a/jni/minicap-shared/aosp/libs/android-23/arm64-v8a/minicap.so b/jni/minicap-shared/aosp/libs/android-23/arm64-v8a/minicap.so index 18bbfda6..fbf4f8ea 100755 Binary files a/jni/minicap-shared/aosp/libs/android-23/arm64-v8a/minicap.so and b/jni/minicap-shared/aosp/libs/android-23/arm64-v8a/minicap.so differ diff --git a/jni/minicap-shared/aosp/libs/android-23/armeabi-v7a/minicap.so b/jni/minicap-shared/aosp/libs/android-23/armeabi-v7a/minicap.so index 61995812..e1c099d0 100755 Binary files a/jni/minicap-shared/aosp/libs/android-23/armeabi-v7a/minicap.so and b/jni/minicap-shared/aosp/libs/android-23/armeabi-v7a/minicap.so differ diff --git a/jni/minicap-shared/aosp/libs/android-23/x86/minicap.so b/jni/minicap-shared/aosp/libs/android-23/x86/minicap.so index d4ed08ea..76f3258b 100755 Binary files a/jni/minicap-shared/aosp/libs/android-23/x86/minicap.so and b/jni/minicap-shared/aosp/libs/android-23/x86/minicap.so differ diff --git a/jni/minicap-shared/aosp/libs/android-23/x86_64/minicap.so b/jni/minicap-shared/aosp/libs/android-23/x86_64/minicap.so index 967e4f40..678f18f6 100755 Binary files a/jni/minicap-shared/aosp/libs/android-23/x86_64/minicap.so and b/jni/minicap-shared/aosp/libs/android-23/x86_64/minicap.so differ diff --git a/jni/minicap-shared/aosp/libs/android-24/arm64-v8a/minicap.so b/jni/minicap-shared/aosp/libs/android-24/arm64-v8a/minicap.so new file mode 100755 index 00000000..86666b8e Binary files /dev/null and b/jni/minicap-shared/aosp/libs/android-24/arm64-v8a/minicap.so differ diff --git a/jni/minicap-shared/aosp/libs/android-24/armeabi-v7a/minicap.so b/jni/minicap-shared/aosp/libs/android-24/armeabi-v7a/minicap.so new file mode 100755 index 00000000..2de2826d Binary files /dev/null and b/jni/minicap-shared/aosp/libs/android-24/armeabi-v7a/minicap.so differ diff --git a/jni/minicap-shared/aosp/libs/android-24/x86/minicap.so b/jni/minicap-shared/aosp/libs/android-24/x86/minicap.so new file mode 100755 index 00000000..d1dfeb53 Binary files /dev/null and b/jni/minicap-shared/aosp/libs/android-24/x86/minicap.so differ diff --git a/jni/minicap-shared/aosp/libs/android-24/x86_64/minicap.so b/jni/minicap-shared/aosp/libs/android-24/x86_64/minicap.so new file mode 100755 index 00000000..b4677d5d Binary files /dev/null and b/jni/minicap-shared/aosp/libs/android-24/x86_64/minicap.so differ diff --git a/jni/minicap-shared/aosp/libs/android-25/arm64-v8a/minicap.so b/jni/minicap-shared/aosp/libs/android-25/arm64-v8a/minicap.so new file mode 100755 index 00000000..53370c62 Binary files /dev/null and b/jni/minicap-shared/aosp/libs/android-25/arm64-v8a/minicap.so differ diff --git a/jni/minicap-shared/aosp/libs/android-25/armeabi-v7a/minicap.so b/jni/minicap-shared/aosp/libs/android-25/armeabi-v7a/minicap.so new file mode 100755 index 00000000..9af961b2 Binary files /dev/null and b/jni/minicap-shared/aosp/libs/android-25/armeabi-v7a/minicap.so differ diff --git a/jni/minicap-shared/aosp/libs/android-25/x86/minicap.so b/jni/minicap-shared/aosp/libs/android-25/x86/minicap.so new file mode 100755 index 00000000..b2909937 Binary files /dev/null and b/jni/minicap-shared/aosp/libs/android-25/x86/minicap.so differ diff --git a/jni/minicap-shared/aosp/libs/android-25/x86_64/minicap.so b/jni/minicap-shared/aosp/libs/android-25/x86_64/minicap.so new file mode 100755 index 00000000..78779bb7 Binary files /dev/null and b/jni/minicap-shared/aosp/libs/android-25/x86_64/minicap.so differ diff --git a/jni/minicap-shared/aosp/src/minicap_24.cpp b/jni/minicap-shared/aosp/src/minicap_24.cpp new file mode 100644 index 00000000..d0025aa3 --- /dev/null +++ b/jni/minicap-shared/aosp/src/minicap_24.cpp @@ -0,0 +1,372 @@ +#include "Minicap.hpp" + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "mcdebug.h" + +static const char* +error_name(int32_t err) { + switch (err) { + case android::NO_ERROR: // also android::OK + return "NO_ERROR"; + case android::UNKNOWN_ERROR: + return "UNKNOWN_ERROR"; + case android::NO_MEMORY: + return "NO_MEMORY"; + case android::INVALID_OPERATION: + return "INVALID_OPERATION"; + case android::BAD_VALUE: + return "BAD_VALUE"; + case android::BAD_TYPE: + return "BAD_TYPE"; + case android::NAME_NOT_FOUND: + return "NAME_NOT_FOUND"; + case android::PERMISSION_DENIED: + return "PERMISSION_DENIED"; + case android::NO_INIT: + return "NO_INIT"; + case android::ALREADY_EXISTS: + return "ALREADY_EXISTS"; + case android::DEAD_OBJECT: // also android::JPARKS_BROKE_IT + return "DEAD_OBJECT"; + case android::FAILED_TRANSACTION: + return "FAILED_TRANSACTION"; + case android::BAD_INDEX: + return "BAD_INDEX"; + case android::NOT_ENOUGH_DATA: + return "NOT_ENOUGH_DATA"; + case android::WOULD_BLOCK: + return "WOULD_BLOCK"; + case android::TIMED_OUT: + return "TIMED_OUT"; + case android::UNKNOWN_TRANSACTION: + return "UNKNOWN_TRANSACTION"; + case android::FDS_NOT_ALLOWED: + return "FDS_NOT_ALLOWED"; + default: + return "UNMAPPED_ERROR"; + } +} + +class FrameProxy: public android::ConsumerBase::FrameAvailableListener { +public: + FrameProxy(Minicap::FrameAvailableListener* listener): mUserListener(listener) { + } + + virtual void + onFrameAvailable(const android::BufferItem& /* item */) { + mUserListener->onFrameAvailable(); + } + +private: + Minicap::FrameAvailableListener* mUserListener; +}; + +class MinicapImpl: public Minicap +{ +public: + MinicapImpl(int32_t displayId) + : mDisplayId(displayId), + mRealWidth(0), + mRealHeight(0), + mDesiredWidth(0), + mDesiredHeight(0), + mDesiredOrientation(0), + mHaveBuffer(false), + mHaveRunningDisplay(false) { + } + + virtual + ~MinicapImpl() { + release(); + } + + virtual int + applyConfigChanges() { + if (mHaveRunningDisplay) { + destroyVirtualDisplay(); + } + + return createVirtualDisplay(); + } + + virtual int + consumePendingFrame(Minicap::Frame* frame) { + android::status_t err; + + if ((err = mConsumer->lockNextBuffer(&mBuffer)) != android::NO_ERROR) { + if (err == -EINTR) { + return err; + } + else { + MCERROR("Unable to lock next buffer %s (%d)", error_name(err), err); + return err; + } + } + + frame->data = mBuffer.data; + frame->format = convertFormat(mBuffer.format); + frame->width = mBuffer.width; + frame->height = mBuffer.height; + frame->stride = mBuffer.stride; + frame->bpp = android::bytesPerPixel(mBuffer.format); + frame->size = mBuffer.stride * mBuffer.height * frame->bpp; + + mHaveBuffer = true; + + return 0; + } + + virtual Minicap::CaptureMethod + getCaptureMethod() { + return METHOD_VIRTUAL_DISPLAY; + } + + virtual int32_t + getDisplayId() { + return mDisplayId; + } + + virtual void + release() { + destroyVirtualDisplay(); + } + + virtual void + releaseConsumedFrame(Minicap::Frame* /* frame */) { + if (mHaveBuffer) { + mConsumer->unlockBuffer(mBuffer); + mHaveBuffer = false; + } + } + + virtual int + setDesiredInfo(const Minicap::DisplayInfo& info) { + mDesiredWidth = info.width; + mDesiredHeight = info.height; + mDesiredOrientation = info.orientation; + return 0; + } + + virtual void + setFrameAvailableListener(Minicap::FrameAvailableListener* listener) { + mUserFrameAvailableListener = listener; + } + + virtual int + setRealInfo(const Minicap::DisplayInfo& info) { + mRealWidth = info.width; + mRealHeight = info.height; + return 0; + } + +private: + int32_t mDisplayId; + uint32_t mRealWidth; + uint32_t mRealHeight; + uint32_t mDesiredWidth; + uint32_t mDesiredHeight; + uint8_t mDesiredOrientation; + android::sp mBufferProducer; + android::sp mBufferConsumer; + android::sp mConsumer; + android::sp mVirtualDisplay; + android::sp mFrameProxy; + Minicap::FrameAvailableListener* mUserFrameAvailableListener; + bool mHaveBuffer; + bool mHaveRunningDisplay; + android::CpuConsumer::LockedBuffer mBuffer; + + int + createVirtualDisplay() { + uint32_t sourceWidth, sourceHeight; + uint32_t targetWidth, targetHeight; + android::status_t err; + + switch (mDesiredOrientation) { + case Minicap::ORIENTATION_90: + sourceWidth = mRealHeight; + sourceHeight = mRealWidth; + targetWidth = mDesiredHeight; + targetHeight = mDesiredWidth; + break; + case Minicap::ORIENTATION_270: + sourceWidth = mRealHeight; + sourceHeight = mRealWidth; + targetWidth = mDesiredHeight; + targetHeight = mDesiredWidth; + break; + case Minicap::ORIENTATION_180: + sourceWidth = mRealWidth; + sourceHeight = mRealHeight; + targetWidth = mDesiredWidth; + targetHeight = mDesiredHeight; + break; + case Minicap::ORIENTATION_0: + default: + sourceWidth = mRealWidth; + sourceHeight = mRealHeight; + targetWidth = mDesiredWidth; + targetHeight = mDesiredHeight; + break; + } + + // Set up virtual display size. + android::Rect layerStackRect(sourceWidth, sourceHeight); + android::Rect visibleRect(targetWidth, targetHeight); + + // Create a Surface for the virtual display to write to. + MCINFO("Creating SurfaceComposerClient"); + android::sp sc = new android::SurfaceComposerClient(); + + MCINFO("Performing SurfaceComposerClient init check"); + if ((err = sc->initCheck()) != android::NO_ERROR) { + MCERROR("Unable to initialize SurfaceComposerClient"); + return err; + } + + // Create virtual display. + MCINFO("Creating virtual display"); + mVirtualDisplay = android::SurfaceComposerClient::createDisplay( + /* const String8& displayName */ android::String8("minicap"), + /* bool secure */ true + ); + + MCINFO("Creating buffer queue"); + android::BufferQueue::createBufferQueue(&mBufferProducer, &mBufferConsumer); + mBufferConsumer->setDefaultBufferSize(targetWidth, targetHeight); + mBufferConsumer->setDefaultBufferFormat(android::PIXEL_FORMAT_RGBA_8888); + + MCINFO("Creating CPU consumer"); + mConsumer = new android::CpuConsumer(mBufferConsumer, 3, false); + mConsumer->setName(android::String8("minicap")); + + MCINFO("Creating frame waiter"); + mFrameProxy = new FrameProxy(mUserFrameAvailableListener); + mConsumer->setFrameAvailableListener(mFrameProxy); + + MCINFO("Publishing virtual display"); + android::SurfaceComposerClient::openGlobalTransaction(); + android::SurfaceComposerClient::setDisplaySurface(mVirtualDisplay, mBufferProducer); + android::SurfaceComposerClient::setDisplayProjection(mVirtualDisplay, + android::DISPLAY_ORIENTATION_0, layerStackRect, visibleRect); + android::SurfaceComposerClient::setDisplayLayerStack(mVirtualDisplay, 0); // default stack + android::SurfaceComposerClient::closeGlobalTransaction(); + + mHaveRunningDisplay = true; + + return 0; + } + + void + destroyVirtualDisplay() { + MCINFO("Destroying virtual display"); + android::SurfaceComposerClient::destroyDisplay(mVirtualDisplay); + + if (mHaveBuffer) { + mConsumer->unlockBuffer(mBuffer); + mHaveBuffer = false; + } + + mBufferProducer = NULL; + mBufferConsumer = NULL; + mConsumer = NULL; + mFrameProxy = NULL; + mVirtualDisplay = NULL; + + mHaveRunningDisplay = false; + } + + static Minicap::Format + convertFormat(android::PixelFormat format) { + switch (format) { + case android::PIXEL_FORMAT_NONE: + return FORMAT_NONE; + case android::PIXEL_FORMAT_CUSTOM: + return FORMAT_CUSTOM; + case android::PIXEL_FORMAT_TRANSLUCENT: + return FORMAT_TRANSLUCENT; + case android::PIXEL_FORMAT_TRANSPARENT: + return FORMAT_TRANSPARENT; + case android::PIXEL_FORMAT_OPAQUE: + return FORMAT_OPAQUE; + case android::PIXEL_FORMAT_RGBA_8888: + return FORMAT_RGBA_8888; + case android::PIXEL_FORMAT_RGBX_8888: + return FORMAT_RGBX_8888; + case android::PIXEL_FORMAT_RGB_888: + return FORMAT_RGB_888; + case android::PIXEL_FORMAT_RGB_565: + return FORMAT_RGB_565; + case android::PIXEL_FORMAT_BGRA_8888: + return FORMAT_BGRA_8888; + case android::PIXEL_FORMAT_RGBA_5551: + return FORMAT_RGBA_5551; + case android::PIXEL_FORMAT_RGBA_4444: + return FORMAT_RGBA_4444; + default: + return FORMAT_UNKNOWN; + } + } +}; + +int +minicap_try_get_display_info(int32_t displayId, Minicap::DisplayInfo* info) { + android::sp dpy = android::SurfaceComposerClient::getBuiltInDisplay(displayId); + + android::DisplayInfo dinfo; + android::status_t err = android::SurfaceComposerClient::getDisplayInfo(dpy, &dinfo); + + if (err != android::NO_ERROR) { + MCERROR("SurfaceComposerClient::getDisplayInfo() failed: %s (%d)\n", error_name(err), err); + return err; + } + + info->width = dinfo.w; + info->height = dinfo.h; + info->orientation = dinfo.orientation; + info->fps = dinfo.fps; + info->density = dinfo.density; + info->xdpi = dinfo.xdpi; + info->ydpi = dinfo.ydpi; + info->secure = dinfo.secure; + info->size = sqrt(pow(dinfo.w / dinfo.xdpi, 2) + pow(dinfo.h / dinfo.ydpi, 2)); + + return 0; +} + +Minicap* +minicap_create(int32_t displayId) { + return new MinicapImpl(displayId); +} + +void +minicap_free(Minicap* mc) { + delete mc; +} + +void +minicap_start_thread_pool() { + android::ProcessState::self()->startThreadPool(); +} diff --git a/jni/minicap-shared/aosp/src/minicap_25.cpp b/jni/minicap-shared/aosp/src/minicap_25.cpp new file mode 100644 index 00000000..d0025aa3 --- /dev/null +++ b/jni/minicap-shared/aosp/src/minicap_25.cpp @@ -0,0 +1,372 @@ +#include "Minicap.hpp" + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "mcdebug.h" + +static const char* +error_name(int32_t err) { + switch (err) { + case android::NO_ERROR: // also android::OK + return "NO_ERROR"; + case android::UNKNOWN_ERROR: + return "UNKNOWN_ERROR"; + case android::NO_MEMORY: + return "NO_MEMORY"; + case android::INVALID_OPERATION: + return "INVALID_OPERATION"; + case android::BAD_VALUE: + return "BAD_VALUE"; + case android::BAD_TYPE: + return "BAD_TYPE"; + case android::NAME_NOT_FOUND: + return "NAME_NOT_FOUND"; + case android::PERMISSION_DENIED: + return "PERMISSION_DENIED"; + case android::NO_INIT: + return "NO_INIT"; + case android::ALREADY_EXISTS: + return "ALREADY_EXISTS"; + case android::DEAD_OBJECT: // also android::JPARKS_BROKE_IT + return "DEAD_OBJECT"; + case android::FAILED_TRANSACTION: + return "FAILED_TRANSACTION"; + case android::BAD_INDEX: + return "BAD_INDEX"; + case android::NOT_ENOUGH_DATA: + return "NOT_ENOUGH_DATA"; + case android::WOULD_BLOCK: + return "WOULD_BLOCK"; + case android::TIMED_OUT: + return "TIMED_OUT"; + case android::UNKNOWN_TRANSACTION: + return "UNKNOWN_TRANSACTION"; + case android::FDS_NOT_ALLOWED: + return "FDS_NOT_ALLOWED"; + default: + return "UNMAPPED_ERROR"; + } +} + +class FrameProxy: public android::ConsumerBase::FrameAvailableListener { +public: + FrameProxy(Minicap::FrameAvailableListener* listener): mUserListener(listener) { + } + + virtual void + onFrameAvailable(const android::BufferItem& /* item */) { + mUserListener->onFrameAvailable(); + } + +private: + Minicap::FrameAvailableListener* mUserListener; +}; + +class MinicapImpl: public Minicap +{ +public: + MinicapImpl(int32_t displayId) + : mDisplayId(displayId), + mRealWidth(0), + mRealHeight(0), + mDesiredWidth(0), + mDesiredHeight(0), + mDesiredOrientation(0), + mHaveBuffer(false), + mHaveRunningDisplay(false) { + } + + virtual + ~MinicapImpl() { + release(); + } + + virtual int + applyConfigChanges() { + if (mHaveRunningDisplay) { + destroyVirtualDisplay(); + } + + return createVirtualDisplay(); + } + + virtual int + consumePendingFrame(Minicap::Frame* frame) { + android::status_t err; + + if ((err = mConsumer->lockNextBuffer(&mBuffer)) != android::NO_ERROR) { + if (err == -EINTR) { + return err; + } + else { + MCERROR("Unable to lock next buffer %s (%d)", error_name(err), err); + return err; + } + } + + frame->data = mBuffer.data; + frame->format = convertFormat(mBuffer.format); + frame->width = mBuffer.width; + frame->height = mBuffer.height; + frame->stride = mBuffer.stride; + frame->bpp = android::bytesPerPixel(mBuffer.format); + frame->size = mBuffer.stride * mBuffer.height * frame->bpp; + + mHaveBuffer = true; + + return 0; + } + + virtual Minicap::CaptureMethod + getCaptureMethod() { + return METHOD_VIRTUAL_DISPLAY; + } + + virtual int32_t + getDisplayId() { + return mDisplayId; + } + + virtual void + release() { + destroyVirtualDisplay(); + } + + virtual void + releaseConsumedFrame(Minicap::Frame* /* frame */) { + if (mHaveBuffer) { + mConsumer->unlockBuffer(mBuffer); + mHaveBuffer = false; + } + } + + virtual int + setDesiredInfo(const Minicap::DisplayInfo& info) { + mDesiredWidth = info.width; + mDesiredHeight = info.height; + mDesiredOrientation = info.orientation; + return 0; + } + + virtual void + setFrameAvailableListener(Minicap::FrameAvailableListener* listener) { + mUserFrameAvailableListener = listener; + } + + virtual int + setRealInfo(const Minicap::DisplayInfo& info) { + mRealWidth = info.width; + mRealHeight = info.height; + return 0; + } + +private: + int32_t mDisplayId; + uint32_t mRealWidth; + uint32_t mRealHeight; + uint32_t mDesiredWidth; + uint32_t mDesiredHeight; + uint8_t mDesiredOrientation; + android::sp mBufferProducer; + android::sp mBufferConsumer; + android::sp mConsumer; + android::sp mVirtualDisplay; + android::sp mFrameProxy; + Minicap::FrameAvailableListener* mUserFrameAvailableListener; + bool mHaveBuffer; + bool mHaveRunningDisplay; + android::CpuConsumer::LockedBuffer mBuffer; + + int + createVirtualDisplay() { + uint32_t sourceWidth, sourceHeight; + uint32_t targetWidth, targetHeight; + android::status_t err; + + switch (mDesiredOrientation) { + case Minicap::ORIENTATION_90: + sourceWidth = mRealHeight; + sourceHeight = mRealWidth; + targetWidth = mDesiredHeight; + targetHeight = mDesiredWidth; + break; + case Minicap::ORIENTATION_270: + sourceWidth = mRealHeight; + sourceHeight = mRealWidth; + targetWidth = mDesiredHeight; + targetHeight = mDesiredWidth; + break; + case Minicap::ORIENTATION_180: + sourceWidth = mRealWidth; + sourceHeight = mRealHeight; + targetWidth = mDesiredWidth; + targetHeight = mDesiredHeight; + break; + case Minicap::ORIENTATION_0: + default: + sourceWidth = mRealWidth; + sourceHeight = mRealHeight; + targetWidth = mDesiredWidth; + targetHeight = mDesiredHeight; + break; + } + + // Set up virtual display size. + android::Rect layerStackRect(sourceWidth, sourceHeight); + android::Rect visibleRect(targetWidth, targetHeight); + + // Create a Surface for the virtual display to write to. + MCINFO("Creating SurfaceComposerClient"); + android::sp sc = new android::SurfaceComposerClient(); + + MCINFO("Performing SurfaceComposerClient init check"); + if ((err = sc->initCheck()) != android::NO_ERROR) { + MCERROR("Unable to initialize SurfaceComposerClient"); + return err; + } + + // Create virtual display. + MCINFO("Creating virtual display"); + mVirtualDisplay = android::SurfaceComposerClient::createDisplay( + /* const String8& displayName */ android::String8("minicap"), + /* bool secure */ true + ); + + MCINFO("Creating buffer queue"); + android::BufferQueue::createBufferQueue(&mBufferProducer, &mBufferConsumer); + mBufferConsumer->setDefaultBufferSize(targetWidth, targetHeight); + mBufferConsumer->setDefaultBufferFormat(android::PIXEL_FORMAT_RGBA_8888); + + MCINFO("Creating CPU consumer"); + mConsumer = new android::CpuConsumer(mBufferConsumer, 3, false); + mConsumer->setName(android::String8("minicap")); + + MCINFO("Creating frame waiter"); + mFrameProxy = new FrameProxy(mUserFrameAvailableListener); + mConsumer->setFrameAvailableListener(mFrameProxy); + + MCINFO("Publishing virtual display"); + android::SurfaceComposerClient::openGlobalTransaction(); + android::SurfaceComposerClient::setDisplaySurface(mVirtualDisplay, mBufferProducer); + android::SurfaceComposerClient::setDisplayProjection(mVirtualDisplay, + android::DISPLAY_ORIENTATION_0, layerStackRect, visibleRect); + android::SurfaceComposerClient::setDisplayLayerStack(mVirtualDisplay, 0); // default stack + android::SurfaceComposerClient::closeGlobalTransaction(); + + mHaveRunningDisplay = true; + + return 0; + } + + void + destroyVirtualDisplay() { + MCINFO("Destroying virtual display"); + android::SurfaceComposerClient::destroyDisplay(mVirtualDisplay); + + if (mHaveBuffer) { + mConsumer->unlockBuffer(mBuffer); + mHaveBuffer = false; + } + + mBufferProducer = NULL; + mBufferConsumer = NULL; + mConsumer = NULL; + mFrameProxy = NULL; + mVirtualDisplay = NULL; + + mHaveRunningDisplay = false; + } + + static Minicap::Format + convertFormat(android::PixelFormat format) { + switch (format) { + case android::PIXEL_FORMAT_NONE: + return FORMAT_NONE; + case android::PIXEL_FORMAT_CUSTOM: + return FORMAT_CUSTOM; + case android::PIXEL_FORMAT_TRANSLUCENT: + return FORMAT_TRANSLUCENT; + case android::PIXEL_FORMAT_TRANSPARENT: + return FORMAT_TRANSPARENT; + case android::PIXEL_FORMAT_OPAQUE: + return FORMAT_OPAQUE; + case android::PIXEL_FORMAT_RGBA_8888: + return FORMAT_RGBA_8888; + case android::PIXEL_FORMAT_RGBX_8888: + return FORMAT_RGBX_8888; + case android::PIXEL_FORMAT_RGB_888: + return FORMAT_RGB_888; + case android::PIXEL_FORMAT_RGB_565: + return FORMAT_RGB_565; + case android::PIXEL_FORMAT_BGRA_8888: + return FORMAT_BGRA_8888; + case android::PIXEL_FORMAT_RGBA_5551: + return FORMAT_RGBA_5551; + case android::PIXEL_FORMAT_RGBA_4444: + return FORMAT_RGBA_4444; + default: + return FORMAT_UNKNOWN; + } + } +}; + +int +minicap_try_get_display_info(int32_t displayId, Minicap::DisplayInfo* info) { + android::sp dpy = android::SurfaceComposerClient::getBuiltInDisplay(displayId); + + android::DisplayInfo dinfo; + android::status_t err = android::SurfaceComposerClient::getDisplayInfo(dpy, &dinfo); + + if (err != android::NO_ERROR) { + MCERROR("SurfaceComposerClient::getDisplayInfo() failed: %s (%d)\n", error_name(err), err); + return err; + } + + info->width = dinfo.w; + info->height = dinfo.h; + info->orientation = dinfo.orientation; + info->fps = dinfo.fps; + info->density = dinfo.density; + info->xdpi = dinfo.xdpi; + info->ydpi = dinfo.ydpi; + info->secure = dinfo.secure; + info->size = sqrt(pow(dinfo.w / dinfo.xdpi, 2) + pow(dinfo.h / dinfo.ydpi, 2)); + + return 0; +} + +Minicap* +minicap_create(int32_t displayId) { + return new MinicapImpl(displayId); +} + +void +minicap_free(Minicap* mc) { + delete mc; +} + +void +minicap_start_thread_pool() { + android::ProcessState::self()->startThreadPool(); +} diff --git a/run.sh b/run.sh index 4184e42d..63c1926f 100755 --- a/run.sh +++ b/run.sh @@ -34,7 +34,8 @@ fi # Create a directory for our resources dir=/data/local/tmp/minicap-devel -adb shell "mkdir $dir 2>/dev/null" +# Keep compatible with older devices that don't have `mkdir -p`. +adb shell "mkdir $dir 2>/dev/null || true" # Upload the binary adb push libs/$abi/$bin $dir