打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
基于Android的Camera2封装,可以实时获取到预览画面和图片
userphoto

2022.05.21 河北

关注

    Android的Camera比较难用,从一代到二代的API接口,对开发者都不友好,这两天正好做个项目,要实时获取到预览画面的图片,使用Camera2做,看了Google的Demo,只能拍照,没法有实时回调,类似于Camera1的OnPreviewCallBack,查阅得知,Camera2用ImageReader来实现,基于源码,自己封装了一个类,先看看分装后的写法

  1. public class MainActivity extends AppCompatActivity {
  2. private AutoFitTextureView mTextureView;
  3. private Camera2Helper helper;
  4. @Override
  5. protected void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. setContentView(R.layout.activity_main);
  8. init();
  9. }
  10. @Override
  11. public void onResume() {
  12. super.onResume();
  13. helper.open(); //会动态请求权限,请重写 onRequestPermissionsResult
  14. }
  15. public void init() {
  16. mTextureView = findViewById(R.id.texture);
  17. helper = new Camera2Helper(this, mTextureView);
  18. helper.setOnImageAvailableListener(new Camera2Helper.OnPreviewCallbackListener() {
  19. @Override
  20. public void onImageAvailable(Image image) {
  21. Log.d("weijw1", "helper onImageAvailable");
  22. }
  23. });
  24. }
  25. @Override
  26. public void onPause() {
  27. helper.closeCamera();
  28. super.onPause();
  29. }
  30. @Override
  31. public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
  32. @NonNull int[] grantResults) {
  33. if (requestCode == helper.getCameraRequestCode()) {
  34. if (grantResults.length != 1 || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
  35. Toast.makeText(getApplicationContext(), R.string.request_permission, Toast.LENGTH_SHORT).show();
  36. }
  37. } else {
  38. super.onRequestPermissionsResult(requestCode, permissions, grantResults);
  39. }
  40. }
  41. }

封装的类,交Camera2Helper,对外常用的接口,就只有 open,closeCamera,和一个setOnImageAvailableListener来接收回调

  1. public class Camera2Helper {
  2. private static final String TAG = "Camera2Helper";
  3. private Activity mActivity;
  4. private AutoFitTextureView mTextureView;
  5. private HandlerThread mBackgroundThread;
  6. private Handler mBackgroundHandler;
  7. private CaptureRequest.Builder mPreviewRequestBuilder;
  8. private CaptureRequest mPreviewRequest;
  9. private static final int REQUEST_CAMERA_PERMISSION = 1;
  10. private ImageReader mImageReader;
  11. private static final int STATE_PREVIEW = 0;
  12. private static final int STATE_WAITING_LOCK = 1;
  13. private static final int STATE_WAITING_PRECAPTURE = 2;
  14. private static final int STATE_WAITING_NON_PRECAPTURE = 3;
  15. private static final int STATE_PICTURE_TAKEN = 4;
  16. private static final int MAX_PREVIEW_WIDTH = 1920;
  17. private static final int MAX_PREVIEW_HEIGHT = 1080;
  18. private int mImageFormat = ImageFormat.YUV_420_888;
  19. private int mState = STATE_PREVIEW;
  20. private int mSensorOrientation; //Orientation of the camera sensor
  21. private String mCameraId;
  22. private Semaphore mCameraOpenCloseLock = new Semaphore(1);
  23. private CameraCaptureSession mCaptureSession;
  24. private CameraDevice mCameraDevice;
  25. private Size mPreviewSize;
  26. private OnOpenErrorListener mOpenErrorListener; //打开错误,不设置才用默认策略
  27. private OnPreviewCallbackListener mImageAvaiableListener; //摄像头画面可达的时候
  28. public Camera2Helper(@NonNull Activity activity, @NonNull AutoFitTextureView textureView) {
  29. this.mActivity = activity;
  30. this.mTextureView = textureView;
  31. }
  32. /**
  33. * {@link TextureView.SurfaceTextureListener} handles several lifecycle events on a
  34. * {@link TextureView}.
  35. */
  36. private final TextureView.SurfaceTextureListener mSurfaceTextureListener
  37. = new TextureView.SurfaceTextureListener() {
  38. @Override
  39. public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {
  40. openCamera(width, height);
  41. }
  42. @Override
  43. public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) {
  44. configureTransform(width, height);
  45. }
  46. @Override
  47. public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
  48. return true;
  49. }
  50. @Override
  51. public void onSurfaceTextureUpdated(SurfaceTexture texture) {
  52. }
  53. };
  54. /**
  55. * {@link CameraDevice.StateCallback} is called when {@link CameraDevice} changes its state.
  56. */
  57. private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
  58. @Override
  59. public void onOpened(@NonNull CameraDevice cameraDevice) {
  60. // This method is called when the camera is opened. We start camera preview here.
  61. mCameraOpenCloseLock.release();
  62. mCameraDevice = cameraDevice;
  63. createCameraPreviewSession();
  64. }
  65. @Override
  66. public void onDisconnected(@NonNull CameraDevice cameraDevice) {
  67. mCameraOpenCloseLock.release();
  68. cameraDevice.close();
  69. mCameraDevice = null;
  70. }
  71. @Override
  72. public void onError(@NonNull CameraDevice cameraDevice, int error) {
  73. mCameraOpenCloseLock.release();
  74. cameraDevice.close();
  75. mCameraDevice = null;
  76. if (mOpenErrorListener != null) {
  77. mOpenErrorListener.onOpenError();
  78. } else {
  79. mActivity.finish();
  80. }
  81. }
  82. };
  83. /**
  84. * A {@link CameraCaptureSession.CaptureCallback} that handles events related to JPEG capture.
  85. */
  86. private CameraCaptureSession.CaptureCallback mCaptureCallback
  87. = new CameraCaptureSession.CaptureCallback() {
  88. private void process(CaptureResult result) {
  89. switch (mState) {
  90. case STATE_PREVIEW: {
  91. // We have nothing to do when the camera preview is working normally.
  92. break;
  93. }
  94. case STATE_WAITING_LOCK: {
  95. Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
  96. if (afState == null) {
  97. } else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState ||
  98. CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) {
  99. // CONTROL_AE_STATE can be null on some devices
  100. Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
  101. if (aeState == null ||
  102. aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
  103. mState = STATE_PICTURE_TAKEN;
  104. } else {
  105. runPrecaptureSequence();
  106. }
  107. }
  108. break;
  109. }
  110. case STATE_WAITING_PRECAPTURE: {
  111. // CONTROL_AE_STATE can be null on some devices
  112. Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
  113. if (aeState == null ||
  114. aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE ||
  115. aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) {
  116. mState = STATE_WAITING_NON_PRECAPTURE;
  117. }
  118. break;
  119. }
  120. case STATE_WAITING_NON_PRECAPTURE: {
  121. // CONTROL_AE_STATE can be null on some devices
  122. Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
  123. if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
  124. mState = STATE_PICTURE_TAKEN;
  125. }
  126. break;
  127. }
  128. }
  129. }
  130. @Override
  131. public void onCaptureProgressed(@NonNull CameraCaptureSession session,
  132. @NonNull CaptureRequest request,
  133. @NonNull CaptureResult partialResult) {
  134. process(partialResult);
  135. }
  136. @Override
  137. public void onCaptureCompleted(@NonNull CameraCaptureSession session,
  138. @NonNull CaptureRequest request,
  139. @NonNull TotalCaptureResult result) {
  140. process(result);
  141. }
  142. };
  143. /**
  144. * Run the precapture sequence for capturing a still image. This method should be called when
  145. * we get a response in {@link #mCaptureCallback} from {}.
  146. */
  147. private void runPrecaptureSequence() {
  148. try {
  149. // This is how to tell the camera to trigger.
  150. mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
  151. CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
  152. // Tell #mCaptureCallback to wait for the precapture sequence to be set.
  153. mState = STATE_WAITING_PRECAPTURE;
  154. mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
  155. mBackgroundHandler);
  156. } catch (CameraAccessException e) {
  157. e.printStackTrace();
  158. }
  159. }
  160. /**
  161. * Creates a new {@link CameraCaptureSession} for camera preview.
  162. */
  163. private void createCameraPreviewSession() {
  164. try {
  165. SurfaceTexture texture = mTextureView.getSurfaceTexture();
  166. assert texture != null;
  167. // We configure the size of default buffer to be the size of camera preview we want.
  168. texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
  169. // This is the output Surface we need to start preview.
  170. Surface surface = new Surface(texture);
  171. // We set up a CaptureRequest.Builder with the output Surface.
  172. mPreviewRequestBuilder
  173. = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
  174. mPreviewRequestBuilder.addTarget(surface);
  175. mPreviewRequestBuilder.addTarget(mImageReader.getSurface()); //不加这句话没有回调
  176. // Here, we create a CameraCaptureSession for camera preview.
  177. mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
  178. new CameraCaptureSession.StateCallback() {
  179. @Override
  180. public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
  181. // The camera is already closed
  182. if (null == mCameraDevice) {
  183. return;
  184. }
  185. // When the session is ready, we start displaying the preview.
  186. mCaptureSession = cameraCaptureSession;
  187. try {
  188. // Auto focus should be continuous for camera preview.
  189. mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
  190. CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
  191. // Flash is automatically enabled when necessary.
  192. // Finally, we start displaying the camera preview.
  193. mPreviewRequest = mPreviewRequestBuilder.build();
  194. mCaptureSession.setRepeatingRequest(mPreviewRequest,
  195. mCaptureCallback, mBackgroundHandler);
  196. } catch (CameraAccessException e) {
  197. e.printStackTrace();
  198. }
  199. }
  200. @Override
  201. public void onConfigureFailed(
  202. @NonNull CameraCaptureSession cameraCaptureSession) {
  203. Toast.makeText(mActivity, "Config Session Failed", Toast.LENGTH_SHORT).show();
  204. }
  205. }, null
  206. );
  207. } catch (CameraAccessException e) {
  208. e.printStackTrace();
  209. }
  210. }
  211. private void requestCameraPermission() {
  212. ActivityCompat.requestPermissions(mActivity, new String[]{
  213. Manifest.permission.CAMERA,
  214. }, REQUEST_CAMERA_PERMISSION);
  215. }
  216. public void open() {
  217. startBackgroundThread();
  218. // When the screen is turned off and turned back on, the SurfaceTexture is already
  219. // available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open
  220. // a camera and start preview from here (otherwise, we wait until the surface is ready in
  221. // the SurfaceTextureListener).
  222. if (mTextureView.isAvailable()) {
  223. openCamera(mTextureView.getWidth(), mTextureView.getHeight());
  224. } else {
  225. mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
  226. }
  227. }
  228. /**
  229. * Opens the camera specified
  230. */
  231. private void openCamera(int width, int height) {
  232. if (ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.CAMERA)
  233. != PackageManager.PERMISSION_GRANTED) {
  234. requestCameraPermission();
  235. return;
  236. }
  237. setUpCameraOutputs(width, height);
  238. configureTransform(width, height);
  239. CameraManager manager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE);
  240. if (manager == null) {
  241. return;
  242. }
  243. try {
  244. if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
  245. throw new RuntimeException("Time out waiting to lock camera opening.");
  246. }
  247. manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
  248. } catch (CameraAccessException e) {
  249. e.printStackTrace();
  250. } catch (InterruptedException e) {
  251. throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
  252. }
  253. }
  254. /**
  255. * Sets up member variables related to camera.
  256. *
  257. * @param width The width of available size for camera preview
  258. * @param height The height of available size for camera preview
  259. */
  260. @SuppressWarnings("SuspiciousNameCombination")
  261. private void setUpCameraOutputs(int width, int height) {
  262. CameraManager manager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE);
  263. if (manager == null) {
  264. return;
  265. }
  266. try {
  267. for (String cameraId : manager.getCameraIdList()) {
  268. CameraCharacteristics characteristics
  269. = manager.getCameraCharacteristics(cameraId);
  270. // We don't use a front facing camera in this sample.
  271. Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
  272. if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {
  273. continue;
  274. }
  275. StreamConfigurationMap map = characteristics.get(
  276. CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
  277. if (map == null) {
  278. continue;
  279. }
  280. // For still image captures, we use the largest availablIe size.
  281. Size largest = Collections.max(
  282. Arrays.asList(map.getOutputSizes(mImageFormat)),
  283. new CompareSizesByArea());
  284. mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(),
  285. mImageFormat, /*maxImages*/2);
  286. mImageReader.setOnImageAvailableListener(
  287. mOnImageAvailableListener, mBackgroundHandler);
  288. // Find out if we need to swap dimension to get the preview size relative to sensor
  289. // coordinate.
  290. int displayRotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
  291. //noinspection ConstantConditions
  292. mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
  293. boolean swappedDimensions = false;
  294. switch (displayRotation) {
  295. case Surface.ROTATION_0:
  296. case Surface.ROTATION_180:
  297. if (mSensorOrientation == 90 || mSensorOrientation == 270) {
  298. swappedDimensions = true;
  299. }
  300. break;
  301. case Surface.ROTATION_90:
  302. case Surface.ROTATION_270:
  303. if (mSensorOrientation == 0 || mSensorOrientation == 180) {
  304. swappedDimensions = true;
  305. }
  306. break;
  307. default:
  308. Log.e(TAG, "Display rotation is invalid: " + displayRotation);
  309. }
  310. Point displaySize = new Point();
  311. mActivity.getWindowManager().getDefaultDisplay().getSize(displaySize);
  312. int rotatedPreviewWidth = width;
  313. int rotatedPreviewHeight = height;
  314. int maxPreviewWidth = displaySize.x;
  315. int maxPreviewHeight = displaySize.y;
  316. if (swappedDimensions) {
  317. rotatedPreviewWidth = height;
  318. rotatedPreviewHeight = width;
  319. maxPreviewWidth = displaySize.y;
  320. maxPreviewHeight = displaySize.x;
  321. }
  322. if (maxPreviewWidth > MAX_PREVIEW_WIDTH) {
  323. maxPreviewWidth = MAX_PREVIEW_WIDTH;
  324. }
  325. if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) {
  326. maxPreviewHeight = MAX_PREVIEW_HEIGHT;
  327. }
  328. // Danger, W.R.! Attempting to use too large a preview size could exceed the camera
  329. // bus' bandwidth limitation, resulting in gorgeous previews but the storage of
  330. // garbage capture data.
  331. mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
  332. rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth,
  333. maxPreviewHeight, largest);
  334. // We fit the aspect ratio of TextureView to the size of preview we picked.
  335. int orientation = mActivity.getResources().getConfiguration().orientation;
  336. if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
  337. mTextureView.setAspectRatio(
  338. mPreviewSize.getWidth(), mPreviewSize.getHeight());
  339. } else {
  340. mTextureView.setAspectRatio(
  341. mPreviewSize.getHeight(), mPreviewSize.getWidth());
  342. }
  343. mCameraId = cameraId;
  344. return;
  345. }
  346. } catch (CameraAccessException e) {
  347. e.printStackTrace();
  348. } catch (NullPointerException e) {
  349. // Currently an NPE is thrown when the Camera2API is used but not supported on the
  350. // device this code runs.
  351. }
  352. }
  353. private void startBackgroundThread() {
  354. mBackgroundThread = new HandlerThread("imageAvailableListener");
  355. mBackgroundThread.start();
  356. mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
  357. }
  358. /**
  359. * Configures the necessary {@link android.graphics.Matrix} transformation to `mTextureView`.
  360. * This method should be called after the camera preview size is determined in
  361. * setUpCameraOutputs and also the size of `mTextureView` is fixed.
  362. *
  363. * @param viewWidth The width of `mTextureView`
  364. * @param viewHeight The height of `mTextureView`
  365. */
  366. private void configureTransform(int viewWidth, int viewHeight) {
  367. if (null == mTextureView || null == mPreviewSize) {
  368. return;
  369. }
  370. int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
  371. Matrix matrix = new Matrix();
  372. RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
  373. RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());
  374. float centerX = viewRect.centerX();
  375. float centerY = viewRect.centerY();
  376. if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
  377. bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
  378. matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
  379. float scale = Math.max(
  380. (float) viewHeight / mPreviewSize.getHeight(),
  381. (float) viewWidth / mPreviewSize.getWidth());
  382. matrix.postScale(scale, scale, centerX, centerY);
  383. matrix.postRotate(90 * (rotation - 2), centerX, centerY);
  384. } else if (Surface.ROTATION_180 == rotation) {
  385. matrix.postRotate(180, centerX, centerY);
  386. }
  387. mTextureView.setTransform(matrix);
  388. }
  389. /**
  390. * Closes the current {@link CameraDevice}.
  391. */
  392. public void closeCamera() {
  393. stopBackgroundThread();
  394. try {
  395. mCameraOpenCloseLock.acquire();
  396. if (null != mCaptureSession) {
  397. mCaptureSession.close();
  398. mCaptureSession = null;
  399. }
  400. if (null != mCameraDevice) {
  401. mCameraDevice.close();
  402. mCameraDevice = null;
  403. }
  404. if (null != mImageReader) {
  405. mImageReader.close();
  406. mImageReader = null;
  407. }
  408. } catch (InterruptedException e) {
  409. throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
  410. } finally {
  411. mCameraOpenCloseLock.release();
  412. }
  413. }
  414. /**
  415. * Stops the background thread and its {@link Handler}.
  416. */
  417. private void stopBackgroundThread() {
  418. mBackgroundThread.quitSafely();
  419. try {
  420. mBackgroundThread.join();
  421. mBackgroundThread = null;
  422. mBackgroundHandler = null;
  423. } catch (InterruptedException e) {
  424. e.printStackTrace();
  425. }
  426. }
  427. /**
  428. * 返回摄像头权限的请求码
  429. *
  430. * @return 返回请求码
  431. */
  432. public int getCameraRequestCode() {
  433. return REQUEST_CAMERA_PERMISSION;
  434. }
  435. /**
  436. * Given {@code choices} of {@code Size}s supported by a camera, choose the smallest one that
  437. * is at least as large as the respective texture view size, and that is at most as large as the
  438. * respective max size, and whose aspect ratio matches with the specified value. If such size
  439. * doesn't exist, choose the largest one that is at most as large as the respective max size,
  440. * and whose aspect ratio matches with the specified value.
  441. *
  442. * @param choices The list of sizes that the camera supports for the intended output
  443. * class
  444. * @param textureViewWidth The width of the texture view relative to sensor coordinate
  445. * @param textureViewHeight The height of the texture view relative to sensor coordinate
  446. * @param maxWidth The maximum width that can be chosen
  447. * @param maxHeight The maximum height that can be chosen
  448. * @param aspectRatio The aspect ratio
  449. * @return The optimal {@code Size}, or an arbitrary one if none were big enough
  450. */
  451. private static Size chooseOptimalSize(Size[] choices, int textureViewWidth,
  452. int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) {
  453. // Collect the supported resolutions that are at least as big as the preview Surface
  454. List<Size> bigEnough = new ArrayList<>();
  455. // Collect the supported resolutions that are smaller than the preview Surface
  456. List<Size> notBigEnough = new ArrayList<>();
  457. int w = aspectRatio.getWidth();
  458. int h = aspectRatio.getHeight();
  459. for (Size option : choices) {
  460. if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight &&
  461. option.getHeight() == option.getWidth() * h / w) {
  462. if (option.getWidth() >= textureViewWidth &&
  463. option.getHeight() >= textureViewHeight) {
  464. bigEnough.add(option);
  465. } else {
  466. notBigEnough.add(option);
  467. }
  468. }
  469. }
  470. // Pick the smallest of those big enough. If there is no one big enough, pick the
  471. // largest of those not big enough.
  472. if (bigEnough.size() > 0) {
  473. return Collections.min(bigEnough, new CompareSizesByArea());
  474. } else if (notBigEnough.size() > 0) {
  475. return Collections.max(notBigEnough, new CompareSizesByArea());
  476. } else {
  477. Log.e(TAG, "Couldn't find any suitable preview size");
  478. return choices[0];
  479. }
  480. }
  481. /**
  482. * Compares two {@code Size}s based on their areas.
  483. */
  484. static class CompareSizesByArea implements Comparator<Size> {
  485. @Override
  486. public int compare(Size lhs, Size rhs) {
  487. // We cast here to ensure the multiplications won't overflow
  488. return Long.signum((long) lhs.getWidth() * lhs.getHeight() -
  489. (long) rhs.getWidth() * rhs.getHeight());
  490. }
  491. }
  492. /**
  493. * This a callback object for the {@link ImageReader}. "onImageAvailable" will be called when a
  494. * still image is ready to be saved.
  495. */
  496. private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
  497. = new ImageReader.OnImageAvailableListener() {
  498. @Override
  499. public void onImageAvailable(ImageReader reader) {
  500. Image image = reader.acquireNextImage();
  501. if (image == null) {
  502. Log.d(TAG, "onImageAvailable,image is null");
  503. return;
  504. }
  505. if (mImageAvaiableListener != null) {
  506. mImageAvaiableListener.onImageAvailable(image);
  507. }
  508. image.close(); //一定要关掉,否则回调和预览都会阻塞
  509. }
  510. };
  511. /**
  512. * 设置视频预览后的回调格式
  513. *
  514. * @param format 遵循ImageFormat格式
  515. */
  516. public Camera2Helper setImageFormat(int format) {
  517. mImageFormat = format;
  518. return this;
  519. }
  520. /**
  521. * 获取摄像头方向
  522. *
  523. * @return 方向,详见mSensorOrientation赋值 {@link Camera2Helper#setUpCameraOutputs}
  524. */
  525. public int getSensorOrientation() {
  526. return mSensorOrientation;
  527. }
  528. /**
  529. * 打开错误的回调,可以不设置,不设置采用默认策略
  530. *
  531. * @param listener 回调listener
  532. */
  533. public Camera2Helper setOnOpenErrorListener(OnOpenErrorListener listener) {
  534. mOpenErrorListener = listener;
  535. return this;
  536. }
  537. /**
  538. * 摄像头图像回调,类似于Camera1的PreviewCallback
  539. *
  540. * @param listener 回调listener
  541. */
  542. public Camera2Helper setOnImageAvailableListener(OnPreviewCallbackListener listener) {
  543. mImageAvaiableListener = listener;
  544. return this;
  545. }
  546. /**
  547. * 打开摄像头错误
  548. */
  549. interface OnOpenErrorListener {
  550. void onOpenError();
  551. }
  552. /**
  553. * 当摄像头数据回调可达的时候
  554. */
  555. interface OnPreviewCallbackListener {
  556. void onImageAvailable(Image image);
  557. }
  558. }

代码比较多,不多说了,都是Camera2的封装,主要是加了

mPreviewRequestBuilder.addTarget(mImageReader.getSurface());    //不加这句话没有回调

Camera2为了避免频繁回调,弱化了onPreviewCallback的接口,它可以根据自己添加的target去回调画面,因为有些人用的场景是拍照,有的人是为了实时画面比对一些东西

然后是AutoFitTextureView,这是GoogleDemo里写的可以自动调节大小的类,现在推荐用TextureView,而不是SurfaceView,前者特性更好点

  1. public class AutoFitTextureView extends TextureView {
  2. private int mRatioWidth = 0;
  3. private int mRatioHeight = 0;
  4. public AutoFitTextureView(Context context) {
  5. this(context, null);
  6. }
  7. public AutoFitTextureView(Context context, AttributeSet attrs) {
  8. this(context, attrs, 0);
  9. }
  10. public AutoFitTextureView(Context context, AttributeSet attrs, int defStyle) {
  11. super(context, attrs, defStyle);
  12. }
  13. /**
  14. * Sets the aspect ratio for this view. The size of the view will be measured based on the ratio
  15. * calculated from the parameters. Note that the actual sizes of parameters don't matter, that
  16. * is, calling setAspectRatio(2, 3) and setAspectRatio(4, 6) make the same result.
  17. *
  18. * @param width Relative horizontal size
  19. * @param height Relative vertical size
  20. */
  21. public void setAspectRatio(int width, int height) {
  22. if (width < 0 || height < 0) {
  23. throw new IllegalArgumentException("Size cannot be negative.");
  24. }
  25. mRatioWidth = width;
  26. mRatioHeight = height;
  27. requestLayout();
  28. }
  29. @Override
  30. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  31. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  32. int width = MeasureSpec.getSize(widthMeasureSpec);
  33. int height = MeasureSpec.getSize(heightMeasureSpec);
  34. if (0 == mRatioWidth || 0 == mRatioHeight) {
  35. setMeasuredDimension(width, height);
  36. } else {
  37. if (width < height * mRatioWidth / mRatioHeight) {
  38. setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
  39. } else {
  40. setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
  41. }
  42. }
  43. }
  44. }

总得来说,接口比一代好用,但是还是不够友好,期待三代的接口

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
Android 专用响应式编程框架 — Agera
Glide生命周期原理
mtkcamera api2学习
android6.0源码分析之Camera API2.0下的Preview(预览)流程分析
百度地图api之路线规划
RecyclerView单选全选功能
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服