Home | History | Annotate | Download | only in webrtc
      1 /*
      2  * libjingle
      3  * Copyright 2015 Google Inc.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *  1. Redistributions of source code must retain the above copyright notice,
      9  *     this list of conditions and the following disclaimer.
     10  *  2. Redistributions in binary form must reproduce the above copyright notice,
     11  *     this list of conditions and the following disclaimer in the documentation
     12  *     and/or other materials provided with the distribution.
     13  *  3. The name of the author may not be used to endorse or promote products
     14  *     derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
     17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
     19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 package org.webrtc;
     29 
     30 import static java.lang.Math.abs;
     31 import static java.lang.Math.ceil;
     32 import android.graphics.ImageFormat;
     33 
     34 import org.json.JSONArray;
     35 import org.json.JSONException;
     36 import org.json.JSONObject;
     37 
     38 import org.webrtc.Logging;
     39 
     40 import java.util.Collections;
     41 import java.util.Comparator;
     42 import java.util.List;
     43 
     44 @SuppressWarnings("deprecation")
     45 public class CameraEnumerationAndroid {
     46   private final static String TAG = "CameraEnumerationAndroid";
     47   // Synchronized on |CameraEnumerationAndroid.this|.
     48   private static Enumerator enumerator = new CameraEnumerator();
     49 
     50   public interface Enumerator {
     51     /**
     52      * Returns a list of supported CaptureFormats for the camera with index |cameraId|.
     53      */
     54     List<CaptureFormat> getSupportedFormats(int cameraId);
     55   }
     56 
     57   public static synchronized void setEnumerator(Enumerator enumerator) {
     58     CameraEnumerationAndroid.enumerator = enumerator;
     59   }
     60 
     61   public static synchronized List<CaptureFormat> getSupportedFormats(int cameraId) {
     62     return enumerator.getSupportedFormats(cameraId);
     63   }
     64 
     65   public static class CaptureFormat {
     66     public final int width;
     67     public final int height;
     68     public final int maxFramerate;
     69     public final int minFramerate;
     70     // TODO(hbos): If VideoCapturerAndroid.startCapture is updated to support
     71     // other image formats then this needs to be updated and
     72     // VideoCapturerAndroid.getSupportedFormats need to return CaptureFormats of
     73     // all imageFormats.
     74     public final int imageFormat = ImageFormat.NV21;
     75 
     76     public CaptureFormat(int width, int height, int minFramerate,
     77         int maxFramerate) {
     78       this.width = width;
     79       this.height = height;
     80       this.minFramerate = minFramerate;
     81       this.maxFramerate = maxFramerate;
     82     }
     83 
     84     // Calculates the frame size of this capture format.
     85     public int frameSize() {
     86       return frameSize(width, height, imageFormat);
     87     }
     88 
     89     // Calculates the frame size of the specified image format. Currently only
     90     // supporting ImageFormat.NV21.
     91     // The size is width * height * number of bytes per pixel.
     92     // http://developer.android.com/reference/android/hardware/Camera.html#addCallbackBuffer(byte[])
     93     public static int frameSize(int width, int height, int imageFormat) {
     94       if (imageFormat != ImageFormat.NV21) {
     95         throw new UnsupportedOperationException("Don't know how to calculate "
     96             + "the frame size of non-NV21 image formats.");
     97       }
     98       return (width * height * ImageFormat.getBitsPerPixel(imageFormat)) / 8;
     99     }
    100 
    101     @Override
    102     public String toString() {
    103       return width + "x" + height + "@[" + minFramerate + ":" + maxFramerate + "]";
    104     }
    105 
    106     public boolean isSameFormat(final CaptureFormat that) {
    107       if (that == null) {
    108         return false;
    109       }
    110       return width == that.width && height == that.height && maxFramerate == that.maxFramerate
    111           && minFramerate == that.minFramerate;
    112     }
    113   }
    114 
    115   // Returns device names that can be used to create a new VideoCapturerAndroid.
    116   public static String[] getDeviceNames() {
    117     String[] names = new String[android.hardware.Camera.getNumberOfCameras()];
    118     for (int i = 0; i < android.hardware.Camera.getNumberOfCameras(); ++i) {
    119       names[i] = getDeviceName(i);
    120     }
    121     return names;
    122   }
    123 
    124   // Returns number of cameras on device.
    125   public static int getDeviceCount() {
    126     return android.hardware.Camera.getNumberOfCameras();
    127   }
    128 
    129   // Returns the name of the camera with camera index. Returns null if the
    130   // camera can not be used.
    131   public static String getDeviceName(int index) {
    132     android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
    133     try {
    134       android.hardware.Camera.getCameraInfo(index, info);
    135     } catch (Exception e) {
    136       Logging.e(TAG, "getCameraInfo failed on index " + index,e);
    137       return null;
    138     }
    139 
    140     String facing =
    141         (info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT) ? "front" : "back";
    142     return "Camera " + index + ", Facing " + facing
    143         + ", Orientation " + info.orientation;
    144   }
    145 
    146   // Returns the name of the front facing camera. Returns null if the
    147   // camera can not be used or does not exist.
    148   public static String getNameOfFrontFacingDevice() {
    149     return getNameOfDevice(android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT);
    150   }
    151 
    152   // Returns the name of the back facing camera. Returns null if the
    153   // camera can not be used or does not exist.
    154   public static String getNameOfBackFacingDevice() {
    155     return getNameOfDevice(android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK);
    156   }
    157 
    158   public static String getSupportedFormatsAsJson(int id) throws JSONException {
    159     List<CaptureFormat> formats = getSupportedFormats(id);
    160     JSONArray json_formats = new JSONArray();
    161     for (CaptureFormat format : formats) {
    162       JSONObject json_format = new JSONObject();
    163       json_format.put("width", format.width);
    164       json_format.put("height", format.height);
    165       json_format.put("framerate", (format.maxFramerate + 999) / 1000);
    166       json_formats.put(json_format);
    167     }
    168     Logging.d(TAG, "Supported formats for camera " + id + ": "
    169         +  json_formats.toString(2));
    170     return json_formats.toString();
    171   }
    172 
    173   // Helper class for finding the closest supported format for the two functions below.
    174   private static abstract class ClosestComparator<T> implements Comparator<T> {
    175     // Difference between supported and requested parameter.
    176     abstract int diff(T supportedParameter);
    177 
    178     @Override
    179     public int compare(T t1, T t2) {
    180       return diff(t1) - diff(t2);
    181     }
    182   }
    183 
    184   public static int[] getFramerateRange(android.hardware.Camera.Parameters parameters,
    185       final int framerate) {
    186     List<int[]> listFpsRange = parameters.getSupportedPreviewFpsRange();
    187     if (listFpsRange.isEmpty()) {
    188       Logging.w(TAG, "No supported preview fps range");
    189       return new int[]{0, 0};
    190     }
    191     return Collections.min(listFpsRange,
    192         new ClosestComparator<int[]>() {
    193           @Override int diff(int[] range) {
    194             final int maxFpsWeight = 10;
    195             return range[android.hardware.Camera.Parameters.PREVIEW_FPS_MIN_INDEX]
    196                 + maxFpsWeight * abs(framerate
    197                     - range[android.hardware.Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
    198           }
    199      });
    200   }
    201 
    202   public static android.hardware.Camera.Size getClosestSupportedSize(
    203       List<android.hardware.Camera.Size> supportedSizes, final int requestedWidth,
    204       final int requestedHeight) {
    205     return Collections.min(supportedSizes,
    206         new ClosestComparator<android.hardware.Camera.Size>() {
    207           @Override int diff(android.hardware.Camera.Size size) {
    208             return abs(requestedWidth - size.width) + abs(requestedHeight - size.height);
    209           }
    210      });
    211   }
    212 
    213   private static String getNameOfDevice(int facing) {
    214     final android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
    215     for (int i = 0; i < android.hardware.Camera.getNumberOfCameras(); ++i) {
    216       try {
    217         android.hardware.Camera.getCameraInfo(i, info);
    218         if (info.facing == facing) {
    219           return getDeviceName(i);
    220         }
    221       } catch (Exception e) {
    222         Logging.e(TAG, "getCameraInfo() failed on index " + i, e);
    223       }
    224     }
    225     return null;
    226   }
    227 }
    228