4

It seems that media pipeline in Windows Phone 8.1 is broken because of a lot of memory management issues.

When you create a background audio app that uses IMediaSource to stream audio in Windows Phone Runtime 8.1, the app's components eventually throw OutOfMemoryException and even StackOverflowException under some circumstances. When looking through the memory dumps, there's a lot of uncollected garbage inside.

The discussion has started on MSDN forums and progressed to this conclusion. I have created a WPDev UserVoice suggestion in order Windows Phone team could notice this, but I still hope it's me (and other guys from MSDN forums) who's wrong and there's a solution for the issue.

I also have a small CodePlex project that also suffers from this, and there's actually an issue report there regarding this exact problem.

I hope that with the help of the community this issue can be worked around or passed directly to Microsoft development team to investigate and eliminate. Thanks!

Update 1:

There's a kind of workaround for StackOverflowException, but it doesn't help against OutOfMemoryException.

Alovchin
  • 663
  • 3
  • 9

2 Answers2

2

Okay, so it seems that the problem is actually with byte array's lifetime in .NET.

In order to resolve the memory problem, one shoud use Windows Runtime's Windows.Storage.Streams.IBuffer. Don't create many new .NET byte arrays in any form, neither by simple new byte[], nor by using System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeBuffer class as it is a managed implementation of IBuffer interface.

Those byte arrays, being once allocated, live long because of being pinned by OverlappedData structures and overflow the memory threshold for background audio task. IBuffers (the real Windows Runtime ones, like Windows.Storage.Streams.Buffer class) contain native arrays that are being deallocated as soon as IBuffer's reference count reaches 0 (zero), they don't rely on GC.

What I've found out is that this problem is not only background audio specific. Actually, I have seen a lot of other questions about similar problems. The solution is to use Windows Runtime backend where possible because it's unmanaged and frees resources as soon as they have zero references.

Thanks to @Soonts for pointing me in the right direction!

Alovchin
  • 663
  • 3
  • 9
1

They had memory issues with the way MSS manages its memory, but they have silently fixed it during some update: WP7 Background Audio - Memory Leak or Not?

I’m not sure, but I think the problem is your code. You just shouldn’t call var buffer = new byte[4096]; each time a sample is requested. Doing so may work on the PC, but for the embedded platform, I don’t think it’s a good idea to stress the memory manager that much.

In my MediaStreamSource implementation, I use a single circular buffer that is allocated when the MSS is constructed, and the portions of the buffer are infinitely reused during the playback. In my GetSampleAsync, I construct an instance of my Stream-implementing class, that doesn’t own any memory, but instead only holds a reference to a portion of that circular buffer. This way, only a few small objects are allocated/deallocated during the playback, thus the audio stream data does not load the memory manager.

Community
  • 1
  • 1
Soonts
  • 20,079
  • 9
  • 57
  • 130
  • This makes sence. Though I'm talking about Windows Phone 8.1 Runtime apps that use another (based on Media Foundation) [MediaStreamSource](http://msdn.microsoft.com/en-us/library/windows.media.core.mediastreamsource.aspx), slightly different from the Silverlight's one. And it buffers the whole stream and plays it simultaneously (I've seen it requested 132 samples before the first sample was processed) so I'm not pretty sure how to determine the optimal size of the circular buffer. Some code snippets would be highly appreciated :) – Alovchin Sep 08 '14 at 06:26
  • And moreover, Silverlight's MediaStreamSource implementation works flawlessly though it also allocates a buffer each time GetSampleAsync is called. – Alovchin Sep 08 '14 at 06:44
  • Your 132 samples are only 528 kb — less than 3 seconds of uncompressed CD-quality audio. The reason — OS audio subsystem also has some buffering inside. – Soonts Sep 08 '14 at 07:19
  • Silverlight and WinRT are different platforms. Very likely, there was slight change in either the CLR or the audio support that caused your less than optimal solution to stop working on WinRT. Besides, now in WinRT there’s a MediaStreamSample.Processed event you can handle to reuse the portion of the circular buffer (in Silverlight, I relied on the Stream.Read method called by the OS on my sample’s stream to mark portions of buffer free). I do not see any other reason to handle this event unless using a circular buffer. – Soonts Sep 08 '14 at 07:20
  • That's correct, but I tried to do that and the system actually requests samples faster than processes them (well, that's pretty obvious), thus making this approach useless. All I can think of now is a combination of single `byte[4096]` buffer and multiple `InMemoryRandomAccessStream`s that I believe (since they are native classes) clear their inner buffer memory on Dispose. – Alovchin Sep 08 '14 at 07:59
  • Well, using `InMemoryRandomAccessStream` is extremely slow, so I'm trying to use `IBuffer` instead. I still use single `byte[]` array that is then passed to `WindowsRuntimeBuffer.Create` method. Though I'm not pretty sure this reduces memory pressure since media pipeline still keeps all the buffered `MediaStreamSample`s, so I think the only solution would be to implement my own buffering algorythm. – Alovchin Sep 09 '14 at 07:41