MOTODEV // TECHNICAL ARTICLES
Welcome to MOTODEV // Please log in.

JSR 135: The Mobile Media API

Introduction

JSR-135, the Mobile Media API (MMAPI), enables the playing and recording of audio, taking pictures and playing videos, plus many other media related activities. One of the interesting capabilities of this API is audio capture and playback. In this article we will illustrate how this API can be used to achieve a very common requirement of recording voice and playing it back to the user.

Getting Started

Before starting to develop using JSR-135, we recommend checking which parts of the API are supported for your specific handset by referring to the device matrix (Device APIs.xml) that comes with the Motorola SDK (and is also available in MOTODEV Studio for Java ME).

For details of the handsets which support this API, see the Emulator A.5 group in the Device/API Matrix, provided as part of the Javadocs in the latest Motorola Java ME SDK for Motorola OS Products.

Mobile Media API Packages

The MMAPI comprises three packages:

  • javax.microedition.media provides some interfaces, an exception, and the Manager class, which is the access point for obtaining system-dependent resources such as a Player for multimedia processing.
  • javax.microedition.media.control defines the specific control types that can be used with a Player: VolumeControl, VideoControl, and others.
  • javax.microedition.media.protocol defines the protocols for handling custom controls. For example, it includes the DataSource class, which is an abstraction for media-control handlers.

Basic Concepts of the Mobile Media API

Protocol and Content Handling

Basically, multimedia processing can be broken into two parts:

  1. Handling the data delivery protocol
  2. Handling the data content

Protocol handling refers to reading data from a source (such as a file, capture device, or streaming server) into a media processing system. Content handling usually requires processing the media data (parsing or decoding, for example) and rendering the media to output devices such as an audio speaker or video display.

Two high-level objects are used in this API: DataSource and Player. Each object encapsulates the two parts of multimedia processing:

  1. DataSource for protocol handling
  2. Player for content handling

A DataSource encapsulates protocol handling. It hides the details of how the data is read from its source, whether the data is coming from a file, streaming server, or proprietary delivery mechanism. DataSource provides a set of methods to allow a Player to read data from it for processing.

The Manager class is a factory mechanism used to create a Player. The underlying implementation of a Player is specific to the content type being requested. Once a Player is created, an application can request Control objects that encapsulate behaviors to control the media playback, capture, and so forth. The Control objects available vary depending on the media supported by the Player.

A Player reads from the DataSource, processes the data, and renders the media to the output device. It provides a set of methods to control media playback and basic synchronization. Players also provide some type-specific controls to access features for specific media types.

A factory mechanism, the Manager, creates a Player from a DataSource. For convenience, the Manager also provides methods to create a Player from a Locator and an InputStream.

Player States

A Player has five states: UNREALIZED, REALIZED, PREFETCHED, STARTED and CLOSED. A Player implementation must allow successful state transition to each of these states using the six state-transition methods:

  • realize
  • prefetch
  • start
  • stop
  • deallocate
  • close

This means that the implementation must guarantee that these methods succeed under normal runtime conditions. This is to ensure that an implementation provides Players that are functional.

The Player interface contains methods to move the Player through the above states.

realize()-->Move the Player from the Unrealized state to the realize state.
prefetch(-->Move the Player from realized state to prefetched state.
start()--> Move the Player to the start state.
stop()-->Move the Player to the close state.
deallocate()-->Asks to release resources.
close()-->Move the Player to the close state.

Capturing Audio

The basic steps for capturing audio are:

  1. Create the Player for capturing audio
  2. From the Player, get the RecordControl
  3. Then use the RecordControl to set the OutputStream
  4. Start recording

The following code demonstrates audio record for 3 seconds:

// Create a DataSource that captures live audio
	p = Manager.createPlayer("capture://audio?encoding=audio/amr");
	p.realize();
// Get the RecordControl over this player and set the record stream
	rc = (RecordControl)p.getControl("RecordControl");
// create an OutputStream which the RecordControl will use to write the recorded data.
	baos = new ByteArrayOutputStream();
	rc.setRecordStream(baos);
// start the recording
	rc.startRecord();
	p.start();
// record for 3 seconds
	Thread.sleep(3000);
	rc.commit();
	p.stop();
// save the recordedData in a byte array
	recordedSound = baos.toByteArray();
// close the player
//p.close();

Audio Playback

Once the audio has been recorded, we can play the audio back. The first step for playing audio is to create a Player. Here is an example:

	Player pl = Manager.createPlayer(recordedInputStream,"audio/amr");

Then extract the byte array from the ByteArrayOutputStream and use it as an argument to a ByteArrayInputStream contructor when creating a new ByteArrayInputStream object. The following code demonstrates this:

	ByteArrayInputStream recordedInputStream = new
	ByteArrayInputStream(recordedSound);

Now you can prefetch the audio and start to play it using the prefetch() and start() methods o the player interface.

The following code demonstrates this

pl.prefetch();
pl.start();

Conclusion

This article provides a basic guide to recording and playing audio. A list of supported JSR-135 features available on each device can be found in the Device Matrix (Device APIs.xml) which comes as part of the Motorola SDK.

For a complete code example, see the Appendix.

Appendix – Code Example

The following code demonstrates the use of the Multi Media API.

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.media.control.RecordControl;
import javax.microedition.media.Manager;
import javax.microedition.media.Player;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

/**
* @author Motodev
* @version
*/
public class CaptureAudio extends MIDlet implements CommandListener
{
private Form captureForm = new Form("Capture Audio");
private Command recordCommand = new Command("Record",Command.OK, 1);
private Command playCommand = new Command("Play", Command.SCREEN, 2);
private StringItem supProp = new StringItem( "", "" );
private byte[] recordedSound;
private Player p;
private RecordControl rc;
private ByteArrayOutputStream baos;
public void startApp()
{
supProp.setText( "Does the device support audio capture ? " +
System.getProperty("supports.audio.capture")+"\n\n");
captureForm.append(supProp );
captureForm.addCommand(recordCommand);
captureForm.addCommand(playCommand);
captureForm.setCommandListener(this);

Display.getDisplay(this).setCurrent(captureForm);
}

public void pauseApp()
{
}

public void destroyApp(boolean unconditional)
{
}

public void commandAction(Command comm, Displayable dis)
{
if(comm == playCommand) {

try
{
captureForm.append("Starting playback\n\n ");
ByteArrayInputStream recordedInputStream = new ByteArrayInputStream(recordedSound);
Player pl = Manager.createPlayer(recordedInputStream,"audio/amr");
pl.prefetch();
pl.start();
}
catch (Exception e) {
captureForm.append("Error: "+e.toString());
}
}
else if(comm == recordCommand) {

try {

captureForm.append(" Record your voice\n\n ");
// Create a DataSource that captures live audio
p = Manager.createPlayer("capture://audio");
p.realize();

// Get the RecordControl over this player and set the record stream
rc = (RecordControl)p.getControl("RecordControl");
// create an OutputStream which the RecordControl will use to write the recorded data.
baos = new ByteArrayOutputStream();
rc.setRecordStream(baos);
// start the recording
rc.startRecord();
p.start();
// record for 3 seconds
Thread.sleep(3000);
rc.commit();
p.stop();
captureForm.append("Done Recording\n\n ");
// save the recordedData in a byte array
recordedSound = baos.toByteArray();
// close the player
//p.close();

}
catch (Exception ex)
{
captureForm.append("Error: " + ex.toString());
}

}
}
}
Note

NOTE: This is a cut down example for clarity; it is good practice to have the capture and playback code running in its own thread.

Back to Top
Was This Document Helpful?
Yes  No 

Additional Comments (Optional)