Refactor video package

pull/482/head
Mattia Iavarone 6 years ago
parent f52fae8652
commit 3b573eb007
  1. 3
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java
  2. 4
      cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/CamcorderProfiles.java
  3. 37
      cameraview/src/main/java/com/otaliastudios/cameraview/video/FullVideoRecorder.java
  4. 29
      cameraview/src/main/java/com/otaliastudios/cameraview/video/SnapshotVideoRecorder.java
  5. 37
      cameraview/src/main/java/com/otaliastudios/cameraview/video/VideoRecorder.java
  6. 8
      cameraview/src/main/java/com/otaliastudios/cameraview/video/encoding/AudioMediaEncoder.java
  7. 22
      cameraview/src/main/java/com/otaliastudios/cameraview/video/encoding/MediaEncoderEngine.java
  8. 18
      cameraview/src/main/java/com/otaliastudios/cameraview/video/encoding/TextureMediaEncoder.java

@ -809,7 +809,8 @@ public class Camera1Engine extends CameraEngine implements Camera.PreviewCallbac
// Reset facing and start. // Reset facing and start.
mFacing = realFacing; mFacing = realFacing;
GlCameraPreview cameraPreview = (GlCameraPreview) mPreview; GlCameraPreview cameraPreview = (GlCameraPreview) mPreview;
mVideoRecorder = new SnapshotVideoRecorder(stub, Camera1Engine.this, cameraPreview); mVideoRecorder = new SnapshotVideoRecorder(stub,
Camera1Engine.this, Camera1Engine.this, cameraPreview);
mVideoRecorder.start(); mVideoRecorder.start();
} }
}); });

@ -18,7 +18,7 @@ import androidx.annotation.NonNull;
/** /**
* Wraps the {@link android.media.CamcorderProfile} static utilities. * Wraps the {@link android.media.CamcorderProfile} static utilities.
*/ */
class CamcorderProfiles { public class CamcorderProfiles {
@SuppressLint("UseSparseArrays") @SuppressLint("UseSparseArrays")
private static Map<Size, Integer> sizeToProfileMap = new HashMap<>(); private static Map<Size, Integer> sizeToProfileMap = new HashMap<>();
@ -44,7 +44,7 @@ class CamcorderProfiles {
* @return a profile * @return a profile
*/ */
@NonNull @NonNull
static CamcorderProfile get(int cameraId, @NonNull Size targetSize) { public static CamcorderProfile get(int cameraId, @NonNull Size targetSize) {
final int targetArea = targetSize.getWidth() * targetSize.getHeight(); final int targetArea = targetSize.getWidth() * targetSize.getHeight();
List<Size> sizes = new ArrayList<>(sizeToProfileMap.keySet()); List<Size> sizes = new ArrayList<>(sizeToProfileMap.keySet());
Collections.sort(sizes, new Comparator<Size>() { Collections.sort(sizes, new Comparator<Size>() {

@ -7,10 +7,9 @@ import android.media.MediaRecorder;
import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.CameraLogger;
import com.otaliastudios.cameraview.VideoResult; import com.otaliastudios.cameraview.VideoResult;
import com.otaliastudios.cameraview.controls.Audio; import com.otaliastudios.cameraview.controls.Audio;
import com.otaliastudios.cameraview.controls.VideoCodec;
import com.otaliastudios.cameraview.engine.Camera1Engine; import com.otaliastudios.cameraview.engine.Camera1Engine;
import com.otaliastudios.cameraview.internal.utils.CamcorderProfiles;
import com.otaliastudios.cameraview.size.Size; import com.otaliastudios.cameraview.size.Size;
import com.otaliastudios.cameraview.utils.CamcorderProfiles;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -39,15 +38,13 @@ public class FullVideoRecorder extends VideoRecorder {
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
// Get a profile of quality compatible with the chosen size. // Get a profile of quality compatible with the chosen size.
mSize = mResult.getRotation() % 180 != 0 ? mResult.getSize().flip() : mResult.getSize(); mSize = mResult.rotation % 180 != 0 ? mResult.size.flip() : mResult.size;
mProfile = CamcorderProfiles.get(cameraId, mSize); mProfile = CamcorderProfiles.get(cameraId, mSize);
} }
// Camera2 constructor here...
@Override @Override
void start() { public void start() {
if (mResult.getAudio() == Audio.ON) { if (mResult.audio == Audio.ON) {
// Must be called before setOutputFormat. // Must be called before setOutputFormat.
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT); mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
} }
@ -60,10 +57,10 @@ public class FullVideoRecorder extends VideoRecorder {
mMediaRecorder.setVideoFrameRate(mResult.videoFrameRate); mMediaRecorder.setVideoFrameRate(mResult.videoFrameRate);
} }
mMediaRecorder.setVideoSize(mSize.getWidth(), mSize.getHeight()); mMediaRecorder.setVideoSize(mSize.getWidth(), mSize.getHeight());
switch (mResult.getVideoCodec()) { switch (mResult.videoCodec) {
case VideoCodec.H_263: mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263); break; case H_263: mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263); break;
case VideoCodec.H_264: mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); break; case H_264: mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); break;
case VideoCodec.DEVICE_DEFAULT: mMediaRecorder.setVideoEncoder(mProfile.videoCodec); break; case DEVICE_DEFAULT: mMediaRecorder.setVideoEncoder(mProfile.videoCodec); break;
} }
if (mResult.videoBitRate <= 0) { if (mResult.videoBitRate <= 0) {
mMediaRecorder.setVideoEncodingBitRate(mProfile.videoBitRate); mMediaRecorder.setVideoEncodingBitRate(mProfile.videoBitRate);
@ -71,7 +68,7 @@ public class FullVideoRecorder extends VideoRecorder {
} else { } else {
mMediaRecorder.setVideoEncodingBitRate(mResult.videoBitRate); mMediaRecorder.setVideoEncodingBitRate(mResult.videoBitRate);
} }
if (mResult.getAudio() == Audio.ON) { if (mResult.audio == Audio.ON) {
mMediaRecorder.setAudioChannels(mProfile.audioChannels); mMediaRecorder.setAudioChannels(mProfile.audioChannels);
mMediaRecorder.setAudioSamplingRate(mProfile.audioSampleRate); mMediaRecorder.setAudioSamplingRate(mProfile.audioSampleRate);
mMediaRecorder.setAudioEncoder(mProfile.audioCodec); mMediaRecorder.setAudioEncoder(mProfile.audioCodec);
@ -82,15 +79,15 @@ public class FullVideoRecorder extends VideoRecorder {
mMediaRecorder.setAudioEncodingBitRate(mResult.audioBitRate); mMediaRecorder.setAudioEncodingBitRate(mResult.audioBitRate);
} }
} }
if (mResult.getLocation() != null) { if (mResult.location != null) {
mMediaRecorder.setLocation( mMediaRecorder.setLocation(
(float) mResult.getLocation().getLatitude(), (float) mResult.location.getLatitude(),
(float) mResult.getLocation().getLongitude()); (float) mResult.location.getLongitude());
} }
mMediaRecorder.setOutputFile(mResult.getFile().getAbsolutePath()); mMediaRecorder.setOutputFile(mResult.file.getAbsolutePath());
mMediaRecorder.setOrientationHint(mResult.getRotation()); mMediaRecorder.setOrientationHint(mResult.rotation);
mMediaRecorder.setMaxFileSize(mResult.getMaxSize()); mMediaRecorder.setMaxFileSize(mResult.maxSize);
mMediaRecorder.setMaxDuration(mResult.getMaxDuration()); mMediaRecorder.setMaxDuration(mResult.maxDuration);
mMediaRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() { mMediaRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {
@Override @Override
public void onInfo(MediaRecorder mediaRecorder, int what, int extra) { public void onInfo(MediaRecorder mediaRecorder, int what, int extra) {
@ -120,7 +117,7 @@ public class FullVideoRecorder extends VideoRecorder {
} }
@Override @Override
void stop() { public void stop() {
if (mMediaRecorder != null) { if (mMediaRecorder != null) {
try { try {
mMediaRecorder.stop(); mMediaRecorder.stop();

@ -4,17 +4,17 @@ import android.graphics.SurfaceTexture;
import android.opengl.EGL14; import android.opengl.EGL14;
import android.os.Build; import android.os.Build;
import com.otaliastudios.cameraview.AudioMediaEncoder;
import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.CameraLogger;
import com.otaliastudios.cameraview.MediaEncoderEngine;
import com.otaliastudios.cameraview.TextureMediaEncoder;
import com.otaliastudios.cameraview.VideoResult; import com.otaliastudios.cameraview.VideoResult;
import com.otaliastudios.cameraview.controls.Audio; import com.otaliastudios.cameraview.controls.Audio;
import com.otaliastudios.cameraview.controls.VideoCodec; import com.otaliastudios.cameraview.engine.CameraEngine;
import com.otaliastudios.cameraview.preview.GlCameraPreview; import com.otaliastudios.cameraview.preview.GlCameraPreview;
import com.otaliastudios.cameraview.preview.RendererFrameCallback; import com.otaliastudios.cameraview.preview.RendererFrameCallback;
import com.otaliastudios.cameraview.preview.RendererThread; import com.otaliastudios.cameraview.preview.RendererThread;
import com.otaliastudios.cameraview.size.Size; import com.otaliastudios.cameraview.size.Size;
import com.otaliastudios.cameraview.video.encoding.AudioMediaEncoder;
import com.otaliastudios.cameraview.video.encoding.MediaEncoderEngine;
import com.otaliastudios.cameraview.video.encoding.TextureMediaEncoder;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -39,24 +39,27 @@ public class SnapshotVideoRecorder extends VideoRecorder implements RendererFram
private MediaEncoderEngine mEncoderEngine; private MediaEncoderEngine mEncoderEngine;
private GlCameraPreview mPreview; private GlCameraPreview mPreview;
private boolean mFlipped;
private int mCurrentState = STATE_NOT_RECORDING; private int mCurrentState = STATE_NOT_RECORDING;
private int mDesiredState = STATE_NOT_RECORDING; private int mDesiredState = STATE_NOT_RECORDING;
private int mTextureId = 0; private int mTextureId = 0;
public SnapshotVideoRecorder(@NonNull VideoResult.Stub stub, @Nullable VideoResultListener listener, @NonNull GlCameraPreview preview) { public SnapshotVideoRecorder(@NonNull VideoResult.Stub stub, @Nullable VideoResultListener listener,
@NonNull CameraEngine engine, @NonNull GlCameraPreview preview) {
super(stub, listener); super(stub, listener);
mPreview = preview; mPreview = preview;
mPreview.addRendererFrameCallback(this); mPreview.addRendererFrameCallback(this);
mFlipped = engine.flip(CameraEngine.REF_SENSOR, CameraEngine.REF_VIEW);
} }
@Override @Override
void start() { public void start() {
mDesiredState = STATE_RECORDING; mDesiredState = STATE_RECORDING;
} }
@Override @Override
void stop() { public void stop() {
mDesiredState = STATE_NOT_RECORDING; mDesiredState = STATE_NOT_RECORDING;
} }
@ -76,16 +79,16 @@ public class SnapshotVideoRecorder extends VideoRecorder implements RendererFram
if (mResult.audioBitRate <= 0) mResult.audioBitRate = DEFAULT_AUDIO_BITRATE; if (mResult.audioBitRate <= 0) mResult.audioBitRate = DEFAULT_AUDIO_BITRATE;
// Video. Ensure width and height are divisible by 2, as I have read somewhere. // Video. Ensure width and height are divisible by 2, as I have read somewhere.
Size size = mResult.getSize(); Size size = mResult.size;
int width = size.getWidth(); int width = size.getWidth();
int height = size.getHeight(); int height = size.getHeight();
width = width % 2 == 0 ? width : width + 1; width = width % 2 == 0 ? width : width + 1;
height = height % 2 == 0 ? height : height + 1; height = height % 2 == 0 ? height : height + 1;
String type = ""; String type = "";
switch (mResult.codec) { switch (mResult.videoCodec) {
case VideoCodec.H_263: type = "video/3gpp"; break; // MediaFormat.MIMETYPE_VIDEO_H263; case H_263: type = "video/3gpp"; break; // MediaFormat.MIMETYPE_VIDEO_H263;
case VideoCodec.H_264: type = "video/avc"; break; // MediaFormat.MIMETYPE_VIDEO_AVC: case H_264: type = "video/avc"; break; // MediaFormat.MIMETYPE_VIDEO_AVC:
case VideoCodec.DEVICE_DEFAULT: type = "video/avc"; break; case DEVICE_DEFAULT: type = "video/avc"; break;
} }
LOG.w("Creating frame encoder. Rotation:", mResult.rotation); LOG.w("Creating frame encoder. Rotation:", mResult.rotation);
TextureMediaEncoder.Config config = new TextureMediaEncoder.Config(width, height, TextureMediaEncoder.Config config = new TextureMediaEncoder.Config(width, height,
@ -94,7 +97,7 @@ public class SnapshotVideoRecorder extends VideoRecorder implements RendererFram
mResult.rotation, mResult.rotation,
type, mTextureId, type, mTextureId,
scaleX, scaleY, scaleX, scaleY,
mPreview.mInputFlipped, mFlipped,
EGL14.eglGetCurrentContext() EGL14.eglGetCurrentContext()
); );
TextureMediaEncoder videoEncoder = new TextureMediaEncoder(config); TextureMediaEncoder videoEncoder = new TextureMediaEncoder(config);

@ -4,6 +4,7 @@ import com.otaliastudios.cameraview.VideoResult;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
/** /**
* Interface for video recording. * Interface for video recording.
@ -12,19 +13,47 @@ import androidx.annotation.Nullable;
*/ */
public abstract class VideoRecorder { public abstract class VideoRecorder {
/* tests */ VideoResult.Stub mResult; /**
/* tests */ VideoResultListener mListener; * Listens for video recorder events.
*/
public interface VideoResultListener {
/**
* The operation was completed, either with success or with an error.
* @param result the result or null if error
* @param exception the error or null if everything went fine
*/
void onVideoResult(@Nullable VideoResult.Stub result, @Nullable Exception exception);
}
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) VideoResult.Stub mResult;
@VisibleForTesting VideoResultListener mListener;
protected Exception mError; protected Exception mError;
/**
* Creates a new video recorder.
* @param stub a video stub
* @param listener a listener
*/
VideoRecorder(@NonNull VideoResult.Stub stub, @Nullable VideoResultListener listener) { VideoRecorder(@NonNull VideoResult.Stub stub, @Nullable VideoResultListener listener) {
mResult = stub; mResult = stub;
mListener = listener; mListener = listener;
} }
/**
* Starts recording a video.
*/
public abstract void start(); public abstract void start();
/**
* Stops recording.
*/
public abstract void stop(); public abstract void stop();
/**
* Subclasses can call this to notify that the result was obtained,
* either with some error (null result) or with the actual stub, filled.
*/
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
protected void dispatchResult() { protected void dispatchResult() {
if (mListener != null) { if (mListener != null) {
@ -34,8 +63,4 @@ public abstract class VideoRecorder {
mError = null; mError = null;
} }
} }
public interface VideoResultListener {
void onVideoResult(@Nullable VideoResult.Stub result, @Nullable Exception exception);
}
} }

@ -25,7 +25,7 @@ import java.util.concurrent.LinkedBlockingQueue;
// TODO create onVideoRecordingStart/onVideoRecordingEnd callbacks // TODO create onVideoRecordingStart/onVideoRecordingEnd callbacks
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
class AudioMediaEncoder extends MediaEncoder { public class AudioMediaEncoder extends MediaEncoder {
private static final String TAG = AudioMediaEncoder.class.getSimpleName(); private static final String TAG = AudioMediaEncoder.class.getSimpleName();
private static final CameraLogger LOG = CameraLogger.create(TAG); private static final CameraLogger LOG = CameraLogger.create(TAG);
@ -59,14 +59,14 @@ class AudioMediaEncoder extends MediaEncoder {
private ByteBufferPool mByteBufferPool; private ByteBufferPool mByteBufferPool;
private Config mConfig; private Config mConfig;
static class Config { public static class Config {
int bitRate; int bitRate;
Config(int bitRate) { public Config(int bitRate) {
this.bitRate = bitRate; this.bitRate = bitRate;
} }
} }
AudioMediaEncoder(@NonNull Config config) { public AudioMediaEncoder(@NonNull Config config) {
mConfig = config; mConfig = config;
} }

@ -15,15 +15,15 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
class MediaEncoderEngine { public class MediaEncoderEngine {
private final static String TAG = MediaEncoderEngine.class.getSimpleName(); private final static String TAG = MediaEncoderEngine.class.getSimpleName();
private final static CameraLogger LOG = CameraLogger.create(TAG); private final static CameraLogger LOG = CameraLogger.create(TAG);
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
final static int STOP_BY_USER = 0; public final static int STOP_BY_USER = 0;
final static int STOP_BY_MAX_DURATION = 1; public final static int STOP_BY_MAX_DURATION = 1;
final static int STOP_BY_MAX_SIZE = 2; public final static int STOP_BY_MAX_SIZE = 2;
private ArrayList<MediaEncoder> mEncoders; private ArrayList<MediaEncoder> mEncoders;
private MediaMuxer mMediaMuxer; private MediaMuxer mMediaMuxer;
@ -36,7 +36,7 @@ class MediaEncoderEngine {
private int mPossibleStopReason; private int mPossibleStopReason;
private final Object mControllerLock = new Object(); private final Object mControllerLock = new Object();
MediaEncoderEngine(@NonNull File file, @NonNull VideoMediaEncoder videoEncoder, @Nullable AudioMediaEncoder audioEncoder, public MediaEncoderEngine(@NonNull File file, @NonNull VideoMediaEncoder videoEncoder, @Nullable AudioMediaEncoder audioEncoder,
final int maxDuration, final long maxSize, @Nullable Listener listener) { final int maxDuration, final long maxSize, @Nullable Listener listener) {
mListener = listener; mListener = listener;
mController = new Controller(); mController = new Controller();
@ -164,14 +164,14 @@ class MediaEncoderEngine {
} }
} }
final void start() { public final void start() {
for (MediaEncoder encoder : mEncoders) { for (MediaEncoder encoder : mEncoders) {
encoder.start(); encoder.start();
} }
} }
@SuppressWarnings("SameParameterValue") @SuppressWarnings("SameParameterValue")
final void notify(final String event, final Object data) { public final void notify(final String event, final Object data) {
for (MediaEncoder encoder : mEncoders) { for (MediaEncoder encoder : mEncoders) {
encoder.notify(event, data); encoder.notify(event, data);
} }
@ -181,7 +181,7 @@ class MediaEncoderEngine {
* This just asks the encoder to stop. We will wait for them to call {@link Controller#requestRelease(int)} * This just asks the encoder to stop. We will wait for them to call {@link Controller#requestRelease(int)}
* to actually stop the muxer, as there might be async stuff going on. * to actually stop the muxer, as there might be async stuff going on.
*/ */
final void stop() { public final void stop() {
for (MediaEncoder encoder : mEncoders) { for (MediaEncoder encoder : mEncoders) {
encoder.stop(); encoder.stop();
} }
@ -212,12 +212,12 @@ class MediaEncoderEngine {
} }
@NonNull @NonNull
VideoMediaEncoder getVideoEncoder() { public VideoMediaEncoder getVideoEncoder() {
return (VideoMediaEncoder) mEncoders.get(0); return (VideoMediaEncoder) mEncoders.get(0);
} }
@Nullable @Nullable
AudioMediaEncoder getAudioEncoder() { public AudioMediaEncoder getAudioEncoder() {
if (mEncoders.size() > 1) { if (mEncoders.size() > 1) {
return (AudioMediaEncoder) mEncoders.get(1); return (AudioMediaEncoder) mEncoders.get(1);
} else { } else {
@ -225,7 +225,7 @@ class MediaEncoderEngine {
} }
} }
interface Listener { public interface Listener {
@EncoderThread @EncoderThread
void onEncoderStop(int stopReason, @Nullable Exception e); void onEncoderStop(int stopReason, @Nullable Exception e);

@ -15,14 +15,14 @@ import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
class TextureMediaEncoder extends VideoMediaEncoder<TextureMediaEncoder.Config> { public class TextureMediaEncoder extends VideoMediaEncoder<TextureMediaEncoder.Config> {
private static final String TAG = TextureMediaEncoder.class.getSimpleName(); private static final String TAG = TextureMediaEncoder.class.getSimpleName();
private static final CameraLogger LOG = CameraLogger.create(TAG); private static final CameraLogger LOG = CameraLogger.create(TAG);
final static String FRAME_EVENT = "frame"; public final static String FRAME_EVENT = "frame";
static class Config extends VideoMediaEncoder.Config { public static class Config extends VideoMediaEncoder.Config {
int textureId; int textureId;
float scaleX; float scaleX;
float scaleY; float scaleY;
@ -30,7 +30,7 @@ class TextureMediaEncoder extends VideoMediaEncoder<TextureMediaEncoder.Config>
EGLContext eglContext; EGLContext eglContext;
int transformRotation; int transformRotation;
Config(int width, int height, int bitRate, int frameRate, int rotation, String mimeType, public Config(int width, int height, int bitRate, int frameRate, int rotation, String mimeType,
int textureId, float scaleX, float scaleY, boolean scaleFlipped, EGLContext eglContext) { int textureId, float scaleX, float scaleY, boolean scaleFlipped, EGLContext eglContext) {
// We rotate the texture using transformRotation. Pass rotation=0 to super so that // We rotate the texture using transformRotation. Pass rotation=0 to super so that
// no rotation metadata is written into the output file. // no rotation metadata is written into the output file.
@ -54,20 +54,20 @@ class TextureMediaEncoder extends VideoMediaEncoder<TextureMediaEncoder.Config>
} }
}); });
TextureMediaEncoder(@NonNull Config config) { public TextureMediaEncoder(@NonNull Config config) {
super(config); super(config);
} }
static class TextureFrame { public static class TextureFrame {
private TextureFrame() {} private TextureFrame() {}
// Nanoseconds, in no meaningful time-base. Should be for offsets only. // Nanoseconds, in no meaningful time-base. Should be for offsets only.
// Typically coming from SurfaceTexture.getTimestamp(). // Typically coming from SurfaceTexture.getTimestamp().
long timestamp; public long timestamp;
float[] transform = new float[16]; public float[] transform = new float[16];
} }
@NonNull @NonNull
TextureFrame acquireFrame() { public TextureFrame acquireFrame() {
if (mFramePool.isEmpty()) { if (mFramePool.isEmpty()) {
throw new RuntimeException("Need more frames than this! Please increase the pool size."); throw new RuntimeException("Need more frames than this! Please increase the pool size.");
} else { } else {

Loading…
Cancel
Save