【问题标题】:Stuttering Playback when playing a stream received via UDP socket播放通过 UDP 套接字接收的流时出现口吃播放
【发布时间】:2013-05-19 23:03:10
【问题描述】:

我想将音频流从 PC(C++ 应用程序,使用 FMOD-API 解码音频数据并通过 UDP 套接字发送)发送到 Android 设备。通信已经开始,我可以在 android 上听到“声音”(100 毫秒的声音,然后是 900 毫秒的静音,交替)。

我不知道为什么声音会断断续续 - 在 PC 上,相同的音频流可以很好地播放质量。我认为问题出在android上..

代码如下:

DatagramSocket  sock = new DatagramSocket(12345);
byte            []bSockBuffer = new byte[1024];
byte            []bRecvBufTmp;
int             iAudioBufSize, iCurAudioBufPos = 0;

sock.setReceiveBufferSize(bSockBuffer.length);

// Audio Stream initialisieren:
iAudioBufSize       = AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);

AudioTrack track    = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_STEREO,
                                    AudioFormat.ENCODING_PCM_16BIT, iAudioBufSize, AudioTrack.MODE_STREAM);

track.play();

while (true)
{
    DatagramPacket pack = new DatagramPacket(bSockBuffer, bSockBuffer.length);

    // Paket empfangen:
    sock.receive(pack);

    track.write(pack.getData(), 0, pack.getLength());
}

我确定正确设置了“AudioTrack”对象,设置与我在 c++ 应用程序中的设置进行比较。

另一个步骤是在临时“byte[]”变量中预缓冲接收到的套接字数据,并在达到缓冲区“iAudioBufSize”的大小时将其写入 AudioTrack 对象。 这没有帮助。

有什么想法吗? 谢谢

[编辑] C++ 应用程序代码,使用了 FMOD API 示例的示例“手动解码”:

FMOD_RESULT F_CALLBACK pcmreadcallback(FMOD_SOUND *sound, void *data, unsigned int datalen)
{
    CCtrlSocket     *cClientTmp = /* Obtaining target client sock here */;
    FMOD_RESULT     result;
    unsigned int    read, uSentTmp, uSizeTmp;

    EnterCriticalSection(&decodecrit);

    if (!decodesound)
        return (FMOD_ERR_FILE_EOF);

    result = decodesound->readData(data, datalen, &read);

    if (result == FMOD_ERR_FILE_EOF)
    {
        // Handle looping:
        decodesound->seekData(0);

        datalen -= read;

        result = decodesound->readData((char*) data + read, datalen, &read);
    }

    // Split package in multiple parts:
    uSentTmp = 0;

    do
    {
        uSizeTmp = (read - uSentTmp);

        if (uSizeTmp > 1024)
            uSizeTmp = 1024;

        uSentTmp += cClientTmp->SendAudioData((char*) data + uSentTmp, uSizeTmp);

    } while (uSentTmp < read);

    LeaveCriticalSection(&decodecrit);

    return (FMOD_OK);
}

【问题讨论】:

  • 您知道 UDP 数据报可能大于 1024 字节,对吧?实际数据包大小是多少?
  • 我已经将我的 c++ 应用程序中每个发送的包都切割成 1024 字节的片段。我还比较了我发送和接收的尺寸 - 它很合身。
  • 您应该尝试更改缓冲区大小。 socket 和 AudioTrack 播放器都将当前值乘以 2 或 3 并检查是否有变化
  • 我已经尝试过尺寸..
  • 改变 AudioTrack-buffer 的大小会改变静音的持续时间。我还尝试了 Thread.sleep([various timings]);在 track.write() 之后; - 改变了沉默的持续时间......

标签: android sockets stream udp audiotrack


【解决方案1】:

我已经解决了这个问题。 混乱是日志文件中的一个条目,它花费了大量时间来创建滞后:(

现在我可以在我的 android 客户端上听到流媒体音乐了。但仍有一些滞后。我已经为套接字和 AudioTrack 缓冲区试验了很多值。 我比较了发送和接收的字节数:在 20 秒内发送 9170000 字节的数据导致在 android 设备上接收 8120000 字节。起初,流快速播放 3 秒(这意味着缓冲区已满?)。 30 秒后流滞后(这意味着缓冲区为空?)。 总体来说音乐质量很好,但是一直有咝咝作响的声音(说明丢失了socket包?)。

我的“PlaybackStart()”函数已更改 - 我不再使用 PCM 读取回调:

FMOD_RESULT CAudioStream::PlaybackStart()
{
    CCtrlSocket     *cClientTmp;
    unsigned int    read, uSentTmp, uSizeTmp;
    FMOD_RESULT result;

    result = system->createStream("C:\\test.mp3", FMOD_OPENONLY | FMOD_ACCURATETIME, 0, &sound);
    if(result != FMOD_OK)
        return (result);

    int iChannels, iBits;
    FMOD_SOUND_FORMAT fFormat;
    FMOD_SOUND_TYPE fType;

    result = sound->getFormat(&fType, &fFormat, &iChannels, &iBits);
    if(result != FMOD_OK)
        return (result);



    void *data;
    unsigned int length = 0;
    int iSampleSec          = 1;                // Playtime
    int iSampleSize         = (44100 * 2 * sizeof(signed short) * iSampleSec);
    int iSleep              = 6;                // Sleep after sending a package
    DWORD   dSleepTotal;


    result = sound->getLength(&length, FMOD_TIMEUNIT_PCMBYTES);
    if(result != FMOD_OK)
        return (result);

    data = malloc(iSampleSize);
    if (!data)
        return (FMOD_RESULT_FORCEINT);

    cClientTmp = (CCtrlSocket*) CCtrlSocket::cServerSock.GetClientSock(CCtrlSocket::cServerSock.GetClientSockCount() - 1);

    do
    {
        result = sound->readData((char*) data, iSampleSize, &read);

        if ((result != FMOD_OK) && (result != FMOD_ERR_FILE_EOF))
            ASSERT(FALSE);

        else if (read > 0)
        {
            dSleepTotal = 0;

            for (int i = 0; i < read; i += NET_SVR_AUDIO_BUFFER)
            {
                // MIN_VAL_LIMITED      ((MIN_VAL(VAL1, VAL2) <= LIMIT) ? LIMIT : MIN_VAL(VAL1, VAL2))
                cClientTmp->SendAudioData((char*) data + i, MIN_VAL_LIMITED(NET_SVR_AUDIO_BUFFER, (read - i), 0));

                // Sleep after sending every package:
                Sleep(iSleep);
                dSleepTotal += iSleep;
            }

            if (dSleepTotal < (iSampleSec * 1000))
            {
                dSleepTotal = (iSampleSec * 1000) - dSleepTotal;

                // Sleep after sending every second playtime:
                Sleep(dSleepTotal);
            }
        }

    } while (read > 0);



    result = sound->release();
    if(result != FMOD_OK)
        return (result);

    result = system->close();
    if(result != FMOD_OK)
        return (result);

    result = system->release();
    if(result != FMOD_OK)
        return (result);

    return (result);
}

我也尝试过不同的睡眠时间。

【讨论】:

  • 解决了问题! UDP 丢失了一些包(那是咝咝作响的噪音),而结巴来自未分类的传入包。在 TCP/IP 上一切正常!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-06-03
  • 2019-05-05
  • 2016-05-21
  • 2012-07-30
  • 2012-03-14
  • 2010-10-22
  • 1970-01-01
相关资源
最近更新 更多