From 46cf690ba3349814a051f8b5ddf767feedfcdf96 Mon Sep 17 00:00:00 2001 From: pi Date: Sat, 25 Oct 2025 10:28:54 +0200 Subject: [PATCH] klappt mit multiplen vom originalen child monitor --- app/build.gradle | 4 +- .../AudioCodecDefines.kt | 3 +- .../DiscoverActivity.kt | 2 +- .../childmonitor_multiple}/ListenActivity.kt | 4 +- .../childmonitor_multiple}/ListenService.kt | 2 +- .../childmonitor_multiple}/MonitorActivity.kt | 4 +- .../childmonitor_multiple}/MonitorService.kt | 110 ++++++++++++++---- .../childmonitor_multiple}/StartActivity.kt | 2 +- .../childmonitor_multiple}/VolumeHistory.kt | 2 +- .../childmonitor_multiple}/VolumeView.kt | 2 +- .../audio/G711UCodec.kt | 2 +- app/src/main/res/layout/activity_discover.xml | 2 +- .../res/layout/activity_discover_address.xml | 2 +- .../res/layout/activity_discover_mdns.xml | 2 +- app/src/main/res/layout/activity_listen.xml | 4 +- app/src/main/res/layout/activity_monitor.xml | 4 +- app/src/main/res/layout/activity_start.xml | 2 +- app/src/main/res/values-de/strings.xml | 2 +- app/src/main/res/values-nl/strings.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- 20 files changed, 113 insertions(+), 46 deletions(-) rename app/src/main/kotlin/{de/rochefort/childmonitor => com/example/childmonitor_multiple}/AudioCodecDefines.kt (92%) rename app/src/main/kotlin/{de/rochefort/childmonitor => com/example/childmonitor_multiple}/DiscoverActivity.kt (99%) rename app/src/main/kotlin/{de/rochefort/childmonitor => com/example/childmonitor_multiple}/ListenActivity.kt (97%) rename app/src/main/kotlin/{de/rochefort/childmonitor => com/example/childmonitor_multiple}/ListenService.kt (99%) rename app/src/main/kotlin/{de/rochefort/childmonitor => com/example/childmonitor_multiple}/MonitorActivity.kt (98%) rename app/src/main/kotlin/{de/rochefort/childmonitor => com/example/childmonitor_multiple}/MonitorService.kt (72%) rename app/src/main/kotlin/{de/rochefort/childmonitor => com/example/childmonitor_multiple}/StartActivity.kt (98%) rename app/src/main/kotlin/{de/rochefort/childmonitor => com/example/childmonitor_multiple}/VolumeHistory.kt (97%) rename app/src/main/kotlin/{de/rochefort/childmonitor => com/example/childmonitor_multiple}/VolumeView.kt (98%) rename app/src/main/kotlin/{de/rochefort/childmonitor => com/example/childmonitor_multiple}/audio/G711UCodec.kt (98%) diff --git a/app/build.gradle b/app/build.gradle index a6f97f1..e3b0bc4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,7 +5,7 @@ android { compileSdk 34 defaultConfig { - applicationId "de.rochefort.childmonitor" + applicationId "com.example.childmonitor_multiple" minSdkVersion 21 targetSdkVersion 34 versionCode 14 @@ -21,7 +21,7 @@ android { dependencies { } - namespace 'de.rochefort.childmonitor' + namespace 'com.example.childmonitor_multiple' lint { abortOnError true warning 'MissingTranslation' diff --git a/app/src/main/kotlin/de/rochefort/childmonitor/AudioCodecDefines.kt b/app/src/main/kotlin/com/example/childmonitor_multiple/AudioCodecDefines.kt similarity index 92% rename from app/src/main/kotlin/de/rochefort/childmonitor/AudioCodecDefines.kt rename to app/src/main/kotlin/com/example/childmonitor_multiple/AudioCodecDefines.kt index 3e75cf0..0c797e2 100644 --- a/app/src/main/kotlin/de/rochefort/childmonitor/AudioCodecDefines.kt +++ b/app/src/main/kotlin/com/example/childmonitor_multiple/AudioCodecDefines.kt @@ -14,10 +14,9 @@ * You should have received a copy of the GNU General Public License * along with Child Monitor. If not, see . */ -package de.rochefort.childmonitor +package com.example.childmonitor_multiple import android.media.AudioFormat -import de.rochefort.childmonitor.audio.G711UCodec object AudioCodecDefines { const val FREQUENCY = 8000 diff --git a/app/src/main/kotlin/de/rochefort/childmonitor/DiscoverActivity.kt b/app/src/main/kotlin/com/example/childmonitor_multiple/DiscoverActivity.kt similarity index 99% rename from app/src/main/kotlin/de/rochefort/childmonitor/DiscoverActivity.kt rename to app/src/main/kotlin/com/example/childmonitor_multiple/DiscoverActivity.kt index 1f46d62..56d2cb2 100644 --- a/app/src/main/kotlin/de/rochefort/childmonitor/DiscoverActivity.kt +++ b/app/src/main/kotlin/com/example/childmonitor_multiple/DiscoverActivity.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with Child Monitor. If not, see . */ -package de.rochefort.childmonitor +package com.example.childmonitor_multiple import android.app.Activity import android.content.Intent diff --git a/app/src/main/kotlin/de/rochefort/childmonitor/ListenActivity.kt b/app/src/main/kotlin/com/example/childmonitor_multiple/ListenActivity.kt similarity index 97% rename from app/src/main/kotlin/de/rochefort/childmonitor/ListenActivity.kt rename to app/src/main/kotlin/com/example/childmonitor_multiple/ListenActivity.kt index 34b8417..73b2f1e 100644 --- a/app/src/main/kotlin/de/rochefort/childmonitor/ListenActivity.kt +++ b/app/src/main/kotlin/com/example/childmonitor_multiple/ListenActivity.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with Child Monitor. If not, see . */ -package de.rochefort.childmonitor +package com.example.childmonitor_multiple import android.app.Activity import android.content.ComponentName @@ -28,7 +28,7 @@ import android.util.Log import android.widget.TextView import android.widget.Toast import androidx.core.content.ContextCompat -import de.rochefort.childmonitor.ListenService.ListenBinder +import com.example.childmonitor_multiple.ListenService.ListenBinder class ListenActivity : Activity() { // Don't attempt to unbind from the service unless the client has received some diff --git a/app/src/main/kotlin/de/rochefort/childmonitor/ListenService.kt b/app/src/main/kotlin/com/example/childmonitor_multiple/ListenService.kt similarity index 99% rename from app/src/main/kotlin/de/rochefort/childmonitor/ListenService.kt rename to app/src/main/kotlin/com/example/childmonitor_multiple/ListenService.kt index 5074ca8..9e5bc38 100644 --- a/app/src/main/kotlin/de/rochefort/childmonitor/ListenService.kt +++ b/app/src/main/kotlin/com/example/childmonitor_multiple/ListenService.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with Child Monitor. If not, see . */ -package de.rochefort.childmonitor +package com.example.childmonitor_multiple import android.app.Notification import android.app.NotificationChannel diff --git a/app/src/main/kotlin/de/rochefort/childmonitor/MonitorActivity.kt b/app/src/main/kotlin/com/example/childmonitor_multiple/MonitorActivity.kt similarity index 98% rename from app/src/main/kotlin/de/rochefort/childmonitor/MonitorActivity.kt rename to app/src/main/kotlin/com/example/childmonitor_multiple/MonitorActivity.kt index 9a48546..f87d778 100644 --- a/app/src/main/kotlin/de/rochefort/childmonitor/MonitorActivity.kt +++ b/app/src/main/kotlin/com/example/childmonitor_multiple/MonitorActivity.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with Child Monitor. If not, see . */ -package de.rochefort.childmonitor +package com.example.childmonitor_multiple import android.app.Activity import android.content.ComponentName @@ -28,7 +28,7 @@ import android.util.Log import android.widget.TextView import android.widget.Toast import androidx.core.content.ContextCompat -import de.rochefort.childmonitor.MonitorService.MonitorBinder +import com.example.childmonitor_multiple.MonitorService.MonitorBinder class MonitorActivity : Activity() { private val connection: ServiceConnection = object : ServiceConnection { diff --git a/app/src/main/kotlin/de/rochefort/childmonitor/MonitorService.kt b/app/src/main/kotlin/com/example/childmonitor_multiple/MonitorService.kt similarity index 72% rename from app/src/main/kotlin/de/rochefort/childmonitor/MonitorService.kt rename to app/src/main/kotlin/com/example/childmonitor_multiple/MonitorService.kt index 2719783..60b6d49 100644 --- a/app/src/main/kotlin/de/rochefort/childmonitor/MonitorService.kt +++ b/app/src/main/kotlin/com/example/childmonitor_multiple/MonitorService.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with Child Monitor. If not, see . */ -package de.rochefort.childmonitor +package com.example.childmonitor_multiple import android.app.Notification import android.app.NotificationChannel @@ -62,11 +62,11 @@ class MonitorService : Service() { val bufferSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding) val audioRecord: AudioRecord = try { AudioRecord( - MediaRecorder.AudioSource.MIC, - frequency, - channelConfiguration, - audioEncoding, - bufferSize + MediaRecorder.AudioSource.MIC, + frequency, + channelConfiguration, + audioEncoding, + bufferSize ) } catch (e: SecurityException) { // This should never happen, we asked for permission before @@ -91,6 +91,64 @@ class MonitorService : Service() { audioRecord.stop() } } + private fun handleMultiClientStreaming(serverSocket: ServerSocket, clients: MutableList) { + val frequency: Int = AudioCodecDefines.FREQUENCY + val channelConfiguration: Int = AudioCodecDefines.CHANNEL_CONFIGURATION_IN + val audioEncoding: Int = AudioCodecDefines.ENCODING + val bufferSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding) + + val audioRecord = AudioRecord( + MediaRecorder.AudioSource.MIC, + frequency, + channelConfiguration, + audioEncoding, + bufferSize + ) + + val pcmBufferSize = bufferSize * 2 + val pcmBuffer = ShortArray(pcmBufferSize) + val ulawBuffer = ByteArray(pcmBufferSize) + + try { + audioRecord.startRecording() + Log.i(TAG, "Mehrere Clients aktiv – Aufnahme gestartet.") + + while (!Thread.currentThread().isInterrupted && !serverSocket.isClosed) { + val read = audioRecord.read(pcmBuffer, 0, bufferSize) + if (read > 0) { + val encoded: Int = AudioCodecDefines.CODEC.encode(pcmBuffer, read, ulawBuffer, 0) + + synchronized(clients) { + val iterator = clients.iterator() + while (iterator.hasNext()) { + val client = iterator.next() + try { + val out = client.getOutputStream() + out.write(ulawBuffer, 0, encoded) + } catch (e: IOException) { + try { client.close() } catch (ignored: IOException) {} + iterator.remove() + Log.w(TAG, "Client getrennt: ${client.inetAddress}") + } + } + } + } + } + } catch (e: Exception) { + Log.e(TAG, "Streaming-Fehler", e) + } finally { + audioRecord.stop() + audioRecord.release() + synchronized(clients) { + clients.forEach { + try { it.close() } catch (_: IOException) {} + } + clients.clear() + } + Log.i(TAG, "Audioaufnahme beendet, alle Clients getrennt.") + } + } + override fun onCreate() { Log.i(TAG, "ChildMonitor start") @@ -130,15 +188,25 @@ class MonitorService : Service() { // Register the service so that parent devices can // locate the child device registerService(localPort) - serverSocket.accept().use { socket -> - Log.i(TAG, "Connection from parent device received") + val clients = mutableListOf() + +// Thread zum Akzeptieren neuer Clients + Thread { + try { + while (!Thread.currentThread().isInterrupted) { + val client = serverSocket.accept() + client.tcpNoDelay = true + synchronized(clients) { clients.add(client) } + Log.i(TAG, "Neuer Client verbunden: ${client.inetAddress}") + } + } catch (e: IOException) { + Log.e(TAG, "Client-Akzeptierungsfehler: ${e.message}") + } + }.start() + +// Aufnahme und Verteilung starten + handleMultiClientStreaming(serverSocket, clients) - // We now have a client connection. - // Unregister so no other clients will - // attempt to connect - unregisterService() - serviceConnection(socket) - } } } catch (e: Exception) { if (this.connectionToken == currentToken) { @@ -195,7 +263,7 @@ class MonitorService : Service() { } } nsdManager.registerService( - serviceInfo, NsdManager.PROTOCOL_DNS_SD, registrationListener) + serviceInfo, NsdManager.PROTOCOL_DNS_SD, registrationListener) } private fun unregisterService() { @@ -209,9 +277,9 @@ class MonitorService : Service() { private fun createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val serviceChannel = NotificationChannel( - CHANNEL_ID, - "Foreground Service Channel", - NotificationManager.IMPORTANCE_DEFAULT + CHANNEL_ID, + "Foreground Service Channel", + NotificationManager.IMPORTANCE_DEFAULT ) this.notificationManager.createNotificationChannel(serviceChannel) } @@ -222,9 +290,9 @@ class MonitorService : Service() { // Set the info for the views that show in the notification panel. val b = NotificationCompat.Builder(this, CHANNEL_ID) b.setSmallIcon(R.drawable.listening_notification) // the status icon - .setOngoing(true) - .setTicker(text) // the status text - .setContentTitle(text) // the label of the entry + .setOngoing(true) + .setTicker(text) // the status text + .setContentTitle(text) // the label of the entry return b.build() } diff --git a/app/src/main/kotlin/de/rochefort/childmonitor/StartActivity.kt b/app/src/main/kotlin/com/example/childmonitor_multiple/StartActivity.kt similarity index 98% rename from app/src/main/kotlin/de/rochefort/childmonitor/StartActivity.kt rename to app/src/main/kotlin/com/example/childmonitor_multiple/StartActivity.kt index e4b9d89..621aab7 100644 --- a/app/src/main/kotlin/de/rochefort/childmonitor/StartActivity.kt +++ b/app/src/main/kotlin/com/example/childmonitor_multiple/StartActivity.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with Child Monitor. If not, see . */ -package de.rochefort.childmonitor +package com.example.childmonitor_multiple import android.Manifest import android.app.Activity diff --git a/app/src/main/kotlin/de/rochefort/childmonitor/VolumeHistory.kt b/app/src/main/kotlin/com/example/childmonitor_multiple/VolumeHistory.kt similarity index 97% rename from app/src/main/kotlin/de/rochefort/childmonitor/VolumeHistory.kt rename to app/src/main/kotlin/com/example/childmonitor_multiple/VolumeHistory.kt index 8ee200d..872b134 100644 --- a/app/src/main/kotlin/de/rochefort/childmonitor/VolumeHistory.kt +++ b/app/src/main/kotlin/com/example/childmonitor_multiple/VolumeHistory.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with Child Monitor. If not, see . */ -package de.rochefort.childmonitor +package com.example.childmonitor_multiple import android.os.Handler import android.os.Looper diff --git a/app/src/main/kotlin/de/rochefort/childmonitor/VolumeView.kt b/app/src/main/kotlin/com/example/childmonitor_multiple/VolumeView.kt similarity index 98% rename from app/src/main/kotlin/de/rochefort/childmonitor/VolumeView.kt rename to app/src/main/kotlin/com/example/childmonitor_multiple/VolumeView.kt index 148a7c0..e7ff342 100644 --- a/app/src/main/kotlin/de/rochefort/childmonitor/VolumeView.kt +++ b/app/src/main/kotlin/com/example/childmonitor_multiple/VolumeView.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with Child Monitor. If not, see . */ -package de.rochefort.childmonitor +package com.example.childmonitor_multiple import android.content.Context import android.graphics.Canvas diff --git a/app/src/main/kotlin/de/rochefort/childmonitor/audio/G711UCodec.kt b/app/src/main/kotlin/com/example/childmonitor_multiple/audio/G711UCodec.kt similarity index 98% rename from app/src/main/kotlin/de/rochefort/childmonitor/audio/G711UCodec.kt rename to app/src/main/kotlin/com/example/childmonitor_multiple/audio/G711UCodec.kt index 4099a96..d571966 100644 --- a/app/src/main/kotlin/de/rochefort/childmonitor/audio/G711UCodec.kt +++ b/app/src/main/kotlin/com/example/childmonitor_multiple/audio/G711UCodec.kt @@ -16,7 +16,7 @@ * Taken from https://android.googlesource.com/platform/external/nist-sip/+/6f95fdeab4481188b6260041b41d1db12b101266/src/com/android/sip/media/G711UCodec.java * Adopted to the needs of the Child Monitor project. */ -package de.rochefort.childmonitor.audio +package com.example.childmonitor_multiple /** * G.711 codec. This class provides u-law conversion. diff --git a/app/src/main/res/layout/activity_discover.xml b/app/src/main/res/layout/activity_discover.xml index 9808555..dd317e9 100644 --- a/app/src/main/res/layout/activity_discover.xml +++ b/app/src/main/res/layout/activity_discover.xml @@ -8,7 +8,7 @@ android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" - tools:context="de.rochefort.childmonitor.MonitorActivity" > + tools:context="com.example.childmonitor_multiple.MonitorActivity" > + tools:context="com.example.childmonitor_multiple.MonitorActivity" > + tools:context="com.example.childmonitor_multiple.MonitorActivity" > + tools:context="com.example.childmonitor_multiple.MonitorActivity"> - diff --git a/app/src/main/res/layout/activity_monitor.xml b/app/src/main/res/layout/activity_monitor.xml index c8de77c..cabfd84 100644 --- a/app/src/main/res/layout/activity_monitor.xml +++ b/app/src/main/res/layout/activity_monitor.xml @@ -9,7 +9,7 @@ android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:keepScreenOn="true" - tools:context="de.rochefort.childmonitor.MonitorActivity" > + tools:context="com.example.childmonitor_multiple.MonitorActivity" > + tools:context="com.example.childmonitor_multiple.MonitorActivity" > + tools:context="com.example.childmonitor_multiple.StartActivity" >