Friday, 21 September 2007

Streaming Audio on J2ME

For years I've been wanting to create a circular audio buffer for J2ME. Midi is okay but what I would really like is MOD playback, with maybe a few real-time sound effects thrown in, and generally a way of altering sound on-the-fly. I'd tried a mixer based on a blocking InputStream, which worked in Sun's WTK emulator but not on any actual phones (which load in the entire stream). I'd also tried a custom MMAPI DataSource, which didn't work either (same result as using an InputStream). I'd even tried the "switching between two Player instances" method, which although it (sorta) worked, wasn't low latency.

One idea I was hopeful about was running an RTSP (Real Time Streaming Protocol) server on the phone and playing the audio stream using MMAPI. This was interesting since the latency could realistically be as low as 1/25th of a second, perfect for generated sound and audio effects. I started by reading the protocol documentation (RFC 2326), along with RTP (RFC 3550) and SDP (RFC 4566), then by snooping the traffic between Quicktime and Darwin Streaming Server. The whole thing was hacked together in marathon coding session, then refined the following day. I'd like to end by saying "and it worked beautifully" but unfortunately whilst the server itself worked, playing the stream didn't work on any of the phones I tried. Grab the source code and have a play. Start the server running in WTK (or use the GCF implementation I posted earlier) and listen to the audio stream (which is just a rasping noise) in Quicktime, Realplayer, VLC, etc. Try the midlet version on a phone and you might get lucky... then again, you might not! I tried it in a few Sony Ericsson and Nokia phones, which I know can play back RTSP streams. The Nokia phones (6600, 6630) showed the most promise, since the loopback address appears to work, unlike the SE phones (K750, K800).

The server isn't the most elegant you'll ever see, and a better method would be to run the RTP part in a single thread, pumping out packets to all the connected clients. Then again, it was only designed for one client (the phone itself) and works well for this purpose. Like most of the code I'm planning on posting, I'm releasing it under a "do with it as you please, only give me credit for it" license. If you have any bugs, questions or suggestions, I'd like to hear them.

18 comments:

Daniel said...

Have you looked at the Audible.com Java player? It includes a decoder for the speech codec they use, and so can play their DRM'd audiobooks on a MIDP phone (downloading new chunks of the audio on demand). Unfortunately there are short gaps in the audio every few minutes, probably as a result of one of the horrible techniques you mentioned.

Carl Woffenden said...

No, I haven't seen that but from what you say I'm guessing it swaps between two Player instances. Using something like a uLaw (or by simply using 8-bit samples) it's possible to squeeze a minute of audio into 500k, which would work well with most modern phones.

When I have the time I plan to try my RTSP server with all of the standard codecs, to hopefully find one that works (and doesn't eat up the CPU for realtime work).

Börje said...

Do you know if the player get realized()? I have a similar application and that is my problem.

Carl Woffenden said...

With the phones I tested I never managed to realize a Player. The K800, which I had the most hope for, doesn't appear to support running a local server and connecting to it on the loopback address. Of the phones I tried that do support the loopback address (6600 & 6630), none support RTSP from MMAPI (but it's still possible to launch RealPlayer using MIDlet.platformRequest(), which also fails since Real can't play any of the encodings I tried).

Jayaseelan said...

Carl Woffenden,
Is it possible to use for making phone call through mobile in J2ME with SIP. How to transfer Audio at real time one mobile to another via ASTERISK SIP server. What is SDP and RTP.

Carl Woffenden said...

Although it works in theory, on real phones it doesn't, so it's not much use (but yes, it would work on the playback side of SIP). RTP is the real-time transport protocol and SDP is the session description protocol.

Jayaseelan said...

Carl Woffenden,

thanks for reply. What i want to know means? While making call using SIP i sent Invite request to SIP server. and It reaches the destination 180 ringing and 200 OK also came to my device. After that how i transfer my voice to that phone using RTP. Any idea would be very useful for me. If u dont mine plz give ur mail id. My id is mailtoseelan@gmail.com.

Regards
Jayaseelan.V

Opu said...

Is it possible to send voice without using rtsp server?
That is only using the RTP with Udp.

apus29@gmail.com

Carl Woffenden said...

Hello, Opu. There should be no reason why you couldn't send voice using any protocol you wanted, as long as you write it yourself. Many phones do support UDP connections but whether it works or not depends on the service provider.

Jorge said...

Hi, I too have spent some months trying to find a workaround for the lack of circular audio buffer... with no convincing results. Only thing that worked was using 2 players one after the other, but of course it only works with noisy sound effects. We used it to create a "crowd" sound effect in a soccer game. Well, congratulations for your efforts, I wish you to succeed. On my side, I think I'll try other platforms (Iphone) because JavaMe is quite frustrating. :-)

Carl Woffenden said...

I tried the two audio players approach but, as you said, didn't find the result acceptable. And like you, in the end I found J2ME too limited and moved on.

applicationbistro.com said...

Has anyone tried using one of the Blackberry simulators with this code?

I am going to try it on my Blackberry Bold (9000).... hope it works.

Param said...

Hi Carl,
I was trying to run ServerDemo.java with the following link rtsp://64.202.98.91:554/ipr.sdp
e.g. Player player = Manager.createPlayer("rtsp://64.202.98.91:554/ipr.sdp");

and I get the exception javax.microedition.media.MediaException: Cannot create a Player for: rtsp://64.202.98.91:554/ipr.sdp

http://www.bbc.co.uk/comedy/wavworld/error/computer.wav plays fine the only problem is with rtsp://64.202.98.91:554/ipr.sdp link; the link play fine in VLC player though

any idea on this?

Any help would be appreciated... Thanks

EnochRoot said...

Hi Carl,
first of all nice work! I tried to use your code on a BlackBerry device and had to change some parts concerning the UI. But it is "working" at least I receive noise in my VLC player. But I do not understand where the actual music data is transferred. Perhaps I do not get it, but don't you just fill the header?

I'll tried to extend the datagram by reading bytes from a wav-file. At least the noise changed and sounds like a very disturbed version of a song. But I am not sure how to set the payload correctly. Thanks for help!

nisha said...

hi

How to do video streaming in j2me
can u guide me please
give me some suggestions...
awaiting for your reply

advance thx

Carl Woffenden said...

@Param

I'm guessing either the player doesn't support the format chosen for the audio. Try experimenting with setting different codec types.

@EnochRoot

I wrote a version that streamed a wav file but it's been so long since I saw this code I don't remember the details. If you hear something approaching the correct sound, I guess you're either not getting the endianess of the audio stream correct or have the wrong format.

@nisha

Sorry, I can't help you on that.

Erco said...

Dear Carl,

As you can see, people are still reading your thread :)

Nice work and I would like to continue working on it. I replaced the "noise" by a wav file containing music, and I'm experiencing the same problem as EnochRoot: when listening to the wav file using VLC as client, the music is very, very noisy, hardly recognizable.

You've indicated to EnochRoot that this might be caused by an error in endianess or another format error. I've been working on this, without positive result.

I've searched internet, but have not been able to find evidence that endianess is of importance for the payload.

Would you have a clue how I can solve this problem or point me in the right direction?

Thanks!

Regards, Erco Argante

Carl Woffenden said...

Hi, Erco. Yeah, I'm surprised the thread is still going!

The byte order of the payload should be important, but a wild guess would put it in network order, the same as Java. The first thing I would do is try streaming 8-bit mono data, then you're guaranteed no endiandess issues. Post your code on-line somewhere and I'll try to take a look.

Regards,

Carl