Android平台-ndk-samples-camera-basic 照相机源码

1、AndroidManifest.xml

D:\WorkSpace\Android\AndroidLearn\GitHubRepo\ndk-samples\camera\basic\src\main\AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    android:versionCode="1"
          android:versionName="1.0">
  <uses-feature android:name="android.hardware.camera" />重点:需要的camera此硬件的功能
  <uses-permission android:name="android.permission.CAMERA" />重点:需要的camera此硬件的运行时权限
  <application
      android:allowBackup="false"
      android:fullBackupContent="false"
      android:icon="@mipmap/ic_launcher"
      android:label="@string/app_name"
      android:screenOrientation="sensorLandscape"
      android:configChanges="keyboardHidden|orientation|screenSize"重点:触发 onConfirationChanged
      android:hasCode="true">
    <activity android:name="com.sample.camera.basic.CameraActivity"
              android:label="@string/app_name"
        android:exported="true">
      <meta-data android:name="android.app.lib_name"
                 android:value="ndk_camera" />重点:ndk_camera
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
  </application>
</manifest>

2、CameraActivity.java

D:\WorkSpace\Android\AndroidLearn\GitHubRepo\ndk-samples\camera\basic\src\main\java\com\sample\camera\basic\CameraActivity.java

class CameraSeekBar {
    int _progress;
    long _min, _max, _absVal;
    SeekBar _seekBar;
    TextView _sliderPrompt;
    CameraSeekBar() {
        _progress = 0;
        _min = _max = _absVal  = 0;
    }

    CameraSeekBar(SeekBar seekBar, TextView textView, long min, long max, long val) {
        _seekBar = seekBar;//重点:CameraSeekBar构建函数
        _sliderPrompt = textView;
        _min = min;
        _max = max;
        _absVal = val;

        if(_min != _max) {
            _progress = (int) ((_absVal - _min) * _seekBar.getMax() / (_max - _min));
            seekBar.setProgress(_progress);
            updateProgress(_progress);//重点:更新进度条
        } else {
            _progress = 0;
            seekBar.setEnabled(false);
        }
    }

    public boolean isSupported() {
        return (_min != _max);
    }
    public void updateProgress(int progress) {//重点:更新进度条
        if (!isSupported())
            return;

        _progress = progress;
        _absVal = (progress * ( _max - _min )) / _seekBar.getMax() + _min;
        int val = (progress * (_seekBar.getWidth() - 2 * _seekBar.getThumbOffset())) / _seekBar.getMax();
        _sliderPrompt.setText("" + _absVal);//重点,设定进度条值的位置
        _sliderPrompt.setX(_seekBar.getX() + val + _seekBar.getThumbOffset() / 2);
    }
    public int getProgress() {
        return _progress;
    }
    public void updateAbsProgress(long val) {//重点:更新绝对值的进度条
        if (!isSupported())
            return;
        int progress = (int)((val - _min) * _seekBar.getMax() / (_max - _min));
        updateProgress(progress);
    }
    public long getAbsProgress() {//重点:获取进度条的对应的绝对值
        return _absVal;
    }
}

public class CameraActivity extends NativeActivity
        implements ActivityCompat.OnRequestPermissionsResultCallback {
    volatile CameraActivity _savedInstance;
    PopupWindow _popupWindow;
    ImageButton _takePhoto;
    CameraSeekBar _exposure, _sensitivity;
    long[] _initParams;//重点,存放摄像头的曝光值,灵敏度值

    private final String DBG_TAG = "NDK-CAMERA-BASIC";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i(DBG_TAG, "OnCreate()");
        // new initialization here... request for permission
        _savedInstance  = this;

        setImmersiveSticky();
        View decorView = getWindow().getDecorView();//重点,view层级的根View为 decorView
        decorView.setOnSystemUiVisibilityChangeListener
                (new View.OnSystemUiVisibilityChangeListener() {
                    @Override
                    public void onSystemUiVisibilityChange(int visibility) {
                        setImmersiveSticky();
                    }
                });
    }

    private boolean isCamera2Device() {//重点,通过hardware下的JAVA工具类,检测camera是否存在
        CameraManager camMgr = (CameraManager)getSystemService(Context.CAMERA_SERVICE);
        boolean camera2Dev = true;
        try {
            String[] cameraIds = camMgr.getCameraIdList();
            if (cameraIds.length != 0 ) {
                for (String id : cameraIds) {
                    CameraCharacteristics characteristics = camMgr.getCameraCharacteristics(id);
                    int deviceLevel = characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
                    int facing = characteristics.get(CameraCharacteristics.LENS_FACING);
                    if (deviceLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY &&
                        facing == LENS_FACING_BACK) {
                        camera2Dev =  false;
                    }
                }
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
            camera2Dev = false;
        }
        return camera2Dev;
    }

    // get current rotation method
    int getRotationDegree() {//重点,获取当前G-Sensor旋转的角度
        return 90 * ((WindowManager)(getSystemService(WINDOW_SERVICE)))
                .getDefaultDisplay()
                .getRotation();
    }
    @Override
    protected void onResume() {
        super.onResume();
        setImmersiveSticky();
    }
    void setImmersiveSticky() {
        View decorView = getWindow().getDecorView();//重点,view层级的根View为 decorView
        decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
    }

    @Override
    protected void onPause() {
        if (_popupWindow != null && _popupWindow.isShowing()) {
            _popupWindow.dismiss();//重点,camera应用退到后台时候,_popupWindow消失
            _popupWindow = null;
        }
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    private static final int PERMISSION_REQUEST_CODE_CAMERA = 1;
    public void RequestCamera() {
        if(!isCamera2Device()) {
            Log.e(DBG_TAG, "Found legacy camera Device, this sample needs camera2 device");
            return;
        }
        if (ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.CAMERA
        ) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(
                    this,
                    new String[]{Manifest.permission.CAMERA},
                    PERMISSION_REQUEST_CODE_CAMERA
            );//重点,请求摄像头的运行时权限组
            return;
        }
        notifyCameraPermission(true);//JAVA侧通知C++侧,运行时权限授权是否成功,第一次通知成功
    }

    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        if (requestCode != PERMISSION_REQUEST_CODE_CAMERA) {//配套的请求运行时权限的CODE值
            // The permissions request isn't ours.
            super.onRequestPermissionsResult(requestCode,
                                             permissions,
                                             grantResults);
            return;
        }

        if (permissions.length == 0) {
            // https://developer.android.com/reference/androidx/core/app/ActivityCompat.OnRequestPermissionsResultCallback#onRequestPermissionsResult(int,java.lang.String[],int[])
            //
            // Note: It is possible that the permissions request interaction with the user is
            // interrupted. In this case you will receive empty permissions and results arrays which
            // should be treated as a cancellation.
            //
            // The docs aren't clear about *why* it might be canceled, so it's not clear what we
            // should do here other than restart the request.
            RequestCamera();//重点,再次请求摄像头的运行时权限
            return;
        }

        boolean granted = Arrays.stream(grantResults)
                .allMatch(element -> element == PackageManager.PERMISSION_GRANTED);
        if (!granted) {
            logDeniedPermissions(permissions, grantResults);//记录运行时权限组的授权情况
        }
        notifyCameraPermission(granted);//JAVA侧通知C++侧,运行时权限授权是否成功,二次确认地方
    }

    private void logDeniedPermissions(//记录运行时权限组的授权情况
            @NonNull String[] requestedPermissions,
            @NonNull int[] grantResults
    ) {
        if (requestedPermissions.length != grantResults.length) {
            throw new IllegalArgumentException(
                    String.format(
                            "requestedPermissions.length (%d) != grantResults.length (%d)",
                            requestedPermissions.length,
                            grantResults.length
                    )
            );
        }

        for (int i = 0; i < requestedPermissions.length; i++) {
            if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                Log.i(DBG_TAG, requestedPermissions[i] + " DENIED");
            }
        }
    }

    /**
     * params[] exposure and sensitivity init values in (min, max, curVa) tuple
     *   0: exposure min
     *   1: exposure max
     *   2: exposure val
     *   3: sensitivity min
     *   4: sensitivity max
     *   5: sensitivity val
     */
    @SuppressLint("InflateParams")//重点,从C++侧调用到JAVA层,刷新Camera的界面
    public void EnableUI(final long[] params)
    {//重点,params存放摄像头的曝光值,灵敏度值
        // make our own copy
        _initParams = new long[params.length];
        System.arraycopy(params, 0, _initParams, 0, params.length);

        runOnUiThread(new Runnable()  {
            @Override
            public void run()  {
                try {
                    if (_popupWindow != null) {
                        _popupWindow.dismiss();
                    }
                    LayoutInflater layoutInflater
                            = (LayoutInflater) getBaseContext()
                            .getSystemService(LAYOUT_INFLATER_SERVICE);
                    View popupView = layoutInflater.inflate(R.layout.widgets, null);//重点,通过视图的填充器,填入widgets视图
                    _popupWindow = new PopupWindow(//配置widgets视图的宽度、高度
                            popupView,
                            WindowManager.LayoutParams.MATCH_PARENT,
                            WindowManager.LayoutParams.WRAP_CONTENT);

                    RelativeLayout mainLayout = new RelativeLayout(_savedInstance);//mainLayout为父类布局,_savedInstance为CameraActivity这个Context
                    ViewGroup.MarginLayoutParams params = new ViewGroup.MarginLayoutParams(
                            -1, -1);//mainLayout为父类布局的参数
                    params.setMargins(0, 0, 0, 0);
                    _savedInstance.setContentView(mainLayout, params);//设定CameraActivity的父类布局

                    // Show our UI over NativeActivity window
                    _popupWindow.showAtLocation(mainLayout, Gravity.BOTTOM | Gravity.START, 0, 0);
                    _popupWindow.update();//_popupWindow窗体覆盖NativeActivity的窗体

                    _takePhoto = (ImageButton) popupView.findViewById(R.id.takePhoto);
                    _takePhoto.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            TakePhoto();//JAVA侧通知C++侧,执行拍照操作
                        }
                    });
                    _takePhoto.setEnabled(true);
                    (popupView.findViewById(R.id.exposureLabel)).setEnabled(true);
                    (popupView.findViewById(R.id.sensitivityLabel)).setEnabled(true);

                    SeekBar seekBar = (SeekBar) popupView.findViewById(R.id.exposure_seekbar);
                    _exposure = new CameraSeekBar(seekBar,
                            (TextView) popupView.findViewById(R.id.exposureVal),
                            _initParams[0], _initParams[1], _initParams[2]);
                    seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                        @Override
                        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                            _exposure.updateProgress(progress);
                            OnExposureChanged(_exposure.getAbsProgress());//JAVA侧通知C++侧,用户设定的摄像头的曝光值
                        }

                        @Override
                        public void onStartTrackingTouch(SeekBar seekBar) {
                        }

                        @Override
                        public void onStopTrackingTouch(SeekBar seekBar) {
                        }
                    });
                    seekBar = ((SeekBar) popupView.findViewById(R.id.sensitivity_seekbar));
                    _sensitivity = new CameraSeekBar(seekBar,
                            (TextView) popupView.findViewById(R.id.sensitivityVal),
                            _initParams[3], _initParams[4], _initParams[5]);
                    seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                        @Override
                        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                            _sensitivity.updateProgress(progress);
                            OnSensitivityChanged(_sensitivity.getAbsProgress());//JAVA侧通知C++侧,用户设定的摄像头的灵敏度值
                        }

                        @Override
                        public void onStartTrackingTouch(SeekBar seekBar) {
                        }

                        @Override
                        public void onStopTrackingTouch(SeekBar seekBar) {
                        }
                    });
                } catch (WindowManager.BadTokenException e) {
                    // UI error out, ignore and continue
                    Log.e(DBG_TAG, "UI Exception Happened: " + e.getMessage());
                }
            }});
    }
    /**
      Called from Native side to notify that a photo is taken
     */
    public void OnPhotoTaken(String fileName) {//重点,在JAVA侧进行toast提示
        final String name = fileName;
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(getApplicationContext(),
                        "Photo saved to " + name, Toast.LENGTH_SHORT).show();
            }
        });
    }

    native static void notifyCameraPermission(boolean granted);//重点,JAVA侧通知C++侧,运行时权限授权是否成功
    native static void TakePhoto();//重点,JAVA侧通知C++侧,执行拍照操作
    native void OnExposureChanged(long exposure);//JAVA侧通知C++侧,用户设定的摄像头的曝光值
    native void OnSensitivityChanged(long sensitivity);//JAVA侧通知C++侧,用户设定的摄像头的灵敏度值

    static {
        System.loadLibrary("ndk_camera");//重点,从JAVA层通知ART,动态加载JNI的共享库
    }
}

3、android_main.cpp

D:\WorkSpace\Android\AndroidLearn\GitHubRepo\ndk-samples\camera\basic\src\main\cpp\android_main.cpp

#include "camera_engine.h"
#include "utils/native_debug.h"

/*
 * SampleEngine global object
 */
 //获取 CameraEngine 实例的函数
static CameraEngine* pEngineObj = nullptr;
CameraEngine* GetAppEngine(void) {
  ASSERT(pEngineObj, "AppEngine has not initialized");
  return pEngineObj;
}

/**
 * Teamplate function for NativeActivity derived applications
 *   Create/Delete camera object with
 *   INIT_WINDOW/TERM_WINDOW command, ignoring other event.
 */
 //重点,响应 application 每个配置变化的函数
static void ProcessAndroidCmd(struct android_app* app, int32_t cmd) {
  CameraEngine* engine = reinterpret_cast<CameraEngine*>(app->userData);
  switch (cmd) {
    case APP_CMD_INIT_WINDOW://初始化 application 的窗体
      if (engine->AndroidApp()->window != NULL) {
        engine->SaveNativeWinRes(ANativeWindow_getWidth(app->window),
                                 ANativeWindow_getHeight(app->window),
                                 ANativeWindow_getFormat(app->window));
        engine->OnAppInitWindow();
      }
      break;
    case APP_CMD_TERM_WINDOW://销毁 application 的窗体
      engine->OnAppTermWindow();
      ANativeWindow_setBuffersGeometry(
          app->window, engine->GetSavedNativeWinWidth(),
          engine->GetSavedNativeWinHeight(), engine->GetSavedNativeWinFormat());
      break;
    case APP_CMD_CONFIG_CHANGED://响应 application 的配置变化,比如显示方向变化、语言变化等
      engine->OnAppConfigChange();
      break;
    case APP_CMD_LOST_FOCUS:
      break;
  }
}

//重点,C++层面的 android_main 主函数
extern "C" void android_main(struct android_app* state) {
  CameraEngine engine(state);
  pEngineObj = &engine;

  state->userData = reinterpret_cast<void*>(&engine);//给android_app的结构体成员赋值为 CameraEngine
  state->onAppCmd = ProcessAndroidCmd;//给android_app的结构体成员赋值为 ProcessAndroidCmd

  // loop waiting for stuff to do.
  while (!state->destroyRequested) {//Application的生命周期一直存在,一直轮询
    struct android_poll_source* source = nullptr;
    auto result  = ALooper_pollOnce(0, NULL, nullptr, (void**)&source);//开始轮询函数
    ASSERT(result != ALOOPER_POLL_ERROR, "ALooper_pollOnce returned an error");
    if (source != NULL) {
      source->process(state, source);//轮询处理
    }
    pEngineObj->DrawFrame();//处理YUV到RGBA格式图像的渲染
  }

  LOGI("CameraEngine thread destroy requested!");
  engine.DeleteCamera();
  pEngineObj = nullptr;
}

/**
 * Handle Android System APP_CMD_INIT_WINDOW message
 *   Request camera persmission from Java side
 *   Create camera object if camera has been granted
 */
 //初始化APP窗体的资源
void CameraEngine::OnAppInitWindow(void) {
  if (!cameraGranted_) {
    // Not permitted to use camera yet, ask(again) and defer other events
    RequestCameraPermission();//如果运行时权限没有授权,从C++侧开始请求运行时权限
    return;
  }

  rotation_ = GetDisplayRotation();//获取Display的旋转角度

  CreateCamera();//创建 Camera的实例
  ASSERT(camera_, "CameraCreation Failed");

  EnableUI();//重点,从C++侧调用到JAVA层,刷新Camera的界面

  // NativeActivity end is ready to display, start pulling images
  cameraReady_ = true;
  camera_->StartPreview(true);//启动Camera,开始预览摄像头的图像
}

/**
 * Handle APP_CMD_TEMR_WINDOW
 */
 //销毁Application关联的Camera实例
void CameraEngine::OnAppTermWindow(void) {
  cameraReady_ = false;
  DeleteCamera();
}

/**
 * Handle APP_CMD_CONFIG_CHANGED
 */
 //重点,Application的配置发生变化,重新配置Camera的窗体
void CameraEngine::OnAppConfigChange(void) {
  int newRotation = GetDisplayRotation();

  if (newRotation != rotation_) {
    OnAppTermWindow();

    rotation_ = newRotation;
    OnAppInitWindow();
  }
}

/**
 * Retrieve saved native window width.
 * @return width of native window
 */
int32_t CameraEngine::GetSavedNativeWinWidth(void) {
  return savedNativeWinRes_.width;
}

/**
 * Retrieve saved native window height.
 * @return height of native window
 */
int32_t CameraEngine::GetSavedNativeWinHeight(void) {
  return savedNativeWinRes_.height;
}

/**
 * Retrieve saved native window format
 * @return format of native window
 */
int32_t CameraEngine::GetSavedNativeWinFormat(void) {
  return savedNativeWinRes_.format;
}

/**
 * Save original NativeWindow Resolution
 * @param w width of native window in pixel
 * @param h height of native window in pixel
 * @param format
 */
 //重点,保存 本地窗口的宽度、高度、图像格式
void CameraEngine::SaveNativeWinRes(int32_t w, int32_t h, int32_t format) {
  savedNativeWinRes_.width = w;
  savedNativeWinRes_.height = h;
  savedNativeWinRes_.format = format;
}

4、camera_ui.cpp

//重点:定义JAVA层和C++层的JNI接口,侧重点从C++层到JAVA层的接口
D:\WorkSpace\Android\AndroidLearn\GitHubRepo\ndk-samples\camera\basic\src\main\cpp\camera_ui.cpp

#include <utils/native_debug.h>

#include "camera_engine.h"//

/**
 * Retrieve current rotation from Java side
 *
 * @return current rotation angle//一部分CameraEngine的函数在此处定义。
 */
int CameraEngine::GetDisplayRotation() {//重点,从C++层获取JAVA层的G-Sensor的角度值
  ASSERT(app_, "Application is not initialized");

  JNIEnv *env;//JNI的系统环境
  ANativeActivity *activity = app_->activity;//实例化ANativeActivity
  activity->vm->GetEnv((void **)&env, JNI_VERSION_1_6);//获取VM虚拟机的环境

  activity->vm->AttachCurrentThread(&env, NULL);//抢占当前的线程

  jobject activityObj = env->NewGlobalRef(activity->clazz);//实例化JAVA侧的ANativeActivity类名
  jclass clz = env->GetObjectClass(activityObj);
  jint newOrientation = env->CallIntMethod(
      activityObj, env->GetMethodID(clz, "getRotationDegree", "()I"));//获取newOrientation,调用JAVA侧的方法名,传入方法名的参数
  env->DeleteGlobalRef(activityObj);

  activity->vm->DetachCurrentThread();//释放当前的线程
  return newOrientation;
}

/**//重点,从C++传递摄像头的曝光、灵敏度参数到JAVA侧的方法
 * Initializate UI on Java side. The 2 seekBars' values are passed in
 * array in the tuple of ( min, max, curVal )
 *   0: exposure min
 *   1: exposure max
 *   2: exposure val
 *   3: sensitivity min
 *   4: sensitivity max
 *   5: sensitivity val
 */
const int kInitDataLen = 6;
void CameraEngine::EnableUI(void) {//一部分CameraEngine的函数在此处定义。
  JNIEnv *jni;
  app_->activity->vm->AttachCurrentThread(&jni, NULL);
  int64_t range[3];

  // 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值