Multimedia applications - Media Sampler

This section provides the source code for the Media Sampler. For the complete Eclipse project ZIP file , see Forum Nokia.

The example includes the following classes:

MediaSamplerMIDlet

package com.nokia.example.mmapi.mediasampler;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

import com.nokia.example.mmapi.mediasampler.data.MediaFactory;
import com.nokia.example.mmapi.mediasampler.viewer.MediaList;

/**
 * MMAPI example MIDlet class.
 */
public class MediaSamplerMIDlet extends MIDlet
{
    private final MediaList list;

    public MediaSamplerMIDlet()
    {
        MediaFactory.setMidlet(this);
        list = new MediaList(this);
    }

    /**
     * Overriden MIDlet method
     */
    public void startApp()
    {
        Displayable current = Display.getDisplay(this).getCurrent();
        if (current == null)
        {
            // first call
            Display.getDisplay(this).setCurrent(list);
        }
        else
        {
            Display.getDisplay(this).setCurrent(current);
        }
    }

    /**
     * Overriden MIDlet method
     */
    public void pauseApp()
    {

    }

    /**
     * Overriden MIDlet method.
     */
    public void destroyApp(boolean unconditional)
    {
        list.releaseResources();
    }

    /**
     * Exits the MIDlet. Called when exit command is selected from the
     * MediaList.
     */
    public void listExit()
    {
        exitRequested();
    }

    /**
     * Exits the MIDlet.
     */
    private void exitRequested()
    {
        destroyApp(false);
        notifyDestroyed();
    }

    /**
     * Displays an error message in Alert.
     * 
     * @param message
     *            String as message to display.
     */
    public void alertError(String message)
    {
        Alert alert = new Alert("Error", message, null, AlertType.ERROR);
        Display display = Display.getDisplay(this);
        Displayable current = display.getCurrent();
        if (!(current instanceof Alert))
        {
            // This next call can't be done when current is an Alert
            display.setCurrent(alert, current);
        }
    }

}

Media

package com.nokia.example.mmapi.mediasampler.data;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;

/**
 * Datatype for the Medias.
 */
public class Media
{
    private String file;

    private String type;

    private int location;

    public static final int LOCATION_JAR = 0;

    public static final int LOCATION_HTTP = 1;

    public static final int LOCATION_FILE = 3;

    public Media(String file, String type, int location)
    {
        this.file = file;
        this.type = type;
        this.location = location;
    }

    /**
     * Return location type of this media.
     * 
     * @return int as type of this media location. Type value is one of the
     *         constants LOCATION_JAR, LOCATION_HTTP or LOCATION_FILE.
     */
    public int getLocation()
    {
        return location;
    }

    /**
     * Return file location of this Media.
     * 
     * @return String as url or path to media.
     */
    public String getFile()
    {
        return file;
    }

    /**
     * Returns mime type of this media.
     * 
     * @return String as mime type
     */
    public String getType()
    {
        return type;
    }

    /**
     * Returns InputStream to the media data.
     * 
     * @return InputStream
     * @throws IOException
     */
    public InputStream getInputStream() throws IOException
    {
        if (location == LOCATION_JAR)
        {
            return getClass().getResourceAsStream(file);
        }
        else if (location == LOCATION_HTTP)
        {
            return urlToStream(file);
        }

        throw new IOException("Not supported location type!");
    }

    /**
     * Reads the content from the specified HTTP URL and returns InputStream
     * where the contents are read.
     * 
     * @return InputStream
     * @throws IOException
     */
    private InputStream urlToStream(String url) throws IOException
    {
        // Open connection to the http url...
        HttpConnection connection = (HttpConnection) Connector.open(url);
        DataInputStream dataIn = connection.openDataInputStream();
        byte[] buffer = new byte[1000];
        int read = -1;
        // Read the content from url.
        ByteArrayOutputStream byteout = new ByteArrayOutputStream();
        while ((read = dataIn.read(buffer)) >= 0)
        {
            byteout.write(buffer, 0, read);
        }
        dataIn.close();
        // Fill InputStream to return with content read from the URL.
        ByteArrayInputStream byteIn = new ByteArrayInputStream(byteout
                .toByteArray());
        return byteIn;

    }
}

MediaFactory

package com.nokia.example.mmapi.mediasampler.data;

import javax.microedition.media.control.ToneControl;
import javax.microedition.midlet.MIDlet;

/**
 * MediaFactory is a provides class for medias.
 */
public class MediaFactory
{
    private static boolean soundInitDone = false;

    private static boolean videoInitDone = false;

    private static Media[] soundMedias;

    private static Media[] videoMedias;

    private static MIDlet mlet;

    public static Media getDefaultARM()
    {
        return getSoundMedias()[1];
    }

    public static Media getDefaultMidi()
    {
        return getSoundMedias()[0];
    }

    public static Media getDefaultXWav()
    {
        return getSoundMedias()[3];
    }

    public static Media getDefaultWav()
    {
        return getSoundMedias()[2];
    }

    public static Media getDRMMidi()
    {
        String drmFile = mlet.getAppProperty("DRM-Audio-Clip");
        Media drmMedia = new Media(drmFile, "audio/midi", Media.LOCATION_FILE);
        return drmMedia;
    }

    public static Media getDefaultVideo()
    {
        return getVideoMedias()[0];
    }

    public static void setMidlet(MIDlet midlet)
    {
        mlet = midlet;
    }

    public static Media[] getAllMedias()
    {
        return null;
    }

    public static Media[] getSoundMedias()
    {
        if (!soundInitDone)
        {
            String armFile = mlet.getAppProperty("AMR-Audio-Clip");
            String midiFile = mlet.getAppProperty("MIDI-Audio-Clip");
            String wavFile = mlet.getAppProperty("WAV-Audio-Clip");

            Media m1 = new Media(midiFile, "audio/midi", Media.LOCATION_JAR);
            Media m2 = new Media(armFile, "audio/amr", Media.LOCATION_JAR);
            Media m3 = new Media(wavFile, "audio/wav", Media.LOCATION_JAR);
            Media m4 = new Media(wavFile, "audio/x-wav", Media.LOCATION_JAR);

            soundMedias = new Media[] { m1, m2, m3, m4 };
            soundInitDone = true;
        }

        return soundMedias;
    }

    public static Media[] getVideoMedias()
    {
        if (!videoInitDone)
        {
            String videoFile = mlet.getAppProperty("Video-Clip");
            Media m = new Media(videoFile, "video/3gpp", Media.LOCATION_JAR);
            videoMedias = new Media[] { m };
            videoInitDone = true;
        }

        return videoMedias;
    }

    public static byte[] getToneSequence()
    {
        byte[] sequence = { ToneControl.VERSION, 1, ToneControl.TEMPO, 30, // times
                                                                            // 4 =
                                                                            // 120
                                                                            // beats-per-minute
                ToneControl.C4, 16, ToneControl.C4 + 2, 16, // D4
                ToneControl.C4 + 4, 16, // E4
                ToneControl.C4 + 5, 16, // F4 (note E# does not exist)
                ToneControl.C4 + 7, 16, // G4
                ToneControl.C4 + 9, 16, // A4
                ToneControl.C4 + 11, 16, // B4
                ToneControl.C4 + 9, 8, // A4
                ToneControl.C4 + 7, 8, // G4
                ToneControl.C4 + 5, 8, // F4 (note E# does not exist)
                ToneControl.C4 + 4, 8, // E4
                ToneControl.C4 + 2, 8, // D4
                ToneControl.C4, 8, };
        return sequence;
    }
}

DRMPlayer

package com.nokia.example.mmapi.mediasampler.model;

import java.io.IOException;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.media.Manager;
import javax.microedition.media.MediaException;
import javax.microedition.media.Player;
import javax.microedition.media.PlayerListener;

import com.nokia.example.mmapi.mediasampler.MediaSamplerMIDlet;
import com.nokia.example.mmapi.mediasampler.viewer.MediaList;

/**
 * Player with DRM protected audio.
 */
public class DRMPlayer implements CommandListener, PlayerListener
{
    private Player player;

    private MediaSamplerMIDlet midlet;

    private final Command stopCommand;

    private MediaList list;

    public DRMPlayer(MediaSamplerMIDlet midlet, MediaList list)
    {
        this.midlet = midlet;
        this.list = list;

        stopCommand = new Command("Stop", Command.ITEM, 2);
    }

    /**
     * Releases resources
     * 
     */
    public void releaseResources()
    {
        discardPlayer();
    }

    public void commandAction(Command cmd, Displayable disp)
    {
        if (cmd == stopCommand)
        {
            discardPlayer();
        }
    }

    public void playerUpdate(final Player p, final String event,
            final Object eventData)
    {
        // queue a call to updateEvent in the user interface event queue
        Display display = Display.getDisplay(midlet);
        display.callSerially(new Runnable()
        {
            public void run()
            {
                DRMPlayer.this.updateEvent(p, event, eventData);
            }
        });
    }

    public void stop()
    {
        if (player != null)
        {
            player.close();
        }
    }

    // Called in case of exception to make sure invalid players are closed
    private void discardPlayer()
    {
        if (player != null)
        {
            player.close();
            player = null;
        }
        list.removeCommand(stopCommand);
    }

    private void updateEvent(Player p, String event, Object eventData)
    {
        if (event == END_OF_MEDIA)
        {
            discardPlayer();
        }
        else if (event == CLOSED)
        {
            player = null;
            list.removeCommand(stopCommand);
        }
    }

    public void playDRMprotected(String file, String type)
    {
        if (file == null)
        {
            midlet.alertError("No file specified for type " + type);
        }
        else
        {
            try
            {
                player = Manager.createPlayer(file);
                player.addPlayerListener(this);
                player.realize();
                player.start();
                list.addCommand(stopCommand);
            }
            catch (IllegalArgumentException iae)
            {
                discardPlayer();
                midlet.alertError("IllegalArgumentException: "
                        + iae.getMessage() + " file: " + file);
            }
            catch (IOException ioe)
            {
                discardPlayer();
                midlet.alertError("IOException: " + ioe.getMessage()
                        + " file: " + file);
            }
            catch (MediaException me)
            {
                discardPlayer();
                midlet.alertError("MediaException: " + me.getMessage()
                        + " file: " + file);
            }
            catch (SecurityException se)
            {
                discardPlayer();
                midlet.alertError("SecurityException: " + se.getMessage()
                        + " file: " + file);
            }
        }
    }
}

PlayerPool

package com.nokia.example.mmapi.mediasampler.model;

import java.io.IOException;
import java.io.InputStream;
import java.util.Vector;

import javax.microedition.media.Manager;
import javax.microedition.media.MediaException;
import javax.microedition.media.Player;
import javax.microedition.media.PlayerListener;
import javax.microedition.media.control.StopTimeControl;
import javax.microedition.media.control.ToneControl;
import javax.microedition.media.control.VolumeControl;

import com.nokia.example.mmapi.mediasampler.MediaSamplerMIDlet;
import com.nokia.example.mmapi.mediasampler.data.Media;

/**
 * PlayerPool enables multiple realized / prefetched players.
 */
public class PlayerPool implements PlayerListener
{
    protected Vector players;

    protected Vector medias;

    protected MediaSamplerMIDlet midlet;

    protected int latency;

    boolean supportMultiPlayer = true;

    private int volumeLevel = 100;

    private long stopTime = StopTimeControl.RESET;

    private int realizedSoundIndex;

    public PlayerPool(MediaSamplerMIDlet midlet, int latency)
    {
        this.midlet = midlet;
        this.latency = latency;
        players = new Vector();
        medias = new Vector();
    }

    /**
     * PlayerListener interface's method.
     */
    public void playerUpdate(Player player, String event, Object eventData)
    {
    }

    /**
     * Returns boolean indication is media mimetype supported by the Device.
     * 
     * @param mimeType
     *            String as media mime-type
     * @return boolean is supported
     */
    public boolean isMediaSupported(String mimeType)
    {
        String[] types = Manager.getSupportedContentTypes(null);
        for (int i = 0; i < types.length; i++)
        {
            if (mimeType.toLowerCase().equals(types[i].toLowerCase()))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * Add media to pool.
     * 
     * @param media -
     *            Media to add
     */
    public void addMedia(Media media) throws MediaException
    {
        String mimeType = media.getType();
        if (!isMediaSupported(mimeType))
        {
            throw new MediaException("Type " + mimeType + " is not supported!");
        }

        addSoundObject(media);
    }

    /**
     * Return true if device supports multiple simultaneous players.
     * 
     * @return a boolean indication are multiple players supported.
     */
    public boolean supportsMulplePlayers()
    {
        return supportMultiPlayer;
    }

    /**
     * Adds Tone Sequence to this
     * 
     * @param sequence
     * @throws MediaException -
     *             if tone sequence is not supported
     */
    public void addToneSequence(byte[] sequence) throws MediaException
    {
        if (!isMediaSupported("audio/x-tone-seq"))
        {
            throw new MediaException(
                    "Tone (audio/x-tone-seq) is not supported!");
        }

        addSoundObject(sequence);
    }

    /**
     * Creates and initializes the Player
     * 
     * @param sequence -
     *            tone sequence data in byte array
     * @return realized tone sequence Player
     * @throws MediaException
     *             thrown by the system while creating the player
     * @throws IOException
     *             thrown by the system while creating the player
     */
    private Player createTonePlayer(byte[] sequence) throws MediaException,
            IOException
    {
        Player player = Manager.createPlayer(Manager.TONE_DEVICE_LOCATOR);
        player.addPlayerListener(this);
        player.realize();
        ToneControl tc = (ToneControl) (player.getControl("ToneControl"));
        tc.setSequence(sequence);
        return player;
    }

    /**
     * Creates and initializes the Player.
     * 
     * @param media -
     *            Media datatype that represents the data
     * @return realized tone sequence Player
     * @throws MediaException
     *             occured while creating the player
     * @throws IOException
     *             occured while creating the player
     */
    private Player createPlayer(Object media) throws MediaException,
            IOException
    {
        if (media instanceof byte[])
        {
            return createTonePlayer((byte[]) media);
        }
        return createPlayer((Media) media);
    }

    /**
     * Creates and initializes the Player.
     * 
     * @param media -
     *            Media datatype that represents the data
     * @return realized tone sequence Player
     * @throws MediaException
     *             occured while creating the player
     * @throws IOException
     *             occured while creating the player
     */
    private Player createPlayer(Media media) throws MediaException, IOException
    {
        InputStream is = media.getInputStream();
        String mediaType = media.getType();

        Player player = Manager.createPlayer(is, mediaType);
        player.addPlayerListener(this);
        player.realize();
        player.prefetch();
        return player;
    }

    /**
     * Closes the players and removes all references.
     */
    public void releaseResources()
    {
        discardAll();
        players.removeAllElements();
        medias.removeAllElements();
    }

    /**
     * Set the volume level of players.
     * 
     * @param volume
     *            int as volume level in range 0-100.
     */
    public void setVolumeLevel(int volume)
    {
        volumeLevel = volume;
        updateVolumeLevel();
    }

    /**
     * Set the stop time of players.
     * 
     * @param time
     *            long as stop time in millis
     */
    public void setStopTime(long time)
    {
        this.stopTime = time;
        updateStopTime();
    }

    /**
     * Plays the specified sound.
     * 
     * @param index
     *            int as sound index to play
     */
    public void playSound(int index) throws MediaException
    {
        if (supportMultiPlayer)
        {
            if (index >= 0 && index < players.size())
            {
                Player player = (Player) players.elementAt(index);
                startPlayer(player);
            }
        }
        else
        {
            // use old player if already exists (has been previously played)
            if (index == realizedSoundIndex)
            {
                Player player = (Player) players.elementAt(0);
                startPlayer(player);
                return;
            }
            // otherwise close old player and create a new one.
            discardAll();
            players.removeAllElements();
            try
            {
                Object obj = medias.elementAt(index);
                Player player = createPlayer(obj);
                players.addElement(player);
                updateStopTime();
                updateVolumeLevel();
                realizedSoundIndex = index;
                startPlayer(player);
            }
            catch (IOException ioe)
            {
                midlet.alertError("IOException: " + ioe.getMessage());
            }
        }
    }

    private void startPlayer(Player player)
    {
        try
        {
            if (player != null)
            {
                if (player.getState() == Player.UNREALIZED)
                {
                    player.prefetch();
                    player.realize();
                }

                if (player.getState() != Player.CLOSED)
                {
                    Thread.sleep(latency);
                    player.start();
                }
            }

            Thread.sleep(latency);
        }
        catch (InterruptedException e)
        {
        }
        catch (MediaException e)
        {
            discardPlayer(player);
            midlet.alertError("MediaException: " + e.getMessage());
        }
    }

    public void discardPlayer(Player player)
    {
        if (player != null)
        {
            player.close();
        }
    }

    public void discardAll()
    {
        for (int i = 0; i < players.size(); i++)
        {
            Player player = (Player) players.elementAt(i);
            discardPlayer(player);
        }
    }

    /**
     * Set volume level to existing players.
     */
    private void updateVolumeLevel()
    {
        int size = players.size();
        for (int i = 0; i < size; i++) // Set the same volume level for all
                                        // players
        {
            Player player = (Player) players.elementAt(i);
            VolumeControl control = (VolumeControl) player
                    .getControl("VolumeControl");
            control.setLevel(volumeLevel);
        }
    }

    /**
     * Set current stop-time to existing players.
     */
    private void updateStopTime()
    {
        int size = players.size();
        for (int i = 0; i < size; i++) // Set the same stop time for all
                                       // players
        {
            Player player = (Player) players.elementAt(i);
            StopTimeControl control = (StopTimeControl) player
                    .getControl("StopTimeControl");
            control.setStopTime(stopTime);
        }
    }

    private void addSoundObject(Object sndObject) throws MediaException
    {
        if (!supportMultiPlayer)
        {
            medias.addElement(sndObject);
        }
        else
        {
            try
            {
                Player player = createPlayer(sndObject);
                players.addElement(player);
                medias.addElement(sndObject);
            }
            catch (MediaException se)
            {
                // Let's assume that second player creation failes even media
                // type
                // should be supported.
                if (supportMultiPlayer && players.size() == 1)
                {
                    Player failedPlayer = (Player) players.elementAt(0);
                    discardPlayer(failedPlayer);
                    players.removeElementAt(0);

                    try
                    {
                        Player player = createPlayer(sndObject);
                        // Assuming that Player creation works if there is not
                        // other instances,
                        // we let the pool work in single Player instance mode.
                        supportMultiPlayer = false;
                        medias.addElement(sndObject);
                        // Update player vector to contain only current.
                        players.removeAllElements();
                        players.addElement(player);
                        realizedSoundIndex = medias.size() - 1;
                    }
                    catch (MediaException me)
                    {
                        // Cannot create player.
                        throw me;
                    }
                    catch (IOException ioe)
                    {
                        midlet.alertError("IOException: " + ioe.getMessage());
                    }
                }
                else
                {
                    // Reject this media and route Exception to method caller.
                    throw se;
                }
            }
            catch (IOException ioe)
            {
                midlet.alertError("IOException: " + ioe.getMessage());
            }
        }
    }

}

AudioCanvas

package com.nokia.example.mmapi.mediasampler.viewer;

import java.util.Vector;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.media.MediaException;

import com.nokia.example.mmapi.mediasampler.MediaSamplerMIDlet;
import com.nokia.example.mmapi.mediasampler.data.Media;
import com.nokia.example.mmapi.mediasampler.data.MediaFactory;
import com.nokia.example.mmapi.mediasampler.model.PlayerPool;

/**
 * Audio play canvas. Audio clip is played when a key is pressed.
 */
public class AudioCanvas extends Canvas implements CommandListener
{
    private PlayerPool pool;

    private MediaSamplerMIDlet midlet;

    private Displayable returnScreen;

    protected String[] supportedMediaNames;

    protected String[] unsupportedMediaNames;

    protected int countOfPlayers = 0;

    protected int volume = 100;

    private boolean initDone;

    private boolean infoMode;

    private Command infoCommand = new Command("Info", Command.SCREEN, 1);

    private Command backCommand = new Command("Back", Command.BACK, 1);

    public AudioCanvas(MediaSamplerMIDlet midlet, Displayable returnScreen,
            int latency)
    {
        this.midlet = midlet;
        this.returnScreen = returnScreen;

        pool = new PlayerPool(midlet, latency);
        initSounds();
        // Init volume level of Players in pool
        pool.setVolumeLevel(volume);
        addCommand(backCommand);
        addCommand(infoCommand);
        setCommandListener(this);
    }

    /**
     * Release loaded resources
     */
    public void releaseResources()
    {
        pool.releaseResources();
    }

    /**
     * Implemented CommandListener method.
     */
    public void commandAction(Command cmd, Displayable d)
    {
        if (cmd == backCommand)
        {
            if (infoMode)
            {
                infoMode = false;
                addCommand(infoCommand);
                repaint();
            }
            else
            {
                Display.getDisplay(midlet).setCurrent(returnScreen);
            }
        }
        else if (cmd == infoCommand)
        {
            infoMode = true;
            removeCommand(infoCommand);
            repaint();
        }
    }

    public void keyPressed(int key)
    {
        int keyCode = key - KEY_NUM0;
        int gameAction = getGameAction(key);

        // Check is the selected audio available for playing.
        if (keyCode > 0 && keyCode <= countOfPlayers)
        {
            try
            {
                pool.playSound(keyCode - 1);
            }
            catch (MediaException e)
            {
                // Swallow the exception.
            }

        }
        else if (gameAction == UP)
        {
            increaseVolume();
        }
        else if (gameAction == DOWN)
        {
            decreaseVolume();
        }
    }

    /**
     * Paint the canvas.
     */
    protected void paint(Graphics g)
    {
        int x = 0;
        int y = 0;
        int w = getWidth();
        int h = getHeight();

        g.setColor(0xFFFFFF);
        g.fillRect(x, y, w, h);

        Font fontPlain = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN,
                Font.SIZE_SMALL);
        Font fontBold = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD,
                Font.SIZE_SMALL);

        g.setColor(0x000000);

        if (infoMode)
        {
            boolean multiSupport = pool.supportsMulplePlayers();
            boolean mixSupport = "true".equals(System
                    .getProperty("supports.mixing"));

            g.setFont(fontBold);
            y = paintTextRow(g, "Supports multiple players:", x, y);
            g.setFont(fontPlain);
            y = paintTextRow(g, "" + multiSupport, x, y);
            g.setFont(fontBold);
            y = paintTextRow(g, "Supports audio mixing:", x, y);
            g.setFont(fontPlain);
            y = paintTextRow(g, "" + mixSupport, x, y);
            if (unsupportedMediaNames.length > 0)
            {
                g.setFont(fontBold);
                g.setColor(0x000000);
                y = paintTextRow(g, "Unsupported sounds:", x, y);
                g.setFont(fontPlain);
                for (int i = 0; i < unsupportedMediaNames.length; i++)
                {
                    String str = unsupportedMediaNames[i];
                    String strToPaint = str;
                    y = paintTextRow(g, strToPaint, x, y);
                }
            }

        }
        else
        {
            g.setFont(fontBold);
            y = paintTextRow(g, "Sound key mapping:", x, y);
            g.setFont(fontPlain);
            for (int i = 0; i < supportedMediaNames.length; i++)
            {
                String str = supportedMediaNames[i];
                String strToPaint = (i + 1) + " = " + str;
                y = paintTextRow(g, strToPaint, x, y);
            }
            y = paintTextRow(g, "Volume level: " + volume, x, y);
        }

    }

    /**
     * Renders a text row to Canvas.
     */
    private int paintTextRow(Graphics g, String text, int x, int y)
    {
        int w = getWidth();

        Font font = g.getFont();
        for (int j = 0; j < text.length(); j++)
        {
            char c = text.charAt(j);
            int cw = font.charWidth(c);
            if (x + cw > w)
            {
                x = 0;
                y += font.getHeight();
            }
            g.drawChar(c, x, y, Graphics.TOP | Graphics.LEFT);
            x += cw;
        }
        y += font.getHeight();
        return y;
    }

    private void increaseVolume()
    {
        volume += 10;
        if (volume > 100)
        {
            volume = 100;
        }
        pool.setVolumeLevel(volume);
        repaint();
    }

    private void decreaseVolume()
    {
        volume -= 10;
        if (volume < 0)
        {
            volume = 0;
        }
        pool.setVolumeLevel(volume);
        repaint();
    }

    /**
     * Loads the medias available on this canvas. Loaded sounds are passed to
     * PlayerPool class which creates players and initializes player states.
     */
    protected void initSounds()
    {
        if (!initDone)
        {
            countOfPlayers = 0;
            Vector supportedMedias = new Vector();
            Vector unsupportedMedias = new Vector();

            // Sound media clips
            Media[] medias = MediaFactory.getSoundMedias();
            for (int i = 0; i < medias.length; i++)
            {
                Media media = medias[i];
                String mediaName = null;
                try
                {
                    mediaName = media.getFile() + " [" + media.getType() + "]";
                    pool.addMedia(media);
                    supportedMedias.addElement(mediaName);
                    countOfPlayers++;
                }
                catch (MediaException e)
                {
                    System.out.println("unsupported media: " + media.getType());
                    unsupportedMedias.addElement(mediaName);
                }
            }

            // Tone sequences
            String mediaName = null;
            try
            {
                mediaName = "Tone sequence";
                pool.addToneSequence(MediaFactory.getToneSequence());
                supportedMedias.addElement(mediaName);
                countOfPlayers++;
            }
            catch (MediaException e)
            {
                System.out.println("Unsupported type: Tone sequence");
                e.printStackTrace();
                unsupportedMedias.addElement(mediaName);
            }

            // Stores results of success and failed Players to String array
            supportedMediaNames = new String[supportedMedias.size()];
            supportedMedias.copyInto(supportedMediaNames);

            unsupportedMediaNames = new String[unsupportedMedias.size()];
            unsupportedMedias.copyInto(unsupportedMediaNames);
            initDone = true;
        }
    }

}

MediaList

package com.nokia.example.mmapi.mediasampler.viewer;

import javax.microedition.lcdui.*;

import com.nokia.example.mmapi.mediasampler.MediaSamplerMIDlet;
import com.nokia.example.mmapi.mediasampler.data.Media;
import com.nokia.example.mmapi.mediasampler.data.MediaFactory;
import com.nokia.example.mmapi.mediasampler.model.DRMPlayer;

/**
 * Media list is the main view in the MIDlet. From this view the user may swich
 * to other views.
 */
public class MediaList extends List implements CommandListener
{
    private final MediaSamplerMIDlet midlet;

    private final VideoSourceSelector sourceSelector;

    private AudioCanvas audioCanvas;

    private SupportForm form;

    private final Command exitCommand;

    public MediaList(MediaSamplerMIDlet midlet)
    {
        super("Media Sampler", IMPLICIT);

        this.midlet = midlet;

        sourceSelector = new VideoSourceSelector(midlet, this);

        append("Play audio", null);
        append("Play video", null);
        append("DRM MIDI", null);
        append("Check MM API support", null);

        exitCommand = new Command("Exit", Command.EXIT, 1);
        addCommand(exitCommand);
        setCommandListener(this);
    }

    /**
     * Release all resources loaded by sub components.
     */
    public void releaseResources()
    {
        if (audioCanvas != null)
        {
            audioCanvas.releaseResources();
        }
    }

    /**
     * Implemented CommandListener method.
     * 
     * Sets the selected Example visible
     */
    public void commandAction(Command cmd, Displayable disp)
    {
        if (cmd == List.SELECT_COMMAND)
        {
            int index = getSelectedIndex();
            if (index != -1) // -1 means nothing selected
            {
                String selected = getString(index);

                if (selected.equals("Play video"))
                {
                    Display.getDisplay(midlet).setCurrent(sourceSelector);
                }
                else if (selected.equals("DRM MIDI"))
                {
                    Media media = MediaFactory.getDRMMidi();
                    DRMPlayer player = new DRMPlayer(midlet, this);
                    player.playDRMprotected(media.getFile(), media.getType());
                }
                else if (selected.equals("Play audio"))
                {
                    if (audioCanvas == null)
                    {
                        audioCanvas = new AudioCanvas(midlet, this, 0);
                    }
                    Display.getDisplay(midlet).setCurrent(audioCanvas);
                }
                else if (selected.equals("Check MM API support"))
                {
                    if (form == null)
                    {
                        form = new SupportForm(midlet, this);
                    }
                    Display.getDisplay(midlet).setCurrent(form);
                }
            }
        }
        else if (cmd == exitCommand)
        {
            midlet.listExit();
        }
    }
}

SupportForm

package com.nokia.example.mmapi.mediasampler.viewer;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.media.Manager;

import com.nokia.example.mmapi.mediasampler.MediaSamplerMIDlet;

/**
 * View that outputs MM API's System properties to a Form.
 */
public class SupportForm extends Form implements CommandListener
{
    private MediaSamplerMIDlet midlet;

    private Displayable medialist;

    private Command backCmd = new Command("Back", Command.STOP, 1);

    public SupportForm(MediaSamplerMIDlet midlet, Displayable medialist)
    {
        super("MM API support check");

        this.midlet = midlet;
        this.medialist = medialist;

        addCommand(backCmd);
        setCommandListener(this);

        init();
    }

    private void init()
    {

        String apiVersion = System.getProperty("microedition.media.version");

        append("MM API version:" + apiVersion + "\n");

        append("Mixing supported: " + System.getProperty("supports.mixing")
                + "\n");

        append("Audio capture supported: "
                + System.getProperty("supports.audio.capture") + "\n");
        append("Video capture supported: "
                + System.getProperty("supports.video.capture") + "\n");

        append("Recording supported: "
                + System.getProperty("supports.recording") + "\n");
        append("Supported audio encodings: "
                + System.getProperty("audio.encodings") + "\n");
        append("Supported video encodings: "
                + System.getProperty("video.encodings") + "\n");
        append("Supported video snaphot encodings: "
                + System.getProperty("video.snapshot.encodings") + "\n");
        append("\n");

        String streamable = System.getProperty("streamable.contents");
        if (streamable == null)
            append("Streaming: not supported.\n");
        else
        {
            append("Streamable contents: " + streamable);

            String[] rtp = Manager.getSupportedContentTypes("rtp");
            if (rtp != null && rtp.length > 0)
            {
                append("RTP protocol supported.");
            }

            String rtsp[] = Manager.getSupportedContentTypes("rtsp");
            if (rtsp != null && rtsp.length > 0)
            {
                append("RTSP protocol supported.");
            }
        }

        String[] contentTypes = Manager.getSupportedContentTypes(null);
        if (contentTypes != null)
        {
            append("\n\nAll supported content types:\n");

            for (int i = 0; i < contentTypes.length; i++)
            {
                append(contentTypes[i] + "\n");
            }
        }
    }

    public void commandAction(Command cmd, Displayable d)
    {
        Display.getDisplay(midlet).setCurrent(medialist);
    }

}

VideoCanvas

package com.nokia.example.mmapi.mediasampler.viewer;

import java.io.*;

import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;
import javax.microedition.lcdui.*;
import javax.microedition.media.*;
import javax.microedition.media.control.*;

import com.nokia.example.mmapi.mediasampler.MediaSamplerMIDlet;

/**
 * VideoCanvas renders video on Canvas.
 */
class VideoCanvas extends Canvas implements CommandListener, PlayerListener
{
    private PlayerController controller;

    private MediaSamplerMIDlet midlet;

    private Displayable returnScreen;

    private String videoFile;

    private Command stopCommand;

    private Command replayCommand;

    private Command backCommand;

    private Player player;

    private boolean initDone;

    private boolean playPending = false;

    /**
     * Constructor.
     * 
     * @param midlet
     *            MediaSamplerMIDlet
     * @param returnScreen
     *            Displayable to set visible when returned from this Canvas
     * @param videoFile
     *            String as path of the source viudeo file.
     */
    VideoCanvas(MediaSamplerMIDlet midlet, Displayable returnScreen,
            String videoFile)
    {
        this.midlet = midlet;
        this.returnScreen = returnScreen;
        this.videoFile = videoFile;
        controller = new PlayerController();

        replayCommand = new Command("Replay", Command.SCREEN, 1);
        stopCommand = new Command("Stop", Command.SCREEN, 2);
        backCommand = new Command("Back", Command.BACK, 1);
        addCommand(backCommand);
        setCommandListener(this);
    }

    /**
     * Set play status to "pending".
     */
    void prepareToPlay()
    {
        controller.start();
        playPending = true;
    }

    /**
     * Play video only when we're displayed. Use playPending flag to avoid
     * restarting if a system screen is visiable.
     */
    public void showNotify()
    {
        if (playPending)
        {
            playPending = false;
            controller.playVideo();
        }
    }

    /**
     * Renders the Canvas.
     */
    public void paint(Graphics g)
    {
        g.setColor(0x00FFFF00); // yellow
        g.fillRect(0, 0, getWidth(), getHeight());
    }

    /**
     * Implemented CommandListener method. Indicates that a command event has
     * occurred on Displayable d.
     */
    public void commandAction(Command c, Displayable d)
    {
        if (c == stopCommand)
        {
            controller.stopVideo();
        }
        else if (c == replayCommand)
        {
            controller.playVideo();
        }
        else if (c == backCommand)
        {
            discardPlayer();
        }
    }

    /**
     * Overriden Canvas method.
     */
    public void keyPressed(int keyCode)
    {
        if (getGameAction(keyCode) == FIRE)
        {
            int state = player.getState();
            if (state == Player.STARTED)
            {
                controller.stopVideo();
            }
            else
            {
                controller.playVideo();
            }
        }
    }

    /**
     * Reads the content from the specified HTTP URL and returns InputStream
     * where the contents are read.
     * 
     * @return InputStream
     * @throws IOException
     */
    private InputStream urlToStream(String url) throws IOException
    {
        // Open connection to the http url...
        HttpConnection connection = (HttpConnection) Connector.open(url);
        DataInputStream dataIn = connection.openDataInputStream();
        byte[] buffer = new byte[1000];
        int read = -1;
        // Read the content from url.
        ByteArrayOutputStream byteout = new ByteArrayOutputStream();
        while ((read = dataIn.read(buffer)) >= 0)
        {
            byteout.write(buffer, 0, read);
        }
        dataIn.close();
        connection.close();
        // Fill InputStream to return with content read from the URL.
        ByteArrayInputStream byteIn = new ByteArrayInputStream(byteout
                .toByteArray());
        return byteIn;
    }

    /**
     * Stops the Player.
     */
    void doStop()
    {
        if (player != null)
        {
            try
            {
                player.stop();
            }
            catch (MediaException e)
            {
                e.printStackTrace();
            }
        }
    }

    /**
     * Initializes and starts the Player.
     */
    void doPlay()
    {
        try
        {
            if (!initDone || player == null)
            {
                initPlayer();
            }

            int state = player.getState();
            if (state == Player.CLOSED)
            {
                player.prefetch();
            }
            else if (state == Player.UNREALIZED)
            {
                player.realize();

            }
            else if (state == Player.REALIZED)
            {
                player.prefetch();
            }

            player.start();
        }
        catch (MediaException me)
        {
            discardPlayer();
            midlet.alertError("MediaException: " + me.getMessage());
        }
        catch (SecurityException se)
        {
            discardPlayer();
            midlet.alertError("SecurityException: " + se.getMessage());
        }
        catch (Exception e)
        {
            discardPlayer();
            midlet.alertError("Exception: " + e.getMessage());
        }
    }

    /**
     * Initializes the video player.
     * 
     * Player is initialized only once to save the memory resorces and to
     * increase performance.
     */
    void initPlayer()
    {
        try
        {
            initDone = false;
            if (videoFile == null)
            {
                midlet.alertError("No video file specified");
                return;
            }
            boolean fromHttp = videoFile.startsWith("http://");

            InputStream is = fromHttp ? urlToStream(videoFile) : getClass()
                    .getResourceAsStream(videoFile);
            player = Manager.createPlayer(is, "video/3gpp");
            player.addPlayerListener(this);
            player.prefetch();
            player.realize();

            // get the video control and attach it to our canvas
            VideoControl videoControl = (VideoControl) (player
                    .getControl("VideoControl"));
            if (videoControl == null)
            {
                midlet.alertError("VideoControl not supported");
            }
            else
            {
                videoControl.initDisplayMode(VideoControl.USE_DIRECT_VIDEO,
                        this);
                videoControl.setVisible(true);
            }
            
            initDone = true;
        }
        catch (IOException ioe)
        {
            discardPlayer();
            midlet.alertError("IOException: " + ioe.getMessage());
        }
        catch (MediaException me)
        {
            discardPlayer();
            midlet.alertError("MediaException: " + me.getMessage());
        }
        catch (SecurityException se)
        {
            discardPlayer();
            midlet.alertError("SecurityException: " + se.getMessage());
        }
        catch (Exception ex)
        {
            discardPlayer();
            midlet.alertError("Exception: " + ex.getMessage());
        }

    }

    /**
     * Called in case of exception to make sure invalid players are closed
     */
    void discardPlayer()
    {
        if (player != null)
        {
            controller.setStopped();
            player.close();
            player = null;
            Display.getDisplay(midlet).setCurrent(returnScreen);
        }
    }

    /**
     * Implemented javax.microedition.media.PlayerListener method.
     */
    public void playerUpdate(final Player p, final String event,
            final Object eventData)
    {
        // queue a call to updateEvent in the user interface event queue
        Display display = Display.getDisplay(midlet);
        display.callSerially(new Runnable()
        {
            public void run()
            {
                VideoCanvas.this.updateEvent(p, event, eventData);
            }
        });
    }

    /**
     * Handles playerUpdate events of the Player.
     * 
     * @param p
     * @param event
     * @param eventData
     */
    void updateEvent(Player p, String event, Object eventData)
    {
        if (event == END_OF_MEDIA)
        {
            addCommand(replayCommand);
            removeCommand(stopCommand);
        }
        else if (event == CLOSED)
        {
            addCommand(replayCommand);
            removeCommand(stopCommand);
        }
        else if (event == STARTED)
        {
            removeCommand(replayCommand);
            addCommand(stopCommand);
        }
        else if (event.equals(ERROR))
        {
        }
    }

    /**
     * PlayerController calls the play and stop methods of the Player. The
     * purpose of this class is only to isolate Player method calls from the
     * event threads (such commandAction(...))
     */
    public class PlayerController extends Thread
    {
        private boolean running;

        // Lock object ofthis Thread
        private Object controllLock = new Object();

        public PlayerController()
        {
        }

        // Activates the Player
        public void playVideo()
        {
            synchronized (controllLock)
            {
                controllLock.notify();
            }
        }

        // Deactivates the player
        public void stopVideo()
        {
            synchronized (controllLock)
            {
                doStop();
            }
        }

        // Terminates this thread
        public void setStopped()
        {
            running = false;
            synchronized (controllLock)
            {
                controllLock.notify();
            }
        }

        public void run()
        {
            VideoCanvas.this.doPlay();

            running = true;
            while (running)
            {
                try
                {
                    synchronized (controllLock)
                    {
                        // Set this thread to wait for playVideo() method call.
                        controllLock.wait();
                        if (!running) // Leave if controller is stopped.
                        {
                            break;
                        }
                        VideoCanvas.this.doPlay();
                    }
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        }
    }
}

VideoSourceSelector

package com.nokia.example.mmapi.mediasampler.viewer;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.List;
import javax.microedition.lcdui.TextField;

import com.nokia.example.mmapi.mediasampler.MediaSamplerMIDlet;
import com.nokia.example.mmapi.mediasampler.data.MediaFactory;

/**
 * List that allow user to select Video source.
 */
public class VideoSourceSelector extends List implements CommandListener
{
    private MediaSamplerMIDlet midlet;

    private List returnList;

    private HTTPUrlForm urlForm;

    private Command backCommand = new Command("Back", Command.BACK, 1);

    public VideoSourceSelector(MediaSamplerMIDlet midlet, MediaList list)
    {
        super("Select Video Source", List.IMPLICIT);
        this.midlet = midlet;
        this.returnList = list;
        urlForm = new HTTPUrlForm();
        append("From http", null);
        append("From jar", null);
        addCommand(backCommand);
        setCommandListener(this);
    }

    public void commandAction(Command cmd, Displayable d)
    {
        if (cmd == SELECT_COMMAND)
        {
            int selection = getSelectedIndex();
            if (selection == 0) // URL source selected
            {
                Display.getDisplay(midlet).setCurrent(urlForm);
            }
            else if (selection == 1) // JAR source selected
            {
                // File name returned by the MediaFactory refers to the
                // "Video-Clip" application property...
                String videoFile = MediaFactory.getDefaultVideo().getFile();
                commitSelection(videoFile);
            }
        }
        else if (cmd == backCommand)
        {
            Display.getDisplay(midlet).setCurrent(returnList);
        }
    }

    /**
     * Initializes and set visible the video canvas with the selected video
     * source.
     * 
     * @param input
     *            String as video http url or file path.
     */
    private void commitSelection(String url)
    {
        try
        {
            VideoCanvas canvas = new VideoCanvas(midlet, returnList, url);
            canvas.prepareToPlay();
            Display.getDisplay(midlet).setCurrent(canvas);
        }
        catch (Exception e)
        {
            midlet.alertError("Cannot open connection: " + e.getMessage());
        }
    }

    /**
     * Form for the video URL input.
     */
    public class HTTPUrlForm extends Form implements CommandListener
    {
        TextField tf = new TextField("URL", "http://", 100, TextField.URL);

        Command cmdOK = new Command("OK", Command.OK, 1);

        Command cmdBack = new Command("Back", Command.BACK, 1);

        public HTTPUrlForm()
        {
            super("HTTP Address");
            String defaultURL = VideoSourceSelector.this.midlet
                    .getAppProperty("Video-URL");
            if (defaultURL != null && defaultURL.length() > 0)
            {
                tf.setString(defaultURL);
            }
            append(tf);
            addCommand(cmdOK);
            addCommand(cmdBack);
            setCommandListener(this);
        }

        public void commandAction(Command cmd, Displayable d)
        {
            if (cmd == cmdOK)
            {
                String url = tf.getString();
                commitSelection(url);
            }
            else
            {
                Display.getDisplay(midlet).setCurrent(VideoSourceSelector.this);
            }
        }
    }
}