Android Camera模块(一)

一、Android Camera架构

1.1 整体架构

架构参考Android Camera简单整理(一)-Camera Android架构(基于Q)

google 官方图:

从上图得知,Android手机中Camera软件主要有大体上有4层:

1.应用层: 应用开发者调用AOSP提供的接口即可,AOSP的接口即Android提供的相机应用的通用接口,这些接口将通过Binder与Framework层的相机服务进行操作与数据传递;

2.Framework层: 位于 frameworks/av/services/camera/libcameraservice/CameraService.cpp ,相机Framework服务是承上启下的作用,上与应用交互,下与HAL曾交互。

3.Hal层: 硬件抽象层,Android 定义好了Framework服务与HAL层通信的协议及接口,HAL层如何实现有各个Vendor自己实现,如Qcom的老架构mm-Camera,新架构Camx架构,Mtk的P之后的Hal3架构.

4.Driver层: 驱动层,数据由硬件到驱动层处理,驱动层接收HAL层数据以及传递Sensor数据给到HAL层,这里当然是各个Sensor芯片不同驱动也不同.

说到这为什么要分这么多层,大体上还是为了分清界限,升级方便, AndroidO Treble架构分析.

Android要适应各个手机组装厂商的不同配置,不同sensor,不管怎么换芯片,从Framework及之上都不需要改变,App也不需要改变就可以在各种配置机器上顺利运行,HAL层对上的接口也由Android定义死,各个平台厂商只需要按照接口实现适合自己平台的HAL层即可.

1.2 Android Camera工作大体流程

绿色框中是应用开发者需要做的操作,蓝色为AOSP提供的API,黄色为Native Framework Service,紫色为HAL层Service.
描述一下步骤:
1 App一般在MainActivity中使用SurfaceView或者SurfaceTexture+TextureView或者GLSurfaceView等控件作为显示预览界面的控件,共同点都是包含了一个单独的Surface作为取相机数据的容器.

2 在MainActivity onCreate的时候调用API 去通知Framework Native Service CameraServer去connect HAL继而打开Camera硬件sensor.

3 openCamera成功会有回调从CameraServer通知到App,在onOpenedCamera或类似回调中去调用类似startPreview的操作.此时会创建CameraCaptureSession,创建过程中会向CameraServer调用ConfigureStream的操作,ConfigureStream的参数中包含了第一步中空间中的Surface的引用,相当于App将Surface容器给到了CameraServer,CameraServer包装了下该Surface容器为stream,通过HIDL传递给HAL,继而HAL也做configureStream操作

4 ConfigureStream成功后CameraServer会给App回调通知ConfigStream成功,接下来App便会调用AOSP setRepeatingRequest接口给到CameraServer,在Camera3Device初始化时起来了一个死循环线程 RequestThread来接收Request。

5 CameraServer将request交到Hal层去处理,得到HAL处理结果后取出该Request的处理Result中的Buffer填到App给到的容器中,SetRepeatingRequest为了预览,则交给Preview的Surface容器,如果是Capture Request则将收到的Buffer交给ImageReader的Surface容器.

6 Surface本质上是BufferQueue的使用者和封装者,当CameraServer中App设置来的Surface容器被填满了BufferQueue机制将会通知到应用,此时App中控件取出各自容器中的内容消费掉,Preview控件中的Surface中的内容将通过View提供到SurfaceFlinger中进行合成最终显示出来,即预览;而ImageReader中的Surface被App取出保存成图片文件消费掉.
比较简单的一张图:

二、Camera APP的启动流程

我们如果打开Android原生相机会呈现以下界面(由于没有Android12的设备所以使用Android模拟器来操作):

接着我们寻找上面这个界面是那个Activity呈现的,直接dump当前resumed的actvitiy:

emulator64_x86_64:/ $ dumpsys activity |grep mResumedmResumedActivity: ActivityRecord{f078923 u0 com.android.camera2/com.android.camera.CameraLauncher t73}

可以清楚的看到当前的Activity是CameraLauncher在代码库种查找这个类发现在

/packages/apps/Camera2/AndroidManifest.xml
83         <activity-alias
84             android:name="com.android.camera.CameraLauncher"
85             android:label="@string/app_name"
86             android:exported="true"
87             android:targetActivity="com.android.camera.CameraActivity">
88             <intent-filter>
89                 <action android:name="android.intent.action.MAIN" />
90
91                 <category android:name="android.intent.category.DEFAULT" />
92                 <category android:name="android.intent.category.LAUNCHER" />
93             </intent-filter>
94         </activity-alias>

可以看到CameraLauncher是CameraActivity的别名,所以我们在打开Camera相机时,其实就是启动了CameraActivity。

接着我们看CameraActivity是如何打开相机并预览的:

/packages/apps/Camera2/src/com/android/camera/CameraActivity.java
177  public class CameraActivity extends QuickActivity
178          implements AppController, CameraAgent.CameraOpenCallback,
179          ShareActionProvider.OnShareTargetSelectedListener {

首先CameraActivity继承自QuickActivity,我们先看一下QuickActivity的实现:

107      @Override
108      protected final void onCreate(Bundle bundle) {109          mExecutionStartNanoTime = SystemClock.elapsedRealtimeNanos();
110          logLifecycle("onCreate", true);
111          mStartupOnCreate = true;
112          super.onCreate(bundle);
113          mMainHandler = new Handler(getMainLooper());
114          onCreateTasks(bundle);
115          logLifecycle("onCreate", false);
116      }...
200      }

可以看到QuickActivity主要就是将Activity的生命周期再次封装了一下:其中各种生命周期对应关系如下:

QuickActivity CameraActivity
onCreate onCreateTasks
onStart onStartTasks
onResume onResumeTasks
onPause onPauseTasks
onStop onStopTasks
onNewIntent onNewIntentTasks

所以CameraActivity创建时会调用到onCreateTasks:

2.1 onCreateTasks

/packages/apps/Camera2/src/com/android/camera/CameraActivity.java
1430      @Override
1431      public void onCreateTasks(Bundle state) {//1.初始化一些Manager并且检查权限
1465          mActiveCameraDeviceTracker = ActiveCameraDeviceTracker.instance();
1466          try {1467              mOneCameraOpener = OneCameraModule.provideOneCameraOpener(
1468                      mFeatureConfig,
1469                      mAppContext,
1470                      mActiveCameraDeviceTracker,
1471                      ResolutionUtil.getDisplayMetrics(this));
1472              mOneCameraManager = OneCameraModule.provideOneCameraManager();
1473          } catch (OneCameraException e) {1474              // Log error and continue start process while showing error dialog..
1475              Log.e(TAG, "Creating camera manager failed.", e);
1476              mFatalErrorHandler.onGenericCameraAccessFailure();
1477          }
1478          profile.mark("OneCameraManager.get");
1464
1465          mActiveCameraDeviceTracker = ActiveCameraDeviceTracker.instance();
1479          //2.初始化CameraController
1480          try {1481              mCameraController = new CameraController(mAppContext, this, mMainHandler,
1482                      CameraAgentFactory.getAndroidCameraAgent(mAppContext,
1483                              CameraAgentFactory.CameraApi.API_1),
1484                      CameraAgentFactory.getAndroidCameraAgent(mAppContext,
1485                              CameraAgentFactory.CameraApi.AUTO),
1486                      mActiveCameraDeviceTracker);
1487              mCameraController.setCameraExceptionHandler(
1488                      new CameraExceptionHandler(mCameraExceptionCallback, mMainHandler));
1489          } catch (AssertionError e) {1490              Log.e(TAG, "Creating camera controller failed.", e);
1491              mFatalErrorHandler.onGenericCameraAccessFailure();
1492          }
1493          //3.初始化ModuleManager并设置module的创建规则
1494          // TODO: Try to move all the resources allocation to happen as soon as
1495          // possible so we can call module.init() at the earliest time.
1496          mModuleManager = new ModuleManagerImpl();
1497
1498          ModulesInfo.setupModules(mAppContext, mModuleManager, mFeatureConfig);
1499
1500          AppUpgrader appUpgrader = new AppUpgrader(this);
1501          appUpgrader.upgrade(mSettingsManager);
1502  ...
1528          //4.设置视图
1529          profile.mark();
1530          setContentView(R.layout.activity_main);
1531          profile.mark("setContentView()");...//5.设置工作模式
1625          setModuleFromModeIndex(getModeIndex());1627          profile.mark();
1628          mCameraAppUI.prepareModuleUI();
1629          profile.mark("Init Current Module UI");//6.初始化模块
1630          mCurrentModule.init(this, isSecureCamera(), isCaptureIntent());
1631          profile.mark("Init CurrentModule");
1687      }

可以看到在Camera的onCreate种主要做了以下几件事:

1.一些基本的初始化

2.初始化CameraController和ModuleManager,并设置module的创建规则。

3.设置视图和工作模式。

4.初始化photo module

这一步我们主要看一下ModuleManager干了什么事,首先初始化了一个ModuleManager,接着调用了 ModulesInfo.setupModules来设置模块的生成规则。

/packages/apps/Camera2/src/com/android/camera/module/ModulesInfo.java
47      public static void setupModules(Context context, ModuleManager moduleManager,
48              OneCameraFeatureConfig config) {49          Resources res = context.getResources();
50          int photoModuleId = context.getResources().getInteger(R.integer.camera_mode_photo);
51          registerPhotoModule(moduleManager, photoModuleId, SettingsScopeNamespaces.PHOTO,
52                  config.isUsingCaptureModule());
53          moduleManager.setDefaultModuleIndex(photoModuleId);
54          registerVideoModule(moduleManager, res.getInteger(R.integer.camera_mode_video),
55                  SettingsScopeNamespaces.VIDEO);
56          if (PhotoSphereHelper.hasLightCycleCapture(context)) {57              registerWideAngleModule(moduleManager, res.getInteger(R.integer.camera_mode_panorama),
58                      SettingsScopeNamespaces.PANORAMA);
59              registerPhotoSphereModule(moduleManager,
60                      res.getInteger(R.integer.camera_mode_photosphere),
61                      SettingsScopeNamespaces.PANORAMA);
62          }
63          if (RefocusHelper.hasRefocusCapture(context)) {64              registerRefocusModule(moduleManager, res.getInteger(R.integer.camera_mode_refocus),
65                      SettingsScopeNamespaces.REFOCUS);
66          }
67          if (GcamHelper.hasGcamAsSeparateModule(config)) {68              registerGcamModule(moduleManager, res.getInteger(R.integer.camera_mode_gcam),
69                      SettingsScopeNamespaces.PHOTO,
70                      config.getHdrPlusSupportLevel(OneCamera.Facing.BACK));
71          }
72          int imageCaptureIntentModuleId = res.getInteger(R.integer.camera_mode_capture_intent);
73          registerCaptureIntentModule(moduleManager, imageCaptureIntentModuleId,
74                  SettingsScopeNamespaces.PHOTO, config.isUsingCaptureModule());
75      }

可以看到setupModules注册了很多模块,主要有PhotoModule,VideoModule和CaptureIntentModule,另外几个则是有条件限制:WideAngleModule,PhotoSphereModule,RefocusModule,GcamModule。由于我们一进app就是拍照,所以我们以PhotoModule为例:

/packages/apps/Camera2/src/com/android/camera/module/ModulesInfo.java
77      private static void registerPhotoModule(ModuleManager moduleManager, final int moduleId,
78              final String namespace, final boolean enableCaptureModule) {79          moduleManager.registerModule(new ModuleManager.ModuleAgent() {80
81              @Override
82              public int getModuleId() {83                  return moduleId;
84              }
85
86              @Override
87              public boolean requestAppForCamera() {88                  // The PhotoModule requests the old app camere, while the new
89                  // capture module is using OneCamera. At some point we'll
90                  // refactor all modules to use OneCamera, then the new module
91                  // doesn't have to manage it itself.
92                  return !enableCaptureModule;
93              }
94
95              @Override
96              public String getScopeNamespace() {97                  return namespace;
98              }
99
100              @Override
101              public ModuleController createModule(AppController app, Intent intent) {102                  Log.v(TAG, "EnableCaptureModule = " + enableCaptureModule);
103                  return enableCaptureModule ? new CaptureModule(app) : new PhotoModule(app);
104              }
105          });
106      }

这里只是将ModuleAgent注册给了moduleManager还并未真正创建module创建module要到设置工作模式时进行。这里有两个点后面会用到

1.注册的PhotoModule的photoModuleId为0

2.注册PhotoModule时传入enableCaptureModule默认为true

接着调用setModuleFromModeIndex(getModeIndex());设置工作模式:getModeIndex默认返回的就是photoIndex 也就是0

/packages/apps/Camera2/src/com/android/camera/CameraActivity.java
2562      private void setModuleFromModeIndex(int modeIndex) {2563          ModuleManagerImpl.ModuleAgent agent = mModuleManager.getModuleAgent(modeIndex);
2564          if (agent == null) {2565              return;
2566          }
2567          if (!agent.requestAppForCamera()) {2568              mCameraController.closeCamera(true);
2569          }
2570          mCurrentModeIndex = agent.getModuleId();//将CameraActivity作为AppControle传入module
2571          mCurrentModule = (CameraModule) agent.createModule(this, getIntent());
2572      }

在setModuleFromModeIndex会通过agent.createModule创建一个module,在这里时创建了一个PhotoModule又因为EnableCaptureModule为true,所以最终mCurrentModule是CaptureModule。接着会调用 mCurrentModule.init去初始化模块。主要就是初始化一些线程并创建一个handler处理请求,并且获得了mOneCameraOpener和 mOneCameraManager,接着生命周期走到onResumed时CameraActivity走到

2.2 onResumeTask

/packages/apps/Camera2/src/com/android/camera/CameraActivity.java
1894      public void onResumeTasks() {1895          mPaused = false;
1896          checkPermissions();
1897          if (!mHasCriticalPermissions) {1898              Log.v(TAG, "onResume: Missing critical permissions.");
1899              finish();
1900              return;
1901          }//1.这里由于我们时正常启动camera app所以都是走到里面的mFirstRunDialog.showIfNecessary
1902          if (!isSecureCamera() && !isCaptureIntent()) {1903              // Show the dialog if necessary. The rest resume logic will be invoked
1904              // at the onFirstRunStateReady() callback.
1905              try {1906                  mFirstRunDialog.showIfNecessary();
1907              } catch (AssertionError e) {1908                  Log.e(TAG, "Creating camera controller failed.", e);
1909                  mFatalErrorHandler.onGenericCameraAccessFailure();
1910              }
1911          } else {1912              // In secure mode from lockscreen, we go straight to camera and will
1913              // show first run dialog next time user enters launcher.
1914              Log.v(TAG, "in secure mode, skipping first run dialog check");
1915              resume();
1916          }
1917      }

这里的 mFirstRunDialog.showIfNecessary();主要就是显示两个权限请求框如下:

这两个图片都确认后会走到FirstRunDialog的onFirstRunStateReady

/packages/apps/Camera2/src/com/android/camera/CameraActivity.java
1661          mFirstRunDialog = new FirstRunDialog(this,
1662                this /* as context */,
1663                mResolutionSetting,
1664                mSettingsManager,
1665                mOneCameraManager,
1666                new FirstRunDialog.FirstRunDialogListener() {1667              @Override
1668              public void onFirstRunStateReady() {1669                  // Run normal resume tasks.
1670                  resume();
1671              }
1672
1673              @Override
1674              public void onFirstRunDialogCancelled() {1675                  // App isn't functional until users finish first run dialog.
1676                  // We need to finish here since users hit back button during
1677                  // first run dialog (b/19593942).
1678                  finish();
1679              }
1680
1681              @Override
1682              public void onCameraAccessException() {1683                  mFatalErrorHandler.onGenericCameraAccessFailure();
1684              }
1685          });

即走到resume()

/packages/apps/Camera2/src/com/android/camera/CameraActivity.java
2002      private void resume() {...
2072
2073          profile.mark();
2074          mCurrentModule.resume();
2075          UsageStatistics.instance().changeScreen(currentUserInterfaceMode(),
2076                  NavigationChange.InteractionCause.BUTTON);
2077          setSwipingEnabled(true);
2078          profile.mark("mCurrentModule.resume");
2079        ...
2153      }

这里我们只关注mCurrentModule的resume

/packages/apps/Camera2/src/com/android/camera/CaptureModule.java
674      @Override
675      public void resume() {....
705          // This means we are resuming with an existing preview texture. This
706          // means we will never get the onSurfaceTextureAvailable call. So we
707          // have to open the camera and start the preview here.
708          SurfaceTexture texture = getPreviewSurfaceTexture();
709
710          guard.mark();
711          if (texture != null) {//在这里会初始化SurfaceTexture并打开相机开始预览
712              initSurfaceTextureConsumer();
713              guard.mark("initSurfaceTextureConsumer");
714          }
715
716          mSoundPlayer.loadSound(R.raw.timer_final_second);
717          mSoundPlayer.loadSound(R.raw.timer_increment);
718
719          guard.mark();
720          mHeadingSensor.activate();
721          guard.stop("mHeadingSensor.activate()");
722      }
633      private void initSurfaceTextureConsumer() {634          synchronized (mSurfaceTextureLock) {635              if (mPreviewSurfaceTexture != null) {636                  mPreviewSurfaceTexture.setDefaultBufferSize(
637                          mAppController.getCameraAppUI().getSurfaceWidth(),
638                          mAppController.getCameraAppUI().getSurfaceHeight());
639              }
640          }
641          reopenCamera();
642      }
644      private void reopenCamera() {645          if (mPaused) {646              return;
647          }//使用线程池执行openCameraAndStartPreview
648          AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {649              @Override
650              public void run() {651                  closeCamera();
652                  if(!mAppController.isPaused()) {653                      openCameraAndStartPreview();
654                  }
655              }
656          });
657      }
658

最终打开相机并预览是通过线程池执行了openCameraAndStartPreview

/packages/apps/Camera2/src/com/android/camera/CaptureModule.java
1315      private void openCameraAndStartPreview() {1371          //1.调用mOneCameraOpener.open去打开相机
1372          mOneCameraOpener.open(cameraId, captureSetting, mCameraHandler, mainThread,
1373                imageRotationCalculator, mBurstController, mSoundPlayer,
1374                new OpenCallback() {1375                    @Override
1376                    public void onFailure() {1377                        Log.e(TAG, "Could not open camera.");
1378                        // Sometimes the failure happens due to the controller
1379                        // being in paused state but mCamera is already
1380                        // initialized.  In these cases we just need to close the
1381                        // camera device without showing the error dialog.
1382                        // Application will properly reopen the camera on the next
1383                        // resume operation (b/21025113).
1384                        boolean isControllerPaused = mAppController.isPaused();
1385                        if (mCamera != null) {1386                            mCamera.close();
1387                        }
1388                        mCamera = null;
1389                        mCameraOpenCloseLock.release();
1390                        if (!isControllerPaused) {1391                            mAppController.getFatalErrorHandler().onCameraOpenFailure();
1392                        }
1393                    }
1394
1395                    @Override
1396                    public void onCameraClosed() {1397                        mCamera = null;
1398                        mCameraOpenCloseLock.release();
1399                    }
1400
1401                    @Override
1402                    public void onCameraOpened(@Nonnull final OneCamera camera) {1403                        Log.d(TAG, "onCameraOpened: " + camera);
1404                        mCamera = camera;
1405
1406                        // A race condition exists where the camera may be in the process
1407                        // of opening (blocked), but the activity gets destroyed. If the
1408                        // preview is initialized or callbacks are invoked on a destroyed
1409                        // activity, bad things can happen.
1410                        if (mAppController.isPaused()) {1411                            onFailure();
1412                            return;
1413                        }
1414
1415                        // When camera is opened, the zoom is implicitly reset to 1.0f
1416                        mZoomValue = 1.0f;
1417
1418                        updatePreviewBufferDimension();
1419
1420                        // If the surface texture is not destroyed, it may have
1421                        // the last frame lingering. We need to hold off setting
1422                        // transform until preview is started.
1423                        updatePreviewBufferSize();
1424                        mState = ModuleState.WATCH_FOR_NEXT_FRAME_AFTER_PREVIEW_STARTED;
1425                        Log.d(TAG, "starting preview ...");
1426
1427                        // TODO: make mFocusController final and remove null
1428                        // check.
1429                        if (mFocusController != null) {1430                            camera.setFocusDistanceListener(mFocusController);
1431                        }
1432
1433                        mMainThread.execute(new Runnable() {1434                            @Override
1435                            public void run() {1436                                mAppController.getCameraAppUI().onChangeCamera();
1437                                mAppController.getButtonManager().enableCameraButton();
1438                            }
1439                        });
1440                         //2.开始预览将SurfaceTexture传给Camera用来显示预览的画面
1441                        // TODO: Consider rolling these two calls into one.
1442                        camera.startPreview(new Surface(getPreviewSurfaceTexture()),
1443                              new CaptureReadyCallback() {1444                                  @Override
1445                                  public void onSetupFailed() {1446                                      // We must release this lock here,
1447                                      // before posting to the main handler
1448                                      // since we may be blocked in pause(),
1449                                      // getting ready to close the camera.
1450                                      mCameraOpenCloseLock.release();
1451                                      Log.e(TAG, "Could not set up preview.");
1452                                      mMainThread.execute(new Runnable() {1453                                          @Override
1454                                          public void run() {1455                                              if (mCamera == null) {1456                                                  Log.d(TAG, "Camera closed, aborting.");
1457                                                  return;
1458                                              }
1459                                              mCamera.close();
1460                                              mCamera = null;
1461                                              // TODO: Show an error message
1462                                              // and exit.
1463                                          }
1464                                      });
1465                                  }
1466
1467                                  @Override
1468                                  public void onReadyForCapture() {1469                                      // We must release this lock here,
1470                                      // before posting to the main handler
1471                                      // since we may be blocked in pause(),
1472                                      // getting ready to close the camera.
1473                                      mCameraOpenCloseLock.release();
1474                                      mMainThread.execute(new Runnable() {1475                                          @Override
1476                                          public void run() {1477                                              Log.d(TAG, "Ready for capture.");
1478                                              if (mCamera == null) {1479                                                  Log.d(TAG, "Camera closed, aborting.");
1480                                                  return;
1481                                              }
1482                                              onPreviewStarted();
1483                                              // May be overridden by
1484                                              // subsequent call to
1485                                              // onReadyStateChanged().
1486                                              onReadyStateChanged(true);
1487                                              mCamera.setReadyStateChangedListener(
1488                                                    CaptureModule.this);
1489                                              // Enable zooming after preview
1490                                              // has started.
1491                                              mUI.initializeZoom(mCamera.getMaxZoom());
1492                                              mCamera.setFocusStateListener(CaptureModule.this);
1493                                          }
1494                                      });
1495                                  }
1496                              });
1497                    }
1498                }, mAppController.getFatalErrorHandler());
1499          guard.stop("mOneCameraOpener.open()");
1500      }

这里的mOneCameraOpener是在module在init时从appcontroler中获取到的:

/packages/apps/Camera2/src/com/android/camera/CaptureModule.java
400      @Override
401      public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {402          Profile guard = mProfiler.create("CaptureModule.init").start();
403          Log.d(TAG, "init UseAutotransformUiLayout = " + USE_AUTOTRANSFORM_UI_LAYOUT);
404          HandlerThread thread = new HandlerThread("CaptureModule.mCameraHandler");
405          thread.start();
406          mCameraHandler = new Handler(thread.getLooper());//从mAppController获取mOneCameraOpener
407          mOneCameraOpener = mAppController.getCameraOpener();
408
409          try {410              mOneCameraManager = OneCameraModule.provideOneCameraManager();
411          } catch (OneCameraException e) {412              Log.e(TAG, "Unable to provide a OneCameraManager. ", e);
413          }...
443      }

而mAppController则是在创建module时将CameraActivity传到module中的

2.3 Camera Open

上一节说到调用mOneCameraOpener.open去打开相机,并且mOneCameraOpener也是CameraActivity在onCreateTask后调用 OneCameraModule.provideOneCameraOpener初始化的,

/packages/apps/Camera2/src/com/android/camera/one/OneCameraModule.java
43      public static OneCameraOpener provideOneCameraOpener(
44              OneCameraFeatureConfig featureConfig,
45              Context context,
46              ActiveCameraDeviceTracker activeCameraDeviceTracker,
47              DisplayMetrics displayMetrics) throws OneCameraException {48          Optional<OneCameraOpener> manager = Camera2OneCameraOpenerImpl.create(
49                featureConfig, context, activeCameraDeviceTracker, displayMetrics);
50          if (!manager.isPresent()) {51              manager = LegacyOneCameraOpenerImpl.create();
52          }
53          if (!manager.isPresent()) {54              throw new OneCameraException("No camera manager is available.");
55          }
56          return manager.get();
57      }

在CameraActivity的onCreateTask中创建的mOneCameraOpener是Camera2OneCameraOpenerImpl,最终打开相机调用的是Camera2OneCameraOpenerImpl的open函数:

/packages/apps/Camera2/src/com/android/camera/one/v2/Camera2OneCameraOpenerImpl.java
102      @Override
103      public void open(
104              final CameraId cameraKey,
105              final OneCameraCaptureSetting captureSetting,
106              final Handler handler,
107              final MainThread mainThread,
108              final ImageRotationCalculator imageRotationCalculator,
109              final BurstFacade burstController,
110              final SoundPlayer soundPlayer,
111              final OpenCallback openCallback,
112              final FatalErrorHandler fatalErrorHandler) {113          try {114              Log.i(TAG, "Opening Camera: " + cameraKey);
115
116              mActiveCameraDeviceTracker.onCameraOpening(cameraKey);
117              //最终通过mCameraManager的openCamera调用到CameraServer去打开相机
118              mCameraManager.openCamera(cameraKey.getValue(), new CameraDevice.StateCallback() {119                  // We may get multiple calls to StateCallback, but only the
120                  // first callback indicates the status of the camera-opening
121                  // operation. For example, we may receive onOpened() and later
122                  // onClosed(), but only the first should be relayed to
123                  // openCallback.
124                  private boolean isFirstCallback = true;
125
126                  @Override
127                  public void onDisconnected(CameraDevice device) {128                      if (isFirstCallback) {129                          isFirstCallback = false;
130                          // If the camera is disconnected before it is opened
131                          // then we must call close.
132                          device.close();
133                          openCallback.onCameraClosed();
134                      }
135                  }
136
137                  @Override
138                  public void onClosed(CameraDevice device) {139                      if (isFirstCallback) {140                          isFirstCallback = false;
141                          openCallback.onCameraClosed();
142                      }
143                  }
144
145                  @Override
146                  public void onError(CameraDevice device, int error) {147                      if (isFirstCallback) {148                          isFirstCallback = false;
149                          device.close();
150                          openCallback.onFailure();
151                      } else {152                          // Ensures we handle the case where an error occurs
153                          // after the camera has been opened.
154                          fatalErrorHandler.onGenericCameraAccessFailure();
155                      }
156                  }
157                  //相机打开后回传的CameraDevice
158                  @Override
159                  public void onOpened(CameraDevice device) {160                      if (isFirstCallback) {161                          isFirstCallback = false;
162                          try {163                              CameraCharacteristics characteristics = mCameraManager
164                                      .getCameraCharacteristics(device.getId());
165                              // TODO: Set boolean based on whether HDR+ is
166                              // enabled.//包装成OneCamera回调给了CaptureModule的onCameraOpened
167                              OneCamera oneCamera = OneCameraCreator.create(
168                                      device,
169                                      characteristics,
170                                      mFeatureConfig,
171                                      captureSetting,
172                                      mDisplayMetrics,
173                                      mContext,
174                                      mainThread,
175                                      imageRotationCalculator,
176                                      burstController,
177                                      soundPlayer, fatalErrorHandler);
178
179                              if (oneCamera != null) {180                                  openCallback.onCameraOpened(oneCamera);
181                              } else {182                                  Log.d(TAG, "Could not construct a OneCamera object!");
183                                  openCallback.onFailure();
184                              }
185                          } catch (CameraAccessException e) {186                              Log.d(TAG, "Could not get camera characteristics", e);
187                              openCallback.onFailure();
188                          } catch (OneCameraAccessException e) {189                              Log.d(TAG, "Could not create OneCamera", e);
190                              openCallback.onFailure();
191                          }
192                      }
193                  }
194              }, handler);
195          } catch (CameraAccessException ex) {196              Log.e(TAG, "Could not open camera. " + ex.getMessage());
197              handler.post(new Runnable() {198                  @Override
199                  public void run() {200                      openCallback.onFailure();
201                  }
202              });
203          } catch (UnsupportedOperationException ex) {204              Log.e(TAG, "Could not open camera. " + ex.getMessage());
205              handler.post(new Runnable() {206                  @Override
207                  public void run() {208                      openCallback.onFailure();
209                  }
210              });
211          } catch (SecurityException ex) {212              fatalErrorHandler.onCameraDisabledFailure();
213          }
214      }

打开相机后,在CaptureModule传入的接口OpenCallback的onOpenCamera中开启预览

2.4 onCameraOpened

/packages/apps/Camera2/src/com/android/camera/CaptureModule.java
1401                    @Override
1402                    public void onCameraOpened(@Nonnull final OneCamera camera) {1403                        Log.d(TAG, "onCameraOpened: " + camera);
1404                        mCamera = camera;
1405
1406                        // A race condition exists where the camera may be in the process
1407                        // of opening (blocked), but the activity gets destroyed. If the
1408                        // preview is initialized or callbacks are invoked on a destroyed
1409                        // activity, bad things can happen.
1410                        if (mAppController.isPaused()) {1411                            onFailure();
1412                            return;
1413                        }
1414
1415                        // When camera is opened, the zoom is implicitly reset to 1.0f
1416                        mZoomValue = 1.0f;
1417
1418                        updatePreviewBufferDimension();
1419
1420                        // If the surface texture is not destroyed, it may have
1421                        // the last frame lingering. We need to hold off setting
1422                        // transform until preview is started.
1423                        updatePreviewBufferSize();
1424                        mState = ModuleState.WATCH_FOR_NEXT_FRAME_AFTER_PREVIEW_STARTED;
1425                        Log.d(TAG, "starting preview ...");
1426
1427                        // TODO: make mFocusController final and remove null
1428                        // check.
1429                        if (mFocusController != null) {1430                            camera.setFocusDistanceListener(mFocusController);
1431                        }
1432
1433                        mMainThread.execute(new Runnable() {1434                            @Override
1435                            public void run() {1436                                mAppController.getCameraAppUI().onChangeCamera();
1437                                mAppController.getButtonManager().enableCameraButton();
1438                            }
1439                        });
1440                         //2.开始预览将SurfaceTexture传给Camera用来显示预览的画面
1441                        // TODO: Consider rolling these two calls into one.
1442                        camera.startPreview(new Surface(getPreviewSurfaceTexture()),
1443                              new CaptureReadyCallback() {1444                                  @Override
1445                                  public void onSetupFailed() {1446                                      // We must release this lock here,
1447                                      // before posting to the main handler
1448                                      // since we may be blocked in pause(),
1449                                      // getting ready to close the camera.
1450                                      mCameraOpenCloseLock.release();
1451                                      Log.e(TAG, "Could not set up preview.");
1452                                      mMainThread.execute(new Runnable() {1453                                          @Override
1454                                          public void run() {1455                                              if (mCamera == null) {1456                                                  Log.d(TAG, "Camera closed, aborting.");
1457                                                  return;
1458                                              }
1459                                              mCamera.close();
1460                                              mCamera = null;
1461                                              // TODO: Show an error message
1462                                              // and exit.
1463                                          }
1464                                      });
1465                                  }
1466

在相机打开后会调用startPreview把CameraUI的SurfaceTexture包装成一个Surface发给camera做预览。

2.5小结

总结来看CameraApp在启动后主要就是干了两件事,打开相机,预览相机。其中打开相机主要是通过Camera2OneCameraOpenerImpl调用CameraManager.openCamera,接收到相机的引用后调用oneCamera的startPreview开启预览。后面我们具体来看这两步是如何实现的。

三、拍照流程

通过Android Device Monitor的Hierarchy View可以了解到一个Activity的层级,比如CameraApp启动后的层级如下:

从中可以轻易找到拍照键的id是shutter_button。接着我们在代码库中搜索整个id可以知道他是在CameraAppUI中初始化的。而且ShutterButton是继承于ImageView,当图片被点击时会回到到onShutterButtonClick函数。

/packages/apps/Camera2/src/com/android/camera/app/CameraAppUI.java
1313      public void prepareModuleUI() {...
1337          //拍照按键
1338          mShutterButton = (ShutterButton) mCameraRootView.findViewById(R.id.shutter_button);
1339          addShutterListener(mController.getCurrentModuleController());
1340          addShutterListener(mModeOptionsOverlay);
1341          addShutterListener(this);...
1385      }
1926      public void addShutterListener(ShutterButton.OnShutterButtonListener listener) {1927          mShutterButton.addOnShutterButtonListener(listener);
1928      }

接着addShutterListener主要就是向ShutterButton注册响应函数,其中CameraAppUI中的onShutterButtonClick内部什么也没做,那么拍照的操作被哪里响应了呢?从上面代码中可以看到还另外添加了一个listener,mController.getCurrentModuleController(),之前有提过mController就是CameraActivity。所以getCurrentModuleController调用到了CameraActivity中:

/packages/apps/Camera2/src/com/android/camera/CameraActivity.java
1072      @Override
1073      public ModuleController getCurrentModuleController() {1074          return mCurrentModule;
1075      }

这边返回的是mCurrentModule,之前也说过mCurrentModule 当前的情况下是CaptureModule,所以当拍照按钮被按下时会调用到CaptureModule的onShutterButtonClick:

/packages/apps/Camera2/src/com/android/camera/CaptureModule.java
486      @Override
487      public void onShutterButtonClick() {488          if (mCamera == null) {489              return;
490          }
491          //1.延迟拍照
492          int countDownDuration = mSettingsManager
493                  .getInteger(SettingsManager.SCOPE_GLOBAL, Keys.KEY_COUNTDOWN_DURATION);
494          if (countDownDuration > 0) {495              // Start count down.
496              mAppController.getCameraAppUI().transitionToCancel();
497              mAppController.getCameraAppUI().hideModeOptions();
498              mUI.setCountdownFinishedListener(this);
499              mUI.startCountdown(countDownDuration);
500              // Will take picture later via listener callback.
501          } else {//2.立即拍照
502              takePictureNow();
503          }
504      }

如果是延迟拍照的话当倒计时完成就会调用到CaptureModule的onCountDownFinished,最终也会调用到takePictureNow。

/packages/apps/Camera2/src/com/android/camera/CaptureModule.java
530      private void takePictureNow() {531          if (mCamera == null) {532              Log.i(TAG, "Not taking picture since Camera is closed.");
533              return;
534          }
535          //1.创建并启动CaptureSession
536          CaptureSession session = createAndStartCaptureSession();
537          int orientation = mAppController.getOrientationManager().getDeviceOrientation()
538                  .getDegrees();
539          //2.组装PhotoCaptureParameters
540          // TODO: This should really not use getExternalCacheDir and instead use
541          // the SessionStorage API. Need to sync with gcam if that's OK.
542          PhotoCaptureParameters params = new PhotoCaptureParameters(
543                  session.getTitle(), orientation, session.getLocation(),
544                  mContext.getExternalCacheDir(), this, mPictureSaverCallback,
545                  mHeadingSensor.getCurrentHeading(), mZoomValue, 0);
546          decorateSessionAtCaptureTime(session);//调用Camera的takePicture进行拍照
547          mCamera.takePicture(params, session);
548      }

四 、录像流程

录像流程和拍照的流程其实比较相似,只不过是当录像时CameraActivity的mCurrentModule是VideoModule,当录像按钮按下时则会调用到VideoModule的onShutterButtonClick,如下:

/packages/apps/Camera2/src/com/android/camera/VideoModule.java
689      @Override
690      public void onShutterButtonClick() {691          if (mSwitchingCamera) {692              return;
693          }//如果点击录像按钮时 当前的状态是正在录像,那么就走关闭录像的流程
694          boolean stop = mMediaRecorderRecording;
695
696          if (stop) {697              // CameraAppUI mishandles mode option enable/disable
698              // for video, override that
699              mAppController.getCameraAppUI().enableModeOptions();
700              onStopVideoRecording();
701          } else {702              // CameraAppUI mishandles mode option enable/disable
703              // for video, override that//录像流程走这里
704              mAppController.getCameraAppUI().disableModeOptions();
705              startVideoRecording();
706          }
707          mAppController.setShutterEnabled(false);
708          if (mCameraSettings != null) {709              mFocusManager.onShutterUp(mCameraSettings.getCurrentFocusMode());
710          }
711
712          // Keep the shutter button disabled when in video capture intent
713          // mode and recording is stopped. It'll be re-enabled when
714          // re-take button is clicked.
715          if (!(mIsVideoCaptureIntent && stop)) {716              mHandler.sendEmptyMessageDelayed(MSG_ENABLE_SHUTTER_BUTTON, SHUTTER_BUTTON_TIMEOUT);
717          }
718      }

从上面的代码可以知道当点击录像按钮时,如果当前没有录像那么就调用startVideoRecording开启录像,如果当前正在录像那么就调用onStopVideoRecording停止录像。

4.1 startVideoRecording

 /packages/apps/Camera2/src/com/android/camera/VideoModule.java
43      private void startVideoRecording() {1344          Log.i(TAG, "startVideoRecording: " + Thread.currentThread());
1345          mUI.cancelAnimations();
1346          mUI.setSwipingEnabled(false);
1347          mUI.hidePassiveFocusIndicator();
1348          mAppController.getCameraAppUI().hideCaptureIndicator();
1349          mAppController.getCameraAppUI().setShouldSuppressCaptureIndicator(true);
1350          //1.更新存储信息
1351          mActivity.updateStorageSpaceAndHint(new CameraActivity.OnStorageUpdateDoneListener() {1352              @Override
1353              public void onStorageUpdateDone(long bytes) {1354                  if (bytes <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {1355                      Log.w(TAG, "Storage issue, ignore the start request");
1356                  } else {1357                      if (mCameraDevice == null) {1358                          Log.v(TAG, "in storage callback after camera closed");
1359                          return;
1360                      }
1361                      if (mPaused == true) {1362                          Log.v(TAG, "in storage callback after module paused");
1363                          return;
1364                      }
1365
1366                      // Monkey is so fast so it could trigger startVideoRecording twice. To prevent
1367                      // app crash (b/17313985), do nothing here for the second storage-checking
1368                      // callback because recording is already started.
1369                      if (mMediaRecorderRecording) {1370                          Log.v(TAG, "in storage callback after recording started");
1371                          return;
1372                      }
1373
1374                      mCurrentVideoUri = null;
1375                      //2.初始化MediaRecoder
1376                      initializeRecorder();
1377                      if (mMediaRecorder == null) {1378                          Log.e(TAG, "Fail to initialize media recorder");
1379                          return;
1380                      }
1381
1382                      try {//3.调用mMediaRecorder.start 开始录像
1383                          mMediaRecorder.start(); // Recording is now started
1384                      } catch (RuntimeException e) {1385                          Log.e(TAG, "Could not start media recorder. ", e);
1386                          mAppController.getFatalErrorHandler().onGenericCameraAccessFailure();
1387                          releaseMediaRecorder();
1388                          // If start fails, frameworks will not lock the camera for us.
1389                          mCameraDevice.lock();
1390                          return;
1391                      }...
1422
1423                      setFocusParameters();
1424                      //更新录像的时间
1425                      updateRecordingTime();
1426                      mActivity.enableKeepScreenOn(true);
1427                  }
1428              }
1429          });
1430      }

在startVideoRecording中会首先做一些UI的切换,接着查询存储空间的剩余量,如果剩余空间过小或者camera没打开,那么就无视录像的请求。否则就调用

initializeRecorder初始化MeidaRecoder,接着调用MediaRecorder.start开启录像。

4.2 initializeRecorder

1087      // Prepares media recorder.
1088      private void initializeRecorder() {1089          Log.i(TAG, "initializeRecorder: " + Thread.currentThread());
1090          // If the mCameraDevice is null, then this activity is going to finish
1091          if (mCameraDevice == null) {1092              Log.w(TAG, "null camera proxy, not recording");
1093              return;
1094          }
1095          Intent intent = mActivity.getIntent();
1096          Bundle myExtras = intent.getExtras();
1097
1098          long requestedSizeLimit = 0;
1099          closeVideoFileDescriptor();
1100          mCurrentVideoUriFromMediaSaved = false;//如果是camera app外部的请求
1101          if (mIsVideoCaptureIntent && myExtras != null) {1102              Uri saveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
1103              if (saveUri != null) {1104                  try {1105                      mVideoFileDescriptor =
1106                              mContentResolver.openFileDescriptor(saveUri, "rw");
1107                      mCurrentVideoUri = saveUri;
1108                  } catch (java.io.FileNotFoundException ex) {1109                      // invalid uri
1110                      Log.e(TAG, ex.toString());
1111                  }
1112              }
1113              requestedSizeLimit = myExtras.getLong(MediaStore.EXTRA_SIZE_LIMIT);
1114          } else {//camera app内部的请求
1115              generateVideoValues();
1116              Uri videoTable = MediaStore.Video.Media.getContentUri(
1117                     MediaStore.VOLUME_EXTERNAL_PRIMARY);
1118              Uri videoUri = mContentResolver.insert(videoTable, mCurrentVideoValues);
1119
1120              try {1121                  mVideoFileDescriptor =
1122                          mContentResolver.openFileDescriptor(videoUri, "rw");
1123                  mCurrentVideoUri = videoUri;
1124              } catch (java.io.FileNotFoundException ex) {1125                  // invalid uri
1126                  mContentResolver.delete(videoUri, null, null);
1127                  Log.e(TAG, ex.toString());
1128              }
1129          }
1130          //1.初始化MediaRecorder
1131          mMediaRecorder = new MediaRecorder();
1132          // Unlock the camera object before passing it to media recorder.
1133          mCameraDevice.unlock();
1134          // We rely here on the fact that the unlock call above is synchronous
1135          // and blocks until it occurs in the handler thread. Thereby ensuring
1136          // that we are up to date with handler requests, and if this proxy had
1137          // ever been released by a prior command, it would be null.
1138          Camera camera = mCameraDevice.getCamera();
1139          // If the camera device is null, the camera proxy is stale and recording
1140          // should be ignored.
1141          if (camera == null) {1142              Log.w(TAG, "null camera within proxy, not recording");
1143              return;
1144          }
1145           //2.设置MediaRecorder
1146          mMediaRecorder.setCamera(camera);
1147          mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
1148          mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
1149          mMediaRecorder.setProfile(mProfile);
1150          mMediaRecorder.setVideoSize(mProfile.videoFrameWidth, mProfile.videoFrameHeight);
1151          mMediaRecorder.setMaxDuration(mMaxVideoDurationInMs);
1152
1153          setRecordLocation();
1154
1155          // Set output file using video Uri.
1156          if (mVideoFileDescriptor != null) {1157              mMediaRecorder.setOutputFile(mVideoFileDescriptor.getFileDescriptor());
1158          } else {1159              releaseMediaRecorder();
1160              throw new RuntimeException("No valid video file descriptor");
1161          }
1162
1163          // Set maximum file size.
1164          long maxFileSize = mActivity.getStorageSpaceBytes() - Storage.LOW_STORAGE_THRESHOLD_BYTES;
1165          if (requestedSizeLimit > 0 && requestedSizeLimit < maxFileSize) {1166              maxFileSize = requestedSizeLimit;
1167          }
1168
1169          try {1170              mMediaRecorder.setMaxFileSize(maxFileSize);
1171          } catch (RuntimeException exception) {1172              // We are going to ignore failure of setMaxFileSize here, as
1173              // a) The composer selected may simply not support it, or
1174              // b) The underlying media framework may not handle 64-bit range
1175              // on the size restriction.
1176          }
1177
1178          int sensorOrientation =
1179                  mActivity.getCameraProvider().getCharacteristics(mCameraId).getSensorOrientation();
1180          int deviceOrientation =
1181                  mAppController.getOrientationManager().getDeviceOrientation().getDegrees();
1182          int rotation = CameraUtil.getImageRotation(
1183                  sensorOrientation, deviceOrientation, isCameraFrontFacing());
1184          mMediaRecorder.setOrientationHint(rotation);
1185
1186          try {//调用MediaRecorder的prepare进入准备状态
1187              mMediaRecorder.prepare();
1188          } catch (IOException e) {1189              Log.e(TAG, "prepare failed", e);
1190              releaseMediaRecorder();
1191              throw new RuntimeException(e);
1192          }
1193
1194          mMediaRecorder.setOnErrorListener(this);
1195          mMediaRecorder.setOnInfoListener(this);
1196      }

initializeRecorder主要做了四件事:

1.新建MediaRecorder对象

2.对MeidaRecoder做一些设置,比如设置camera,音视频的资源类型,视频的大小(一帧的宽高),输出文件的路径等等。

3.调用MeidaRecoder.prepare 进入准备状态

4.设置两个回调监听

4.3 onStopVideoRecording

/packages/apps/Camera2/src/com/android/camera/VideoModule.java
654      private void onStopVideoRecording() {655          mAppController.getCameraAppUI().setSwipeEnabled(true);
656          boolean recordFail = stopVideoRecording();
657          if (mIsVideoCaptureIntent) {658              if (mQuickCapture) {659                  doReturnToCaller(!recordFail);
660              } else if (!recordFail) {661                  showCaptureResult();
662              }
663          } else if (!recordFail){664              // Start capture animation.
665              if (!mPaused && ApiHelper.HAS_SURFACE_TEXTURE_RECORDING) {666                  // The capture animation is disabled on ICS because we use SurfaceView
667                  // for preview during recording. When the recording is done, we switch
668                  // back to use SurfaceTexture for preview and we need to stop then start
669                  // the preview. This will cause the preview flicker since the preview
670                  // will not be continuous for a short period of time.
671                  mAppController.startFlashAnimation(false);
672              }
673          }
674      }
1458      private boolean stopVideoRecording() {1459          // Do nothing if camera device is still capturing photo. Monkey test can trigger app crashes
1460          // (b/17313985) without this check. Crash could also be reproduced by continuously tapping
1461          // on shutter button and preview with two fingers.
1462          if (mSnapshotInProgress) {1463              Log.v(TAG, "Skip stopVideoRecording since snapshot in progress");
1464              return true;
1465          }
1466          Log.v(TAG, "stopVideoRecording");
1467
1468          // Re-enable sound as early as possible to avoid interfering with stop
1469          // recording sound.
1470          restoreRingerMode();
1471
1472          mUI.setSwipingEnabled(true);
1473          mUI.showPassiveFocusIndicator();
1474          mAppController.getCameraAppUI().setShouldSuppressCaptureIndicator(false);
1475
1476          boolean fail = false;
1477          if (mMediaRecorderRecording) {1478              boolean shouldAddToMediaStoreNow = false;
1479
1480              try {//1.设置回调为null并调用MediaRecorder的stop函数来停止录像
1481                  mMediaRecorder.setOnErrorListener(null);
1482                  mMediaRecorder.setOnInfoListener(null);
1483                  mMediaRecorder.stop();
1484                  shouldAddToMediaStoreNow = true;
1485              } catch (RuntimeException e) {1486                  Log.e(TAG, "stop fail",  e);
1487                  fail = true;
1488              }
1489              mMediaRecorderRecording = false;
1490              mActivity.unlockOrientation();
1491
1492              // If the activity is paused, this means activity is interrupted
1493              // during recording. Release the camera as soon as possible because
1494              // face unlock or other applications may need to use the camera.
1495              if (mPaused) {1496                  // b/16300704: Monkey is fast so it could pause the module while recording.
1497                  // stopPreview should definitely be called before switching off.
1498                  stopPreview();
1499                  closeCamera();
1500              }
1501
1502              mUI.showRecordingUI(false);
1503              // The orientation was fixed during video recording. Now make it
1504              // reflect the device orientation as video recording is stopped.
1505              mUI.setOrientationIndicator(0, true);
1506              mActivity.enableKeepScreenOn(false);
1507              if (shouldAddToMediaStoreNow && !fail) {1508                  if (mIsVideoCaptureIntent) {1509                      // if no file save is needed, we can show the post capture UI now
1510                      showCaptureResult();
1511                  } else {//把生成的录像文件记录插入到MediaProvider的数据库中
1512                      saveVideo();
1513                  }
1514              }
1515          }
1516          // release media recorder
1517          releaseMediaRecorder();
1518
1519          mAppController.getCameraAppUI().showModeOptions();
1520          mAppController.getCameraAppUI().animateBottomBarToFullSize(mShutterIconId);
1521          if (!mPaused && mCameraDevice != null) {1522              setFocusParameters();
1523              mCameraDevice.lock();
1524              if (!ApiHelper.HAS_SURFACE_TEXTURE_RECORDING) {1525                  stopPreview();
1526                  // Switch back to use SurfaceTexture for preview.
1527                  startPreview();
1528              }
1529              // Update the parameters here because the parameters might have been altered
1530              // by MediaRecorder.
1531              mCameraSettings = mCameraDevice.getSettings();
1532          }
1533
1534          // Check this in advance of each shot so we don't add to shutter
1535          // latency. It's true that someone else could write to the SD card
1536          // in the mean time and fill it, but that could have happened
1537          // between the shutter press and saving the file too.
1538          mActivity.updateStorageSpaceAndHint(null);
1539
1540          return fail;
1541      }

停止录像最终则是调用到了MediaRecoder的stop函数,接着保存记录到数据库中。

关于MeidaRecoder的具体代码可以参考Android Camera模块(三)

Android Camera模块(一)相关推荐

  1. 我心依旧之Android Camera模块FW/HAL3探学序

    前沿: 目前对于Android Camera软硬件技术发展的主流方向是高像素.高帧率.多摄像头.超强的ISP以及各种视频图形处理算法等等.当前主流的Android系统中较为常见的Camera模块还均是 ...

  2. android camera fragment,Android Camera 模块分析(三)

    第三部分 Camera的主要实现分析 3.1 JAVA程序部分 在packages/apps/Camera/src/com/android/camera/ 目录的Camera.java文件中,包含了对 ...

  3. android camera 工作原理,Android Camera原理之openCamera模块(一)

    我们平时开发,知道怎么调度api,怎么调起camera,怎么调用camera的实例来操作camera就可以了,但是这些调度的背后都做了什么事情,我们可能不太清楚,本文打算从openCamera这个调用 ...

  4. Android 7.0 GMS测试 Camera模块CTS fail项分析

    在上一篇博客中我们提到Camera模块fail项,本篇博客我们单独讲解Camera模块的. GMS中涉及Camera的有: 1.CTS部分的CtsCameraTestCases模块 2.CTS VER ...

  5. android camera 降低帧率_Android性能问题分析之bugreport

    Android手机性能问题一直是用户关注的重点,分析性能问题则成为工程师日常工作的一部分.根据问题的类型通常有适合的工具可供使用,比如systrace ,traceview,simpleperf等可视 ...

  6. android camera工程师,浅析Android Camera架构

    本博文是基于Android 4.4讲解 1.application 层: 当我们Android工程师想打开camera时通常直接调用Camera.java中的  Camer.open(cameraId ...

  7. Android Camera设置setPreviewCallback实现onPreviewFrame接口实时截取每一帧视频流数据

    1 概述 通过Android Camera拍摄预览中设置setPreviewCallback实现onPreviewFrame接口,实时截取每一帧视频流数据 2 知识点 ① Android Camera ...

  8. qcom Android Camera【转】

    本文转载自:http://blog.csdn.net/Wilsonboliu/article/details/54949196 1.总体架构 Android Camera 框架从整体上看是一个 cli ...

  9. android camera(三):camera V4L2 FIMC

    关键词: android  camera CMM 模组 camera参数  CAMIF   V4L2   平台信息: 内核: linux 系统: android 平台:S5PV310(samsung ...

最新文章

  1. 国内丨中国人工智能标准化体系建设提速
  2. 【项目】uniapp前端接收后端springboot数据并渲染到界面+使用swiper和uView
  3. Java面试题集(二)list与Map相关知识(1.2)
  4. Nginx Rewrite规则初探
  5. EntLib.com Forum / YAF 开源论坛--源码的目录结构(对分析代码很有帮助)
  6. 安装linux到服务器配置,CentOS 6.3 服务器安装配置
  7. 办公软件自学教程有哪些
  8. Android简单模仿微信扫一扫二维码
  9. jenkins从节点linux安装,linux Jenkins配置salve节点实现过程图解
  10. 2004年11月网络工程师级试题
  11. 439、Java框架93 -【SpringMVC - 拦截器】 2020.12.14
  12. 电子DIY:用单片机设计一款USB游戏手柄
  13. 日语与计算机论文,日语毕业论文指导初探
  14. 浮点数定化--altera 乘除法ip使用FPGA学习笔记
  15. 如何快速设计一款门磁传感器产品?App即可确认门窗关闭
  16. html课堂笔记2.24
  17. 湖北师范大学计算机考研资料汇总
  18. 判断一个数是不是素数
  19. STATCOM主电路拓扑结构分类
  20. 一文总览Polkadot生态

热门文章

  1. Motoman机器人离线编程——Visio Studio环境配置
  2. 深入了解火山PC的编码转换处理
  3. html 特殊符号怎么打出来,怎么打出进度条 电脑上的特殊符号怎么打出来的?
  4. cuda安装失败怎么办
  5. 教练技术培训系列篇三——高效学习
  6. 【python】点坐标旋转
  7. 电子小制作:有声倒计时装置
  8. 用C#实现实时监测文件夹,把word/excel/ppt自动转为pdf
  9. win7 iE8 删除临时文件时 总提示Internet 控制面板已停止工作
  10. parity使用教程