In a LAN session, when someone sends voice data to another computer, the audio plays a single packet’s worth of voice and then just stops, even though there is still a steady stream of voice packets being sent and received. The receiver will continue to not play any voice audio, though the voice data is still being queued up. The sender must stop sending, and then start sending after some delay (around 1 second of silence) . Then the receiver will start playing all the queued up voice audio, and all the new audio must wait until the old audio is played back.
For consistent testing, I am using the ‘Stereo Mix’ recording device in Windows, which uses my computer’s audio output as microphone input.
Steps:
- Setup a simple blueprints LAN session, with voice enabled, voice always on (push to talk is off)
- Have a client send some voice (a song sent thru ‘Stereo Mix’ for easier and more consistent testing)
- Observe the receiver playing a tiny blip of audio and then silence
- Stop the song for a few seconds, then resume it
- Observe the receiver starting to play the start of the song which had been queued up
Here are some of my settings:
DefaultEngine.ini
[OnlineSubsystem]
DefaultPlatformService=Null
bHasVoiceEnabled=true
[Voice]
bEnabled=true
[/Script/OnlineSubsystemUtils.IpNetDriver]
MaxClientRate=30000
MaxInternetClientRate=30000
[/Script/Engine.Player]
ConfiguredLanSpeed=30000
ConfiguredInternetSpeed=30000
DefaultGame.ini
[/Script/Engine.GameSession]
bRequiresPushToTalk=false
I investigated by littering the source with logs, and discovered some useful info. Upon receiving the first voice packet, the voice engine consumes it, but then the buffer runs dry before the next voice packet arrives and the voice engine stops playback or stalls or something. When the next voice packet does arrive, it gets queued up in SoundWaveProcedural’s QueuedAudio, but the voice engine is still stuck or stopped or something. The only way to get it unstuck is to stop sending voice packets for some time, and then to resume sending them (basically have some period of silence). Then the voice engine will start playing through the queued voice data again.
I have a possible solution that appears to work, but I’m sure there is a better, more proper fix.
In VoiceEngineImpl.h, I added:
/** Is in process of queueing up some data to come back from starvation */
bool bRestartFromStarvation;
In VoiceEngineImpl.cpp, function SubmitRemoteVoiceData, at the bottom inside the last big if:
if (!QueuedData.AudioComponent->IsActive())
{
QueuedData.AudioComponent->Play();
}
USoundWaveProcedural* SoundStreaming = CastChecked<USoundWaveProcedural>(QueuedData.AudioComponent->Sound);
if (!bAudioComponentCreated && SoundStreaming->GetAvailableAudioByteCount() == 0)
{
// VOIP audio component was starved! This used to be a log inside an if(0). I added the following assignment
bRestartFromStarvation = true;
}
SoundStreaming->QueueAudio(DecompressedVoiceBuffer.GetData(), BytesWritten);
// I added this part too. If we are starved, hold out until we have some cushion so that we can play the audio without micro-stutters due to small packet timing inconsistencies. Then restart the audio component so it starts playing again
if (bRestartFromStarvation && SoundStreaming->GetAvailableAudioByteCount() >= (int32)(VOICE_SAMPLE_RATE * sizeof(int16) / 2))
{
bRestartFromStarvation = false;
QueuedData.AudioComponent->Stop();
QueuedData.AudioComponent->Play();
}
I hope that helps, let me know if I can help clarify anything!