| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.media; | 5 package org.chromium.media; |
| 6 | 6 |
| 7 import android.annotation.TargetApi; | 7 import android.annotation.TargetApi; |
| 8 import android.content.Context; | 8 import android.content.Context; |
| 9 import android.graphics.ImageFormat; | 9 import android.graphics.ImageFormat; |
| 10 import android.graphics.Rect; | 10 import android.graphics.Rect; |
| 11 import android.hardware.camera2.CameraAccessException; | 11 import android.hardware.camera2.CameraAccessException; |
| 12 import android.hardware.camera2.CameraCaptureSession; | 12 import android.hardware.camera2.CameraCaptureSession; |
| 13 import android.hardware.camera2.CameraCharacteristics; | 13 import android.hardware.camera2.CameraCharacteristics; |
| 14 import android.hardware.camera2.CameraDevice; | 14 import android.hardware.camera2.CameraDevice; |
| 15 import android.hardware.camera2.CameraManager; | 15 import android.hardware.camera2.CameraManager; |
| 16 import android.hardware.camera2.CameraMetadata; | 16 import android.hardware.camera2.CameraMetadata; |
| 17 import android.hardware.camera2.CaptureRequest; | 17 import android.hardware.camera2.CaptureRequest; |
| 18 import android.hardware.camera2.params.StreamConfigurationMap; | 18 import android.hardware.camera2.params.StreamConfigurationMap; |
| 19 import android.media.Image; | 19 import android.media.Image; |
| 20 import android.media.ImageReader; | 20 import android.media.ImageReader; |
| 21 import android.os.Build; | 21 import android.os.Build; |
| 22 import android.os.Handler; | 22 import android.os.Handler; |
| 23 import android.os.HandlerThread; | 23 import android.os.HandlerThread; |
| 24 import android.util.Range; |
| 24 import android.util.Size; | 25 import android.util.Size; |
| 25 import android.view.Surface; | 26 import android.view.Surface; |
| 26 | 27 |
| 27 import org.chromium.base.Log; | 28 import org.chromium.base.Log; |
| 28 import org.chromium.base.annotations.JNINamespace; | 29 import org.chromium.base.annotations.JNINamespace; |
| 29 | 30 |
| 30 import java.nio.ByteBuffer; | 31 import java.nio.ByteBuffer; |
| 31 import java.util.ArrayList; | 32 import java.util.ArrayList; |
| 32 import java.util.List; | 33 import java.util.List; |
| 33 | 34 |
| (...skipping 319 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 353 } | 354 } |
| 354 } | 355 } |
| 355 | 356 |
| 356 private void changeCameraStateAndNotify(CameraState state) { | 357 private void changeCameraStateAndNotify(CameraState state) { |
| 357 synchronized (mCameraStateLock) { | 358 synchronized (mCameraStateLock) { |
| 358 mCameraState = state; | 359 mCameraState = state; |
| 359 mCameraStateLock.notifyAll(); | 360 mCameraStateLock.notifyAll(); |
| 360 } | 361 } |
| 361 } | 362 } |
| 362 | 363 |
| 364 // Finds the closest Size to (|width|x|height|) in |sizes|, and returns it o
r null. |
| 365 // Ignores |width| or |height| if either is zero (== don't care). |
| 366 private static Size findClosestSizeInArray(Size[] sizes, int width, int heig
ht) { |
| 367 if (sizes == null) return null; |
| 368 Size closestSize = null; |
| 369 int minDiff = Integer.MAX_VALUE; |
| 370 for (Size size : sizes) { |
| 371 final int diff = ((width > 0) ? Math.abs(size.getWidth() - width) :
0) |
| 372 + ((height > 0) ? Math.abs(size.getHeight() - height) : 0); |
| 373 if (diff < minDiff) { |
| 374 minDiff = diff; |
| 375 closestSize = size; |
| 376 } |
| 377 } |
| 378 if (minDiff == Integer.MAX_VALUE) { |
| 379 Log.e(TAG, "Couldn't find resolution close to (%dx%d)", width, heigh
t); |
| 380 return null; |
| 381 } |
| 382 return closestSize; |
| 383 } |
| 384 |
| 363 static boolean isLegacyDevice(Context appContext, int id) { | 385 static boolean isLegacyDevice(Context appContext, int id) { |
| 364 final CameraCharacteristics cameraCharacteristics = | 386 final CameraCharacteristics cameraCharacteristics = |
| 365 getCameraCharacteristics(appContext, id); | 387 getCameraCharacteristics(appContext, id); |
| 366 return cameraCharacteristics != null | 388 return cameraCharacteristics != null |
| 367 && cameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTE
D_HARDWARE_LEVEL) | 389 && cameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTE
D_HARDWARE_LEVEL) |
| 368 == CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY; | 390 == CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY; |
| 369 } | 391 } |
| 370 | 392 |
| 371 static int getNumberOfCameras(Context appContext) { | 393 static int getNumberOfCameras(Context appContext) { |
| 372 final CameraManager manager = | 394 final CameraManager manager = |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 469 Log.e(TAG, "allocate() invoked while Camera is busy opening/conf
iguring."); | 491 Log.e(TAG, "allocate() invoked while Camera is busy opening/conf
iguring."); |
| 470 return false; | 492 return false; |
| 471 } | 493 } |
| 472 } | 494 } |
| 473 final CameraCharacteristics cameraCharacteristics = getCameraCharacteris
tics(mContext, mId); | 495 final CameraCharacteristics cameraCharacteristics = getCameraCharacteris
tics(mContext, mId); |
| 474 final StreamConfigurationMap streamMap = | 496 final StreamConfigurationMap streamMap = |
| 475 cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CO
NFIGURATION_MAP); | 497 cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CO
NFIGURATION_MAP); |
| 476 | 498 |
| 477 // Find closest supported size. | 499 // Find closest supported size. |
| 478 final Size[] supportedSizes = streamMap.getOutputSizes(ImageFormat.YUV_4
20_888); | 500 final Size[] supportedSizes = streamMap.getOutputSizes(ImageFormat.YUV_4
20_888); |
| 479 if (supportedSizes == null) return false; | 501 final Size closestSupportedSize = findClosestSizeInArray(supportedSizes,
width, height); |
| 480 Size closestSupportedSize = null; | 502 if (closestSupportedSize == null) { |
| 481 int minDiff = Integer.MAX_VALUE; | |
| 482 for (Size size : supportedSizes) { | |
| 483 final int diff = | |
| 484 Math.abs(size.getWidth() - width) + Math.abs(size.getHeight(
) - height); | |
| 485 if (diff < minDiff) { | |
| 486 minDiff = diff; | |
| 487 closestSupportedSize = size; | |
| 488 } | |
| 489 } | |
| 490 if (minDiff == Integer.MAX_VALUE) { | |
| 491 Log.e(TAG, "No supported resolutions."); | 503 Log.e(TAG, "No supported resolutions."); |
| 492 return false; | 504 return false; |
| 493 } | 505 } |
| 494 Log.d(TAG, "allocate: matched (%d x %d)", closestSupportedSize.getWidth(
), | 506 Log.d(TAG, "allocate: matched (%d x %d)", closestSupportedSize.getWidth(
), |
| 495 closestSupportedSize.getHeight()); | 507 closestSupportedSize.getHeight()); |
| 496 | 508 |
| 497 // |mCaptureFormat| is also used to configure the ImageReader. | 509 // |mCaptureFormat| is also used to configure the ImageReader. |
| 498 mCaptureFormat = new VideoCaptureFormat(closestSupportedSize.getWidth(), | 510 mCaptureFormat = new VideoCaptureFormat(closestSupportedSize.getWidth(), |
| 499 closestSupportedSize.getHeight(), frameRate, ImageFormat.YUV_420
_888); | 511 closestSupportedSize.getHeight(), frameRate, ImageFormat.YUV_420
_888); |
| 500 int expectedFrameSize = mCaptureFormat.mWidth * mCaptureFormat.mHeight | 512 int expectedFrameSize = mCaptureFormat.mWidth * mCaptureFormat.mHeight |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 555 if (mCameraDevice == null) return false; | 567 if (mCameraDevice == null) return false; |
| 556 mCameraDevice.close(); | 568 mCameraDevice.close(); |
| 557 changeCameraStateAndNotify(CameraState.STOPPED); | 569 changeCameraStateAndNotify(CameraState.STOPPED); |
| 558 mCropRect = new Rect(); | 570 mCropRect = new Rect(); |
| 559 return true; | 571 return true; |
| 560 } | 572 } |
| 561 | 573 |
| 562 public PhotoCapabilities getPhotoCapabilities() { | 574 public PhotoCapabilities getPhotoCapabilities() { |
| 563 final CameraCharacteristics cameraCharacteristics = getCameraCharacteris
tics(mContext, mId); | 575 final CameraCharacteristics cameraCharacteristics = getCameraCharacteris
tics(mContext, mId); |
| 564 | 576 |
| 577 int minIso = 0; |
| 578 int maxIso = 0; |
| 579 final Range<Integer> iso_range = |
| 580 cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_SENS
ITIVITY_RANGE); |
| 581 if (iso_range != null) { |
| 582 minIso = iso_range.getLower(); |
| 583 maxIso = iso_range.getUpper(); |
| 584 } |
| 585 final int currentIso = mPreviewRequest.get(CaptureRequest.SENSOR_SENSITI
VITY); |
| 586 |
| 587 final StreamConfigurationMap streamMap = |
| 588 cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CO
NFIGURATION_MAP); |
| 589 final Size[] supportedSizes = streamMap.getOutputSizes(ImageFormat.JPEG)
; |
| 590 int minWidth = Integer.MAX_VALUE; |
| 591 int minHeight = Integer.MAX_VALUE; |
| 592 int maxWidth = 0; |
| 593 int maxHeight = 0; |
| 594 for (Size size : supportedSizes) { |
| 595 if (size.getWidth() < minWidth) minWidth = size.getWidth(); |
| 596 if (size.getHeight() < minHeight) minHeight = size.getHeight(); |
| 597 if (size.getWidth() > maxWidth) maxWidth = size.getWidth(); |
| 598 if (size.getHeight() > maxHeight) maxHeight = size.getHeight(); |
| 599 } |
| 600 final int currentHeight = mCaptureFormat.getHeight(); |
| 601 final int currentWidth = mCaptureFormat.getWidth(); |
| 602 |
| 565 // The Min and Max zoom are returned as x100 by the API to avoid using f
loating point. There | 603 // The Min and Max zoom are returned as x100 by the API to avoid using f
loating point. There |
| 566 // is no min-zoom per se, so clamp it to always 100 (TODO(mcasas): make
const member). | 604 // is no min-zoom per se, so clamp it to always 100 (TODO(mcasas): make
const member). |
| 567 final int minZoom = 100; | 605 final int minZoom = 100; |
| 568 final int maxZoom = Math.round(mMaxZoom * 100); | 606 final int maxZoom = Math.round(mMaxZoom * 100); |
| 569 | |
| 570 // Width Ratio x100 is used as measure of current zoom. | 607 // Width Ratio x100 is used as measure of current zoom. |
| 571 final int currentZoom = 100 * mPreviewRequest.get(CaptureRequest.SCALER_
CROP_REGION).width() | 608 final int currentZoom = 100 * mPreviewRequest.get(CaptureRequest.SCALER_
CROP_REGION).width() |
| 572 / cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_AC
TIVE_ARRAY_SIZE) | 609 / cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_AC
TIVE_ARRAY_SIZE) |
| 573 .width(); | 610 .width(); |
| 574 | 611 |
| 575 final int focusMode = mPreviewRequest.get(CaptureRequest.CONTROL_AF_MODE
); | 612 final int focusMode = mPreviewRequest.get(CaptureRequest.CONTROL_AF_MODE
); |
| 576 Log.d(TAG, "focusMode " + focusMode); | 613 Log.d(TAG, "focusMode " + focusMode); |
| 577 final boolean isFocusManual = (focusMode == CameraMetadata.CONTROL_AF_MO
DE_OFF) | 614 final boolean isFocusManual = (focusMode == CameraMetadata.CONTROL_AF_MO
DE_OFF) |
| 578 || (focusMode == CameraMetadata.CONTROL_AF_MODE_EDOF); | 615 || (focusMode == CameraMetadata.CONTROL_AF_MODE_EDOF); |
| 579 | 616 |
| 580 return new PhotoCapabilities(maxZoom, minZoom, currentZoom, !isFocusManu
al); | 617 return new PhotoCapabilities(minIso, maxIso, currentIso, maxHeight, minH
eight, |
| 618 currentHeight, maxWidth, minWidth, currentWidth, maxZoom, minZoo
m, currentZoom, |
| 619 !isFocusManual); |
| 581 } | 620 } |
| 582 | 621 |
| 583 @Override | 622 @Override |
| 584 public void setZoom(int zoom) { | 623 public void setZoom(int zoom) { |
| 585 final CameraCharacteristics cameraCharacteristics = getCameraCharacteris
tics(mContext, mId); | 624 final CameraCharacteristics cameraCharacteristics = getCameraCharacteris
tics(mContext, mId); |
| 586 final Rect canvas = | 625 final Rect canvas = |
| 587 cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTI
VE_ARRAY_SIZE); | 626 cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTI
VE_ARRAY_SIZE); |
| 588 | 627 |
| 589 final float normalizedZoom = Math.max(100, Math.min(zoom, mMaxZoom * 100
)) / 100; | 628 final float normalizedZoom = Math.max(100, Math.min(zoom, mMaxZoom * 100
)) / 100; |
| 590 final float cropFactor = (normalizedZoom - 1) / (2 * normalizedZoom); | 629 final float cropFactor = (normalizedZoom - 1) / (2 * normalizedZoom); |
| 591 | 630 |
| 592 mCropRect = new Rect(Math.round(canvas.width() * cropFactor), | 631 mCropRect = new Rect(Math.round(canvas.width() * cropFactor), |
| 593 Math.round(canvas.height() * cropFactor), | 632 Math.round(canvas.height() * cropFactor), |
| 594 Math.round(canvas.width() * (1 - cropFactor)), | 633 Math.round(canvas.width() * (1 - cropFactor)), |
| 595 Math.round(canvas.height() * (1 - cropFactor))); | 634 Math.round(canvas.height() * (1 - cropFactor))); |
| 596 Log.d(TAG, "zoom level " + normalizedZoom + ", rectangle: " + mCropRect.
toString()); | 635 Log.d(TAG, "zoom level " + normalizedZoom + ", rectangle: " + mCropRect.
toString()); |
| 597 | 636 |
| 598 final Handler mainHandler = new Handler(mContext.getMainLooper()); | 637 final Handler mainHandler = new Handler(mContext.getMainLooper()); |
| 599 mainHandler.post(new Runnable() { | 638 mainHandler.post(new Runnable() { |
| 600 @Override | 639 @Override |
| 601 public void run() { | 640 public void run() { |
| 602 mPreviewSession.close(); // Asynchronously kill the CaptureSessi
on. | 641 mPreviewSession.close(); // Asynchronously kill the CaptureSessi
on. |
| 603 createPreviewObjects(); | 642 createPreviewObjects(); |
| 604 } | 643 } |
| 605 }); | 644 }); |
| 606 } | 645 } |
| 607 | 646 |
| 608 @Override | 647 @Override |
| 609 public boolean takePhoto(final long callbackId) { | 648 public boolean takePhoto(final long callbackId, int width, int height) { |
| 610 Log.d(TAG, "takePhoto " + callbackId); | 649 Log.d(TAG, "takePhoto " + callbackId); |
| 611 if (mCameraDevice == null || mCameraState != CameraState.STARTED) return
false; | 650 if (mCameraDevice == null || mCameraState != CameraState.STARTED) return
false; |
| 612 | 651 |
| 613 final ImageReader imageReader = ImageReader.newInstance(mCaptureFormat.g
etWidth(), | 652 final CameraCharacteristics cameraCharacteristics = getCameraCharacteris
tics(mContext, mId); |
| 614 mCaptureFormat.getHeight(), ImageFormat.JPEG, 1 /* maxImages */)
; | 653 final StreamConfigurationMap streamMap = |
| 654 cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CO
NFIGURATION_MAP); |
| 655 final Size[] supportedSizes = streamMap.getOutputSizes(ImageFormat.JPEG)
; |
| 656 final Size closestSize = findClosestSizeInArray(supportedSizes, width, h
eight); |
| 657 |
| 658 Log.d(TAG, "requested resolution: (%dx%d)", width, height); |
| 659 if (closestSize != null) { |
| 660 Log.d(TAG, " matched (%dx%d)", closestSize.getWidth(), closestSize.g
etHeight()); |
| 661 } |
| 662 final ImageReader imageReader = ImageReader.newInstance( |
| 663 (closestSize != null) ? closestSize.getWidth() : mCaptureFormat.
getWidth(), |
| 664 (closestSize != null) ? closestSize.getHeight() : mCaptureFormat
.getHeight(), |
| 665 ImageFormat.JPEG, 1 /* maxImages */); |
| 615 | 666 |
| 616 HandlerThread thread = new HandlerThread("CameraPicture"); | 667 HandlerThread thread = new HandlerThread("CameraPicture"); |
| 617 thread.start(); | 668 thread.start(); |
| 618 final Handler backgroundHandler = new Handler(thread.getLooper()); | 669 final Handler backgroundHandler = new Handler(thread.getLooper()); |
| 619 | 670 |
| 620 final CrPhotoReaderListener photoReaderListener = new CrPhotoReaderListe
ner(callbackId); | 671 final CrPhotoReaderListener photoReaderListener = new CrPhotoReaderListe
ner(callbackId); |
| 621 imageReader.setOnImageAvailableListener(photoReaderListener, backgroundH
andler); | 672 imageReader.setOnImageAvailableListener(photoReaderListener, backgroundH
andler); |
| 622 | 673 |
| 623 final List<Surface> surfaceList = new ArrayList<Surface>(1); | 674 final List<Surface> surfaceList = new ArrayList<Surface>(1); |
| 624 surfaceList.add(imageReader.getSurface()); | 675 surfaceList.add(imageReader.getSurface()); |
| (...skipping 26 matching lines...) Expand all Loading... |
| 651 return false; | 702 return false; |
| 652 } | 703 } |
| 653 return true; | 704 return true; |
| 654 } | 705 } |
| 655 | 706 |
| 656 @Override | 707 @Override |
| 657 public void deallocate() { | 708 public void deallocate() { |
| 658 Log.d(TAG, "deallocate"); | 709 Log.d(TAG, "deallocate"); |
| 659 } | 710 } |
| 660 } | 711 } |
| OLD | NEW |