diff --git a/encoder/src/main/java/com/pedro/encoder/input/sources/audio/AudioFileSource.kt b/encoder/src/main/java/com/pedro/encoder/input/sources/audio/AudioFileSource.kt index 62c70c94e..4959a1172 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/sources/audio/AudioFileSource.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/sources/audio/AudioFileSource.kt @@ -79,6 +79,7 @@ class AudioFileSource( override fun start(getMicrophoneData: GetMicrophoneData) { this.getMicrophoneData = getMicrophoneData + if (isRunning()) return audioDecoder.prepareAudio() audioDecoder.start() running = true @@ -89,6 +90,7 @@ class AudioFileSource( } override fun stop() { + this.getMicrophoneData = null running = false audioDecoder.stop() } diff --git a/encoder/src/main/java/com/pedro/encoder/input/sources/audio/BufferAudioSource.kt b/encoder/src/main/java/com/pedro/encoder/input/sources/audio/BufferAudioSource.kt index dbe18c868..fa7b318e4 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/sources/audio/BufferAudioSource.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/sources/audio/BufferAudioSource.kt @@ -90,6 +90,7 @@ class BufferAudioSource( override fun start(getMicrophoneData: GetMicrophoneData) { this.getMicrophoneData = getMicrophoneData + if (isRunning()) return synchronized(lock) { buffer.fill(0) readIndex = 0 @@ -221,6 +222,7 @@ class BufferAudioSource( } override fun stop() { + this.getMicrophoneData = null running = false runBlocking { job?.cancelAndJoin() } } diff --git a/encoder/src/main/java/com/pedro/encoder/input/sources/audio/InternalAudioSource.kt b/encoder/src/main/java/com/pedro/encoder/input/sources/audio/InternalAudioSource.kt index 920f0be78..8e77d8550 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/sources/audio/InternalAudioSource.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/sources/audio/InternalAudioSource.kt @@ -60,35 +60,31 @@ class InternalAudioSource( override fun start(getMicrophoneData: GetMicrophoneData) { this.getMicrophoneData = getMicrophoneData - if (!isRunning()) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - handlerThread = HandlerThread(TAG) - handlerThread.start() - MediaProjectionHandler.mediaProjection?.registerCallback(mediaProjectionCallback, Handler(handlerThread.looper)) - val config = AudioPlaybackCaptureConfiguration.Builder(MediaProjectionHandler.mediaProjection!!) - .addMatchingUsage(AudioAttributes.USAGE_MEDIA) - .addMatchingUsage(AudioAttributes.USAGE_GAME) - .addMatchingUsage(AudioAttributes.USAGE_UNKNOWN).build() - try { - val result = microphone.createInternalMicrophone(config, sampleRate, isStereo, - echoCanceler, noiseSuppressor) - if (!result) throw IllegalArgumentException("Failed to create internal audio source") - } catch (e: UnsupportedOperationException) { - throw IllegalArgumentException("invalid MediaProjection used") - } - } else { - throw IllegalStateException("Using internal audio in a invalid Android version. Android 10+ is necessary") - } - microphone.start() + if (isRunning()) return + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + throw IllegalStateException("Using internal audio in a invalid Android version. Android 10+ is necessary") } + handlerThread = HandlerThread(TAG) + handlerThread.start() + MediaProjectionHandler.mediaProjection?.registerCallback(mediaProjectionCallback, Handler(handlerThread.looper)) + val config = AudioPlaybackCaptureConfiguration.Builder(MediaProjectionHandler.mediaProjection!!) + .addMatchingUsage(AudioAttributes.USAGE_MEDIA) + .addMatchingUsage(AudioAttributes.USAGE_GAME) + .addMatchingUsage(AudioAttributes.USAGE_UNKNOWN).build() + try { + val result = microphone.createInternalMicrophone(config, sampleRate, isStereo, + echoCanceler, noiseSuppressor) + if (!result) throw IllegalArgumentException("Failed to create internal audio source") + } catch (_: UnsupportedOperationException) { + throw IllegalArgumentException("invalid MediaProjection used") + } + microphone.start() } override fun stop() { - if (isRunning()) { - this.getMicrophoneData = null - microphone.stop() - handlerThread.quitSafely() - } + this.getMicrophoneData = null + microphone.stop() + handlerThread.quitSafely() } override fun isRunning(): Boolean = microphone.isRunning diff --git a/encoder/src/main/java/com/pedro/encoder/input/sources/audio/MicrophoneSource.kt b/encoder/src/main/java/com/pedro/encoder/input/sources/audio/MicrophoneSource.kt index a32dbd8ae..af7ab6349 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/sources/audio/MicrophoneSource.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/sources/audio/MicrophoneSource.kt @@ -52,23 +52,20 @@ class MicrophoneSource( override fun start(getMicrophoneData: GetMicrophoneData) { this.getMicrophoneData = getMicrophoneData - if (!isRunning()) { - val result = microphone.createMicrophone(audioSource, sampleRate, isStereo, echoCanceler, noiseSuppressor) - if (!result) { - throw IllegalArgumentException("Failed to create microphone audio source") - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - microphone.setPreferredDevice(preferredDevice) - } - microphone.start() + if (isRunning()) return + val result = microphone.createMicrophone(audioSource, sampleRate, isStereo, echoCanceler, noiseSuppressor) + if (!result) { + throw IllegalArgumentException("Failed to create microphone audio source") } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + microphone.setPreferredDevice(preferredDevice) + } + microphone.start() } override fun stop() { - if (isRunning()) { - this.getMicrophoneData = null - microphone.stop() - } + this.getMicrophoneData = null + microphone.stop() } override fun isRunning(): Boolean = microphone.isRunning diff --git a/encoder/src/main/java/com/pedro/encoder/input/sources/audio/MixAudioSource.kt b/encoder/src/main/java/com/pedro/encoder/input/sources/audio/MixAudioSource.kt index c6e68f8ec..85a30abe2 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/sources/audio/MixAudioSource.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/sources/audio/MixAudioSource.kt @@ -82,31 +82,29 @@ class MixAudioSource( override fun start(getMicrophoneData: GetMicrophoneData) { this.getMicrophoneData = getMicrophoneData - if (!isRunning()) { - handlerThread = HandlerThread(TAG) - handlerThread.start() - MediaProjectionHandler.mediaProjection?.registerCallback(mediaProjectionCallback, Handler(handlerThread.looper)) - val config = AudioPlaybackCaptureConfiguration.Builder(MediaProjectionHandler.mediaProjection!!) - .addMatchingUsage(AudioAttributes.USAGE_MEDIA) - .addMatchingUsage(AudioAttributes.USAGE_GAME) - .addMatchingUsage(AudioAttributes.USAGE_UNKNOWN).build() - val result = microphone.createMixMicrophone(microphoneAudioSource, config, sampleRate, isStereo, echoCanceler, noiseSuppressor) - if (!result) { - throw IllegalArgumentException("Failed to create microphone audio source") - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - microphone.setPreferredDevice(preferredDevice) - } - microphone.start() + if (isRunning()) return + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + throw IllegalStateException("Using mix audio in a invalid Android version. Android 10+ is necessary") } + handlerThread = HandlerThread(TAG) + handlerThread.start() + MediaProjectionHandler.mediaProjection?.registerCallback(mediaProjectionCallback, Handler(handlerThread.looper)) + val config = AudioPlaybackCaptureConfiguration.Builder(MediaProjectionHandler.mediaProjection!!) + .addMatchingUsage(AudioAttributes.USAGE_MEDIA) + .addMatchingUsage(AudioAttributes.USAGE_GAME) + .addMatchingUsage(AudioAttributes.USAGE_UNKNOWN).build() + val result = microphone.createMixMicrophone(microphoneAudioSource, config, sampleRate, isStereo, echoCanceler, noiseSuppressor) + if (!result) { + throw IllegalArgumentException("Failed to create microphone audio source") + } + microphone.setPreferredDevice(preferredDevice) + microphone.start() } override fun stop() { - if (isRunning()) { - getMicrophoneData = null - microphone.stop() - handlerThread.quitSafely() - } + getMicrophoneData = null + microphone.stop() + handlerThread.quitSafely() } override fun isRunning(): Boolean = microphone.isRunning diff --git a/encoder/src/main/java/com/pedro/encoder/input/sources/audio/SilenceAudioSource.kt b/encoder/src/main/java/com/pedro/encoder/input/sources/audio/SilenceAudioSource.kt index 892b95996..2c1e441cb 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/sources/audio/SilenceAudioSource.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/sources/audio/SilenceAudioSource.kt @@ -29,6 +29,7 @@ import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking +import kotlin.time.Duration.Companion.milliseconds /** * Created by pedro on 25/7/25. @@ -48,16 +49,18 @@ class SilenceAudioSource: AudioSource(), GetMicrophoneData { override fun start(getMicrophoneData: GetMicrophoneData) { this.getMicrophoneData = getMicrophoneData + if (isRunning()) return running = true job = CoroutineScope(Dispatchers.IO).launch { while (running) { getMicrophoneData.inputPCMData(Frame(buffer, 0, buffer.size, TimeUtils.getCurrentTimeMicro())) - delay(sleepTime) + delay(sleepTime.milliseconds) } } } override fun stop() { + this.getMicrophoneData = null running = false runBlocking { job?.cancelAndJoin() } } diff --git a/encoder/src/main/java/com/pedro/encoder/input/sources/video/BitmapSource.kt b/encoder/src/main/java/com/pedro/encoder/input/sources/video/BitmapSource.kt index 613322711..73357d3b7 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/sources/video/BitmapSource.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/sources/video/BitmapSource.kt @@ -27,6 +27,7 @@ import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking +import kotlin.time.Duration.Companion.milliseconds /** * Created by pedro on 19/3/24. @@ -44,6 +45,8 @@ class BitmapSource(private val bitmap: Bitmap): VideoSource() { } override fun start(surfaceTexture: SurfaceTexture) { + this.surfaceTexture = surfaceTexture + if (isRunning()) return val scaledBitmap = Bitmap.createScaledBitmap(bitmap, width, height, true) surfaceTexture.setDefaultBufferSize(width, height) surface = Surface(surfaceTexture) @@ -56,7 +59,7 @@ class BitmapSource(private val bitmap: Bitmap): VideoSource() { surface?.unlockCanvasAndPost(canvas) } catch (_: Exception) { } //sleep to emulate fps - delay(1000 / fps.toLong()) + delay((1000 / fps.toLong()).milliseconds) } } } diff --git a/encoder/src/main/java/com/pedro/encoder/input/sources/video/BufferVideoSource.kt b/encoder/src/main/java/com/pedro/encoder/input/sources/video/BufferVideoSource.kt index 3aed88868..e01c66e20 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/sources/video/BufferVideoSource.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/sources/video/BufferVideoSource.kt @@ -88,6 +88,8 @@ class BufferVideoSource( } override fun start(surfaceTexture: SurfaceTexture) { + this.surfaceTexture = surfaceTexture + if (isRunning()) return scope = CoroutineScope(Dispatchers.IO) queue.clear() running = true @@ -116,6 +118,7 @@ class BufferVideoSource( decoder.stop() queue.clear() surface?.release() + surface = null } override fun release() { } diff --git a/encoder/src/main/java/com/pedro/encoder/input/sources/video/Camera1Source.kt b/encoder/src/main/java/com/pedro/encoder/input/sources/video/Camera1Source.kt index 03278acf4..2a055064c 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/sources/video/Camera1Source.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/sources/video/Camera1Source.kt @@ -50,16 +50,15 @@ class Camera1Source(context: Context): VideoSource() { override fun start(surfaceTexture: SurfaceTexture) { this.surfaceTexture = surfaceTexture - if (!isRunning()) { - surfaceTexture.setDefaultBufferSize(width, height) - camera.setSurfaceTexture(surfaceTexture) - camera.start(facing, width, height, fps) - camera.setPreviewOrientation(90) // necessary to use the same orientation than camera2 - } + if (isRunning()) return + surfaceTexture.setDefaultBufferSize(width, height) + camera.setSurfaceTexture(surfaceTexture) + camera.start(facing, width, height, fps) + camera.setPreviewOrientation(90) // necessary to use the same orientation than camera2 } override fun stop() { - if (isRunning()) camera.stop() + camera.stop() } override fun release() {} diff --git a/encoder/src/main/java/com/pedro/encoder/input/sources/video/Camera2Source.kt b/encoder/src/main/java/com/pedro/encoder/input/sources/video/Camera2Source.kt index ae903c28a..353448dce 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/sources/video/Camera2Source.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/sources/video/Camera2Source.kt @@ -55,15 +55,14 @@ class Camera2Source(context: Context): VideoSource() { override fun start(surfaceTexture: SurfaceTexture) { this.surfaceTexture = surfaceTexture - if (!isRunning()) { - surfaceTexture.setDefaultBufferSize(width, height) - camera.prepareCamera(surfaceTexture, width, height, fps, facing) - camera.openCameraFacing(facing) - } + if (isRunning()) return + surfaceTexture.setDefaultBufferSize(width, height) + camera.prepareCamera(surfaceTexture, width, height, fps, facing) + camera.openCameraFacing(facing) } override fun stop() { - if (isRunning()) camera.closeCamera() + camera.closeCamera() } override fun release() {} diff --git a/encoder/src/main/java/com/pedro/encoder/input/sources/video/ScreenSource.kt b/encoder/src/main/java/com/pedro/encoder/input/sources/video/ScreenSource.kt index a144943df..2a067b210 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/sources/video/ScreenSource.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/sources/video/ScreenSource.kt @@ -43,6 +43,7 @@ class ScreenSource @JvmOverloads constructor( private val TAG = "ScreenSource" private var virtualDisplay: VirtualDisplay? = null + private var surface: Surface? = null private var handlerThread = HandlerThread(TAG) private val mediaProjectionCallback = mediaProjectionCallback ?: object : MediaProjection.Callback() {} private val virtualDisplayCallback = virtualDisplayCallback ?: object : VirtualDisplay.Callback() {} @@ -58,32 +59,41 @@ class ScreenSource @JvmOverloads constructor( override fun start(surfaceTexture: SurfaceTexture) { this.surfaceTexture = surfaceTexture - if (!isRunning()) { - val flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR - //Adapt MediaProjection render to stream resolution - val shouldRotate = rotation == 90 || rotation == 270 - val displayWidth = if (shouldRotate) height else width - val displayHeight = if (shouldRotate) width else height - if (shouldRotate) surfaceTexture.setDefaultBufferSize(height, width) - handlerThread = HandlerThread(TAG) - handlerThread.start() - MediaProjectionHandler.mediaProjection?.registerCallback(mediaProjectionCallback, Handler(handlerThread.looper)) - virtualDisplay = MediaProjectionHandler.mediaProjection?.createVirtualDisplay(TAG, + if (isRunning()) return + val flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR + //Adapt MediaProjection render to stream resolution + val shouldRotate = rotation == 90 || rotation == 270 + val displayWidth = if (shouldRotate) height else width + val displayHeight = if (shouldRotate) width else height + if (shouldRotate) surfaceTexture.setDefaultBufferSize(height, width) + val handlerThread = HandlerThread(TAG) + handlerThread.start() + MediaProjectionHandler.mediaProjection?.registerCallback(mediaProjectionCallback, Handler(handlerThread.looper)) + val surface = Surface(surfaceTexture) + try { + val virtualDisplay = MediaProjectionHandler.mediaProjection?.createVirtualDisplay(TAG, displayWidth, displayHeight, dpi, flags, - Surface(surfaceTexture), virtualDisplayCallback, Handler(handlerThread.looper) + surface, virtualDisplayCallback, Handler(handlerThread.looper) ) if (virtualDisplay == null) { throw IllegalArgumentException("Failed to create internal virtual display") } + this.virtualDisplay = virtualDisplay + } catch (e: Exception) { + surface.release() + handlerThread.quitSafely() + throw e } + this.surface = surface + this.handlerThread = handlerThread } override fun stop() { - if (isRunning()) { - virtualDisplay?.release() - virtualDisplay = null - handlerThread.quitSafely() - } + virtualDisplay?.release() + virtualDisplay = null + surface?.release() + surface = null + handlerThread.quitSafely() } override fun release() { diff --git a/encoder/src/main/java/com/pedro/encoder/input/sources/video/VideoFileSource.kt b/encoder/src/main/java/com/pedro/encoder/input/sources/video/VideoFileSource.kt index a9f0c5bd3..f84937eef 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/sources/video/VideoFileSource.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/sources/video/VideoFileSource.kt @@ -46,6 +46,7 @@ class VideoFileSource( onFinish(true) } } + private var surface: Surface? = null private var running = false private var videoDecoder = VideoDecoder(videoDecoderInterface, decoderInterface) @@ -63,14 +64,19 @@ class VideoFileSource( override fun start(surfaceTexture: SurfaceTexture) { this.surfaceTexture = surfaceTexture - videoDecoder.prepareVideo(Surface(surfaceTexture)) + if (isRunning()) return + val surface = Surface(surfaceTexture) + videoDecoder.prepareVideo(surface) videoDecoder.start() running = true + this.surface = surface } override fun stop() { running = false videoDecoder.stop() + this.surface?.release() + this.surface = null } override fun release() { @@ -93,7 +99,7 @@ class VideoFileSource( videoDecoder.isLoopMode = enabled } - @Throws(IOException::class) + @Throws(IOException::class, IllegalStateException::class) fun replaceFile(context: Context, uri: Uri) { val wasRunning = videoDecoder.isRunning val videoDecoder = VideoDecoder(videoDecoderInterface, decoderInterface) @@ -102,8 +108,12 @@ class VideoFileSource( this.videoDecoder.stop() this.videoDecoder = videoDecoder if (wasRunning) { - videoDecoder.prepareVideo(Surface(surfaceTexture)) + val surfaceTexture = this.surfaceTexture ?: throw IllegalStateException("SurfaceTexture was not available") + val surface = Surface(surfaceTexture) + videoDecoder.prepareVideo(surface) videoDecoder.start() + this.surface?.release() + this.surface = surface } } diff --git a/encoder/src/main/java/com/pedro/encoder/input/video/Camera2ApiManager.kt b/encoder/src/main/java/com/pedro/encoder/input/video/Camera2ApiManager.kt index 74db8a2db..d3676620a 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/video/Camera2ApiManager.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/video/Camera2ApiManager.kt @@ -74,7 +74,7 @@ class Camera2ApiManager(context: Context) { private val TAG = "Camera2ApiManager" private var cameraDevice: CameraDevice? = null - private var surfaceEncoder = Surface(SurfaceTexture(-1).apply { release() }) //input surfaceEncoder from videoEncoder + private var surfaceEncoder: Surface? = null private val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager private var cameraCaptureSession: CameraCaptureSession? = null var isPrepared: Boolean = false @@ -129,6 +129,7 @@ class Camera2ApiManager(context: Context) { fun prepareCamera(surfaceTexture: SurfaceTexture, width: Int, height: Int, fps: Int) { val optimalResolution = requiredSize ?: getOptimalResolution(Size(width, height), getCameraResolutions(facing)) Log.i(TAG, "optimal resolution set to: " + optimalResolution.width + "x" + optimalResolution.height) + if (!isRunning) this.surfaceEncoder?.release() surfaceTexture.setDefaultBufferSize(optimalResolution.width, optimalResolution.height) this.surfaceEncoder = Surface(surfaceTexture) this.fps = fps @@ -154,8 +155,12 @@ class Camera2ApiManager(context: Context) { private fun startPreview(cameraDevice: CameraDevice, handler: Handler) { try { + val surface = surfaceEncoder ?: run { + cameraCallbacks?.onCameraError("You need prepare camera before open it") + return + } val listSurfaces = mutableListOf() - listSurfaces.add(surfaceEncoder) + listSurfaces.add(surface) imageReader?.let { listSurfaces.add(it.surface) } val captureRequest = drawSurface(cameraDevice, listSurfaces) createCaptureSession( @@ -868,9 +873,10 @@ class Camera2ApiManager(context: Context) { } fun reOpenCamera(cameraId: String) { - if (cameraDevice != null) { + val surface = surfaceEncoder + if (cameraDevice != null && surface != null) { closeCamera(false) - prepareCamera(surfaceEncoder, fps) + prepareCamera(surface, fps) openCameraId(cameraId) } } @@ -956,7 +962,8 @@ class Camera2ApiManager(context: Context) { cameraDevice?.close() cameraDevice = null if (resetSurface) { - surfaceEncoder = Surface(SurfaceTexture(-1).apply { release() }) + surfaceEncoder?.release() + surfaceEncoder = null builderInputSurface = null } isPrepared = false @@ -978,8 +985,9 @@ class Camera2ApiManager(context: Context) { } }, Handler(imageThread.looper)) this.imageReader = imageReader - if (wasRunning) { - prepareCamera(surfaceEncoder, fps) + val surface = surfaceEncoder + if (wasRunning && surface != null) { + prepareCamera(surface, fps) openLastCamera() } } @@ -990,8 +998,9 @@ class Camera2ApiManager(context: Context) { if (wasRunning) closeCamera(false) imageReader.close() this.imageReader = null - if (wasRunning) { - prepareCamera(surfaceEncoder, fps) + val surface = surfaceEncoder + if (wasRunning && surface != null) { + prepareCamera(surface, fps) openLastCamera() } } diff --git a/extra-sources/src/main/java/com/pedro/extrasources/CameraUvcSource.kt b/extra-sources/src/main/java/com/pedro/extrasources/CameraUvcSource.kt index 01039ffa3..b259a6c65 100644 --- a/extra-sources/src/main/java/com/pedro/extrasources/CameraUvcSource.kt +++ b/extra-sources/src/main/java/com/pedro/extrasources/CameraUvcSource.kt @@ -43,6 +43,7 @@ class CameraUvcSource: VideoSource() { override fun start(surfaceTexture: SurfaceTexture) { this.surfaceTexture = surfaceTexture + if (isRunning()) return surface = Surface(surfaceTexture) cameraHelper = CameraHelper() cameraHelper?.setStateCallback(stateCallback) diff --git a/extra-sources/src/main/java/com/pedro/extrasources/CameraXSource.kt b/extra-sources/src/main/java/com/pedro/extrasources/CameraXSource.kt index 8d524eb5b..fa80fcdd6 100644 --- a/extra-sources/src/main/java/com/pedro/extrasources/CameraXSource.kt +++ b/extra-sources/src/main/java/com/pedro/extrasources/CameraXSource.kt @@ -37,7 +37,6 @@ import androidx.camera.core.TorchState import androidx.camera.core.resolutionselector.ResolutionSelector import androidx.camera.core.resolutionselector.ResolutionStrategy import androidx.camera.lifecycle.ProcessCameraProvider -import androidx.core.content.ContextCompat import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry @@ -46,7 +45,7 @@ import com.pedro.encoder.input.sources.video.VideoSource import com.pedro.encoder.input.video.Camera2ApiManager.ImageCallback import com.pedro.encoder.input.video.Camera2ResolutionCalculator.getOptimalResolution import com.pedro.encoder.input.video.CameraHelper -import java.util.concurrent.ExecutionException +import java.util.concurrent.ExecutorService import java.util.concurrent.Executors /** @@ -64,6 +63,7 @@ class CameraXSource( private var facing = CameraSelector.LENS_FACING_BACK private var cameraSelectorBuilder: CameraSelector.Builder = CameraSelector.Builder() private var surface: Surface? = null + private var executor: ExecutorService? = null private var autoFocusEnabled = false private var autoExposureEnabled = false private var autoWhiteBalanceEnabled = false @@ -92,10 +92,11 @@ class CameraXSource( } override fun start(surfaceTexture: SurfaceTexture) { + this.surfaceTexture = surfaceTexture + if (isRunning()) return val facing = if (facing == CameraSelector.LENS_FACING_BACK) CameraHelper.Facing.BACK else CameraHelper.Facing.FRONT val optimalResolution = requiredSize ?: getOptimalResolution(Size(width, height), getCameraResolutions(facing).toTypedArray()) surfaceTexture.setDefaultBufferSize(optimalResolution.width, optimalResolution.height) - this.surfaceTexture = surfaceTexture lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START) val cameraSelector = cameraSelectorBuilder .requireLensFacing(this.facing) @@ -103,9 +104,13 @@ class CameraXSource( preview.setSurfaceProvider { val surface = Surface(surfaceTexture) - it.provideSurface(surface, Executors.newSingleThreadExecutor()) { + val executor = Executors.newSingleThreadExecutor() + it.provideSurface(surface, executor) { + surface.release() + executor.shutdown() } this.surface = surface + this.executor = executor } camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview) } @@ -115,9 +120,11 @@ class CameraXSource( camera?.let { cameraProvider.unbindAll() camera = null - surface?.release() - surface = null } + surface?.release() + surface = null + executor?.shutdown() + executor = null } override fun release() { diff --git a/extra-sources/src/main/java/com/pedro/extrasources/Media3AudioSource.kt b/extra-sources/src/main/java/com/pedro/extrasources/Media3AudioSource.kt index e2f40c3ea..08f41cd04 100644 --- a/extra-sources/src/main/java/com/pedro/extrasources/Media3AudioSource.kt +++ b/extra-sources/src/main/java/com/pedro/extrasources/Media3AudioSource.kt @@ -25,6 +25,7 @@ class Media3AudioSource( ): AudioSource() { private var player: ExoPlayer? = null + private var running = false private val processor = AudioBufferProcessor { bytes -> val frame = Frame(bytes, 0, bytes.size, TimeUtils.getCurrentTimeMicro()) @@ -52,6 +53,7 @@ class Media3AudioSource( override fun start(getMicrophoneData: GetMicrophoneData) { this.getMicrophoneData = getMicrophoneData + if (isRunning()) return player = ExoPlayer.Builder(context, TracksRenderersFactory(context, MediaFrame.Type.AUDIO, processor)).build().also { exoPlayer -> val mediaItem = MediaItem.fromUri(path) exoPlayer.setMediaItem(mediaItem) @@ -65,10 +67,12 @@ class Media3AudioSource( } }) player?.play() + running = true } override fun stop() { - getMicrophoneData = null + this.getMicrophoneData = null + running = false player?.release() player = null } @@ -77,7 +81,7 @@ class Media3AudioSource( } - override fun isRunning(): Boolean = player?.isPlaying == true + override fun isRunning(): Boolean = running fun getPlayer() = player } \ No newline at end of file diff --git a/extra-sources/src/main/java/com/pedro/extrasources/Media3VideoSource.kt b/extra-sources/src/main/java/com/pedro/extrasources/Media3VideoSource.kt index 8534db1ac..4b991d526 100644 --- a/extra-sources/src/main/java/com/pedro/extrasources/Media3VideoSource.kt +++ b/extra-sources/src/main/java/com/pedro/extrasources/Media3VideoSource.kt @@ -27,6 +27,7 @@ class Media3VideoSource( private var player: ExoPlayer? = null private var surface: Surface? = null + private var running = false override fun create(width: Int, height: Int, fps: Int, rotation: Int): Boolean { val mediaExtractor = Media3Extractor(context) @@ -41,6 +42,8 @@ class Media3VideoSource( } override fun start(surfaceTexture: SurfaceTexture) { + this.surfaceTexture = surfaceTexture + if (isRunning()) return surface = Surface(surfaceTexture) player = ExoPlayer.Builder(context, TracksRenderersFactory(context, MediaFrame.Type.VIDEO)).build().also { exoPlayer -> exoPlayer.setVideoSurface(surface) @@ -56,9 +59,11 @@ class Media3VideoSource( } }) player?.play() + running = true } override fun stop() { + running = false player?.release() player = null surface?.release() @@ -69,7 +74,7 @@ class Media3VideoSource( } - override fun isRunning(): Boolean = player?.isPlaying == true + override fun isRunning(): Boolean = running override fun getOrientationConfig() = OrientationConfig(forced = OrientationForced.LANDSCAPE)