Chromium Code Reviews| Index: media/base/android/java/src/org/chromium/media/VideoCapture.java |
| diff --git a/media/base/android/java/src/org/chromium/media/VideoCapture.java b/media/base/android/java/src/org/chromium/media/VideoCapture.java |
| index 7e53b7c0232ede40ed0b8d59e77a50816ffeebdd..244697abf8ba0f3fd80fff1446c933c363146a30 100644 |
| --- a/media/base/android/java/src/org/chromium/media/VideoCapture.java |
| +++ b/media/base/android/java/src/org/chromium/media/VideoCapture.java |
| @@ -9,6 +9,7 @@ import android.graphics.ImageFormat; |
| import android.graphics.SurfaceTexture; |
| import android.hardware.Camera; |
| import android.hardware.Camera.PreviewCallback; |
| +import android.hardware.Camera.Size; |
| import android.opengl.GLES20; |
| import android.util.Log; |
| import android.view.Surface; |
| @@ -18,7 +19,7 @@ import org.chromium.base.CalledByNative; |
| import org.chromium.base.JNINamespace; |
| import java.io.IOException; |
| -import java.util.Iterator; |
| +import java.util.ArrayList; |
| import java.util.List; |
| import java.util.concurrent.locks.ReentrantLock; |
| @@ -30,10 +31,34 @@ import java.util.concurrent.locks.ReentrantLock; |
| **/ |
| @JNINamespace("media") |
| public class VideoCapture implements PreviewCallback { |
| - static class CaptureCapability { |
| + static class CaptureFormat { |
| + public CaptureFormat( |
| + int width, int height, int framerate, int pixelformat) { |
| + mWidth = width; |
| + mHeight = height; |
| + mFramerate = framerate; |
| + mPixelFormat = pixelformat; |
| + } |
| public int mWidth; |
| public int mHeight; |
| - public int mDesiredFps; |
| + public final int mFramerate; |
| + public final int mPixelFormat; |
| + @CalledByNative("CaptureFormat") |
| + public int getWidth() { |
| + return mWidth; |
| + } |
| + @CalledByNative("CaptureFormat") |
| + public int getHeight() { |
| + return mHeight; |
| + } |
| + @CalledByNative("CaptureFormat") |
| + public int getFramerate() { |
| + return mFramerate; |
| + } |
| + @CalledByNative("CaptureFormat") |
| + public int getPixelFormat() { |
| + return mPixelFormat; |
| + } |
| } |
| // Some devices don't support YV12 format correctly, even with JELLY_BEAN or |
| @@ -63,17 +88,17 @@ public class VideoCapture implements PreviewCallback { |
| "ODROID-U2", |
| }; |
| - static void applyMinDimensions(CaptureCapability capability) { |
| + static void applyMinDimensions(CaptureFormat format) { |
| // NOTE: this can discard requested aspect ratio considerations. |
| for (IdAndSizes buggyDevice : s_CAPTURESIZE_BUGGY_DEVICE_LIST) { |
| if (buggyDevice.mModel.contentEquals(android.os.Build.MODEL) && |
| buggyDevice.mDevice.contentEquals(android.os.Build.DEVICE)) { |
| - capability.mWidth = (buggyDevice.mMinWidth > capability.mWidth) |
| + format.mWidth = (buggyDevice.mMinWidth > format.mWidth) |
| ? buggyDevice.mMinWidth |
| - : capability.mWidth; |
| - capability.mHeight = (buggyDevice.mMinHeight > capability.mHeight) |
| + : format.mWidth; |
| + format.mHeight = (buggyDevice.mMinHeight > format.mHeight) |
| ? buggyDevice.mMinHeight |
| - : capability.mHeight; |
| + : format.mHeight; |
| } |
| } |
| } |
| @@ -94,7 +119,6 @@ public class VideoCapture implements PreviewCallback { |
| private Camera mCamera; |
| public ReentrantLock mPreviewBufferLock = new ReentrantLock(); |
| - private int mImageFormat = ImageFormat.YV12; |
| private Context mContext = null; |
| // True when native code has started capture. |
| private boolean mIsRunning = false; |
| @@ -112,7 +136,7 @@ public class VideoCapture implements PreviewCallback { |
| private int mCameraFacing = 0; |
| private int mDeviceOrientation = 0; |
| - CaptureCapability mCurrentCapability = null; |
| + CaptureFormat mCaptureFormat = null; |
| private static final String TAG = "VideoCapture"; |
| @CalledByNative |
| @@ -121,6 +145,64 @@ public class VideoCapture implements PreviewCallback { |
| return new VideoCapture(context, id, nativeVideoCaptureDeviceAndroid); |
| } |
| + @CalledByNative |
| + public static CaptureFormat[] getDeviceSupportedFormats(int id) { |
| + Camera camera; |
| + try { |
| + camera = Camera.open(id); |
| + } catch (RuntimeException ex) { |
| + Log.e(TAG, "Camera.open: " + ex); |
| + return null; |
| + } |
| + Camera.Parameters parameters = camera.getParameters(); |
| + |
| + ArrayList<CaptureFormat> formatList = new ArrayList<CaptureFormat>(); |
| + // getSupportedPreview{Formats,FpsRange,PreviewSizes}() returns Lists |
| + // with at least one element, but when the camera is in bad state, they |
| + // can return null pointers; in that case we use a 0 entry, so we can |
| + // retrieve as much information as possible. |
| + List<Integer> pixelFormats = parameters.getSupportedPreviewFormats(); |
| + if (pixelFormats == null) { |
| + pixelFormats = new ArrayList<Integer>(); |
| + } |
| + if (pixelFormats.size() == 0) { |
| + pixelFormats.add(ImageFormat.UNKNOWN); |
| + } |
| + for (Integer previewFormat : pixelFormats) { |
| + int pixelFormat = |
| + AndroidImageFormatList.ANDROID_IMAGEFORMAT_UNKNOWN; |
| + if (previewFormat == ImageFormat.YV12) { |
| + pixelFormat = AndroidImageFormatList.ANDROID_IMAGEFORMAT_YV12; |
| + } else if (previewFormat == ImageFormat.NV21) { |
| + continue; |
|
mcasas
2014/02/14 20:57:49
NV21 is explicitly skipped.
|
| + } |
|
wjia(left Chromium)
2014/02/14 22:58:07
I thought this over again. Need some clarification
mcasas
2014/02/15 10:12:22
The supported formats are cached in VideoCaptureMa
|
| + |
| + List<int[]> listFpsRange = parameters.getSupportedPreviewFpsRange(); |
| + if (listFpsRange == null) { |
| + listFpsRange = new ArrayList<int[]>(); |
| + } |
| + if (listFpsRange.size() == 0) { |
| + listFpsRange.add(new int[] {0, 0}); |
| + } |
| + for (int[] fpsRange : listFpsRange) { |
| + List<Camera.Size> supportedSizes = |
| + parameters.getSupportedPreviewSizes(); |
| + if (supportedSizes == null) { |
| + supportedSizes = new ArrayList<Camera.Size>(); |
| + } |
| + if (supportedSizes.size() == 0) { |
| + supportedSizes.add(camera.new Size(0, 0)); |
| + } |
| + for (Camera.Size size : supportedSizes) { |
| + formatList.add(new CaptureFormat(size.width, size.height, |
| + (fpsRange[0] + 999 ) / 1000, pixelFormat)); |
| + } |
| + } |
| + } |
| + camera.release(); |
| + return formatList.toArray(new CaptureFormat[formatList.size()]); |
| + } |
| + |
| public VideoCapture( |
| Context context, int id, long nativeVideoCaptureDeviceAndroid) { |
| mContext = context; |
| @@ -136,7 +218,7 @@ public class VideoCapture implements PreviewCallback { |
| try { |
| mCamera = Camera.open(mId); |
| } catch (RuntimeException ex) { |
| - Log.e(TAG, "allocate:Camera.open: " + ex); |
| + Log.e(TAG, "allocate: Camera.open: " + ex); |
| return false; |
| } |
| @@ -150,25 +232,20 @@ public class VideoCapture implements PreviewCallback { |
| Camera.Parameters parameters = mCamera.getParameters(); |
| - // Calculate fps. |
| + // getSupportedPreviewFpsRange() returns a List with at least one |
| + // element, but when camera is in bad state, it can return null pointer. |
| List<int[]> listFpsRange = parameters.getSupportedPreviewFpsRange(); |
| if (listFpsRange == null || listFpsRange.size() == 0) { |
| Log.e(TAG, "allocate: no fps range found"); |
| return false; |
| } |
| int frameRateInMs = frameRate * 1000; |
| - Iterator itFpsRange = listFpsRange.iterator(); |
| - int[] fpsRange = (int[]) itFpsRange.next(); |
| // Use the first range as default. |
| - int fpsMin = fpsRange[0]; |
| - int fpsMax = fpsRange[1]; |
| - int newFrameRate = (fpsMin + 999) / 1000; |
| - while (itFpsRange.hasNext()) { |
| - fpsRange = (int[]) itFpsRange.next(); |
| - if (fpsRange[0] <= frameRateInMs && |
| - frameRateInMs <= fpsRange[1]) { |
| - fpsMin = fpsRange[0]; |
| - fpsMax = fpsRange[1]; |
| + int[] fpsMinMax = listFpsRange.get(0); |
| + int newFrameRate = (fpsMinMax[0] + 999) / 1000; |
| + for (int[] fpsRange : listFpsRange) { |
| + if (fpsRange[0] <= frameRateInMs && frameRateInMs <= fpsRange[1]) { |
| + fpsMinMax = fpsRange; |
| newFrameRate = frameRate; |
| break; |
| } |
| @@ -176,21 +253,16 @@ public class VideoCapture implements PreviewCallback { |
| frameRate = newFrameRate; |
| Log.d(TAG, "allocate: fps set to " + frameRate); |
| - mCurrentCapability = new CaptureCapability(); |
| - mCurrentCapability.mDesiredFps = frameRate; |
| - |
| // Calculate size. |
| List<Camera.Size> listCameraSize = |
| parameters.getSupportedPreviewSizes(); |
| int minDiff = Integer.MAX_VALUE; |
| int matchedWidth = width; |
| int matchedHeight = height; |
| - Iterator itCameraSize = listCameraSize.iterator(); |
| - while (itCameraSize.hasNext()) { |
| - Camera.Size size = (Camera.Size) itCameraSize.next(); |
| + for (Camera.Size size : listCameraSize) { |
| int diff = Math.abs(size.width - width) + |
| Math.abs(size.height - height); |
| - Log.d(TAG, "allocate: support resolution (" + |
| + Log.d(TAG, "allocate: supported (" + |
| size.width + ", " + size.height + "), diff=" + diff); |
| // TODO(wjia): Remove this hack (forcing width to be multiple |
| // of 32) by supporting stride in video frame buffer. |
| @@ -206,16 +278,15 @@ public class VideoCapture implements PreviewCallback { |
| Log.e(TAG, "allocate: can not find a multiple-of-32 resolution"); |
| return false; |
| } |
| - mCurrentCapability.mWidth = matchedWidth; |
| - mCurrentCapability.mHeight = matchedHeight; |
| + |
| + mCaptureFormat = new CaptureFormat( |
| + matchedWidth, matchedHeight, frameRate, |
| + BuggyDeviceHack.getImageFormat()); |
| // Hack to avoid certain capture resolutions under a minimum one, |
| // see http://crbug.com/305294 |
| - BuggyDeviceHack.applyMinDimensions(mCurrentCapability); |
| - |
| - Log.d(TAG, "allocate: matched (" + mCurrentCapability.mWidth + "x" + |
| - mCurrentCapability.mHeight + ")"); |
| - |
| - mImageFormat = BuggyDeviceHack.getImageFormat(); |
| + BuggyDeviceHack.applyMinDimensions(mCaptureFormat); |
| + Log.d(TAG, "allocate: matched (" + mCaptureFormat.mWidth + "x" + |
| + mCaptureFormat.mHeight + ")"); |
| if (parameters.isVideoStabilizationSupported()) { |
| Log.d(TAG, "Image stabilization supported, currently: " |
| @@ -224,11 +295,10 @@ public class VideoCapture implements PreviewCallback { |
| } else { |
| Log.d(TAG, "Image stabilization not supported."); |
| } |
| - |
| - parameters.setPreviewSize(mCurrentCapability.mWidth, |
| - mCurrentCapability.mHeight); |
| - parameters.setPreviewFormat(mImageFormat); |
| - parameters.setPreviewFpsRange(fpsMin, fpsMax); |
| + parameters.setPreviewSize(mCaptureFormat.mWidth, |
| + mCaptureFormat.mHeight); |
| + parameters.setPreviewFormat(mCaptureFormat.mPixelFormat); |
| + parameters.setPreviewFpsRange(fpsMinMax[0], fpsMinMax[1]); |
| mCamera.setParameters(parameters); |
| // Set SurfaceTexture. Android Capture needs a SurfaceTexture even if |
| @@ -258,9 +328,10 @@ public class VideoCapture implements PreviewCallback { |
| return false; |
| } |
| - int bufSize = mCurrentCapability.mWidth * |
| - mCurrentCapability.mHeight * |
| - ImageFormat.getBitsPerPixel(mImageFormat) / 8; |
| + int bufSize = mCaptureFormat.mWidth * |
| + mCaptureFormat.mHeight * |
| + ImageFormat.getBitsPerPixel( |
| + mCaptureFormat.mPixelFormat) / 8; |
| for (int i = 0; i < NUM_CAPTURE_BUFFERS; i++) { |
| byte[] buffer = new byte[bufSize]; |
| mCamera.addCallbackBuffer(buffer); |
| @@ -272,22 +343,22 @@ public class VideoCapture implements PreviewCallback { |
| @CalledByNative |
| public int queryWidth() { |
| - return mCurrentCapability.mWidth; |
| + return mCaptureFormat.mWidth; |
| } |
| @CalledByNative |
| public int queryHeight() { |
| - return mCurrentCapability.mHeight; |
| + return mCaptureFormat.mHeight; |
| } |
| @CalledByNative |
| public int queryFrameRate() { |
| - return mCurrentCapability.mDesiredFps; |
| + return mCaptureFormat.mFramerate; |
| } |
| @CalledByNative |
| public int getColorspace() { |
| - switch (mImageFormat) { |
| + switch (mCaptureFormat.mPixelFormat) { |
| case ImageFormat.YV12: |
| return AndroidImageFormatList.ANDROID_IMAGEFORMAT_YV12; |
| case ImageFormat.NV21: |
| @@ -351,7 +422,7 @@ public class VideoCapture implements PreviewCallback { |
| mCamera.setPreviewTexture(null); |
| if (mGlTextures != null) |
| GLES20.glDeleteTextures(1, mGlTextures, 0); |
| - mCurrentCapability = null; |
| + mCaptureFormat = null; |
| mCamera.release(); |
| mCamera = null; |
| } catch (IOException ex) { |