Game Development Community

Memory leak in streaming audio

by Richard McKinney · in Torque Game Builder · 11/01/2006 (4:42 pm) · 7 replies

I have posted a fix for this bug below and replicated the fix in the report bugs forums. Check the last post here for the fixes to get rid of the streaming ogg memory leak.

Starting and stopping streaming audio causes a memory leak. I've tried many different versions of openal32.dll/wrap_oal.dll and they all have it. It looks like the memory is being allocated by openal, so you can't see it as a leak in Torque.

I can open up a console and just keep doing:
$musicHandle = alxPlay (musicProfile);
alxStop ($musicHandle);

over and over again and memory will jump about 500k each time. If you're only starting and stopping music a couple of times in a game you won't notice it much, but when you're changing music at rapid intervals it becomes a problem quickly.

Currently, to get around the memory being leaked I'm picking a central point in our game and reinitializing the openAL driver entirely calling:

OpenALShutdownDriver();
OpenALInitDriver();

This frees up the memory that was leaked, but this is not an ideal solution (although I think I'm going to have to ship with it). I am watching this with perfmon, not just task manager. It looks like memory's just not getting freed correctly from the alDeleteBuffers() call.

There is no problem with audio profiles whose descriptions do not have isStreaming=true set, but that adds 70-90 megs from music alone to our runtime memory requirement so it's a pretty bad solution.

I tried using alxCreateSource() and playing those instead of the profiles, but when using streaming audio, that fails as soon as you stop the audio with "alxStop: handle in inactive list" because the stream was created with AUDIOHANDLE_INACTIVE_BIT, but isn't placed in the inactive list. Maybe I'm just using that wrong, or maybe I need to play with the source more.

If I can find a better solution to this, I'll update, but until then has anyone else fixed/circumvented this leak? I have a few more things to try still, but I'm almost out of time.

Thanks for any information!

#1
11/01/2006 (9:52 pm)
To update here are the lines that allocate memory in this cycle and what they're allocating, followed by what's deallocating on the alxStop:

alxPlay:
streamSource->mDescription = *desc; (added 4096 bytes)

if(vf.ov_open(stream, NULL, 0) < 0) (added 102,400 bytes)

long ret = oggRead(data, BUFFERSIZE, ENDIAN, &current_section); (added 45,056 bytes, 0 on subsequent iterations)

alBufferData(mBufferList[loop], format, data, ret, freq); (added 36,864 bytes each iteration for a total of 516,096 bytes)

Total allocated: 667,648k

alxStop:
alDeleteBuffers() call frees 0 bytes...
vf.ov_clear() deleted 135,168 bytes.

Total deallocated: 135,168.

Total missing: 532,480 bytes. That's exactly the size alloc'd by alBufferData() + 16k.
#2
11/02/2006 (9:31 am)
I have identified the source of this problem. The buffers in openAL are reference counted and they're never being reduced to zero, so the one time VorbisStreamSource calls alDeleteBuffers() it just skips over them. Here is a fix to this issue. I will replicate this fix in the report bugs forum.

In audio/VorbisStreamSource.h, add the following line in the private section (just above clearBuffers() is fine):

void deleteBuffers();
In audio/VorbisStreamSource.cc:

in VorbisStreamSource::Clear() change
if(mBufferList[0] != 0)
      alDeleteBuffers(NUMBUFFERS, mBufferList);
   for(int i = 0; i < NUMBUFFERS; i++)
      mBufferList[i] = 0;
to
deleteBuffers();
in VorbisStreamSource::freeStream() change
if(mBufferList[0] != 0)
         alDeleteBuffers(NUMBUFFERS, mBufferList);
      for(int i = 0; i < NUMBUFFERS; i++)
         mBufferList[i] = 0;
to
deleteBuffers();
and finally add the deleteBuffers method just below VorbisStreamSource::freeStream():
void VorbisStreamSource::deleteBuffers()
{
   // Ensure that the refCount on the buffers is zero:
   alSourceUnqueueBuffers (mSource, NUMBUFFERS, mBufferList);

   if(mBufferList[0] != 0)
      alDeleteBuffers(NUMBUFFERS, mBufferList);
   for(int i = 0; i < NUMBUFFERS; i++)
      mBufferList[i] = 0;
}


If you're lazy you could just add that call to alSourceUnqueueBuffers() to both freeStream() and clear() instead of calling deleteBuffers().
#3
02/10/2010 (12:52 pm)
Great fix, works perfect on TGE :)
#4
02/10/2010 (11:15 pm)
Has this made it into the latest TGB or are we still having to manually edit the source to fix this? The original post was from 2006 lol...
#5
02/07/2011 (7:18 am)
It is not added to TGB 1.7.5
#6
02/07/2011 (8:10 am)
Adding in to my copy. thanks
#7
01/22/2012 (1:59 pm)
Bumping this 2006 thread, since this fix does not seem to be in 1.76 either.