We're implementing a program for Android phones that plays audio streamed from the internet. Here's approximately what we do:
Our target devices so far are Droid and Nexus One. Everything works great on Nexus One, but the MP3 decode is too slow on Droid. The audio playback starts to skip if we put the Droid under load. We are not permitted to decode the MP3 data to SD card, but I know that's not our problem anyways.
We didn't write our own MP3 decoder, but used MPADEC (http://sourceforge.net/projects/mpadec/). It's free and was easy to integrate with our program. We compile it with the NDK.
After exhaustive analysis with various profiling tools, we're convinced that it's this decoder that is falling behind.
Here's the options we're thinking about:
Find another MP3 decoder that we can compile with the Android NDK. This MP3 decoder would have to be either optimized to run on mobile ARM devices or maybe use integer-only math or some other optimizations to increase performance.
Since the built-in Android MediaPlayer service will take URLs, we might be able to implement a tiny HTTP server in our program and serve the MediaPlayer with the decrypted MP3s. That way we can take advantage of the built-in MP3 decoder.
Get access to the built-in MP3 decoder through the NDK. I don't know if this is possible.
Does anyone have any suggestions on what we can do to speed up our MP3 decoding?
-- Rob Sz
The right way to do this is to build your own firmware and do the decryption as part of a custom OpenCORE codec. That, of course, will limit you to devices where you can install that firmware.
Bear in mind that the rest of this is somewhat speculative. I actually have a need to do something similar to what you're describing, but I don't have time set aside to tackle the problem for a couple of months. So, I'll describe this in the fashion of how I'd approach the problem.
One solution is the one described in twk's answer. You don't have to use the SD card, but you probably do have to have a world-readable temporary file in your app-local file store (getFilesDir()
). Download the first chunk, decrypt it, write it out as a complete world-readable MP3 file (but with a suitably obscure directory/path), and hand it to a MediaPlayer
via setDataSource()
. While that plays, you download/decrypt and set up a second MediaPlayer
instance, which starts playback as soon as the first one ends, for as seamless a transition as possible. You then reset the first MediaPlayer
and reuse it with your third chunk, ping-ponging between the two.
A related solution would be in jleedev's comment. It's pretty much the same thing, except that you provide a FileDescriptor
via a ContentProvider
. This has an option to let you use a socket, which may let you avoid the temporary file. However, the ContentProvider
will have to itself be publicly accessible, and so the temporary file, with an obscure directory, might actually be more private.
If you are worried about the fact that these things can be read by other processes, please understand that MediaPlayer
itself (or, rather, the OpenCORE subsystem) is in another process. Also, your proposed HTTP server is world-readable on the device as well. So, security by obscurity is your only viable option if you are going to let MediaPlayer
do the decoding.
AFAIK, the NDK does not grant access to OpenCORE, though I confess as to having limited NDK experience, so I may be wrong. There are certainly other MP3 decoders available (ffmpeg
/mplayer
, etc.), though how readily these could be converted into an NDK library is unclear.
So, it really boils down to who you're trying to defend against. If you're trying to defend against the user, you're probably going to have to decode it yourself, somehow.