API 21中将原来的camera API弃用转而推荐使用新增的camera2 API,这是一个大的动作,因为新API换了架构,让开发者用起来更难了。
先来看看camera2包架构示意图:
这里引用了管道的概念将安卓设备和摄像头之间联通起来,系统向摄像头发送Capture请求,而摄像头会返回CameraMetadata。这一切建立在一个叫作CameraCaptureSession的会话中。
下面是camera2包中的主要类:
其中CameraManager是那个站在高处统管所有摄像投设备(CameraDevice)的管理者,而每个CameraDevice自己会负责建立CameraCaptureSession以及建立CaptureRequest。CameraCharacteristics是CameraDevice的属性描述类,非要做个对比的话,那么它与原来的CameraInfo有相似性。
类图中有着三个重要的callback,虽然这增加了阅读代码的难度,但是你必须要习惯,因为这是新包的风格。其中CameraCaptureSession.CaptureCallback将处理预览和拍照图片的工作,需要重点对待。
这些类是如何相互配合的?下面是简单的流程图。
我是用SurfaceView作为显示对象(当然还可以TextureView去显示,详见参考中的项目)
核心代码如下:
<code class="hljs java has-numbering"> mCameraManager = (CameraManager) <span class="hljs-keyword">this</span>.getSystemService(Context.CAMERA_SERVICE);
mSurfaceView = (SurfaceView)findViewById(R.id.surfaceview);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(<span class="hljs-keyword">new</span> SurfaceHolder.Callback() {
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">surfaceCreated</span>(SurfaceHolder holder) {
initCameraAndPreview();
}
});</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li></ul><code class="hljs avrasm has-numbering"> private void initCameraAndPreview() {
Log<span class="hljs-preprocessor">.d</span>(<span class="hljs-string">"linc"</span>,<span class="hljs-string">"init camera and preview"</span>)<span class="hljs-comment">;</span>
HandlerThread handlerThread = new HandlerThread(<span class="hljs-string">"Camera2"</span>)<span class="hljs-comment">;</span>
handlerThread<span class="hljs-preprocessor">.start</span>()<span class="hljs-comment">;</span>
mHandler = new Handler(handlerThread<span class="hljs-preprocessor">.getLooper</span>())<span class="hljs-comment">;</span>
try {
mCameraId = <span class="hljs-string">""</span>+CameraCharacteristics<span class="hljs-preprocessor">.LENS</span>_FACING_FRONT<span class="hljs-comment">;</span>
mImageReader = ImageReader<span class="hljs-preprocessor">.newInstance</span>(mSurfaceView<span class="hljs-preprocessor">.getWidth</span>(), mSurfaceView<span class="hljs-preprocessor">.getHeight</span>(),
ImageFormat<span class="hljs-preprocessor">.JPEG</span>,<span class="hljs-comment">/*maxImages*/</span><span class="hljs-number">7</span>)<span class="hljs-comment">;</span>
mImageReader<span class="hljs-preprocessor">.setOnImageAvailableListener</span>(mOnImageAvailableListener, mHandler)<span class="hljs-comment">;</span>
mCameraManager<span class="hljs-preprocessor">.openCamera</span>(mCameraId, DeviceStateCallback, mHandler)<span class="hljs-comment">;</span>
} catch (CameraAccessException e) {
Log<span class="hljs-preprocessor">.e</span>(<span class="hljs-string">"linc"</span>, <span class="hljs-string">"open camera failed."</span> + e<span class="hljs-preprocessor">.getMessage</span>())<span class="hljs-comment">;</span>
}
}</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li></ul><code class="hljs java has-numbering"><span class="hljs-keyword">private</span> CameraDevice.StateCallback DeviceStateCallback = <span class="hljs-keyword">new</span> CameraDevice.StateCallback() {
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onOpened</span>(CameraDevice camera) {
Log.d(<span class="hljs-string">"linc"</span>,<span class="hljs-string">"DeviceStateCallback:camera was opend."</span>);
mCameraOpenCloseLock.release();
mCameraDevice = camera;
<span class="hljs-keyword">try</span> {
createCameraCaptureSession();
} <span class="hljs-keyword">catch</span> (CameraAccessException e) {
e.printStackTrace();
}
}
};</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li></ul><code class="hljs avrasm has-numbering"> private void createCameraCaptureSession() throws CameraAccessException {
Log<span class="hljs-preprocessor">.d</span>(<span class="hljs-string">"linc"</span>,<span class="hljs-string">"createCameraCaptureSession"</span>)<span class="hljs-comment">;</span>
mPreviewBuilder = mCameraDevice<span class="hljs-preprocessor">.createCaptureRequest</span>(CameraDevice<span class="hljs-preprocessor">.TEMPLATE</span>_PREVIEW)<span class="hljs-comment">;</span>
mPreviewBuilder<span class="hljs-preprocessor">.addTarget</span>(mSurfaceHolder<span class="hljs-preprocessor">.getSurface</span>())<span class="hljs-comment">;</span>
mState = STATE_PREVIEW<span class="hljs-comment">;</span>
mCameraDevice<span class="hljs-preprocessor">.createCaptureSession</span>(
Arrays<span class="hljs-preprocessor">.asList</span>(mSurfaceHolder<span class="hljs-preprocessor">.getSurface</span>(), mImageReader<span class="hljs-preprocessor">.getSurface</span>()),
mSessionPreviewStateCallback, mHandler)<span class="hljs-comment">;</span>
}</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li></ul><code class="hljs avrasm has-numbering">private CameraCaptureSession<span class="hljs-preprocessor">.StateCallback</span> mSessionPreviewStateCallback = new
CameraCaptureSession<span class="hljs-preprocessor">.StateCallback</span>() {
@Override
public void onConfigured(CameraCaptureSession session) {
Log<span class="hljs-preprocessor">.d</span>(<span class="hljs-string">"linc"</span>,<span class="hljs-string">"mSessionPreviewStateCallback onConfigured"</span>)<span class="hljs-comment">;</span>
mSession = session<span class="hljs-comment">;</span>
try {
mPreviewBuilder<span class="hljs-preprocessor">.set</span>(CaptureRequest<span class="hljs-preprocessor">.CONTROL</span>_AF_MODE,
CaptureRequest<span class="hljs-preprocessor">.CONTROL</span>_AF_MODE_CONTINUOUS_PICTURE)<span class="hljs-comment">;</span>
mPreviewBuilder<span class="hljs-preprocessor">.set</span>(CaptureRequest<span class="hljs-preprocessor">.CONTROL</span>_AE_MODE,
CaptureRequest<span class="hljs-preprocessor">.CONTROL</span>_AE_MODE_ON_AUTO_FLASH)<span class="hljs-comment">;</span>
session<span class="hljs-preprocessor">.setRepeatingRequest</span>(mPreviewBuilder<span class="hljs-preprocessor">.build</span>(), mSessionCaptureCallback, mHandler)<span class="hljs-comment">;</span>
} catch (CameraAccessException e) {
e<span class="hljs-preprocessor">.printStackTrace</span>()<span class="hljs-comment">;</span>
Log<span class="hljs-preprocessor">.e</span>(<span class="hljs-string">"linc"</span>,<span class="hljs-string">"set preview builder failed."</span>+e<span class="hljs-preprocessor">.getMessage</span>())<span class="hljs-comment">;</span>
}
}
}<span class="hljs-comment">;</span></code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li></ul><code class="hljs java has-numbering"><span class="hljs-keyword">private</span> CameraCaptureSession.CaptureCallback mSessionCaptureCallback =
<span class="hljs-keyword">new</span> CameraCaptureSession.CaptureCallback() {
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCaptureCompleted</span>(CameraCaptureSession session, CaptureRequest request,
TotalCaptureResult result) {
<span class="hljs-comment">// Log.d("linc","mSessionCaptureCallback, onCaptureCompleted");</span>
mSession = session;
checkState(result);
}
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCaptureProgressed</span>(CameraCaptureSession session, CaptureRequest request,
CaptureResult partialResult) {
Log.d(<span class="hljs-string">"linc"</span>,<span class="hljs-string">"mSessionCaptureCallback, onCaptureProgressed"</span>);
mSession = session;
checkState(partialResult);
}
<span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">checkState</span>(CaptureResult result) {
<span class="hljs-keyword">switch</span> (mState) {
<span class="hljs-keyword">case</span> STATE_PREVIEW:
<span class="hljs-comment">// NOTHING</span>
<span class="hljs-keyword">break</span>;
<span class="hljs-keyword">case</span> STATE_WAITING_CAPTURE:
<span class="hljs-keyword">int</span> afState = result.get(CaptureResult.CONTROL_AF_STATE);
<span class="hljs-keyword">if</span> (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState ||
CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState
|| CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED == afState
|| CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED == afState) {
<span class="hljs-comment">//do something like save picture</span>
}
<span class="hljs-keyword">break</span>;
}
}
};</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li></ul>按下capture按钮:
<code class="hljs cs has-numbering"> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCapture</span>(View view) {
<span class="hljs-keyword">try</span> {
Log.i(<span class="hljs-string">"linc"</span>, <span class="hljs-string">"take picture"</span>);
mState = STATE_WAITING_CAPTURE;
mSession.setRepeatingRequest(mPreviewBuilder.build(), mSessionCaptureCallback, mHandler);
} <span class="hljs-keyword">catch</span> (CameraAccessException e) {
e.printStackTrace();
}
}</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li></ul>测试用genemotion模拟器,直接调用笔记本的摄像头。
配置图如下:
demo界面如下图:
源码:
请参考github中的两个demo项目:
https://github.com/pinguo-yuyidong/Camera2
https://github.com/googlesamples/android-Camera2Basic