CarEvsGLSurfaceView supports multiple streams
Because only a single GL context is available globally, it is diffcult
that applications manage GLSurface per each camera. To handle a scenario
with multiple previews on the display, CarEvsGLSurfaceView takes an
array of CarEvsGLSurfaceView.BufferCallback object, which represents a
stream of HardwareBuffer objects, and their desired locations on the
display, and renders contents of HardwareBuffer object at desired
positions.
Bug: 191940626
Test: Launch CarEvsCameraPreviewApp CarEvsMultiCameraPreviewApp and
confirm both apps are running properly.
Change-Id: I66ebe50d9c455020e5c062fe964968455f63f583
diff --git a/car-evs-helper-lib/src/com/android/car/internal/evs/CarEvsGLSurfaceView.java b/car-evs-helper-lib/src/com/android/car/internal/evs/CarEvsGLSurfaceView.java
index c798ee3..2d4ed17 100644
--- a/car-evs-helper-lib/src/com/android/car/internal/evs/CarEvsGLSurfaceView.java
+++ b/car-evs-helper-lib/src/com/android/car/internal/evs/CarEvsGLSurfaceView.java
@@ -26,13 +26,22 @@
import com.android.internal.util.Preconditions;
import com.android.car.internal.evs.GLES20CarEvsBufferRenderer;
+import java.util.ArrayList;
+
/**
* GPU-backed SurfaceView to render a hardware buffer described by {@link CarEvsBufferDescriptor}.
*/
public final class CarEvsGLSurfaceView extends GLSurfaceView {
private static final String TAG = CarEvsGLSurfaceView.class.getSimpleName();
private static final int DEFAULT_IN_PLANE_ROTATION_ANGLE = 0;
-
+ private static final float DEFAULT_1X1_POSITION[][] = {
+ {
+ -1.0f, 1.0f, 0.0f,
+ 1.0f, 1.0f, 0.0f,
+ -1.0f, -1.0f, 0.0f,
+ 1.0f, -1.0f, 0.0f,
+ },
+ };
private final GLES20CarEvsBufferRenderer mRenderer;
/** An interface to pull and return {@code CarEvsBufferDescriptor} object to render. */
@@ -55,11 +64,12 @@
void onBufferProcessed(@NonNull CarEvsBufferDescriptor buffer);
}
- private CarEvsGLSurfaceView(Context context, BufferCallback callback, int angleInDegree) {
+ private CarEvsGLSurfaceView(Context context, ArrayList<BufferCallback> callbacks,
+ int angleInDegree, float[][] positions) {
super(context);
setEGLContextClientVersion(2);
- mRenderer = new GLES20CarEvsBufferRenderer(context, callback, angleInDegree);
+ mRenderer = new GLES20CarEvsBufferRenderer(callbacks, angleInDegree, positions);
setRenderer(mRenderer);
setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
@@ -75,27 +85,66 @@
/**
* Creates a {@link CarEvsGLSurfaceView} object with the default in-plane rotation angle.
*
- * @param context Current appliation context.
- * @param callback {@link CarEvsGLSurfaceView.BufferCallback} object.
+ * @param context The Context this view is running in, through which it can access resources,
+ * etc.
+ * @param callbacks An array of {@link CarEvsGLSurfaceView.BufferCallback} objects.
*
*/
- public static CarEvsGLSurfaceView create(Context context, BufferCallback callback) {
- return create(context, callback, DEFAULT_IN_PLANE_ROTATION_ANGLE);
+ public static CarEvsGLSurfaceView create(Context context,
+ ArrayList<BufferCallback> callbacks) {
+ return create(context, callbacks, DEFAULT_IN_PLANE_ROTATION_ANGLE, DEFAULT_1X1_POSITION);
}
/**
* Creates a {@link CarEvsGLSurfaceView} object with a given in-plane rotation angle in degree.
*
- * @param context Current appliation context.
- * @param callback {@link CarEvsGLSurfaceView.BufferCallback} object.
+ * @param context The Context this view is running in, through which it can access resources,
+ * etc.
+ * @param callbacks An array of {@link CarEvsGLSurfaceView.BufferCallback} objects.
* @param angleInDegree In-plane counter-clockwise rotation angle in degree.
+ *
*/
- public static CarEvsGLSurfaceView create(Context context, BufferCallback callback,
+ public static CarEvsGLSurfaceView create(Context context, ArrayList<BufferCallback> callbacks,
int angleInDegree) {
+ return create(context, callbacks, angleInDegree, DEFAULT_1X1_POSITION);
+ }
+
+ /**
+ * Creates a {@link CarEvsGLSurfaceView} object with the default in-plane rotation angle and a
+ * gridview with given rows and columns.
+ *
+ * @param context The Context this view is running in, through which it can access resources,
+ * etc.
+ * @param callbacks An array of {@link CarEvsGLSurfaceView.BufferCallback} objects.
+ * @param positions Matrices that define an area where each buffer will be rendered.
+ *
+ */
+ public static CarEvsGLSurfaceView create(Context context, ArrayList<BufferCallback> callbacks,
+ float positions[][]) {
+ return create(context, callbacks, DEFAULT_IN_PLANE_ROTATION_ANGLE, positions);
+ }
+
+ /**
+ * Creates a {@link CarEvsGLSurfaceView} object with a given in-plane rotation angle in degree
+ * and a gridview with given rows and columns.
+ *
+ * @param context The Context this view is running in, through which it can access resources,
+ * etc.
+ * @param callbacks An array of {@link CarEvsGLSurfaceView.BufferCallback} objects.
+ * @param angleInDegree In-plane counter-clockwise rotation angle in degree.
+ * @param positions Matrices that define an area where each buffer will be rendered.
+ *
+ */
+ public static CarEvsGLSurfaceView create(Context context, ArrayList<BufferCallback> callbacks,
+ int angleInDegree, float positions[][]) {
Preconditions.checkArgument(context != null, "Context cannot be null.");
- Preconditions.checkArgument(callback != null, "BufferCallback cannot be null.");
+ Preconditions.checkArgument(callbacks != null, "BufferCallback cannot be null.");
+ Preconditions.checkArgument(callbacks.size() > 0,
+ "At least one BufferCallback object must exist.");
+ Preconditions.checkArgument(callbacks.size() <= positions.length,
+ "At least " + callbacks.size() + " positions are needed.");
- return new CarEvsGLSurfaceView(context, callback, angleInDegree);
+ return new CarEvsGLSurfaceView(context, callbacks, angleInDegree, positions);
}
}
diff --git a/car-evs-helper-lib/src/com/android/car/internal/evs/GLES20CarEvsBufferRenderer.java b/car-evs-helper-lib/src/com/android/car/internal/evs/GLES20CarEvsBufferRenderer.java
index 8de69f1..be79d71 100644
--- a/car-evs-helper-lib/src/com/android/car/internal/evs/GLES20CarEvsBufferRenderer.java
+++ b/car-evs-helper-lib/src/com/android/car/internal/evs/GLES20CarEvsBufferRenderer.java
@@ -20,7 +20,6 @@
import android.annotation.NonNull;
import android.car.evs.CarEvsBufferDescriptor;
-import android.content.Context;
import android.hardware.HardwareBuffer;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
@@ -32,6 +31,7 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
+import java.util.ArrayList;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
@@ -46,13 +46,13 @@
private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
private static final int FLOAT_SIZE_BYTES = 4;
- private static final float[] sVertCarPosData = {
+ private static final float[] sVertPosData = {
-1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f };
- private static final float[] sVertCarTexData = {
+ private static final float[] sVertTexData = {
-0.5f, -0.5f,
0.5f, -0.5f,
-0.5f, 0.5f,
@@ -64,7 +64,7 @@
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f };
- private final String mVertexShader =
+ private static final String mVertexShader =
"attribute vec4 pos; \n" +
"attribute vec2 tex; \n" +
"uniform mat4 cameraMat; \n" +
@@ -75,7 +75,7 @@
" uv = tex; \n" +
"} \n";
- private final String mFragmentShader =
+ private static final String mFragmentShader =
"precision mediump float; \n" +
"uniform sampler2D tex; \n" +
"varying vec2 uv; \n" +
@@ -84,154 +84,169 @@
" gl_FragColor = texture2D(tex, uv); \n" +
"} \n";
+ private static final int INDEX_TO_X_STRIDE = 0;
+ private static final int INDEX_TO_Y_STRIDE = 1;
+
private final Object mLock = new Object();
- private final CarEvsGLSurfaceView.BufferCallback mCallback;
- private final Context mContext;
- private final FloatBuffer mVertCarPos;
- private final FloatBuffer mVertCarTex;
+ private final ArrayList<CarEvsGLSurfaceView.BufferCallback> mCallbacks;
+ private final FloatBuffer mVertPos;
+ private final FloatBuffer mVertTex;
+ private final int mTextureId[];
+ private final float mPositions[][];
private int mProgram;
- private int mTextureId;
private int mWidth;
private int mHeight;
- // Native method to update the texture with a received frame buffer
+ // Hold buffers currently in use.
@GuardedBy("mLock")
- private CarEvsBufferDescriptor mBufferInUse;
+ private final CarEvsBufferDescriptor mBufferInUse[];
/** Load jni on initialization. */
static {
System.loadLibrary("carevsglrenderer_jni");
}
- public GLES20CarEvsBufferRenderer(@NonNull Context context,
- @NonNull CarEvsGLSurfaceView.BufferCallback callback, int angleInDegree) {
+ public GLES20CarEvsBufferRenderer(ArrayList<CarEvsGLSurfaceView.BufferCallback> callbacks,
+ int angleInDegree, float[][] positions) {
- Preconditions.checkArgument(context != null, "Context cannot be null.");
- Preconditions.checkArgument(callback != null, "Callback cannot be null.");
+ Preconditions.checkArgument(callbacks != null, "Callback cannot be null.");
+ Preconditions.checkArgument(callbacks.size() <= positions.length,
+ "At least " + callbacks.size() + " positions are needed.");
- mContext = context;
- mCallback = callback;
+ mCallbacks = callbacks;
- mVertCarPos = ByteBuffer.allocateDirect(sVertCarPosData.length * FLOAT_SIZE_BYTES)
- .order(ByteOrder.nativeOrder()).asFloatBuffer();
- mVertCarPos.put(sVertCarPosData).position(0);
+ mTextureId = new int[callbacks.size()];
+ mPositions = positions;
+ mBufferInUse = new CarEvsBufferDescriptor[callbacks.size()];
+
+ mVertPos = ByteBuffer.allocateDirect(sVertPosData.length * FLOAT_SIZE_BYTES)
+ .order(ByteOrder.nativeOrder()).asFloatBuffer();
+ mVertPos.put(sVertPosData).position(0);
double angleInRadian = Math.toRadians(angleInDegree);
float[] rotated = {0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f};
float sin = (float)Math.sin(angleInRadian);
float cos = (float)Math.cos(angleInRadian);
- rotated[0] += cos * sVertCarTexData[0] - sin * sVertCarTexData[1];
- rotated[1] += sin * sVertCarTexData[0] + cos * sVertCarTexData[1];
- rotated[2] += cos * sVertCarTexData[2] - sin * sVertCarTexData[3];
- rotated[3] += sin * sVertCarTexData[2] + cos * sVertCarTexData[3];
- rotated[4] += cos * sVertCarTexData[4] - sin * sVertCarTexData[5];
- rotated[5] += sin * sVertCarTexData[4] + cos * sVertCarTexData[5];
- rotated[6] += cos * sVertCarTexData[6] - sin * sVertCarTexData[7];
- rotated[7] += sin * sVertCarTexData[6] + cos * sVertCarTexData[7];
+ rotated[0] += cos * sVertTexData[0] - sin * sVertTexData[1];
+ rotated[1] += sin * sVertTexData[0] + cos * sVertTexData[1];
+ rotated[2] += cos * sVertTexData[2] - sin * sVertTexData[3];
+ rotated[3] += sin * sVertTexData[2] + cos * sVertTexData[3];
+ rotated[4] += cos * sVertTexData[4] - sin * sVertTexData[5];
+ rotated[5] += sin * sVertTexData[4] + cos * sVertTexData[5];
+ rotated[6] += cos * sVertTexData[6] - sin * sVertTexData[7];
+ rotated[7] += sin * sVertTexData[6] + cos * sVertTexData[7];
- mVertCarTex = ByteBuffer.allocateDirect(sVertCarTexData.length * FLOAT_SIZE_BYTES)
+ mVertTex = ByteBuffer.allocateDirect(sVertTexData.length * FLOAT_SIZE_BYTES)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
- mVertCarTex.put(rotated).position(0);
+ mVertTex.put(rotated).position(0);
}
public void clearBuffer() {
- CarEvsBufferDescriptor bufferToReturn = null;
- synchronized (mLock) {
- if (mBufferInUse == null) {
- return;
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ CarEvsBufferDescriptor bufferToReturn = null;
+ synchronized (mLock) {
+ if (mBufferInUse[i] == null) {
+ continue;
+ }
+
+ bufferToReturn = mBufferInUse[i];
+ mBufferInUse[i] = null;
}
- bufferToReturn = mBufferInUse;
- mBufferInUse = null;
+ // bufferToReturn is not null here.
+ mCallbacks.get(i).onBufferProcessed(bufferToReturn);
}
-
- // bufferToReturn is not null here.
- mCallback.onBufferProcessed(bufferToReturn);
}
@Override
public void onDrawFrame(GL10 glUnused) {
// Use the GLES20 class's static methods instead of a passed GL10 interface.
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ CarEvsBufferDescriptor bufferToRender = null;
+ CarEvsBufferDescriptor bufferToReturn = null;
+ CarEvsBufferDescriptor newFrame = mCallbacks.get(i).onBufferRequested();
- CarEvsBufferDescriptor bufferToRender = null;
- CarEvsBufferDescriptor bufferToReturn = null;
- CarEvsBufferDescriptor newFrame = mCallback.onBufferRequested();
-
- synchronized (mLock) {
- if (newFrame != null) {
- // If a new frame has not been delivered yet, we're using a previous frame.
- if (mBufferInUse != null) {
- bufferToReturn = mBufferInUse;
+ synchronized (mLock) {
+ if (newFrame != null) {
+ if (mBufferInUse[i] != null) {
+ bufferToReturn = mBufferInUse[i];
+ }
+ mBufferInUse[i] = newFrame;
}
- mBufferInUse = newFrame;
+ bufferToRender = mBufferInUse[i];
}
- bufferToRender = mBufferInUse;
- }
- if (bufferToRender == null) {
- if (DBG) {
- Log.d(TAG, "No buffer to draw.");
+ if (bufferToRender == null) {
+ if (DBG) {
+ Log.d(TAG, "No buffer to draw from a callback " + i);
+ }
+ continue;
}
- return;
+
+ if (bufferToReturn != null) {
+ mCallbacks.get(i).onBufferProcessed(bufferToReturn);
+ }
+
+ // Specify a shader program to use
+ GLES20.glUseProgram(mProgram);
+
+ // Set a cameraMat as 4x4 identity matrix
+ int matrix = GLES20.glGetUniformLocation(mProgram, "cameraMat");
+ if (matrix < 0) {
+ throw new RuntimeException("Could not get a attribute location for cameraMat");
+ }
+ GLES20.glUniformMatrix4fv(/* location= */ matrix, /* count= */ 1,
+ /* transpose= */ false, /* value= */ sIdentityMatrix, 0);
+
+ // Retrieve a hardware buffer from a descriptor and update the texture
+ HardwareBuffer buffer = bufferToRender.getHardwareBuffer();
+
+ // Update the texture with a given hardware buffer
+ if (!nUpdateTexture(buffer, mTextureId[i])) {
+ throw new RuntimeException(
+ "Failed to update the texture with the preview frame");
+ }
}
- if (bufferToReturn != null) {
- mCallback.onBufferProcessed(bufferToReturn);
- }
-
- // Specify a shader program to use
- GLES20.glUseProgram(mProgram);
-
- // Set a cameraMat as 4x4 identity matrix
- int matrix = GLES20.glGetUniformLocation(mProgram, "cameraMat");
- if (matrix < 0) {
- throw new RuntimeException("Could not get a attribute location for cameraMat");
- }
- GLES20.glUniformMatrix4fv(matrix, 1, false, sIdentityMatrix, 0);
-
- // Retrieve a hardware buffer from a descriptor and update the texture
- HardwareBuffer buffer = bufferToRender.getHardwareBuffer();
-
- // Update the texture with a given hardware buffer
- if (!nUpdateTexture(buffer, mTextureId)) {
- throw new RuntimeException(
- "Failed to update the texture with the preview frame");
- }
-
- GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
- GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
-
// Select active texture unit
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
- // Bind a named texture to the target
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId);
+ // Render Hardware buffers
+ for (int i = 0; i < mTextureId.length; i++) {
+ mVertPos.put(mPositions[i]).position(0);
- // Use a texture slot 0 as the source
- int sampler = GLES20.glGetUniformLocation(mProgram, "tex");
- if (sampler < 0) {
- throw new RuntimeException("Could not get a attribute location for tex");
+ // Bind a named texture to the target
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId[i]);
+
+ // Use a texture slot 0 as the source
+ int sampler = GLES20.glGetUniformLocation(mProgram, "tex");
+ if (sampler < 0) {
+ throw new RuntimeException("Could not get a attribute location for tex");
+ }
+ GLES20.glUniform1i(sampler, 0);
+
+ // We'll ignore the alpha value
+ GLES20.glDisable(GLES20.GL_BLEND);
+
+ // Bind a named texture to the target
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId[i]);
+
+ // Define an array of generic vertex attribute data
+ GLES20.glVertexAttribPointer(0, 3, GLES20.GL_FLOAT, false, 0, mVertPos);
+ GLES20.glVertexAttribPointer(1, 2, GLES20.GL_FLOAT, false, 0, mVertTex);
+
+ // Enable a generic vertex attribute array
+ GLES20.glEnableVertexAttribArray(0);
+ GLES20.glEnableVertexAttribArray(1);
+
+ // Render primitives from array data
+ GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
+
+ GLES20.glDisableVertexAttribArray(0);
+ GLES20.glDisableVertexAttribArray(1);
}
- GLES20.glUniform1i(sampler, 0);
-
- // We'll ignore the alpha value
- GLES20.glDisable(GLES20.GL_BLEND);
-
- // Define an array of generic vertex attribute data
- GLES20.glVertexAttribPointer(0, 3, GLES20.GL_FLOAT, false, 0, mVertCarPos);
- GLES20.glVertexAttribPointer(1, 2, GLES20.GL_FLOAT, false, 0, mVertCarTex);
-
- // Enable a generic vertex attribute array
- GLES20.glEnableVertexAttribArray(0);
- GLES20.glEnableVertexAttribArray(1);
-
- // Render primitives from array data
- GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
-
- GLES20.glDisableVertexAttribArray(0);
- GLES20.glDisableVertexAttribArray(1);
// Wait until all GL execution is complete
GLES20.glFinish();
@@ -256,26 +271,26 @@
}
// Generate texture name
- int[] textures = new int[1];
- GLES20.glGenTextures(1, textures, 0);
- mTextureId = textures[0];
- if (mTextureId <= 0) {
- Log.e(TAG, "Did not get a texture handle");
- return;
- }
+ GLES20.glGenTextures(/* n= */ mTextureId.length, mTextureId, /* offset= */ 0);
+ for (var id : mTextureId) {
+ if (id <= 0) {
+ Log.e(TAG, "Did not get a texture handle, id=" + id);
+ continue;
+ }
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId);
- // Use a linear interpolation to upscale the texture
- GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
- GLES20.GL_LINEAR);
- // Use a nearest-neighbor to downscale the texture
- GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
- GLES20.GL_NEAREST);
- // Clamp s, t coordinates at the edges
- GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
- GLES20.GL_CLAMP_TO_EDGE);
- GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
- GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, id);
+ // Use a linear interpolation to upscale the texture
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
+ GLES20.GL_LINEAR);
+ // Use a nearest-neighbor to downscale the texture
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_NEAREST);
+ // Clamp s, t coordinates at the edges
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
+ GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
+ GLES20.GL_CLAMP_TO_EDGE);
+ }
}
private int loadShader(int shaderType, String source) {
diff --git a/tests/CarEvsCameraPreviewApp/src/com/google/android/car/evs/CarEvsCameraPreviewActivity.java b/tests/CarEvsCameraPreviewApp/src/com/google/android/car/evs/CarEvsCameraPreviewActivity.java
index 54aba39..f9b93f4 100644
--- a/tests/CarEvsCameraPreviewApp/src/com/google/android/car/evs/CarEvsCameraPreviewActivity.java
+++ b/tests/CarEvsCameraPreviewApp/src/com/google/android/car/evs/CarEvsCameraPreviewActivity.java
@@ -73,6 +73,15 @@
private final static int STREAM_STATE_INVISIBLE = 2;
private final static int STREAM_STATE_LOST = 3;
+ private final static float DEFAULT_1X1_POSITION[][] = {
+ {
+ -1.0f, 1.0f, 0.0f,
+ 1.0f, 1.0f, 0.0f,
+ -1.0f, -1.0f, 0.0f,
+ 1.0f, -1.0f, 0.0f,
+ },
+ };
+
private static String streamStateToString(int state) {
switch (state) {
case STREAM_STATE_STOPPED:
@@ -261,8 +270,12 @@
Car.createCar(getApplicationContext(), /* handler = */ null,
Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, mCarServiceLifecycleListener);
- mEvsView = CarEvsGLSurfaceView.create(getApplication(), this, getApplicationContext()
- .getResources().getInteger(R.integer.config_evsRearviewCameraInPlaneRotationAngle));
+ // Packaging parameters to create CarEvsGLSurfaceView.
+ ArrayList callbacks = new ArrayList<>(1);
+ callbacks.add(CarEvsManager.SERVICE_TYPE_REARVIEW, this);
+ mEvsView = CarEvsGLSurfaceView.create(getApplication(), callbacks, getApplicationContext()
+ .getResources().getInteger(R.integer.config_evsRearviewCameraInPlaneRotationAngle),
+ DEFAULT_1X1_POSITION);
mRootView = (ViewGroup) LayoutInflater.from(this).inflate(
R.layout.evs_preview_activity, /* root= */ null);
mPreviewContainer = mRootView.findViewById(R.id.evs_preview_container);