├── .gitignore ├── BasicBeautifyProcessor ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ ├── com │ └── github │ │ ├── guikunzhi │ │ └── beautify │ │ │ └── BasicBeautifyShaders.java │ │ └── piasy │ │ └── cameracompat │ │ └── processor │ │ └── basic │ │ └── BasicBeautifyProcessor.java │ └── jp │ └── co │ └── cyberagent │ └── android │ └── gpuimage │ └── BasicBeautyFilter.java ├── CHANGELOG.md ├── CameraCompat ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── cpp │ ├── CMakeLists.txt │ └── RgbYuvEncoder.c │ ├── java │ └── com │ │ └── github │ │ └── piasy │ │ └── cameracompat │ │ ├── CameraCompat.java │ │ ├── compat │ │ ├── Camera1Helper.java │ │ ├── Camera1PreviewCallback.java │ │ ├── Camera1PreviewFragment.java │ │ ├── Camera2Helper.java │ │ ├── Camera2PreviewCallback.java │ │ ├── Camera2PreviewFragment.java │ │ ├── CameraFrameCallback.java │ │ ├── CameraHelper.java │ │ ├── NoOpChain.java │ │ ├── PreviewFragment.java │ │ ├── PreviewSize.java │ │ └── events │ │ │ ├── CameraAccessError.java │ │ │ ├── SwitchBeautifyEvent.java │ │ │ ├── SwitchCameraEvent.java │ │ │ ├── SwitchFlashEvent.java │ │ │ └── SwitchMirrorEvent.java │ │ ├── gpuimage │ │ ├── GLFilterGroup.java │ │ ├── GLRender.java │ │ └── SurfaceInitCallback.java │ │ ├── processor │ │ ├── DirectChain.java │ │ ├── GPUImageChain.java │ │ ├── Processor.java │ │ ├── ProcessorChain.java │ │ └── RgbYuvConverter.java │ │ └── utils │ │ ├── CameraImageUtil.java │ │ ├── GLUtil.java │ │ └── Profiler.java │ └── res │ └── layout │ └── preview_fragment.xml ├── LICENSE ├── README.md ├── app ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── github │ │ └── piasy │ │ └── cameracompat │ │ └── example │ │ ├── DemoApplication.java │ │ ├── MainActivity.java │ │ └── PublishActivity.java │ └── res │ ├── layout │ ├── activity_main.xml │ ├── activity_profiling.xml │ ├── activity_publish.xml │ └── activity_watch.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── art └── screenshot.jpg ├── build.gradle ├── gradle.properties ├── gradle ├── publish.gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── wiki └── Procedure.md /.gitignore: -------------------------------------------------------------------------------- 1 | **.iml 2 | .idea 3 | .gradle 4 | /local.properties 5 | .DS_Store 6 | **/build 7 | /captures 8 | 9 | /CameraCompat/.externalNativeBuild 10 | /bintray.properties 11 | -------------------------------------------------------------------------------- /BasicBeautifyProcessor/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /BasicBeautifyProcessor/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | apply plugin: 'com.android.library' 26 | //apply from: "https://raw.githubusercontent.com/Piasy/BintrayUploadScript/master/bintray.gradle" 27 | 28 | android { 29 | compileSdkVersion rootProject.ext.androidCompileSdkVersion 30 | buildToolsVersion rootProject.ext.androidBuildToolsVersion 31 | 32 | defaultConfig { 33 | minSdkVersion rootProject.ext.minSdkVersion 34 | targetSdkVersion rootProject.ext.targetSdkVersion 35 | versionCode rootProject.ext.releaseVersionCode 36 | versionName rootProject.ext.releaseVersionName 37 | } 38 | buildTypes { 39 | release { 40 | minifyEnabled false 41 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 42 | } 43 | } 44 | } 45 | 46 | dependencies { 47 | compile project(':CameraCompat') 48 | } 49 | -------------------------------------------------------------------------------- /BasicBeautifyProcessor/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/piasy/tools/android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /BasicBeautifyProcessor/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /BasicBeautifyProcessor/src/main/java/com/github/guikunzhi/beautify/BasicBeautifyShaders.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Guikz 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.guikunzhi.beautify; 26 | 27 | /** 28 | * Created by Piasy{github.com/Piasy} on 7/6/16. 29 | */ 30 | 31 | public final class BasicBeautifyShaders { 32 | public static final String VERTEX_SHADER = 33 | "attribute vec4 position;\n" 34 | + "attribute vec4 inputTextureCoordinate;\n" 35 | + "const int GAUSSIAN_SAMPLES = 9;\n" 36 | + "uniform float texelWidthOffset;\n" 37 | + "uniform float texelHeightOffset;\n" 38 | + "varying vec2 textureCoordinate;\n" 39 | + "varying vec2 blurCoordinates[GAUSSIAN_SAMPLES];\n" 40 | + "\n" 41 | + "void main() {\n" 42 | + " gl_Position = position;\n" 43 | + " textureCoordinate = inputTextureCoordinate.xy;\n" 44 | + " // Calculate the positions for the blur\n" 45 | + " int multiplier = 0;\n" 46 | + " vec2 blurStep;\n" 47 | + " vec2 singleStepOffset = vec2(texelWidthOffset, texelHeightOffset);\n" 48 | + " for (int i = 0; i < GAUSSIAN_SAMPLES; i++) {\n" 49 | + " multiplier = (i - ((GAUSSIAN_SAMPLES - 1) / 2));\n" 50 | + " // Blur in x (horizontal)\n" 51 | + " blurStep = float(multiplier) * singleStepOffset;\n" 52 | + " blurCoordinates[i] = inputTextureCoordinate.xy + blurStep;\n" 53 | + " }\n" 54 | + "}"; 55 | 56 | public static final String FRAGMENT_SHADER = 57 | "uniform sampler2D inputImageTexture;\n" 58 | + "const lowp int GAUSSIAN_SAMPLES = 9;\n" 59 | + "varying mediump vec2 textureCoordinate;\n" 60 | + "varying mediump vec2 blurCoordinates[GAUSSIAN_SAMPLES];\n" 61 | + "\n" 62 | + "uniform mediump float distanceNormalizationFactor;\n" 63 | + "const mediump float smoothDegree = 0.6;\n" 64 | + "\n" 65 | + "void main() {\n" 66 | + " lowp vec4 centralColor;\n" 67 | + " lowp float gaussianWeightTotal;\n" 68 | + " lowp vec4 sum;\n" 69 | + " lowp vec4 sampleColor;\n" 70 | + " lowp float distanceFromCentralColor;\n" 71 | + " lowp float gaussianWeight;\n" 72 | + " mediump vec4 origin = texture2D(inputImageTexture,textureCoordinate);\n" 73 | + "\n" 74 | + " centralColor = texture2D(inputImageTexture, blurCoordinates[4]);\n" 75 | + " gaussianWeightTotal = 0.18;\n" 76 | + " sum = centralColor * 0.18;\n" 77 | + "\n" 78 | + " sampleColor = texture2D(inputImageTexture, blurCoordinates[0]);\n" 79 | + " distanceFromCentralColor = min(distance(centralColor, sampleColor) * " 80 | + "distanceNormalizationFactor, 1.0);\n" 81 | + " gaussianWeight = 0.05 * (1.0 - distanceFromCentralColor);\n" 82 | + " gaussianWeightTotal += gaussianWeight;\n" 83 | + " sum += sampleColor * gaussianWeight;\n" 84 | + "\n" 85 | + " sampleColor = texture2D(inputImageTexture, blurCoordinates[1]);\n" 86 | + " distanceFromCentralColor = min(distance(centralColor, sampleColor) * " 87 | + "distanceNormalizationFactor, 1.0);\n" 88 | + " gaussianWeight = 0.09 * (1.0 - distanceFromCentralColor);\n" 89 | + " gaussianWeightTotal += gaussianWeight;\n" 90 | + " sum += sampleColor * gaussianWeight;\n" 91 | + "\n" 92 | + " sampleColor = texture2D(inputImageTexture, blurCoordinates[2]);\n" 93 | + " distanceFromCentralColor = min(distance(centralColor, sampleColor) * " 94 | + "distanceNormalizationFactor, 1.0);\n" 95 | + " gaussianWeight = 0.12 * (1.0 - distanceFromCentralColor);\n" 96 | + " gaussianWeightTotal += gaussianWeight;\n" 97 | + " sum += sampleColor * gaussianWeight;\n" 98 | + "\n" 99 | + " sampleColor = texture2D(inputImageTexture, blurCoordinates[3]);\n" 100 | + " distanceFromCentralColor = min(distance(centralColor, sampleColor) * " 101 | + "distanceNormalizationFactor, 1.0);\n" 102 | + " gaussianWeight = 0.15 * (1.0 - distanceFromCentralColor);\n" 103 | + " gaussianWeightTotal += gaussianWeight;\n" 104 | + " sum += sampleColor * gaussianWeight;\n" 105 | + "\n" 106 | + " sampleColor = texture2D(inputImageTexture, blurCoordinates[5]);\n" 107 | + " distanceFromCentralColor = min(distance(centralColor, sampleColor) * " 108 | + "distanceNormalizationFactor, 1.0);\n" 109 | + " gaussianWeight = 0.15 * (1.0 - distanceFromCentralColor);\n" 110 | + " gaussianWeightTotal += gaussianWeight;\n" 111 | + " sum += sampleColor * gaussianWeight;\n" 112 | + "\n" 113 | + " sampleColor = texture2D(inputImageTexture, blurCoordinates[6]);\n" 114 | + " distanceFromCentralColor = min(distance(centralColor, sampleColor) * " 115 | + "distanceNormalizationFactor, 1.0);\n" 116 | + " gaussianWeight = 0.12 * (1.0 - distanceFromCentralColor);\n" 117 | + " gaussianWeightTotal += gaussianWeight;\n" 118 | + " sum += sampleColor * gaussianWeight;\n" 119 | + "\n" 120 | + " sampleColor = texture2D(inputImageTexture, blurCoordinates[7]);\n" 121 | + " distanceFromCentralColor = min(distance(centralColor, sampleColor) * " 122 | + "distanceNormalizationFactor, 1.0);\n" 123 | + " gaussianWeight = 0.09 * (1.0 - distanceFromCentralColor);\n" 124 | + " gaussianWeightTotal += gaussianWeight;\n" 125 | + " sum += sampleColor * gaussianWeight;\n" 126 | + "\n" 127 | + " sampleColor = texture2D(inputImageTexture, blurCoordinates[8]);\n" 128 | + " distanceFromCentralColor = min(distance(centralColor, sampleColor) * " 129 | + "distanceNormalizationFactor, 1.0);\n" 130 | + " gaussianWeight = 0.05 * (1.0 - distanceFromCentralColor);\n" 131 | + " gaussianWeightTotal += gaussianWeight;\n" 132 | + " sum += sampleColor * gaussianWeight;\n" 133 | + "\n" 134 | + " mediump vec4 bilateral = sum / gaussianWeightTotal;\n" 135 | + " mediump vec4 smoothOut;\n" 136 | + " lowp float r = origin.r;\n" 137 | + " lowp float g = origin.g;\n" 138 | + " lowp float b = origin.b;\n" 139 | + " if (r > 0.3725 && g > 0.1568 && b > 0.0784 && r > b && (max(max(r, g), b) - min" 140 | + "(min(r, g), b)) > 0.0588 && abs(r-g) > 0.0588) {\n" 141 | + " smoothOut = (1.0 - smoothDegree) * (origin - bilateral) + bilateral;\n" 142 | + " }\n" 143 | + " else {\n" 144 | + " smoothOut = origin;\n" 145 | + " }\n" 146 | + " smoothOut.r = log(1.0 + 0.2 * smoothOut.r)/log(1.2);\n" 147 | + " smoothOut.g = log(1.0 + 0.2 * smoothOut.g)/log(1.2);\n" 148 | + " smoothOut.b = log(1.0 + 0.2 * smoothOut.b)/log(1.2);\n" 149 | + " gl_FragColor = smoothOut;\n" 150 | + "}\n"; 151 | } 152 | -------------------------------------------------------------------------------- /BasicBeautifyProcessor/src/main/java/com/github/piasy/cameracompat/processor/basic/BasicBeautifyProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.processor.basic; 26 | 27 | import android.media.Image; 28 | import com.github.piasy.cameracompat.processor.Processor; 29 | import java.util.ArrayList; 30 | import java.util.List; 31 | import jp.co.cyberagent.android.gpuimage.BasicBeautyFilter; 32 | import jp.co.cyberagent.android.gpuimage.GPUImageFilter; 33 | 34 | /** 35 | * Created by Piasy{github.com/Piasy} on 17/10/2016. 36 | */ 37 | 38 | public class BasicBeautifyProcessor implements Processor { 39 | 40 | @Override 41 | public void setUp() { 42 | } 43 | 44 | @Override 45 | public List getFilters() { 46 | List filters = new ArrayList<>(); 47 | filters.add(new GPUImageFilter()); 48 | filters.add(new BasicBeautyFilter(4)); 49 | return filters; 50 | } 51 | 52 | @Override 53 | public void tearDown() { 54 | } 55 | 56 | @Override 57 | public void onFrameData(byte[] data, int width, int height, 58 | Runnable postProcessedTask) { 59 | 60 | } 61 | 62 | @Override 63 | public void onFrameData(Image image, Runnable postProcessedTask) { 64 | 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /BasicBeautifyProcessor/src/main/java/jp/co/cyberagent/android/gpuimage/BasicBeautyFilter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author wysaid 3 | * @mail admin@wysaid.org 4 | */ 5 | 6 | package jp.co.cyberagent.android.gpuimage; 7 | 8 | import android.opengl.GLES20; 9 | import com.github.guikunzhi.beautify.BasicBeautifyShaders; 10 | 11 | public class BasicBeautyFilter extends GPUImageTwoPassTextureSamplingFilter { 12 | 13 | private float mDistanceNormalizationFactor = 1f; 14 | private float mTexelSpacingMultiplier = 1f; 15 | 16 | public BasicBeautyFilter(float distanceNormalizationFactor) { 17 | super(BasicBeautifyShaders.VERTEX_SHADER, BasicBeautifyShaders.FRAGMENT_SHADER, 18 | BasicBeautifyShaders.VERTEX_SHADER, BasicBeautifyShaders.FRAGMENT_SHADER); 19 | this.mDistanceNormalizationFactor = distanceNormalizationFactor; 20 | this.mTexelSpacingMultiplier = 4.0f; 21 | } 22 | 23 | @Override 24 | public void onInit() { 25 | super.onInit(); 26 | initTexelOffsets(); 27 | } 28 | 29 | protected void initTexelOffsets() { 30 | float ratio = getHorizontalTexelOffsetRatio(); 31 | GPUImageFilter filter = mFilters.get(0); 32 | int distanceNormalizationFactor = GLES20.glGetUniformLocation(filter.getProgram(), 33 | "distanceNormalizationFactor"); 34 | filter.setFloat(distanceNormalizationFactor, this.mDistanceNormalizationFactor); 35 | 36 | int texelWidthOffsetLocation = GLES20.glGetUniformLocation(filter.getProgram(), 37 | "texelWidthOffset"); 38 | int texelHeightOffsetLocation = GLES20.glGetUniformLocation(filter.getProgram(), 39 | "texelHeightOffset"); 40 | filter.setFloat(texelWidthOffsetLocation, ratio / mOutputWidth); 41 | filter.setFloat(texelHeightOffsetLocation, 0); 42 | 43 | ratio = getVerticalTexelOffsetRatio(); 44 | filter = mFilters.get(1); 45 | distanceNormalizationFactor = GLES20.glGetUniformLocation(filter.getProgram(), 46 | "distanceNormalizationFactor"); 47 | filter.setFloat(distanceNormalizationFactor, this.mDistanceNormalizationFactor); 48 | 49 | texelWidthOffsetLocation = GLES20.glGetUniformLocation(filter.getProgram(), 50 | "texelWidthOffset"); 51 | texelHeightOffsetLocation = GLES20.glGetUniformLocation(filter.getProgram(), 52 | "texelHeightOffset"); 53 | 54 | filter.setFloat(texelWidthOffsetLocation, 0); 55 | filter.setFloat(texelHeightOffsetLocation, ratio / mOutputHeight); 56 | } 57 | 58 | @Override 59 | public void onOutputSizeChanged(int width, int height) { 60 | super.onOutputSizeChanged(width, height); 61 | initTexelOffsets(); 62 | } 63 | 64 | public float getVerticalTexelOffsetRatio() { 65 | return mTexelSpacingMultiplier; 66 | } 67 | 68 | public float getHorizontalTexelOffsetRatio() { 69 | return mTexelSpacingMultiplier; 70 | } 71 | 72 | /** 73 | * A normalization factor for the distance between central color and sample color. 74 | */ 75 | public void setDistanceNormalizationFactor(float distanceNormalizationFactor) { 76 | this.mDistanceNormalizationFactor = distanceNormalizationFactor; 77 | runOnDraw(new Runnable() { 78 | @Override 79 | public void run() { 80 | initTexelOffsets(); 81 | } 82 | }); 83 | } 84 | 85 | /** 86 | * A scaling for the size of the applied blur, default of 4.0 87 | */ 88 | public void setTexelSpacingMultiplier(float texelSpacingMultiplier) { 89 | this.mTexelSpacingMultiplier = texelSpacingMultiplier; 90 | runOnDraw(new Runnable() { 91 | @Override 92 | public void run() { 93 | initTexelOffsets(); 94 | } 95 | }); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change log 2 | 3 | + v1.3.0 4 | - move out RxQrCode, handle runtime permission 5 | - findOptSize 6 | + v1.2.5 7 | - try better when decode qr code from image file 8 | + v1.2.3 9 | - generate qr code into file 10 | + v1.2.2 11 | - fix: CameraCompat.reset must be called by user, in activity’s onDestroy 12 | + v1.2.1 13 | - fix memory leak in CameraCompat 14 | -------------------------------------------------------------------------------- /CameraCompat/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'me.tatarka.retrolambda' 3 | apply plugin: 'android-apt' 4 | apply from: "https://raw.githubusercontent.com/Piasy/BintrayUploadScript/master/bintray.gradle" 5 | 6 | apt { 7 | arguments { 8 | fragmentArgsLib true 9 | } 10 | } 11 | 12 | android { 13 | compileSdkVersion rootProject.ext.androidCompileSdkVersion 14 | buildToolsVersion rootProject.ext.androidBuildToolsVersion 15 | 16 | defaultConfig { 17 | minSdkVersion rootProject.ext.minSdkVersion 18 | targetSdkVersion rootProject.ext.targetSdkVersion 19 | versionCode rootProject.ext.releaseVersionCode 20 | versionName rootProject.ext.releaseVersionName 21 | 22 | externalNativeBuild { 23 | cmake { 24 | arguments '-DANDROID_TOOLCHAIN=clang' 25 | } 26 | } 27 | 28 | // annotationProcessor not work for ButterKnife 29 | /*javaCompileOptions { 30 | annotationProcessorOptions { 31 | className 'com.hannesdorfmann.fragmentargs.processor.ArgProcessor' 32 | 33 | // Arguments are optional. 34 | arguments = [ fragmentArgsLib : 'true' ] 35 | } 36 | }*/ 37 | } 38 | buildTypes { 39 | release { 40 | minifyEnabled false 41 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 42 | } 43 | } 44 | externalNativeBuild { 45 | cmake { 46 | path "src/main/cpp/CMakeLists.txt" 47 | } 48 | } 49 | 50 | compileOptions { 51 | sourceCompatibility JavaVersion.VERSION_1_8 52 | targetCompatibility JavaVersion.VERSION_1_8 53 | } 54 | } 55 | 56 | dependencies { 57 | compile "com.android.support:support-v4:$rootProject.ext.androidSupportSdkVersion" 58 | compile 'com.getkeepsafe.relinker:relinker:1.2.1' 59 | compile 'com.github.piasy:safelyandroid:1.2.4' 60 | compile group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.1' 61 | compile "jp.co.cyberagent.android.gpuimage:gpuimage-library:$rootProject.ext.gpuImageVersion" 62 | 63 | compile "com.hannesdorfmann.fragmentargs:annotation:$rootProject.ext.fragmentArgsVersion" 64 | apt "com.hannesdorfmann.fragmentargs:processor:$rootProject.ext.fragmentArgsVersion" 65 | 66 | compile 'org.greenrobot:eventbus:3.0.0' 67 | 68 | compile('com.tbruyelle.rxpermissions2:rxpermissions:0.8.2') { 69 | exclude module: 'rxjava' 70 | } 71 | compile 'io.reactivex.rxjava2:rxjava:2.0.0' 72 | } 73 | -------------------------------------------------------------------------------- /CameraCompat/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/piasy/tools/android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /CameraCompat/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /CameraCompat/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4.1) 2 | 3 | add_library(rgb-yuv-converter-library SHARED 4 | RgbYuvEncoder.c) 5 | 6 | # Include libraries needed for rgb-yuv-converter-library 7 | target_link_libraries(rgb-yuv-converter-library 8 | android 9 | log) 10 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/CameraCompat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat; 26 | 27 | import android.content.Context; 28 | import android.os.Build; 29 | import android.os.Bundle; 30 | import android.support.annotation.IdRes; 31 | import android.support.annotation.IntDef; 32 | import android.support.annotation.WorkerThread; 33 | import android.support.v4.app.FragmentManager; 34 | import com.github.piasy.cameracompat.compat.Camera1PreviewFragment; 35 | import com.github.piasy.cameracompat.compat.Camera1PreviewFragmentBuilder; 36 | import com.github.piasy.cameracompat.compat.Camera2PreviewFragment; 37 | import com.github.piasy.cameracompat.compat.Camera2PreviewFragmentBuilder; 38 | import com.github.piasy.cameracompat.compat.events.SwitchBeautifyEvent; 39 | import com.github.piasy.cameracompat.compat.events.SwitchCameraEvent; 40 | import com.github.piasy.cameracompat.compat.events.SwitchFlashEvent; 41 | import com.github.piasy.cameracompat.compat.events.SwitchMirrorEvent; 42 | import com.github.piasy.cameracompat.processor.DirectChain; 43 | import com.github.piasy.cameracompat.processor.GPUImageChain; 44 | import com.github.piasy.cameracompat.processor.Processor; 45 | import com.github.piasy.cameracompat.processor.ProcessorChain; 46 | import com.github.piasy.cameracompat.processor.RgbYuvConverter; 47 | import com.github.piasy.cameracompat.utils.Profiler; 48 | import java.util.ArrayList; 49 | import java.util.List; 50 | import org.greenrobot.eventbus.EventBus; 51 | 52 | /** 53 | * Created by Piasy{github.com/Piasy} on 5/29/16. 54 | */ 55 | public final class CameraCompat { 56 | 57 | public static final String CAMERA_PREVIEW_FRAGMENT = "CameraPreviewFragment"; 58 | public static final int ERR_PERMISSION = 1; 59 | public static final int ERR_UNKNOWN = 2; 60 | private static final int DEFAULT_WIDTH = 640; 61 | private static final int DEFAULT_HEIGHT = 480; 62 | private static final int REQUEST_CODE = 1234; 63 | private static volatile CameraCompat sCameraCompat; 64 | private final int mPreviewWidth; 65 | private final int mPreviewHeight; 66 | private final boolean mIsFrontCamera; 67 | private final boolean mIsFlashOpen; 68 | private final boolean mIsBeautifyOn; 69 | private final boolean mIsMirrorEnabled; 70 | private final EventBus mEventBus; 71 | private final ProcessorChain mProcessorChain; 72 | private final Profiler mProfiler; 73 | private final VideoCaptureCallback mVideoCaptureCallback; 74 | private final ErrorHandler mErrorHandler; 75 | 76 | private CameraCompat(Builder builder) { 77 | mPreviewWidth = builder.mPreviewWidth; 78 | mPreviewHeight = builder.mPreviewHeight; 79 | mIsFrontCamera = builder.mIsFrontCamera; 80 | mIsFlashOpen = builder.mIsFlashOpen; 81 | mIsBeautifyOn = builder.mIsBeautifyOn; 82 | mIsMirrorEnabled = builder.mIsMirrorEnabled; 83 | mEventBus = builder.mEventBus; 84 | mVideoCaptureCallback = builder.mVideoCaptureCallback; 85 | mErrorHandler = builder.mErrorHandler; 86 | if (builder.mMetricListener != null) { 87 | mProfiler = new Profiler(builder.mMetricListener); 88 | } else { 89 | mProfiler = null; 90 | } 91 | if (builder.mProcessors.isEmpty()) { 92 | mProcessorChain = new DirectChain(mIsFrontCamera, mVideoCaptureCallback); 93 | } else { 94 | mProcessorChain = new GPUImageChain(builder.mProcessors, mIsBeautifyOn, 95 | mIsFrontCamera, mVideoCaptureCallback, mProfiler); 96 | } 97 | } 98 | 99 | public static void init(Context context) { 100 | RgbYuvConverter.loadLibrary(context); 101 | } 102 | 103 | /** 104 | * can't be called from {@link com.github.piasy.cameracompat.compat.PreviewFragment}, 105 | * otherwise recreate will have race condition 106 | */ 107 | public static void reset() { 108 | sCameraCompat = null; 109 | } 110 | 111 | public static CameraCompat getInstance() { 112 | if (sCameraCompat == null) { 113 | throw new IllegalStateException("CameraCompat is not initialized!"); 114 | } 115 | return sCameraCompat; 116 | } 117 | 118 | /** 119 | * a short-cut 120 | */ 121 | @WorkerThread 122 | public static void onError(@ErrorCode int code) { 123 | if (sCameraCompat == null) { 124 | throw new IllegalStateException("CameraCompat is not initialized!"); 125 | } 126 | sCameraCompat.mErrorHandler.onError(code); 127 | } 128 | 129 | /** 130 | * add {@link Camera1PreviewFragment} or {@link Camera2PreviewFragment} to start preview 131 | * 132 | * @param savedInstance to determine whether should add a new fragment 133 | * @param fragmentManager the fragment manager 134 | * @param container the container id to hold this fragment 135 | * @return {@code true} if the fragment is newly added 136 | */ 137 | public boolean startPreview(Bundle savedInstance, FragmentManager fragmentManager, 138 | @IdRes int container) { 139 | // API 21 has bug on Camera2, the YUV_420_888 data has all zero in UV planes, 140 | // ref: comments of `convertYUV_420_888ToRGB` function in this blog post: 141 | // https://attiapp.wordpress.com/2015/06/28/convert-yuv_420_888-to-rgb-with-camera2-api-2/ 142 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { 143 | if (savedInstance == null 144 | || fragmentManager.findFragmentByTag(CAMERA_PREVIEW_FRAGMENT) == null) { 145 | Camera2PreviewFragment fragment = 146 | Camera2PreviewFragmentBuilder.newCamera2PreviewFragment(mIsBeautifyOn, 147 | mIsFlashOpen, mIsFrontCamera, mIsMirrorEnabled, mPreviewHeight, 148 | mPreviewWidth); 149 | fragmentManager.beginTransaction() 150 | .add(container, fragment, CAMERA_PREVIEW_FRAGMENT) 151 | .commit(); 152 | return true; 153 | } 154 | } else { 155 | if (savedInstance == null 156 | || fragmentManager.findFragmentByTag(CAMERA_PREVIEW_FRAGMENT) == null) { 157 | Camera1PreviewFragment fragment = 158 | Camera1PreviewFragmentBuilder.newCamera1PreviewFragment(mIsBeautifyOn, 159 | mIsFlashOpen, mIsFrontCamera, mIsMirrorEnabled, mPreviewHeight, 160 | mPreviewWidth); 161 | fragmentManager.beginTransaction() 162 | .add(container, fragment, CAMERA_PREVIEW_FRAGMENT) 163 | .commit(); 164 | return true; 165 | } 166 | } 167 | return false; 168 | } 169 | 170 | public EventBus getEventBus() { 171 | return mEventBus; 172 | } 173 | 174 | public void switchBeautify() { 175 | mEventBus.post(new SwitchBeautifyEvent()); 176 | } 177 | 178 | public void switchCamera() { 179 | mEventBus.post(new SwitchCameraEvent()); 180 | } 181 | 182 | public void switchFlash() { 183 | mEventBus.post(new SwitchFlashEvent()); 184 | } 185 | 186 | public void switchMirror() { 187 | mEventBus.post(new SwitchMirrorEvent()); 188 | } 189 | 190 | public Profiler getProfiler() { 191 | return mProfiler; 192 | } 193 | 194 | public VideoCaptureCallback getVideoCaptureCallback() { 195 | return mVideoCaptureCallback; 196 | } 197 | 198 | public ProcessorChain getProcessorChain() { 199 | return mProcessorChain; 200 | } 201 | 202 | /** 203 | * Video will have the specified width, but the height will be trimmed. 204 | * 205 | * width > height 206 | */ 207 | public interface VideoCaptureCallback { 208 | /** 209 | * will be called only once, just before the first call of {@link #onFrameData(byte[], int, 210 | * int)} with new size. 211 | */ 212 | @WorkerThread 213 | void onVideoSizeChanged(int width, int height); 214 | 215 | @WorkerThread 216 | void onFrameData(final byte[] data, final int width, final int height); 217 | } 218 | 219 | public interface ErrorHandler { 220 | @WorkerThread 221 | void onError(@ErrorCode int code); 222 | } 223 | 224 | @IntDef(value = { ERR_PERMISSION, ERR_UNKNOWN }) 225 | public @interface ErrorCode { 226 | } 227 | 228 | public static final class Builder { 229 | private final VideoCaptureCallback mVideoCaptureCallback; 230 | private final ErrorHandler mErrorHandler; 231 | private final List mProcessors; 232 | private int mPreviewWidth = DEFAULT_WIDTH; 233 | private int mPreviewHeight = DEFAULT_HEIGHT; 234 | private boolean mIsFrontCamera; 235 | private boolean mIsFlashOpen; 236 | private boolean mIsBeautifyOn; 237 | private boolean mIsMirrorEnabled; 238 | private EventBus mEventBus; 239 | private Profiler.MetricListener mMetricListener; 240 | 241 | public Builder(VideoCaptureCallback videoCaptureCallback, ErrorHandler errorHandler) { 242 | mVideoCaptureCallback = videoCaptureCallback; 243 | mErrorHandler = errorHandler; 244 | mProcessors = new ArrayList<>(); 245 | } 246 | 247 | /** 248 | * @param previewWidth sensor width, e.g. 640*480, width is 640, no matter what orientation 249 | */ 250 | public Builder previewWidth(int previewWidth) { 251 | mPreviewWidth = previewWidth; 252 | return this; 253 | } 254 | 255 | /** 256 | * @param previewHeight sensor width, e.g. 640*480, height is 480, no matter what 257 | * orientation 258 | */ 259 | public Builder previewHeight(int previewHeight) { 260 | mPreviewHeight = previewHeight; 261 | return this; 262 | } 263 | 264 | public Builder frontCamera(boolean frontCamera) { 265 | mIsFrontCamera = frontCamera; 266 | return this; 267 | } 268 | 269 | public Builder flashOpen(boolean flashOpen) { 270 | mIsFlashOpen = flashOpen; 271 | return this; 272 | } 273 | 274 | public Builder beautifyOn(boolean beautifyOn) { 275 | mIsBeautifyOn = beautifyOn; 276 | return this; 277 | } 278 | 279 | public Builder enableMirror(boolean enableMirror) { 280 | mIsMirrorEnabled = enableMirror; 281 | return this; 282 | } 283 | 284 | public Builder eventBus(EventBus eventBus) { 285 | mEventBus = eventBus; 286 | return this; 287 | } 288 | 289 | public Builder addProcessor(Processor processor) { 290 | mProcessors.add(processor); 291 | return this; 292 | } 293 | 294 | public Builder metricListener(Profiler.MetricListener metricListener) { 295 | mMetricListener = metricListener; 296 | return this; 297 | } 298 | 299 | public CameraCompat build() { 300 | if (mEventBus == null) { 301 | mEventBus = EventBus.builder().build(); 302 | } 303 | CameraCompat instance = new CameraCompat(this); 304 | sCameraCompat = instance; 305 | return instance; 306 | } 307 | } 308 | } 309 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/compat/Camera1Helper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.compat; 26 | 27 | import android.annotation.TargetApi; 28 | import android.hardware.Camera; 29 | import android.os.Build; 30 | import com.github.piasy.cameracompat.CameraCompat; 31 | import java.util.ArrayList; 32 | import java.util.List; 33 | import jp.co.cyberagent.android.gpuimage.Rotation; 34 | 35 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 36 | class Camera1Helper extends CameraHelper { 37 | 38 | private final CameraController mCameraController; 39 | private final Camera1PreviewCallback mPreviewCallback; 40 | private Camera mCamera; 41 | 42 | Camera1Helper(int previewWidth, int previewHeight, int activityRotation, boolean isFront, 43 | CameraController cameraController, Camera1PreviewCallback previewCallback) { 44 | super(previewWidth, previewHeight, activityRotation, isFront); 45 | mCameraController = cameraController; 46 | mPreviewCallback = previewCallback; 47 | } 48 | 49 | @Override 50 | protected boolean startPreview() { 51 | try { 52 | mCamera = openCamera(); 53 | Camera.Parameters parameters = mCamera.getParameters(); 54 | if (parameters.getSupportedFocusModes() 55 | .contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) { 56 | parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); 57 | } 58 | PreviewSize previewSize = findOptSize(mPreviewWidth, mPreviewHeight); 59 | parameters.setPreviewSize(previewSize.getWidth(), previewSize.getHeight()); 60 | mCamera.setParameters(parameters); 61 | mCamera.setPreviewCallback(mPreviewCallback); 62 | Rotation rotation = getRotation(); 63 | mCamera.setDisplayOrientation(mIsFront ? (360 - rotation.asInt()) : rotation.asInt()); 64 | mCameraController.onOpened(mCamera, rotation, mIsFront, false); 65 | } catch (RuntimeException e) { 66 | CameraCompat.onError(CameraCompat.ERR_UNKNOWN); 67 | return false; 68 | } 69 | return true; 70 | } 71 | 72 | @Override 73 | protected boolean stopPreview() { 74 | if (mCamera == null) { 75 | return true; 76 | } 77 | try { 78 | mCamera.stopPreview(); 79 | mCamera.setPreviewCallback(null); 80 | mCamera.release(); 81 | mCamera = null; 82 | } catch (RuntimeException e) { 83 | CameraCompat.onError(CameraCompat.ERR_UNKNOWN); 84 | return false; 85 | } 86 | return true; 87 | } 88 | 89 | @Override 90 | protected int getSensorDegree() { 91 | return getCameraInfo(getCurrentCameraId()).orientation; 92 | } 93 | 94 | @Override 95 | protected boolean canOperateFlash() { 96 | return mCamera != null; 97 | } 98 | 99 | @Override 100 | protected void doOpenFlash() throws RuntimeException { 101 | Camera.Parameters parameters = mCamera.getParameters(); 102 | parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); 103 | mCamera.setParameters(parameters); 104 | } 105 | 106 | @Override 107 | protected void doCloseFlash() throws RuntimeException { 108 | Camera.Parameters parameters = mCamera.getParameters(); 109 | parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); 110 | mCamera.setParameters(parameters); 111 | } 112 | 113 | @Override 114 | protected List getSupportedSize() throws RuntimeException { 115 | List supportedSize = mCamera.getParameters().getSupportedPreviewSizes(); 116 | List results = new ArrayList<>(); 117 | for (int i = 0, size = supportedSize.size(); i < size; i++) { 118 | Camera.Size option = supportedSize.get(i); 119 | results.add(new PreviewSize(option.width, option.height)); 120 | } 121 | return results; 122 | } 123 | 124 | private Camera openCamera() { 125 | return Camera.open(mIsFront ? Camera.CameraInfo.CAMERA_FACING_FRONT 126 | : Camera.CameraInfo.CAMERA_FACING_BACK); 127 | } 128 | 129 | private int getCurrentCameraId() { 130 | return mIsFront ? Camera.CameraInfo.CAMERA_FACING_FRONT 131 | : Camera.CameraInfo.CAMERA_FACING_BACK; 132 | } 133 | 134 | private Camera.CameraInfo getCameraInfo(final int cameraId) { 135 | Camera.CameraInfo info = new Camera.CameraInfo(); 136 | Camera.getCameraInfo(cameraId, info); 137 | return info; 138 | } 139 | 140 | interface CameraController { 141 | void onOpened(final Camera camera, final Rotation rotation, final boolean flipHorizontal, 142 | final boolean flipVertical); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/compat/Camera1PreviewCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.compat; 26 | 27 | import android.annotation.TargetApi; 28 | import android.hardware.Camera; 29 | import android.os.Build; 30 | import com.github.piasy.cameracompat.CameraCompat; 31 | 32 | /** 33 | * Created by Piasy{github.com/Piasy} on 5/24/16. 34 | */ 35 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 36 | class Camera1PreviewCallback implements Camera.PreviewCallback { 37 | private final CameraFrameCallback mCameraFrameCallback; 38 | 39 | Camera1PreviewCallback(CameraFrameCallback cameraFrameCallback) { 40 | mCameraFrameCallback = cameraFrameCallback; 41 | } 42 | 43 | @Override 44 | public void onPreviewFrame(final byte[] data, final Camera camera) { 45 | try { 46 | Camera.Size size = camera.getParameters().getPreviewSize(); 47 | mCameraFrameCallback.onFrameData(data, size.width, size.height, () -> { 48 | camera.addCallbackBuffer(data); 49 | // errors will be caught at where this task is executed 50 | }); 51 | } catch (RuntimeException | OutOfMemoryError e) { 52 | CameraCompat.onError(CameraCompat.ERR_UNKNOWN); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/compat/Camera1PreviewFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.compat; 26 | 27 | import android.annotation.TargetApi; 28 | import android.graphics.SurfaceTexture; 29 | import android.hardware.Camera; 30 | import android.os.Build; 31 | import android.os.Bundle; 32 | import android.support.annotation.Nullable; 33 | import android.support.v4.app.Fragment; 34 | import com.github.piasy.cameracompat.CameraCompat; 35 | import com.github.piasy.safelyandroid.misc.CheckUtil; 36 | import com.hannesdorfmann.fragmentargs.annotation.FragmentWithArgs; 37 | import java.io.IOException; 38 | import jp.co.cyberagent.android.gpuimage.Rotation; 39 | 40 | /** 41 | * A simple {@link Fragment} subclass. 42 | */ 43 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 44 | @FragmentWithArgs 45 | public class Camera1PreviewFragment extends PreviewFragment 46 | implements Camera1Helper.CameraController { 47 | 48 | public Camera1PreviewFragment() { 49 | // Required empty public constructor 50 | } 51 | 52 | @Override 53 | public void onCreate(@Nullable Bundle savedInstanceState) { 54 | super.onCreate(savedInstanceState); 55 | Camera1PreviewFragmentBuilder.injectArguments(this); 56 | } 57 | 58 | @Override 59 | protected CameraHelper createCameraHelper() { 60 | int activityRotation = getActivity().getWindowManager().getDefaultDisplay().getRotation(); 61 | return new Camera1Helper(mPreviewWidth, mPreviewHeight, activityRotation, 62 | mIsDefaultFrontCamera, this, new Camera1PreviewCallback(mProcessorChain)); 63 | } 64 | 65 | @Override 66 | public synchronized void onOpened(final Camera camera, Rotation rotation, boolean flipHorizontal, 67 | boolean flipVertical) { 68 | if (!isResumed() || !CheckUtil.nonNull(mProcessorChain)) { 69 | return; 70 | } 71 | mProcessorChain.onCameraOpened(rotation, flipHorizontal, flipVertical, 72 | surfaceTexture -> startPreviewDirectly(surfaceTexture, camera)); 73 | } 74 | 75 | private void startPreviewDirectly(SurfaceTexture surfaceTexture, Camera camera) { 76 | try { 77 | camera.setPreviewTexture(surfaceTexture); 78 | camera.startPreview(); 79 | mProcessorChain.resume(); 80 | } catch (IOException | RuntimeException e) { 81 | CameraCompat.onError(CameraCompat.ERR_UNKNOWN); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/compat/Camera2Helper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.compat; 26 | 27 | import android.annotation.TargetApi; 28 | import android.graphics.ImageFormat; 29 | import android.graphics.SurfaceTexture; 30 | import android.hardware.camera2.CameraAccessException; 31 | import android.hardware.camera2.CameraCaptureSession; 32 | import android.hardware.camera2.CameraCharacteristics; 33 | import android.hardware.camera2.CameraDevice; 34 | import android.hardware.camera2.CameraManager; 35 | import android.hardware.camera2.params.StreamConfigurationMap; 36 | import android.media.ImageReader; 37 | import android.os.Build; 38 | import android.os.Handler; 39 | import android.os.HandlerThread; 40 | import android.support.annotation.NonNull; 41 | import android.text.TextUtils; 42 | import android.util.Size; 43 | import android.view.Surface; 44 | import com.github.piasy.cameracompat.CameraCompat; 45 | import com.github.piasy.cameracompat.compat.events.CameraAccessError; 46 | import java.util.ArrayList; 47 | import java.util.Collections; 48 | import java.util.List; 49 | import jp.co.cyberagent.android.gpuimage.Rotation; 50 | 51 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 52 | class Camera2Helper extends CameraHelper { 53 | 54 | private final CameraController mCameraController; 55 | private final Camera2PreviewCallback mPreviewCallback; 56 | private final CameraManager mCameraManager; 57 | private String mFrontCameraId; 58 | private String mBackCameraId; 59 | private CameraDevice mCameraDevice; 60 | private HandlerThread mBackgroundThread; 61 | private Handler mCamera2Handler; 62 | private CameraCaptureSession mCaptureSession; 63 | private ImageReader mImageReader; 64 | private final CameraDevice.StateCallback mCameraCallback = new CameraDevice.StateCallback() { 65 | @Override 66 | public void onOpened(@NonNull CameraDevice camera) { 67 | try { 68 | mCameraDevice = camera; 69 | mCameraController.onOpened(mCameraDevice, mImageReader, mCamera2Handler, 70 | getRotation(), mIsFront, false); 71 | } catch (IllegalStateException e) { 72 | CameraCompat.onError(CameraCompat.ERR_UNKNOWN); 73 | } 74 | } 75 | 76 | @Override 77 | public void onDisconnected(@NonNull CameraDevice camera) { 78 | camera.close(); 79 | mCameraDevice = null; 80 | } 81 | 82 | @Override 83 | public void onError(@NonNull CameraDevice camera, int error) { 84 | camera.close(); 85 | mCameraDevice = null; 86 | } 87 | }; 88 | private List mOutputTargets; 89 | 90 | Camera2Helper(int previewWidth, int previewHeight, int activityRotation, boolean isFront, 91 | CameraController cameraController, CameraManager cameraManager, 92 | Camera2PreviewCallback previewCallback) throws CameraAccessException { 93 | super(previewWidth, previewHeight, activityRotation, isFront); 94 | mCameraController = cameraController; 95 | mCameraManager = cameraManager; 96 | mPreviewCallback = previewCallback; 97 | initCameraIds(); 98 | } 99 | 100 | void previewSessionStarted(CameraCaptureSession captureSession) { 101 | mCaptureSession = captureSession; 102 | } 103 | 104 | void outputTargetChanged(List targets) { 105 | mOutputTargets = targets; 106 | } 107 | 108 | @Override 109 | protected boolean startPreview() { 110 | try { 111 | mBackgroundThread = new HandlerThread("PreviewFragmentV21Thread"); 112 | mBackgroundThread.start(); 113 | mCamera2Handler = new Handler(mBackgroundThread.getLooper()); 114 | PreviewSize size = findOptSize(mPreviewWidth, mPreviewHeight); 115 | mImageReader = ImageReader.newInstance(size.getWidth(), size.getHeight(), 116 | ImageFormat.YUV_420_888, 2); 117 | mImageReader.setOnImageAvailableListener(mPreviewCallback, mCamera2Handler); 118 | mCameraManager.openCamera(getCurrentCameraId(), mCameraCallback, mCamera2Handler); 119 | } catch (SecurityException | CameraAccessException | IllegalStateException | 120 | CameraAccessError e) { 121 | CameraCompat.onError(CameraCompat.ERR_PERMISSION); 122 | return false; 123 | } catch (RuntimeException e) { 124 | // http://crashes.to/s/3a4227c2262 125 | // http://crashes.to/s/17d9761180d 126 | CameraCompat.onError(CameraCompat.ERR_UNKNOWN); 127 | return false; 128 | } 129 | return true; 130 | } 131 | 132 | @Override 133 | protected boolean stopPreview() { 134 | try { 135 | if (null != mCaptureSession) { 136 | mCaptureSession.close(); 137 | mCaptureSession = null; 138 | } 139 | if (null != mCameraDevice) { 140 | mCameraDevice.close(); 141 | mCameraDevice = null; 142 | } 143 | if (null != mBackgroundThread) { 144 | mBackgroundThread.quitSafely(); 145 | mBackgroundThread.join(); 146 | mBackgroundThread = null; 147 | mCamera2Handler = null; 148 | } 149 | if (mImageReader != null) { 150 | // http://crashes.to/s/099a8255d2b 151 | mImageReader.setOnImageAvailableListener(null, null); 152 | } 153 | } catch (InterruptedException e) { 154 | CameraCompat.onError(CameraCompat.ERR_UNKNOWN); 155 | return false; 156 | } 157 | return true; 158 | } 159 | 160 | @Override 161 | protected int getSensorDegree() { 162 | CameraCharacteristics characteristics; 163 | Integer orientation = null; 164 | try { 165 | characteristics = mCameraManager.getCameraCharacteristics(getCurrentCameraId()); 166 | orientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); 167 | } catch (CameraAccessException | SecurityException e) { 168 | CameraCompat.onError(CameraCompat.ERR_PERMISSION); 169 | } 170 | return orientation == null ? 0 : orientation; 171 | } 172 | 173 | @Override 174 | protected boolean canOperateFlash() { 175 | return mCameraDevice != null; 176 | } 177 | 178 | @Override 179 | protected void doOpenFlash() throws RuntimeException { 180 | try { 181 | operateFlash(true); 182 | } catch (CameraAccessException e) { 183 | throw new CameraAccessError(); 184 | } 185 | } 186 | 187 | @Override 188 | protected void doCloseFlash() throws RuntimeException { 189 | try { 190 | operateFlash(false); 191 | } catch (CameraAccessException e) { 192 | throw new CameraAccessError(); 193 | } 194 | } 195 | 196 | @Override 197 | protected List getSupportedSize() { 198 | try { 199 | CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics( 200 | getCurrentCameraId()); 201 | StreamConfigurationMap map = 202 | characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 203 | if (map == null) { 204 | return Collections.singletonList(new PreviewSize(mPreviewWidth, mPreviewHeight)); 205 | } 206 | Size[] supportedSize = map.getOutputSizes(SurfaceTexture.class); 207 | if (supportedSize == null || supportedSize.length == 0) { 208 | return Collections.singletonList(new PreviewSize(mPreviewWidth, mPreviewHeight)); 209 | } 210 | List results = new ArrayList<>(); 211 | for (Size size : supportedSize) { 212 | results.add(new PreviewSize(size.getWidth(), size.getHeight())); 213 | } 214 | return results; 215 | } catch (CameraAccessException e) { 216 | throw new CameraAccessError(); 217 | } 218 | } 219 | 220 | private void operateFlash(boolean isOpen) throws CameraAccessException, SecurityException { 221 | CameraCharacteristics characteristics = 222 | mCameraManager.getCameraCharacteristics(mBackCameraId); 223 | Boolean available = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); 224 | boolean flashSupported = available == null ? false : available; 225 | if (!flashSupported) { 226 | return; 227 | } 228 | mCameraController.onSettingsChanged(mCameraDevice, mCaptureSession, mOutputTargets, isOpen, 229 | mCamera2Handler); 230 | } 231 | 232 | private void initCameraIds() throws CameraAccessException { 233 | for (String cameraId : mCameraManager.getCameraIdList()) { 234 | CameraCharacteristics characteristics = 235 | mCameraManager.getCameraCharacteristics(cameraId); 236 | 237 | Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING); 238 | if (facing != null) { 239 | if (facing == CameraCharacteristics.LENS_FACING_FRONT) { 240 | mFrontCameraId = cameraId; 241 | } else if (facing == CameraCharacteristics.LENS_FACING_BACK) { 242 | mBackCameraId = cameraId; 243 | } 244 | } 245 | } 246 | } 247 | 248 | private String getCurrentCameraId() throws IllegalStateException { 249 | String id = mIsFront ? mFrontCameraId : mBackCameraId; 250 | if (TextUtils.isEmpty(id)) { 251 | throw new IllegalStateException("Get a null camera id: " + mIsFront); 252 | } 253 | return id; 254 | } 255 | 256 | interface CameraController { 257 | void onOpened(final CameraDevice cameraDevice, final ImageReader imageReader, 258 | final Handler cameraHandler, final Rotation rotation, final boolean flipHorizontal, 259 | final boolean flipVertical); 260 | 261 | void onSettingsChanged(final CameraDevice cameraDevice, 262 | final CameraCaptureSession captureSession, final List targets, 263 | final boolean isFlashOn, final Handler cameraHandler); 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/compat/Camera2PreviewCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.compat; 26 | 27 | import android.annotation.TargetApi; 28 | import android.media.Image; 29 | import android.media.ImageReader; 30 | import android.os.Build; 31 | import com.github.piasy.cameracompat.CameraCompat; 32 | 33 | /** 34 | * Created by Piasy{github.com/Piasy} on 5/24/16. 35 | */ 36 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 37 | class Camera2PreviewCallback implements ImageReader.OnImageAvailableListener { 38 | private final CameraFrameCallback mCameraFrameCallback; 39 | 40 | Camera2PreviewCallback(CameraFrameCallback cameraFrameCallback) { 41 | mCameraFrameCallback = cameraFrameCallback; 42 | } 43 | 44 | @Override 45 | public void onImageAvailable(ImageReader reader) { 46 | try { 47 | final Image image = reader.acquireLatestImage(); 48 | if (image != null) { 49 | mCameraFrameCallback.onFrameData(image, image::close); 50 | } 51 | } catch (OutOfMemoryError | IllegalStateException e) { 52 | CameraCompat.onError(CameraCompat.ERR_UNKNOWN); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/compat/Camera2PreviewFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.compat; 26 | 27 | import android.annotation.TargetApi; 28 | import android.content.Context; 29 | import android.hardware.camera2.CameraAccessException; 30 | import android.hardware.camera2.CameraCaptureSession; 31 | import android.hardware.camera2.CameraDevice; 32 | import android.hardware.camera2.CameraManager; 33 | import android.hardware.camera2.CameraMetadata; 34 | import android.hardware.camera2.CaptureRequest; 35 | import android.media.ImageReader; 36 | import android.os.Build; 37 | import android.os.Bundle; 38 | import android.os.Handler; 39 | import android.support.annotation.NonNull; 40 | import android.support.annotation.Nullable; 41 | import android.view.Surface; 42 | import com.github.piasy.cameracompat.CameraCompat; 43 | import com.github.piasy.safelyandroid.misc.CheckUtil; 44 | import com.hannesdorfmann.fragmentargs.annotation.FragmentWithArgs; 45 | import java.util.Arrays; 46 | import java.util.List; 47 | import jp.co.cyberagent.android.gpuimage.Rotation; 48 | 49 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 50 | @FragmentWithArgs 51 | public class Camera2PreviewFragment extends PreviewFragment 52 | implements Camera2Helper.CameraController { 53 | 54 | private Camera2Helper mCamera2Helper; 55 | 56 | public Camera2PreviewFragment() { 57 | // Required empty public constructor 58 | } 59 | 60 | @Override 61 | public void onCreate(@Nullable Bundle savedInstanceState) { 62 | super.onCreate(savedInstanceState); 63 | Camera2PreviewFragmentBuilder.injectArguments(this); 64 | } 65 | 66 | @Override 67 | protected CameraHelper createCameraHelper() { 68 | try { 69 | CameraManager cameraManager = 70 | (CameraManager) getActivity().getSystemService(Context.CAMERA_SERVICE); 71 | int activityRotation = 72 | getActivity().getWindowManager().getDefaultDisplay().getRotation(); 73 | mCamera2Helper = new Camera2Helper(mPreviewWidth, mPreviewHeight, activityRotation, 74 | mIsDefaultFrontCamera, this, cameraManager, 75 | new Camera2PreviewCallback(mProcessorChain)); 76 | } catch (CameraAccessException | SecurityException e) { 77 | CameraCompat.onError(CameraCompat.ERR_PERMISSION); 78 | } catch (AssertionError e) { 79 | // http://crashes.to/s/7e3aebc3390 80 | CameraCompat.onError(CameraCompat.ERR_UNKNOWN); 81 | } 82 | return mCamera2Helper; 83 | } 84 | 85 | /** 86 | * Creates a new {@link CameraCaptureSession} for camera preview. 87 | */ 88 | private void startPreviewDirectly(final CameraDevice cameraDevice, List targets, 89 | final boolean isFlashOn, final Handler cameraHandler) { 90 | try { 91 | if (cameraDevice == null) { 92 | CameraCompat.onError(CameraCompat.ERR_UNKNOWN); 93 | return; 94 | } 95 | final CaptureRequest.Builder captureRequestBuilder = 96 | cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 97 | for (int i = 0, size = targets.size(); i < size; i++) { 98 | captureRequestBuilder.addTarget(targets.get(i)); 99 | } 100 | 101 | cameraDevice.createCaptureSession(targets, new CameraCaptureSession.StateCallback() { 102 | @Override 103 | public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) { 104 | try { 105 | captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, 106 | CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO); 107 | captureRequestBuilder.set(CaptureRequest.FLASH_MODE, 108 | isFlashOn ? CameraMetadata.FLASH_MODE_TORCH 109 | : CameraMetadata.FLASH_MODE_OFF); 110 | 111 | cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), 112 | null, cameraHandler); 113 | mCamera2Helper.previewSessionStarted(cameraCaptureSession); 114 | mProcessorChain.resume(); 115 | } catch (CameraAccessException | SecurityException e) { 116 | CameraCompat.onError(CameraCompat.ERR_PERMISSION); 117 | } catch (IllegalStateException e) { 118 | CameraCompat.onError(CameraCompat.ERR_UNKNOWN); 119 | } 120 | } 121 | 122 | @Override 123 | public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) { 124 | CameraCompat.onError(CameraCompat.ERR_UNKNOWN); 125 | } 126 | }, cameraHandler); 127 | } catch (CameraAccessException | SecurityException e) { 128 | CameraCompat.onError(CameraCompat.ERR_PERMISSION); 129 | } catch (IllegalStateException e) { 130 | CameraCompat.onError(CameraCompat.ERR_UNKNOWN); 131 | } 132 | } 133 | 134 | @Override 135 | public void onOpened(final CameraDevice cameraDevice, final ImageReader imageReader, 136 | final Handler cameraHandler, final Rotation rotation, final boolean flipHorizontal, 137 | final boolean flipVertical) { 138 | if (!isResumed() || !CheckUtil.nonNull(mProcessorChain)) { 139 | return; 140 | } 141 | mProcessorChain.onCameraOpened(rotation, flipHorizontal, flipVertical, 142 | surfaceTexture -> { 143 | // fix MX5 preview not show bug: http://stackoverflow.com/a/34337226/3077508 144 | surfaceTexture.setDefaultBufferSize(mPreviewWidth, mPreviewHeight); 145 | Surface surface = new Surface(surfaceTexture); 146 | List targets = Arrays.asList(surface, imageReader.getSurface()); 147 | mCamera2Helper.outputTargetChanged(targets); 148 | startPreviewDirectly(cameraDevice, targets, mIsDefaultFlashOpen, cameraHandler); 149 | }); 150 | } 151 | 152 | @Override 153 | public void onSettingsChanged(final CameraDevice cameraDevice, 154 | final CameraCaptureSession captureSession, final List targets, 155 | final boolean isFlashOn, final Handler cameraHandler) { 156 | try { 157 | final CaptureRequest.Builder captureRequestBuilder = 158 | cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 159 | for (int i = 0, size = targets.size(); i < size; i++) { 160 | captureRequestBuilder.addTarget(targets.get(i)); 161 | } 162 | captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, 163 | CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO); 164 | captureRequestBuilder.set(CaptureRequest.FLASH_MODE, 165 | isFlashOn ? CameraMetadata.FLASH_MODE_TORCH : CameraMetadata.FLASH_MODE_OFF); 166 | captureSession.setRepeatingRequest(captureRequestBuilder.build(), null, cameraHandler); 167 | } catch (CameraAccessException | SecurityException e) { 168 | CameraCompat.onError(CameraCompat.ERR_PERMISSION); 169 | } catch (IllegalStateException e) { 170 | CameraCompat.onError(CameraCompat.ERR_UNKNOWN); 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/compat/CameraFrameCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.compat; 26 | 27 | import android.annotation.TargetApi; 28 | import android.media.Image; 29 | import android.os.Build; 30 | 31 | /** 32 | * Created by Piasy{github.com/Piasy} on 5/24/16. 33 | */ 34 | public interface CameraFrameCallback { 35 | void onFrameData(final byte[] data, final int width, final int height, 36 | final Runnable postProcessedTask); 37 | 38 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 39 | void onFrameData(final Image image, final Runnable postProcessedTask); 40 | } 41 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/compat/CameraHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.compat; 26 | 27 | import android.view.Surface; 28 | import com.github.piasy.cameracompat.CameraCompat; 29 | import com.github.piasy.cameracompat.compat.events.CameraAccessError; 30 | import java.util.ArrayList; 31 | import java.util.Collections; 32 | import java.util.List; 33 | import jp.co.cyberagent.android.gpuimage.Rotation; 34 | 35 | /** 36 | * Created by Piasy{github.com/Piasy} on 7/21/16. 37 | */ 38 | 39 | abstract class CameraHelper { 40 | protected final int mPreviewWidth; 41 | protected final int mPreviewHeight; 42 | protected boolean mIsFront; 43 | 44 | private final int mActivityRotation; 45 | private boolean mFlashLightOn; 46 | 47 | CameraHelper(int previewWidth, int previewHeight, int activityRotation, boolean isFront) { 48 | mPreviewWidth = previewWidth; 49 | mPreviewHeight = previewHeight; 50 | mActivityRotation = activityRotation; 51 | mIsFront = isFront; 52 | } 53 | 54 | final Rotation getRotation() { 55 | int activityDegree = 0; 56 | switch (mActivityRotation) { 57 | case Surface.ROTATION_0: 58 | activityDegree = 0; 59 | break; 60 | case Surface.ROTATION_90: 61 | activityDegree = 90; 62 | break; 63 | case Surface.ROTATION_180: 64 | activityDegree = 180; 65 | break; 66 | case Surface.ROTATION_270: 67 | activityDegree = 270; 68 | break; 69 | } 70 | 71 | int sensorDegree = getSensorDegree(); 72 | int degree; 73 | if (mIsFront) { 74 | degree = (sensorDegree + activityDegree) % 360; 75 | } else { 76 | degree = (sensorDegree - activityDegree + 360) % 360; 77 | } 78 | Rotation ret = Rotation.NORMAL; 79 | switch (degree) { 80 | case 90: 81 | ret = Rotation.ROTATION_90; 82 | break; 83 | case 180: 84 | ret = Rotation.ROTATION_180; 85 | break; 86 | case 270: 87 | ret = Rotation.ROTATION_270; 88 | break; 89 | } 90 | return ret; 91 | } 92 | 93 | boolean switchCamera() { 94 | if (!stopPreview()) { 95 | return false; 96 | } 97 | mIsFront = !mIsFront; 98 | if (mIsFront) { 99 | mFlashLightOn = false; 100 | } 101 | return startPreview(); 102 | } 103 | 104 | boolean switchFlash() { 105 | if (mIsFront || !canOperateFlash()) { 106 | return false; 107 | } 108 | try { 109 | mFlashLightOn = !mFlashLightOn; 110 | if (mFlashLightOn) { 111 | doOpenFlash(); 112 | } else { 113 | doCloseFlash(); 114 | } 115 | } catch (CameraAccessError | SecurityException e) { 116 | CameraCompat.onError(CameraCompat.ERR_PERMISSION); 117 | return false; 118 | } catch (RuntimeException e) { 119 | CameraCompat.onError(CameraCompat.ERR_UNKNOWN); 120 | return false; 121 | } 122 | 123 | return true; 124 | } 125 | 126 | protected PreviewSize findOptSize(int desiredWidth, int desiredHeight) { 127 | List supportedSize = getSupportedSize(); 128 | List qualifiedSize = new ArrayList<>(); 129 | for (int i = 0, size = supportedSize.size(); i < size; i++) { 130 | PreviewSize option = supportedSize.get(i); 131 | if (desiredWidth > desiredHeight) { 132 | if (option.getWidth() >= desiredWidth && option.getHeight() >= desiredHeight) { 133 | qualifiedSize.add(option); 134 | } 135 | } else { 136 | if (option.getWidth() >= desiredHeight && option.getHeight() >= desiredWidth) { 137 | qualifiedSize.add(option); 138 | } 139 | } 140 | } 141 | if (qualifiedSize.size() > 0) { 142 | return Collections.min(qualifiedSize, (lhs, rhs) -> { 143 | int delta = lhs.getWidth() * lhs.getHeight() - rhs.getWidth() * rhs.getHeight(); 144 | return Integer.signum(delta); 145 | }); 146 | } else { 147 | return new PreviewSize(desiredWidth, desiredHeight); 148 | } 149 | } 150 | 151 | protected abstract boolean startPreview(); 152 | 153 | protected abstract boolean stopPreview(); 154 | 155 | protected abstract int getSensorDegree(); 156 | 157 | protected abstract boolean canOperateFlash(); 158 | 159 | protected abstract void doOpenFlash() throws RuntimeException; 160 | 161 | protected abstract void doCloseFlash() throws RuntimeException; 162 | 163 | protected abstract List getSupportedSize() throws RuntimeException; 164 | } 165 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/compat/NoOpChain.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.compat; 26 | 27 | import android.content.Context; 28 | import android.media.Image; 29 | import android.view.View; 30 | import com.github.piasy.cameracompat.gpuimage.SurfaceInitCallback; 31 | import com.github.piasy.cameracompat.processor.ProcessorChain; 32 | import jp.co.cyberagent.android.gpuimage.Rotation; 33 | 34 | /** 35 | * Created by Piasy{github.com/Piasy} on 27/10/2016. 36 | */ 37 | 38 | final class NoOpChain implements ProcessorChain { 39 | static final NoOpChain INSTANCE = new NoOpChain(); 40 | 41 | private NoOpChain() { 42 | } 43 | 44 | @Override 45 | public void setUp() { 46 | 47 | } 48 | 49 | @Override 50 | public View createSurface(Context context) { 51 | return new View(context); 52 | } 53 | 54 | @Override 55 | public void initSurface(Context context) { 56 | 57 | } 58 | 59 | @Override 60 | public void onCameraOpened(Rotation rotation, boolean flipHorizontal, boolean flipVertical, 61 | SurfaceInitCallback callback) { 62 | 63 | } 64 | 65 | @Override 66 | public void pause() { 67 | 68 | } 69 | 70 | @Override 71 | public void resume() { 72 | 73 | } 74 | 75 | @Override 76 | public void cameraSwitched() { 77 | 78 | } 79 | 80 | @Override 81 | public void switchMirror() { 82 | 83 | } 84 | 85 | @Override 86 | public void tearDown() { 87 | 88 | } 89 | 90 | @Override 91 | public void onFrameData(byte[] data, int width, int height, 92 | Runnable postProcessedTask) { 93 | 94 | } 95 | 96 | @Override 97 | public void onFrameData(Image image, Runnable postProcessedTask) { 98 | 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/compat/PreviewFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.compat; 26 | 27 | import android.Manifest; 28 | import android.content.Context; 29 | import android.os.Bundle; 30 | import android.support.annotation.Nullable; 31 | import android.support.v4.app.Fragment; 32 | import android.view.LayoutInflater; 33 | import android.view.View; 34 | import android.view.ViewGroup; 35 | import com.github.piasy.cameracompat.CameraCompat; 36 | import com.github.piasy.cameracompat.R; 37 | import com.github.piasy.cameracompat.compat.events.SwitchBeautifyEvent; 38 | import com.github.piasy.cameracompat.compat.events.SwitchCameraEvent; 39 | import com.github.piasy.cameracompat.compat.events.SwitchFlashEvent; 40 | import com.github.piasy.cameracompat.compat.events.SwitchMirrorEvent; 41 | import com.github.piasy.cameracompat.processor.GPUImageChain; 42 | import com.github.piasy.cameracompat.processor.ProcessorChain; 43 | import com.github.piasy.safelyandroid.misc.CheckUtil; 44 | import com.hannesdorfmann.fragmentargs.annotation.Arg; 45 | import com.tbruyelle.rxpermissions2.RxPermissions; 46 | import io.reactivex.disposables.Disposable; 47 | import javax.annotation.concurrent.GuardedBy; 48 | import org.greenrobot.eventbus.EventBus; 49 | import org.greenrobot.eventbus.Subscribe; 50 | import org.greenrobot.eventbus.ThreadMode; 51 | 52 | /** 53 | * Created by Piasy{github.com/Piasy} on 6/29/16. 54 | */ 55 | 56 | abstract class PreviewFragment extends Fragment { 57 | 58 | @GuardedBy("this") 59 | protected ProcessorChain mProcessorChain; 60 | 61 | @Arg 62 | int mPreviewWidth; 63 | @Arg 64 | int mPreviewHeight; 65 | @Arg 66 | boolean mIsDefaultFrontCamera; 67 | @Arg 68 | boolean mIsDefaultFlashOpen; 69 | @Arg 70 | boolean mIsDefaultBeautifyOn; 71 | @Arg 72 | boolean mIsDefaultMirrorEnabled; 73 | 74 | private ViewGroup mPreviewContainer; 75 | private CameraHelper mCameraHelper; 76 | @GuardedBy("this") 77 | private EventBus mEventBus; 78 | private Disposable mPermissionRequest; 79 | 80 | public PreviewFragment() { 81 | // Required empty public constructor 82 | } 83 | 84 | @Override 85 | public void onAttach(Context context) { 86 | super.onAttach(context); 87 | try { 88 | mProcessorChain = CameraCompat.getInstance().getProcessorChain(); 89 | mEventBus = CameraCompat.getInstance().getEventBus(); 90 | } catch (IllegalStateException e) { 91 | // this could happen when the host Activity is recreated, and before user init 92 | // CameraCompat instance, this preview fragment get attached. 93 | 94 | // currently we could only cover this problem to avoid crash, but 95 | // TODO: 27/10/2016 could we notify user that error happen? 96 | // although leave this Activity and enter again would solve this problem 97 | 98 | mProcessorChain = NoOpChain.INSTANCE; 99 | mEventBus = EventBus.getDefault(); 100 | } 101 | } 102 | 103 | @Override 104 | public void onCreate(@Nullable Bundle savedInstanceState) { 105 | super.onCreate(savedInstanceState); 106 | setRetainInstance(true); 107 | } 108 | 109 | @Override 110 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 111 | Bundle savedInstanceState) { 112 | mEventBus.register(this); 113 | mProcessorChain.setUp(); 114 | return inflater.inflate(R.layout.preview_fragment, container, false); 115 | } 116 | 117 | @Override 118 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 119 | super.onViewCreated(view, savedInstanceState); 120 | 121 | mPreviewContainer = (ViewGroup) view.findViewById(R.id.mPreviewContainer); 122 | mPreviewContainer.addView(mProcessorChain.createSurface(getContext())); 123 | mCameraHelper = createCameraHelper(); 124 | mProcessorChain.initSurface(getContext()); 125 | } 126 | 127 | @Override 128 | public void onResume() { 129 | super.onResume(); 130 | 131 | disposePermissionRequest(); 132 | mPermissionRequest = RxPermissions.getInstance(getContext()) 133 | .request(Manifest.permission.CAMERA) 134 | .subscribe(grant -> { 135 | if (grant && CheckUtil.nonNull(mCameraHelper)) { 136 | mCameraHelper.startPreview(); 137 | } else { 138 | CameraCompat.onError(CameraCompat.ERR_PERMISSION); 139 | } 140 | }, throwable -> CameraCompat.onError(CameraCompat.ERR_PERMISSION)); 141 | } 142 | 143 | @Override 144 | public void onPause() { 145 | super.onPause(); 146 | 147 | if (CheckUtil.nonNull(mCameraHelper)) { 148 | mCameraHelper.stopPreview(); 149 | } 150 | } 151 | 152 | @Override 153 | public void onDestroyView() { 154 | super.onDestroyView(); 155 | mEventBus.unregister(this); 156 | mProcessorChain.tearDown(); 157 | disposePermissionRequest(); 158 | } 159 | 160 | @Override 161 | public void onDetach() { 162 | super.onDetach(); 163 | 164 | synchronized (this) { 165 | mProcessorChain = null; 166 | mEventBus = null; 167 | } 168 | } 169 | 170 | private void disposePermissionRequest() { 171 | if (mPermissionRequest != null && !mPermissionRequest.isDisposed()) { 172 | mPermissionRequest.dispose(); 173 | mPermissionRequest = null; 174 | } 175 | } 176 | 177 | protected abstract CameraHelper createCameraHelper(); 178 | 179 | @Subscribe(threadMode = ThreadMode.ASYNC) 180 | public synchronized void onSwitchBeautify(SwitchBeautifyEvent event) { 181 | if (isResumed() && mProcessorChain instanceof GPUImageChain) { 182 | ((GPUImageChain) mProcessorChain).switchBeautify(); 183 | } 184 | } 185 | 186 | @Subscribe(threadMode = ThreadMode.ASYNC) 187 | public synchronized void onSwitchCamera(SwitchCameraEvent event) { 188 | if (isResumed() && CheckUtil.nonNull(mProcessorChain, mCameraHelper)) { 189 | mProcessorChain.pause(); 190 | mCameraHelper.switchCamera(); 191 | mProcessorChain.cameraSwitched(); 192 | } 193 | } 194 | 195 | @Subscribe(threadMode = ThreadMode.ASYNC) 196 | public synchronized void onSwitchFlash(SwitchFlashEvent event) { 197 | if (isResumed() && CheckUtil.nonNull(mCameraHelper)) { 198 | mCameraHelper.switchFlash(); 199 | } 200 | } 201 | 202 | @Subscribe(threadMode = ThreadMode.ASYNC) 203 | public synchronized void onSwitchMirror(SwitchMirrorEvent event) { 204 | if (isResumed() && CheckUtil.nonNull(mProcessorChain)) { 205 | mProcessorChain.switchMirror(); 206 | } 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/compat/PreviewSize.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.compat; 26 | 27 | /** 28 | * Created by Piasy{github.com/Piasy} on 30/11/2016. 29 | */ 30 | 31 | public class PreviewSize { 32 | private final int mWidth; 33 | private final int mHeight; 34 | 35 | public PreviewSize(int width, int height) { 36 | mWidth = width; 37 | mHeight = height; 38 | } 39 | 40 | public int getWidth() { 41 | return mWidth; 42 | } 43 | 44 | public int getHeight() { 45 | return mHeight; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/compat/events/CameraAccessError.java: -------------------------------------------------------------------------------- 1 | package com.github.piasy.cameracompat.compat.events; 2 | 3 | /** 4 | * Created by Piasy{github.com/Piasy} on 8/3/16. 5 | */ 6 | 7 | public class CameraAccessError extends RuntimeException { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/compat/events/SwitchBeautifyEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.compat.events; 26 | 27 | /** 28 | * Created by Piasy{github.com/Piasy} on 6/29/16. 29 | */ 30 | 31 | public class SwitchBeautifyEvent { 32 | } 33 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/compat/events/SwitchCameraEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.compat.events; 26 | 27 | /** 28 | * Created by Piasy{github.com/Piasy} on 6/29/16. 29 | */ 30 | 31 | public class SwitchCameraEvent { 32 | } 33 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/compat/events/SwitchFlashEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.compat.events; 26 | 27 | /** 28 | * Created by Piasy{github.com/Piasy} on 6/29/16. 29 | */ 30 | 31 | public class SwitchFlashEvent { 32 | } 33 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/compat/events/SwitchMirrorEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.compat.events; 26 | 27 | /** 28 | * Created by Piasy{github.com/Piasy} on 6/29/16. 29 | */ 30 | 31 | public class SwitchMirrorEvent { 32 | } 33 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/gpuimage/GLFilterGroup.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.gpuimage; 26 | 27 | import android.annotation.SuppressLint; 28 | import android.opengl.GLES20; 29 | import com.github.piasy.cameracompat.utils.GLUtil; 30 | import java.nio.ByteBuffer; 31 | import java.nio.ByteOrder; 32 | import java.nio.FloatBuffer; 33 | import java.util.ArrayList; 34 | import java.util.List; 35 | import jp.co.cyberagent.android.gpuimage.GPUImageFilter; 36 | import jp.co.cyberagent.android.gpuimage.Rotation; 37 | import jp.co.cyberagent.android.gpuimage.util.TextureRotationUtil; 38 | 39 | import static jp.co.cyberagent.android.gpuimage.util.TextureRotationUtil.TEXTURE_NO_ROTATION; 40 | 41 | /** 42 | * Created by Piasy{github.com/Piasy} on 5/31/16. 43 | */ 44 | public class GLFilterGroup extends GPUImageFilter { 45 | 46 | protected List mFilters; 47 | protected List mMergedFilters; 48 | private final FloatBuffer mGLCubeBuffer; 49 | private final FloatBuffer mGLTextureBuffer; 50 | private final FloatBuffer mGLTextureFlipBuffer; 51 | private int[] mFrameBuffers; 52 | private int[] mFrameBufferTextures; 53 | private ImageDumpedListener mImageDumpedListener; 54 | private volatile int mImageWidth; 55 | private volatile int mImageHeight; 56 | private ByteBuffer mRgbaBuf; 57 | 58 | /** 59 | * Instantiates a new GPUImageFilterGroup with the given filters. 60 | * 61 | * @param filters the filters which represent this filter 62 | */ 63 | public GLFilterGroup(List filters) { 64 | mFilters = filters; 65 | if (mFilters == null) { 66 | mFilters = new ArrayList<>(); 67 | } else { 68 | updateMergedFilters(); 69 | } 70 | 71 | mGLCubeBuffer = ByteBuffer.allocateDirect(GLRender.CUBE.length * 4) 72 | .order(ByteOrder.nativeOrder()) 73 | .asFloatBuffer(); 74 | mGLCubeBuffer.put(GLRender.CUBE).position(0); 75 | 76 | mGLTextureBuffer = ByteBuffer.allocateDirect(TEXTURE_NO_ROTATION.length * 4) 77 | .order(ByteOrder.nativeOrder()) 78 | .asFloatBuffer(); 79 | mGLTextureBuffer.put(TEXTURE_NO_ROTATION).position(0); 80 | 81 | float[] flipTexture = TextureRotationUtil.getRotation(Rotation.NORMAL, false, true); 82 | mGLTextureFlipBuffer = ByteBuffer.allocateDirect(flipTexture.length * 4) 83 | .order(ByteOrder.nativeOrder()) 84 | .asFloatBuffer(); 85 | mGLTextureFlipBuffer.put(flipTexture).position(0); 86 | } 87 | 88 | public void setImageDumpedListener(ImageDumpedListener imageDumpedListener) { 89 | mImageDumpedListener = imageDumpedListener; 90 | } 91 | 92 | /* 93 | * (non-Javadoc) 94 | * @see jp.co.cyberagent.android.gpuimage.GPUImageFilter#onInit() 95 | */ 96 | @Override 97 | public void onInit() { 98 | super.onInit(); 99 | for (GPUImageFilter filter : mFilters) { 100 | filter.init(); 101 | } 102 | } 103 | 104 | /* 105 | * (non-Javadoc) 106 | * @see jp.co.cyberagent.android.gpuimage.GPUImageFilter#onDestroy() 107 | */ 108 | @Override 109 | public void onDestroy() { 110 | destroyFrameBuffers(); 111 | for (GPUImageFilter filter : mFilters) { 112 | filter.destroy(); 113 | } 114 | super.onDestroy(); 115 | } 116 | 117 | /* 118 | * (non-Javadoc) 119 | * @see 120 | * jp.co.cyberagent.android.gpuimage.GPUImageFilter#onOutputSizeChanged(int, 121 | * int) 122 | */ 123 | @Override 124 | public void onOutputSizeChanged(final int width, final int height) { 125 | super.onOutputSizeChanged(width, height); 126 | if (mFrameBuffers != null) { 127 | destroyFrameBuffers(); 128 | } 129 | 130 | int size = mFilters.size(); 131 | for (int i = 0; i < size - 1; i++) { 132 | mFilters.get(i).onOutputSizeChanged(mImageHeight, mImageWidth); 133 | } 134 | mFilters.get(size - 1).onOutputSizeChanged(width, height); 135 | 136 | if (mMergedFilters.size() > 0) { 137 | size = mMergedFilters.size(); 138 | mFrameBuffers = new int[size - 1]; 139 | mFrameBufferTextures = new int[size - 1]; 140 | 141 | for (int i = 0; i < size - 1; i++) { 142 | GLES20.glGenFramebuffers(1, mFrameBuffers, i); 143 | GLES20.glGenTextures(1, mFrameBufferTextures, i); 144 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mFrameBufferTextures[i]); 145 | GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, mImageHeight, 146 | mImageWidth, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null); 147 | GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, 148 | GLES20.GL_LINEAR); 149 | GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, 150 | GLES20.GL_LINEAR); 151 | GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, 152 | GLES20.GL_CLAMP_TO_EDGE); 153 | GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, 154 | GLES20.GL_CLAMP_TO_EDGE); 155 | 156 | GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffers[i]); 157 | GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, 158 | GLES20.GL_TEXTURE_2D, mFrameBufferTextures[i], 0); 159 | 160 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); 161 | GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); 162 | } 163 | } 164 | } 165 | 166 | /* 167 | * (non-Javadoc) 168 | * @see jp.co.cyberagent.android.gpuimage.GPUImageFilter#onDraw(int, 169 | * java.nio.FloatBuffer, java.nio.FloatBuffer) 170 | */ 171 | @SuppressLint("WrongCall") 172 | @Override 173 | public void onDraw(final int textureId, final FloatBuffer cubeBuffer, 174 | final FloatBuffer textureBuffer) { 175 | runPendingOnDrawTasks(); 176 | if (!isInitialized() || mFrameBuffers == null || mFrameBufferTextures == null) { 177 | return; 178 | } 179 | int size = mMergedFilters.size(); 180 | int previousTexture = textureId; 181 | int currentOutputWidth = mOutputWidth; 182 | int currentOutputHeight = mOutputHeight; 183 | for (int i = 0; i < size; i++) { 184 | GPUImageFilter filter = mMergedFilters.get(i); 185 | boolean isLast = i == size - 1; 186 | if (!isLast) { 187 | GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffers[i]); 188 | GLES20.glClearColor(0, 0, 0, 0); 189 | 190 | mOutputWidth = mImageHeight; 191 | mOutputHeight = mImageWidth; 192 | GLES20.glViewport(0, 0, mOutputWidth, mOutputHeight); 193 | } else { 194 | mOutputWidth = currentOutputWidth; 195 | mOutputHeight = currentOutputHeight; 196 | // avoid extra stride at the edge of screen 197 | GLES20.glViewport(-7, -7, mOutputWidth + 14, mOutputHeight + 14); 198 | } 199 | 200 | if (i == 0) { 201 | filter.onDraw(previousTexture, cubeBuffer, textureBuffer); 202 | } else if (isLast) { 203 | filter.onDraw(previousTexture, mGLCubeBuffer, 204 | (size % 2 == 0) ? mGLTextureFlipBuffer : mGLTextureBuffer); 205 | } else { 206 | filter.onDraw(previousTexture, mGLCubeBuffer, mGLTextureBuffer); 207 | } 208 | if (i == size - 2 && mOutputWidth * mOutputHeight != 0) { 209 | // just before last filter, read data from GPU 210 | dumpImage(mOutputWidth, mOutputHeight); 211 | } 212 | 213 | if (!isLast) { 214 | GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); 215 | previousTexture = mFrameBufferTextures[i]; 216 | } 217 | } 218 | } 219 | 220 | private void destroyFrameBuffers() { 221 | if (mFrameBufferTextures != null) { 222 | GLES20.glDeleteTextures(mFrameBufferTextures.length, mFrameBufferTextures, 0); 223 | mFrameBufferTextures = null; 224 | } 225 | if (mFrameBuffers != null) { 226 | GLES20.glDeleteFramebuffers(mFrameBuffers.length, mFrameBuffers, 0); 227 | mFrameBuffers = null; 228 | } 229 | } 230 | 231 | public void onImageSizeChanged(int imageWidth, int imageHeight) { 232 | mImageWidth = imageWidth; 233 | mImageHeight = imageHeight; 234 | mRgbaBuf = ByteBuffer.allocateDirect(mImageWidth * mImageHeight * 4); 235 | onOutputSizeChanged(mOutputWidth, mOutputHeight); 236 | } 237 | 238 | private void dumpImage(int width, int height) { 239 | if (mImageDumpedListener == null) { 240 | return; 241 | } 242 | mRgbaBuf.position(0); 243 | GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, mRgbaBuf); 244 | GLUtil.dumpGlError("glReadPixels"); 245 | mImageDumpedListener.imageDumped(mRgbaBuf, width, height); 246 | } 247 | 248 | public List getFilters() { 249 | return mFilters; 250 | } 251 | 252 | public List getMergedFilters() { 253 | return mMergedFilters; 254 | } 255 | 256 | public void updateMergedFilters() { 257 | if (mFilters == null) { 258 | return; 259 | } 260 | 261 | if (mMergedFilters == null) { 262 | mMergedFilters = new ArrayList<>(); 263 | } else { 264 | mMergedFilters.clear(); 265 | } 266 | 267 | List filters; 268 | for (GPUImageFilter filter : mFilters) { 269 | if (filter instanceof GLFilterGroup) { 270 | ((GLFilterGroup) filter).updateMergedFilters(); 271 | filters = ((GLFilterGroup) filter).getMergedFilters(); 272 | if (filters == null || filters.isEmpty()) continue; 273 | mMergedFilters.addAll(filters); 274 | continue; 275 | } 276 | mMergedFilters.add(filter); 277 | } 278 | } 279 | 280 | public interface ImageDumpedListener { 281 | void imageDumped(ByteBuffer rgba, int width, int height); 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/gpuimage/SurfaceInitCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.gpuimage; 26 | 27 | import android.graphics.SurfaceTexture; 28 | 29 | /** 30 | * Created by Piasy{github.com/Piasy} on 5/24/16. 31 | */ 32 | public interface SurfaceInitCallback { 33 | void onSurfaceTextureInitiated(SurfaceTexture surfaceTexture); 34 | } 35 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/processor/DirectChain.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.processor; 26 | 27 | import android.annotation.TargetApi; 28 | import android.content.Context; 29 | import android.graphics.SurfaceTexture; 30 | import android.media.Image; 31 | import android.os.Build; 32 | import android.view.TextureView; 33 | import android.view.View; 34 | import android.view.ViewGroup; 35 | import android.widget.FrameLayout; 36 | import com.github.piasy.cameracompat.CameraCompat; 37 | import com.github.piasy.cameracompat.gpuimage.SurfaceInitCallback; 38 | import java.nio.ByteBuffer; 39 | import jp.co.cyberagent.android.gpuimage.Rotation; 40 | 41 | /** 42 | * Created by Piasy{github.com/Piasy} on 18/10/2016. 43 | */ 44 | 45 | public class DirectChain implements ProcessorChain, TextureView.SurfaceTextureListener { 46 | private final CameraCompat.VideoCaptureCallback mVideoCaptureCallback; 47 | 48 | private TextureView mTextureView; 49 | private volatile SurfaceInitCallback mPendingNotify = null; 50 | 51 | private Rotation mRotation; 52 | 53 | private int mOutputWidth; 54 | private int mOutputHeight; 55 | private volatile int mVideoWidth; 56 | private volatile int mVideoHeight; 57 | 58 | private volatile boolean mIsFrontCamera; 59 | private volatile boolean mEnableMirror; 60 | 61 | private ByteBuffer mGLYuvBuffer; 62 | 63 | public DirectChain(boolean defaultFrontCamera, 64 | CameraCompat.VideoCaptureCallback videoCaptureCallback) { 65 | mVideoCaptureCallback = videoCaptureCallback; 66 | mIsFrontCamera = defaultFrontCamera; 67 | } 68 | 69 | @Override 70 | public void setUp() { 71 | } 72 | 73 | @Override 74 | public View createSurface(Context context) { 75 | mTextureView = new TextureView(context); 76 | FrameLayout.LayoutParams params = new FrameLayout.LayoutParams( 77 | ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); 78 | mTextureView.setLayoutParams(params); 79 | mTextureView.setKeepScreenOn(true); 80 | return mTextureView; 81 | } 82 | 83 | @Override 84 | public void initSurface(Context context) { 85 | mTextureView.setSurfaceTextureListener(this); 86 | } 87 | 88 | @Override 89 | public void onCameraOpened(Rotation rotation, boolean flipHorizontal, boolean flipVertical, 90 | SurfaceInitCallback callback) { 91 | mRotation = rotation; 92 | if (mTextureView.getSurfaceTexture() != null) { 93 | callback.onSurfaceTextureInitiated(mTextureView.getSurfaceTexture()); 94 | mTextureView.post(this::adjustImageScaling); 95 | } else { 96 | mPendingNotify = callback; 97 | } 98 | } 99 | 100 | @Override 101 | public void pause() { 102 | } 103 | 104 | @Override 105 | public void resume() { 106 | } 107 | 108 | @Override 109 | public void cameraSwitched() { 110 | mIsFrontCamera = !mIsFrontCamera; 111 | } 112 | 113 | @Override 114 | public void switchMirror() { 115 | mEnableMirror = !mEnableMirror; 116 | } 117 | 118 | @Override 119 | public void tearDown() { 120 | } 121 | 122 | @Override 123 | public void onFrameData(byte[] data, int width, int height, 124 | Runnable postProcessedTask) { 125 | notifyVideoSizeChanged(width, height); 126 | sendNormalImage(width, height, data); 127 | postProcessedTask.run(); 128 | } 129 | 130 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 131 | @Override 132 | public void onFrameData(Image image, Runnable postProcessedTask) { 133 | notifyVideoSizeChanged(image.getWidth(), image.getHeight()); 134 | sendNormalImage(image); 135 | postProcessedTask.run(); 136 | } 137 | 138 | /** 139 | * direct chain won't change frame size, so the size is ignored, but actually they are equal. 140 | */ 141 | private void notifyVideoSizeChanged(int width, int height) { 142 | if (mVideoWidth != 0) { 143 | return; 144 | } 145 | mVideoWidth = width; 146 | mVideoHeight = height; 147 | if (mGLYuvBuffer == null) { 148 | mGLYuvBuffer = ByteBuffer.allocateDirect(mVideoWidth * mVideoHeight * 3 / 2); 149 | } 150 | mVideoCaptureCallback.onVideoSizeChanged(mVideoWidth, mVideoHeight); 151 | mTextureView.post(this::adjustImageScaling); 152 | } 153 | 154 | private void adjustImageScaling() { 155 | if (mRotation == null || mVideoWidth == 0 || mVideoHeight == 0) { 156 | return; 157 | } 158 | float videoWidth = mVideoWidth; 159 | float videoHeight = mVideoHeight; 160 | if (mRotation == Rotation.ROTATION_270 || mRotation == Rotation.ROTATION_90) { 161 | videoWidth = mVideoHeight; 162 | videoHeight = mVideoWidth; 163 | } 164 | float ratioW = mOutputWidth / videoWidth; 165 | float ratioH = mOutputHeight / videoHeight; 166 | float ratioMax = Math.max(ratioW, ratioH); 167 | int outputWidthNew = Math.round(videoWidth * ratioMax); 168 | int outputHeightNew = Math.round(videoHeight * ratioMax); 169 | FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mTextureView.getLayoutParams(); 170 | params.width = outputWidthNew; 171 | params.height = outputHeightNew; 172 | if (ratioW > ratioH) { 173 | params.topMargin = -((int) Math.ceil((outputHeightNew - mOutputHeight) / 2)); 174 | } else { 175 | params.leftMargin = -((int) Math.ceil((outputWidthNew - mOutputWidth) / 2)); 176 | } 177 | mTextureView.setLayoutParams(params); 178 | } 179 | 180 | private void sendNormalImage(int width, int height, byte[] data) { 181 | if (mIsFrontCamera && mEnableMirror) { 182 | if (mRotation == Rotation.ROTATION_90) { 183 | RgbYuvConverter.yuvCropFlip(width, height, data, mVideoHeight, 184 | mGLYuvBuffer.array()); 185 | } else { 186 | RgbYuvConverter.yuvCropRotateC180Flip(width, height, data, mVideoHeight, 187 | mGLYuvBuffer.array()); 188 | } 189 | } else { 190 | if (mRotation == Rotation.ROTATION_90) { 191 | RgbYuvConverter.yuvCropRotateC180(width, height, data, mVideoHeight, 192 | mGLYuvBuffer.array()); 193 | } else { 194 | RgbYuvConverter.yuvCrop(width, height, data, mVideoHeight, mGLYuvBuffer.array()); 195 | } 196 | } 197 | mVideoCaptureCallback.onFrameData(mGLYuvBuffer.array(), width, mVideoHeight); 198 | } 199 | 200 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 201 | private void sendNormalImage(Image image) { 202 | if (mIsFrontCamera && mEnableMirror) { 203 | if (mRotation == Rotation.ROTATION_90) { 204 | RgbYuvConverter.image2yuvCropFlip(image, mVideoHeight, mGLYuvBuffer.array()); 205 | } else { 206 | RgbYuvConverter.image2yuvCropRotateC180Flip(image, mVideoHeight, 207 | mGLYuvBuffer.array()); 208 | } 209 | } else { 210 | if (mRotation == Rotation.ROTATION_90) { 211 | RgbYuvConverter.image2yuvCropRotateC180(image, mVideoHeight, mGLYuvBuffer.array()); 212 | } else { 213 | RgbYuvConverter.image2yuvCrop(image, mVideoHeight, mGLYuvBuffer.array()); 214 | } 215 | } 216 | mVideoCaptureCallback.onFrameData(mGLYuvBuffer.array(), image.getWidth(), mVideoHeight); 217 | } 218 | 219 | @Override 220 | public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 221 | mOutputWidth = width; 222 | mOutputHeight = height; 223 | adjustImageScaling(); 224 | if (mPendingNotify != null) { 225 | mPendingNotify.onSurfaceTextureInitiated(surface); 226 | mPendingNotify = null; 227 | } 228 | } 229 | 230 | @Override 231 | public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 232 | } 233 | 234 | @Override 235 | public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 236 | return false; 237 | } 238 | 239 | @Override 240 | public void onSurfaceTextureUpdated(SurfaceTexture surface) { 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/processor/GPUImageChain.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.processor; 26 | 27 | import android.annotation.TargetApi; 28 | import android.content.Context; 29 | import android.graphics.PixelFormat; 30 | import android.media.Image; 31 | import android.opengl.GLSurfaceView; 32 | import android.os.Build; 33 | import android.view.View; 34 | import android.view.ViewGroup; 35 | import android.widget.FrameLayout; 36 | import com.github.piasy.cameracompat.CameraCompat; 37 | import com.github.piasy.cameracompat.gpuimage.GLFilterGroup; 38 | import com.github.piasy.cameracompat.gpuimage.GLRender; 39 | import com.github.piasy.cameracompat.gpuimage.SurfaceInitCallback; 40 | import com.github.piasy.cameracompat.utils.GLUtil; 41 | import com.github.piasy.cameracompat.utils.Profiler; 42 | import java.nio.ByteBuffer; 43 | import java.util.ArrayList; 44 | import java.util.Collections; 45 | import java.util.List; 46 | import jp.co.cyberagent.android.gpuimage.GPUImageFilter; 47 | import jp.co.cyberagent.android.gpuimage.Rotation; 48 | 49 | /** 50 | * Created by Piasy{github.com/Piasy} on 18/10/2016. 51 | */ 52 | 53 | public class GPUImageChain implements ProcessorChain, GLFilterGroup.ImageDumpedListener, 54 | GLRender.VideoSizeChangedListener { 55 | private final List mProcessors; 56 | private final CameraCompat.VideoCaptureCallback mVideoCaptureCallback; 57 | private final boolean mDefaultFilterEnabled; 58 | private final Profiler mProfiler; 59 | 60 | private GLSurfaceView mGLSurfaceView; 61 | private GLRender mGLRender; 62 | private volatile boolean mIsFrontCamera; 63 | private volatile boolean mEnableMirror; 64 | 65 | private ByteBuffer mGLRgbaBuffer; 66 | private ByteBuffer mGLYuvBuffer; 67 | 68 | public GPUImageChain(List processors, boolean defaultEnableFilter, 69 | boolean defaultFrontCamera, CameraCompat.VideoCaptureCallback videoCaptureCallback, 70 | Profiler profiler) { 71 | mProcessors = Collections.unmodifiableList(new ArrayList<>(processors)); 72 | mVideoCaptureCallback = videoCaptureCallback; 73 | mDefaultFilterEnabled = defaultEnableFilter; 74 | mIsFrontCamera = defaultFrontCamera; 75 | mProfiler = profiler; 76 | } 77 | 78 | @Override 79 | public void setUp() { 80 | for (Processor processor : mProcessors) { 81 | processor.setUp(); 82 | } 83 | } 84 | 85 | @Override 86 | public View createSurface(Context context) { 87 | mGLSurfaceView = new GLSurfaceView(context); 88 | FrameLayout.LayoutParams params = new FrameLayout.LayoutParams( 89 | ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); 90 | mGLSurfaceView.setLayoutParams(params); 91 | mGLSurfaceView.setKeepScreenOn(true); 92 | return mGLSurfaceView; 93 | } 94 | 95 | @Override 96 | public void initSurface(Context context) { 97 | List filters = new ArrayList<>(); 98 | for (Processor processor : mProcessors) { 99 | filters.addAll(processor.getFilters()); 100 | } 101 | filters.add(new GPUImageFilter()); 102 | GLFilterGroup filterGroup = new GLFilterGroup(filters); 103 | filterGroup.setImageDumpedListener(this); 104 | mGLRender = new GLRender(filterGroup, mDefaultFilterEnabled, this, mProfiler); 105 | 106 | if (GLUtil.isSupportOpenGLES2(context)) { 107 | mGLSurfaceView.setEGLContextClientVersion(2); 108 | } 109 | mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0); 110 | mGLSurfaceView.getHolder().setFormat(PixelFormat.RGBA_8888); 111 | mGLSurfaceView.setRenderer(mGLRender); 112 | mGLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); 113 | mGLSurfaceView.requestRender(); 114 | } 115 | 116 | @Override 117 | public void onCameraOpened(Rotation rotation, boolean flipHorizontal, boolean flipVertical, 118 | SurfaceInitCallback callback) { 119 | mGLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); 120 | mGLRender.setRotationCamera(rotation, flipHorizontal, flipVertical); 121 | mGLRender.setUpSurfaceTexture(callback); 122 | } 123 | 124 | @Override 125 | public void pause() { 126 | mGLRender.pauseDrawing(); 127 | } 128 | 129 | @Override 130 | public void resume() { 131 | mGLRender.resumeDrawing(); 132 | } 133 | 134 | @Override 135 | public synchronized void cameraSwitched() { 136 | mIsFrontCamera = !mIsFrontCamera; 137 | } 138 | 139 | @Override 140 | public synchronized void switchMirror() { 141 | mEnableMirror = !mEnableMirror; 142 | } 143 | 144 | @Override 145 | public void tearDown() { 146 | for (Processor processor : mProcessors) { 147 | processor.tearDown(); 148 | } 149 | } 150 | 151 | public synchronized void switchBeautify() { 152 | mGLRender.switchFilter(); 153 | } 154 | 155 | @Override 156 | public void onFrameData(byte[] data, int width, int height, 157 | Runnable postProcessedTask) { 158 | if (mGLRgbaBuffer == null) { 159 | mGLRgbaBuffer = ByteBuffer.allocateDirect(width * height * 4); 160 | } 161 | if (mGLYuvBuffer == null) { 162 | // 16 bytes alignment 163 | int bufHeight = (width * mGLRender.getFrameWidth() / mGLRender.getFrameHeight()) 164 | & 0xfffffff0; 165 | mGLYuvBuffer = ByteBuffer.allocateDirect(width * bufHeight * 3 / 2); 166 | } 167 | if (!mGLRender.isBusyDrawing()) { 168 | RgbYuvConverter.yuv2rgba(width, height, data, mGLRgbaBuffer.array()); 169 | mGLRender.scheduleDrawFrame(mGLRgbaBuffer, width, height, () -> { 170 | if (!mGLRender.isEnableFilter() && !mGLRender.isPaused()) { 171 | sendNormalImage(width, height, data); 172 | } 173 | postProcessedTask.run(); 174 | }); 175 | } 176 | } 177 | 178 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 179 | @Override 180 | public void onFrameData(final Image image, final Runnable postProcessedTask) { 181 | final int width = image.getWidth(); 182 | final int height = image.getHeight(); 183 | if (mGLRgbaBuffer == null) { 184 | mGLRgbaBuffer = ByteBuffer.allocateDirect(width * height * 4); 185 | } 186 | if (mGLYuvBuffer == null) { 187 | // 16 bytes alignment 188 | int bufHeight = (width * mGLRender.getFrameWidth() / mGLRender.getFrameHeight()) 189 | & 0xfffffff0; 190 | mGLYuvBuffer = ByteBuffer.allocateDirect(width * bufHeight * 3 / 2); 191 | } 192 | if (!mGLRender.isBusyDrawing()) { 193 | RgbYuvConverter.image2rgba(image, mGLRgbaBuffer.array()); 194 | mGLRender.scheduleDrawFrame(mGLRgbaBuffer, width, height, () -> { 195 | if (!mGLRender.isEnableFilter() && !mGLRender.isPaused()) { 196 | sendNormalImage(image); 197 | } 198 | postProcessedTask.run(); 199 | }); 200 | } else { 201 | postProcessedTask.run(); 202 | } 203 | } 204 | 205 | @Override 206 | public void imageDumped(ByteBuffer rgba, int width, int height) { 207 | sendBeautifyImage(rgba, width, height); 208 | } 209 | 210 | private void sendBeautifyImage(ByteBuffer rgba, int width, int height) { 211 | if (mIsFrontCamera) { 212 | if (mEnableMirror) { 213 | RgbYuvConverter.rgba2yuvRotateC90(width, height, rgba.array(), 214 | mGLYuvBuffer.array()); 215 | } else { 216 | RgbYuvConverter.rgba2yuvRotateC90Flip(width, height, rgba.array(), 217 | mGLYuvBuffer.array()); 218 | } 219 | } else { 220 | RgbYuvConverter.rgba2yuvRotateC90(width, height, rgba.array(), mGLYuvBuffer.array()); 221 | } 222 | mVideoCaptureCallback.onFrameData(mGLYuvBuffer.array(), height, width); 223 | } 224 | 225 | private void sendNormalImage(int width, int height, byte[] data) { 226 | if (mIsFrontCamera && mEnableMirror) { 227 | if (mGLRender.getRotation() == Rotation.ROTATION_90) { 228 | RgbYuvConverter.yuvCropFlip(width, height, data, mGLRender.getVideoHeight(), 229 | mGLYuvBuffer.array()); 230 | } else { 231 | RgbYuvConverter.yuvCropRotateC180Flip(width, height, data, 232 | mGLRender.getVideoHeight(), mGLYuvBuffer.array()); 233 | } 234 | } else { 235 | if (mGLRender.getRotation() == Rotation.ROTATION_90) { 236 | RgbYuvConverter.yuvCropRotateC180(width, height, data, mGLRender.getVideoHeight(), 237 | mGLYuvBuffer.array()); 238 | } else { 239 | RgbYuvConverter.yuvCrop(width, height, data, mGLRender.getVideoHeight(), 240 | mGLYuvBuffer.array()); 241 | } 242 | } 243 | mVideoCaptureCallback.onFrameData(mGLYuvBuffer.array(), width, mGLRender.getVideoHeight()); 244 | } 245 | 246 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 247 | private void sendNormalImage(Image image) { 248 | if (mIsFrontCamera && mEnableMirror) { 249 | if (mGLRender.getRotation() == Rotation.ROTATION_90) { 250 | RgbYuvConverter.image2yuvCropFlip(image, mGLRender.getVideoHeight(), 251 | mGLYuvBuffer.array()); 252 | } else { 253 | RgbYuvConverter.image2yuvCropRotateC180Flip(image, mGLRender.getVideoHeight(), 254 | mGLYuvBuffer.array()); 255 | } 256 | } else { 257 | if (mGLRender.getRotation() == Rotation.ROTATION_90) { 258 | RgbYuvConverter.image2yuvCropRotateC180(image, mGLRender.getVideoHeight(), 259 | mGLYuvBuffer.array()); 260 | } else { 261 | RgbYuvConverter.image2yuvCrop(image, mGLRender.getVideoHeight(), 262 | mGLYuvBuffer.array()); 263 | } 264 | } 265 | mVideoCaptureCallback.onFrameData(mGLYuvBuffer.array(), image.getWidth(), 266 | mGLRender.getVideoHeight()); 267 | } 268 | 269 | @Override 270 | public void onVideoSizeChanged(int width, int height) { 271 | mVideoCaptureCallback.onVideoSizeChanged(width, height); 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/processor/Processor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.processor; 26 | 27 | import com.github.piasy.cameracompat.compat.CameraFrameCallback; 28 | import java.util.List; 29 | import jp.co.cyberagent.android.gpuimage.GPUImageFilter; 30 | 31 | /** 32 | * Created by Piasy{github.com/Piasy} on 17/10/2016. 33 | */ 34 | 35 | public interface Processor extends CameraFrameCallback { 36 | void setUp(); 37 | 38 | List getFilters(); 39 | 40 | void tearDown(); 41 | } 42 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/processor/ProcessorChain.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.processor; 26 | 27 | import android.content.Context; 28 | import android.view.View; 29 | import com.github.piasy.cameracompat.compat.CameraFrameCallback; 30 | import com.github.piasy.cameracompat.gpuimage.SurfaceInitCallback; 31 | import jp.co.cyberagent.android.gpuimage.Rotation; 32 | 33 | /** 34 | * Created by Piasy{github.com/Piasy} on 18/10/2016. 35 | */ 36 | 37 | public interface ProcessorChain extends CameraFrameCallback { 38 | void setUp(); 39 | 40 | View createSurface(Context context); 41 | 42 | void initSurface(Context context); 43 | 44 | void onCameraOpened(Rotation rotation, boolean flipHorizontal, boolean flipVertical, 45 | SurfaceInitCallback callback); 46 | 47 | void pause(); 48 | 49 | void resume(); 50 | 51 | void cameraSwitched(); 52 | 53 | void switchMirror(); 54 | 55 | void tearDown(); 56 | } 57 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/processor/RgbYuvConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.processor; 26 | 27 | import android.annotation.TargetApi; 28 | import android.content.Context; 29 | import android.media.Image; 30 | import android.os.Build; 31 | import com.getkeepsafe.relinker.ReLinker; 32 | import java.nio.ByteBuffer; 33 | 34 | /** 35 | * Created by Piasy{github.com/Piasy} on 6/2/16. 36 | * 37 | * from http://www.equasys.de/colorconversion.html 38 | * https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion 39 | * 40 | * packed mode, and Cr comes first, ref: http://stackoverflow.com/a/12610396/3077508 41 | * 42 | * {@code 43 | * float operation: 44 | * R = 1.164 * (Y-16) + 1.596 * (Cr-128) 45 | * G = 1.164 * (Y-16) - 0.392 * (Cb-128) - 0.813 * (Cr-128) 46 | * B = 1.164 * (Y-16) + 2.017 * (Cb-128) 47 | * 48 | * transform to bit operation: 49 | * 1.164 = 1 + (1 >> 3) + (1 >> 5) + (1 >> 7) ~= 1 + (1 >> 3) + (1 >> 5) = 1.15625 , 0.0078125 , 2 50 | * 1.596 = 1 + (1 >> 1) + (1 >> 4) + (1 >> 5) 51 | * 0.392 = (1 >> 2) + (1 >> 3) + (1 >> 6) ~= (1 >> 1) - (1 >> 3) = 0.375 , 0.017 , 4.352 52 | * 0.813 = (1 >> 1) + (1 >> 2) + (1 >> 4) ~= 1 - (1 >> 3) - (1 >> 4) = 0.8125 , 0.0005 , 0.128 53 | * 2.017 = 2 + (1 >> 6) ~= 2 , 0.017 , 4 54 | * 55 | * Y = Y - 16, Cb = Cb - 128, Cr = Cr - 128 56 | * Y = Y + (Y >> 3) + (Y >> 5) + (Y >> 7) 57 | * 58 | * R = Y + Cr + (Cr >> 1) + (Cr >> 4) + (Cr >> 5) 59 | * G = Y - (Cb >> 2) - (Cb >> 3) - (Cb >> 6) - (Cr >> 1) - (Cr >> 2) - (Cr >> 4) 60 | * B = Y + (Cb << 1) + (Cb >> 6) 61 | * } 62 | * 63 | * Perf: 64 | * float 640*480: 458060886 ns 65 | * bit 640*480: 406342604 ns, 12.7 % faster than float 66 | * jni (bit) 640*480: 15664062 ns, 2494.1 % faster than bit 67 | * jni (GpuImage) 640*480: 13116771 ns, 19.4 % faster than jni, but why?? 68 | * 69 | * 70 | * {@code 71 | * float operation: 72 | * Y = 0.257 * R + 0.504 * G + 0.098 * B + 16 73 | * Cb = -0.148 * R - 0.291 * G + 0.439 * B + 128 74 | * Cr = 0.439 * R - 0.368 * G - 0.071 * B + 128 75 | * 76 | * transform to bit operation: 77 | * 0.257 = (1 >> 2) + (1 >> 7) 78 | * 0.504 = (1 >> 1) + (1 >> 8) 79 | * 0.098 = (1 >> 4) + (1 >> 5) + (1 >> 8) 80 | * 0.148 = (1 >> 3) + (1 >> 6) + (1 >> 7) 81 | * 0.291 = (1 >> 2) + (1 >> 5) + (1 >> 7) 82 | * 0.439 = (1 >> 2) + (1 >> 3) + (1 >> 4) = (1 >> 1) - (1 >> 4) 83 | * 0.368 = (1 >> 2) + (1 >> 3) - (1 >> 7) 84 | * 0.071 = (1 >> 4) + (1 >> 7) 85 | * 86 | * Y = (R >> 2) + (R >> 7) + (G >> 1) + (G >> 8) + (B >> 4) + (B >> 5) + (B >> 8) + 16 87 | * Cb = -(R >> 3) - (R >> 6) - (R >> 7) - (G >> 2) - (G >> 5) - (G >> 7) + (B >> 1) - (B >> 4) + 128 88 | * Cr = (R >> 1) - (R >> 4) - (G >> 2) - (G >> 3) + (G >> 7) - (B >> 4) - (B >> 7) + 128 89 | * } 90 | * 91 | * Perf: 92 | * float 640*480: 227084323 ns 93 | * bit 640*480: 181100573 ns, 25.4 % faster than float 94 | * jni (bit) 640*480: 11113646 ns, 1529.5 % faster than bit 95 | */ 96 | public class RgbYuvConverter { 97 | 98 | public static void loadLibrary(Context context) { 99 | ReLinker.loadLibrary(context, "rgb-yuv-converter-library"); 100 | } 101 | 102 | public static native int yuv2rgba(int width, int height, byte[] yuvIn, byte[] rgbaOut); 103 | 104 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 105 | public static int image2rgba(Image image, byte[] rgbaOut) { 106 | Image.Plane[] planes = image.getPlanes(); 107 | ByteBuffer Y = planes[0].getBuffer(); 108 | ByteBuffer Cr = planes[2].getBuffer(); 109 | int CrPixelStride = planes[2].getPixelStride(); 110 | ByteBuffer Cb = planes[1].getBuffer(); 111 | int CbPixelStride = planes[1].getPixelStride(); 112 | return image2rgba(image.getWidth(), image.getHeight(), Y, Cr, Cb, CrPixelStride, 113 | CbPixelStride, rgbaOut); 114 | } 115 | 116 | private static native int image2rgba(int width, int height, ByteBuffer Y, ByteBuffer Cr, 117 | ByteBuffer Cb, int CrPixelStride, int CbPixelStride, byte[] rgbaOut); 118 | 119 | /** 120 | * rotate 90 degree in counter clockwise and change to yuv 121 | */ 122 | public static native int rgba2yuv(int width, int height, int[] rgbaIn, byte[] yuvOut); 123 | 124 | /** 125 | * rotate 90 degree in counter clockwise and change to yuv 126 | */ 127 | public static native int rgba2yuvRotateC90(int width, int height, byte[] rgbaIn, byte[] yuvOut); 128 | 129 | /** 130 | * rotate 90 degree in counter clockwise and change to yuv 131 | */ 132 | public static native int rgba2yuvRotateC90Flip(int width, int height, byte[] rgbaIn, 133 | byte[] yuvOut); 134 | 135 | /** 136 | * rotate 180 degree in counter clockwise 137 | */ 138 | public static native int yuvCropRotateC180(int width, int height, byte[] yuvIn, 139 | int outputHeight, byte[] yuvOut); 140 | 141 | public static native int yuvCrop(int width, int height, byte[] yuvIn, 142 | int outputHeight, byte[] yuvOut); 143 | 144 | public static native int yuvCropRotateC180Flip(int width, int height, byte[] yuvIn, 145 | int outputHeight, byte[] yuvOut); 146 | 147 | public static native int yuvCropFlip(int width, int height, byte[] yuvIn, 148 | int outputHeight, byte[] yuvOut); 149 | 150 | /** 151 | * rotate 180 degree in counter clockwise and change to yuv 152 | */ 153 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 154 | public static int image2yuvCropRotateC180(Image imageIn, int outputHeight, byte[] yuvOut) { 155 | Image.Plane[] planes = imageIn.getPlanes(); 156 | ByteBuffer Y = planes[0].getBuffer(); 157 | ByteBuffer Cr = planes[2].getBuffer(); 158 | int CrPixelStride = planes[2].getPixelStride(); 159 | ByteBuffer Cb = planes[1].getBuffer(); 160 | int CbPixelStride = planes[1].getPixelStride(); 161 | return image2yuvCropRotateC180(imageIn.getWidth(), imageIn.getHeight(), Y, Cr, Cb, 162 | CrPixelStride, CbPixelStride, outputHeight, yuvOut); 163 | } 164 | 165 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 166 | public static int image2yuvCrop(Image imageIn, int outputHeight, byte[] yuvOut) { 167 | Image.Plane[] planes = imageIn.getPlanes(); 168 | ByteBuffer Y = planes[0].getBuffer(); 169 | ByteBuffer Cr = planes[2].getBuffer(); 170 | int CrPixelStride = planes[2].getPixelStride(); 171 | ByteBuffer Cb = planes[1].getBuffer(); 172 | int CbPixelStride = planes[1].getPixelStride(); 173 | return image2yuvCrop(imageIn.getWidth(), imageIn.getHeight(), Y, Cr, Cb, CrPixelStride, 174 | CbPixelStride, outputHeight, yuvOut); 175 | } 176 | 177 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 178 | public static int image2yuvCropRotateC180Flip(Image imageIn, int outputHeight, byte[] yuvOut) { 179 | Image.Plane[] planes = imageIn.getPlanes(); 180 | ByteBuffer Y = planes[0].getBuffer(); 181 | ByteBuffer Cr = planes[2].getBuffer(); 182 | int CrPixelStride = planes[2].getPixelStride(); 183 | ByteBuffer Cb = planes[1].getBuffer(); 184 | int CbPixelStride = planes[1].getPixelStride(); 185 | return image2yuvCropRotateC180Flip(imageIn.getWidth(), imageIn.getHeight(), Y, Cr, Cb, 186 | CrPixelStride, CbPixelStride, outputHeight, yuvOut); 187 | } 188 | 189 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 190 | public static int image2yuvCropFlip(Image imageIn, int outputHeight, byte[] yuvOut) { 191 | Image.Plane[] planes = imageIn.getPlanes(); 192 | ByteBuffer Y = planes[0].getBuffer(); 193 | ByteBuffer Cr = planes[2].getBuffer(); 194 | int CrPixelStride = planes[2].getPixelStride(); 195 | ByteBuffer Cb = planes[1].getBuffer(); 196 | int CbPixelStride = planes[1].getPixelStride(); 197 | return image2yuvCropFlip(imageIn.getWidth(), imageIn.getHeight(), Y, Cr, Cb, CrPixelStride, 198 | CbPixelStride, outputHeight, yuvOut); 199 | } 200 | 201 | private static native int image2yuvCropRotateC180(int width, int height, ByteBuffer Y, 202 | ByteBuffer Cr, ByteBuffer Cb, int CrPixelStride, int CbPixelStride, int outputWidth, 203 | byte[] yuvOut); 204 | 205 | private static native int image2yuvCrop(int width, int height, ByteBuffer Y, ByteBuffer Cr, 206 | ByteBuffer Cb, int CrPixelStride, int CbPixelStride, int outputWidth, byte[] yuvOut); 207 | 208 | private static native int image2yuvCropRotateC180Flip(int width, int height, ByteBuffer Y, 209 | ByteBuffer Cr, ByteBuffer Cb, int CrPixelStride, int CbPixelStride, int outputWidth, 210 | byte[] yuvOut); 211 | 212 | private static native int image2yuvCropFlip(int width, int height, ByteBuffer Y, ByteBuffer Cr, 213 | ByteBuffer Cb, int CrPixelStride, int CbPixelStride, int outputWidth, byte[] yuvOut); 214 | } 215 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/utils/CameraImageUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.utils; 26 | 27 | import android.annotation.TargetApi; 28 | import android.graphics.Bitmap; 29 | import android.graphics.ImageFormat; 30 | import android.graphics.Rect; 31 | import android.media.Image; 32 | import android.os.Build; 33 | import android.os.Environment; 34 | import android.util.Log; 35 | import java.io.BufferedOutputStream; 36 | import java.io.File; 37 | import java.io.FileOutputStream; 38 | import java.io.IOException; 39 | import java.nio.Buffer; 40 | import java.nio.ByteBuffer; 41 | 42 | /** 43 | * Created by Piasy{github.com/Piasy} on 5/27/16. 44 | */ 45 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 46 | public class CameraImageUtil { 47 | 48 | public static byte[] getDataFromImage(Image image) { 49 | Rect crop = image.getCropRect(); 50 | int format = image.getFormat(); 51 | int width = crop.width(); 52 | int height = crop.height(); 53 | Image.Plane[] planes = image.getPlanes(); 54 | byte[] data = new byte[width * height * ImageFormat.getBitsPerPixel(format) / 8]; 55 | byte[] rowData = new byte[planes[0].getRowStride()]; 56 | int channelOffset = 0; 57 | int outputStride = 1; 58 | for (int i = 0; i < planes.length; i++) { 59 | switch (i) { 60 | case 0: 61 | channelOffset = 0; 62 | outputStride = 1; 63 | break; 64 | case 1: 65 | channelOffset = width * height; 66 | outputStride = 1; 67 | break; 68 | case 2: 69 | channelOffset = (int) (width * height * 1.25); 70 | outputStride = 1; 71 | break; 72 | } 73 | ByteBuffer buffer = planes[i].getBuffer(); 74 | int rowStride = planes[i].getRowStride(); 75 | int pixelStride = planes[i].getPixelStride(); 76 | int shift = (i == 0) ? 0 : 1; 77 | int w = width >> shift; 78 | int h = height >> shift; 79 | buffer.position(rowStride * (crop.top >> shift) + pixelStride * (crop.left >> shift)); 80 | for (int row = 0; row < h; row++) { 81 | int length; 82 | if (pixelStride == 1) { 83 | length = w; 84 | buffer.get(data, channelOffset, length); 85 | channelOffset += length; 86 | } else { 87 | length = (w - 1) * pixelStride + 1; 88 | buffer.get(rowData, 0, length); 89 | for (int col = 0; col < w; col++) { 90 | data[channelOffset] = rowData[col * pixelStride]; 91 | channelOffset += outputStride; 92 | } 93 | } 94 | if (row < h - 1) { 95 | buffer.position(buffer.position() + rowStride - length); 96 | } 97 | } 98 | } 99 | return data; 100 | } 101 | 102 | public static void image2yuv(Image imageIn, byte[] yuvOut) { 103 | Image.Plane[] planes = imageIn.getPlanes(); 104 | ByteBuffer Y = planes[0].getBuffer(); 105 | ByteBuffer Cr = planes[2].getBuffer(); 106 | int CrPixelStride = planes[2].getPixelStride(); 107 | ByteBuffer Cb = planes[1].getBuffer(); 108 | int CbPixelStride = planes[1].getPixelStride(); 109 | for (int i = 0, size = imageIn.getWidth() * imageIn.getHeight(); i < size; i++) { 110 | yuvOut[i] = Y.get(i); 111 | } 112 | for (int i = 0, size = imageIn.getWidth() * imageIn.getHeight(); i < size / 4; i++) { 113 | yuvOut[size + i * 2] = Cr.get(i * CrPixelStride); 114 | yuvOut[size + i * 2 + 1] = Cb.get(i * CbPixelStride); 115 | } 116 | try { 117 | File file = new File( 118 | Environment.getExternalStorageDirectory().getAbsolutePath() + "/dump.y"); 119 | FileOutputStream outputStream = new FileOutputStream(file); 120 | byte[] bytes = new byte[Y.remaining()]; 121 | Y.get(bytes); 122 | outputStream.write(bytes); 123 | outputStream.close(); 124 | 125 | file = new File( 126 | Environment.getExternalStorageDirectory().getAbsolutePath() + "/dump.cb"); 127 | outputStream = new FileOutputStream(file); 128 | bytes = new byte[Cb.remaining()]; 129 | Cb.get(bytes); 130 | outputStream.write(bytes); 131 | outputStream.close(); 132 | 133 | file = new File( 134 | Environment.getExternalStorageDirectory().getAbsolutePath() + "/dump.cr"); 135 | outputStream = new FileOutputStream(file); 136 | bytes = new byte[Cr.remaining()]; 137 | Cr.get(bytes); 138 | outputStream.write(bytes); 139 | outputStream.close(); 140 | } catch (IOException e) { 141 | e.printStackTrace(); 142 | } 143 | } 144 | 145 | public static void saveRgb2Bitmap(Buffer buf, String filename, int width, int height) { 146 | // Save the generated bitmap to a PNG so we can see what it looks like. 147 | Log.d("CameraCompat", "Creating " 148 | + Environment.getExternalStorageDirectory().getAbsolutePath() 149 | + "/" 150 | + filename); 151 | BufferedOutputStream bos = null; 152 | try { 153 | bos = new BufferedOutputStream(new FileOutputStream( 154 | Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + filename)); 155 | Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 156 | bmp.copyPixelsFromBuffer(buf); 157 | bmp.compress(Bitmap.CompressFormat.PNG, 90, bos); 158 | bmp.recycle(); 159 | } catch (IOException e) { 160 | e.printStackTrace(); 161 | } finally { 162 | if (bos != null) { 163 | try { 164 | bos.close(); 165 | } catch (IOException e) { 166 | e.printStackTrace(); 167 | } 168 | } 169 | } 170 | } 171 | 172 | public static void saveRawRgbData(ByteBuffer buf, int width, int height, String prefix) { 173 | // Save the generated bitmap to a PNG so we can see what it looks like. 174 | String filename = Environment.getExternalStorageDirectory().getAbsolutePath() 175 | + "/" 176 | + prefix 177 | + "_" 178 | + width 179 | + "_" 180 | + height 181 | + ".rgb"; 182 | Log.d("CameraCompat", "Creating " + filename); 183 | FileOutputStream outputStream; 184 | try { 185 | outputStream = new FileOutputStream(filename); 186 | outputStream.write(buf.array()); 187 | outputStream.close(); 188 | } catch (IOException e) { 189 | e.printStackTrace(); 190 | } 191 | } 192 | 193 | public static void saveRawRgbData(byte[] buf, int width, int height) { 194 | // Save the generated bitmap to a PNG so we can see what it looks like. 195 | String filename = Environment.getExternalStorageDirectory().getAbsolutePath() 196 | + "/dump_r_" 197 | + width 198 | + "_" 199 | + height 200 | + ".rgb"; 201 | Log.d("CameraCompat", "Creating " + filename); 202 | FileOutputStream outputStream; 203 | try { 204 | outputStream = new FileOutputStream(filename); 205 | outputStream.write(buf); 206 | outputStream.close(); 207 | } catch (IOException e) { 208 | e.printStackTrace(); 209 | } 210 | } 211 | 212 | public static void saveRawYuvData(byte[] buf, int width, int height, String prefix) { 213 | // Save the generated bitmap to a PNG so we can see what it looks like. 214 | String filename = Environment.getExternalStorageDirectory().getAbsolutePath() 215 | + "/" 216 | + prefix 217 | + "_" 218 | + width 219 | + "_" 220 | + height 221 | + ".yuv"; 222 | Log.d("CameraCompat", "Creating " + filename); 223 | FileOutputStream outputStream; 224 | try { 225 | outputStream = new FileOutputStream(filename); 226 | outputStream.write(buf); 227 | outputStream.close(); 228 | } catch (IOException e) { 229 | e.printStackTrace(); 230 | } 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/utils/GLUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.utils; 26 | 27 | import android.app.ActivityManager; 28 | import android.content.Context; 29 | import android.content.pm.ConfigurationInfo; 30 | import android.graphics.Bitmap; 31 | import android.opengl.GLES20; 32 | import android.util.Log; 33 | import java.nio.ByteBuffer; 34 | import java.nio.IntBuffer; 35 | import jp.co.cyberagent.android.gpuimage.OpenGlUtils; 36 | 37 | /** 38 | * Created by Piasy{github.com/Piasy} on 5/24/16. 39 | */ 40 | 41 | public final class GLUtil { 42 | public static final int NO_TEXTURE = -1; 43 | 44 | private GLUtil() { 45 | // no instance 46 | } 47 | 48 | public static boolean isSupportOpenGLES2(final Context context) { 49 | final ActivityManager activityManager = 50 | (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 51 | final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo(); 52 | return configurationInfo.reqGlEsVersion >= 0x20000; 53 | } 54 | 55 | public static int loadTexture(final Bitmap img, final int usedTexId) { 56 | return OpenGlUtils.loadTexture(img, usedTexId, true); 57 | } 58 | 59 | public static int loadTexture(final ByteBuffer data, final int width, final int height, 60 | final int usedTexId) { 61 | int textures[] = new int[1]; 62 | if (usedTexId == NO_TEXTURE) { 63 | GLES20.glGenTextures(1, textures, 0); 64 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]); 65 | GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, 66 | GLES20.GL_LINEAR); 67 | GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, 68 | GLES20.GL_LINEAR); 69 | GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, 70 | GLES20.GL_CLAMP_TO_EDGE); 71 | GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, 72 | GLES20.GL_CLAMP_TO_EDGE); 73 | GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0, 74 | GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, data); 75 | } else { 76 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, usedTexId); 77 | GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, width, height, GLES20.GL_RGBA, 78 | GLES20.GL_UNSIGNED_BYTE, data); 79 | textures[0] = usedTexId; 80 | } 81 | return textures[0]; 82 | } 83 | 84 | public static int loadTextureAsBitmap(final IntBuffer data, final int width, final int height, 85 | final int usedTexId) { 86 | Bitmap bitmap = Bitmap.createBitmap(data.array(), width, height, Bitmap.Config.ARGB_8888); 87 | return loadTexture(bitmap, usedTexId); 88 | } 89 | 90 | public static int loadShader(final String strSource, final int iType) { 91 | return OpenGlUtils.loadShader(strSource, iType); 92 | } 93 | 94 | public static int loadProgram(final String strVSource, final String strFSource) { 95 | return OpenGlUtils.loadProgram(strVSource, strFSource); 96 | } 97 | 98 | public static float rnd(final float min, final float max) { 99 | return OpenGlUtils.rnd(min, max); 100 | } 101 | 102 | public static void dumpGlError(String op) { 103 | int error; 104 | while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { 105 | Log.d("CameraCompat", "** " + op + ": glError " + error); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /CameraCompat/src/main/java/com/github/piasy/cameracompat/utils/Profiler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.utils; 26 | 27 | /** 28 | * Created by Piasy{github.com/Piasy} on 7/6/16. 29 | */ 30 | 31 | public class Profiler { 32 | private final MetricListener mMetricListener; 33 | 34 | public Profiler(MetricListener metricListener) { 35 | mMetricListener = metricListener; 36 | } 37 | 38 | public void metric(long preDraw, long yuv2rgba, long draw, long readPixels, long rgba2yuv) { 39 | if (preDraw <= 0 || yuv2rgba <= 0 || draw <= 0 || readPixels <= 0 || rgba2yuv <= 0) { 40 | return; 41 | } 42 | mMetricListener.onMetric(new Metric(preDraw, yuv2rgba, draw, readPixels, rgba2yuv)); 43 | } 44 | 45 | public interface MetricListener { 46 | void onMetric(Metric metric); 47 | } 48 | 49 | public static class Metric { 50 | public final long preDraw; 51 | public final long yuv2rgba; 52 | public final long draw; 53 | public final long readPixels; 54 | public final long rgba2yuv; 55 | 56 | public Metric(long preDraw, long yuv2rgba, long draw, long readPixels, long rgba2yuv) { 57 | this.preDraw = preDraw; 58 | this.yuv2rgba = yuv2rgba; 59 | this.draw = draw; 60 | this.readPixels = readPixels; 61 | this.rgba2yuv = rgba2yuv; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /CameraCompat/src/main/res/layout/preview_fragment.xml: -------------------------------------------------------------------------------- 1 | 24 | 25 | 31 | 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Piasy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CameraCompat 2 | 3 | Create a camera preview app with beautify and camera api compatibility! 4 | 5 | [ ![Download](https://api.bintray.com/packages/piasy/maven/CameraCompat/images/download.svg) ](https://bintray.com/piasy/maven/CameraCompat/_latestVersion) 6 | 7 | ![screenshot](art/screenshot.jpg) 8 | 9 | ## Usage 10 | 11 | ### dependency 12 | 13 | ``` gradle 14 | allprojects { 15 | repositories { 16 | maven { 17 | url "http://dl.bintray.com/piasy/maven" 18 | } 19 | } 20 | } 21 | 22 | compile 'com.github.piasy:CameraCompat:1.3.0' 23 | 24 | // sorry that it doesn't work now, pr is welcome! 25 | compile 'com.github.piasy:BasicBeautifyProcessor:1.3.0' 26 | ``` 27 | 28 | ### initialize 29 | 30 | ``` java 31 | // initialize in application's onCreate 32 | CameraCompat.init(getApplicationContext()); 33 | ``` 34 | 35 | ### add a ViewGroup into your layout 36 | 37 | ``` xml 38 | 43 | ``` 44 | 45 | ### start preview 46 | 47 | ``` java 48 | // start preview in your activity or fragment 49 | CameraCompat.Builder builder = new CameraCompat.Builder(this, this) 50 | .beautifyOn(false) // default beautify option 51 | .flashOpen(false) // default flash option 52 | .previewWidth(639) // preview width 53 | .previewHeight(479) // preview height 54 | .enableMirror(false) // default mirror option 55 | .frontCamera(false); // default camera option 56 | if (mBeautifyCapable) { 57 | // add beautify processor, sorry that BasicBeautifyProcessor doesn't work now, 58 | // but in our production app, our real beautify processor works well, 59 | // I'm not an expert about open-gl, pr is welcome! 60 | builder.addProcessor(new BasicBeautifyProcessor()); 61 | } 62 | mCameraCompat = builder.build(); 63 | mCameraCompat.startPreview(null, 64 | getSupportFragmentManager(), 65 | R.id.mPreviewContainer); // add this ViewGroup in your layout 66 | ``` 67 | 68 | ### receive callbacks 69 | 70 | ``` java 71 | @WorkerThread 72 | @Override 73 | public void onVideoSizeChanged(int width, int height) { 74 | Log.d("PublishActivity", "onVideoSizeChanged width = " + width 75 | + ", height = " + height); 76 | } 77 | 78 | @WorkerThread 79 | @Override 80 | public void onFrameData(final byte[] data, final int width, final int height) { 81 | Log.d("PublishActivity", "onFrameData width = " + width 82 | + ", height = " + height 83 | + ", data length = " + data.length); 84 | } 85 | 86 | @WorkerThread 87 | @Override 88 | public void onError(@CameraCompat.ErrorCode int code) { 89 | runOnUiThread(() -> 90 | Toast.makeText(this, "@CameraCompat.ErrorCode " + code, Toast.LENGTH_SHORT).show()); 91 | } 92 | ``` 93 | 94 | ### control behaviour 95 | 96 | ``` java 97 | mCameraCompat.switchBeautify(); 98 | mCameraCompat.switchCamera(); 99 | mCameraCompat.switchFlash(); 100 | mCameraCompat.switchMirror(); 101 | ``` 102 | 103 | for resize and visibility control, just operate your `ViewGroup`. 104 | 105 | [Full example can be found here](https://github.com/Piasy/CameraCompat/blob/master/app/src/main/java/com/github/piasy/cameracompat/example/PublishActivity.java). 106 | 107 | ## Try demo app 108 | 109 | Demo app can be downloaded from https://fir.im/CCT . Thanks for fir.im! 110 | 111 | ## Todo 112 | 113 | - [ ] basic beautify processor 114 | - [ ] configure data format received in `onFrameData`, currently the frame is 90 degree rotated clockwise 115 | - [ ] focus 116 | - [ ] use libyuv rather than self writen converter 117 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'me.tatarka.retrolambda' 3 | apply plugin: 'android-apt' 4 | apply plugin: 'com.jakewharton.butterknife' 5 | 6 | android { 7 | compileSdkVersion rootProject.ext.androidCompileSdkVersion 8 | buildToolsVersion rootProject.ext.androidBuildToolsVersion 9 | 10 | defaultConfig { 11 | minSdkVersion rootProject.ext.minSdkVersion 12 | targetSdkVersion rootProject.ext.targetSdkVersion 13 | versionCode rootProject.ext.releaseVersionCode 14 | versionName rootProject.ext.releaseVersionName 15 | 16 | applicationId "com.github.piasy.cameracompat.example" 17 | ndk { 18 | abiFilter "armeabi" 19 | } 20 | } 21 | buildTypes { 22 | release { 23 | minifyEnabled false 24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 25 | } 26 | } 27 | 28 | compileOptions { 29 | sourceCompatibility JavaVersion.VERSION_1_8 30 | targetCompatibility JavaVersion.VERSION_1_8 31 | } 32 | } 33 | 34 | dependencies { 35 | compile "com.android.support:appcompat-v7:$rootProject.ext.androidSupportSdkVersion" 36 | compile "com.jakewharton:butterknife:$rootProject.ext.butterKnifeVersion" 37 | apt "com.jakewharton:butterknife-compiler:$rootProject.ext.butterKnifeVersion" 38 | compile "com.github.yatatsu:autobundle:$rootProject.ext.autoBundleVersion" 39 | apt "com.github.yatatsu:autobundle-processor:$rootProject.ext.autoBundleVersion" 40 | compile 'com.afollestad.material-dialogs:core:0.9.0.2' 41 | 42 | compile 'com.squareup.leakcanary:leakcanary-android:1.5' 43 | 44 | compile project(':CameraCompat') 45 | compile project(':BasicBeautifyProcessor') 46 | 47 | compile("io.reactivex:rxandroid:$rootProject.ext.rxAndroidVersion") { 48 | exclude module: 'rxjava' 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/piasy/tools/android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 25 | 27 | 28 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/piasy/cameracompat/example/DemoApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.example; 26 | 27 | import android.app.Application; 28 | import com.squareup.leakcanary.LeakCanary; 29 | 30 | /** 31 | * Created by Piasy{github.com/Piasy} on 04/11/2016. 32 | */ 33 | 34 | public class DemoApplication extends Application { 35 | @Override 36 | public void onCreate() { 37 | super.onCreate(); 38 | 39 | if (LeakCanary.isInAnalyzerProcess(this)) { 40 | return; 41 | } 42 | LeakCanary.install(this); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/piasy/cameracompat/example/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.example; 26 | 27 | import android.os.Bundle; 28 | import android.support.v7.app.AppCompatActivity; 29 | import butterknife.ButterKnife; 30 | import butterknife.OnClick; 31 | import com.github.piasy.cameracompat.CameraCompat; 32 | 33 | public class MainActivity extends AppCompatActivity { 34 | 35 | @Override 36 | protected void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | 39 | CameraCompat.init(getApplicationContext()); 40 | 41 | setContentView(R.layout.activity_main); 42 | ButterKnife.bind(this); 43 | } 44 | 45 | @OnClick(R2.id.mBtnBeautifyPublish) 46 | public void beautifyPublish() { 47 | startActivity(PublishActivityAutoBundle.createIntentBuilder(true).build(this)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/piasy/cameracompat/example/PublishActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 Piasy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.piasy.cameracompat.example; 26 | 27 | import android.os.Bundle; 28 | import android.support.annotation.WorkerThread; 29 | import android.support.v7.app.AppCompatActivity; 30 | import android.util.Log; 31 | import android.view.View; 32 | import android.view.ViewGroup; 33 | import android.view.WindowManager; 34 | import android.widget.FrameLayout; 35 | import android.widget.Toast; 36 | import butterknife.BindView; 37 | import butterknife.ButterKnife; 38 | import butterknife.OnClick; 39 | import com.github.piasy.cameracompat.CameraCompat; 40 | import com.github.piasy.cameracompat.processor.basic.BasicBeautifyProcessor; 41 | import com.yatatsu.autobundle.AutoBundle; 42 | import com.yatatsu.autobundle.AutoBundleField; 43 | 44 | public class PublishActivity extends AppCompatActivity implements CameraCompat.VideoCaptureCallback, 45 | CameraCompat.ErrorHandler { 46 | 47 | @AutoBundleField 48 | boolean mBeautifyCapable; 49 | 50 | @BindView(R2.id.mPreviewContainer) 51 | View mContainer; 52 | @BindView(R2.id.mBtnSwitchBeautify) 53 | View mBtnSwitchBeautify; 54 | 55 | private CameraCompat mCameraCompat; 56 | 57 | private boolean mHide = false; 58 | private boolean mIsBig = true; 59 | 60 | @Override 61 | protected void onCreate(Bundle savedInstanceState) { 62 | AutoBundle.bind(this); 63 | super.onCreate(savedInstanceState); 64 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); 65 | 66 | setContentView(R.layout.activity_publish); 67 | ButterKnife.bind(this); 68 | 69 | if (!mBeautifyCapable) { 70 | mBtnSwitchBeautify.setVisibility(View.GONE); 71 | } 72 | 73 | start(); 74 | } 75 | 76 | @Override 77 | protected void onDestroy() { 78 | super.onDestroy(); 79 | CameraCompat.reset(); 80 | } 81 | 82 | @OnClick(R2.id.mBtnSwitchBeautify) 83 | public void switchBeautify() { 84 | mCameraCompat.switchBeautify(); 85 | } 86 | 87 | @OnClick(R2.id.mBtnSwitchCamera) 88 | public void switchCamera() { 89 | mCameraCompat.switchCamera(); 90 | } 91 | 92 | @OnClick(R2.id.mBtnSwitchFlash) 93 | public void switchFlash() { 94 | mCameraCompat.switchFlash(); 95 | } 96 | 97 | @OnClick(R2.id.mBtnSwitchMirror) 98 | public void switchMirror() { 99 | mCameraCompat.switchMirror(); 100 | } 101 | 102 | @OnClick(R2.id.mBtnSwitchVisibility) 103 | public void switchVisibility() { 104 | if (mHide) { 105 | mHide = false; 106 | mContainer.setVisibility(View.VISIBLE); 107 | } else { 108 | mHide = true; 109 | mContainer.setVisibility(View.INVISIBLE); 110 | } 111 | } 112 | 113 | @OnClick(R2.id.mBtnResize) 114 | public void resize() { 115 | if (mIsBig) { 116 | mIsBig = false; 117 | FrameLayout.LayoutParams params 118 | = (FrameLayout.LayoutParams) mContainer.getLayoutParams(); 119 | params.height = 300; 120 | params.width = 160; 121 | mContainer.setLayoutParams(params); 122 | 123 | getSupportFragmentManager().beginTransaction() 124 | .remove(getSupportFragmentManager().findFragmentByTag( 125 | CameraCompat.CAMERA_PREVIEW_FRAGMENT)) 126 | .commit(); 127 | 128 | start(); 129 | } else { 130 | mIsBig = true; 131 | FrameLayout.LayoutParams params 132 | = (FrameLayout.LayoutParams) mContainer.getLayoutParams(); 133 | params.height = ViewGroup.LayoutParams.MATCH_PARENT; 134 | params.width = ViewGroup.LayoutParams.MATCH_PARENT; 135 | mContainer.setLayoutParams(params); 136 | 137 | getSupportFragmentManager().beginTransaction() 138 | .remove(getSupportFragmentManager().findFragmentByTag( 139 | CameraCompat.CAMERA_PREVIEW_FRAGMENT)) 140 | .commit(); 141 | 142 | start(); 143 | } 144 | } 145 | 146 | private void start() { 147 | CameraCompat.Builder builder = new CameraCompat.Builder(this, this) 148 | .beautifyOn(false) // default beautify option 149 | .flashOpen(false) // default flash option 150 | .previewWidth(639) // preview width 151 | .previewHeight(479) // preview height 152 | .enableMirror(false) // default mirror option 153 | .frontCamera(false); // default camera option 154 | if (mBeautifyCapable) { 155 | // add beautify processor, sorry that BasicBeautifyProcessor doesn't work now, 156 | // but in our production app, our real beautify processor works well, 157 | // I'm not an expert about open-gl, pr is welcome! 158 | builder.addProcessor(new BasicBeautifyProcessor()); 159 | } 160 | mCameraCompat = builder.build(); 161 | mCameraCompat.startPreview(null, 162 | getSupportFragmentManager(), 163 | R.id.mPreviewContainer); // add this ViewGroup in your layout 164 | } 165 | 166 | @WorkerThread 167 | @Override 168 | public void onVideoSizeChanged(int width, int height) { 169 | Log.d("PublishActivity", "onVideoSizeChanged width = " + width 170 | + ", height = " + height); 171 | } 172 | 173 | @WorkerThread 174 | @Override 175 | public void onFrameData(final byte[] data, final int width, final int height) { 176 | Log.d("PublishActivity", "onFrameData width = " + width 177 | + ", height = " + height 178 | + ", data length = " + data.length); 179 | } 180 | 181 | @WorkerThread 182 | @Override 183 | public void onError(@CameraCompat.ErrorCode int code) { 184 | runOnUiThread(() -> 185 | Toast.makeText(this, "@CameraCompat.ErrorCode " + code, Toast.LENGTH_SHORT).show()); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 31 |