【问题标题】:Force Java Android Socket to Send Data Immediately强制 Java Android Socket 立即发送数据
【发布时间】:2016-10-25 05:11:11
【问题描述】:

作为一个爱好项目,我正在编写一个 android voip 客户端。将语音数据写入套接字 (Vars.mediaSocket) 时,很多时候,数据不会立即通过 wifi 发送出去,而是停止,然后它会立即发送 20 秒的语音。然后它将再次停止并等待 30 秒,然后发送 30 秒的语音。等待是不一致的,但过一段时间它会立即连续发送语音数据。我已经尝试了从使用 DataOutputStream 到设置套接字输出缓冲区大小、将 sendbuffer 大小设置为大、小,最后将语音数据从 32 字节块缓冲到 128 字节到 32kb 的所有内容。

        Utils.logcat(Const.LOGD, encTag, "MediaCodec encoder thread has started");
        isEncoding = true;
        byte[] amrbuffer = new byte[32];
        short[] wavbuffer = new short[160];
        int outputCounter = 0;

        //setup the wave audio recorder. since it is released and restarted, it needs to be setup here and not onCreate
        wavRecorder = null; //remove pointer to the old recorder for safety
        wavRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLESWAV, AudioFormat.CHANNEL_IN_MONO, FORMAT, 160);
        wavRecorder.startRecording();
        AmrEncoder.init(0);

        while(!micMute)
        {
            int totalRead = 0, dataRead;
            while(totalRead < 160)
            {//although unlikely to be necessary, buffer the mic input
                dataRead = wavRecorder.read(wavbuffer, totalRead, 160 - totalRead);
                totalRead = totalRead + dataRead;
            }
            int encodeLength = AmrEncoder.encode(AmrEncoder.Mode.MR122.ordinal(), wavbuffer, amrbuffer);

            try
            {
                Vars.mediaSocket.getOutputStream().write(amrbuffer);
                Vars.mediaSocket.getOutputStream().flush();
            }
            catch (IOException i)
            {
                Utils.logcat(Const.LOGE, encTag, "Cannot send amr out the media socket");
                Utils.dumpException(tag, i);
            }

我有什么遗漏吗?为了模拟第二部手机,我有另一个客户端,它只是简单地读取语音数据,将其丢弃,然后在循环中再次读取。我可以在模拟的第二部手机中确认,当真手机停止发送语音时,模拟手机的socket.read挂起,直到真手机再次开始发送语音。

我真的希望不必为套接字编写 jni,因为我对此一无所知,并希望我可以将应用程序编写为标准的 java 应用程序。

CASE CLOSED:原来是服务器端的错误,但简化回归基础的建议仍然是个好主意。

【问题讨论】:

  • 不要刷新内部循环。如果没有缓冲它是没有意义的(在这种情况下),如果有缓冲它会破坏它。

标签: java android sockets


【解决方案1】:

通过在写入任何数据之前读取大量数据,您自己增加了大部分延迟。您应该只使用标准的 Java 复制循环:

byte[] buffer = new byte[8192];
int count;
while ((count = in.read(buffer)) > 0)
{
    out.write(buffer, 0, count);
}

您需要对其进行调整以合并您的编解码器步骤。请注意,您不需要整个输入大小的缓冲区。您可以调整其大小以适合自己,但 8192 是一个很好的起点。您可以将其增加到 32k,但不要减少它。如果您的编解码器需要固定大小的块中的数据,请使用该大小的缓冲区和DataInputStream.readFully()。但缓冲区越大,延迟越长。

编辑您的代码的具体问题:

byte[] amrbuffer = new byte[AMRBUFFERSIZE];
byte[] outputbuffer = new byte [outputBufferSize];

删除(见下文)。

short[] wavbuffer = new short[WAVBUFFERSIZE];
int outputCounter = 0;

删除outputCounter

        //setup the wave audio recorder. since it is released and restarted, it needs to be setup here and not onCreate
        wavRecorder = null; //remove pointer to the old recorder for safety

毫无意义。删除。

        wavRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLESWAV, AudioFormat.CHANNEL_IN_MONO, FORMAT, WAVBUFFERSIZE);
        wavRecorder.startRecording();
        AmrEncoder.init(0);

好的。

        try
        {
            Vars.mediaSocket.setSendBufferSize(outputBufferSize);
        }
        catch (SocketException e)
        {
            e.printStackTrace();
        }

毫无意义。消除。套接字发送缓冲区应尽可能大。除非您知道它的默认大小是 outputBufferSize,否则这样做没有任何好处。无论如何,我们将完全摆脱outputBuffer

        while(!micMute)
        {
            int totalRead = 0, dataRead;
            while(totalRead < WAVBUFFERSIZE)
            {//although unlikely to be necessary, buffer the mic input
                dataRead = wavRecorder.read(wavbuffer, totalRead, WAVBUFFERSIZE - totalRead);
                totalRead = totalRead + dataRead;
            }
            int encodeLength = AmrEncoder.encode(AmrEncoder.Mode.MR122.ordinal(), wavbuffer, amrbuffer);

好的。

            if(outputCounter == outputBufferSize)
            {
                Utils.logcat(Const.LOGD, encTag, "Sending output buffer");
                try
                {
                    Vars.mediaSocket.getOutputStream().write(outputbuffer);
                    Vars.mediaSocket.getOutputStream().flush();
                }
                catch (IOException i)
                {
                    Utils.logcat(Const.LOGE, encTag, "Cannot send amr out the media socket");
                    Utils.dumpException(tag, i);
                }
                outputCounter = 0;
            }
            System.arraycopy(amrbuffer, 0, outputbuffer, outputCounter, encodeLength);
            outputCounter = outputCounter + encodeLength;
            Utils.logcat(Const.LOGD, encTag, "Output buffer fill: " + outputCounter);

删除以上所有内容并替换

        Vars.mediaSocket.getOutputStream().write(amrbuffer, 0, encodeLength);

这也意味着您可以按照承诺摆脱“outputBuffer”。

注意不要在循环内部刷新。事实上,刷新套接字输出流没有任何作用,但一般原则仍然成立。

【讨论】:

  • 我在直接out.write(buffer)之后添加了“写入前读取大量数据”。那是我的第一次尝试,仍然有这个问题。编解码器输出 32 字节的块。
  • 如果与我上面写的内容不一致,那就太复杂了。缓冲区越大,在写入任何内容之前尝试填充它的次数越多,延迟就会越大。这肯定很明显吗?
  • 非常感谢您的评论。真的超越了职责范围:)。我不确定你是否看到了我的编辑,但我将代码改回了我的原始实现,我认为它没有你不喜欢的不必要的东西。原来的实现还是有问题。
  • 经过更多调试后,我的服务器端代码出现了问题,但由于在设备端进行了很多试验和错误,我认为这是设备问题真的很像。我仍然在做你的简化建议:-)
猜你喜欢
  • 1970-01-01
  • 2015-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-03
  • 2013-11-01
  • 2016-10-16
  • 1970-01-01
相关资源
最近更新 更多