Add 15 fps support for Android devices with missing 15 fps
camera mode.

Some latest Android devices support only 30 fps for front camera,
but HW VP8 encoder performance is not enough for 720p 30 fps
encoding. Add 15 fps support for these devices by allowing
frame drop in Android camera wrapper.

BUG=
R=tkchin@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/24149004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@7571 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java b/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java
index 926b350..801edbf 100644
--- a/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java
+++ b/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java
@@ -11,11 +11,13 @@
 package org.webrtc.videoengine;
 
 import java.io.IOException;
+import java.util.List;
 import java.util.concurrent.Exchanger;
 
 import android.content.Context;
 import android.graphics.ImageFormat;
 import android.graphics.SurfaceTexture;
+import android.hardware.Camera.Parameters;
 import android.hardware.Camera.PreviewCallback;
 import android.hardware.Camera;
 import android.opengl.GLES11Ext;
@@ -58,6 +60,7 @@
   private double averageDurationMs;
   private long lastCaptureTimeMs;
   private int frameCount;
+  private int frameDropRatio;
 
   // Requests future capturers to send their frames to |localPreview| directly.
   public static void setLocalPreview(SurfaceHolder localPreview) {
@@ -170,7 +173,38 @@
         parameters.setVideoStabilization(true);
       }
       parameters.setPreviewSize(width, height);
+
+      // Check if requested fps range is supported by camera,
+      // otherwise calculate frame drop ratio.
+      List<int[]> supportedFpsRanges = parameters.getSupportedPreviewFpsRange();
+      frameDropRatio = Integer.MAX_VALUE;
+      for (int i = 0; i < supportedFpsRanges.size(); i++) {
+        int[] range = supportedFpsRanges.get(i);
+        if (range[Parameters.PREVIEW_FPS_MIN_INDEX] == min_mfps &&
+            range[Parameters.PREVIEW_FPS_MAX_INDEX] == max_mfps) {
+          frameDropRatio = 1;
+          break;
+        }
+        if (range[Parameters.PREVIEW_FPS_MIN_INDEX] % min_mfps == 0 &&
+            range[Parameters.PREVIEW_FPS_MAX_INDEX] % max_mfps == 0) {
+          int dropRatio = range[Parameters.PREVIEW_FPS_MAX_INDEX] / max_mfps;
+          frameDropRatio = Math.min(dropRatio, frameDropRatio);
+        }
+      }
+      if (frameDropRatio == Integer.MAX_VALUE) {
+        Log.e(TAG, "Can not find camera fps range");
+        error = new RuntimeException("Can not find camera fps range");
+        exchange(result, false);
+        return;
+      }
+      if (frameDropRatio > 1) {
+        Log.d(TAG, "Frame dropper is enabled. Ratio: " + frameDropRatio);
+      }
+      min_mfps *= frameDropRatio;
+      max_mfps *= frameDropRatio;
+      Log.d(TAG, "Camera preview mfps range: " + min_mfps + " - " + max_mfps);
       parameters.setPreviewFpsRange(min_mfps, max_mfps);
+
       int format = ImageFormat.NV21;
       parameters.setPreviewFormat(format);
       camera.setParameters(parameters);
@@ -180,7 +214,7 @@
       }
       camera.setPreviewCallbackWithBuffer(this);
       frameCount = 0;
-      averageDurationMs = 1000 / max_mfps;
+      averageDurationMs = 1000000.0f / (max_mfps / frameDropRatio);
       camera.startPreview();
       exchange(result, true);
       return;
@@ -296,8 +330,13 @@
       throw new RuntimeException("Unexpected camera in callback!");
     }
     frameCount++;
+    // Check if frame needs to be dropped.
+    if ((frameDropRatio > 1) && (frameCount % frameDropRatio) > 0) {
+      camera.addCallbackBuffer(data);
+      return;
+    }
     long captureTimeMs = SystemClock.elapsedRealtime();
-    if (frameCount > 1) {
+    if (frameCount > frameDropRatio) {
       double durationMs = captureTimeMs - lastCaptureTimeMs;
       averageDurationMs = 0.9 * averageDurationMs + 0.1 * durationMs;
       if ((frameCount % 30) == 0) {
diff --git a/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java b/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java
index fe207ca..4b0089b 100644
--- a/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java
+++ b/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java
@@ -75,6 +75,36 @@
           sizes.put(size);
         }
 
+        boolean is30fpsRange = false;
+        boolean is15fpsRange = false;
+        // If there is constant 30 fps mode, but no 15 fps - add 15 fps
+        // mode to the list of supported ranges. Frame drop will be done
+        // in software.
+        for (int[] range : supportedFpsRanges) {
+          if (range[Parameters.PREVIEW_FPS_MIN_INDEX] == 30000 &&
+              range[Parameters.PREVIEW_FPS_MAX_INDEX] == 30000) {
+            is30fpsRange = true;
+          }
+          if (range[Parameters.PREVIEW_FPS_MIN_INDEX] == 15000 &&
+              range[Parameters.PREVIEW_FPS_MAX_INDEX] == 15000) {
+            is15fpsRange = true;
+          }
+        }
+        if (is30fpsRange && !is15fpsRange) {
+          Log.d(TAG, "Adding 15 fps support");
+          int[] newRange = new int [Parameters.PREVIEW_FPS_MAX_INDEX + 1];
+          newRange[Parameters.PREVIEW_FPS_MIN_INDEX] = 15000;
+          newRange[Parameters.PREVIEW_FPS_MAX_INDEX] = 15000;
+          for (int j = 0; j < supportedFpsRanges.size(); j++ ) {
+            int[] range = supportedFpsRanges.get(j);
+            if (range[Parameters.PREVIEW_FPS_MAX_INDEX] >
+                newRange[Parameters.PREVIEW_FPS_MAX_INDEX]) {
+              supportedFpsRanges.add(j, newRange);
+              break;
+            }
+          }
+        }
+
         JSONArray mfpsRanges = new JSONArray();
         for (int[] range : supportedFpsRanges) {
           JSONObject mfpsRange = new JSONObject();