Index: media/base/android/java/src/org/chromium/media/VideoCapture.java |
=================================================================== |
--- media/base/android/java/src/org/chromium/media/VideoCapture.java (revision 0) |
+++ media/base/android/java/src/org/chromium/media/VideoCapture.java (revision 0) |
@@ -0,0 +1,329 @@ |
+// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.media; |
+ |
+import android.content.Context; |
+import android.graphics.ImageFormat; |
+import android.graphics.SurfaceTexture; |
+import android.graphics.SurfaceTexture.OnFrameAvailableListener; |
+import android.hardware.Camera; |
+import android.hardware.Camera.PreviewCallback; |
+import android.opengl.GLES11Ext; |
+import android.opengl.GLES20; |
+import android.util.Log; |
+import android.view.Surface; |
+import android.view.WindowManager; |
+ |
+import java.io.IOException; |
+import java.lang.Integer; |
+import java.lang.Object; |
+import java.util.concurrent.locks.ReentrantLock; |
+import java.util.Iterator; |
+import java.util.List; |
+import javax.microedition.khronos.opengles.GL10; |
+ |
+import org.chromium.base.CalledByNative; |
+ |
+public class VideoCapture implements PreviewCallback, OnFrameAvailableListener { |
+ public class CaptureCapability { |
+ public int mWidth = 0; |
+ public int mHeight = 0; |
+ public int mDesiredFps = 0; |
+ public int mFpsLowInMs = 0; |
+ public int mFpsHighInMs = 0; |
+ } |
+ |
+ private Camera mCamera; |
+ public ReentrantLock mPreviewBufferLock = new ReentrantLock(); |
+ private int mPixelFormat = ImageFormat.YV12; |
+ private Context mContext = null; |
+ // True when native code has started capture. |
+ private boolean mIsRunning=false; |
+ |
+ private final int mNumCaptureBuffers = 3; |
+ private int mExpectedFrameSize = 0; |
+ private int mId = 0; |
+ // Native callback context variable. |
+ private long mNativeCapture = 0; |
+ private int[] mGlTextures = null; |
+ private SurfaceTexture mSurfaceTexture = null; |
+ private static final int GL_TEXTURE_EXTERNAL_OES = 0x8D65; |
+ |
+ private int mCameraOrientation = 0; |
+ private int mCameraFacing = 0; |
+ private int mDeviceOrientation = 0; |
+ |
+ CaptureCapability mCurrentCapability = null; |
+ |
+ // Returns an instance of VideoCapture. |
+ @CalledByNative |
+ public static VideoCapture createVideoCapture(Context context, int id, |
+ long nativeCapture) { |
+ Log.d("JAVA VideoCapture", "createVideoCapture: " + id); |
+ return new VideoCapture(context, id, nativeCapture); |
+ } |
+ |
+ public VideoCapture(Context context, int id, long nativeCapture) { |
+ Log.d("JAVA VideoCapture", "VideoCapture constructor: " + id); |
+ mContext = context; |
+ mId = id; |
+ mNativeCapture = nativeCapture; |
+ } |
+ |
+ @CalledByNative |
+ public int allocate(int width, int height, int frameRate) { |
+ Log.d("JAVA VideoCapture", "allocate: requested width=" + width + |
+ ", height=" + height + ", frameRate=" + frameRate); |
+ try { |
+ mCamera = Camera.open(mId); |
+ Camera.CameraInfo camera_info = new Camera.CameraInfo(); |
+ Camera.getCameraInfo(mId, camera_info); |
+ mCameraOrientation = camera_info.orientation; |
+ mCameraFacing = camera_info.facing; |
+ mDeviceOrientation = getDeviceOrientation(); |
+ Log.d("JAVA VideoCapture", |
+ "allocate: device orientation=" + mDeviceOrientation + |
+ ", camera orientation=" + mCameraOrientation + |
+ ", facing=" + mCameraFacing); |
+ |
+ Camera.Parameters parameters = mCamera.getParameters(); |
+ |
+ // Calculate fps. |
+ List<int[]> listFpsRange = parameters.getSupportedPreviewFpsRange(); |
+ int frameRateInMs = frameRate * 1000; |
+ boolean fpsIsSupported = false; |
+ int fpsMin = 0; |
+ int fpsMax = 0; |
+ Iterator itFpsRange = listFpsRange.iterator(); |
+ while (itFpsRange.hasNext()) { |
+ int[] fpsRange = (int[])itFpsRange.next(); |
+ if (fpsRange[0] <= frameRateInMs && |
+ frameRateInMs <= fpsRange[1]) { |
+ fpsIsSupported = true; |
+ fpsMin = fpsRange[0]; |
+ fpsMax = fpsRange[1]; |
+ break; |
+ } |
+ } |
+ |
+ if (!fpsIsSupported) { |
+ Log.e("JAVA VideoCapture", "allocate: fps " + frameRate + |
+ " is not supported"); |
+ return -1; |
+ } |
+ |
+ mCurrentCapability = new CaptureCapability(); |
+ mCurrentCapability.mFpsLowInMs = fpsMin; |
+ mCurrentCapability.mFpsHighInMs = fpsMax; |
+ 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(); |
+ int diff = Math.abs(size.width - width) + |
+ Math.abs(size.height - height); |
+ Log.d("JAVA VideoCapture", "allocate: support resolution (" + |
+ size.width + ", " + size.height + "), diff=" + diff); |
+ if (diff < minDiff) { |
+ minDiff = diff; |
+ matchedWidth = size.width; |
+ matchedHeight = size.height; |
+ } |
+ } |
+ mCurrentCapability.mWidth = matchedWidth; |
+ mCurrentCapability.mHeight = matchedHeight; |
+ Log.d("JAVA VideoCaptuer", "allocate: matched width=" + |
+ matchedWidth + ", height=" + matchedHeight); |
+ |
+ parameters.setPreviewSize(matchedWidth, matchedHeight); |
+ parameters.setPreviewFormat(mPixelFormat); |
+ parameters.setPreviewFpsRange(fpsMin, fpsMax); |
+ mCamera.setParameters(parameters); |
+ |
+ // Set SurfaceTexture. |
+ mGlTextures = new int[1]; |
+ // Generate one texture pointer and bind it as an external texture. |
+ GLES20.glGenTextures(1, mGlTextures, 0); |
+ GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mGlTextures[0]); |
+ // No mip-mapping with camera source. |
+ GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, |
+ GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); |
+ GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, |
+ GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); |
+ // Clamp to edge is only option. |
+ GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, |
+ GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE); |
+ GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, |
+ GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE); |
+ |
+ mSurfaceTexture = new SurfaceTexture(mGlTextures[0]); |
+ mSurfaceTexture.setOnFrameAvailableListener(this); |
+ |
+ mCamera.setPreviewTexture(mSurfaceTexture); |
+ |
+ int bufSize = matchedWidth * matchedHeight * |
+ ImageFormat.getBitsPerPixel(mPixelFormat) / 8; |
+ for (int i = 0; i < mNumCaptureBuffers; i++) { |
+ byte[] buffer = new byte[bufSize]; |
qinmin
2013/01/22 21:31:42
indent by 4
wjia(left Chromium)
2013/01/22 22:40:01
Done.
|
+ mCamera.addCallbackBuffer(buffer); |
+ } |
+ mExpectedFrameSize = bufSize; |
+ } |
+ catch (Exception ex) { |
+ return -1; |
+ } |
+ return 0; |
+ } |
+ |
+ @CalledByNative |
+ public int queryWidth() { |
+ return mCurrentCapability.mWidth; |
+ } |
+ |
+ @CalledByNative |
+ public int queryHeight() { |
+ return mCurrentCapability.mHeight; |
+ } |
+ |
+ @CalledByNative |
+ public int queryFrameRate() { |
+ return mCurrentCapability.mDesiredFps; |
+ } |
+ |
+ @CalledByNative |
+ public int startCapture() { |
+ Log.d("JAVA VideoCapture", "startCapture:"); |
+ if (mCamera == null) { |
+ Log.e("JAVA VideoCapture", "startCapture: camera is null"); |
+ return -1; |
+ } |
+ |
+ try { |
+ mPreviewBufferLock.lock(); |
+ if (mIsRunning) { |
zhongping.wang
2013/01/23 05:09:24
not unlock mPreviewBufferLock
wjia(left Chromium)
2013/01/24 05:39:16
Switched to use try{} finally{}.
|
+ return 0; |
+ } |
+ mIsRunning = true; |
+ mPreviewBufferLock.unlock(); |
+ mCamera.setPreviewCallbackWithBuffer(this); |
+ mCamera.startPreview(); |
+ } |
+ catch (Exception ex) { |
+ Log.e("JAVA VideoCapture", "startCapture: failed to start camera"); |
+ return -1; |
+ } |
+ return 0; |
+ } |
+ |
+ @CalledByNative |
+ public int stopCapture() { |
+ Log.d("JAVA VideoCapture", "stopCapture:"); |
+ if (mCamera == null) { |
+ Log.d("JAVA VideoCapture", "stopCapture: camera is null"); |
+ return 0; |
+ } |
+ |
+ try { |
+ mPreviewBufferLock.lock(); |
+ if (!mIsRunning) { |
zhongping.wang
2013/01/23 05:09:24
not unlock mPreviewBufferLock
wjia(left Chromium)
2013/01/24 05:39:16
Done.
|
+ return 0; |
+ } |
+ mIsRunning = false; |
+ mPreviewBufferLock.unlock(); |
+ |
+ mCamera.stopPreview(); |
+ mCamera.setPreviewCallbackWithBuffer(null); |
+ } |
+ catch (Exception ex) { |
+ Log.e("JAVA VideoCapture", "stopCapture: failed to stop camera"); |
+ return -1; |
+ } |
+ |
+ return 0; |
+ } |
+ |
+ @CalledByNative |
+ public void deallocate() { |
+ Log.d("JAVA VideoCapture", "deallocate:"); |
+ if (mCamera == null) |
+ return; |
+ |
+ stopCapture(); |
+ try { |
+ mCamera.setPreviewTexture(null); |
+ mSurfaceTexture.setOnFrameAvailableListener(null); |
+ GLES20.glDeleteTextures(1, mGlTextures, 0); |
+ mCurrentCapability = null; |
+ mCamera.release(); |
+ mCamera = null; |
+ } |
+ catch (Exception ex) { |
+ Log.e("JAVA VideoCapture", "deallocate: failed to deallocate camera"); |
+ return; |
+ } |
+ } |
+ |
+ @Override |
+ public void onPreviewFrame(byte[] data, Camera camera) { |
+ mPreviewBufferLock.lock(); |
+ |
+ if (mIsRunning) { |
+ if (data.length == mExpectedFrameSize) { |
+ int rotation = getDeviceOrientation(); |
+ if (rotation != mDeviceOrientation) { |
+ mDeviceOrientation = rotation; |
qinmin
2013/01/22 21:31:42
indent by 4
wjia(left Chromium)
2013/01/22 22:40:01
Done.
|
+ Log.d("JAVA VideoCapture", |
+ "onPreviewFrame: device orientation=" + |
+ mDeviceOrientation + ", camera orientation=" + |
+ mCameraOrientation); |
+ } |
+ boolean flip_vert = false; |
+ boolean flip_horiz = false; |
+ if (mCameraFacing == Camera.CameraInfo.CAMERA_FACING_FRONT) { |
+ rotation = (mCameraOrientation + rotation) % 360; |
+ rotation = (360 - rotation) % 360; |
+ flip_horiz = (rotation == 180 || rotation == 0); |
+ flip_vert = !flip_horiz; |
+ } else { |
+ rotation = (mCameraOrientation - rotation + 360) % 360; |
+ } |
+ nativeOnFrameAvailable(data, mExpectedFrameSize, mNativeCapture, |
+ rotation, flip_vert, flip_horiz); |
+ if (camera != null) { |
+ camera.addCallbackBuffer(data); |
+ } |
+ } |
+ } |
+ mPreviewBufferLock.unlock(); |
+ } |
+ |
+ @Override |
+ public void onFrameAvailable(SurfaceTexture surfaceTexture) { } |
zhongping.wang
2013/01/23 05:09:24
Is interface OnFrameAvailableListener necessary si
wjia(left Chromium)
2013/01/24 05:39:16
Done.
|
+ |
+ private native void nativeOnFrameAvailable(byte[] data, int length, |
+ long nativeCapture, int rotation, |
+ boolean flip_vert, boolean flip_horiz); |
+ |
+ private int getDeviceOrientation() { |
+ int orientation = 0; |
+ if (mContext != null) { |
+ WindowManager wm = (WindowManager)mContext.getSystemService( |
+ Context.WINDOW_SERVICE); |
qinmin
2013/01/22 21:31:42
indent by 8
wjia(left Chromium)
2013/01/22 22:40:01
Done.
|
+ switch(wm.getDefaultDisplay().getRotation()) { |
+ case Surface.ROTATION_0 : orientation = 0 ; break; |
qinmin
2013/01/22 21:31:42
use new lines
wjia(left Chromium)
2013/01/22 22:40:01
Done.
|
+ case Surface.ROTATION_90 : orientation = 90 ; break; |
+ case Surface.ROTATION_180: orientation = 180; break; |
+ case Surface.ROTATION_270: orientation = 270; break; |
+ } |
+ } |
+ return orientation; |
+ } |
+} |