【发布时间】:2019-10-16 16:08:18
【问题描述】:
我的问题有点变了,请看下面的EDIT 2
我正在学习如何在 Android 上使用服务和录音。
我想创建一个应用程序,除了启动服务之外什么都不做:在请求权限(RECORD_AUDIO,INTERNET)后,应用程序只从MainActivity 调用startService()。
然后该服务将录制音频并将其流式传输到给定的 IP 地址。我从this question 的答案中获得了音频流服务器和客户端的灵感。到目前为止,我正在 Android Studio Emulator 中的 Android 6.0 上测试该应用程序。
这是我服务的onStartCommand() 方法。我创建了一个新线程,等待用户在权限请求对话框中单击“允许”。当我收到权限时,我初始化了AudioRecord,然后我在while-loop 中读取它并将数据发送出去。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Thread streamThread = new Thread(new Runnable() {
@Override
public void run() {
waitForPermissions();
try {
DatagramSocket socket = new DatagramSocket();
byte[] buffer = new byte[minBufSize];
DatagramPacket packet;
final InetAddress destination = InetAddress.getByName(address);
recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, channelConfig, audioFormat, minBufSize * 10);
recorder.startRecording();
while (status == true) {
minBufSize = recorder.read(buffer, 0, buffer.length);
packet = new DatagramPacket(buffer, buffer.length, destination, port);
socket.send(packet);
}
} catch (...) {...}
}
});
streamThread.start();
return START_STICKY;
}
在onDestroy() 中,我将标志status 设置为false,以便在onStartCommand() 中创建的线程中的循环终止。然后我释放recorder,以便它可以被初始化并再次使用。
@Override
public void onDestroy(){
status = false;
recorder.stop();
recorder.release();
super.onDestroy();
}
这个应用程序只能部分工作:
- 当我在电脑上启动监听服务器和模拟器中的应用程序时,我可以正常听到音频。
- 当我最小化应用程序(点击主页按钮)时,音频流仍然正常。
-
我的问题是,当我终止应用程序(在正在运行的应用程序屏幕中向右滑动)时,音频停止。在 Logcat 中,我可以看到服务已重新启动并照常运行(初始化
AudioRecord和所有内容,没有抛出异常),只是我什么都听不到。当我再次运行应用程序(并因此再次调用startService())时,我在 Logcat 中看到一个异常,告诉我AudioRecord启动失败,错误代码为 -38(意味着麦克风正在被前一个服务实例)。
我在这里做错了什么?为什么在我终止应用程序时服务正在运行,但音频没有流式传输?
非常感谢您的回答。
编辑:
我知道这种方法不适用于较新的 Android 版本。这个应用程序仅供私人使用,将在我的一部旧手机上运行,无论是 Android 5.1.1 还是 6.0。
阅读this answer 中的文章后,我将之前在onDestroy 中的内容移至onTaskRemoved。然而,一切都没有改变。
服务在重新启动后显然仍在发送一些数据包。我在 Wireshark 中查看了它们,数据包仍在离开设备,服务器仍在接收它们。数据包的有效载荷是非零的,对于每个数据包都是唯一的。
编辑 2:
我在 Android 应用程序中将缓冲区大小更改为 AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat) * 10,在服务器应用程序中将缓冲区大小更改为 AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat) * 20。现在,当我滑动应用程序时,Service 重新启动,然后我可以听到一些声音。但是重启后声音很粗,我不知道为什么。当我卸载应用程序,保持服务器运行,然后再次安装并运行应用程序时,它也会变得很粗。
所以问题可能出在服务器端。服务器可能有什么问题?我使用SourceDataLine 是否正常工作?服务器代码如下所示(取自here):
...
static int sampleRate = 8000;
static int bufferLen = minBufSize * 20;
public static void main(String args[]) throws Exception {
DatagramSocket serverSocket = new DatagramSocket(port);
byte[] receiveData = new byte[bufferLen];
format = new AudioFormat(sampleRate, 16, 1, true, false);
dataLineInfo = new DataLine.Info(SourceDataLine.class, format);
sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
sourceDataLine.open(format);
sourceDataLine.start();
FloatControl volumeControl = (FloatControl) sourceDataLine.getControl(FloatControl.Type.MASTER_GAIN);
volumeControl.setValue(volumeControl.getMaximum());
while (status == true) {
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
serverSocket.receive(receivePacket);
toSpeaker(receivePacket.getData(), receivePacket.getLength());
}
sourceDataLine.drain();
sourceDataLine.close();
}
public static void toSpeaker(byte soundbytes[], int length) {
try {
sourceDataLine.write(soundbytes, 0, length);
} catch (...) {...}
}
...
服务器的采样率与 Android 应用相同。格式为AudioFormat.ENCODING_PCM_16BIT。
【问题讨论】:
-
请记住,这种方法不适用于 Android 9 或更高版本。在这些 Android 版本上,您只能在应用处于前台时录制音频,或者从在活动时显示通知的前台服务录制音频。
-
@Michael 我知道这件事。我正在尝试编写的这个应用程序仅用于个人学习目的,并且针对的是我的 Android 6 旧手机。
标签: java android audio android-service audio-recording