Add thread annotations

v2
Mattia Iavarone 6 years ago
parent fa899437ca
commit 5b303f0522
  1. 8
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/GlCameraPreviewTest.java
  2. 5
      cameraview/src/main/gles/com/otaliastudios/cameraview/AudioMediaEncoder.java
  3. 5
      cameraview/src/main/gles/com/otaliastudios/cameraview/MediaEncoder.java
  4. 9
      cameraview/src/main/gles/com/otaliastudios/cameraview/MediaEncoderEngine.java
  5. 4
      cameraview/src/main/gles/com/otaliastudios/cameraview/OldMediaEncoder.java
  6. 8
      cameraview/src/main/gles/com/otaliastudios/cameraview/TextureMediaEncoder.java
  7. 9
      cameraview/src/main/gles/com/otaliastudios/cameraview/VideoMediaEncoder.java
  8. 12
      cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java
  9. 3
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
  10. 21
      cameraview/src/main/java/com/otaliastudios/cameraview/SnapshotPictureRecorder.java
  11. 8
      cameraview/src/main/java/com/otaliastudios/cameraview/SnapshotVideoRecorder.java
  12. 15
      cameraview/src/main/views/com/otaliastudios/cameraview/GlCameraPreview.java

@ -10,20 +10,20 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class GLCameraPreviewTest extends CameraPreviewTest {
public class GlCameraPreviewTest extends CameraPreviewTest {
@Override
protected CameraPreview createPreview(Context context, ViewGroup parent, CameraPreview.SurfaceCallback callback) {
return new GLCameraPreview(context, parent, callback);
return new GlCameraPreview(context, parent, callback);
}
@Override
protected float getCropScaleY() {
return 1F / ((GLCameraPreview) preview).mScaleY;
return 1F / ((GlCameraPreview) preview).mScaleY;
}
@Override
protected float getCropScaleX() {
return 1F / ((GLCameraPreview) preview).mScaleX;
return 1F / ((GlCameraPreview) preview).mScaleX;
}
}

@ -17,26 +17,31 @@ class AudioMediaEncoder extends MediaEncoder {
}
@EncoderThread
@Override
void prepare(MediaEncoderEngine.Controller controller) {
super.prepare(controller);
}
@EncoderThread
@Override
void start() {
}
@EncoderThread
@Override
void notify(String event, Object data) {
}
@EncoderThread
@Override
void stop() {
}
@EncoderThread
@Override
void release() {

@ -21,17 +21,22 @@ abstract class MediaEncoder {
private MediaEncoderEngine.Controller mController;
private int mTrackIndex;
@EncoderThread
void prepare(MediaEncoderEngine.Controller controller) {
mController = controller;
mBufferInfo = new MediaCodec.BufferInfo();
}
@EncoderThread
abstract void start();
@EncoderThread
abstract void notify(String event, Object data);
@EncoderThread
abstract void stop();
@EncoderThread
abstract void release();
/**

@ -15,6 +15,8 @@ import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
@interface EncoderThread {}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
class MediaEncoderEngine {
@ -60,6 +62,7 @@ class MediaEncoderEngine {
mMediaMuxerStartCount++;
if (mMediaMuxerStartCount == mEncoders.size()) {
mMediaMuxer.start();
mMediaMuxerStarted = true;
}
return track;
}
@ -110,9 +113,11 @@ class MediaEncoderEngine {
encoder.release();
}
if (mMediaMuxer != null) {
// TODO: stop() throws an exception if you haven't fed it any data. Keep track
// of frames submitted, and don't call stop() if we haven't written anything.
// stop() throws an exception if you haven't fed it any data.
// We can just swallow I think.
try {
mMediaMuxer.stop();
} catch (Exception e) {};
mMediaMuxer.release();
mMediaMuxer = null;
}

@ -327,7 +327,7 @@ class OldMediaEncoder implements Runnable {
long timestamp = (((long) inputMessage.arg1) << 32) | (((long) inputMessage.arg2) & 0xffffffffL);
float[] transform = (float[]) obj;
// We must scale this matrix like GLCameraPreview does, because it might have some cropping.
// We must scale this matrix like GlCameraPreview does, because it might have some cropping.
// Scaling takes place with respect to the (0, 0, 0) point, so we must apply a Translation to compensate.
float scaleX = encoder.mTransformationScaleX;
@ -337,7 +337,7 @@ class OldMediaEncoder implements Runnable {
Matrix.translateM(transform, 0, scaleTranslX, scaleTranslY, 0);
Matrix.scaleM(transform, 0, scaleX, scaleY, 1);
// We also must rotate this matrix. In GLCameraPreview it is not needed because it is a live
// We also must rotate this matrix. In GlCameraPreview it is not needed because it is a live
// stream, but the output video, must be correctly rotated based on the device rotation at the moment.
// Rotation also takes place with respect to the origin (the Z axis), so we must
// translate to origin, rotate, then back to where we were.

@ -48,6 +48,7 @@ class TextureMediaEncoder extends VideoMediaEncoder<TextureMediaEncoder.Config>
super(config);
}
@EncoderThread
@Override
void prepare(MediaEncoderEngine.Controller controller) {
super.prepare(controller);
@ -58,6 +59,7 @@ class TextureMediaEncoder extends VideoMediaEncoder<TextureMediaEncoder.Config>
mViewport = new EglViewport();
}
@EncoderThread
@Override
void release() {
super.release();
@ -75,12 +77,14 @@ class TextureMediaEncoder extends VideoMediaEncoder<TextureMediaEncoder.Config>
}
}
@EncoderThread
@Override
void start() {
super.start();
// Nothing to do here. Waiting for the first frame.
}
@EncoderThread
@Override
void notify(String event, Object data) {
if (event.equals(FRAME_EVENT)) {
@ -99,7 +103,7 @@ class TextureMediaEncoder extends VideoMediaEncoder<TextureMediaEncoder.Config>
long timestamp = (((long) arg1) << 32) | (((long) arg2) & 0xffffffffL);
float[] transform = frame.transform;
// We must scale this matrix like GLCameraPreview does, because it might have some cropping.
// We must scale this matrix like GlCameraPreview does, because it might have some cropping.
// Scaling takes place with respect to the (0, 0, 0) point, so we must apply a Translation to compensate.
float scaleX = mConfig.scaleX;
@ -109,7 +113,7 @@ class TextureMediaEncoder extends VideoMediaEncoder<TextureMediaEncoder.Config>
Matrix.translateM(transform, 0, scaleTranslX, scaleTranslY, 0);
Matrix.scaleM(transform, 0, scaleX, scaleY, 1);
// We also must rotate this matrix. In GLCameraPreview it is not needed because it is a live
// We also must rotate this matrix. In GlCameraPreview it is not needed because it is a live
// stream, but the output video, must be correctly rotated based on the device rotation at the moment.
// Rotation also takes place with respect to the origin (the Z axis), so we must
// translate to origin, rotate, then back to where we were.

@ -13,6 +13,11 @@ import android.view.Surface;
import java.io.IOException;
import java.nio.ByteBuffer;
/**
* This alone does nothing.
* Subclasses must make sure they write each frame onto the given Surface {@link #mSurface}.
* @param <C> the config object.
*/
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
abstract class VideoMediaEncoder<C extends VideoMediaEncoder.Config> extends MediaEncoder {
@ -42,6 +47,7 @@ abstract class VideoMediaEncoder<C extends VideoMediaEncoder.Config> extends Med
mConfig = config;
}
@EncoderThread
@Override
void prepare(MediaEncoderEngine.Controller controller) {
super.prepare(controller);
@ -67,18 +73,21 @@ abstract class VideoMediaEncoder<C extends VideoMediaEncoder.Config> extends Med
mMediaCodec.start();
}
@EncoderThread
@Override
void start() {
// Nothing to do here. Waiting for the first frame.
mFrameNum = 0;
}
@EncoderThread
@Override
void stop() {
mFrameNum = -1;
drain(true);
}
@EncoderThread
@Override
void release() {
if (mMediaCodec != null) {

@ -6,18 +6,14 @@ import android.graphics.ImageFormat;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.graphics.YuvImage;
import android.hardware.Camera;
import android.location.Location;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import android.support.media.ExifInterface;
import android.view.SurfaceHolder;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@ -645,8 +641,8 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
@SuppressLint("NewApi")
@Override
void takeVideoSnapshot(@NonNull final File file) {
if (!(mPreview instanceof GLCameraPreview)) {
throw new IllegalStateException("Video snapshots are only supported with GLCameraPreview.");
if (!(mPreview instanceof GlCameraPreview)) {
throw new IllegalStateException("Video snapshots are only supported with GlCameraPreview.");
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
throw new IllegalStateException("Video snapshots are only supported starting from API 18.");
@ -663,7 +659,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
videoResult.codec = mVideoCodec;
videoResult.location = mLocation;
// What matters as size here is the preview size, which is passed to the GLCameraPreview
// What matters as size here is the preview size, which is passed to the GlCameraPreview
// surface texture, which is then passed to the encoder. The view size has no influence.
// CROPPING: We must make sure that the encoder applies our special transformation
@ -700,7 +696,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
videoResult.maxSize = mVideoMaxSize;
videoResult.maxDuration = mVideoMaxDuration;
GLCameraPreview cameraPreview = (GLCameraPreview) mPreview;
GlCameraPreview cameraPreview = (GlCameraPreview) mPreview;
mVideoRecorder = new SnapshotVideoRecorder(videoResult, Camera1.this, cameraPreview);
mVideoRecorder.start();
}

@ -7,7 +7,6 @@ import android.app.Activity;
import android.arch.lifecycle.Lifecycle;
import android.arch.lifecycle.LifecycleObserver;
import android.arch.lifecycle.LifecycleOwner;
import android.arch.lifecycle.Lifecycling;
import android.arch.lifecycle.OnLifecycleEvent;
import android.content.Context;
import android.content.ContextWrapper;
@ -240,7 +239,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
}
}
case GL_SURFACE: default: {
return new GLCameraPreview(context, container, null);
return new GlCameraPreview(context, container, null);
}
}
}

@ -1,7 +1,6 @@
package com.otaliastudios.cameraview;
import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
@ -9,19 +8,9 @@ import android.graphics.YuvImage;
import android.hardware.Camera;
import android.opengl.EGL14;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* A {@link PictureResult} that uses standard APIs.
@ -50,21 +39,22 @@ class SnapshotPictureRecorder extends PictureRecorder {
@Override
void take() {
if (mPreview instanceof GLCameraPreview) {
takeGl((GLCameraPreview) mPreview);
if (mPreview instanceof GlCameraPreview) {
takeGl((GlCameraPreview) mPreview);
} else {
takeLegacy();
}
}
@SuppressLint("NewApi")
private void takeGl(final GLCameraPreview preview) {
preview.addRendererFrameCallback(new GLCameraPreview.RendererFrameCallback() {
private void takeGl(final GlCameraPreview preview) {
preview.addRendererFrameCallback(new GlCameraPreview.RendererFrameCallback() {
int mTextureId;
SurfaceTexture mSurfaceTexture;
float[] mTransform;
@RendererThread
public void onRendererTextureCreated(int textureId) {
mTextureId = textureId;
mSurfaceTexture = new SurfaceTexture(mTextureId, true);
@ -75,6 +65,7 @@ class SnapshotPictureRecorder extends PictureRecorder {
mTransform = new float[16];
}
@RendererThread
@Override
public void onRendererFrame(SurfaceTexture surfaceTexture, final float scaleX, final float scaleY) {
preview.removeRendererFrameCallback(this);

@ -14,7 +14,7 @@ import android.support.annotation.RequiresApi;
* TODO when cropping is huge, the first frame of the video result, noticeably, has no transformation applied. Don't know why.
*/
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
class SnapshotVideoRecorder extends VideoRecorder implements GLCameraPreview.RendererFrameCallback {
class SnapshotVideoRecorder extends VideoRecorder implements GlCameraPreview.RendererFrameCallback {
private static final String TAG = SnapshotVideoRecorder.class.getSimpleName();
private static final CameraLogger LOG = CameraLogger.create(TAG);
@ -25,13 +25,13 @@ class SnapshotVideoRecorder extends VideoRecorder implements GLCameraPreview.Ren
private OldMediaEncoder mEncoder;
private MediaEncoderEngine mEncoderEngine;
private GLCameraPreview mPreview;
private GlCameraPreview mPreview;
private int mCurrentState = STATE_NOT_RECORDING;
private int mDesiredState = STATE_NOT_RECORDING;
private int mTextureId = 0;
SnapshotVideoRecorder(VideoResult stub, VideoResultListener listener, GLCameraPreview preview) {
SnapshotVideoRecorder(VideoResult stub, VideoResultListener listener, GlCameraPreview preview) {
super(stub, listener);
if (USE_OLD_ENCODER) {
mEncoder = new OldMediaEncoder();
@ -60,11 +60,13 @@ class SnapshotVideoRecorder extends VideoRecorder implements GLCameraPreview.Ren
mDesiredState = STATE_NOT_RECORDING;
}
@RendererThread
@Override
public void onRendererTextureCreated(int textureId) {
mTextureId = textureId;
}
@RendererThread
@Override
public void onRendererFrame(SurfaceTexture surfaceTexture, float scaleX, float scaleY) {
if (mCurrentState == STATE_NOT_RECORDING && mDesiredState == STATE_RECORDING) {

@ -21,6 +21,9 @@ import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
import javax.microedition.khronos.opengles.GL10;
@interface RendererThread {}
/**
* - The android camera will stream image to the given {@link SurfaceTexture}.
*
@ -53,7 +56,7 @@ import javax.microedition.khronos.opengles.GL10;
* Callbacks are guaranteed to be called on the renderer thread, which means that we can fetch
* the GL context that was created and is managed by the {@link GLSurfaceView}.
*/
class GLCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture> implements GLSurfaceView.Renderer {
class GlCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture> implements GLSurfaceView.Renderer {
private boolean mDispatched;
private final float[] mTransformMatrix = new float[16];
@ -64,7 +67,7 @@ class GLCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture> imple
/* for tests */ float mScaleX = 1F;
/* for tests */ float mScaleY = 1F;
GLCameraPreview(Context context, ViewGroup parent, SurfaceCallback callback) {
GlCameraPreview(Context context, ViewGroup parent, SurfaceCallback callback) {
super(context, parent, callback);
}
@ -119,7 +122,7 @@ class GLCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture> imple
}
}
// Renderer thread
@RendererThread
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
mOutputViewport = new EglViewport();
@ -144,7 +147,7 @@ class GLCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture> imple
});
}
// Renderer thread
@RendererThread
@SuppressWarnings("StatementWithEmptyBody")
@Override
public void onSurfaceChanged(GL10 gl, final int width, final int height) {
@ -170,7 +173,7 @@ class GLCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture> imple
}
}
// Renderer thread
@RendererThread
@Override
public void onDrawFrame(GL10 gl) {
// Latch the latest frame. If there isn't anything new,
@ -255,6 +258,7 @@ class GLCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture> imple
*
* @param textureId the GL texture linked to the image stream
*/
@RendererThread
void onRendererTextureCreated(int textureId);
/**
@ -266,6 +270,7 @@ class GLCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture> imple
* @param scaleX the scaleX (in REF_VIEW) value
* @param scaleY the scaleY (in REF_VIEW) value
*/
@RendererThread
void onRendererFrame(SurfaceTexture surfaceTexture, float scaleX, float scaleY);
}
Loading…
Cancel
Save