Merge pull request 'compatibility-to-childmonitor-clients' (#2) from compatibility-to-childmonitor-clients into master
Reviewed-on: #2
This commit was merged in pull request #2.
This commit is contained in:
@@ -12,20 +12,22 @@ import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import com.example.babyphone.R;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
private static final int SAMPLE_RATE = 44100;
|
||||
private static final int SAMPLE_RATE = 8000;
|
||||
private static final int PORT = 50005;
|
||||
private boolean isStreaming = false;
|
||||
private Thread recordingThread;
|
||||
@@ -42,23 +44,23 @@ public class MainActivity extends AppCompatActivity {
|
||||
Button startStreamButton = findViewById(R.id.startStreamButton);
|
||||
|
||||
startStreamButton.setOnClickListener(v -> {
|
||||
if (!isStreaming) {
|
||||
if (checkPermissions()) {
|
||||
startStreaming();
|
||||
startStreamButton.setText("Stop Streaming");
|
||||
if (!isStreaming) {
|
||||
if (checkPermissions()) {
|
||||
startStreaming();
|
||||
startStreamButton.setText("Stop Streaming");
|
||||
}
|
||||
} else {
|
||||
stopStreaming();
|
||||
startStreamButton.setText("Start Streaming");
|
||||
}
|
||||
} else {
|
||||
stopStreaming();
|
||||
startStreamButton.setText("Start Streaming");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private boolean checkPermissions() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(this,
|
||||
new String[]{Manifest.permission.RECORD_AUDIO}, 1);
|
||||
new String[]{Manifest.permission.RECORD_AUDIO}, 1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -78,65 +80,97 @@ public class MainActivity extends AppCompatActivity {
|
||||
|
||||
// Thread: Neue Clients annehmen
|
||||
clientListenerThread = new Thread(() -> {
|
||||
while (isStreaming) {
|
||||
try {
|
||||
Socket client = serverSocket.accept();
|
||||
synchronized (clients) {
|
||||
clients.add(client);
|
||||
}
|
||||
showToast("Neuer Client: " + client.getInetAddress());
|
||||
} catch (IOException e) {
|
||||
if (isStreaming) {
|
||||
showToast("Fehler beim Client-Connect: " + e.getMessage());
|
||||
while (isStreaming) {
|
||||
try {
|
||||
Socket client = serverSocket.accept();
|
||||
synchronized (clients) {
|
||||
clients.add(client);
|
||||
}
|
||||
showToast("Neuer Client: " + client.getInetAddress());
|
||||
} catch (IOException e) {
|
||||
if (isStreaming) {
|
||||
showToast("Fehler beim Client-Connect: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
clientListenerThread.start();
|
||||
|
||||
// Thread: Audio aufnehmen und an alle Clients senden
|
||||
// Thread: Audio aufnehmen und an alle Clients senden (mit µ-law)
|
||||
// Thread: Audio aufnehmen und an alle Clients senden (µ-Law, 8kHz)
|
||||
recordingThread = new Thread(() -> {
|
||||
int minBufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE,
|
||||
AudioFormat.CHANNEL_IN_MONO,
|
||||
AudioFormat.ENCODING_PCM_16BIT);
|
||||
int minBufferSize = AudioRecord.getMinBufferSize(8000, // neue Sample-Rate
|
||||
AudioFormat.CHANNEL_IN_MONO,
|
||||
AudioFormat.ENCODING_PCM_16BIT);
|
||||
|
||||
AudioRecord recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
|
||||
SAMPLE_RATE,
|
||||
AudioFormat.CHANNEL_IN_MONO,
|
||||
AudioFormat.ENCODING_PCM_16BIT,
|
||||
minBufferSize);
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(this,
|
||||
new String[]{Manifest.permission.RECORD_AUDIO},
|
||||
1001);
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] buffer = new byte[minBufferSize];
|
||||
AudioRecord recorder = null;
|
||||
try {
|
||||
recorder = new AudioRecord(
|
||||
MediaRecorder.AudioSource.MIC,
|
||||
44100, // Aufnahme weiterhin mit nativer Rate
|
||||
AudioFormat.CHANNEL_IN_MONO,
|
||||
AudioFormat.ENCODING_PCM_16BIT,
|
||||
minBufferSize
|
||||
);
|
||||
} catch (SecurityException e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
recorder.startRecording();
|
||||
byte[] pcmBuffer = new byte[minBufferSize];
|
||||
recorder.startRecording();
|
||||
|
||||
while (isStreaming) {
|
||||
int read = recorder.read(buffer, 0, buffer.length);
|
||||
if (read > 0) {
|
||||
synchronized (clients) {
|
||||
Iterator<Socket> iterator = clients.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Socket client = iterator.next();
|
||||
try {
|
||||
OutputStream out = client.getOutputStream();
|
||||
out.write(buffer, 0, read);
|
||||
} catch (IOException e) {
|
||||
// Verbindung verloren – entfernen
|
||||
// Buffer für Resampling
|
||||
short[] pcmSamples = new short[minBufferSize / 2];
|
||||
|
||||
while (isStreaming) {
|
||||
int read = recorder.read(pcmBuffer, 0, pcmBuffer.length);
|
||||
if (read > 0) {
|
||||
// PCM Byte-Array → short[]
|
||||
ByteBuffer.wrap(pcmBuffer, 0, read).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(pcmSamples, 0, read / 2);
|
||||
|
||||
// Resample 44.1kHz -> 8kHz (einfaches Downsampling, z.B. jede 5.5. Probe nehmen)
|
||||
short[] downsampled = new short[(int) (pcmSamples.length * 8000f / 44100f)];
|
||||
for (int i = 0; i < downsampled.length; i++) {
|
||||
downsampled[i] = pcmSamples[(int) (i * 44100f / 8000f)];
|
||||
}
|
||||
|
||||
// PCM → µ-Law
|
||||
byte[] muLawData = new byte[downsampled.length];
|
||||
for (int i = 0; i < downsampled.length; i++) {
|
||||
muLawData[i] = linearToMuLawSample(downsampled[i]);
|
||||
}
|
||||
|
||||
// Senden an alle Clients
|
||||
synchronized (clients) {
|
||||
Iterator<Socket> iterator = clients.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Socket client = iterator.next();
|
||||
try {
|
||||
client.close();
|
||||
} catch (IOException ignored) {
|
||||
OutputStream out = client.getOutputStream();
|
||||
out.write(muLawData);
|
||||
} catch (IOException e) {
|
||||
try { client.close(); } catch (IOException ignored) {}
|
||||
iterator.remove();
|
||||
showToast("Client getrennt");
|
||||
}
|
||||
iterator.remove();
|
||||
showToast("Client getrennt");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
recorder.stop();
|
||||
recorder.release();
|
||||
});
|
||||
recorder.stop();
|
||||
recorder.release();
|
||||
});
|
||||
|
||||
|
||||
recordingThread.start();
|
||||
}
|
||||
@@ -147,11 +181,10 @@ public class MainActivity extends AppCompatActivity {
|
||||
// Schließe alle Clients
|
||||
synchronized (clients) {
|
||||
for (Socket client : clients) {
|
||||
try {
|
||||
client.close();
|
||||
} catch (IOException ignored) {
|
||||
try {
|
||||
client.close();
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
}
|
||||
clients.clear();
|
||||
}
|
||||
|
||||
@@ -168,4 +201,30 @@ public class MainActivity extends AppCompatActivity {
|
||||
private void showToast(String msg) {
|
||||
runOnUiThread(() -> Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show());
|
||||
}
|
||||
|
||||
// ========================
|
||||
// PCM16 → µ-Law Umwandlung
|
||||
// ========================
|
||||
private static byte linearToMuLawSample(short sample) {
|
||||
final int MU = 255;
|
||||
int sign = (sample >> 8) & 0x80;
|
||||
if (sign != 0) sample = (short) -sample;
|
||||
if (sample > 32635) sample = 32635;
|
||||
sample = (short) (sample + 132);
|
||||
int exponent = 7;
|
||||
for (int expMask = 0x4000; (sample & expMask) == 0 && exponent > 0; exponent--, expMask >>= 1) {}
|
||||
int mantissa = (sample >> ((exponent == 0) ? 4 : (exponent + 3))) & 0x0F;
|
||||
int muLawByte = ~(sign | (exponent << 4) | mantissa);
|
||||
return (byte) muLawByte;
|
||||
}
|
||||
|
||||
private static byte[] pcm16ToMuLaw(byte[] pcmData, int length) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(length / 2);
|
||||
ByteBuffer bb = ByteBuffer.wrap(pcmData, 0, length).order(ByteOrder.LITTLE_ENDIAN);
|
||||
while (bb.remaining() > 1) {
|
||||
short sample = bb.getShort();
|
||||
baos.write(linearToMuLawSample(sample));
|
||||
}
|
||||
return baos.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user