1 /* 2 AndroidVideoApi9JniWrapper.java 3 Copyright (C) 2010 Belledonne Communications, Grenoble, France 4 5 This program is free software; you can redistribute it and/or 6 modify it under the terms of the GNU General Public License 7 as published by the Free Software Foundation; either version 2 8 of the License, or (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 */ 19 package org.linphone.mediastream.video.capture; 20 21 import java.util.List; 22 23 import org.linphone.mediastream.Log; 24 25 import android.annotation.TargetApi; 26 import android.graphics.ImageFormat; 27 import android.hardware.Camera; 28 import android.hardware.Camera.Parameters; 29 import android.hardware.Camera.Size; 30 import android.os.Build; 31 32 @TargetApi(Build.VERSION_CODES.GINGERBREAD) 33 public class AndroidVideoApi9JniWrapper { detectCameras(int[] indexes, int[] frontFacing, int[] orientation)34 static public int detectCameras(int[] indexes, int[] frontFacing, int[] orientation) { 35 return AndroidVideoApi5JniWrapper.detectCameras(indexes, frontFacing, orientation); 36 } 37 38 /** 39 * Return the hw-available available resolution best matching the requested one. 40 * Best matching meaning : 41 * - try to find the same one 42 * - try to find one just a little bigger (ex: CIF when asked QVGA) 43 * - as a fallback the nearest smaller one 44 * @param cameraId Camera id 45 * @param requestedW Requested video size width 46 * @param requestedH Requested video size height 47 * @return int[width, height] of the chosen resolution, may be null if no 48 * resolution can possibly match the requested one 49 */ selectNearestResolutionAvailable(int cameraId, int requestedW, int requestedH)50 static public int[] selectNearestResolutionAvailable(int cameraId, int requestedW, int requestedH) { 51 Log.d("selectNearestResolutionAvailable: " + cameraId + ", " + requestedW + "x" + requestedH); 52 return AndroidVideoApi5JniWrapper.selectNearestResolutionAvailableForCamera(cameraId, requestedW, requestedH); 53 } 54 startRecording(int cameraId, int width, int height, int fps, int rotation, final long nativePtr)55 public static Object startRecording(int cameraId, int width, int height, int fps, int rotation, final long nativePtr) { 56 Log.d("startRecording(" + cameraId + ", " + width + ", " + height + ", " + fps + ", " + rotation + ", " + nativePtr + ")"); 57 try { 58 Camera camera = Camera.open(cameraId); 59 Parameters params = camera.getParameters(); 60 61 params.setPreviewSize(width, height); 62 int[] chosenFps = findClosestEnclosingFpsRange(fps*1000, params.getSupportedPreviewFpsRange()); 63 params.setPreviewFpsRange(chosenFps[0], chosenFps[1]); 64 camera.setParameters(params); 65 66 int bufferSize = (width * height * ImageFormat.getBitsPerPixel(params.getPreviewFormat())) / 8; 67 camera.addCallbackBuffer(new byte[bufferSize]); 68 camera.addCallbackBuffer(new byte[bufferSize]); 69 70 camera.setPreviewCallbackWithBuffer(new Camera.PreviewCallback() { 71 public void onPreviewFrame(byte[] data, Camera camera) { 72 // forward image data to JNI 73 if (data == null) { 74 // It appears that there is a bug in the camera driver that is asking for a buffer size bigger than it should 75 Parameters params = camera.getParameters(); 76 Size size = params.getPreviewSize(); 77 int bufferSize = (size.width * size.height * ImageFormat.getBitsPerPixel(params.getPreviewFormat())) / 8; 78 bufferSize += bufferSize / 20; 79 camera.addCallbackBuffer(new byte[bufferSize]); 80 } else if (AndroidVideoApi5JniWrapper.isRecording) { 81 AndroidVideoApi5JniWrapper.putImage(nativePtr, data); 82 camera.addCallbackBuffer(data); 83 } 84 } 85 }); 86 87 setCameraDisplayOrientation(rotation, cameraId, camera); 88 camera.startPreview(); 89 AndroidVideoApi5JniWrapper.isRecording = true; 90 Log.d("Returning camera object: " + camera); 91 return camera; 92 } catch (Exception exc) { 93 exc.printStackTrace(); 94 return null; 95 } 96 } 97 stopRecording(Object cam)98 public static void stopRecording(Object cam) { 99 AndroidVideoApi5JniWrapper.isRecording = false; 100 AndroidVideoApi8JniWrapper.stopRecording(cam); 101 } 102 setPreviewDisplaySurface(Object cam, Object surf)103 public static void setPreviewDisplaySurface(Object cam, Object surf) { 104 AndroidVideoApi5JniWrapper.setPreviewDisplaySurface(cam, surf); 105 } 106 setCameraDisplayOrientation(int rotationDegrees, int cameraId, Camera camera)107 private static void setCameraDisplayOrientation(int rotationDegrees, int cameraId, Camera camera) { 108 android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo(); 109 android.hardware.Camera.getCameraInfo(cameraId, info); 110 111 int result; 112 if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { 113 result = (info.orientation + rotationDegrees) % 360; 114 result = (360 - result) % 360; // compensate the mirror 115 } else { // back-facing 116 result = (info.orientation - rotationDegrees + 360) % 360; 117 } 118 119 Log.w("Camera preview orientation: "+ result); 120 try { 121 camera.setDisplayOrientation(result); 122 } catch (Exception exc) { 123 Log.e("Failed to execute: camera[" + camera + "].setDisplayOrientation(" + result + ")"); 124 exc.printStackTrace(); 125 } 126 } 127 findClosestEnclosingFpsRange(int expectedFps, List<int[]> fpsRanges)128 private static int[] findClosestEnclosingFpsRange(int expectedFps, List<int[]> fpsRanges) { 129 Log.d("Searching for closest fps range from " + expectedFps); 130 if (fpsRanges == null || fpsRanges.size() == 0) { 131 return new int[] { 0, 0 }; 132 } 133 134 // init with first element 135 int[] closestRange = fpsRanges.get(0); 136 int measure = Math.abs(closestRange[0] - expectedFps) 137 + Math.abs(closestRange[1] - expectedFps); 138 for (int[] curRange : fpsRanges) { 139 if (curRange[0] > expectedFps || curRange[1] < expectedFps) continue; 140 int curMeasure = Math.abs(curRange[0] - expectedFps) 141 + Math.abs(curRange[1] - expectedFps); 142 if (curMeasure < measure) { 143 closestRange=curRange; 144 measure = curMeasure; 145 Log.d("a better range has been found: w="+closestRange[0]+",h="+closestRange[1]); 146 } 147 } 148 Log.d("The closest fps range is w="+closestRange[0]+",h="+closestRange[1]); 149 return closestRange; 150 } 151 } 152