Implementation

For information about the design and functionality of the MIDlet, see section Design.

The MediaList view is created and displayed by the MediaSamplerMIDlet class. A reference of MediaSamplerMIDlet is passed to all LCDUI elements used by the application (MediaList, AudioCanvas, VideoSourceSelector, and so on). The reference is used for getting the display and for displaying an alert when an error occurs. MediaList creates and displays the classes AudioCanvas, VideoSourceSelector, SupportForm, and DRMPlayer.

The MediaFactory class provides media-specific information such as available sound and video medias, and their MIME types and file paths in the MIDlet package (JAR file). MediaFactory reads resource-specific file path properties from the application descriptor (JAD file). Data information returned from MediaFactory is encapsulated in the Media class.

UI classes in the viewer package display the UI or route to another display.

For more information about the key aspects of implementing the MIDlet, see:

Media loading

The following code shows the method for loading resources as InputStream objects:

    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!");
    }

The following code shows the method for fetching content from a specified HTTP URL in the form of InputStream objects:

    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;
    }

ToneControl is the interface that enables playback of a user-defined monotonic tone sequence:

    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;
    }

Player pool

PlayerPool is a class that handles audio player pooling and playing. DRMPlayer plays DRM-protected audio files.

The following code shows the method for adding media to a pool:

    public void addMedia(Media media) throws MediaException {
        String mimeType = media.getType();
        if (!isMediaSupported(mimeType)) {
            throw new MediaException("Type " + mimeType + " is not supported!");
        }
        addSoundObject(media);
    }

The following code shows the method for adding a tone sequence:

    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);
    }

The following code shows the method for checking whether the media MIME type is supported by the device:

    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;
    }

The getSupportedContentTypes method returns the list of supported content types for the given protocol.

The following code shows the method for adding sound objects:

    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());
            }
        }
    }

The Player class controls the rendering of time-based media data. It provides the methods to manage the Player's lifecycle, controls the playback progress, obtains the presentation components, and controls and provides the means to synchronize with other Players.

The following code shows the methods for creating and initializing the Player with Object, Media, and byte[] parameters:

    private Player createPlayer(Object media) throws MediaException, IOException {
        if (media instanceof byte[]) {
            return createTonePlayer((byte[]) media);
        }
        return createPlayer((Media) media);
    }

    // ...

    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;
    }

    // ...

    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;
    }

The Manager class is the access point for obtaining system dependent resources, such as Players, for multimedia processing. The createPlayer method creates a Player from an input locator. The addPlayerListener method adds a player listener for a player, and the realize method constructs portions of the Player without acquiring the scarce and exclusive resources. The getControl method obtains the object that implements the specified Control interface, and the setSequence method sets the tone sequence.

The following code shows the method for playing a specific sound:

    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());
        }
    }

The getState method fetches the current state of the specified Player.

The following code shows the methods for closing a specific player or all players:

    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);
        }
    }

The following code shows the method for setting the volume level to the 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");
            actualVolume = (int) (((float) globalVolume / 100) * (float) midletVolume);
            control.setLevel(midletVolume);
        }
    }

The setLevel method sets the volume using a linear point scale with values between 0 and 100.

The following code shows the method for setting the current stop time to the 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);
        }
    }

The StopTimeControl class allows the specifying of a preset stop time for a Player. The setStopTime method sets the media time at which you want the Player to stop.

Playing DRM-protected files

A separate DRMPlayer object handles the playing of DRM-protected files. DRM-protected files can only be played using a direct locator string to a file. It is not possible to play DRM-protected files packaged inside the JAR file. For more information, see section Playing DRM-protected media.

The following code shows the method for playing DRM-protected files:

    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);
            }
        }
    }

Video playback

The following code shows the methods for stopping, and initializing and starting the player:

    void doStop() {
        if (player != null) {
            try {
                player.stop();
            } catch (MediaException e) {
                e.printStackTrace();
            }
        }
    }

    // ...

    void play() {
        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());
        }
    }

The following code shows the method for initializing the video player:

    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());
        }
    }

The VideoControl class controls the display of video. A Player which supports the playback of video must provide a VideoControl through its getControl and getControls methods. The initDisplayMode method initializes the mode on how the video is displayed.

Supported media types

The SupportForm class shows supported media types:

    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");
            }
        }
    }