Repository: yangmingchuan/CameraMaster Branch: master Commit: 5340823cd85f Files: 103 Total size: 501.9 KB Directory structure: gitextract_43xg_rh9/ ├── .gitignore ├── .idea/ │ ├── caches/ │ │ └── build_file_checksums.ser │ ├── codeStyles/ │ │ └── Project.xml │ ├── gradle.xml │ ├── inspectionProfiles/ │ │ └── Project_Default.xml │ ├── jarRepositories.xml │ ├── misc.xml │ ├── modules.xml │ ├── runConfigurations.xml │ └── vcs.xml ├── README.md ├── app/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── camera/ │ │ └── cn/ │ │ └── cameramaster/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── assets/ │ │ │ └── web/ │ │ │ ├── css/ │ │ │ │ └── login.css │ │ │ ├── index.html │ │ │ └── login.html │ │ ├── java/ │ │ │ └── camera/ │ │ │ └── cn/ │ │ │ └── cameramaster/ │ │ │ ├── OpenReceiver.java │ │ │ ├── adapter/ │ │ │ │ ├── EffectAdapter.java │ │ │ │ ├── MenuAdapter.java │ │ │ │ └── SenseAdapter.java │ │ │ ├── base/ │ │ │ │ ├── App.java │ │ │ │ └── BaseActivity.java │ │ │ ├── bean/ │ │ │ │ └── Lab.java │ │ │ ├── filter/ │ │ │ │ ├── AFilter.java │ │ │ │ └── TextureFilter.java │ │ │ ├── server/ │ │ │ │ ├── AnyEventType.java │ │ │ │ ├── CoreService.java │ │ │ │ ├── ServerManager.java │ │ │ │ ├── TestController.java │ │ │ │ ├── component/ │ │ │ │ │ └── LoggerInterceptor.java │ │ │ │ ├── model/ │ │ │ │ │ └── UserInfo.java │ │ │ │ └── util/ │ │ │ │ ├── FileUtils.java │ │ │ │ └── NetUtils.java │ │ │ ├── ui/ │ │ │ │ ├── Camera2Activity.java │ │ │ │ ├── CameraActivity.java │ │ │ │ ├── CameraSurfaceViewActivity.java │ │ │ │ ├── CameraVideoActivity.java │ │ │ │ ├── GoogleCameraActivity.java │ │ │ │ ├── MainActivity.java │ │ │ │ └── ShowPicActivity.java │ │ │ ├── util/ │ │ │ │ ├── AppConstant.java │ │ │ │ ├── BitmapUtils.java │ │ │ │ ├── Camera2Util.java │ │ │ │ ├── CameraUtil.java │ │ │ │ ├── CameraV2.java │ │ │ │ ├── CompareSizesByArea.java │ │ │ │ ├── FilterEngine.java │ │ │ │ ├── LabUtil.java │ │ │ │ ├── MatrixUtils.java │ │ │ │ ├── Utils.java │ │ │ │ ├── cameravideo/ │ │ │ │ │ ├── CameraHelper.java │ │ │ │ │ ├── CoordinateTransformer.java │ │ │ │ │ ├── ICamera2.java │ │ │ │ │ ├── IVideoControl.java │ │ │ │ │ └── VideoPlayer.java │ │ │ │ └── render/ │ │ │ │ └── CameraV2Renderer.java │ │ │ └── view/ │ │ │ ├── AnimationTextView.java │ │ │ ├── AutoFitTextureView.java │ │ │ ├── AutoLocateHorizontalView.java │ │ │ ├── AutoTextureView.java │ │ │ ├── AwbSeekBar.java │ │ │ ├── AwbSeekBarChangeListener.java │ │ │ ├── CameraV2GLSurfaceView.java │ │ │ ├── ShowSurfaceView.java │ │ │ └── SleepThread.java │ │ └── res/ │ │ ├── anim/ │ │ │ ├── alpha_in.xml │ │ │ └── alpha_out.xml │ │ ├── drawable/ │ │ │ ├── ic_camera.xml │ │ │ ├── ic_fouces.xml │ │ │ ├── ic_launcher_background.xml │ │ │ └── ic_launcher_camera.xml │ │ ├── drawable-v24/ │ │ │ └── ic_launcher_foreground.xml │ │ ├── layout/ │ │ │ ├── activity_camera.xml │ │ │ ├── activity_camera2.xml │ │ │ ├── activity_camera_sv.xml │ │ │ ├── activity_camera_video.xml │ │ │ ├── activity_google_camera.xml │ │ │ ├── activity_look_camera.xml │ │ │ ├── activity_main.xml │ │ │ ├── activity_show_pic.xml │ │ │ ├── item_age.xml │ │ │ └── item_rv_text.xml │ │ ├── mipmap-anydpi-v26/ │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ ├── mipmap-xhdpi/ │ │ │ ├── ic_launcher_background.xml │ │ │ └── icon_back.xml │ │ ├── raw/ │ │ │ ├── base_fragment_shader.glsl │ │ │ └── base_vertex_shader.glsl │ │ ├── values/ │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── xml/ │ │ └── file_paths.xml │ └── test/ │ └── java/ │ └── camera/ │ └── cn/ │ └── cameramaster/ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.iml .gradle /local.properties /.idea/workspace.xml /.idea/libraries .DS_Store /build /captures .externalNativeBuild ================================================ FILE: .idea/codeStyles/Project.xml ================================================
xmlns:android ^$
xmlns:.* ^$ BY_NAME
.*:id http://schemas.android.com/apk/res/android
.*:name http://schemas.android.com/apk/res/android
name ^$
style ^$
.* ^$ BY_NAME
.* http://schemas.android.com/apk/res/android ANDROID_ATTRIBUTE_ORDER
.* .* BY_NAME
================================================ FILE: .idea/gradle.xml ================================================ ================================================ FILE: .idea/inspectionProfiles/Project_Default.xml ================================================ ================================================ FILE: .idea/jarRepositories.xml ================================================ ================================================ FILE: .idea/misc.xml ================================================ 1.8 ================================================ FILE: .idea/modules.xml ================================================ ================================================ FILE: .idea/runConfigurations.xml ================================================ ================================================ FILE: .idea/vcs.xml ================================================ ================================================ FILE: README.md ================================================ # SunCamera 一个 学习自定义的 Camera1 、Camera2 、Camera X 和 OpenglES 的Demo # 版本迭代 - 1.3 完善部分bug (暂时停止维护,抽空整理成底层SDK,相对架构来一次深度的学习) - 1.2 添加 拍照录像及硬件设备信息调节界面 - 1.1 添加 Camera 2 相机使用界面 - 1.0 添加 Camera 1 相机使用界面 # 感谢 https://github.com/chensowf/Camera 录像代码部分参考和学习。感谢开源 # 效果图 1. 目前 拍照 摄像界面 效果界面主要 在 CameraVideoActivity 界面中。 ![image](https://github.com/yangmingchuan/CameraMaster/blob/5d92dda84ff32038e7abe94a7a15c40eea7f1a66/app/src/main/res/drawable-v24/64F105A2D530CDE27A0F21CBC6C0B877.gif) 2.camera+surfaceview 博客地址:https://www.jianshu.com/p/af39024896ce ![image](https://upload-images.jianshu.io/upload_images/6188347-cd61d9a329522b0a?imageMogr2/auto-orient/) 3. camera2 + TextureView 博客地址 : https://www.jianshu.com/p/c99fd9dfd77f 4. camera2 + glSurfaceview + opengles 博客地址 : https://www.jianshu.com/p/e4ece3f21ec8 ![image](https://img-blog.csdnimg.cn/2019040210145280.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI3OTQ4NjU5,size_16,color_FFFFFF,t_70) ================================================ FILE: app/.gitignore ================================================ /build ================================================ FILE: app/build.gradle ================================================ apply plugin: 'com.android.application' android { compileSdkVersion 27 defaultConfig { applicationId "camera.cn.cameramaster" minSdkVersion 17 targetSdkVersion 27 versionCode 103 versionName "1.0.3" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { debug { debuggable true zipAlignEnabled false minifyEnabled false shrinkResources false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } release { debuggable false zipAlignEnabled true minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } buildToolsVersion '27.0.3' } dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'com.android.support:appcompat-v7:27.1.1' implementation 'com.android.support.constraint:constraint-layout:1.1.3' testImplementation 'junit:junit:4.12' implementation 'com.android.support:cardview-v7:27.1.1' implementation 'com.android.support:recyclerview-v7:27.1.1' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' // butterknife implementation 'com.jakewharton:butterknife:8.8.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' // permission implementation 'com.yanzhenjie:permission:2.0.0-rc4' implementation 'com.android.support:exifinterface:27.1.1' // api implementation 'com.yanzhenjie.andserver:api:2.0.4' annotationProcessor 'com.yanzhenjie.andserver:processor:2.0.4' // load json implementation 'com.alibaba:fastjson:1.2.48' implementation 'com.yanzhenjie:loading:1.0.0' // event bus implementation 'org.greenrobot:eventbus:3.1.1' api "io.reactivex.rxjava2:rxjava:2.1.0" api "io.reactivex.rxjava2:rxandroid:2.1.0" } ================================================ FILE: app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the # proguardFiles setting in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile ================================================ FILE: app/src/androidTest/java/camera/cn/cameramaster/ExampleInstrumentedTest.java ================================================ package camera.cn.cameramaster; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; /** * Instrumented test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() throws Exception { // Context of the app under test. Context appContext = InstrumentationRegistry.getTargetContext(); assertEquals("camera.cn.cameramaster", appContext.getPackageName()); } } ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/assets/web/css/login.css ================================================ .sec_body { color:#404040;background:#EBEBEB;text-shadow:#ddd 0 1px 0px;font-family:Helvetica;line-height:1.5;font-size:small; } .center_father {color:#404040;text-align: center;} .center_son { margin-right: auto; margin-left: auto; } ================================================ FILE: app/src/main/assets/web/index.html ================================================ AndServer Sample
Login

More, please see the sample code.
================================================ FILE: app/src/main/assets/web/login.html ================================================ Sign In

Sign In

Account:  
     
Password:  
     

Account: 123, Password: 123

================================================ FILE: app/src/main/java/camera/cn/cameramaster/OpenReceiver.java ================================================ package camera.cn.cameramaster; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log; import camera.cn.cameramaster.ui.MainActivity; /** * 开机 * @packageName: cn.tongue.tonguecamera * @fileName: OpenReceiver * @date: 2019/1/28 13:09 * @author: ymc * @QQ:745612618 */ public class OpenReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.i("broadCastReceiver","onReceiver..."); Intent mBootIntent = new Intent(context, MainActivity.class); // 必须设置FLAG_ACTIVITY_NEW_TASK mBootIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(mBootIntent); } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/adapter/EffectAdapter.java ================================================ package camera.cn.cameramaster.adapter; import android.content.Context; import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import butterknife.BindView; import butterknife.ButterKnife; import camera.cn.cameramaster.R; /** * effect 适配器 * * @packageName: cn.tongue.tonguecamera.adapter * @fileName: EffectAdapter * @date: 2019/4/17 14:33 * @author: ymc * @QQ:745612618 */ public class EffectAdapter extends RecyclerView.Adapter { private LayoutInflater mLayoutInflater; private String[] effectArr; public EffectAdapter(Context mContext,String[] arr) { this.effectArr = arr; mLayoutInflater = LayoutInflater.from(mContext); } @NonNull @Override public EffectViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { return new EffectViewHolder(mLayoutInflater.inflate(R.layout.item_rv_text, parent, false)); } @Override public void onBindViewHolder(@NonNull EffectViewHolder holder, int position) { holder.mTextView.setText(effectArr[position]); } @Override public int getItemCount() { return effectArr.length; } public class EffectViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { @BindView(R.id.text_view) TextView mTextView; EffectViewHolder(View view) { super(view); ButterKnife.bind(this, view); view.setOnClickListener(this); } @Override public void onClick(View v) { if (effectOnItemClickListener != null) { effectOnItemClickListener.itemOnClick(getPosition()); } } } private EffectOnItemClickListener effectOnItemClickListener; public interface EffectOnItemClickListener { void itemOnClick(int position); } public void setEffectOnItemClickListener(EffectOnItemClickListener effectOnItemClickListener) { this.effectOnItemClickListener = effectOnItemClickListener; } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/adapter/MenuAdapter.java ================================================ package camera.cn.cameramaster.adapter; import android.content.Context; import android.graphics.Color; import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import java.util.List; import camera.cn.cameramaster.R; import camera.cn.cameramaster.view.AutoLocateHorizontalView; /** * 选项适配器 * * @author ymc * @date 2019年5月8日 10:38:54 */ public class MenuAdapter extends RecyclerView.Adapter implements AutoLocateHorizontalView.IAutoLocateHorizontalView { private Context context; private View view; private List ages; private AutoLocateHorizontalView recyclerView; public MenuAdapter(Context context, List ages, AutoLocateHorizontalView recyclerView){ this.context = context; this.ages = ages; this.recyclerView = recyclerView; } @NonNull @Override public ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { view = LayoutInflater.from(context).inflate(R.layout.item_age,parent,false); return new ItemViewHolder(view); } @Override public void onBindViewHolder(@NonNull ItemViewHolder holder, int position) { holder.tvAge.setText(ages.get(position)); } @Override public int getItemCount() { return ages.size(); } @Override public View getItemView() { return view; } @Override public void onViewSelected(boolean isSelected, int pos, RecyclerView.ViewHolder holder, int itemWidth) { if(isSelected) { ((ItemViewHolder) holder).tvAge.setTextSize(TypedValue.COMPLEX_UNIT_SP,16); ((ItemViewHolder) holder).tvAge.setTextColor(Color.WHITE); ((ItemViewHolder) holder).tvAge.setAlpha(1.0f); }else{ ((ItemViewHolder) holder).tvAge.setTextSize(TypedValue.COMPLEX_UNIT_SP,14); ((ItemViewHolder) holder).tvAge.setTextColor(Color.rgb(0xfe,0xfe,0xfe)); ((ItemViewHolder) holder).tvAge.setAlpha(0.5f); } } class ItemViewHolder extends RecyclerView.ViewHolder{ TextView tvAge; ItemViewHolder(View itemView) { super(itemView); tvAge = itemView.findViewById(R.id.tv_age); tvAge.setTag(this); tvAge.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ItemViewHolder itemViewHolder = (ItemViewHolder)v.getTag(); int position = recyclerView.getChildAdapterPosition(itemViewHolder.itemView); position--; recyclerView.moveToPosition(position); } }); } } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/adapter/SenseAdapter.java ================================================ package camera.cn.cameramaster.adapter; import android.content.Context; import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import butterknife.BindView; import butterknife.ButterKnife; import camera.cn.cameramaster.R; /** * effect 适配器 * * @packageName: cn.tongue.tonguecamera.adapter * @fileName: EffectAdapter * @date: 2019/4/17 14:33 * @author: ymc * @QQ:745612618 */ public class SenseAdapter extends RecyclerView.Adapter { private LayoutInflater mLayoutInflater; private String[] senseArr; public SenseAdapter(Context mContext,String[] arr) { this.senseArr = arr; mLayoutInflater = LayoutInflater.from(mContext); } @NonNull @Override public EffectViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { return new EffectViewHolder(mLayoutInflater.inflate(R.layout.item_rv_text, parent, false)); } @Override public void onBindViewHolder(@NonNull EffectViewHolder holder, int position) { holder.mTextView.setText(senseArr[position]); } @Override public int getItemCount() { return senseArr.length; } public class EffectViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { @BindView(R.id.text_view) TextView mTextView; EffectViewHolder(View view) { super(view); ButterKnife.bind(this, view); view.setOnClickListener(this); } @Override public void onClick(View v) { if (senseOnItemClickListener != null) { senseOnItemClickListener.itemOnClick(getPosition()); } } } private SenseOnItemClickListener senseOnItemClickListener; public interface SenseOnItemClickListener { void itemOnClick(int position); } public void setSenseOnItemClickListener(SenseOnItemClickListener senseOnItemClickListener) { this.senseOnItemClickListener = senseOnItemClickListener; } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/base/App.java ================================================ /* * Copyright 2018 Yan Zhenjie. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package camera.cn.cameramaster.base; import android.app.Application; import android.content.Context; import android.os.Environment; import android.support.annotation.NonNull; import com.yanzhenjie.andserver.util.IOUtils; import java.io.File; import camera.cn.cameramaster.server.util.FileUtils; /** * application * * Created by YanZhenjie on 2018/6/9. * @author ymc */ public class App extends Application { private static App mInstance; private File mRootDir; @Override public void onCreate() { super.onCreate(); if (mInstance == null) { mInstance = this; initRootPath(this); } } @NonNull public static App getInstance() { return mInstance; } @NonNull public File getRootDir() { return mRootDir; } private void initRootPath(Context context) { if (mRootDir != null) { return; } if (FileUtils.storageAvailable()) { mRootDir = Environment.getExternalStorageDirectory(); } else { mRootDir = context.getFilesDir(); } mRootDir = new File(mRootDir, "AndServer"); IOUtils.createFolder(mRootDir); } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/base/BaseActivity.java ================================================ package camera.cn.cameramaster.base; import android.os.Build; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.view.ViewCompat; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import butterknife.ButterKnife; import butterknife.Unbinder; import camera.cn.cameramaster.R; /** * 基础Activity * * @packageName: cn.ymc.vip.suntimejava.base * @fileName: BaseActivity * @date: 2019/1/8 17:35 * @author: ymc * @QQ:745612618 */ public abstract class BaseActivity extends AppCompatActivity { protected BaseActivity activity; private Unbinder bun; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); //去除标题栏 supportRequestWindowFeature(Window.FEATURE_NO_TITLE); //去除状态栏 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(getLayoutId()); // getSupportActionBar().hide(); bun = ButterKnife.bind(this); activity = this; initStatusColor(); initView(); initData(); } /** * 设置透明状态栏,这样才能让 ContentView 向上 6.0小米手机设置 tootlbar 会被挤上去 * window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); */ private void initStatusColor() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){ Window window = activity.getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); //设置状态栏颜色 window.setStatusBarColor(getColor(R.color.theme)); ViewGroup mContentView = activity.findViewById(Window.ID_ANDROID_CONTENT); View mChildView = mContentView.getChildAt(0); if (mChildView != null) { //注意不是设置 ContentView 的 FitsSystemWindows, 而是设置 ContentView 的第一个子 View . 使其不为系统 View 预留空间. ViewCompat.setFitsSystemWindows(mChildView, false); } } } protected abstract int getLayoutId(); protected abstract void initView(); protected abstract void initData(); @Override protected void onDestroy() { bun.unbind(); super.onDestroy(); } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/bean/Lab.java ================================================ package camera.cn.cameramaster.bean; /** * lab 实体类 * * @packageName: cn.tongue.tonguecamera.bean * @fileName: Lab * @date: 2019/4/3 13:37 * @author: ymc * @QQ:745612618 */ public class Lab { public double L; public double a; public double b; public double getL() { return L; } public void setL(double l) { L = l; } public double getA() { return a; } public void setA(double a) { this.a = a; } public double getB() { return b; } public void setB(double b) { this.b = b; } @Override public String toString() { return "Lab l:" + L + " Lab a:" + a + " Lab b:" + b; } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/filter/AFilter.java ================================================ /* * * AFilter.java * * Created by Wuwang on 2016/11/19 * Copyright © 2016年 深圳哎吖科技. All rights reserved. */ package camera.cn.cameramaster.filter; import android.content.res.Resources; import android.opengl.GLES20; import android.util.Log; import android.util.SparseArray; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.ShortBuffer; import java.util.Arrays; import camera.cn.cameramaster.util.MatrixUtils; /** * Description: */ public abstract class AFilter { private static final String TAG="Filter"; public static final int KEY_OUT=0x101; public static final int KEY_IN=0x102; public static final int KEY_INDEX=0x201; public static boolean DEBUG=true; /** * 单位矩阵 */ public static final float[] OM= MatrixUtils.getOriginalMatrix(); /** * 程序句柄 */ protected int mProgram; /** * 顶点坐标句柄 */ protected int mHPosition; /** * 纹理坐标句柄 */ protected int mHCoord; /** * 总变换矩阵句柄 */ protected int mHMatrix; /** * 默认纹理贴图句柄 */ protected int mHTexture; protected Resources mRes; /** * 顶点坐标Buffer */ protected FloatBuffer mVerBuffer; /** * 纹理坐标Buffer */ protected FloatBuffer mTexBuffer; /** * 索引坐标Buffer */ protected ShortBuffer mindexBuffer; protected int mFlag=0; private float[] matrix= Arrays.copyOf(OM,16); private int textureType=0; //默认使用Texture2D0 private int textureId=0; //顶点坐标 private float pos[] = { -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, }; //纹理坐标 private float[] coord={ 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, }; private SparseArray mBools; private SparseArray mInts; private SparseArray mFloats; public AFilter(Resources mRes){ this.mRes=mRes; initBuffer(); } public final void create(){ onCreate(); } public final void setSize(int width,int height){ onSizeChanged(width,height); } public void draw(){ onClear(); onUseProgram(); onSetExpandData(); onBindTexture(); onDraw(); } public void setMatrix(float[] matrix){ this.matrix=matrix; } public float[] getMatrix(){ return matrix; } public final void setTextureType(int type){ this.textureType=type; } public final int getTextureType(){ return textureType; } public final int getTextureId(){ return textureId; } public final void setTextureId(int textureId){ this.textureId=textureId; } public void setFlag(int flag){ this.mFlag=flag; } public int getFlag(){ return mFlag; } public void setFloat(int type,float ... params){ if(mFloats==null){ mFloats=new SparseArray<>(); } mFloats.put(type,params); } public void setInt(int type,int ... params){ if(mInts==null){ mInts=new SparseArray<>(); } mInts.put(type,params); } public void setBool(int type,boolean ... params){ if(mBools==null){ mBools=new SparseArray<>(); } mBools.put(type,params); } public boolean getBool(int type,int index) { if (mBools == null){ return false; } boolean[] b = mBools.get(type); return !(b == null || b.length <= index) && b[index]; } public int getInt(int type,int index){ if (mInts == null){ return 0; } int[] b = mInts.get(type); if(b == null || b.length <= index){ return 0; } return b[index]; } public float getFloat(int type,int index){ if (mFloats == null) { return 0; } float[] b = mFloats.get(type); if(b == null || b.length <= index){ return 0; } return b[index]; } public int getOutputTexture(){ return -1; } /** * 实现此方法,完成程序的创建,可直接调用createProgram来实现 */ protected abstract void onCreate(); protected abstract void onSizeChanged(int width,int height); protected final void createProgram(String vertex, String fragment){ mProgram= uCreateGlProgram(vertex,fragment); mHPosition= GLES20.glGetAttribLocation(mProgram, "vPosition"); mHCoord= GLES20.glGetAttribLocation(mProgram,"vCoord"); mHMatrix= GLES20.glGetUniformLocation(mProgram,"vMatrix"); mHTexture= GLES20.glGetUniformLocation(mProgram,"vTexture"); } protected final void createProgramByAssetsFile(String vertex, String fragment){ createProgram(uRes(mRes,vertex),uRes(mRes,fragment)); } /** * Buffer初始化 */ protected void initBuffer(){ ByteBuffer a= ByteBuffer.allocateDirect(32); a.order(ByteOrder.nativeOrder()); mVerBuffer=a.asFloatBuffer(); mVerBuffer.put(pos); mVerBuffer.position(0); ByteBuffer b= ByteBuffer.allocateDirect(32); b.order(ByteOrder.nativeOrder()); mTexBuffer=b.asFloatBuffer(); mTexBuffer.put(coord); mTexBuffer.position(0); } protected void onUseProgram(){ GLES20.glUseProgram(mProgram); } /** * 启用顶点坐标和纹理坐标进行绘制 */ protected void onDraw(){ GLES20.glEnableVertexAttribArray(mHPosition); GLES20.glVertexAttribPointer(mHPosition,2, GLES20.GL_FLOAT, false, 0,mVerBuffer); GLES20.glEnableVertexAttribArray(mHCoord); GLES20.glVertexAttribPointer(mHCoord, 2, GLES20.GL_FLOAT, false, 0, mTexBuffer); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP,0,4); GLES20.glDisableVertexAttribArray(mHPosition); GLES20.glDisableVertexAttribArray(mHCoord); } /** * 清除画布 */ protected void onClear(){ GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f); GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); } /** * 设置其他扩展数据 */ protected void onSetExpandData(){ GLES20.glUniformMatrix4fv(mHMatrix,1,false,matrix,0); } /** * 绑定默认纹理 */ protected void onBindTexture(){ GLES20.glActiveTexture(GLES20.GL_TEXTURE0+textureType); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,getTextureId()); GLES20.glUniform1i(mHTexture,textureType); } public static void glError(int code,Object index){ if(DEBUG&&code!=0){ Log.e(TAG,"glError:"+code+"---"+index); } } //通过路径加载Assets中的文本内容 public static String uRes(Resources mRes, String path){ StringBuilder result=new StringBuilder(); try{ InputStream is=mRes.getAssets().open(path); int ch; byte[] buffer=new byte[1024]; while (-1!=(ch=is.read(buffer))){ result.append(new String(buffer,0,ch)); } }catch (Exception e){ return null; } return result.toString().replaceAll("\\r\\n","\n"); } //创建GL程序 public static int uCreateGlProgram(String vertexSource, String fragmentSource){ int vertex=uLoadShader(GLES20.GL_VERTEX_SHADER,vertexSource); if(vertex==0){ return 0; } int fragment=uLoadShader(GLES20.GL_FRAGMENT_SHADER,fragmentSource); if(fragment==0){ return 0; } int program= GLES20.glCreateProgram(); if(program!=0){ GLES20.glAttachShader(program,vertex); GLES20.glAttachShader(program,fragment); GLES20.glLinkProgram(program); int[] linkStatus=new int[1]; GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS,linkStatus,0); if(linkStatus[0]!= GLES20.GL_TRUE){ glError(1,"Could not link program:"+ GLES20.glGetProgramInfoLog(program)); GLES20.glDeleteProgram(program); program=0; } } return program; } //加载shader public static int uLoadShader(int shaderType,String source){ int shader= GLES20.glCreateShader(shaderType); if(0!=shader){ GLES20.glShaderSource(shader,source); GLES20.glCompileShader(shader); int[] compiled=new int[1]; GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS,compiled,0); if(compiled[0]==0){ glError(1,"Could not compile shader:"+shaderType); glError(1,"GLES20 Error:"+ GLES20.glGetShaderInfoLog(shader)); GLES20.glDeleteShader(shader); shader=0; } } return shader; } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/filter/TextureFilter.java ================================================ package camera.cn.cameramaster.filter; import android.content.res.Resources; import android.graphics.SurfaceTexture; import java.nio.ByteBuffer; /** * @packageName: cn.tongue.tonguecamera.filter * @fileName: TextureFilter * @date: 2019/3/15 14:40 * @author: ymc * @QQ:745612618 */ public class TextureFilter extends AFilter{ private int width=0; private int height=0; private int[] fFrame = new int[1]; private int[] fTexture = new int[1]; private int[] mCameraTexture=new int[1]; private SurfaceTexture mSurfaceTexture; private float[] mCoordOM=new float[16]; /** * 获取Track数据 */ private ByteBuffer tBuffer; public TextureFilter(Resources mRes) { super(mRes); } @Override protected void onCreate() { } @Override protected void onSizeChanged(int width, int height) { } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/server/AnyEventType.java ================================================ package camera.cn.cameramaster.server; /** * event bus 消息类 * * @packageName: cn.tongue.tonguecamera.eventbus * @fileName: AnyEventType * @date: 2019/4/24 18:36 * @author: ymc * @QQ:745612618 */ public class AnyEventType { public AnyEventType(){} } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/server/CoreService.java ================================================ /* * Copyright © 2018 Yan Zhenjie. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package camera.cn.cameramaster.server; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.support.annotation.Nullable; import android.util.Log; import com.yanzhenjie.andserver.AndServer; import com.yanzhenjie.andserver.Server; import java.util.concurrent.TimeUnit; import camera.cn.cameramaster.server.util.NetUtils; /** * server * * Created by Yan Zhenjie on 2018/6/9. * @author ymc */ public class CoreService extends Service { private static final String TAG = "CoreService"; private Server mServer; @Override public void onCreate() { mServer = AndServer.serverBuilder() .inetAddress(NetUtils.getLocalIPAddress()) .port(8081) .timeout(10, TimeUnit.SECONDS) .listener(new Server.ServerListener() { @Override public void onStarted() { String hostAddress = mServer.getInetAddress().getHostAddress(); ServerManager.onServerStart(CoreService.this, hostAddress); Log.e(TAG, "onStarted: "); } @Override public void onStopped() { ServerManager.onServerStop(CoreService.this); Log.e(TAG, "onStopped: "); } @Override public void onException(Exception e) { ServerManager.onServerError(CoreService.this, e.getMessage()); Log.e(TAG, "onException: "); } }) .build(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { startServer(); return START_STICKY; } @Override public void onDestroy() { stopServer(); super.onDestroy(); } /** * Start server. */ private void startServer() { if (mServer.isRunning()) { String hostAddress = mServer.getInetAddress().getHostAddress(); ServerManager.onServerStart(CoreService.this, hostAddress); } else { mServer.startup(); } } /** * Stop server. */ private void stopServer() { mServer.shutdown(); } @Nullable @Override public IBinder onBind(Intent intent) { return null; } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/server/ServerManager.java ================================================ /* * Copyright © 2018 Yan Zhenjie. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package camera.cn.cameramaster.server; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Build; import android.support.annotation.RequiresApi; import camera.cn.cameramaster.ui.GoogleCameraActivity; /** * * Created by Yan Zhenjie on 2018/6/9. * @author ymc */ public class ServerManager extends BroadcastReceiver { private static final String ACTION = "com.yanzhenjie.andserver.receiver"; private static final String CMD_KEY = "CMD_KEY"; private static final String MESSAGE_KEY = "MESSAGE_KEY"; private static final int CMD_VALUE_START = 1; private static final int CMD_VALUE_ERROR = 2; private static final int CMD_VALUE_STOP = 4; /** * Notify serverStart. * * @param context context. */ public static void onServerStart(Context context, String hostAddress) { sendBroadcast(context, CMD_VALUE_START, hostAddress); } /** * Notify serverStop. * * @param context context. */ public static void onServerError(Context context, String error) { sendBroadcast(context, CMD_VALUE_ERROR, error); } /** * Notify serverStop. * * @param context context. */ public static void onServerStop(Context context) { sendBroadcast(context, CMD_VALUE_STOP); } private static void sendBroadcast(Context context, int cmd) { sendBroadcast(context, cmd, null); } private static void sendBroadcast(Context context, int cmd, String message) { Intent broadcast = new Intent(ACTION); broadcast.putExtra(CMD_KEY, cmd); broadcast.putExtra(MESSAGE_KEY, message); context.sendBroadcast(broadcast); } private GoogleCameraActivity mActivity; private Intent mService; public ServerManager(GoogleCameraActivity activity) { this.mActivity = activity; mService = new Intent(activity, CoreService.class); } /** * Register broadcast. */ public void register() { IntentFilter filter = new IntentFilter(ACTION); mActivity.registerReceiver(this, filter); } /** * UnRegister broadcast. */ public void unRegister() { mActivity.unregisterReceiver(this); } public void startServer() { mActivity.startService(mService); } public void stopServer() { mActivity.stopService(mService); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (ACTION.equals(action)) { int cmd = intent.getIntExtra(CMD_KEY, 0); switch (cmd) { case CMD_VALUE_START: { String ip = intent.getStringExtra(MESSAGE_KEY); mActivity.onServerStart(ip); break; } case CMD_VALUE_ERROR: { String error = intent.getStringExtra(MESSAGE_KEY); mActivity.onServerError(error); break; } case CMD_VALUE_STOP: { mActivity.onServerStop(); break; } default: break; } } } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/server/TestController.java ================================================ package camera.cn.cameramaster.server; import android.os.Build; import android.support.annotation.RequiresApi; import android.util.Log; import com.alibaba.fastjson.JSON; import com.yanzhenjie.andserver.annotation.Addition; import com.yanzhenjie.andserver.annotation.CookieValue; import com.yanzhenjie.andserver.annotation.FormPart; import com.yanzhenjie.andserver.annotation.GetMapping; import com.yanzhenjie.andserver.annotation.PathVariable; import com.yanzhenjie.andserver.annotation.PostMapping; import com.yanzhenjie.andserver.annotation.RequestBody; import com.yanzhenjie.andserver.annotation.RequestMapping; import com.yanzhenjie.andserver.annotation.RequestParam; import com.yanzhenjie.andserver.annotation.ResponseBody; import com.yanzhenjie.andserver.annotation.RestController; import com.yanzhenjie.andserver.http.HttpRequest; import com.yanzhenjie.andserver.http.HttpResponse; import com.yanzhenjie.andserver.http.cookie.Cookie; import com.yanzhenjie.andserver.http.multipart.MultipartFile; import com.yanzhenjie.andserver.http.session.Session; import com.yanzhenjie.andserver.util.MediaType; import org.greenrobot.eventbus.EventBus; import java.io.File; import java.io.IOException; import java.util.List; import camera.cn.cameramaster.server.model.UserInfo; import camera.cn.cameramaster.server.util.FileUtils; /** * 测试控制器 * * @packageName: ymc.cn.servertest * @fileName: TestController * @date: 2019/4/23 16:15 * @author: ymc * @QQ:745612618 */ @RestController @RequestMapping(path = "/test") @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public class TestController { private static final String TAG = "TestController"; private static final String LOGIN_ATTRIBUTE = "USER.LOGIN.SIGN"; @ResponseBody @GetMapping("/take") public void takeCamera() { EventBus.getDefault().post(new AnyEventType()); } @GetMapping(path = "/get/{userId}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) String info(@PathVariable(name = "userId") String userId) { return userId; } @PostMapping(path = "/login", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) String login(HttpRequest request, HttpResponse response, @RequestParam(name = "account",required = false ) String account, @RequestParam(name = "password",required = false) String password) { Session session = request.getValidSession(); session.setAttribute(LOGIN_ATTRIBUTE, true); Cookie cookie = new Cookie("account", account + "=" + password); response.addCookie(cookie); return "login successful"; } @Addition(stringType = "login", booleanType = true) @GetMapping(path = "/userInfo", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) UserInfo userInfo(@CookieValue("account") String account) { Log.e(TAG, "Account: " + account); UserInfo userInfo = new UserInfo(); userInfo.setmUserId("123"); userInfo.setmUserName("AndServer"); return userInfo; } @PostMapping(path = "/upload", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) String upload(@RequestParam(name = "header") MultipartFile file) throws IOException { File localFile = FileUtils.createRandomFile(file); file.transferTo(localFile); return localFile.getAbsolutePath(); } @GetMapping(path = "/consume", consumes = {"application/json", "!application/xml"}) String consume() { return "Consume is successful"; } @GetMapping(path = "/produce", produces = {"application/json; charset=utf-8"}) String produce() { return "Produce is successful"; } @GetMapping(path = "/include", params = {"name=123"}) String include(@RequestParam(name = "name") String name) { return name; } @GetMapping(path = "/exclude", params = "name!=123") String exclude() { return "Exclude is successful."; } @GetMapping(path = {"/mustKey", "/getName"}, params = "name") String getMustKey(@RequestParam(name = "name") String name) { return name; } @PostMapping(path = {"/mustKey", "/postName"}, params = "name") String postMustKey(@RequestParam(name = "name") String name) { return name; } @GetMapping(path = "/noName", params = "!name") String noName() { return "NoName is successful."; } @PostMapping(path = "/formPart") String forPart(@FormPart(name = "user") UserInfo userInfo) { return JSON.toJSONString(userInfo); } @PostMapping(path = "/jsonBody") String jsonBody(@RequestBody UserInfo userInfo) { return JSON.toJSONString(userInfo); } @PostMapping(path = "/listBody") String jsonBody(@RequestBody List infoList) { return JSON.toJSONString(infoList); } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/server/component/LoggerInterceptor.java ================================================ /* * Copyright 2018 Yan Zhenjie. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package camera.cn.cameramaster.server.component; import android.support.annotation.NonNull; import android.util.Log; import com.alibaba.fastjson.JSON; import com.yanzhenjie.andserver.annotation.Interceptor; import com.yanzhenjie.andserver.framework.HandlerInterceptor; import com.yanzhenjie.andserver.framework.handler.RequestHandler; import com.yanzhenjie.andserver.http.HttpMethod; import com.yanzhenjie.andserver.http.HttpRequest; import com.yanzhenjie.andserver.http.HttpResponse; import com.yanzhenjie.andserver.util.MultiValueMap; /** * 拦截器 * * @author ymc */ @Interceptor public class LoggerInterceptor implements HandlerInterceptor { private static final String TAG = "LoggerInterceptor"; @Override public boolean onIntercept(@NonNull HttpRequest request, @NonNull HttpResponse response, @NonNull RequestHandler handler) { String path = request.getPath(); HttpMethod method = request.getMethod(); MultiValueMap valueMap = request.getParameter(); Log.e(TAG, "Path: " + path); Log.e(TAG, "Method: " + method.value()); Log.e(TAG, "Param: " + JSON.toJSONString(valueMap)); return false; } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/server/model/UserInfo.java ================================================ package camera.cn.cameramaster.server.model; import android.os.Parcel; import android.os.Parcelable; import com.alibaba.fastjson.annotation.JSONField; /** * 用户 * * @packageName: ymc.cn.servertest.model * @fileName: UserInfo * @date: 2019/4/23 17:31 * @author: ymc * @QQ:745612618 */ public class UserInfo implements Parcelable { @JSONField(name = "userId") private String mUserId; @JSONField(name = "userName") private String mUserName; public static final Creator CREATOR = new Creator() { @Override public UserInfo createFromParcel(Parcel in) { return new UserInfo(in); } @Override public UserInfo[] newArray(int size) { return new UserInfo[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mUserId); dest.writeString(mUserName); } public UserInfo() { } protected UserInfo(Parcel in) { mUserId = in.readString(); mUserName = in.readString(); } public String getmUserId() { return mUserId; } public void setmUserId(String mUserId) { this.mUserId = mUserId; } public String getmUserName() { return mUserName; } public void setmUserName(String mUserName) { this.mUserName = mUserName; } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/server/util/FileUtils.java ================================================ /* * Copyright 2018 Yan Zhenjie. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package camera.cn.cameramaster.server.util; import android.os.Environment; import android.webkit.MimeTypeMap; import com.yanzhenjie.andserver.http.multipart.MultipartFile; import com.yanzhenjie.andserver.util.StringUtils; import java.io.File; import java.util.UUID; import camera.cn.cameramaster.base.App; /** * Created by YanZhenjie on 2018/6/9. */ public class FileUtils { /** * Create a random file based on mimeType. * * @param file file. * * @return file object. */ public static File createRandomFile(MultipartFile file) { String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(file.getContentType().toString()); if (StringUtils.isEmpty(extension)) { extension = MimeTypeMap.getFileExtensionFromUrl(file.getFilename()); } String uuid = UUID.randomUUID().toString(); return new File(App.getInstance().getRootDir(), uuid + "." + extension); } /** * SD is available. * * @return true, otherwise is false. */ public static boolean storageAvailable() { if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { File sd = new File(Environment.getExternalStorageDirectory().getAbsolutePath()); return sd.canWrite(); } else { return false; } } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/server/util/NetUtils.java ================================================ /* * Copyright © 2018 Yan Zhenjie. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package camera.cn.cameramaster.server.util; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.util.Enumeration; import java.util.regex.Pattern; /** * Created by YanZhenjie on 2018/6/9. */ public class NetUtils { /** * Ipv4 address check. */ private static final Pattern IPV4_PATTERN = Pattern.compile( "^(" + "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}" + "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"); /** * Check if valid IPV4 address. * * @param input the address string to check for validity. * * @return True if the input parameter is a valid IPv4 address. */ public static boolean isIPv4Address(String input) { return IPV4_PATTERN.matcher(input).matches(); } /** * Get local Ip address. */ public static InetAddress getLocalIPAddress() { Enumeration enumeration = null; try { enumeration = NetworkInterface.getNetworkInterfaces(); } catch (SocketException e) { e.printStackTrace(); } if (enumeration != null) { while (enumeration.hasMoreElements()) { NetworkInterface nif = enumeration.nextElement(); Enumeration inetAddresses = nif.getInetAddresses(); if (inetAddresses != null) { while (inetAddresses.hasMoreElements()) { InetAddress inetAddress = inetAddresses.nextElement(); if (!inetAddress.isLoopbackAddress() && isIPv4Address(inetAddress.getHostAddress())) { return inetAddress; } } } } } return null; } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/ui/Camera2Activity.java ================================================ package camera.cn.cameramaster.ui; import android.Manifest; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.ImageFormat; import android.graphics.SurfaceTexture; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraManager; import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.CaptureFailure; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.TotalCaptureResult; import android.hardware.camera2.params.StreamConfigurationMap; import android.media.Image; import android.media.ImageReader; import android.os.Build; import android.os.Environment; import android.support.annotation.RequiresApi; import android.support.v4.app.ActivityCompat; import android.util.Size; import android.util.SparseIntArray; import android.view.Surface; import android.view.TextureView; import android.view.View; import android.widget.ImageView; import android.widget.Toast; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; import butterknife.BindView; import butterknife.OnClick; import camera.cn.cameramaster.R; import camera.cn.cameramaster.base.BaseActivity; import camera.cn.cameramaster.util.AppConstant; import camera.cn.cameramaster.util.BitmapUtils; /** * 基于 camera2 自定义拍照界面 (5.0以上) * * @author ymc * @date 2019年1月28日 13:56:02 */ public class Camera2Activity extends BaseActivity { @BindView(R.id.textureView) TextureView textureView; @BindView(R.id.iv_back2) ImageView ivBack; @BindView(R.id.camera_flash2) ImageView ivFlash2; @BindView(R.id.camera_switch2) ImageView ivSwitch2; @BindView(R.id.img_camera2) ImageView ivCamera2; private CameraDevice mCameraDevice; /** * 摄像头id(0代表后置摄像头,1代表前置摄像头) */ private String mCameraId = "0"; private ImageReader imageReader; public static int height; public static int width; private Size previewSize; private CaptureRequest.Builder captureRequestBuilder; private CaptureRequest mCaptureRequest; private CameraCaptureSession mPreviewSession; /** * 判断是否有拍照权限的标识码 */ private final int RESULT_CODE_CAMERA = 1; private CaptureRequest.Builder mCaptureRequestBuilder; private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); static { ORIENTATIONS.append(Surface.ROTATION_0, 90); ORIENTATIONS.append(Surface.ROTATION_90, 0); ORIENTATIONS.append(Surface.ROTATION_180, 270); ORIENTATIONS.append(Surface.ROTATION_270, 180); } @Override protected int getLayoutId() { return R.layout.activity_camera2; } @Override protected void initView() { textureView.setSurfaceTextureListener(surfaceTextureListener); } @Override protected void initData() { } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @OnClick({R.id.iv_back2, R.id.img_camera2}) public void onClick(View view) { switch (view.getId()) { case R.id.iv_back2: finish(); break; case R.id.img_camera2: takePicture(); break; default: break; } } /** * 启动拍照 */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private void startCamera() { if (textureView.isAvailable()) { if (mCameraDevice == null) { openCamera(); } } else { textureView.setSurfaceTextureListener(surfaceTextureListener); } } /** * 拍照 */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private void takePicture() { try { if (mCameraDevice == null) { return; } // 创建拍照请求 captureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); // 设置自动对焦模式 captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); // 将imageReader的surface设为目标 captureRequestBuilder.addTarget(imageReader.getSurface()); // 获取设备方向 int rotation = getWindowManager().getDefaultDisplay().getRotation(); // 根据设备方向计算设置照片的方向 captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION , ORIENTATIONS.get(rotation)); // 停止连续取景 mPreviewSession.stopRepeating(); //拍照 CaptureRequest captureRequest = captureRequestBuilder.build(); //设置拍照监听 mPreviewSession.capture(captureRequest, captureCallback, null); } catch (CameraAccessException e) { e.printStackTrace(); } } /** * 监听拍照结果 */ private CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() { // 拍照成功 @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) { // 重设自动对焦模式 captureRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL); // 设置自动曝光模式 captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); try { //重新进行预览 mPreviewSession.setRepeatingRequest(mCaptureRequest, null, null); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request, CaptureFailure failure) { super.onCaptureFailed(session, request, failure); } }; /** * TextureView的监听 */ private TextureView.SurfaceTextureListener surfaceTextureListener = new TextureView.SurfaceTextureListener() { @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { Camera2Activity.width = width; Camera2Activity.height = height; openCamera(); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { stopCamera(); return true; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { } }; /** * 打开摄像头 */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private void openCamera() { CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); //设置摄像头特性 setCameraCharacteristics(manager); try { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { String[] perms = {"android.permission.CAMERA"}; ActivityCompat.requestPermissions(Camera2Activity.this, perms, RESULT_CODE_CAMERA); } else { manager.openCamera(mCameraId, stateCallback, null); } } catch (CameraAccessException e) { e.printStackTrace(); } } /** * 设置摄像头的参数 */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private void setCameraCharacteristics(CameraManager manager) { try { // 获取指定摄像头的特性 CameraCharacteristics characteristics = manager.getCameraCharacteristics(mCameraId); // 获取摄像头支持的配置属性 StreamConfigurationMap map = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); // 获取摄像头支持的最大尺寸 Size largest = Collections.max( Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)), new CompareSizesByArea()); // 创建一个ImageReader对象,用于获取摄像头的图像数据 imageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), ImageFormat.JPEG, 2); //设置获取图片的监听 imageReader.setOnImageAvailableListener(imageAvailableListener, null); // 获取最佳的预览尺寸 previewSize = chooseOptimalSize(map.getOutputSizes( SurfaceTexture.class), width, height, largest); } catch (CameraAccessException e) { e.printStackTrace(); } catch (NullPointerException e) { } } /** * 为Size定义一个比较器Comparator */ static class CompareSizesByArea implements Comparator { @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override public int compare(Size lhs, Size rhs) { return Long.signum((long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight()); } } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private static Size chooseOptimalSize(Size[] choices , int width, int height, Size aspectRatio) { // 收集摄像头支持的大过预览Surface的分辨率 List bigEnough = new ArrayList<>(); int w = aspectRatio.getWidth(); int h = aspectRatio.getHeight(); for (Size option : choices) { if (option.getHeight() == option.getWidth() * h / w && option.getWidth() >= width && option.getHeight() >= height) { bigEnough.add(option); } } if (bigEnough.size() > 0) { return Collections.min(bigEnough, new CompareSizesByArea()); } else { //没有合适的预览尺寸 return choices[0]; } } /** * 摄像头状态的监听 */ private CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() { // 摄像头被打开时触发该方法 @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override public void onOpened(CameraDevice cameraDevice) { mCameraDevice = cameraDevice; // 开始预览 takePreview(); } // 摄像头断开连接时触发该方法 @Override public void onDisconnected(CameraDevice cameraDevice) { stopCamera(); } // 打开摄像头出现错误时触发该方法 @Override public void onError(CameraDevice cameraDevice, int error) { stopCamera(); } }; /** * 开始预览 */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private void takePreview() { SurfaceTexture mSurfaceTexture = textureView.getSurfaceTexture(); //设置TextureView的缓冲区大小 mSurfaceTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight()); //获取Surface显示预览数据 Surface mSurface = new Surface(mSurfaceTexture); try { //创建预览请求 mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); // 设置自动对焦模式 mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); //设置Surface作为预览数据的显示界面 mCaptureRequestBuilder.addTarget(mSurface); //创建相机捕获会话,第一个参数是捕获数据的输出Surface列表,第二个参数是CameraCaptureSession的状态回调接口,当它创建好后会回调onConfigured方法,第三个参数用来确定Callback在哪个线程执行,为null的话就在当前线程执行 mCameraDevice.createCaptureSession(Arrays.asList(mSurface, imageReader.getSurface()), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(CameraCaptureSession session) { try { //开始预览 mCaptureRequest = mCaptureRequestBuilder.build(); mPreviewSession = session; //设置反复捕获数据的请求,这样预览界面就会一直有数据显示 mPreviewSession.setRepeatingRequest(mCaptureRequest, null, null); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed(CameraCaptureSession session) { } }, null); } catch (CameraAccessException e) { e.printStackTrace(); } } /** * 释放 */ private void stopCamera() { if (mCameraDevice != null) { mCameraDevice.close(); mCameraDevice = null; } } /** * 监听拍照的图片 */ private ImageReader.OnImageAvailableListener imageAvailableListener = new ImageReader.OnImageAvailableListener() { // 当照片数据可用时激发该方法 @Override public void onImageAvailable(ImageReader reader) { //先验证手机是否有sdcard String status = Environment.getExternalStorageState(); if (!status.equals(Environment.MEDIA_MOUNTED)) { Toast.makeText(getApplicationContext(), "你的sd卡不可用。", Toast.LENGTH_SHORT).show(); return; } Image image = reader.acquireNextImage(); ByteBuffer buffer = image.getPlanes()[0].getBuffer(); byte[] data = new byte[buffer.remaining()]; buffer.get(data); String filePath = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/" + System.currentTimeMillis() + ".jpg"; Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); BitmapUtils.saveJPGE_After(getApplicationContext(), bitmap, filePath, 100); if (!bitmap.isRecycled()) { bitmap.recycle(); } Intent intent = new Intent(); intent.putExtra(AppConstant.KEY.IMG_PATH, filePath); intent.putExtra(AppConstant.KEY.PIC_WIDTH, width); intent.putExtra(AppConstant.KEY.PIC_HEIGHT, height); setResult(AppConstant.RESULT_CODE.RESULT_OK, intent); finish(); } }; @Override protected void onPause() { super.onPause(); if (mCameraDevice != null) { stopCamera(); } } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override protected void onResume() { super.onResume(); startCamera(); } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/ui/CameraActivity.java ================================================ package camera.cn.cameramaster.ui; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.ImageFormat; import android.graphics.Rect; import android.graphics.YuvImage; import android.hardware.Camera; import android.os.Environment; import android.util.DisplayMetrics; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.widget.ImageView; import android.widget.Toast; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import butterknife.BindView; import butterknife.OnClick; import camera.cn.cameramaster.R; import camera.cn.cameramaster.base.BaseActivity; import camera.cn.cameramaster.util.AppConstant; import camera.cn.cameramaster.util.BitmapUtils; import camera.cn.cameramaster.util.CameraUtil; import camera.cn.cameramaster.view.ShowSurfaceView; /** * 拍照界面 * 5.0 版本以前的拍照 * * @author ymc */ public class CameraActivity extends BaseActivity implements SurfaceHolder.Callback { private static final String TAG = "CameraActivity"; @BindView(R.id.surfaceView2) ShowSurfaceView svShow; @BindView(R.id.surfaceView) SurfaceView svContent; @BindView(R.id.img_camera) ImageView ivCamera; @BindView(R.id.camera_flash) ImageView ivFlash; @BindView(R.id.camera_switch) ImageView ivSwitch; @BindView(R.id.iv_back) ImageView ivBack; private Camera mCamera; private SurfaceHolder mHolder; private CameraUtil cameraInstance; /** * 屏幕宽高 */ private int screenWidth; private int screenHeight; /** * 图片宽高 */ private int picWidth; /** * 是否有界面 */ private boolean isView = true; /** * 拍照id 1: 前摄像头 0:后摄像头 */ private int mCameraId = 0; /** * 闪光灯类型 0 :关闭 1: 打开 2:自动 */ private int light_type = 0; /** * 图片高度 */ private int picHeight; @Override protected int getLayoutId() { return R.layout.activity_camera; } @Override protected void initView() { mHolder = svContent.getHolder(); mHolder.addCallback(this); } @Override protected void initData() { cameraInstance = CameraUtil.getInstance(); DisplayMetrics dm = getResources().getDisplayMetrics(); screenWidth = dm.widthPixels; screenHeight = dm.heightPixels; } @Override protected void onResume() { super.onResume(); if (mCamera == null) { mCamera = getCamera(mCameraId); if (mHolder != null) { startPreview(mCamera, mHolder); } } } @OnClick({R.id.img_camera, R.id.camera_flash, R.id.camera_switch, R.id.iv_back}) public void OnClick(View view) { switch (view.getId()) { // 点击拍照 case R.id.img_camera: switch (light_type) { case 0: //关闭 cameraInstance.turnLightOff(mCamera); break; case 1: cameraInstance.turnLightOn(mCamera); break; case 2: //自动 cameraInstance.turnLightAuto(mCamera); break; default: break; } takePhoto(); break; // 切换闪光灯 case R.id.camera_flash: if (mCameraId == 1) { Toast.makeText(this, "请切换到后置摄像头", Toast.LENGTH_LONG).show(); return; } Camera.Parameters parameters = mCamera.getParameters(); switch (light_type) { case 0: //打开 light_type = 1; ivFlash.setImageResource(R.mipmap.icon_camera_on); parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);//开启 mCamera.setParameters(parameters); break; case 1: //自动 light_type = 2; parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO); mCamera.setParameters(parameters); ivFlash.setImageResource(R.mipmap.icon_camera_a); break; case 2: //关闭 light_type = 0; //关闭 parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); mCamera.setParameters(parameters); ivFlash.setImageResource(R.mipmap.icon_camera_off); break; default: break; } break; //切换前后摄像头 case R.id.camera_switch: switchCamera(); break; // 返回按钮 case R.id.iv_back: finish(); break; default: break; } } /** * 切换前后摄像头 */ public void switchCamera() { releaseCamera(); mCameraId = (mCameraId + 1) % Camera.getNumberOfCameras(); mCamera = getCamera(mCameraId); if (mHolder != null) { startPreview(mCamera, mHolder); } } /** * 拍照 */ private void takePhoto() { mCamera.takePicture(null, null, new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { isView = false; //将data 转换为位图 或者你也可以直接保存为文件使用 FileOutputStream //这里我相信大部分都有其他用处把 比如加个水印 后续再讲解 Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); Bitmap saveBitmap = cameraInstance.setTakePicktrueOrientation(mCameraId, bitmap); saveBitmap = Bitmap.createScaledBitmap(saveBitmap, screenWidth, screenHeight, true); String imgpath = getExternalFilesDir(Environment.DIRECTORY_DCIM).getPath() + File.separator + System.currentTimeMillis() + ".jpeg"; Log.e(TAG, "imgpath: --- " + imgpath); BitmapUtils.saveJPGE_After(getApplicationContext(), saveBitmap, imgpath, 100); if (!bitmap.isRecycled()) { bitmap.recycle(); } if (!saveBitmap.isRecycled()) { saveBitmap.recycle(); } Intent intent = new Intent(); intent.putExtra(AppConstant.KEY.IMG_PATH, imgpath); intent.putExtra(AppConstant.KEY.PIC_WIDTH, picWidth); intent.putExtra(AppConstant.KEY.PIC_HEIGHT, picHeight); setResult(AppConstant.RESULT_CODE.RESULT_OK, intent); finish(); } }); } @Override public void surfaceCreated(SurfaceHolder holder) { startPreview(mCamera, holder); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { mCamera.stopPreview(); startPreview(mCamera, holder); } @Override public void surfaceDestroyed(SurfaceHolder holder) { releaseCamera(); } /** * 释放相机资源 */ private void releaseCamera() { if (mCamera != null) { mCamera.setPreviewCallback(null); mCamera.stopPreview(); mCamera.release(); mCamera = null; } } /** * 预览相机 */ private void startPreview(Camera camera, SurfaceHolder holder) { try { setupCamera(camera); camera.setPreviewDisplay(holder); mCamera.setPreviewCallback(new Camera.PreviewCallback() { @Override public void onPreviewFrame(byte[] data, Camera camera) { Camera.Size size = camera.getParameters().getPreviewSize(); ByteArrayOutputStream stream = new ByteArrayOutputStream(); try{ // YUV转为RGB YuvImage image = new YuvImage(data, ImageFormat.NV21, size.width, size.height, null); image.compressToJpeg(new Rect(0, 0, size.width/2, size.height/2), 20, stream); Bitmap bmp = BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size()); svShow.setBitmap(BitmapUtils.rotateMyBitmap(BitmapUtils.ImgaeToNegative(bmp))); stream.close(); }catch(Exception ex){ Log.e("Sys","Error:"+ex.getMessage()); } } }); cameraInstance.setCameraDisplayOrientation(this, mCameraId, camera); camera.startPreview(); isView = true; } catch (IOException e) { e.printStackTrace(); } } /** * 设置surfaceView的尺寸 因为camera默认是横屏,所以取得支持尺寸也都是横屏的尺寸 * 我们在startPreview方法里面把它矫正了过来,但是这里我们设置设置surfaceView的尺寸的时候要注意 previewSize.height mLayoutList = new LinkedList<>(); /** * visible与invisible之间切换的动画 */ private TranslateAnimation mShowAction; /** * 设置 rgb 色域 * @param whiteBalance 0- 100 * @return RggbChannelVector */ public static RggbChannelVector colorTemperature(int whiteBalance) { float temperature = whiteBalance/100; float red; float green; float blue; //Calculate red if (temperature <= 66) red = 255; else { red = temperature - 60; red = (float) (329.698727446 * (Math.pow((double) red, -0.1332047592))); if (red < 0) red = 0; if (red > 255) red = 255; } //Calculate green if (temperature <= 66) { green = temperature; green = (float) (99.4708025861 * Math.log(green) - 161.1195681661); if (green < 0) green = 0; if (green > 255) green = 255; } else { green = temperature - 60; green = (float) (288.1221695283 * (Math.pow((double) green, -0.0755148492))); if (green < 0) green = 0; if (green > 255) green = 255; } //calculate blue if (temperature >= 66) blue = 255; else if (temperature <= 19) blue = 0; else { blue = temperature - 10; blue = (float) (138.5177312231 * Math.log(blue) - 305.0447927307); if (blue < 0) blue = 0; if (blue > 255) blue = 255; } Log.e(TAG, "red=" + red + ", green=" + green + ", blue=" + blue + ", paroess:"+whiteBalance); return new RggbChannelVector((red/255) * 2, (green/255), (green/255), (blue/255) * 2); } @Override protected int getLayoutId() { return R.layout.activity_camera_video; } @Override protected void initView() { // 将底部布局 依次添加到 列表中 mLayoutList.clear(); mLayoutList.add(mLayoutBottom); mLayoutList.add(layoutAe); mLayoutList.add(layoutAwb); mLayoutList.add(llEffect); mLayoutList.add(llSense); mLayoutList.add(llAWBSetting); // 初始化 切换动画 mShowAction = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, -1.0f, Animation.RELATIVE_TO_SELF, 0.0f); mShowAction.setDuration(100); mVideoPlayer = new VideoPlayer(); //设置时间戳回调 mVideoPlayer.setPlaySeekTimeListener(this); MODE = getIntent().getIntExtra("mode", AppConstant.CAMERA_MODE); if (MODE == AppConstant.CAMERA_MODE) { //摄像头模式 initCameraMode(); } else if (MODE == AppConstant.VIDEO_MODE) { //视频播放模式 mVideoPath = getIntent().getStringExtra("videoPath"); initVideoMode(); } mFoucesAnimation = new FoucesAnimation(); // 淡入动画 mAlphaInAnimation = new AlphaAnimation(0.0f, 1.0f); mAlphaInAnimation.setDuration(500); // 淡出动画 mAlphaOutAnimation = new AlphaAnimation(1.0f, 0.0f); mAlphaOutAnimation.setDuration(500); sbAwb.setmOnAwbSeekBarChangeListener(cameraHelper); LinearLayoutManager ms = new LinearLayoutManager(this); ms.setOrientation(LinearLayoutManager.HORIZONTAL); LinearLayoutManager ms1 = new LinearLayoutManager(this); ms1.setOrientation(LinearLayoutManager.HORIZONTAL); evSenseList.setLayoutManager(ms); evEffectList.setLayoutManager(ms1); sAdapter = new SenseAdapter(this, AppConstant.senseArr); effectAdapter = new EffectAdapter(this,AppConstant.effectArr); evSenseList.setAdapter(sAdapter); evEffectList.setAdapter(effectAdapter); // rv 点击事件 initListener(); } @Override protected void initData() { mCameraPath = cameraHelper.getPhotoFilePath(); mVideoPath = cameraHelper.getVideoFilePath(); sbAWB.setOnSeekBarChangeListener(new awbSettingSeekBarListener()); sbAe.setOnSeekBarChangeListener(new CameraSeekBarListener()); } /** * 初始化 录像 */ private void initVideoMode() { hindMenu(); hindSwitchCamera(); hindVideoRecordSeekBar(); mVideoPlayer.setPlayStateListener(this); videoRecord.setVisibility(View.GONE); videoHintText.setVisibility(View.GONE); videoTexture.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //单机屏幕显示出控件 if (videoMinePlay.getVisibility() == View.VISIBLE) { hindPlayView(); } else { showPlayView(); videoTexture.postDelayed(mHindViewRunnable, 3000); } } }); videoSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { private int progress; @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (fromUser) { this.progress = progress; } } @Override public void onStartTrackingTouch(SeekBar seekBar) { //触摸进度条取消几秒后隐藏的事件 videoTexture.removeCallbacks(mHindViewRunnable); } @Override public void onStopTrackingTouch(SeekBar seekBar) { mVideoPlayer.seekTo(progress); videoTexture.postDelayed(mHindViewRunnable, 3000); } }); } /** * 初始化 拍照 */ @RequiresApi(api = Build.VERSION_CODES.M) @SuppressLint("ClickableViewAccessibility") private void initCameraMode() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { isNoPremissionPause = true; } initCamera(mNowCameraType); cameraHelper = new CameraHelper(this); cameraHelper.setTakePhotoListener(this); cameraHelper.setCameraReady(this); cameraHelper.setShowTextView(tvSbTxt); mVideoPlayer.setLoopPlay(true); List menus = new ArrayList<>(); menus.add("拍照"); menus.add("录像"); menus.add("曝光"); menus.add("白平衡"); menus.add("效果"); menus.add("感觉"); menus.add("手动设置"); mMenuAdapter = new MenuAdapter(this, menus, videoMenu); LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this); linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); videoMenu.setLayoutManager(linearLayoutManager); videoMenu.setAdapter(mMenuAdapter); videoMenu.setOnSelectedPositionChangedListener(this); mCameraTouch = new CameraTouch(); videoMenu.setOnTouchListener(new HorizontalViewTouchListener()); registerSensor(); initScaleSeekbar(); } /** * 初始化摄像头 * * @param cameraType */ private void initCamera(ICamera2.CameraType cameraType) { if (cameraHelper == null) { return; } if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { return; } cameraHelper.setTextureView(videoTexture); cameraHelper.openCamera(cameraType); } /** * 初始化 scale seekBar */ private void initScaleSeekbar() { videoScale.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (fromUser) { float scale = (float) progress / (float) seekBar.getMax() * cameraHelper.getMaxZoom(); cameraHelper.cameraZoom(scale); mCameraTouch.setScale(scale); } } @Override public void onStartTrackingTouch(SeekBar seekBar) { removeSeekBarRunnable(); } @Override public void onStopTrackingTouch(SeekBar seekBar) { seekBarDelayedHind(); } }); } /** * 点击事件 * * @param view view */ @RequiresApi(api = Build.VERSION_CODES.M) @OnClick({R.id.video_switch_camera, R.id.video_switch_flash}) public void cameraOnClickListener(View view) { switch (view.getId()) { // 切换摄像头状态 case R.id.video_switch_camera: if (mNowCameraType == ICamera2.CameraType.FRONT) { cameraHelper.switchCamera(ICamera2.CameraType.BACK); mNowCameraType = ICamera2.CameraType.BACK; } else { cameraHelper.switchCamera(ICamera2.CameraType.FRONT); mNowCameraType = ICamera2.CameraType.FRONT; } mCameraTouch.resetScale(); break; case R.id.video_switch_flash: Object o = videoSwitchFlash.getTag(); if (o == null || ((int) o) == 0) { videoSwitchFlash.setBackgroundResource(R.mipmap.flash_auto); videoSwitchFlash.setTag(1); cameraHelper.flashSwitchState(ICamera2.FlashState.AUTO); } else if (((int) o) == 1) { videoSwitchFlash.setBackgroundResource(R.mipmap.flash_open); videoSwitchFlash.setTag(2); cameraHelper.flashSwitchState(ICamera2.FlashState.OPEN); } else { videoSwitchFlash.setBackgroundResource(R.mipmap.flash_close); videoSwitchFlash.setTag(0); cameraHelper.flashSwitchState(ICamera2.FlashState.CLOSE); } break; default: break; } } /** * 传感器继承方法 重力发生改变 * 根据重力方向 动态旋转拍照图片角度(暂时关闭该方法) * * 使用以下方法 * int rotation = getWindowManager().getDefaultDisplay().getRotation(); * @param event event */ @Override public void onSensorChanged(SensorEvent event) { if (event.sensor.getType() == Sensor.TYPE_ORIENTATION) { float x = event.values[0]; float y = event.values[1]; float z = event.values[2]; // Log.e(TAG, "onSensorChanged: x: " + x +" y: "+y +" z : "+z); if (z > 55.0f) { //向左横屏 } else if (z < -55.0f) { //向右横屏 } else if (y > 60.0f) { //是倒竖屏 } else { //正竖屏 } } if (event.sensor.getType() == Sensor.TYPE_LIGHT) { float light = event.values[0]; cameraHelper.setLight(light); } } /** * 注册陀螺仪传感器 */ private void registerSensor() { SensorManager mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); Sensor mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION); Sensor mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); if (mSensor == null) { return; } mSensorManager.registerListener(this, mSensor, Sensor.TYPE_ORIENTATION); mSensorManager.registerListener(this, mLightSensor, Sensor.TYPE_LIGHT); } /** * 当已注册传感器的精度发生变化时调用 * * @param sensor sensor * @param accuracy 传感器的新精度 */ @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } @Override public void onSeekTime(int allTime, final int time) { if (videoSeekBar ==null || videoSeekBar.getVisibility() != View.VISIBLE){ return; } if (videoSeekBar.getMax() != allTime){ videoSeekBar.setMax(allTime); } videoSeekBar.setProgress(time); videoSeekTime.post(new Runnable() { @Override public void run() { float t = (float) time / 1000.0f; videoSeekTime.setText(cameraHelper.secToTime(Math.round(t))); } }); } @Override public void onStartListener(int width, int height) { videoTexture.setVideoAspectRatio(width, height); videoMinePlay.setImageResource(R.mipmap.ic_pause); videoPlay.setImageResource(R.mipmap.ic_pause); } @Override public void onCompletionListener() { hasPlaying = false; videoMinePlay.setImageResource(R.mipmap.ic_play); videoPlay.setImageResource(R.mipmap.ic_play); videoPlay.setVisibility(View.VISIBLE); } /** * 拍照完成回调 * * @param file 文件 * @param photoRotation 角度 * @param width 宽度 * @param height 高度 */ @Override public void onTakePhotoFinish(final File file, int photoRotation, int width, int height) { runOnUiThread(new Runnable() { @Override public void run() { hindSwitchCamera(); hindMenu(); showRecordEndView(); videoSwitchFlash.setVisibility(View.GONE); videoRecord.setVisibility(View.GONE); videoHintText.setVisibility(View.GONE); TEXTURE_STATE = AppConstant.TEXTURE_PHOTO_STATE; videoTexture.setVisibility(View.GONE); videoPhoto.setImageURI(cameraHelper.getUriFromFile(CameraVideoActivity.this, file)); videoPhoto.setVisibility(View.VISIBLE); } }); } /** * 相机准备完毕 */ @Override public void onCameraReady() { videoRecord.setClickable(true); } /** * 横向菜单列表 修改点击事件 * * @param pos */ @Override public void selectedPositionChanged(int pos) { Log.e(TAG, "selectedPositionChanged: "+pos); switch (pos){ case 0:{ showLayout(0, false); NOW_MODE = AppConstant.VIDEO_TAKE_PHOTO; cameraHelper.setCameraState(ICamera2.CameraMode.TAKE_PHOTO); videoHintText.setText("点击拍照"); break; } case 1:{ showLayout(0, false); NOW_MODE = AppConstant.VIDEO_RECORD_MODE; cameraHelper.setCameraState(ICamera2.CameraMode.RECORD_VIDEO); videoHintText.setText("点击录像"); break; } // 调整 曝光 case 2:{ showLayout(SHOW_AE, true); break; } // 调整 白平衡 case 3:{ showLayout(SHOW_AWB, true); break; } // 调整 效果 case 4:{ showLayout(3, true); break; } // 调整 感觉 case 5:{ showLayout(4, true); break; } // 调整 感觉 case 6:{ showLayout(5, true); break; } } } /** * 录像时长倒计时 */ @SuppressLint("SetTextI18n") private void recordCountDown() { videoTime.setVisibility(View.VISIBLE); videoRecordSeekBar.setVisibility(View.VISIBLE); final int count = 10; mDisposable = Observable.interval(1, 1, TimeUnit.SECONDS) .take(count + 1) .map(new Function() { @Override public Long apply(Long aLong) { return count - aLong; } }).observeOn(AndroidSchedulers.mainThread()) .subscribe(new Consumer() { @Override public void accept(Long aLong) { long time = 11 - aLong; if (time < 10) { videoTime.setText("0:0" + String.valueOf(time)); } else { videoTime.setText("0:" + String.valueOf(time)); } videoRecordSeekBar.setProgress((int) time); if (time == AppConstant.VIDEO_MAX_TIME) { videoTime.postDelayed(new Runnable() { @Override public void run() { recordVideoOrTakePhoto(); hindVideoRecordSeekBar(); } }, 300); } } }); } /** * 拍照或者录像 */ @OnClick(R.id.video_record) public void recordVideoOrTakePhoto() { if (hasRecordClick) { return; } hasRecordClick = true; if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission (this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { Log.e(TAG, "cameraOnClickListener: 动态权限获取失败..."); return; } //拍照 if (NOW_MODE == AppConstant.VIDEO_TAKE_PHOTO && mCameraPath!=null) { int rotation = getWindowManager().getDefaultDisplay().getRotation(); cameraHelper.setDeviceRotation(rotation); cameraHelper.takePhone(mCameraPath, ICamera2.MediaType.JPEG); } //录制视频 if (NOW_MODE == AppConstant.VIDEO_RECORD_MODE) { if (!hasRecording) { // 暂停录像 if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission (this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { return; } mVideoPath = cameraHelper.getVideoFilePath(); hasRecording = cameraHelper.startVideoRecord(mVideoPath, MediaRecorder.OutputFormat.MPEG_4); if (hasRecording) { videoRecord.setImageResource(R.mipmap.ic_recording); hindSwitchCamera(); recordCountDown(); hindMenu(); // mVideoHintText.setVisibility(View.GONE); videoHintText.setText("点击停止"); videoSwitchFlash.setVisibility(View.GONE); TEXTURE_STATE = AppConstant.TEXTURE_RECORD_STATE; } } else { // 开始录像 if (mDisposable != null && !mDisposable.isDisposed()){ mDisposable.dispose(); } mDisposable = null; videoSeekTime.setVisibility(View.GONE); hasRecording = false; cameraHelper.stopVideoRecord(); videoRecord.setImageResource(R.drawable.ic_camera); videoRecord.setVisibility(View.GONE); videoHintText.setVisibility(View.GONE); showRecordEndView(); hindVideoRecordSeekBar(); playVideo(); } } hasRecordClick = false; } /** * 返回 取消拍照或者 录像 */ @OnClick(R.id.video_delete) public void deleteVideoOrPicture() { if (TEXTURE_STATE == AppConstant.TEXTURE_PLAY_STATE) { mVideoPlayer.stop(); cameraHelper.startBackgroundThread(); cameraHelper.openCamera(mNowCameraType); mCameraTouch.resetScale(); //重新打开摄像头重置一下放大倍数 File file = new File(mVideoPath); if (file.exists()){ file.delete(); } videoHintText.setText("点击录像"); } else if (TEXTURE_STATE == AppConstant.TEXTURE_PHOTO_STATE) { File file = new File(mCameraPath); if (file.exists()){ file.delete(); } cameraHelper.resumePreview(); videoTexture.setVisibility(View.VISIBLE); videoPhoto.setVisibility(View.GONE); videoHintText.setText("点击拍照"); } initData(); TEXTURE_STATE = AppConstant.TEXTURE_PREVIEW_STATE; hindRecordEndView(); videoSwitchCamera.setVisibility(View.VISIBLE); videoMenu.setVisibility(View.VISIBLE); videoRecord.setVisibility(View.VISIBLE); videoTime.setVisibility(View.GONE); videoTime.setText("0:00"); videoHintText.setVisibility(View.VISIBLE); videoSwitchFlash.setVisibility(View.VISIBLE); } /** * 发送视频或者图片 */ @OnClick(R.id.video_save) public void saveVideoOrPhoto() { final Intent data; data = new Intent(); if (NOW_MODE == AppConstant.VIDEO_TAKE_PHOTO){ data.putExtra("path", mCameraPath); data.putExtra("mediaType", "image"); saveMedia(new File(mCameraPath)); } else if (NOW_MODE == AppConstant.VIDEO_RECORD_MODE) { data.putExtra("path", mVideoPath); data.putExtra("mediaType", "video"); saveMedia(new File(mVideoPath)); } setResult(RESULT_OK, data); finish(); } /** * 移除对焦 消失任务 */ private void removeImageFoucesRunnable() { videoFouces.removeCallbacks(mImageFoucesRunnable); } /** * 添加 延时消失任务 */ private void imageFoucesDelayedHind() { videoFouces.postDelayed(mImageFoucesRunnable, 500); } /** * seekBar 添加延时消失任务 */ private void seekBarDelayedHind() { if (isCanHind) { videoScaleBarLayout.postDelayed(SeekBarLayoutRunnalbe, 2000); } isCanHind = false; } /** * 移除隐藏 seekBar消失的任务 */ private void removeSeekBarRunnable() { isCanHind = true; videoScale.removeCallbacks(SeekBarLayoutRunnalbe); } /** * 显示播放视频 的 确认和删除按钮 */ private void showRecordEndView() { videoSave.setVisibility(View.VISIBLE); videoDelete.setVisibility(View.VISIBLE); } /** * 隐藏视频录像的进度条 */ private void hindVideoRecordSeekBar() { videoRecordSeekBar.setVisibility(View.GONE); videoRecordSeekBar.setProgress(0); } /** * 关闭摄像头 */ private void closeCamera() { videoRecord.setClickable(false); cameraHelper.closeCamera(); cameraHelper.stopBackgroundThread(); } /** * 播放视频 */ private void playVideo() { closeCamera(); if (mVideoPath != null && mVideoPlayer != null) { mVideoPlayer.setDataSourceAndPlay(mVideoPath); hasPlaying = true; //视频播放状态 TEXTURE_STATE = AppConstant.TEXTURE_PLAY_STATE; } } /** * 移动焦点图标 * * @param x x坐标 * @param y y坐标 */ private void moveFouces(int x, int y) { videoFouces.setVisibility(View.VISIBLE); RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) videoFouces.getLayoutParams(); videoFouces.setLayoutParams(layoutParams); mFoucesAnimation.setDuration(500); mFoucesAnimation.setRepeatCount(0); mFoucesAnimation.setOldMargin(x, y); videoFouces.startAnimation(mFoucesAnimation); cameraHelper.requestFocus(x, y); } @Override protected void onResume() { super.onResume(); if (cameraHelper != null) { cameraHelper.startBackgroundThread(); } if (videoTexture.isAvailable()) { if (MODE == AppConstant.CAMERA_MODE) { if (TEXTURE_STATE == AppConstant.TEXTURE_PREVIEW_STATE) { //预览状态 initCamera(mNowCameraType); } else if (TEXTURE_STATE == AppConstant.TEXTURE_PLAY_STATE) { //视频播放状态 mVideoPlayer.play(); } mVideoPlayer.setVideoPlayWindow(new Surface(videoTexture.getSurfaceTexture())); } } else { videoTexture.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { if (MODE == AppConstant.CAMERA_MODE) { if (TEXTURE_STATE == AppConstant.TEXTURE_PREVIEW_STATE) { //预览状态 initCamera(mNowCameraType); } else if (TEXTURE_STATE == AppConstant.TEXTURE_PLAY_STATE) { //视频播放状态 mVideoPlayer.play(); } mVideoPlayer.setVideoPlayWindow(new Surface(videoTexture.getSurfaceTexture())); } else if (MODE == AppConstant.VIDEO_MODE) { mVideoPlayer.setVideoPlayWindow(new Surface(videoTexture.getSurfaceTexture())); Log.e("videoPath", "path:" + mVideoPath); mVideoPlayer.setDataSourceAndPlay(mVideoPath); hasPlaying = true; //视频播放状态 TEXTURE_STATE = AppConstant.TEXTURE_PLAY_STATE; } } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { return true; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { } }); } } @Override protected void onPause() { super.onPause(); if (isNoPremissionPause) { isNoPremissionPause = false; return; } Log.e("camera", "mode:" + MODE); if (MODE == AppConstant.CAMERA_MODE) { if (TEXTURE_STATE == AppConstant.TEXTURE_PREVIEW_STATE) { cameraHelper.closeCamera(); cameraHelper.stopBackgroundThread(); } else if (TEXTURE_STATE == AppConstant.TEXTURE_PLAY_STATE) { mVideoPlayer.pause(); } } } /** * 隐藏切换摄像头按钮 */ private void hindSwitchCamera() { videoSwitchCamera.setVisibility(View.GONE); } /** * 隐藏切换菜单 */ private void hindMenu() { videoMenu.setVisibility(View.GONE); } /** * 刷新相册 * * @param mediaFile 文件 */ private void saveMedia(File mediaFile) { Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); Uri uri = Uri.fromFile(mediaFile); intent.setData(uri); sendBroadcast(intent); } /** * 隐藏录像完成后底部两个按钮 */ private void hindRecordEndView() { videoSave.setVisibility(View.GONE); videoDelete.setVisibility(View.GONE); } /** * 隐藏播放界面的控件出来 */ private void hindPlayView() { videoSeekBar.setVisibility(View.GONE); videoMinePlay.setVisibility(View.GONE); videoPlay.setVisibility(View.GONE); videoSeekTime.setVisibility(View.GONE); } /** * 显示播放界面的控件出来 */ private void showPlayView() { videoSeekBar.setVisibility(View.VISIBLE); videoMinePlay.setVisibility(View.VISIBLE); videoPlay.setVisibility(View.VISIBLE); videoSeekTime.setVisibility(View.VISIBLE); } /** * 显示和隐藏控件 * * @param showWhat * @param showOrNot */ private void showLayout(int showWhat, boolean showOrNot) { View v = mLayoutList.get(showWhat); if (showOrNot) { //全部隐藏但是AF/AE的显示出来 for (int i = 0; i < mLayoutBottom.getChildCount(); i++) { if (mLayoutBottom.getChildAt(i).getVisibility() == View.VISIBLE) { mLayoutBottom.getChildAt(i).setVisibility(View.GONE); } } v.startAnimation(mShowAction); v.setVisibility(View.VISIBLE); } else { //全部隐藏但是capture的显示出来 for (int i = 0; i < mLayoutBottom.getChildCount(); i++) { if (mLayoutBottom.getChildAt(i).getVisibility() == View.VISIBLE) { mLayoutBottom.getChildAt(i).setVisibility(View.GONE); } } rlCamera.startAnimation(mShowAction); rlCamera.setVisibility(View.VISIBLE); } } /** * rv点击事件 初始化 */ private void initListener() { sAdapter.setSenseOnItemClickListener(new SenseAdapter.SenseOnItemClickListener() { @Override public void itemOnClick(int position) { cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_USE_SCENE_MODE); switch (position) { case 0: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_DISABLED); break; case 1: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_FACE_PRIORITY); break; case 2: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_ACTION); break; case 3: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_PORTRAIT); break; case 4: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_LANDSCAPE); break; case 5: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_NIGHT); break; case 6: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_NIGHT_PORTRAIT); break; case 7: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_THEATRE); break; case 8: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_BEACH); break; case 9: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_SNOW); break; case 10: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_SUNSET); break; case 11: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_STEADYPHOTO); break; case 12: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_FIREWORKS); break; case 13: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_SPORTS); break; case 14: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_PARTY); break; case 15: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_CANDLELIGHT); break; case 16: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_BARCODE); break; default: break; } } }); effectAdapter.setEffectOnItemClickListener(new EffectAdapter.EffectOnItemClickListener() { @Override public void itemOnClick(int position) { switch (position) { case 0: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_EFFECT_MODE, CameraMetadata.CONTROL_EFFECT_MODE_AQUA); break; case 1: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_EFFECT_MODE, CameraMetadata.CONTROL_EFFECT_MODE_BLACKBOARD); break; case 2: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_EFFECT_MODE, CameraMetadata.CONTROL_EFFECT_MODE_MONO); break; case 3: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_EFFECT_MODE, CameraMetadata.CONTROL_EFFECT_MODE_NEGATIVE); break; case 4: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_EFFECT_MODE, CameraMetadata.CONTROL_EFFECT_MODE_POSTERIZE); break; case 5: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_EFFECT_MODE, CameraMetadata.CONTROL_EFFECT_MODE_SEPIA); break; case 6: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_EFFECT_MODE, CameraMetadata.CONTROL_EFFECT_MODE_SOLARIZE); break; case 7: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_EFFECT_MODE, CameraMetadata.CONTROL_EFFECT_MODE_WHITEBOARD); break; case 8: cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_EFFECT_MODE, CameraMetadata.CONTROL_EFFECT_MODE_OFF); break; default: break; } } }); } /** * 横向列表 touch事件 (拍照预览 缩放) */ private class HorizontalViewTouchListener implements View.OnTouchListener { private long mClickOn; private float mLastX; private float mLastY; @Override public boolean onTouch(View view, MotionEvent motionEvent) { switch (motionEvent.getActionMasked()) { case MotionEvent.ACTION_DOWN: if (motionEvent.getPointerCount() == 1) { mClickOn = System.currentTimeMillis(); mLastX = motionEvent.getX(); mLastY = motionEvent.getY(); } break; // 用户两指按下事件 case MotionEvent.ACTION_POINTER_DOWN: mCameraTouch.onScaleStart(motionEvent); return true; case MotionEvent.ACTION_MOVE: if (motionEvent.getPointerCount() == 2) { mCameraTouch.onScale(motionEvent); return true; } else { float x = motionEvent.getX() - mLastX; float y = motionEvent.getY() - mLastY; if (Math.abs(x) >= 10 || Math.abs(y) >= 10) { mClickOn = 0; } } break; case MotionEvent.ACTION_UP: if (motionEvent.getPointerCount() == 1) { if ((System.currentTimeMillis() - mClickOn) < 500) { moveFouces((int) motionEvent.getX(), (int) motionEvent.getY()); } } break; case MotionEvent.ACTION_POINTER_UP: mCameraTouch.onScaleEnd(motionEvent); return true; default: break; } return false; } } /** * TextureView 触摸方法 */ private class CameraTouch { private float mOldScale = 1.0f; private float mScale; private float mSpan = 0; private float mOldSpan; private float mFirstDistance = 0; public void onScale(MotionEvent event) { if (event.getPointerCount() == 2) { if (mFirstDistance == 0) { mFirstDistance = distance(event); } float distance = distance(event); float scale; if (distance > mFirstDistance) { scale = (distance - mFirstDistance) / 80; scale = scale + mSpan; mOldSpan = scale; mScale = scale; } else if (distance < mFirstDistance) { scale = distance / mFirstDistance; mOldSpan = scale; mScale = scale * mOldScale; } else { return; } cameraHelper.cameraZoom(mScale); videoScale.setProgress((int) ((mScale / cameraHelper.getMaxZoom()) * videoScale.getMax())); if (mScale < 1.0f) { videoScale.setProgress(0); } } } /** * scale 开始 * * @param event */ public void onScaleStart(MotionEvent event) { mFirstDistance = 0; setScaleMax((int) cameraHelper.getMaxZoom()); videoScaleBarLayout.setVisibility(View.VISIBLE); removeSeekBarRunnable(); } /** * scale 结束 * * @param event MotionEvent */ private void onScaleEnd(MotionEvent event) { if (mScale < 1.0f) { mOldScale = 1.0f; } else if (mScale > cameraHelper.getMaxZoom()) { mOldScale = cameraHelper.getMaxZoom(); } else { mOldScale = mScale; } mSpan = mOldSpan; if (event != null) { seekBarDelayedHind(); } } /** * 重置 缩放 */ public void resetScale() { mOldScale = 1.0f; mSpan = 0f; mFirstDistance = 0f; videoScale.setProgress(0); } public void setScale(float scale) { mScale = scale; mOldSpan = scale; onScaleEnd(null); } /** * 计算两个手指间的距离 * * @param event MotionEvent * @return 距离 */ private float distance(MotionEvent event) { float dx = event.getX(1) - event.getX(0); float dy = event.getY(1) - event.getY(0); // 使用勾股定理返回两点之间的距离 return (float) Math.sqrt(dx * dx + dy * dy); } private void setScaleMax(int max) { videoScale.setMax(max * 100); } } /** * camera 点击对焦动画 */ private class FoucesAnimation extends Animation { private int width = cameraHelper.dip2px(CameraVideoActivity.this, 150); private int W = cameraHelper.dip2px(CameraVideoActivity.this, 65); private int oldMarginLeft; private int oldMarginTop; @Override protected void applyTransformation(float interpolatedTime, Transformation t) { RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) videoFouces.getLayoutParams(); int w = (int) (width * (1 - interpolatedTime)); if (w < W) { w = W; } layoutParams.width = w; layoutParams.height = w; if (w == W) { videoFouces.setLayoutParams(layoutParams); return; } layoutParams.leftMargin = oldMarginLeft - (w / 2); layoutParams.topMargin = oldMarginTop + (w / 8); videoFouces.setLayoutParams(layoutParams); } public void setOldMargin(int oldMarginLeft, int oldMarginTop) { this.oldMarginLeft = oldMarginLeft; this.oldMarginTop = oldMarginTop; removeImageFoucesRunnable(); imageFoucesDelayedHind(); } } /** * 曝光 ae 滑动监听事件 */ private class CameraSeekBarListener implements SeekBar.OnSeekBarChangeListener { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { switch (seekBar.getId()){ case R.id.sb_ae: { if (switchAe.isChecked()) { // 曝光增益 if (cameraHelper.getRange1() == null) { break; } Log.e(TAG, "曝光增益范围:" + cameraHelper.getRange1().toString()); int maxmax = cameraHelper.getRange1().getUpper(); int minmin = cameraHelper.getRange1().getLower(); int all = maxmax - minmin; int time = 100 / all; int ae = ((progress / time) - maxmax) > maxmax ? maxmax : ((progress / time) - maxmax) < minmin ? minmin : ((progress / time) - maxmax); cameraHelper.setAERegions(ae); tvSbTxt.setText("曝光增益:" + ae); } else { // 曝光时间 if (cameraHelper.getEtr() == null) { tvSbTxt.setText("获取曝光时间失败"); break; } Log.e(TAG, "曝光时间范围:" + cameraHelper.getEtr().toString()); long max = cameraHelper.getEtr().getUpper(); long min = cameraHelper.getEtr().getLower(); long ae = ((progress * (max - min)) / 100 + min); cameraHelper.setAeTime(ae); tvSbTxt.setText("曝光时间:" + ae); } break; } } } @Override public void onStartTrackingTouch(SeekBar seekBar) { tvSbTxt.setVisibility(View.VISIBLE); tvSbTxt.startAnimation(mAlphaInAnimation); } @Override public void onStopTrackingTouch(SeekBar seekBar) { tvSbTxt.startAnimation(mAlphaOutAnimation); tvSbTxt.setVisibility(View.INVISIBLE); } } /** * 手动白平衡 滑动监听事件 */ private class awbSettingSeekBarListener implements SeekBar.OnSeekBarChangeListener { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { cameraHelper.setCameraBuilerMode(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest.CONTROL_AWB_MODE_OFF); cameraHelper.setCameraBuilerMode(CaptureRequest.COLOR_CORRECTION_MODE, CaptureRequest.COLOR_CORRECTION_MODE_TRANSFORM_MATRIX); // 小米华为 没有的权限 cameraHelper.setCameraBuilerMode(CaptureRequest.COLOR_CORRECTION_GAINS, colorTemperature(progress+2000)); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/ui/GoogleCameraActivity.java ================================================ package camera.cn.cameramaster.ui; import android.Manifest; import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.ImageFormat; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.SurfaceTexture; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraManager; import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.TotalCaptureResult; import android.hardware.camera2.params.StreamConfigurationMap; import android.media.Image; import android.media.ImageReader; import android.media.MediaRecorder; import android.net.Uri; import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.annotation.RequiresApi; import android.support.v4.content.ContextCompat; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; import android.util.Log; import android.util.Range; import android.util.Size; import android.util.SparseIntArray; import android.view.Surface; import android.view.TextureView; import android.view.View; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.ScaleAnimation; import android.view.animation.TranslateAnimation; import android.widget.CompoundButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.SeekBar; import android.widget.Switch; import android.widget.TextView; import android.widget.Toast; import com.yanzhenjie.loading.dialog.LoadingDialog; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import butterknife.BindView; import butterknife.OnCheckedChanged; import butterknife.OnClick; import camera.cn.cameramaster.R; import camera.cn.cameramaster.adapter.EffectAdapter; import camera.cn.cameramaster.adapter.SenseAdapter; import camera.cn.cameramaster.base.BaseActivity; import camera.cn.cameramaster.server.AnyEventType; import camera.cn.cameramaster.view.AwbSeekBarChangeListener; import camera.cn.cameramaster.server.ServerManager; import camera.cn.cameramaster.util.AppConstant; import camera.cn.cameramaster.util.CompareSizesByArea; import camera.cn.cameramaster.util.Utils; import camera.cn.cameramaster.view.AutoTextureView; import camera.cn.cameramaster.view.ShowSurfaceView; import camera.cn.cameramaster.view.AnimationTextView; import camera.cn.cameramaster.view.AwbSeekBar; import static android.provider.MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO; import static camera.cn.cameramaster.util.AppConstant.SHOW_AE; import static camera.cn.cameramaster.util.AppConstant.SHOW_AWB; import static camera.cn.cameramaster.util.AppConstant.SHOW_EFFECT; import static camera.cn.cameramaster.util.AppConstant.SHOW_ISO; import static camera.cn.cameramaster.util.AppConstant.SHOW_SENSE; import static camera.cn.cameramaster.util.AppConstant.SHOW_ZOOM; /** * google 拍照 demo * * @author ymc * @date 2019年1月28日 17:32:45 * @url https://github.com/googlesamples/android-Camera2Basic */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public class GoogleCameraActivity extends BaseActivity { private static final String TAG = "GoogleCameraActivity"; /** * 这里为了测试 相机将画布设置为 1像素,如果 想要看原版效果, * 则隐藏 ShowSurfaceView ,将AutoFitTextureView设置充满 */ @BindView(R.id.textureView_g) AutoTextureView mTextureView; @BindView(R.id.surfaceView2) ShowSurfaceView svShow; /** * 闪光灯 */ @BindView(R.id.iv_flash) ImageView ivFlash; /** * 曝光 */ @BindView(R.id.iv_ae) ImageView ivAW; /** * 白平衡 */ @BindView(R.id.iv_awb) ImageView ivAWB; /** * 光感度 */ @BindView(R.id.iv_iso) ImageView ivISO; /** * 放大倍数 */ @BindView(R.id.iv_zoom) ImageView ivZoom; /** * 影响 */ @BindView(R.id.iv_effect) ImageView ivEffect; /** * 感应 */ @BindView(R.id.iv_sense) ImageView ivSense; /** * 底部切换布局 */ @BindView(R.id.layout_bottom) RelativeLayout mLayoutBottom; /** * 拍照布局 */ @BindView(R.id.homecamera_bottom_relative2) RelativeLayout mLayoutCapture; @BindView(R.id.img_camera_g) ImageView ivCamereVideo; @BindView(R.id.switch_ae) Switch switchAe; @BindView(R.id.sb_ae) SeekBar sbAe; @BindView(R.id.layout_ae) LinearLayout layoutAe; @BindView(R.id.sb_zoom) SeekBar sbZoom; @BindView(R.id.layout_zoom) LinearLayout layoutZoom; @BindView(R.id.sb_awb) AwbSeekBar sbAwb; @BindView(R.id.layout_awb) LinearLayout layoutAwb; @BindView(R.id.switch_iso) Switch switchIso; @BindView(R.id.sb_iso) SeekBar sbIso; @BindView(R.id.layout_iso) LinearLayout layoutIso; @BindView(R.id.txt_window_txt) AnimationTextView txtWindowTxt; @BindView(R.id.txt_sb_txt) TextView tvSbTxt; @BindView(R.id.layout_effect) LinearLayout llEffect; @BindView(R.id.rv_effect_list) RecyclerView evEffectList; @BindView(R.id.layout_sense) LinearLayout llSense; @BindView(R.id.rv_sense_list) RecyclerView evSenseList; /** * visible与invisible之间切换的动画 */ private TranslateAnimation mShowAction; /** * Conversion from screen rotation to JPEG orientation. */ private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); private static final int REQUEST_CAMERA_PERMISSION = 1; /** * 相机预览状态 */ private static final int STATE_PREVIEW = 0; /** * 等待相机锁定 */ private static final int STATE_WAITING_LOCK = 1; /** * 相机状态:等待曝光处于预捕获状态。 */ private static final int STATE_WAITING_PRECAPTURE = 2; /** * 相机状态:等待曝光状态不是预捕获。 */ private static final int STATE_WAITING_NON_PRECAPTURE = 3; /** * 相机状态:已拍摄照片。 */ private static final int STATE_PICTURE_TAKEN = 4; /** * Camera2 API保证的最大预览宽度 */ private static final int MAX_PREVIEW_WIDTH = 1920; /** * Camera2 API保证的最大预览高度 */ private static final int MAX_PREVIEW_HEIGHT = 1280; /** * 当前拍照id */ private String mCameraId; /** * 相机预览 */ private CameraCaptureSession mCaptureSession; private CameraDevice mCameraDevice; /** * 预览 */ private Size mPreviewSize; /** * 相机预览 */ private CaptureRequest.Builder mPreviewRequestBuilder; private CaptureRequest mPreviewRequest; /** * 视频对象 */ private MediaRecorder mMediaRecorder; /** * 当前相机状态. */ private int mState = STATE_PREVIEW; /** * 在关闭相机之前阻止应用程序退出 */ private Semaphore mCameraOpenCloseLock = new Semaphore(1); /** * 当前相机设备是否支持Flash */ private boolean mFlashSupported; /** * 相机传感器的方向 */ private int mSensorOrientation; private HandlerThread mBackgroundThread; private Handler mBackgroundHandler; private ImageReader mImageReader; private File mFile; private File mVideoPath; /** * true : 正在录制 / false :反之 */ private boolean hasVideoOn = false; /** * 闪光灯类型 0 :关闭 1: 打开 2:自动 */ private int flishType = 0; /** * 是否显示底部 布局的按钮 */ private boolean showAeFlag = false; /** * 底部 布局集合 */ private List mLayoutList = new LinkedList<>(); /** * 文字动画 */ private ScaleAnimation mScaleWindowAnimation; /** * 淡入动画 */ private AlphaAnimation mAlphaInAnimation; /** * 淡出动画 */ private AlphaAnimation mAlphaOutAnimation; static { ORIENTATIONS.append(Surface.ROTATION_0, 90); ORIENTATIONS.append(Surface.ROTATION_90, 0); ORIENTATIONS.append(Surface.ROTATION_180, 270); ORIENTATIONS.append(Surface.ROTATION_270, 180); } /** * 加载动画 */ private LoadingDialog mDialog; /** * url */ private String mRootUrl; /** * 服务管理器 */ private ServerManager mServerManager; /** * 是否显示Awb的按钮 */ private boolean showAwbFlag = false; /** * 是否显示 iso 的按钮 */ private boolean showIsoFlag = false; /** * 是否显示 effect 的按钮 */ private boolean showEffectFlag = false; /** * 是否显示 iso 的按钮 */ private boolean showSenseFlag = false; /** * 相机配置 */ private CameraCharacteristics characteristics; /** * 相机 曝光 范围 */ private Range range1; /** * 曝光时间 */ private Range etr; /** * 相机 iso 范围 */ private Range isoRange; /** * zoom 是否显示标识 */ private boolean showZoomFlag = false; /** * 相机 管理类 */ private CameraManager manager; private SenseAdapter sAdapter; private EffectAdapter effectAdapter; /** * 图片保存名称 */ private String fileName; /** * 静态大小 */ private Size largest; private Surface surface; /** * 是否正在录制中 */ private boolean isRecording = false; @Override protected int getLayoutId() { return R.layout.activity_google_camera; } @SuppressLint("ClickableViewAccessibility") @Override protected void initView() { mShowAction = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, -1.0f, Animation.RELATIVE_TO_SELF, 0.0f); mShowAction.setDuration(300); mScaleWindowAnimation = new ScaleAnimation(2.0f, 1.0f, 2.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); mScaleWindowAnimation.setDuration(300); mAlphaInAnimation = new AlphaAnimation(0.0f, 1.0f); mAlphaInAnimation.setDuration(500); mAlphaOutAnimation = new AlphaAnimation(1.0f, 0.0f); mAlphaOutAnimation.setDuration(500); mMediaRecorder = new MediaRecorder(); txtWindowTxt.setmAnimation(mScaleWindowAnimation); sbAe.setOnSeekBarChangeListener(new CameraSeekBarListener()); sbZoom.setOnSeekBarChangeListener(new CameraSeekBarListener()); sbIso.setOnSeekBarChangeListener(new CameraSeekBarListener()); LinearLayoutManager ms = new LinearLayoutManager(this); ms.setOrientation(LinearLayoutManager.HORIZONTAL); LinearLayoutManager ms1 = new LinearLayoutManager(this); ms1.setOrientation(LinearLayoutManager.HORIZONTAL); evSenseList.setLayoutManager(ms); evEffectList.setLayoutManager(ms1); sAdapter = new SenseAdapter(this, AppConstant.senseArr); effectAdapter = new EffectAdapter(this,AppConstant.effectArr); evSenseList.setAdapter(sAdapter); evEffectList.setAdapter(effectAdapter); //注册 eventbus EventBus.getDefault().register(this); } @Subscribe public void onEvent(AnyEventType event) { lockFocus(); } @Override protected void initData() { fileName = System.currentTimeMillis() + ".jpg"; mFile = new File(getExternalFilesDir(null), fileName); // 将底部布局 依次添加到 列表中 mLayoutList.clear(); mLayoutList.add(mLayoutBottom); mLayoutList.add(layoutAe); mLayoutList.add(layoutAwb); mLayoutList.add(layoutIso); mLayoutList.add(layoutZoom); mLayoutList.add(llEffect); mLayoutList.add(llSense); // AndServer run in the service. mServerManager = new ServerManager(this); mServerManager.register(); mServerManager.startServer(); } @RequiresApi(api = Build.VERSION_CODES.M) @Override public void onResume() { super.onResume(); startBackgroundThread(); // 存在关联则打开相机,没有则绑定事件 if (mTextureView.isAvailable()) { openCamera(mTextureView.getWidth(), mTextureView.getHeight()); } else { mTextureView.setSurfaceTextureListener(mSurfaceTextureListener); } } /** * SurfaceTextureListener 监听事件 */ private final TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() { @RequiresApi(api = Build.VERSION_CODES.M) @Override public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) { openCamera(width, height); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) { configureTransform(width, height); } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) { return true; } @Override public void onSurfaceTextureUpdated(SurfaceTexture texture) { } }; /** * CameraDevice 改变状态时候 调用 */ private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(@NonNull CameraDevice cameraDevice) { //打开相机时会调用此方法。 我们在这里开始相机预览。 mCameraOpenCloseLock.release(); mCameraDevice = cameraDevice; createCameraPreviewSession(); } @Override public void onDisconnected(@NonNull CameraDevice cameraDevice) { mCameraOpenCloseLock.release(); cameraDevice.close(); mCameraDevice = null; } @Override public void onError(@NonNull CameraDevice cameraDevice, int error) { mCameraOpenCloseLock.release(); cameraDevice.close(); mCameraDevice = null; finish(); } }; /** * ImageReader 的回调对象。 静止图像已准备好保存。 */ private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() { @Override public void onImageAvailable(ImageReader reader) { Image mImage = reader.acquireNextImage(); ByteBuffer buffer = mImage.getPlanes()[0].getBuffer(); byte[] bytes = new byte[buffer.remaining()]; buffer.get(bytes); FileOutputStream output = null; try { output = new FileOutputStream(mFile); output.write(bytes); } catch (IOException e) { e.printStackTrace(); } finally { mImage.close(); if (null != output) { try { output.close(); } catch (IOException e) { e.printStackTrace(); } } } // 其次把文件插入到系统图库 try { MediaStore.Images.Media.insertImage(getContentResolver(), mFile.getAbsolutePath(), fileName, null); } catch (FileNotFoundException e) { e.printStackTrace(); } // 最后通知图库更新 sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse(mFile.getPath()))); Toast.makeText(GoogleCameraActivity.this, "保存成功", Toast.LENGTH_SHORT).show(); // mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mFile)); // Image image = reader.acquireLatestImage(); // if (image != null) { // int imageWidth = image.getWidth(); // int imageHeight = image.getHeight(); // byte[] data68 = Camera2Util.getBytesFromImageAsType(image, 2); // int rgb[] = Camera2Util.decodeYUV420SP(data68, imageWidth, imageHeight); // Bitmap bitmap2 = Bitmap.createBitmap(rgb, 0, imageWidth, // imageWidth, imageHeight, Bitmap.Config.ARGB_8888); // Bitmap d65bitmap = BitmapUtils.rotateMyBitmap(BitmapUtils.ImgaeToNegative(bitmap2)); // svShow.setBitmap(d65bitmap); // image.close(); // } // setResult(AppConstant.RESULT_CODE.RESULT_OK); // finish(); } }; /** * 处理与jpg文件捕捉的事件监听(预览) */ private CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() { private void process(CaptureResult result) { switch (mState) { case STATE_PREVIEW: { // 预览正常 break; } case STATE_WAITING_LOCK: { Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); if (afState == null) { captureStillPicture(); } else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState || CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) { Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) { mState = STATE_PICTURE_TAKEN; captureStillPicture(); } else { runPrecaptureSequence(); } } break; } case STATE_WAITING_PRECAPTURE: { Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE || aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) { mState = STATE_WAITING_NON_PRECAPTURE; } break; } case STATE_WAITING_NON_PRECAPTURE: { Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) { mState = STATE_PICTURE_TAKEN; captureStillPicture(); } break; } default: break; } } @Override public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) { process(partialResult); } @Override public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { process(result); } }; /** * 给定摄像机支持的尺寸 否则选择最小的一个尺寸 * * @param choices 相机支持预期输出的尺寸列表 * @param textureViewWidth 纹理视图相对于传感器坐标的宽度 * @param textureViewHeight 纹理视图相对于传感器坐标的高度 * @param maxWidth 最大宽度 * @param maxHeight 最大高度 * @param aspectRatio 纵横比 * @return size */ private static Size chooseOptimalSize(Size[] choices, int textureViewWidth, int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) { // 收集至少与预览Surface一样大的支持的分辨率 List bigEnough = new ArrayList<>(); // 收集小于预览Surface的支持的分辨率 List notBigEnough = new ArrayList<>(); int w = aspectRatio.getWidth(); int h = aspectRatio.getHeight(); for (Size option : choices) { if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight && option.getHeight() == option.getWidth() * h / w) { if (option.getWidth() >= textureViewWidth && option.getHeight() >= textureViewHeight) { bigEnough.add(option); } else { notBigEnough.add(option); } } } // 挑选适合的尺寸 if (bigEnough.size() > 0) { return Collections.min(bigEnough, new CompareSizesByArea()); } else if (notBigEnough.size() > 0) { return Collections.max(notBigEnough, new CompareSizesByArea()); } else { Log.e(TAG, "Couldn't find any suitable preview size"); return choices[0]; } } @Override public void onPause() { closeCamera(); stopBackgroundThread(); super.onPause(); } /** * 设置与摄像头相关的成员变量。 * * @param width 摄像机预览的可用大小宽度 * @param height 相机预览的可用尺寸高度 */ @SuppressWarnings("SuspiciousNameCombination") private void setUpCameraOutputs(int width, int height) { CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); try { for (String cameraId : manager.getCameraIdList()) { characteristics = manager.getCameraCharacteristics(cameraId); // 仅适用后置摄像头 Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING); if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) { continue; } //得到相机支持的流配置(包括支持的图片分辨率等),不支持就返回 StreamConfigurationMap map = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); if (map == null) { continue; } List> ss = characteristics.getAvailableCaptureRequestKeys(); Log.e(TAG, ss.toString()); // 曝光增益 范围 range1 = characteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE); //获取支持的iso范围 isoRange = characteristics.get(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE); // 曝光时长 int[] avails = characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES); // 白平衡 int[] aa = characteristics.get(CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES); // 最大白平衡数 Integer maxAwb = characteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AWB); //获取曝光时间 etr = characteristics.get(CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE); // 静态图像捕获,选择最大可用大小。 largest = Collections.max( Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)), new CompareSizesByArea()); // w: 720 h :960 // Size largest = Collections.max( // Arrays.asList(map.getOutputSizes(ImageFormat.YUV_420_888)), // new CompareSizesByArea()); // mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), // ImageFormat.YUV_420_888, 2); mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), ImageFormat.JPEG, 1); mImageReader.setOnImageAvailableListener( mOnImageAvailableListener, mBackgroundHandler); //了解我们是否需要交换尺寸以获得相对于传感器的预览尺寸 int displayRotation = getWindowManager().getDefaultDisplay().getRotation(); mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); boolean swappedDimensions = false; switch (displayRotation) { case Surface.ROTATION_0: case Surface.ROTATION_180: if (mSensorOrientation == 90 || mSensorOrientation == 270) { swappedDimensions = true; } break; case Surface.ROTATION_90: case Surface.ROTATION_270: if (mSensorOrientation == 0 || mSensorOrientation == 180) { swappedDimensions = true; } break; default: Log.e(TAG, "Display rotation is invalid: " + displayRotation); } Point displaySize = new Point(); activity.getWindowManager().getDefaultDisplay().getSize(displaySize); int rotatedPreviewWidth = width; int rotatedPreviewHeight = height; int maxPreviewWidth = displaySize.x; int maxPreviewHeight = displaySize.y; //如果需要颠倒方向 if (swappedDimensions) { rotatedPreviewWidth = height; rotatedPreviewHeight = width; maxPreviewWidth = displaySize.y; maxPreviewHeight = displaySize.x; } if (maxPreviewWidth > MAX_PREVIEW_WIDTH) { maxPreviewWidth = MAX_PREVIEW_WIDTH; } if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) { maxPreviewHeight = MAX_PREVIEW_HEIGHT; } mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth, maxPreviewHeight, largest); // 将TextureView的宽高比与我们选择的预览大小相匹配。 int orientation = getResources().getConfiguration().orientation; if (orientation == Configuration.ORIENTATION_LANDSCAPE) { mTextureView.setAspectRatio( mPreviewSize.getWidth(), mPreviewSize.getHeight()); } else { mTextureView.setAspectRatio( mPreviewSize.getHeight(), mPreviewSize.getWidth()); } // 检查 远光灯 Boolean available = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); mFlashSupported = available == null ? false : available; mCameraId = cameraId; return; } } catch (CameraAccessException e) { e.printStackTrace(); } catch (NullPointerException ignored) { } } /** * 打开相机 * * @param width 宽度 * @param height 长度 */ @RequiresApi(api = Build.VERSION_CODES.M) private void openCamera(int width, int height) { if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION); return; } setUpCameraOutputs(width, height); configureTransform(width, height); manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); try { if (!mCameraOpenCloseLock.tryAcquire(2300, TimeUnit.MILLISECONDS)) { throw new RuntimeException("Time out waiting to lock camera opening."); } manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } catch (InterruptedException e) { throw new RuntimeException("Interrupted while trying to lock camera opening.", e); } } /** * Closes the current {@link CameraDevice}. */ private void closeCamera() { try { mCameraOpenCloseLock.acquire(); if (null != mCaptureSession) { mCaptureSession.close(); mCaptureSession = null; } if (null != mCameraDevice) { mCameraDevice.close(); mCameraDevice = null; } if (null != mImageReader) { mImageReader.close(); mImageReader = null; } } catch (InterruptedException e) { throw new RuntimeException("Interrupted while trying to lock camera closing.", e); } finally { mCameraOpenCloseLock.release(); } } /** * 打开 BackgroundThread */ private void startBackgroundThread() { mBackgroundThread = new HandlerThread("CameraBackground"); mBackgroundThread.start(); mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); } /** * 关闭 BackgroundThread */ private void stopBackgroundThread() { mBackgroundThread.quitSafely(); try { mBackgroundThread.join(); mBackgroundThread = null; mBackgroundHandler = null; } catch (InterruptedException e) { e.printStackTrace(); } } /** * 创建一个新的相机预览 */ private void createCameraPreviewSession() { try { SurfaceTexture texture = mTextureView.getSurfaceTexture(); //将默认缓冲区的大小配置为相机预览的大小。 texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); surface = new Surface(texture); //使用Surface设置CaptureRequest.Builder mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); mPreviewRequestBuilder.addTarget(surface); //添加这句话 可以在 mImageReader 监听回调中持续获取 预览图片 // mPreviewRequestBuilder.addTarget(mImageReader.getSurface()); mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) { if (null == mCameraDevice) { return; } mCaptureSession = cameraCaptureSession; try { // 自动变焦是连续的 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); setAutoFlash(mPreviewRequestBuilder); //禁用所有自动设置 // mPreviewRequestBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_OFF); //而 ISO 和 Exposure Time 与之相反,仅在 aeMode 关闭时才起作用 //只是禁用自动曝光,白平衡继续开启,自己设置iso等值,必须禁用曝光 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_OFF); //设置曝光时间 ms单位 // mPreviewRequestBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, 1000L); // mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, 1); // 设置 iso 灵敏度 mPreviewRequestBuilder.set(CaptureRequest.SENSOR_SENSITIVITY, 800); // mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest.CONTROL_AWB_MODE_AUTO); //设置曝光补偿 // mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, 10); // 设置帧 持续时长 // mPreviewRequestBuilder.set(CaptureRequest.SENSOR_FRAME_DURATION, 1000L); mPreviewRequest = mPreviewRequestBuilder.build(); mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler); SetListener(); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed( @NonNull CameraCaptureSession cameraCaptureSession) { } }, null ); } catch (CameraAccessException e) { e.printStackTrace(); } } /** * Matrix 转换配置为 mTextureView * * @param viewWidth mTextureView 宽度 * @param viewHeight mTextureView 高度 */ private void configureTransform(int viewWidth, int viewHeight) { if (null == mTextureView || null == mPreviewSize) { return; } int rotation = getWindowManager().getDefaultDisplay().getRotation(); Matrix matrix = new Matrix(); RectF viewRect = new RectF(0, 0, viewWidth, viewHeight); RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth()); float centerX = viewRect.centerX(); float centerY = viewRect.centerY(); if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) { bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY()); matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL); float scale = Math.max( (float) viewHeight / mPreviewSize.getHeight(), (float) viewWidth / mPreviewSize.getWidth()); matrix.postScale(scale, scale, centerX, centerY); matrix.postRotate(90 * (rotation - 2), centerX, centerY); } else if (Surface.ROTATION_180 == rotation) { matrix.postRotate(180, centerX, centerY); } mTextureView.setTransform(matrix); } /** * 锁定焦点设置 */ public void lockFocus() { try { // 相机锁定的方法 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START); // mCaptureCallback 等待锁定 mState = STATE_WAITING_LOCK; mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); Log.e(TAG, "lockFocus: " + e.getMessage()); } } /** * 运行预捕获序列以捕获静止图像。 在调用此方法时调用 */ private void runPrecaptureSequence() { try { // 相机触发的方法 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); // mCaptureCallback等待设置预捕获序列。 mState = STATE_WAITING_PRECAPTURE; mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } /** * 拍摄静止图片。 当我们得到响应时,应该调用此方法 */ private void captureStillPicture() { try { if (null == activity || null == mCameraDevice) { return; } final CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); captureBuilder.addTarget(mImageReader.getSurface()); // 使用与预览相同的AE和AF模式。 captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); setAutoFlash(captureBuilder); int rotation = getWindowManager().getDefaultDisplay().getRotation(); //对于方向为90的设备,我们只需从ORIENTATIONS返回我们的映射 //对于方向为270的设备,我们需要将JPEG旋转180度 captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, (ORIENTATIONS.get(rotation) + mSensorOrientation + 270) % 360); CameraCaptureSession.CaptureCallback CaptureCallback = new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { Log.e(TAG, mFile.toString()); unlockFocus(); } }; mCaptureSession.stopRepeating(); mCaptureSession.abortCaptures(); mCaptureSession.capture(captureBuilder.build(), CaptureCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } /** * 解锁焦点 在静止图像捕获序列时调用此方法 */ private void unlockFocus() { try { // Reset the auto-focus trigger mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL); setAutoFlash(mPreviewRequestBuilder); mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler); // After this, the camera will go back to the normal state of preview. mState = STATE_PREVIEW; mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } /** * 更新 Preview */ private void updatePreview() { try { mPreviewRequest = mPreviewRequestBuilder.build(); mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } @SuppressLint("MissingPermission") @OnClick({R.id.iv_back_g, R.id.iv_flash, R.id.iv_ae, R.id.iv_awb, R.id.iv_iso,R.id.img_camera_g, R.id.iv_zoom, R.id.iv_change_camera, R.id.iv_effect, R.id.iv_sense, R.id.iv_images}) public void onClick(View view) { switch (view.getId()) { case R.id.img_camera_g: { lockFocus(); break; } case R.id.iv_back_g: { finish(); break; } case R.id.iv_images: { // 挑选 相册信息 https://www.jianshu.com/p/498c9d06c193 break; } case R.id.iv_zoom: { showZoomFlag = !showZoomFlag; showLayout(SHOW_ZOOM, showZoomFlag); break; } case R.id.iv_ae: { showAeFlag = !showAeFlag; showLayout(SHOW_AE, showAeFlag); break; } case R.id.iv_awb: showAwbFlag = !showAwbFlag; showLayout(SHOW_AWB, showAwbFlag); break; case R.id.iv_iso: showIsoFlag = !showIsoFlag; showLayout(SHOW_ISO, showIsoFlag); break; case R.id.iv_effect: showEffectFlag = !showEffectFlag; showLayout(SHOW_EFFECT, showEffectFlag); break; case R.id.iv_sense: showSenseFlag = !showSenseFlag; showLayout(SHOW_SENSE, showSenseFlag); break; case R.id.iv_change_camera: { if ("1".equals(mCameraId)) { mCameraId = "0"; } else if ("0".equals(mCameraId)) { mCameraId = "1"; } else { mCameraId = "0"; } //关闭相机再开启另外个摄像头 if (mCaptureSession != null) { mCaptureSession.close(); mCaptureSession = null; } if (mCameraDevice != null) { mCameraDevice.close(); mCameraDevice = null; } try { manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } break; } case R.id.iv_flash: { if (!mFlashSupported) { Log.e(TAG, "该设备暂不支持 闪光灯"); return; } switch (flishType) { case 0: // 从关闭切换到打开 flishType = 1; mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CameraMetadata.CONTROL_AE_MODE_ON); mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_SINGLE); ivFlash.setImageResource(R.mipmap.btn_flash_on_normal); break; case 1: //从打开切换到 自动 flishType = 2; mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH); ivFlash.setImageResource(R.mipmap.btn_flash_auto_normal); break; case 2: //自动切换到关闭 flishType = 0; mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CameraMetadata.CONTROL_AE_MODE_ON); mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_OFF); ivFlash.setImageResource(R.mipmap.btn_flash_off_normal); break; default: break; } updatePreview(); } default: break; } } private void setAutoFlash(CaptureRequest.Builder requestBuilder) { if (mFlashSupported) { requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); } } /** * 显示和隐藏控件 * * @param showWhat * @param showOrNot */ private void showLayout(int showWhat, boolean showOrNot) { View v = mLayoutList.get(showWhat); if (showOrNot) { //全部隐藏但是AF/AE的显示出来 for (int i = 0; i < mLayoutBottom.getChildCount(); i++) { if (mLayoutBottom.getChildAt(i).getVisibility() == View.VISIBLE) { mLayoutBottom.getChildAt(i).setVisibility(View.INVISIBLE); } } v.startAnimation(mShowAction); v.setVisibility(View.VISIBLE); } else { //全部隐藏但是capture的显示出来 for (int i = 0; i < mLayoutBottom.getChildCount(); i++) { if (mLayoutBottom.getChildAt(i).getVisibility() == View.VISIBLE) { mLayoutBottom.getChildAt(i).setVisibility(View.INVISIBLE); } } mLayoutCapture.startAnimation(mShowAction); mLayoutCapture.setVisibility(View.VISIBLE); } } /** * switch 修改事件 * * @param buttonView view * @param isChecked boolean */ @OnCheckedChanged({R.id.switch_ae, R.id.switch_iso}) public void CameraOnCheckChangedListener(CompoundButton buttonView, boolean isChecked) { //翻转的时候mPreviewRequestBuilder变为了null,会挂掉 if (mPreviewRequestBuilder == null) { return; } switch (buttonView.getId()) { case R.id.switch_ae: { switchIso.setChecked(isChecked); if (isChecked) { mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CameraMetadata.CONTROL_AE_MODE_ON); layoutIso.getChildAt(1).setEnabled(false); } else { mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CameraMetadata.CONTROL_AE_MODE_OFF); layoutIso.getChildAt(1).setEnabled(true); } break; } case R.id.switch_iso: { switchAe.setChecked(isChecked); if (isChecked) { mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CameraMetadata.CONTROL_AE_MODE_ON); layoutIso.getChildAt(1).setEnabled(false); } else { mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CameraMetadata.CONTROL_AE_MODE_OFF); layoutIso.getChildAt(1).setEnabled(true); } break; } default: break; } updatePreview(); } /** * seekbar 滑动监听事件 */ private class CameraSeekBarListener implements SeekBar.OnSeekBarChangeListener { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { switch (seekBar.getId()) { case R.id.sb_ae: { if (switchAe.isChecked()) { // 曝光增益 if (range1 == null) { break; } Log.e(TAG, "曝光增益范围:" + range1.toString()); int maxmax = range1.getUpper(); int minmin = range1.getLower(); int all = maxmax - minmin; int time = 100 / all; int ae = ((progress / time) - maxmax) > maxmax ? maxmax : ((progress / time) - maxmax) < minmin ? minmin : ((progress / time) - maxmax); mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, ae); tvSbTxt.setText("曝光增益:" + ae); } else { // 曝光时间 if (etr == null) { tvSbTxt.setText("获取曝光时间失败"); break; } Log.e(TAG, "曝光时间范围:" + etr.toString()); long max = etr.getUpper(); long min = etr.getLower(); long ae = ((progress * (max - min)) / 100 + min); mPreviewRequestBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, ae); tvSbTxt.setText("曝光时间:" + ae); } break; } case R.id.sb_iso: { if (isoRange == null) { tvSbTxt.setText("获取iso失败"); break; } int max1 = isoRange.getUpper(); int min1 = isoRange.getLower(); int iso = ((progress * (max1 - min1)) / 100 + min1); mPreviewRequestBuilder.set(CaptureRequest.SENSOR_SENSITIVITY, iso); tvSbTxt.setText("灵敏度:" + iso); break; } case R.id.sb_zoom: { Rect rect = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); int radio = characteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM).intValue() / 2; int realRadio = characteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM).intValue(); int centerX = rect.centerX(); int centerY = rect.centerY(); int minMidth = (rect.right - ((progress * centerX) / 100 / radio) - 1) - ((progress * centerX / radio) / 100 + 8); int minHeight = (rect.bottom - ((progress * centerY) / 100 / radio) - 1) - ((progress * centerY / radio) / 100 + 16); if (minMidth < rect.right / realRadio || minHeight < rect.bottom / realRadio) { Log.e("sb_zoom", "sb_zoomsb_zoomsb_zoom"); return; } // Rect newRect = new Rect(20, 20, rect.right - ((i * centerX) / 100 / radio) - 1, rect.bottom - ((i * centerY) / 100 / radio) - 1); Rect newRect = new Rect((progress * centerX / radio) / 100 + 40, (progress * centerY / radio) / 100 + 40, rect.right - ((progress * centerX) / 100 / radio) - 1, rect.bottom - ((progress * centerY) / 100 / radio) - 1); Log.i("sb_zoom", "left--->" + ((progress * centerX / radio) / 100 + 8) + ",,,top--->" + ((progress * centerY / radio) / 100 + 16) + ",,,right--->" + (rect.right - ((progress * centerX) / 100 / radio) - 1) + ",,,bottom--->" + (rect.bottom - ((progress * centerY) / 100 / radio) - 1)); mPreviewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, newRect); tvSbTxt.setText("放大:" + progress + "%"); break; } default: break; } updatePreview(); } @Override public void onStartTrackingTouch(SeekBar seekBar) { tvSbTxt.setVisibility(View.VISIBLE); tvSbTxt.startAnimation(mAlphaInAnimation); } @Override public void onStopTrackingTouch(SeekBar seekBar) { tvSbTxt.startAnimation(mAlphaOutAnimation); tvSbTxt.setVisibility(View.INVISIBLE); } } /** * 在系统相机 配置分配完整后 添加绑定事件 */ private void SetListener() { // 配置 白平衡滑动事件 sbAwb.setmOnAwbSeekBarChangeListener(new AwbSeekBarChangeListener(GoogleCameraActivity.this, tvSbTxt, mPreviewRequestBuilder, mCaptureSession, mBackgroundHandler, mCaptureCallback)); sAdapter.setSenseOnItemClickListener(new SenseAdapter.SenseOnItemClickListener() { @Override public void itemOnClick(int position) { mPreviewRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_USE_SCENE_MODE); switch (position) { case 0: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_DISABLED); break; case 1: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_FACE_PRIORITY); break; case 2: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_ACTION); break; case 3: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_PORTRAIT); break; case 4: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_LANDSCAPE); break; case 5: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_NIGHT); break; case 6: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_NIGHT_PORTRAIT); break; case 7: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_THEATRE); break; case 8: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_BEACH); break; case 9: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_SNOW); break; case 10: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_SUNSET); break; case 11: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_STEADYPHOTO); break; case 12: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_FIREWORKS); break; case 13: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_SPORTS); break; case 14: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_PARTY); break; case 15: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_CANDLELIGHT); break; case 16: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_BARCODE); break; default: break; } updatePreview(); } }); effectAdapter.setEffectOnItemClickListener(new EffectAdapter.EffectOnItemClickListener() { @Override public void itemOnClick(int position) { switch (position) { case 0: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_EFFECT_MODE, CameraMetadata.CONTROL_EFFECT_MODE_AQUA); break; case 1: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_EFFECT_MODE, CameraMetadata.CONTROL_EFFECT_MODE_BLACKBOARD); break; case 2: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_EFFECT_MODE, CameraMetadata.CONTROL_EFFECT_MODE_MONO); break; case 3: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_EFFECT_MODE, CameraMetadata.CONTROL_EFFECT_MODE_NEGATIVE); break; case 4: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_EFFECT_MODE, CameraMetadata.CONTROL_EFFECT_MODE_POSTERIZE); break; case 5: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_EFFECT_MODE, CameraMetadata.CONTROL_EFFECT_MODE_SEPIA); break; case 6: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_EFFECT_MODE, CameraMetadata.CONTROL_EFFECT_MODE_SOLARIZE); break; case 7: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_EFFECT_MODE, CameraMetadata.CONTROL_EFFECT_MODE_WHITEBOARD); break; case 8: mPreviewRequestBuilder.set(CaptureRequest.CONTROL_EFFECT_MODE, CameraMetadata.CONTROL_EFFECT_MODE_OFF); break; default: break; } updatePreview(); } }); } /** * 录制预览 */ private void startRecordingVideo() { if (null == mCameraDevice || !mTextureView.isAvailable() || null == mPreviewSize) { return; } try { if (null != mCaptureSession) { mCaptureSession.close(); mCaptureSession = null; } setUpMediaRecorder(); SurfaceTexture mtexture = mTextureView.getSurfaceTexture(); assert mtexture != null; mtexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD); List surfaces = new ArrayList<>(); // Set up Surface for the camera preview Surface previewSurface = new Surface(mtexture); surfaces.add(previewSurface); mPreviewRequestBuilder.addTarget(previewSurface); // Set up Surface for the MediaRecorder Surface recorderSurface = mMediaRecorder.getSurface(); surfaces.add(recorderSurface); mPreviewRequestBuilder.addTarget(recorderSurface); mCameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) { //设置反复捕获数据的请求,这样预览界面就会一直有数据显示 mCaptureSession = cameraCaptureSession; updatePreview(); Log.e(TAG, "onConfigured: " + Thread.currentThread().getName()); // Start recording mMediaRecorder.start(); } @Override public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) { } }, mBackgroundHandler); } catch (CameraAccessException | IOException e) { e.printStackTrace(); } } /** * 录制结束 */ private void stopRecordingVideo() { // Stop recording mMediaRecorder.stop(); mMediaRecorder.reset(); Toast.makeText(this, "Video saved: " + mVideoPath.getAbsolutePath(), Toast.LENGTH_SHORT).show(); createCameraPreviewSession(); } /** * 设置 MediaRecorder * * @throws IOException */ private void setUpMediaRecorder() throws IOException { mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); mVideoPath = Utils.getOutputMediaFile(this,MEDIA_TYPE_VIDEO); mMediaRecorder.setOutputFile(mVideoPath.getAbsolutePath()); mMediaRecorder.setVideoEncodingBitRate(10000000); mMediaRecorder.setVideoFrameRate(30); mMediaRecorder.setVideoSize(largest.getWidth(), largest.getHeight()); mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); int rotation = getWindowManager().getDefaultDisplay().getRotation(); switch (mSensorOrientation) { case 90: mMediaRecorder.setOrientationHint(ORIENTATIONS.get(rotation)); break; case 270: mMediaRecorder.setOrientationHint(ORIENTATIONS.get(rotation)); break; default: break; } mMediaRecorder.prepare(); } /** * Start notify. */ public void onServerStart(String ip) { closeDialog(); if (!TextUtils.isEmpty(ip)) { List addressList = new LinkedList<>(); mRootUrl = "http://" + ip + ":8080/"; addressList.add(mRootUrl); addressList.add("http://" + ip + ":8080/login.html"); Log.e(TAG, "onServerStart: " + TextUtils.join("\n", addressList)); } else { mRootUrl = null; Log.e(TAG, "onServerStart: " + getString(R.string.server_ip_error)); } } /** * Error notify. */ public void onServerError(String message) { closeDialog(); mRootUrl = null; Log.e(TAG, "onServerError: " + message); } /** * Stop notify. */ public void onServerStop() { closeDialog(); mRootUrl = null; } private void showDialog() { if (mDialog == null) { mDialog = new LoadingDialog(this); } if (!mDialog.isShowing()) { mDialog.show(); } } private void closeDialog() { if (mDialog != null && mDialog.isShowing()) { mDialog.dismiss(); } } @Override protected void onDestroy() { mServerManager.unRegister(); EventBus.getDefault().unregister(this); super.onDestroy(); } private static final String LOGIN_ATTRIBUTE = "USER.LOGIN.SIGN"; public CaptureRequest.Builder getBuilder() { return mPreviewRequestBuilder; } public CameraCaptureSession getSession() { return mCaptureSession; } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/ui/MainActivity.java ================================================ package camera.cn.cameramaster.ui; import android.Manifest; import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; import android.net.ConnectivityManager; import android.os.Build; import android.support.annotation.RequiresApi; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; import com.yanzhenjie.permission.Action; import com.yanzhenjie.permission.AndPermission; import com.yanzhenjie.permission.Rationale; import com.yanzhenjie.permission.RequestExecutor; import java.io.File; import java.lang.reflect.Method; import java.util.List; import butterknife.BindView; import butterknife.OnClick; import camera.cn.cameramaster.R; import camera.cn.cameramaster.base.BaseActivity; import camera.cn.cameramaster.util.AppConstant; /** * 首页 * * @author ymc */ public class MainActivity extends BaseActivity { private final static String TAG = "MainActivity"; @BindView(R.id.btn_camera) public Button btn; @BindView(R.id.tv_message) TextView tvMsg; private File mD65File; private File mFile; @Override protected int getLayoutId() { return R.layout.activity_main; } @Override protected void initView() { mD65File = new File(getExternalFilesDir(""), "picD65.jpg"); mFile = new File(getExternalFilesDir(null), "pic.jpg"); } @Override protected void initData() { requestPermission(); // 设置关闭 移动网络 setDataConnectionState(this,false); } @SuppressLint("WrongConstant") public static void setDataConnectionState(Context cxt, boolean state) { ConnectivityManager connectivityManager = null; Class connectivityManagerClz = null; try { connectivityManager = (ConnectivityManager) cxt .getSystemService("connectivity"); connectivityManagerClz = connectivityManager.getClass(); Method method = connectivityManagerClz.getMethod( "setMobileDataEnabled", new Class[] { boolean.class }); method.invoke(connectivityManager, state); } catch (Exception e) { e.printStackTrace(); } } @OnClick({R.id.btn_camera, R.id.btn_camera2, R.id.btn_filter_camera2, R.id.btn_camera2_video}) public void onClick(View view) { switch (view.getId()) { case R.id.btn_camera: Intent intent = new Intent(this, CameraActivity.class); startActivityForResult(intent, 0); break; case R.id.btn_camera2: Intent intent2 = new Intent(this, GoogleCameraActivity.class); startActivityForResult(intent2, 1); break; case R.id.btn_filter_camera2: Intent intentFilter2 = new Intent(this, CameraSurfaceViewActivity.class); startActivityForResult(intentFilter2, 0); break; case R.id.btn_camera2_video: Intent intentVideo = new Intent(this, CameraVideoActivity.class); startActivity(intentVideo); break; default: break; } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode != AppConstant.RESULT_CODE.RESULT_OK) { return; } if (requestCode == 0) { String imgPath = data.getStringExtra(AppConstant.KEY.IMG_PATH); int picWidth = data.getIntExtra(AppConstant.KEY.PIC_WIDTH, 0); int picHeight = data.getIntExtra(AppConstant.KEY.PIC_HEIGHT, 0); Intent intent = new Intent(activity, ShowPicActivity.class); intent.putExtra(AppConstant.KEY.PIC_WIDTH, picWidth); intent.putExtra(AppConstant.KEY.PIC_HEIGHT, picHeight); intent.putExtra(AppConstant.KEY.IMG_PATH, imgPath); startActivity(intent); } else if (requestCode == 1) { // tvMsg.setText("图片 D65 光源处理中 请勿退出...."); // new Thread(new Runnable() { // @Override // public void run() { // final long starttime = System.currentTimeMillis(); // FileInputStream fis = null; // FileOutputStream output = null; // try { // fis = new FileInputStream(mFile); // Bitmap bitmap = BitmapFactory.decodeStream(fis); // Bitmap d65bitmap = BitmapUtils.ImgaeToNegative(bitmap); // output = new FileOutputStream(mD65File); // d65bitmap.compress(Bitmap.CompressFormat.JPEG, 100, output); // runOnUiThread(new Runnable() { // @Override // public void run() { // tvMsg.setText("处理完毕.耗时:"+ (System.currentTimeMillis() - starttime)+ " ms"); // } // }); // } catch (FileNotFoundException e) { // e.printStackTrace(); // } // } // }).start(); } } /** * 动态申请 (电话/位置/存储) */ @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) private void requestPermission() { AndPermission.with(this) .permission(Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO) .rationale(new Rationale() { @Override public void showRationale(Context context, List permissions, RequestExecutor executor) { executor.execute(); } }) .onGranted(new Action() { @Override public void onAction(List permissions) { Log.e(TAG, "用户给权限"); } }) .onDenied(new Action() { @Override public void onAction(List permissions) { if (AndPermission.hasAlwaysDeniedPermission(MainActivity.this, permissions)) { // 打开权限设置页 AndPermission.permissionSetting(MainActivity.this).execute(); return; } Log.e(TAG, "用户拒绝权限"); } }) .start(); } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/ui/ShowPicActivity.java ================================================ package camera.cn.cameramaster.ui; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.Matrix; import android.graphics.Paint; import android.widget.ImageView; import android.widget.SeekBar; import butterknife.BindView; import camera.cn.cameramaster.R; import camera.cn.cameramaster.base.BaseActivity; import camera.cn.cameramaster.util.AppConstant; /** * 显示照片界面 */ public class ShowPicActivity extends BaseActivity { @BindView(R.id.img) ImageView iv; @BindView(R.id.seekBar1) SeekBar seekBarRed; @BindView(R.id.seekBar2) SeekBar seekBarGreen; @BindView(R.id.seekBar3) SeekBar seekBarB; @BindView(R.id.seekBar4) SeekBar seekBarH; private Canvas canvas; //颜色矩阵 private ColorMatrix colorMatrix; private Paint paint; private int picWidth; private int picHeight; private Bitmap bitmap; private Bitmap alterBitemp; private int redProgress = 128; private int greenProgress = 128; private int blueProgress = 128; private int aplaraProgress = 128; @Override protected int getLayoutId() { return R.layout.activity_show_pic; } @Override protected void initView() { seekBarRed.setOnSeekBarChangeListener(new seekBar1Listen()); seekBarGreen.setOnSeekBarChangeListener(new seekBar2Listen()); seekBarB.setOnSeekBarChangeListener(new seekBar3Listen()); seekBarH.setOnSeekBarChangeListener(new seekBar4Listen()); } @Override protected void initData() { picWidth = getIntent().getIntExtra(AppConstant.KEY.PIC_WIDTH, 0); picHeight = getIntent().getIntExtra(AppConstant.KEY.PIC_HEIGHT, 0); // iv.setImageURI(Uri.parse(getIntent().getStringExtra(AppConstant.KEY.IMG_PATH))); // iv.setLayoutParams(new RelativeLayout.LayoutParams(picWidth, picHeight)); canvasBitmap(getIntent().getStringExtra(AppConstant.KEY.IMG_PATH)); colorMatrix.set(new float[]{ -1,0,0,0,255, 0,-1,0,0,255, 0,0,-1,0,255, 0,0,0,aplaraProgress/128.0f,0, }); paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); canvas.drawBitmap(bitmap, new Matrix(), paint); iv.setImageBitmap(alterBitemp); } /** * 画布-修改图片 */ private void canvasBitmap(String path) { //返回图像bitmap对象 bitmap = BitmapFactory.decodeFile(path, null); //可修改olterbitmap 属性与 bitmap一致 alterBitemp = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(),bitmap.getConfig()); System.out.println(bitmap.getWidth()+" :"+bitmap.getHeight()); //画布 canvas = new Canvas(alterBitemp); //canvas.drawBitmap()画笔-合成模式 colorMatrix = new ColorMatrix(); paint = new Paint(); paint.setColor(Color.BLACK); //设置画笔过滤器-new()的颜色矩阵 paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); paint.setAntiAlias(true); canvas.drawBitmap(bitmap, new Matrix(), paint); //显示imageView iv.setImageBitmap(alterBitemp); } /** * Red 修改监听事件 */ class seekBar1Listen implements SeekBar.OnSeekBarChangeListener { @Override public void onProgressChanged(SeekBar seekBar, int progress,boolean fromUser) { redProgress = progress; colorMatrix.set(new float[]{ redProgress/128.0f,0,0,0,0, 0,greenProgress/128.0f,0,0,0, 0,0,blueProgress/128.0f,0,0, 0,0,0,aplaraProgress/128.0f,0, }); paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); canvas.drawBitmap(bitmap, new Matrix(), paint); iv.setImageBitmap(alterBitemp); } @Override public void onStartTrackingTouch(SeekBar seekBar) {} @Override public void onStopTrackingTouch(SeekBar seekBar) {} } /** * green 修改监听事件 */ class seekBar2Listen implements SeekBar.OnSeekBarChangeListener { @Override public void onProgressChanged(SeekBar seekBar, int progress,boolean fromUser) { greenProgress = progress; colorMatrix.set(new float[]{ redProgress/128.0f,0,0,0,0, 0,greenProgress/128.0f,0,0,0, 0,0,blueProgress/128.0f,0,0, 0,0,0,aplaraProgress/128.0f,0, }); paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); canvas.drawBitmap(bitmap, new Matrix(), paint); iv.setImageBitmap(alterBitemp); } @Override public void onStartTrackingTouch(SeekBar seekBar) {} @Override public void onStopTrackingTouch(SeekBar seekBar) {} } /** * blue 修改监听事件 */ class seekBar3Listen implements SeekBar.OnSeekBarChangeListener { @Override public void onProgressChanged(SeekBar seekBar, int progress,boolean fromUser) { blueProgress = progress; colorMatrix.set(new float[]{ redProgress/128.0f,0,0,0,0, 0,greenProgress/128.0f,0,0,0, 0,0,blueProgress/128.0f,0,0, 0,0,0,aplaraProgress/128.0f,0, }); paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); canvas.drawBitmap(bitmap, new Matrix(), paint); iv.setImageBitmap(alterBitemp); } @Override public void onStartTrackingTouch(SeekBar seekBar) {} @Override public void onStopTrackingTouch(SeekBar seekBar) {} } /** * height 修改监听事件 */ class seekBar4Listen implements SeekBar.OnSeekBarChangeListener { @Override public void onProgressChanged(SeekBar seekBar, int progress,boolean fromUser) { aplaraProgress = progress; colorMatrix.set(new float[]{ redProgress/128.0f,0,0,0,0, 0,greenProgress/128.0f,0,0,0, 0,0,blueProgress/128.0f,0,0, 0,0,0,aplaraProgress/128.0f,0, }); paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); canvas.drawBitmap(bitmap, new Matrix(), paint); iv.setImageBitmap(alterBitemp); } @Override public void onStartTrackingTouch(SeekBar seekBar) {} @Override public void onStopTrackingTouch(SeekBar seekBar) {} } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/util/AppConstant.java ================================================ package camera.cn.cameramaster.util; /** * 应用 常量类 * * @packageName: cn.ymc.suncamera.util * @fileName: AppConstant * @date: 2019/1/24 16:54 * @author: ymc * @QQ:745612618 */ public class AppConstant { /** * 摄像头模式 */ public final static int CAMERA_MODE = 0; /** * 视频播放器模式 */ public final static int VIDEO_MODE = 1; /** * 视频最长的时长是10s */ public final static int VIDEO_MAX_TIME = 10; /** * 视频播放模式 */ public final static int VIDEO_PLAY_MODE = 0; /** * 视频录像模式 */ public final static int VIDEO_RECORD_MODE = 1; /** * 拍照模式 */ public final static int VIDEO_TAKE_PHOTO = 2; /** * 当前面板是预览状态 */ public final static int TEXTURE_PREVIEW_STATE = 0; /** * 当前面板是录像状态 */ public final static int TEXTURE_RECORD_STATE = 1; /** * 当前面板是图片状态 */ public final static int TEXTURE_PHOTO_STATE = 2; /** * 当前面板是视频播放状态 */ public final static int TEXTURE_PLAY_STATE = 3; /** * 当前是摄像头模式还是视频播放模式 */ public int MODE; /** * 当前的模式,默认为拍照模式 */ public int NOW_MODE = VIDEO_TAKE_PHOTO; /** * 感觉数组 */ public static String[] senseArr = {"DISABLED", "FACE_PRIORITY", "ACTION", "PORTRAIT", "LANDSCAPE", "NIGHT" , "NIGHT_PORTRAIT", "THEATRE", "BEACH", "SNOW", "SUNSET", "STEADYPHOTO", "FIREWORKS", "SPORTS", "PARTY", "CANDLELIGHT", "BARCODE"}; public static String[] effectArr = {"aqua", "blackboard", "monoColor", "negative", "posterization", "sepia" , "solarisation", "whiteboard", "off"}; public interface KEY{ String IMG_PATH = "IMG_PATH"; String VIDEO_PATH = "VIDEO_PATH"; String PIC_WIDTH = "PIC_WIDTH"; String PIC_HEIGHT = "PIC_HEIGHT"; } public interface RESULT_CODE { int RESULT_OK = -1; int RESULT_CANCELED = 0; int RESULT_ERROR = 1; } /** * 底部状态 : 曝光 */ public static final int SHOW_AE = 1; /** * 白平衡 */ public static final int SHOW_AWB = 2; /** * 感光度 */ public static final int SHOW_ISO = 3; /** * 放大 倍数 */ public static final int SHOW_ZOOM = 4; /** * effect */ public static final int SHOW_EFFECT = 5; /** * sense */ public static final int SHOW_SENSE = 6; /** * 手动设置 */ public static final int SHOW_SETTING = 6; /** * lab rgb 数组 */ public static double[][] labmap = {{37.986,13.555,14.059},{65.711,18.13,17.81},{49.927,-4.88,-21.925}, {43.139,-13.095,21.905} ,{55.112,8.844,-25.399},{70.719,-33.397,-0.199}, {62.661,36.067,57.096},{40.02,10.41,-45.964},{51.124,48.239,16.248}, {30.325,22.976,-21.587},{72.532,-23.709,57.255},{71.941,19.363,67.857}, {28.778,14.179,-50.297},{55.261,-38.342,31.37},{42.101,53.378,28.19}, {81.733,4.039,79.819} ,{51.935,49.986,-14.574},{51.038,-28.631,-28.638}, {96.539,-0.425,1.186},{81.257,-0.638,-0.335},{66.766,-0.734,-0.504}, {50.867,-0.153,-0.27} ,{35.656,-0.421,-1.231},{20.461,-0.079,-0.973}}; public static double [][] rgbmap = {{115,82,68},{194,150,130},{98,122,157},{87,108,67}, {133,128,177},{103,189,170},{214,126,44},{80,91,166},{193,90,99},{94,60,108}, {157,188,64},{224,163,46},{56,61,150},{70,148,73},{175,54,60},{231,199,31}, {187,86,149},{8,133,161},{243,243,242},{200,200,200},{160,160,160}, {122,122,121},{85,85,85},{52,52,52}}; } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/util/BitmapUtils.java ================================================ package camera.cn.cameramaster.util; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.media.ExifInterface; import android.media.MediaScannerConnection; import android.util.Log; import android.view.View; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; /** * Bitmap处理工具 * * @date 2019年1月25日 14:27:33 */ public class BitmapUtils { private BitmapUtils() { } /** * 800*480 */ private static final int CONFIG_480P = 1; /** * 1280*720 */ private static final int CONFIG_720P = 2; /** * 1920*1080 */ private static final int CONFIG_1080P = 3; /** * 2560*1440 */ private static final int CONFIG_2K = 4; private static int getSize(int config) { int size = 0; switch (config) { case CONFIG_480P: size = 480; break; case CONFIG_720P: size = 720; break; case CONFIG_1080P: size = 1080; break; case CONFIG_2K: size = 1440; break; default: break; } return size; } /** * 通过资源id转化成Bitmap * * @param context 对象 * @param resId 资源id * @return bitmap */ public static Bitmap ReadBitmapById(Context context, int resId) { BitmapFactory.Options opt = new BitmapFactory.Options(); opt.inPreferredConfig = Bitmap.Config.RGB_565; opt.inPurgeable = true; opt.inInputShareable = true; InputStream is = context.getResources().openRawResource(resId); return BitmapFactory.decodeStream(is, null, opt); } /** * Bitmap --> byte[] * * @param bmp Bitmap * @return byte[] */ public static byte[] readBitmap(Bitmap bmp) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); bmp.compress(Bitmap.CompressFormat.JPEG, 60, baos); try { baos.flush(); baos.close(); } catch (IOException e) { e.printStackTrace(); } return baos.toByteArray(); } /** * 旋转图片 * * @param angle * @param bitmap * @return */ public static Bitmap rotaingImageView(int angle, Bitmap bitmap) { // 旋转图片 动作 Matrix matrix = new Matrix(); matrix.postRotate(angle); // 创建新的图片 Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); if (resizedBitmap != bitmap && bitmap != null && !bitmap.isRecycled()) { bitmap.recycle(); bitmap = null; } return resizedBitmap; } /** * 读取图片旋转的角度 * * @param filename * @return */ public static void setPictureDegree(String filename, int degree) { try { if (degree == 0) { return; } int rotate = android.support.media.ExifInterface.ORIENTATION_UNDEFINED; switch (degree) { case 90: rotate = android.support.media.ExifInterface.ORIENTATION_ROTATE_90; break; case 180: rotate = android.support.media.ExifInterface.ORIENTATION_ROTATE_180; break; case 270: rotate = android.support.media.ExifInterface.ORIENTATION_ROTATE_270; break; default: break; } android.support.media.ExifInterface exifInterface = new android.support.media.ExifInterface(filename); exifInterface.setAttribute(android.support.media.ExifInterface.TAG_ORIENTATION, String.valueOf(rotate)); exifInterface.saveAttributes(); } catch (Exception e) { e.printStackTrace(); } } /** * 返回适应屏幕尺寸的位图 * * @param bit bit * @param config config */ private static Bitmap getRightSzieBitmap(Bitmap bit, int config) { // 得到理想宽度 int ww = getSize(config); // 获取图片宽度 int w = bit.getWidth(); // 计算缩放率 float rate = 1f; if (w > ww) { rate = (float) ww / (float) w; } // 重新绘图 Bitmap bitmap = Bitmap.createBitmap((int) (bit.getWidth() * rate), (int) (bit.getHeight() * rate), Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); Rect rect = new Rect(0, 0, (int) (bit.getWidth() * rate), (int) (bit.getHeight() * rate)); canvas.drawBitmap(bit, null, rect, null); return bitmap; } /** * 返回适应屏幕的位图 更节省内存 * * @param fileName file name * @param config config * @return Bitmap */ public static Bitmap getRightSzieBitmap(String fileName, int config) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(fileName, options); options.inJustDecodeBounds = false; int w = options.outWidth; int ww = getSize(config); if ((ww * 2) < w) { options.inSampleSize = 2; } // 重新绘图 Bitmap bitmap = BitmapFactory.decodeFile(fileName, options); return getRightSzieBitmap(bitmap, config); } /** * 图片去色,返回灰度图片 * * @param bmpOriginal 传入的图片 * @return 去色后的图片 */ private static Bitmap toGrayscale(Bitmap bmpOriginal) { int width, height; height = bmpOriginal.getHeight(); width = bmpOriginal.getWidth(); Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Config.RGB_565); Canvas c = new Canvas(bmpGrayscale); Paint paint = new Paint(); ColorMatrix cm = new ColorMatrix(); cm.setSaturation(0); ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm); paint.setColorFilter(f); c.drawBitmap(bmpOriginal, 0, 0, paint); return bmpGrayscale; } public static Bitmap replaceBitmapColor(Bitmap oldBitmap, int oldColor, int newColor) { Bitmap mBitmap = oldBitmap.copy(Config.ARGB_8888, true); int mBitmapWidth = mBitmap.getWidth(); int mBitmapHeight = mBitmap.getHeight(); for (int i = 0; i < mBitmapHeight; i++) { for (int j = 0; j < mBitmapWidth; j++) { int color = mBitmap.getPixel(j, i); if (color == oldColor) { //将被替换色替换为需要替换成的颜色附近的值,都替换为相同的颜色略显单调 mBitmap.setPixel(j, i, (int) (newColor + Math.random() * 100000)); } } } return mBitmap; } /** * 将图片 D65 转换 为位图 * * @param bitmap 原来图片 * @return 新图片 */ public static Bitmap ImgaeToNegative(Bitmap bitmap) { //其实我们获得宽和高就是图片像素的宽和高 //它们的乘积就是总共一张图片拥有的像素点数 int width = bitmap.getWidth(); int height = bitmap.getHeight(); Bitmap bmp = Bitmap.createBitmap(width, height, Config.ARGB_8888); //用来存储旧的色素点的数组 int[] oldPx = new int[width * height]; //用来存储新的像素点的数组 int[] newPx = new int[width * height]; int color;//用来存储原来颜色值 int r, g, b, a;//存储颜色的四个分量:红,绿,蓝,透明度 //该方法用来将图片的像素写入到oldPx中,我们这样子设置,就会获取全部的像素点 //第一个参数为写入的数组,第二个参数为读取第一个的像素点的偏移量,一般设置为0 //第三个参数为写入时,多少个像素点作为一行,第三个和第四个参数为读取的起点坐标 //第五个参数表示读取的长度,第六个表示读取的高度 bitmap.getPixels(oldPx, 0, width, 0, 0, width, height); // 存放 rgb double[] rgbmap = new double[3]; //下面用循环来处理每一个像素点 long startTime = System.currentTimeMillis(); int index = 0; for (int i = 0; i < width * height; i++) { //获取一个原来的像素点 color = oldPx[i]; r = Color.red(color); g = Color.green(color); b = Color.blue(color); a = Color.alpha(color); rgbmap[0] = r; rgbmap[1] = g; rgbmap[2] = b; // D65 光源 换算 double[] xyz = LabUtil.sRGB2XYZ(rgbmap); double[] lab = LabUtil.XYZ2Lab(xyz); double[] xyz2 = LabUtil.Lab2XYZ(lab); double[] rgb = LabUtil.XYZ2sRGB(xyz2); //下面计算生成新的颜色分量 r = (int) rgb[0]; g = (int) rgb[1]; b = (int) rgb[2]; if(rgbmap[0]!=r || rgbmap[1]!=g || rgbmap[2]!=b){ index ++; } //下面主要保证r g b 的值都必须在0~255之内 if (r > 255) { r = 255; } else if (r < 0) { r = 0; } if (g > 255) { g = 255; } else if (g < 0) { g = 0; } if (b > 255) { b = 255; } else if (b < 0) { b = 0; } //下面合成新的像素点,并添加到newPx中 color = Color.argb(a, r, g, b); newPx[i] = color; } //然后重要的一步,为bmp设置新颜色了,该方法中的参数意义与getPixels中的一样 //无非是将newPx写入到bmp中 bmp.setPixels(newPx, 0, width, 0, 0, width, height); Log.e("camera2", "图片转换需要时间 :"+ (System.currentTimeMillis()-startTime) +" 修改次数:"+ index); return bmp; } /** * 去色同时加圆角 * * @param bmpOriginal 原图 * @param pixels 圆角弧度 * @return 修改后的图片 */ public static Bitmap toGrayscale(Bitmap bmpOriginal, int pixels) { return toRoundCorner(toGrayscale(bmpOriginal), pixels); } /** * 把图片变成圆角 * * @param bitmap 需要修改的图片 * @param pixels 圆角的弧度 * @return 圆角图片 */ private static Bitmap toRoundCorner(Bitmap bitmap, int pixels) { Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888); Canvas canvas = new Canvas(output); final int color = 0xff424242; final Paint paint = new Paint(); final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); final RectF rectF = new RectF(rect); final float roundPx = pixels; paint.setAntiAlias(true); canvas.drawARGB(0, 0, 0, 0); paint.setColor(color); canvas.drawRoundRect(rectF, roundPx, roundPx, paint); paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); canvas.drawBitmap(bitmap, rect, rect, paint); return output; } /** * 使圆角功能支持BitampDrawable * * @param bitmapDrawable bitmapDrawable * @param pixels pixels * @return BitmapDrawable */ @SuppressWarnings("deprecation") public static BitmapDrawable toRoundCorner(BitmapDrawable bitmapDrawable, int pixels) { Bitmap bitmap = bitmapDrawable.getBitmap(); bitmapDrawable = new BitmapDrawable(toRoundCorner(bitmap, pixels)); return bitmapDrawable; } /** * 读取路径中的图片,然后将其转化为缩放后的bitmap返回 * * @param path path */ public static Bitmap saveBefore(String path) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; // 获取这个图片的宽和高 Bitmap bitmap = BitmapFactory.decodeFile(path, options); options.inJustDecodeBounds = false; // 计算缩放比 int be = (int) (options.outHeight / (float) 200); if (be <= 0) be = 1; options.inSampleSize = 2; // 图片长宽各缩小二分之一 // 重新读入图片,注意这次要把options.inJustDecodeBounds 设为 false哦 bitmap = BitmapFactory.decodeFile(path, options); int w = bitmap.getWidth(); int h = bitmap.getHeight(); System.out.println(w + " " + h); // savePNG_After(bitmap,path); saveJPGE_After(bitmap, path, 90); return bitmap; } /** * 将Bitmap转换成指定大小 * * @param bitmap * @param width * @param height * @return */ public static Bitmap createBitmapBySize(Bitmap bitmap, int width, int height) { return Bitmap.createScaledBitmap(bitmap, width, height, true); } // 图片按比例大小压缩方法 public static Bitmap getImageFromPath(String srcPath, float maxWidth, float maxHeight) { /*if (!isFileAtPath(srcPath)) { return null; }*/ try { BitmapFactory.Options newOpts = new BitmapFactory.Options(); // 开始读入图片,此时把options.inJustDecodeBounds 设回true了 newOpts.inJustDecodeBounds = true; Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);// 此时返回bm为空 newOpts.inJustDecodeBounds = false; int w = newOpts.outWidth; int h = newOpts.outHeight; Log.d("getImageFromPath", "bSize:newOpts.out.w=" + w + " h=" + h); float aBili = (float) maxHeight / (float) maxWidth; float bBili = (float) h / (float) w; // be=1表示不缩放,be=2代表大小变成原来的1/2,注意be只能是2的次幂,即使算出的不是2的次幂,使用时也会自动转换成2的次幂 int be = 1; if (aBili > bBili) { if (w > maxWidth) { be = (int) (w / maxWidth); } } else { if (h > maxHeight) { be = (int) (h / maxHeight); } } if (be <= 1) {//如果是放大,则不放大 be = 1; } newOpts.inSampleSize = be;// 设置缩放比例 bitmap = BitmapFactory.decodeFile(srcPath, newOpts); int degree = readPictureDegree(srcPath); if (degree != 0) { bitmap = rotaingImageView(degree, bitmap); } if (bitmap == null) { return null; } return bitmap; } catch (Exception e) { return null; } } public static Bitmap decodeBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { BitmapFactory.Options options = new BitmapFactory.Options(); //可以只获取宽高而不加载 options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); //计算压缩比例 options = calculateInSampleSize(options, reqWidth, reqHeight); return BitmapFactory.decodeResource(res, resId, options); } /** * 图片压缩处理(使用Options的方法) * * @param reqWidth 目标宽度 * @param reqHeight 目标高度 * @使用方法 首先你要将Options的inJustDecodeBounds属性设置为true,BitmapFactory.decode一次图片。 * 然后将Options连同期望的宽度和高度一起传递到到本方法中。 * 之后再使用本方法的返回值做参数调用BitmapFactory.decode创建图片。 * @explain BitmapFactory创建bitmap会尝试为已经构建的bitmap分配内存 * ,这时就会很容易导致OOM出现。为此每一种创建方法都提供了一个可选的Options参数 * ,将这个参数的inJustDecodeBounds属性设置为true就可以让解析方法禁止为bitmap分配内存 * ,返回值也不再是一个Bitmap对象, 而是null。虽然Bitmap是null了,但是Options的outWidth、 * outHeight和outMimeType属性都会被赋值。 */ public static BitmapFactory.Options calculateInSampleSize( final BitmapFactory.Options options, int reqWidth, int reqHeight) { // 源图片的高度和宽度 final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { // 计算出实际宽高和目标宽高的比率 final int heightRatio = Math.round((float) height / (float) reqHeight); final int widthRatio = Math.round((float) width / (float) reqWidth); // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高 // 一定都会大于等于目标的宽和高。 inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } // 设置压缩比例 options.inSampleSize = inSampleSize; options.inJustDecodeBounds = false; return options; } /* public static BitmapFactory.Options createBitmap(BitmapFactory.Options options ,int bwidth, int bheight, int reqWidth, int reqHeight){ int inSampleSize = 1; if (bheight > reqHeight || bwidth > reqWidth) { // 计算出实际宽高和目标宽高的比率 final int heightRatio = Math.round((float) bheight / (float) reqHeight); final int widthRatio = Math.round((float) bwidth / (float) reqWidth); // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高 // 一定都会大于等于目标的宽和高。 inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } // 设置压缩比例 options.inSampleSize = inSampleSize; options.inJustDecodeBounds = false; return options; } */ //提取图像Alpha位图 public static Bitmap getAlphaBitmap(Bitmap mBitmap, int mColor) { //BitmapDrawable的getIntrinsicWidth()方法,Bitmap的getWidth()方法 //注意这两个方法的区别 //Bitmap mAlphaBitmap = Bitmap.createBitmap(mBitmapDrawable.getIntrinsicWidth(), mBitmapDrawable.getIntrinsicHeight(), Config.ARGB_8888); Bitmap mAlphaBitmap = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), Config.ARGB_8888); Canvas mCanvas = new Canvas(mAlphaBitmap); Paint mPaint = new Paint(); mPaint.setColor(mColor); //从原位图中提取只包含alpha的位图 Bitmap alphaBitmap = mBitmap.extractAlpha(); //在画布上(mAlphaBitmap)绘制alpha位图 mCanvas.drawBitmap(alphaBitmap, 0, 0, mPaint); return mAlphaBitmap; } /** * 旋转 bitmap * * @param bmp bitmap * @return 旋转后的 bitmap */ public static Bitmap rotateMyBitmap(Bitmap bmp) { //*****旋转一下 Matrix matrix = new Matrix(); matrix.postRotate(90); Bitmap bitmap = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), Config.ARGB_8888); Bitmap nbmp2 = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true); return nbmp2; } /** * 读取图片属性:旋转的角度 * * @param path 图片绝对路径 * @return degree旋转的角度 */ public static int readPictureDegree(String path) { int degree = 0; try { ExifInterface exifInterface = new ExifInterface(path); int orientation = exifInterface.getAttributeInt( ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); switch (orientation) { case ExifInterface.ORIENTATION_ROTATE_90: degree = 90; break; case ExifInterface.ORIENTATION_ROTATE_180: degree = 180; break; case ExifInterface.ORIENTATION_ROTATE_270: degree = 270; break; } } catch (IOException e) { e.printStackTrace(); } return degree; } /** * 保存图片为PNG * * @param bitmap * @param name */ public static void savePNG_After(Bitmap bitmap, String name) { File file = new File(name); try { FileOutputStream out = new FileOutputStream(file); if (bitmap.compress(Bitmap.CompressFormat.PNG, 100, out)) { out.flush(); out.close(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /** * 保存图片为JPEG * * @param bitmap * @param path */ public static boolean saveJPGE_After(Bitmap bitmap, String path, int quality) { File file = new File(path); makeDir(file); try { FileOutputStream out = new FileOutputStream(file); if (bitmap.compress(Bitmap.CompressFormat.JPEG, quality, out)) { out.flush(); out.close(); } } catch (Exception e) { e.printStackTrace(); return false; } return true; } /** * 保存图片为JPEG * * @param bitmap * @param path */ public static void saveJPGE_After(Context context, Bitmap bitmap, String path, int quality) { File file = new File(path); makeDir(file); try { FileOutputStream out = new FileOutputStream(file); if (bitmap.compress(Bitmap.CompressFormat.JPEG, quality, out)) { out.flush(); out.close(); } updateResources(context, file.getPath()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /** * 保存图片为PNG * * @param bitmap * @param path */ public static void saveJPGE_After_PNG(Context context, Bitmap bitmap, String path, int quality) { File file = new File(path); makeDir(file); try { FileOutputStream out = new FileOutputStream(file); if (bitmap.compress(Bitmap.CompressFormat.PNG, quality, out)) { out.flush(); out.close(); } updateResources(context, file.getPath()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /** * 保存图片为PNG * * @param bitmap * @param path */ public static void saveJPGE_After_WebP(Context context, Bitmap bitmap, String path, int quality) { File file = new File(path); makeDir(file); try { FileOutputStream out = new FileOutputStream(file); if (bitmap.compress(Bitmap.CompressFormat.WEBP, quality, out)) { out.flush(); out.close(); } updateResources(context, file.getPath()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private static void makeDir(File file) { File tempPath = new File(file.getParent()); if (!tempPath.exists()) { tempPath.mkdirs(); } } /** * 图片合成 * * @param src * @param watermark * @return */ public static Bitmap createBitmap(Bitmap src, Bitmap watermark) { if (src == null) { return null; } int w = src.getWidth(); int h = src.getHeight(); int ww = watermark.getWidth(); int wh = watermark.getHeight(); // create the new blank bitmap Bitmap newb = Bitmap.createBitmap(w, h, Config.ARGB_8888);// 创建一个新的和SRC长度宽度一样的位图 Canvas cv = new Canvas(newb); // draw src into cv.drawBitmap(src, 0, 0, null);// 在 0,0坐标开始画入src // draw watermark into cv.drawBitmap(watermark, w - ww + 5, h - wh + 5, null);// 在src的右下角画入水印 // save all clip cv.save(Canvas.ALL_SAVE_FLAG);// 保存 // store cv.restore();// 存储 return newb; } /** * Bitmap 转 Drawable * * @param bitmap * @return */ public static Drawable bitmapToDrawableByBD(Bitmap bitmap) { @SuppressWarnings("deprecation") Drawable drawable = new BitmapDrawable(bitmap); return drawable; } /** * 将图片转换成byte[]以便能将其存到数据库 */ public static byte[] getByteFromBitmap(Bitmap bitmap) { ByteArrayOutputStream out = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out); try { out.flush(); out.close(); } catch (IOException e) { e.printStackTrace(); // Log.e(TAG, "transform byte exception"); } return out.toByteArray(); } /** * 将数据库中的二进制图片转换成位图 * * @param temp * @return */ public static Bitmap getBitmapFromByte(byte[] temp) { if (temp != null) { Bitmap bitmap = BitmapFactory.decodeByteArray(temp, 0, temp.length); return bitmap; } else { // Bitmap bitmap=BitmapFactory.decodeResource(getResources(), // R.drawable.contact_add_icon); return null; } } /** * 将手机中的文件转换为Bitmap类型 * * @param f * @return */ public static Bitmap getBitemapFromFile(File f) { if (!f.exists()) return null; try { return BitmapFactory.decodeFile(f.getAbsolutePath()); } catch (Exception ex) { return null; } } /** * 镜像水平翻转 * * @param bmp * @return */ public Bitmap convertMirrorBmp(Bitmap bmp) { int w = bmp.getWidth(); int h = bmp.getHeight(); Matrix matrix = new Matrix(); matrix.postScale(-1, 1); // 镜像水平翻转 Bitmap convertBmp = Bitmap.createBitmap(bmp, 0, 0, w, h, matrix, true); return convertBmp; } /** * 垂直翻转 * * @param bmp * @return */ public static Bitmap convertVertical(Bitmap bmp) { int w = bmp.getWidth(); int h = bmp.getHeight(); Matrix matrix = new Matrix(); matrix.postScale(1, -1); // 镜像垂直翻转 Bitmap convertBmp = Bitmap.createBitmap(bmp, 0, 0, w, h, matrix, true); return convertBmp; } /** * 将手机中的文件转换为Bitmap类型 * * @param path 期望宽高 * @return */ public static Bitmap decodeFile(String path, int screenWidth, int screenHeight) { Bitmap bm = null; BitmapFactory.Options opt = new BitmapFactory.Options(); //这个isjustdecodebounds很重要 opt.inJustDecodeBounds = true; bm = BitmapFactory.decodeFile(path, opt); //获取到这个图片的原始宽度和高度 int picWidth = opt.outWidth; int picHeight = opt.outHeight; //isSampleSize是表示对图片的缩放程度,比如值为2图片的宽度和高度都变为以前的1/2 opt.inSampleSize = 1; //根据屏的大小和图片大小计算出缩放比例 if (picWidth > picHeight) { if (picWidth > screenWidth) { opt.inSampleSize = picWidth / screenWidth; } } else { if (picHeight > screenHeight) { opt.inSampleSize = picHeight / screenHeight; } } //这次再真正地生成一个有像素的,经过缩放了的bitmap opt.inJustDecodeBounds = false; bm = BitmapFactory.decodeFile(path, opt); return bm; } /** * 把资源图片转换成Bitmap * * @param drawable 资源图片 * @return 位图 */ public static Bitmap getBitmapFromDrawable(Drawable drawable) { int width = drawable.getBounds().width(); int height = drawable.getBounds().height(); Bitmap bitmap = Bitmap.createBitmap(width, height, drawable .getOpacity() != PixelFormat.OPAQUE ? Config.ARGB_8888 : Config.RGB_565); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, width, height); drawable.draw(canvas); return bitmap; } /** * <<<<<<< HEAD * 翻转 * * @param bitmap * @return */ public static Bitmap flip(Bitmap bitmap) { // 点中了翻转 Matrix m = new Matrix(); m.postScale(-1, 1); bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, true); return bitmap; } /** * view turnto bitmap * ======= * 将View转为Bitmap * >>>>>>> master * * @param view * @return */ public static Bitmap getViewBitmap(View view) { view.clearFocus(); // 清除视图焦点 view.setPressed(false);// 将视图设为不可点击 boolean willNotCache = view.willNotCacheDrawing();// 返回视图是否可以保存他的画图缓存 view.setWillNotCacheDrawing(false); // Reset the drawing cache background color to fully transparent // for the duration of this operation //将视图在此操作时置为透明 int color = view.getDrawingCacheBackgroundColor();// 获得绘制缓存位图的背景颜色 view.setDrawingCacheBackgroundColor(0);// 设置绘图背景颜色 if (color != 0) {// 如果获得的背景不是黑色的则释放以前的绘图缓存 view.destroyDrawingCache();// 释放绘图资源所使用的缓存 } view.buildDrawingCache();// 重新创建绘图缓存,此时的背景色是黑色 Bitmap cacheBitmap = view.getDrawingCache();// 将绘图缓存得到的,注意这里得到的只是一个图像的引用 if (cacheBitmap == null) { return null; } Bitmap bitmap = null; try { bitmap = Bitmap.createBitmap(cacheBitmap);// 将位图实例化 } catch (OutOfMemoryError e) { while (bitmap == null) { System.gc(); System.runFinalization(); bitmap = Bitmap.createBitmap(cacheBitmap);// 将位图实例化 } } view.destroyDrawingCache();// Restore the view //恢复视图 view.setWillNotCacheDrawing(willNotCache);// 返回以前缓存设置 view.setDrawingCacheBackgroundColor(color);// 返回以前的缓存颜色设置 return bitmap; } /** * 将View转为Bitmap * * @param view * @return */ public static Bitmap convertViewToBitmap(View view) { view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); view.buildDrawingCache(); Bitmap bitmap = view.getDrawingCache(); return bitmap; } /** * 将View转为Bitmap * * @param view * @return */ public static Bitmap getBitmapFromView(View view) { view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); Bitmap bitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); view.draw(canvas); return bitmap; } public static void updateResources(Context context, String path) { MediaScannerConnection.scanFile(context, new String[]{path}, null, null); } public static Bitmap returnSaturationBitmap(Context context, Bitmap bitmap, int screenWidth, int screenHeight) { Bitmap bmp = null; /* int maxWidth = MyApplication.getInstance().getScreenWidth() - SystemUtils.dp2px(context, 20); int maxHeight = maxWidth * 4 / 3;*/ // - SystemUtils.dp2px(context, 20) int reqWidth = screenWidth; int reqHeight = reqWidth * 4 / 3; bmp = createBitmap(bitmap, reqWidth, reqHeight); ColorMatrix cMatrix = new ColorMatrix(); // 设置饱和度 cMatrix.setSaturation(0.0f); Paint paint = new Paint(); paint.setColorFilter(new ColorMatrixColorFilter(cMatrix)); Canvas canvas = new Canvas(bmp); // 在Canvas上绘制一个已经存在的Bitmap。这样,dstBitmap就和srcBitmap一摸一样了 canvas.drawBitmap(bitmap, 0, 0, paint); return bmp; } /** * 创建期望大小的bitmap * * @param bitmap * @param reqWidth * @return */ public static Bitmap createBitmap(Bitmap bitmap, int reqWidth, int reqHeight) { Bitmap bmp = null; int inSampleSize = 0; int bWidth = bitmap.getWidth(); int bHeight = bitmap.getHeight(); if (bHeight > reqHeight || bWidth > reqWidth) { // 计算出实际宽高和目标宽高的比率 final int heightRatio = Math.round((float) bHeight / (float) reqHeight); final int widthRatio = Math.round((float) bWidth / (float) reqWidth); // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高 // 一定都会大于等于目标的宽和高。 inSampleSize = heightRatio < widthRatio ? widthRatio : heightRatio; } try { if (inSampleSize != 0) { bmp = Bitmap.createBitmap(bWidth / inSampleSize, bHeight / inSampleSize, Config.ARGB_8888); } else { bmp = Bitmap.createBitmap(bWidth, bHeight, Config.ARGB_8888); } } catch (OutOfMemoryError e) { e.printStackTrace(); while (bmp == null) { System.gc(); System.runFinalization(); if (inSampleSize != 0) { bmp = Bitmap.createBitmap(bWidth / inSampleSize, bHeight / inSampleSize, Config.ARGB_8888); } else { bmp = Bitmap.createBitmap(bWidth, bHeight, Config.ARGB_8888); } } } return bmp; } /** * 圆形Bitmap * * @param bitmap * @return */ public static Bitmap getRoundedCornerBitmap(Bitmap bitmap) { Bitmap outBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888); Canvas canvas = new Canvas(outBitmap); final int color = 0xff424242; final Paint paint = new Paint(); final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); final RectF rectF = new RectF(rect); final float roundPX = bitmap.getWidth() / 2; paint.setAntiAlias(true); canvas.drawARGB(0, 0, 0, 0); paint.setColor(color); canvas.drawRoundRect(rectF, roundPX, roundPX, paint); paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); canvas.drawBitmap(bitmap, rect, rect, paint); return outBitmap; } /** * 改变bitmap 对比度 * * @param bitmap * @param progress * @return */ public static Bitmap returnContrastBitmap(Bitmap bitmap, int progress) { //曝光度 Bitmap contrast_bmp = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888); // int brightness = progress - 127; float contrast = (float) ((progress + 64) / 128.0); ColorMatrix contrast_cMatrix = new ColorMatrix(); contrast_cMatrix.set(new float[]{contrast, 0, 0, 0, 0, 0, contrast, 0, 0, 0,// 改变对比度 0, 0, contrast, 0, 0, 0, 0, 0, 1, 0}); Paint contrast_paint = new Paint(); contrast_paint.setColorFilter(new ColorMatrixColorFilter(contrast_cMatrix)); Canvas contrast_canvas = new Canvas(contrast_bmp); // 在Canvas上绘制一个已经存在的Bitmap。这样,dstBitmap就和srcBitmap一摸一样了 contrast_canvas.drawBitmap(bitmap, 0, 0, contrast_paint); return contrast_bmp; } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/util/Camera2Util.java ================================================ package camera.cn.cameramaster.util; import android.graphics.ImageFormat; import android.media.Image; import android.os.Build; import android.support.annotation.RequiresApi; import android.util.Log; import java.nio.ByteBuffer; /** * camera2 中 yuv 420 格式图片 转为 rgb 图片 * * @packageName: cn.tongue.tonguecamera.util * @fileName: Camera2Util * @date: 2019/3/13 13:37 * @author: ymc * @QQ:745612618 */ public class Camera2Util { public static final int YUV420P = 0; public static final int YUV420SP = 1; public static final int NV21 = 2; private static final String TAG = "Camera2Util"; /*** * 此方法内注释以640*480为例 * 未考虑CropRect的 */ @RequiresApi(api = Build.VERSION_CODES.KITKAT) public static byte[] getBytesFromImageAsType(Image image, int type) { try { //获取源数据,如果是YUV格式的数据planes.length = 3 //plane[i]里面的实际数据可能存在byte[].length <= capacity (缓冲区总大小) final Image.Plane[] planes = image.getPlanes(); //数据有效宽度,一般的,图片width <= rowStride,这也是导致byte[].length <= capacity的原因 // 所以我们只取width部分 int width = image.getWidth(); int height = image.getHeight(); //此处用来装填最终的YUV数据,需要1.5倍的图片大小,因为Y U V 比例为 4:1:1 byte[] yuvBytes = new byte[width * height * ImageFormat.getBitsPerPixel(ImageFormat.YUV_420_888) / 8]; //目标数组的装填到的位置 int dstIndex = 0; //临时存储uv数据的 byte uBytes[] = new byte[width * height / 4]; byte vBytes[] = new byte[width * height / 4]; int uIndex = 0; int vIndex = 0; int pixelsStride, rowStride; for (int i = 0; i < planes.length; i++) { pixelsStride = planes[i].getPixelStride(); rowStride = planes[i].getRowStride(); ByteBuffer buffer = planes[i].getBuffer(); //如果pixelsStride==2,一般的Y的buffer长度=640*480,UV的长度=640*480/2-1 //源数据的索引,y的数据是byte中连续的,u的数据是v向左移以为生成的,两者都是偶数位为有效数据 byte[] bytes = new byte[buffer.capacity()]; buffer.get(bytes); int srcIndex = 0; if (i == 0) { //直接取出来所有Y的有效区域,也可以存储成一个临时的bytes,到下一步再copy for (int j = 0; j < height; j++) { System.arraycopy(bytes, srcIndex, yuvBytes, dstIndex, width); srcIndex += rowStride; dstIndex += width; } } else if (i == 1) { //根据pixelsStride取相应的数据 for (int j = 0; j < height / 2; j++) { for (int k = 0; k < width / 2; k++) { uBytes[uIndex++] = bytes[srcIndex]; srcIndex += pixelsStride; } if (pixelsStride == 2) { srcIndex += rowStride - width; } else if (pixelsStride == 1) { srcIndex += rowStride - width / 2; } } } else if (i == 2) { //根据pixelsStride取相应的数据 for (int j = 0; j < height / 2; j++) { for (int k = 0; k < width / 2; k++) { vBytes[vIndex++] = bytes[srcIndex]; srcIndex += pixelsStride; } if (pixelsStride == 2) { srcIndex += rowStride - width; } else if (pixelsStride == 1) { srcIndex += rowStride - width / 2; } } } } // image.close(); //根据要求的结果类型进行填充 switch (type) { case YUV420P: System.arraycopy(uBytes, 0, yuvBytes, dstIndex, uBytes.length); System.arraycopy(vBytes, 0, yuvBytes, dstIndex + uBytes.length, vBytes.length); break; case YUV420SP: for (int i = 0; i < vBytes.length; i++) { yuvBytes[dstIndex++] = uBytes[i]; yuvBytes[dstIndex++] = vBytes[i]; } break; case NV21: for (int i = 0; i < vBytes.length; i++) { yuvBytes[dstIndex++] = vBytes[i]; yuvBytes[dstIndex++] = uBytes[i]; } break; } return yuvBytes; } catch (final Exception e) { if (image != null) { image.close(); } Log.i(TAG, e.toString()); } return null; } /*** * YUV420 转化成 RGB */ public static int[] decodeYUV420SP(byte[] yuv420sp, int width, int height) { final int frameSize = width * height; int rgb[] = new int[frameSize]; for (int j = 0, yp = 0; j < height; j++) { int uvp = frameSize + (j >> 1) * width, u = 0, v = 0; for (int i = 0; i < width; i++, yp++) { int y = (0xff & ((int) yuv420sp[yp])) - 16; if (y < 0) { y = 0; } if ((i & 1) == 0) { v = (0xff & yuv420sp[uvp++]) - 128; u = (0xff & yuv420sp[uvp++]) - 128; } int y1192 = 1192 * y; int r = (y1192 + 1634 * v); int g = (y1192 - 833 * v - 400 * u); int b = (y1192 + 2066 * u); if (r < 0) { r = 0; } else if (r > 262143) { r = 262143; } if (g < 0) { g = 0; } else if (g > 262143) { g = 262143; } if (b < 0) { b = 0; } else if (b > 262143) { b = 262143; } rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff); } } return rgb; } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/util/CameraUtil.java ================================================ package camera.cn.cameramaster.util; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.Matrix; import android.hardware.Camera; import android.hardware.Camera.Size; import android.util.Log; import android.view.Surface; import java.util.Collections; import java.util.Comparator; import java.util.List; /** * 拍照工具类 */ public class CameraUtil { private static final String TAG = "CameraUtil"; /** * 降序 */ private CameraDropSizeComparator dropSizeComparator = new CameraDropSizeComparator(); /** * 升序 */ private CameraAscendSizeComparator ascendSizeComparator = new CameraAscendSizeComparator(); private static CameraUtil instance = null; private CameraUtil() { } public static CameraUtil getInstance() { if (instance == null) { instance = new CameraUtil(); return instance; } else { return instance; } } private int getRecorderRotation(int cameraId) { Camera.CameraInfo info = new Camera.CameraInfo(); Camera.getCameraInfo(cameraId, info); return info.orientation; } /** * 获取所有支持的返回视频尺寸 * * @param list list * @param minHeight minHeight * @return Size */ private Size getPropVideoSize(List list, int minHeight) { Collections.sort(list, ascendSizeComparator); int i = 0; for (Size s : list) { if ((s.height >= minHeight)) { break; } i++; } if (i == list.size()) { i = 0; } return list.get(i); } /** * 保证预览方向正确 * * @param activity activity * @param cameraId cameraId * @param camera camera */ public void setCameraDisplayOrientation(Activity activity, int cameraId, Camera camera) { Camera.CameraInfo info = new Camera.CameraInfo(); Camera.getCameraInfo(cameraId, info); int rotation = activity.getWindowManager().getDefaultDisplay() .getRotation(); int degrees = 0; switch (rotation) { case Surface.ROTATION_0: degrees = 0; break; case Surface.ROTATION_90: degrees = 90; break; case Surface.ROTATION_180: degrees = 180; break; case Surface.ROTATION_270: degrees = 270; break; default: break; } int result; if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { result = (info.orientation + degrees) % 360; result = (360 - result) % 360; } else { result = (info.orientation - degrees + 360) % 360; } //设置角度 camera.setDisplayOrientation(result); } public Bitmap setTakePicktrueOrientation(int id, Bitmap bitmap) { Camera.CameraInfo info = new Camera.CameraInfo(); Camera.getCameraInfo(id, info); bitmap = rotaingImageView(id, info.orientation, bitmap); return bitmap; } /** * 把相机拍照返回照片转正 * * @param angle 旋转角度 * @return bitmap 图片 */ private Bitmap rotaingImageView(int id, int angle, Bitmap bitmap) { //矩阵 Matrix matrix = new Matrix(); matrix.postRotate(angle); //加入翻转 把相机拍照返回照片转正 if (id == 1) { matrix.postScale(-1, 1); } // 创建新的图片 Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); return resizedBitmap; } /** * 获取所有支持的预览尺寸 * * @param list list * @param minWidth minWidth * @return Size */ private Size getPropPreviewSize(List list, int minWidth) { Collections.sort(list, ascendSizeComparator); int i = 0; for (Size s : list) { if ((s.width >= minWidth)) { break; } i++; } if (i == list.size()) { i = 0; } return list.get(i); } /** * 获取所有支持的返回图片尺寸 * * @param list list * @param minWidth minWidth * @return Size */ private Size getPropPictureSize(List list, int minWidth) { Collections.sort(list, ascendSizeComparator); int i = 0; for (Size s : list) { if ((s.width >= minWidth)) { break; } i++; } if (i == list.size()) { i = 0; } return list.get(i); } /** * 获取所有支持的返回视频尺寸 * * @param list list * @param minHeight minHeight * @return Size */ public Size getPropSizeForHeight(List list, int minHeight) { Collections.sort(list, ascendSizeComparator); int i = 0; for (Size s : list) { if ((s.height >= minHeight)) { Log.e(TAG, "getPropSizeForHeight: s.height=" + s.height); break; } i++; } if (i == list.size()) { i = list.size(); } return list.get(i); } /** * 根据 宽度和高度找到是否有相等的 尺寸 如果没有 就获取最小的 值 * @param list list * @param th 高度 * @param minWidth 宽度 * @return size */ public Size getPicPreviewSize(List list, int th, int minWidth){ Collections.sort(list, ascendSizeComparator); int i = 0; for(int x=0;x list, float th, int minWidth){ Collections.sort(list, ascendSizeComparator); int i = 0; for(Size s:list){ if((s.width >= minWidth) && equalRate(s, th)){ Log.i(TAG, "PictureSize : w = " + s.width + "h = " + s.height); break; } i++; } if(i == list.size()){ i = 0;//如果没找到,就选最小的size } return list.get(i); } /** * 升序 按照高度 */ private class CameraAscendSizeComparatorForHeight implements Comparator { @Override public int compare(Size lhs, Size rhs) { if (lhs.height == rhs.height) { return 0; } else if (lhs.height > rhs.height) { return 1; } else { return -1; } } } private boolean equalRate(Size s, float rate) { float r = (float) (s.width) / (float) (s.height); return Math.abs(r - rate) <= 0.03; } /** * 降序 */ private class CameraDropSizeComparator implements Comparator { @Override public int compare(Size lhs, Size rhs) { if (lhs.width == rhs.width) { return 0; } else if (lhs.width < rhs.width) { return 1; } else { return -1; } } } /** * 升序 */ private class CameraAscendSizeComparator implements Comparator { @Override public int compare(Size lhs, Size rhs) { if (lhs.width == rhs.width) { return 0; } else if (lhs.width > rhs.width) { return 1; } else { return -1; } } } /** * 打印支持的previewSizes * * @param params */ private void printSupportPreviewSize(Camera.Parameters params) { List previewSizes = params.getSupportedPreviewSizes(); for (int i = 0; i < previewSizes.size(); i++) { Size size = previewSizes.get(i); } } /** * 打印支持的pictureSizes * * @param params */ private void printSupportPictureSize(Camera.Parameters params) { List pictureSizes = params.getSupportedPictureSizes(); for (int i = 0; i < pictureSizes.size(); i++) { Size size = pictureSizes.get(i); } } /** * 打印支持的聚焦模式 * * @param params params */ private void printSupportFocusMode(Camera.Parameters params) { List focusModes = params.getSupportedFocusModes(); for (String mode : focusModes) { Log.e(TAG, "printSupportFocusMode: " + mode); } } /** * 打开闪关灯 * * @param mCamera mCamera */ public void turnLightOn(Camera mCamera) { if (mCamera == null) { return; } Camera.Parameters parameters = mCamera.getParameters(); if (parameters == null) { return; } List flashModes = parameters.getSupportedFlashModes(); if (flashModes == null) { return; } String flashMode = parameters.getFlashMode(); if (!Camera.Parameters.FLASH_MODE_ON.equals(flashMode)) { // Turn on the flash if (flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) { parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); mCamera.setParameters(parameters); } } } /** * 自动模式闪光灯 * * @param mCamera mCamera */ public void turnLightAuto(Camera mCamera) { if (mCamera == null) { return; } Camera.Parameters parameters = mCamera.getParameters(); if (parameters == null) { return; } List flashModes = parameters.getSupportedFlashModes(); if (flashModes == null) { return; } String flashMode = parameters.getFlashMode(); if (!Camera.Parameters.FLASH_MODE_AUTO.equals(flashMode)) { // Turn on the flash if (flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) { parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); mCamera.setParameters(parameters); } } } /** * 关闭闪光灯 * * @param mCamera mCamera */ public void turnLightOff(Camera mCamera) { if (mCamera == null) { return; } Camera.Parameters parameters = mCamera.getParameters(); if (parameters == null) { return; } List flashModes = parameters.getSupportedFlashModes(); String flashMode = parameters.getFlashMode(); if (flashModes == null) { return; } if (!Camera.Parameters.FLASH_MODE_OFF.equals(flashMode)) { if (flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) { parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); mCamera.setParameters(parameters); } } } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/util/CameraV2.java ================================================ package camera.cn.cameramaster.util; import android.Manifest; import android.app.Activity; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.ImageFormat; import android.graphics.Point; import android.graphics.SurfaceTexture; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraManager; import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.TotalCaptureResult; import android.hardware.camera2.params.StreamConfigurationMap; import android.media.Image; import android.media.ImageReader; import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.support.annotation.NonNull; import android.support.annotation.RequiresApi; import android.support.v4.app.ActivityCompat; import android.util.Log; import android.util.Size; import android.util.SparseIntArray; import android.view.Surface; import android.widget.Toast; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import camera.cn.cameramaster.view.CameraV2GLSurfaceView; /** * camera2 拍照工具类 * * 参考url : [https://blog.csdn.net/lb377463323/article/details/78054892] * * @author ymc * @date 2019年2月12日 13:59:30 * */ public class CameraV2 { private static final String TAG = "CameraV2"; private Activity mActivity; private CameraDevice mCameraDevice; private String mCameraId; /** * 预览尺寸 */ private Size mPreviewSize; private HandlerThread mBackgroundThread; private Handler mBackgroundHandler; private SurfaceTexture mSurfaceTexture; /** * 相机预览 */ private CaptureRequest.Builder mPreviewRequestBuilder; private CaptureRequest mPreviewRequest; private CameraCaptureSession mCaptureSession; private ImageReader mImageReader; /** * Camera2 API保证的最大预览宽度 */ private static final int MAX_PREVIEW_WIDTH = 1920; /** * Camera2 API保证的最大预览高度 */ private static final int MAX_PREVIEW_HEIGHT = 1280; /** * 相机传感器的方向 */ private int mSensorOrientation; private File mFile; /** * 当前相机状态. */ private int mState = STATE_PREVIEW; /** * 相机预览状态 */ private static final int STATE_PREVIEW = 0; /** * 等待相机锁定 */ private static final int STATE_WAITING_LOCK = 1; /** * 相机状态:等待曝光处于预捕获状态。 */ private static final int STATE_WAITING_PRECAPTURE = 2; /** * 相机状态:等待曝光状态不是预捕获。 */ private static final int STATE_WAITING_NON_PRECAPTURE = 3; /** * 相机状态:已拍摄照片。 */ private static final int STATE_PICTURE_TAKEN = 4; /** * 屏幕旋转转换为JPEG方向。 */ private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); static { ORIENTATIONS.append(Surface.ROTATION_0, 90); ORIENTATIONS.append(Surface.ROTATION_90, 0); ORIENTATIONS.append(Surface.ROTATION_180, 270); ORIENTATIONS.append(Surface.ROTATION_270, 180); } /** * 在关闭相机之前阻止应用程序退出 */ private Semaphore mCameraOpenCloseLock = new Semaphore(1); /** * 当前相机设备是否支持Flash */ private boolean mFlashSupported; /** * 绘制容器 */ private Surface surface; public CameraV2(Activity activity) { mActivity = activity; } /** * 设置与摄像头相关的成员变量。 * * @param width 摄像机预览的可用大小宽度 * @param height 相机预览的可用尺寸高度 */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public Size setUpCameraOutputs(int width, int height) { mFile = new File(mActivity.getExternalFilesDir(null), "pic.png"); CameraManager manager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE); try { for (String cameraId : manager.getCameraIdList()) { CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId); // 不使用前置摄像头 Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING); if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) { continue; } StreamConfigurationMap map = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); if (map == null) { continue; } // 静态图像捕获,选择最大可用大小。 Size largest = Collections.max( Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)), new CompareSizesByArea()); mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), ImageFormat.JPEG, 2); mImageReader.setOnImageAvailableListener( mOnImageAvailableListener, mBackgroundHandler); //了解我们是否需要交换尺寸以获得相对于传感器的预览尺寸 int displayRotation = mActivity.getWindowManager().getDefaultDisplay().getRotation(); mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); boolean swappedDimensions = false; switch (displayRotation) { case Surface.ROTATION_0: case Surface.ROTATION_180: if (mSensorOrientation == 90 || mSensorOrientation == 270) { swappedDimensions = true; } break; case Surface.ROTATION_90: case Surface.ROTATION_270: if (mSensorOrientation == 0 || mSensorOrientation == 180) { swappedDimensions = true; } break; default: Log.e(TAG, "Display rotation is invalid: " + displayRotation); } Point displaySize = new Point(); mActivity.getWindowManager().getDefaultDisplay().getSize(displaySize); int rotatedPreviewWidth = width; int rotatedPreviewHeight = height; int maxPreviewWidth = displaySize.x; int maxPreviewHeight = displaySize.y; if (swappedDimensions) { rotatedPreviewWidth = height; rotatedPreviewHeight = width; maxPreviewWidth = displaySize.y; maxPreviewHeight = displaySize.x; } if (maxPreviewWidth > MAX_PREVIEW_WIDTH) { maxPreviewWidth = MAX_PREVIEW_WIDTH; } if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) { maxPreviewHeight = MAX_PREVIEW_HEIGHT; } mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth, maxPreviewHeight, largest); // 将TextureView的宽高比与我们选择的预览大小相匹配。 int orientation = mActivity.getResources().getConfiguration().orientation; // if (orientation == Configuration.ORIENTATION_LANDSCAPE) { // mTextureView.setAspectRatio( // mPreviewSize.getWidth(), mPreviewSize.getHeight()); // } else { // mTextureView.setAspectRatio( // mPreviewSize.getHeight(), mPreviewSize.getWidth()); // } // 检查 远光灯 Boolean available = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); mFlashSupported = available == null ? false : available; mCameraId = cameraId; } } catch (CameraAccessException e) { e.printStackTrace(); } catch (NullPointerException ignored) { } return mPreviewSize; } public void startBackgroundThread() { mBackgroundThread = new HandlerThread("CameraThread"); mBackgroundThread.start(); mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); } /** * 关闭 BackgroundThread */ public void stopBackgroundThread() { mBackgroundThread.quitSafely(); try { mBackgroundThread.join(); mBackgroundThread = null; mBackgroundHandler = null; } catch (InterruptedException e) { e.printStackTrace(); } } /** * 打开相机 * * @return boolean */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public boolean openCamera() { CameraManager cameraManager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE); try { if (ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { return false; } cameraManager.openCamera(mCameraId, mStateCallback, mBackgroundHandler); if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { throw new RuntimeException("Time out waiting to lock camera opening."); } } catch (CameraAccessException | InterruptedException e) { e.printStackTrace(); return false; } return true; } /** * CameraDevice 改变状态时候 调用 */ private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override public void onOpened(@NonNull CameraDevice camera) { mCameraDevice = camera; //打开相机时会调用此方法。 我们在这里开始相机预览。 mCameraOpenCloseLock.release(); // createCameraPreviewSession(); } @Override public void onDisconnected(@NonNull CameraDevice camera) { mCameraOpenCloseLock.release(); camera.close(); mCameraDevice = null; } @Override public void onError(@NonNull CameraDevice camera, int error) { mCameraOpenCloseLock.release(); camera.close(); mCameraDevice = null; mActivity.finish(); } }; /** * 赋值surfaceTexture * * @param surfaceTexture surfaceTexture */ public void setPreviewTexture(SurfaceTexture surfaceTexture) { mSurfaceTexture = surfaceTexture; } /** * 创建相机 并打开预览 */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public void createCameraPreviewSession() { mSurfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); surface = new Surface(mSurfaceTexture); try { mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); mPreviewRequestBuilder.addTarget(surface); mCameraDevice.createCaptureSession(Arrays.asList(surface,mImageReader.getSurface()), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession session) { if (null == mCameraDevice) { return; } mCaptureSession = session; try { // 自动变焦是连续的 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); if (mFlashSupported) { mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); } // 显示相机预览 mPreviewRequest = mPreviewRequestBuilder.build(); mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed(@NonNull CameraCaptureSession session) { } }, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } /** * ImageReader 的回调对象。 静止图像已准备好保存。 */ private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() { @Override public void onImageAvailable(ImageReader reader) { Toast.makeText(mActivity,"camera2 ok",Toast.LENGTH_SHORT).show(); mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mFile)); CameraV2GLSurfaceView.shouldTakePic = true; } }; /** * 比较 两个 size */ static class CompareSizesByArea implements Comparator { @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override public int compare(Size lhs, Size rhs) { // 在这里投射以确保乘法不会溢出 return Long.signum((long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight()); } } /** * 给定摄像机支持的尺寸 否则选择最小的一个尺寸 * * @param choices 相机支持预期输出的尺寸列表 * @param textureViewWidth 纹理视图相对于传感器坐标的宽度 * @param textureViewHeight 纹理视图相对于传感器坐标的高度 * @param maxWidth 最大宽度 * @param maxHeight 最大高度 * @param aspectRatio 纵横比 * @return size */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private static Size chooseOptimalSize(Size[] choices, int textureViewWidth, int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) { // 收集至少与预览Surface一样大的支持的分辨率 List bigEnough = new ArrayList<>(); // 收集小于预览Surface的支持的分辨率 List notBigEnough = new ArrayList<>(); int w = aspectRatio.getWidth(); int h = aspectRatio.getHeight(); for (Size option : choices) { if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight && option.getHeight() == option.getWidth() * h / w) { if (option.getWidth() >= textureViewWidth && option.getHeight() >= textureViewHeight) { bigEnough.add(option); } else { notBigEnough.add(option); } } } // 挑选适合的尺寸 if (bigEnough.size() > 0) { return Collections.min(bigEnough, new CompareSizesByArea()); } else if (notBigEnough.size() > 0) { return Collections.max(notBigEnough, new CompareSizesByArea()); } else { Log.e(TAG, "Couldn't find any suitable preview size"); return choices[0]; } } /** * 锁定焦点设置 */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public void lockFocus() { try { // 相机锁定的方法 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START); // mCaptureCallback 等待锁定 mState = STATE_WAITING_LOCK; mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } /** * Closes the current {@link CameraDevice}. */ public void closeCamera() { try { mCameraOpenCloseLock.acquire(); if (null != mCaptureSession) { mCaptureSession.close(); mCaptureSession = null; } if (null != mCameraDevice) { mCameraDevice.close(); mCameraDevice = null; } if (null != mImageReader) { mImageReader.close(); mImageReader = null; } } catch (InterruptedException e) { e.printStackTrace(); } finally { mCameraOpenCloseLock.release(); } } /** * 处理与jpg文件捕捉的事件监听(预览) */ private CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() { @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private void process(CaptureResult result) { switch (mState) { case STATE_PREVIEW: { // 预览正常 break; } case STATE_WAITING_LOCK: { Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); if (afState == null) { captureStillPicture(); } else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState || CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) { Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) { mState = STATE_PICTURE_TAKEN; captureStillPicture(); } else { runPrecaptureSequence(); } } break; } case STATE_WAITING_PRECAPTURE: { Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE || aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) { mState = STATE_WAITING_NON_PRECAPTURE; } break; } case STATE_WAITING_NON_PRECAPTURE: { Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) { mState = STATE_PICTURE_TAKEN; captureStillPicture(); } break; } default: break; } } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) { process(partialResult); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { process(result); } }; /** * 拍摄静止图片。 当我们得到响应时,应该调用此方法 */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private void captureStillPicture() { try { if (null == mActivity || null == mCameraDevice) { return; } final CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); captureBuilder.addTarget(mImageReader.getSurface()); // 使用与预览相同的AE和AF模式。 captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); // 查看使用支持 flash if (mFlashSupported) { captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); } int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation(); captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation)); CameraCaptureSession.CaptureCallback capturecallback = new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { Log.e(TAG, "--------------------:" + mFile.toString()); unlockFocus(); } }; mCaptureSession.stopRepeating(); mCaptureSession.abortCaptures(); mCaptureSession.capture(captureBuilder.build(), capturecallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); Log.e(TAG, "capture err: " + e.getMessage()); } } /** * 解锁焦点 在静止图像捕获序列时调用此方法 */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private void unlockFocus() { try { mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL); if (mFlashSupported) { mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); } mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler); // 相机转为正常状态 mState = STATE_PREVIEW; mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } /** * 从指定的屏幕旋转中检索JPEG方向。 */ private int getOrientation(int rotation) { //对于方向为90的设备,我们只需从ORIENTATIONS返回我们的映射 //对于方向为270的设备,我们需要将JPEG旋转180度 return (ORIENTATIONS.get(rotation) + mSensorOrientation + 270) % 360; } /** * 保存文件 */ private static class ImageSaver implements Runnable { private final Image mImage; private final File mFile; ImageSaver(Image image, File file) { mImage = image; mFile = file; } @Override public void run() { ByteBuffer buffer = mImage.getPlanes()[0].getBuffer(); byte[] bytes = new byte[buffer.remaining()]; buffer.get(bytes); FileOutputStream output = null; try { output = new FileOutputStream(mFile); output.write(bytes); } catch (IOException e) { e.printStackTrace(); } finally { mImage.close(); if (null != output) { try { output.close(); } catch (IOException e) { e.printStackTrace(); } } } } } /** * 运行预捕获序列以捕获静止图像。 在调用此方法时调用 */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private void runPrecaptureSequence() { try { // 相机触发的方法 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); // mCaptureCallback等待设置预捕获序列。 mState = STATE_WAITING_PRECAPTURE; mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/util/CompareSizesByArea.java ================================================ package camera.cn.cameramaster.util; import android.os.Build; import android.support.annotation.RequiresApi; import android.util.Size; import java.util.Comparator; /** * 比较 工具 * * @packageName: cn.tongue.tonguecamera.util * @fileName: CompareSizesByArea * @date: 2019/4/16 13:42 * @author: ymc * @QQ:745612618 */ public class CompareSizesByArea implements Comparator { @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override public int compare(Size lhs, Size rhs) { // 在这里投射以确保乘法不会溢出 return Long.signum((long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight()); } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/util/FilterEngine.java ================================================ package camera.cn.cameramaster.util; import android.annotation.SuppressLint; import android.content.Context; import android.opengl.GLES11Ext; import android.opengl.GLES20; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import camera.cn.cameramaster.R; import static android.opengl.GLES20.GL_FLOAT; import static android.opengl.GLES20.GL_FRAGMENT_SHADER; import static android.opengl.GLES20.GL_TRIANGLES; import static android.opengl.GLES20.GL_VERTEX_SHADER; import static android.opengl.GLES20.glActiveTexture; import static android.opengl.GLES20.glAttachShader; import static android.opengl.GLES20.glBindTexture; import static android.opengl.GLES20.glCompileShader; import static android.opengl.GLES20.glCreateProgram; import static android.opengl.GLES20.glCreateShader; import static android.opengl.GLES20.glDrawArrays; import static android.opengl.GLES20.glEnableVertexAttribArray; import static android.opengl.GLES20.glGetAttribLocation; import static android.opengl.GLES20.glGetError; import static android.opengl.GLES20.glGetUniformLocation; import static android.opengl.GLES20.glLinkProgram; import static android.opengl.GLES20.glShaderSource; import static android.opengl.GLES20.glUniform1i; import static android.opengl.GLES20.glUniformMatrix4fv; import static android.opengl.GLES20.glUseProgram; import static android.opengl.GLES20.glVertexAttribPointer; /** * 滤镜 工具 * 参考url : [https://blog.csdn.net/lb377463323/article/details/78054892] * * @date 2019年2月12日 14:10:07 * @author ymc */ public class FilterEngine { @SuppressLint("StaticFieldLeak") private static FilterEngine filterEngine = null; private Context mContext; /** * 存放顶点的Color数组 */ private FloatBuffer mBuffer; private int mOESTextureId = -1; private int vertexShader = -1; private int fragmentShader = -1; private int mShaderProgram = -1; private int aPositionLocation = -1; private int aTextureCoordLocation = -1; private int uTextureMatrixLocation = -1; private int uTextureSamplerLocation = -1; /** * 每行前两个值为顶点坐标,后两个为纹理坐标 */ private static final float[] VERTEX_DATA = { 1f, 1f, 1f, 1f, -1f, 1f, 0f, 1f, -1f, -1f, 0f, 0f, 1f, 1f, 1f, 1f, -1f, -1f, 0f, 0f, 1f, -1f, 1f, 0f }; public static final String POSITION_ATTRIBUTE = "aPosition"; public static final String TEXTURE_COORD_ATTRIBUTE = "aTextureCoordinate"; public static final String TEXTURE_MATRIX_UNIFORM = "uTextureMatrix"; public static final String TEXTURE_SAMPLER_UNIFORM = "uTextureSampler"; public static final String COLOR_TYPE = "vColorType"; /** * 构造方法 * @param oestextureid oes id * @param context 上下文 */ public FilterEngine(int oestextureid, Context context) { mContext = context; mOESTextureId = oestextureid; mBuffer = createBuffer(VERTEX_DATA); /** * 预览相机的着色器,顶点着色器不变,需要修改片元着色器,不再用sampler2D采样, * 需要使用samplerExternalOES 纹理采样器,并且要在头部增加使用扩展纹理的声明 * #extension GL_OES_EGL_image_external : require。 */ fragmentShader = loadShader(GL_FRAGMENT_SHADER, Utils.readShaderFromResource(mContext, R.raw.base_fragment_shader)); vertexShader = loadShader(GL_VERTEX_SHADER, Utils.readShaderFromResource(mContext, R.raw.base_vertex_shader)); mShaderProgram = linkProgram(vertexShader, fragmentShader); } /** * 创建 FloatBuffer 数组 (防止内存回收) * @param vertexData float 数组 * @return FloatBuffer */ private FloatBuffer createBuffer(float[] vertexData) { FloatBuffer buffer = ByteBuffer.allocateDirect(vertexData.length * 4) .order(ByteOrder.nativeOrder()) .asFloatBuffer(); buffer.put(vertexData, 0, vertexData.length).position(0); return buffer; } /** * 加载着色器 * GL_VERTEX_SHADER 代表生成顶点着色器 * GL_FRAGMENT_SHADER 代表生成片段着色器 * * @param type 类型 * @param shaderSource shader string * @return shader */ private int loadShader(int type, String shaderSource) { int shader = glCreateShader(type); if (shader == 0) { throw new RuntimeException("Create Shader Failed!" + glGetError()); } glShaderSource(shader, shaderSource); glCompileShader(shader); return shader; } /** * 将两个Shader链接至program中 * @param verShader verShader * @param fragShader fragShader * @return program */ private int linkProgram(int verShader, int fragShader) { int program = glCreateProgram(); if (program == 0) { throw new RuntimeException("Create Program Failed!" + glGetError()); } //附着顶点和片段着色器 glAttachShader(program, verShader); glAttachShader(program, fragShader); // 绑定 program glLinkProgram(program); //告诉OpenGL ES使用此program glUseProgram(program); return program; } public void drawTexture(float[] transformMatrix) { aPositionLocation = glGetAttribLocation(mShaderProgram, FilterEngine.POSITION_ATTRIBUTE); aTextureCoordLocation = glGetAttribLocation(mShaderProgram, FilterEngine.TEXTURE_COORD_ATTRIBUTE); uTextureMatrixLocation = glGetUniformLocation(mShaderProgram, FilterEngine.TEXTURE_MATRIX_UNIFORM); uTextureSamplerLocation = glGetUniformLocation(mShaderProgram, FilterEngine.TEXTURE_SAMPLER_UNIFORM); glActiveTexture(GLES20.GL_TEXTURE0); glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mOESTextureId); glUniform1i(uTextureSamplerLocation, 0); glUniformMatrix4fv(uTextureMatrixLocation, 1, false, transformMatrix, 0); if (mBuffer != null) { mBuffer.position(0); glEnableVertexAttribArray(aPositionLocation); glVertexAttribPointer(aPositionLocation, 2, GL_FLOAT, false, 16, mBuffer); mBuffer.position(2); glEnableVertexAttribArray(aTextureCoordLocation); glVertexAttribPointer(aTextureCoordLocation, 2, GL_FLOAT, false, 16, mBuffer); glDrawArrays(GL_TRIANGLES, 0, 6); } } public int getShaderProgram() { return mShaderProgram; } public FloatBuffer getBuffer() { return mBuffer; } public int getOESTextureId() { return mOESTextureId; } public void setOESTextureId(int OESTextureId) { mOESTextureId = OESTextureId; } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/util/LabUtil.java ================================================ package camera.cn.cameramaster.util; import java.util.Arrays; /** * rgb 转 lab 工具类 * * @fileName: LabUtil * @date: 2019/4/3 13:38 * @author: ymc * @QQ:745612618 */ public class LabUtil { public static void main(String[] args) { for (int i = 0; i < 24; i++) { double[] a = AppConstant.rgbmap[i]; int fx = 0; int fx1 = -30; double rrr = a[0] + fx > 255 ? 255 :a[0] + fx; double ggg = a[1] + fx > 255 ? 255 :a[1] + fx; double bbb = a[2] + fx1 > 255 ? 255 :a[2] + fx1; double[] xyz = LabUtil.sRGB2XYZ(new double[]{rrr,ggg,bbb}); double[] lab = LabUtil.XYZ2Lab(xyz); double cha = Math.sqrt(Math.pow((lab[0] - AppConstant.labmap[i][0]), 2) + Math.pow((lab[1] - AppConstant.labmap[i][1]), 2) + Math.pow((lab[2] - AppConstant.labmap[i][2]), 2)); System.out.println(Arrays.toString(new double[]{rrr,ggg,bbb}) + " lab[]:"+ Arrays.toString(lab) + " 色差:"+cha); System.out.println("lab误差 l: "+(lab[0] - AppConstant.labmap[i][0]) + " a: "+(lab[1] - AppConstant.labmap[i][1])+" b: "+(lab[2] - AppConstant.labmap[i][2])); // Log.e("", a.toString() + " lab[]:"+lab.toString() + " 色差:"+cha); } } /** * D65 or D50 */ private static boolean hasD50 = false; /** * lab 转 xyz * * @param Lab * @return */ public static double[] Lab2XYZ(double[] Lab) { double[] XYZ = new double[3]; double L, a, b; double fx, fy, fz; double Xn, Yn, Zn; // D50 if (hasD50) { Xn = 96.42; Yn = 100; Zn = 82.51; } else { // D65 Xn = 95.04; Yn = 100; Zn = 108.89; } L = Lab[0]; a = Lab[1]; b = Lab[2]; fy = (L + 16) / 116; fx = a / 500 + fy; fz = fy - b / 200; if (fx > 0.2069) { XYZ[0] = Xn * Math.pow(fx, 3); } else { XYZ[0] = Xn * (fx - 0.1379) * 0.1284; } if ((fy > 0.2069) || (L > 8)) { XYZ[1] = Yn * Math.pow(fy, 3); } else { XYZ[1] = Yn * (fy - 0.1379) * 0.1284; } if (fz > 0.2069) { XYZ[2] = Zn * Math.pow(fz, 3); } else { XYZ[2] = Zn * (fz - 0.1379) * 0.1284; } return XYZ; } /** * xyz 转为 lab * * @param XYZ * @return */ public static double[] XYZ2Lab(double[] XYZ) { double[] Lab = new double[3]; double X, Y, Z; X = XYZ[0]; Y = XYZ[1]; Z = XYZ[2]; double Xn, Yn, Zn; if (hasD50) { // D50 Xn = 96.42; Yn = 100; Zn = 82.51; } else { // D65 Xn = 95.04; Yn = 100; Zn = 108.89; } double XXn, YYn, ZZn; XXn = X / Xn; YYn = Y / Yn; ZZn = Z / Zn; double fx, fy, fz; if (XXn > 0.008856) { fx = Math.pow(XXn, 0.333333); } else { fx = 7.787 * XXn + 0.137931; } if (YYn > 0.008856) { fy = Math.pow(YYn, 0.333333); } else { fy = 7.787 * YYn + 0.137931; } if (ZZn > 0.008856) { fz = Math.pow(ZZn, 0.333333); } else { fz = 7.787 * ZZn + 0.137931; } Lab[0] = 116 * fy - 16; Lab[1] = 500 * (fx - fy); Lab[2] = 200 * (fy - fz); return Lab; } /** * rgb 转 xyz * * @return double[] */ public static double[] sRGB2XYZ(double[] RGB) { double[] XYZ = new double[3]; double sR, sG, sB; sR = RGB[0]; sG = RGB[1]; sB = RGB[2]; sR /= 255; sG /= 255; sB /= 255; if (sR <= 0.04045) { sR = sR / 12.92; } else { sR = Math.pow(((sR + 0.055) / 1.055), 2.4); } if (sG <= 0.04045) { sG = sG / 12.92; } else { sG = Math.pow(((sG + 0.055) / 1.055), 2.4); } if (sB <= 0.04045) { sB = sB / 12.92; } else { sB = Math.pow(((sB + 0.055) / 1.055), 2.4); } if (hasD50) { // D50 XYZ[0] = 43.6052025 * sR + 38.5081593 * sG + 14.3087414 * sB; XYZ[1] = 22.2491598 * sR + 71.6886060 * sG + 6.0621486 * sB; XYZ[2] = 1.3929122 * sR + 9.7097002 * sG + 71.4185470 * sB; } else { // D65 XYZ[0] = 41.24 * sR + 35.76 * sG + 18.05 * sB; XYZ[1] = 21.26 * sR + 71.52 * sG + 7.22 * sB; XYZ[2] = 1.93 * sR + 11.92 * sG + 95.05 * sB; } return XYZ; } /** * xyz 转 rgb * * @param XYZ double[] * @return double[] */ public static double[] XYZ2sRGB(double[] XYZ) { double[] sRGB = new double[3]; double X, Y, Z; double dr = 0, dg = 0, db = 0; X = XYZ[0]; Y = XYZ[1]; Z = XYZ[2]; if (hasD50) { // TODO: 2019/4/3 D50格式暂时没有找到 D50格式 } else { dr = 0.032406 * X - 0.015371 * Y - 0.0049895 * Z; dg = -0.0096891 * X + 0.018757 * Y + 0.00041914 * Z; db = 0.00055708 * X - 0.0020401 * Y + 0.01057 * Z; } if (dr <= 0.00313) { dr = dr * 12.92; } else { dr = Math.exp(Math.log(dr) / 2.4) * 1.055 - 0.055; } if (dg <= 0.00313) { dg = dg * 12.92; } else { dg = Math.exp(Math.log(dg) / 2.4) * 1.055 - 0.055; } if (db <= 0.00313) { db = db * 12.92; } else { db = Math.exp(Math.log(db) / 2.4) * 1.055 - 0.055; } dr = dr * 255; dg = dg * 255; db = db * 255; dr = Math.min(255, dr); dg = Math.min(255, dg); db = Math.min(255, db); sRGB[0] = dr + 0.5; sRGB[1] = dg + 0.5; sRGB[2] = db + 0.5; return sRGB; } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/util/MatrixUtils.java ================================================ /* * * FastDrawerHelper.java * * Created by Wuwang on 2016/11/17 * Copyright © 2016年 深圳哎吖科技. All rights reserved. */ package camera.cn.cameramaster.util; import android.opengl.Matrix; /** * 矩阵 工具类 */ public enum MatrixUtils { ; public static final int TYPE_FITXY=0; public static final int TYPE_CENTERCROP=1; public static final int TYPE_CENTERINSIDE=2; public static final int TYPE_FITSTART=3; public static final int TYPE_FITEND=4; MatrixUtils(){ } /** * use {@link #getMatrix} instead */ @Deprecated public static void getShowMatrix(float[] matrix,int imgWidth,int imgHeight,int viewWidth,int viewHeight){ if(imgHeight>0&&imgWidth>0&&viewWidth>0&&viewHeight>0){ float sWhView=(float)viewWidth/viewHeight; float sWhImg=(float)imgWidth/imgHeight; float[] projection=new float[16]; float[] camera=new float[16]; if(sWhImg>sWhView){ Matrix.orthoM(projection,0,-sWhView/sWhImg,sWhView/sWhImg,-1,1,1,3); }else{ Matrix.orthoM(projection,0,-1,1,-sWhImg/sWhView,sWhImg/sWhView,1,3); } Matrix.setLookAtM(camera,0,0,0,1,0,0,0,0,1,0); Matrix.multiplyMM(matrix,0,projection,0,camera,0); } } public static void getMatrix(float[] matrix,int type,int imgWidth,int imgHeight,int viewWidth, int viewHeight){ if(imgHeight>0&&imgWidth>0&&viewWidth>0&&viewHeight>0){ float[] projection=new float[16]; float[] camera=new float[16]; if(type==TYPE_FITXY){ Matrix.orthoM(projection,0,-1,1,-1,1,1,3); Matrix.setLookAtM(camera,0,0,0,1,0,0,0,0,1,0); Matrix.multiplyMM(matrix,0,projection,0,camera,0); } float sWhView=(float)viewWidth/viewHeight; float sWhImg=(float)imgWidth/imgHeight; if(sWhImg>sWhView){ switch (type){ case TYPE_CENTERCROP: Matrix.orthoM(projection,0,-sWhView/sWhImg,sWhView/sWhImg,-1,1,1,3); break; case TYPE_CENTERINSIDE: Matrix.orthoM(projection,0,-1,1,-sWhImg/sWhView,sWhImg/sWhView,1,3); break; case TYPE_FITSTART: Matrix.orthoM(projection,0,-1,1,1-2*sWhImg/sWhView,1,1,3); break; case TYPE_FITEND: Matrix.orthoM(projection,0,-1,1,-1,2*sWhImg/sWhView-1,1,3); break; } }else{ switch (type){ case TYPE_CENTERCROP: Matrix.orthoM(projection,0,-1,1,-sWhImg/sWhView,sWhImg/sWhView,1,3); break; case TYPE_CENTERINSIDE: Matrix.orthoM(projection,0,-sWhView/sWhImg,sWhView/sWhImg,-1,1,1,3); break; case TYPE_FITSTART: Matrix.orthoM(projection,0,-1,2*sWhView/sWhImg-1,-1,1,1,3); break; case TYPE_FITEND: Matrix.orthoM(projection,0,1-2*sWhView/sWhImg,1,-1,1,1,3); break; } } Matrix.setLookAtM(camera,0,0,0,1,0,0,0,0,1,0); Matrix.multiplyMM(matrix,0,projection,0,camera,0); } } public static void getCenterInsideMatrix(float[] matrix,int imgWidth,int imgHeight,int viewWidth,int viewHeight){ if(imgHeight>0&&imgWidth>0&&viewWidth>0&&viewHeight>0){ float sWhView=(float)viewWidth/viewHeight; float sWhImg=(float)imgWidth/imgHeight; float[] projection=new float[16]; float[] camera=new float[16]; if(sWhImg>sWhView){ Matrix.orthoM(projection,0,-1,1,-sWhImg/sWhView,sWhImg/sWhView,1,3); }else{ Matrix.orthoM(projection,0,-sWhView/sWhImg,sWhView/sWhImg,-1,1,1,3); } Matrix.setLookAtM(camera,0,0,0,1,0,0,0,0,1,0); Matrix.multiplyMM(matrix,0,projection,0,camera,0); } } public static float[] rotate(float[] m,float angle){ Matrix.rotateM(m,0,angle,0,0,1); return m; } public static float[] flip(float[] m,boolean x,boolean y){ if(x||y){ Matrix.scaleM(m,0,x?-1:1,y?-1:1,1); } return m; } public static float[] scale(float[] m,float x,float y){ Matrix.scaleM(m,0,x,y,1); return m; } public static float[] getOriginalMatrix(){ return new float[]{ 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 }; } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/util/Utils.java ================================================ package camera.cn.cameramaster.util; import android.content.Context; import android.opengl.GLES11Ext; import android.opengl.GLES20; import android.os.Environment; import android.util.Log; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.text.SimpleDateFormat; import java.util.Date; import javax.microedition.khronos.opengles.GL10; import static android.provider.MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE; import static android.provider.MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO; /** * opengles utils * * @date 2019年2月12日 14:03:59 * @author ymc */ public class Utils { private static final String TAG = "Utils"; /** * 创建 oes id * @return oesId */ public static int createOESTextureObject() { int[] tex = new int[1]; //生成一个纹理 GLES20.glGenTextures(1, tex, 0); //将此纹理绑定到外部纹理上 GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, tex[0]); //设置纹理过滤参数 GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST); GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE); GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE); //解除纹理绑定 GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0); return tex[0]; } public static String readShaderFromResource(Context context, int resourceId) { StringBuilder builder = new StringBuilder(); InputStream is = null; InputStreamReader isr = null; BufferedReader br = null; try { is = context.getResources().openRawResource(resourceId); isr = new InputStreamReader(is); br = new BufferedReader(isr); String line; while ((line = br.readLine()) != null) { builder.append(line).append("\n"); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (is != null) { is.close(); is = null; } if (isr != null) { isr.close(); isr = null; } if (br != null) { br.close(); br = null; } } catch (IOException e) { e.printStackTrace(); } } return builder.toString(); } /** * 获取输出照片视频路径 * @param mContext 上下文 * @param mediaType 拍照视频类型 * @return 文件地址 */ public static File getOutputMediaFile(Context mContext, int mediaType) { String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); String fileName = null; File storageDir = null; if (mediaType == MEDIA_TYPE_IMAGE) { fileName = "JPEG_" + timeStamp + "_"; storageDir = mContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES); } else if (mediaType == MEDIA_TYPE_VIDEO) { fileName = "MP4_" + timeStamp + "_"; storageDir = mContext.getExternalFilesDir(Environment.DIRECTORY_MOVIES); } // Create the storage directory if it does not exist if (!storageDir.exists()) { if (!storageDir.mkdirs()) { Log.d(TAG, "failed to create directory"); return null; } } File file = null; try { file = File.createTempFile( fileName, /* prefix */ (mediaType == MEDIA_TYPE_IMAGE) ? ".jpg" : ".mp4", /* suffix */ storageDir /* directory */ ); Log.d(TAG, "getOutputMediaFile: absolutePath==" + file.getAbsolutePath()); } catch (IOException e) { e.printStackTrace(); } return file; } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/util/cameravideo/CameraHelper.java ================================================ package camera.cn.cameramaster.util.cameravideo; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.graphics.ImageFormat; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.SurfaceTexture; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraManager; import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.CaptureFailure; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.TotalCaptureResult; import android.hardware.camera2.params.MeteringRectangle; import android.hardware.camera2.params.StreamConfigurationMap; import android.media.Image; import android.media.ImageReader; import android.media.MediaRecorder; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; import android.support.annotation.RequiresApi; import android.support.v4.content.FileProvider; import android.util.Log; import android.util.Range; import android.util.Size; import android.util.SparseIntArray; import android.view.Surface; import android.view.TextureView; import android.view.View; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.SeekBar; import android.widget.TextView; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import camera.cn.cameramaster.R; import camera.cn.cameramaster.view.AutoFitTextureView; import camera.cn.cameramaster.view.AwbSeekBar; /** * 摄像头整合帮助类 * * @date 2019年5月9日 17:08:21 */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public class CameraHelper implements ICamera2, AwbSeekBar.OnAwbSeekBarChangeListener { private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); static { ORIENTATIONS.append(Surface.ROTATION_0, 90); ORIENTATIONS.append(Surface.ROTATION_90, 0); ORIENTATIONS.append(Surface.ROTATION_180, 270); ORIENTATIONS.append(Surface.ROTATION_270, 180); } /** * 淡入 淡出 动画 */ private final Animation mAlphaInAnimation; private final Animation mAlphaOutAnimation; /** * 设备旋转方向 */ private int mDeviceRotation; private int mPhotoRotation; /** * 光强 */ private float mLight; private AtomicBoolean mIsCameraOpen; private CameraManager mCameraManager; private TakePhotoListener mTakePhotoListener; private CameraReady mCameraReady; /** * 摄像头的id集合 */ private String[] mCameraIds; /** * 摄像头支持的最大size */ private Size mLargest; /** * 可缩放区域 */ private Size mZoomSize; private Size mVideoSize; private Context mContext; /** * 相机 曝光 范围 */ private Range range1; /** * 需要打开的摄像头id */ private String mCameraId; private MediaRecorder mMediaRecorder; private CaptureRequest.Builder mPreviewBuilder; private CameraDevice mCameraDevice; private CameraCaptureSession mPreviewSession; private TextureView mTextureView; /** * 后台线程 */ private HandlerThread mBackgroundThread; /** * 后台handle */ private Handler mBackgroundHandler; private AtomicBoolean mIsRecordVideo = new AtomicBoolean(); private CameraType mNowCameraType; /** * 拍照的图片读取类 */ private ImageReader mImageReader; /** * 是否支持闪光灯 */ private boolean mFlashSupported; /** * 图片的路径 */ private String mPhotoPath; /** * Orientation of the camera sensor */ private int mSensorOrientation; /** * 最大的放大倍数 */ private float mMaxZoom = 0; /** * 放大的矩阵,拍照使用 */ private Rect mZoomRect; /** * 摄像头支持的分辨率流集合 */ private StreamConfigurationMap mMap; private FlashState mNowFlashState = FlashState.CLOSE; private boolean mIsCapture = false; private CameraCharacteristics mCharacteristics; private boolean mNoAFRun = false; private boolean mIsAFRequest = false; private CameraMode CAMERA_STATE = CameraMode.TAKE_PHOTO; private Surface mPreViewSurface; private Surface mRecordSurface; private CoordinateTransformer mCoordinateTransformer; private Rect mPreviewRect; private Rect mFocusRect; /** * iso 范围 */ private Range isoRange; public Range getIsoRange() { return isoRange; } /** * 根据摄像头管理器获取一个帮助类 * * @param context 实体类 */ public CameraHelper(Context context) { this.mContext = context; mIsCameraOpen = new AtomicBoolean(false); CameraManager cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); mCameraManager = cameraManager; try { mCameraIds = mCameraManager.getCameraIdList(); } catch (CameraAccessException e) { e.printStackTrace(); } mFocusRect = new Rect(); mAlphaInAnimation = AnimationUtils.loadAnimation(mContext, R.anim.alpha_in); mAlphaOutAnimation = AnimationUtils.loadAnimation(mContext, R.anim.alpha_out); } private TextView mTextView; /** * 状态文字 赋值 * @param mTextView tv */ public void setShowTextView(TextView mTextView){ this.mTextView = mTextView; } @Override public void cameraZoom(float scale) { if (scale < 1.0f) { scale = 1.0f; } if (scale <= mMaxZoom) { int cropW = (int) ((mZoomSize.getWidth() / (mMaxZoom * 2.6)) * scale); int cropH = (int) ((mZoomSize.getHeight() / (mMaxZoom * 2.6)) * scale); Rect zoom = new Rect(cropW, cropH, mZoomSize.getWidth() - cropW, mZoomSize.getHeight() - cropH); mPreviewBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoom); mZoomRect = zoom; updatePreview(); //重复更新预览请求 } } /** * 获取最大zoom * * @return 放大数值 */ public float getMaxZoom() { return mMaxZoom; } /** * 初始化拍照的图片读取类 */ private void initImageReader() { //取最大的分辨率 Size largest = Collections.max(Arrays.asList(mMap.getOutputSizes(ImageFormat.JPEG)), new CompareSizesByArea()); mZoomSize = largest; //实例化拍照用的图片读取类 if (mImageReader != null) { mImageReader.close(); } mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), ImageFormat.JPEG, 2); } /** * 初始化一个适合的预览尺寸 */ private void initSize() { Size largest = Collections.max( Arrays.asList(mMap.getOutputSizes(ImageFormat.JPEG)), new CompareSizesByArea()); Point displaySize = new Point(); ((Activity) mContext).getWindowManager().getDefaultDisplay().getSize(displaySize); mLargest = chooseOptimalSize(mMap.getOutputSizes(SurfaceTexture.class), this.mTextureView.getWidth(), this.mTextureView.getHeight(), displaySize.x, displaySize.y, largest ); } @RequiresApi(api = Build.VERSION_CODES.M) @SuppressLint("MissingPermission") @Override public boolean openCamera(CameraType cameraType) { if (mIsCameraOpen.get()) { return true; } mIsCameraOpen.set(true); mZoomRect = null; this.mNowCameraType = cameraType; int cameraTypeId; switch (cameraType) { default: case BACK: cameraTypeId = CameraCharacteristics.LENS_FACING_BACK; break; case FRONT: cameraTypeId = CameraCharacteristics.LENS_FACING_FRONT; break; case USB: cameraTypeId = CameraCharacteristics.LENS_FACING_EXTERNAL; break; } try { for (String cameraId : mCameraIds) { mCharacteristics = mCameraManager.getCameraCharacteristics(cameraId); Integer facing = mCharacteristics.get(CameraCharacteristics.LENS_FACING); // 曝光增益 范围 range1 = mCharacteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE); //获取支持的iso范围 isoRange = mCharacteristics.get(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE); // 自动曝光模式 int[] avails = mCharacteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES); // 白平衡mode 列表 int[] aa = mCharacteristics.get(CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES); // 最大白平衡数 Integer maxAwb = mCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AWB); //获取曝光时间 范围 etr = mCharacteristics.get(CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE); Boolean awbAva = mCharacteristics.get(CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE); if (facing != null && facing != cameraTypeId) { continue; } // 获取最大 放大倍数 Float maxZoom = mCharacteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM); if (maxZoom != null) { mMaxZoom = maxZoom; } //获取摄像头支持的流配置信息 mMap = mCharacteristics .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); if (mMap == null) { return false; } //初始化拍照的图片读取类 initImageReader(); //初始化尺寸 initSize(); //获取摄像头角度 mSensorOrientation = mCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); mVideoSize = chooseVideoSize(mMap.getOutputSizes(MediaRecorder.class)); if (mTextureView != null) { ((AutoFitTextureView) mTextureView).setAspectRatio(mLargest.getHeight(), mLargest.getWidth()); } //检查是否这个摄像头是否支持闪光灯,拍照模式的时候使用 Boolean available = mCharacteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); mFlashSupported = available == null ? false : available; this.mCameraId = cameraId; mPreviewRect = new Rect(0, 0, mTextureView.getWidth(), mTextureView.getHeight()); mCoordinateTransformer = new CoordinateTransformer(mCharacteristics, new RectF(mPreviewRect)); } } catch (Exception e) { e.printStackTrace(); } return openCamera(mCameraId); } @SuppressLint("MissingPermission") private boolean openCamera(String cameraId) { try { mCameraManager.openCamera(cameraId, mStateCallback, null); } catch (CameraAccessException e) { e.printStackTrace(); return false; } return true; } @Override public void closeCamera() { Log.e("camera", "关闭摄像头"); mIsCameraOpen.set(false); closePreviewSession(); if (mCameraDevice != null) { mCameraDevice.close(); mCameraDevice = null; } if (mImageReader != null) { mImageReader.close(); mImageReader = null; } } @RequiresApi(api = Build.VERSION_CODES.M) @Override public boolean switchCamera(CameraType cameraType) { closeCamera(); return openCamera(cameraType); } @Override public boolean startPreview() { if (mBackgroundHandler == null) { return false; } try { //初始化预览的尺寸 initSize(); SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture(); surfaceTexture.setDefaultBufferSize(mLargest.getWidth(), mLargest.getHeight()); mPreViewSurface = new Surface(surfaceTexture); //创建一个预览请求 mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); //添加预览输出目标画面 mPreviewBuilder.addTarget(mPreViewSurface); if (mZoomRect != null) { //放大的矩阵 mPreviewBuilder.set(CaptureRequest.SCALER_CROP_REGION, mZoomRect); } //当前线程创建一个预览请求 mCameraDevice.createCaptureSession(Arrays.asList(mPreViewSurface, mImageReader.getSurface()), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(CameraCaptureSession session) { mPreviewSession = session; setup3AControlsLocked(mPreviewBuilder); //重复更新预览请求 updatePreview(); if (mCameraReady != null) { mCameraReady.onCameraReady(); } } @Override public void onConfigureFailed(CameraCaptureSession session) { } }, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); return false; } return true; } /** * 更新预览界面 */ private void updatePreview() { if (mCameraDevice == null) { return; } try { // mPreviewBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO); mPreviewSession.setRepeatingRequest( mPreviewBuilder.build(), null, mBackgroundHandler ); } catch (Exception e) { e.printStackTrace(); } } @Override public void resumePreview() { try { if (!mNoAFRun) { mPreviewBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL); mPreviewBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE); } if (!isLegacyLocked()) { // Tell the camera to lock focus. mPreviewBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CameraMetadata.CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL); mPreviewBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CameraMetadata.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE); } mIsAFRequest = false; mCameraState = 0; mPreviewSession.capture(mPreviewBuilder.build(), null, mBackgroundHandler); updatePreview(); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public boolean startVideoRecord(String path, int mediaType) { if (mIsRecordVideo.get()){ new Throwable("video record is recording"); } if (path == null){ new Throwable("path can not null"); } if (mediaType != MediaRecorder.OutputFormat.MPEG_4){ new Throwable("this mediaType can not support"); } if (!setVideoRecordParam(path)){ return false; } startRecordVideo(); return true; } /** * 设置录像的参数 * * @param path * @return */ private boolean setVideoRecordParam(String path) { mMediaRecorder = new MediaRecorder(); mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); mMediaRecorder.setOutputFile(path); int bitRate = mVideoSize.getWidth() * mVideoSize.getHeight(); bitRate = mVideoSize.getWidth() < 1080 ? bitRate * 2 : bitRate; mMediaRecorder.setVideoEncodingBitRate(bitRate); mMediaRecorder.setVideoFrameRate(15); mMediaRecorder.setVideoSize(mVideoSize.getWidth(), mVideoSize.getHeight()); mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); mMediaRecorder.setAudioEncodingBitRate(8000); mMediaRecorder.setAudioChannels(1); mMediaRecorder.setAudioSamplingRate(8000); mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); if (mNowCameraType == CameraType.BACK) { //后置摄像头图像要旋转90度 mMediaRecorder.setOrientationHint(90); } else { //前置摄像头图像要旋转270度 mMediaRecorder.setOrientationHint(270); } try { mMediaRecorder.prepare(); } catch (IOException e) { e.printStackTrace(); return false; } return true; } @Override public void stopVideoRecord() { if (mIsRecordVideo.get()) { mIsRecordVideo.set(false); } else { return; } mMediaRecorder.setOnErrorListener(null); mMediaRecorder.setOnInfoListener(null); mMediaRecorder.setPreviewDisplay(null); mMediaRecorder.stop(); mMediaRecorder.reset(); mMediaRecorder.release(); // startPreview(); } @Override public boolean takePhone(String path, MediaType mediaType) { this.mPhotoPath = path; setTakePhotoFlashMode(mPreviewBuilder); updatePreview(); // lockFocus(); if (!mNoAFRun) { if (mIsAFRequest) { mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO); mPreviewBuilder.set(CaptureRequest.CONTROL_AF_REGIONS, AFRegions); mPreviewBuilder.set(CaptureRequest.CONTROL_AE_REGIONS, AERegions); } mPreviewBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START); } if (!isLegacyLocked()) { // Tell the camera to lock focus. mPreviewBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CameraMetadata.CONTROL_AE_PRECAPTURE_TRIGGER_START); } mCameraState = WAITING_LOCK; if (!mFlashSupported) { capturePhoto(); } else { switch (mNowFlashState) { case CLOSE: capturePhoto(); break; case OPEN: case AUTO: mBackgroundHandler.postDelayed(new Runnable() { @Override public void run() { try { mPreviewSession.capture(mPreviewBuilder.build(), mCaptureCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } }, 800); break; default: break; } } return true; } private boolean isLegacyLocked() { return mCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY; } private void setup3AControlsLocked(CaptureRequest.Builder builder) { // Enable auto-magical 3A run by camera device builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO); Float minFocusDist = mCharacteristics.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE); // If MINIMUM_FOCUS_DISTANCE is 0, lens is fixed-focus and we need to skip the AF run. mNoAFRun = (minFocusDist == null || minFocusDist == 0); if (!mNoAFRun) { // If there is a "continuous picture" mode available, use it, otherwise default to AUTO. if (contains(mCharacteristics.get( CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES), CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)) { builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); } else { builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO); } } // If there is an auto-magical flash control mode available, use it, otherwise default to // the "on" mode, which is guaranteed to always be available. if (mNowFlashState != FlashState.CLOSE) { if (contains(mCharacteristics.get( CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES), CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH)) { builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); } else { builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON); } } // If there is an auto-magical white balance control mode available, use it. if (contains(mCharacteristics.get( CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES), CaptureRequest.CONTROL_AWB_MODE_AUTO)) { // Allow AWB to run auto-magically if this device supports this builder.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest.CONTROL_AWB_MODE_AUTO); } } /** * 真正拍照 */ private void capturePhoto() { mIsCapture = true; final CaptureRequest.Builder captureBuilder; try { //设置拍照后的回调监听 mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler); captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); captureBuilder.addTarget(mImageReader.getSurface()); //设置自动对焦 captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); // Use the same AE and AF modes as the preview. /* if(mNowFlashState != FlashState.CLOSE) { if(mFlashSupported) setup3AControlsLocked(captureBuilder); }*/ mPhotoRotation = getOrientation(mDeviceRotation); captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, mPhotoRotation); //放大的矩阵 if (mZoomRect != null) { captureBuilder.set(CaptureRequest.SCALER_CROP_REGION, mZoomRect); } setTakePhotoFlashMode(captureBuilder); captureBuilder.setTag(1); mPreviewSession.stopRepeating(); mPreviewSession.abortCaptures(); mBackgroundHandler.postDelayed(new Runnable() { @Override public void run() { try { mPreviewSession.capture(captureBuilder.build(), new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) { } @Override public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request, CaptureFailure failure) { } }, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } }, 200); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public Size getPreViewSize() { return mLargest; } @Override public void setSurface(Surface surface) { //this.mSurface = surface; } /** * 如果设置了textureView则不用设置Surface * * @param textureView textureView */ @Override public void setTextureView(TextureView textureView) { this.mTextureView = textureView; } @Override public void setTakePhotoListener(TakePhotoListener mTakePhotoListener) { this.mTakePhotoListener = mTakePhotoListener; } @Override public void setCameraReady(CameraReady cameraReady) { this.mCameraReady = cameraReady; } @Override public void flashSwitchState(FlashState mFlashState) { mNowFlashState = mFlashState; if (CAMERA_STATE == CameraMode.TAKE_PHOTO) { setTakePhotoFlashMode(mPreviewBuilder); updatePreview(); } } @Override public void setCameraState(CameraMode cameraMode) { CAMERA_STATE = cameraMode; if (CAMERA_STATE == CameraMode.TAKE_PHOTO) { setTakePhotoFlashMode(mPreviewBuilder); updatePreview(); } } private void toFocusRect(RectF rectF) { mFocusRect.left = Math.round(rectF.left); mFocusRect.top = Math.round(rectF.top); mFocusRect.right = Math.round(rectF.right); mFocusRect.bottom = Math.round(rectF.bottom); } private MeteringRectangle calcTapAreaForCamera2(int areaSize, int weight, float x, float y) { int left = clamp((int) x - areaSize / 2, mPreviewRect.left, mPreviewRect.right - areaSize); int top = clamp((int) y - areaSize / 2, mPreviewRect.top, mPreviewRect.bottom - areaSize); RectF rectF = new RectF(left, top, left + areaSize, top + areaSize); toFocusRect(mCoordinateTransformer.toCameraSpace(rectF)); return new MeteringRectangle(mFocusRect, weight); } private MeteringRectangle[] AFRegions; private MeteringRectangle[] AERegions; @Override public void requestFocus(float x, float y) { mIsAFRequest = true; MeteringRectangle rect = calcTapAreaForCamera2( mTextureView.getWidth() / 5, 1000, x, y); AFRegions = new MeteringRectangle[]{rect}; AERegions = new MeteringRectangle[]{rect}; Log.e("AFRegions", "AFRegions:" + AFRegions[0].toString()); try { final CaptureRequest.Builder mFocusBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); mFocusBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO); mFocusBuilder.set(CaptureRequest.CONTROL_AF_REGIONS, AFRegions); mFocusBuilder.set(CaptureRequest.CONTROL_AE_REGIONS, AERegions); if (mZoomRect != null) { mFocusBuilder.set(CaptureRequest.SCALER_CROP_REGION, mZoomRect); } mFocusBuilder.addTarget(mPreViewSurface); if (CAMERA_STATE == CameraMode.RECORD_VIDEO) { if (mRecordSurface != null) { mFocusBuilder.addTarget(mRecordSurface); setRecordVideoFlashMode(mFocusBuilder); } } mPreviewSession.setRepeatingRequest(mFocusBuilder.build(), null, mBackgroundHandler); // mFocusBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CameraMetadata.CONTROL_AE_PRECAPTURE_TRIGGER_START); mFocusBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START); mPreviewSession.capture(mFocusBuilder.build(), null, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } /** * 开启后台线程 */ public void startBackgroundThread() { mBackgroundThread = new HandlerThread(CameraHelper.class.getSimpleName()); mBackgroundThread.start(); mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); } /** * 停止后台进程 */ public void stopBackgroundThread() { if (mBackgroundThread != null){ mBackgroundThread.quitSafely(); } try { if (mBackgroundThread != null){ mBackgroundThread.join(); } mBackgroundThread = null; mBackgroundHandler = null; } catch (InterruptedException e) { e.printStackTrace(); } } private void setTakePhotoFlashMode(CaptureRequest.Builder builder) { if (!mFlashSupported){ return; } switch (mNowFlashState) { case CLOSE: builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON); builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF); break; case OPEN: builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH); break; case AUTO: builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); Log.e("mode", "自动闪光灯"); break; default: break; } } /** * 设置 录像闪光灯 * @param builder */ private void setRecordVideoFlashMode(CaptureRequest.Builder builder) { if (!mFlashSupported){ return; } switch (mNowFlashState) { case CLOSE: builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF); break; case OPEN: builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH); break; case AUTO: if (mLight < 10.0f) { builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH); } break; } } /** * 设置光线强度 */ public void setLight(float light) { this.mLight = light; } /** * 开始录像 */ private void startRecordVideo() { try { closePreviewSession(); //录像的时候 取最大的分辨率 mLargest = Collections.max(Arrays.asList(mMap.getOutputSizes(SurfaceTexture.class)), new CompareSizesByArea()); if (mCameraDevice == null) { return; } mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD); setRecordVideoFlashMode(mPreviewBuilder); SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture(); surfaceTexture.setDefaultBufferSize(mLargest.getWidth(), mLargest.getHeight()); /* if(mSurface != null) mSurface.release();*/ mPreViewSurface = new Surface(surfaceTexture); mPreviewBuilder.addTarget(mPreViewSurface); mRecordSurface = mMediaRecorder.getSurface(); mPreviewBuilder.addTarget(mRecordSurface); List surfaceList = new ArrayList<>(); surfaceList.add(mPreViewSurface); surfaceList.add(mRecordSurface); if (mZoomRect != null){ //放大的矩阵 mPreviewBuilder.set(CaptureRequest.SCALER_CROP_REGION, mZoomRect); } mCameraDevice.createCaptureSession(surfaceList, new CameraCaptureSession.StateCallback() { @Override public void onConfigured(CameraCaptureSession session) { mPreviewSession = session; updatePreview(); mIsRecordVideo.set(true); mMediaRecorder.start(); } @Override public void onConfigureFailed(CameraCaptureSession session) { } }, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } private void closePreviewSession() { if (mPreviewSession != null) { mPreviewSession.close(); mPreviewSession = null; } } /** * 释放资源 */ public void destroy() { //mSurface.release(); } private int getOrientation(int rotation) { return (ORIENTATIONS.get(rotation) + mSensorOrientation + 270) % 360; } /** * 设置当前相机位置 * * @param rotation 角度 */ public void setDeviceRotation(int rotation) { this.mDeviceRotation = rotation; } /** * seekBar 滑动监听事件 */ @Override public void doInProgress1() { mTextView.setText("自动"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_AUTO); updatePreview(); } @Override public void doInProgress2() { mTextView.setText("多云"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_CLOUDY_DAYLIGHT); updatePreview(); } @Override public void doInProgress3() { mTextView.setText("白天"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_DAYLIGHT); updatePreview(); } @Override public void doInProgress4() { mTextView.setText("日光灯"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_FLUORESCENT); updatePreview(); } @Override public void doInProgress5() { mTextView.setText("白炽灯"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_INCANDESCENT); updatePreview(); } @Override public void doInProgress6() { mTextView.setText("阴影"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_SHADE); updatePreview(); } @Override public void doInProgress7() { mTextView.setText("黄昏"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_TWILIGHT); updatePreview(); } @Override public void doInProgress8() { mTextView.setText("暖光"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_WARM_FLUORESCENT); updatePreview(); } @Override public void onStopTrackingTouch(int num) { switch (num) { case 0: mTextView.setText("自动"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_AUTO); break; case 10: mTextView.setText("多云"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_CLOUDY_DAYLIGHT); break; case 20: mTextView.setText("白天"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_DAYLIGHT); break; case 30: mTextView.setText("日光灯"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_FLUORESCENT); break; case 40: mTextView.setText("白炽灯"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_INCANDESCENT); break; case 50: mTextView.setText("阴影"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_SHADE); break; case 60: mTextView.setText("黄昏"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_TWILIGHT); break; case 70: mTextView.setText("暖光"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_WARM_FLUORESCENT); break; } updatePreview(); mTextView.startAnimation(mAlphaOutAnimation); mTextView.setVisibility(View.INVISIBLE); } @Override public void onStartTrackingTouch(SeekBar seekBar) { mTextView.setVisibility(View.VISIBLE); mTextView.startAnimation(mAlphaInAnimation); } /** * 异步保存照片 */ private class PhotoSaver implements Runnable { /** * 图片文件 */ private File mFile; /** * 拍照的图片 */ private Image mImage; public PhotoSaver(Image image, File file) { this.mImage = image; this.mFile = file; } @Override public void run() { ByteBuffer byteBuffer = mImage.getPlanes()[0].getBuffer(); byte[] buffer = new byte[byteBuffer.remaining()]; byteBuffer.get(buffer); FileOutputStream fileOutputStream = null; try { fileOutputStream = new FileOutputStream(mFile); fileOutputStream.write(buffer); } catch (Exception e) { e.printStackTrace(); } finally { mImage.close(); byteBuffer.clear(); if (fileOutputStream != null) { try { fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } resumePreview(); if (mTakePhotoListener != null){ mTakePhotoListener.onTakePhotoFinish(mFile, mPhotoRotation, 0, 0); } } } } private static final int WAITING_LOCK = 1; private int mCameraState = 0; private CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) { process(result); } @Override public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult) { process(partialResult); } private void process(CaptureResult result) { switch (mCameraState) { case WAITING_LOCK: boolean readyToCapture = true; if (!mNoAFRun) { Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); if (afState == null) { break; } // If auto-focus has reached locked state, we are ready to capture readyToCapture = (afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED || afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED); } // If we are running on an non-legacy device, we should also wait until // auto-exposure and auto-white-balance have converged as well before // taking a picture. if (!isLegacyLocked()) { Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); Integer awbState = result.get(CaptureResult.CONTROL_AWB_STATE); if (aeState == null || awbState == null) { break; } readyToCapture = readyToCapture && aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED && awbState == CaptureResult.CONTROL_AWB_STATE_CONVERGED; } // If we haven't finished the pre-capture sequence but have hit our maximum // wait timeout, too bad! Begin capture anyway. if (!readyToCapture) { readyToCapture = true; } if (readyToCapture) { // Capture once for each user tap of the "Picture" button. capturePhoto(); // After this, the camera will go back to the normal state of preview. mCameraState = 0; } } } }; /** * 拍照的有效回调 */ private ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() { @Override public void onImageAvailable(ImageReader reader) { if (mIsCapture) { Image image = reader.acquireNextImage(); new Thread(new PhotoSaver(image, new File(mPhotoPath))).start(); mIsCapture = false; } } }; /** * 打开摄像头状态回调 */ private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(CameraDevice camera) { mCameraDevice = camera; startPreview(); } @Override public void onDisconnected(CameraDevice camera) { camera.close(); mCameraDevice = null; } @Override public void onError(CameraDevice camera, int error) { camera.close(); mCameraDevice = null; } }; private static Size chooseVideoSize(Size[] choices) { for (Size size : choices) { if (size.getWidth() == size.getHeight() * 4 / 3 && size.getWidth() <= 1080) { return size; } } return choices[choices.length - 1]; } /** * 选择一个适合的预览尺寸,不然有一些机型不支持 * * @param choices * @param textureViewWidth * @param textureViewHeight * @param maxWidth * @param maxHeight * @param aspectRatio * @return */ private static Size chooseOptimalSize(Size[] choices, int textureViewWidth, int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) { // Collect the supported resolutions that are at least as big as the preview Surface List bigEnough = new ArrayList<>(); // Collect the supported resolutions that are smaller than the preview Surface List notBigEnough = new ArrayList<>(); int w = aspectRatio.getWidth(); int h = aspectRatio.getHeight(); for (Size option : choices) { if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight && option.getHeight() == option.getWidth() * h / w) { if (option.getWidth() >= textureViewWidth && option.getHeight() >= textureViewHeight) { bigEnough.add(option); } else { notBigEnough.add(option); } } } // Pick the smallest of those big enough. If there is no one big enough, pick the // largest of those not big enough. if (bigEnough.size() > 0) { return Collections.min(bigEnough, new CompareSizesByArea()); } else if (notBigEnough.size() > 0) { return Collections.max(notBigEnough, new CompareSizesByArea()); } else { return choices[0]; } } /** * Compares two {@code Size}s based on their areas. */ static class CompareSizesByArea implements Comparator { @Override public int compare(Size lhs, Size rhs) { // We cast here to ensure the multiplications won't overflow return Long.signum((long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight()); } } /** * Return true if the given array contains the given integer. * * @param modes array to check. * @param mode integer to get for. * @return true if the array contains the given integer, otherwise false. */ private static boolean contains(int[] modes, int mode) { if (modes == null) { return false; } for (int i : modes) { if (i == mode) { return true; } } return false; } private static int clamp(int x, int min, int max) { if (x > max) { return max; } if (x < min) { return min; } return x; } /** * 视频录像保存的路径 * * @return */ public String getVideoFilePath() { // File dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsoluteFile(); // return (dir == null ? "" : (dir.getAbsolutePath() + "/")) // + System.currentTimeMillis() + ".mp4"; return String.valueOf(new File(mContext.getExternalFilesDir(null), System.currentTimeMillis() + ".mp4")); } /** * 图片拍照的路径 * * @return 图片路径 */ public String getPhotoFilePath() { // File dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsoluteFile(); // return (dir == null ? "" : (dir.getAbsolutePath() + "/")) + System.currentTimeMillis() + ".jpeg"; return String.valueOf(new File(mContext.getExternalFilesDir(null), System.currentTimeMillis() + ".jpg")); } /** * dip 转 px * @param context 上下文 * @param dipValue dip * @return px */ public int dip2px(Context context, float dipValue) { return (int) (dipValue * context.getResources().getDisplayMetrics().density + 0.5f); } /** * 整数s转 xx:xx:xx * * @param time time * @return time */ public String secToTime(int time) { String timeStr; int hour; int minute; int second; if (time <= 0){ return "00:00"; } else { minute = time / 60; if (minute < 60) { second = time % 60; timeStr = unitFormat(minute) + ":" + unitFormat(second); } else { hour = minute / 60; if (hour > 99){ return "99:59:59"; } minute = minute % 60; second = time - hour * 3600 - minute * 60; timeStr = unitFormat(hour) + ":" + unitFormat(minute) + ":" + unitFormat(second); } } return timeStr; } /** * 格式化 时间 * @param i 时间 * @return 格式化时间 */ private String unitFormat(int i) { String retStr; if (i >= 0 && i < 10){ retStr = "0" + Integer.toString(i); }else{ retStr = "" + i; } return retStr; } /** * 根据 文件获取uri * @param context 上下文对象 * @param file 文件 * @return uri */ public Uri getUriFromFile(Context context, File file) { if (Build.VERSION.SDK_INT >= 24) { return FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", file); } else { return Uri.fromFile(file); } } public Range getRange1 (){ return range1; } /** * 设置ae 属性 */ public void setAERegions(int ae){ mPreviewBuilder.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, ae); updatePreview(); } /** * 曝光时间 */ private Range etr; public Range getEtr(){ return etr; } /** * 设置ae 属性 */ public void setAeTime(long aeTime){ mPreviewBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, aeTime); updatePreview(); } /** * 设置 mPreviewBuilder 种类 * @param key CaptureRequest * @param value v * @param */ public void setCameraBuilerMode(CaptureRequest.Key key, T value) { mPreviewBuilder.set(key, value); updatePreview(); } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/util/cameravideo/CoordinateTransformer.java ================================================ package camera.cn.cameramaster.util.cameravideo; import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.RectF; import android.hardware.camera2.CameraCharacteristics; import android.os.Build; import android.support.annotation.RequiresApi; /** * 将坐标转换为预览坐标空间和相机驱动程序坐标空间。 * @author ymc */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public class CoordinateTransformer { private final Matrix mPreviewToCameraTransform; private RectF mDriverRectF; /** * 将矩形转换为相机坐标或从相机坐标转换并预览坐标空间。 * @param chr camera characteristics * @param previewRect the preview rectangle size and position. */ public CoordinateTransformer(CameraCharacteristics chr, RectF previewRect) { if (!hasNonZeroArea(previewRect)) { throw new IllegalArgumentException("previewRect"); } Rect rect = chr.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); Integer sensorOrientation = chr.get(CameraCharacteristics.SENSOR_ORIENTATION); int rotation = sensorOrientation == null ? 90 : sensorOrientation; mDriverRectF = new RectF(rect); Integer face = chr.get(CameraCharacteristics.LENS_FACING); boolean mirrorX = face != null && face == CameraCharacteristics.LENS_FACING_FRONT; mPreviewToCameraTransform = previewToCameraTransform(mirrorX, rotation, previewRect); } /** * 将预览视图空间中的矩形转换为新的矩形相机视图空间。 * * @param source the rectangle in preview view space * @return the rectangle in camera view space. */ public RectF toCameraSpace(RectF source) { RectF result = new RectF(); mPreviewToCameraTransform.mapRect(result, source); return result; } private Matrix previewToCameraTransform(boolean mirrorX, int sensorOrientation, RectF previewRect) { Matrix transform = new Matrix(); // Need mirror for front camera. transform.setScale(mirrorX ? -1 : 1, 1); // Because preview orientation is different form sensor orientation, // rotate to same orientation, Counterclockwise. transform.postRotate(-sensorOrientation); // Map rotated matrix to preview rect transform.mapRect(previewRect); // Map preview coordinates to driver coordinates Matrix fill = new Matrix(); fill.setRectToRect(previewRect, mDriverRectF, Matrix.ScaleToFit.FILL); // Concat the previous transform on top of the fill behavior. transform.setConcat(fill, transform); // finally get transform matrix return transform; } private boolean hasNonZeroArea(RectF rect) { return rect.width() != 0 && rect.height() != 0; } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/util/cameravideo/ICamera2.java ================================================ package camera.cn.cameramaster.util.cameravideo; import android.util.Size; import android.view.Surface; import android.view.TextureView; import java.io.File; /** * 相机 接口类 */ public interface ICamera2 { /** * 缩放 * @param zoom 缩放比例 */ void cameraZoom(float zoom); /** * 打开摄像头 */ boolean openCamera(CameraType cameraType); /** * 关闭摄像头 */ void closeCamera(); /** * 切换摄像头 * @param cameraType 切换摄像头类型 * @return boolean 是否切换成功 */ boolean switchCamera(CameraType cameraType); /** * 开启相机的预览模式 */ boolean startPreview(); /** * 停止预览 */ void resumePreview(); /** * 开始视频的录制 * @param path 存储路径 * @param mediaType 文件类型 */ boolean startVideoRecord(String path, int mediaType); /** * 停止视频录制 */ void stopVideoRecord(); /** * 拍照 * @param path 存储路径 * @param mediaType 文件类型 */ boolean takePhone(String path, MediaType mediaType); /** * 获取预览大小 * @return */ Size getPreViewSize(); void setSurface(Surface surface); void setTextureView(TextureView textureView); void setTakePhotoListener(TakePhotoListener mTakePhotoListener); void setCameraReady(CameraReady mCameraReady); void flashSwitchState(FlashState mFlashState); void setCameraState(CameraMode cameraMode); /** * 手动请求对焦 * @param x * @param y */ void requestFocus(float x, float y); /** * 摄像头模式类型 */ enum CameraMode { RECORD_VIDEO, TAKE_PHOTO } /** * 当前只有mp4类型 */ enum MediaType { MP4, JPEG, } /** * 摄像头类型 */ enum CameraType { FRONT, BACK, USB } /** * 灯光状态 */ enum FlashState { CLOSE, AUTO, OPEN } interface TakePhotoListener{ void onTakePhotoFinish(File file, int photoRotation, int width, int height); } interface CameraReady { void onCameraReady(); } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/util/cameravideo/IVideoControl.java ================================================ package camera.cn.cameramaster.util.cameravideo; /** * 视频接口 */ public interface IVideoControl { /** * 播放视频 */ void play(); /** * 暂停视频播放 */ void pause(); /** * 继续播放视频,前提是暂停了视频 */ void resume(); /** * 停止播放视频 */ void stop(); /** * 进度条快进 * @param timeStamp */ void seekTo(int timeStamp); void setPlaySeekTimeListener(PlaySeekTimeListener mPlaySeekTimeListener); void setPlayStateListener(PlayStateListener mPlayStateListener); interface PlaySeekTimeListener { void onSeekTime(int allTime, int time); } /** * 播放状态 */ interface PlayStateListener{ void onStartListener(int width, int height); void onCompletionListener(); } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/util/cameravideo/VideoPlayer.java ================================================ package camera.cn.cameramaster.util.cameravideo; import android.content.Context; import android.media.MediaPlayer; import android.net.Uri; import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.support.annotation.RequiresApi; import android.view.Surface; import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; /** * 视频播放器 * * @author ymc */ public class VideoPlayer implements IVideoControl { private MediaPlayer mMediaPlayer; private Surface mSurface; private String mVideoFilePath; private IVideoControl.PlaySeekTimeListener mPlaySeekTimeListener; private IVideoControl.PlayStateListener mPlayStateListener; private HandlerThread mHandlerThread; private Handler mHandle; private AtomicBoolean mIsNowSeekTime; private boolean mIsLoop = false; public VideoPlayer() { mMediaPlayer = new MediaPlayer(); mHandlerThread = new HandlerThread(VideoPlayer.class.getSimpleName()); mHandlerThread.start(); mHandle = new Handler(mHandlerThread.getLooper()); mIsNowSeekTime = new AtomicBoolean(false); } public void setLoopPlay(boolean isLoop) { // mMediaPlayer.setLooping(isLoop); this.mIsLoop = isLoop; } public void setDataSourceAndPlay(Context context, Uri uri) { try { mMediaPlayer.setDataSource(context,uri); mMediaPlayer.setSurface(this.mSurface); mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { @Override public boolean onError(MediaPlayer mp, int what, int extra) { stopVideoSeekTime(); return false; } }); mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { play(); startVideoSeekTime(); } }); mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { if(mPlayStateListener != null){ mPlayStateListener.onCompletionListener(); } if(mIsLoop){ play(); } } }); mMediaPlayer.prepareAsync(); } catch (IOException e) { e.printStackTrace(); mMediaPlayer = null; } } public void setDataSourceAndPlay(String path) { mVideoFilePath = path; try { mMediaPlayer.setDataSource(mVideoFilePath); mMediaPlayer.setSurface(this.mSurface); mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { @Override public boolean onError(MediaPlayer mp, int what, int extra) { stopVideoSeekTime(); return false; } }); mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { play(); startVideoSeekTime(); } }); mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { if(mPlayStateListener != null){ mPlayStateListener.onCompletionListener(); } if(mIsLoop){ play(); } } }); mMediaPlayer.prepareAsync(); } catch (IOException e) { e.printStackTrace(); mMediaPlayer = null; } } @Override public void play() { if(mMediaPlayer != null && !mMediaPlayer.isPlaying()) { mMediaPlayer.start(); if(mPlayStateListener != null){ mPlayStateListener.onStartListener(mMediaPlayer.getVideoWidth(), mMediaPlayer.getVideoHeight()); } } } @Override public void pause() { if(mMediaPlayer != null && mMediaPlayer.isPlaying()){ mMediaPlayer.pause(); } } @Override public void resume() { if(mMediaPlayer != null && !mMediaPlayer.isPlaying()){ mMediaPlayer.start(); } } @Override public void stop() { if(mMediaPlayer != null) { mMediaPlayer.stop(); mMediaPlayer.reset(); stopVideoSeekTime(); } } @Override public void seekTo(int timeStamp) { if(mMediaPlayer != null){ mMediaPlayer.seekTo(timeStamp); } } @Override public void setPlaySeekTimeListener(IVideoControl.PlaySeekTimeListener playSeekTimeListener) { this.mPlaySeekTimeListener = playSeekTimeListener; } @Override public void setPlayStateListener(PlayStateListener playStateListener) { this.mPlayStateListener = playStateListener; } public void setVideoPlayWindow(Surface surface) { this.mSurface = surface; } public boolean isPlaying(){ return mMediaPlayer!=null && mMediaPlayer.isPlaying(); } /** * 视频播放进度 */ private void startVideoSeekTime() { if(mPlaySeekTimeListener == null){ return; } mIsNowSeekTime.set(true); mHandle.post(new Runnable() { @Override public void run() { while (mIsNowSeekTime.get()) { try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } if (mMediaPlayer == null){ return; } synchronized (mMediaPlayer) { if (mMediaPlayer == null){ return; } mPlaySeekTimeListener.onSeekTime(mMediaPlayer.getDuration(), mMediaPlayer.getCurrentPosition()); } } } }); } private void stopVideoSeekTime() { mIsNowSeekTime.set(false); } @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) public void destroy() { mHandlerThread.quitSafely(); if(mMediaPlayer != null) { synchronized (mMediaPlayer) { mMediaPlayer.release(); mMediaPlayer = null; } } mSurface.release(); } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/util/render/CameraV2Renderer.java ================================================ package camera.cn.cameramaster.util.render; import android.content.Context; import android.graphics.Bitmap; import android.graphics.SurfaceTexture; import android.opengl.GLES11Ext; import android.opengl.GLES20; import android.opengl.GLSurfaceView; import android.os.Build; import android.support.annotation.RequiresApi; import android.util.Log; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.nio.FloatBuffer; import java.nio.IntBuffer; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import camera.cn.cameramaster.util.CameraV2; import camera.cn.cameramaster.util.FilterEngine; import camera.cn.cameramaster.util.Utils; import camera.cn.cameramaster.view.CameraV2GLSurfaceView; import static android.opengl.GLES11Ext.GL_TEXTURE_EXTERNAL_OES; import static android.opengl.GLES20.GL_FLOAT; import static android.opengl.GLES20.GL_FRAMEBUFFER; import static android.opengl.GLES20.GL_TRIANGLES; import static android.opengl.GLES20.glActiveTexture; import static android.opengl.GLES20.glBindFramebuffer; import static android.opengl.GLES20.glBindTexture; import static android.opengl.GLES20.glClearColor; import static android.opengl.GLES20.glDrawArrays; import static android.opengl.GLES20.glEnableVertexAttribArray; import static android.opengl.GLES20.glGenFramebuffers; import static android.opengl.GLES20.glGetAttribLocation; import static android.opengl.GLES20.glGetUniformLocation; import static android.opengl.GLES20.glUniform1i; import static android.opengl.GLES20.glUniformMatrix4fv; import static android.opengl.GLES20.glVertexAttribPointer; import static android.opengl.GLES20.glViewport; /** * camera render * 参考url : [https://blog.csdn.net/lb377463323/article/details/78054892] * * @author ymc * @date 2019年2月12日 13:39:55 */ public class CameraV2Renderer implements GLSurfaceView.Renderer { private static final String TAG = "CameraV2Renderer"; private int surfaceWidth; private int surfaceHeight; private Context mContext; private CameraV2GLSurfaceView mCameraV2GLSurfaceView; private CameraV2 mCamera; private boolean bIsPreviewStarted; private int mOESTextureId = -1; private SurfaceTexture mSurfaceTexture; private float[] transformMatrix = new float[16]; /** * 存放顶点的Color数组 */ private FloatBuffer mDataBuffer; private int mShaderProgram = -1; private int aPositionLocation = -1; private int aTextureCoordLocation = -1; private int uTextureMatrixLocation = -1; private int uTextureSamplerLocation = -1; private int uColorType = -1; private int[] mFBOIds = new int[1]; private int[] fFrame = new int[1]; private int[] fTexture = new int[1]; float[] arrays1 ={1.0f,2.0f,3.0f,4.0f}; float[] arrays2 ={4,5,6}; float[] arrays3 ={7,8,9}; int arraysSize = 4; private int hChangeColor = -1; private int hChangeColor2 = -1; private int hChangeColor3 = -1; private int hArraySize = -1; public void init(CameraV2GLSurfaceView surfaceView, CameraV2 camera, boolean isPreviewStarted, Context context) { mContext = context; mCameraV2GLSurfaceView = surfaceView; mCamera = camera; bIsPreviewStarted = isPreviewStarted; } /** * GLSurfaceView 创建 * * @param gl GL10 * @param config EGLConfig */ @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { mOESTextureId = Utils.createOESTextureObject(); FilterEngine mFilterEngine = new FilterEngine(mOESTextureId, mContext); mDataBuffer = mFilterEngine.getBuffer(); mShaderProgram = mFilterEngine.getShaderProgram(); glGenFramebuffers(1, mFBOIds, 0); glBindFramebuffer(GL_FRAMEBUFFER, mFBOIds[0]); uColorType = glGetUniformLocation(mShaderProgram, FilterEngine.COLOR_TYPE); hChangeColor = GLES20.glGetUniformLocation(mShaderProgram, "vChangeColor"); hChangeColor2 = GLES20.glGetUniformLocation(mShaderProgram, "vChangeColorB"); hChangeColor3 = GLES20.glGetUniformLocation(mShaderProgram, "vChangeColorC"); hArraySize = GLES20.glGetUniformLocation(mShaderProgram, "vArraysSize"); } /** * GLSurfaceView 改变 * @param gl GL10 * @param width 宽度 * @param height 长度 */ @Override public void onSurfaceChanged(GL10 gl, int width, int height) { glViewport(0, 0, width, height); surfaceWidth = width; surfaceHeight = height; } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override public void onDrawFrame(GL10 gl) { Long t1 = System.currentTimeMillis(); if (mSurfaceTexture != null) { //更新纹理图像 mSurfaceTexture.updateTexImage(); //获取外部纹理的矩阵,用来确定纹理的采样位置,没有此矩阵可能导致图像翻转等问题 mSurfaceTexture.getTransformMatrix(transformMatrix); } if (!bIsPreviewStarted) { // 创建 SurfaceTexture bIsPreviewStarted = initSurfaceTexture(); bIsPreviewStarted = true; return; } //glClear(GL_COLOR_BUFFER_BIT); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); //获取Shader中定义的变量在program中的位置 aPositionLocation = glGetAttribLocation(mShaderProgram, FilterEngine.POSITION_ATTRIBUTE); aTextureCoordLocation = glGetAttribLocation(mShaderProgram, FilterEngine.TEXTURE_COORD_ATTRIBUTE); uTextureMatrixLocation = glGetUniformLocation(mShaderProgram, FilterEngine.TEXTURE_MATRIX_UNIFORM); // 激活纹理单位 glActiveTexture(GL_TEXTURE_EXTERNAL_OES); // 绑定外部纹理到纹理单元0 glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mOESTextureId); //将此纹理单元床位片段着色器的uTextureSampler外部纹理采样器 glUniform1i(uTextureSamplerLocation, 0); glUniform1i(uColorType,1); glUniform1i(hArraySize,1); GLES20.glUniform3fv(hChangeColor,1, arrays1,0); GLES20.glUniform3fv(hChangeColor2,1, arrays2,0); GLES20.glUniform3fv(hChangeColor3,1, arrays3,0); //将纹理矩阵传给片段着色器 glUniformMatrix4fv(uTextureMatrixLocation, 1, false, transformMatrix, 0); if (mDataBuffer != null) { //顶点坐标从位置0开始读取 mDataBuffer.position(0); glEnableVertexAttribArray(aPositionLocation); glVertexAttribPointer(aPositionLocation, 2, GL_FLOAT, false, 16, mDataBuffer); //纹理坐标从位置2开始读取 mDataBuffer.position(2); glEnableVertexAttribArray(aTextureCoordLocation); glVertexAttribPointer(aTextureCoordLocation, 2, GL_FLOAT, false, 16, mDataBuffer); } //glDrawElements(GL_TRIANGLE_FAN, 6,GL_UNSIGNED_INT, 0); //glDrawArrays(GL_TRIANGLE_FAN, 0 , 6); //绘制两个三角形(6个顶点) glDrawArrays(GL_TRIANGLES, 0, 6); //glDrawArrays(GL_TRIANGLES, 3, 3); glBindFramebuffer(GL_FRAMEBUFFER, 0); /** * 根据标识 是否截图 * 参考url: [http://hounychang.github.io/2015/05/13/%E5%AF%B9GLSurfaceView%E6%88%AA%E5%9B%BE/] */ if (CameraV2GLSurfaceView.shouldTakePic) { CameraV2GLSurfaceView.shouldTakePic = false; // bindfbo(); int w = surfaceWidth; int h = surfaceHeight; int b[] = new int[w * h]; int bt[] = new int[w * h]; IntBuffer buffer = IntBuffer.wrap(b); buffer.position(0); GLES20.glReadPixels(0, 0, w, h, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buffer); for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { int pix = b[i * w + j]; int pb = (pix >> 16) & 0xff; int pr = (pix << 16) & 0x00ff0000; int pix1 = (pix & 0xff00ff00) | pr | pb; bt[(h - i - 1) * w + j] = pix1; } } Bitmap inBitmap; inBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); //为了图像能小一点,使用了RGB_565而不是ARGB_8888 inBitmap.copyPixelsFromBuffer(buffer); inBitmap = Bitmap.createBitmap(bt, w, h, Bitmap.Config.ARGB_8888); ByteArrayOutputStream bos = new ByteArrayOutputStream(); inBitmap.compress(Bitmap.CompressFormat.PNG, 90, bos); byte[] bitmapData = bos.toByteArray(); ByteArrayInputStream fis = new ByteArrayInputStream(bitmapData); File mFile = new File(mContext.getExternalFilesDir(null), "pic1.png"); try { FileOutputStream fos = new FileOutputStream(mFile); byte[] buf = new byte[1024]; int len; while ((len = fis.read(buf)) > 0) { fos.write(buf, 0, len); } fis.close(); fos.close(); } catch (IOException e) { e.printStackTrace(); } finally { //旋转角度 // int rotate = BitmapRotating.readPictureDegree(mFile.getPath()); // BitmapRotating.rotaingImageView(rotate,inBitmap); inBitmap.recycle(); // unbindfbo(); } } long t2 = System.currentTimeMillis(); long t = t2 - t1; Log.i(TAG, "onDrawFrame: time: " + t); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private boolean initSurfaceTexture() { if (mCamera == null || mCameraV2GLSurfaceView == null) { Log.i(TAG, "mCamera or mGLSurfaceView is null!"); return false; } // 根据 oesId 创建 SurfaceTexture mSurfaceTexture = new SurfaceTexture(mOESTextureId); mSurfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() { @Override public void onFrameAvailable(SurfaceTexture surfaceTexture) { // 每获取到一帧数据时请求OpenGL ES进行渲染 mCameraV2GLSurfaceView.requestRender(); } }); //讲此SurfaceTexture作为相机预览输出 (相互绑定) mCamera.setPreviewTexture(mSurfaceTexture); mCamera.createCameraPreviewSession(); return true; } /** * 改变截图时候界面卡顿的现象 * 参考url : [https://blog.csdn.net/SXH_Android/article/details/78835966] *

* 问题:加上后确实没有卡顿现象 但是截图会是 黑屏 */ private void bindfbo() { GLES20.glGenFramebuffers(1, fFrame, 0); GLES20.glGenTextures(1, fTexture, 0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, fTexture[0]); GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, surfaceWidth, surfaceHeight, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); GLES20.glBindFramebuffer(GL_FRAMEBUFFER, fFrame[0]); GLES20.glFramebufferTexture2D(GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, fTexture[0], 0); int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER); if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) { throw new RuntimeException("status:" + status + ", hex:" + Integer.toHexString(status)); } } private void unbindfbo() { GLES20.glBindFramebuffer(GL_FRAMEBUFFER, 0); GLES20.glDeleteTextures(1, fTexture, 0); GLES20.glDeleteFramebuffers(1, fFrame, 0); } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/view/AnimationTextView.java ================================================ package camera.cn.cameramaster.view; import android.annotation.SuppressLint; import android.content.Context; import android.os.Build; import android.os.Handler; import android.support.annotation.RequiresApi; import android.util.AttributeSet; import android.view.animation.Animation; import android.widget.TextView; /** * 文字显示 一秒后消失 */ @SuppressLint("AppCompatCustomView") public class AnimationTextView extends TextView { private Handler mMainHandler; private Animation mAnimation; /** * 防止又换了个text,但是上次哪个还没有消失即将小时就把新的text的给消失了 */ public int mTimes = 0; public AnimationTextView(Context context) { super(context); } public AnimationTextView(Context context, AttributeSet attrs) { super(context, attrs); } public AnimationTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public AnimationTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } public void setmMainHandler(Handler mMainHandler) { this.mMainHandler = mMainHandler; } public void setmAnimation(Animation mAnimation) { this.mAnimation = mAnimation; } public void start(String text, int message) { if (mAnimation == null || mMainHandler == null) { return; } this.setVisibility(VISIBLE); mTimes++; this.setText(text); this.startAnimation(mAnimation); new Thread(new SleepThread(mMainHandler, message, 1000, Integer.valueOf(mTimes))).start(); } public void stop() { this.setVisibility(GONE); } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/view/AutoFitTextureView.java ================================================ package camera.cn.cameramaster.view; import android.content.Context; import android.graphics.Matrix; import android.util.AttributeSet; import android.view.TextureView; /** * 自定义 TextureView * 重新计算预览宽高 * * @fileName: AutoFitTextureView * @date: 2019/1/28 17:00 * @author: ymc * @QQ:745612618 */ public class AutoFitTextureView extends TextureView { private int mRatioWidth = 0; private int mRatioHeight = 0; public AutoFitTextureView(Context context) { this(context, null); } public AutoFitTextureView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public AutoFitTextureView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } /** * 设置此视图的纵横比。将根据比率测量视图的大小 根据参数计算。注意,参数的实际大小无关紧要 * 调用setAspectRatio(2,3)和setAspectRatio(4,6)会产生相同的结果。 * * @param width Relative horizontal size * @param height Relative vertical size */ public void setAspectRatio(int width, int height) { if (width < 0 || height < 0) { throw new IllegalArgumentException("Size cannot be negative."); } mRatioWidth = width; mRatioHeight = height; float mRatio = (float) mRatioWidth / (float) mRatioHeight; //算出相机的缩放比例 float w = mRatio * getHeight(); float scale; if (w > getWidth()) scale = w / (float) getWidth(); else scale = (float) getWidth() / w; Matrix matrix = new Matrix(); matrix.postScale(scale, 1, getWidth() / 2, getHeight() / 2); setTransform(matrix); } /** * 视频宽度适配 * @param width * @param height */ public void setVideoAspectRatio(int width, int height) { if (width < 0 || height < 0) { throw new IllegalArgumentException("Size cannot be negative."); } mRatioWidth = width; mRatioHeight = height; float mRatio = (float) mRatioWidth / (float) mRatioHeight; //算出相机的缩放比例 if(mRatio < 1.0) { setAspectRatio(width, height); }else { float h = getWidth() / mRatio; float scale; if (h > getHeight()) scale = (float) getHeight() / h; else scale = h / (float) getHeight(); Matrix matrix = new Matrix(); matrix.postScale(1, scale, getWidth() / 2, getHeight() / 2); setTransform(matrix); } } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/view/AutoLocateHorizontalView.java ================================================ package camera.cn.cameramaster.view; import android.content.Context; import android.support.annotation.Nullable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.Scroller; /** * 横向recyclerView * * @author ymc */ public class AutoLocateHorizontalView extends RecyclerView { /** * 一个屏幕中显示多少个item,必须为奇数 */ private int itemCount = 5; /** * 初始时选中的位置 */ private int initPos = 0; private int deltaX; private WrapperAdapter wrapAdapter; private Adapter adapter; private LinearLayoutManager linearLayoutManager; private boolean isInit; private OnSelectedPositionChangedListener listener; /** * 刚初始化时是否触发位置改变的监听 */ private boolean isFirstPosChanged = true; /** * 记录上次选中的位置 */ private int oldSelectedPos = initPos; /** * 当前被选中的位置 */ private int selectPos = initPos; private Scroller mScroller; /** * 当要调用moveToPosition()方法时要先记录已经移动了多少位置 */ private int oldMoveX; private boolean isMoveFinished = true; public AutoLocateHorizontalView(Context context) { super(context); } public AutoLocateHorizontalView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } public AutoLocateHorizontalView(Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } private void init() { mScroller = new Scroller(getContext()); getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { if (isInit) { if (initPos >= adapter.getItemCount()) { initPos = adapter.getItemCount() - 1; } if (isFirstPosChanged && listener != null) { listener.selectedPositionChanged(initPos); } linearLayoutManager.scrollToPositionWithOffset(0, -initPos * (wrapAdapter.getItemWidth())); isInit = false; } } }); } /** * 设置初始化时选中的位置,该方法必须在 * {@link AutoLocateHorizontalView#setAdapter(android.support.v7.widget.RecyclerView.Adapter) } * 之前调用 * * @param initPos 初始位置,如果位置超过了item的数量则默认选中最后一项item */ public void setInitPos(int initPos) { if (adapter != null) { throw new RuntimeException("This method should be called before setAdapter()!"); } this.initPos = initPos; selectPos = initPos; oldSelectedPos = initPos; } /** * 设置每次显示多少个item,该方法必须在{@link AutoLocateHorizontalView#setAdapter(android.support.v7.widget.RecyclerView.Adapter) }之前调用 * * @param itemCount 必须为奇数,否则默认会设置成小于它的最大奇数 */ public void setItemCount(int itemCount) { if (adapter != null) { throw new RuntimeException("This method should be called before setAdapter()!"); } if (itemCount % 2 == 0) { this.itemCount = itemCount - 1; } else { this.itemCount = itemCount; } } /** * 删除item后偏移距离可能需要重新计算,从而保证selectPos的正确 * * @param adapter */ private void correctDeltax(Adapter adapter) { if (adapter.getItemCount() <= selectPos) { deltaX -= wrapAdapter.getItemWidth() * (selectPos - adapter.getItemCount() + 1); } calculateSelectedPos(); } /** * 删除时选中的数据发生改变,要重新回调方法 * * @param startPos */ private void reCallListenerWhenRemove(int startPos) { if (startPos <= selectPos && listener != null) { correctDeltax(adapter); listener.selectedPositionChanged(selectPos); } else { correctDeltax(adapter); } } /** * 添加数据时选中的数据发生改变,要重新回调方法 * * @param startPos */ private void reCallListenerWhenAdd(int startPos) { if (startPos <= selectPos && listener != null) { listener.selectedPositionChanged(selectPos); } } /** * 当使用整体刷新时要重新回调方法 */ private void reCallListenerWhenChanged() { if (listener != null) { listener.selectedPositionChanged(selectPos); } } @Override public void setAdapter(final Adapter adapter) { this.adapter = adapter; this.wrapAdapter = new WrapperAdapter(adapter, getContext(), itemCount); adapter.registerAdapterDataObserver(new AdapterDataObserver() { @Override public void onChanged() { super.onChanged(); wrapAdapter.notifyDataSetChanged(); reCallListenerWhenChanged(); } @Override public void onItemRangeInserted(int positionStart, int itemCount) { wrapAdapter.notifyDataSetChanged(); reCallListenerWhenAdd(positionStart); } @Override public void onItemRangeRemoved(int positionStart, int itemCount) { wrapAdapter.notifyDataSetChanged(); reCallListenerWhenRemove(positionStart); } }); deltaX = 0; if (linearLayoutManager == null) { linearLayoutManager = new LinearLayoutManager(getContext()); } linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); super.setLayoutManager(linearLayoutManager); super.setAdapter(this.wrapAdapter); isInit = true; } @Override public void setLayoutManager(LayoutManager layout) { if (!(layout instanceof LinearLayoutManager)) { throw new IllegalStateException("The LayoutManager here must be LinearLayoutManager!"); } this.linearLayoutManager = (LinearLayoutManager) layout; } @Override public void onScrollStateChanged(int state) { super.onScrollStateChanged(state); if (state == SCROLL_STATE_IDLE) { if (wrapAdapter == null) { return; } int itemWidth = wrapAdapter.getItemWidth(); int headerFooterWidth = wrapAdapter.getHeaderFooterWidth(); if (itemWidth == 0 || headerFooterWidth == 0) { //此时adapter还没有准备好,忽略此次调用 return; } //超出上个item的位置 int overLastPosOffset = deltaX % itemWidth; if (overLastPosOffset == 0) { //刚好处于一个item选中位置,无需滑动偏移纠正 } else if (Math.abs(overLastPosOffset) <= itemWidth / 2) { scrollBy(-overLastPosOffset, 0); } else if (overLastPosOffset > 0) { scrollBy((itemWidth - overLastPosOffset), 0); } else { scrollBy(-(itemWidth + overLastPosOffset), 0); } calculateSelectedPos(); //此处通知刷新是为了重新绘制之前被选中的位置以及刚刚被选中的位置 wrapAdapter.notifyItemChanged(oldSelectedPos + 1); wrapAdapter.notifyItemChanged(selectPos + 1); oldSelectedPos = selectPos; if (listener != null) { listener.selectedPositionChanged(selectPos); } } } public void moveToPosition(int position) { if(position < 0 || position > adapter.getItemCount() - 1){ throw new IllegalArgumentException("Your position should be from 0 to "+(adapter.getItemCount()-1)); } oldMoveX = 0; isMoveFinished = false; int itemWidth = wrapAdapter.getItemWidth(); if (position != selectPos) { int deltx = (position - selectPos) * itemWidth; mScroller.startScroll(getScrollX(), getScrollY(), deltx, 0); postInvalidate(); } } @Override public void computeScroll() { super.computeScroll(); if (mScroller.computeScrollOffset()) { int x = mScroller.getCurrX() - oldMoveX; oldMoveX += x; scrollBy(x, 0); } else if (mScroller.isFinished()) { //此处通知刷新是为了重新绘制之前被选中的位置以及刚刚被选中的位置 if (isMoveFinished) { return; } wrapAdapter.notifyItemChanged(oldSelectedPos + 1); wrapAdapter.notifyItemChanged(selectPos + 1); oldSelectedPos = selectPos; if (listener != null) { listener.selectedPositionChanged(selectPos); } isMoveFinished = true; } } @Override public void onScrolled(int dx, int dy) { super.onScrolled(dx, dy); deltaX += dx; calculateSelectedPos(); } private void calculateSelectedPos() { int itemWidth = wrapAdapter.getItemWidth(); if (deltaX > 0) { selectPos = (deltaX) / itemWidth + initPos; } else { selectPos = initPos + (deltaX) / itemWidth; } } class WrapperAdapter extends RecyclerView.Adapter { private Context context; private RecyclerView.Adapter adapter; private int itemCount; private static final int HEADER_FOOTER_TYPE = -1; private View itemView; /** * 头部或尾部的宽度 */ private int headerFooterWidth; /** * 每个item的宽度 */ private int itemWidth; public WrapperAdapter(Adapter adapter, Context context, int itemCount) { this.adapter = adapter; this.context = context; this.itemCount = itemCount; if (adapter instanceof IAutoLocateHorizontalView) { itemView = ((IAutoLocateHorizontalView) adapter).getItemView(); } else { throw new RuntimeException(adapter.getClass().getSimpleName() + " should implements com.jianglei.view.AutoLocateHorizontalView.IAutoLocateHorizontalView !"); } } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == HEADER_FOOTER_TYPE) { View view = new View(context); headerFooterWidth = parent.getMeasuredWidth() / 2 - (parent.getMeasuredWidth() / itemCount) / 2; RecyclerView.LayoutParams params = new LayoutParams(headerFooterWidth, ViewGroup.LayoutParams.MATCH_PARENT); view.setLayoutParams(params); return new HeaderFooterViewHolder(view); } ViewHolder holder = adapter.onCreateViewHolder(parent, viewType); itemView = ((IAutoLocateHorizontalView) adapter).getItemView(); int width = parent.getMeasuredWidth() / itemCount; ViewGroup.LayoutParams params = itemView.getLayoutParams(); if (params != null) { params.width = width; itemWidth = width; itemView.setLayoutParams(params); } return holder; } @SuppressWarnings("unchecked") @Override public void onBindViewHolder(ViewHolder holder, int position) { if (!isHeaderOrFooter(position)) { adapter.onBindViewHolder(holder, position - 1); if (selectPos == position - 1) { ((IAutoLocateHorizontalView) adapter).onViewSelected(true, position - 1, holder, itemWidth); } else { ((IAutoLocateHorizontalView) adapter).onViewSelected(false, position - 1, holder, itemWidth); } } } @Override public int getItemCount() { return adapter.getItemCount() + 2; } @Override public int getItemViewType(int position) { if (position == 0 || position == getItemCount() - 1) { return HEADER_FOOTER_TYPE; } return adapter.getItemViewType(position - 1); } private boolean isHeaderOrFooter(int pos) { if (pos == 0 || pos == getItemCount() - 1) { return true; } return false; } public int getHeaderFooterWidth() { return headerFooterWidth; } public int getItemWidth() { return itemWidth; } class HeaderFooterViewHolder extends RecyclerView.ViewHolder { HeaderFooterViewHolder(View itemView) { super(itemView); } } } public interface IAutoLocateHorizontalView { /** * 获取item的根布局 */ View getItemView(); /** * 当item被选中时会触发这个回调,可以修改被选中时的样式 * * @param isSelected 是否被选中 * @param pos 当前view的位置 * @param holder * @param itemWidth 当前整个item的宽度 */ void onViewSelected(boolean isSelected, int pos, ViewHolder holder, int itemWidth); } /*** * 选中位置改变时的监听 */ public interface OnSelectedPositionChangedListener { void selectedPositionChanged(int pos); } public void setOnSelectedPositionChangedListener(OnSelectedPositionChangedListener listener) { this.listener = listener; } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/view/AutoTextureView.java ================================================ package camera.cn.cameramaster.view; import android.content.Context; import android.util.AttributeSet; import android.view.TextureView; /** * 自定义 正方形预览界面 * * @packageName: camera.cn.cameramaster.view * @fileName: AutoTextureView * @date: 2019/5/9 16:15 * @author: ymc * @QQ:745612618 */ public class AutoTextureView extends TextureView { private int mRatioWidth = 0; private int mRatioHeight = 0; public AutoTextureView(Context context) { super(context); } public AutoTextureView(Context context, AttributeSet attrs) { super(context, attrs); } public AutoTextureView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } /** * Sets the aspect ratio for this view. The size of the view will be measured based on the ratio * calculated from the parameters. Note that the actual sizes of parameters don't matter, that * is, calling setAspectRatio(2, 3) and setAspectRatio(4, 6) make the same result. * * @param width Relative horizontal size * @param height Relative vertical size */ public void setAspectRatio(int width, int height) { if (width < 0 || height < 0) { throw new IllegalArgumentException("Size cannot be negative."); } mRatioWidth = width; mRatioHeight = height; requestLayout(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); if (0 == mRatioWidth || 0 == mRatioHeight) { setMeasuredDimension(width, height); } else { if (width < height * mRatioWidth / mRatioHeight) { setMeasuredDimension(width, width * mRatioHeight / mRatioWidth); } else { setMeasuredDimension(height * mRatioWidth / mRatioHeight, height); } } } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/view/AwbSeekBar.java ================================================ package camera.cn.cameramaster.view; import android.annotation.SuppressLint; import android.content.Context; import android.os.Build; import android.support.annotation.RequiresApi; import android.util.AttributeSet; import android.widget.SeekBar; /** * 自定义 8种白平衡状态 选择 seekBar * * @author ymc */ @SuppressLint("AppCompatCustomView") public class AwbSeekBar extends SeekBar { private int mProgress; private AwbSeekBar mAwbSeekBar = this; private OnAwbSeekBarChangeListener mOnAwbSeekBarChangeListener; public AwbSeekBar(Context context) { super(context); init(); } public AwbSeekBar(Context context, AttributeSet attrs) { super(context, attrs); init(); } public AwbSeekBar(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public AwbSeekBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(); } public void setmOnAwbSeekBarChangeListener(OnAwbSeekBarChangeListener mOnAwbSeekBarChangeListener) { this.mOnAwbSeekBarChangeListener = mOnAwbSeekBarChangeListener; } private void init() { this.setMax(70); this.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { mProgress = progress; if (mOnAwbSeekBarChangeListener != null) { if (0 <= mProgress && mProgress < 5) { mOnAwbSeekBarChangeListener.doInProgress1(); } else if (5 <= mProgress && mProgress < 15) { mOnAwbSeekBarChangeListener.doInProgress2(); } else if (15 <= mProgress && mProgress < 25) { mOnAwbSeekBarChangeListener.doInProgress3(); } else if (25 <= mProgress && mProgress < 35) { mOnAwbSeekBarChangeListener.doInProgress4(); } else if (35 <= mProgress && mProgress < 45) { mOnAwbSeekBarChangeListener.doInProgress5(); } else if (45 <= mProgress && mProgress < 55) { mOnAwbSeekBarChangeListener.doInProgress6(); } else if (55 <= mProgress && mProgress < 65) { mOnAwbSeekBarChangeListener.doInProgress7(); } else if (65 <= mProgress && mProgress < 70) { mOnAwbSeekBarChangeListener.doInProgress8(); } } } @Override public void onStartTrackingTouch(SeekBar seekBar) { mOnAwbSeekBarChangeListener.onStartTrackingTouch(seekBar); } @Override public void onStopTrackingTouch(SeekBar seekBar) { int num = 0; if (0 <= mProgress && mProgress < 5) { mAwbSeekBar.setProgress(0); num = 0; } else if (5 <= mProgress && mProgress < 15) { mAwbSeekBar.setProgress(10); num = 10; } else if (15 <= mProgress && mProgress < 25) { mAwbSeekBar.setProgress(20); num = 20; } else if (25 <= mProgress && mProgress < 35) { mAwbSeekBar.setProgress(30); num = 30; } else if (35 <= mProgress && mProgress < 45) { mAwbSeekBar.setProgress(40); num = 40; } else if (45 <= mProgress && mProgress < 55) { mAwbSeekBar.setProgress(50); num = 50; } else if (55 <= mProgress && mProgress < 65) { mAwbSeekBar.setProgress(60); num = 60; } else if (65 <= mProgress && mProgress < 70) { mAwbSeekBar.setProgress(70); num = 70; } if (mOnAwbSeekBarChangeListener != null) { mOnAwbSeekBarChangeListener.onStopTrackingTouch(num); } } }); } public interface OnAwbSeekBarChangeListener { public abstract void doInProgress1(); public abstract void doInProgress2(); public abstract void doInProgress3(); public abstract void doInProgress4(); public abstract void doInProgress5(); public abstract void doInProgress6(); public abstract void doInProgress7(); public abstract void doInProgress8(); public abstract void onStopTrackingTouch(int num); public abstract void onStartTrackingTouch(SeekBar seekBar); } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/view/AwbSeekBarChangeListener.java ================================================ package camera.cn.cameramaster.view; import android.content.Context; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.CaptureRequest; import android.os.Build; import android.os.Handler; import android.support.annotation.RequiresApi; import android.util.Log; import android.view.View; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.SeekBar; import android.widget.TextView; import camera.cn.cameramaster.R; import camera.cn.cameramaster.view.AwbSeekBar; /** * 自定义 seekbar 滑动监听事件 * @author ymc */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public class AwbSeekBarChangeListener implements AwbSeekBar.OnAwbSeekBarChangeListener { private TextView mTextView; private CaptureRequest.Builder mPreviewBuilder; private CameraCaptureSession mCameraCaptureSession; private Handler mHandler; private CameraCaptureSession.CaptureCallback mPreviewSessionCallback; private Animation mAlphaInAnimation; private Animation mAlphaOutAnimation; public AwbSeekBarChangeListener(Context mContext, TextView mTextView, CaptureRequest.Builder mPreviewBuilder, CameraCaptureSession mCameraCaptureSession, Handler mHandler, CameraCaptureSession.CaptureCallback mPreviewSessionCallback) { this.mTextView = mTextView; this.mPreviewBuilder = mPreviewBuilder; this.mCameraCaptureSession = mCameraCaptureSession; this.mHandler = mHandler; this.mPreviewSessionCallback = mPreviewSessionCallback; mAlphaInAnimation = AnimationUtils.loadAnimation(mContext, R.anim.alpha_in); mAlphaOutAnimation = AnimationUtils.loadAnimation(mContext, R.anim.alpha_out); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override public void doInProgress1() { mTextView.setText("自动"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_AUTO); updatePreview(); } @Override public void doInProgress2() { mTextView.setText("多云"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_CLOUDY_DAYLIGHT); updatePreview(); } @Override public void doInProgress3() { mTextView.setText("白天"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_DAYLIGHT); updatePreview(); } @Override public void doInProgress4() { mTextView.setText("日光灯"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_FLUORESCENT); updatePreview(); } @Override public void doInProgress5() { mTextView.setText("白炽灯"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_INCANDESCENT); updatePreview(); } @Override public void doInProgress6() { mTextView.setText("阴影"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_SHADE); updatePreview(); } @Override public void doInProgress7() { mTextView.setText("黄昏"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_TWILIGHT); updatePreview(); } @Override public void doInProgress8() { mTextView.setText("暖光"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_WARM_FLUORESCENT); updatePreview(); } @Override public void onStopTrackingTouch(int num) { switch (num) { case 0: mTextView.setText("自动"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_AUTO); break; case 10: mTextView.setText("多云"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_CLOUDY_DAYLIGHT); break; case 20: mTextView.setText("白天"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_DAYLIGHT); break; case 30: mTextView.setText("日光灯"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_FLUORESCENT); break; case 40: mTextView.setText("白炽灯"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_INCANDESCENT); break; case 50: mTextView.setText("阴影"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_SHADE); break; case 60: mTextView.setText("黄昏"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_TWILIGHT); break; case 70: mTextView.setText("暖光"); mPreviewBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_WARM_FLUORESCENT); break; } updatePreview(); mTextView.startAnimation(mAlphaOutAnimation); mTextView.setVisibility(View.INVISIBLE); } @Override public void onStartTrackingTouch(SeekBar seekBar) { mTextView.setVisibility(View.VISIBLE); mTextView.startAnimation(mAlphaInAnimation); } /** * 更新预览 */ private void updatePreview() { try { mCameraCaptureSession.setRepeatingRequest(mPreviewBuilder.build(), mPreviewSessionCallback, mHandler); } catch (CameraAccessException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); Log.i("updatePreview", "ExceptionExceptionException"); } } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/view/CameraV2GLSurfaceView.java ================================================ package camera.cn.cameramaster.view; import android.content.Context; import android.opengl.GLSurfaceView; import camera.cn.cameramaster.util.render.CameraV2Renderer; import camera.cn.cameramaster.util.CameraV2; /** * CameraV2 GLSurfaceView * 参考url : [https://blog.csdn.net/lb377463323/article/details/78054892] * * @date 2019年2月12日 13:41:16 * @author ymc */ public class CameraV2GLSurfaceView extends GLSurfaceView { public static boolean shouldTakePic = false; public void init(CameraV2 camera, boolean isPreviewStarted, Context context) { setEGLContextClientVersion(2); CameraV2Renderer mCameraV2Renderer = new CameraV2Renderer(); mCameraV2Renderer.init(this, camera, isPreviewStarted, context); setRenderer(mCameraV2Renderer); setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); } public CameraV2GLSurfaceView(Context context) { super(context); } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/view/ShowSurfaceView.java ================================================ package camera.cn.cameramaster.view; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import android.view.SurfaceHolder; import android.view.SurfaceView; /** * @packageName: cn.tongue.tonguecamera.view * @fileName: ShowSurfaceView * @date: 2019/3/7 13:17 * @author: ymc * @QQ:745612618 */ public class ShowSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable { private SurfaceHolder mSurfaceHolder; /** * 绘图的Canvas */ private Canvas mCanvas; /** * 子线程标志位 */ private boolean mIsDrawing; private Bitmap bitmap; private Paint mPaint; public ShowSurfaceView(Context context) { this(context, null); } public ShowSurfaceView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ShowSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mSurfaceHolder = getHolder(); mSurfaceHolder.addCallback(this); setFocusable(true); setKeepScreenOn(true); setFocusableInTouchMode(true); mPaint = new Paint(); mPaint.setColor(Color.BLACK); mPaint.setStyle(Paint.Style.STROKE); mPaint.setAntiAlias(true); mPaint.setStrokeWidth(5); } @Override public void surfaceCreated(SurfaceHolder holder) { mIsDrawing = true; //开启子线程 new Thread(this).start(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { //改变 } @Override public void surfaceDestroyed(SurfaceHolder holder) { mIsDrawing = false; } @Override public void run() { while (mIsDrawing) { drawSomething(); } } //绘图逻辑 private void drawSomething() { try { //获得canvas对象 mCanvas = mSurfaceHolder.lockCanvas(); // Bitmap bitmap1= BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher); if (bitmap != null) { mCanvas.drawBitmap(bitmap, 0, 0, mPaint); } //绘图 } catch (Exception e) { } finally { if (mCanvas != null) { //释放canvas对象并提交画布 mSurfaceHolder.unlockCanvasAndPost(mCanvas); } } } public Bitmap getBitmap() { return bitmap; } public void setBitmap(Bitmap bitmap) { this.bitmap = bitmap; } } ================================================ FILE: app/src/main/java/camera/cn/cameramaster/view/SleepThread.java ================================================ package camera.cn.cameramaster.view; import android.os.Handler; import android.os.Message; /** * 睡眠线程 * * @author ymc */ public class SleepThread implements Runnable { private Handler mMainHandler; private int what; private long mTime; private Object mObject; public SleepThread(Handler mainHandler, int what, long mTime, Object mObject) { this.mMainHandler = mainHandler; this.what = what; this.mTime = mTime; this.mObject = mObject; } @Override public void run() { try { Thread.sleep(mTime); } catch (InterruptedException e) { e.printStackTrace(); } Message message = mMainHandler.obtainMessage(); message.what = what; message.obj = mObject; mMainHandler.sendMessage(message); } } ================================================ FILE: app/src/main/res/anim/alpha_in.xml ================================================ ================================================ FILE: app/src/main/res/anim/alpha_out.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_camera.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_fouces.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_launcher_background.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_launcher_camera.xml ================================================ ================================================ FILE: app/src/main/res/drawable-v24/ic_launcher_foreground.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_camera.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_camera2.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_camera_sv.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_camera_video.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_google_camera.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_look_camera.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_main.xml ================================================