Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class AudioFileSource(

override fun start(getMicrophoneData: GetMicrophoneData) {
this.getMicrophoneData = getMicrophoneData
if (isRunning()) return
audioDecoder.prepareAudio()
audioDecoder.start()
running = true
Expand All @@ -89,6 +90,7 @@ class AudioFileSource(
}

override fun stop() {
this.getMicrophoneData = null
running = false
audioDecoder.stop()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class BufferAudioSource(

override fun start(getMicrophoneData: GetMicrophoneData) {
this.getMicrophoneData = getMicrophoneData
if (isRunning()) return
synchronized(lock) {
buffer.fill(0)
readIndex = 0
Expand Down Expand Up @@ -221,6 +222,7 @@ class BufferAudioSource(
}

override fun stop() {
this.getMicrophoneData = null
running = false
runBlocking { job?.cancelAndJoin() }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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() }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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)
Expand All @@ -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)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -116,6 +118,7 @@ class BufferVideoSource(
decoder.stop()
queue.clear()
surface?.release()
surface = null
}

override fun release() { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
Expand All @@ -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() {
Expand Down
Loading
Loading