2015-01-10 107 views
3

嗨,我想在我的Android手機上並排顯示實時攝像頭預覽。 像這樣:[android並排攝像頭

https://lh6.googleusercontent.com/-Ngo4tD4AvWY/U8JOHxxQ9KI/AAAAAAAACK8/NZjnw1Yqg8A/w384-h287/2.jpg

它的工作原理,如果只是左半邊屏幕顯示實時預覽或只是右側, 但如果我要顯示在左邊,分辯側同時我只看到一個黑色屏幕 我不知道爲什麼。

清單

<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
    package="de.test.normaltest.sbscameraview" > 

    <uses-permission android:name="android.permission.CAMERA" /> 
    <uses-feature android:name="android.hardware.camera" /> 

    <application 
     android:allowBackup="true" 
     android:icon="@drawable/ic_launcher" 
     android:label="@string/app_name" 
     android:theme="@style/AppTheme" > 
     <activity 
      android:name=".MainActivity" 
      android:label="@string/app_name" 
      android:screenOrientation="landscape"> 

      <intent-filter> 
       <action android:name="android.intent.action.MAIN" /> 

       <category android:name="android.intent.category.LAUNCHER" /> 
      </intent-filter> 
     </activity> 
    </application> 

MainActivity

public class MainActivity extends Activity { 
    private CameraView cameraView1; 
    private CameraView cameraView2; 
    private Camera mCamera1; 
    private Camera mCamera2; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 

     mCamera1 = getCameraInstance(); 
     cameraView1 = new CameraView(this, mCamera1); 
     cameraView2 = new CameraView(this, mCamera1); 

     FrameLayout preview1 = (FrameLayout) findViewById(R.id.camera_preview1); 
     preview1.addView(cameraView1); 

     FrameLayout preview2 = (FrameLayout) findViewById(R.id.camera_preview2); 
     preview2.addView(cameraView2); 
    } 

    /** A safe way to get an instance of the Camera object. */ 
    public static Camera getCameraInstance(){ 
     Camera c = null; 
     try { 
      c = Camera.open(); // attempt to get a Camera instance 
     } 
     catch (Exception e){ 
      Log.i("camera", "null"); 
     } 
     return c; // returns null if camera is unavailable 
    } 
} 

CameraView

public class CameraView extends SurfaceView implements SurfaceHolder.Callback { 
    private SurfaceHolder mHolder; 
    private Camera mCamera; 


    public CameraView(Context context, Camera camera) { 
     super(context); 

     mHolder = getHolder(); 
     mHolder.addCallback(this); 

     mCamera = camera; 
    } 

    @Override 
    public void surfaceCreated(SurfaceHolder holder) { 
     try { 
      mCamera.setPreviewDisplay(holder); 
      mCamera.startPreview(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 

    } 

    @Override 
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 
     Log.i("CameraView", "surfaceChanged"); 
    } 

    @Override 
    public void surfaceDestroyed(SurfaceHolder holder) { 

    } 
} 
+0

AFAIK,您只能打開一次相機。 – CommonsWare

+0

是的,我只做一次。在「getCameraInstance」方法中,然後將對象賦予兩個SurfaceView對象。 它也適用,如果我創建兩個cameraViews,只需添加「cameraView2」 –

+0

「我將對象賦予兩個SurfaceView對象」 - 一個「Camera」一次只能使用一個「SurfaceHolder」。 – CommonsWare

回答

2

您可以使用這些代碼來打開攝像頭在谷歌紙板。

清單:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
package="com.oculab.mehmettaha.vrcamera" > 

<uses-permission android:name="android.permission.NFC" /> 
<uses-permission android:name="android.permission.VIBRATE" /> 
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> 
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 
<uses-permission android:name="android.permission.CAMERA" /> 

<uses-feature android:name="android.hardware.camera" android:required="false" /> 
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" /> 
<uses-feature android:name="android.hardware.camera.front" android:required="false" /> 

<uses-feature android:glEsVersion="0x00020000" android:required="true" /> 


<application 
    android:allowBackup="true" 
    android:icon="@mipmap/ic_launcher" 
    android:label="@string/app_name" 
    android:theme="@style/AppTheme"> 
    <activity 
     android:name=".MainActivity" 
     android:label="@string/app_name" 
     android:screenOrientation="landscape" 
     android:configChanges="orientation|keyboardHidden|screenSize"> 
     <intent-filter> 
      <action android:name="android.intent.action.MAIN" /> 

      <category android:name="android.intent.category.LAUNCHER" /> 
     </intent-filter> 
    </activity> 
</application> 

CardboardOverlayView.java

public class CardboardOverlayView extends LinearLayout { 
private static final String TAG = CardboardOverlayView.class.getSimpleName(); 
private final CardboardOverlayEyeView mLeftView; 
private final CardboardOverlayEyeView mRightView; 
private AlphaAnimation mTextFadeAnimation; 

public CardboardOverlayView(Context context, AttributeSet attrs) { 
    super(context, attrs); 
    setOrientation(HORIZONTAL); 

    LayoutParams params = new LayoutParams(
      LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 1.0f); 
    params.setMargins(0, 0, 0, 0); 

    mLeftView = new CardboardOverlayEyeView(context, attrs); 
    mLeftView.setLayoutParams(params); 
    addView(mLeftView); 

    mRightView = new CardboardOverlayEyeView(context, attrs); 
    mRightView.setLayoutParams(params); 
    addView(mRightView); 

    // Set some reasonable defaults. 
    setDepthOffset(0.016f); 
    setColor(Color.rgb(150, 255, 180)); 
    setVisibility(View.VISIBLE); 

    mTextFadeAnimation = new AlphaAnimation(1.0f, 0.0f); 
    mTextFadeAnimation.setDuration(5000); 
} 

public void show3DToast(String message) { 
    setText(message); 
    setTextAlpha(1f); 
    mTextFadeAnimation.setAnimationListener(new EndAnimationListener() { 
     @Override 
     public void onAnimationEnd(Animation animation) { 
      setTextAlpha(0f); 
     } 
    }); 
    startAnimation(mTextFadeAnimation); 
} 

private abstract class EndAnimationListener implements Animation.AnimationListener { 
    @Override public void onAnimationRepeat(Animation animation) {} 
    @Override public void onAnimationStart(Animation animation) {} 
} 

private void setDepthOffset(float offset) { 
    mLeftView.setOffset(offset); 
    mRightView.setOffset(-offset); 
} 

private void setText(String text) { 
    mLeftView.setText(text); 
    mRightView.setText(text); 
} 

private void setTextAlpha(float alpha) { 
    mLeftView.setTextViewAlpha(alpha); 
    mRightView.setTextViewAlpha(alpha); 
} 

private void setColor(int color) { 
    mLeftView.setColor(color); 
    mRightView.setColor(color); 
} 

/** 
* A simple view group containing some horizontally centered text underneath a horizontally 
* centered image. 
* 
* This is a helper class for CardboardOverlayView. 
*/ 
private class CardboardOverlayEyeView extends ViewGroup { 
    private final ImageView imageView; 
    private final TextView textView; 
    private float offset; 

    public CardboardOverlayEyeView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     imageView = new ImageView(context, attrs); 
     imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 
     imageView.setAdjustViewBounds(true); // Preserve aspect ratio. 
     addView(imageView); 

     textView = new TextView(context, attrs); 
     textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14.0f); 
     textView.setTypeface(textView.getTypeface(), Typeface.BOLD); 
     textView.setGravity(Gravity.CENTER); 
     textView.setShadowLayer(3.0f, 0.0f, 0.0f, Color.DKGRAY); 
     addView(textView); 
    } 

    public void setColor(int color) { 
     imageView.setColorFilter(color); 
     textView.setTextColor(color); 
    } 

    public void setText(String text) { 
     textView.setText(text); 
    } 

    public void setTextViewAlpha(float alpha) { 
     textView.setAlpha(alpha); 
    } 

    public void setOffset(float offset) { 
     this.offset = offset; 
    } 

    @Override 
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 
     // Width and height of this ViewGroup. 
     final int width = right - left; 
     final int height = bottom - top; 

     // The size of the image, given as a fraction of the dimension as a ViewGroup. We multiply 
     // both width and heading with this number to compute the image's bounding box. Inside the 
     // box, the image is the horizontally and vertically centered. 
     final float imageSize = 0.12f; 

     // The fraction of this ViewGroup's height by which we shift the image off the ViewGroup's 
     // center. Positive values shift downwards, negative values shift upwards. 
     final float verticalImageOffset = -0.07f; 

     // Vertical position of the text, specified in fractions of this ViewGroup's height. 
     final float verticalTextPos = 0.52f; 

     // Layout ImageView 
     float imageMargin = (1.0f - imageSize)/2.0f; 
     float leftMargin = (int) (width * (imageMargin + offset)); 
     float topMargin = (int) (height * (imageMargin + verticalImageOffset)); 
     imageView.layout(
       (int) leftMargin, (int) topMargin, 
       (int) (leftMargin + width * imageSize), (int) (topMargin + height * imageSize)); 

     // Layout TextView 
     leftMargin = offset * width; 
     topMargin = height * verticalTextPos; 
     textView.layout(
       (int) leftMargin, (int) topMargin, 
       (int) (leftMargin + width), (int) (topMargin + height * (1.0f - verticalTextPos))); 
    } 
} 
    } 

MainActivity.java

public class MainActivity extends CardboardActivity implements CardboardView.StereoRenderer, OnFrameAvailableListener { 

private static final String TAG = "VRCamMtMMainAc"; 
private static final int GL_TEXTURE_EXTERNAL_OES = 0x8D65; 
private Camera camera; 

private final String vertexShaderCode = 
     "attribute vec4 position;" + 
       "attribute vec2 inputTextureCoordinate;" + 
       "varying vec2 textureCoordinate;" + 
       "void main()" + 
       "{" + 
       "gl_Position = position;" + 
       "textureCoordinate = inputTextureCoordinate;" + 
       "}"; 

private final String fragmentShaderCode = 
     "#extension GL_OES_EGL_image_external : require\n" + 
       "precision mediump float;" + 
       "varying vec2 textureCoordinate;       \n" + 
       "uniform samplerExternalOES s_texture;    \n" + 
       "void main(void) {" + 
       " gl_FragColor = texture2D(s_texture, textureCoordinate);\n" + 
       //" gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" + 
       "}"; 

private FloatBuffer vertexBuffer, textureVerticesBuffer, vertexBuffer2; 
private ShortBuffer drawListBuffer, buf2; 
private int mProgram; 
private int mPositionHandle, mPositionHandle2; 
private int mColorHandle; 
private int mTextureCoordHandle; 


// number of coordinates per vertex in this array 
static final int COORDS_PER_VERTEX = 2; 
static float squareVertices[] = { // in counterclockwise order: 
     -1.0f, -1.0f, // 0.left - mid 
     1.0f, -1.0f, // 1. right - mid 
     -1.0f, 1.0f, // 2. left - top 
     1.0f, 1.0f, // 3. right - top 

}; 


private short drawOrder[] = {0, 2, 1, 1, 2, 3}; // order to draw vertices 
private short drawOrder2[] = {2, 0, 3, 3, 0, 1}; // order to draw vertices 

static float textureVertices[] = { 
     0.0f, 1.0f, // A. left-bottom 
     1.0f, 1.0f, // B. right-bottom 
     0.0f, 0.0f, // C. left-top 
     1.0f, 0.0f // D. right-top 
}; 

private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex 

private ByteBuffer indexBuffer; // Buffer for index-array 

private int texture; 
private CardboardOverlayView mOverlayView; 
private CardboardView cardboardView; 
private SurfaceTexture surface; 
private float[] mView; 
private float[] mCamera; 

public void startCamera(int texture) { 
    surface = new SurfaceTexture(texture); 
    surface.setOnFrameAvailableListener(this); 

    camera = Camera.open(); 
    Camera.Parameters params = camera.getParameters(); 
    params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); 

    // Check what resolutions are supported by your camera 
    /*List<Camera.Size> sizes = params.getSupportedPictureSizes(); 

    // Iterate through all available resolutions and choose one. 
    // The chosen resolution will be stored in mSize. 
    Camera.Size mSize = null; 
    for (Camera.Size size : sizes) { 
     Log.i(TAG, "Available resolution: " + size.width + "x" + size.height); 
     mSize = size; 
    } 
    params.setPictureSize(5312,2988);*/ 

    camera.setParameters(params); 
    try { 
     camera.setPreviewTexture(surface); 
     camera.startPreview(); 
    } catch (IOException ioe) 
    { 
     Log.i(TAG, "CAM LAUNCH FAILED"); 
    } 

} 

static private int createTexture() { 
    int[] texture = new int[1]; 

    GLES20.glGenTextures(1, texture, 0); 
    GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture[0]); 
    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); 
    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); 

    return texture[0]; 
} 


private int loadGLShader(int type, String code) { 
    int shader = GLES20.glCreateShader(type); 
    GLES20.glShaderSource(shader, code); 
    GLES20.glCompileShader(shader); 

    // Get the compilation status. 
    final int[] compileStatus = new int[1]; 
    GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0); 

    // If the compilation failed, delete the shader. 
    if (compileStatus[0] == 0) { 
     Log.e(TAG, "Error compiling shader: " + GLES20.glGetShaderInfoLog(shader)); 
     GLES20.glDeleteShader(shader); 
     shader = 0; 
    } 

    if (shader == 0) { 
     throw new RuntimeException("Error creating shader."); 
    } 

    return shader; 
} 

/** 
* Checks if we've had an error inside of OpenGL ES, and if so what that error is. 
* 
* @param func 
*/ 
private static void checkGLError(String func) { 
    int error; 
    while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { 
     Log.e(TAG, func + ": glError " + error); 
     throw new RuntimeException(func + ": glError " + error); 
    } 
} 

/** 
* Sets the view to our CardboardView and initializes the transformation matrices we will use 
* to render our scene. 
* 
* @param savedInstanceState 
*/ 
@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 

    setContentView(R.layout.activity_main); 
    cardboardView = (CardboardView) findViewById(R.id.cardboard_view); 
    cardboardView.setRenderer(this); 
    setCardboardView(cardboardView); 

    mCamera = new float[16]; 
    mView = new float[16]; 
    mOverlayView = (CardboardOverlayView) findViewById(R.id.overlay); 
    //mOverlayView.show3DToast("Pull the magnet when you find an object."); 
} 

@Override 
public void onRendererShutdown() { 
    Log.i(TAG, "onRendererShutdown"); 
} 

@Override 
public void onSurfaceChanged(int width, int height) { 
    Log.i(TAG, "onSurfaceChanged"); 
} 

/** 
* Creates the buffers we use to store information about the 3D world. OpenGL doesn't use Java 
* arrays, but rather needs data in a format it can understand. Hence we use ByteBuffers. 
* 
* @param config The EGL configuration used when creating the surface. 
*/ 
@Override 
public void onSurfaceCreated(EGLConfig config) { 
    Log.i(TAG, "onSurfaceCreated"); 
    GLES20.glClearColor(0.1f, 0.1f, 0.1f, 0.5f); // Dark background so text shows up well 

    ByteBuffer bb = ByteBuffer.allocateDirect(squareVertices.length * 4); 
    bb.order(ByteOrder.nativeOrder()); 
    vertexBuffer = bb.asFloatBuffer(); 
    vertexBuffer.put(squareVertices); 
    vertexBuffer.position(0); 

    ByteBuffer dlb = ByteBuffer.allocateDirect(drawOrder.length * 2); 
    dlb.order(ByteOrder.nativeOrder()); 
    drawListBuffer = dlb.asShortBuffer(); 
    drawListBuffer.put(drawOrder); 
    drawListBuffer.position(0); 

    ByteBuffer bb2 = ByteBuffer.allocateDirect(textureVertices.length * 4); 
    bb2.order(ByteOrder.nativeOrder()); 
    textureVerticesBuffer = bb2.asFloatBuffer(); 
    textureVerticesBuffer.put(textureVertices); 
    textureVerticesBuffer.position(0); 

    int vertexShader = loadGLShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); 
    int fragmentShader = loadGLShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); 

    mProgram = GLES20.glCreateProgram();    // create empty OpenGL ES Program 
    GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program 
    GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program 
    GLES20.glLinkProgram(mProgram); 

    texture = createTexture(); 
    startCamera(texture); 
} 

/** 
* Prepares OpenGL ES before we draw a frame. 
* 
* @param headTransform The head transformation in the new frame. 
*/ 
@Override 
public void onNewFrame(HeadTransform headTransform) { 
    float[] mtx = new float[16]; 
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); 
    surface.updateTexImage(); 
    surface.getTransformMatrix(mtx); 
} 

@Override 
public void onFrameAvailable(SurfaceTexture arg0) { 
    this.cardboardView.requestRender(); 
} 

/** 
* Draws a frame for an eye. The transformation for that eye (from the camera) is passed in as 
* a parameter. 
* 
* @param transform The transformations to apply to render this eye. 
*/ 
@Override 
public void onDrawEye(EyeTransform transform) { 
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); 

    GLES20.glUseProgram(mProgram); 

    GLES20.glActiveTexture(GL_TEXTURE_EXTERNAL_OES); 
    GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture); 


    mPositionHandle = GLES20.glGetAttribLocation(mProgram, "position"); 
    GLES20.glEnableVertexAttribArray(mPositionHandle); 
    GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, 
      false, vertexStride, vertexBuffer); 


    mTextureCoordHandle = GLES20.glGetAttribLocation(mProgram, "inputTextureCoordinate"); 
    GLES20.glEnableVertexAttribArray(mTextureCoordHandle); 
    GLES20.glVertexAttribPointer(mTextureCoordHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, 
      false, vertexStride, textureVerticesBuffer); 

    mColorHandle = GLES20.glGetAttribLocation(mProgram, "s_texture"); 


    GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, 
      GLES20.GL_UNSIGNED_SHORT, drawListBuffer); 


    // Disable vertex array 
    GLES20.glDisableVertexAttribArray(mPositionHandle); 
    GLES20.glDisableVertexAttribArray(mTextureCoordHandle); 

    Matrix.multiplyMM(mView, 0, transform.getEyeView(), 0, mCamera, 0); 

} 

@Override 
public void onFinishFrame(Viewport viewport) { 
} 

@Override 
public void onCardboardTrigger() { 

} 


} 

activity_main.xml中

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
android:id="@+id/ui_layout" 
android:orientation="vertical" 
android:layout_width="fill_parent" 
android:layout_height="fill_parent" > 


<com.google.vrtoolkit.cardboard.CardboardView 
    android:id="@+id/cardboard_view" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:layout_alignParentTop="true" 
    android:layout_alignParentLeft="true" /> 

<com.oculab.mehmettaha.vrcamera.CardboardOverlayView 
    android:id="@+id/overlay" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:layout_alignParentLeft="true" 
    android:layout_alignParentTop="true" /> 

</RelativeLayout> 

別忘了如cardboard.jar和libprotobuf的Java -...罐子進口圖書館

最後,你應該得到的結果是這樣的:

Camera on Google Cardboard

乾杯!

+0

我試過了下面的代碼示例。我無法讓它正常工作。我遇到的唯一問題是在'onFrameAvailable(SurfaceTexture arg0){this.FireboardView.requestRender();}'和'onDrawEye(EyeTransform變換)'我確信這些都是簡單的修復,但我被卡住了。 – Buildersrejected