content: /*
   * Copyright (C) 2014 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.android.ex.camera2.portability;
  
  import static android.hardware.camera2.CameraCharacteristics.*;
  
  import android.graphics.ImageFormat;
  import android.graphics.Point;
  import android.graphics.SurfaceTexture;
  import android.hardware.camera2.CameraCharacteristics;
  import android.hardware.camera2.params.StreamConfigurationMap;
  import android.media.MediaRecorder;
  import android.util.Range;
  import android.util.Rational;
  
  import com.android.ex.camera2.portability.debug.Log;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  
  /**
   * The subclass of {@link CameraCapabilities} for Android Camera 2 API.
   */
  public class AndroidCamera2Capabilities extends CameraCapabilities {
      private static Log.Tag TAG = new Log.Tag("AndCam2Capabs");
  
      AndroidCamera2Capabilities(CameraCharacteristics p) {
          super(new Stringifier());
  
          StreamConfigurationMap s = p.get(SCALER_STREAM_CONFIGURATION_MAP);
  
          for (Range<Integer> fpsRange : p.get(CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES)) {
              mSupportedPreviewFpsRange.add(new int[] { fpsRange.getLower(), fpsRange.getUpper() });
          }
  
          // TODO: We only support TextureView preview rendering
          mSupportedPreviewSizes.addAll(Size.buildListFromAndroidSizes(Arrays.asList(
                  s.getOutputSizes(SurfaceTexture.class))));
          for (int format : s.getOutputFormats()) {
              mSupportedPreviewFormats.add(format);
          }
  
          // TODO: We only support MediaRecorder video capture
          mSupportedVideoSizes.addAll(Size.buildListFromAndroidSizes(Arrays.asList(
                  s.getOutputSizes(MediaRecorder.class))));
  
          // TODO: We only support JPEG image capture
          mSupportedPhotoSizes.addAll(Size.buildListFromAndroidSizes(Arrays.asList(
                  s.getOutputSizes(ImageFormat.JPEG))));
          mSupportedPhotoFormats.addAll(mSupportedPreviewFormats);
  
          buildSceneModes(p);
          buildFlashModes(p);
          buildFocusModes(p);
          buildWhiteBalances(p);
          // TODO: Populate mSupportedFeatures
  
          // TODO: Populate mPreferredPreviewSizeForVideo
  
          Range<Integer> ecRange = p.get(CONTROL_AE_COMPENSATION_RANGE);
          mMinExposureCompensation = ecRange.getLower();
          mMaxExposureCompensation = ecRange.getUpper();
  
          Rational ecStep = p.get(CONTROL_AE_COMPENSATION_STEP);
          mExposureCompensationStep = (float) ecStep.getNumerator() / ecStep.getDenominator();
  
          mMaxNumOfFacesSupported = p.get(STATISTICS_INFO_MAX_FACE_COUNT);
          mMaxNumOfMeteringArea = p.get(CONTROL_MAX_REGIONS_AE);
  
          mMaxZoomRatio = p.get(SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);
          // TODO: Populate mHorizontalViewAngle
          // TODO: Populate mVerticalViewAngle
          // TODO: Populate mZoomRatioList
          // TODO: Populate mMaxZoomIndex
  
          if (supports(FocusMode.AUTO)) {
              mMaxNumOfFocusAreas = p.get(CONTROL_MAX_REGIONS_AF);
              if (mMaxNumOfFocusAreas > 0) {
                  mSupportedFeatures.add(Feature.FOCUS_AREA);
              }
          }
          if (mMaxNumOfMeteringArea > 0) {
              mSupportedFeatures.add(Feature.METERING_AREA);
          }
  
          if (mMaxZoomRatio > CameraCapabilities.ZOOM_RATIO_UNZOOMED) {
              mSupportedFeatures.add(Feature.ZOOM);
          }
  
          // TODO: Detect other features
      }
  
      private void buildSceneModes(CameraCharacteristics p) {
          int[] scenes = p.get(CONTROL_AVAILABLE_SCENE_MODES);
          if (scenes != null) {
              for (int scene : scenes) {
                  SceneMode equiv = sceneModeFromInt(scene);
                  if (equiv != null) {
                      mSupportedSceneModes.add(equiv);
                  }
              }
          }
      }
  
      private void buildFlashModes(CameraCharacteristics p) {
          mSupportedFlashModes.add(FlashMode.OFF);
          if (p.get(FLASH_INFO_AVAILABLE)) {
              mSupportedFlashModes.add(FlashMode.AUTO);
              mSupportedFlashModes.add(FlashMode.ON);
              mSupportedFlashModes.add(FlashMode.TORCH);
              for (int expose : p.get(CONTROL_AE_AVAILABLE_MODES)) {
                  if (expose == CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE) {
                      mSupportedFlashModes.add(FlashMode.RED_EYE);
                  }
              }
          }
      }
  
      private void buildFocusModes(CameraCharacteristics p) {
          int[] focuses = p.get(CONTROL_AF_AVAILABLE_MODES);
          if (focuses != null) {
              for (int focus : focuses) {
                  FocusMode equiv = focusModeFromInt(focus);
                  if (equiv != null) {
                      mSupportedFocusModes.add(equiv);
                  }
              }
          }
      }
  
      private void buildWhiteBalances(CameraCharacteristics p) {
          int[] bals = p.get(CONTROL_AWB_AVAILABLE_MODES);
          if (bals != null) {
              for (int bal : bals) {
                  WhiteBalance equiv = whiteBalanceFromInt(bal);
                  if (equiv != null) {
                      mSupportedWhiteBalances.add(equiv);
                  }
              }
          }
      }
  
      /**
       * Converts the API-related integer representation of the focus mode to the
       * abstract representation.
       *
       * @param fm The integral representation.
       * @return The mode represented by the input integer, or {@code null} if it
       *         cannot be converted.
       */
      public static FocusMode focusModeFromInt(int fm) {
          switch (fm) {
              case CONTROL_AF_MODE_AUTO:
                  return FocusMode.AUTO;
              case CONTROL_AF_MODE_CONTINUOUS_PICTURE:
                  return FocusMode.CONTINUOUS_PICTURE;
              case CONTROL_AF_MODE_CONTINUOUS_VIDEO:
                  return FocusMode.CONTINUOUS_VIDEO;
              case CONTROL_AF_MODE_EDOF:
                  return FocusMode.EXTENDED_DOF;
              case CONTROL_AF_MODE_OFF:
                  return FocusMode.FIXED;
              // TODO: We cannot support INFINITY
              case CONTROL_AF_MODE_MACRO:
                  return FocusMode.MACRO;
          }
          Log.w(TAG, "Unable to convert from API 2 focus mode: " + fm);
          return null;
      }
  
      /**
       * Converts the API-related integer representation of the scene mode to the
       * abstract representation.
       *
       * @param sm The integral representation.
       * @return The mode represented by the input integer, or {@code null} if it
       *         cannot be converted.
       */
      public static SceneMode sceneModeFromInt(int sm) {
          switch (sm) {
              case CONTROL_SCENE_MODE_DISABLED:
                  return SceneMode.AUTO;
              case CONTROL_SCENE_MODE_ACTION:
                  return SceneMode.ACTION;
              case CONTROL_SCENE_MODE_BARCODE:
                  return SceneMode.BARCODE;
              case CONTROL_SCENE_MODE_BEACH:
                  return SceneMode.BEACH;
              case CONTROL_SCENE_MODE_CANDLELIGHT:
                  return SceneMode.CANDLELIGHT;
              case CONTROL_SCENE_MODE_FIREWORKS:
                  return SceneMode.FIREWORKS;
              case CONTROL_SCENE_MODE_LANDSCAPE:
                  return SceneMode.LANDSCAPE;
              case CONTROL_SCENE_MODE_NIGHT:
                  return SceneMode.NIGHT;
              // TODO: We cannot support NIGHT_PORTRAIT
              case CONTROL_SCENE_MODE_PARTY:
                  return SceneMode.PARTY;
              case CONTROL_SCENE_MODE_PORTRAIT:
                  return SceneMode.PORTRAIT;
              case CONTROL_SCENE_MODE_SNOW:
                  return SceneMode.SNOW;
              case CONTROL_SCENE_MODE_SPORTS:
                  return SceneMode.SPORTS;
              case CONTROL_SCENE_MODE_STEADYPHOTO:
                  return SceneMode.STEADYPHOTO;
              case CONTROL_SCENE_MODE_SUNSET:
                  return SceneMode.SUNSET;
              case CONTROL_SCENE_MODE_THEATRE:
                  return SceneMode.THEATRE;
              case CONTROL_SCENE_MODE_HDR:
                  return SceneMode.HDR;
              // TODO: We cannot expose FACE_PRIORITY, or HIGH_SPEED_VIDEO
          }
  
          Log.w(TAG, "Unable to convert from API 2 scene mode: " + sm);
          return null;
      }
  
      /**
       * Converts the API-related integer representation of the white balance to
       * the abstract representation.
       *
       * @param wb The integral representation.
       * @return The balance represented by the input integer, or {@code null} if
       *         it cannot be converted.
       */
      public static WhiteBalance whiteBalanceFromInt(int wb) {
          switch (wb) {
              case CONTROL_AWB_MODE_AUTO:
                  return WhiteBalance.AUTO;
              case CONTROL_AWB_MODE_CLOUDY_DAYLIGHT:
                  return WhiteBalance.CLOUDY_DAYLIGHT;
              case CONTROL_AWB_MODE_DAYLIGHT:
                  return WhiteBalance.DAYLIGHT;
              case CONTROL_AWB_MODE_FLUORESCENT:
                  return WhiteBalance.FLUORESCENT;
              case CONTROL_AWB_MODE_INCANDESCENT:
                  return WhiteBalance.INCANDESCENT;
              case CONTROL_AWB_MODE_SHADE:
                  return WhiteBalance.SHADE;
              case CONTROL_AWB_MODE_TWILIGHT:
                  return WhiteBalance.TWILIGHT;
              case CONTROL_AWB_MODE_WARM_FLUORESCENT:
                  return WhiteBalance.WARM_FLUORESCENT;
          }
          Log.w(TAG, "Unable to convert from API 2 white balance: " + wb);
          return null;
      }
  }
  