Quantcast
Channel: Piterwilson
Viewing all articles
Browse latest Browse all 20

Android MediaPlayer, not quite there yet.

$
0
0

Disclaimer : I started writing this post weeks ago and since i have found out that is possible to use a relatively easier solution to the MediaPlayer issues by using AudioTrack in combination with MediaExtractor and MediaCodec, skipping the NDK solutions. I will write about this in an upcoming post.

This is a personal rant. One that has built up over months of frustration. It describes how difficult it has been to work with Android’s MediaPlayer class for the specific task of playing an .MP3 stream from a server.

I had to build the same exact app for iOS and the simplicity that the Cocoa framework offers for the exact same task is what you would expect.

So. Playing an .MP3 stream from a server. A trivial enough task. Surely one that is bound to come back over and over. One that is easily achieved in so many other languages. You can even get this done on a modern html page using a simple <audio> tag. (Even Actionscript had this down like a boss)

On iOS a look at the documentation takes us to the AVPlayer class. . Even when you add a bit of asynchronous stuff that needs to happen on a separate thread not to freeze up the UI, the result is pretty pleasing. The buffering time is reasonable. Did i hit a snag? well, a simple Google search and the solution was a my fingertips in seconds (Thanks stack overflow).The playback is smooth. After some time fiddling with the code, i had it working. Nice. Moving on to Android.

on Android, the recommended solution provided is called the MediaPlayer class.  In theory, it sounds pretty fine. I really like the way you set up listeners for all kinds of wonderful events.

mMediaPlayer = new MediaPlayer();
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setOnPreparedListener(this);
mMediaPlayer.setOnErrorListener(this);
mMediaPlayer.setOnCompletionListener(this);
mMediaPlayer.setOnInfoListener(this);
// let’s go already!
mMediaPlayer.setDataSource(this.mUrlString);
mMediaPlayer.prepareAsync();

At this point, after a bit fiddling with the code, i had this thing working. Nice. I thought…

The first thing i started to notice is that the buffer time, seemed a little bit slow. I didn’t pay to much attention at first, but it got painfully obvious after a while. I decided to figure out why.

Trough much painful Googling, it turned out that the MediaPlayer class has not way to manually set the amount of buffer required to start playback. It turned out, as a matter of fact that this value was HARDCODED. That’s right. Hardcoded.

wtf-mp

On top of this arbitrary condition, Google had added one more when they upgraded the code on version 4. They actually increased the value of the buffer by a factor of 8 from what it was in version 2. Fantastic. So that’s the reason for the long loading time at the start. What can you do? absolutely nothing (unless you don’t use MediaPlayer). This problem remains open even on Kit-Kat.

(see this official bug report https://code.google.com/p/android/issues/detail?id=29870 for more details)

The curious case of the Samsung S3

Love or hate it, Android’s fragmentation is a fact. There are thousands of devices to support, and we are not talking merely about screen sizes and Android OS versions. Sometimes event thought they all run a certain Android version they will not behave the same. One of the most embarrassing cases is the MediaPlayer behavior on the Samsung S3 when it’s running 4.1.2. That would be… just the most popular Android phone in the country (i’m talking about The Netherlands)?

To this day, despite all the fanfare of the new Kit-Kat release our S3 still runs 4.1.2 because of Samsung and their slow reaction time to write a new version of their customised rom.

At the beginning of the development process, besides the real slow simulator, we had a Galaxy Note 2, a Samsung Galaxy s2P and some HTC phone as test equipment. After taming MediaPlayer we were content at the result. So the buffer was a bit large… ok.

We released our radio app and the reports started coming in : The app doesn’t work. It doesn’t play the stream. The buffering wheel stays stuck or the stream plains refuses to play at all. This is how i learned what Android fragmentation can really mean.

After much panic, we were able to secure a S3 test machine, and low and behold… just as reported, the thing would just not play.

I eagerly plugged in the decide to my computer so i could examine the logcat output. And there it was clear as daylight… the phone would just not behave. The MediaPlayer class would fetch the audio buffer, freeze up for a few seconds and then spit out all kinds of errors that made no sense. The sequence goes come thing like this:

onInfo what 703 extra 141
MEDIA_ERROR -110
onInfo what 701 extra 0
info/warning (702, 0)
onInfo what 702 extra 0
Error (-38,0)

Sometimes i would also get the onCompletion handler randomly firing after all these errors.

It would seem that MediaPlayer chokes on the stream, falls all over itself (freezing the UI for a couple of seconds) then gives up throwing up error codes that are not defined in the official documentation.

I scrambled to stack overflow and found the following

http://stackoverflow.com/a/18378273/2754355

I wasn’t able to find anything else of substance regarding this issue. All i knew is that the code worked on every other phone except this. The most popular phone in the country.

My first thought was what a sloppy piece of code this Android Media Player is.

My second thought was… there are plenty of Android apps out there with stream players that are quite fast. How did they do it? i went back to the drawing board.

After a lot of research i figured out that all of these apps do not use the MediaPlayer class. Instead, these apps are using what i knows as “native code” ported to Android using the NDK. In other words, most of these apps don’t run Android code, instead they run C/C++ code ported to Android.

I saw code samples (that i couldn’t run) where people ported FFMPEG to either play the stream altogether, or mixed solutions like grabbing the stream and then using FFMPEG to convert it to PCM and then use other Android classes like AudioTrack to play the stream. Some other people had similar solutions using OpenSL ES.

I even found a player called acc-android-decoder which includes among others, the possibility to play an Mp3 stream with quite fast reaction time. It also includes a pretty hefty warning about licensing fees you might owe to patent trolls if you decide to use the library. A man from such a company, called technicolor told me that i had to pay a license to play an mp3 over the Internet.

Since other people out there had done this NDK bit, i thought i would give it a go. How hard could it be?

It is very hard. The documentation about the NDK is very short and confusing. This is a very complex operation requiring you to be fluent in the following:

  • Java
  • JNI (A kind of halfway between C/C++ and Java)
  • C/C++
  • Installing all kinds of Unix dependencies (makefiles … )

This is not for your average designer/developer.

During my research i exhausted all online resources hitting various dead ends (most examples dating from Android 2) that may or may not have to do with the configuration on my Mac OS Mavericks installation. Following tutorials online was painful as the people who write about this advanced topics have a tendency to skip over important things that they deem people should already know. Another point is that the installation of a lot of components might present lots of errors that are never covered in the guides.

I also bought some books, of which the best i found to be “Android Native Development Kit Cookbook”. This book actually got me trough my basic training on native code for Android. At this point i am comfortable with the NDK and writing not too complex JNI, C/C++ code.

It did not however, get me to a point where i could write the code required to play a stream using FFMPEG because that requires the documentation about FFMPEG to be accessible to programmers not familiar with C/C++ which it isn’t.

A couple of weeks into all of this, i realized that i am not a C programmer and i began to question if this level of complexity is something i want to deal with. Should i really be spending 2/3 weeks learning about all of this overly complicated topics only to be able to play a simple mp3 stream from a server?

This is where people will disagree. I bet that people with strong Java skills and a devoted attitude to Android will advice me to push harder and keep at C/C++, JNI and all the rest. Personally, i am a designer first and a developer second. I enjoy html, css, javascript, action script, php, Objective C and even Java. I seriously question why i should also be a C/C++ programmer to accomplish a simple task like this. Why has he Android team provided such a crippled, sloppy piece of work. I doubt that the person who wrote this class, actually uses it.

What kind of platform says : “Hey, here’s all this great code. By the way one of the classes doesn’t work, there’s no documentation about it, but it’s ok! … just write code in another language and port it to Android!”.

It’s like saying that you need to be some kind of Java, C/C++ engineer just to play a sound from the web. Or be rich to play off people like technicolor.

Compare that to AVPLayer, the Audio tag in html, the Sound class in action script and so on, which just work out of the box.

Android MediaPlayer. It’s not quite there yet.

Google is delivering an inferior product. They have the most popular platform. Why do they drag everyone down with sloppy code like this?

 


Viewing all articles
Browse latest Browse all 20

Latest Images

Trending Articles





Latest Images