#java #audio #audioinputstream
#java #Аудио #audioinputstream
Вопрос:
(извините, не говорящий по-английски, ожидайте много грамматических / синтаксических ошибок)
Я разрабатываю программное обеспечение для управления IP-камерой D-Link (серии DCS-xxxx и другие). Поскольку эта камера отображает аудиопоток (в некоторых моделях даже есть динамик для двунаправленной связи), я хотел бы воспроизвести его по запросу пользователя.
Все точки входа находятся за базовой аутентификацией http (но, как ни странно, я не могу использовать http:USER:PASS@192.168.1.100 , потому что я получаю 401).
Для этого я использую пакет javax.sound. *, но по какой-то причине звук начинает воспроизводиться после от 15 до 20 секунд, с общей задержкой 30-40 секунд РЕДАКТИРОВАТЬ: в среднем 45 секунд, но звук воспроизводится с самого начала, так что это еще хуже.
Это класс (минимальный, только для целей тестирования)
import java.io.IOException;
import java.net.Authenticator;
import java.net.MalformedURLException;
import java.net.PasswordAuthentication;
import java.net.URL;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
public class AudioPlayer implements Runnable{
private URL URL;
private String USERNAME;
private String PASSWORD;
private volatile boolean stop = false;
public AudioPlayer(String url, String user, String pass) throws MalformedURLException{
this.URL = new URL(url);
this.USERNAME = user;
this.PASSWORD = pass;
}
public void shutdown() {
stop = true;
}
@Override
public void run() {
Authenticator.setDefault (new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication (USERNAME, PASSWORD.toCharArray());
}
});
try {
Clip clip = AudioSystem.getClip();
AudioInputStream inputStream = AudioSystem.getAudioInputStream(URL);
clip.open(inputStream);
clip.start();
while(!stop amp;amp; clip.isRunning()) {}
clip.stop();
System.err.println("AUDIO PLAYER STOPPED");
} catch (LineUnavailableException | IOException | UnsupportedAudioFileException e) {
e.printStackTrace();
}
}
}
Часть аутентификации необходима, потому что ipcam использует базовую аутентификацию http.
Я где-то читал, что AudioSystem
сделать несколько проходов с разными алгоритмами, чтобы получить правильный, затем сбросить поток в начало и только после этого начать воспроизведение. Итак, из-за этого, возможно AudioSystem
, возникли некоторые проблемы с пониманием того, какой тип кодека использовать (возможно, нужен какой-то заголовок), и потратили довольно много времени, прежде чем начать воспроизведение звука.
Стоит знать, что даже VLC с трудом поспевает за потоковой передачей, теряя до 8 секунд перед воспроизведением (8 секунд намного лучше, чем 20). IPCam находится в локальной сети.
Что-то не так с моим кодом? Какой-то метод, который я не вижу?
На самом деле не знаю, где искать с этим.
Я не смог найти никакого значимого ответа здесь или где-либо еще.
Комментарии:
1. вы могли бы проверить, где происходит задержка, чтобы получить audioinputstream? клип?
2. Теперь еще хуже, почему idk: для выполнения требуется 3 секунды
AudioSystem.getAudioInputStream
, а для выполнения в среднем 45 секундclip.open(inputStream)
.
Ответ №1:
Повозившись с одним ответом, я нашел решение, которое обеспечивает задержку от 1 до 2 секунд (что соответствует той же задержке официального приложения или конфигурации веб-страницы, что в значительной степени идеально).
private void playStreamedURL() throws IOException {
//to avoid 401 error
Authenticator.setDefault (new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
//USERNAME and PASSWORD are defined in the class
return new PasswordAuthentication (USERNAME, PASSWORD.toCharArray());
}
});
AudioInputStream AIS = null;
SourceDataLine line = null;
try {
//get the input stream
AIS = AudioSystem.getAudioInputStream(this.URL);
//get the format, Very Important!
AudioFormat format = AIS.getFormat();
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
//create the output line
line = (SourceDataLine) AudioSystem.getLine(info);
//open the line with the specified format (other solution manually create the format
//and thats is a big problem because things like sampleRate aren't standard
//For example, the IpCam i use for testing use 11205 as sample rate.
line.open(format);
int framesize = format.getFrameSize();
//NOT_SPECIFIED is -1, wich create problem with the buffer definition, so it's revalued if necessary
if(framesize == AudioSystem.NOT_SPECIFIED)
framesize = 1;
//the buffer used to read and write bytes from stream to audio line
byte[] buffer = new byte[4 * 1024 * framesize];
int total = 0;
boolean playing = false;
int r, towrite, remaining;
while( (r = AIS.read(buffer, total, buffer.length - total)) >= 0 ) { //or !=-1
total = r;
//avoid start the line more than one time
if (!playing) {
line.start();
playing = true;
}
//actually play the sound (the frames in the buffer)
towrite = (total / framesize) * framesize;
line.write(buffer, 0, towrite);
//if some byte remain, overwrite them into the buffer and change the total
remaining = total - towrite;
if (remaining > 0)
System.arraycopy(buffer, towrite, buffer, 0, remaining);
total = remaining;
}
//line.drain() can be used, but it will consume the rest of the buffer.
line.stop();
line.flush();
} catch (UnsupportedAudioFileException | IOException | LineUnavailableException e) {
e.printStackTrace();
} finally {
if (line != null)
line.close();
if (AIS != null)
AIS.close();
}
}
Тем не менее, некоторая оптимизация может быть выполнена, но она работает.