diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/SurfaceManager.java b/encoder/src/main/java/com/pedro/encoder/input/gl/SurfaceManager.java index c03b9959e..4f40d3c8c 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/gl/SurfaceManager.java +++ b/encoder/src/main/java/com/pedro/encoder/input/gl/SurfaceManager.java @@ -30,6 +30,8 @@ import com.pedro.encoder.utils.gl.GlUtil; +import java.util.concurrent.atomic.AtomicBoolean; + /** * Created by pedro on 9/09/17. */ @@ -44,10 +46,10 @@ public class SurfaceManager { private EGLContext eglContext = EGL14.EGL_NO_CONTEXT; private EGLSurface eglSurface = EGL14.EGL_NO_SURFACE; private EGLDisplay eglDisplay = EGL14.EGL_NO_DISPLAY; - private volatile boolean isReady = false; + private final AtomicBoolean isReady = new AtomicBoolean(false); public boolean isReady() { - return isReady; + return isReady.get(); } public void makeCurrent() { @@ -74,7 +76,7 @@ public void setPresentationTime(long nsecs) { * Prepares EGL. We want a GLES 2.0 context and a surface that supports recording. */ public void eglSetup(int width, int height, Surface surface, EGLContext eglSharedContext) { - if (isReady) { + if (isReady()) { Log.e(TAG, "already ready, ignored"); return; } @@ -155,7 +157,7 @@ public void eglSetup(int width, int height, Surface surface, EGLContext eglShare eglSurface = EGL14.eglCreateWindowSurface(eglDisplay, configs[0], surface, surfaceAttribs, 0); } GlUtil.checkEglError("eglCreateWindowSurface"); - isReady = true; + isReady.set(true); Log.i(TAG, "GL initialized"); } @@ -194,10 +196,10 @@ public void release() { eglDisplay = EGL14.EGL_NO_DISPLAY; eglContext = EGL14.EGL_NO_CONTEXT; eglSurface = EGL14.EGL_NO_SURFACE; - isReady = false; } else { Log.e(TAG, "GL already released"); } + isReady.set(false); } public EGLContext getEglContext() { diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/render/MainRender.kt b/encoder/src/main/java/com/pedro/encoder/input/gl/render/MainRender.kt index 66052bae5..8fc3d7a59 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/gl/render/MainRender.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/gl/render/MainRender.kt @@ -24,6 +24,7 @@ import androidx.annotation.RequiresApi import com.pedro.encoder.input.gl.FilterAction import com.pedro.encoder.input.gl.render.filters.BaseFilterRender import com.pedro.encoder.utils.gl.AspectRatioMode +import java.util.concurrent.atomic.AtomicBoolean /** * Created by pedro on 20/3/22. @@ -38,8 +39,7 @@ class MainRender { private var previewHeight = 0 private var context: Context? = null private var filterRenders: MutableList = ArrayList() - @Volatile - var isReady = false + private val running = AtomicBoolean(false) fun initGl(context: Context, encoderWidth: Int, encoderHeight: Int, previewWidth: Int, previewHeight: Int) { this.context = context @@ -51,9 +51,11 @@ class MainRender { screenRender.setStreamSize(encoderWidth, encoderHeight) screenRender.setTexId(cameraRender.texId) screenRender.initGl(context) - isReady = true + running.set(true) } + fun isReady(): Boolean = running.get() + fun drawOffScreen() { cameraRender.draw() for (baseFilterRender in filterRenders) baseFilterRender.draw() @@ -77,7 +79,7 @@ class MainRender { } fun release() { - isReady = false + running.set(false) cameraRender.release() for (baseFilterRender in filterRenders) baseFilterRender.release() filterRenders.clear() diff --git a/library/src/main/java/com/pedro/library/base/StreamBase.kt b/library/src/main/java/com/pedro/library/base/StreamBase.kt index e4e921917..87a871492 100644 --- a/library/src/main/java/com/pedro/library/base/StreamBase.kt +++ b/library/src/main/java/com/pedro/library/base/StreamBase.kt @@ -266,7 +266,7 @@ abstract class StreamBase( if (!surface.isValid) throw IllegalArgumentException("Make sure the Surface is valid") if (isOnPreview) throw IllegalStateException("Preview already started, stopPreview before startPreview again") isOnPreview = true - if (!glInterface.running) glInterface.start() + if (!glInterface.isRunning) glInterface.start() if (!videoSource.isRunning()) { videoSource.start(glInterface.surfaceTexture) } @@ -371,7 +371,7 @@ abstract class StreamBase( protected fun getVideoFps() = videoEncoder.fps private fun startSources() { - if (!glInterface.running) glInterface.start() + if (!glInterface.isRunning) glInterface.start() if (!videoSource.isRunning()) { videoSource.start(glInterface.surfaceTexture) } @@ -384,10 +384,10 @@ abstract class StreamBase( private fun stopSources() { if (!isOnPreview) videoSource.stop() audioSource.stop() - videoEncoder.stop() - audioEncoder.stop() glInterface.removeMediaCodecSurface() if (!isOnPreview) glInterface.stop() + videoEncoder.stop() + audioEncoder.stop() if (!isRecording) recordController.resetFormats() } diff --git a/library/src/main/java/com/pedro/library/view/GlStreamInterface.kt b/library/src/main/java/com/pedro/library/view/GlStreamInterface.kt index 3f8fa33cc..33b1d9e09 100644 --- a/library/src/main/java/com/pedro/library/view/GlStreamInterface.kt +++ b/library/src/main/java/com/pedro/library/view/GlStreamInterface.kt @@ -38,6 +38,7 @@ import java.util.concurrent.BlockingQueue import java.util.concurrent.ExecutorService import java.util.concurrent.Executors import java.util.concurrent.LinkedBlockingQueue +import java.util.concurrent.atomic.AtomicBoolean /** @@ -47,9 +48,7 @@ import java.util.concurrent.LinkedBlockingQueue class GlStreamInterface(private val context: Context): OnFrameAvailableListener, GlInterface { private var takePhotoCallback: TakePhotoCallback? = null - @Volatile - var running = false - private set + private val running = AtomicBoolean(false) private val surfaceManager = SurfaceManager() private val surfaceManagerEncoder = SurfaceManager() private val surfaceManagerPhoto = SurfaceManager() @@ -108,7 +107,7 @@ class GlStreamInterface(private val context: Context): OnFrameAvailableListener, setForceRender(enabled, 5) } - override fun isRunning(): Boolean = running + override fun isRunning(): Boolean = running.get() override fun getSurfaceTexture(): SurfaceTexture { return mainRender.getSurfaceTexture() @@ -146,7 +145,7 @@ class GlStreamInterface(private val context: Context): OnFrameAvailableListener, mainRender.initGl(context, encoderWidth, encoderHeight, encoderWidth, encoderHeight) surfaceManagerPhoto.release() surfaceManagerPhoto.eglSetup(encoderWidth, encoderHeight, surfaceManager) - running = true + running.set(true) mainRender.getSurfaceTexture().setOnFrameAvailableListener(this) forceRender.start { executor?.execute { draw(true) } } if (autoHandleOrientation) sensorRotationManager.start() @@ -154,7 +153,7 @@ class GlStreamInterface(private val context: Context): OnFrameAvailableListener, } override fun stop() { - running = false + running.set(false) executor?.secureSubmit { forceRender.stop() sensorRotationManager.stop() @@ -168,17 +167,17 @@ class GlStreamInterface(private val context: Context): OnFrameAvailableListener, } private fun draw(forced: Boolean) { - if (!running || fpsLimiter.limitFPS()) return + if (!isRunning || fpsLimiter.limitFPS()) return if (!forced) forceRender.frameAvailable() - if (surfaceManager.isReady && mainRender.isReady) { + if (surfaceManager.isReady && mainRender.isReady()) { surfaceManager.makeCurrent() mainRender.updateFrame() mainRender.drawOffScreen() surfaceManager.swapBuffer() } - if (!filterQueue.isEmpty() && mainRender.isReady) { + if (!filterQueue.isEmpty() && mainRender.isReady()) { try { val filter = filterQueue.take() mainRender.setFilterAction(filter.filterAction, filter.position, filter.baseFilterRender) @@ -194,7 +193,7 @@ class GlStreamInterface(private val context: Context): OnFrameAvailableListener, OrientationForced.NONE -> isPortrait } // render VideoEncoder (stream and record) - if (surfaceManagerEncoder.isReady && mainRender.isReady) { + if (surfaceManagerEncoder.isReady && mainRender.isReady()) { val w = if (muteVideo) 0 else encoderWidth val h = if (muteVideo) 0 else encoderHeight surfaceManagerEncoder.makeCurrent() @@ -203,7 +202,7 @@ class GlStreamInterface(private val context: Context): OnFrameAvailableListener, surfaceManagerEncoder.swapBuffer() } //render surface photo if request photo - if (takePhotoCallback != null && surfaceManagerPhoto.isReady && mainRender.isReady) { + if (takePhotoCallback != null && surfaceManagerPhoto.isReady && mainRender.isReady()) { surfaceManagerPhoto.makeCurrent() mainRender.drawScreen(encoderWidth, encoderHeight, AspectRatioMode.NONE, streamOrientation, isStreamVerticalFlip, isStreamHorizontalFlip) @@ -212,7 +211,7 @@ class GlStreamInterface(private val context: Context): OnFrameAvailableListener, surfaceManagerPhoto.swapBuffer() } // render preview - if (surfaceManagerPreview.isReady && mainRender.isReady) { + if (surfaceManagerPreview.isReady && mainRender.isReady()) { val w = if (previewWidth == 0) encoderWidth else previewWidth val h = if (previewHeight == 0) encoderHeight else previewHeight surfaceManagerPreview.makeCurrent() @@ -223,6 +222,7 @@ class GlStreamInterface(private val context: Context): OnFrameAvailableListener, } override fun onFrameAvailable(surfaceTexture: SurfaceTexture?) { + if (!isRunning) return executor?.execute { draw(false) } } diff --git a/library/src/main/java/com/pedro/library/view/OpenGlView.java b/library/src/main/java/com/pedro/library/view/OpenGlView.java index 97df778ab..40bffdbff 100644 --- a/library/src/main/java/com/pedro/library/view/OpenGlView.java +++ b/library/src/main/java/com/pedro/library/view/OpenGlView.java @@ -45,6 +45,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicBoolean; /** * Created by pedro on 10/03/18. @@ -54,7 +55,7 @@ public class OpenGlView extends SurfaceView implements GlInterface, SurfaceTexture.OnFrameAvailableListener, SurfaceHolder.Callback { - private volatile boolean running = false; + private AtomicBoolean running = new AtomicBoolean(false); private final MainRender mainRender = new MainRender(); private final SurfaceManager surfaceManagerPhoto = new SurfaceManager(); private final SurfaceManager surfaceManager = new SurfaceManager(); @@ -213,7 +214,7 @@ public void setForceRender(boolean enabled) { @Override public boolean isRunning() { - return running; + return running.get(); } @Override @@ -233,7 +234,7 @@ public void takePhoto(TakePhotoCallback takePhotoCallback) { } private void draw(boolean forced) { - if (!running || fpsLimiter.limitFPS()) return; + if (!isRunning() || fpsLimiter.limitFPS()) return; if (!forced) forceRenderer.frameAvailable(); if (surfaceManager.isReady() && mainRender.isReady()) { @@ -311,7 +312,7 @@ public void start() { mainRender.initGl(getContext(), encoderWidth, encoderHeight, encoderWidth, encoderHeight); surfaceManagerPhoto.release(); surfaceManagerPhoto.eglSetup(encoderWidth, encoderHeight, surfaceManager); - running = true; + running.set(true); mainRender.getSurfaceTexture().setOnFrameAvailableListener(this); forceRenderer.start(() -> { ExecutorService ex = this.executor; @@ -325,7 +326,7 @@ public void start() { @Override public void stop() { - running = false; + running.set(false); ExecutorService executor = this.executor; if (executor == null) return; ExtensionsKt.secureSubmit(executor, () -> { @@ -342,6 +343,7 @@ public void stop() { @Override public void onFrameAvailable(SurfaceTexture surfaceTexture) { + if (!isRunning()) return; ExecutorService ex = this.executor; if (ex == null) return; ex.execute(() -> draw(false));