从ipcam播放音频流的极端延迟

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从ipcam播放音频流的极端延迟相关的知识,希望对你有一定的参考价值。

(对不起,不是说英语的人,期待很多语法/句法错误)

我正在开发一款软件来管理D-Link Ip Cam(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 && clip.isRunning()) {}
            clip.stop();
            System.err.println("AUDIO PLAYER STOPPED");
        } catch (LineUnavailableException | IOException | UnsupportedAudioFileException e) {
            e.printStackTrace();
        }
    }

}

需要Authenticator部分,因为ip cam使用基本的http身份验证。

我已经读过某个地方,AudioSystem使用不同的算法进行多次传递以获得正确的算法,然后将流重置为开头,然后才开始播放。所以,正因为如此,也许AudioSystem有一些问题需要实现使用什么类型的编解码器(可能需要某种头)并且在开始播放音频之前花了相当长的时间。

值得一提的是,即使VLC也在努力跟上流媒体,在播放前最多会丢失8秒(8秒优于20秒)。 IpCam位于本地网络上。

我的代码有问题吗?有些方法我看不到?

真的不知道在哪里看这个。

我无法在这里或其他地方找到任何有意义的答案。

答案

在摆弄一个答案之后,我找到了解决方案,提供了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();
        }

    }

仍然可以进行一些优化,但它可以工作。

以上是关于从ipcam播放音频流的极端延迟的主要内容,如果未能解决你的问题,请参考以下文章

来自流的NAudio?播放音频

蓝牙延迟和 Android 音频提示

在 Java 中从麦克风播放音频时减少延迟

从 url 中提取音频片段并使用纯 Web Audio API 播放

如何从实时流中播放音频

aac 流的 HTML5 音频播放在 type="audio/mp4" 的 iOS 11 中不起作用