├── .gitignore ├── .gitmodules ├── .npmignore ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── LICENSE ├── Makefile ├── README.md ├── build-remote.sh ├── example ├── .gitignore ├── README.md ├── app.js ├── package.json └── public │ └── index.html ├── gdb.sh ├── jni ├── Android.mk ├── Application.mk ├── minicap-shared │ ├── Android.mk │ ├── README.md │ ├── aosp │ │ ├── .rsync-filter │ │ ├── Android.mk │ │ ├── Makefile │ │ ├── include │ │ │ ├── Minicap.hpp │ │ │ └── mcdebug.h │ │ ├── libs │ │ │ ├── android-10 │ │ │ │ └── armeabi-v7a │ │ │ │ │ └── minicap.so │ │ │ ├── android-14 │ │ │ │ ├── armeabi-v7a │ │ │ │ │ └── minicap.so │ │ │ │ └── x86 │ │ │ │ │ └── minicap.so │ │ │ ├── android-15 │ │ │ │ ├── armeabi-v7a │ │ │ │ │ └── minicap.so │ │ │ │ └── x86 │ │ │ │ │ └── minicap.so │ │ │ ├── android-16 │ │ │ │ ├── armeabi-v7a │ │ │ │ │ └── minicap.so │ │ │ │ └── x86 │ │ │ │ │ └── minicap.so │ │ │ ├── android-17 │ │ │ │ ├── armeabi-v7a │ │ │ │ │ └── minicap.so │ │ │ │ └── x86 │ │ │ │ │ └── minicap.so │ │ │ ├── android-18 │ │ │ │ ├── armeabi-v7a │ │ │ │ │ └── minicap.so │ │ │ │ └── x86 │ │ │ │ │ └── minicap.so │ │ │ ├── android-19 │ │ │ │ ├── armeabi-v7a │ │ │ │ │ └── minicap.so │ │ │ │ └── x86 │ │ │ │ │ └── minicap.so │ │ │ ├── android-21 │ │ │ │ ├── arm64-v8a │ │ │ │ │ └── minicap.so │ │ │ │ ├── armeabi-v7a │ │ │ │ │ └── minicap.so │ │ │ │ ├── x86 │ │ │ │ │ └── minicap.so │ │ │ │ └── x86_64 │ │ │ │ │ └── minicap.so │ │ │ ├── android-22 │ │ │ │ ├── arm64-v8a │ │ │ │ │ └── minicap.so │ │ │ │ ├── armeabi-v7a │ │ │ │ │ └── minicap.so │ │ │ │ ├── x86 │ │ │ │ │ └── minicap.so │ │ │ │ └── x86_64 │ │ │ │ │ └── minicap.so │ │ │ ├── android-23 │ │ │ │ ├── arm64-v8a │ │ │ │ │ └── minicap.so │ │ │ │ ├── armeabi-v7a │ │ │ │ │ └── minicap.so │ │ │ │ ├── x86 │ │ │ │ │ └── minicap.so │ │ │ │ └── x86_64 │ │ │ │ │ └── minicap.so │ │ │ ├── android-24 │ │ │ │ ├── arm64-v8a │ │ │ │ │ └── minicap.so │ │ │ │ ├── armeabi-v7a │ │ │ │ │ └── minicap.so │ │ │ │ ├── x86 │ │ │ │ │ └── minicap.so │ │ │ │ └── x86_64 │ │ │ │ │ └── minicap.so │ │ │ ├── android-25 │ │ │ │ ├── arm64-v8a │ │ │ │ │ └── minicap.so │ │ │ │ ├── armeabi-v7a │ │ │ │ │ └── minicap.so │ │ │ │ ├── x86 │ │ │ │ │ └── minicap.so │ │ │ │ └── x86_64 │ │ │ │ │ └── minicap.so │ │ │ ├── android-26 │ │ │ │ ├── arm64-v8a │ │ │ │ │ └── minicap.so │ │ │ │ ├── armeabi-v7a │ │ │ │ │ └── minicap.so │ │ │ │ ├── x86 │ │ │ │ │ └── minicap.so │ │ │ │ └── x86_64 │ │ │ │ │ └── minicap.so │ │ │ ├── android-27 │ │ │ │ ├── arm64-v8a │ │ │ │ │ └── minicap.so │ │ │ │ ├── armeabi-v7a │ │ │ │ │ └── minicap.so │ │ │ │ ├── x86 │ │ │ │ │ └── minicap.so │ │ │ │ └── x86_64 │ │ │ │ │ └── minicap.so │ │ │ ├── android-28 │ │ │ │ ├── arm64-v8a │ │ │ │ │ └── minicap.so │ │ │ │ ├── armeabi-v7a │ │ │ │ │ └── minicap.so │ │ │ │ ├── x86 │ │ │ │ │ └── minicap.so │ │ │ │ └── x86_64 │ │ │ │ │ └── minicap.so │ │ │ ├── android-29 │ │ │ │ ├── arm64-v8a │ │ │ │ │ └── minicap.so │ │ │ │ ├── armeabi-v7a │ │ │ │ │ └── minicap.so │ │ │ │ ├── x86 │ │ │ │ │ └── minicap.so │ │ │ │ └── x86_64 │ │ │ │ │ └── minicap.so │ │ │ └── android-9 │ │ │ │ └── armeabi-v7a │ │ │ │ └── minicap.so │ │ └── src │ │ │ ├── minicap_14.cpp │ │ │ ├── minicap_16.cpp │ │ │ ├── minicap_17.cpp │ │ │ ├── minicap_18.cpp │ │ │ ├── minicap_19.cpp │ │ │ ├── minicap_21.cpp │ │ │ ├── minicap_22.cpp │ │ │ ├── minicap_23.cpp │ │ │ ├── minicap_24.cpp │ │ │ ├── minicap_25.cpp │ │ │ ├── minicap_26.cpp │ │ │ ├── minicap_27.cpp │ │ │ ├── minicap_28.cpp │ │ │ ├── minicap_29.cpp │ │ │ └── minicap_9.cpp │ └── mock │ │ └── Minicap.cpp ├── minicap │ ├── Android.mk │ ├── JpgEncoder.cpp │ ├── JpgEncoder.hpp │ ├── Projection.hpp │ ├── SimpleServer.cpp │ ├── SimpleServer.hpp │ ├── minicap.cpp │ └── util │ │ ├── debug.h │ │ └── formatter.hpp └── vendor │ └── Android.mk ├── package-lock.json ├── package.json └── run.sh /.gitignore: -------------------------------------------------------------------------------- 1 | /*.tgz 2 | /.env 3 | /gdb.setup 4 | /libs/ 5 | /libs/ 6 | /obj/ 7 | /prebuilt/ 8 | /temp/ 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libjpeg-turbo"] 2 | path = jni/vendor/libjpeg-turbo 3 | url = https://github.com/openstf/android-libjpeg-turbo.git 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /.env 2 | /.gitmodules 3 | /.npmignore 4 | /*.tgz 5 | /build-remote.sh 6 | /CONTRIBUTING.md 7 | /example/ 8 | /gdb.setup 9 | /gdb.sh 10 | /jni/ 11 | /libs/ 12 | /Makefile 13 | /obj/ 14 | /run.sh 15 | /temp/ 16 | /yarn-error.log 17 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We are happy to accept any contributions that make sense and respect the rules listed below. 4 | 5 | ## How to contribute 6 | 7 | 1. Fork the repo. 8 | 2. Create a feature branch for your contribution out of the `master` branch. Only one contribution per branch is accepted. 9 | 3. Implement your contribution while respecting our rules (see below). 10 | 4. Make sure that your contribution builds and all necessary files have been committed. 11 | 5. Submit a pull request against our `master` branch! 12 | 13 | ## Rules 14 | 15 | * **Do** use feature branches. 16 | * **Do** conform to existing coding style so that your contribution fits in. 17 | * **Do** use [EditorConfig](http://editorconfig.org/) to enforce our [whitespace rules](.editorconfig). If your editor is not supported, enforce the settings manually. 18 | * **Do not** commit any generated files, unless already in the repo. If absolutely necessary, explain why. 19 | * **Do not** create any top level files or directories. If absolutely necessary, explain why and update [.gitignore](.gitignore) if appropriate. 20 | 21 | ## License 22 | 23 | By contributing your code, you agree to license your contribution under our [LICENSE](LICENSE). 24 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **What is the issue or idea you have?** 2 | 3 | **Have you tried STF?** 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2013 CyberAgent, Inc. 2 | Copyright © 2016 The OpenSTF Project 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: default clean prebuilt 2 | 3 | NDKBUILT := \ 4 | libs/arm64-v8a/minicap \ 5 | libs/arm64-v8a/minicap-nopie \ 6 | libs/armeabi-v7a/minicap \ 7 | libs/armeabi-v7a/minicap-nopie \ 8 | libs/x86/minicap \ 9 | libs/x86/minicap-nopie \ 10 | libs/x86_64/minicap \ 11 | libs/x86_64/minicap-nopie \ 12 | 13 | default: prebuilt 14 | 15 | clean: 16 | ndk-build clean 17 | rm -rf prebuilt 18 | 19 | $(NDKBUILT): 20 | ndk-build 21 | 22 | # It may feel a bit redundant to list everything here. However it also 23 | # acts as a safeguard to make sure that we really are including everything 24 | # that is supposed to be there. 25 | prebuilt: \ 26 | prebuilt/armeabi-v7a/bin/minicap \ 27 | prebuilt/armeabi-v7a/bin/minicap-nopie \ 28 | prebuilt/armeabi-v7a/lib/android-9/minicap.so \ 29 | prebuilt/armeabi-v7a/lib/android-10/minicap.so \ 30 | prebuilt/armeabi-v7a/lib/android-14/minicap.so \ 31 | prebuilt/armeabi-v7a/lib/android-15/minicap.so \ 32 | prebuilt/armeabi-v7a/lib/android-16/minicap.so \ 33 | prebuilt/armeabi-v7a/lib/android-17/minicap.so \ 34 | prebuilt/armeabi-v7a/lib/android-18/minicap.so \ 35 | prebuilt/armeabi-v7a/lib/android-19/minicap.so \ 36 | prebuilt/armeabi-v7a/lib/android-21/minicap.so \ 37 | prebuilt/armeabi-v7a/lib/android-22/minicap.so \ 38 | prebuilt/armeabi-v7a/lib/android-23/minicap.so \ 39 | prebuilt/armeabi-v7a/lib/android-24/minicap.so \ 40 | prebuilt/armeabi-v7a/lib/android-25/minicap.so \ 41 | prebuilt/armeabi-v7a/lib/android-26/minicap.so \ 42 | prebuilt/armeabi-v7a/lib/android-27/minicap.so \ 43 | prebuilt/armeabi-v7a/lib/android-28/minicap.so \ 44 | prebuilt/armeabi-v7a/lib/android-29/minicap.so \ 45 | prebuilt/arm64-v8a/bin/minicap \ 46 | prebuilt/arm64-v8a/bin/minicap-nopie \ 47 | prebuilt/arm64-v8a/lib/android-21/minicap.so \ 48 | prebuilt/arm64-v8a/lib/android-22/minicap.so \ 49 | prebuilt/arm64-v8a/lib/android-23/minicap.so \ 50 | prebuilt/arm64-v8a/lib/android-24/minicap.so \ 51 | prebuilt/arm64-v8a/lib/android-25/minicap.so \ 52 | prebuilt/arm64-v8a/lib/android-26/minicap.so \ 53 | prebuilt/arm64-v8a/lib/android-27/minicap.so \ 54 | prebuilt/arm64-v8a/lib/android-28/minicap.so \ 55 | prebuilt/arm64-v8a/lib/android-29/minicap.so \ 56 | prebuilt/x86/bin/minicap \ 57 | prebuilt/x86/bin/minicap-nopie \ 58 | prebuilt/x86/lib/android-14/minicap.so \ 59 | prebuilt/x86/lib/android-15/minicap.so \ 60 | prebuilt/x86/lib/android-16/minicap.so \ 61 | prebuilt/x86/lib/android-17/minicap.so \ 62 | prebuilt/x86/lib/android-18/minicap.so \ 63 | prebuilt/x86/lib/android-19/minicap.so \ 64 | prebuilt/x86/lib/android-21/minicap.so \ 65 | prebuilt/x86/lib/android-22/minicap.so \ 66 | prebuilt/x86/lib/android-23/minicap.so \ 67 | prebuilt/x86/lib/android-24/minicap.so \ 68 | prebuilt/x86/lib/android-25/minicap.so \ 69 | prebuilt/x86/lib/android-26/minicap.so \ 70 | prebuilt/x86/lib/android-27/minicap.so \ 71 | prebuilt/x86/lib/android-28/minicap.so \ 72 | prebuilt/x86/lib/android-29/minicap.so \ 73 | prebuilt/x86_64/bin/minicap \ 74 | prebuilt/x86_64/bin/minicap-nopie \ 75 | prebuilt/x86_64/lib/android-21/minicap.so \ 76 | prebuilt/x86_64/lib/android-22/minicap.so \ 77 | prebuilt/x86_64/lib/android-23/minicap.so \ 78 | prebuilt/x86_64/lib/android-24/minicap.so \ 79 | prebuilt/x86_64/lib/android-25/minicap.so \ 80 | prebuilt/x86_64/lib/android-26/minicap.so \ 81 | prebuilt/x86_64/lib/android-27/minicap.so \ 82 | prebuilt/x86_64/lib/android-28/minicap.so \ 83 | prebuilt/x86_64/lib/android-29/minicap.so \ 84 | 85 | prebuilt/%/bin/minicap: libs/%/minicap 86 | mkdir -p $(@D) 87 | cp $^ $@ 88 | 89 | prebuilt/%/bin/minicap-nopie: libs/%/minicap-nopie 90 | mkdir -p $(@D) 91 | cp $^ $@ 92 | 93 | prebuilt/armeabi-v7a/lib/%/minicap.so: jni/minicap-shared/aosp/libs/%/armeabi-v7a/minicap.so 94 | mkdir -p $(@D) 95 | cp $^ $@ 96 | 97 | prebuilt/arm64-v8a/lib/%/minicap.so: jni/minicap-shared/aosp/libs/%/arm64-v8a/minicap.so 98 | mkdir -p $(@D) 99 | cp $^ $@ 100 | 101 | prebuilt/x86/lib/%/minicap.so: jni/minicap-shared/aosp/libs/%/x86/minicap.so 102 | mkdir -p $(@D) 103 | cp $^ $@ 104 | 105 | prebuilt/x86_64/lib/%/minicap.so: jni/minicap-shared/aosp/libs/%/x86_64/minicap.so 106 | mkdir -p $(@D) 107 | cp $^ $@ 108 | -------------------------------------------------------------------------------- /build-remote.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -xueo pipefail 4 | 5 | builder=$1 6 | 7 | rsync \ 8 | --recursive \ 9 | --copy-links \ 10 | --perms \ 11 | --times \ 12 | -FF ./jni/minicap-shared/aosp/ "$builder":minicap/ 13 | 14 | ssh -T "$builder" "cd minicap && make -j 1" 15 | 16 | rsync \ 17 | --recursive \ 18 | --copy-links \ 19 | --perms \ 20 | --times \ 21 | "$builder":minicap/libs/ ./jni/minicap-shared/aosp/libs/ 22 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Example: minicap over WebSockets 2 | 3 | A quick and dirty example to show how minicap might be used as part of an application. Also useful for testing. 4 | 5 | ## Requirements 6 | 7 | * [Node.js](https://nodejs.org/) >= 0.12 (for this example only) 8 | * [ADB](http://developer.android.com/intl/ja/tools/help/adb.html) 9 | * An Android device with USB debugging enabled. 10 | 11 | ## Running 12 | 13 | 1. Check that your device is connected and ADB is running with `adb devices`. The following steps may not work properly if you don't. 14 | ``` 15 | adb devices 16 | ``` 17 | 2. Set up a forward for the server we'll soon have running inside the device. Note that due to laziness the port is currently fixed to 1717. 18 | ``` 19 | adb forward tcp:1717 localabstract:minicap 20 | ``` 21 | 3. Get information about your display. Unfortunately the easy API methods we could use for automatic detection segfault on some Samsung devices, presumably due to maker customizations. You'll need to know the display width and height in pixels. Here are some ways to do it: 22 | ``` 23 | adb shell wm size 24 | adb shell dumpsys display 25 | ``` 26 | 4. Start the minicap server. The most convenient way is to use the helper script at the root of this repo. 27 | ``` 28 | # Try ./run.sh -h for help 29 | ./run.sh -P 720x1280@720x1280/0 30 | ``` 31 | The first set is the true size of your display, and the second set is the size of the desired projection. Larger projections require more processing power and bandwidth. The final argument is the rotation of the display. Note that this is not the rotation you want it to have, it simply specifies the display's current rotation, which is used to normalize the output frames between Android versions. If the rotation changes you have to restart the server. 32 | 5. Start the example app. 33 | ``` 34 | PORT=9002 node app.js 35 | ``` 36 | 6. Open http://localhost:9002 in your browser. 37 | -------------------------------------------------------------------------------- /example/app.js: -------------------------------------------------------------------------------- 1 | var WebSocketServer = require('ws').Server 2 | , http = require('http') 3 | , express = require('express') 4 | , path = require('path') 5 | , net = require('net') 6 | , app = express() 7 | 8 | var PORT = process.env.PORT || 9002 9 | 10 | app.use(express.static(path.join(__dirname, '/public'))) 11 | 12 | var server = http.createServer(app) 13 | var wss = new WebSocketServer({ server: server }) 14 | 15 | wss.on('connection', function(ws) { 16 | console.info('Got a client') 17 | 18 | var stream = net.connect({ 19 | port: 1717 20 | }) 21 | 22 | stream.on('error', function() { 23 | console.error('Be sure to run `adb forward tcp:1717 localabstract:minicap`') 24 | process.exit(1) 25 | }) 26 | 27 | var readBannerBytes = 0 28 | var bannerLength = 2 29 | var readFrameBytes = 0 30 | var frameBodyLength = 0 31 | var frameBody = new Buffer(0) 32 | var banner = { 33 | version: 0 34 | , length: 0 35 | , pid: 0 36 | , realWidth: 0 37 | , realHeight: 0 38 | , virtualWidth: 0 39 | , virtualHeight: 0 40 | , orientation: 0 41 | , quirks: 0 42 | } 43 | 44 | function tryRead() { 45 | for (var chunk; (chunk = stream.read());) { 46 | console.info('chunk(length=%d)', chunk.length) 47 | for (var cursor = 0, len = chunk.length; cursor < len;) { 48 | if (readBannerBytes < bannerLength) { 49 | switch (readBannerBytes) { 50 | case 0: 51 | // version 52 | banner.version = chunk[cursor] 53 | break 54 | case 1: 55 | // length 56 | banner.length = bannerLength = chunk[cursor] 57 | break 58 | case 2: 59 | case 3: 60 | case 4: 61 | case 5: 62 | // pid 63 | banner.pid += 64 | (chunk[cursor] << ((readBannerBytes - 2) * 8)) >>> 0 65 | break 66 | case 6: 67 | case 7: 68 | case 8: 69 | case 9: 70 | // real width 71 | banner.realWidth += 72 | (chunk[cursor] << ((readBannerBytes - 6) * 8)) >>> 0 73 | break 74 | case 10: 75 | case 11: 76 | case 12: 77 | case 13: 78 | // real height 79 | banner.realHeight += 80 | (chunk[cursor] << ((readBannerBytes - 10) * 8)) >>> 0 81 | break 82 | case 14: 83 | case 15: 84 | case 16: 85 | case 17: 86 | // virtual width 87 | banner.virtualWidth += 88 | (chunk[cursor] << ((readBannerBytes - 14) * 8)) >>> 0 89 | break 90 | case 18: 91 | case 19: 92 | case 20: 93 | case 21: 94 | // virtual height 95 | banner.virtualHeight += 96 | (chunk[cursor] << ((readBannerBytes - 18) * 8)) >>> 0 97 | break 98 | case 22: 99 | // orientation 100 | banner.orientation += chunk[cursor] * 90 101 | break 102 | case 23: 103 | // quirks 104 | banner.quirks = chunk[cursor] 105 | break 106 | } 107 | 108 | cursor += 1 109 | readBannerBytes += 1 110 | 111 | if (readBannerBytes === bannerLength) { 112 | console.log('banner', banner) 113 | } 114 | } 115 | else if (readFrameBytes < 4) { 116 | frameBodyLength += (chunk[cursor] << (readFrameBytes * 8)) >>> 0 117 | cursor += 1 118 | readFrameBytes += 1 119 | console.info('headerbyte%d(val=%d)', readFrameBytes, frameBodyLength) 120 | } 121 | else { 122 | if (len - cursor >= frameBodyLength) { 123 | console.info('bodyfin(len=%d,cursor=%d)', frameBodyLength, cursor) 124 | 125 | frameBody = Buffer.concat([ 126 | frameBody 127 | , chunk.slice(cursor, cursor + frameBodyLength) 128 | ]) 129 | 130 | // Sanity check for JPG header, only here for debugging purposes. 131 | if (frameBody[0] !== 0xFF || frameBody[1] !== 0xD8) { 132 | console.error( 133 | 'Frame body does not start with JPG header', frameBody) 134 | process.exit(1) 135 | } 136 | 137 | ws.send(frameBody, { 138 | binary: true 139 | }) 140 | 141 | cursor += frameBodyLength 142 | frameBodyLength = readFrameBytes = 0 143 | frameBody = new Buffer(0) 144 | } 145 | else { 146 | console.info('body(len=%d)', len - cursor) 147 | 148 | frameBody = Buffer.concat([ 149 | frameBody 150 | , chunk.slice(cursor, len) 151 | ]) 152 | 153 | frameBodyLength -= len - cursor 154 | readFrameBytes += len - cursor 155 | cursor = len 156 | } 157 | } 158 | } 159 | } 160 | } 161 | 162 | stream.on('readable', tryRead) 163 | 164 | ws.on('close', function() { 165 | console.info('Lost a client') 166 | stream.end() 167 | }) 168 | }) 169 | 170 | server.listen(PORT) 171 | console.info('Listening on port %d', PORT) 172 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "express": "^4.12.3", 4 | "ws": "^0.7.1" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 49 | -------------------------------------------------------------------------------- /gdb.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Fail on error, verbose output 4 | set -exo pipefail 5 | 6 | # Check NDK setup 7 | if [ -z "$NDK_ROOT" ]; then 8 | echo 'Environment variable $NDK_ROOT required for operation' >&2 9 | exit 1 10 | fi 11 | 12 | # Figure out which ABIs the device supports 13 | abi=$(adb shell getprop ro.product.cpu.abi | tr -d '\r') 14 | sdk=$(adb shell getprop ro.build.version.sdk | tr -d '\r') 15 | 16 | # PIE is only supported since SDK 16 17 | if (($sdk >= 16)); then 18 | bin=minicap 19 | else 20 | bin=minicap-nopie 21 | fi 22 | 23 | # Check if we're debuggable 24 | gdb_setup=$(make --no-print-dir -f "$NDK_ROOT/build/core/build-local.mk" -C "$PWD" DUMP_NDK_APP_GDBSETUP APP_ABI=$abi) 25 | if [ ! -f "$gdb_setup" ]; then 26 | echo "Unable to find $gdb_setup, rebuild with 'ndk-build NDK_DEBUG=1'" 27 | exit 4 28 | fi 29 | 30 | # Get output directory 31 | out=$(make --no-print-dir -f "$NDK_ROOT/build/core/build-local.mk" -C "$PWD" DUMP_TARGET_OUT APP_ABI=$abi) 32 | 33 | # ABI-specific config 34 | prebabi=$abi 35 | libpath=lib 36 | case $abi in 37 | "armeabi" | "armeabi-v7a") 38 | prebabi=arm 39 | ;; 40 | "arm64-v8a") 41 | libpath=lib64 42 | prebabi=arm64 43 | ;; 44 | "x86_64") 45 | libpath=lib64 46 | ;; 47 | "mips64") 48 | libpath=lib64 49 | ;; 50 | esac 51 | 52 | # Validate prebuilt mapping 53 | if [ ! -e "$NDK_ROOT/prebuilt/android-$prebabi" ]; then 54 | echo "Unable to find prebuilts in $NDK_ROOT/prebuilt/android-$prebabi; incorrect mapping?" >&2 55 | exit 3 56 | fi 57 | 58 | # Find toolchain 59 | toolchain=$(make --no-print-dir -f "$NDK_ROOT/build/core/build-local.mk" -C "$PWD" DUMP_TOOLCHAIN_PREFIX APP_ABI=$abi) 60 | gdbclient="${toolchain}gdb" 61 | gdbserver="$NDK_ROOT/prebuilt/android-$prebabi/gdbserver" 62 | 63 | # Create a directory for our resources 64 | dir=/data/local/tmp/minicap-gdb 65 | adb shell "mkdir $dir 2>/dev/null" 66 | 67 | # Find pid 68 | pid=$(adb shell ps | tr -d '\r' | awk '$NF ~ /\/minicap$/ {print $2}' | tail -n1) 69 | if [ -z "$pid" ]; then 70 | echo 'Unable to find minicap pid' >&2 71 | exit 2 72 | fi 73 | 74 | # Upload gdbserver 75 | adb push "$gdbserver" $dir 76 | 77 | # Pull libs 78 | adb pull /system/bin/linker $out/linker 79 | adb pull /system/$libpath $out 80 | 81 | # Launch gdbserver 82 | adb shell $dir/gdbserver :5039 --attach $pid & 83 | gdbserver_pid=$! 84 | trap "kill $gdbserver_pid 2>/dev/null" EXIT 85 | 86 | # Set up the forward 87 | adb forward tcp:5039 tcp:5039 88 | 89 | # Find initial gdb.setup 90 | cp -f "$gdb_setup" gdb.setup 91 | 92 | # Augment gdb.setup 93 | cat <>gdb.setup 94 | file libs/$abi/$bin 95 | target remote :5039 96 | EOT 97 | 98 | # Launch gdb 99 | "$gdbclient" -x gdb.setup 100 | 101 | # Clean up 102 | adb shell rm -r $dir 103 | -------------------------------------------------------------------------------- /jni/Android.mk: -------------------------------------------------------------------------------- 1 | include $(call all-subdir-makefiles) 2 | -------------------------------------------------------------------------------- /jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := armeabi-v7a arm64-v8a x86 x86_64 2 | 3 | # Get C++11 working 4 | APP_CPPFLAGS += -std=c++11 -fexceptions 5 | APP_STL := c++_static 6 | 7 | # Disable PIE for SDK <16 support. Enable manually for >=5.0 8 | # where necessary. 9 | APP_PIE := false 10 | 11 | APP_CFLAGS += \ 12 | -Ofast \ 13 | -funroll-loops \ 14 | -fno-strict-aliasing \ 15 | 16 | ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) 17 | # http://community.arm.com/groups/tools/blog/2013/04/15/arm-cortex-a-processors-and-gcc-command-lines 18 | APP_CFLAGS += \ 19 | -march=armv7-a \ 20 | -mfpu=neon \ 21 | -mfloat-abi=softfp \ 22 | -marm \ 23 | -fprefetch-loop-arrays \ 24 | -DHAVE_NEON=1 \ 25 | 26 | else ifeq ($(TARGET_ARCH_ABI),armeabi-v7a-hard) 27 | # http://community.arm.com/groups/tools/blog/2013/04/15/arm-cortex-a-processors-and-gcc-command-lines 28 | APP_CFLAGS += \ 29 | -march=armv7-a \ 30 | -mfpu=neon \ 31 | -mfloat-abi=hard \ 32 | -marm \ 33 | -fprefetch-loop-arrays \ 34 | -DHAVE_NEON=1 \ 35 | -mhard-float \ 36 | -D_NDK_MATH_NO_SOFTFP=1 \ 37 | 38 | APP_LDFLAGS += -lm_hard 39 | 40 | else ifeq ($(TARGET_ARCH_ABI),arm64-v8a) 41 | # http://community.arm.com/groups/tools/blog/2013/04/15/arm-cortex-a-processors-and-gcc-command-lines 42 | APP_CFLAGS += \ 43 | -mcpu=cortex-a15 \ 44 | -mfpu=neon-vfpv4 \ 45 | -mfloat-abi=softfp \ 46 | -DHAVE_NEON=1 \ 47 | 48 | else ifeq ($(TARGET_ARCH_ABI),x86) 49 | # From Android on x86: An Introduction to Optimizing for Intel® Architecture 50 | # Chapter 12 51 | APP_CFLAGS += \ 52 | -fprefetch-loop-arrays \ 53 | -fno-short-enums \ 54 | -finline-limit=300 \ 55 | -fomit-frame-pointer \ 56 | -mssse3 \ 57 | -mfpmath=sse \ 58 | -masm=intel \ 59 | -DHAVE_NEON=1 \ 60 | 61 | endif 62 | -------------------------------------------------------------------------------- /jni/minicap-shared/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(abspath $(call my-dir)) 2 | include $(CLEAR_VARS) 3 | 4 | LOCAL_MODULE := minicap-shared 5 | 6 | LOCAL_MODULE_FILENAME := minicap 7 | 8 | LOCAL_SRC_FILES := \ 9 | mock/Minicap.cpp \ 10 | 11 | LOCAL_C_INCLUDES := \ 12 | $(LOCAL_PATH)/aosp/include \ 13 | 14 | LOCAL_EXPORT_C_INCLUDES := \ 15 | $(LOCAL_PATH)/aosp/include \ 16 | 17 | include $(BUILD_SHARED_LIBRARY) 18 | -------------------------------------------------------------------------------- /jni/minicap-shared/README.md: -------------------------------------------------------------------------------- 1 | # minicap-shared 2 | 3 | This module provides the shared library used by minicap. Due to the use of private APIs, it must be built inside the AOSP source tree for each SDK level and architecture. We commit and ship prebuilt libraries inside the source tree for convenience purposes, as getting them compiled can be a major obstacle. The rest of this README assumes that you wish to compile the libraries by yourself, possibly due to trust issues and/or modifications. 4 | 5 | ## Requirements 6 | 7 | There are several ways to set everything up and build the libraries, so we'll just cover the way we've done it. You may adjust the process however you want, but don't expect us to hold your hand if something goes wrong. Overall getting everything set up takes a considerable amount of time, with moderate skill requirements as well. It would be best if you follow the guide unless you're very confident you can do it. 8 | 9 | ### Operating system 10 | 11 | Let's start by saying that **compiling under OS X will not work**. AOSP requires a case-sensitive file system. Furthermore, different branches of the AOSP source tree require different versions of Xcode and JDK, making the process nearly impossible. Try if you're crazy. 12 | 13 | What we use is Linux, latest stable CoreOS to be precise. Our process relies on Docker containers to do the actual compile. The source code is copied via rsync to the Linux machine and then compiled. Any artifacts are transmitted back and added to our source tree so that normal people will not have to go through the whole process. 14 | 15 | Using this setup, the minimum server-side machine requirements are: 16 | 17 | * [docker](https://www.docker.com/) 18 | * [SSH](http://www.openssh.com/) 19 | * A user account with SSH public key authentication and sudo-less access to docker 20 | * 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. 21 | 22 | You'll also need [rsync](https://rsync.samba.org/) and [SSH](http://www.openssh.com/) properly set up on your development machine. 23 | 24 | ### Docker images 25 | 26 | Pull the required docker images on the build server as follows: 27 | 28 | ```bash 29 | docker pull openstf/aosp:jdk6 30 | docker pull openstf/aosp:jdk7 31 | docker pull openstf/aosp:jdk8 32 | ``` 33 | 34 | These images will be used to both check out code as well as to compile it. 35 | 36 | **A note about security:** containers created from these images currently run under the root user by default. If this is not acceptable to you, you will need to modify the `Makefile` and most of the commands below as you see fit. 37 | 38 | ### AOSP branches 39 | 40 | Currently the following branches are required to build the libraries for all supported SDK levels and architectures: 41 | 42 | | Branch | SDK | Docker image to build with | 43 | |---------------------|-----|-------------------------------| 44 | | android-2.3_r1 | 9 | openstf/aosp:jdk6 | 45 | | android-2.3.3_r1 | 10 | openstf/aosp:jdk6 | 46 | | android-4.0.1_r1 | 14 | openstf/aosp:jdk6 | 47 | | android-4.0.3_r1 | 15 | openstf/aosp:jdk6 | 48 | | android-4.1.1_r1 | 16 | openstf/aosp:jdk6 | 49 | | android-4.2_r1 | 17 | openstf/aosp:jdk6 | 50 | | android-4.3_r1 | 18 | openstf/aosp:jdk6 | 51 | | android-4.4_r1 | 19 | openstf/aosp:jdk6 | 52 | | android-5.0.1_r1 | 21 | openstf/aosp:jdk7 | 53 | | android-5.1.0_r1 | 22 | openstf/aosp:jdk7 | 54 | | android-6.0.0_r1 | 23 | openstf/aosp:jdk7 | 55 | | android-7.0.0_r1 | 24 | openstf/aosp:jdk8 | 56 | | android-7.1.0_r1 | 25 | openstf/aosp:jdk8 | 57 | | android-8.0.0_r1 | 26 | openstf/aosp:jdk8 | 58 | | android-8.1.0_r1 | 27 | openstf/aosp:jdk8 | 59 | | android-9.0.0_r1 | 28 | openstf/aosp:jdk8 | 60 | 61 | Furthermore, to make use of our provided Makefile, you should check out the branches to `/media/aosp` for maximum ease of use. 62 | 63 | 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. 64 | 65 | You can also skip the mirror and download each branch directly, but that will stress the AOSP server unnecessarily. 66 | 67 | Our recommended solution is to use a mirror and use it for all the branches you can, and if a branch fails due to a missing repo, then checkout that branch directly from the server. 68 | 69 | ### AOSP authentication 70 | 71 | AOSP has fairly recently introduced heavy bandwidth caps on their repos. You may have to authenticate yourself [as explained](https://source.android.com/source/downloading.html#using-authentication) by the download guide. Use the link in the guide to get your `.gitcookies` file. 72 | 73 | You'll need to put the file on your build machine. It needs to have correct permissions (i.e. `0600`) and belong to the user checking out the code. Since our process uses the root user inside the containers, make sure to set the owner and group as well. 74 | 75 | ```bash 76 | chown root:root .gitcookies 77 | chmod 0600 .gitcookies 78 | ``` 79 | 80 | Use sudo where required. 81 | 82 | We will later mount this file on our containers when checking out code. Note that if you still run into bandwidth caps you may just have to wait. 83 | 84 | ### Get familiar with the checkout/build wrapper script 85 | 86 | There are bundled helper scripts inside the docker images. To see what commands you have available, run the following command. 87 | 88 | ```bash 89 | docker run -ti --rm openstf/aosp:jdk7 /aosp.sh help 90 | ``` 91 | 92 | ### Creating an AOSP mirror 93 | 94 | Now that we're a bit more familiar with the helper script, let's start fetching our mirror. 95 | 96 | ```bash 97 | docker run -ti --rm \ 98 | -v /media/aosp/mirror:/mirror \ 99 | -v $PWD/.gitcookies:/root/.gitcookies:ro \ 100 | openstf/aosp:jdk7 \ 101 | /aosp.sh create-mirror 102 | ``` 103 | 104 | 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. 105 | 106 | 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. 107 | 108 | You should rerun the command whenever a new branch you're interested in gets added to AOSP to sync the mirrored repos. 109 | 110 | ### Check out branches (using mirror) 111 | 112 | We had a table of the needed AOSP branches earlier. The docker image for each SDK level may be different, but it should not make much difference when simply checking out code, so you should be able to use one image to check out all branches. However, if you do run into problems, you may also wish to try using the proper JDK when checking out. Check out the branch table above to see which one to use for each branch. 113 | 114 | For each branch in the table, run the following command: 115 | 116 | ```bash 117 | docker run -ti --rm \ 118 | -v /media/aosp/mirror:/mirror \ 119 | -v /media/aosp/android-5.1.0_r1:/aosp \ 120 | -v $PWD/.gitcookies:/root/.gitcookies:ro \ 121 | openstf/aosp:jdk7 \ 122 | /aosp.sh checkout-branch android-5.1.0_r1 123 | ``` 124 | 125 | Note that the branch name is both in the mounted folder name and an option to the checkout-branch command. You have to replace both when checking out other branches. 126 | 127 | Checking out a single branch should not take that long, perhaps 10-20 minutes per branch. Use a faster disk for better results. 128 | 129 | Should an error occur, you can try running the command again. However, since we're using the mirror, errors shouldn't really happen. Like explained earlier, the mirror may be incomplete. If that's the case, you may have to try a direct download instead, explained next. 130 | 131 | ### Check out branches (using direct download) 132 | 133 | For each branch you wish to download directly from the AOSP servers, run the following command: 134 | 135 | ```bash 136 | docker run -ti --rm \ 137 | -v /media/aosp/android-5.1.0_r1:/aosp \ 138 | -v $PWD/.gitcookies:/root/.gitcookies:ro \ 139 | openstf/aosp:jdk7 \ 140 | /aosp.sh checkout-branch --no-mirror android-5.1.0_r1 141 | ``` 142 | 143 | Note that the branch name is both in the mounted folder name and an option to the checkout-branch command. You have to replace both when checking out other branches. 144 | 145 | Should an error occur, you can try running the command again. Depending on your luck you may have to run the command several times, and if you hit a bandwidth cap, distribute your downloads over several days. 146 | 147 | Checking out a single branch will easily take a few hours, but it is considerably faster than setting up a full mirror. 148 | 149 | ### Saving disk space (optional) 150 | 151 | If you're sure that you'll never need to use the Android `repo` tool on a branch you've already checked out, you can save ~40-50% of disk space by deleting the `.repo` folder inside each branch. However, be very careful not to delete the mirror's `.repo`, since you'll probably want to update it at some point in the future. 152 | 153 | ### Building 154 | 155 | There's a `Makefile` in the `aosp` folder containing a build command for each SDK level and architecture we're interested in. If you're developing on Linux directly you may be able to `make` the libraries directly. 156 | 157 | More likely, though, you'll actually want to use the `build-remote.sh` script at the root of the minicap repo. It's not very pretty but it works. It transmits the source code to the target machine and uses one of the docker images to run `make` inside a docker container (which is done because CoreOS doesn't come with make). 158 | 159 | First, you'll need to tell the script where you're building. 160 | 161 | ```bash 162 | export BUILD_HOST=core@stf-build001 163 | ``` 164 | 165 | This will be passed to SSH and rsync, and is able to benefit from your SSH config (which you must set up yourself as you like). 166 | 167 | Now, run the build script: 168 | 169 | ```bash 170 | ./build-remote.sh 171 | ``` 172 | 173 | The first compile will take a long, long time, since it needs to compile all dependencies on the first run, for each architecture. 174 | 175 | ### Rebuilding 176 | 177 | Any recompile after the first time will benefit from the already build dependencies and will go a lot faster, although it will still take a considerable amount of time. The `Makefile` attemps to be intelligent about which branches need to be rebuilt, but touching the common code base or the `Android.mk` file will usually require a recompile on each SDK and architecture. That by itself is reasonably fast, but setting up the AOSP build takes the most time anyway. It may take 5-15 minutes in total to do an "empty run" for each branch and architecture. 178 | 179 | Our advice? Be damn sure your code compiles before actually compiling :) 180 | 181 | 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. 182 | 183 | In any case, congratulations, you're done! 184 | 185 | ### Tips 186 | 187 | 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: 188 | 189 | ```sh 190 | mkfs.btrfs -L AOSP /dev/sdc1 191 | ``` 192 | 193 | And here's the corresponding unit: 194 | 195 | ```systemd 196 | [Mount] 197 | What=/dev/disk/by-label/AOSP 198 | Where=/media/aosp 199 | Type=btrfs 200 | 201 | [Install] 202 | WantedBy=multi-user.target 203 | ``` 204 | 205 | Now just run `systemctl enable media-aosp.mount && systemctl start media-aosp.mount`. 206 | -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/.rsync-filter: -------------------------------------------------------------------------------- 1 | - .git 2 | - libs 3 | -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | include $(CLEAR_VARS) 3 | 4 | LOCAL_MODULE := minicap 5 | 6 | LOCAL_MODULE_TAGS := optional 7 | 8 | ifneq ($(OVERRIDE_PLATFORM_SDK_VERSION),) 9 | LOCAL_SRC_FILES += src/minicap_$(OVERRIDE_PLATFORM_SDK_VERSION).cpp 10 | else ifeq ($(PLATFORM_SDK_VERSION),29) 11 | LOCAL_SRC_FILES += src/minicap_29.cpp 12 | else ifeq ($(PLATFORM_SDK_VERSION),28) 13 | LOCAL_SRC_FILES += src/minicap_28.cpp 14 | else ifeq ($(PLATFORM_SDK_VERSION),27) 15 | LOCAL_SRC_FILES += src/minicap_27.cpp 16 | else ifeq ($(PLATFORM_SDK_VERSION),26) 17 | LOCAL_SRC_FILES += src/minicap_26.cpp 18 | else ifeq ($(PLATFORM_SDK_VERSION),25) 19 | LOCAL_SRC_FILES += src/minicap_25.cpp 20 | else ifeq ($(PLATFORM_SDK_VERSION),24) 21 | LOCAL_SRC_FILES += src/minicap_24.cpp 22 | else ifeq ($(PLATFORM_SDK_VERSION),23) 23 | LOCAL_SRC_FILES += src/minicap_23.cpp 24 | else ifeq ($(PLATFORM_SDK_VERSION),22) 25 | LOCAL_SRC_FILES += src/minicap_22.cpp 26 | else ifeq ($(PLATFORM_SDK_VERSION),21) 27 | LOCAL_SRC_FILES += src/minicap_21.cpp 28 | else ifeq ($(PLATFORM_SDK_VERSION),19) 29 | LOCAL_SRC_FILES += src/minicap_19.cpp 30 | else ifeq ($(PLATFORM_SDK_VERSION),18) 31 | LOCAL_SRC_FILES += src/minicap_18.cpp 32 | else ifeq ($(PLATFORM_SDK_VERSION),17) 33 | LOCAL_SRC_FILES += src/minicap_17.cpp 34 | else ifeq ($(PLATFORM_SDK_VERSION),16) 35 | LOCAL_SRC_FILES += src/minicap_16.cpp 36 | else ifeq ($(PLATFORM_SDK_VERSION),15) 37 | LOCAL_SRC_FILES += src/minicap_14.cpp 38 | else ifeq ($(PLATFORM_SDK_VERSION),14) 39 | LOCAL_SRC_FILES += src/minicap_14.cpp 40 | else ifeq ($(PLATFORM_SDK_VERSION),10) 41 | LOCAL_SRC_FILES += src/minicap_9.cpp 42 | else ifeq ($(PLATFORM_SDK_VERSION),9) 43 | LOCAL_SRC_FILES += src/minicap_9.cpp 44 | endif 45 | 46 | LOCAL_PRELINK_MODULE := false 47 | 48 | LOCAL_SHARED_LIBRARIES := \ 49 | libcutils \ 50 | libutils \ 51 | libbinder \ 52 | libui \ 53 | liblog \ 54 | 55 | ifeq ($(PLATFORM_SDK_VERSION),$(filter $(PLATFORM_SDK_VERSION),10 9)) 56 | LOCAL_SHARED_LIBRARIES += libsurfaceflinger_client 57 | else 58 | LOCAL_SHARED_LIBRARIES += libgui 59 | endif 60 | 61 | LOCAL_C_INCLUDES += \ 62 | $(LOCAL_PATH)/include \ 63 | 64 | LOCAL_CFLAGS += -DPLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION) 65 | 66 | include $(BUILD_SHARED_LIBRARY) 67 | -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/include/Minicap.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MINICAP_HPP 2 | #define MINICAP_HPP 3 | 4 | #include 5 | 6 | class Minicap { 7 | public: 8 | enum CaptureMethod { 9 | METHOD_FRAMEBUFFER = 1, 10 | METHOD_SCREENSHOT = 2, 11 | METHOD_VIRTUAL_DISPLAY = 3, 12 | }; 13 | 14 | enum Format { 15 | FORMAT_NONE = 0x01, 16 | FORMAT_CUSTOM = 0x02, 17 | FORMAT_TRANSLUCENT = 0x03, 18 | FORMAT_TRANSPARENT = 0x04, 19 | FORMAT_OPAQUE = 0x05, 20 | FORMAT_RGBA_8888 = 0x06, 21 | FORMAT_RGBX_8888 = 0x07, 22 | FORMAT_RGB_888 = 0x08, 23 | FORMAT_RGB_565 = 0x09, 24 | FORMAT_BGRA_8888 = 0x0a, 25 | FORMAT_RGBA_5551 = 0x0b, 26 | FORMAT_RGBA_4444 = 0x0c, 27 | FORMAT_UNKNOWN = 0x00, 28 | }; 29 | 30 | enum Orientation { 31 | ORIENTATION_0 = 0, 32 | ORIENTATION_90 = 1, 33 | ORIENTATION_180 = 2, 34 | ORIENTATION_270 = 3, 35 | }; 36 | 37 | struct DisplayInfo { 38 | uint32_t width; 39 | uint32_t height; 40 | float fps; 41 | float density; 42 | float xdpi; 43 | float ydpi; 44 | float size; 45 | uint8_t orientation; 46 | bool secure; 47 | }; 48 | 49 | struct Frame { 50 | void const* data; 51 | Format format; 52 | uint32_t width; 53 | uint32_t height; 54 | uint32_t stride; 55 | uint32_t bpp; 56 | size_t size; 57 | }; 58 | 59 | struct FrameAvailableListener { 60 | virtual 61 | ~FrameAvailableListener() {} 62 | 63 | virtual void 64 | onFrameAvailable() = 0; 65 | }; 66 | 67 | Minicap() {} 68 | 69 | virtual 70 | ~Minicap() {} 71 | 72 | // Applies changes made by setDesiredInfo() and setRealInfo(). Must be 73 | // called before attempting to wait or consume frames. 74 | virtual int 75 | applyConfigChanges() = 0; 76 | 77 | // Consumes a frame. Must be called after waitForFrame(). 78 | virtual int 79 | consumePendingFrame(Frame* frame) = 0; 80 | 81 | // Peek behind the scenes to see which capture method is actually 82 | // being used. 83 | virtual CaptureMethod 84 | getCaptureMethod() = 0; 85 | 86 | // Get display ID. 87 | virtual int32_t 88 | getDisplayId() = 0; 89 | 90 | // Release all resources. 91 | virtual void 92 | release() = 0; 93 | 94 | // Releases a consumed frame so that it can be reused by Android again. 95 | // Must be called before consumePendingFrame() is called again. 96 | virtual void 97 | releaseConsumedFrame(Frame* frame) = 0; 98 | 99 | // Set desired information about the display. Currently, only the 100 | // following properties are actually used: width, height and orientation. 101 | // After the configuration has been applied, new frames should satisfy 102 | // the requirements. 103 | virtual int 104 | setDesiredInfo(const DisplayInfo& info) = 0; 105 | 106 | // Sets the frame available listener. 107 | virtual void 108 | setFrameAvailableListener(FrameAvailableListener* listener) = 0; 109 | 110 | // Set the display's real information. This cannot be accessed automatically 111 | // due to manufacturers (mainly Samsung) having customized 112 | // android::DisplayInfo. The information has to be gathered somehow and then 113 | // passed on here. Currently only the following properties are actually 114 | // used: width and height. 115 | virtual int 116 | setRealInfo(const DisplayInfo& info) = 0; 117 | }; 118 | 119 | // Attempt to get information about the given display. This may segfault 120 | // on some devices due to manufacturer (mainly Samsung) customizations. 121 | int 122 | minicap_try_get_display_info(int32_t displayId, Minicap::DisplayInfo* info); 123 | 124 | // Creates a new Minicap instance for the current platform. 125 | Minicap* 126 | minicap_create(int32_t displayId); 127 | 128 | // Frees a Minicap instance. Don't call delete yourself as it won't have 129 | // access to the platform-specific modifications. 130 | void 131 | minicap_free(Minicap* mc); 132 | 133 | // Starts an Android thread pool. Must be called before doing anything else. 134 | void 135 | minicap_start_thread_pool(); 136 | 137 | #endif 138 | -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/include/mcdebug.h: -------------------------------------------------------------------------------- 1 | #ifndef __minicap_dbg_h__ 2 | #define __minicap_dbg_h__ 3 | 4 | // These macros were originally from 5 | // http://c.learncodethehardway.org/book/ex20.html 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #ifdef NDEBUG 12 | #define MCDEBUG(M, ...) 13 | #else 14 | #define MCDEBUG(M, ...) fprintf(stderr, "DEBUG: %s:%d: " M "\n", __FILE__, __LINE__, ##__VA_ARGS__) 15 | #endif 16 | 17 | #define MCCLEAN_ERRNO() (errno == 0 ? "None" : strerror(errno)) 18 | 19 | #define MCERROR(M, ...) fprintf(stderr, "ERROR: (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, MCCLEAN_ERRNO(), ##__VA_ARGS__) 20 | 21 | #define MCWARN(M, ...) fprintf(stderr, "WARN: (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, MCCLEAN_ERRNO(), ##__VA_ARGS__) 22 | 23 | #define MCINFO(M, ...) fprintf(stderr, "INFO: (%s:%d) " M "\n", __FILE__, __LINE__, ##__VA_ARGS__) 24 | 25 | #define MCCHECK(A, M, ...) if(!(A)) { MCERROR(M, ##__VA_ARGS__); errno=0; goto error; } 26 | 27 | #define MCSENTINEL(M, ...) { MCERROR(M, ##__VA_ARGS__); errno=0; goto error; } 28 | 29 | #define MCCHECK_MEM(A) check((A), "Out of memory.") 30 | 31 | #define MCCHECK_DEBUG(A, M, ...) if(!(A)) { MCDEBUG(M, ##__VA_ARGS__); errno=0; goto error; } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-10/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-10/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-14/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-14/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-14/x86/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-14/x86/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-15/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-15/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-15/x86/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-15/x86/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-16/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-16/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-16/x86/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-16/x86/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-17/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-17/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-17/x86/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-17/x86/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-18/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-18/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-18/x86/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-18/x86/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-19/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-19/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-19/x86/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-19/x86/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-21/arm64-v8a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-21/arm64-v8a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-21/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-21/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-21/x86/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-21/x86/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-21/x86_64/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-21/x86_64/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-22/arm64-v8a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-22/arm64-v8a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-22/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-22/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-22/x86/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-22/x86/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-22/x86_64/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-22/x86_64/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-23/arm64-v8a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-23/arm64-v8a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-23/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-23/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-23/x86/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-23/x86/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-23/x86_64/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-23/x86_64/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-24/arm64-v8a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-24/arm64-v8a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-24/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-24/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-24/x86/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-24/x86/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-24/x86_64/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-24/x86_64/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-25/arm64-v8a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-25/arm64-v8a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-25/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-25/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-25/x86/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-25/x86/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-25/x86_64/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-25/x86_64/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-26/arm64-v8a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-26/arm64-v8a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-26/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-26/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-26/x86/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-26/x86/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-26/x86_64/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-26/x86_64/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-27/arm64-v8a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-27/arm64-v8a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-27/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-27/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-27/x86/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-27/x86/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-27/x86_64/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-27/x86_64/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-28/arm64-v8a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-28/arm64-v8a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-28/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-28/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-28/x86/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-28/x86/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-28/x86_64/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-28/x86_64/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-29/arm64-v8a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-29/arm64-v8a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-29/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-29/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-29/x86/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-29/x86/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-29/x86_64/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-29/x86_64/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/libs/android-9/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstf/minicap/0ee92b4741e45660040c759fc63df75ce8c4c4b5/jni/minicap-shared/aosp/libs/android-9/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/src/minicap_14.cpp: -------------------------------------------------------------------------------- 1 | #include "Minicap.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | #include "mcdebug.h" 21 | 22 | static const char* 23 | error_name(int32_t err) { 24 | switch (err) { 25 | case android::NO_ERROR: // also android::OK 26 | return "NO_ERROR"; 27 | case android::UNKNOWN_ERROR: 28 | return "UNKNOWN_ERROR"; 29 | case android::NO_MEMORY: 30 | return "NO_MEMORY"; 31 | case android::INVALID_OPERATION: 32 | return "INVALID_OPERATION"; 33 | case android::BAD_VALUE: 34 | return "BAD_VALUE"; 35 | case android::BAD_TYPE: 36 | return "BAD_TYPE"; 37 | case android::NAME_NOT_FOUND: 38 | return "NAME_NOT_FOUND"; 39 | case android::PERMISSION_DENIED: 40 | return "PERMISSION_DENIED"; 41 | case android::NO_INIT: 42 | return "NO_INIT"; 43 | case android::ALREADY_EXISTS: 44 | return "ALREADY_EXISTS"; 45 | case android::DEAD_OBJECT: // also android::JPARKS_BROKE_IT 46 | return "DEAD_OBJECT"; 47 | case android::FAILED_TRANSACTION: 48 | return "FAILED_TRANSACTION"; 49 | case android::BAD_INDEX: 50 | return "BAD_INDEX"; 51 | case android::NOT_ENOUGH_DATA: 52 | return "NOT_ENOUGH_DATA"; 53 | case android::WOULD_BLOCK: 54 | return "WOULD_BLOCK"; 55 | case android::TIMED_OUT: 56 | return "TIMED_OUT"; 57 | case android::UNKNOWN_TRANSACTION: 58 | return "UNKNOWN_TRANSACTION"; 59 | case android::FDS_NOT_ALLOWED: 60 | return "FDS_NOT_ALLOWED"; 61 | default: 62 | return "UNMAPPED_ERROR"; 63 | } 64 | } 65 | 66 | class MinicapImpl: public Minicap { 67 | public: 68 | MinicapImpl(int32_t displayId) 69 | : mDisplayId(displayId), 70 | mComposer(android::ComposerService::getComposerService()), 71 | mDesiredWidth(0), 72 | mDesiredHeight(0) { 73 | } 74 | 75 | virtual 76 | ~MinicapImpl() { 77 | release(); 78 | } 79 | 80 | virtual int 81 | applyConfigChanges() { 82 | mUserFrameAvailableListener->onFrameAvailable(); 83 | return 0; 84 | } 85 | 86 | virtual int 87 | consumePendingFrame(Minicap::Frame* frame) { 88 | uint32_t width, height; 89 | android::PixelFormat format; 90 | android::status_t err; 91 | 92 | mHeap = NULL; 93 | err = mComposer->captureScreen(mDisplayId, &mHeap, 94 | &width, &height, &format, mDesiredWidth, mDesiredHeight, 0, -1UL); 95 | 96 | if (err != android::NO_ERROR) { 97 | MCERROR("ComposerService::captureScreen() failed %s", error_name(err)); 98 | return err; 99 | } 100 | 101 | frame->data = mHeap->getBase(); 102 | frame->width = width; 103 | frame->height = height; 104 | frame->format = convertFormat(format); 105 | frame->stride = width; 106 | frame->bpp = android::bytesPerPixel(format); 107 | frame->size = mHeap->getSize(); 108 | 109 | return 0; 110 | } 111 | 112 | virtual Minicap::CaptureMethod 113 | getCaptureMethod() { 114 | return METHOD_SCREENSHOT; 115 | } 116 | 117 | virtual int32_t 118 | getDisplayId() { 119 | return mDisplayId; 120 | } 121 | 122 | virtual void 123 | release() { 124 | mHeap = NULL; 125 | } 126 | 127 | virtual void 128 | releaseConsumedFrame(Minicap::Frame* /* frame */) { 129 | mHeap = NULL; 130 | return mUserFrameAvailableListener->onFrameAvailable(); 131 | } 132 | 133 | virtual int 134 | setDesiredInfo(const Minicap::DisplayInfo& info) { 135 | mDesiredWidth = info.width; 136 | mDesiredHeight = info.height; 137 | return 0; 138 | } 139 | 140 | virtual void 141 | setFrameAvailableListener(Minicap::FrameAvailableListener* listener) { 142 | mUserFrameAvailableListener = listener; 143 | } 144 | 145 | virtual int 146 | setRealInfo(const Minicap::DisplayInfo& info) { 147 | return 0; 148 | } 149 | 150 | private: 151 | int32_t mDisplayId; 152 | android::sp mComposer; 153 | android::sp mHeap; 154 | uint32_t mDesiredWidth; 155 | uint32_t mDesiredHeight; 156 | Minicap::FrameAvailableListener* mUserFrameAvailableListener; 157 | 158 | static Minicap::Format 159 | convertFormat(android::PixelFormat format) { 160 | switch (format) { 161 | case android::PIXEL_FORMAT_NONE: 162 | return FORMAT_NONE; 163 | case android::PIXEL_FORMAT_CUSTOM: 164 | return FORMAT_CUSTOM; 165 | case android::PIXEL_FORMAT_TRANSLUCENT: 166 | return FORMAT_TRANSLUCENT; 167 | case android::PIXEL_FORMAT_TRANSPARENT: 168 | return FORMAT_TRANSPARENT; 169 | case android::PIXEL_FORMAT_OPAQUE: 170 | return FORMAT_OPAQUE; 171 | case android::PIXEL_FORMAT_RGBA_8888: 172 | return FORMAT_RGBA_8888; 173 | case android::PIXEL_FORMAT_RGBX_8888: 174 | return FORMAT_RGBX_8888; 175 | case android::PIXEL_FORMAT_RGB_888: 176 | return FORMAT_RGB_888; 177 | case android::PIXEL_FORMAT_RGB_565: 178 | return FORMAT_RGB_565; 179 | case android::PIXEL_FORMAT_BGRA_8888: 180 | return FORMAT_BGRA_8888; 181 | case android::PIXEL_FORMAT_RGBA_5551: 182 | return FORMAT_RGBA_5551; 183 | case android::PIXEL_FORMAT_RGBA_4444: 184 | return FORMAT_RGBA_4444; 185 | default: 186 | return FORMAT_UNKNOWN; 187 | } 188 | } 189 | }; 190 | 191 | int 192 | minicap_try_get_display_info(int32_t displayId, Minicap::DisplayInfo* info) { 193 | android::DisplayInfo dinfo; 194 | android::status_t err = android::SurfaceComposerClient::getDisplayInfo(displayId, &dinfo); 195 | 196 | if (err != android::NO_ERROR) { 197 | MCERROR("SurfaceComposerClient::getDisplayInfo() failed: %s (%d)\n", error_name(err), err); 198 | return err; 199 | } 200 | 201 | info->width = dinfo.w; 202 | info->height = dinfo.h; 203 | info->orientation = dinfo.orientation; 204 | info->fps = dinfo.fps; 205 | info->density = dinfo.density; 206 | info->xdpi = dinfo.xdpi; 207 | info->ydpi = dinfo.ydpi; 208 | info->secure = false; 209 | info->size = sqrt(pow(dinfo.w / dinfo.xdpi, 2) + pow(dinfo.h / dinfo.ydpi, 2)); 210 | 211 | return 0; 212 | } 213 | 214 | Minicap* 215 | minicap_create(int32_t displayId) { 216 | return new MinicapImpl(displayId); 217 | } 218 | 219 | void 220 | minicap_free(Minicap* mc) { 221 | delete mc; 222 | } 223 | 224 | void 225 | minicap_start_thread_pool() { 226 | android::ProcessState::self()->startThreadPool(); 227 | } 228 | -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/src/minicap_16.cpp: -------------------------------------------------------------------------------- 1 | #include "Minicap.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | #include "mcdebug.h" 23 | 24 | static const char* 25 | error_name(int32_t err) { 26 | switch (err) { 27 | case android::NO_ERROR: // also android::OK 28 | return "NO_ERROR"; 29 | case android::UNKNOWN_ERROR: 30 | return "UNKNOWN_ERROR"; 31 | case android::NO_MEMORY: 32 | return "NO_MEMORY"; 33 | case android::INVALID_OPERATION: 34 | return "INVALID_OPERATION"; 35 | case android::BAD_VALUE: 36 | return "BAD_VALUE"; 37 | case android::BAD_TYPE: 38 | return "BAD_TYPE"; 39 | case android::NAME_NOT_FOUND: 40 | return "NAME_NOT_FOUND"; 41 | case android::PERMISSION_DENIED: 42 | return "PERMISSION_DENIED"; 43 | case android::NO_INIT: 44 | return "NO_INIT"; 45 | case android::ALREADY_EXISTS: 46 | return "ALREADY_EXISTS"; 47 | case android::DEAD_OBJECT: // also android::JPARKS_BROKE_IT 48 | return "DEAD_OBJECT"; 49 | case android::FAILED_TRANSACTION: 50 | return "FAILED_TRANSACTION"; 51 | case android::BAD_INDEX: 52 | return "BAD_INDEX"; 53 | case android::NOT_ENOUGH_DATA: 54 | return "NOT_ENOUGH_DATA"; 55 | case android::WOULD_BLOCK: 56 | return "WOULD_BLOCK"; 57 | case android::TIMED_OUT: 58 | return "TIMED_OUT"; 59 | case android::UNKNOWN_TRANSACTION: 60 | return "UNKNOWN_TRANSACTION"; 61 | case android::FDS_NOT_ALLOWED: 62 | return "FDS_NOT_ALLOWED"; 63 | default: 64 | return "UNMAPPED_ERROR"; 65 | } 66 | } 67 | 68 | class MinicapImpl: public Minicap { 69 | public: 70 | MinicapImpl(int32_t displayId) 71 | : mDisplayId(displayId), 72 | mComposer(android::ComposerService::getComposerService()), 73 | mDesiredWidth(0), 74 | mDesiredHeight(0) { 75 | } 76 | 77 | virtual 78 | ~MinicapImpl() { 79 | release(); 80 | } 81 | 82 | virtual int 83 | applyConfigChanges() { 84 | mUserFrameAvailableListener->onFrameAvailable(); 85 | return 0; 86 | } 87 | 88 | virtual int 89 | consumePendingFrame(Minicap::Frame* frame) { 90 | uint32_t width, height; 91 | android::PixelFormat format; 92 | android::status_t err; 93 | 94 | mHeap = NULL; 95 | err = mComposer->captureScreen(mDisplayId, &mHeap, 96 | &width, &height, &format, mDesiredWidth, mDesiredHeight, 0, -1UL); 97 | 98 | if (err != android::NO_ERROR) { 99 | MCERROR("ComposerService::captureScreen() failed %s", error_name(err)); 100 | return err; 101 | } 102 | 103 | frame->data = mHeap->getBase(); 104 | frame->width = width; 105 | frame->height = height; 106 | frame->format = convertFormat(format); 107 | frame->stride = width; 108 | frame->bpp = android::bytesPerPixel(format); 109 | frame->size = mHeap->getSize(); 110 | 111 | return 0; 112 | } 113 | 114 | virtual Minicap::CaptureMethod 115 | getCaptureMethod() { 116 | return METHOD_SCREENSHOT; 117 | } 118 | 119 | virtual int32_t 120 | getDisplayId() { 121 | return mDisplayId; 122 | } 123 | 124 | virtual void 125 | release() { 126 | mHeap = NULL; 127 | } 128 | 129 | virtual void 130 | releaseConsumedFrame(Minicap::Frame* /* frame */) { 131 | mHeap = NULL; 132 | return mUserFrameAvailableListener->onFrameAvailable(); 133 | } 134 | 135 | virtual int 136 | setDesiredInfo(const Minicap::DisplayInfo& info) { 137 | mDesiredWidth = info.width; 138 | mDesiredHeight = info.height; 139 | return 0; 140 | } 141 | 142 | virtual void 143 | setFrameAvailableListener(Minicap::FrameAvailableListener* listener) { 144 | mUserFrameAvailableListener = listener; 145 | } 146 | 147 | virtual int 148 | setRealInfo(const Minicap::DisplayInfo& info) { 149 | return 0; 150 | } 151 | 152 | private: 153 | int32_t mDisplayId; 154 | android::sp mComposer; 155 | android::sp mHeap; 156 | uint32_t mDesiredWidth; 157 | uint32_t mDesiredHeight; 158 | Minicap::FrameAvailableListener* mUserFrameAvailableListener; 159 | 160 | static Minicap::Format 161 | convertFormat(android::PixelFormat format) { 162 | switch (format) { 163 | case android::PIXEL_FORMAT_NONE: 164 | return FORMAT_NONE; 165 | case android::PIXEL_FORMAT_CUSTOM: 166 | return FORMAT_CUSTOM; 167 | case android::PIXEL_FORMAT_TRANSLUCENT: 168 | return FORMAT_TRANSLUCENT; 169 | case android::PIXEL_FORMAT_TRANSPARENT: 170 | return FORMAT_TRANSPARENT; 171 | case android::PIXEL_FORMAT_OPAQUE: 172 | return FORMAT_OPAQUE; 173 | case android::PIXEL_FORMAT_RGBA_8888: 174 | return FORMAT_RGBA_8888; 175 | case android::PIXEL_FORMAT_RGBX_8888: 176 | return FORMAT_RGBX_8888; 177 | case android::PIXEL_FORMAT_RGB_888: 178 | return FORMAT_RGB_888; 179 | case android::PIXEL_FORMAT_RGB_565: 180 | return FORMAT_RGB_565; 181 | case android::PIXEL_FORMAT_BGRA_8888: 182 | return FORMAT_BGRA_8888; 183 | case android::PIXEL_FORMAT_RGBA_5551: 184 | return FORMAT_RGBA_5551; 185 | case android::PIXEL_FORMAT_RGBA_4444: 186 | return FORMAT_RGBA_4444; 187 | default: 188 | return FORMAT_UNKNOWN; 189 | } 190 | } 191 | }; 192 | 193 | int 194 | minicap_try_get_display_info(int32_t displayId, Minicap::DisplayInfo* info) { 195 | android::DisplayInfo dinfo; 196 | android::status_t err = android::SurfaceComposerClient::getDisplayInfo(displayId, &dinfo); 197 | 198 | if (err != android::NO_ERROR) { 199 | MCERROR("SurfaceComposerClient::getDisplayInfo() failed: %s (%d)\n", error_name(err), err); 200 | return err; 201 | } 202 | 203 | info->width = dinfo.w; 204 | info->height = dinfo.h; 205 | info->orientation = dinfo.orientation; 206 | info->fps = dinfo.fps; 207 | info->density = dinfo.density; 208 | info->xdpi = dinfo.xdpi; 209 | info->ydpi = dinfo.ydpi; 210 | info->secure = false; 211 | info->size = sqrt(pow(dinfo.w / dinfo.xdpi, 2) + pow(dinfo.h / dinfo.ydpi, 2)); 212 | 213 | return 0; 214 | } 215 | 216 | Minicap* 217 | minicap_create(int32_t displayId) { 218 | return new MinicapImpl(displayId); 219 | } 220 | 221 | void 222 | minicap_free(Minicap* mc) { 223 | delete mc; 224 | } 225 | 226 | void 227 | minicap_start_thread_pool() { 228 | android::ProcessState::self()->startThreadPool(); 229 | } 230 | -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/src/minicap_17.cpp: -------------------------------------------------------------------------------- 1 | #include "Minicap.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "mcdebug.h" 27 | 28 | static const char* 29 | error_name(int32_t err) { 30 | switch (err) { 31 | case android::NO_ERROR: // also android::OK 32 | return "NO_ERROR"; 33 | case android::UNKNOWN_ERROR: 34 | return "UNKNOWN_ERROR"; 35 | case android::NO_MEMORY: 36 | return "NO_MEMORY"; 37 | case android::INVALID_OPERATION: 38 | return "INVALID_OPERATION"; 39 | case android::BAD_VALUE: 40 | return "BAD_VALUE"; 41 | case android::BAD_TYPE: 42 | return "BAD_TYPE"; 43 | case android::NAME_NOT_FOUND: 44 | return "NAME_NOT_FOUND"; 45 | case android::PERMISSION_DENIED: 46 | return "PERMISSION_DENIED"; 47 | case android::NO_INIT: 48 | return "NO_INIT"; 49 | case android::ALREADY_EXISTS: 50 | return "ALREADY_EXISTS"; 51 | case android::DEAD_OBJECT: // also android::JPARKS_BROKE_IT 52 | return "DEAD_OBJECT"; 53 | case android::FAILED_TRANSACTION: 54 | return "FAILED_TRANSACTION"; 55 | case android::BAD_INDEX: 56 | return "BAD_INDEX"; 57 | case android::NOT_ENOUGH_DATA: 58 | return "NOT_ENOUGH_DATA"; 59 | case android::WOULD_BLOCK: 60 | return "WOULD_BLOCK"; 61 | case android::TIMED_OUT: 62 | return "TIMED_OUT"; 63 | case android::UNKNOWN_TRANSACTION: 64 | return "UNKNOWN_TRANSACTION"; 65 | case android::FDS_NOT_ALLOWED: 66 | return "FDS_NOT_ALLOWED"; 67 | default: 68 | return "UNMAPPED_ERROR"; 69 | } 70 | } 71 | 72 | class FrameProxy: public android::ConsumerBase::FrameAvailableListener { 73 | public: 74 | FrameProxy(Minicap::FrameAvailableListener* listener): mUserListener(listener) { 75 | } 76 | 77 | virtual void 78 | onFrameAvailable() { 79 | mUserListener->onFrameAvailable(); 80 | } 81 | 82 | private: 83 | Minicap::FrameAvailableListener* mUserListener; 84 | }; 85 | 86 | class MinicapImpl: public Minicap 87 | { 88 | public: 89 | MinicapImpl(int32_t displayId) 90 | : mDisplayId(displayId), 91 | mRealWidth(0), 92 | mRealHeight(0), 93 | mDesiredWidth(0), 94 | mDesiredHeight(0), 95 | mDesiredOrientation(0), 96 | mHaveBuffer(false), 97 | mHaveRunningDisplay(false) { 98 | } 99 | 100 | virtual 101 | ~MinicapImpl() { 102 | release(); 103 | } 104 | 105 | virtual int 106 | applyConfigChanges() { 107 | if (mHaveRunningDisplay) { 108 | destroyVirtualDisplay(); 109 | } 110 | 111 | return createVirtualDisplay(); 112 | } 113 | 114 | virtual int 115 | consumePendingFrame(Minicap::Frame* frame) { 116 | android::status_t err; 117 | 118 | if ((err = mConsumer->lockNextBuffer(&mBuffer)) != android::NO_ERROR) { 119 | if (err == -EINTR) { 120 | return err; 121 | } 122 | else { 123 | MCERROR("Unable to lock next buffer %s (%d)", error_name(err), err); 124 | return err; 125 | } 126 | } 127 | 128 | frame->data = mBuffer.data; 129 | frame->format = convertFormat(mBuffer.format); 130 | frame->width = mBuffer.width; 131 | frame->height = mBuffer.height; 132 | frame->stride = mBuffer.stride; 133 | frame->bpp = android::bytesPerPixel(mBuffer.format); 134 | frame->size = mBuffer.stride * mBuffer.height * frame->bpp; 135 | 136 | mHaveBuffer = true; 137 | 138 | return 0; 139 | } 140 | 141 | virtual Minicap::CaptureMethod 142 | getCaptureMethod() { 143 | return METHOD_VIRTUAL_DISPLAY; 144 | } 145 | 146 | virtual int32_t 147 | getDisplayId() { 148 | return mDisplayId; 149 | } 150 | 151 | virtual void 152 | release() { 153 | destroyVirtualDisplay(); 154 | } 155 | 156 | virtual void 157 | releaseConsumedFrame(Minicap::Frame* /* frame */) { 158 | if (mHaveBuffer) { 159 | mConsumer->unlockBuffer(mBuffer); 160 | mHaveBuffer = false; 161 | } 162 | } 163 | 164 | virtual int 165 | setDesiredInfo(const Minicap::DisplayInfo& info) { 166 | mDesiredWidth = info.width; 167 | mDesiredHeight = info.height; 168 | mDesiredOrientation = info.orientation; 169 | return 0; 170 | } 171 | 172 | virtual void 173 | setFrameAvailableListener(Minicap::FrameAvailableListener* listener) { 174 | mUserFrameAvailableListener = listener; 175 | } 176 | 177 | virtual int 178 | setRealInfo(const Minicap::DisplayInfo& info) { 179 | mRealWidth = info.width; 180 | mRealHeight = info.height; 181 | return 0; 182 | } 183 | 184 | private: 185 | int32_t mDisplayId; 186 | uint32_t mRealWidth; 187 | uint32_t mRealHeight; 188 | uint32_t mDesiredWidth; 189 | uint32_t mDesiredHeight; 190 | uint8_t mDesiredOrientation; 191 | android::sp mBufferQueue; 192 | android::sp mConsumer; 193 | android::sp mVirtualDisplay; 194 | android::sp mFrameProxy; 195 | Minicap::FrameAvailableListener* mUserFrameAvailableListener; 196 | bool mHaveBuffer; 197 | bool mHaveRunningDisplay; 198 | android::CpuConsumer::LockedBuffer mBuffer; 199 | 200 | int 201 | createVirtualDisplay() { 202 | uint32_t sourceWidth, sourceHeight; 203 | uint32_t targetWidth, targetHeight; 204 | android::status_t err; 205 | 206 | switch (mDesiredOrientation) { 207 | case Minicap::ORIENTATION_90: 208 | sourceWidth = mRealHeight; 209 | sourceHeight = mRealWidth; 210 | targetWidth = mDesiredHeight; 211 | targetHeight = mDesiredWidth; 212 | break; 213 | case Minicap::ORIENTATION_270: 214 | sourceWidth = mRealHeight; 215 | sourceHeight = mRealWidth; 216 | targetWidth = mDesiredHeight; 217 | targetHeight = mDesiredWidth; 218 | break; 219 | case Minicap::ORIENTATION_180: 220 | sourceWidth = mRealWidth; 221 | sourceHeight = mRealHeight; 222 | targetWidth = mDesiredWidth; 223 | targetHeight = mDesiredHeight; 224 | break; 225 | case Minicap::ORIENTATION_0: 226 | default: 227 | sourceWidth = mRealWidth; 228 | sourceHeight = mRealHeight; 229 | targetWidth = mDesiredWidth; 230 | targetHeight = mDesiredHeight; 231 | break; 232 | } 233 | 234 | // Set up virtual display size. 235 | android::Rect layerStackRect(sourceWidth, sourceHeight); 236 | android::Rect visibleRect(targetWidth, targetHeight); 237 | 238 | // Create a Surface for the virtual display to write to. 239 | MCINFO("Creating SurfaceComposerClient"); 240 | android::sp sc = new android::SurfaceComposerClient(); 241 | 242 | MCINFO("Performing SurfaceComposerClient init check"); 243 | if ((err = sc->initCheck()) != android::NO_ERROR) { 244 | MCERROR("Unable to initialize SurfaceComposerClient"); 245 | return err; 246 | } 247 | 248 | // Create virtual display. 249 | MCINFO("Creating virtual display"); 250 | mVirtualDisplay = android::SurfaceComposerClient::createDisplay( 251 | /* const String8& displayName */ android::String8("minicap"), 252 | /* bool secure */ true 253 | ); 254 | 255 | MCINFO("Creating CPU consumer"); 256 | // Some devices have a modified, larger CpuConsumer. Try to account 257 | // for that by increasing the size. 258 | mConsumer = new(operator new(sizeof(android::CpuConsumer) + 100)) android::CpuConsumer(3); 259 | mConsumer->setName(android::String8("minicap")); 260 | 261 | MCINFO("Creating buffer queue"); 262 | mBufferQueue = mConsumer->getBufferQueue(); 263 | mBufferQueue->setSynchronousMode(false); 264 | mBufferQueue->setDefaultBufferSize(targetWidth, targetHeight); 265 | mBufferQueue->setDefaultBufferFormat(android::PIXEL_FORMAT_RGBA_8888); 266 | 267 | MCINFO("Creating frame waiter"); 268 | mFrameProxy = new FrameProxy(mUserFrameAvailableListener); 269 | mConsumer->setFrameAvailableListener(mFrameProxy); 270 | 271 | MCINFO("Publishing virtual display"); 272 | android::SurfaceComposerClient::openGlobalTransaction(); 273 | android::SurfaceComposerClient::setDisplaySurface(mVirtualDisplay, mBufferQueue); 274 | android::SurfaceComposerClient::setDisplayProjection(mVirtualDisplay, 275 | android::DISPLAY_ORIENTATION_0, layerStackRect, visibleRect); 276 | android::SurfaceComposerClient::setDisplayLayerStack(mVirtualDisplay, 0); // default stack 277 | android::SurfaceComposerClient::closeGlobalTransaction(); 278 | 279 | mHaveRunningDisplay = true; 280 | 281 | return 0; 282 | } 283 | 284 | void 285 | destroyVirtualDisplay() { 286 | MCINFO("Destroying virtual display"); 287 | 288 | if (mHaveBuffer) { 289 | mConsumer->unlockBuffer(mBuffer); 290 | mHaveBuffer = false; 291 | } 292 | 293 | mBufferQueue = NULL; 294 | mConsumer = NULL; 295 | mFrameProxy = NULL; 296 | mVirtualDisplay = NULL; 297 | 298 | mHaveRunningDisplay = false; 299 | } 300 | 301 | static Minicap::Format 302 | convertFormat(android::PixelFormat format) { 303 | switch (format) { 304 | case android::PIXEL_FORMAT_NONE: 305 | return FORMAT_NONE; 306 | case android::PIXEL_FORMAT_CUSTOM: 307 | return FORMAT_CUSTOM; 308 | case android::PIXEL_FORMAT_TRANSLUCENT: 309 | return FORMAT_TRANSLUCENT; 310 | case android::PIXEL_FORMAT_TRANSPARENT: 311 | return FORMAT_TRANSPARENT; 312 | case android::PIXEL_FORMAT_OPAQUE: 313 | return FORMAT_OPAQUE; 314 | case android::PIXEL_FORMAT_RGBA_8888: 315 | return FORMAT_RGBA_8888; 316 | case android::PIXEL_FORMAT_RGBX_8888: 317 | return FORMAT_RGBX_8888; 318 | case android::PIXEL_FORMAT_RGB_888: 319 | return FORMAT_RGB_888; 320 | case android::PIXEL_FORMAT_RGB_565: 321 | return FORMAT_RGB_565; 322 | case android::PIXEL_FORMAT_BGRA_8888: 323 | return FORMAT_BGRA_8888; 324 | case android::PIXEL_FORMAT_RGBA_5551: 325 | return FORMAT_RGBA_5551; 326 | case android::PIXEL_FORMAT_RGBA_4444: 327 | return FORMAT_RGBA_4444; 328 | default: 329 | return FORMAT_UNKNOWN; 330 | } 331 | } 332 | }; 333 | 334 | int 335 | minicap_try_get_display_info(int32_t displayId, Minicap::DisplayInfo* info) { 336 | android::sp dpy = android::SurfaceComposerClient::getBuiltInDisplay(displayId); 337 | 338 | android::DisplayInfo dinfo; 339 | android::status_t err = android::SurfaceComposerClient::getDisplayInfo(dpy, &dinfo); 340 | 341 | if (err != android::NO_ERROR) { 342 | MCERROR("SurfaceComposerClient::getDisplayInfo() failed: %s (%d)\n", error_name(err), err); 343 | return err; 344 | } 345 | 346 | info->width = dinfo.w; 347 | info->height = dinfo.h; 348 | info->orientation = dinfo.orientation; 349 | info->fps = dinfo.fps; 350 | info->density = dinfo.density; 351 | info->xdpi = dinfo.xdpi; 352 | info->ydpi = dinfo.ydpi; 353 | info->secure = dinfo.secure; 354 | info->size = sqrt(pow(dinfo.w / dinfo.xdpi, 2) + pow(dinfo.h / dinfo.ydpi, 2)); 355 | 356 | return 0; 357 | } 358 | 359 | Minicap* 360 | minicap_create(int32_t displayId) { 361 | return new MinicapImpl(displayId); 362 | } 363 | 364 | void 365 | minicap_free(Minicap* mc) { 366 | delete mc; 367 | } 368 | 369 | void 370 | minicap_start_thread_pool() { 371 | android::ProcessState::self()->startThreadPool(); 372 | } 373 | -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/src/minicap_18.cpp: -------------------------------------------------------------------------------- 1 | #include "Minicap.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "mcdebug.h" 27 | 28 | static const char* 29 | error_name(int32_t err) { 30 | switch (err) { 31 | case android::NO_ERROR: // also android::OK 32 | return "NO_ERROR"; 33 | case android::UNKNOWN_ERROR: 34 | return "UNKNOWN_ERROR"; 35 | case android::NO_MEMORY: 36 | return "NO_MEMORY"; 37 | case android::INVALID_OPERATION: 38 | return "INVALID_OPERATION"; 39 | case android::BAD_VALUE: 40 | return "BAD_VALUE"; 41 | case android::BAD_TYPE: 42 | return "BAD_TYPE"; 43 | case android::NAME_NOT_FOUND: 44 | return "NAME_NOT_FOUND"; 45 | case android::PERMISSION_DENIED: 46 | return "PERMISSION_DENIED"; 47 | case android::NO_INIT: 48 | return "NO_INIT"; 49 | case android::ALREADY_EXISTS: 50 | return "ALREADY_EXISTS"; 51 | case android::DEAD_OBJECT: // also android::JPARKS_BROKE_IT 52 | return "DEAD_OBJECT"; 53 | case android::FAILED_TRANSACTION: 54 | return "FAILED_TRANSACTION"; 55 | case android::BAD_INDEX: 56 | return "BAD_INDEX"; 57 | case android::NOT_ENOUGH_DATA: 58 | return "NOT_ENOUGH_DATA"; 59 | case android::WOULD_BLOCK: 60 | return "WOULD_BLOCK"; 61 | case android::TIMED_OUT: 62 | return "TIMED_OUT"; 63 | case android::UNKNOWN_TRANSACTION: 64 | return "UNKNOWN_TRANSACTION"; 65 | case android::FDS_NOT_ALLOWED: 66 | return "FDS_NOT_ALLOWED"; 67 | default: 68 | return "UNMAPPED_ERROR"; 69 | } 70 | } 71 | 72 | class FrameProxy: public android::ConsumerBase::FrameAvailableListener { 73 | public: 74 | FrameProxy(Minicap::FrameAvailableListener* listener): mUserListener(listener) { 75 | } 76 | 77 | virtual void 78 | onFrameAvailable() { 79 | mUserListener->onFrameAvailable(); 80 | } 81 | 82 | private: 83 | Minicap::FrameAvailableListener* mUserListener; 84 | }; 85 | 86 | class MinicapImpl: public Minicap 87 | { 88 | public: 89 | MinicapImpl(int32_t displayId) 90 | : mDisplayId(displayId), 91 | mRealWidth(0), 92 | mRealHeight(0), 93 | mDesiredWidth(0), 94 | mDesiredHeight(0), 95 | mDesiredOrientation(0), 96 | mHaveBuffer(false), 97 | mHaveRunningDisplay(false) { 98 | } 99 | 100 | virtual 101 | ~MinicapImpl() { 102 | release(); 103 | } 104 | 105 | virtual int 106 | applyConfigChanges() { 107 | if (mHaveRunningDisplay) { 108 | destroyVirtualDisplay(); 109 | } 110 | 111 | return createVirtualDisplay(); 112 | } 113 | 114 | virtual int 115 | consumePendingFrame(Minicap::Frame* frame) { 116 | android::status_t err; 117 | 118 | if ((err = mConsumer->lockNextBuffer(&mBuffer)) != android::NO_ERROR) { 119 | if (err == -EINTR) { 120 | return err; 121 | } 122 | else { 123 | MCERROR("Unable to lock next buffer %s (%d)", error_name(err), err); 124 | return err; 125 | } 126 | } 127 | 128 | frame->data = mBuffer.data; 129 | frame->format = convertFormat(mBuffer.format); 130 | frame->width = mBuffer.width; 131 | frame->height = mBuffer.height; 132 | frame->stride = mBuffer.stride; 133 | frame->bpp = android::bytesPerPixel(mBuffer.format); 134 | frame->size = mBuffer.stride * mBuffer.height * frame->bpp; 135 | 136 | mHaveBuffer = true; 137 | 138 | return 0; 139 | } 140 | 141 | virtual Minicap::CaptureMethod 142 | getCaptureMethod() { 143 | return METHOD_VIRTUAL_DISPLAY; 144 | } 145 | 146 | virtual int32_t 147 | getDisplayId() { 148 | return mDisplayId; 149 | } 150 | 151 | virtual void 152 | release() { 153 | destroyVirtualDisplay(); 154 | } 155 | 156 | virtual void 157 | releaseConsumedFrame(Minicap::Frame* /* frame */) { 158 | if (mHaveBuffer) { 159 | mConsumer->unlockBuffer(mBuffer); 160 | mHaveBuffer = false; 161 | } 162 | } 163 | 164 | virtual int 165 | setDesiredInfo(const Minicap::DisplayInfo& info) { 166 | mDesiredWidth = info.width; 167 | mDesiredHeight = info.height; 168 | mDesiredOrientation = info.orientation; 169 | return 0; 170 | } 171 | 172 | virtual void 173 | setFrameAvailableListener(Minicap::FrameAvailableListener* listener) { 174 | mUserFrameAvailableListener = listener; 175 | } 176 | 177 | virtual int 178 | setRealInfo(const Minicap::DisplayInfo& info) { 179 | mRealWidth = info.width; 180 | mRealHeight = info.height; 181 | return 0; 182 | } 183 | 184 | private: 185 | int32_t mDisplayId; 186 | uint32_t mRealWidth; 187 | uint32_t mRealHeight; 188 | uint32_t mDesiredWidth; 189 | uint32_t mDesiredHeight; 190 | uint8_t mDesiredOrientation; 191 | android::sp mBufferQueue; 192 | android::sp mConsumer; 193 | android::sp mVirtualDisplay; 194 | android::sp mFrameProxy; 195 | Minicap::FrameAvailableListener* mUserFrameAvailableListener; 196 | bool mHaveBuffer; 197 | bool mHaveRunningDisplay; 198 | android::CpuConsumer::LockedBuffer mBuffer; 199 | 200 | int 201 | createVirtualDisplay() { 202 | uint32_t sourceWidth, sourceHeight; 203 | uint32_t targetWidth, targetHeight; 204 | android::status_t err; 205 | 206 | switch (mDesiredOrientation) { 207 | case Minicap::ORIENTATION_90: 208 | sourceWidth = mRealHeight; 209 | sourceHeight = mRealWidth; 210 | targetWidth = mDesiredHeight; 211 | targetHeight = mDesiredWidth; 212 | break; 213 | case Minicap::ORIENTATION_270: 214 | sourceWidth = mRealHeight; 215 | sourceHeight = mRealWidth; 216 | targetWidth = mDesiredHeight; 217 | targetHeight = mDesiredWidth; 218 | break; 219 | case Minicap::ORIENTATION_180: 220 | sourceWidth = mRealWidth; 221 | sourceHeight = mRealHeight; 222 | targetWidth = mDesiredWidth; 223 | targetHeight = mDesiredHeight; 224 | break; 225 | case Minicap::ORIENTATION_0: 226 | default: 227 | sourceWidth = mRealWidth; 228 | sourceHeight = mRealHeight; 229 | targetWidth = mDesiredWidth; 230 | targetHeight = mDesiredHeight; 231 | break; 232 | } 233 | 234 | // Set up virtual display size. 235 | android::Rect layerStackRect(sourceWidth, sourceHeight); 236 | android::Rect visibleRect(targetWidth, targetHeight); 237 | 238 | // Create a Surface for the virtual display to write to. 239 | MCINFO("Creating SurfaceComposerClient"); 240 | android::sp sc = new android::SurfaceComposerClient(); 241 | 242 | MCINFO("Performing SurfaceComposerClient init check"); 243 | if ((err = sc->initCheck()) != android::NO_ERROR) { 244 | MCERROR("Unable to initialize SurfaceComposerClient"); 245 | return err; 246 | } 247 | 248 | // Create virtual display. 249 | MCINFO("Creating virtual display"); 250 | mVirtualDisplay = android::SurfaceComposerClient::createDisplay( 251 | /* const String8& displayName */ android::String8("minicap"), 252 | /* bool secure */ true 253 | ); 254 | 255 | MCINFO("Creating CPU consumer"); 256 | mConsumer = new android::CpuConsumer(3, false); 257 | mConsumer->setName(android::String8("minicap")); 258 | 259 | MCINFO("Creating buffer queue"); 260 | mBufferQueue = mConsumer->getBufferQueue(); 261 | mBufferQueue->setDefaultBufferSize(targetWidth, targetHeight); 262 | mBufferQueue->setDefaultBufferFormat(android::PIXEL_FORMAT_RGBA_8888); 263 | 264 | MCINFO("Creating frame waiter"); 265 | mFrameProxy = new FrameProxy(mUserFrameAvailableListener); 266 | mConsumer->setFrameAvailableListener(mFrameProxy); 267 | 268 | MCINFO("Publishing virtual display"); 269 | android::SurfaceComposerClient::openGlobalTransaction(); 270 | android::SurfaceComposerClient::setDisplaySurface(mVirtualDisplay, mBufferQueue); 271 | android::SurfaceComposerClient::setDisplayProjection(mVirtualDisplay, 272 | android::DISPLAY_ORIENTATION_0, layerStackRect, visibleRect); 273 | android::SurfaceComposerClient::setDisplayLayerStack(mVirtualDisplay, 0); // default stack 274 | android::SurfaceComposerClient::closeGlobalTransaction(); 275 | 276 | mHaveRunningDisplay = true; 277 | 278 | return 0; 279 | } 280 | 281 | void 282 | destroyVirtualDisplay() { 283 | MCINFO("Destroying virtual display"); 284 | 285 | if (mHaveBuffer) { 286 | mConsumer->unlockBuffer(mBuffer); 287 | mHaveBuffer = false; 288 | } 289 | 290 | mBufferQueue = NULL; 291 | mConsumer = NULL; 292 | mFrameProxy = NULL; 293 | mVirtualDisplay = NULL; 294 | 295 | mHaveRunningDisplay = false; 296 | } 297 | 298 | static Minicap::Format 299 | convertFormat(android::PixelFormat format) { 300 | switch (format) { 301 | case android::PIXEL_FORMAT_NONE: 302 | return FORMAT_NONE; 303 | case android::PIXEL_FORMAT_CUSTOM: 304 | return FORMAT_CUSTOM; 305 | case android::PIXEL_FORMAT_TRANSLUCENT: 306 | return FORMAT_TRANSLUCENT; 307 | case android::PIXEL_FORMAT_TRANSPARENT: 308 | return FORMAT_TRANSPARENT; 309 | case android::PIXEL_FORMAT_OPAQUE: 310 | return FORMAT_OPAQUE; 311 | case android::PIXEL_FORMAT_RGBA_8888: 312 | return FORMAT_RGBA_8888; 313 | case android::PIXEL_FORMAT_RGBX_8888: 314 | return FORMAT_RGBX_8888; 315 | case android::PIXEL_FORMAT_RGB_888: 316 | return FORMAT_RGB_888; 317 | case android::PIXEL_FORMAT_RGB_565: 318 | return FORMAT_RGB_565; 319 | case android::PIXEL_FORMAT_BGRA_8888: 320 | return FORMAT_BGRA_8888; 321 | case android::PIXEL_FORMAT_RGBA_5551: 322 | return FORMAT_RGBA_5551; 323 | case android::PIXEL_FORMAT_RGBA_4444: 324 | return FORMAT_RGBA_4444; 325 | default: 326 | return FORMAT_UNKNOWN; 327 | } 328 | } 329 | }; 330 | 331 | int 332 | minicap_try_get_display_info(int32_t displayId, Minicap::DisplayInfo* info) { 333 | android::sp dpy = android::SurfaceComposerClient::getBuiltInDisplay(displayId); 334 | 335 | android::DisplayInfo dinfo; 336 | android::status_t err = android::SurfaceComposerClient::getDisplayInfo(dpy, &dinfo); 337 | 338 | if (err != android::NO_ERROR) { 339 | MCERROR("SurfaceComposerClient::getDisplayInfo() failed: %s (%d)\n", error_name(err), err); 340 | return err; 341 | } 342 | 343 | info->width = dinfo.w; 344 | info->height = dinfo.h; 345 | info->orientation = dinfo.orientation; 346 | info->fps = dinfo.fps; 347 | info->density = dinfo.density; 348 | info->xdpi = dinfo.xdpi; 349 | info->ydpi = dinfo.ydpi; 350 | info->secure = dinfo.secure; 351 | info->size = sqrt(pow(dinfo.w / dinfo.xdpi, 2) + pow(dinfo.h / dinfo.ydpi, 2)); 352 | 353 | return 0; 354 | } 355 | 356 | Minicap* 357 | minicap_create(int32_t displayId) { 358 | return new MinicapImpl(displayId); 359 | } 360 | 361 | void 362 | minicap_free(Minicap* mc) { 363 | delete mc; 364 | } 365 | 366 | void 367 | minicap_start_thread_pool() { 368 | android::ProcessState::self()->startThreadPool(); 369 | } 370 | -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/src/minicap_19.cpp: -------------------------------------------------------------------------------- 1 | #include "Minicap.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | // Terrible hack to access ScreenshotClient's mBufferQueue. It's too risky 15 | // to do `new android::BufferQueue()` by ourselves, makers tend to customize 16 | // it. 17 | #define private public 18 | #include 19 | #undef private 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #include "mcdebug.h" 33 | 34 | static const char* 35 | error_name(int32_t err) { 36 | switch (err) { 37 | case android::NO_ERROR: // also android::OK 38 | return "NO_ERROR"; 39 | case android::UNKNOWN_ERROR: 40 | return "UNKNOWN_ERROR"; 41 | case android::NO_MEMORY: 42 | return "NO_MEMORY"; 43 | case android::INVALID_OPERATION: 44 | return "INVALID_OPERATION"; 45 | case android::BAD_VALUE: 46 | return "BAD_VALUE"; 47 | case android::BAD_TYPE: 48 | return "BAD_TYPE"; 49 | case android::NAME_NOT_FOUND: 50 | return "NAME_NOT_FOUND"; 51 | case android::PERMISSION_DENIED: 52 | return "PERMISSION_DENIED"; 53 | case android::NO_INIT: 54 | return "NO_INIT"; 55 | case android::ALREADY_EXISTS: 56 | return "ALREADY_EXISTS"; 57 | case android::DEAD_OBJECT: // also android::JPARKS_BROKE_IT 58 | return "DEAD_OBJECT"; 59 | case android::FAILED_TRANSACTION: 60 | return "FAILED_TRANSACTION"; 61 | case android::BAD_INDEX: 62 | return "BAD_INDEX"; 63 | case android::NOT_ENOUGH_DATA: 64 | return "NOT_ENOUGH_DATA"; 65 | case android::WOULD_BLOCK: 66 | return "WOULD_BLOCK"; 67 | case android::TIMED_OUT: 68 | return "TIMED_OUT"; 69 | case android::UNKNOWN_TRANSACTION: 70 | return "UNKNOWN_TRANSACTION"; 71 | case android::FDS_NOT_ALLOWED: 72 | return "FDS_NOT_ALLOWED"; 73 | default: 74 | return "UNMAPPED_ERROR"; 75 | } 76 | } 77 | 78 | class FrameProxy: public android::ConsumerBase::FrameAvailableListener { 79 | public: 80 | FrameProxy(Minicap::FrameAvailableListener* listener): mUserListener(listener) { 81 | } 82 | 83 | virtual void 84 | onFrameAvailable() { 85 | mUserListener->onFrameAvailable(); 86 | } 87 | 88 | private: 89 | Minicap::FrameAvailableListener* mUserListener; 90 | }; 91 | 92 | class MinicapImpl: public Minicap 93 | { 94 | public: 95 | MinicapImpl(int32_t displayId) 96 | : mDisplayId(displayId), 97 | mRealWidth(0), 98 | mRealHeight(0), 99 | mDesiredWidth(0), 100 | mDesiredHeight(0), 101 | mDesiredOrientation(0), 102 | mHaveBuffer(false), 103 | mHaveRunningDisplay(false) { 104 | } 105 | 106 | virtual 107 | ~MinicapImpl() { 108 | release(); 109 | } 110 | 111 | virtual int 112 | applyConfigChanges() { 113 | if (mHaveRunningDisplay) { 114 | destroyVirtualDisplay(); 115 | } 116 | 117 | return createVirtualDisplay(); 118 | } 119 | 120 | virtual int 121 | consumePendingFrame(Minicap::Frame* frame) { 122 | android::status_t err; 123 | 124 | if ((err = mConsumer->lockNextBuffer(&mBuffer)) != android::NO_ERROR) { 125 | if (err == -EINTR) { 126 | return err; 127 | } 128 | else { 129 | MCERROR("Unable to lock next buffer %s (%d)", error_name(err), err); 130 | return err; 131 | } 132 | } 133 | 134 | frame->data = mBuffer.data; 135 | frame->format = convertFormat(mBuffer.format); 136 | frame->width = mBuffer.width; 137 | frame->height = mBuffer.height; 138 | frame->stride = mBuffer.stride; 139 | frame->bpp = android::bytesPerPixel(mBuffer.format); 140 | frame->size = mBuffer.stride * mBuffer.height * frame->bpp; 141 | 142 | mHaveBuffer = true; 143 | 144 | return 0; 145 | } 146 | 147 | virtual Minicap::CaptureMethod 148 | getCaptureMethod() { 149 | return METHOD_VIRTUAL_DISPLAY; 150 | } 151 | 152 | virtual int32_t 153 | getDisplayId() { 154 | return mDisplayId; 155 | } 156 | 157 | virtual void 158 | release() { 159 | destroyVirtualDisplay(); 160 | } 161 | 162 | virtual void 163 | releaseConsumedFrame(Minicap::Frame* /* frame */) { 164 | if (mHaveBuffer) { 165 | mConsumer->unlockBuffer(mBuffer); 166 | mHaveBuffer = false; 167 | } 168 | } 169 | 170 | virtual int 171 | setDesiredInfo(const Minicap::DisplayInfo& info) { 172 | mDesiredWidth = info.width; 173 | mDesiredHeight = info.height; 174 | mDesiredOrientation = info.orientation; 175 | return 0; 176 | } 177 | 178 | virtual void 179 | setFrameAvailableListener(Minicap::FrameAvailableListener* listener) { 180 | mUserFrameAvailableListener = listener; 181 | } 182 | 183 | virtual int 184 | setRealInfo(const Minicap::DisplayInfo& info) { 185 | mRealWidth = info.width; 186 | mRealHeight = info.height; 187 | return 0; 188 | } 189 | 190 | private: 191 | int32_t mDisplayId; 192 | uint32_t mRealWidth; 193 | uint32_t mRealHeight; 194 | uint32_t mDesiredWidth; 195 | uint32_t mDesiredHeight; 196 | uint8_t mDesiredOrientation; 197 | android::sp mBufferQueue; 198 | android::sp mConsumer; 199 | android::sp mVirtualDisplay; 200 | android::sp mFrameProxy; 201 | Minicap::FrameAvailableListener* mUserFrameAvailableListener; 202 | bool mHaveBuffer; 203 | bool mHaveRunningDisplay; 204 | android::CpuConsumer::LockedBuffer mBuffer; 205 | android::ScreenshotClient mScreenshotClient; 206 | 207 | int 208 | createVirtualDisplay() { 209 | uint32_t sourceWidth, sourceHeight; 210 | uint32_t targetWidth, targetHeight; 211 | android::status_t err; 212 | 213 | switch (mDesiredOrientation) { 214 | case Minicap::ORIENTATION_90: 215 | sourceWidth = mRealHeight; 216 | sourceHeight = mRealWidth; 217 | targetWidth = mDesiredHeight; 218 | targetHeight = mDesiredWidth; 219 | break; 220 | case Minicap::ORIENTATION_270: 221 | sourceWidth = mRealHeight; 222 | sourceHeight = mRealWidth; 223 | targetWidth = mDesiredHeight; 224 | targetHeight = mDesiredWidth; 225 | break; 226 | case Minicap::ORIENTATION_180: 227 | sourceWidth = mRealWidth; 228 | sourceHeight = mRealHeight; 229 | targetWidth = mDesiredWidth; 230 | targetHeight = mDesiredHeight; 231 | break; 232 | case Minicap::ORIENTATION_0: 233 | default: 234 | sourceWidth = mRealWidth; 235 | sourceHeight = mRealHeight; 236 | targetWidth = mDesiredWidth; 237 | targetHeight = mDesiredHeight; 238 | break; 239 | } 240 | 241 | // Set up virtual display size. 242 | android::Rect layerStackRect(sourceWidth, sourceHeight); 243 | android::Rect visibleRect(targetWidth, targetHeight); 244 | 245 | // Create a Surface for the virtual display to write to. 246 | MCINFO("Creating SurfaceComposerClient"); 247 | android::sp sc = new android::SurfaceComposerClient(); 248 | 249 | MCINFO("Performing SurfaceComposerClient init check"); 250 | if ((err = sc->initCheck()) != android::NO_ERROR) { 251 | MCERROR("Unable to initialize SurfaceComposerClient"); 252 | return err; 253 | } 254 | 255 | // Create virtual display. 256 | MCINFO("Creating virtual display"); 257 | mVirtualDisplay = android::SurfaceComposerClient::createDisplay( 258 | /* const String8& displayName */ android::String8("minicap"), 259 | /* bool secure */ true 260 | ); 261 | 262 | MCINFO("Creating buffer queue"); 263 | // Many devices fail if we create a BufferQueue by ourselves. 264 | // Fortunately, we can steal one from the very stable ScreenshotClient 265 | // and repurpose it. 266 | mScreenshotClient.getCpuConsumer(); 267 | mBufferQueue = mScreenshotClient.mBufferQueue; 268 | 269 | MCINFO("Creating CPU consumer"); 270 | // Some devices have a modified, larger CpuConsumer. Try to account 271 | // for that by increasing the size. Example devices include Asus MeMO 272 | // Pad 7 (ME176). 273 | mConsumer = new(operator new(sizeof(android::CpuConsumer) + 100)) android::CpuConsumer(mBufferQueue, 3, false); 274 | mConsumer->setName(android::String8("minicap")); 275 | mConsumer->setDefaultBufferSize(targetWidth, targetHeight); 276 | mConsumer->setDefaultBufferFormat(android::PIXEL_FORMAT_RGBA_8888); 277 | 278 | MCINFO("Creating frame waiter"); 279 | mFrameProxy = new FrameProxy(mUserFrameAvailableListener); 280 | mConsumer->setFrameAvailableListener(mFrameProxy); 281 | 282 | MCINFO("Publishing virtual display"); 283 | android::SurfaceComposerClient::openGlobalTransaction(); 284 | android::SurfaceComposerClient::setDisplaySurface(mVirtualDisplay, mBufferQueue); 285 | android::SurfaceComposerClient::setDisplayProjection(mVirtualDisplay, 286 | android::DISPLAY_ORIENTATION_0, layerStackRect, visibleRect); 287 | android::SurfaceComposerClient::setDisplayLayerStack(mVirtualDisplay, 0); // default stack 288 | android::SurfaceComposerClient::closeGlobalTransaction(); 289 | 290 | mHaveRunningDisplay = true; 291 | 292 | return 0; 293 | } 294 | 295 | void 296 | destroyVirtualDisplay() { 297 | MCINFO("Destroying virtual display"); 298 | android::SurfaceComposerClient::destroyDisplay(mVirtualDisplay); 299 | 300 | if (mHaveBuffer) { 301 | mConsumer->unlockBuffer(mBuffer); 302 | mHaveBuffer = false; 303 | } 304 | 305 | mBufferQueue = NULL; 306 | mConsumer = NULL; 307 | mFrameProxy = NULL; 308 | mVirtualDisplay = NULL; 309 | 310 | mHaveRunningDisplay = false; 311 | } 312 | 313 | static Minicap::Format 314 | convertFormat(android::PixelFormat format) { 315 | switch (format) { 316 | case android::PIXEL_FORMAT_NONE: 317 | return FORMAT_NONE; 318 | case android::PIXEL_FORMAT_CUSTOM: 319 | return FORMAT_CUSTOM; 320 | case android::PIXEL_FORMAT_TRANSLUCENT: 321 | return FORMAT_TRANSLUCENT; 322 | case android::PIXEL_FORMAT_TRANSPARENT: 323 | return FORMAT_TRANSPARENT; 324 | case android::PIXEL_FORMAT_OPAQUE: 325 | return FORMAT_OPAQUE; 326 | case android::PIXEL_FORMAT_RGBA_8888: 327 | return FORMAT_RGBA_8888; 328 | case android::PIXEL_FORMAT_RGBX_8888: 329 | return FORMAT_RGBX_8888; 330 | case android::PIXEL_FORMAT_RGB_888: 331 | return FORMAT_RGB_888; 332 | case android::PIXEL_FORMAT_RGB_565: 333 | return FORMAT_RGB_565; 334 | case android::PIXEL_FORMAT_BGRA_8888: 335 | return FORMAT_BGRA_8888; 336 | case android::PIXEL_FORMAT_RGBA_5551: 337 | return FORMAT_RGBA_5551; 338 | case android::PIXEL_FORMAT_RGBA_4444: 339 | return FORMAT_RGBA_4444; 340 | default: 341 | return FORMAT_UNKNOWN; 342 | } 343 | } 344 | }; 345 | 346 | int 347 | minicap_try_get_display_info(int32_t displayId, Minicap::DisplayInfo* info) { 348 | android::sp dpy = android::SurfaceComposerClient::getBuiltInDisplay(displayId); 349 | 350 | android::DisplayInfo dinfo; 351 | android::status_t err = android::SurfaceComposerClient::getDisplayInfo(dpy, &dinfo); 352 | 353 | if (err != android::NO_ERROR) { 354 | MCERROR("SurfaceComposerClient::getDisplayInfo() failed: %s (%d)\n", error_name(err), err); 355 | return err; 356 | } 357 | 358 | info->width = dinfo.w; 359 | info->height = dinfo.h; 360 | info->orientation = dinfo.orientation; 361 | info->fps = dinfo.fps; 362 | info->density = dinfo.density; 363 | info->xdpi = dinfo.xdpi; 364 | info->ydpi = dinfo.ydpi; 365 | info->secure = dinfo.secure; 366 | info->size = sqrt(pow(dinfo.w / dinfo.xdpi, 2) + pow(dinfo.h / dinfo.ydpi, 2)); 367 | 368 | return 0; 369 | } 370 | 371 | Minicap* 372 | minicap_create(int32_t displayId) { 373 | return new MinicapImpl(displayId); 374 | } 375 | 376 | void 377 | minicap_free(Minicap* mc) { 378 | delete mc; 379 | } 380 | 381 | void 382 | minicap_start_thread_pool() { 383 | android::ProcessState::self()->startThreadPool(); 384 | } 385 | -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/src/minicap_21.cpp: -------------------------------------------------------------------------------- 1 | #include "Minicap.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "mcdebug.h" 27 | 28 | static const char* 29 | error_name(int32_t err) { 30 | switch (err) { 31 | case android::NO_ERROR: // also android::OK 32 | return "NO_ERROR"; 33 | case android::UNKNOWN_ERROR: 34 | return "UNKNOWN_ERROR"; 35 | case android::NO_MEMORY: 36 | return "NO_MEMORY"; 37 | case android::INVALID_OPERATION: 38 | return "INVALID_OPERATION"; 39 | case android::BAD_VALUE: 40 | return "BAD_VALUE"; 41 | case android::BAD_TYPE: 42 | return "BAD_TYPE"; 43 | case android::NAME_NOT_FOUND: 44 | return "NAME_NOT_FOUND"; 45 | case android::PERMISSION_DENIED: 46 | return "PERMISSION_DENIED"; 47 | case android::NO_INIT: 48 | return "NO_INIT"; 49 | case android::ALREADY_EXISTS: 50 | return "ALREADY_EXISTS"; 51 | case android::DEAD_OBJECT: // also android::JPARKS_BROKE_IT 52 | return "DEAD_OBJECT"; 53 | case android::FAILED_TRANSACTION: 54 | return "FAILED_TRANSACTION"; 55 | case android::BAD_INDEX: 56 | return "BAD_INDEX"; 57 | case android::NOT_ENOUGH_DATA: 58 | return "NOT_ENOUGH_DATA"; 59 | case android::WOULD_BLOCK: 60 | return "WOULD_BLOCK"; 61 | case android::TIMED_OUT: 62 | return "TIMED_OUT"; 63 | case android::UNKNOWN_TRANSACTION: 64 | return "UNKNOWN_TRANSACTION"; 65 | case android::FDS_NOT_ALLOWED: 66 | return "FDS_NOT_ALLOWED"; 67 | default: 68 | return "UNMAPPED_ERROR"; 69 | } 70 | } 71 | 72 | class FrameProxy: public android::ConsumerBase::FrameAvailableListener { 73 | public: 74 | FrameProxy(Minicap::FrameAvailableListener* listener): mUserListener(listener) { 75 | } 76 | 77 | virtual void 78 | onFrameAvailable() { 79 | mUserListener->onFrameAvailable(); 80 | } 81 | 82 | private: 83 | Minicap::FrameAvailableListener* mUserListener; 84 | }; 85 | 86 | class MinicapImpl: public Minicap 87 | { 88 | public: 89 | MinicapImpl(int32_t displayId) 90 | : mDisplayId(displayId), 91 | mRealWidth(0), 92 | mRealHeight(0), 93 | mDesiredWidth(0), 94 | mDesiredHeight(0), 95 | mDesiredOrientation(0), 96 | mHaveBuffer(false), 97 | mHaveRunningDisplay(false) { 98 | } 99 | 100 | virtual 101 | ~MinicapImpl() { 102 | release(); 103 | } 104 | 105 | virtual int 106 | applyConfigChanges() { 107 | if (mHaveRunningDisplay) { 108 | destroyVirtualDisplay(); 109 | } 110 | 111 | return createVirtualDisplay(); 112 | } 113 | 114 | virtual int 115 | consumePendingFrame(Minicap::Frame* frame) { 116 | android::status_t err; 117 | 118 | if ((err = mConsumer->lockNextBuffer(&mBuffer)) != android::NO_ERROR) { 119 | if (err == -EINTR) { 120 | return err; 121 | } 122 | else { 123 | MCERROR("Unable to lock next buffer %s (%d)", error_name(err), err); 124 | return err; 125 | } 126 | } 127 | 128 | frame->data = mBuffer.data; 129 | frame->format = convertFormat(mBuffer.format); 130 | frame->width = mBuffer.width; 131 | frame->height = mBuffer.height; 132 | frame->stride = mBuffer.stride; 133 | frame->bpp = android::bytesPerPixel(mBuffer.format); 134 | frame->size = mBuffer.stride * mBuffer.height * frame->bpp; 135 | 136 | mHaveBuffer = true; 137 | 138 | return 0; 139 | } 140 | 141 | virtual Minicap::CaptureMethod 142 | getCaptureMethod() { 143 | return METHOD_VIRTUAL_DISPLAY; 144 | } 145 | 146 | virtual int32_t 147 | getDisplayId() { 148 | return mDisplayId; 149 | } 150 | 151 | virtual void 152 | release() { 153 | destroyVirtualDisplay(); 154 | } 155 | 156 | virtual void 157 | releaseConsumedFrame(Minicap::Frame* /* frame */) { 158 | if (mHaveBuffer) { 159 | mConsumer->unlockBuffer(mBuffer); 160 | mHaveBuffer = false; 161 | } 162 | } 163 | 164 | virtual int 165 | setDesiredInfo(const Minicap::DisplayInfo& info) { 166 | mDesiredWidth = info.width; 167 | mDesiredHeight = info.height; 168 | mDesiredOrientation = info.orientation; 169 | return 0; 170 | } 171 | 172 | virtual void 173 | setFrameAvailableListener(Minicap::FrameAvailableListener* listener) { 174 | mUserFrameAvailableListener = listener; 175 | } 176 | 177 | virtual int 178 | setRealInfo(const Minicap::DisplayInfo& info) { 179 | mRealWidth = info.width; 180 | mRealHeight = info.height; 181 | return 0; 182 | } 183 | 184 | private: 185 | int32_t mDisplayId; 186 | uint32_t mRealWidth; 187 | uint32_t mRealHeight; 188 | uint32_t mDesiredWidth; 189 | uint32_t mDesiredHeight; 190 | uint8_t mDesiredOrientation; 191 | android::sp mBufferProducer; 192 | android::sp mBufferConsumer; 193 | android::sp mConsumer; 194 | android::sp mVirtualDisplay; 195 | android::sp mFrameProxy; 196 | Minicap::FrameAvailableListener* mUserFrameAvailableListener; 197 | bool mHaveBuffer; 198 | bool mHaveRunningDisplay; 199 | android::CpuConsumer::LockedBuffer mBuffer; 200 | 201 | int 202 | createVirtualDisplay() { 203 | uint32_t sourceWidth, sourceHeight; 204 | uint32_t targetWidth, targetHeight; 205 | android::status_t err; 206 | 207 | switch (mDesiredOrientation) { 208 | case Minicap::ORIENTATION_90: 209 | sourceWidth = mRealHeight; 210 | sourceHeight = mRealWidth; 211 | targetWidth = mDesiredHeight; 212 | targetHeight = mDesiredWidth; 213 | break; 214 | case Minicap::ORIENTATION_270: 215 | sourceWidth = mRealHeight; 216 | sourceHeight = mRealWidth; 217 | targetWidth = mDesiredHeight; 218 | targetHeight = mDesiredWidth; 219 | break; 220 | case Minicap::ORIENTATION_180: 221 | sourceWidth = mRealWidth; 222 | sourceHeight = mRealHeight; 223 | targetWidth = mDesiredWidth; 224 | targetHeight = mDesiredHeight; 225 | break; 226 | case Minicap::ORIENTATION_0: 227 | default: 228 | sourceWidth = mRealWidth; 229 | sourceHeight = mRealHeight; 230 | targetWidth = mDesiredWidth; 231 | targetHeight = mDesiredHeight; 232 | break; 233 | } 234 | 235 | // Set up virtual display size. 236 | android::Rect layerStackRect(sourceWidth, sourceHeight); 237 | android::Rect visibleRect(targetWidth, targetHeight); 238 | 239 | // Create a Surface for the virtual display to write to. 240 | MCINFO("Creating SurfaceComposerClient"); 241 | android::sp sc = new android::SurfaceComposerClient(); 242 | 243 | MCINFO("Performing SurfaceComposerClient init check"); 244 | if ((err = sc->initCheck()) != android::NO_ERROR) { 245 | MCERROR("Unable to initialize SurfaceComposerClient"); 246 | return err; 247 | } 248 | 249 | // Create virtual display. 250 | MCINFO("Creating virtual display"); 251 | mVirtualDisplay = android::SurfaceComposerClient::createDisplay( 252 | /* const String8& displayName */ android::String8("minicap"), 253 | /* bool secure */ true 254 | ); 255 | 256 | MCINFO("Creating buffer queue"); 257 | android::BufferQueue::createBufferQueue(&mBufferProducer, &mBufferConsumer); 258 | mBufferConsumer->setDefaultBufferSize(targetWidth, targetHeight); 259 | mBufferConsumer->setDefaultBufferFormat(android::PIXEL_FORMAT_RGBA_8888); 260 | 261 | MCINFO("Creating CPU consumer"); 262 | mConsumer = new android::CpuConsumer(mBufferConsumer, 3, false); 263 | mConsumer->setName(android::String8("minicap")); 264 | 265 | MCINFO("Creating frame waiter"); 266 | mFrameProxy = new FrameProxy(mUserFrameAvailableListener); 267 | mConsumer->setFrameAvailableListener(mFrameProxy); 268 | 269 | MCINFO("Publishing virtual display"); 270 | android::SurfaceComposerClient::openGlobalTransaction(); 271 | android::SurfaceComposerClient::setDisplaySurface(mVirtualDisplay, mBufferProducer); 272 | android::SurfaceComposerClient::setDisplayProjection(mVirtualDisplay, 273 | android::DISPLAY_ORIENTATION_0, layerStackRect, visibleRect); 274 | android::SurfaceComposerClient::setDisplayLayerStack(mVirtualDisplay, 0); // default stack 275 | android::SurfaceComposerClient::closeGlobalTransaction(); 276 | 277 | mHaveRunningDisplay = true; 278 | 279 | return 0; 280 | } 281 | 282 | void 283 | destroyVirtualDisplay() { 284 | MCINFO("Destroying virtual display"); 285 | android::SurfaceComposerClient::destroyDisplay(mVirtualDisplay); 286 | 287 | if (mHaveBuffer) { 288 | mConsumer->unlockBuffer(mBuffer); 289 | mHaveBuffer = false; 290 | } 291 | 292 | mBufferProducer = NULL; 293 | mBufferConsumer = NULL; 294 | mConsumer = NULL; 295 | mFrameProxy = NULL; 296 | mVirtualDisplay = NULL; 297 | 298 | mHaveRunningDisplay = false; 299 | } 300 | 301 | static Minicap::Format 302 | convertFormat(android::PixelFormat format) { 303 | switch (format) { 304 | case android::PIXEL_FORMAT_NONE: 305 | return FORMAT_NONE; 306 | case android::PIXEL_FORMAT_CUSTOM: 307 | return FORMAT_CUSTOM; 308 | case android::PIXEL_FORMAT_TRANSLUCENT: 309 | return FORMAT_TRANSLUCENT; 310 | case android::PIXEL_FORMAT_TRANSPARENT: 311 | return FORMAT_TRANSPARENT; 312 | case android::PIXEL_FORMAT_OPAQUE: 313 | return FORMAT_OPAQUE; 314 | case android::PIXEL_FORMAT_RGBA_8888: 315 | return FORMAT_RGBA_8888; 316 | case android::PIXEL_FORMAT_RGBX_8888: 317 | return FORMAT_RGBX_8888; 318 | case android::PIXEL_FORMAT_RGB_888: 319 | return FORMAT_RGB_888; 320 | case android::PIXEL_FORMAT_RGB_565: 321 | return FORMAT_RGB_565; 322 | case android::PIXEL_FORMAT_BGRA_8888: 323 | return FORMAT_BGRA_8888; 324 | case android::PIXEL_FORMAT_RGBA_5551: 325 | return FORMAT_RGBA_5551; 326 | case android::PIXEL_FORMAT_RGBA_4444: 327 | return FORMAT_RGBA_4444; 328 | default: 329 | return FORMAT_UNKNOWN; 330 | } 331 | } 332 | }; 333 | 334 | int 335 | minicap_try_get_display_info(int32_t displayId, Minicap::DisplayInfo* info) { 336 | android::sp dpy = android::SurfaceComposerClient::getBuiltInDisplay(displayId); 337 | 338 | android::Vector configs; 339 | android::status_t err = android::SurfaceComposerClient::getDisplayConfigs(dpy, &configs); 340 | 341 | if (err != android::NO_ERROR) { 342 | MCERROR("SurfaceComposerClient::getDisplayInfo() failed: %s (%d)\n", error_name(err), err); 343 | return err; 344 | } 345 | 346 | int activeConfig = android::SurfaceComposerClient::getActiveConfig(dpy); 347 | if(static_cast(activeConfig) >= configs.size()) { 348 | MCERROR("Active config %d not inside configs (size %zu)\n", activeConfig, configs.size()); 349 | return android::BAD_VALUE; 350 | } 351 | android::DisplayInfo dinfo = configs[activeConfig]; 352 | 353 | info->width = dinfo.w; 354 | info->height = dinfo.h; 355 | info->orientation = dinfo.orientation; 356 | info->fps = dinfo.fps; 357 | info->density = dinfo.density; 358 | info->xdpi = dinfo.xdpi; 359 | info->ydpi = dinfo.ydpi; 360 | info->secure = dinfo.secure; 361 | info->size = sqrt(pow(dinfo.w / dinfo.xdpi, 2) + pow(dinfo.h / dinfo.ydpi, 2)); 362 | 363 | return 0; 364 | } 365 | 366 | Minicap* 367 | minicap_create(int32_t displayId) { 368 | return new MinicapImpl(displayId); 369 | } 370 | 371 | void 372 | minicap_free(Minicap* mc) { 373 | delete mc; 374 | } 375 | 376 | void 377 | minicap_start_thread_pool() { 378 | android::ProcessState::self()->startThreadPool(); 379 | } 380 | -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/src/minicap_22.cpp: -------------------------------------------------------------------------------- 1 | #include "Minicap.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "mcdebug.h" 27 | 28 | static const char* 29 | error_name(int32_t err) { 30 | switch (err) { 31 | case android::NO_ERROR: // also android::OK 32 | return "NO_ERROR"; 33 | case android::UNKNOWN_ERROR: 34 | return "UNKNOWN_ERROR"; 35 | case android::NO_MEMORY: 36 | return "NO_MEMORY"; 37 | case android::INVALID_OPERATION: 38 | return "INVALID_OPERATION"; 39 | case android::BAD_VALUE: 40 | return "BAD_VALUE"; 41 | case android::BAD_TYPE: 42 | return "BAD_TYPE"; 43 | case android::NAME_NOT_FOUND: 44 | return "NAME_NOT_FOUND"; 45 | case android::PERMISSION_DENIED: 46 | return "PERMISSION_DENIED"; 47 | case android::NO_INIT: 48 | return "NO_INIT"; 49 | case android::ALREADY_EXISTS: 50 | return "ALREADY_EXISTS"; 51 | case android::DEAD_OBJECT: // also android::JPARKS_BROKE_IT 52 | return "DEAD_OBJECT"; 53 | case android::FAILED_TRANSACTION: 54 | return "FAILED_TRANSACTION"; 55 | case android::BAD_INDEX: 56 | return "BAD_INDEX"; 57 | case android::NOT_ENOUGH_DATA: 58 | return "NOT_ENOUGH_DATA"; 59 | case android::WOULD_BLOCK: 60 | return "WOULD_BLOCK"; 61 | case android::TIMED_OUT: 62 | return "TIMED_OUT"; 63 | case android::UNKNOWN_TRANSACTION: 64 | return "UNKNOWN_TRANSACTION"; 65 | case android::FDS_NOT_ALLOWED: 66 | return "FDS_NOT_ALLOWED"; 67 | default: 68 | return "UNMAPPED_ERROR"; 69 | } 70 | } 71 | 72 | // This trick is needed for many Samsung devices running 5.1. Examples include 73 | // Galaxy S5 Neo, Galaxy J1, Galaxy A8 and so on. 74 | struct CompatFrameAvailableListener : public virtual android::RefBase { 75 | virtual void onFrameAvailable(const android::BufferItem& item) = 0; 76 | virtual void onFrameReplaced() {}; 77 | }; 78 | 79 | class FrameProxy: public CompatFrameAvailableListener { 80 | public: 81 | FrameProxy(Minicap::FrameAvailableListener* listener): mUserListener(listener) { 82 | } 83 | 84 | virtual void 85 | onFrameAvailable(const android::BufferItem& /* item */) { 86 | mUserListener->onFrameAvailable(); 87 | } 88 | 89 | private: 90 | Minicap::FrameAvailableListener* mUserListener; 91 | }; 92 | 93 | class MinicapImpl: public Minicap 94 | { 95 | public: 96 | MinicapImpl(int32_t displayId) 97 | : mDisplayId(displayId), 98 | mRealWidth(0), 99 | mRealHeight(0), 100 | mDesiredWidth(0), 101 | mDesiredHeight(0), 102 | mDesiredOrientation(0), 103 | mHaveBuffer(false), 104 | mHaveRunningDisplay(false) { 105 | } 106 | 107 | virtual 108 | ~MinicapImpl() { 109 | release(); 110 | } 111 | 112 | virtual int 113 | applyConfigChanges() { 114 | if (mHaveRunningDisplay) { 115 | destroyVirtualDisplay(); 116 | } 117 | 118 | return createVirtualDisplay(); 119 | } 120 | 121 | virtual int 122 | consumePendingFrame(Minicap::Frame* frame) { 123 | android::status_t err; 124 | 125 | if ((err = mConsumer->lockNextBuffer(&mBuffer)) != android::NO_ERROR) { 126 | if (err == -EINTR) { 127 | return err; 128 | } 129 | else { 130 | MCERROR("Unable to lock next buffer %s (%d)", error_name(err), err); 131 | return err; 132 | } 133 | } 134 | 135 | frame->data = mBuffer.data; 136 | frame->format = convertFormat(mBuffer.format); 137 | frame->width = mBuffer.width; 138 | frame->height = mBuffer.height; 139 | frame->stride = mBuffer.stride; 140 | frame->bpp = android::bytesPerPixel(mBuffer.format); 141 | frame->size = mBuffer.stride * mBuffer.height * frame->bpp; 142 | 143 | mHaveBuffer = true; 144 | 145 | return 0; 146 | } 147 | 148 | virtual Minicap::CaptureMethod 149 | getCaptureMethod() { 150 | return METHOD_VIRTUAL_DISPLAY; 151 | } 152 | 153 | virtual int32_t 154 | getDisplayId() { 155 | return mDisplayId; 156 | } 157 | 158 | virtual void 159 | release() { 160 | destroyVirtualDisplay(); 161 | } 162 | 163 | virtual void 164 | releaseConsumedFrame(Minicap::Frame* /* frame */) { 165 | if (mHaveBuffer) { 166 | mConsumer->unlockBuffer(mBuffer); 167 | mHaveBuffer = false; 168 | } 169 | } 170 | 171 | virtual int 172 | setDesiredInfo(const Minicap::DisplayInfo& info) { 173 | mDesiredWidth = info.width; 174 | mDesiredHeight = info.height; 175 | mDesiredOrientation = info.orientation; 176 | return 0; 177 | } 178 | 179 | virtual void 180 | setFrameAvailableListener(Minicap::FrameAvailableListener* listener) { 181 | mUserFrameAvailableListener = listener; 182 | } 183 | 184 | virtual int 185 | setRealInfo(const Minicap::DisplayInfo& info) { 186 | mRealWidth = info.width; 187 | mRealHeight = info.height; 188 | return 0; 189 | } 190 | 191 | private: 192 | int32_t mDisplayId; 193 | uint32_t mRealWidth; 194 | uint32_t mRealHeight; 195 | uint32_t mDesiredWidth; 196 | uint32_t mDesiredHeight; 197 | uint8_t mDesiredOrientation; 198 | android::sp mBufferProducer; 199 | android::sp mBufferConsumer; 200 | android::sp mConsumer; 201 | android::sp mVirtualDisplay; 202 | android::sp mFrameProxy; 203 | Minicap::FrameAvailableListener* mUserFrameAvailableListener; 204 | bool mHaveBuffer; 205 | bool mHaveRunningDisplay; 206 | android::CpuConsumer::LockedBuffer mBuffer; 207 | 208 | int 209 | createVirtualDisplay() { 210 | uint32_t sourceWidth, sourceHeight; 211 | uint32_t targetWidth, targetHeight; 212 | android::status_t err; 213 | 214 | switch (mDesiredOrientation) { 215 | case Minicap::ORIENTATION_90: 216 | sourceWidth = mRealHeight; 217 | sourceHeight = mRealWidth; 218 | targetWidth = mDesiredHeight; 219 | targetHeight = mDesiredWidth; 220 | break; 221 | case Minicap::ORIENTATION_270: 222 | sourceWidth = mRealHeight; 223 | sourceHeight = mRealWidth; 224 | targetWidth = mDesiredHeight; 225 | targetHeight = mDesiredWidth; 226 | break; 227 | case Minicap::ORIENTATION_180: 228 | sourceWidth = mRealWidth; 229 | sourceHeight = mRealHeight; 230 | targetWidth = mDesiredWidth; 231 | targetHeight = mDesiredHeight; 232 | break; 233 | case Minicap::ORIENTATION_0: 234 | default: 235 | sourceWidth = mRealWidth; 236 | sourceHeight = mRealHeight; 237 | targetWidth = mDesiredWidth; 238 | targetHeight = mDesiredHeight; 239 | break; 240 | } 241 | 242 | // Set up virtual display size. 243 | android::Rect layerStackRect(sourceWidth, sourceHeight); 244 | android::Rect visibleRect(targetWidth, targetHeight); 245 | 246 | // Create a Surface for the virtual display to write to. 247 | MCINFO("Creating SurfaceComposerClient"); 248 | android::sp sc = new android::SurfaceComposerClient(); 249 | 250 | MCINFO("Performing SurfaceComposerClient init check"); 251 | if ((err = sc->initCheck()) != android::NO_ERROR) { 252 | MCERROR("Unable to initialize SurfaceComposerClient"); 253 | return err; 254 | } 255 | 256 | // Create virtual display. 257 | MCINFO("Creating virtual display"); 258 | mVirtualDisplay = android::SurfaceComposerClient::createDisplay( 259 | /* const String8& displayName */ android::String8("minicap"), 260 | /* bool secure */ true 261 | ); 262 | 263 | MCINFO("Creating buffer queue"); 264 | android::BufferQueue::createBufferQueue(&mBufferProducer, &mBufferConsumer); 265 | mBufferConsumer->setDefaultBufferSize(targetWidth, targetHeight); 266 | mBufferConsumer->setDefaultBufferFormat(android::PIXEL_FORMAT_RGBA_8888); 267 | 268 | MCINFO("Creating CPU consumer"); 269 | mConsumer = new android::CpuConsumer(mBufferConsumer, 3, false); 270 | mConsumer->setName(android::String8("minicap")); 271 | 272 | MCINFO("Creating frame waiter"); 273 | mFrameProxy = reinterpret_cast(new FrameProxy(mUserFrameAvailableListener)); 274 | mConsumer->setFrameAvailableListener(mFrameProxy); 275 | 276 | MCINFO("Publishing virtual display"); 277 | android::SurfaceComposerClient::openGlobalTransaction(); 278 | android::SurfaceComposerClient::setDisplaySurface(mVirtualDisplay, mBufferProducer); 279 | android::SurfaceComposerClient::setDisplayProjection(mVirtualDisplay, 280 | android::DISPLAY_ORIENTATION_0, layerStackRect, visibleRect); 281 | android::SurfaceComposerClient::setDisplayLayerStack(mVirtualDisplay, 0); // default stack 282 | android::SurfaceComposerClient::closeGlobalTransaction(); 283 | 284 | mHaveRunningDisplay = true; 285 | 286 | return 0; 287 | } 288 | 289 | void 290 | destroyVirtualDisplay() { 291 | MCINFO("Destroying virtual display"); 292 | android::SurfaceComposerClient::destroyDisplay(mVirtualDisplay); 293 | 294 | if (mHaveBuffer) { 295 | mConsumer->unlockBuffer(mBuffer); 296 | mHaveBuffer = false; 297 | } 298 | 299 | mBufferProducer = NULL; 300 | mBufferConsumer = NULL; 301 | mConsumer = NULL; 302 | mFrameProxy = NULL; 303 | mVirtualDisplay = NULL; 304 | 305 | mHaveRunningDisplay = false; 306 | } 307 | 308 | static Minicap::Format 309 | convertFormat(android::PixelFormat format) { 310 | switch (format) { 311 | case android::PIXEL_FORMAT_NONE: 312 | return FORMAT_NONE; 313 | case android::PIXEL_FORMAT_CUSTOM: 314 | return FORMAT_CUSTOM; 315 | case android::PIXEL_FORMAT_TRANSLUCENT: 316 | return FORMAT_TRANSLUCENT; 317 | case android::PIXEL_FORMAT_TRANSPARENT: 318 | return FORMAT_TRANSPARENT; 319 | case android::PIXEL_FORMAT_OPAQUE: 320 | return FORMAT_OPAQUE; 321 | case android::PIXEL_FORMAT_RGBA_8888: 322 | return FORMAT_RGBA_8888; 323 | case android::PIXEL_FORMAT_RGBX_8888: 324 | return FORMAT_RGBX_8888; 325 | case android::PIXEL_FORMAT_RGB_888: 326 | return FORMAT_RGB_888; 327 | case android::PIXEL_FORMAT_RGB_565: 328 | return FORMAT_RGB_565; 329 | case android::PIXEL_FORMAT_BGRA_8888: 330 | return FORMAT_BGRA_8888; 331 | case android::PIXEL_FORMAT_RGBA_5551: 332 | return FORMAT_RGBA_5551; 333 | case android::PIXEL_FORMAT_RGBA_4444: 334 | return FORMAT_RGBA_4444; 335 | default: 336 | return FORMAT_UNKNOWN; 337 | } 338 | } 339 | }; 340 | 341 | int 342 | minicap_try_get_display_info(int32_t displayId, Minicap::DisplayInfo* info) { 343 | android::sp dpy = android::SurfaceComposerClient::getBuiltInDisplay(displayId); 344 | 345 | android::Vector configs; 346 | android::status_t err = android::SurfaceComposerClient::getDisplayConfigs(dpy, &configs); 347 | 348 | if (err != android::NO_ERROR) { 349 | MCERROR("SurfaceComposerClient::getDisplayInfo() failed: %s (%d)\n", error_name(err), err); 350 | return err; 351 | } 352 | 353 | int activeConfig = android::SurfaceComposerClient::getActiveConfig(dpy); 354 | if(static_cast(activeConfig) >= configs.size()) { 355 | MCERROR("Active config %d not inside configs (size %zu)\n", activeConfig, configs.size()); 356 | return android::BAD_VALUE; 357 | } 358 | android::DisplayInfo dinfo = configs[activeConfig]; 359 | 360 | info->width = dinfo.w; 361 | info->height = dinfo.h; 362 | info->orientation = dinfo.orientation; 363 | info->fps = dinfo.fps; 364 | info->density = dinfo.density; 365 | info->xdpi = dinfo.xdpi; 366 | info->ydpi = dinfo.ydpi; 367 | info->secure = dinfo.secure; 368 | info->size = sqrt(pow(dinfo.w / dinfo.xdpi, 2) + pow(dinfo.h / dinfo.ydpi, 2)); 369 | 370 | return 0; 371 | } 372 | 373 | Minicap* 374 | minicap_create(int32_t displayId) { 375 | return new MinicapImpl(displayId); 376 | } 377 | 378 | void 379 | minicap_free(Minicap* mc) { 380 | delete mc; 381 | } 382 | 383 | void 384 | minicap_start_thread_pool() { 385 | android::ProcessState::self()->startThreadPool(); 386 | } 387 | -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/src/minicap_23.cpp: -------------------------------------------------------------------------------- 1 | #include "Minicap.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "mcdebug.h" 27 | 28 | static const char* 29 | error_name(int32_t err) { 30 | switch (err) { 31 | case android::NO_ERROR: // also android::OK 32 | return "NO_ERROR"; 33 | case android::UNKNOWN_ERROR: 34 | return "UNKNOWN_ERROR"; 35 | case android::NO_MEMORY: 36 | return "NO_MEMORY"; 37 | case android::INVALID_OPERATION: 38 | return "INVALID_OPERATION"; 39 | case android::BAD_VALUE: 40 | return "BAD_VALUE"; 41 | case android::BAD_TYPE: 42 | return "BAD_TYPE"; 43 | case android::NAME_NOT_FOUND: 44 | return "NAME_NOT_FOUND"; 45 | case android::PERMISSION_DENIED: 46 | return "PERMISSION_DENIED"; 47 | case android::NO_INIT: 48 | return "NO_INIT"; 49 | case android::ALREADY_EXISTS: 50 | return "ALREADY_EXISTS"; 51 | case android::DEAD_OBJECT: // also android::JPARKS_BROKE_IT 52 | return "DEAD_OBJECT"; 53 | case android::FAILED_TRANSACTION: 54 | return "FAILED_TRANSACTION"; 55 | case android::BAD_INDEX: 56 | return "BAD_INDEX"; 57 | case android::NOT_ENOUGH_DATA: 58 | return "NOT_ENOUGH_DATA"; 59 | case android::WOULD_BLOCK: 60 | return "WOULD_BLOCK"; 61 | case android::TIMED_OUT: 62 | return "TIMED_OUT"; 63 | case android::UNKNOWN_TRANSACTION: 64 | return "UNKNOWN_TRANSACTION"; 65 | case android::FDS_NOT_ALLOWED: 66 | return "FDS_NOT_ALLOWED"; 67 | default: 68 | return "UNMAPPED_ERROR"; 69 | } 70 | } 71 | 72 | class FrameProxy: public android::ConsumerBase::FrameAvailableListener { 73 | public: 74 | FrameProxy(Minicap::FrameAvailableListener* listener): mUserListener(listener) { 75 | } 76 | 77 | virtual void 78 | onFrameAvailable(const android::BufferItem& /* item */) { 79 | mUserListener->onFrameAvailable(); 80 | } 81 | 82 | private: 83 | Minicap::FrameAvailableListener* mUserListener; 84 | }; 85 | 86 | class MinicapImpl: public Minicap 87 | { 88 | public: 89 | MinicapImpl(int32_t displayId) 90 | : mDisplayId(displayId), 91 | mRealWidth(0), 92 | mRealHeight(0), 93 | mDesiredWidth(0), 94 | mDesiredHeight(0), 95 | mDesiredOrientation(0), 96 | mHaveBuffer(false), 97 | mHaveRunningDisplay(false) { 98 | } 99 | 100 | virtual 101 | ~MinicapImpl() { 102 | release(); 103 | } 104 | 105 | virtual int 106 | applyConfigChanges() { 107 | if (mHaveRunningDisplay) { 108 | destroyVirtualDisplay(); 109 | } 110 | 111 | return createVirtualDisplay(); 112 | } 113 | 114 | virtual int 115 | consumePendingFrame(Minicap::Frame* frame) { 116 | android::status_t err; 117 | 118 | if ((err = mConsumer->lockNextBuffer(&mBuffer)) != android::NO_ERROR) { 119 | if (err == -EINTR) { 120 | return err; 121 | } 122 | else { 123 | MCERROR("Unable to lock next buffer %s (%d)", error_name(err), err); 124 | return err; 125 | } 126 | } 127 | 128 | frame->data = mBuffer.data; 129 | frame->format = convertFormat(mBuffer.format); 130 | frame->width = mBuffer.width; 131 | frame->height = mBuffer.height; 132 | frame->stride = mBuffer.stride; 133 | frame->bpp = android::bytesPerPixel(mBuffer.format); 134 | frame->size = mBuffer.stride * mBuffer.height * frame->bpp; 135 | 136 | mHaveBuffer = true; 137 | 138 | return 0; 139 | } 140 | 141 | virtual Minicap::CaptureMethod 142 | getCaptureMethod() { 143 | return METHOD_VIRTUAL_DISPLAY; 144 | } 145 | 146 | virtual int32_t 147 | getDisplayId() { 148 | return mDisplayId; 149 | } 150 | 151 | virtual void 152 | release() { 153 | destroyVirtualDisplay(); 154 | } 155 | 156 | virtual void 157 | releaseConsumedFrame(Minicap::Frame* /* frame */) { 158 | if (mHaveBuffer) { 159 | mConsumer->unlockBuffer(mBuffer); 160 | mHaveBuffer = false; 161 | } 162 | } 163 | 164 | virtual int 165 | setDesiredInfo(const Minicap::DisplayInfo& info) { 166 | mDesiredWidth = info.width; 167 | mDesiredHeight = info.height; 168 | mDesiredOrientation = info.orientation; 169 | return 0; 170 | } 171 | 172 | virtual void 173 | setFrameAvailableListener(Minicap::FrameAvailableListener* listener) { 174 | mUserFrameAvailableListener = listener; 175 | } 176 | 177 | virtual int 178 | setRealInfo(const Minicap::DisplayInfo& info) { 179 | mRealWidth = info.width; 180 | mRealHeight = info.height; 181 | return 0; 182 | } 183 | 184 | private: 185 | int32_t mDisplayId; 186 | uint32_t mRealWidth; 187 | uint32_t mRealHeight; 188 | uint32_t mDesiredWidth; 189 | uint32_t mDesiredHeight; 190 | uint8_t mDesiredOrientation; 191 | android::sp mBufferProducer; 192 | android::sp mBufferConsumer; 193 | android::sp mConsumer; 194 | android::sp mVirtualDisplay; 195 | android::sp mFrameProxy; 196 | Minicap::FrameAvailableListener* mUserFrameAvailableListener; 197 | bool mHaveBuffer; 198 | bool mHaveRunningDisplay; 199 | android::CpuConsumer::LockedBuffer mBuffer; 200 | 201 | int 202 | createVirtualDisplay() { 203 | uint32_t sourceWidth, sourceHeight; 204 | uint32_t targetWidth, targetHeight; 205 | android::status_t err; 206 | 207 | switch (mDesiredOrientation) { 208 | case Minicap::ORIENTATION_90: 209 | sourceWidth = mRealHeight; 210 | sourceHeight = mRealWidth; 211 | targetWidth = mDesiredHeight; 212 | targetHeight = mDesiredWidth; 213 | break; 214 | case Minicap::ORIENTATION_270: 215 | sourceWidth = mRealHeight; 216 | sourceHeight = mRealWidth; 217 | targetWidth = mDesiredHeight; 218 | targetHeight = mDesiredWidth; 219 | break; 220 | case Minicap::ORIENTATION_180: 221 | sourceWidth = mRealWidth; 222 | sourceHeight = mRealHeight; 223 | targetWidth = mDesiredWidth; 224 | targetHeight = mDesiredHeight; 225 | break; 226 | case Minicap::ORIENTATION_0: 227 | default: 228 | sourceWidth = mRealWidth; 229 | sourceHeight = mRealHeight; 230 | targetWidth = mDesiredWidth; 231 | targetHeight = mDesiredHeight; 232 | break; 233 | } 234 | 235 | // Set up virtual display size. 236 | android::Rect layerStackRect(sourceWidth, sourceHeight); 237 | android::Rect visibleRect(targetWidth, targetHeight); 238 | 239 | // Create a Surface for the virtual display to write to. 240 | MCINFO("Creating SurfaceComposerClient"); 241 | android::sp sc = new android::SurfaceComposerClient(); 242 | 243 | MCINFO("Performing SurfaceComposerClient init check"); 244 | if ((err = sc->initCheck()) != android::NO_ERROR) { 245 | MCERROR("Unable to initialize SurfaceComposerClient"); 246 | return err; 247 | } 248 | 249 | // Create virtual display. 250 | MCINFO("Creating virtual display"); 251 | mVirtualDisplay = android::SurfaceComposerClient::createDisplay( 252 | /* const String8& displayName */ android::String8("minicap"), 253 | /* bool secure */ true 254 | ); 255 | 256 | MCINFO("Creating buffer queue"); 257 | android::BufferQueue::createBufferQueue(&mBufferProducer, &mBufferConsumer); 258 | mBufferConsumer->setDefaultBufferSize(targetWidth, targetHeight); 259 | mBufferConsumer->setDefaultBufferFormat(android::PIXEL_FORMAT_RGBA_8888); 260 | 261 | MCINFO("Creating CPU consumer"); 262 | mConsumer = new android::CpuConsumer(mBufferConsumer, 3, false); 263 | mConsumer->setName(android::String8("minicap")); 264 | 265 | MCINFO("Creating frame waiter"); 266 | mFrameProxy = new FrameProxy(mUserFrameAvailableListener); 267 | mConsumer->setFrameAvailableListener(mFrameProxy); 268 | 269 | MCINFO("Publishing virtual display"); 270 | android::SurfaceComposerClient::openGlobalTransaction(); 271 | android::SurfaceComposerClient::setDisplaySurface(mVirtualDisplay, mBufferProducer); 272 | android::SurfaceComposerClient::setDisplayProjection(mVirtualDisplay, 273 | android::DISPLAY_ORIENTATION_0, layerStackRect, visibleRect); 274 | android::SurfaceComposerClient::setDisplayLayerStack(mVirtualDisplay, 0); // default stack 275 | android::SurfaceComposerClient::closeGlobalTransaction(); 276 | 277 | mHaveRunningDisplay = true; 278 | 279 | return 0; 280 | } 281 | 282 | void 283 | destroyVirtualDisplay() { 284 | MCINFO("Destroying virtual display"); 285 | android::SurfaceComposerClient::destroyDisplay(mVirtualDisplay); 286 | 287 | if (mHaveBuffer) { 288 | mConsumer->unlockBuffer(mBuffer); 289 | mHaveBuffer = false; 290 | } 291 | 292 | mBufferProducer = NULL; 293 | mBufferConsumer = NULL; 294 | mConsumer = NULL; 295 | mFrameProxy = NULL; 296 | mVirtualDisplay = NULL; 297 | 298 | mHaveRunningDisplay = false; 299 | } 300 | 301 | static Minicap::Format 302 | convertFormat(android::PixelFormat format) { 303 | switch (format) { 304 | case android::PIXEL_FORMAT_NONE: 305 | return FORMAT_NONE; 306 | case android::PIXEL_FORMAT_CUSTOM: 307 | return FORMAT_CUSTOM; 308 | case android::PIXEL_FORMAT_TRANSLUCENT: 309 | return FORMAT_TRANSLUCENT; 310 | case android::PIXEL_FORMAT_TRANSPARENT: 311 | return FORMAT_TRANSPARENT; 312 | case android::PIXEL_FORMAT_OPAQUE: 313 | return FORMAT_OPAQUE; 314 | case android::PIXEL_FORMAT_RGBA_8888: 315 | return FORMAT_RGBA_8888; 316 | case android::PIXEL_FORMAT_RGBX_8888: 317 | return FORMAT_RGBX_8888; 318 | case android::PIXEL_FORMAT_RGB_888: 319 | return FORMAT_RGB_888; 320 | case android::PIXEL_FORMAT_RGB_565: 321 | return FORMAT_RGB_565; 322 | case android::PIXEL_FORMAT_BGRA_8888: 323 | return FORMAT_BGRA_8888; 324 | case android::PIXEL_FORMAT_RGBA_5551: 325 | return FORMAT_RGBA_5551; 326 | case android::PIXEL_FORMAT_RGBA_4444: 327 | return FORMAT_RGBA_4444; 328 | default: 329 | return FORMAT_UNKNOWN; 330 | } 331 | } 332 | }; 333 | 334 | int 335 | minicap_try_get_display_info(int32_t displayId, Minicap::DisplayInfo* info) { 336 | android::sp dpy = android::SurfaceComposerClient::getBuiltInDisplay(displayId); 337 | 338 | android::Vector configs; 339 | android::status_t err = android::SurfaceComposerClient::getDisplayConfigs(dpy, &configs); 340 | 341 | if (err != android::NO_ERROR) { 342 | MCERROR("SurfaceComposerClient::getDisplayInfo() failed: %s (%d)\n", error_name(err), err); 343 | return err; 344 | } 345 | 346 | int activeConfig = android::SurfaceComposerClient::getActiveConfig(dpy); 347 | if(static_cast(activeConfig) >= configs.size()) { 348 | MCERROR("Active config %d not inside configs (size %zu)\n", activeConfig, configs.size()); 349 | return android::BAD_VALUE; 350 | } 351 | android::DisplayInfo dinfo = configs[activeConfig]; 352 | 353 | info->width = dinfo.w; 354 | info->height = dinfo.h; 355 | info->orientation = dinfo.orientation; 356 | info->fps = dinfo.fps; 357 | info->density = dinfo.density; 358 | info->xdpi = dinfo.xdpi; 359 | info->ydpi = dinfo.ydpi; 360 | info->secure = dinfo.secure; 361 | info->size = sqrt(pow(dinfo.w / dinfo.xdpi, 2) + pow(dinfo.h / dinfo.ydpi, 2)); 362 | 363 | return 0; 364 | } 365 | 366 | Minicap* 367 | minicap_create(int32_t displayId) { 368 | return new MinicapImpl(displayId); 369 | } 370 | 371 | void 372 | minicap_free(Minicap* mc) { 373 | delete mc; 374 | } 375 | 376 | void 377 | minicap_start_thread_pool() { 378 | android::ProcessState::self()->startThreadPool(); 379 | } 380 | -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/src/minicap_24.cpp: -------------------------------------------------------------------------------- 1 | #include "Minicap.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "mcdebug.h" 27 | 28 | static const char* 29 | error_name(int32_t err) { 30 | switch (err) { 31 | case android::NO_ERROR: // also android::OK 32 | return "NO_ERROR"; 33 | case android::UNKNOWN_ERROR: 34 | return "UNKNOWN_ERROR"; 35 | case android::NO_MEMORY: 36 | return "NO_MEMORY"; 37 | case android::INVALID_OPERATION: 38 | return "INVALID_OPERATION"; 39 | case android::BAD_VALUE: 40 | return "BAD_VALUE"; 41 | case android::BAD_TYPE: 42 | return "BAD_TYPE"; 43 | case android::NAME_NOT_FOUND: 44 | return "NAME_NOT_FOUND"; 45 | case android::PERMISSION_DENIED: 46 | return "PERMISSION_DENIED"; 47 | case android::NO_INIT: 48 | return "NO_INIT"; 49 | case android::ALREADY_EXISTS: 50 | return "ALREADY_EXISTS"; 51 | case android::DEAD_OBJECT: // also android::JPARKS_BROKE_IT 52 | return "DEAD_OBJECT"; 53 | case android::FAILED_TRANSACTION: 54 | return "FAILED_TRANSACTION"; 55 | case android::BAD_INDEX: 56 | return "BAD_INDEX"; 57 | case android::NOT_ENOUGH_DATA: 58 | return "NOT_ENOUGH_DATA"; 59 | case android::WOULD_BLOCK: 60 | return "WOULD_BLOCK"; 61 | case android::TIMED_OUT: 62 | return "TIMED_OUT"; 63 | case android::UNKNOWN_TRANSACTION: 64 | return "UNKNOWN_TRANSACTION"; 65 | case android::FDS_NOT_ALLOWED: 66 | return "FDS_NOT_ALLOWED"; 67 | default: 68 | return "UNMAPPED_ERROR"; 69 | } 70 | } 71 | 72 | class FrameProxy: public android::ConsumerBase::FrameAvailableListener { 73 | public: 74 | FrameProxy(Minicap::FrameAvailableListener* listener): mUserListener(listener) { 75 | } 76 | 77 | virtual void 78 | onFrameAvailable(const android::BufferItem& /* item */) { 79 | mUserListener->onFrameAvailable(); 80 | } 81 | 82 | private: 83 | Minicap::FrameAvailableListener* mUserListener; 84 | }; 85 | 86 | class MinicapImpl: public Minicap 87 | { 88 | public: 89 | MinicapImpl(int32_t displayId) 90 | : mDisplayId(displayId), 91 | mRealWidth(0), 92 | mRealHeight(0), 93 | mDesiredWidth(0), 94 | mDesiredHeight(0), 95 | mDesiredOrientation(0), 96 | mHaveBuffer(false), 97 | mHaveRunningDisplay(false) { 98 | } 99 | 100 | virtual 101 | ~MinicapImpl() { 102 | release(); 103 | } 104 | 105 | virtual int 106 | applyConfigChanges() { 107 | if (mHaveRunningDisplay) { 108 | destroyVirtualDisplay(); 109 | } 110 | 111 | return createVirtualDisplay(); 112 | } 113 | 114 | virtual int 115 | consumePendingFrame(Minicap::Frame* frame) { 116 | android::status_t err; 117 | 118 | if ((err = mConsumer->lockNextBuffer(&mBuffer)) != android::NO_ERROR) { 119 | if (err == -EINTR) { 120 | return err; 121 | } 122 | else { 123 | MCERROR("Unable to lock next buffer %s (%d)", error_name(err), err); 124 | return err; 125 | } 126 | } 127 | 128 | frame->data = mBuffer.data; 129 | frame->format = convertFormat(mBuffer.format); 130 | frame->width = mBuffer.width; 131 | frame->height = mBuffer.height; 132 | frame->stride = mBuffer.stride; 133 | frame->bpp = android::bytesPerPixel(mBuffer.format); 134 | frame->size = mBuffer.stride * mBuffer.height * frame->bpp; 135 | 136 | mHaveBuffer = true; 137 | 138 | return 0; 139 | } 140 | 141 | virtual Minicap::CaptureMethod 142 | getCaptureMethod() { 143 | return METHOD_VIRTUAL_DISPLAY; 144 | } 145 | 146 | virtual int32_t 147 | getDisplayId() { 148 | return mDisplayId; 149 | } 150 | 151 | virtual void 152 | release() { 153 | destroyVirtualDisplay(); 154 | } 155 | 156 | virtual void 157 | releaseConsumedFrame(Minicap::Frame* /* frame */) { 158 | if (mHaveBuffer) { 159 | mConsumer->unlockBuffer(mBuffer); 160 | mHaveBuffer = false; 161 | } 162 | } 163 | 164 | virtual int 165 | setDesiredInfo(const Minicap::DisplayInfo& info) { 166 | mDesiredWidth = info.width; 167 | mDesiredHeight = info.height; 168 | mDesiredOrientation = info.orientation; 169 | return 0; 170 | } 171 | 172 | virtual void 173 | setFrameAvailableListener(Minicap::FrameAvailableListener* listener) { 174 | mUserFrameAvailableListener = listener; 175 | } 176 | 177 | virtual int 178 | setRealInfo(const Minicap::DisplayInfo& info) { 179 | mRealWidth = info.width; 180 | mRealHeight = info.height; 181 | return 0; 182 | } 183 | 184 | private: 185 | int32_t mDisplayId; 186 | uint32_t mRealWidth; 187 | uint32_t mRealHeight; 188 | uint32_t mDesiredWidth; 189 | uint32_t mDesiredHeight; 190 | uint8_t mDesiredOrientation; 191 | android::sp mBufferProducer; 192 | android::sp mBufferConsumer; 193 | android::sp mConsumer; 194 | android::sp mVirtualDisplay; 195 | android::sp mFrameProxy; 196 | Minicap::FrameAvailableListener* mUserFrameAvailableListener; 197 | bool mHaveBuffer; 198 | bool mHaveRunningDisplay; 199 | android::CpuConsumer::LockedBuffer mBuffer; 200 | 201 | int 202 | createVirtualDisplay() { 203 | uint32_t sourceWidth, sourceHeight; 204 | uint32_t targetWidth, targetHeight; 205 | android::status_t err; 206 | 207 | switch (mDesiredOrientation) { 208 | case Minicap::ORIENTATION_90: 209 | sourceWidth = mRealHeight; 210 | sourceHeight = mRealWidth; 211 | targetWidth = mDesiredHeight; 212 | targetHeight = mDesiredWidth; 213 | break; 214 | case Minicap::ORIENTATION_270: 215 | sourceWidth = mRealHeight; 216 | sourceHeight = mRealWidth; 217 | targetWidth = mDesiredHeight; 218 | targetHeight = mDesiredWidth; 219 | break; 220 | case Minicap::ORIENTATION_180: 221 | sourceWidth = mRealWidth; 222 | sourceHeight = mRealHeight; 223 | targetWidth = mDesiredWidth; 224 | targetHeight = mDesiredHeight; 225 | break; 226 | case Minicap::ORIENTATION_0: 227 | default: 228 | sourceWidth = mRealWidth; 229 | sourceHeight = mRealHeight; 230 | targetWidth = mDesiredWidth; 231 | targetHeight = mDesiredHeight; 232 | break; 233 | } 234 | 235 | // Set up virtual display size. 236 | android::Rect layerStackRect(sourceWidth, sourceHeight); 237 | android::Rect visibleRect(targetWidth, targetHeight); 238 | 239 | // Create a Surface for the virtual display to write to. 240 | MCINFO("Creating SurfaceComposerClient"); 241 | android::sp sc = new android::SurfaceComposerClient(); 242 | 243 | MCINFO("Performing SurfaceComposerClient init check"); 244 | if ((err = sc->initCheck()) != android::NO_ERROR) { 245 | MCERROR("Unable to initialize SurfaceComposerClient"); 246 | return err; 247 | } 248 | 249 | // Create virtual display. 250 | MCINFO("Creating virtual display"); 251 | mVirtualDisplay = android::SurfaceComposerClient::createDisplay( 252 | /* const String8& displayName */ android::String8("minicap"), 253 | /* bool secure */ true 254 | ); 255 | 256 | MCINFO("Creating buffer queue"); 257 | android::BufferQueue::createBufferQueue(&mBufferProducer, &mBufferConsumer); 258 | mBufferConsumer->setDefaultBufferSize(targetWidth, targetHeight); 259 | mBufferConsumer->setDefaultBufferFormat(android::PIXEL_FORMAT_RGBA_8888); 260 | 261 | MCINFO("Creating CPU consumer"); 262 | mConsumer = new android::CpuConsumer(mBufferConsumer, 3, false); 263 | mConsumer->setName(android::String8("minicap")); 264 | 265 | MCINFO("Creating frame waiter"); 266 | mFrameProxy = new FrameProxy(mUserFrameAvailableListener); 267 | mConsumer->setFrameAvailableListener(mFrameProxy); 268 | 269 | MCINFO("Publishing virtual display"); 270 | android::SurfaceComposerClient::openGlobalTransaction(); 271 | android::SurfaceComposerClient::setDisplaySurface(mVirtualDisplay, mBufferProducer); 272 | android::SurfaceComposerClient::setDisplayProjection(mVirtualDisplay, 273 | android::DISPLAY_ORIENTATION_0, layerStackRect, visibleRect); 274 | android::SurfaceComposerClient::setDisplayLayerStack(mVirtualDisplay, 0); // default stack 275 | android::SurfaceComposerClient::closeGlobalTransaction(); 276 | 277 | mHaveRunningDisplay = true; 278 | 279 | return 0; 280 | } 281 | 282 | void 283 | destroyVirtualDisplay() { 284 | MCINFO("Destroying virtual display"); 285 | android::SurfaceComposerClient::destroyDisplay(mVirtualDisplay); 286 | 287 | if (mHaveBuffer) { 288 | mConsumer->unlockBuffer(mBuffer); 289 | mHaveBuffer = false; 290 | } 291 | 292 | mBufferProducer = NULL; 293 | mBufferConsumer = NULL; 294 | mConsumer = NULL; 295 | mFrameProxy = NULL; 296 | mVirtualDisplay = NULL; 297 | 298 | mHaveRunningDisplay = false; 299 | } 300 | 301 | static Minicap::Format 302 | convertFormat(android::PixelFormat format) { 303 | switch (format) { 304 | case android::PIXEL_FORMAT_NONE: 305 | return FORMAT_NONE; 306 | case android::PIXEL_FORMAT_CUSTOM: 307 | return FORMAT_CUSTOM; 308 | case android::PIXEL_FORMAT_TRANSLUCENT: 309 | return FORMAT_TRANSLUCENT; 310 | case android::PIXEL_FORMAT_TRANSPARENT: 311 | return FORMAT_TRANSPARENT; 312 | case android::PIXEL_FORMAT_OPAQUE: 313 | return FORMAT_OPAQUE; 314 | case android::PIXEL_FORMAT_RGBA_8888: 315 | return FORMAT_RGBA_8888; 316 | case android::PIXEL_FORMAT_RGBX_8888: 317 | return FORMAT_RGBX_8888; 318 | case android::PIXEL_FORMAT_RGB_888: 319 | return FORMAT_RGB_888; 320 | case android::PIXEL_FORMAT_RGB_565: 321 | return FORMAT_RGB_565; 322 | case android::PIXEL_FORMAT_BGRA_8888: 323 | return FORMAT_BGRA_8888; 324 | case android::PIXEL_FORMAT_RGBA_5551: 325 | return FORMAT_RGBA_5551; 326 | case android::PIXEL_FORMAT_RGBA_4444: 327 | return FORMAT_RGBA_4444; 328 | default: 329 | return FORMAT_UNKNOWN; 330 | } 331 | } 332 | }; 333 | 334 | int 335 | minicap_try_get_display_info(int32_t displayId, Minicap::DisplayInfo* info) { 336 | android::sp dpy = android::SurfaceComposerClient::getBuiltInDisplay(displayId); 337 | 338 | android::Vector configs; 339 | android::status_t err = android::SurfaceComposerClient::getDisplayConfigs(dpy, &configs); 340 | 341 | if (err != android::NO_ERROR) { 342 | MCERROR("SurfaceComposerClient::getDisplayInfo() failed: %s (%d)\n", error_name(err), err); 343 | return err; 344 | } 345 | 346 | int activeConfig = android::SurfaceComposerClient::getActiveConfig(dpy); 347 | if(static_cast(activeConfig) >= configs.size()) { 348 | MCERROR("Active config %d not inside configs (size %zu)\n", activeConfig, configs.size()); 349 | return android::BAD_VALUE; 350 | } 351 | android::DisplayInfo dinfo = configs[activeConfig]; 352 | 353 | info->width = dinfo.w; 354 | info->height = dinfo.h; 355 | info->orientation = dinfo.orientation; 356 | info->fps = dinfo.fps; 357 | info->density = dinfo.density; 358 | info->xdpi = dinfo.xdpi; 359 | info->ydpi = dinfo.ydpi; 360 | info->secure = dinfo.secure; 361 | info->size = sqrt(pow(dinfo.w / dinfo.xdpi, 2) + pow(dinfo.h / dinfo.ydpi, 2)); 362 | 363 | return 0; 364 | } 365 | 366 | Minicap* 367 | minicap_create(int32_t displayId) { 368 | return new MinicapImpl(displayId); 369 | } 370 | 371 | void 372 | minicap_free(Minicap* mc) { 373 | delete mc; 374 | } 375 | 376 | void 377 | minicap_start_thread_pool() { 378 | android::ProcessState::self()->startThreadPool(); 379 | } 380 | -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/src/minicap_26.cpp: -------------------------------------------------------------------------------- 1 | #include "Minicap.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include "mcdebug.h" 28 | 29 | static const char* 30 | error_name(int32_t err) { 31 | switch (err) { 32 | case android::NO_ERROR: // also android::OK 33 | return "NO_ERROR"; 34 | case android::UNKNOWN_ERROR: 35 | return "UNKNOWN_ERROR"; 36 | case android::NO_MEMORY: 37 | return "NO_MEMORY"; 38 | case android::INVALID_OPERATION: 39 | return "INVALID_OPERATION"; 40 | case android::BAD_VALUE: 41 | return "BAD_VALUE"; 42 | case android::BAD_TYPE: 43 | return "BAD_TYPE"; 44 | case android::NAME_NOT_FOUND: 45 | return "NAME_NOT_FOUND"; 46 | case android::PERMISSION_DENIED: 47 | return "PERMISSION_DENIED"; 48 | case android::NO_INIT: 49 | return "NO_INIT"; 50 | case android::ALREADY_EXISTS: 51 | return "ALREADY_EXISTS"; 52 | case android::DEAD_OBJECT: // also android::JPARKS_BROKE_IT 53 | return "DEAD_OBJECT"; 54 | case android::FAILED_TRANSACTION: 55 | return "FAILED_TRANSACTION"; 56 | case android::BAD_INDEX: 57 | return "BAD_INDEX"; 58 | case android::NOT_ENOUGH_DATA: 59 | return "NOT_ENOUGH_DATA"; 60 | case android::WOULD_BLOCK: 61 | return "WOULD_BLOCK"; 62 | case android::TIMED_OUT: 63 | return "TIMED_OUT"; 64 | case android::UNKNOWN_TRANSACTION: 65 | return "UNKNOWN_TRANSACTION"; 66 | case android::FDS_NOT_ALLOWED: 67 | return "FDS_NOT_ALLOWED"; 68 | default: 69 | return "UNMAPPED_ERROR"; 70 | } 71 | } 72 | 73 | class FrameProxy: public android::ConsumerBase::FrameAvailableListener { 74 | public: 75 | FrameProxy(Minicap::FrameAvailableListener* listener): mUserListener(listener) { 76 | } 77 | 78 | virtual void 79 | onFrameAvailable(const android::BufferItem& /* item */) { 80 | mUserListener->onFrameAvailable(); 81 | } 82 | 83 | private: 84 | Minicap::FrameAvailableListener* mUserListener; 85 | }; 86 | 87 | class MinicapImpl: public Minicap 88 | { 89 | public: 90 | MinicapImpl(int32_t displayId) 91 | : mDisplayId(displayId), 92 | mRealWidth(0), 93 | mRealHeight(0), 94 | mDesiredWidth(0), 95 | mDesiredHeight(0), 96 | mDesiredOrientation(0), 97 | mHaveBuffer(false), 98 | mHaveRunningDisplay(false) { 99 | } 100 | 101 | virtual 102 | ~MinicapImpl() { 103 | release(); 104 | } 105 | 106 | virtual int 107 | applyConfigChanges() { 108 | if (mHaveRunningDisplay) { 109 | destroyVirtualDisplay(); 110 | } 111 | 112 | return createVirtualDisplay(); 113 | } 114 | 115 | virtual int 116 | consumePendingFrame(Minicap::Frame* frame) { 117 | android::status_t err; 118 | 119 | if ((err = mConsumer->lockNextBuffer(&mBuffer)) != android::NO_ERROR) { 120 | if (err == -EINTR) { 121 | return err; 122 | } 123 | else { 124 | MCERROR("Unable to lock next buffer %s (%d)", error_name(err), err); 125 | return err; 126 | } 127 | } 128 | 129 | frame->data = mBuffer.data; 130 | frame->format = convertFormat(mBuffer.format); 131 | frame->width = mBuffer.width; 132 | frame->height = mBuffer.height; 133 | frame->stride = mBuffer.stride; 134 | frame->bpp = android::bytesPerPixel(mBuffer.format); 135 | frame->size = mBuffer.stride * mBuffer.height * frame->bpp; 136 | 137 | mHaveBuffer = true; 138 | 139 | return 0; 140 | } 141 | 142 | virtual Minicap::CaptureMethod 143 | getCaptureMethod() { 144 | return METHOD_VIRTUAL_DISPLAY; 145 | } 146 | 147 | virtual int32_t 148 | getDisplayId() { 149 | return mDisplayId; 150 | } 151 | 152 | virtual void 153 | release() { 154 | destroyVirtualDisplay(); 155 | } 156 | 157 | virtual void 158 | releaseConsumedFrame(Minicap::Frame* /* frame */) { 159 | if (mHaveBuffer) { 160 | mConsumer->unlockBuffer(mBuffer); 161 | mHaveBuffer = false; 162 | } 163 | } 164 | 165 | virtual int 166 | setDesiredInfo(const Minicap::DisplayInfo& info) { 167 | mDesiredWidth = info.width; 168 | mDesiredHeight = info.height; 169 | mDesiredOrientation = info.orientation; 170 | return 0; 171 | } 172 | 173 | virtual void 174 | setFrameAvailableListener(Minicap::FrameAvailableListener* listener) { 175 | mUserFrameAvailableListener = listener; 176 | } 177 | 178 | virtual int 179 | setRealInfo(const Minicap::DisplayInfo& info) { 180 | mRealWidth = info.width; 181 | mRealHeight = info.height; 182 | return 0; 183 | } 184 | 185 | private: 186 | int32_t mDisplayId; 187 | uint32_t mRealWidth; 188 | uint32_t mRealHeight; 189 | uint32_t mDesiredWidth; 190 | uint32_t mDesiredHeight; 191 | uint8_t mDesiredOrientation; 192 | android::sp mBufferProducer; 193 | android::sp mBufferConsumer; 194 | android::sp mConsumer; 195 | android::sp mVirtualDisplay; 196 | android::sp mFrameProxy; 197 | Minicap::FrameAvailableListener* mUserFrameAvailableListener; 198 | bool mHaveBuffer; 199 | bool mHaveRunningDisplay; 200 | android::CpuConsumer::LockedBuffer mBuffer; 201 | 202 | int 203 | createVirtualDisplay() { 204 | uint32_t sourceWidth, sourceHeight; 205 | uint32_t targetWidth, targetHeight; 206 | android::status_t err; 207 | 208 | switch (mDesiredOrientation) { 209 | case Minicap::ORIENTATION_90: 210 | sourceWidth = mRealHeight; 211 | sourceHeight = mRealWidth; 212 | targetWidth = mDesiredHeight; 213 | targetHeight = mDesiredWidth; 214 | break; 215 | case Minicap::ORIENTATION_270: 216 | sourceWidth = mRealHeight; 217 | sourceHeight = mRealWidth; 218 | targetWidth = mDesiredHeight; 219 | targetHeight = mDesiredWidth; 220 | break; 221 | case Minicap::ORIENTATION_180: 222 | sourceWidth = mRealWidth; 223 | sourceHeight = mRealHeight; 224 | targetWidth = mDesiredWidth; 225 | targetHeight = mDesiredHeight; 226 | break; 227 | case Minicap::ORIENTATION_0: 228 | default: 229 | sourceWidth = mRealWidth; 230 | sourceHeight = mRealHeight; 231 | targetWidth = mDesiredWidth; 232 | targetHeight = mDesiredHeight; 233 | break; 234 | } 235 | 236 | // Set up virtual display size. 237 | android::Rect layerStackRect(sourceWidth, sourceHeight); 238 | android::Rect visibleRect(targetWidth, targetHeight); 239 | 240 | // Create a Surface for the virtual display to write to. 241 | MCINFO("Creating SurfaceComposerClient"); 242 | android::sp sc = new android::SurfaceComposerClient(); 243 | 244 | MCINFO("Performing SurfaceComposerClient init check"); 245 | if ((err = sc->initCheck()) != android::NO_ERROR) { 246 | MCERROR("Unable to initialize SurfaceComposerClient"); 247 | return err; 248 | } 249 | 250 | // This is now REQUIRED in O Developer Preview 1 or there's a segfault 251 | // when the sp goes out of scope. 252 | sc = NULL; 253 | 254 | // Create virtual display. 255 | MCINFO("Creating virtual display"); 256 | mVirtualDisplay = android::SurfaceComposerClient::createDisplay( 257 | /* const String8& displayName */ android::String8("minicap"), 258 | /* bool secure */ true 259 | ); 260 | 261 | MCINFO("Creating buffer queue"); 262 | android::BufferQueue::createBufferQueue(&mBufferProducer, &mBufferConsumer, false); 263 | 264 | MCINFO("Setting buffer options"); 265 | mBufferConsumer->setDefaultBufferSize(targetWidth, targetHeight); 266 | mBufferConsumer->setDefaultBufferFormat(android::PIXEL_FORMAT_RGBA_8888); 267 | 268 | MCINFO("Creating CPU consumer"); 269 | mConsumer = new android::CpuConsumer(mBufferConsumer, 3, false); 270 | mConsumer->setName(android::String8("minicap")); 271 | 272 | MCINFO("Creating frame waiter"); 273 | mFrameProxy = new FrameProxy(mUserFrameAvailableListener); 274 | mConsumer->setFrameAvailableListener(mFrameProxy); 275 | 276 | MCINFO("Publishing virtual display"); 277 | android::SurfaceComposerClient::openGlobalTransaction(); 278 | android::SurfaceComposerClient::setDisplaySurface(mVirtualDisplay, mBufferProducer); 279 | android::SurfaceComposerClient::setDisplayProjection(mVirtualDisplay, 280 | android::DISPLAY_ORIENTATION_0, layerStackRect, visibleRect); 281 | android::SurfaceComposerClient::setDisplayLayerStack(mVirtualDisplay, 0); // default stack 282 | android::SurfaceComposerClient::closeGlobalTransaction(); 283 | 284 | mHaveRunningDisplay = true; 285 | 286 | return 0; 287 | } 288 | 289 | void 290 | destroyVirtualDisplay() { 291 | MCINFO("Destroying virtual display"); 292 | android::SurfaceComposerClient::destroyDisplay(mVirtualDisplay); 293 | 294 | if (mHaveBuffer) { 295 | mConsumer->unlockBuffer(mBuffer); 296 | mHaveBuffer = false; 297 | } 298 | 299 | mBufferProducer = NULL; 300 | mBufferConsumer = NULL; 301 | mConsumer = NULL; 302 | mFrameProxy = NULL; 303 | mVirtualDisplay = NULL; 304 | 305 | mHaveRunningDisplay = false; 306 | } 307 | 308 | static Minicap::Format 309 | convertFormat(android::PixelFormat format) { 310 | switch (format) { 311 | case android::PIXEL_FORMAT_NONE: 312 | return FORMAT_NONE; 313 | case android::PIXEL_FORMAT_CUSTOM: 314 | return FORMAT_CUSTOM; 315 | case android::PIXEL_FORMAT_TRANSLUCENT: 316 | return FORMAT_TRANSLUCENT; 317 | case android::PIXEL_FORMAT_TRANSPARENT: 318 | return FORMAT_TRANSPARENT; 319 | case android::PIXEL_FORMAT_OPAQUE: 320 | return FORMAT_OPAQUE; 321 | case android::PIXEL_FORMAT_RGBA_8888: 322 | return FORMAT_RGBA_8888; 323 | case android::PIXEL_FORMAT_RGBX_8888: 324 | return FORMAT_RGBX_8888; 325 | case android::PIXEL_FORMAT_RGB_888: 326 | return FORMAT_RGB_888; 327 | case android::PIXEL_FORMAT_RGB_565: 328 | return FORMAT_RGB_565; 329 | case android::PIXEL_FORMAT_BGRA_8888: 330 | return FORMAT_BGRA_8888; 331 | case android::PIXEL_FORMAT_RGBA_5551: 332 | return FORMAT_RGBA_5551; 333 | case android::PIXEL_FORMAT_RGBA_4444: 334 | return FORMAT_RGBA_4444; 335 | default: 336 | return FORMAT_UNKNOWN; 337 | } 338 | } 339 | }; 340 | 341 | int 342 | minicap_try_get_display_info(int32_t displayId, Minicap::DisplayInfo* info) { 343 | android::sp dpy = android::SurfaceComposerClient::getBuiltInDisplay(displayId); 344 | 345 | android::Vector configs; 346 | android::status_t err = android::SurfaceComposerClient::getDisplayConfigs(dpy, &configs); 347 | 348 | if (err != android::NO_ERROR) { 349 | MCERROR("SurfaceComposerClient::getDisplayInfo() failed: %s (%d)\n", error_name(err), err); 350 | return err; 351 | } 352 | 353 | int activeConfig = android::SurfaceComposerClient::getActiveConfig(dpy); 354 | if(static_cast(activeConfig) >= configs.size()) { 355 | MCERROR("Active config %d not inside configs (size %zu)\n", activeConfig, configs.size()); 356 | return android::BAD_VALUE; 357 | } 358 | android::DisplayInfo dinfo = configs[activeConfig]; 359 | 360 | info->width = dinfo.w; 361 | info->height = dinfo.h; 362 | info->orientation = dinfo.orientation; 363 | info->fps = dinfo.fps; 364 | info->density = dinfo.density; 365 | info->xdpi = dinfo.xdpi; 366 | info->ydpi = dinfo.ydpi; 367 | info->secure = dinfo.secure; 368 | info->size = sqrt(pow(dinfo.w / dinfo.xdpi, 2) + pow(dinfo.h / dinfo.ydpi, 2)); 369 | 370 | return 0; 371 | } 372 | 373 | Minicap* 374 | minicap_create(int32_t displayId) { 375 | return new MinicapImpl(displayId); 376 | } 377 | 378 | void 379 | minicap_free(Minicap* mc) { 380 | delete mc; 381 | } 382 | 383 | void 384 | minicap_start_thread_pool() { 385 | android::ProcessState::self()->startThreadPool(); 386 | } 387 | -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/src/minicap_27.cpp: -------------------------------------------------------------------------------- 1 | #include "Minicap.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include "mcdebug.h" 28 | 29 | static const char* 30 | error_name(int32_t err) { 31 | switch (err) { 32 | case android::NO_ERROR: // also android::OK 33 | return "NO_ERROR"; 34 | case android::UNKNOWN_ERROR: 35 | return "UNKNOWN_ERROR"; 36 | case android::NO_MEMORY: 37 | return "NO_MEMORY"; 38 | case android::INVALID_OPERATION: 39 | return "INVALID_OPERATION"; 40 | case android::BAD_VALUE: 41 | return "BAD_VALUE"; 42 | case android::BAD_TYPE: 43 | return "BAD_TYPE"; 44 | case android::NAME_NOT_FOUND: 45 | return "NAME_NOT_FOUND"; 46 | case android::PERMISSION_DENIED: 47 | return "PERMISSION_DENIED"; 48 | case android::NO_INIT: 49 | return "NO_INIT"; 50 | case android::ALREADY_EXISTS: 51 | return "ALREADY_EXISTS"; 52 | case android::DEAD_OBJECT: // also android::JPARKS_BROKE_IT 53 | return "DEAD_OBJECT"; 54 | case android::FAILED_TRANSACTION: 55 | return "FAILED_TRANSACTION"; 56 | case android::BAD_INDEX: 57 | return "BAD_INDEX"; 58 | case android::NOT_ENOUGH_DATA: 59 | return "NOT_ENOUGH_DATA"; 60 | case android::WOULD_BLOCK: 61 | return "WOULD_BLOCK"; 62 | case android::TIMED_OUT: 63 | return "TIMED_OUT"; 64 | case android::UNKNOWN_TRANSACTION: 65 | return "UNKNOWN_TRANSACTION"; 66 | case android::FDS_NOT_ALLOWED: 67 | return "FDS_NOT_ALLOWED"; 68 | default: 69 | return "UNMAPPED_ERROR"; 70 | } 71 | } 72 | 73 | class FrameProxy: public android::ConsumerBase::FrameAvailableListener { 74 | public: 75 | FrameProxy(Minicap::FrameAvailableListener* listener): mUserListener(listener) { 76 | } 77 | 78 | virtual void 79 | onFrameAvailable(const android::BufferItem& /* item */) { 80 | mUserListener->onFrameAvailable(); 81 | } 82 | 83 | private: 84 | Minicap::FrameAvailableListener* mUserListener; 85 | }; 86 | 87 | class MinicapImpl: public Minicap 88 | { 89 | public: 90 | MinicapImpl(int32_t displayId) 91 | : mDisplayId(displayId), 92 | mRealWidth(0), 93 | mRealHeight(0), 94 | mDesiredWidth(0), 95 | mDesiredHeight(0), 96 | mDesiredOrientation(0), 97 | mHaveBuffer(false), 98 | mHaveRunningDisplay(false) { 99 | } 100 | 101 | virtual 102 | ~MinicapImpl() { 103 | release(); 104 | } 105 | 106 | virtual int 107 | applyConfigChanges() { 108 | if (mHaveRunningDisplay) { 109 | destroyVirtualDisplay(); 110 | } 111 | 112 | return createVirtualDisplay(); 113 | } 114 | 115 | virtual int 116 | consumePendingFrame(Minicap::Frame* frame) { 117 | android::status_t err; 118 | 119 | if ((err = mConsumer->lockNextBuffer(&mBuffer)) != android::NO_ERROR) { 120 | if (err == -EINTR) { 121 | return err; 122 | } 123 | else { 124 | MCERROR("Unable to lock next buffer %s (%d)", error_name(err), err); 125 | return err; 126 | } 127 | } 128 | 129 | frame->data = mBuffer.data; 130 | frame->format = convertFormat(mBuffer.format); 131 | frame->width = mBuffer.width; 132 | frame->height = mBuffer.height; 133 | frame->stride = mBuffer.stride; 134 | frame->bpp = android::bytesPerPixel(mBuffer.format); 135 | frame->size = mBuffer.stride * mBuffer.height * frame->bpp; 136 | 137 | mHaveBuffer = true; 138 | 139 | return 0; 140 | } 141 | 142 | virtual Minicap::CaptureMethod 143 | getCaptureMethod() { 144 | return METHOD_VIRTUAL_DISPLAY; 145 | } 146 | 147 | virtual int32_t 148 | getDisplayId() { 149 | return mDisplayId; 150 | } 151 | 152 | virtual void 153 | release() { 154 | destroyVirtualDisplay(); 155 | } 156 | 157 | virtual void 158 | releaseConsumedFrame(Minicap::Frame* /* frame */) { 159 | if (mHaveBuffer) { 160 | mConsumer->unlockBuffer(mBuffer); 161 | mHaveBuffer = false; 162 | } 163 | } 164 | 165 | virtual int 166 | setDesiredInfo(const Minicap::DisplayInfo& info) { 167 | mDesiredWidth = info.width; 168 | mDesiredHeight = info.height; 169 | mDesiredOrientation = info.orientation; 170 | return 0; 171 | } 172 | 173 | virtual void 174 | setFrameAvailableListener(Minicap::FrameAvailableListener* listener) { 175 | mUserFrameAvailableListener = listener; 176 | } 177 | 178 | virtual int 179 | setRealInfo(const Minicap::DisplayInfo& info) { 180 | mRealWidth = info.width; 181 | mRealHeight = info.height; 182 | return 0; 183 | } 184 | 185 | private: 186 | int32_t mDisplayId; 187 | uint32_t mRealWidth; 188 | uint32_t mRealHeight; 189 | uint32_t mDesiredWidth; 190 | uint32_t mDesiredHeight; 191 | uint8_t mDesiredOrientation; 192 | android::sp mBufferProducer; 193 | android::sp mBufferConsumer; 194 | android::sp mConsumer; 195 | android::sp mVirtualDisplay; 196 | android::sp mFrameProxy; 197 | Minicap::FrameAvailableListener* mUserFrameAvailableListener; 198 | bool mHaveBuffer; 199 | bool mHaveRunningDisplay; 200 | android::CpuConsumer::LockedBuffer mBuffer; 201 | 202 | int 203 | createVirtualDisplay() { 204 | uint32_t sourceWidth, sourceHeight; 205 | uint32_t targetWidth, targetHeight; 206 | android::status_t err; 207 | 208 | switch (mDesiredOrientation) { 209 | case Minicap::ORIENTATION_90: 210 | sourceWidth = mRealHeight; 211 | sourceHeight = mRealWidth; 212 | targetWidth = mDesiredHeight; 213 | targetHeight = mDesiredWidth; 214 | break; 215 | case Minicap::ORIENTATION_270: 216 | sourceWidth = mRealHeight; 217 | sourceHeight = mRealWidth; 218 | targetWidth = mDesiredHeight; 219 | targetHeight = mDesiredWidth; 220 | break; 221 | case Minicap::ORIENTATION_180: 222 | sourceWidth = mRealWidth; 223 | sourceHeight = mRealHeight; 224 | targetWidth = mDesiredWidth; 225 | targetHeight = mDesiredHeight; 226 | break; 227 | case Minicap::ORIENTATION_0: 228 | default: 229 | sourceWidth = mRealWidth; 230 | sourceHeight = mRealHeight; 231 | targetWidth = mDesiredWidth; 232 | targetHeight = mDesiredHeight; 233 | break; 234 | } 235 | 236 | // Set up virtual display size. 237 | android::Rect layerStackRect(sourceWidth, sourceHeight); 238 | android::Rect visibleRect(targetWidth, targetHeight); 239 | 240 | // Create a Surface for the virtual display to write to. 241 | MCINFO("Creating SurfaceComposerClient"); 242 | android::sp sc = new android::SurfaceComposerClient(); 243 | 244 | MCINFO("Performing SurfaceComposerClient init check"); 245 | if ((err = sc->initCheck()) != android::NO_ERROR) { 246 | MCERROR("Unable to initialize SurfaceComposerClient"); 247 | return err; 248 | } 249 | 250 | // This is now REQUIRED in O Developer Preview 1 or there's a segfault 251 | // when the sp goes out of scope. 252 | sc = NULL; 253 | 254 | // Create virtual display. 255 | MCINFO("Creating virtual display"); 256 | mVirtualDisplay = android::SurfaceComposerClient::createDisplay( 257 | /* const String8& displayName */ android::String8("minicap"), 258 | /* bool secure */ true 259 | ); 260 | 261 | MCINFO("Creating buffer queue"); 262 | android::BufferQueue::createBufferQueue(&mBufferProducer, &mBufferConsumer, false); 263 | 264 | MCINFO("Setting buffer options"); 265 | mBufferConsumer->setDefaultBufferSize(targetWidth, targetHeight); 266 | mBufferConsumer->setDefaultBufferFormat(android::PIXEL_FORMAT_RGBA_8888); 267 | 268 | MCINFO("Creating CPU consumer"); 269 | mConsumer = new android::CpuConsumer(mBufferConsumer, 3, false); 270 | mConsumer->setName(android::String8("minicap")); 271 | 272 | MCINFO("Creating frame waiter"); 273 | mFrameProxy = new FrameProxy(mUserFrameAvailableListener); 274 | mConsumer->setFrameAvailableListener(mFrameProxy); 275 | 276 | MCINFO("Publishing virtual display"); 277 | android::SurfaceComposerClient::openGlobalTransaction(); 278 | android::SurfaceComposerClient::setDisplaySurface(mVirtualDisplay, mBufferProducer); 279 | android::SurfaceComposerClient::setDisplayProjection(mVirtualDisplay, 280 | android::DISPLAY_ORIENTATION_0, layerStackRect, visibleRect); 281 | android::SurfaceComposerClient::setDisplayLayerStack(mVirtualDisplay, 0); // default stack 282 | android::SurfaceComposerClient::closeGlobalTransaction(); 283 | 284 | mHaveRunningDisplay = true; 285 | 286 | return 0; 287 | } 288 | 289 | void 290 | destroyVirtualDisplay() { 291 | MCINFO("Destroying virtual display"); 292 | android::SurfaceComposerClient::destroyDisplay(mVirtualDisplay); 293 | 294 | if (mHaveBuffer) { 295 | mConsumer->unlockBuffer(mBuffer); 296 | mHaveBuffer = false; 297 | } 298 | 299 | mBufferProducer = NULL; 300 | mBufferConsumer = NULL; 301 | mConsumer = NULL; 302 | mFrameProxy = NULL; 303 | mVirtualDisplay = NULL; 304 | 305 | mHaveRunningDisplay = false; 306 | } 307 | 308 | static Minicap::Format 309 | convertFormat(android::PixelFormat format) { 310 | switch (format) { 311 | case android::PIXEL_FORMAT_NONE: 312 | return FORMAT_NONE; 313 | case android::PIXEL_FORMAT_CUSTOM: 314 | return FORMAT_CUSTOM; 315 | case android::PIXEL_FORMAT_TRANSLUCENT: 316 | return FORMAT_TRANSLUCENT; 317 | case android::PIXEL_FORMAT_TRANSPARENT: 318 | return FORMAT_TRANSPARENT; 319 | case android::PIXEL_FORMAT_OPAQUE: 320 | return FORMAT_OPAQUE; 321 | case android::PIXEL_FORMAT_RGBA_8888: 322 | return FORMAT_RGBA_8888; 323 | case android::PIXEL_FORMAT_RGBX_8888: 324 | return FORMAT_RGBX_8888; 325 | case android::PIXEL_FORMAT_RGB_888: 326 | return FORMAT_RGB_888; 327 | case android::PIXEL_FORMAT_RGB_565: 328 | return FORMAT_RGB_565; 329 | case android::PIXEL_FORMAT_BGRA_8888: 330 | return FORMAT_BGRA_8888; 331 | case android::PIXEL_FORMAT_RGBA_5551: 332 | return FORMAT_RGBA_5551; 333 | case android::PIXEL_FORMAT_RGBA_4444: 334 | return FORMAT_RGBA_4444; 335 | default: 336 | return FORMAT_UNKNOWN; 337 | } 338 | } 339 | }; 340 | 341 | int 342 | minicap_try_get_display_info(int32_t displayId, Minicap::DisplayInfo* info) { 343 | android::sp dpy = android::SurfaceComposerClient::getBuiltInDisplay(displayId); 344 | 345 | android::Vector configs; 346 | android::status_t err = android::SurfaceComposerClient::getDisplayConfigs(dpy, &configs); 347 | 348 | if (err != android::NO_ERROR) { 349 | MCERROR("SurfaceComposerClient::getDisplayInfo() failed: %s (%d)\n", error_name(err), err); 350 | return err; 351 | } 352 | 353 | int activeConfig = android::SurfaceComposerClient::getActiveConfig(dpy); 354 | if(static_cast(activeConfig) >= configs.size()) { 355 | MCERROR("Active config %d not inside configs (size %zu)\n", activeConfig, configs.size()); 356 | return android::BAD_VALUE; 357 | } 358 | android::DisplayInfo dinfo = configs[activeConfig]; 359 | 360 | info->width = dinfo.w; 361 | info->height = dinfo.h; 362 | info->orientation = dinfo.orientation; 363 | info->fps = dinfo.fps; 364 | info->density = dinfo.density; 365 | info->xdpi = dinfo.xdpi; 366 | info->ydpi = dinfo.ydpi; 367 | info->secure = dinfo.secure; 368 | info->size = sqrt(pow(dinfo.w / dinfo.xdpi, 2) + pow(dinfo.h / dinfo.ydpi, 2)); 369 | 370 | return 0; 371 | } 372 | 373 | Minicap* 374 | minicap_create(int32_t displayId) { 375 | return new MinicapImpl(displayId); 376 | } 377 | 378 | void 379 | minicap_free(Minicap* mc) { 380 | delete mc; 381 | } 382 | 383 | void 384 | minicap_start_thread_pool() { 385 | android::ProcessState::self()->startThreadPool(); 386 | } 387 | -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/src/minicap_28.cpp: -------------------------------------------------------------------------------- 1 | #include "Minicap.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include "mcdebug.h" 28 | 29 | static const char* 30 | error_name(int32_t err) { 31 | switch (err) { 32 | case android::NO_ERROR: // also android::OK 33 | return "NO_ERROR"; 34 | case android::UNKNOWN_ERROR: 35 | return "UNKNOWN_ERROR"; 36 | case android::NO_MEMORY: 37 | return "NO_MEMORY"; 38 | case android::INVALID_OPERATION: 39 | return "INVALID_OPERATION"; 40 | case android::BAD_VALUE: 41 | return "BAD_VALUE"; 42 | case android::BAD_TYPE: 43 | return "BAD_TYPE"; 44 | case android::NAME_NOT_FOUND: 45 | return "NAME_NOT_FOUND"; 46 | case android::PERMISSION_DENIED: 47 | return "PERMISSION_DENIED"; 48 | case android::NO_INIT: 49 | return "NO_INIT"; 50 | case android::ALREADY_EXISTS: 51 | return "ALREADY_EXISTS"; 52 | case android::DEAD_OBJECT: // also android::JPARKS_BROKE_IT 53 | return "DEAD_OBJECT"; 54 | case android::FAILED_TRANSACTION: 55 | return "FAILED_TRANSACTION"; 56 | case android::BAD_INDEX: 57 | return "BAD_INDEX"; 58 | case android::NOT_ENOUGH_DATA: 59 | return "NOT_ENOUGH_DATA"; 60 | case android::WOULD_BLOCK: 61 | return "WOULD_BLOCK"; 62 | case android::TIMED_OUT: 63 | return "TIMED_OUT"; 64 | case android::UNKNOWN_TRANSACTION: 65 | return "UNKNOWN_TRANSACTION"; 66 | case android::FDS_NOT_ALLOWED: 67 | return "FDS_NOT_ALLOWED"; 68 | default: 69 | return "UNMAPPED_ERROR"; 70 | } 71 | } 72 | 73 | class FrameProxy: public android::ConsumerBase::FrameAvailableListener { 74 | public: 75 | FrameProxy(Minicap::FrameAvailableListener* listener): mUserListener(listener) { 76 | } 77 | 78 | virtual void 79 | onFrameAvailable(const android::BufferItem& /* item */) { 80 | mUserListener->onFrameAvailable(); 81 | } 82 | 83 | private: 84 | Minicap::FrameAvailableListener* mUserListener; 85 | }; 86 | 87 | class MinicapImpl: public Minicap 88 | { 89 | public: 90 | MinicapImpl(int32_t displayId) 91 | : mDisplayId(displayId), 92 | mRealWidth(0), 93 | mRealHeight(0), 94 | mDesiredWidth(0), 95 | mDesiredHeight(0), 96 | mDesiredOrientation(0), 97 | mHaveBuffer(false), 98 | mHaveRunningDisplay(false) { 99 | } 100 | 101 | virtual 102 | ~MinicapImpl() { 103 | release(); 104 | } 105 | 106 | virtual int 107 | applyConfigChanges() { 108 | if (mHaveRunningDisplay) { 109 | destroyVirtualDisplay(); 110 | } 111 | 112 | return createVirtualDisplay(); 113 | } 114 | 115 | virtual int 116 | consumePendingFrame(Minicap::Frame* frame) { 117 | android::status_t err; 118 | 119 | if ((err = mConsumer->lockNextBuffer(&mBuffer)) != android::NO_ERROR) { 120 | if (err == -EINTR) { 121 | return err; 122 | } 123 | else { 124 | MCERROR("Unable to lock next buffer %s (%d)", error_name(err), err); 125 | return err; 126 | } 127 | } 128 | 129 | frame->data = mBuffer.data; 130 | frame->format = convertFormat(mBuffer.format); 131 | frame->width = mBuffer.width; 132 | frame->height = mBuffer.height; 133 | frame->stride = mBuffer.stride; 134 | frame->bpp = android::bytesPerPixel(mBuffer.format); 135 | frame->size = mBuffer.stride * mBuffer.height * frame->bpp; 136 | 137 | mHaveBuffer = true; 138 | 139 | return 0; 140 | } 141 | 142 | virtual Minicap::CaptureMethod 143 | getCaptureMethod() { 144 | return METHOD_VIRTUAL_DISPLAY; 145 | } 146 | 147 | virtual int32_t 148 | getDisplayId() { 149 | return mDisplayId; 150 | } 151 | 152 | virtual void 153 | release() { 154 | destroyVirtualDisplay(); 155 | } 156 | 157 | virtual void 158 | releaseConsumedFrame(Minicap::Frame* /* frame */) { 159 | if (mHaveBuffer) { 160 | mConsumer->unlockBuffer(mBuffer); 161 | mHaveBuffer = false; 162 | } 163 | } 164 | 165 | virtual int 166 | setDesiredInfo(const Minicap::DisplayInfo& info) { 167 | mDesiredWidth = info.width; 168 | mDesiredHeight = info.height; 169 | mDesiredOrientation = info.orientation; 170 | return 0; 171 | } 172 | 173 | virtual void 174 | setFrameAvailableListener(Minicap::FrameAvailableListener* listener) { 175 | mUserFrameAvailableListener = listener; 176 | } 177 | 178 | virtual int 179 | setRealInfo(const Minicap::DisplayInfo& info) { 180 | mRealWidth = info.width; 181 | mRealHeight = info.height; 182 | return 0; 183 | } 184 | 185 | private: 186 | int32_t mDisplayId; 187 | uint32_t mRealWidth; 188 | uint32_t mRealHeight; 189 | uint32_t mDesiredWidth; 190 | uint32_t mDesiredHeight; 191 | uint8_t mDesiredOrientation; 192 | android::sp mBufferProducer; 193 | android::sp mBufferConsumer; 194 | android::sp mConsumer; 195 | android::sp mVirtualDisplay; 196 | android::sp mFrameProxy; 197 | Minicap::FrameAvailableListener* mUserFrameAvailableListener; 198 | bool mHaveBuffer; 199 | bool mHaveRunningDisplay; 200 | android::CpuConsumer::LockedBuffer mBuffer; 201 | 202 | int 203 | createVirtualDisplay() { 204 | uint32_t sourceWidth, sourceHeight; 205 | uint32_t targetWidth, targetHeight; 206 | android::status_t err; 207 | 208 | switch (mDesiredOrientation) { 209 | case Minicap::ORIENTATION_90: 210 | sourceWidth = mRealHeight; 211 | sourceHeight = mRealWidth; 212 | targetWidth = mDesiredHeight; 213 | targetHeight = mDesiredWidth; 214 | break; 215 | case Minicap::ORIENTATION_270: 216 | sourceWidth = mRealHeight; 217 | sourceHeight = mRealWidth; 218 | targetWidth = mDesiredHeight; 219 | targetHeight = mDesiredWidth; 220 | break; 221 | case Minicap::ORIENTATION_180: 222 | sourceWidth = mRealWidth; 223 | sourceHeight = mRealHeight; 224 | targetWidth = mDesiredWidth; 225 | targetHeight = mDesiredHeight; 226 | break; 227 | case Minicap::ORIENTATION_0: 228 | default: 229 | sourceWidth = mRealWidth; 230 | sourceHeight = mRealHeight; 231 | targetWidth = mDesiredWidth; 232 | targetHeight = mDesiredHeight; 233 | break; 234 | } 235 | 236 | // Set up virtual display size. 237 | android::Rect layerStackRect(sourceWidth, sourceHeight); 238 | android::Rect visibleRect(targetWidth, targetHeight); 239 | 240 | // Create a Surface for the virtual display to write to. 241 | MCINFO("Creating SurfaceComposerClient"); 242 | android::sp sc = new android::SurfaceComposerClient(); 243 | 244 | MCINFO("Performing SurfaceComposerClient init check"); 245 | if ((err = sc->initCheck()) != android::NO_ERROR) { 246 | MCERROR("Unable to initialize SurfaceComposerClient"); 247 | return err; 248 | } 249 | 250 | // This is now REQUIRED in O Developer Preview 1 or there's a segfault 251 | // when the sp goes out of scope. 252 | sc = NULL; 253 | 254 | // Create virtual display. 255 | MCINFO("Creating virtual display"); 256 | mVirtualDisplay = android::SurfaceComposerClient::createDisplay( 257 | /* const String8& displayName */ android::String8("minicap"), 258 | /* bool secure */ true 259 | ); 260 | 261 | MCINFO("Creating buffer queue"); 262 | android::BufferQueue::createBufferQueue(&mBufferProducer, &mBufferConsumer, false); 263 | 264 | MCINFO("Setting buffer options"); 265 | mBufferConsumer->setDefaultBufferSize(targetWidth, targetHeight); 266 | mBufferConsumer->setDefaultBufferFormat(android::PIXEL_FORMAT_RGBA_8888); 267 | 268 | MCINFO("Creating CPU consumer"); 269 | mConsumer = new android::CpuConsumer(mBufferConsumer, 3, false); 270 | mConsumer->setName(android::String8("minicap")); 271 | 272 | MCINFO("Creating frame waiter"); 273 | mFrameProxy = new FrameProxy(mUserFrameAvailableListener); 274 | mConsumer->setFrameAvailableListener(mFrameProxy); 275 | 276 | MCINFO("Publishing virtual display"); 277 | android::SurfaceComposerClient::Transaction t; 278 | t.setDisplaySurface(mVirtualDisplay, mBufferProducer); 279 | t.setDisplayProjection(mVirtualDisplay, 280 | android::DISPLAY_ORIENTATION_0, layerStackRect, visibleRect); 281 | t.setDisplayLayerStack(mVirtualDisplay, 0); // default stack 282 | t.apply(); 283 | 284 | mHaveRunningDisplay = true; 285 | 286 | return 0; 287 | } 288 | 289 | void 290 | destroyVirtualDisplay() { 291 | MCINFO("Destroying virtual display"); 292 | android::SurfaceComposerClient::destroyDisplay(mVirtualDisplay); 293 | 294 | if (mHaveBuffer) { 295 | mConsumer->unlockBuffer(mBuffer); 296 | mHaveBuffer = false; 297 | } 298 | 299 | mBufferProducer = NULL; 300 | mBufferConsumer = NULL; 301 | mConsumer = NULL; 302 | mFrameProxy = NULL; 303 | mVirtualDisplay = NULL; 304 | 305 | mHaveRunningDisplay = false; 306 | } 307 | 308 | static Minicap::Format 309 | convertFormat(android::PixelFormat format) { 310 | switch (format) { 311 | case android::PIXEL_FORMAT_NONE: 312 | return FORMAT_NONE; 313 | case android::PIXEL_FORMAT_CUSTOM: 314 | return FORMAT_CUSTOM; 315 | case android::PIXEL_FORMAT_TRANSLUCENT: 316 | return FORMAT_TRANSLUCENT; 317 | case android::PIXEL_FORMAT_TRANSPARENT: 318 | return FORMAT_TRANSPARENT; 319 | case android::PIXEL_FORMAT_OPAQUE: 320 | return FORMAT_OPAQUE; 321 | case android::PIXEL_FORMAT_RGBA_8888: 322 | return FORMAT_RGBA_8888; 323 | case android::PIXEL_FORMAT_RGBX_8888: 324 | return FORMAT_RGBX_8888; 325 | case android::PIXEL_FORMAT_RGB_888: 326 | return FORMAT_RGB_888; 327 | case android::PIXEL_FORMAT_RGB_565: 328 | return FORMAT_RGB_565; 329 | case android::PIXEL_FORMAT_BGRA_8888: 330 | return FORMAT_BGRA_8888; 331 | case android::PIXEL_FORMAT_RGBA_5551: 332 | return FORMAT_RGBA_5551; 333 | case android::PIXEL_FORMAT_RGBA_4444: 334 | return FORMAT_RGBA_4444; 335 | default: 336 | return FORMAT_UNKNOWN; 337 | } 338 | } 339 | }; 340 | 341 | int 342 | minicap_try_get_display_info(int32_t displayId, Minicap::DisplayInfo* info) { 343 | android::sp dpy = android::SurfaceComposerClient::getBuiltInDisplay(displayId); 344 | 345 | android::Vector configs; 346 | android::status_t err = android::SurfaceComposerClient::getDisplayConfigs(dpy, &configs); 347 | 348 | if (err != android::NO_ERROR) { 349 | MCERROR("SurfaceComposerClient::getDisplayInfo() failed: %s (%d)\n", error_name(err), err); 350 | return err; 351 | } 352 | 353 | int activeConfig = android::SurfaceComposerClient::getActiveConfig(dpy); 354 | if(static_cast(activeConfig) >= configs.size()) { 355 | MCERROR("Active config %d not inside configs (size %zu)\n", activeConfig, configs.size()); 356 | return android::BAD_VALUE; 357 | } 358 | android::DisplayInfo dinfo = configs[activeConfig]; 359 | 360 | info->width = dinfo.w; 361 | info->height = dinfo.h; 362 | info->orientation = dinfo.orientation; 363 | info->fps = dinfo.fps; 364 | info->density = dinfo.density; 365 | info->xdpi = dinfo.xdpi; 366 | info->ydpi = dinfo.ydpi; 367 | info->secure = dinfo.secure; 368 | info->size = sqrt(pow(dinfo.w / dinfo.xdpi, 2) + pow(dinfo.h / dinfo.ydpi, 2)); 369 | 370 | return 0; 371 | } 372 | 373 | Minicap* 374 | minicap_create(int32_t displayId) { 375 | return new MinicapImpl(displayId); 376 | } 377 | 378 | void 379 | minicap_free(Minicap* mc) { 380 | delete mc; 381 | } 382 | 383 | void 384 | minicap_start_thread_pool() { 385 | android::ProcessState::self()->startThreadPool(); 386 | } 387 | -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/src/minicap_29.cpp: -------------------------------------------------------------------------------- 1 | #include "Minicap.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include "mcdebug.h" 28 | 29 | static const char* 30 | error_name(int32_t err) { 31 | switch (err) { 32 | case android::NO_ERROR: // also android::OK 33 | return "NO_ERROR"; 34 | case android::UNKNOWN_ERROR: 35 | return "UNKNOWN_ERROR"; 36 | case android::NO_MEMORY: 37 | return "NO_MEMORY"; 38 | case android::INVALID_OPERATION: 39 | return "INVALID_OPERATION"; 40 | case android::BAD_VALUE: 41 | return "BAD_VALUE"; 42 | case android::BAD_TYPE: 43 | return "BAD_TYPE"; 44 | case android::NAME_NOT_FOUND: 45 | return "NAME_NOT_FOUND"; 46 | case android::PERMISSION_DENIED: 47 | return "PERMISSION_DENIED"; 48 | case android::NO_INIT: 49 | return "NO_INIT"; 50 | case android::ALREADY_EXISTS: 51 | return "ALREADY_EXISTS"; 52 | case android::DEAD_OBJECT: // also android::JPARKS_BROKE_IT 53 | return "DEAD_OBJECT"; 54 | case android::FAILED_TRANSACTION: 55 | return "FAILED_TRANSACTION"; 56 | case android::BAD_INDEX: 57 | return "BAD_INDEX"; 58 | case android::NOT_ENOUGH_DATA: 59 | return "NOT_ENOUGH_DATA"; 60 | case android::WOULD_BLOCK: 61 | return "WOULD_BLOCK"; 62 | case android::TIMED_OUT: 63 | return "TIMED_OUT"; 64 | case android::UNKNOWN_TRANSACTION: 65 | return "UNKNOWN_TRANSACTION"; 66 | case android::FDS_NOT_ALLOWED: 67 | return "FDS_NOT_ALLOWED"; 68 | default: 69 | return "UNMAPPED_ERROR"; 70 | } 71 | } 72 | 73 | class FrameProxy: public android::ConsumerBase::FrameAvailableListener { 74 | public: 75 | FrameProxy(Minicap::FrameAvailableListener* listener): mUserListener(listener) { 76 | } 77 | 78 | virtual void 79 | onFrameAvailable(const android::BufferItem& /* item */) { 80 | mUserListener->onFrameAvailable(); 81 | } 82 | 83 | private: 84 | Minicap::FrameAvailableListener* mUserListener; 85 | }; 86 | 87 | class MinicapImpl: public Minicap 88 | { 89 | public: 90 | MinicapImpl(int32_t displayId) 91 | : mDisplayId(displayId), 92 | mRealWidth(0), 93 | mRealHeight(0), 94 | mDesiredWidth(0), 95 | mDesiredHeight(0), 96 | mDesiredOrientation(0), 97 | mHaveBuffer(false), 98 | mHaveRunningDisplay(false) { 99 | } 100 | 101 | virtual 102 | ~MinicapImpl() { 103 | release(); 104 | } 105 | 106 | virtual int 107 | applyConfigChanges() { 108 | if (mHaveRunningDisplay) { 109 | destroyVirtualDisplay(); 110 | } 111 | 112 | return createVirtualDisplay(); 113 | } 114 | 115 | virtual int 116 | consumePendingFrame(Minicap::Frame* frame) { 117 | android::status_t err; 118 | 119 | if ((err = mConsumer->lockNextBuffer(&mBuffer)) != android::NO_ERROR) { 120 | if (err == -EINTR) { 121 | return err; 122 | } 123 | else { 124 | MCERROR("Unable to lock next buffer %s (%d)", error_name(err), err); 125 | return err; 126 | } 127 | } 128 | 129 | frame->data = mBuffer.data; 130 | frame->format = convertFormat(mBuffer.format); 131 | frame->width = mBuffer.width; 132 | frame->height = mBuffer.height; 133 | frame->stride = mBuffer.stride; 134 | frame->bpp = android::bytesPerPixel(mBuffer.format); 135 | frame->size = mBuffer.stride * mBuffer.height * frame->bpp; 136 | 137 | mHaveBuffer = true; 138 | 139 | return 0; 140 | } 141 | 142 | virtual Minicap::CaptureMethod 143 | getCaptureMethod() { 144 | return METHOD_VIRTUAL_DISPLAY; 145 | } 146 | 147 | virtual int32_t 148 | getDisplayId() { 149 | return mDisplayId; 150 | } 151 | 152 | virtual void 153 | release() { 154 | destroyVirtualDisplay(); 155 | } 156 | 157 | virtual void 158 | releaseConsumedFrame(Minicap::Frame* /* frame */) { 159 | if (mHaveBuffer) { 160 | mConsumer->unlockBuffer(mBuffer); 161 | mHaveBuffer = false; 162 | } 163 | } 164 | 165 | virtual int 166 | setDesiredInfo(const Minicap::DisplayInfo& info) { 167 | mDesiredWidth = info.width; 168 | mDesiredHeight = info.height; 169 | mDesiredOrientation = info.orientation; 170 | return 0; 171 | } 172 | 173 | virtual void 174 | setFrameAvailableListener(Minicap::FrameAvailableListener* listener) { 175 | mUserFrameAvailableListener = listener; 176 | } 177 | 178 | virtual int 179 | setRealInfo(const Minicap::DisplayInfo& info) { 180 | mRealWidth = info.width; 181 | mRealHeight = info.height; 182 | return 0; 183 | } 184 | 185 | private: 186 | int32_t mDisplayId; 187 | uint32_t mRealWidth; 188 | uint32_t mRealHeight; 189 | uint32_t mDesiredWidth; 190 | uint32_t mDesiredHeight; 191 | uint8_t mDesiredOrientation; 192 | android::sp mBufferProducer; 193 | android::sp mBufferConsumer; 194 | android::sp mConsumer; 195 | android::sp mVirtualDisplay; 196 | android::sp mFrameProxy; 197 | Minicap::FrameAvailableListener* mUserFrameAvailableListener; 198 | bool mHaveBuffer; 199 | bool mHaveRunningDisplay; 200 | android::CpuConsumer::LockedBuffer mBuffer; 201 | 202 | int 203 | createVirtualDisplay() { 204 | uint32_t sourceWidth, sourceHeight; 205 | uint32_t targetWidth, targetHeight; 206 | android::status_t err; 207 | 208 | switch (mDesiredOrientation) { 209 | case Minicap::ORIENTATION_90: 210 | sourceWidth = mRealHeight; 211 | sourceHeight = mRealWidth; 212 | targetWidth = mDesiredHeight; 213 | targetHeight = mDesiredWidth; 214 | break; 215 | case Minicap::ORIENTATION_270: 216 | sourceWidth = mRealHeight; 217 | sourceHeight = mRealWidth; 218 | targetWidth = mDesiredHeight; 219 | targetHeight = mDesiredWidth; 220 | break; 221 | case Minicap::ORIENTATION_180: 222 | sourceWidth = mRealWidth; 223 | sourceHeight = mRealHeight; 224 | targetWidth = mDesiredWidth; 225 | targetHeight = mDesiredHeight; 226 | break; 227 | case Minicap::ORIENTATION_0: 228 | default: 229 | sourceWidth = mRealWidth; 230 | sourceHeight = mRealHeight; 231 | targetWidth = mDesiredWidth; 232 | targetHeight = mDesiredHeight; 233 | break; 234 | } 235 | 236 | // Set up virtual display size. 237 | android::Rect layerStackRect(sourceWidth, sourceHeight); 238 | android::Rect visibleRect(targetWidth, targetHeight); 239 | 240 | // Create a Surface for the virtual display to write to. 241 | MCINFO("Creating SurfaceComposerClient"); 242 | android::sp sc = new android::SurfaceComposerClient(); 243 | 244 | MCINFO("Performing SurfaceComposerClient init check"); 245 | if ((err = sc->initCheck()) != android::NO_ERROR) { 246 | MCERROR("Unable to initialize SurfaceComposerClient"); 247 | return err; 248 | } 249 | 250 | // This is now REQUIRED in O Developer Preview 1 or there's a segfault 251 | // when the sp goes out of scope. 252 | sc = NULL; 253 | 254 | // Create virtual display. 255 | MCINFO("Creating virtual display"); 256 | mVirtualDisplay = android::SurfaceComposerClient::createDisplay( 257 | /* const String8& displayName */ android::String8("minicap"), 258 | /* bool secure */ true 259 | ); 260 | 261 | MCINFO("Creating buffer queue"); 262 | android::BufferQueue::createBufferQueue(&mBufferProducer, &mBufferConsumer, false); 263 | 264 | MCINFO("Setting buffer options"); 265 | mBufferConsumer->setDefaultBufferSize(targetWidth, targetHeight); 266 | mBufferConsumer->setDefaultBufferFormat(android::PIXEL_FORMAT_RGBA_8888); 267 | 268 | MCINFO("Creating CPU consumer"); 269 | mConsumer = new android::CpuConsumer(mBufferConsumer, 3, false); 270 | mConsumer->setName(android::String8("minicap")); 271 | 272 | MCINFO("Creating frame waiter"); 273 | mFrameProxy = new FrameProxy(mUserFrameAvailableListener); 274 | mConsumer->setFrameAvailableListener(mFrameProxy); 275 | 276 | MCINFO("Publishing virtual display"); 277 | android::SurfaceComposerClient::Transaction t; 278 | t.setDisplaySurface(mVirtualDisplay, mBufferProducer); 279 | t.setDisplayProjection(mVirtualDisplay, 280 | android::DISPLAY_ORIENTATION_0, layerStackRect, visibleRect); 281 | t.setDisplayLayerStack(mVirtualDisplay, 0); // default stack 282 | t.apply(); 283 | 284 | mHaveRunningDisplay = true; 285 | 286 | return 0; 287 | } 288 | 289 | void 290 | destroyVirtualDisplay() { 291 | MCINFO("Destroying virtual display"); 292 | android::SurfaceComposerClient::destroyDisplay(mVirtualDisplay); 293 | 294 | if (mHaveBuffer) { 295 | mConsumer->unlockBuffer(mBuffer); 296 | mHaveBuffer = false; 297 | } 298 | 299 | mBufferProducer = NULL; 300 | mBufferConsumer = NULL; 301 | mConsumer = NULL; 302 | mFrameProxy = NULL; 303 | mVirtualDisplay = NULL; 304 | 305 | mHaveRunningDisplay = false; 306 | } 307 | 308 | static Minicap::Format 309 | convertFormat(android::PixelFormat format) { 310 | switch (format) { 311 | case android::PIXEL_FORMAT_NONE: 312 | return FORMAT_NONE; 313 | case android::PIXEL_FORMAT_CUSTOM: 314 | return FORMAT_CUSTOM; 315 | case android::PIXEL_FORMAT_TRANSLUCENT: 316 | return FORMAT_TRANSLUCENT; 317 | case android::PIXEL_FORMAT_TRANSPARENT: 318 | return FORMAT_TRANSPARENT; 319 | case android::PIXEL_FORMAT_OPAQUE: 320 | return FORMAT_OPAQUE; 321 | case android::PIXEL_FORMAT_RGBA_8888: 322 | return FORMAT_RGBA_8888; 323 | case android::PIXEL_FORMAT_RGBX_8888: 324 | return FORMAT_RGBX_8888; 325 | case android::PIXEL_FORMAT_RGB_888: 326 | return FORMAT_RGB_888; 327 | case android::PIXEL_FORMAT_RGB_565: 328 | return FORMAT_RGB_565; 329 | case android::PIXEL_FORMAT_BGRA_8888: 330 | return FORMAT_BGRA_8888; 331 | case android::PIXEL_FORMAT_RGBA_5551: 332 | return FORMAT_RGBA_5551; 333 | case android::PIXEL_FORMAT_RGBA_4444: 334 | return FORMAT_RGBA_4444; 335 | default: 336 | return FORMAT_UNKNOWN; 337 | } 338 | } 339 | }; 340 | 341 | int 342 | minicap_try_get_display_info(int32_t displayId, Minicap::DisplayInfo* info) { 343 | android::sp dpy = android::SurfaceComposerClient::getPhysicalDisplayToken(displayId); 344 | if(!dpy) { 345 | MCINFO("could not get display for id: %d, using internal display", displayId); 346 | dpy = android::SurfaceComposerClient::getInternalDisplayToken(); 347 | } 348 | 349 | android::Vector configs; 350 | android::status_t err = android::SurfaceComposerClient::getDisplayConfigs(dpy, &configs); 351 | 352 | if (err != android::NO_ERROR) { 353 | MCERROR("SurfaceComposerClient::getDisplayInfo() failed: %s (%d)\n", error_name(err), err); 354 | return err; 355 | } 356 | 357 | int activeConfig = android::SurfaceComposerClient::getActiveConfig(dpy); 358 | if(static_cast(activeConfig) >= configs.size()) { 359 | MCERROR("Active config %d not inside configs (size %zu)\n", activeConfig, configs.size()); 360 | return android::BAD_VALUE; 361 | } 362 | android::DisplayInfo dinfo = configs[activeConfig]; 363 | 364 | info->width = dinfo.w; 365 | info->height = dinfo.h; 366 | info->orientation = dinfo.orientation; 367 | info->fps = dinfo.fps; 368 | info->density = dinfo.density; 369 | info->xdpi = dinfo.xdpi; 370 | info->ydpi = dinfo.ydpi; 371 | info->secure = dinfo.secure; 372 | info->size = sqrt(pow(dinfo.w / dinfo.xdpi, 2) + pow(dinfo.h / dinfo.ydpi, 2)); 373 | 374 | return 0; 375 | } 376 | 377 | Minicap* 378 | minicap_create(int32_t displayId) { 379 | return new MinicapImpl(displayId); 380 | } 381 | 382 | void 383 | minicap_free(Minicap* mc) { 384 | delete mc; 385 | } 386 | 387 | void 388 | minicap_start_thread_pool() { 389 | android::ProcessState::self()->startThreadPool(); 390 | } 391 | -------------------------------------------------------------------------------- /jni/minicap-shared/aosp/src/minicap_9.cpp: -------------------------------------------------------------------------------- 1 | #include "Minicap.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | #include "mcdebug.h" 21 | 22 | static const char* 23 | error_name(int32_t err) { 24 | switch (err) { 25 | case android::NO_ERROR: // also android::OK 26 | return "NO_ERROR"; 27 | case android::UNKNOWN_ERROR: 28 | return "UNKNOWN_ERROR"; 29 | case android::NO_MEMORY: 30 | return "NO_MEMORY"; 31 | case android::INVALID_OPERATION: 32 | return "INVALID_OPERATION"; 33 | case android::BAD_VALUE: 34 | return "BAD_VALUE"; 35 | case android::BAD_TYPE: 36 | return "BAD_TYPE"; 37 | case android::NAME_NOT_FOUND: 38 | return "NAME_NOT_FOUND"; 39 | case android::PERMISSION_DENIED: 40 | return "PERMISSION_DENIED"; 41 | case android::NO_INIT: 42 | return "NO_INIT"; 43 | case android::ALREADY_EXISTS: 44 | return "ALREADY_EXISTS"; 45 | case android::DEAD_OBJECT: // also android::JPARKS_BROKE_IT 46 | return "DEAD_OBJECT"; 47 | case android::FAILED_TRANSACTION: 48 | return "FAILED_TRANSACTION"; 49 | case android::BAD_INDEX: 50 | return "BAD_INDEX"; 51 | case android::NOT_ENOUGH_DATA: 52 | return "NOT_ENOUGH_DATA"; 53 | case android::WOULD_BLOCK: 54 | return "WOULD_BLOCK"; 55 | case android::TIMED_OUT: 56 | return "TIMED_OUT"; 57 | case android::UNKNOWN_TRANSACTION: 58 | return "UNKNOWN_TRANSACTION"; 59 | default: 60 | return "UNMAPPED_ERROR"; 61 | } 62 | } 63 | 64 | class MinicapImpl: public Minicap { 65 | public: 66 | MinicapImpl(int32_t displayId) 67 | : mDisplayId(displayId), 68 | mComposer(android::ComposerService::getComposerService()), 69 | mDesiredWidth(0), 70 | mDesiredHeight(0) { 71 | } 72 | 73 | virtual 74 | ~MinicapImpl() { 75 | release(); 76 | } 77 | 78 | virtual int 79 | applyConfigChanges() { 80 | mUserFrameAvailableListener->onFrameAvailable(); 81 | return 0; 82 | } 83 | 84 | virtual int 85 | consumePendingFrame(Minicap::Frame* frame) { 86 | uint32_t width, height; 87 | android::PixelFormat format; 88 | android::status_t err; 89 | 90 | mHeap = NULL; 91 | err = mComposer->captureScreen(mDisplayId, &mHeap, 92 | &width, &height, &format, mDesiredWidth, mDesiredHeight); 93 | 94 | if (err != android::NO_ERROR) { 95 | MCERROR("ComposerService::captureScreen() failed %s", error_name(err)); 96 | return err; 97 | } 98 | 99 | frame->data = mHeap->getBase(); 100 | frame->width = width; 101 | frame->height = height; 102 | frame->format = convertFormat(format); 103 | frame->stride = width; 104 | frame->bpp = android::bytesPerPixel(format); 105 | frame->size = mHeap->getSize(); 106 | 107 | return 0; 108 | } 109 | 110 | virtual Minicap::CaptureMethod 111 | getCaptureMethod() { 112 | return METHOD_SCREENSHOT; 113 | } 114 | 115 | virtual int32_t 116 | getDisplayId() { 117 | return mDisplayId; 118 | } 119 | 120 | virtual void 121 | release() { 122 | mHeap = NULL; 123 | } 124 | 125 | virtual void 126 | releaseConsumedFrame(Minicap::Frame* /* frame */) { 127 | mHeap = NULL; 128 | return mUserFrameAvailableListener->onFrameAvailable(); 129 | } 130 | 131 | virtual int 132 | setDesiredInfo(const Minicap::DisplayInfo& info) { 133 | mDesiredWidth = info.width; 134 | mDesiredHeight = info.height; 135 | return 0; 136 | } 137 | 138 | virtual void 139 | setFrameAvailableListener(Minicap::FrameAvailableListener* listener) { 140 | mUserFrameAvailableListener = listener; 141 | } 142 | 143 | virtual int 144 | setRealInfo(const Minicap::DisplayInfo& info) { 145 | return 0; 146 | } 147 | 148 | private: 149 | int32_t mDisplayId; 150 | android::sp mComposer; 151 | android::sp mHeap; 152 | uint32_t mDesiredWidth; 153 | uint32_t mDesiredHeight; 154 | Minicap::FrameAvailableListener* mUserFrameAvailableListener; 155 | 156 | static Minicap::Format 157 | convertFormat(android::PixelFormat format) { 158 | switch (format) { 159 | case android::PIXEL_FORMAT_NONE: 160 | return FORMAT_NONE; 161 | case android::PIXEL_FORMAT_CUSTOM: 162 | return FORMAT_CUSTOM; 163 | case android::PIXEL_FORMAT_TRANSLUCENT: 164 | return FORMAT_TRANSLUCENT; 165 | case android::PIXEL_FORMAT_TRANSPARENT: 166 | return FORMAT_TRANSPARENT; 167 | case android::PIXEL_FORMAT_OPAQUE: 168 | return FORMAT_OPAQUE; 169 | case android::PIXEL_FORMAT_RGBA_8888: 170 | return FORMAT_RGBA_8888; 171 | case android::PIXEL_FORMAT_RGBX_8888: 172 | return FORMAT_RGBX_8888; 173 | case android::PIXEL_FORMAT_RGB_888: 174 | return FORMAT_RGB_888; 175 | case android::PIXEL_FORMAT_RGB_565: 176 | return FORMAT_RGB_565; 177 | case android::PIXEL_FORMAT_BGRA_8888: 178 | return FORMAT_BGRA_8888; 179 | case android::PIXEL_FORMAT_RGBA_5551: 180 | return FORMAT_RGBA_5551; 181 | case android::PIXEL_FORMAT_RGBA_4444: 182 | return FORMAT_RGBA_4444; 183 | default: 184 | return FORMAT_UNKNOWN; 185 | } 186 | } 187 | }; 188 | 189 | int 190 | minicap_try_get_display_info(int32_t displayId, Minicap::DisplayInfo* info) { 191 | android::DisplayInfo dinfo; 192 | android::status_t err = android::SurfaceComposerClient::getDisplayInfo(displayId, &dinfo); 193 | 194 | if (err != android::NO_ERROR) { 195 | MCERROR("SurfaceComposerClient::getDisplayInfo() failed: %s (%d)\n", error_name(err), err); 196 | return err; 197 | } 198 | 199 | info->width = dinfo.w; 200 | info->height = dinfo.h; 201 | info->orientation = dinfo.orientation; 202 | info->fps = dinfo.fps; 203 | info->density = dinfo.density; 204 | info->xdpi = dinfo.xdpi; 205 | info->ydpi = dinfo.ydpi; 206 | info->secure = false; 207 | info->size = sqrt(pow(dinfo.w / dinfo.xdpi, 2) + pow(dinfo.h / dinfo.ydpi, 2)); 208 | 209 | return 0; 210 | } 211 | 212 | Minicap* 213 | minicap_create(int32_t displayId) { 214 | return new MinicapImpl(displayId); 215 | } 216 | 217 | void 218 | minicap_free(Minicap* mc) { 219 | delete mc; 220 | } 221 | 222 | void 223 | minicap_start_thread_pool() { 224 | android::ProcessState::self()->startThreadPool(); 225 | } 226 | -------------------------------------------------------------------------------- /jni/minicap-shared/mock/Minicap.cpp: -------------------------------------------------------------------------------- 1 | // The only purpose of this file is to make the project build without any 2 | // complaints about missing libraries (which exist on the device side). 3 | // While we could also use one of the actual built libs, they might depend 4 | // on other shared libraries, making this right here the easiest way to 5 | // enable interoperability. 6 | 7 | #include "Minicap.hpp" 8 | 9 | int 10 | minicap_try_get_display_info(int32_t displayId, Minicap::DisplayInfo* info) { 11 | return 0; 12 | } 13 | 14 | Minicap* 15 | minicap_create(int32_t displayId) { 16 | return NULL; 17 | } 18 | 19 | void 20 | minicap_free(Minicap* mc) { 21 | } 22 | 23 | void 24 | minicap_start_thread_pool() { 25 | } 26 | -------------------------------------------------------------------------------- /jni/minicap/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | include $(CLEAR_VARS) 3 | 4 | LOCAL_MODULE := minicap-common 5 | 6 | LOCAL_SRC_FILES := \ 7 | JpgEncoder.cpp \ 8 | SimpleServer.cpp \ 9 | minicap.cpp \ 10 | 11 | LOCAL_STATIC_LIBRARIES := \ 12 | libjpeg-turbo \ 13 | 14 | LOCAL_SHARED_LIBRARIES := \ 15 | minicap-shared \ 16 | 17 | include $(BUILD_STATIC_LIBRARY) 18 | 19 | include $(CLEAR_VARS) 20 | 21 | # Enable PIE manually. Will get reset on $(CLEAR_VARS). 22 | LOCAL_CFLAGS += -fPIE 23 | LOCAL_LDFLAGS += -fPIE -pie 24 | 25 | LOCAL_MODULE := minicap 26 | 27 | LOCAL_STATIC_LIBRARIES := minicap-common 28 | 29 | include $(BUILD_EXECUTABLE) 30 | 31 | include $(CLEAR_VARS) 32 | 33 | LOCAL_MODULE := minicap-nopie 34 | 35 | LOCAL_STATIC_LIBRARIES := minicap-common 36 | 37 | include $(BUILD_EXECUTABLE) 38 | -------------------------------------------------------------------------------- /jni/minicap/JpgEncoder.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "JpgEncoder.hpp" 4 | #include "util/debug.h" 5 | 6 | JpgEncoder::JpgEncoder(unsigned int prePadding, unsigned int postPadding) 7 | : mTjHandle(tjInitCompress()), 8 | mSubsampling(TJSAMP_420), 9 | mEncodedData(NULL), 10 | mPrePadding(prePadding), 11 | mPostPadding(postPadding), 12 | mMaxWidth(0), 13 | mMaxHeight(0) 14 | { 15 | } 16 | 17 | JpgEncoder::~JpgEncoder() { 18 | tjFree(mEncodedData); 19 | } 20 | 21 | bool 22 | JpgEncoder::encode(Minicap::Frame* frame, unsigned int quality) { 23 | unsigned char* offset = getEncodedData(); 24 | 25 | return 0 == tjCompress2( 26 | mTjHandle, 27 | (unsigned char*) frame->data, 28 | frame->width, 29 | frame->stride * frame->bpp, 30 | frame->height, 31 | convertFormat(frame->format), 32 | &offset, 33 | &mEncodedSize, 34 | mSubsampling, 35 | quality, 36 | TJFLAG_FASTDCT | TJFLAG_NOREALLOC 37 | ); 38 | } 39 | 40 | int 41 | JpgEncoder::getEncodedSize() { 42 | return mEncodedSize; 43 | } 44 | 45 | unsigned char* 46 | JpgEncoder::getEncodedData() { 47 | return mEncodedData + mPrePadding; 48 | } 49 | 50 | bool 51 | JpgEncoder::reserveData(uint32_t width, uint32_t height) { 52 | if (width == mMaxWidth && height == mMaxHeight) { 53 | return 0; 54 | } 55 | 56 | tjFree(mEncodedData); 57 | 58 | unsigned long maxSize = mPrePadding + mPostPadding + tjBufSize( 59 | width, 60 | height, 61 | mSubsampling 62 | ); 63 | 64 | MCINFO("Allocating %ld bytes for JPG encoder", maxSize); 65 | 66 | mEncodedData = tjAlloc(maxSize); 67 | 68 | if (mEncodedData == NULL) { 69 | return false; 70 | } 71 | 72 | mMaxWidth = width; 73 | mMaxHeight = height; 74 | 75 | return true; 76 | } 77 | 78 | int 79 | JpgEncoder::convertFormat(Minicap::Format format) { 80 | switch (format) { 81 | case Minicap::FORMAT_RGBA_8888: 82 | return TJPF_RGBA; 83 | case Minicap::FORMAT_RGBX_8888: 84 | return TJPF_RGBX; 85 | case Minicap::FORMAT_RGB_888: 86 | return TJPF_RGB; 87 | case Minicap::FORMAT_BGRA_8888: 88 | return TJPF_BGRA; 89 | default: 90 | throw std::runtime_error("Unsupported pixel format"); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /jni/minicap/JpgEncoder.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MINICAP_JPG_ENCODER_HPP 2 | #define MINICAP_JPG_ENCODER_HPP 3 | 4 | #include 5 | 6 | #include "Minicap.hpp" 7 | 8 | class JpgEncoder { 9 | public: 10 | JpgEncoder(unsigned int prePadding, unsigned int postPadding); 11 | 12 | ~JpgEncoder(); 13 | 14 | bool 15 | encode(Minicap::Frame* frame, unsigned int quality); 16 | 17 | int 18 | getEncodedSize(); 19 | 20 | unsigned char* 21 | getEncodedData(); 22 | 23 | bool 24 | reserveData(uint32_t width, uint32_t height); 25 | 26 | private: 27 | tjhandle mTjHandle; 28 | int mSubsampling; 29 | unsigned int mPrePadding; 30 | unsigned int mPostPadding; 31 | unsigned int mMaxWidth; 32 | unsigned int mMaxHeight; 33 | unsigned char* mEncodedData; 34 | unsigned long mEncodedSize; 35 | 36 | static int 37 | convertFormat(Minicap::Format format); 38 | }; 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /jni/minicap/Projection.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MINICAP_PROJECTION_HPP 2 | #define MINICAP_PROJECTION_HPP 3 | 4 | #include 5 | #include 6 | 7 | class Projection { 8 | public: 9 | class Parser { 10 | public: 11 | Parser(): mState(real_width_start) { 12 | } 13 | 14 | bool 15 | parse(Projection& proj, const char* lo, const char* hi) { 16 | consume: while (lo < hi) { 17 | switch (mState) { 18 | case real_width_start: 19 | if (isDigit(*lo)) { 20 | proj.realWidth += (*lo - 48); 21 | mState = real_width_continued; 22 | lo += 1; 23 | goto consume; 24 | } 25 | return false; 26 | case real_width_continued: 27 | if (isDigit(*lo)) { 28 | proj.realWidth *= 10; 29 | proj.realWidth += (*lo - 48); 30 | lo += 1; 31 | goto consume; 32 | } 33 | if (*lo == 'x') { 34 | mState = real_height_start; 35 | lo += 1; 36 | goto consume; 37 | } 38 | return false; 39 | case real_height_start: 40 | if (isDigit(*lo)) { 41 | proj.realHeight += (*lo - 48); 42 | mState = real_height_continued; 43 | lo += 1; 44 | goto consume; 45 | } 46 | return false; 47 | case real_height_continued: 48 | if (isDigit(*lo)) { 49 | proj.realHeight *= 10; 50 | proj.realHeight += (*lo - 48); 51 | lo += 1; 52 | goto consume; 53 | } 54 | if (*lo == '@') { 55 | mState = virtual_width_start; 56 | lo += 1; 57 | goto consume; 58 | } 59 | return false; 60 | case virtual_width_start: 61 | if (isDigit(*lo)) { 62 | proj.virtualWidth += (*lo - 48); 63 | mState = virtual_width_continued; 64 | lo += 1; 65 | goto consume; 66 | } 67 | return false; 68 | case virtual_width_continued: 69 | if (isDigit(*lo)) { 70 | proj.virtualWidth *= 10; 71 | proj.virtualWidth += (*lo - 48); 72 | lo += 1; 73 | goto consume; 74 | } 75 | if (*lo == 'x') { 76 | mState = virtual_height_start; 77 | lo += 1; 78 | goto consume; 79 | } 80 | return false; 81 | case virtual_height_start: 82 | if (isDigit(*lo)) { 83 | proj.virtualHeight += (*lo - 48); 84 | mState = virtual_height_continued; 85 | lo += 1; 86 | goto consume; 87 | } 88 | return false; 89 | case virtual_height_continued: 90 | if (isDigit(*lo)) { 91 | proj.virtualHeight *= 10; 92 | proj.virtualHeight += (*lo - 48); 93 | lo += 1; 94 | goto consume; 95 | } 96 | if (*lo == '/') { 97 | mState = rotation_start; 98 | lo += 1; 99 | goto consume; 100 | } 101 | return false; 102 | case rotation_start: 103 | if (*lo == '0') { 104 | proj.rotation = 0; 105 | mState = satisfied; 106 | lo += 1; 107 | goto consume; 108 | } 109 | if (*lo == '9') { 110 | mState = rotation_90_2; 111 | lo += 1; 112 | goto consume; 113 | } 114 | if (*lo == '1') { 115 | mState = rotation_180_2; 116 | lo += 1; 117 | goto consume; 118 | } 119 | if (*lo == '2') { 120 | mState = rotation_270_2; 121 | lo += 1; 122 | goto consume; 123 | } 124 | return false; 125 | case rotation_90_2: 126 | if (*lo == '0') { 127 | proj.rotation = 1; 128 | mState = satisfied; 129 | lo += 1; 130 | goto consume; 131 | } 132 | return false; 133 | case rotation_180_2: 134 | if (*lo == '8') { 135 | mState = rotation_180_3; 136 | lo += 1; 137 | goto consume; 138 | } 139 | return false; 140 | case rotation_180_3: 141 | if (*lo == '0') { 142 | proj.rotation = 2; 143 | mState = satisfied; 144 | lo += 1; 145 | goto consume; 146 | } 147 | return false; 148 | case rotation_270_2: 149 | if (*lo == '7') { 150 | mState = rotation_270_3; 151 | lo += 1; 152 | goto consume; 153 | } 154 | return false; 155 | case rotation_270_3: 156 | if (*lo == '0') { 157 | proj.rotation = 3; 158 | mState = satisfied; 159 | lo += 1; 160 | goto consume; 161 | } 162 | return false; 163 | case satisfied: 164 | return false; 165 | } 166 | } 167 | 168 | return mState == satisfied; 169 | } 170 | 171 | private: 172 | enum State { 173 | real_width_start, 174 | real_width_continued, 175 | real_height_start, 176 | real_height_continued, 177 | virtual_width_start, 178 | virtual_width_continued, 179 | virtual_height_start, 180 | virtual_height_continued, 181 | rotation_start, 182 | rotation_90_2, 183 | rotation_180_2, 184 | rotation_180_3, 185 | rotation_270_2, 186 | rotation_270_3, 187 | satisfied, 188 | }; 189 | 190 | State mState; 191 | 192 | inline bool 193 | isDigit(int input) { 194 | return input >= '0' && input <= '9'; 195 | } 196 | }; 197 | 198 | const uint32_t MAX_WIDTH = 10000; 199 | const uint32_t MAX_HEIGHT = 10000; 200 | 201 | uint32_t realWidth; 202 | uint32_t realHeight; 203 | uint32_t virtualWidth; 204 | uint32_t virtualHeight; 205 | uint32_t rotation; 206 | 207 | Projection() 208 | : realWidth(0), 209 | realHeight(0), 210 | virtualWidth(0), 211 | virtualHeight(0), 212 | rotation(0) { 213 | } 214 | 215 | void 216 | forceMaximumSize() { 217 | if (virtualWidth > realWidth) { 218 | virtualWidth = realWidth; 219 | } 220 | 221 | if (virtualHeight > realHeight) { 222 | virtualHeight = realHeight; 223 | } 224 | } 225 | 226 | void 227 | forceAspectRatio() { 228 | double aspect = static_cast(realWidth) / static_cast(realHeight); 229 | 230 | if (virtualHeight > (uint32_t) (virtualWidth / aspect)) { 231 | virtualHeight = static_cast(round(virtualWidth / aspect)); 232 | } 233 | else { 234 | virtualWidth = static_cast(round(virtualHeight * aspect)); 235 | } 236 | } 237 | 238 | bool 239 | valid() { 240 | return realWidth > 0 && realHeight > 0 && 241 | virtualWidth > 0 && virtualHeight > 0 && 242 | virtualWidth <= realWidth && virtualHeight <= realHeight; 243 | } 244 | 245 | friend std::ostream& 246 | operator<< (std::ostream& stream, const Projection& proj) { 247 | stream << proj.realWidth << 'x' << proj.realHeight << '@' 248 | << proj.virtualWidth << 'x' << proj.virtualHeight << '/' << proj.rotation; 249 | return stream; 250 | } 251 | }; 252 | 253 | #endif 254 | -------------------------------------------------------------------------------- /jni/minicap/SimpleServer.cpp: -------------------------------------------------------------------------------- 1 | #include "SimpleServer.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | SimpleServer::SimpleServer(): mFd(0) { 12 | } 13 | 14 | SimpleServer::~SimpleServer() { 15 | if (mFd > 0) { 16 | ::close(mFd); 17 | } 18 | } 19 | 20 | int 21 | SimpleServer::start(const char* sockname) { 22 | int sfd = socket(AF_UNIX, SOCK_STREAM, 0); 23 | 24 | if (sfd < 0) { 25 | return sfd; 26 | } 27 | 28 | struct sockaddr_un addr; 29 | memset(&addr, 0, sizeof(addr)); 30 | addr.sun_family = AF_UNIX; 31 | strncpy(&addr.sun_path[1], sockname, strlen(sockname)); 32 | 33 | if (::bind(sfd, (struct sockaddr*) &addr, 34 | sizeof(sa_family_t) + strlen(sockname) + 1) < 0) { 35 | ::close(sfd); 36 | return -1; 37 | } 38 | 39 | ::listen(sfd, 1); 40 | 41 | mFd = sfd; 42 | 43 | return mFd; 44 | } 45 | 46 | int 47 | SimpleServer::accept() { 48 | struct sockaddr_un addr; 49 | socklen_t addr_len = sizeof(addr); 50 | return ::accept(mFd, (struct sockaddr *) &addr, &addr_len); 51 | } 52 | -------------------------------------------------------------------------------- /jni/minicap/SimpleServer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MINICAP_SIMPLE_SERVER_HPP 2 | #define MINICAP_SIMPLE_SERVER_HPP 3 | 4 | class SimpleServer { 5 | public: 6 | SimpleServer(); 7 | ~SimpleServer(); 8 | 9 | int 10 | start(const char* sockname); 11 | 12 | int accept(); 13 | 14 | private: 15 | int mFd; 16 | }; 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /jni/minicap/util/debug.h: -------------------------------------------------------------------------------- 1 | #ifndef __minicap_dbg_h__ 2 | #define __minicap_dbg_h__ 3 | 4 | // These macros were originally from 5 | // http://c.learncodethehardway.org/book/ex20.html 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #ifdef NDEBUG 12 | #define MCDEBUG(M, ...) 13 | #else 14 | #define MCDEBUG(M, ...) fprintf(stderr, "DEBUG: %s:%d: " M "\n", __FILE__, __LINE__, ##__VA_ARGS__) 15 | #endif 16 | 17 | #define MCCLEAN_ERRNO() (errno == 0 ? "None" : strerror(errno)) 18 | 19 | #define MCERROR(M, ...) fprintf(stderr, "ERROR: (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, MCCLEAN_ERRNO(), ##__VA_ARGS__) 20 | 21 | #define MCWARN(M, ...) fprintf(stderr, "WARN: (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, MCCLEAN_ERRNO(), ##__VA_ARGS__) 22 | 23 | #define MCINFO(M, ...) fprintf(stderr, "INFO: (%s:%d) " M "\n", __FILE__, __LINE__, ##__VA_ARGS__) 24 | 25 | #define MCCHECK(A, M, ...) if(!(A)) { MCERROR(M, ##__VA_ARGS__); errno=0; goto error; } 26 | 27 | #define MCSENTINEL(M, ...) { MCERROR(M, ##__VA_ARGS__); errno=0; goto error; } 28 | 29 | #define MCCHECK_MEM(A) check((A), "Out of memory.") 30 | 31 | #define MCCHECK_DEBUG(A, M, ...) if(!(A)) { MCDEBUG(M, ##__VA_ARGS__); errno=0; goto error; } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /jni/minicap/util/formatter.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MINICAP_UTIL_FORMATTER_H 2 | #define MINICAP_UTIL_FORMATTER_H 3 | 4 | // Originally from http://stackoverflow.com/a/12262626/1540573 5 | class formatter 6 | { 7 | public: 8 | formatter() {} 9 | ~formatter() {} 10 | 11 | template 12 | formatter & operator << (const Type & value) 13 | { 14 | stream_ << value; 15 | return *this; 16 | } 17 | 18 | std::string str() const { return stream_.str(); } 19 | operator std::string () const { return stream_.str(); } 20 | 21 | enum ConvertToString 22 | { 23 | to_str 24 | }; 25 | std::string operator >> (ConvertToString) { return stream_.str(); } 26 | 27 | private: 28 | std::stringstream stream_; 29 | 30 | formatter(const formatter &); 31 | formatter & operator = (formatter &); 32 | }; 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /jni/vendor/Android.mk: -------------------------------------------------------------------------------- 1 | include $(call all-subdir-makefiles) 2 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "minicap-prebuilt", 3 | "version": "2.3.0", 4 | "lockfileVersion": 1 5 | } 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "minicap-prebuilt", 3 | "version": "2.3.2", 4 | "description": "Prebuilt binaries of minicap.", 5 | "keywords": [ 6 | "minicap" 7 | ], 8 | "bugs": { 9 | "url": "https://github.com/openstf/minicap/issues" 10 | }, 11 | "license": "Apache-2.0", 12 | "author": { 13 | "name": "The OpenSTF Project", 14 | "email": "contact@openstf.io", 15 | "url": "https://openstf.io" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/openstf/minicap.git" 20 | }, 21 | "scripts": { 22 | "prepublish": "make" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Fail on error, verbose output 4 | set -exo pipefail 5 | 6 | # Build project 7 | ndk-build NDK_DEBUG=1 1>&2 8 | 9 | # Figure out which ABI and SDK the device has 10 | abi=$(adb shell getprop ro.product.cpu.abi | tr -d '\r') 11 | sdk=$(adb shell getprop ro.build.version.sdk | tr -d '\r') 12 | pre=$(adb shell getprop ro.build.version.preview_sdk | tr -d '\r') 13 | rel=$(adb shell getprop ro.build.version.release | tr -d '\r') 14 | 15 | if [[ -n "$pre" && "$pre" > "0" ]]; then 16 | sdk=$(($sdk + 1)) 17 | fi 18 | 19 | # PIE is only supported since SDK 16 20 | if (($sdk >= 16)); then 21 | bin=minicap 22 | else 23 | bin=minicap-nopie 24 | fi 25 | 26 | args= 27 | if [ "$1" = "autosize" ]; then 28 | set +o pipefail 29 | size=$(adb shell dumpsys window | grep -Eo 'init=[0-9]+x[0-9]+' | head -1 | cut -d= -f 2) 30 | if [ "$size" = "" ]; then 31 | w=$(adb shell dumpsys window | grep -Eo 'DisplayWidth=[0-9]+' | head -1 | cut -d= -f 2) 32 | h=$(adb shell dumpsys window | grep -Eo 'DisplayHeight=[0-9]+' | head -1 | cut -d= -f 2) 33 | size="${w}x${h}" 34 | fi 35 | args="-P $size@$size/0" 36 | set -o pipefail 37 | shift 38 | fi 39 | 40 | # Create a directory for our resources 41 | dir=/data/local/tmp/minicap-devel 42 | # Keep compatible with older devices that don't have `mkdir -p`. 43 | adb shell "mkdir $dir 2>/dev/null || true" 44 | 45 | # Upload the binary 46 | adb push libs/$abi/$bin $dir 47 | 48 | # Upload the shared library 49 | if [ -e jni/minicap-shared/aosp/libs/android-$rel/$abi/minicap.so ]; then 50 | adb push jni/minicap-shared/aosp/libs/android-$rel/$abi/minicap.so $dir 51 | else 52 | adb push jni/minicap-shared/aosp/libs/android-$sdk/$abi/minicap.so $dir 53 | fi 54 | 55 | # Run! 56 | adb shell LD_LIBRARY_PATH=$dir $dir/$bin $args "$@" 57 | 58 | # Clean up 59 | adb shell rm -r $dir 60 | --------------------------------------------------------------------------------