序
想了下还是挤挤时间,把相机这基础流程写完吧,前面每篇写的都还是挺耗时的(就是累了,想偷偷懒,哈哈哈哈),那接着前面的几篇文章,给这一些列写上一个中规中矩的结局吧~
APP层
以下是相机openCamera,configStream,startPreview,capture全过程的示例代码,不用再分段演示了,注释也写的比较详细了。
package com.example.simplecamera;import android.Manifest;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
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.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Size;
import android.view.Surface;
import android.view.SurfaceTexture;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.FrameLayout;import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Locale;public class MainActivity extends AppCompatActivity {// 权限请求码private static final int REQUEST_CAMERA_PERMISSION = 100;// 相机相关变量private String mCameraId;private CameraDevice mCameraDevice;private CameraCaptureSession mCaptureSession;private CaptureRequest.Builder mPreviewRequestBuilder;private Size mPreviewSize;private ImageReader mImageReader;// UI组件private TextureView mTextureView;private Button mCaptureButton;private TextView mStatusText;// 后台线程private HandlerThread mBackgroundThread;private Handler mBackgroundHandler;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 初始化UImTextureView = new TextureView(this);FrameLayout previewContainer = findViewById(R.id.preview_container);previewContainer.addView(mTextureView);mCaptureButton = findViewById(R.id.capture_button);mStatusText = findViewById(R.id.status_text);// 设置拍照按钮点击事件mCaptureButton.setOnClickListener(v -> takePicture());mCaptureButton.setEnabled(false);// 设置TextureView监听mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);}// SurfaceTexture监听,用于处理预览准备就绪private final TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() {@Overridepublic void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {// 检查权限if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.CAMERA},REQUEST_CAMERA_PERMISSION);return;}// 权限已授予,打开相机openCamera(width, height);}@Overridepublic void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}@Overridepublic boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {return true;}@Overridepublic void onSurfaceTextureUpdated(SurfaceTexture surface) {}};// 打开相机private void openCamera(int width, int height) {startBackgroundThread();// 获取相机管理器CameraManager manager = (CameraManager) getSystemService(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;}// 选择合适的预览尺寸mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),width, height);// 初始化ImageReader用于拍照mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(), ImageFormat.JPEG, 1);mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);mCameraId = cameraId;break;}// 打开相机if (mCameraId != null) {if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {return;}manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);} else {showStatus("未找到可用相机");}} catch (CameraAccessException e) {showStatus("打开相机失败: " + e.getMessage());e.printStackTrace();}}// 相机状态回调private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {@Overridepublic void onOpened(@NonNull CameraDevice camera) {mCameraDevice = camera;createCameraPreviewSession();runOnUiThread(() -> {mCaptureButton.setEnabled(true);showStatus("相机已就绪");});}@Overridepublic void onDisconnected(@NonNull CameraDevice camera) {camera.close();mCameraDevice = null;runOnUiThread(() -> {mCaptureButton.setEnabled(false);showStatus("相机已断开");});}@Overridepublic void onError(@NonNull CameraDevice camera, int error) {camera.close();mCameraDevice = null;runOnUiThread(() -> {mCaptureButton.setEnabled(false);showStatus("相机错误: " + error);});}};// 创建相机预览会话private void createCameraPreviewSession() {try {SurfaceTexture texture = mTextureView.getSurfaceTexture();if (texture == null) {throw new NullPointerException("TextureView为null");}// 设置预览尺寸texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());// 创建预览的SurfaceSurface surface = new Surface(texture);// 创建预览请求mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);mPreviewRequestBuilder.addTarget(surface);// 创建相机捕获会话mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),new CameraCaptureSession.StateCallback() {@Overridepublic void onConfigured(@NonNull CameraCaptureSession session) {mCaptureSession = session;try {// 设置自动对焦和自动曝光mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);// 开始预览mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(),null, mBackgroundHandler);} catch (CameraAccessException e) {showStatus("预览失败: " + e.getMessage());e.printStackTrace();}}@Overridepublic void onConfigureFailed(@NonNull CameraCaptureSession session) {showStatus("预览配置失败");}}, null);} catch (CameraAccessException e) {showStatus("创建会话失败: " + e.getMessage());e.printStackTrace();}}// 拍照private void takePicture() {if (mCameraDevice == null || mCaptureSession == null) {Log.w(TAG, "Cannot take picture - camera not ready");return;}try {// 创建拍照请求final CaptureRequest.Builder captureBuilder =mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);captureBuilder.addTarget(mImageReader.getSurface());// 设置自动对焦和曝光captureBuilder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);captureBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,CaptureRequest.CONTROL_AF_TRIGGER_START);captureBuilder.set(CaptureRequest.CONTROL_AE_MODE,CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);// 设置照片方向int rotation = getWindowManager().getDefaultDisplay().getRotation();captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation));mCaptureSession.capture(captureBuilder.build(), captureCallback, mBackgroundHandler);} catch (CameraAccessException e) {showStatus("拍照失败: " + e.getMessage());e.printStackTrace();}}// 处理拍摄的图片private final ImageReader.OnImageAvailableListener mOnImageAvailableListener =new ImageReader.OnImageAvailableListener() {@Overridepublic void onImageAvailable(ImageReader reader) {// 在后台处理图片mBackgroundHandler.post(() -> {Image image = reader.acquireNextImage();if (image != null) {File imageFile = saveImageToFile(image);String message = imageFile != null ? "照片已保存: " + imageFile.getAbsolutePath() : "保存照片失败";runOnUiThread(() -> showStatus(message));image.close();}});}};// 保存图片到文件private File saveImageToFile(Image image) {ByteBuffer buffer = image.getPlanes()[0].getBuffer();byte[] bytes = new byte[buffer.remaining()];buffer.get(bytes);// 创建图片文件String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());String fileName = "IMG_" + timeStamp + ".jpg";File storageDir = getExternalFilesDir(null);File imageFile = new File(storageDir, fileName);FileOutputStream output = null;try {output = new FileOutputStream(imageFile);output.write(bytes);return imageFile;} catch (IOException e) {e.printStackTrace();return null;} finally {if (output != null) {try {output.close();} catch (IOException e) {e.printStackTrace();}}}}// 选择最佳的预览尺寸private Size chooseOptimalSize(Size[] choices, int width, int height) {List<Size> bigEnough = new ArrayList<>();for (Size option : choices) {if (option.getHeight() == option.getWidth() * height / width &&option.getWidth() >= width && option.getHeight() >= height) {bigEnough.add(option);}}if (bigEnough.size() > 0) {return Collections.min(bigEnough, new CompareSizesByArea());} else {return choices[0];}}// 按面积比较尺寸的比较器private static class CompareSizesByArea implements Comparator<Size> {@Overridepublic int compare(Size lhs, Size rhs) {return Long.signum((long) lhs.getWidth() * lhs.getHeight() -(long) rhs.getWidth() * rhs.getHeight());}}// 获取照片方向private int getOrientation(int rotation) {switch (rotation) {case Surface.ROTATION_0: return 90;case Surface.ROTATION_90: return 0;case Surface.ROTATION_180: return 270;case Surface.ROTATION_270: return 180;default: return 90;}}// 启动后台线程private void startBackgroundThread() {mBackgroundThread = new HandlerThread("CameraBackground");mBackgroundThread.start();mBackgroundHandler = new Handler(mBackgroundThread.getLooper());}// 停止后台线程private void stopBackgroundThread() {mBackgroundThread.quitSafely();try {mBackgroundThread.join();mBackgroundThread = null;mBackgroundHandler = null;} catch (InterruptedException e) {e.printStackTrace();}}// 显示状态信息private void showStatus(String message) {runOnUiThread(() -> {mStatusText.setText(message);Toast.makeText(this, message, Toast.LENGTH_SHORT).show();});}// 权限请求结果处理@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,@NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);if (requestCode == REQUEST_CAMERA_PERMISSION) {if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {// 权限已授予,重新尝试打开相机if (mTextureView.isAvailable()) {openCamera(mTextureView.getWidth(), mTextureView.getHeight());}} else {showStatus("需要相机权限才能使用应用");}}}@Overrideprotected void onResume() {super.onResume();startBackgroundThread();if (mTextureView.isAvailable()) {openCamera(mTextureView.getWidth(), mTextureView.getHeight());} else {mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);}}@Overrideprotected void onPause() {closeCamera();stopBackgroundThread();super.onPause();}// 关闭相机private void closeCamera() {if (mCaptureSession != null) {mCaptureSession.close();mCaptureSession = null;}if (mCameraDevice != null) {mCameraDevice.close();mCameraDevice = null;}if (mImageReader != null) {mImageReader.close();mImageReader = null;}}
}
需要明确拍照过程是基于打开相机,配流之后的一个流程,所以点击拍照按钮进行拍照流程的时候需要判断相机连接,配流过程是否正常,即需要判断
if (mCameraDevice == null || mCaptureSession == null) {Log.w(TAG, "Cannot take picture - camera not ready");return;}
如果都正常了,那就开始本文的关键流程分析,如下:
mCaptureSession.capture(captureBuilder.build(), captureCallback, mBackgroundHandler);
Framework
由前面的文章介绍,我们就知道mCaptureSession其实就是CameraCaptureSessionImpl实例化对象
,所以继续看
frameworks/base/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@Overridepublic int capture(CaptureRequest request, CaptureCallback callback,Handler handler) throws CameraAccessException {checkCaptureRequest(request);synchronized (mDeviceImpl.mInterfaceLock) {checkNotClosed();handler = checkHandler(handler, callback);if (DEBUG) {Log.v(TAG, mIdString + "capture - request " + request + ", callback " + callback +" handler " + handler);}//看到这里是不是很熟悉,是不是跟上一篇讲解setRepeatingRequest过程很像//即主要看参数的函数调用mDeviceImpl.capturereturn addPendingSequence(mDeviceImpl.capture(request,createCaptureCallbackProxy(handler, callback), mDeviceExecutor));}}
这个时候就接着跳转到CameraDeviceImpl类看capture()实现了
frameworks/base/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
public int capture(CaptureRequest request, CaptureCallback callback, Executor executor)throws CameraAccessException {if (DEBUG) {Log.d(TAG, "calling capture");}List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();requestList.add(request);return submitCaptureRequest(requestList, callback, executor, /*streaming*/false);}
然后直接看下一步,关键流程已加注释
private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback,Executor executor, boolean repeating) throws CameraAccessException {//这里带的参数是false,说明了拍照的时候是不需要停预览的,上一篇起预览的时候//这里是true,是必定要停止上一次预览连接的if (repeating) {stopRepeating();}//以下是关键函数requestInfo = mRemoteDevice.submitRequestList(requestArray, repeating);return requestInfo.getRequestId();}
通过binder调用到这里了
frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
binder::Status CameraDeviceClient::submitRequestList(const std::vector<hardware::camera2::CaptureRequest>& requests,bool streaming,/*out*/hardware::camera2::utils::SubmitInfo *submitInfo) {if (streaming) {//预览环节,上一章跟踪的这个流程err = mDevice->setStreamingRequestList(metadataRequestList, surfaceMapList,&(submitInfo->mLastFrameNumber));} else {//拍照环节,这章跟踪这一个流程err = mDevice->captureList(metadataRequestList, surfaceMapList,&(submitInfo->mLastFrameNumber));}return res;
}
继续
frameworks/av/services/camera/libcameraservice/device3/Camera3Device.cpp
status_t Camera3Device::captureList(const List<const PhysicalCameraSettingsList> &requestsList,const std::list<const SurfaceMap> &surfaceMaps,int64_t *lastFrameNumber) {ATRACE_CALL();return submitRequestsHelper(requestsList, surfaceMaps, /*repeating*/false, lastFrameNumber);
}
注意这个时候传的repeating是false了
status_t Camera3Device::submitRequestsHelper(const List<const PhysicalCameraSettingsList> &requests,const std::list<const SurfaceMap> &surfaceMaps,bool repeating,/*out*/int64_t *lastFrameNumber) {if (repeating) {//预览流程res = mRequestThread->setRepeatingRequests(requestList, lastFrameNumber);} else {//拍照流程res = mRequestThread->queueRequestList(requestList, lastFrameNumber);}return res;}
status_t Camera3Device::RequestThread::queueRequestList(List<sp<CaptureRequest> > &requests,/*out*/int64_t *lastFrameNumber) {ATRACE_CALL();Mutex::Autolock l(mRequestLock);for (List<sp<CaptureRequest> >::iterator it = requests.begin(); it != requests.end();++it) {//添加到请求队列mRequestQueue.push_back(*it);}if (lastFrameNumber != NULL) {*lastFrameNumber = mFrameNumber + mRequestQueue.size() - 1;ALOGV("%s: requestId %d, mFrameNumber %" PRId32 ", lastFrameNumber %" PRId64 ".",__FUNCTION__, (*(requests.begin()))->mResultExtras.requestId, mFrameNumber,*lastFrameNumber);}unpauseForNewRequests();return OK;
}
然后回到RequestThread
bool Camera3Device::RequestThread::threadLoop() //省略n行代码,以下流程跟预览流程一致,只提出来关键函数waitForNextRequestBatch();if (mNextRequests.size() == 0) {return true;}res = prepareHalRequests();submitRequestSuccess = sendRequestsBatch();return submitRequestSuccess;
}
hardware/qcom/camera/msm8998/QCamera2/HAL3/QCamera3HWI.cpp
int QCamera3HardwareInterface::processCaptureRequest(camera3_capture_request_t *request,List<InternalRequest> &internallyRequestedStreams)
{
//省略内容
}
这咋跟预览一样一样的,很明显,我!跟!丢!了!
回去!
frameworks/av/services/camera/libcameraservice/device3/Camera3Device.cpp
status_t Camera3Device::submitRequestsHelper(const List<const PhysicalCameraSettingsList> &requests,const std::list<const SurfaceMap> &surfaceMaps,bool repeating,/*out*/int64_t *lastFrameNumber) {if (repeating) {//预览流程res = mRequestThread->setRepeatingRequests(requestList, lastFrameNumber);} else {//拍照流程res = mRequestThread->queueRequestList(requestList, lastFrameNumber);}return res;}
仔细观察一下这里的实现有什么不一样呢
从上面代码可以发现capture流程会被放在mRequestQueue里;repeating 流程会放在mRepeatingRequests,在threadLoop() 的waitForNextRequestBatch() 可以知道,capture流程会先塞进请求队列里面
但好像只知道这些也不足以hal层区分拍照和预览啊,其实主要还有一个点漏了,就是APP请求拍照时候,还设置了请求的参数,其实这个就是区分的关键
拍照:
// 创建拍照请求
final CaptureRequest.Builder captureBuilder =mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE)
预览:
// 创建预览请求
mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
下发链路可概括为:
应用层设置意图 → Framework 层(CameraService)校验、打包为 camera3_capture_request_t → 通过 HIDL 接口跨进程传递到 HAL → HAL 层解析元数据识别预览,拍照意图。
整个过程中,Framework 层负责元数据的序列化和跨进程传递,HAL 层通过解析元数据中的具体值(1 对应 PREVIEW)来区分请求类型,最终路由到对应的处理逻辑,本章就说这么多了,其他的如驱动控制,数据编码,数据回传等后面有空再搞了,困了。
大概总结一下拍照全流程:
应用层(发起拍照请求)→ 框架层(校验/封装请求)→ HAL层(解析/配置硬件)→ 驱动层(控制传感器)→ 硬件(采集/处理数据)
→ 驱动层(传输数据)→ HAL层(写入缓冲区)→ 框架层(通知应用)→ 应用层(读取/保存数据)
简单看了下应该没啥大问题,睡觉!