From d08c56c821a66e35d09e9f16e147f010a3027da3 Mon Sep 17 00:00:00 2001 From: Mattia Iavarone Date: Mon, 17 Sep 2018 22:30:21 -0300 Subject: [PATCH] Delete old media encoder --- .../cameraview/OldMediaEncoder.java | 366 ------------------ .../cameraview/OldMediaEncoderCore.java | 211 ---------- .../cameraview/SnapshotVideoRecorder.java | 87 ++--- 3 files changed, 25 insertions(+), 639 deletions(-) delete mode 100644 cameraview/src/main/gles/com/otaliastudios/cameraview/OldMediaEncoder.java delete mode 100644 cameraview/src/main/gles/com/otaliastudios/cameraview/OldMediaEncoderCore.java diff --git a/cameraview/src/main/gles/com/otaliastudios/cameraview/OldMediaEncoder.java b/cameraview/src/main/gles/com/otaliastudios/cameraview/OldMediaEncoder.java deleted file mode 100644 index 1c9251c6..00000000 --- a/cameraview/src/main/gles/com/otaliastudios/cameraview/OldMediaEncoder.java +++ /dev/null @@ -1,366 +0,0 @@ -/* - * Copyright 2013 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.otaliastudios.cameraview; - -import android.graphics.SurfaceTexture; -import android.opengl.EGLContext; -import android.opengl.Matrix; -import android.os.Build; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.support.annotation.RequiresApi; -import android.util.Log; - -import java.io.File; -import java.io.IOException; -import java.lang.ref.WeakReference; - - -/** - * -- from grafika -- - * - * Encode a movie from frames rendered from an external texture image. - *

- * The object wraps an encoder running on a dedicated thread. The various control messages - * may be sent from arbitrary threads (typically the app UI thread). The encoder thread - * manages both sides of the encoder (feeding and draining); the only external input is - * the GL texture. - *

- * The design is complicated slightly by the need to create an EGL context that shares state - * with a view that gets restarted if (say) the device orientation changes. When the view - * in question is a GLSurfaceView, we don't have full control over the EGL context creation - * on that side, so we have to bend a bit backwards here. - *

- * To use: - *

- * - * TODO: tweak the API (esp. textureId) so it's less awkward for simple use cases. - */ -@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) -class OldMediaEncoder implements Runnable { - private static final String TAG = OldMediaEncoder.class.getSimpleName(); - - private static final int MSG_START_RECORDING = 0; - private static final int MSG_STOP_RECORDING = 1; - private static final int MSG_FRAME_AVAILABLE = 2; - private static final int MSG_SET_TEXTURE_ID = 3; - private static final int MSG_QUIT = 4; - - // ----- accessed exclusively by encoder thread ----- - private EglWindowSurface mInputWindowSurface; - private EglCore mEglCore; - private EglViewport mFullScreen; - private int mTextureId; - private int mFrameNum = -1; // Important - private OldMediaEncoderCore mVideoEncoder; - private float mTransformationScaleX = 1F; - private float mTransformationScaleY = 1F; - private int mTransformationRotation = 0; - - // ----- accessed by multiple threads ----- - private volatile EncoderHandler mHandler; - - private final Object mLooperReadyLock = new Object(); // guards ready/running - private boolean mLooperReady; - private boolean mRunning; - - /** - * Encoder configuration. - *

- * Object is immutable, which means we can safely pass it between threads without - * explicit synchronization (and don't need to worry about it getting tweaked out from - * under us). - *

- */ - static class Config { - final File mOutputFile; - final int mWidth; - final int mHeight; - final int mBitRate; - final int mFrameRate; - final int mRotation; - final float mScaleX; - final float mScaleY; - final EGLContext mEglContext; - final String mMimeType; - - Config(File outputFile, int width, int height, - int bitRate, int frameRate, - int rotation, - float scaleX, float scaleY, - String mimeType, - EGLContext sharedEglContext) { - mOutputFile = outputFile; - mWidth = width; - mHeight = height; - mBitRate = bitRate; - mFrameRate = frameRate; - mEglContext = sharedEglContext; - mScaleX = scaleX; - mScaleY = scaleY; - mRotation = rotation; - mMimeType = mimeType; - } - - @Override - public String toString() { - return "Config: " + mWidth + "x" + mHeight + " @" + mBitRate + - " to '" + mOutputFile.toString() + "' ctxt=" + mEglContext; - } - } - - private void prepareEncoder(Config config) { - OldMediaEncoderCore.VideoConfig videoConfig = new OldMediaEncoderCore.VideoConfig( - config.mWidth, config.mHeight, config.mBitRate, config.mFrameRate, - 0, // The video encoder rotation does not work, so we apply it here using Matrix.rotateM(). - config.mMimeType); - try { - mVideoEncoder = new OldMediaEncoderCore(videoConfig, config.mOutputFile); - } catch (IOException ioe) { - throw new RuntimeException(ioe); - } - mEglCore = new EglCore(config.mEglContext, EglCore.FLAG_RECORDABLE); - mInputWindowSurface = new EglWindowSurface(mEglCore, mVideoEncoder.getInputSurface(), true); - mInputWindowSurface.makeCurrent(); // drawing will happen on the InputWindowSurface, which - // is backed by mVideoEncoder.getInputSurface() - mFullScreen = new EglViewport(); - mTransformationScaleX = config.mScaleX; - mTransformationScaleY = config.mScaleY; - mTransformationRotation = config.mRotation; - } - - private void releaseEncoder() { - mVideoEncoder.release(); - if (mInputWindowSurface != null) { - mInputWindowSurface.release(); - mInputWindowSurface = null; - } - if (mFullScreen != null) { - mFullScreen.release(true); - mFullScreen = null; - } - if (mEglCore != null) { - mEglCore.release(); - mEglCore = null; - } - } - - /** - * Tells the video recorder to start recording. (Call from non-encoder thread.) - *

- * Creates a new thread, which will create an encoder using the provided configuration. - *

- * Returns after the recorder thread has started and is ready to accept Messages. The - * encoder may not yet be fully configured. - */ - public void startRecording(Config config) { - Log.d(TAG, "Encoder: startRecording()"); - synchronized (mLooperReadyLock) { - if (mRunning) { - Log.w(TAG, "Encoder thread already running"); - return; - } - mRunning = true; - new Thread(this, "TextureMovieEncoder").start(); - while (!mLooperReady) { - try { - mLooperReadyLock.wait(); - } catch (InterruptedException ie) { - // ignore - } - } - } - - mHandler.sendMessage(mHandler.obtainMessage(MSG_START_RECORDING, config)); - } - - /** - * Tells the video recorder to stop recording. (Call from non-encoder thread.) - *

- * Returns immediately; the encoder/muxer may not yet be finished creating the movie. - *

- */ - public void stopRecording(Runnable onStop) { - mHandler.sendMessage(mHandler.obtainMessage(MSG_STOP_RECORDING, onStop)); - mHandler.sendMessage(mHandler.obtainMessage(MSG_QUIT)); - // We don't know when these will actually finish (or even start). We don't want to - // delay the UI thread though, so we return immediately. - } - - /** - * Returns true if recording has been started. - */ - public boolean isRecording() { - synchronized (mLooperReadyLock) { - return mRunning; - } - } - - /** - * Tells the video recorder that a new frame is available. (Call from non-encoder thread.) - *

- * This function sends a message and returns immediately. This isn't sufficient -- we - * don't want the caller to latch a new frame until we're done with this one -- but we - * can get away with it so long as the input frame rate is reasonable and the encoder - * thread doesn't stall. - *

- * TODO: either block here until the texture has been rendered onto the encoder surface, - * or have a separate "block if still busy" method that the caller can execute immediately - * before it calls updateTexImage(). The latter is preferred because we don't want to - * stall the caller while this thread does work. - */ - public void frameAvailable(SurfaceTexture st) { - synchronized (mLooperReadyLock) { - if (!mLooperReady) { - return; - } - } - - float[] transform = new float[16]; // TODO - avoid alloc every frame. Not easy, need a pool - st.getTransformMatrix(transform); - long timestamp = st.getTimestamp(); - if (timestamp == 0) { - // Seeing this after device is toggled off/on with power button. The - // first frame back has a zero timestamp. - // MPEG4Writer thinks this is cause to abort() in native code, so it's very - // important that we just ignore the frame. - Log.w(TAG, "HEY: got SurfaceTexture with timestamp of zero"); - return; - } - - mHandler.sendMessage(mHandler.obtainMessage(MSG_FRAME_AVAILABLE, - (int) (timestamp >> 32), (int) timestamp, transform)); - } - - /** - * Tells the video recorder what texture name to use. This is the external texture that - * we're receiving camera previews in. (Call from non-encoder thread.) - *

- * TODO: do something less clumsy - */ - public void setTextureId(int id) { - synchronized (mLooperReadyLock) { - if (!mLooperReady) return; - } - mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_TEXTURE_ID, id, 0, null)); - } - - /** - * Encoder thread entry point. Establishes Looper/Handler and waits for messages. - *

- * @see java.lang.Thread#run() - */ - @Override - public void run() { - // Establish a Looper for this thread, and define a Handler for it. - Looper.prepare(); - synchronized (mLooperReadyLock) { - mHandler = new EncoderHandler(this); - mLooperReady = true; - mLooperReadyLock.notify(); - } - Looper.loop(); - Log.d(TAG, "Encoder thread exiting"); - synchronized (mLooperReadyLock) { - mLooperReady = mRunning = false; - mHandler = null; - } - } - - /** - * Handles encoder state change requests. The handler is created on the encoder thread. - */ - private static class EncoderHandler extends Handler { - private WeakReference mWeakEncoder; - - public EncoderHandler(OldMediaEncoder encoder) { - mWeakEncoder = new WeakReference<>(encoder); - } - - @Override // runs on encoder thread - public void handleMessage(Message inputMessage) { - int what = inputMessage.what; - Object obj = inputMessage.obj; - - OldMediaEncoder encoder = mWeakEncoder.get(); - if (encoder == null) { - Log.w(TAG, "EncoderHandler.handleMessage: encoder is null"); - return; - } - - switch (what) { - case MSG_START_RECORDING: - encoder.mFrameNum = 0; - Config config = (Config) obj; - encoder.prepareEncoder(config); - break; - case MSG_STOP_RECORDING: - encoder.mFrameNum = -1; - encoder.mVideoEncoder.drainEncoder(true); - encoder.releaseEncoder(); - ((Runnable) obj).run(); - break; - case MSG_FRAME_AVAILABLE: - if (encoder.mFrameNum < 0) break; - encoder.mFrameNum++; - 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. - // Scaling takes place with respect to the (0, 0, 0) point, so we must apply a Translation to compensate. - - float scaleX = encoder.mTransformationScaleX; - float scaleY = encoder.mTransformationScaleY; - float scaleTranslX = (1F - scaleX) / 2F; - float scaleTranslY = (1F - scaleY) / 2F; - 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 - // 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. - - Matrix.translateM(transform, 0, 0.5F, 0.5F, 0); - Matrix.rotateM(transform, 0, encoder.mTransformationRotation, 0, 0, 1); - Matrix.translateM(transform, 0, -0.5F, -0.5F, 0); - - encoder.mVideoEncoder.drainEncoder(false); - encoder.mFullScreen.drawFrame(encoder.mTextureId, transform); - encoder.mInputWindowSurface.setPresentationTime(timestamp); - encoder.mInputWindowSurface.swapBuffers(); - break; - case MSG_SET_TEXTURE_ID: - encoder.mTextureId = inputMessage.arg1; - break; - case MSG_QUIT: - Looper.myLooper().quit(); - break; - default: - throw new RuntimeException("Unhandled msg what=" + what); - } - } - } - -} \ No newline at end of file diff --git a/cameraview/src/main/gles/com/otaliastudios/cameraview/OldMediaEncoderCore.java b/cameraview/src/main/gles/com/otaliastudios/cameraview/OldMediaEncoderCore.java deleted file mode 100644 index 3123e460..00000000 --- a/cameraview/src/main/gles/com/otaliastudios/cameraview/OldMediaEncoderCore.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright 2014 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.otaliastudios.cameraview; - - -import android.media.MediaCodec; -import android.media.MediaCodecInfo; -import android.media.MediaFormat; -import android.media.MediaMuxer; -import android.os.Build; -import android.support.annotation.RequiresApi; -import android.util.Log; -import android.view.Surface; - -import java.io.File; -import java.io.IOException; -import java.nio.ByteBuffer; - -/** - * -- From grafika VideoEncoderCore.java -- - * - * This class wraps up the core components used for surface-input video encoding. - *

- * Once created, frames are fed to the input surface. Remember to provide the presentation - * time stamp, and always call drainEncoder() before swapBuffers() to ensure that the - * producer side doesn't get backed up. - *

- * This class is not thread-safe, with one exception: it is valid to use the input surface - * on one thread, and drain the output on a different thread. - */ -@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) -class OldMediaEncoderCore { - - private MediaMuxer mMuxer; - private boolean mMuxerStarted; - private MediaCodec mVideoEncoder; - private Surface mVideoInputSurface; - private MediaCodec.BufferInfo mBufferInfo; - private int mTrackIndex; - - - static class VideoConfig { - int width; - int height; - int bitRate; - int frameRate; - int rotation; - String mimeType; - - VideoConfig(int width, int height, int bitRate, int frameRate, int rotation, String mimeType) { - this.width = width; - this.height = height; - this.bitRate = bitRate; - this.frameRate = frameRate; - this.rotation = rotation; - this.mimeType = mimeType; - } - } - /** - * Configures encoder and muxer state, and prepares the input Surface. - */ - OldMediaEncoderCore(VideoConfig videoConfig, File outputFile) throws IOException { - mBufferInfo = new MediaCodec.BufferInfo(); - - MediaFormat format = MediaFormat.createVideoFormat(videoConfig.mimeType, videoConfig.width, videoConfig.height); - - // Set some properties. Failing to specify some of these can cause the MediaCodec - // configure() call to throw an unhelpful exception. - format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); - format.setInteger(MediaFormat.KEY_BIT_RATE, videoConfig.bitRate); - format.setInteger(MediaFormat.KEY_FRAME_RATE, videoConfig.frameRate); - format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5); - format.setInteger("rotation-degrees", videoConfig.rotation); - - // Create a MediaCodec encoder, and configure it with our format. Get a Surface - // we can use for input and wrap it with a class that handles the EGL work. - mVideoEncoder = MediaCodec.createEncoderByType(videoConfig.mimeType); - mVideoEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); - mVideoInputSurface = mVideoEncoder.createInputSurface(); - mVideoEncoder.start(); - - // Create a MediaMuxer. We can't add the video track and start() the muxer here, - // because our MediaFormat doesn't have the Magic Goodies. These can only be - // obtained from the encoder after it has started processing data. - // - // We're not actually interested in multiplexing audio. We just want to convert - // the raw H.264 elementary stream we get from MediaCodec into a .mp4 file. - mMuxer = new MediaMuxer(outputFile.toString(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); - - mTrackIndex = -1; - mMuxerStarted = false; - } - - /** - * Returns the encoder's input surface. - */ - public Surface getInputSurface() { - return mVideoInputSurface; - } - - /** - * Releases encoder resources. - */ - public void release() { - if (mVideoEncoder != null) { - mVideoEncoder.stop(); - mVideoEncoder.release(); - mVideoEncoder = null; - } - if (mMuxer != 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. - mMuxer.stop(); - mMuxer.release(); - mMuxer = null; - } - } - - /** - * Extracts all pending data from the encoder and forwards it to the muxer. - *

- * If endOfStream is not set, this returns when there is no more data to drain. If it - * is set, we send EOS to the encoder, and then iterate until we see EOS on the output. - * Calling this with endOfStream set should be done once, right before stopping the muxer. - *

- * We're just using the muxer to get a .mp4 file (instead of a raw H.264 stream). We're - * not recording audio. - */ - public void drainEncoder(boolean endOfStream) { - final int TIMEOUT_USEC = 10000; - - if (endOfStream) { - mVideoEncoder.signalEndOfInputStream(); - } - - ByteBuffer[] encoderOutputBuffers = mVideoEncoder.getOutputBuffers(); - while (true) { - int encoderStatus = mVideoEncoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC); - if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) { - // no output available yet - if (!endOfStream) { - break; // out of while - } - } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { - // not expected for an encoder - encoderOutputBuffers = mVideoEncoder.getOutputBuffers(); - } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { - // should happen before receiving buffers, and should only happen once - if (mMuxerStarted) { - throw new RuntimeException("format changed twice"); - } - MediaFormat newFormat = mVideoEncoder.getOutputFormat(); - - // now that we have the Magic Goodies, start the muxer - mTrackIndex = mMuxer.addTrack(newFormat); - mMuxer.start(); - mMuxerStarted = true; - } else if (encoderStatus < 0) { - Log.w("OldMediaEncoderCore", "unexpected result from encoder.dequeueOutputBuffer: " + encoderStatus); - // let's ignore it - } else { - ByteBuffer encodedData = encoderOutputBuffers[encoderStatus]; - if (encodedData == null) { - throw new RuntimeException("encoderOutputBuffer " + encoderStatus + " was null"); - } - - if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { - // The codec config data was pulled out and fed to the muxer when we got - // the INFO_OUTPUT_FORMAT_CHANGED status. Ignore it. - mBufferInfo.size = 0; - } - - if (mBufferInfo.size != 0) { - if (!mMuxerStarted) { - throw new RuntimeException("muxer hasn't started"); - } - - // adjust the ByteBuffer values to match BufferInfo (not needed?) - encodedData.position(mBufferInfo.offset); - encodedData.limit(mBufferInfo.offset + mBufferInfo.size); - - mMuxer.writeSampleData(mTrackIndex, encodedData, mBufferInfo); - } - - mVideoEncoder.releaseOutputBuffer(encoderStatus, false); - - if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { - if (!endOfStream) { - Log.w("OldMediaEncoderCore", "reached end of stream unexpectedly"); - } - break; // out of while - } - } - } - } -} - diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/SnapshotVideoRecorder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/SnapshotVideoRecorder.java index ea7dc79c..b4022cb0 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/SnapshotVideoRecorder.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/SnapshotVideoRecorder.java @@ -18,12 +18,10 @@ class SnapshotVideoRecorder extends VideoRecorder implements GlCameraPreview.Ren private static final String TAG = SnapshotVideoRecorder.class.getSimpleName(); private static final CameraLogger LOG = CameraLogger.create(TAG); - private static final boolean USE_OLD_ENCODER = false; private static final int STATE_RECORDING = 0; private static final int STATE_NOT_RECORDING = 1; - private OldMediaEncoder mEncoder; private MediaEncoderEngine mEncoderEngine; private GlCameraPreview mPreview; @@ -33,9 +31,6 @@ class SnapshotVideoRecorder extends VideoRecorder implements GlCameraPreview.Ren SnapshotVideoRecorder(VideoResult stub, VideoResultListener listener, GlCameraPreview preview) { super(stub, listener); - if (USE_OLD_ENCODER) { - mEncoder = new OldMediaEncoder(); - } mPreview = preview; mPreview.addRendererFrameCallback(this); } @@ -82,71 +77,39 @@ class SnapshotVideoRecorder extends VideoRecorder implements GlCameraPreview.Ren case H_264: type = "video/avc"; break; // MediaFormat.MIMETYPE_VIDEO_AVC: case DEVICE_DEFAULT: type = "video/avc"; break; } - if (USE_OLD_ENCODER) { - OldMediaEncoder.Config configuration = new OldMediaEncoder.Config( - mResult.getFile(), - width, - height, - 1000000, - 30, - mResult.getRotation(), - scaleX, - scaleY, - type, - EGL14.eglGetCurrentContext() - ); - mEncoder.startRecording(configuration); - mEncoder.setTextureId(mTextureId); - } else { - TextureMediaEncoder.Config config = new TextureMediaEncoder.Config( - width, height, - 1000000, - 30, - mResult.getRotation(), - type, mTextureId, - scaleX, scaleY, - EGL14.eglGetCurrentContext() - ); - TextureMediaEncoder videoEncoder = new TextureMediaEncoder(config); - mEncoderEngine = new MediaEncoderEngine(mResult.file, videoEncoder, null); - mEncoderEngine.start(); - } + TextureMediaEncoder.Config config = new TextureMediaEncoder.Config( + width, height, + 1000000, + 30, + mResult.getRotation(), + type, mTextureId, + scaleX, scaleY, + EGL14.eglGetCurrentContext() + ); + TextureMediaEncoder videoEncoder = new TextureMediaEncoder(config); + mEncoderEngine = new MediaEncoderEngine(mResult.file, videoEncoder, null); + mEncoderEngine.start(); mResult.rotation = 0; // We will rotate the result instead. mCurrentState = STATE_RECORDING; } if (mCurrentState == STATE_RECORDING) { - if (USE_OLD_ENCODER) { - mEncoder.frameAvailable(surfaceTexture); - } else { - TextureMediaEncoder.Frame frame = new TextureMediaEncoder.Frame(); - frame.timestamp = surfaceTexture.getTimestamp(); - frame.transform = new float[16]; - surfaceTexture.getTransformMatrix(frame.transform); - mEncoderEngine.notify(TextureMediaEncoder.FRAME_EVENT, frame); - } + TextureMediaEncoder.Frame frame = new TextureMediaEncoder.Frame(); + frame.timestamp = surfaceTexture.getTimestamp(); + frame.transform = new float[16]; + surfaceTexture.getTransformMatrix(frame.transform); + mEncoderEngine.notify(TextureMediaEncoder.FRAME_EVENT, frame); } if (mCurrentState == STATE_RECORDING && mDesiredState == STATE_NOT_RECORDING) { - if (USE_OLD_ENCODER) { - mEncoder.stopRecording(new Runnable() { - @Override - public void run() { - // We are in the encoder thread. - dispatchResult(); - } - }); - mEncoder = null; - } else { - mEncoderEngine.stop(new Runnable() { - @Override - public void run() { - // We are in the encoder thread. - dispatchResult(); - } - }); - mEncoderEngine = null; - } + mEncoderEngine.stop(new Runnable() { + @Override + public void run() { + // We are in the encoder thread. + dispatchResult(); + } + }); + mEncoderEngine = null; mCurrentState = STATE_NOT_RECORDING; mPreview.removeRendererFrameCallback(SnapshotVideoRecorder.this); mPreview = null;